@secuprint/mmdbjs
v1.0.0
Published
A module that allow MaxMind Database in the browser.
Readme
secuprint_mmdbjs
Frontend-only — Library to load and query MaxMind-style database files (MMDB) in the browser or from an
ArrayBuffer/Uint8Array. Provides fast prefix-based IP lookup (IPv4/IPv6), pointer caching and utilities with no external dependencies.
Table of contents
Status / Purpose
secuprint_mmdbjs is a TypeScript module designed to integrate a MaxMind-style binary database into web (client) applications. It parses the MMDB binary format, resolves IP prefixes via a bitwise trie, implements a pointer LRU cache, and exposes synchronous lookup functions once the DB is loaded.
Important: This library is implemented for browser / frontend use. When a string source is provided it uses fetch internally. For non-browser environments, pass an ArrayBuffer/Uint8Array directly.
Installation
Install from npm or yarn:
npm install git+https://github.com/secuprint/secuprint_mmdbjs.gitIt can also be imported from the published bundle (e.g. dist/index.mjs) if you include the generated bundle in a project.
Quick usage (examples)
Browser / URL
import Geo from 'secuprint_mmdbjs';
const geo = new Geo();
(async () => {
await geo.load('https://example.com/path/to/GeoLite2-City.mmdb');
const result = geo.lookup('8.8.8.8'); // result object | null
console.log(result);
})();ArrayBuffer / File (FileReader)
import Geo from 'secuprint_mmdbjs';
const geo = new Geo();
const fileInput = document.querySelector('input[type=file]')!;
fileInput.addEventListener('change', async (ev) => {
const f = (ev.target as HTMLInputElement).files![0];
const buffer = await f.arrayBuffer(); // ArrayBuffer
await geo.load(buffer);
console.log(geo.lookup('2001:4860:4860::8888'));
});Constructor with autoload
import Geo from 'secuprint_mmdbjs';
const geo = new Geo({ source: 'https://example.com/geo.mmdb', autoload: true });
// If autoload is true, the public promise can be awaited.
await geo.loadPromise;
const r = geo.lookup('1.2.3.4');Direct query with query
query is a convenience method that ensures the DB is loaded (and reloads if a different URL is provided), then performs the lookup.
import Geo from 'secuprint_mmdbjs';
const geo = new Geo();
const result = await geo.query('https://example.com/geo.mmdb', '8.8.4.4');
console.log(result);Public API (reference)
Exported types
type RecordValue = any;
type RecordObj = { value: RecordValue; ptr: number };
export interface GeoOptions {
source?: string | ArrayBuffer | Uint8Array;
lru?: number; // pointer cache size (default: 2000)
autoload?: boolean; // if true and source provided: load automatically
}Class Geo
export default class Geo {
constructor(options?: GeoOptions);
// Loads the DB. `source` can be a URL (string) or ArrayBuffer|Uint8Array.
// `lru` overrides the pointer cache size.
public async load(source: string | ArrayBuffer | Uint8Array, lru = 2000): Promise<void>;
// Synchronous lookup by IP (IPv4 or IPv6).
// Requires the DB to be already loaded (load() called previously).
// Returns `any` (data object) or `null` if not found.
public lookup(ip: string): any | null;
// Ensures the DB is loaded (if not, loads from sourceOrUrl) and does lookup.
// Returns Promise<any | null>.
public async query(sourceOrUrl: string | ArrayBuffer | Uint8Array, ip: string): Promise<any | null>;
// Clears all buffers and resets state (frees memory).
public clear(): void;
// Indicates whether a DB is currently loaded.
public hasDB(): boolean;
// Public property that may contain a promise if autoload was requested in constructor.
public loadPromise: Promise<void> | null;
}Behavior details and errors
load(source, lru)
- If
sourceis a string: performsfetch(source)internally. ThrowsError("Fetch failed: " + res.status)if the response is not OK. - If
sourceisArrayBuffer/Uint8Array, uses it directly. - Initializes binary readers,
TextDecoder, and cache (Map). - Searches for metadata position via an internal binary marker. If not found, throws
Error("Bad DB: metadata marker not found"). - Extracts
metadataand configuresrecordSize,numNodes,nodeSize,dataStart. - Creates internal
IpDecoderand computes IPv4 start pointer.
lookup(ip)
- Throws
Error("DB not loaded")if the DB is not loaded. - Supports IPv4 and IPv6 (IP is parsed internally).
- Returns
nullif the trie path leads to thenumNodesmarker (no entry).
query(sourceOrUrl, ip)
- If the DB is not loaded, loads
sourceOrUrl(if string: fetch). - If the DB is already loaded and
sourceOrUrlis a string different from"__loaded__", reloads the DB from that URL.
clear()
- Removes references to the buffer,
DataView,TextDecoder, cache and other internals, leaving the object ready to be reused withload().
Specific errors
Bad DB: metadata marker not found— incompatible format or truncated file.Fetch failed: <status>— failed to download from URL.DB not loaded— callinglookupbeforeload.
Internals (technical summary)
The parser detects the metadata section by searching for a static binary marker.
Binary readers:
- Primitive:
_read,_readBuffer,_readUTF8,_read32,_readDouble,_readFloat. - High-level:
readData(dispatches by type),readPointer(resolves pointers),cachedRead(LRU cache).
- Primitive:
Supported types: strings, maps, arrays, doubles, floats, 16/32/64/128-bit integers, booleans, bytes.
The prefix trie for IPs uses
readLeftandreadRight, behavior depends onrecordSize.IpDecodersupports IPv4 and IPv6 parsing andbitAt(i)for trie traversal.Pointers use size+base arithmetic; optimized paths exist for small sizes and 32-bit addresses.
Performance and configuration
Pointer cache: default
pointerCacheSizeis2000. Adjustable viaGeoOptions.lruor by passinglrutoload(...).- Simple LRU implemented over
Map: when exceedingpointerCacheSize, the oldest entry is removed.
- Simple LRU implemented over
Optimized binary reading: once
_read*functions are initialized, per-read branching is minimized.lookupis synchronous and optimized; primary latency is inload(I/O + parsing).
Expected return types
null— when the IP has no entry.string— for simple string records.number— for numeric types.object— for maps; may include:names— object keyed by language codes, e.g.{ en: "Country", es: "País" }.- Nested objects, arrays, bytes (as
Uint8Array).
No rigid schema is enforced; return values depend on the MMDB file used.
Frequently asked questions (FAQ)
Can I use this in Node.js?
The library is intended for the browser and uses fetch for URL loading. It can work in Node.js environments that provide globalThis.fetch (Node 18+). For other Node.js setups, pass ArrayBuffer/Uint8Array directly.
How do I obtain the MMDB file? This README does not include links. Use your preferred data source (MaxMind or compatible dumps). The file must contain the metadata marker expected by the parser.
Does it support streaming or partial loading?
No. The library expects the full DB in memory as a Uint8Array.
What does autoload do?
If options.autoload is true and options.source is provided, load(source) is started automatically and the promise is assigned to geo.loadPromise.
Final notes / License
- The package declares
"license": "SEE LICENSE FILE". Include aLICENSEfile (MIT, Apache, etc.) before publishing. - Verify
fetchcompatibility for your target browsers. - For unit tests use small MMDB files and both IPv4/IPv6 test cases.
Reference snippets (relevant code)
Type and default export
type RecordValue = any;
type RecordObj = { value: RecordValue; ptr: number };
export interface GeoOptions {
source?: string | ArrayBuffer | Uint8Array;
lru?: number;
autoload?: boolean;
}
export default class Geo { /* ... */ }Important detectable errors
Bad DB: metadata marker not foundFetch failed: <status>DB not loaded
