ts-ip2region2 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -82
- package/addon.cpp +52 -66
- package/dist/index.d.ts +33 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -14
- package/dist/index.js.map +1 -1
- package/package.json +7 -14
- package/LICENSE +0 -201
- package/README_CN.md +0 -135
- package/ip2region/Makefile +0 -27
- package/ip2region/ReadMe.md +0 -341
- package/ip2region/main.c +0 -428
- package/ip2region/test_util +0 -0
- package/ip2region/test_util.c +0 -213
package/README.md
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
[English](README.md) | [中文](README_CN.md)
|
|
1
|
+
# ts-ip2region2
|
|
4
2
|
|
|
5
3
|
High-performance Node.js native addon for ip2region xdb query with IPv4/IPv6 support. Written in TypeScript with full type definitions.
|
|
6
4
|
|
|
7
|
-
> **Note**: This project is adapted from the official [ip2region C binding](https://github.com/lionsoul2014/ip2region/tree/master/binding/c) and enhanced with TypeScript support.
|
|
8
|
-
|
|
9
5
|
## Features
|
|
10
6
|
|
|
11
7
|
- 🚀 **High Performance** - Native C++ implementation with microsecond-level response
|
|
@@ -14,34 +10,34 @@ High-performance Node.js native addon for ip2region xdb query with IPv4/IPv6 sup
|
|
|
14
10
|
- 🔒 **Memory Safe** - Automatic resource management prevents memory leaks
|
|
15
11
|
- 📝 **TypeScript** - Complete TypeScript type definitions included
|
|
16
12
|
- 🔧 **Cross Platform** - Works on Windows, Linux, and macOS
|
|
13
|
+
- 📦 **Bundled Data** - Includes compressed database files
|
|
17
14
|
|
|
18
15
|
## Installation
|
|
19
16
|
|
|
20
17
|
```bash
|
|
21
18
|
npm install ts-ip2region2
|
|
22
|
-
# or
|
|
23
|
-
pnpm add ts-ip2region2
|
|
24
|
-
# or
|
|
25
|
-
yarn add ts-ip2region2
|
|
26
19
|
```
|
|
27
20
|
|
|
28
|
-
|
|
21
|
+
The database files are automatically included and extracted during installation.
|
|
29
22
|
|
|
30
23
|
## Quick Start
|
|
31
24
|
|
|
32
25
|
```typescript
|
|
33
26
|
import { Ip2Region } from 'ts-ip2region2';
|
|
34
27
|
|
|
35
|
-
// Create searcher instance
|
|
36
|
-
const searcher = new Ip2Region(
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
// Create searcher instance (uses bundled data)
|
|
29
|
+
const searcher = new Ip2Region();
|
|
30
|
+
|
|
31
|
+
// Or specify custom database path
|
|
32
|
+
const searcher2 = new Ip2Region('./custom.xdb', {
|
|
33
|
+
cachePolicy: 'vectorIndex',
|
|
34
|
+
ipVersion: 'v4'
|
|
39
35
|
});
|
|
40
36
|
|
|
41
37
|
// Query IP address
|
|
42
38
|
const result = searcher.search('120.229.45.2');
|
|
43
39
|
console.log(result);
|
|
44
|
-
// Output: { region: '
|
|
40
|
+
// Output: { region: '中国|广东省|深圳市|移动', ioCount: 3, took: 1000 }
|
|
45
41
|
|
|
46
42
|
// Clean up
|
|
47
43
|
searcher.close();
|
|
@@ -52,9 +48,12 @@ searcher.close();
|
|
|
52
48
|
### Constructor
|
|
53
49
|
|
|
54
50
|
```typescript
|
|
55
|
-
new Ip2Region(dbPath
|
|
51
|
+
new Ip2Region(dbPath?: string, options?: Ip2RegionOptions)
|
|
56
52
|
```
|
|
57
53
|
|
|
54
|
+
- `dbPath` (optional): Path to custom xdb file. If not provided, uses bundled data.
|
|
55
|
+
- `options` (optional): Configuration options
|
|
56
|
+
|
|
58
57
|
### Methods
|
|
59
58
|
|
|
60
59
|
- `search(ip: string): SearchResult` - Query IP address location
|
|
@@ -65,71 +64,6 @@ new Ip2Region(dbPath: string, options?: Ip2RegionOptions)
|
|
|
65
64
|
- `Ip2Region.verify(dbPath: string): boolean` - Verify xdb file
|
|
66
65
|
- `Ip2Region.verifyDetailed(dbPath: string): VerifyResult` - Verify with detailed info
|
|
67
66
|
|
|
68
|
-
### Types
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
interface SearchResult {
|
|
72
|
-
region: string; // Geographic location
|
|
73
|
-
ioCount: number; // IO operation count
|
|
74
|
-
took: number; // Query time in microseconds
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
interface Ip2RegionOptions {
|
|
78
|
-
cachePolicy?: 'file' | 'vectorIndex' | 'content';
|
|
79
|
-
ipVersion?: 'v4' | 'v6';
|
|
80
|
-
}
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
## Cache Strategies
|
|
84
|
-
|
|
85
|
-
| Strategy | Memory | Performance | Use Case |
|
|
86
|
-
| ------------- | ------- | ----------- | ------------------------- |
|
|
87
|
-
| `file` | Minimal | Good | Memory-constrained |
|
|
88
|
-
| `vectorIndex` | Medium | Better | General use (recommended) |
|
|
89
|
-
| `content` | High | Best | High-concurrency |
|
|
90
|
-
|
|
91
|
-
## Examples
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
# Build and run example
|
|
95
|
-
npm run example
|
|
96
|
-
|
|
97
|
-
# Build and run tests
|
|
98
|
-
npm test
|
|
99
|
-
|
|
100
|
-
# Build native addon
|
|
101
|
-
npm run build
|
|
102
|
-
|
|
103
|
-
# Compile TypeScript
|
|
104
|
-
npm run compile
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## Project Structure
|
|
108
|
-
|
|
109
|
-
```
|
|
110
|
-
ts-ip2region2/
|
|
111
|
-
├── src/ # TypeScript source code
|
|
112
|
-
│ └── index.ts # Main TypeScript API
|
|
113
|
-
├── dist/ # Compiled JavaScript output
|
|
114
|
-
│ ├── index.js
|
|
115
|
-
│ ├── index.d.ts
|
|
116
|
-
│ └── ...
|
|
117
|
-
├── data/ # Sample xdb database files
|
|
118
|
-
│ ├── ip2region_v4.xdb
|
|
119
|
-
│ └── ip2region_v6.xdb
|
|
120
|
-
├── ip2region/ # Original ip2region C source code
|
|
121
|
-
│ ├── xdb_api.h
|
|
122
|
-
│ ├── xdb_util.c
|
|
123
|
-
│ └── xdb_searcher.c
|
|
124
|
-
├── addon.cpp # Node.js addon implementation
|
|
125
|
-
├── binding.gyp # Build configuration
|
|
126
|
-
├── example.js # JavaScript example
|
|
127
|
-
├── test.js # JavaScript tests
|
|
128
|
-
├── tsconfig.json # TypeScript configuration
|
|
129
|
-
├── package.json
|
|
130
|
-
└── README.md
|
|
131
|
-
```
|
|
132
|
-
|
|
133
67
|
## License
|
|
134
68
|
|
|
135
|
-
Apache-2.0
|
|
69
|
+
Apache-2.0
|
package/addon.cpp
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
#endif
|
|
4
4
|
|
|
5
5
|
#include <napi.h>
|
|
6
|
+
#include <string>
|
|
7
|
+
#include <cstdio>
|
|
6
8
|
|
|
7
9
|
extern "C" {
|
|
8
10
|
#include "ip2region/xdb_api.h"
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
#include <memory>
|
|
12
|
-
|
|
13
13
|
class Ip2RegionSearcher : public Napi::ObjectWrap<Ip2RegionSearcher> {
|
|
14
14
|
public:
|
|
15
15
|
static Napi::Object Init(Napi::Env env, Napi::Object exports);
|
|
@@ -51,13 +51,8 @@ Ip2RegionSearcher::Ip2RegionSearcher(const Napi::CallbackInfo& info)
|
|
|
51
51
|
Napi::Env env = info.Env();
|
|
52
52
|
Napi::HandleScope scope(env);
|
|
53
53
|
|
|
54
|
-
if (info.Length() < 1) {
|
|
55
|
-
Napi::TypeError::New(env, "Expected
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!info[0].IsString()) {
|
|
60
|
-
Napi::TypeError::New(env, "Expected string for db_path").ThrowAsJavaScriptException();
|
|
54
|
+
if (info.Length() < 1 || !info[0].IsString()) {
|
|
55
|
+
Napi::TypeError::New(env, "Expected database path as string").ThrowAsJavaScriptException();
|
|
61
56
|
return;
|
|
62
57
|
}
|
|
63
58
|
|
|
@@ -77,56 +72,53 @@ Ip2RegionSearcher::Ip2RegionSearcher(const Napi::CallbackInfo& info)
|
|
|
77
72
|
}
|
|
78
73
|
}
|
|
79
74
|
|
|
80
|
-
//
|
|
75
|
+
// Initialize winsock (Windows only)
|
|
81
76
|
int err = xdb_init_winsock();
|
|
82
77
|
if (err != 0) {
|
|
83
|
-
Napi::Error::New(env, "Failed to
|
|
78
|
+
Napi::Error::New(env, "Failed to initialize network layer").ThrowAsJavaScriptException();
|
|
84
79
|
return;
|
|
85
80
|
}
|
|
86
81
|
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// 确定IP版本
|
|
96
|
-
xdb_version_t* version;
|
|
97
|
-
if (ip_version == "v4" || ip_version == "ipv4") {
|
|
98
|
-
version = XDB_IPv4;
|
|
99
|
-
} else if (ip_version == "v6" || ip_version == "ipv6") {
|
|
100
|
-
version = XDB_IPv6;
|
|
101
|
-
} else {
|
|
102
|
-
Napi::Error::New(env, "Invalid IP version").ThrowAsJavaScriptException();
|
|
82
|
+
// Determine IP version
|
|
83
|
+
xdb_version_t* version = (ip_version == "v4" || ip_version == "ipv4") ? XDB_IPv4 :
|
|
84
|
+
(ip_version == "v6" || ip_version == "ipv6") ? XDB_IPv6 : nullptr;
|
|
85
|
+
|
|
86
|
+
if (!version) {
|
|
87
|
+
xdb_clean_winsock();
|
|
88
|
+
Napi::Error::New(env, "Invalid IP version. Use 'v4' or 'v6'").ThrowAsJavaScriptException();
|
|
103
89
|
return;
|
|
104
90
|
}
|
|
105
91
|
|
|
106
|
-
//
|
|
92
|
+
// Initialize searcher based on cache strategy
|
|
107
93
|
if (cache_policy == "file") {
|
|
108
94
|
err = xdb_new_with_file_only(version, &searcher, db_path.c_str());
|
|
109
95
|
} else if (cache_policy == "vectorIndex") {
|
|
110
96
|
v_index = xdb_load_vector_index_from_file(db_path.c_str());
|
|
111
|
-
if (v_index
|
|
112
|
-
|
|
97
|
+
if (!v_index) {
|
|
98
|
+
xdb_clean_winsock();
|
|
99
|
+
Napi::Error::New(env, "Failed to load vector index from database file").ThrowAsJavaScriptException();
|
|
113
100
|
return;
|
|
114
101
|
}
|
|
115
102
|
err = xdb_new_with_vector_index(version, &searcher, db_path.c_str(), v_index);
|
|
116
103
|
} else if (cache_policy == "content") {
|
|
117
104
|
c_buffer = xdb_load_content_from_file(db_path.c_str());
|
|
118
|
-
if (c_buffer
|
|
119
|
-
|
|
105
|
+
if (!c_buffer) {
|
|
106
|
+
xdb_clean_winsock();
|
|
107
|
+
Napi::Error::New(env, "Failed to load database content into memory").ThrowAsJavaScriptException();
|
|
120
108
|
return;
|
|
121
109
|
}
|
|
122
110
|
err = xdb_new_with_buffer(version, &searcher, c_buffer);
|
|
123
111
|
} else {
|
|
124
|
-
|
|
112
|
+
xdb_clean_winsock();
|
|
113
|
+
Napi::Error::New(env, "Invalid cache policy. Use 'file', 'vectorIndex', or 'content'").ThrowAsJavaScriptException();
|
|
125
114
|
return;
|
|
126
115
|
}
|
|
127
116
|
|
|
128
117
|
if (err != 0) {
|
|
129
|
-
|
|
118
|
+
if (v_index) xdb_free_vector_index(v_index);
|
|
119
|
+
if (c_buffer) xdb_free_content(c_buffer);
|
|
120
|
+
xdb_clean_winsock();
|
|
121
|
+
Napi::Error::New(env, "Failed to initialize IP geolocation searcher").ThrowAsJavaScriptException();
|
|
130
122
|
return;
|
|
131
123
|
}
|
|
132
124
|
|
|
@@ -136,16 +128,16 @@ Ip2RegionSearcher::Ip2RegionSearcher(const Napi::CallbackInfo& info)
|
|
|
136
128
|
Ip2RegionSearcher::~Ip2RegionSearcher() {
|
|
137
129
|
if (is_initialized) {
|
|
138
130
|
xdb_close(&searcher);
|
|
139
|
-
|
|
140
|
-
if (v_index != nullptr) {
|
|
131
|
+
if (v_index) {
|
|
141
132
|
xdb_free_vector_index(v_index);
|
|
133
|
+
v_index = nullptr;
|
|
142
134
|
}
|
|
143
|
-
|
|
144
|
-
if (c_buffer != nullptr) {
|
|
135
|
+
if (c_buffer) {
|
|
145
136
|
xdb_free_content(c_buffer);
|
|
137
|
+
c_buffer = nullptr;
|
|
146
138
|
}
|
|
147
|
-
|
|
148
139
|
xdb_clean_winsock();
|
|
140
|
+
is_initialized = false;
|
|
149
141
|
}
|
|
150
142
|
}
|
|
151
143
|
|
|
@@ -159,7 +151,7 @@ Napi::Value Ip2RegionSearcher::Search(const Napi::CallbackInfo& info) {
|
|
|
159
151
|
}
|
|
160
152
|
|
|
161
153
|
if (info.Length() < 1 || !info[0].IsString()) {
|
|
162
|
-
Napi::TypeError::New(env, "Expected
|
|
154
|
+
Napi::TypeError::New(env, "Expected IP address as string").ThrowAsJavaScriptException();
|
|
163
155
|
return env.Null();
|
|
164
156
|
}
|
|
165
157
|
|
|
@@ -168,24 +160,24 @@ Napi::Value Ip2RegionSearcher::Search(const Napi::CallbackInfo& info) {
|
|
|
168
160
|
xdb_region_buffer_t region;
|
|
169
161
|
int err = xdb_region_buffer_init(®ion, nullptr, 0);
|
|
170
162
|
if (err != 0) {
|
|
171
|
-
Napi::Error::New(env, "Failed to
|
|
163
|
+
Napi::Error::New(env, "Failed to initialize region buffer").ThrowAsJavaScriptException();
|
|
172
164
|
return env.Null();
|
|
173
165
|
}
|
|
174
166
|
|
|
175
|
-
long start_time = xdb_now();
|
|
167
|
+
const long start_time = xdb_now();
|
|
176
168
|
err = xdb_search_by_string(&searcher, ip_string.c_str(), ®ion);
|
|
177
|
-
long
|
|
169
|
+
const long elapsed_time = xdb_now() - start_time;
|
|
178
170
|
|
|
179
171
|
if (err != 0) {
|
|
180
172
|
xdb_region_buffer_free(®ion);
|
|
181
|
-
Napi::Error::New(env, "
|
|
173
|
+
Napi::Error::New(env, "IP address lookup failed").ThrowAsJavaScriptException();
|
|
182
174
|
return env.Null();
|
|
183
175
|
}
|
|
184
176
|
|
|
185
177
|
Napi::Object result = Napi::Object::New(env);
|
|
186
178
|
result.Set("region", Napi::String::New(env, region.value ? region.value : ""));
|
|
187
179
|
result.Set("ioCount", Napi::Number::New(env, xdb_get_io_count(&searcher)));
|
|
188
|
-
result.Set("took", Napi::Number::New(env,
|
|
180
|
+
result.Set("took", Napi::Number::New(env, elapsed_time));
|
|
189
181
|
|
|
190
182
|
xdb_region_buffer_free(®ion);
|
|
191
183
|
return result;
|
|
@@ -196,17 +188,14 @@ Napi::Value Ip2RegionSearcher::Close(const Napi::CallbackInfo& info) {
|
|
|
196
188
|
|
|
197
189
|
if (is_initialized) {
|
|
198
190
|
xdb_close(&searcher);
|
|
199
|
-
|
|
200
|
-
if (v_index != nullptr) {
|
|
191
|
+
if (v_index) {
|
|
201
192
|
xdb_free_vector_index(v_index);
|
|
202
193
|
v_index = nullptr;
|
|
203
194
|
}
|
|
204
|
-
|
|
205
|
-
if (c_buffer != nullptr) {
|
|
195
|
+
if (c_buffer) {
|
|
206
196
|
xdb_free_content(c_buffer);
|
|
207
197
|
c_buffer = nullptr;
|
|
208
198
|
}
|
|
209
|
-
|
|
210
199
|
xdb_clean_winsock();
|
|
211
200
|
is_initialized = false;
|
|
212
201
|
}
|
|
@@ -214,37 +203,34 @@ Napi::Value Ip2RegionSearcher::Close(const Napi::CallbackInfo& info) {
|
|
|
214
203
|
return env.Undefined();
|
|
215
204
|
}
|
|
216
205
|
|
|
217
|
-
// 静态函数用于验证xdb文件
|
|
218
206
|
Napi::Value VerifyXdb(const Napi::CallbackInfo& info) {
|
|
219
207
|
Napi::Env env = info.Env();
|
|
220
208
|
|
|
221
209
|
if (info.Length() < 1 || !info[0].IsString()) {
|
|
222
|
-
Napi::TypeError::New(env, "Expected
|
|
210
|
+
Napi::TypeError::New(env, "Expected database path as string").ThrowAsJavaScriptException();
|
|
223
211
|
return env.Null();
|
|
224
212
|
}
|
|
225
213
|
|
|
226
|
-
std::string db_path = info[0].As<Napi::String>().Utf8Value();
|
|
214
|
+
const std::string db_path = info[0].As<Napi::String>().Utf8Value();
|
|
215
|
+
|
|
216
|
+
int error_code = 0;
|
|
217
|
+
FILE* file_handle = std::fopen(db_path.c_str(), "rb");
|
|
227
218
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if (handle == NULL) {
|
|
231
|
-
err = -1;
|
|
219
|
+
if (!file_handle) {
|
|
220
|
+
error_code = -1; // File not found or cannot be opened
|
|
232
221
|
} else {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
err = 1;
|
|
222
|
+
xdb_header_t* header = xdb_load_header(file_handle);
|
|
223
|
+
if (!header) {
|
|
224
|
+
error_code = 1; // Invalid file format
|
|
237
225
|
} else {
|
|
238
|
-
// =只检查header是否正常加载
|
|
239
|
-
err = 0; // header加载成功即认为文件有效
|
|
240
226
|
xdb_free_header(header);
|
|
241
227
|
}
|
|
242
|
-
fclose(
|
|
228
|
+
std::fclose(file_handle);
|
|
243
229
|
}
|
|
244
230
|
|
|
245
231
|
Napi::Object result = Napi::Object::New(env);
|
|
246
|
-
result.Set("valid", Napi::Boolean::New(env,
|
|
247
|
-
result.Set("errorCode", Napi::Number::New(env,
|
|
232
|
+
result.Set("valid", Napi::Boolean::New(env, error_code == 0));
|
|
233
|
+
result.Set("errorCode", Napi::Number::New(env, error_code));
|
|
248
234
|
|
|
249
235
|
return result;
|
|
250
236
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,53 +1,65 @@
|
|
|
1
1
|
export interface SearchResult {
|
|
2
|
-
/**
|
|
2
|
+
/** Geographic location information string */
|
|
3
3
|
region: string;
|
|
4
|
-
/** IO
|
|
4
|
+
/** Number of IO operations */
|
|
5
5
|
ioCount: number;
|
|
6
|
-
/**
|
|
6
|
+
/** Query time in microseconds */
|
|
7
7
|
took: number;
|
|
8
8
|
}
|
|
9
9
|
export interface Ip2RegionOptions {
|
|
10
|
-
/**
|
|
10
|
+
/** Cache strategy: 'file' | 'vectorIndex' | 'content' */
|
|
11
11
|
cachePolicy?: 'file' | 'vectorIndex' | 'content';
|
|
12
|
-
/** IP
|
|
12
|
+
/** IP version: 'v4' | 'v6' */
|
|
13
13
|
ipVersion?: 'v4' | 'v6';
|
|
14
14
|
}
|
|
15
15
|
export interface VerifyResult {
|
|
16
|
-
/**
|
|
16
|
+
/** Verification result */
|
|
17
17
|
valid: boolean;
|
|
18
|
-
/**
|
|
18
|
+
/** Error code */
|
|
19
19
|
errorCode: number;
|
|
20
20
|
}
|
|
21
21
|
export default class Ip2Region {
|
|
22
22
|
private searcher;
|
|
23
|
+
private readonly dbPath;
|
|
24
|
+
private readonly options;
|
|
23
25
|
/**
|
|
24
|
-
*
|
|
25
|
-
* @param dbPath xdb
|
|
26
|
-
* @param options
|
|
26
|
+
* Create ip2region searcher instance
|
|
27
|
+
* @param dbPath Path to xdb database file (optional, will use bundled data if not provided)
|
|
28
|
+
* @param options Configuration options
|
|
27
29
|
*/
|
|
28
|
-
constructor(dbPath
|
|
30
|
+
constructor(dbPath?: string, options?: Ip2RegionOptions);
|
|
29
31
|
/**
|
|
30
|
-
*
|
|
31
|
-
* @param ip IP
|
|
32
|
-
* @returns
|
|
32
|
+
* Query geographic location information for IP address
|
|
33
|
+
* @param ip IP address string (supports IPv4 and IPv6)
|
|
34
|
+
* @returns Query result
|
|
33
35
|
*/
|
|
34
36
|
search(ip: string): SearchResult;
|
|
35
37
|
/**
|
|
36
|
-
*
|
|
38
|
+
* Close searcher and release resources
|
|
37
39
|
*/
|
|
38
40
|
close(): void;
|
|
39
41
|
/**
|
|
40
|
-
*
|
|
41
|
-
* @param dbPath xdb
|
|
42
|
-
* @returns
|
|
42
|
+
* Verify the validity of xdb file
|
|
43
|
+
* @param dbPath Path to xdb file
|
|
44
|
+
* @returns Verification result
|
|
43
45
|
*/
|
|
44
46
|
static verify(dbPath: string): boolean;
|
|
45
47
|
/**
|
|
46
|
-
*
|
|
47
|
-
* @param dbPath xdb
|
|
48
|
-
* @returns
|
|
48
|
+
* Verify xdb file and return detailed information
|
|
49
|
+
* @param dbPath Path to xdb file
|
|
50
|
+
* @returns Object containing valid and errorCode
|
|
49
51
|
*/
|
|
50
52
|
static verifyDetailed(dbPath: string): VerifyResult;
|
|
53
|
+
/**
|
|
54
|
+
* Get current configuration
|
|
55
|
+
* @returns Current options
|
|
56
|
+
*/
|
|
57
|
+
getOptions(): Required<Ip2RegionOptions>;
|
|
58
|
+
/**
|
|
59
|
+
* Get database file path
|
|
60
|
+
* @returns Database path
|
|
61
|
+
*/
|
|
62
|
+
getDbPath(): string;
|
|
51
63
|
}
|
|
52
64
|
export { Ip2Region };
|
|
53
65
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IACzB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC7B,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IACjD,8BAA8B;IAC9B,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IACzB,0BAA0B;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,CAAC,OAAO,OAAO,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAM;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IAErD;;;;OAIG;gBACS,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAsB3D;;;;OAIG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY;IAOhC;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAQtC;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY;IAOnD;;;OAGG;IACH,UAAU,IAAI,QAAQ,CAAC,gBAAgB,CAAC;IAIxC;;;OAGG;IACH,SAAS,IAAI,MAAM;CAGtB;AAED,OAAO,EAAE,SAAS,EAAE,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -2,46 +2,87 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Ip2Region = void 0;
|
|
4
4
|
const addon = require('../build/Release/ip2region.node');
|
|
5
|
+
const ts_ip2region2_data_1 = require("ts-ip2region2-data");
|
|
5
6
|
class Ip2Region {
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
-
* @param dbPath xdb
|
|
9
|
-
* @param options
|
|
8
|
+
* Create ip2region searcher instance
|
|
9
|
+
* @param dbPath Path to xdb database file (optional, will use bundled data if not provided)
|
|
10
|
+
* @param options Configuration options
|
|
10
11
|
*/
|
|
11
12
|
constructor(dbPath, options = {}) {
|
|
12
|
-
this.
|
|
13
|
+
this.options = {
|
|
14
|
+
cachePolicy: options.cachePolicy || 'vectorIndex',
|
|
15
|
+
ipVersion: options.ipVersion || 'v4',
|
|
16
|
+
};
|
|
17
|
+
if (!dbPath) {
|
|
18
|
+
if (!(0, ts_ip2region2_data_1.isDataExtracted)()) {
|
|
19
|
+
throw new Error('Bundled data not found. Please ensure ts-ip2region2-data is properly installed.');
|
|
20
|
+
}
|
|
21
|
+
const dataPaths = (0, ts_ip2region2_data_1.getDataPaths)();
|
|
22
|
+
this.dbPath = this.options.ipVersion === 'v6' ? dataPaths.v6 : dataPaths.v4;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
if (typeof dbPath !== 'string') {
|
|
26
|
+
throw new TypeError('Database path must be a string');
|
|
27
|
+
}
|
|
28
|
+
this.dbPath = dbPath;
|
|
29
|
+
}
|
|
30
|
+
this.searcher = new addon.Ip2RegionSearcher(this.dbPath, this.options);
|
|
13
31
|
}
|
|
14
32
|
/**
|
|
15
|
-
*
|
|
16
|
-
* @param ip IP
|
|
17
|
-
* @returns
|
|
33
|
+
* Query geographic location information for IP address
|
|
34
|
+
* @param ip IP address string (supports IPv4 and IPv6)
|
|
35
|
+
* @returns Query result
|
|
18
36
|
*/
|
|
19
37
|
search(ip) {
|
|
38
|
+
if (!ip || typeof ip !== 'string') {
|
|
39
|
+
throw new TypeError('IP address must be a non-empty string');
|
|
40
|
+
}
|
|
20
41
|
return this.searcher.search(ip);
|
|
21
42
|
}
|
|
22
43
|
/**
|
|
23
|
-
*
|
|
44
|
+
* Close searcher and release resources
|
|
24
45
|
*/
|
|
25
46
|
close() {
|
|
26
47
|
this.searcher.close();
|
|
27
48
|
}
|
|
28
49
|
/**
|
|
29
|
-
*
|
|
30
|
-
* @param dbPath xdb
|
|
31
|
-
* @returns
|
|
50
|
+
* Verify the validity of xdb file
|
|
51
|
+
* @param dbPath Path to xdb file
|
|
52
|
+
* @returns Verification result
|
|
32
53
|
*/
|
|
33
54
|
static verify(dbPath) {
|
|
55
|
+
if (!dbPath || typeof dbPath !== 'string') {
|
|
56
|
+
throw new TypeError('Database path must be a non-empty string');
|
|
57
|
+
}
|
|
34
58
|
const result = addon.verifyXdb(dbPath);
|
|
35
59
|
return result.valid;
|
|
36
60
|
}
|
|
37
61
|
/**
|
|
38
|
-
*
|
|
39
|
-
* @param dbPath xdb
|
|
40
|
-
* @returns
|
|
62
|
+
* Verify xdb file and return detailed information
|
|
63
|
+
* @param dbPath Path to xdb file
|
|
64
|
+
* @returns Object containing valid and errorCode
|
|
41
65
|
*/
|
|
42
66
|
static verifyDetailed(dbPath) {
|
|
67
|
+
if (!dbPath || typeof dbPath !== 'string') {
|
|
68
|
+
throw new TypeError('Database path must be a non-empty string');
|
|
69
|
+
}
|
|
43
70
|
return addon.verifyXdb(dbPath);
|
|
44
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* Get current configuration
|
|
74
|
+
* @returns Current options
|
|
75
|
+
*/
|
|
76
|
+
getOptions() {
|
|
77
|
+
return { ...this.options };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get database file path
|
|
81
|
+
* @returns Database path
|
|
82
|
+
*/
|
|
83
|
+
getDbPath() {
|
|
84
|
+
return this.dbPath;
|
|
85
|
+
}
|
|
45
86
|
}
|
|
46
87
|
exports.default = Ip2Region;
|
|
47
88
|
exports.Ip2Region = Ip2Region;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,MAAM,KAAK,GAAG,OAAO,CAAC,iCAAiC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,MAAM,KAAK,GAAG,OAAO,CAAC,iCAAiC,CAAC,CAAC;AACzD,2DAAmE;AAyBnE,MAAqB,SAAS;IAK1B;;;;OAIG;IACH,YAAY,MAAe,EAAE,UAA4B,EAAE;QACvD,IAAI,CAAC,OAAO,GAAG;YACX,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,aAAa;YACjD,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,IAAI;SACvC,CAAC;QAEF,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,IAAI,CAAC,IAAA,oCAAe,GAAE,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CAAC,iFAAiF,CAAC,CAAC;YACvG,CAAC;YACD,MAAM,SAAS,GAAG,IAAA,iCAAY,GAAE,CAAC;YACjC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;QAChF,CAAC;aAAM,CAAC;YACJ,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAC7B,MAAM,IAAI,SAAS,CAAC,gCAAgC,CAAC,CAAC;YAC1D,CAAC;YACD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACzB,CAAC;QAED,IAAI,CAAC,QAAQ,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3E,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,EAAU;QACb,IAAI,CAAC,EAAE,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;YAChC,MAAM,IAAI,SAAS,CAAC,uCAAuC,CAAC,CAAC;QACjE,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;IAED;;OAEG;IACH,KAAK;QACD,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,MAAc;QACxB,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACvC,OAAO,MAAM,CAAC,KAAK,CAAC;IACxB,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,MAAc;QAChC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED;;;OAGG;IACH,UAAU;QACN,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,SAAS;QACL,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;CACJ;AA3FD,4BA2FC;AAEQ,8BAAS"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-ip2region2",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "TypeScript-first ip2region implementation with native Node.js addon. Ultra-fast IPv4/IPv6 geolocation queries.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
"clean": "node-gyp clean",
|
|
13
13
|
"test": "npm run build:all && node test.js",
|
|
14
14
|
"example": "npm run build:all && node example.js",
|
|
15
|
-
"prepublishOnly": "npm run build:all"
|
|
16
|
-
"semantic-release": "semantic-release"
|
|
15
|
+
"prepublishOnly": "npm run build:all"
|
|
17
16
|
},
|
|
18
17
|
"keywords": [
|
|
19
18
|
"typescript",
|
|
@@ -30,25 +29,20 @@
|
|
|
30
29
|
"license": "Apache-2.0",
|
|
31
30
|
"repository": {
|
|
32
31
|
"type": "git",
|
|
33
|
-
"url": "https://github.com/Steven-Qiang/ts-ip2region2.git"
|
|
32
|
+
"url": "https://github.com/Steven-Qiang/ts-ip2region2.git",
|
|
33
|
+
"directory": "packages/ts-ip2region2"
|
|
34
34
|
},
|
|
35
35
|
"bugs": {
|
|
36
36
|
"url": "https://github.com/Steven-Qiang/ts-ip2region2/issues"
|
|
37
37
|
},
|
|
38
38
|
"homepage": "https://github.com/Steven-Qiang/ts-ip2region2#readme",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"node-addon-api": "^8.5.0"
|
|
40
|
+
"node-addon-api": "^8.5.0",
|
|
41
|
+
"ts-ip2region2-data": "*"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"@semantic-release/changelog": "^6.0.3",
|
|
44
|
-
"@semantic-release/commit-analyzer": "^13.0.1",
|
|
45
|
-
"@semantic-release/git": "^10.0.1",
|
|
46
|
-
"@semantic-release/github": "^11.0.6",
|
|
47
|
-
"@semantic-release/npm": "^12.0.2",
|
|
48
|
-
"@semantic-release/release-notes-generator": "^14.1.0",
|
|
49
44
|
"@types/node": "^24.7.0",
|
|
50
45
|
"node-gyp": "^11.4.2",
|
|
51
|
-
"semantic-release": "^24.2.9",
|
|
52
46
|
"typescript": "^5.9.3"
|
|
53
47
|
},
|
|
54
48
|
"engines": {
|
|
@@ -68,8 +62,7 @@
|
|
|
68
62
|
"addon.cpp",
|
|
69
63
|
"ip2region/**/*",
|
|
70
64
|
"binding.gyp",
|
|
71
|
-
"README.md"
|
|
72
|
-
"README_CN.md"
|
|
65
|
+
"README.md"
|
|
73
66
|
],
|
|
74
67
|
"gypfile": true,
|
|
75
68
|
"publishConfig": {
|