@stll/anonymize-wasm 2.8.2 → 2.9.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 +13 -4
- package/dist/native/index_bg.wasm +0 -0
- package/dist/native/native-pipeline.cs.stlanonpkg +0 -0
- package/dist/native/native-pipeline.de.stlanonpkg +0 -0
- package/dist/native/native-pipeline.en.stlanonpkg +0 -0
- package/dist/native/native-pipeline.stlanonpkg +0 -0
- package/dist/wasm.d.mts +16 -1
- package/dist/wasm.mjs +481 -72
- package/dist/wasm.mjs.map +1 -1
- package/package.json +4 -1
package/dist/wasm.mjs
CHANGED
|
@@ -69,6 +69,11 @@ const isNativeAnonymizeBinding = (candidate) => {
|
|
|
69
69
|
return isBindingPropertyBag(preparedSearch) && NATIVE_BINDING_PARITY_MEMBERS.factories.every((name) => typeof preparedSearch[name] === "function");
|
|
70
70
|
};
|
|
71
71
|
const CALLER_DETECTION_CONTRACT_VERSION = 2;
|
|
72
|
+
const CALLER_DETECTION_MAX_COUNT = 1e6;
|
|
73
|
+
const CALLER_DETECTION_TEXT_MAX_BYTES = 64 * 1024 * 1024;
|
|
74
|
+
const CALLER_DETECTION_REQUEST_JSON_MAX_BYTES = 16 * 1024 * 1024;
|
|
75
|
+
const SESSION_CALLER_MAX_INPUTS = 1e5;
|
|
76
|
+
const SESSION_CALLER_INPUTS_JSON_MAX_BYTES = 64 * 1024 * 1024;
|
|
72
77
|
const EXTERNAL_DETECTION_BATCH_VERSION = 1;
|
|
73
78
|
const EXTERNAL_DETECTION_BATCH_MAX_BYTES = 16 * 1024 * 1024;
|
|
74
79
|
const EXTERNAL_DETECTION_DOCUMENT_MAX_BYTES = 64 * 1024 * 1024;
|
|
@@ -84,17 +89,184 @@ const EXTERNAL_DETECTION_OFFSET_UNITS = {
|
|
|
84
89
|
const convert_external_detection_batch$1 = ({ binding, document, batch }) => {
|
|
85
90
|
return binding.convertExternalDetectionBatch(document, typeof batch === "string" ? batch : JSON.stringify(batch));
|
|
86
91
|
};
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
92
|
+
const utf8ByteLengthWithin = (text, maximum) => {
|
|
93
|
+
let bytes = 0;
|
|
94
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
95
|
+
const unit = text.charCodeAt(index);
|
|
96
|
+
if (unit <= 127) bytes += 1;
|
|
97
|
+
else if (unit <= 2047) bytes += 2;
|
|
98
|
+
else if (unit >= 55296 && unit <= 56319 && index + 1 < text.length && text.charCodeAt(index + 1) >= 56320 && text.charCodeAt(index + 1) <= 57343) {
|
|
99
|
+
bytes += 4;
|
|
100
|
+
index += 1;
|
|
101
|
+
} else bytes += 3;
|
|
102
|
+
if (bytes > maximum) return;
|
|
103
|
+
}
|
|
104
|
+
return bytes;
|
|
105
|
+
};
|
|
106
|
+
const validateCallerDetectionInput = (fullText, detections) => {
|
|
107
|
+
if (!Array.isArray(detections)) throw new TypeError("Caller detections must be an array");
|
|
108
|
+
if (detections.length > 1e6) throw new RangeError(`Caller detections contains ${detections.length} items; the maximum is ${CALLER_DETECTION_MAX_COUNT}`);
|
|
109
|
+
const textBytes = utf8ByteLengthWithin(fullText, CALLER_DETECTION_TEXT_MAX_BYTES);
|
|
110
|
+
if (textBytes === void 0) throw new RangeError(`Caller detection text exceeds the ${CALLER_DETECTION_TEXT_MAX_BYTES}-byte maximum`);
|
|
111
|
+
return textBytes;
|
|
112
|
+
};
|
|
113
|
+
var BoundedJsonSink = class {
|
|
114
|
+
#maximumBytes;
|
|
115
|
+
#label;
|
|
116
|
+
#reportedMaximumBytes;
|
|
117
|
+
#bytes = 0;
|
|
118
|
+
constructor(maximumBytes, label, suffix) {
|
|
119
|
+
this.#maximumBytes = maximumBytes - suffix.length;
|
|
120
|
+
this.#label = label;
|
|
121
|
+
this.#reportedMaximumBytes = maximumBytes;
|
|
122
|
+
}
|
|
123
|
+
appendAscii(value) {
|
|
124
|
+
this.#reserve(value.length);
|
|
125
|
+
this.capture(value);
|
|
126
|
+
}
|
|
127
|
+
appendOffset(value, field) {
|
|
128
|
+
this.#requireNumber(value, field);
|
|
129
|
+
if (!Number.isInteger(value) || value < 0 || value > 4294967295) throw new RangeError(`${field} must be an integer between 0 and 4294967295`);
|
|
130
|
+
this.#appendNumber(value);
|
|
131
|
+
}
|
|
132
|
+
appendScore(value, field) {
|
|
133
|
+
this.#requireNumber(value, field);
|
|
134
|
+
if (!Number.isFinite(value) || value < 0 || value > 1) throw new RangeError(`${field} must be finite and between 0 and 1`);
|
|
135
|
+
this.#appendNumber(value);
|
|
136
|
+
}
|
|
137
|
+
appendString(value, field) {
|
|
138
|
+
if (typeof value !== "string") throw new TypeError(`${field} must be a string`);
|
|
139
|
+
this.appendAscii("\"");
|
|
140
|
+
let runStart = 0;
|
|
141
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
142
|
+
const unit = value.charCodeAt(index);
|
|
143
|
+
const escape = jsonEscape(unit);
|
|
144
|
+
if (escape !== void 0) {
|
|
145
|
+
this.#appendReservedRun(value, runStart, index);
|
|
146
|
+
this.appendAscii(escape);
|
|
147
|
+
runStart = index + 1;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (unit >= 55296 && unit <= 56319 && index + 1 < value.length && value.charCodeAt(index + 1) >= 56320 && value.charCodeAt(index + 1) <= 57343) {
|
|
151
|
+
this.#reserve(4);
|
|
152
|
+
index += 1;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
if (unit >= 55296 && unit <= 57343) {
|
|
156
|
+
this.#appendReservedRun(value, runStart, index);
|
|
157
|
+
this.appendAscii(`\\u${unit.toString(16).padStart(4, "0")}`);
|
|
158
|
+
runStart = index + 1;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
let unitBytes = 3;
|
|
162
|
+
if (unit <= 127) unitBytes = 1;
|
|
163
|
+
else if (unit <= 2047) unitBytes = 2;
|
|
164
|
+
this.#reserve(unitBytes);
|
|
165
|
+
}
|
|
166
|
+
this.#appendReservedRun(value, runStart, value.length);
|
|
167
|
+
this.appendAscii("\"");
|
|
168
|
+
}
|
|
169
|
+
#appendReservedRun(value, start, end) {
|
|
170
|
+
if (end > start) this.capture(value.slice(start, end));
|
|
171
|
+
}
|
|
172
|
+
#appendNumber(value) {
|
|
173
|
+
this.appendAscii(JSON.stringify(value));
|
|
174
|
+
}
|
|
175
|
+
#requireNumber(value, field) {
|
|
176
|
+
if (typeof value !== "number") throw new TypeError(`${field} must be a number`);
|
|
177
|
+
}
|
|
178
|
+
#reserve(bytes) {
|
|
179
|
+
if (bytes > this.#maximumBytes - this.#bytes) throw new RangeError(`${this.#label} exceeds the ${this.#reportedMaximumBytes}-byte maximum`);
|
|
180
|
+
this.#bytes += bytes;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
var CountingJsonBudget = class extends BoundedJsonSink {
|
|
184
|
+
capture(value) {}
|
|
185
|
+
};
|
|
186
|
+
var BoundedJsonWriter = class extends BoundedJsonSink {
|
|
187
|
+
#chunks = [];
|
|
188
|
+
#suffix;
|
|
189
|
+
constructor(maximumBytes, label, suffix) {
|
|
190
|
+
super(maximumBytes, label, suffix);
|
|
191
|
+
this.#suffix = suffix;
|
|
192
|
+
}
|
|
193
|
+
finish() {
|
|
194
|
+
return this.#chunks.join("") + this.#suffix;
|
|
195
|
+
}
|
|
196
|
+
capture(value) {
|
|
197
|
+
this.#chunks.push(value);
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
const jsonEscape = (unit) => {
|
|
201
|
+
switch (unit) {
|
|
202
|
+
case 8: return "\\b";
|
|
203
|
+
case 9: return "\\t";
|
|
204
|
+
case 10: return "\\n";
|
|
205
|
+
case 12: return "\\f";
|
|
206
|
+
case 13: return "\\r";
|
|
207
|
+
case 34: return "\\\"";
|
|
208
|
+
case 92: return "\\\\";
|
|
209
|
+
default: return unit < 32 ? `\\u${unit.toString(16).padStart(4, "0")}` : void 0;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
const callerDetectionRequestJson = (fullText, detections) => {
|
|
213
|
+
validateCallerDetectionInput(fullText, detections);
|
|
214
|
+
return serializeCallerDetectionRequest(detections);
|
|
215
|
+
};
|
|
216
|
+
const serializeCallerDetectionRequest = (detections) => {
|
|
217
|
+
const writer = new BoundedJsonWriter(CALLER_DETECTION_REQUEST_JSON_MAX_BYTES, "Caller detection request JSON", "]}");
|
|
218
|
+
writer.appendAscii(`{"version":2,"detections":[`);
|
|
219
|
+
for (let index = 0; index < detections.length; index += 1) {
|
|
220
|
+
const detection = detections[index];
|
|
221
|
+
if (detection === void 0) throw new TypeError("Caller detections must not be sparse");
|
|
222
|
+
if (index > 0) writer.appendAscii(",");
|
|
223
|
+
writer.appendAscii("{\"start\":");
|
|
224
|
+
writer.appendOffset(detection.start, "Caller detection start");
|
|
225
|
+
writer.appendAscii(",\"end\":");
|
|
226
|
+
writer.appendOffset(detection.end, "Caller detection end");
|
|
227
|
+
writer.appendAscii(",\"label\":");
|
|
228
|
+
writer.appendString(detection.label, "Caller detection label");
|
|
229
|
+
writer.appendAscii(",\"score\":");
|
|
230
|
+
writer.appendScore(detection.score, "Caller detection score");
|
|
231
|
+
writer.appendAscii(",\"provider_id\":");
|
|
232
|
+
writer.appendString(detection.providerId, "Caller detection providerId");
|
|
233
|
+
writer.appendAscii(",\"detection_id\":");
|
|
234
|
+
writer.appendString(detection.detectionId, "Caller detection detectionId");
|
|
235
|
+
writer.appendAscii("}");
|
|
236
|
+
}
|
|
237
|
+
return writer.finish();
|
|
238
|
+
};
|
|
239
|
+
const toBindingSessionCallerInputs = (inputs) => {
|
|
240
|
+
if (!Array.isArray(inputs)) throw new TypeError("Session caller inputs must be an array");
|
|
241
|
+
if (inputs.length > 1e5) throw new RangeError(`Session caller inputs contains ${inputs.length} items; the maximum is ${SESSION_CALLER_MAX_INPUTS}`);
|
|
242
|
+
let detectionCount = 0;
|
|
243
|
+
let textBytes = 0;
|
|
244
|
+
const bindingInputs = [];
|
|
245
|
+
const budget = new CountingJsonBudget(SESSION_CALLER_INPUTS_JSON_MAX_BYTES, "Session caller inputs JSON", "]");
|
|
246
|
+
budget.appendAscii("[");
|
|
247
|
+
for (let index = 0; index < inputs.length; index += 1) {
|
|
248
|
+
const input = inputs[index];
|
|
249
|
+
if (input === void 0) throw new TypeError("Session caller inputs must not be sparse");
|
|
250
|
+
const { detections, fullText } = input;
|
|
251
|
+
const inputTextBytes = validateCallerDetectionInput(fullText, detections);
|
|
252
|
+
detectionCount += detections.length;
|
|
253
|
+
if (detectionCount > 1e6) throw new RangeError(`Session caller detections contains ${detectionCount} items; the maximum is ${CALLER_DETECTION_MAX_COUNT}`);
|
|
254
|
+
textBytes += inputTextBytes;
|
|
255
|
+
if (textBytes > 67108864) throw new RangeError(`Session caller text contains ${textBytes} bytes; the maximum is ${CALLER_DETECTION_TEXT_MAX_BYTES}`);
|
|
256
|
+
const requestJson = serializeCallerDetectionRequest(detections);
|
|
257
|
+
if (index > 0) budget.appendAscii(",");
|
|
258
|
+
budget.appendAscii("{\"full_text\":");
|
|
259
|
+
budget.appendString(fullText, "Session caller fullText");
|
|
260
|
+
budget.appendAscii(",\"request_json\":");
|
|
261
|
+
budget.appendString(requestJson, "Session caller requestJson");
|
|
262
|
+
budget.appendAscii("}");
|
|
263
|
+
bindingInputs.push({
|
|
264
|
+
fullText,
|
|
265
|
+
requestJson
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return bindingInputs;
|
|
269
|
+
};
|
|
98
270
|
var PreparedNativeRedactionSession = class {
|
|
99
271
|
#session;
|
|
100
272
|
constructor(session) {
|
|
@@ -198,10 +370,7 @@ var PreparedNativeRedactionSession = class {
|
|
|
198
370
|
planTextBatchWithCallerDetections({ inputs, operators, observedAtEpochSeconds }) {
|
|
199
371
|
const bindingOperators = toBindingOperatorConfig(operators);
|
|
200
372
|
return new PreparedNativeSessionRedactionPlan(this.#session.planStaticEntitiesWithCallerDetections({
|
|
201
|
-
inputs: inputs
|
|
202
|
-
fullText,
|
|
203
|
-
requestJson: callerDetectionRequestJson(detections)
|
|
204
|
-
})),
|
|
373
|
+
inputs: toBindingSessionCallerInputs(inputs),
|
|
205
374
|
...bindingOperators === void 0 ? {} : { operators: bindingOperators },
|
|
206
375
|
...observedAtEpochSeconds === void 0 ? {} : { observedAtEpochSeconds }
|
|
207
376
|
}));
|
|
@@ -286,7 +455,7 @@ var PreparedNativeAnonymizer = class {
|
|
|
286
455
|
return this.#prepared.redactStaticEntitiesJson(fullText, bindingOperators);
|
|
287
456
|
}
|
|
288
457
|
redactStaticEntitiesWithCallerDetections(fullText, options) {
|
|
289
|
-
const requestJson = callerDetectionRequestJson(options.detections);
|
|
458
|
+
const requestJson = callerDetectionRequestJson(fullText, options.detections);
|
|
290
459
|
const operators = toBindingOperatorConfig(options.operators);
|
|
291
460
|
const result = JSON.parse(this.#prepared.redactStaticEntitiesWithCallerDetectionsJson(fullText, {
|
|
292
461
|
requestJson,
|
|
@@ -298,7 +467,7 @@ var PreparedNativeAnonymizer = class {
|
|
|
298
467
|
return this.redactStaticEntitiesWithCallerDetections(fullText, options);
|
|
299
468
|
}
|
|
300
469
|
redactStaticEntitiesWithCallerDetectionsDiagnosticsJson(fullText, options) {
|
|
301
|
-
const requestJson = callerDetectionRequestJson(options.detections);
|
|
470
|
+
const requestJson = callerDetectionRequestJson(fullText, options.detections);
|
|
302
471
|
const operators = toBindingOperatorConfig(options.operators);
|
|
303
472
|
return this.#prepared.redactStaticEntitiesWithCallerDetectionsDiagnosticsJson(fullText, {
|
|
304
473
|
requestJson,
|
|
@@ -628,10 +797,15 @@ const wrapSession = (raw) => ({
|
|
|
628
797
|
deleteJson: raw.deleteJson.bind(raw),
|
|
629
798
|
redactStaticEntitiesJson: (fullText, operators) => raw.redactStaticEntitiesJson(fullText, json(operators)),
|
|
630
799
|
redactStaticEntitiesJsonAt: (fullText, observedAtEpochSeconds, operators) => raw.redactStaticEntitiesJsonAt(fullText, observedAtEpochSeconds, json(operators)),
|
|
631
|
-
planStaticEntitiesWithCallerDetections: ({ inputs, operators, observedAtEpochSeconds }) =>
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
800
|
+
planStaticEntitiesWithCallerDetections: ({ inputs, operators, observedAtEpochSeconds }) => {
|
|
801
|
+
const inputsJson = JSON.stringify(inputs.map(({ fullText, requestJson }) => ({
|
|
802
|
+
full_text: fullText,
|
|
803
|
+
request_json: requestJson
|
|
804
|
+
})));
|
|
805
|
+
const inputsJsonBytes = new TextEncoder().encode(inputsJson).byteLength;
|
|
806
|
+
if (inputsJsonBytes > 67108864) throw new RangeError(`Session caller inputs JSON contains ${inputsJsonBytes} bytes; the maximum is ${SESSION_CALLER_INPUTS_JSON_MAX_BYTES}`);
|
|
807
|
+
return raw.planStaticEntitiesWithCallerDetections(inputsJson, json(operators), observedAtEpochSeconds);
|
|
808
|
+
}
|
|
635
809
|
});
|
|
636
810
|
const json = (value) => value === void 0 ? void 0 : JSON.stringify(value);
|
|
637
811
|
const canonicalResult = (result) => ({
|
|
@@ -664,50 +838,62 @@ const assertPdfPixelPages = (pagePixels) => {
|
|
|
664
838
|
for (const [index, page] of pagePixels.entries()) if (!(page instanceof Uint8Array)) throw new TypeError(`PDF pagePixels[${index}] must be a Uint8Array`);
|
|
665
839
|
};
|
|
666
840
|
//#endregion
|
|
667
|
-
//#region src/
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
841
|
+
//#region src/data/language-scopes.json
|
|
842
|
+
var language_scopes_default = {
|
|
843
|
+
_comment: "Default dictionary scopes for content language hints. The all-language city scope preserves the data package defaults and covers every supported language. Lower-level caller config can still override name corpus languages and deny-list countries independently.",
|
|
844
|
+
allLanguageCityCountries: [
|
|
845
|
+
"AR",
|
|
846
|
+
"AT",
|
|
847
|
+
"AU",
|
|
848
|
+
"BE",
|
|
849
|
+
"BG",
|
|
850
|
+
"BO",
|
|
851
|
+
"BR",
|
|
852
|
+
"CA",
|
|
853
|
+
"CH",
|
|
854
|
+
"CL",
|
|
855
|
+
"CO",
|
|
856
|
+
"CR",
|
|
857
|
+
"CU",
|
|
858
|
+
"CZ",
|
|
859
|
+
"DE",
|
|
860
|
+
"DK",
|
|
861
|
+
"DO",
|
|
862
|
+
"EC",
|
|
863
|
+
"ES",
|
|
864
|
+
"FI",
|
|
865
|
+
"FR",
|
|
866
|
+
"GB",
|
|
867
|
+
"GR",
|
|
868
|
+
"GT",
|
|
869
|
+
"HN",
|
|
870
|
+
"HR",
|
|
871
|
+
"HU",
|
|
872
|
+
"IE",
|
|
873
|
+
"IT",
|
|
874
|
+
"LU",
|
|
875
|
+
"LV",
|
|
876
|
+
"MC",
|
|
877
|
+
"MD",
|
|
878
|
+
"MX",
|
|
879
|
+
"NI",
|
|
880
|
+
"NL",
|
|
881
|
+
"NO",
|
|
882
|
+
"NZ",
|
|
883
|
+
"PA",
|
|
884
|
+
"PE",
|
|
885
|
+
"PL",
|
|
886
|
+
"PT",
|
|
887
|
+
"PY",
|
|
888
|
+
"RO",
|
|
889
|
+
"SE",
|
|
890
|
+
"SI",
|
|
891
|
+
"SK",
|
|
892
|
+
"SV",
|
|
893
|
+
"US",
|
|
894
|
+
"UY",
|
|
895
|
+
"VE"
|
|
896
|
+
],
|
|
711
897
|
languages: {
|
|
712
898
|
"cs": {
|
|
713
899
|
"nameCorpusLanguages": ["cs", "sk"],
|
|
@@ -800,7 +986,10 @@ const scopeData = {
|
|
|
800
986
|
}
|
|
801
987
|
}
|
|
802
988
|
};
|
|
803
|
-
|
|
989
|
+
//#endregion
|
|
990
|
+
//#region src/language-scope.ts
|
|
991
|
+
const scopeData = language_scopes_default;
|
|
992
|
+
const normalizeLanguage$2 = (language) => language.trim().toLowerCase();
|
|
804
993
|
const fallbackLanguage = (language) => {
|
|
805
994
|
const index = language.indexOf("-");
|
|
806
995
|
return index === -1 ? null : language.slice(0, index);
|
|
@@ -814,7 +1003,7 @@ const uniquePush = (target, values) => {
|
|
|
814
1003
|
}
|
|
815
1004
|
};
|
|
816
1005
|
const resolveLanguageScope = (language) => {
|
|
817
|
-
const normalized = normalizeLanguage$
|
|
1006
|
+
const normalized = normalizeLanguage$2(language);
|
|
818
1007
|
if (normalized.length === 0) return null;
|
|
819
1008
|
const exact = scopeData.languages[normalized];
|
|
820
1009
|
if (exact !== void 0) return exact;
|
|
@@ -830,21 +1019,50 @@ const applyPipelineLanguageScope = (config) => {
|
|
|
830
1019
|
if (languages.length === 0) return config;
|
|
831
1020
|
const nameCorpusLanguages = [];
|
|
832
1021
|
const denyListCountries = [];
|
|
1022
|
+
let hasResolvedScope = false;
|
|
833
1023
|
for (const language of languages) {
|
|
834
1024
|
const scope = resolveLanguageScope(language);
|
|
835
1025
|
if (scope === null) continue;
|
|
1026
|
+
hasResolvedScope = true;
|
|
836
1027
|
uniquePush(nameCorpusLanguages, scope.nameCorpusLanguages ?? []);
|
|
837
1028
|
uniquePush(denyListCountries, scope.denyListCountries ?? []);
|
|
838
1029
|
}
|
|
839
1030
|
const next = {};
|
|
840
|
-
if (config.nameCorpusLanguages === void 0 &&
|
|
841
|
-
if (config.denyListCountries === void 0 &&
|
|
1031
|
+
if (config.nameCorpusLanguages === void 0 && hasResolvedScope) next.nameCorpusLanguages = nameCorpusLanguages;
|
|
1032
|
+
if (config.denyListCountries === void 0 && hasResolvedScope) next.denyListCountries = denyListCountries;
|
|
842
1033
|
return Object.keys(next).length === 0 ? config : {
|
|
843
1034
|
...config,
|
|
844
1035
|
...next
|
|
845
1036
|
};
|
|
846
1037
|
};
|
|
847
1038
|
//#endregion
|
|
1039
|
+
//#region src/build-native-package.ts
|
|
1040
|
+
const EMPTY_NAME_CORPUS_SCOPE = ["und"];
|
|
1041
|
+
const defaultDictionaryBundleOptions = (config) => ({
|
|
1042
|
+
...config.denyListCountries === void 0 ? { cityCountries: language_scopes_default.allLanguageCityCountries } : {
|
|
1043
|
+
countries: config.denyListCountries,
|
|
1044
|
+
cityCountries: config.denyListCountries
|
|
1045
|
+
},
|
|
1046
|
+
...config.nameCorpusLanguages === void 0 ? {} : { nameLanguages: config.nameCorpusLanguages.length === 0 ? EMPTY_NAME_CORPUS_SCOPE : config.nameCorpusLanguages }
|
|
1047
|
+
});
|
|
1048
|
+
//#endregion
|
|
1049
|
+
//#region src/context.ts
|
|
1050
|
+
/** Create a fresh, empty pipeline context. */
|
|
1051
|
+
const createPipelineContext = () => ({
|
|
1052
|
+
nativePipelinePackage: null,
|
|
1053
|
+
nativePipelinePackageKey: "",
|
|
1054
|
+
nativePipelinePackagePromise: null
|
|
1055
|
+
});
|
|
1056
|
+
/**
|
|
1057
|
+
* Module-level default context. Used when callers
|
|
1058
|
+
* don't provide an explicit context, preserving full
|
|
1059
|
+
* backward compatibility with the existing API.
|
|
1060
|
+
*/
|
|
1061
|
+
const defaultContext = createPipelineContext();
|
|
1062
|
+
//#endregion
|
|
1063
|
+
//#region src/types.ts
|
|
1064
|
+
const isLegalFormsEnabled = (config) => config.enableLegalForms !== false;
|
|
1065
|
+
//#endregion
|
|
848
1066
|
//#region src/util/language-selection.ts
|
|
849
1067
|
const normalizeLanguageCode = (language) => language.trim().toLowerCase();
|
|
850
1068
|
const normalizeLanguageSelection = (languages) => languages === void 0 ? [] : languages.map(normalizeLanguageCode).filter((language) => language.length > 0);
|
|
@@ -1034,6 +1252,141 @@ const nativePackageCacheKey = ({ binding, config, gazetteerEntries, compressed }
|
|
|
1034
1252
|
pipelineConfigKey(config, gazetteerEntries)
|
|
1035
1253
|
].join(":");
|
|
1036
1254
|
//#endregion
|
|
1255
|
+
//#region src/native-default-config.ts
|
|
1256
|
+
const DEFAULT_NATIVE_PIPELINE_CONFIG = {
|
|
1257
|
+
threshold: .3,
|
|
1258
|
+
enableTriggerPhrases: true,
|
|
1259
|
+
enableRegex: true,
|
|
1260
|
+
enableLegalForms: true,
|
|
1261
|
+
enableNameCorpus: true,
|
|
1262
|
+
enableDenyList: true,
|
|
1263
|
+
enableGazetteer: false,
|
|
1264
|
+
enableCountries: true,
|
|
1265
|
+
enableConfidenceBoost: true,
|
|
1266
|
+
enableCoreference: true,
|
|
1267
|
+
enableHotwordRules: true,
|
|
1268
|
+
enableZoneClassification: true,
|
|
1269
|
+
standaloneStreetDetection: "off",
|
|
1270
|
+
labels: [...DEFAULT_ENTITY_LABELS],
|
|
1271
|
+
workspaceId: "native-pipeline-default"
|
|
1272
|
+
};
|
|
1273
|
+
//#endregion
|
|
1274
|
+
//#region src/pipeline-language.ts
|
|
1275
|
+
const isSupportedLanguage = (language) => Object.hasOwn(language_scopes_default.languages, language);
|
|
1276
|
+
const SUPPORTED_LANGUAGES = Object.freeze(Object.keys(language_scopes_default.languages).filter(isSupportedLanguage).toSorted());
|
|
1277
|
+
const normalizeLanguage$1 = (language) => {
|
|
1278
|
+
if (typeof language !== "string") throw new TypeError("Pipeline language codes must be strings");
|
|
1279
|
+
const normalized = language.trim().toLowerCase();
|
|
1280
|
+
if (!isSupportedLanguage(normalized)) throw new RangeError(`Unsupported pipeline language ${JSON.stringify(language)}; expected one of: ${SUPPORTED_LANGUAGES.join(", ")}`);
|
|
1281
|
+
return normalized;
|
|
1282
|
+
};
|
|
1283
|
+
const normalizePipelineLanguageSelection = (selection) => {
|
|
1284
|
+
if (selection === void 0 || typeof selection === "string" && selection.trim().toLowerCase() === "all") return { type: "all" };
|
|
1285
|
+
const requested = Array.isArray(selection) ? selection : [selection];
|
|
1286
|
+
if (requested.length === 0) throw new RangeError("Pipeline language selection must not be empty");
|
|
1287
|
+
const normalized = [...new Set(requested.map(normalizeLanguage$1))].toSorted();
|
|
1288
|
+
const first = normalized.at(0);
|
|
1289
|
+
if (first === void 0) throw new RangeError("Pipeline language selection must not be empty");
|
|
1290
|
+
return {
|
|
1291
|
+
type: "languages",
|
|
1292
|
+
languages: [first, ...normalized.slice(1)]
|
|
1293
|
+
};
|
|
1294
|
+
};
|
|
1295
|
+
const pipelineLanguageSelectionKey = (selection) => selection.type === "all" ? "all" : selection.languages.join(",");
|
|
1296
|
+
//#endregion
|
|
1297
|
+
//#region src/create-pipeline.ts
|
|
1298
|
+
const dictionaryCache = /* @__PURE__ */ new Map();
|
|
1299
|
+
const semanticPipelineCache = /* @__PURE__ */ new WeakMap();
|
|
1300
|
+
const MAX_SEMANTIC_PIPELINE_CACHE_ENTRIES = 8;
|
|
1301
|
+
const getCachedEntry = (cache, key) => {
|
|
1302
|
+
const cached = cache.get(key);
|
|
1303
|
+
if (cached === void 0) return;
|
|
1304
|
+
cache.delete(key);
|
|
1305
|
+
cache.set(key, cached);
|
|
1306
|
+
return cached;
|
|
1307
|
+
};
|
|
1308
|
+
const setCachedEntry = (cache, key, value) => {
|
|
1309
|
+
cache.set(key, value);
|
|
1310
|
+
if (cache.size <= MAX_SEMANTIC_PIPELINE_CACHE_ENTRIES) return;
|
|
1311
|
+
const oldestKey = cache.keys().next().value;
|
|
1312
|
+
if (oldestKey !== void 0) cache.delete(oldestKey);
|
|
1313
|
+
};
|
|
1314
|
+
const loadSemanticDictionaries = (key, config) => {
|
|
1315
|
+
const cached = getCachedEntry(dictionaryCache, key);
|
|
1316
|
+
if (cached !== void 0) return cached;
|
|
1317
|
+
let dictionaries;
|
|
1318
|
+
dictionaries = import("@stll/anonymize-data/cities").then(({ loadDictionaryBundle }) => loadDictionaryBundle(defaultDictionaryBundleOptions(config))).catch((error) => {
|
|
1319
|
+
if (dictionaryCache.get(key) === dictionaries) dictionaryCache.delete(key);
|
|
1320
|
+
throw error;
|
|
1321
|
+
});
|
|
1322
|
+
setCachedEntry(dictionaryCache, key, dictionaries);
|
|
1323
|
+
return dictionaries;
|
|
1324
|
+
};
|
|
1325
|
+
const pipelineConfigFor = (selection) => {
|
|
1326
|
+
if (selection.type === "all") return {
|
|
1327
|
+
...DEFAULT_NATIVE_PIPELINE_CONFIG,
|
|
1328
|
+
labels: [...DEFAULT_NATIVE_PIPELINE_CONFIG.labels]
|
|
1329
|
+
};
|
|
1330
|
+
const [language, ...languages] = selection.languages;
|
|
1331
|
+
return applyPipelineLanguageScope({
|
|
1332
|
+
...DEFAULT_NATIVE_PIPELINE_CONFIG,
|
|
1333
|
+
labels: [...DEFAULT_NATIVE_PIPELINE_CONFIG.labels],
|
|
1334
|
+
workspaceId: `default-pipeline:${pipelineLanguageSelectionKey(selection)}`,
|
|
1335
|
+
...languages.length === 0 ? { language } : { languages: [language, ...languages] }
|
|
1336
|
+
});
|
|
1337
|
+
};
|
|
1338
|
+
const semanticPipelineCacheFor = (binding) => {
|
|
1339
|
+
const cached = semanticPipelineCache.get(binding);
|
|
1340
|
+
if (cached !== void 0) return cached;
|
|
1341
|
+
const created = /* @__PURE__ */ new Map();
|
|
1342
|
+
semanticPipelineCache.set(binding, created);
|
|
1343
|
+
return created;
|
|
1344
|
+
};
|
|
1345
|
+
const createSemanticPipeline = ({ binding, selection }) => {
|
|
1346
|
+
const key = pipelineLanguageSelectionKey(selection);
|
|
1347
|
+
const cache = semanticPipelineCacheFor(binding);
|
|
1348
|
+
const cached = getCachedEntry(cache, key);
|
|
1349
|
+
if (cached !== void 0) return cached;
|
|
1350
|
+
const config = pipelineConfigFor(selection);
|
|
1351
|
+
let pipeline;
|
|
1352
|
+
pipeline = loadSemanticDictionaries(key, config).then((dictionaries) => createNativePipelineFromConfig({
|
|
1353
|
+
binding,
|
|
1354
|
+
config: {
|
|
1355
|
+
...config,
|
|
1356
|
+
dictionaries
|
|
1357
|
+
}
|
|
1358
|
+
})).catch((error) => {
|
|
1359
|
+
if (cache.get(key) === pipeline) cache.delete(key);
|
|
1360
|
+
throw error;
|
|
1361
|
+
});
|
|
1362
|
+
setCachedEntry(cache, key, pipeline);
|
|
1363
|
+
return pipeline;
|
|
1364
|
+
};
|
|
1365
|
+
//#endregion
|
|
1366
|
+
//#region src/redact.ts
|
|
1367
|
+
/**
|
|
1368
|
+
* Serialize the redaction key to JSON for export.
|
|
1369
|
+
* Includes operator metadata so the export is self-describing.
|
|
1370
|
+
*/
|
|
1371
|
+
const exportRedactionKey = (redactionMap, operatorMap) => {
|
|
1372
|
+
const entries = {};
|
|
1373
|
+
for (const [placeholder, value] of redactionMap) entries[placeholder] = {
|
|
1374
|
+
original: value,
|
|
1375
|
+
operator: operatorMap.get(placeholder) ?? "replace"
|
|
1376
|
+
};
|
|
1377
|
+
return JSON.stringify({ entries }, null, 2);
|
|
1378
|
+
};
|
|
1379
|
+
/**
|
|
1380
|
+
* De-anonymise text using a redaction key.
|
|
1381
|
+
* Replaces placeholders back with original values.
|
|
1382
|
+
* Only works for reversible operators (replace).
|
|
1383
|
+
*/
|
|
1384
|
+
const deanonymise = (redactedText, redactionMap) => {
|
|
1385
|
+
let result = redactedText;
|
|
1386
|
+
for (const [placeholder, original] of redactionMap) result = result.replaceAll(placeholder, original);
|
|
1387
|
+
return result;
|
|
1388
|
+
};
|
|
1389
|
+
//#endregion
|
|
1037
1390
|
//#region src/wasm.ts
|
|
1038
1391
|
const GLUE_MODULE = "index.js";
|
|
1039
1392
|
const WASM_MODULE = "index_bg.wasm";
|
|
@@ -1042,10 +1395,18 @@ const NATIVE_ASSET_DIR = "native";
|
|
|
1042
1395
|
const ASSET_DIR_ENV = "STLL_ANONYMIZE_ASSET_DIR";
|
|
1043
1396
|
const DEFAULT_PACKAGE_FILE = "native-pipeline.stlanonpkg";
|
|
1044
1397
|
const LANGUAGE_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
1398
|
+
const HTML_MEDIA_TYPES = /* @__PURE__ */ new Set(["application/xhtml+xml", "text/html"]);
|
|
1045
1399
|
const DEFAULT_PIPELINE_CACHE_KEY = "<default>";
|
|
1046
1400
|
const DEFAULT_PIPELINE_CACHE_MAX_ENTRIES = 32;
|
|
1047
1401
|
let bindingPromise;
|
|
1048
1402
|
const defaultPipelineCache = /* @__PURE__ */ new Map();
|
|
1403
|
+
const unavailablePackageUrls = /* @__PURE__ */ new Set();
|
|
1404
|
+
var PreparedPackageUnavailableError = class extends Error {
|
|
1405
|
+
constructor(href, options) {
|
|
1406
|
+
super(`Prepared package is unavailable: ${href}`, options);
|
|
1407
|
+
this.name = "PreparedPackageUnavailableError";
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1049
1410
|
/** Instantiate (once) and return the wasm binding. Safe to call repeatedly:
|
|
1050
1411
|
* the underlying wasm module is instantiated a single time and cached. */
|
|
1051
1412
|
const getBinding = () => {
|
|
@@ -1098,11 +1459,27 @@ const toPackageBytes = async (source) => {
|
|
|
1098
1459
|
if (source instanceof Uint8Array) return source;
|
|
1099
1460
|
if (source instanceof ArrayBuffer) return new Uint8Array(source);
|
|
1100
1461
|
const href = source instanceof URL ? source.href : source;
|
|
1101
|
-
if (href.startsWith("file:"))
|
|
1462
|
+
if (href.startsWith("file:")) try {
|
|
1463
|
+
return await readFileUrlBytes(href);
|
|
1464
|
+
} catch (error) {
|
|
1465
|
+
if (isMissingFileError(error)) throw new PreparedPackageUnavailableError(href, { cause: error });
|
|
1466
|
+
throw error;
|
|
1467
|
+
}
|
|
1102
1468
|
const response = await fetch(href);
|
|
1103
|
-
if (!response.ok)
|
|
1469
|
+
if (!response.ok) {
|
|
1470
|
+
if (response.status === 404) throw new PreparedPackageUnavailableError(href);
|
|
1471
|
+
throw new Error(`Failed to fetch prepared package (${response.status} ${response.statusText})`);
|
|
1472
|
+
}
|
|
1473
|
+
if (isHtmlResponse(response)) throw new PreparedPackageUnavailableError(href);
|
|
1104
1474
|
return new Uint8Array(await response.arrayBuffer());
|
|
1105
1475
|
};
|
|
1476
|
+
const isHtmlResponse = (response) => {
|
|
1477
|
+
const contentType = response.headers.get("content-type");
|
|
1478
|
+
if (contentType === null) return false;
|
|
1479
|
+
const mediaType = contentType.split(";", 1).at(0)?.trim().toLowerCase();
|
|
1480
|
+
return mediaType !== void 0 && HTML_MEDIA_TYPES.has(mediaType);
|
|
1481
|
+
};
|
|
1482
|
+
const isMissingFileError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
1106
1483
|
/** Read a `file:` URL through node:fs. The import is dynamic and gated behind
|
|
1107
1484
|
* the `file:` check (never reached in browsers); the specifier is a runtime
|
|
1108
1485
|
* value so the bundler leaves it alone, mirroring the runtime glue import in
|
|
@@ -1143,6 +1520,7 @@ const loadDefaultPipeline = async (language, options) => {
|
|
|
1143
1520
|
try {
|
|
1144
1521
|
return await loadPipeline(defaultPackageUrl(language), options);
|
|
1145
1522
|
} catch (error) {
|
|
1523
|
+
if (!(error instanceof PreparedPackageUnavailableError)) throw error;
|
|
1146
1524
|
const normalized = language === void 0 ? void 0 : normalizeLanguage(language);
|
|
1147
1525
|
const baseLanguage = normalized?.split("-").at(0);
|
|
1148
1526
|
if (baseLanguage === void 0 || baseLanguage === normalized) throw error;
|
|
@@ -1197,6 +1575,37 @@ const getDefaultPipeline = (language, options) => {
|
|
|
1197
1575
|
touchDefaultPipelineCacheEntry(key, pipeline);
|
|
1198
1576
|
return pipeline;
|
|
1199
1577
|
};
|
|
1578
|
+
const createPipeline = async ({ language, ...bindingOptions } = {}) => {
|
|
1579
|
+
const selection = normalizePipelineLanguageSelection(language);
|
|
1580
|
+
if (selection.type === "all") {
|
|
1581
|
+
const packageUrl = defaultPackageUrl();
|
|
1582
|
+
if (!unavailablePackageUrls.has(packageUrl.href)) try {
|
|
1583
|
+
return await getDefaultPipeline(void 0, bindingOptions);
|
|
1584
|
+
} catch (error) {
|
|
1585
|
+
if (!(error instanceof PreparedPackageUnavailableError)) throw error;
|
|
1586
|
+
unavailablePackageUrls.add(packageUrl.href);
|
|
1587
|
+
}
|
|
1588
|
+
return createSemanticPipeline({
|
|
1589
|
+
binding: await resolveBinding(bindingOptions),
|
|
1590
|
+
selection
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
const [singleLanguage, ...additionalLanguages] = selection.languages;
|
|
1594
|
+
if (additionalLanguages.length === 0) {
|
|
1595
|
+
const packageUrl = defaultPackageUrl(singleLanguage);
|
|
1596
|
+
if (!unavailablePackageUrls.has(packageUrl.href)) try {
|
|
1597
|
+
return await getDefaultPipeline(singleLanguage, bindingOptions);
|
|
1598
|
+
} catch (error) {
|
|
1599
|
+
if (!(error instanceof PreparedPackageUnavailableError)) throw error;
|
|
1600
|
+
unavailablePackageUrls.add(packageUrl.href);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
return createSemanticPipeline({
|
|
1604
|
+
binding: await resolveBinding(bindingOptions),
|
|
1605
|
+
selection
|
|
1606
|
+
});
|
|
1607
|
+
};
|
|
1608
|
+
const create_pipeline = createPipeline;
|
|
1200
1609
|
const redactDefaultText = async (fullText, operators, language) => (await getDefaultPipeline(language)).redactText(fullText, operators);
|
|
1201
1610
|
const redactDefaultTextJson = async (fullText, operators, language) => (await getDefaultPipeline(language)).redact_text_json(fullText, operators);
|
|
1202
1611
|
const native_package_version = async (options) => native_package_version$1(await resolveBinding(options));
|
|
@@ -1271,6 +1680,6 @@ const normalizeLanguage = (language) => {
|
|
|
1271
1680
|
return normalized;
|
|
1272
1681
|
};
|
|
1273
1682
|
//#endregion
|
|
1274
|
-
export { CALLER_DETECTION_CONTRACT_VERSION, CAPABILITY_MANIFEST, CAPABILITY_MANIFEST_SCHEMA_VERSION, CAPABILITY_PARITY_PROFILES, CAPABILITY_RUNTIMES, CAPABILITY_SURFACES, DEFAULT_ENTITY_LABELS, DETECTION_SOURCES, DETECTOR_PRIORITY, ENTITY_CAPABILITIES, ENTITY_LABELS, ENTITY_SELECTIONS, EXTERNAL_DETECTION_BATCH_MAX_BYTES, EXTERNAL_DETECTION_BATCH_VERSION, EXTERNAL_DETECTION_DOCUMENT_MAX_BYTES, EXTERNAL_DETECTION_MAX_DETECTIONS, EXTERNAL_DETECTION_MAX_LABEL_MAPPINGS, EXTERNAL_DETECTION_MAX_METADATA_BYTES, EXTERNAL_DETECTION_OFFSET_UNITS, EXTERNAL_DETECTION_PROVIDER_ID_MAX_BYTES, NATIVE_BINDING_PARITY_MEMBERS, OPERATOR_TYPES, PreparedAnonymizer, PreparedNativeAnonymizer, PreparedNativePipeline, PreparedNativeRedactionSession, PreparedNativeSessionRedactionPlan, PreparedSearch, assertNativeBindingVersion, assertNativePipelineSupported, convert_external_detection_batch, createNativeAnonymizerFromConfig, createNativeAnonymizerFromPackage, createNativePipelineFromConfig, createNativePipelineFromPackage, createPipelineContext, deanonymise, defaultPackageUrl, diagnostics_json, diagnostics_stream_json, encodeNativeSearchConfig, encodeNativeSearchConfigInput, exportRedactionKey, getBinding, getDefaultPipeline, getNativeBindingVersion, getNativePipelineCompatibility, inspect_pdf_json, isNativeAnonymizeBinding, loadDefaultPipeline, loadPipeline, load_prepared_package, native_package_version, normalize_for_search, prepareNativePipelineConfig, prepareNativePipelinePackage, prepareNativeSearchPackage, prepare_search_package, redactDefaultText, redactDefaultTextJson, redact_text, redact_text_json, redact_text_stream_json, summary_diagnostics_json };
|
|
1683
|
+
export { CALLER_DETECTION_CONTRACT_VERSION, CALLER_DETECTION_MAX_COUNT, CALLER_DETECTION_REQUEST_JSON_MAX_BYTES, CALLER_DETECTION_TEXT_MAX_BYTES, CAPABILITY_MANIFEST, CAPABILITY_MANIFEST_SCHEMA_VERSION, CAPABILITY_PARITY_PROFILES, CAPABILITY_RUNTIMES, CAPABILITY_SURFACES, DEFAULT_ENTITY_LABELS, DETECTION_SOURCES, DETECTOR_PRIORITY, ENTITY_CAPABILITIES, ENTITY_LABELS, ENTITY_SELECTIONS, EXTERNAL_DETECTION_BATCH_MAX_BYTES, EXTERNAL_DETECTION_BATCH_VERSION, EXTERNAL_DETECTION_DOCUMENT_MAX_BYTES, EXTERNAL_DETECTION_MAX_DETECTIONS, EXTERNAL_DETECTION_MAX_LABEL_MAPPINGS, EXTERNAL_DETECTION_MAX_METADATA_BYTES, EXTERNAL_DETECTION_OFFSET_UNITS, EXTERNAL_DETECTION_PROVIDER_ID_MAX_BYTES, NATIVE_BINDING_PARITY_MEMBERS, OPERATOR_TYPES, PreparedAnonymizer, PreparedNativeAnonymizer, PreparedNativePipeline, PreparedNativeRedactionSession, PreparedNativeSessionRedactionPlan, PreparedSearch, SESSION_CALLER_INPUTS_JSON_MAX_BYTES, SESSION_CALLER_MAX_INPUTS, SUPPORTED_LANGUAGES, assertNativeBindingVersion, assertNativePipelineSupported, convert_external_detection_batch, createNativeAnonymizerFromConfig, createNativeAnonymizerFromPackage, createNativePipelineFromConfig, createNativePipelineFromPackage, createPipeline, createPipelineContext, create_pipeline, deanonymise, defaultPackageUrl, diagnostics_json, diagnostics_stream_json, encodeNativeSearchConfig, encodeNativeSearchConfigInput, exportRedactionKey, getBinding, getDefaultPipeline, getNativeBindingVersion, getNativePipelineCompatibility, inspect_pdf_json, isNativeAnonymizeBinding, loadDefaultPipeline, loadPipeline, load_prepared_package, native_package_version, normalize_for_search, prepareNativePipelineConfig, prepareNativePipelinePackage, prepareNativeSearchPackage, prepare_search_package, redactDefaultText, redactDefaultTextJson, redact_text, redact_text_json, redact_text_stream_json, summary_diagnostics_json };
|
|
1275
1684
|
|
|
1276
1685
|
//# sourceMappingURL=wasm.mjs.map
|