Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
or a custom `source`. Native entries also offer synchronous construction.
- Async clients use `lookupAsync`, `lookupManyAsync`, `lookupManySettledAsync`
and `closeAsync`; async adapters now implement `lookupAsync` and optional `closeAsync`.
Synchronous query method names remain unchanged. See the README migration table.
Synchronous query method names remain unchanged.

### Added and improved

Expand Down
18 changes: 1 addition & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,22 +176,6 @@ GitHub Actions 的 `Data updates` 每周日北京时间 00:00(UTC 周六 16:00
自动创建或更新数据 PR,也可手动运行。PR 合并和 npm 发布仍由维护者决定。
这是仓库维护工具,不会在导入、安装或查询时联网;操作与失败恢复说明见[数据维护](data/README.md)。

## 从 3.x 升级

4.0 统一使用类构造器,并明确区分同步与异步方法。已有 3.x 调用需要调整:

| 3.x | 4.0 |
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| `createLookup(source, options)` | `new IP2Region({ source, ...options })`,从根入口导入 |
| `createAsyncLookup(source, options)` | `new AsyncIP2Region({ source, ...options })` |
| 原生入口 `openDefault(options)` | `await IP2Region.open(options)` |
| 原生入口 `openDatabase(path, factory, options)` | 内置格式使用 `await IP2Region.open({ database: { format, path }, ...options })`;自定义读取器通过 `source` 接入 |
| 异步客户端 `lookup/lookupMany/lookupManySettled/close` | `lookupAsync/lookupManyAsync/lookupManySettledAsync/closeAsync` |
| 异步 adapter 的 `close()` | `closeAsync()`;`lookupAsync()` 保持不变 |

同步查询方法不变,外部来源仍默认借用。构造原生客户端时会同步读文件;需要保留异步加载行为,
请使用 `IP2Region.open()`。3.x 类型 `ReaderFactory` 和 `DefaultOptions` 不再导出;原生入口现在提供
`IP2RegionOptions`、`DatabaseOptions`、`DatabasesOptions`,自定义来源使用根入口的 adapter 类型。
运行时最低版本与 3.0 相同,见[安装](#安装)。
开发与测试见[开发指南](docs/development.md);性能数据与复现命令见[性能记录](docs/performance.md)。

代码采用 [MIT](LICENSE) 许可;内置数据许可见[数据来源](data/README.md)。
9 changes: 3 additions & 6 deletions data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,7 @@ recording new digests and rerunning reference and native-runtime validation.
branch weekly at **Sunday 00:00 Asia/Shanghai** (Saturday 16:00 UTC).
GitHub may delay scheduled runs.
It can also be run from Actions → Data updates → Run workflow on `main`.
The scheduled workflow starts operating after it is merged into the default branch.
This repository's development/release branch is `main`; set **Settings → General →
Default branch** to `main` before enabling the schedule. A workflow present only
on `main` cannot run on a schedule while `master` remains the default branch.
The schedule runs from the repository's default branch, `main`.

Each run resolves a full upstream commit SHA, then uses the same staging and
validation pipeline described below. Only changes to XDB bytes or the license
Expand Down Expand Up @@ -92,8 +89,8 @@ maintenance tools, not runtime APIs; normal imports, installation and lookups
never download data. Network staging additionally requires `curl` on PATH and
uses its inherited proxy and CA configuration.

Build once, then stage a complete, pinned upstream revision into a **new directory
outside the checkout**:
Stage a complete, pinned upstream revision into a **new directory outside the
checkout** (the npm commands build the tools before running them):

```sh
npm run data:stage -- --revision f8b0792b0cd729f5b2593f690aeed225e49c1967 --stage /tmp/ip2region-candidate
Expand Down
27 changes: 6 additions & 21 deletions docs/adapters.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,26 +96,11 @@ const settled = await query.lookupManySettledAsync(ips, { concurrency: 4 });

## 可运行示例与可复用测试

[examples/custom](../examples/custom/) 中的代码通过公开包入口导入,随 npm 包分发,
同时接受类型检查和 Node、Bun、Deno 的共享测试:
[examples/custom](../examples/custom/) 提供同步函数、类、异步服务和自定义 JSON 字节格式,
通过公开包入口导入,随 npm 包分发,并在 Node、Bun、Deno 上验证。
文件目录与运行命令见[示例说明](../examples/custom/README.md)。

| 文件 | 用途 |
| ------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| [readers.mjs](../examples/custom/readers.mjs) | 有状态的 `MapReader`、可注入传输函数的 `ServiceReader`、自定义字节格式 `JSONReader` |
| [contracts.mjs](../examples/custom/contracts.mjs) | 可复制的同步 / 异步契约测试注册函数 |
| [custom-suite.mjs](../examples/custom/custom-suite.mjs) | 函数、类和自定义格式的完整接入与测试案例 |
| [custom.test.mjs](../examples/custom/custom.test.mjs) | 可单独执行的 Node 测试入口 |

在仓库中运行 `npm run test:examples`。包安装后也可直接运行:

```sh
node --test node_modules/ip2region/examples/custom/custom.test.mjs
```

Bun、Deno 使用仓库的 `tests/bun.test.mjs`、`tests/deno.test.mjs` 执行同一组案例。
示例全部使用内存记录、合成 JSON 和可控异步函数,不请求外部服务、不需要商业数据库。

接入自己的来源时,复制 `contracts.mjs`,提供每次创建独立来源的 `freshSource` 工厂,以及
一个已知命中地址、一个已知未命中地址和预期记录。测试注册函数可传入 Node 的 `test`、
Bun 的 `test` 或 `Deno.test`。再为自己的格式或传输补充损坏数据、权限、超时和资源清理测试。
复用 `contracts.mjs` 时传入测试注册函数、每次创建独立来源的工厂函数,以及 `{ hit, miss, expected }` 样本。
测试注册函数可使用 Node 的 `test`、Bun 的 `test` 或 `Deno.test`。
再为自己的格式或传输补充损坏数据、权限、超时和资源清理测试。
这些测试验证接口与解码行为,地理位置准确性需要单独的可追溯数据样本。
14 changes: 4 additions & 10 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,7 @@ and native runtime file loaders.
native file APIs and share the same query engines. User-supplied readers do
not require a registry or a library release.

## Delivery and validation

1. Pure core and extension contract: strict IP parsing, synchronous/asynchronous
functions and instances, errors, batches, ownership and close behavior.
2. Native loading and ip2region XDB dual-stack data, IPIP IPDB and IP2Location BIN.
Validate each format with synthetic fixtures and an independent reader.
3. Document data provenance and actual format/field/runtime capabilities.
4. Test real tarballs, declarations, ESM/CJS, runtime permission failures and
memory-only operation. Record loading/memory/query performance separately
from location accuracy.
## Batch and data boundaries

Batch calls retain input order. `lookupMany` fails on the first error;
`lookupManySettled` records per-item errors and continues. Async batches default
Expand All @@ -64,3 +55,6 @@ checks each reader's family support and owns their cleanup. Results retain the
selected reader's interpretation and source; composite metadata retains the
individual datasets. Maintainer data staging and application are explicit tools,
outside runtime loading and queries.

Build/test commands are in [development.md](development.md), with observed results
and coverage limits in [validation.md](validation.md).
28 changes: 7 additions & 21 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ emitting files, including the JSDoc-typed custom adapter examples.
`npm run lint:fix` applies lint fixes; `npm run format` formats the repository. Generated output, lockfiles and the verbatim upstream license are
excluded from formatting. CI and release validation enforce the same checks.

`npm test` builds ESM/CJS and runs Node core, independent-reference and release
helper tests, including the shared custom adapter examples. `test:package`
`npm test` builds ESM/CJS and runs Node core, independent-reference, release-helper
and data-update tests, including the shared custom adapter examples. `test:package`
additionally packs the library and exercises isolated consumers, declarations, exports, permission failures and bundled data hashes.
To validate all native consumers, install Bun and Deno and run:

Expand Down Expand Up @@ -50,22 +50,8 @@ deterministic corpus, with optional built revisions as a baseline. It validates
every result before measuring and writes JSON; see
[the workload and commands](performance.md#repository-xdb-measurements).

Data maintenance is explicit and separate from runtime use. `npm run data:stage --
--revision <full-commit-sha> --stage <new-directory>` downloads pinned candidates
and writes a review report; `--from <directory>` stages local files without claiming
their revision was verified upstream. `data:validate` rechecks the stage and
installed baseline, and `data:apply` replaces both snapshots and their metadata.
See [data maintenance](../data/README.md) for commands and recovery limits.
Network staging needs `curl`; offline staging and tests do not. Formatting uses
the repository's existing development formatter.

The scheduled Data updates workflow wraps the same updater with content-based
change detection and one reusable PR branch. See [scheduled pull requests](../data/README.md#scheduled-pull-requests)
for timing, GitHub permissions, CI dispatch and manual invocation. Its orchestration
tests run offline with the update-tool tests, including unchanged data, pending
candidates, license-only updates and failures that preserve the checkout.

`npm test` includes offline update-tool tests in temporary repositories: invalid
files, provenance, changed stages/baselines, locks, and replacement-failure rollback.
Native composition and async batch tests are shared across all three runtime
consumers; package checks also validate public option types and the data manifest.
Data maintenance uses `data:stage`, `data:validate` and `data:apply`; the weekly
Data updates workflow proposes changes on one reusable PR branch. Commands,
provenance, permissions, reports and recovery limits are documented in
[data maintenance](../data/README.md). Network staging needs `curl`; the updater
and orchestration tests run offline as part of `npm test`.
3 changes: 2 additions & 1 deletion docs/formats.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ IPv4 range endpoints use little endian; IPv6 endpoints retain all 128 bits in
network order. The reader validates sorted non-overlapping ranges, segment/data
bounds and every vector entry. Inclusive and exclusive vector end pointers from
upstream makers are normalized; queries never read past the final segment.
Missing vectors, sparse gaps and zero-length records return null.
Empty vector buckets with no indexed ranges, sparse gaps and zero-length records
return null. Missing or inconsistent required vector entries raise INVALID_DATABASE.

XDB contains a UTF-8 region string, **not a field schema**. `createXDB(bytes)`
defaults to uninterpreted raw records and advertises no geographic fields.
Expand Down
110 changes: 16 additions & 94 deletions docs/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,28 +176,6 @@ IP2REGION_BUN=bun IP2REGION_DENO=deno npm run benchmark:matrix -- \
--samples=500000 --output=/tmp/ip2region-ip-parsing.json
```

### Baseline before parsing optimization, 2026-10-24

Linux x64; Intel Xeon Platinum 8573C; five visible logical CPUs. These are the
constructor API's results before query-performance changes were applied.
Each cell is the median of five processes. Loading and RSS columns are from the
standard-record cases. The [raw report](benchmarks/2026-10-24.json) retains all
60 runs, including per-run latency percentiles and the measured source hashes.

| Runtime | Family | Raw queries/s | Standard queries/s | Load ms | RSS delta MiB |
| ------------ | ------ | ------------: | -----------------: | ------: | ------------: |
| Node 24.19.0 | IPv4 | 434,499 | 284,949 | 70.48 | 17.15 |
| Node 24.19.0 | IPv6 | 184,334 | 159,182 | 99.01 | 41.96 |
| Bun 1.4.4 | IPv4 | 520,266 | 381,143 | 78.68 | 24.78 |
| Bun 1.4.4 | IPv6 | 210,987 | 179,326 | 111.57 | 48.57 |
| Deno 2.9.6 | IPv4 | 298,565 | 203,181 | 71.09 | 16.62 |
| Deno 2.9.6 | IPv6 | 162,207 | 132,405 | 131.23 | 41.46 |

Raw mode extracts `extensions.region` without column mapping. Standard mode
also builds country, subdivision, city, ISP and raw-field extensions. All 4,096
addresses per family returned records and matched the directly decoded segment
contents in every worker. No other library's performance data is included.

### Reproduce the matrix

```sh
Expand All @@ -223,75 +201,19 @@ and cross-engine memory accounting are uncontrolled. RSS after requested GC is
not peak memory. BIN/IPDB synthetic fixtures validate correctness and execution;
no full-size BIN/IPDB performance result is recorded.

## Historical native-loader measurements, 2026-10-23

These historical observations precede the constructor API. The commands above
use the current API; this table has not been rerun as a before/after comparison.

Linux x64; INTEL(R) XEON(R) PLATINUM 8573C; 5 visible logical CPUs; 18,882,674,688 bytes reported host memory. Each case ran in three fresh, sequential processes; the tables show the median of each measurement.

Data is pinned to the hashes in [data provenance](../data/README.md): IPv4
11,122,642 bytes, IPv6 37,277,813 bytes, combined 48,400,455 bytes.
The complete host, inputs and all 54 runs are saved in
[the JSON report](benchmarks/2026-10-23.json).

### Dual-stack

| Runtime | Load ms | RSS delta MiB | p50 µs | p95 µs | p99 µs | Queries/s |
| ------------ | ------: | ------------: | -----: | -----: | -----: | --------: |
| Node 22.12.0 | 255.01 | 93.17 | 4.24 | 9.29 | 67.16 | 194,819 |
| Node 24.19.0 | 272.99 | 90.21 | 2.98 | 6.08 | 35.66 | 241,521 |
| Bun 1.2.0 | 466.12 | 110.67 | 5.53 | 11.74 | 44.97 | 181,178 |
| Bun 1.4.4 | 201.32 | 59.11 | 3.22 | 7.22 | 28.04 | 290,304 |
| Deno 2.0.0 | 196.74 | 97.85 | 3.71 | 6.58 | 38.26 | 199,511 |
| Deno 2.9.6 | 216.10 | 91.18 | 4.32 | 9.42 | 75.35 | 179,068 |

### Single-family

| Runtime | Family | Load ms | RSS delta MiB | p50 µs | Queries/s |
| ------------ | ------ | ------: | ------------: | -----: | --------: |
| Node 22.12.0 | IPv4 | 71.08 | 27.97 | 2.68 | 266,930 |
| Node 22.12.0 | IPv6 | 224.90 | 78.29 | 5.06 | 176,959 |
| Node 24.19.0 | IPv4 | 74.43 | 24.41 | 2.72 | 322,089 |
| Node 24.19.0 | IPv6 | 136.03 | 75.21 | 4.36 | 166,821 |
| Bun 1.2.0 | IPv4 | 161.24 | 35.52 | 4.00 | 309,353 |
| Bun 1.2.0 | IPv6 | 249.75 | 84.64 | 7.80 | 151,237 |
| Bun 1.4.4 | IPv4 | 79.59 | 24.93 | 2.24 | 422,472 |
| Bun 1.4.4 | IPv6 | 122.14 | 49.28 | 3.79 | 194,797 |
| Deno 2.0.0 | IPv4 | 72.36 | 34.19 | 3.24 | 228,187 |
| Deno 2.0.0 | IPv6 | 109.51 | 83.32 | 5.25 | 138,894 |
| Deno 2.9.6 | IPv4 | 91.80 | 27.53 | 3.42 | 235,854 |
| Deno 2.9.6 | IPv6 | 138.14 | 77.37 | 5.07 | 126,194 |

Single-family loading reads only that file. The table quantifies its lower
loading cost on this host; it does not establish a universal memory budget or
rank runtimes for production workloads.

### Historical workload and limits

- IPv4 inputs: `120.24.78.68`, `8.8.8.8`, `1.1.1.1`, `192.0.2.1`, `0.0.0.0`,
`255.255.255.255`. IPv6 inputs: `2001:4860:4860::8888`,
`2606:4700:4700::1111`, `2001:db8::1`, `2001:db8::2`. Each case cycles through
only its selected families; this small mix includes reserved ranges.
- 1,000 warm-up lookups, 10,000 individually timed lookups, then a separately
timed loop of 100,000 lookups for throughput. Latency includes parsing, result
allocation and clock overhead; throughput avoids per-lookup timing overhead.
- Loading includes file I/O, copying and index validation, excluding module import
and requested GC. Memory deltas are sampled before loading and after requested
GC. The OS page cache and host scheduling are uncontrolled.
- All measured bundled lookups returned records, including records without
geographic fields. The runner checked runtime/loader identity, selected
families, hashes, sample/hit counts and finite positive timings. This measures
coverage and execution, not geographic accuracy.

Memory counters have different accounting across engines, and external-buffer
reclamation can lag. RSS includes allocator and runtime behavior. The current
workload differs from the earlier six-address Node-only baseline, so its latency
numbers are not a direct regression comparison. Compare changes using identical
files, inputs, mapping mode, runtime, host and cache conditions.

Benchmarks run manually. For performance changes, measure the base and proposed
revisions on the same host with identical runtimes, inputs and data. The recorded
measurements describe this environment and do not establish a regression check.
No real BIN/IPDB performance claim is made without a fixed, licensed input;
synthetic fixtures validate those readers and benchmark execution only.
## Historical baselines

Raw reports retain the earlier measurements without repeating their tables here:

- [2026-10-24 XDB baseline](benchmarks/2026-10-24.json): 60 fresh-process runs
before the parsing optimization, using 100,000 throughput queries per case.
- [2026-10-23 native loaders](benchmarks/2026-10-23.json): 54 runs before the
constructor API, covering dual-stack and single-family loading on minimum and
current runtimes. The workload cycles through ten fixed addresses, with 1,000
warm-up queries, 10,000 latency samples and 100,000 throughput queries.

These are separate workloads and revisions, not the denominators for the paired
comparisons above. The reports retain runtime/host metadata, file hashes, loading,
memory and per-run timings. Cross-engine memory counters differ; RSS after GC is
not peak memory, and OS page cache and scheduling are uncontrolled. No full-size
BIN/IPDB performance claim is made without a fixed, licensed input.
Loading
Loading