@firela/pii-redactor 0.1.0 → 0.1.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/index-node.cjs +16 -7
- package/index-node.mjs +19 -0
- package/index-worker.js +22 -9
- package/index.d.ts +2 -2
- package/package.json +4 -2
package/index-node.cjs
CHANGED
|
@@ -48,16 +48,20 @@ function processFailure(code, message) {
|
|
|
48
48
|
// to first use (lazy _exports cache below) so the module is cheap to require.
|
|
49
49
|
const wasmBytes = fs.readFileSync(path.join(__dirname, 'pii_redactor.opt.wasm'));
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
// Cache the in-flight instantiation PROMISE (not the resolved value) so
|
|
52
|
+
// concurrent cold-start callers await the SAME instantiate — no parallel
|
|
53
|
+
// instantiation race.
|
|
54
|
+
let _exportsPromise;
|
|
52
55
|
|
|
53
56
|
/// Lazily instantiate the wasm module ONCE. Cached so the cold-start cost is
|
|
54
57
|
/// paid only on the first parse/detect call. Returns the exports object.
|
|
55
58
|
async function getExports() {
|
|
56
|
-
if (!
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
if (!_exportsPromise) {
|
|
60
|
+
_exportsPromise = WebAssembly.instantiate(wasmBytes, {}).then(({ instance }) => { // {} = zero imports
|
|
61
|
+
return instance.exports;
|
|
62
|
+
});
|
|
59
63
|
}
|
|
60
|
-
return
|
|
64
|
+
return _exportsPromise;
|
|
61
65
|
}
|
|
62
66
|
|
|
63
67
|
function makeParse(ex) {
|
|
@@ -103,11 +107,16 @@ function makeParse(ex) {
|
|
|
103
107
|
}
|
|
104
108
|
|
|
105
109
|
const resultBytes = Buffer.from(new Uint8Array(ex.memory.buffer, ptr, len));
|
|
106
|
-
const
|
|
107
|
-
|
|
110
|
+
const decoded = resultBytes.toString('utf8'); // decode while buffer is valid
|
|
108
111
|
// Free BOTH buffers every cycle (input + result) per the FFI contract.
|
|
109
112
|
ex.free_result(inputPtr, inputLen);
|
|
110
113
|
ex.free_result(ptr, len);
|
|
114
|
+
let json;
|
|
115
|
+
try {
|
|
116
|
+
json = JSON.parse(decoded);
|
|
117
|
+
} catch (e) {
|
|
118
|
+
return processFailure('INTERNAL', 'parse returned malformed JSON: ' + ((e && e.message) || String(e)));
|
|
119
|
+
}
|
|
111
120
|
return json;
|
|
112
121
|
};
|
|
113
122
|
}
|
package/index-node.mjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// ESM entry for Node.js consumers (Phase 74 UAT gap-closure, test 8).
|
|
2
|
+
//
|
|
3
|
+
// Why this exists: the isomorphic loaders branch ONLY at module-load. The CJS
|
|
4
|
+
// loader (index-node.cjs) reads the wasm via fs + WebAssembly.instantiate(bytes);
|
|
5
|
+
// the Worker loader (index-worker.js) does a static `import mod from '*.wasm'`,
|
|
6
|
+
// which is valid only under a Cloudflare Worker bundler — Node ESM `import` of
|
|
7
|
+
// the Worker entry throws ERR_UNKNOWN_FILE_EXTENSION. package.json routes Node
|
|
8
|
+
// ESM resolution here via exports.node.import.
|
|
9
|
+
//
|
|
10
|
+
// We bridge to the PROVEN CJS loader with createRequire instead of duplicating
|
|
11
|
+
// the FFI cycle — index-node.cjs locates the wasm via path.join(__dirname, ...),
|
|
12
|
+
// and __dirname inside that .cjs is its own directory, so the wasm resolves
|
|
13
|
+
// correctly regardless of how the .cjs was required. The parse/detect contract
|
|
14
|
+
// is therefore byte-identical to the CommonJS path (no second implementation).
|
|
15
|
+
import { createRequire } from 'node:module';
|
|
16
|
+
|
|
17
|
+
const require = createRequire(import.meta.url);
|
|
18
|
+
|
|
19
|
+
export const { parse, detect } = require('./index-node.cjs');
|
package/index-worker.js
CHANGED
|
@@ -35,14 +35,23 @@ const REQUIRED_EXPORTS = ['alloc', 'parse', 'detect', 'free_result', 'memory'];
|
|
|
35
35
|
// instantiation; the result is cached so the cold-start cost is paid once). `mod`
|
|
36
36
|
// is a pre-compiled WebAssembly.Module, so instantiate() returns an Instance
|
|
37
37
|
// (unlike the bytes overload, which returns {module, instance}) — handle both.
|
|
38
|
-
|
|
38
|
+
// Cache the in-flight instantiation PROMISE (not the resolved value) so
|
|
39
|
+
// concurrent cold-start callers await the SAME instantiate — no parallel
|
|
40
|
+
// instantiation race. Validates the FFI exports once on first use.
|
|
41
|
+
let _exportsPromise;
|
|
39
42
|
async function getExports() {
|
|
40
|
-
if (!
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
43
|
+
if (!_exportsPromise) {
|
|
44
|
+
_exportsPromise = WebAssembly.instantiate(mod, {}).then((result) => { // {} = zero imports
|
|
45
|
+
const instance = result.instance || result; // bytes→{instance}, Module→Instance
|
|
46
|
+
const ex = instance.exports;
|
|
47
|
+
const missing = REQUIRED_EXPORTS.filter((name) => !ex[name]);
|
|
48
|
+
if (missing.length) {
|
|
49
|
+
throw new Error('wasm missing required exports: ' + missing.join(', '));
|
|
50
|
+
}
|
|
51
|
+
return ex;
|
|
52
|
+
});
|
|
44
53
|
}
|
|
45
|
-
return
|
|
54
|
+
return _exportsPromise;
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
function processFailure(code, message) {
|
|
@@ -99,11 +108,16 @@ export async function parse(bytes) {
|
|
|
99
108
|
}
|
|
100
109
|
|
|
101
110
|
const resultBytes = new Uint8Array(ex.memory.buffer, ptr, len);
|
|
102
|
-
const
|
|
103
|
-
|
|
111
|
+
const decoded = new TextDecoder().decode(resultBytes); // decode while buffer is valid
|
|
104
112
|
// Free BOTH buffers every cycle (input + result) per the FFI contract.
|
|
105
113
|
ex.free_result(inputPtr, inputLen);
|
|
106
114
|
ex.free_result(ptr, len);
|
|
115
|
+
let json;
|
|
116
|
+
try {
|
|
117
|
+
json = JSON.parse(decoded);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
return processFailure('INTERNAL', 'parse returned malformed JSON: ' + ((e && e.message) || String(e)));
|
|
120
|
+
}
|
|
107
121
|
return json;
|
|
108
122
|
}
|
|
109
123
|
|
|
@@ -130,5 +144,4 @@ export async function detect(bytes) {
|
|
|
130
144
|
return conf;
|
|
131
145
|
}
|
|
132
146
|
|
|
133
|
-
export { parse, detect };
|
|
134
147
|
export default { parse, detect };
|
package/index.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ export type ParseResult = ProcessSuccess | ProcessFailure;
|
|
|
77
77
|
* `errors` lists the categorized error(s). The host never throws on
|
|
78
78
|
* parser-level failures — they arrive as `ProcessFailure`.
|
|
79
79
|
*/
|
|
80
|
-
export declare function parse(bytes: Uint8Array
|
|
80
|
+
export declare function parse(bytes: Uint8Array): Promise<ParseResult>;
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
83
|
* Detection confidence for the given bytes.
|
|
@@ -85,4 +85,4 @@ export declare function parse(bytes: Uint8Array | Buffer): Promise<ParseResult>;
|
|
|
85
85
|
* @param bytes - Statement bytes to identify.
|
|
86
86
|
* @returns Confidence in {0, 50, 100}: 0 = no match, 50 = partial, 100 = definite.
|
|
87
87
|
*/
|
|
88
|
-
export declare function detect(bytes: Uint8Array
|
|
88
|
+
export declare function detect(bytes: Uint8Array): Promise<number>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firela/pii-redactor",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "firela parser/sanitizer WASM module — isomorphic Node.js + Cloudflare Worker loader (ADR-0101 compiled-only).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index-node.cjs",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
10
|
"node": {
|
|
11
|
-
"require": "./index-node.cjs"
|
|
11
|
+
"require": "./index-node.cjs",
|
|
12
|
+
"import": "./index-node.mjs"
|
|
12
13
|
},
|
|
13
14
|
"worker": "./index-worker.js",
|
|
14
15
|
"default": "./index-worker.js"
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
},
|
|
18
19
|
"files": [
|
|
19
20
|
"index-node.cjs",
|
|
21
|
+
"index-node.mjs",
|
|
20
22
|
"index-worker.js",
|
|
21
23
|
"index.d.ts",
|
|
22
24
|
"pii_redactor.opt.wasm"
|