plevinjs 0.2.1 → 0.2.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.
Files changed (63) hide show
  1. package/README.md +78 -13
  2. package/dist/cli.d.ts +4 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +16 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/compress.d.ts +5 -0
  7. package/dist/compress.d.ts.map +1 -0
  8. package/dist/compress.js +954 -0
  9. package/dist/compress.js.map +1 -0
  10. package/dist/countries.d.ts +1 -1
  11. package/dist/countries.d.ts.map +1 -1
  12. package/dist/countries.js +251 -250
  13. package/dist/countries.js.map +1 -1
  14. package/dist/derive.d.ts +1 -0
  15. package/dist/derive.d.ts.map +1 -1
  16. package/dist/derive.js +12 -6
  17. package/dist/derive.js.map +1 -1
  18. package/dist/estimate.d.ts +38 -0
  19. package/dist/estimate.d.ts.map +1 -0
  20. package/dist/estimate.js +263 -0
  21. package/dist/estimate.js.map +1 -0
  22. package/dist/extra.d.ts.map +1 -1
  23. package/dist/extra.js +12 -12
  24. package/dist/extra.js.map +1 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +51 -6
  27. package/dist/index.js.map +1 -1
  28. package/dist/leaves.d.ts +30 -0
  29. package/dist/leaves.d.ts.map +1 -0
  30. package/dist/leaves.js +183 -0
  31. package/dist/leaves.js.map +1 -0
  32. package/dist/lzma.d.ts +18 -0
  33. package/dist/lzma.d.ts.map +1 -1
  34. package/dist/lzma.js +18 -18
  35. package/dist/lzma.js.map +1 -1
  36. package/dist/matcher.d.ts +25 -0
  37. package/dist/matcher.d.ts.map +1 -0
  38. package/dist/matcher.js +198 -0
  39. package/dist/matcher.js.map +1 -0
  40. package/dist/models.d.ts +7 -0
  41. package/dist/models.d.ts.map +1 -1
  42. package/dist/plevin.min.js +258 -257
  43. package/dist/reader.d.ts.map +1 -1
  44. package/dist/reader.js +12 -1
  45. package/dist/reader.js.map +1 -1
  46. package/dist/selection.d.ts +34 -0
  47. package/dist/selection.d.ts.map +1 -0
  48. package/dist/selection.js +338 -0
  49. package/dist/selection.js.map +1 -0
  50. package/dist/slim.d.ts +21 -0
  51. package/dist/slim.d.ts.map +1 -0
  52. package/dist/slim.js +639 -0
  53. package/dist/slim.js.map +1 -0
  54. package/dist/slim.min.js +1 -0
  55. package/dist/stats.d.ts +61 -0
  56. package/dist/stats.d.ts.map +1 -0
  57. package/dist/stats.js +368 -0
  58. package/dist/stats.js.map +1 -0
  59. package/dist/writer.d.ts +32 -0
  60. package/dist/writer.d.ts.map +1 -0
  61. package/dist/writer.js +325 -0
  62. package/dist/writer.js.map +1 -0
  63. package/package.json +7 -2
