gheim 0.1.1 → 0.1.3
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 +40 -19
- package/dist/cjs/index.cjs +289 -111
- package/dist/cjs/openai/index.cjs +313 -106
- package/dist/esm/index.js +296 -113
- package/dist/esm/openai/index.js +318 -106
- package/dist/types/core/session.d.ts +42 -6
- package/dist/types/core/session.d.ts.map +1 -1
- package/dist/types/detectors/composite.d.ts +58 -0
- package/dist/types/detectors/composite.d.ts.map +1 -0
- package/dist/types/detectors/index.d.ts +3 -1
- package/dist/types/detectors/index.d.ts.map +1 -1
- package/dist/types/detectors/local.d.ts +65 -2
- package/dist/types/detectors/local.d.ts.map +1 -1
- package/dist/types/index.d.ts +6 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/normalizers.d.ts +5 -2
- package/dist/types/normalizers.d.ts.map +1 -1
- package/dist/types/openai/_chat.d.ts.map +1 -1
- package/dist/types/openai/_client.d.ts +15 -2
- package/dist/types/openai/_client.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined")
|
|
5
|
+
return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
3
8
|
|
|
4
9
|
// src/core/sentinels.ts
|
|
5
10
|
var LABEL_TO_TAG = {
|
|
@@ -37,98 +42,6 @@ function isPossibleSentinelPrefix(buffer) {
|
|
|
37
42
|
return PARTIAL_SENTINEL_RE.test(buffer);
|
|
38
43
|
}
|
|
39
44
|
|
|
40
|
-
// src/normalizers.ts
|
|
41
|
-
import { createRequire as createRequire2 } from "node:module";
|
|
42
|
-
var _require = null;
|
|
43
|
-
var _requireResolved = false;
|
|
44
|
-
function _getRequire() {
|
|
45
|
-
if (_requireResolved)
|
|
46
|
-
return _require;
|
|
47
|
-
_requireResolved = true;
|
|
48
|
-
try {
|
|
49
|
-
_require = createRequire2(import.meta.url);
|
|
50
|
-
} catch {
|
|
51
|
-
_require = null;
|
|
52
|
-
}
|
|
53
|
-
return _require;
|
|
54
|
-
}
|
|
55
|
-
function loadSync(name, errMsg) {
|
|
56
|
-
const req = _getRequire();
|
|
57
|
-
if (!req)
|
|
58
|
-
throw new Error(errMsg);
|
|
59
|
-
try {
|
|
60
|
-
return req(name);
|
|
61
|
-
} catch {
|
|
62
|
-
throw new Error(errMsg);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function loadPhoneLib() {
|
|
66
|
-
return loadSync("libphonenumber-js", "the e164 normalizer requires libphonenumber-js. " + "Install with: bun add libphonenumber-js");
|
|
67
|
-
}
|
|
68
|
-
function e164(opts = {}) {
|
|
69
|
-
const lib = loadPhoneLib();
|
|
70
|
-
return (s) => {
|
|
71
|
-
try {
|
|
72
|
-
const parsed = lib.parsePhoneNumberFromString(s, opts.region);
|
|
73
|
-
if (!parsed || !parsed.isValid())
|
|
74
|
-
return null;
|
|
75
|
-
return parsed.format("E.164");
|
|
76
|
-
} catch {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function loadChrono(opts) {
|
|
82
|
-
const mod = loadSync("chrono-node", "the isoDate normalizer requires chrono-node. " + "Install with: bun add chrono-node");
|
|
83
|
-
if (opts.locale) {
|
|
84
|
-
const localeParser = mod[opts.locale];
|
|
85
|
-
if (localeParser)
|
|
86
|
-
return localeParser;
|
|
87
|
-
}
|
|
88
|
-
return mod;
|
|
89
|
-
}
|
|
90
|
-
function isoDate(opts = {}) {
|
|
91
|
-
const parser = loadChrono(opts);
|
|
92
|
-
return (s) => {
|
|
93
|
-
const d = parser.parseDate(s);
|
|
94
|
-
if (!d)
|
|
95
|
-
return null;
|
|
96
|
-
return d.toISOString().slice(0, 10);
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
function germanTransliteration() {
|
|
100
|
-
const table = {
|
|
101
|
-
ä: "ae",
|
|
102
|
-
Ä: "ae",
|
|
103
|
-
ö: "oe",
|
|
104
|
-
Ö: "oe",
|
|
105
|
-
ü: "ue",
|
|
106
|
-
Ü: "ue",
|
|
107
|
-
ß: "ss"
|
|
108
|
-
};
|
|
109
|
-
return (s) => {
|
|
110
|
-
let out = "";
|
|
111
|
-
for (const ch of s.normalize("NFKC")) {
|
|
112
|
-
out += table[ch] ?? ch;
|
|
113
|
-
}
|
|
114
|
-
return out.toLowerCase().split(/\s+/).filter(Boolean).join(" ");
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
var BUILTINS = {
|
|
118
|
-
e164: () => e164(),
|
|
119
|
-
iso_date: () => isoDate(),
|
|
120
|
-
german: () => germanTransliteration()
|
|
121
|
-
};
|
|
122
|
-
function resolveNormalizer(spec) {
|
|
123
|
-
if (typeof spec === "function")
|
|
124
|
-
return spec;
|
|
125
|
-
const factory = BUILTINS[spec];
|
|
126
|
-
if (!factory) {
|
|
127
|
-
throw new Error(`unknown built-in normalizer: ${JSON.stringify(spec)}. ` + `Available: ${Object.keys(BUILTINS).join(", ")}, or pass a callable.`);
|
|
128
|
-
}
|
|
129
|
-
return factory();
|
|
130
|
-
}
|
|
131
|
-
|
|
132
45
|
// src/core/session.ts
|
|
133
46
|
function normalizeSurface(s) {
|
|
134
47
|
return s.normalize("NFKC").toLowerCase().split(/\s+/).filter(Boolean).join(" ");
|
|
@@ -141,8 +54,8 @@ class Session {
|
|
|
141
54
|
resolvedNormalizers = {};
|
|
142
55
|
constructor(opts = {}) {
|
|
143
56
|
if (opts.normalizers) {
|
|
144
|
-
for (const [label,
|
|
145
|
-
this.resolvedNormalizers[label] =
|
|
57
|
+
for (const [label, fn] of Object.entries(opts.normalizers)) {
|
|
58
|
+
this.resolvedNormalizers[label] = fn;
|
|
146
59
|
}
|
|
147
60
|
}
|
|
148
61
|
}
|
|
@@ -166,8 +79,11 @@ class Session {
|
|
|
166
79
|
return sentinel;
|
|
167
80
|
}
|
|
168
81
|
applySpans(text, spans) {
|
|
82
|
+
return this.applySpansDetailed(text, spans).redacted;
|
|
83
|
+
}
|
|
84
|
+
applySpansDetailed(text, spans) {
|
|
169
85
|
if (spans.length === 0)
|
|
170
|
-
return text;
|
|
86
|
+
return { redacted: text, merged: [] };
|
|
171
87
|
const sorted = [...spans].sort((a, b) => a.start - b.start || b.end - b.start - (a.end - a.start));
|
|
172
88
|
const merged = mergeAdjacent(text, sorted);
|
|
173
89
|
const out = [];
|
|
@@ -183,7 +99,7 @@ class Session {
|
|
|
183
99
|
cursor = end;
|
|
184
100
|
}
|
|
185
101
|
out.push(text.slice(cursor));
|
|
186
|
-
return out.join("");
|
|
102
|
+
return { redacted: out.join(""), merged };
|
|
187
103
|
}
|
|
188
104
|
restore(text) {
|
|
189
105
|
if (Object.keys(this.mapping).length === 0)
|
|
@@ -381,8 +297,10 @@ class LocalDetector {
|
|
|
381
297
|
chunkTokens;
|
|
382
298
|
chunkOverlapTokens;
|
|
383
299
|
batchSize;
|
|
300
|
+
actualDevice = null;
|
|
384
301
|
_pipeline = null;
|
|
385
302
|
_loadPromise = null;
|
|
303
|
+
_onProgress;
|
|
386
304
|
constructor(opts = {}) {
|
|
387
305
|
this.model = opts.model ?? DEFAULT_MODEL2;
|
|
388
306
|
this.device = opts.device ?? "auto";
|
|
@@ -391,11 +309,13 @@ class LocalDetector {
|
|
|
391
309
|
this.chunkTokens = opts.chunkTokens ?? DEFAULT_CHUNK_TOKENS;
|
|
392
310
|
this.chunkOverlapTokens = opts.chunkOverlapTokens ?? DEFAULT_OVERLAP_TOKENS;
|
|
393
311
|
this.batchSize = opts.batchSize ?? DEFAULT_BATCH_SIZE;
|
|
312
|
+
this._onProgress = opts.onProgress;
|
|
394
313
|
if (this.chunkOverlapTokens >= this.chunkTokens) {
|
|
395
314
|
throw new Error(`chunkOverlapTokens (${this.chunkOverlapTokens}) must be smaller than chunkTokens (${this.chunkTokens})`);
|
|
396
315
|
}
|
|
397
316
|
if (opts.pipelineInstance) {
|
|
398
317
|
this._pipeline = opts.pipelineInstance;
|
|
318
|
+
this.actualDevice = this.device === "auto" ? null : this.device;
|
|
399
319
|
}
|
|
400
320
|
}
|
|
401
321
|
async load() {
|
|
@@ -404,24 +324,70 @@ class LocalDetector {
|
|
|
404
324
|
if (this._loadPromise)
|
|
405
325
|
return this._loadPromise;
|
|
406
326
|
this._loadPromise = (async () => {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
327
|
+
const mod = await loadTransformersJs();
|
|
328
|
+
const sequence = await this._planDeviceSequence();
|
|
329
|
+
let lastErr;
|
|
330
|
+
for (let i = 0;i < sequence.length; i++) {
|
|
331
|
+
const dev = sequence[i] ?? "wasm";
|
|
332
|
+
try {
|
|
333
|
+
const pipe = await mod.pipeline("token-classification", this.model, {
|
|
334
|
+
device: dev,
|
|
335
|
+
dtype: this.dtype,
|
|
336
|
+
progress_callback: this._onProgress ? (raw) => this._emitProgress(raw, dev) : undefined
|
|
337
|
+
});
|
|
338
|
+
this._pipeline = pipe;
|
|
339
|
+
this.actualDevice = dev;
|
|
340
|
+
return pipe;
|
|
341
|
+
} catch (e) {
|
|
342
|
+
lastErr = e;
|
|
343
|
+
if (i < sequence.length - 1)
|
|
344
|
+
continue;
|
|
345
|
+
throw e;
|
|
346
|
+
}
|
|
412
347
|
}
|
|
413
|
-
|
|
414
|
-
device: this.device,
|
|
415
|
-
dtype: this.dtype
|
|
416
|
-
});
|
|
417
|
-
this._pipeline = pipe;
|
|
418
|
-
return pipe;
|
|
348
|
+
throw lastErr ?? new Error("LocalDetector.load(): no candidate backends");
|
|
419
349
|
})();
|
|
420
350
|
return this._loadPromise;
|
|
421
351
|
}
|
|
352
|
+
async _planDeviceSequence() {
|
|
353
|
+
if (this.device !== "auto")
|
|
354
|
+
return [this.device];
|
|
355
|
+
const seq = [];
|
|
356
|
+
const nav = typeof navigator !== "undefined" ? navigator : null;
|
|
357
|
+
if (nav?.gpu?.requestAdapter) {
|
|
358
|
+
try {
|
|
359
|
+
const adapter = await nav.gpu.requestAdapter();
|
|
360
|
+
if (adapter)
|
|
361
|
+
seq.push("webgpu");
|
|
362
|
+
} catch {}
|
|
363
|
+
}
|
|
364
|
+
seq.push("wasm");
|
|
365
|
+
return seq;
|
|
366
|
+
}
|
|
367
|
+
_emitProgress(raw, device) {
|
|
368
|
+
if (!this._onProgress)
|
|
369
|
+
return;
|
|
370
|
+
const event = {
|
|
371
|
+
phase: raw.status ?? "progress",
|
|
372
|
+
device
|
|
373
|
+
};
|
|
374
|
+
if (raw.file)
|
|
375
|
+
event.file = raw.file;
|
|
376
|
+
if (raw.status === "progress") {
|
|
377
|
+
if (raw.loaded != null)
|
|
378
|
+
event.loadedBytes = raw.loaded;
|
|
379
|
+
if (raw.total != null)
|
|
380
|
+
event.totalBytes = raw.total;
|
|
381
|
+
if (raw.progress != null) {
|
|
382
|
+
const norm = raw.progress > 1 ? raw.progress / 100 : raw.progress;
|
|
383
|
+
event.fraction = Math.max(0, Math.min(1, norm));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
this._onProgress(event);
|
|
387
|
+
}
|
|
422
388
|
async detect(text, opts) {
|
|
423
389
|
if (opts?.model && opts.model !== this.model) {
|
|
424
|
-
throw new Error(`LocalDetector was constructed with model='${this.model}'; per-call model override ('${opts.model}') not supported.`);
|
|
390
|
+
throw new Error(`LocalDetector was constructed with model='${this.model}'; ` + `per-call model override ('${opts.model}') not supported.`);
|
|
425
391
|
}
|
|
426
392
|
if (!text)
|
|
427
393
|
return [];
|
|
@@ -430,7 +396,7 @@ class LocalDetector {
|
|
|
430
396
|
}
|
|
431
397
|
async detectBatch(texts, opts) {
|
|
432
398
|
if (opts?.model && opts.model !== this.model) {
|
|
433
|
-
throw new Error(`LocalDetector was constructed with model='${this.model}'; per-call model override ('${opts.model}') not supported.`);
|
|
399
|
+
throw new Error(`LocalDetector was constructed with model='${this.model}'; ` + `per-call model override ('${opts.model}') not supported.`);
|
|
434
400
|
}
|
|
435
401
|
if (texts.length === 0)
|
|
436
402
|
return [];
|
|
@@ -591,6 +557,221 @@ function chunkByChars(text, chunkChars, overlapChars) {
|
|
|
591
557
|
function normalizeWord(word) {
|
|
592
558
|
return word.replace(/##/g, "").trim();
|
|
593
559
|
}
|
|
560
|
+
async function loadTransformersJs() {
|
|
561
|
+
try {
|
|
562
|
+
return await import("@huggingface/transformers");
|
|
563
|
+
} catch {
|
|
564
|
+
throw new Error("LocalDetector requires `@huggingface/transformers`. Install with: bun add @huggingface/transformers");
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
// src/detectors/composite.ts
|
|
568
|
+
function luhnOk(s) {
|
|
569
|
+
const digits = [];
|
|
570
|
+
for (const c of s) {
|
|
571
|
+
if (c >= "0" && c <= "9")
|
|
572
|
+
digits.push(Number(c));
|
|
573
|
+
}
|
|
574
|
+
if (digits.length < 13 || digits.length > 19)
|
|
575
|
+
return false;
|
|
576
|
+
let total = 0;
|
|
577
|
+
for (let i = 0;i < digits.length; i++) {
|
|
578
|
+
let d = digits[digits.length - 1 - i] ?? 0;
|
|
579
|
+
if (i % 2 === 1) {
|
|
580
|
+
d *= 2;
|
|
581
|
+
if (d > 9)
|
|
582
|
+
d -= 9;
|
|
583
|
+
}
|
|
584
|
+
total += d;
|
|
585
|
+
}
|
|
586
|
+
return total % 10 === 0;
|
|
587
|
+
}
|
|
588
|
+
function ibanChOk(raw) {
|
|
589
|
+
const s = raw.replace(/[\s-]/g, "");
|
|
590
|
+
if (s.length !== 21 || !s.startsWith("CH") || !/^\d+$/.test(s.slice(2))) {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
const rearranged = s.slice(4) + s.slice(0, 4);
|
|
594
|
+
let numeric = "";
|
|
595
|
+
for (const c of rearranged) {
|
|
596
|
+
numeric += c >= "A" && c <= "Z" ? String(c.charCodeAt(0) - 55) : c;
|
|
597
|
+
}
|
|
598
|
+
let rem = 0;
|
|
599
|
+
for (const ch of numeric) {
|
|
600
|
+
rem = (rem * 10 + Number(ch)) % 97;
|
|
601
|
+
}
|
|
602
|
+
return rem === 1;
|
|
603
|
+
}
|
|
604
|
+
function ahvOk(s) {
|
|
605
|
+
const digits = [];
|
|
606
|
+
for (const c of s) {
|
|
607
|
+
if (c >= "0" && c <= "9")
|
|
608
|
+
digits.push(Number(c));
|
|
609
|
+
}
|
|
610
|
+
if (digits.length !== 13)
|
|
611
|
+
return false;
|
|
612
|
+
if (digits[0] !== 7 || digits[1] !== 5 || digits[2] !== 6)
|
|
613
|
+
return false;
|
|
614
|
+
let total = 0;
|
|
615
|
+
for (let i = 0;i < 12; i++) {
|
|
616
|
+
total += (digits[i] ?? 0) * (i % 2 === 0 ? 1 : 3);
|
|
617
|
+
}
|
|
618
|
+
const check = (10 - total % 10) % 10;
|
|
619
|
+
return digits[12] === check;
|
|
620
|
+
}
|
|
621
|
+
function vatCheOk(s) {
|
|
622
|
+
const digits = [];
|
|
623
|
+
for (const c of s) {
|
|
624
|
+
if (c >= "0" && c <= "9")
|
|
625
|
+
digits.push(Number(c));
|
|
626
|
+
}
|
|
627
|
+
if (digits.length < 9)
|
|
628
|
+
return false;
|
|
629
|
+
const weights = [5, 4, 3, 2, 7, 6, 5, 4];
|
|
630
|
+
let total = 0;
|
|
631
|
+
for (let i = 0;i < 8; i++) {
|
|
632
|
+
total += (digits[i] ?? 0) * (weights[i] ?? 0);
|
|
633
|
+
}
|
|
634
|
+
let expected = 11 - total % 11;
|
|
635
|
+
if (expected === 11)
|
|
636
|
+
expected = 0;
|
|
637
|
+
else if (expected === 10)
|
|
638
|
+
return false;
|
|
639
|
+
return digits[8] === expected;
|
|
640
|
+
}
|
|
641
|
+
var REGEXES = [
|
|
642
|
+
{
|
|
643
|
+
label: "private_email",
|
|
644
|
+
pattern: /\b[\w._%+\-]+@[\w.\-]+\.[A-Za-z]{2,}\b/g
|
|
645
|
+
},
|
|
646
|
+
{
|
|
647
|
+
label: "private_url",
|
|
648
|
+
pattern: /https?:\/\/[^\s)>\]]+/g
|
|
649
|
+
},
|
|
650
|
+
{
|
|
651
|
+
label: "private_phone",
|
|
652
|
+
pattern: /(?:\+41|0041|0)\s?(?:\(0\)\s?)?\d{2}[\s\-/.]?\d{3}[\s\-/.]?\d{2}[\s\-/.]?\d{2}\b/g
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
label: "account_number",
|
|
656
|
+
pattern: /\bCH\d{2}(?:[\s-]?\d){17}\b/g,
|
|
657
|
+
validator: ibanChOk
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
label: "account_number",
|
|
661
|
+
pattern: /\b756\.\d{4}\.\d{4}\.\d{2}\b/g,
|
|
662
|
+
validator: ahvOk
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
label: "account_number",
|
|
666
|
+
pattern: /\b756\d{10}\b/g,
|
|
667
|
+
validator: ahvOk
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
label: "account_number",
|
|
671
|
+
pattern: /\bCHE-\d{3}\.\d{3}\.\d{3}(?:\s+(?:MWST|TVA|IVA))?\b/g,
|
|
672
|
+
validator: vatCheOk
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
label: "account_number",
|
|
676
|
+
pattern: /\b(?:\d[\s-]?){15,16}\d\b/g,
|
|
677
|
+
validator: luhnOk
|
|
678
|
+
},
|
|
679
|
+
{
|
|
680
|
+
label: "private_date",
|
|
681
|
+
pattern: /\b(?:\d{1,2}[./]\d{1,2}[./]\d{4}|\d{4}[./-]\d{1,2}[./-]\d{1,2})\b/g
|
|
682
|
+
},
|
|
683
|
+
{
|
|
684
|
+
label: "secret",
|
|
685
|
+
pattern: /\bsk-(?:proj-)?[A-Za-z0-9_\-]{20,}\b/g
|
|
686
|
+
},
|
|
687
|
+
{
|
|
688
|
+
label: "secret",
|
|
689
|
+
pattern: /\bghp_[A-Za-z0-9]{36}\b/g
|
|
690
|
+
},
|
|
691
|
+
{
|
|
692
|
+
label: "secret",
|
|
693
|
+
pattern: /\bAKIA[A-Z0-9]{16}\b/g
|
|
694
|
+
}
|
|
695
|
+
];
|
|
696
|
+
var URL_TRAIL_TRIM = '.,;:!?")>]';
|
|
697
|
+
function trimUrl(text, start, end) {
|
|
698
|
+
while (end > start && URL_TRAIL_TRIM.includes(text[end - 1] ?? ""))
|
|
699
|
+
end--;
|
|
700
|
+
return [start, end];
|
|
701
|
+
}
|
|
702
|
+
function findRegexSpans(text) {
|
|
703
|
+
const candidates = [];
|
|
704
|
+
for (const { label, pattern, validator } of REGEXES) {
|
|
705
|
+
pattern.lastIndex = 0;
|
|
706
|
+
let m;
|
|
707
|
+
while ((m = pattern.exec(text)) !== null) {
|
|
708
|
+
let start = m.index;
|
|
709
|
+
let end = m.index + m[0].length;
|
|
710
|
+
if (label === "private_url")
|
|
711
|
+
[start, end] = trimUrl(text, start, end);
|
|
712
|
+
const value = text.slice(start, end);
|
|
713
|
+
if (!value.trim())
|
|
714
|
+
continue;
|
|
715
|
+
if (validator && !validator(value))
|
|
716
|
+
continue;
|
|
717
|
+
candidates.push({
|
|
718
|
+
start,
|
|
719
|
+
end,
|
|
720
|
+
label,
|
|
721
|
+
text: value,
|
|
722
|
+
score: 1
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
candidates.sort((a, b) => a.start !== b.start ? a.start - b.start : b.end - b.start - (a.end - a.start));
|
|
727
|
+
const out = [];
|
|
728
|
+
let lastEnd = -1;
|
|
729
|
+
for (const sp of candidates) {
|
|
730
|
+
if (sp.start < lastEnd)
|
|
731
|
+
continue;
|
|
732
|
+
out.push(sp);
|
|
733
|
+
lastEnd = sp.end;
|
|
734
|
+
}
|
|
735
|
+
return out;
|
|
736
|
+
}
|
|
737
|
+
function maskSpans(text, spans) {
|
|
738
|
+
if (spans.length === 0)
|
|
739
|
+
return text;
|
|
740
|
+
const chars = [...text];
|
|
741
|
+
for (const sp of spans) {
|
|
742
|
+
for (let i = sp.start;i < sp.end && i < chars.length; i++) {
|
|
743
|
+
chars[i] = " ";
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
return chars.join("");
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
class CompositeDetector {
|
|
750
|
+
model;
|
|
751
|
+
constructor(opts) {
|
|
752
|
+
if (!opts || !opts.model) {
|
|
753
|
+
throw new Error("CompositeDetector requires { model: Detector } — pass any object with " + "a .detect(text) method (e.g. new LocalDetector({...})).");
|
|
754
|
+
}
|
|
755
|
+
this.model = opts.model;
|
|
756
|
+
}
|
|
757
|
+
async detect(text, opts) {
|
|
758
|
+
if (!text)
|
|
759
|
+
return [];
|
|
760
|
+
const regexSpans = findRegexSpans(text);
|
|
761
|
+
const masked = maskSpans(text, regexSpans);
|
|
762
|
+
const modelSpans = await this.model.detect(masked, opts);
|
|
763
|
+
const out = [...regexSpans];
|
|
764
|
+
const regexRanges = regexSpans.map((s) => [s.start, s.end]);
|
|
765
|
+
for (const sp of modelSpans) {
|
|
766
|
+
const overlaps = regexRanges.some(([rs, re]) => sp.start < re && rs < sp.end);
|
|
767
|
+
if (overlaps)
|
|
768
|
+
continue;
|
|
769
|
+
out.push(sp);
|
|
770
|
+
}
|
|
771
|
+
out.sort((a, b) => a.start !== b.start ? a.start - b.start : b.end - b.start - (a.end - a.start));
|
|
772
|
+
return out;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
594
775
|
// src/detectors/index.ts
|
|
595
776
|
function defaultDetector() {
|
|
596
777
|
const hasKey = typeof process !== "undefined" && Boolean(process.env?.GHEIM_API_KEY);
|
|
@@ -645,10 +826,11 @@ async function* deanonymizeStream(chunks, session) {
|
|
|
645
826
|
}
|
|
646
827
|
|
|
647
828
|
// src/index.ts
|
|
648
|
-
var VERSION = "0.1.
|
|
829
|
+
var VERSION = "0.1.3";
|
|
649
830
|
export {
|
|
650
831
|
renderSentinel,
|
|
651
832
|
parseSentinel,
|
|
833
|
+
mergeAdjacent,
|
|
652
834
|
labelTag,
|
|
653
835
|
isPossibleSentinelPrefix,
|
|
654
836
|
defaultDetector,
|
|
@@ -664,5 +846,6 @@ export {
|
|
|
664
846
|
DEFAULT_MODEL as REMOTE_DEFAULT_MODEL,
|
|
665
847
|
LocalDetector,
|
|
666
848
|
LABEL_TO_TAG,
|
|
667
|
-
DEFAULT_BASE_URL
|
|
849
|
+
DEFAULT_BASE_URL,
|
|
850
|
+
CompositeDetector
|
|
668
851
|
};
|