ts-ip2region2 1.0.0 → 1.0.2
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/LICENSE +201 -0
- package/README.md +41 -26
- package/README_CN.md +44 -28
- package/addon.cpp +52 -66
- package/dist/index.d.ts +32 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +51 -14
- package/dist/index.js.map +1 -1
- package/package.json +15 -14
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ip2region Node.js
|
|
1
|
+
# ip2region Node.js (TypeScript)
|
|
2
2
|
|
|
3
3
|
[English](README.md) | [中文](README_CN.md)
|
|
4
4
|
|
|
@@ -9,7 +9,7 @@ High-performance Node.js native addon for ip2region xdb query with IPv4/IPv6 sup
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
11
|
- 🚀 **High Performance** - Native C++ implementation with microsecond-level response
|
|
12
|
-
- 🌐 **IPv4 & IPv6** - Full support for both IPv4 and IPv6 address queries
|
|
12
|
+
- 🌐 **IPv4 & IPv6** - Full support for both IPv4 and IPv6 address queries
|
|
13
13
|
- 💾 **Multiple Cache Strategies** - file/vectorIndex/content caching options
|
|
14
14
|
- 🔒 **Memory Safe** - Automatic resource management prevents memory leaks
|
|
15
15
|
- 📝 **TypeScript** - Complete TypeScript type definitions included
|
|
@@ -18,8 +18,11 @@ High-performance Node.js native addon for ip2region xdb query with IPv4/IPv6 sup
|
|
|
18
18
|
## Installation
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npm install
|
|
22
|
-
|
|
21
|
+
npm install ts-ip2region2
|
|
22
|
+
# or
|
|
23
|
+
pnpm add ts-ip2region2
|
|
24
|
+
# or
|
|
25
|
+
yarn add ts-ip2region2
|
|
23
26
|
```
|
|
24
27
|
|
|
25
28
|
> **Database Required**: Download the xdb database files from [ip2region data](https://github.com/lionsoul2014/ip2region/tree/master/data) before using.
|
|
@@ -27,18 +30,18 @@ npm run build:all
|
|
|
27
30
|
## Quick Start
|
|
28
31
|
|
|
29
32
|
```typescript
|
|
30
|
-
import Ip2Region from '
|
|
33
|
+
import { Ip2Region } from 'ts-ip2region2';
|
|
31
34
|
|
|
32
35
|
// Create searcher instance
|
|
33
36
|
const searcher = new Ip2Region('./data/ip2region_v4.xdb', {
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
cachePolicy: 'vectorIndex', // file/vectorIndex/content
|
|
38
|
+
ipVersion: 'v4', // v4/v6
|
|
36
39
|
});
|
|
37
40
|
|
|
38
41
|
// Query IP address
|
|
39
|
-
const result = searcher.search('
|
|
42
|
+
const result = searcher.search('120.229.45.2');
|
|
40
43
|
console.log(result);
|
|
41
|
-
// Output: { region: '
|
|
44
|
+
// Output: { region: '中国|广东省|深圳市|移动', ioCount: 3, took: 1000 }
|
|
42
45
|
|
|
43
46
|
// Clean up
|
|
44
47
|
searcher.close();
|
|
@@ -47,68 +50,81 @@ searcher.close();
|
|
|
47
50
|
## API Reference
|
|
48
51
|
|
|
49
52
|
### Constructor
|
|
53
|
+
|
|
50
54
|
```typescript
|
|
51
55
|
new Ip2Region(dbPath: string, options?: Ip2RegionOptions)
|
|
52
56
|
```
|
|
53
57
|
|
|
54
58
|
### Methods
|
|
59
|
+
|
|
55
60
|
- `search(ip: string): SearchResult` - Query IP address location
|
|
56
61
|
- `close(): void` - Release resources
|
|
57
62
|
|
|
58
63
|
### Static Methods
|
|
64
|
+
|
|
59
65
|
- `Ip2Region.verify(dbPath: string): boolean` - Verify xdb file
|
|
60
66
|
- `Ip2Region.verifyDetailed(dbPath: string): VerifyResult` - Verify with detailed info
|
|
61
67
|
|
|
62
68
|
### Types
|
|
69
|
+
|
|
63
70
|
```typescript
|
|
64
71
|
interface SearchResult {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
region: string; // Geographic location
|
|
73
|
+
ioCount: number; // IO operation count
|
|
74
|
+
took: number; // Query time in microseconds
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
interface Ip2RegionOptions {
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
cachePolicy?: 'file' | 'vectorIndex' | 'content';
|
|
79
|
+
ipVersion?: 'v4' | 'v6';
|
|
73
80
|
}
|
|
74
81
|
```
|
|
75
82
|
|
|
76
83
|
## Cache Strategies
|
|
77
84
|
|
|
78
|
-
| Strategy
|
|
79
|
-
|
|
80
|
-
| `file`
|
|
81
|
-
| `vectorIndex` | Medium
|
|
82
|
-
| `content`
|
|
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 |
|
|
83
90
|
|
|
84
91
|
## Examples
|
|
85
92
|
|
|
86
93
|
```bash
|
|
87
|
-
#
|
|
94
|
+
# Build and run example
|
|
88
95
|
npm run example
|
|
89
96
|
|
|
90
|
-
#
|
|
97
|
+
# Build and run tests
|
|
91
98
|
npm test
|
|
99
|
+
|
|
100
|
+
# Build native addon
|
|
101
|
+
npm run build
|
|
102
|
+
|
|
103
|
+
# Compile TypeScript
|
|
104
|
+
npm run compile
|
|
92
105
|
```
|
|
93
106
|
|
|
94
107
|
## Project Structure
|
|
95
108
|
|
|
96
109
|
```
|
|
97
|
-
|
|
110
|
+
ts-ip2region2/
|
|
98
111
|
├── src/ # TypeScript source code
|
|
99
|
-
│
|
|
100
|
-
│ ├── example.ts # TypeScript example
|
|
101
|
-
│ └── test.ts # TypeScript tests
|
|
112
|
+
│ └── index.ts # Main TypeScript API
|
|
102
113
|
├── dist/ # Compiled JavaScript output
|
|
103
114
|
│ ├── index.js
|
|
104
115
|
│ ├── index.d.ts
|
|
105
116
|
│ └── ...
|
|
117
|
+
├── data/ # Sample xdb database files
|
|
118
|
+
│ ├── ip2region_v4.xdb
|
|
119
|
+
│ └── ip2region_v6.xdb
|
|
106
120
|
├── ip2region/ # Original ip2region C source code
|
|
107
121
|
│ ├── xdb_api.h
|
|
108
122
|
│ ├── xdb_util.c
|
|
109
123
|
│ └── xdb_searcher.c
|
|
110
124
|
├── addon.cpp # Node.js addon implementation
|
|
111
125
|
├── binding.gyp # Build configuration
|
|
126
|
+
├── example.js # JavaScript example
|
|
127
|
+
├── test.js # JavaScript tests
|
|
112
128
|
├── tsconfig.json # TypeScript configuration
|
|
113
129
|
├── package.json
|
|
114
130
|
└── README.md
|
|
@@ -117,4 +133,3 @@ ip2region-nodejs/
|
|
|
117
133
|
## License
|
|
118
134
|
|
|
119
135
|
Apache-2.0
|
|
120
|
-
|
package/README_CN.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# ip2region Node.js
|
|
1
|
+
# ip2region Node.js (TypeScript)
|
|
2
2
|
|
|
3
3
|
[English](README.md) | [中文](README_CN.md)
|
|
4
4
|
|
|
5
5
|
高性能的 ip2region xdb 查询 Node.js 原生扩展,支持 IPv4/IPv6。使用 TypeScript 编写,包含完整的类型定义。
|
|
6
6
|
|
|
7
|
-
> **说明**: 本项目基于官方 [ip2region C语言客户端](https://github.com/lionsoul2014/ip2region/tree/master/binding/c) 改造,并增强了 TypeScript 支持。
|
|
7
|
+
> **说明**: 本项目基于官方 [ip2region C 语言客户端](https://github.com/lionsoul2014/ip2region/tree/master/binding/c) 改造,并增强了 TypeScript 支持。
|
|
8
8
|
|
|
9
9
|
## 特性
|
|
10
10
|
|
|
@@ -18,8 +18,11 @@
|
|
|
18
18
|
## 安装
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
npm install
|
|
22
|
-
|
|
21
|
+
npm install ts-ip2region2
|
|
22
|
+
# or
|
|
23
|
+
pnpm add ts-ip2region2
|
|
24
|
+
# or
|
|
25
|
+
yarn add ts-ip2region2
|
|
23
26
|
```
|
|
24
27
|
|
|
25
28
|
> **需要数据库**: 使用前请从 [ip2region data](https://github.com/lionsoul2014/ip2region/tree/master/data) 下载 xdb 数据库文件。
|
|
@@ -27,18 +30,18 @@ npm run build:all
|
|
|
27
30
|
## 快速开始
|
|
28
31
|
|
|
29
32
|
```typescript
|
|
30
|
-
import Ip2Region from '
|
|
33
|
+
import { Ip2Region } from 'ts-ip2region2';
|
|
31
34
|
|
|
32
35
|
// 创建查询器实例
|
|
33
36
|
const searcher = new Ip2Region('./data/ip2region_v4.xdb', {
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
cachePolicy: 'vectorIndex', // file/vectorIndex/content
|
|
38
|
+
ipVersion: 'v4', // v4/v6
|
|
36
39
|
});
|
|
37
40
|
|
|
38
41
|
// 查询IP地址
|
|
39
|
-
const result = searcher.search('
|
|
42
|
+
const result = searcher.search('120.229.45.2');
|
|
40
43
|
console.log(result);
|
|
41
|
-
// 输出: { region: '
|
|
44
|
+
// 输出: { region: '中国|广东省|深圳市|移动', ioCount: 3, took: 1000 }
|
|
42
45
|
|
|
43
46
|
// 清理资源
|
|
44
47
|
searcher.close();
|
|
@@ -47,68 +50,81 @@ searcher.close();
|
|
|
47
50
|
## API 参考
|
|
48
51
|
|
|
49
52
|
### 构造函数
|
|
53
|
+
|
|
50
54
|
```typescript
|
|
51
55
|
new Ip2Region(dbPath: string, options?: Ip2RegionOptions)
|
|
52
56
|
```
|
|
53
57
|
|
|
54
58
|
### 方法
|
|
55
|
-
|
|
59
|
+
|
|
60
|
+
- `search(ip: string): SearchResult` - 查询 IP 地址位置
|
|
56
61
|
- `close(): void` - 释放资源
|
|
57
62
|
|
|
58
63
|
### 静态方法
|
|
59
|
-
|
|
64
|
+
|
|
65
|
+
- `Ip2Region.verify(dbPath: string): boolean` - 验证 xdb 文件
|
|
60
66
|
- `Ip2Region.verifyDetailed(dbPath: string): VerifyResult` - 验证并返回详细信息
|
|
61
67
|
|
|
62
68
|
### 类型定义
|
|
69
|
+
|
|
63
70
|
```typescript
|
|
64
71
|
interface SearchResult {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
region: string; // 地理位置
|
|
73
|
+
ioCount: number; // IO操作次数
|
|
74
|
+
took: number; // 查询耗时(微秒)
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
interface Ip2RegionOptions {
|
|
71
|
-
|
|
72
|
-
|
|
78
|
+
cachePolicy?: 'file' | 'vectorIndex' | 'content';
|
|
79
|
+
ipVersion?: 'v4' | 'v6';
|
|
73
80
|
}
|
|
74
81
|
```
|
|
75
82
|
|
|
76
83
|
## 缓存策略
|
|
77
84
|
|
|
78
|
-
| 策略
|
|
79
|
-
|
|
80
|
-
| `file`
|
|
81
|
-
| `vectorIndex` | 中等
|
|
82
|
-
| `content`
|
|
85
|
+
| 策略 | 内存占用 | 性能 | 使用场景 |
|
|
86
|
+
| ------------- | -------- | ---- | -------------- |
|
|
87
|
+
| `file` | 最小 | 良好 | 内存受限环境 |
|
|
88
|
+
| `vectorIndex` | 中等 | 更好 | 一般使用(推荐) |
|
|
89
|
+
| `content` | 较高 | 最佳 | 高并发场景 |
|
|
83
90
|
|
|
84
91
|
## 示例
|
|
85
92
|
|
|
86
93
|
```bash
|
|
87
|
-
#
|
|
94
|
+
# 构建并运行示例
|
|
88
95
|
npm run example
|
|
89
96
|
|
|
90
|
-
#
|
|
97
|
+
# 构建并运行测试
|
|
91
98
|
npm test
|
|
99
|
+
|
|
100
|
+
# 构建原生扩展
|
|
101
|
+
npm run build
|
|
102
|
+
|
|
103
|
+
# 编译 TypeScript
|
|
104
|
+
npm run compile
|
|
92
105
|
```
|
|
93
106
|
|
|
94
107
|
## 项目结构
|
|
95
108
|
|
|
96
109
|
```
|
|
97
|
-
|
|
110
|
+
ts-ip2region2/
|
|
98
111
|
├── src/ # TypeScript 源代码
|
|
99
|
-
│
|
|
100
|
-
│ ├── example.ts # TypeScript 示例
|
|
101
|
-
│ └── test.ts # TypeScript 测试
|
|
112
|
+
│ └── index.ts # 主要 TypeScript API
|
|
102
113
|
├── dist/ # 编译后的 JavaScript 输出
|
|
103
114
|
│ ├── index.js
|
|
104
115
|
│ ├── index.d.ts
|
|
105
116
|
│ └── ...
|
|
117
|
+
├── data/ # 示例 xdb 数据库文件
|
|
118
|
+
│ ├── ip2region_v4.xdb
|
|
119
|
+
│ └── ip2region_v6.xdb
|
|
106
120
|
├── ip2region/ # 原始 ip2region C 源代码
|
|
107
121
|
│ ├── xdb_api.h
|
|
108
122
|
│ ├── xdb_util.c
|
|
109
123
|
│ └── xdb_searcher.c
|
|
110
124
|
├── addon.cpp # Node.js 扩展实现
|
|
111
125
|
├── binding.gyp # 构建配置
|
|
126
|
+
├── example.js # JavaScript 示例
|
|
127
|
+
├── test.js # JavaScript 测试
|
|
112
128
|
├── tsconfig.json # TypeScript 配置
|
|
113
129
|
├── package.json
|
|
114
130
|
└── README.md
|
|
@@ -116,4 +132,4 @@ ip2region-nodejs/
|
|
|
116
132
|
|
|
117
133
|
## 许可证
|
|
118
134
|
|
|
119
|
-
Apache-2.0
|
|
135
|
+
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
|
|
28
|
+
* @param options Configuration options
|
|
27
29
|
*/
|
|
28
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":"AASA,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,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB;IAc1D;;;;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,83 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Ip2Region = void 0;
|
|
4
4
|
const addon = require('../build/Release/ip2region.node');
|
|
5
|
+
// Error codes for verification
|
|
6
|
+
const ERROR_CODES = {
|
|
7
|
+
SUCCESS: 0,
|
|
8
|
+
FILE_NOT_FOUND: -1,
|
|
9
|
+
INVALID_FORMAT: 1,
|
|
10
|
+
};
|
|
5
11
|
class Ip2Region {
|
|
6
12
|
/**
|
|
7
|
-
*
|
|
8
|
-
* @param dbPath xdb
|
|
9
|
-
* @param options
|
|
13
|
+
* Create ip2region searcher instance
|
|
14
|
+
* @param dbPath Path to xdb database file
|
|
15
|
+
* @param options Configuration options
|
|
10
16
|
*/
|
|
11
17
|
constructor(dbPath, options = {}) {
|
|
12
|
-
|
|
18
|
+
if (!dbPath || typeof dbPath !== 'string') {
|
|
19
|
+
throw new TypeError('Database path must be a non-empty string');
|
|
20
|
+
}
|
|
21
|
+
this.dbPath = dbPath;
|
|
22
|
+
this.options = {
|
|
23
|
+
cachePolicy: options.cachePolicy || 'vectorIndex',
|
|
24
|
+
ipVersion: options.ipVersion || 'v4',
|
|
25
|
+
};
|
|
26
|
+
this.searcher = new addon.Ip2RegionSearcher(dbPath, this.options);
|
|
13
27
|
}
|
|
14
28
|
/**
|
|
15
|
-
*
|
|
16
|
-
* @param ip IP
|
|
17
|
-
* @returns
|
|
29
|
+
* Query geographic location information for IP address
|
|
30
|
+
* @param ip IP address string (supports IPv4 and IPv6)
|
|
31
|
+
* @returns Query result
|
|
18
32
|
*/
|
|
19
33
|
search(ip) {
|
|
34
|
+
if (!ip || typeof ip !== 'string') {
|
|
35
|
+
throw new TypeError('IP address must be a non-empty string');
|
|
36
|
+
}
|
|
20
37
|
return this.searcher.search(ip);
|
|
21
38
|
}
|
|
22
39
|
/**
|
|
23
|
-
*
|
|
40
|
+
* Close searcher and release resources
|
|
24
41
|
*/
|
|
25
42
|
close() {
|
|
26
43
|
this.searcher.close();
|
|
27
44
|
}
|
|
28
45
|
/**
|
|
29
|
-
*
|
|
30
|
-
* @param dbPath xdb
|
|
31
|
-
* @returns
|
|
46
|
+
* Verify the validity of xdb file
|
|
47
|
+
* @param dbPath Path to xdb file
|
|
48
|
+
* @returns Verification result
|
|
32
49
|
*/
|
|
33
50
|
static verify(dbPath) {
|
|
51
|
+
if (!dbPath || typeof dbPath !== 'string') {
|
|
52
|
+
throw new TypeError('Database path must be a non-empty string');
|
|
53
|
+
}
|
|
34
54
|
const result = addon.verifyXdb(dbPath);
|
|
35
55
|
return result.valid;
|
|
36
56
|
}
|
|
37
57
|
/**
|
|
38
|
-
*
|
|
39
|
-
* @param dbPath xdb
|
|
40
|
-
* @returns
|
|
58
|
+
* Verify xdb file and return detailed information
|
|
59
|
+
* @param dbPath Path to xdb file
|
|
60
|
+
* @returns Object containing valid and errorCode
|
|
41
61
|
*/
|
|
42
62
|
static verifyDetailed(dbPath) {
|
|
63
|
+
if (!dbPath || typeof dbPath !== 'string') {
|
|
64
|
+
throw new TypeError('Database path must be a non-empty string');
|
|
65
|
+
}
|
|
43
66
|
return addon.verifyXdb(dbPath);
|
|
44
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Get current configuration
|
|
70
|
+
* @returns Current options
|
|
71
|
+
*/
|
|
72
|
+
getOptions() {
|
|
73
|
+
return { ...this.options };
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get database file path
|
|
77
|
+
* @returns Database path
|
|
78
|
+
*/
|
|
79
|
+
getDbPath() {
|
|
80
|
+
return this.dbPath;
|
|
81
|
+
}
|
|
45
82
|
}
|
|
46
83
|
exports.default = Ip2Region;
|
|
47
84
|
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;AAEzD,+BAA+B;AAC/B,MAAM,WAAW,GAAG;IAChB,OAAO,EAAE,CAAC;IACV,cAAc,EAAE,CAAC,CAAC;IAClB,cAAc,EAAE,CAAC;CACX,CAAC;AAyBX,MAAqB,SAAS;IAK1B;;;;OAIG;IACH,YAAY,MAAc,EAAE,UAA4B,EAAE;QACtD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;QACpE,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,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,QAAQ,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACtE,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;AAnFD,4BAmFC;AAEQ,8BAAS"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-ip2region2",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
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",
|
|
@@ -37,19 +37,19 @@
|
|
|
37
37
|
},
|
|
38
38
|
"homepage": "https://github.com/Steven-Qiang/ts-ip2region2#readme",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"node-addon-api": "^
|
|
40
|
+
"node-addon-api": "^8.5.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"semantic-release": "^
|
|
47
|
-
"@semantic-release/
|
|
48
|
-
"@semantic-release/
|
|
49
|
-
"@
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
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
|
+
"@types/node": "^24.7.0",
|
|
50
|
+
"node-gyp": "^11.4.2",
|
|
51
|
+
"semantic-release": "^24.2.9",
|
|
52
|
+
"typescript": "^5.9.3"
|
|
53
53
|
},
|
|
54
54
|
"engines": {
|
|
55
55
|
"node": ">=16.0.0"
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
],
|
|
74
74
|
"gypfile": true,
|
|
75
75
|
"publishConfig": {
|
|
76
|
-
"access": "public"
|
|
76
|
+
"access": "public",
|
|
77
|
+
"provenance": true
|
|
77
78
|
}
|
|
78
|
-
}
|
|
79
|
+
}
|