package/README.md CHANGED
@@ -12,7 +12,7 @@ No API, no rate limit, no lookup leaving the machine or the browser tab.
12
12
  [![npm](https://img.shields.io/npm/v/plevinjs?color=1868f2)](https://www.npmjs.com/package/plevinjs)
13
13
  [![Types](https://img.shields.io/badge/types-included-1868f2)](https://www.npmjs.com/package/plevinjs?activeTab=code)
14
14
  [![License](https://img.shields.io/badge/license-Apache--2.0-1868f2)](https://github.com/tn3w/plevin/blob/master/LICENSE)
15
- [![Fields](https://img.shields.io/badge/fields-101-6f42c1)](#fields)
15
+ [![Fields](https://img.shields.io/badge/fields-108-6f42c1)](#fields)
16
16
  [![Warm](https://img.shields.io/badge/warm%20lookups-5M%2Fs-2ea043)](#speed)
17
17
 
18
18
  </div>
@@ -52,9 +52,9 @@ Open once, reuse for every lookup. Nothing is downloaded or cached for you.
52
52
 
53
53
  | file | size | carries |
54
54
  | ----------------------------------------------------- | ------- | ---------------------------------------- |
55
- | `plevin.plv` | 17.3 MB | every field |
56
- | `plevin.metro-place.plv` | 5.4 MB | city, region, postal, coordinates, metro |
57
- | `plevin.network.plv` | 7.0 MB | ASN, operator, routing |
55
+ | `plevin.plv` | 18.7 MB | every field |
56
+ | `plevin.metro-place.plv` | 6.3 MB | city, region, postal, coordinates, metro |
57
+ | `plevin.network.plv` | 7.4 MB | ASN, operator, routing, registry |
58
58
  | `plevin.abuse-level-abuse-provider-abuse-service.plv` | 4.1 MB | abuse level, service and provider |
59
59
  | `plevin.place-country-code.plv` | 378 KB | country code |
60
60
 
@@ -82,8 +82,8 @@ No build step:
82
82
  | `https://esm.sh/plevinjs` | the modules, imports rewritten |
83
83
  | `https://plevin.tn3w.dev/plevin/plevin.min.js` | the bundle beside the databases |
84
84
 
85
- - **Pin a version for production:** `cdn.jsdelivr.net/npm/plevinjs@0.2.1`.
86
- - **Pick the smallest build:** the country build is 378 KB against 17.3 MB.
85
+ - **Pin a version for production:** `cdn.jsdelivr.net/npm/plevinjs@0.2.2`.
86
+ - **Pick the smallest build:** the country build is 378 KB against 18.7 MB.
87
87
  - **Cache the file** so it downloads once per visitor:
88
88
 
89
89
  ```js
@@ -119,7 +119,9 @@ found.place;
119
119
  },
120
120
  country: {
121
121
  code: 'AU', name: 'Australia', official: null, common: null, iso3: 'AUS',
122
- numeric: '036', flag: '🇦🇺', european_union: false, driving_side: 'left',
122
+ numeric: '036', flag: '🇦🇺', currency: 'AUD', currency_name: 'Australian Dollar',
123
+ calling_code: '+61', languages: ['en'], european_union: false,
124
+ driving_side: 'left',
123
125
  },
124
126
  time: {
125
127
  timezone: 'Australia/Brisbane', abbreviation: 'AEST',
@@ -131,7 +133,8 @@ found.place;
131
133
  found.network;
132
134
  {
133
135
  asn: 13335, handle: 'CLOUDFLARENET', prefix: 24, cidr: '1.1.1.0/24',
134
- start: '1.1.1.0', end: '1.1.1.255', rir: 'apnic', rpki: 'valid', roas: 1,
136
+ start: '1.1.1.0', end: '1.1.1.255', rir: 'apnic', country: 'AU', since: 2011,
137
+ rpki: 'valid', roas: 1,
135
138
  operator: {
136
139
  company: 'Cloudflare, Inc.', brand: 'Cloudflare', domain: 'cloudflare.com',
137
140
  website: 'https://www.cloudflare.com', category: 'content', tier: 2,
@@ -147,7 +150,7 @@ db.lookup("185.220.101.1").abuse;
147
150
  {
148
151
  name: 'Tor', provider: 'Tor', service: 'tor_exit_node', evidence: 'measured',
149
152
  threat: 'spam', level: 'high', risk: 0.99, network_risk: 0.86, last_seen_days: 1,
150
- is_malicious: true, is_anycast: false, is_satellite: false,
153
+ is_malicious: true, is_anycast: false, is_satellite: false, is_crawler: false,
151
154
  is_hosting_provider: true, is_proxy: false, is_public_proxy: false,
152
155
  is_residential_proxy: false, is_anonymous_vpn: false, is_tor_exit_node: true,
153
156
  is_private_relay: false, is_anonymous: true,
@@ -156,9 +159,10 @@ db.lookup("185.220.101.1").abuse;
156
159
 
157
160
  JavaScript specifics:
158
161
 
159
- - `country` comes from an ISO 3166 table in the package and `time` from the runtime's
160
- `Intl` data, so neither needs an install or a network call. A different zone
161
- database can move a daylight-saving boundary.
162
+ - `country` comes from a table in the package, generated from pycountry, Babel and
163
+ phonenumbers by [`test/countries.py`](test/countries.py), and `time` from the
164
+ runtime's `Intl` data, so neither needs an install or a network call. A different
165
+ zone database can move a daylight-saving boundary.
162
166
  - `number` is a `bigint` for v6, so `JSON.stringify` needs a replacer.
163
167
 
164
168
  Address fields (`compressed`, `expanded`, `arpa`, `is_*`, `tunnel`, `embedded_ipv4`,
@@ -252,7 +256,8 @@ of heap with the whole world touched.
252
256
  ## LZMA
253
257
 
254
258
  Blocks are raw LZMA1, which neither `DecompressionStream` nor `zlib` reads, so the
255
- package ships its own decoder, checked against liblzma on every block.
259
+ package ships its own decoder, checked against liblzma on every block, and, for
260
+ [`slim`](#slim-databases), an encoder that writes the bytes liblzma writes.
256
261
 
257
262
  ```js
258
263
  import { decompress } from "plevinjs/lzma";
@@ -260,6 +265,52 @@ import { decompress } from "plevinjs/lzma";
260
265
  decompress(block, [3, 0, 0]); // tuning: literal context, literal position, match position bits
261
266
  ```
262
267
 
268
+ ## Slim databases
269
+
270
+ Cut the builder's raw file down to the fields you use, in the browser or on a server.
271
+ It is the builder's selection pipeline in JavaScript, run on `plevin.raw`: the same
272
+ terms, columns, rows and boundaries merged, blocks re-encoded and compressed, so the
273
+ result opens like any other file.
274
+
275
+ ```js
276
+ import { slim } from "plevinjs/slim";
277
+
278
+ const raw = new Uint8Array(await (await fetch(url)).arrayBuffer()); // plevin.raw
279
+ const small = slim(raw, "place.country.code+network.asn"); // 400 KB, not 18.7 MB
280
+ const db = new Plevin(small);
281
+ ```
282
+
283
+ ```bash
284
+ npx plevinjs place+metro # reads plevin.raw, writes plevin.metro-place.plv
285
+ ```
286
+
287
+ | terms | keeps |
288
+ | ---------------------- | --------------------------------------------------------- |
289
+ | `full` | every field the source holds |
290
+ | `place+metro` | every field under `place`, plus `metro`; join with `+` |
291
+ | `abuse.is_tor_exit_node` | one flag; rows narrowed to Tor exits only |
292
+
293
+ - **Raw in:** [`plevin.raw`](https://github.com/tn3w/plevin/releases/latest/download/plevin.raw)
294
+ (30 MB) is the world the builder cuts from. Slimmed files are not raw: they cannot be
295
+ cut again and `slim` throws on them.
296
+ - **Same as the builder:** byte for byte. The encoder is a port of liblzma's preset 9
297
+ (binary-tree matcher, optimal parse, range coder), checked on every block of the
298
+ release files; the same terms give the same file, `full` included. The build date
299
+ stays the raw file's.
300
+ - **Only what the source holds:** a term that matches nothing throws.
301
+ - **Size before building:** `estimate(stats, terms)` from `plevinjs/estimate` reads a
302
+ few hundred KB of counted facts (`new Slimmer(raw).stats()`, or the `stats.json`
303
+ beside the databases) and answers instantly, without decoding anything. Rates are fitted
304
+ to 172 builds; on 64 others it landed within 10% for four in five and within
305
+ about 2× at worst, tiny selections being the loosest. A build is exact.
306
+ - **Without code:** [plevin.tn3w.dev/#slim](https://plevin.tn3w.dev/#slim) picks the
307
+ fields, shows the size first and saves the file; it runs `slim` in a worker.
308
+ - **Out of the CDN bundle:** `plevinjs` and `plevin.min.js` never import it. Reach it at
309
+ `plevinjs/slim`, `https://esm.sh/plevinjs/slim` or
310
+ `https://cdn.jsdelivr.net/npm/plevinjs/dist/slim.min.js`.
311
+ - **Cost:** the full cut needs about 2 GB of heap and under two minutes; a country file
312
+ under 10 s. No native module is needed.
313
+
263
314
  ## Development
264
315
 
265
316
  | module | job |
@@ -267,6 +318,15 @@ decompress(block, [3, 0, 0]); // tuning: literal context, literal position, ma
267
318
  | `index.ts` | `Plevin`, `open`, result shaping |
268
319
  | `reader.ts` | file format: sections, blocks, groups, bisection |
269
320
  | `lzma.ts` | resumable LZMA1 decoder |
321
+ | `slim.ts` | `slim`: the raw file's rows, links and boundaries cut to a selection |
322
+ | `selection.ts` | terms, fields, columns, the builder's tables |
323
+ | `writer.ts` | blocks, tuning, header: the builder's `file.rs` |
324
+ | `compress.ts` | LZMA1 encoder, liblzma preset 9 step for step |
325
+ | `matcher.ts` | its binary-tree match finder |
326
+ | `estimate.ts` | `estimate`: a size from counted facts, no decoding |
327
+ | `leaves.ts` | what decides a boundary, which a selection keeps |
328
+ | `stats.ts` | `collect`: the counted facts `estimate` reads |
329
+ | `cli.ts` | `npx plevinjs` |
270
330
  | `address.ts` | parsing, spelling, special ranges |
271
331
  | `naming.ts` | DNS and own address |
272
332
  | `extra.ts` | country and clock, from `countries.ts`/`zones.ts` |
@@ -281,8 +341,13 @@ npm run build # dist/ and the CDN bundle
281
341
 
282
342
  node test/compare.ts ../plevin.plv sample.json # field for field against Python
283
343
  node test/blocks.ts ../plevin.plv 100000 # every block against liblzma
344
+ node test/stats.ts ../plevin.raw stats.json # the facts `estimate` reads
284
345
  ```
285
346
 
347
+ `npm test` also splits `../plevin.raw` and compares each `plevin.*.plv` beside it, which
348
+ must come from the same builder run (`plevin-builder raw place+metro network ...`), and
349
+ `PLEVIN_RAW` / `PLEVIN_DB` point elsewhere. Files over 8 MB are skipped.
350
+
286
351
  ## License
287
352
 
288
353
  Apache 2.0, see [LICENSE](https://github.com/tn3w/plevin/blob/master/LICENSE). The
package/dist/cli.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ /** Cuts a database down from the shell: npx plevinjs place+metro. */
3
+ export {};
4
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,qEAAqE"}
package/dist/cli.js ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ /** Cuts a database down from the shell: npx plevinjs place+metro. */
3
+ import { readFile, writeFile } from "node:fs/promises";
4
+ import { fileName, slim } from "./slim.js";
5
+ const args = process.argv.slice(2);
6
+ const [input, terms, output] = args[0]?.endsWith(".raw") ? args : ["plevin.raw", ...args];
7
+ if (!terms) {
8
+ console.error("usage: npx plevinjs [plevin.raw] <terms> [output.plv]");
9
+ console.error("terms: full, or fields joined by +, e.g. place.country.code+network.asn");
10
+ process.exit(1);
11
+ }
12
+ const bytes = slim(new Uint8Array(await readFile(input)), terms);
13
+ const target = output ?? fileName(terms);
14
+ await writeFile(target, bytes);
15
+ console.log(`${target}: ${bytes.length} bytes`);
16
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,qEAAqE;AAErE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE3C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,GAAG,IAAI,CAAC,CAAC;AAE1F,IAAI,CAAC,KAAK,EAAE,CAAC;IACX,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;IACvE,OAAO,CAAC,KAAK,CACX,yEAAyE,CAC1E,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACjE,MAAM,MAAM,GAAG,MAAM,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC;AACzC,MAAM,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAC/B,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,KAAK,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,5 @@
1
+ /** Raw LZMA1 to an end marker, step for step the encoder liblzma runs at preset 9. */
2
+ import { type Tuning } from "./lzma.ts";
3
+ /** One block as a raw stream, the way `lzma.ts` and liblzma read it. */
4
+ export declare const compress: (data: Uint8Array, tuning: Tuning) => Uint8Array;
5
+ //# sourceMappingURL=compress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAAA,sFAAsF;AAEtF,OAAO,EAiBL,KAAK,MAAM,EACZ,MAAM,WAAW,CAAC;AA6pCnB,wEAAwE;AACxE,eAAO,MAAM,QAAQ,SAAU,UAAU,UAAU,MAAM,KAAG,UAG3D,CAAC"}