gheim 0.1.5 → 0.1.6

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.
@@ -157,8 +157,8 @@ class Session {
157
157
  }
158
158
  out.push(text.slice(cursor));
159
159
  let redacted = out.join("");
160
- redacted = recoverDuplicateOccurrences(redacted, this.mapping);
161
- return { redacted, merged };
160
+ const recovered = recoverDuplicateOccurrences(text, redacted, merged, this.mapping);
161
+ return { redacted: recovered.redacted, merged: recovered.merged };
162
162
  }
163
163
  restore(text) {
164
164
  if (Object.keys(this.mapping).length === 0)
@@ -217,14 +217,36 @@ function mergeAdjacent(text, spans) {
217
217
  function escapeRegex(s) {
218
218
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
219
219
  }
220
- function recoverDuplicateOccurrences(redacted, mapping) {
220
+ function recoverDuplicateOccurrences(text, redacted, merged, mapping) {
221
+ const surfaceToLabel = new Map;
222
+ for (const m of merged)
223
+ surfaceToLabel.set(text.slice(m.start, m.end), m.label);
224
+ const covered = merged.map((m) => [m.start, m.end]);
225
+ const overlaps = (s, e) => covered.some(([cs, ce]) => s < ce && e > cs);
221
226
  const entries = Object.entries(mapping).filter(([, surface]) => surface && surface.length > 0).sort((a, b) => b[1].length - a[1].length);
222
227
  let result = redacted;
228
+ const additional = [];
223
229
  for (const [sentinel, surface] of entries) {
224
- const pattern = new RegExp(`(?<![\\p{L}\\p{N}_])${escapeRegex(surface)}(?![\\p{L}\\p{N}_])`, "gu");
225
- result = result.replace(pattern, sentinel);
230
+ const boundaryPattern = `(?<![\\p{L}\\p{N}_])${escapeRegex(surface)}(?![\\p{L}\\p{N}_])`;
231
+ result = result.replace(new RegExp(boundaryPattern, "gu"), sentinel);
232
+ const label = surfaceToLabel.get(surface);
233
+ if (!label)
234
+ continue;
235
+ const origRe = new RegExp(boundaryPattern, "gu");
236
+ let match;
237
+ while ((match = origRe.exec(text)) !== null) {
238
+ const start = match.index;
239
+ const end = start + surface.length;
240
+ if (overlaps(start, end))
241
+ continue;
242
+ additional.push({ start, end, label });
243
+ covered.push([start, end]);
244
+ }
226
245
  }
227
- return result;
246
+ if (additional.length === 0)
247
+ return { redacted: result, merged };
248
+ const augmented = [...merged, ...additional].sort((a, b) => a.start - b.start);
249
+ return { redacted: result, merged: augmented };
228
250
  }
229
251
  // src/core/stream.ts
230
252
  var SENTINEL_AT = /<([A-Z][A-Z0-9_]*)_(\d+)>/y;
@@ -1168,4 +1190,4 @@ async function* deanonymizeStream(chunks, session) {
1168
1190
  }
1169
1191
 
1170
1192
  // src/index.ts
1171
- var VERSION = "0.1.5";
1193
+ var VERSION = "0.1.6";
@@ -1086,8 +1086,8 @@ class Session {
1086
1086
  }
1087
1087
  out.push(text.slice(cursor));
1088
1088
  let redacted = out.join("");
1089
- redacted = recoverDuplicateOccurrences(redacted, this.mapping);
1090
- return { redacted, merged };
1089
+ const recovered = recoverDuplicateOccurrences(text, redacted, merged, this.mapping);
1090
+ return { redacted: recovered.redacted, merged: recovered.merged };
1091
1091
  }
1092
1092
  restore(text) {
1093
1093
  if (Object.keys(this.mapping).length === 0)
@@ -1146,14 +1146,36 @@ function mergeAdjacent(text, spans) {
1146
1146
  function escapeRegex(s) {
1147
1147
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1148
1148
  }
1149
- function recoverDuplicateOccurrences(redacted, mapping) {
1149
+ function recoverDuplicateOccurrences(text, redacted, merged, mapping) {
1150
+ const surfaceToLabel = new Map;
1151
+ for (const m of merged)
1152
+ surfaceToLabel.set(text.slice(m.start, m.end), m.label);
1153
+ const covered = merged.map((m) => [m.start, m.end]);
1154
+ const overlaps = (s, e) => covered.some(([cs, ce]) => s < ce && e > cs);
1150
1155
  const entries = Object.entries(mapping).filter(([, surface]) => surface && surface.length > 0).sort((a, b) => b[1].length - a[1].length);
1151
1156
  let result = redacted;
1157
+ const additional = [];
1152
1158
  for (const [sentinel, surface] of entries) {
1153
- const pattern = new RegExp(`(?<![\\p{L}\\p{N}_])${escapeRegex(surface)}(?![\\p{L}\\p{N}_])`, "gu");
1154
- result = result.replace(pattern, sentinel);
1159
+ const boundaryPattern = `(?<![\\p{L}\\p{N}_])${escapeRegex(surface)}(?![\\p{L}\\p{N}_])`;
1160
+ result = result.replace(new RegExp(boundaryPattern, "gu"), sentinel);
1161
+ const label = surfaceToLabel.get(surface);
1162
+ if (!label)
1163
+ continue;
1164
+ const origRe = new RegExp(boundaryPattern, "gu");
1165
+ let match;
1166
+ while ((match = origRe.exec(text)) !== null) {
1167
+ const start = match.index;
1168
+ const end = start + surface.length;
1169
+ if (overlaps(start, end))
1170
+ continue;
1171
+ additional.push({ start, end, label });
1172
+ covered.push([start, end]);
1173
+ }
1155
1174
  }
1156
- return result;
1175
+ if (additional.length === 0)
1176
+ return { redacted: result, merged };
1177
+ const augmented = [...merged, ...additional].sort((a, b) => a.start - b.start);
1178
+ return { redacted: result, merged: augmented };
1157
1179
  }
1158
1180
 
1159
1181
  // src/openai/_base.ts
package/dist/esm/index.js CHANGED
@@ -100,8 +100,8 @@ class Session {
100
100
  }
101
101
  out.push(text.slice(cursor));
102
102
  let redacted = out.join("");
103
- redacted = recoverDuplicateOccurrences(redacted, this.mapping);
104
- return { redacted, merged };
103
+ const recovered = recoverDuplicateOccurrences(text, redacted, merged, this.mapping);
104
+ return { redacted: recovered.redacted, merged: recovered.merged };
105
105
  }
106
106
  restore(text) {
107
107
  if (Object.keys(this.mapping).length === 0)
@@ -160,14 +160,36 @@ function mergeAdjacent(text, spans) {
160
160
  function escapeRegex(s) {
161
161
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
162
162
  }
163
- function recoverDuplicateOccurrences(redacted, mapping) {
163
+ function recoverDuplicateOccurrences(text, redacted, merged, mapping) {
164
+ const surfaceToLabel = new Map;
165
+ for (const m of merged)
166
+ surfaceToLabel.set(text.slice(m.start, m.end), m.label);
167
+ const covered = merged.map((m) => [m.start, m.end]);
168
+ const overlaps = (s, e) => covered.some(([cs, ce]) => s < ce && e > cs);
164
169
  const entries = Object.entries(mapping).filter(([, surface]) => surface && surface.length > 0).sort((a, b) => b[1].length - a[1].length);
165
170
  let result = redacted;
171
+ const additional = [];
166
172
  for (const [sentinel, surface] of entries) {
167
- const pattern = new RegExp(`(?<![\\p{L}\\p{N}_])${escapeRegex(surface)}(?![\\p{L}\\p{N}_])`, "gu");
168
- result = result.replace(pattern, sentinel);
173
+ const boundaryPattern = `(?<![\\p{L}\\p{N}_])${escapeRegex(surface)}(?![\\p{L}\\p{N}_])`;
174
+ result = result.replace(new RegExp(boundaryPattern, "gu"), sentinel);
175
+ const label = surfaceToLabel.get(surface);
176
+ if (!label)
177
+ continue;
178
+ const origRe = new RegExp(boundaryPattern, "gu");
179
+ let match;
180
+ while ((match = origRe.exec(text)) !== null) {
181
+ const start = match.index;
182
+ const end = start + surface.length;
183
+ if (overlaps(start, end))
184
+ continue;
185
+ additional.push({ start, end, label });
186
+ covered.push([start, end]);
187
+ }
169
188
  }
170
- return result;
189
+ if (additional.length === 0)
190
+ return { redacted: result, merged };
191
+ const augmented = [...merged, ...additional].sort((a, b) => a.start - b.start);
192
+ return { redacted: result, merged: augmented };
171
193
  }
172
194
  // src/core/stream.ts
173
195
  var SENTINEL_AT = /<([A-Z][A-Z0-9_]*)_(\d+)>/y;
@@ -1111,7 +1133,7 @@ async function* deanonymizeStream(chunks, session) {
1111
1133
  }
1112
1134
 
1113
1135
  // src/index.ts
1114
- var VERSION = "0.1.5";
1136
+ var VERSION = "0.1.6";
1115
1137
  export {
1116
1138
  renderSentinel,
1117
1139
  parseSentinel,
@@ -1043,8 +1043,8 @@ class Session {
1043
1043
  }
1044
1044
  out.push(text.slice(cursor));
1045
1045
  let redacted = out.join("");
1046
- redacted = recoverDuplicateOccurrences(redacted, this.mapping);
1047
- return { redacted, merged };
1046
+ const recovered = recoverDuplicateOccurrences(text, redacted, merged, this.mapping);
1047
+ return { redacted: recovered.redacted, merged: recovered.merged };
1048
1048
  }
1049
1049
  restore(text) {
1050
1050
  if (Object.keys(this.mapping).length === 0)
@@ -1103,14 +1103,36 @@ function mergeAdjacent(text, spans) {
1103
1103
  function escapeRegex(s) {
1104
1104
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1105
1105
  }
1106
- function recoverDuplicateOccurrences(redacted, mapping) {
1106
+ function recoverDuplicateOccurrences(text, redacted, merged, mapping) {
1107
+ const surfaceToLabel = new Map;
1108
+ for (const m of merged)
1109
+ surfaceToLabel.set(text.slice(m.start, m.end), m.label);
1110
+ const covered = merged.map((m) => [m.start, m.end]);
1111
+ const overlaps = (s, e) => covered.some(([cs, ce]) => s < ce && e > cs);
1107
1112
  const entries = Object.entries(mapping).filter(([, surface]) => surface && surface.length > 0).sort((a, b) => b[1].length - a[1].length);
1108
1113
  let result = redacted;
1114
+ const additional = [];
1109
1115
  for (const [sentinel, surface] of entries) {
1110
- const pattern = new RegExp(`(?<![\\p{L}\\p{N}_])${escapeRegex(surface)}(?![\\p{L}\\p{N}_])`, "gu");
1111
- result = result.replace(pattern, sentinel);
1116
+ const boundaryPattern = `(?<![\\p{L}\\p{N}_])${escapeRegex(surface)}(?![\\p{L}\\p{N}_])`;
1117
+ result = result.replace(new RegExp(boundaryPattern, "gu"), sentinel);
1118
+ const label = surfaceToLabel.get(surface);
1119
+ if (!label)
1120
+ continue;
1121
+ const origRe = new RegExp(boundaryPattern, "gu");
1122
+ let match;
1123
+ while ((match = origRe.exec(text)) !== null) {
1124
+ const start = match.index;
1125
+ const end = start + surface.length;
1126
+ if (overlaps(start, end))
1127
+ continue;
1128
+ additional.push({ start, end, label });
1129
+ covered.push([start, end]);
1130
+ }
1112
1131
  }
1113
- return result;
1132
+ if (additional.length === 0)
1133
+ return { redacted: result, merged };
1134
+ const augmented = [...merged, ...additional].sort((a, b) => a.start - b.start);
1135
+ return { redacted: result, merged: augmented };
1114
1136
  }
1115
1137
 
1116
1138
  // src/openai/_base.ts
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/core/session.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGjD;;;gEAGgE;AAChE,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAkBtD,uCAAuC;AACvC,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAC1C;AAED,qBAAa,OAAO;IAClB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IACrC,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,mBAAmB,CAAkC;gBAEjD,IAAI,GAAE,cAAmB;IAQrC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAiBhD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,IAAI,GAAG,UAAU,CAAC,EAAE,GAAG,MAAM;IAIvE;;;;;;;OAOG;IACH,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,CAAC,IAAI,GAAG,UAAU,CAAC,EAAE,GACpC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE;IA+B7C,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAK7B,MAAM,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE;IAI7C,MAAM,CAAC,QAAQ,CACb,GAAG,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,EACxC,IAAI,GAAE,cAAmB,GACxB,OAAO;CAqBX;AAID,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,CAAC,IAAI,GAAG,UAAU,CAAC,EAAE,GACpC,UAAU,EAAE,CAqBd"}
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/core/session.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGjD;;;gEAGgE;AAChE,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;AAkBtD,uCAAuC;AACvC,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CAC1C;AAED,qBAAa,OAAO;IAClB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAM;IACrC,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,mBAAmB,CAAkC;gBAEjD,IAAI,GAAE,cAAmB;IAQrC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM;IAiBhD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,IAAI,GAAG,UAAU,CAAC,EAAE,GAAG,MAAM;IAIvE;;;;;;;OAOG;IACH,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,CAAC,IAAI,GAAG,UAAU,CAAC,EAAE,GACpC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE;IA8B7C,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAK7B,MAAM,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE;IAI7C,MAAM,CAAC,QAAQ,CACb,GAAG,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,EACxC,IAAI,GAAE,cAAmB,GACxB,OAAO;CAqBX;AAID,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS,CAAC,IAAI,GAAG,UAAU,CAAC,EAAE,GACpC,UAAU,EAAE,CAqBd"}
@@ -17,7 +17,7 @@ export { DEFAULT_BASE_URL, REMOTE_DEFAULT_MODEL, RemoteDetector, LocalDetector,
17
17
  export type { RemoteDetectorOptions, LocalDetectorOptions, LocalDetectorLoadEvent, CompositeDetectorOptions, CalibratedDetectorOptions, } from "./detectors/index.ts";
18
18
  export { anonymizeMessages, anonymizeText, deanonymizeStream, deanonymizeText, } from "./plain.ts";
19
19
  export type { ChatMessage } from "./plain.ts";
20
- export declare const VERSION = "0.1.5";
20
+ export declare const VERSION = "0.1.6";
21
21
  export { mergeAdjacent } from "./core/session.ts";
22
22
  export type { MergedSpan } from "./core/session.ts";
23
23
  //# sourceMappingURL=index.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gheim",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "PII round-trip for LLM APIs: anonymize before the request, de-anonymize the stream on the way back.",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Joel Barmettler <joel.barmettler@gmail.com>",