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 CHANGED
@@ -1,11 +1,7 @@
1
- # ip2region Node.js (TypeScript)
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
- > **Database Required**: Download the xdb database files from [ip2region data](https://github.com/lionsoul2014/ip2region/tree/master/data) before using.
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('./data/ip2region_v4.xdb', {
37
- cachePolicy: 'vectorIndex', // file/vectorIndex/content
38
- ipVersion: 'v4', // v4/v6
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: '中国|0|江苏省|苏州市|电信', ioCount: 3, took: 1000 }
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: string, options?: Ip2RegionOptions)
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 at least 1 argument").ThrowAsJavaScriptException();
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
- // 初始化winsock (Windows)
75
+ // Initialize winsock (Windows only)
81
76
  int err = xdb_init_winsock();
82
77
  if (err != 0) {
83
- Napi::Error::New(env, "Failed to init winsock").ThrowAsJavaScriptException();
78
+ Napi::Error::New(env, "Failed to initialize network layer").ThrowAsJavaScriptException();
84
79
  return;
85
80
  }
86
81
 
87
- // 验证xdb文件 (跳过严格验证,因为某些xdb文件可能有兼容性问题)
88
- // err = xdb_verify_from_file(db_path.c_str());
89
- // if (err != 0) {
90
- // std::string error_msg = "Failed to verify xdb file, error code: " + std::to_string(err);
91
- // Napi::Error::New(env, error_msg).ThrowAsJavaScriptException();
92
- // return;
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
- // 根据缓存策略初始化searcher
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 == nullptr) {
112
- Napi::Error::New(env, "Failed to load vector index").ThrowAsJavaScriptException();
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 == nullptr) {
119
- Napi::Error::New(env, "Failed to load content").ThrowAsJavaScriptException();
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
- Napi::Error::New(env, "Invalid cache policy").ThrowAsJavaScriptException();
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
- Napi::Error::New(env, "Failed to create searcher").ThrowAsJavaScriptException();
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 string for IP address").ThrowAsJavaScriptException();
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(&region, nullptr, 0);
170
162
  if (err != 0) {
171
- Napi::Error::New(env, "Failed to init region buffer").ThrowAsJavaScriptException();
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(), &region);
177
- long cost_time = xdb_now() - start_time;
169
+ const long elapsed_time = xdb_now() - start_time;
178
170
 
179
171
  if (err != 0) {
180
172
  xdb_region_buffer_free(&region);
181
- Napi::Error::New(env, "Search failed").ThrowAsJavaScriptException();
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, cost_time));
180
+ result.Set("took", Napi::Number::New(env, elapsed_time));
189
181
 
190
182
  xdb_region_buffer_free(&region);
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 string for db_path").ThrowAsJavaScriptException();
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
- int err = 0;
229
- FILE *handle = fopen(db_path.c_str(), "rb");
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
- // 加载header
234
- xdb_header_t *header = xdb_load_header(handle);
235
- if (header == NULL) {
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(handle);
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, err == 0));
247
- result.Set("errorCode", Napi::Number::New(env, err));
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
- /** 缓存策略: 'file' | 'vectorIndex' | 'content' */
10
+ /** Cache strategy: 'file' | 'vectorIndex' | 'content' */
11
11
  cachePolicy?: 'file' | 'vectorIndex' | 'content';
12
- /** IP版本: 'v4' | 'v6' */
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
- * 创建ip2region查询器实例
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: string, options?: Ip2RegionOptions);
30
+ constructor(dbPath?: string, options?: Ip2RegionOptions);
29
31
  /**
30
- * 查询IP地址对应的地理位置信息
31
- * @param ip IP地址字符串(支持IPv4和IPv6)
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
- * 验证xdb文件的有效性
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
- * 验证xdb文件并返回详细信息
47
- * @param dbPath xdb文件路径
48
- * @returns 包含 valid 和 errorCode 的对象
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IACzB,gBAAgB;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe;IACf,IAAI,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC7B,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAAC;IACjD,wBAAwB;IACxB,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IACzB,WAAW;IACX,KAAK,EAAE,OAAO,CAAC;IACf,WAAW;IACX,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,CAAC,OAAO,OAAO,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAM;IAEtB;;;;OAIG;gBACS,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAI1D;;;;OAIG;IACH,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY;IAIhC;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;;;OAIG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAKtC;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY;CAGtD;AAED,OAAO,EAAE,SAAS,EAAE,CAAA"}
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
- * 创建ip2region查询器实例
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.searcher = new addon.Ip2RegionSearcher(dbPath, options);
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
- * 查询IP地址对应的地理位置信息
16
- * @param ip IP地址字符串(支持IPv4和IPv6)
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
- * 验证xdb文件的有效性
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
- * 验证xdb文件并返回详细信息
39
- * @param dbPath xdb文件路径
40
- * @returns 包含 valid 和 errorCode 的对象
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;AAyBzD,MAAqB,SAAS;IAG1B;;;;OAIG;IACH,YAAY,MAAc,EAAE,UAA4B,EAAE;QACtD,IAAI,CAAC,QAAQ,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,EAAU;QACb,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,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,OAAO,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;CACJ;AA9CD,4BA8CC;AAEQ,8BAAS"}
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.1",
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": {