@blindfold/sdk 1.0.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/dist/index.mjs ADDED
@@ -0,0 +1,568 @@
1
+ import { EntityType, PIIScanner, __toCommonJS, init_regex, regex_exports } from "./regex-ByjZg3Zy.mjs";
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+
5
+ //#region src/errors.ts
6
+ /**
7
+ * Base error class for Blindfold SDK
8
+ */
9
+ var BlindfoldError = class BlindfoldError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = "BlindfoldError";
13
+ Object.setPrototypeOf(this, BlindfoldError.prototype);
14
+ }
15
+ };
16
+ /**
17
+ * Error thrown when authentication fails
18
+ */
19
+ var AuthenticationError = class AuthenticationError extends BlindfoldError {
20
+ constructor(message = "Authentication failed. Please check your API key.") {
21
+ super(message);
22
+ this.name = "AuthenticationError";
23
+ Object.setPrototypeOf(this, AuthenticationError.prototype);
24
+ }
25
+ };
26
+ /**
27
+ * Error thrown when API request fails
28
+ */
29
+ var APIError = class APIError extends BlindfoldError {
30
+ statusCode;
31
+ responseBody;
32
+ constructor(message, statusCode, responseBody) {
33
+ super(message);
34
+ this.name = "APIError";
35
+ this.statusCode = statusCode;
36
+ this.responseBody = responseBody;
37
+ Object.setPrototypeOf(this, APIError.prototype);
38
+ }
39
+ };
40
+ /**
41
+ * Error thrown when network request fails
42
+ */
43
+ var NetworkError = class NetworkError extends BlindfoldError {
44
+ constructor(message = "Network request failed. Please check your connection.") {
45
+ super(message);
46
+ this.name = "NetworkError";
47
+ Object.setPrototypeOf(this, NetworkError.prototype);
48
+ }
49
+ };
50
+
51
+ //#endregion
52
+ //#region src/client.ts
53
+ const DEFAULT_BASE_URL = "https://api.blindfold.dev/api/public/v1";
54
+ const RETRYABLE_STATUS_CODES = new Set([
55
+ 429,
56
+ 500,
57
+ 502,
58
+ 503,
59
+ 504
60
+ ]);
61
+ const REGION_URLS = {
62
+ eu: "https://eu-api.blindfold.dev/api/public/v1",
63
+ us: "https://us-api.blindfold.dev/api/public/v1"
64
+ };
65
+ function sleep(ms) {
66
+ return new Promise((resolve) => setTimeout(resolve, ms));
67
+ }
68
+ const BUNDLED_POLICIES_PATH = path.join(__dirname, "policies.json");
69
+ function loadPolicies(policiesFile) {
70
+ const bundled = JSON.parse(fs.readFileSync(BUNDLED_POLICIES_PATH, "utf-8"));
71
+ if (policiesFile) {
72
+ const user = JSON.parse(fs.readFileSync(policiesFile, "utf-8"));
73
+ return {
74
+ ...bundled,
75
+ ...user
76
+ };
77
+ }
78
+ return bundled;
79
+ }
80
+ /**
81
+ * Blindfold client for tokenization and detokenization.
82
+ *
83
+ * When no `apiKey` is provided, all methods run locally using the
84
+ * built-in regex PII scanner. Set `mode: "local"` to force local
85
+ * mode even when an API key is present.
86
+ */
87
+ var Blindfold = class {
88
+ apiKey;
89
+ baseUrl;
90
+ userId;
91
+ maxRetries;
92
+ retryDelay;
93
+ mode;
94
+ locales;
95
+ policies;
96
+ _scanner;
97
+ /**
98
+ * Create a new Blindfold client
99
+ * @param config - Configuration options. Can be omitted entirely for local-only mode.
100
+ */
101
+ constructor(config = {}) {
102
+ this.apiKey = config.apiKey;
103
+ this.mode = config.mode;
104
+ this.locales = config.locales;
105
+ this.policies = loadPolicies(config.policiesFile);
106
+ if (config.region && !config.baseUrl) {
107
+ const regionUrl = REGION_URLS[config.region];
108
+ if (!regionUrl) throw new Error(`Invalid region '${config.region}'. Must be one of: ${Object.keys(REGION_URLS).join(", ")}`);
109
+ this.baseUrl = regionUrl;
110
+ } else this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
111
+ this.userId = config.userId;
112
+ this.maxRetries = config.maxRetries ?? 2;
113
+ this.retryDelay = config.retryDelay ?? .5;
114
+ if (this.useLocal && config.region) console.warn(`region='${config.region}' has no effect in local mode. Set an apiKey to use region-based routing.`);
115
+ }
116
+ get useLocal() {
117
+ if (this.mode === "local") return true;
118
+ return !this.apiKey;
119
+ }
120
+ getScanner() {
121
+ if (!this._scanner) {
122
+ const { PIIScanner: PIIScanner$1 } = (init_regex(), __toCommonJS(regex_exports));
123
+ this._scanner = new PIIScanner$1(this.locales ? { locales: this.locales } : void 0);
124
+ }
125
+ return this._scanner;
126
+ }
127
+ resolvePolicy(policy, entities, scoreThreshold) {
128
+ if (entities) return {
129
+ entities,
130
+ threshold: scoreThreshold
131
+ };
132
+ if (policy) {
133
+ const policyDef = this.policies[policy];
134
+ if (policyDef) return {
135
+ entities: policyDef.entities,
136
+ threshold: scoreThreshold ?? policyDef.threshold
137
+ };
138
+ else console.warn(`Unknown policy '${policy}' in local mode, detecting all entities`);
139
+ }
140
+ return { threshold: scoreThreshold };
141
+ }
142
+ retryWait(attempt, error) {
143
+ if (error && error.statusCode === 429) {
144
+ const body = error.responseBody;
145
+ if (body && typeof body.retry_after === "number") return body.retry_after * 1e3;
146
+ }
147
+ const delay = this.retryDelay * 2 ** attempt * 1e3;
148
+ const jitter = delay * .1 * Math.random();
149
+ return delay + jitter;
150
+ }
151
+ /**
152
+ * Make an authenticated request to the API
153
+ */
154
+ async request(endpoint, method, body) {
155
+ const url = `${this.baseUrl}${endpoint}`;
156
+ const headers = { "Content-Type": "application/json" };
157
+ if (this.apiKey) headers["X-API-Key"] = this.apiKey;
158
+ if (this.userId) headers["X-Blindfold-User-Id"] = this.userId;
159
+ let lastError = new NetworkError("Request failed");
160
+ for (let attempt = 0; attempt <= this.maxRetries; attempt++) try {
161
+ const response = await fetch(url, {
162
+ method,
163
+ headers,
164
+ body: body ? JSON.stringify(body) : void 0
165
+ });
166
+ if (response.status === 401 || response.status === 403) throw new AuthenticationError("Authentication failed. Please check your API key.");
167
+ if (!response.ok) {
168
+ let errorMessage = `API request failed with status ${response.status}`;
169
+ let responseBody;
170
+ try {
171
+ responseBody = await response.json();
172
+ const errorData = responseBody;
173
+ errorMessage = errorData.detail || errorData.message || errorMessage;
174
+ } catch {
175
+ errorMessage = `${errorMessage}: ${response.statusText}`;
176
+ }
177
+ throw new APIError(errorMessage, response.status, responseBody);
178
+ }
179
+ return await response.json();
180
+ } catch (error) {
181
+ if (error instanceof AuthenticationError) throw error;
182
+ if (error instanceof APIError) {
183
+ if (RETRYABLE_STATUS_CODES.has(error.statusCode) && attempt < this.maxRetries) {
184
+ await sleep(this.retryWait(attempt, error));
185
+ continue;
186
+ }
187
+ throw error;
188
+ }
189
+ if (error instanceof NetworkError) {
190
+ lastError = error;
191
+ if (attempt < this.maxRetries) {
192
+ await sleep(this.retryWait(attempt));
193
+ continue;
194
+ }
195
+ throw error;
196
+ }
197
+ if (error instanceof TypeError && error.message.includes("fetch")) {
198
+ lastError = new NetworkError("Network request failed. Please check your connection and the API URL.");
199
+ if (attempt < this.maxRetries) {
200
+ await sleep(this.retryWait(attempt));
201
+ continue;
202
+ }
203
+ throw lastError;
204
+ }
205
+ throw new NetworkError(error instanceof Error ? error.message : "Unknown error occurred");
206
+ }
207
+ throw lastError;
208
+ }
209
+ /**
210
+ * Tokenize text by replacing sensitive information with tokens
211
+ * @param text - Text to tokenize
212
+ * @param config - Optional configuration
213
+ * @returns Promise with tokenized text and mapping
214
+ */
215
+ async tokenize(text, config) {
216
+ if (this.useLocal) return this.tokenizeLocal(text, config);
217
+ return this.request("/tokenize", "POST", {
218
+ text,
219
+ ...config
220
+ });
221
+ }
222
+ tokenizeLocal(text, config) {
223
+ const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold);
224
+ const scanner = this.getScanner();
225
+ const result = scanner.tokenize(text, resolved);
226
+ const detected = result.matches.filter((m) => threshold == null || m.score >= threshold).map((m) => ({
227
+ type: m.entityType,
228
+ text: m.text,
229
+ start: m.start,
230
+ end: m.end,
231
+ score: m.score
232
+ }));
233
+ return {
234
+ text: result.text,
235
+ mapping: result.mapping,
236
+ detected_entities: detected,
237
+ entities_count: detected.length
238
+ };
239
+ }
240
+ /**
241
+ * Detect PII in text without modifying it
242
+ *
243
+ * Returns only the detected entities with their types, positions,
244
+ * and confidence scores. The original text is not transformed.
245
+ *
246
+ * When no API key is set (or `mode: "local"`), detection runs locally
247
+ * using the built-in regex scanner.
248
+ *
249
+ * @param text - Text to analyze for PII
250
+ * @param config - Optional configuration (entities, score_threshold, policy)
251
+ * @returns Promise with detected entities
252
+ */
253
+ async detect(text, config) {
254
+ if (this.useLocal) return this.detectLocal(text, config);
255
+ return this.request("/detect", "POST", {
256
+ text,
257
+ ...config
258
+ });
259
+ }
260
+ detectLocal(text, config) {
261
+ const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold);
262
+ const scanner = this.getScanner();
263
+ const matches = scanner.detect(text, resolved);
264
+ const detected = [];
265
+ for (const m of matches) {
266
+ if (threshold != null && m.score < threshold) continue;
267
+ detected.push({
268
+ type: m.entityType,
269
+ text: m.text,
270
+ start: m.start,
271
+ end: m.end,
272
+ score: m.score
273
+ });
274
+ }
275
+ return {
276
+ detected_entities: detected,
277
+ entities_count: detected.length
278
+ };
279
+ }
280
+ /**
281
+ * Detokenize text by replacing tokens with original values
282
+ *
283
+ * This method performs detokenization CLIENT-SIDE for better performance,
284
+ * security, and to work offline. No API call is made.
285
+ *
286
+ * @param text - Tokenized text
287
+ * @param mapping - Token mapping from tokenize response
288
+ * @returns DetokenizeResponse with original text
289
+ */
290
+ detokenize(text, mapping) {
291
+ let result = text;
292
+ let replacements = 0;
293
+ const sortedTokens = Object.keys(mapping).sort((a, b) => b.length - a.length);
294
+ for (const token of sortedTokens) {
295
+ const originalValue = mapping[token];
296
+ const regex = new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
297
+ const matches = result.match(regex);
298
+ if (matches) {
299
+ result = result.replace(regex, originalValue);
300
+ replacements += matches.length;
301
+ }
302
+ }
303
+ return {
304
+ text: result,
305
+ replacements_made: replacements
306
+ };
307
+ }
308
+ /**
309
+ * Redact (permanently remove) sensitive information from text
310
+ *
311
+ * WARNING: Redaction is irreversible - original data cannot be restored!
312
+ *
313
+ * When no API key is set (or `mode: "local"`), redaction runs locally
314
+ * using the built-in regex scanner, replacing PII with `<Entity Name>` placeholders.
315
+ *
316
+ * @param text - Text to redact
317
+ * @param config - Optional configuration (masking_char, entities)
318
+ * @returns Promise with redacted text and detected entities
319
+ */
320
+ async redact(text, config) {
321
+ if (this.useLocal) return this.redactLocal(text, config);
322
+ return this.request("/redact", "POST", {
323
+ text,
324
+ ...config
325
+ });
326
+ }
327
+ redactLocal(text, config) {
328
+ const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold);
329
+ const scanner = this.getScanner();
330
+ const [redactedText, matches] = scanner.redact(text, resolved);
331
+ const detected = [];
332
+ for (const m of matches) {
333
+ if (threshold != null && m.score < threshold) continue;
334
+ detected.push({
335
+ type: m.entityType,
336
+ text: m.text,
337
+ start: m.start,
338
+ end: m.end,
339
+ score: m.score
340
+ });
341
+ }
342
+ return {
343
+ text: redactedText,
344
+ detected_entities: detected,
345
+ entities_count: detected.length
346
+ };
347
+ }
348
+ /**
349
+ * Mask (partially hide) sensitive information from text
350
+ *
351
+ * @param text - Text to mask
352
+ * @param config - Optional configuration (chars_to_show, from_end, masking_char, entities)
353
+ * @returns Promise with masked text and detected entities
354
+ */
355
+ async mask(text, config) {
356
+ if (this.useLocal) return this.maskLocal(text, config);
357
+ return this.request("/mask", "POST", {
358
+ text,
359
+ ...config
360
+ });
361
+ }
362
+ maskLocal(text, config) {
363
+ const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold);
364
+ const scanner = this.getScanner();
365
+ const result = scanner.mask(text, config?.chars_to_show ?? 3, config?.from_end ?? false, config?.masking_char ?? "*", resolved);
366
+ const detected = result.matches.filter((m) => threshold == null || m.score >= threshold).map((m) => ({
367
+ type: m.entityType,
368
+ text: m.text,
369
+ start: m.start,
370
+ end: m.end,
371
+ score: m.score
372
+ }));
373
+ return {
374
+ text: result.text,
375
+ detected_entities: detected,
376
+ entities_count: detected.length
377
+ };
378
+ }
379
+ /**
380
+ * Synthesize (replace real data with synthetic fake data)
381
+ *
382
+ * When no API key is set (or `mode: "local"`), synthesis runs locally
383
+ * using format-preserving random generation.
384
+ *
385
+ * @param text - Text to synthesize
386
+ * @param config - Optional configuration (language, entities)
387
+ * @returns Promise with synthetic text and detected entities
388
+ */
389
+ async synthesize(text, config) {
390
+ if (this.useLocal) return this.synthesizeLocal(text, config);
391
+ return this.request("/synthesize", "POST", {
392
+ text,
393
+ ...config
394
+ });
395
+ }
396
+ synthesizeLocal(text, config) {
397
+ const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold);
398
+ const scanner = this.getScanner();
399
+ const result = scanner.synthesize(text, void 0, resolved);
400
+ const detected = result.matches.filter((m) => threshold == null || m.score >= threshold).map((m) => ({
401
+ type: m.entityType,
402
+ text: m.text,
403
+ start: m.start,
404
+ end: m.end,
405
+ score: m.score
406
+ }));
407
+ return {
408
+ text: result.text,
409
+ detected_entities: detected,
410
+ entities_count: detected.length
411
+ };
412
+ }
413
+ /**
414
+ * Hash (replace with deterministic hash values)
415
+ *
416
+ * @param text - Text to hash
417
+ * @param config - Optional configuration (hash_type, hash_prefix, hash_length, entities)
418
+ * @returns Promise with hashed text and detected entities
419
+ */
420
+ async hash(text, config) {
421
+ if (this.useLocal) return this.hashLocal(text, config);
422
+ return this.request("/hash", "POST", {
423
+ text,
424
+ ...config
425
+ });
426
+ }
427
+ hashLocal(text, config) {
428
+ const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold);
429
+ const scanner = this.getScanner();
430
+ const result = scanner.hash(text, config?.hash_type ?? "sha256", config?.hash_prefix ?? "HASH_", config?.hash_length ?? 16, resolved);
431
+ const detected = result.matches.filter((m) => threshold == null || m.score >= threshold).map((m) => ({
432
+ type: m.entityType,
433
+ text: m.text,
434
+ start: m.start,
435
+ end: m.end,
436
+ score: m.score
437
+ }));
438
+ return {
439
+ text: result.text,
440
+ detected_entities: detected,
441
+ entities_count: detected.length
442
+ };
443
+ }
444
+ /**
445
+ * Encrypt (reversibly protect) sensitive data in text using AES encryption
446
+ *
447
+ * @param text - Text to encrypt
448
+ * @param config - Optional configuration (encryption_key, entities)
449
+ * @returns Promise with encrypted text and detected entities
450
+ */
451
+ async encrypt(text, config) {
452
+ if (this.useLocal) return this.encryptLocal(text, config);
453
+ return this.request("/encrypt", "POST", {
454
+ text,
455
+ ...config
456
+ });
457
+ }
458
+ encryptLocal(text, config) {
459
+ if (!config?.encryption_key) throw new Error("encryption_key is required for local encryption mode");
460
+ const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold);
461
+ const scanner = this.getScanner();
462
+ const result = scanner.encrypt(text, config.encryption_key, resolved);
463
+ const detected = result.matches.filter((m) => threshold == null || m.score >= threshold).map((m) => ({
464
+ type: m.entityType,
465
+ text: m.text,
466
+ start: m.start,
467
+ end: m.end,
468
+ score: m.score
469
+ }));
470
+ return {
471
+ text: result.text,
472
+ detected_entities: detected,
473
+ entities_count: detected.length
474
+ };
475
+ }
476
+ /**
477
+ * Tokenize multiple texts in a single request
478
+ * @param texts - Array of texts to tokenize (max 100)
479
+ * @param config - Optional configuration
480
+ * @returns Promise with batch results
481
+ */
482
+ async tokenizeBatch(texts, config) {
483
+ return this.request("/tokenize", "POST", {
484
+ texts,
485
+ ...config
486
+ });
487
+ }
488
+ /**
489
+ * Detect PII in multiple texts in a single request
490
+ * @param texts - Array of texts to analyze (max 100)
491
+ * @param config - Optional configuration
492
+ * @returns Promise with batch results
493
+ */
494
+ async detectBatch(texts, config) {
495
+ return this.request("/detect", "POST", {
496
+ texts,
497
+ ...config
498
+ });
499
+ }
500
+ /**
501
+ * Redact PII from multiple texts in a single request
502
+ * @param texts - Array of texts to redact (max 100)
503
+ * @param config - Optional configuration
504
+ * @returns Promise with batch results
505
+ */
506
+ async redactBatch(texts, config) {
507
+ return this.request("/redact", "POST", {
508
+ texts,
509
+ ...config
510
+ });
511
+ }
512
+ /**
513
+ * Mask PII in multiple texts in a single request
514
+ * @param texts - Array of texts to mask (max 100)
515
+ * @param config - Optional configuration
516
+ * @returns Promise with batch results
517
+ */
518
+ async maskBatch(texts, config) {
519
+ return this.request("/mask", "POST", {
520
+ texts,
521
+ ...config
522
+ });
523
+ }
524
+ /**
525
+ * Synthesize multiple texts in a single request
526
+ * @param texts - Array of texts to synthesize (max 100)
527
+ * @param config - Optional configuration
528
+ * @returns Promise with batch results
529
+ */
530
+ async synthesizeBatch(texts, config) {
531
+ return this.request("/synthesize", "POST", {
532
+ texts,
533
+ ...config
534
+ });
535
+ }
536
+ /**
537
+ * Hash PII in multiple texts in a single request
538
+ * @param texts - Array of texts to hash (max 100)
539
+ * @param config - Optional configuration
540
+ * @returns Promise with batch results
541
+ */
542
+ async hashBatch(texts, config) {
543
+ return this.request("/hash", "POST", {
544
+ texts,
545
+ ...config
546
+ });
547
+ }
548
+ /**
549
+ * Encrypt PII in multiple texts in a single request
550
+ * @param texts - Array of texts to encrypt (max 100)
551
+ * @param config - Optional configuration
552
+ * @returns Promise with batch results
553
+ */
554
+ async encryptBatch(texts, config) {
555
+ return this.request("/encrypt", "POST", {
556
+ texts,
557
+ ...config
558
+ });
559
+ }
560
+ };
561
+
562
+ //#endregion
563
+ //#region src/index.ts
564
+ init_regex();
565
+
566
+ //#endregion
567
+ export { APIError, AuthenticationError, Blindfold, BlindfoldError, EntityType, NetworkError, PIIScanner };
568
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["message: string","statusCode: number","responseBody?: unknown","REGION_URLS: Record<string, string>","ms: number","policiesFile?: string","bundled: PolicyMap","user: PolicyMap","config: BlindfoldConfig","PIIScanner","policy?: string","entities?: string[]","scoreThreshold?: number","attempt: number","error?: APIError","endpoint: string","method: string","body?: Record<string, unknown>","headers: Record<string, string>","lastError: Error","responseBody: unknown","text: string","config?: TokenizeConfig","detected: DetectedEntity[]","config?: DetectConfig","mapping: Record<string, string>","config?: RedactConfig","config?: MaskConfig","config?: SynthesizeConfig","config?: HashConfig","config?: EncryptConfig","texts: string[]"],"sources":["../src/errors.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["/**\n * Base error class for Blindfold SDK\n */\nexport class BlindfoldError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'BlindfoldError'\n Object.setPrototypeOf(this, BlindfoldError.prototype)\n }\n}\n\n/**\n * Error thrown when authentication fails\n */\nexport class AuthenticationError extends BlindfoldError {\n constructor(message: string = 'Authentication failed. Please check your API key.') {\n super(message)\n this.name = 'AuthenticationError'\n Object.setPrototypeOf(this, AuthenticationError.prototype)\n }\n}\n\n/**\n * Error thrown when API request fails\n */\nexport class APIError extends BlindfoldError {\n statusCode: number\n responseBody?: unknown\n\n constructor(message: string, statusCode: number, responseBody?: unknown) {\n super(message)\n this.name = 'APIError'\n this.statusCode = statusCode\n this.responseBody = responseBody\n Object.setPrototypeOf(this, APIError.prototype)\n }\n}\n\n/**\n * Error thrown when network request fails\n */\nexport class NetworkError extends BlindfoldError {\n constructor(message: string = 'Network request failed. Please check your connection.') {\n super(message)\n this.name = 'NetworkError'\n Object.setPrototypeOf(this, NetworkError.prototype)\n }\n}\n","import type {\n BlindfoldConfig,\n DetectConfig,\n DetectResponse,\n DetectedEntity,\n TokenizeConfig,\n TokenizeResponse,\n DetokenizeResponse,\n RedactConfig,\n RedactResponse,\n MaskConfig,\n MaskResponse,\n SynthesizeConfig,\n SynthesizeResponse,\n HashConfig,\n HashResponse,\n EncryptConfig,\n EncryptResponse,\n BatchResponse,\n APIErrorResponse,\n} from './types'\nimport { AuthenticationError, APIError, NetworkError } from './errors'\nimport type { PIIScanner as PIIScannerType } from './regex'\nimport * as fs from 'fs'\nimport * as path from 'path'\n\nconst DEFAULT_BASE_URL = 'https://api.blindfold.dev/api/public/v1'\nconst RETRYABLE_STATUS_CODES = new Set([429, 500, 502, 503, 504])\n\nconst REGION_URLS: Record<string, string> = {\n eu: 'https://eu-api.blindfold.dev/api/public/v1',\n us: 'https://us-api.blindfold.dev/api/public/v1',\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\ninterface PolicyDefinition {\n entities: string[]\n threshold?: number\n}\n\ntype PolicyMap = Record<string, PolicyDefinition>\n\nconst BUNDLED_POLICIES_PATH = path.join(__dirname, 'policies.json')\n\nfunction loadPolicies(policiesFile?: string): PolicyMap {\n const bundled: PolicyMap = JSON.parse(fs.readFileSync(BUNDLED_POLICIES_PATH, 'utf-8'))\n if (policiesFile) {\n const user: PolicyMap = JSON.parse(fs.readFileSync(policiesFile, 'utf-8'))\n return { ...bundled, ...user }\n }\n return bundled\n}\n\n/**\n * Blindfold client for tokenization and detokenization.\n *\n * When no `apiKey` is provided, all methods run locally using the\n * built-in regex PII scanner. Set `mode: \"local\"` to force local\n * mode even when an API key is present.\n */\nexport class Blindfold {\n private apiKey?: string\n private baseUrl: string\n private userId?: string\n private maxRetries: number\n private retryDelay: number\n private mode?: string\n private locales?: string[]\n private policies: PolicyMap\n private _scanner?: PIIScannerType\n\n /**\n * Create a new Blindfold client\n * @param config - Configuration options. Can be omitted entirely for local-only mode.\n */\n constructor(config: BlindfoldConfig = {}) {\n this.apiKey = config.apiKey\n this.mode = config.mode\n this.locales = config.locales\n this.policies = loadPolicies(config.policiesFile)\n if (config.region && !config.baseUrl) {\n const regionUrl = REGION_URLS[config.region]\n if (!regionUrl) {\n throw new Error(\n `Invalid region '${config.region}'. Must be one of: ${Object.keys(REGION_URLS).join(', ')}`\n )\n }\n this.baseUrl = regionUrl\n } else {\n this.baseUrl = config.baseUrl || DEFAULT_BASE_URL\n }\n this.userId = config.userId\n this.maxRetries = config.maxRetries ?? 2\n this.retryDelay = config.retryDelay ?? 0.5\n\n if (this.useLocal && config.region) {\n console.warn(\n `region='${config.region}' has no effect in local mode. ` +\n 'Set an apiKey to use region-based routing.'\n )\n }\n }\n\n private get useLocal(): boolean {\n if (this.mode === 'local') return true\n return !this.apiKey\n }\n\n private getScanner(): PIIScannerType {\n if (!this._scanner) {\n const { PIIScanner } = require('./regex') as typeof import('./regex')\n this._scanner = new PIIScanner(this.locales ? { locales: this.locales } : undefined)\n }\n return this._scanner\n }\n\n private resolvePolicy(\n policy?: string,\n entities?: string[],\n scoreThreshold?: number\n ): { entities?: string[]; threshold?: number } {\n if (entities) {\n return { entities, threshold: scoreThreshold }\n }\n if (policy) {\n const policyDef = this.policies[policy]\n if (policyDef) {\n return {\n entities: policyDef.entities,\n threshold: scoreThreshold ?? policyDef.threshold,\n }\n } else {\n console.warn(`Unknown policy '${policy}' in local mode, detecting all entities`)\n }\n }\n return { threshold: scoreThreshold }\n }\n\n private retryWait(attempt: number, error?: APIError): number {\n if (error && error.statusCode === 429) {\n const body = error.responseBody as Record<string, unknown> | undefined\n if (body && typeof body.retry_after === 'number') {\n return body.retry_after * 1000\n }\n }\n const delay = this.retryDelay * 2 ** attempt * 1000\n const jitter = delay * 0.1 * Math.random()\n return delay + jitter\n }\n\n /**\n * Make an authenticated request to the API\n */\n private async request<T>(\n endpoint: string,\n method: string,\n body?: Record<string, unknown>\n ): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n }\n\n if (this.apiKey) {\n headers['X-API-Key'] = this.apiKey\n }\n\n if (this.userId) {\n headers['X-Blindfold-User-Id'] = this.userId\n }\n\n let lastError: Error = new NetworkError('Request failed')\n\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n })\n\n // Handle authentication errors\n if (response.status === 401 || response.status === 403) {\n throw new AuthenticationError('Authentication failed. Please check your API key.')\n }\n\n // Handle other error responses\n if (!response.ok) {\n let errorMessage = `API request failed with status ${response.status}`\n let responseBody: unknown\n\n try {\n responseBody = await response.json()\n const errorData = responseBody as APIErrorResponse\n errorMessage = errorData.detail || errorData.message || errorMessage\n } catch {\n // If we can't parse the error response, use the status text\n errorMessage = `${errorMessage}: ${response.statusText}`\n }\n\n throw new APIError(errorMessage, response.status, responseBody)\n }\n\n return (await response.json()) as T\n } catch (error) {\n // Never retry auth errors\n if (error instanceof AuthenticationError) {\n throw error\n }\n\n // Retry retryable API errors\n if (error instanceof APIError) {\n if (RETRYABLE_STATUS_CODES.has(error.statusCode) && attempt < this.maxRetries) {\n await sleep(this.retryWait(attempt, error))\n continue\n }\n throw error\n }\n\n // Retry network errors\n if (error instanceof NetworkError) {\n lastError = error\n if (attempt < this.maxRetries) {\n await sleep(this.retryWait(attempt))\n continue\n }\n throw error\n }\n\n // Handle raw fetch errors (network failures)\n if (error instanceof TypeError && error.message.includes('fetch')) {\n lastError = new NetworkError(\n 'Network request failed. Please check your connection and the API URL.'\n )\n if (attempt < this.maxRetries) {\n await sleep(this.retryWait(attempt))\n continue\n }\n throw lastError\n }\n\n // Non-retryable unknown errors\n throw new NetworkError(error instanceof Error ? error.message : 'Unknown error occurred')\n }\n }\n\n throw lastError\n }\n\n /**\n * Tokenize text by replacing sensitive information with tokens\n * @param text - Text to tokenize\n * @param config - Optional configuration\n * @returns Promise with tokenized text and mapping\n */\n async tokenize(text: string, config?: TokenizeConfig): Promise<TokenizeResponse> {\n if (this.useLocal) {\n return this.tokenizeLocal(text, config)\n }\n return this.request<TokenizeResponse>('/tokenize', 'POST', {\n text,\n ...config,\n })\n }\n\n private tokenizeLocal(text: string, config?: TokenizeConfig): TokenizeResponse {\n const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold)\n const scanner = this.getScanner()\n const result = scanner.tokenize(text, resolved)\n\n const detected: DetectedEntity[] = result.matches\n .filter((m) => threshold == null || m.score >= threshold)\n .map((m) => ({\n type: m.entityType, text: m.text, start: m.start, end: m.end, score: m.score,\n }))\n\n return { text: result.text, mapping: result.mapping, detected_entities: detected, entities_count: detected.length }\n }\n\n /**\n * Detect PII in text without modifying it\n *\n * Returns only the detected entities with their types, positions,\n * and confidence scores. The original text is not transformed.\n *\n * When no API key is set (or `mode: \"local\"`), detection runs locally\n * using the built-in regex scanner.\n *\n * @param text - Text to analyze for PII\n * @param config - Optional configuration (entities, score_threshold, policy)\n * @returns Promise with detected entities\n */\n async detect(text: string, config?: DetectConfig): Promise<DetectResponse> {\n if (this.useLocal) {\n return this.detectLocal(text, config)\n }\n return this.request<DetectResponse>('/detect', 'POST', {\n text,\n ...config,\n })\n }\n\n private detectLocal(text: string, config?: DetectConfig): DetectResponse {\n const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold)\n const scanner = this.getScanner()\n const matches = scanner.detect(text, resolved)\n\n const detected: DetectedEntity[] = []\n for (const m of matches) {\n if (threshold != null && m.score < threshold) continue\n detected.push({ type: m.entityType, text: m.text, start: m.start, end: m.end, score: m.score })\n }\n\n return { detected_entities: detected, entities_count: detected.length }\n }\n\n /**\n * Detokenize text by replacing tokens with original values\n *\n * This method performs detokenization CLIENT-SIDE for better performance,\n * security, and to work offline. No API call is made.\n *\n * @param text - Tokenized text\n * @param mapping - Token mapping from tokenize response\n * @returns DetokenizeResponse with original text\n */\n detokenize(text: string, mapping: Record<string, string>): DetokenizeResponse {\n let result = text\n let replacements = 0\n\n // Sort tokens by length (longest first) to avoid partial replacements\n const sortedTokens = Object.keys(mapping).sort((a, b) => b.length - a.length)\n\n for (const token of sortedTokens) {\n const originalValue = mapping[token]\n const regex = new RegExp(token.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'g')\n const matches = result.match(regex)\n\n if (matches) {\n result = result.replace(regex, originalValue)\n replacements += matches.length\n }\n }\n\n return {\n text: result,\n replacements_made: replacements,\n }\n }\n\n /**\n * Redact (permanently remove) sensitive information from text\n *\n * WARNING: Redaction is irreversible - original data cannot be restored!\n *\n * When no API key is set (or `mode: \"local\"`), redaction runs locally\n * using the built-in regex scanner, replacing PII with `<Entity Name>` placeholders.\n *\n * @param text - Text to redact\n * @param config - Optional configuration (masking_char, entities)\n * @returns Promise with redacted text and detected entities\n */\n async redact(text: string, config?: RedactConfig): Promise<RedactResponse> {\n if (this.useLocal) {\n return this.redactLocal(text, config)\n }\n return this.request<RedactResponse>('/redact', 'POST', {\n text,\n ...config,\n })\n }\n\n private redactLocal(text: string, config?: RedactConfig): RedactResponse {\n const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold)\n const scanner = this.getScanner()\n const [redactedText, matches] = scanner.redact(text, resolved)\n\n const detected: DetectedEntity[] = []\n for (const m of matches) {\n if (threshold != null && m.score < threshold) continue\n detected.push({ type: m.entityType, text: m.text, start: m.start, end: m.end, score: m.score })\n }\n\n return { text: redactedText, detected_entities: detected, entities_count: detected.length }\n }\n\n /**\n * Mask (partially hide) sensitive information from text\n *\n * @param text - Text to mask\n * @param config - Optional configuration (chars_to_show, from_end, masking_char, entities)\n * @returns Promise with masked text and detected entities\n */\n async mask(text: string, config?: MaskConfig): Promise<MaskResponse> {\n if (this.useLocal) {\n return this.maskLocal(text, config)\n }\n return this.request<MaskResponse>('/mask', 'POST', {\n text,\n ...config,\n })\n }\n\n private maskLocal(text: string, config?: MaskConfig): MaskResponse {\n const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold)\n const scanner = this.getScanner()\n const result = scanner.mask(\n text,\n config?.chars_to_show ?? 3,\n config?.from_end ?? false,\n config?.masking_char ?? '*',\n resolved\n )\n\n const detected: DetectedEntity[] = result.matches\n .filter((m) => threshold == null || m.score >= threshold)\n .map((m) => ({\n type: m.entityType, text: m.text, start: m.start, end: m.end, score: m.score,\n }))\n\n return { text: result.text, detected_entities: detected, entities_count: detected.length }\n }\n\n /**\n * Synthesize (replace real data with synthetic fake data)\n *\n * When no API key is set (or `mode: \"local\"`), synthesis runs locally\n * using format-preserving random generation.\n *\n * @param text - Text to synthesize\n * @param config - Optional configuration (language, entities)\n * @returns Promise with synthetic text and detected entities\n */\n async synthesize(text: string, config?: SynthesizeConfig): Promise<SynthesizeResponse> {\n if (this.useLocal) {\n return this.synthesizeLocal(text, config)\n }\n return this.request<SynthesizeResponse>('/synthesize', 'POST', {\n text,\n ...config,\n })\n }\n\n private synthesizeLocal(text: string, config?: SynthesizeConfig): SynthesizeResponse {\n const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold)\n const scanner = this.getScanner()\n const result = scanner.synthesize(text, undefined, resolved)\n\n const detected: DetectedEntity[] = result.matches\n .filter((m) => threshold == null || m.score >= threshold)\n .map((m) => ({\n type: m.entityType, text: m.text, start: m.start, end: m.end, score: m.score,\n }))\n\n return { text: result.text, detected_entities: detected, entities_count: detected.length }\n }\n\n /**\n * Hash (replace with deterministic hash values)\n *\n * @param text - Text to hash\n * @param config - Optional configuration (hash_type, hash_prefix, hash_length, entities)\n * @returns Promise with hashed text and detected entities\n */\n async hash(text: string, config?: HashConfig): Promise<HashResponse> {\n if (this.useLocal) {\n return this.hashLocal(text, config)\n }\n return this.request<HashResponse>('/hash', 'POST', {\n text,\n ...config,\n })\n }\n\n private hashLocal(text: string, config?: HashConfig): HashResponse {\n const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold)\n const scanner = this.getScanner()\n const result = scanner.hash(\n text,\n config?.hash_type ?? 'sha256',\n config?.hash_prefix ?? 'HASH_',\n config?.hash_length ?? 16,\n resolved\n )\n\n const detected: DetectedEntity[] = result.matches\n .filter((m) => threshold == null || m.score >= threshold)\n .map((m) => ({\n type: m.entityType, text: m.text, start: m.start, end: m.end, score: m.score,\n }))\n\n return { text: result.text, detected_entities: detected, entities_count: detected.length }\n }\n\n /**\n * Encrypt (reversibly protect) sensitive data in text using AES encryption\n *\n * @param text - Text to encrypt\n * @param config - Optional configuration (encryption_key, entities)\n * @returns Promise with encrypted text and detected entities\n */\n async encrypt(text: string, config?: EncryptConfig): Promise<EncryptResponse> {\n if (this.useLocal) {\n return this.encryptLocal(text, config)\n }\n return this.request<EncryptResponse>('/encrypt', 'POST', {\n text,\n ...config,\n })\n }\n\n private encryptLocal(text: string, config?: EncryptConfig): EncryptResponse {\n if (!config?.encryption_key) {\n throw new Error('encryption_key is required for local encryption mode')\n }\n const { entities: resolved, threshold } = this.resolvePolicy(config?.policy, config?.entities, config?.score_threshold)\n const scanner = this.getScanner()\n const result = scanner.encrypt(text, config.encryption_key, resolved)\n\n const detected: DetectedEntity[] = result.matches\n .filter((m) => threshold == null || m.score >= threshold)\n .map((m) => ({\n type: m.entityType, text: m.text, start: m.start, end: m.end, score: m.score,\n }))\n\n return { text: result.text, detected_entities: detected, entities_count: detected.length }\n }\n\n // ===== Batch methods =====\n\n /**\n * Tokenize multiple texts in a single request\n * @param texts - Array of texts to tokenize (max 100)\n * @param config - Optional configuration\n * @returns Promise with batch results\n */\n async tokenizeBatch(texts: string[], config?: TokenizeConfig): Promise<BatchResponse> {\n return this.request<BatchResponse>('/tokenize', 'POST', {\n texts,\n ...config,\n })\n }\n\n /**\n * Detect PII in multiple texts in a single request\n * @param texts - Array of texts to analyze (max 100)\n * @param config - Optional configuration\n * @returns Promise with batch results\n */\n async detectBatch(texts: string[], config?: DetectConfig): Promise<BatchResponse> {\n return this.request<BatchResponse>('/detect', 'POST', {\n texts,\n ...config,\n })\n }\n\n /**\n * Redact PII from multiple texts in a single request\n * @param texts - Array of texts to redact (max 100)\n * @param config - Optional configuration\n * @returns Promise with batch results\n */\n async redactBatch(texts: string[], config?: RedactConfig): Promise<BatchResponse> {\n return this.request<BatchResponse>('/redact', 'POST', {\n texts,\n ...config,\n })\n }\n\n /**\n * Mask PII in multiple texts in a single request\n * @param texts - Array of texts to mask (max 100)\n * @param config - Optional configuration\n * @returns Promise with batch results\n */\n async maskBatch(texts: string[], config?: MaskConfig): Promise<BatchResponse> {\n return this.request<BatchResponse>('/mask', 'POST', {\n texts,\n ...config,\n })\n }\n\n /**\n * Synthesize multiple texts in a single request\n * @param texts - Array of texts to synthesize (max 100)\n * @param config - Optional configuration\n * @returns Promise with batch results\n */\n async synthesizeBatch(texts: string[], config?: SynthesizeConfig): Promise<BatchResponse> {\n return this.request<BatchResponse>('/synthesize', 'POST', {\n texts,\n ...config,\n })\n }\n\n /**\n * Hash PII in multiple texts in a single request\n * @param texts - Array of texts to hash (max 100)\n * @param config - Optional configuration\n * @returns Promise with batch results\n */\n async hashBatch(texts: string[], config?: HashConfig): Promise<BatchResponse> {\n return this.request<BatchResponse>('/hash', 'POST', {\n texts,\n ...config,\n })\n }\n\n /**\n * Encrypt PII in multiple texts in a single request\n * @param texts - Array of texts to encrypt (max 100)\n * @param config - Optional configuration\n * @returns Promise with batch results\n */\n async encryptBatch(texts: string[], config?: EncryptConfig): Promise<BatchResponse> {\n return this.request<BatchResponse>('/encrypt', 'POST', {\n texts,\n ...config,\n })\n }\n}\n","export { Blindfold } from './client'\nexport type {\n BlindfoldConfig,\n DetectConfig,\n DetectResponse,\n TokenizeConfig,\n TokenizeResponse,\n DetokenizeResponse,\n DetectedEntity,\n BatchResponse,\n APIErrorResponse,\n} from './types'\nexport { BlindfoldError, AuthenticationError, APIError, NetworkError } from './errors'\nexport { PIIScanner, EntityType } from './regex'\nexport type { PIIMatch } from './regex'\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { EntityType$1 as EntityType, PIIMatch, PIIScanner$1 as PIIScanner, PIIScannerOptions } from "../index-CsE6Vhax.mjs";
2
+ export { EntityType, PIIMatch, PIIScanner, PIIScannerOptions };
@@ -0,0 +1,2 @@
1
+ import { EntityType, PIIMatch, PIIScanner, PIIScannerOptions } from "../index-Dfv8zV_d.js";
2
+ export { EntityType, PIIMatch, PIIScanner, PIIScannerOptions };
@@ -0,0 +1,5 @@
1
+ const require_regex = require('../regex-BEaK0E7Y.js');
2
+
3
+ require_regex.init_regex();
4
+ exports.EntityType = require_regex.EntityType
5
+ exports.PIIScanner = require_regex.PIIScanner
@@ -0,0 +1,4 @@
1
+ import { EntityType, PIIScanner, init_regex } from "../regex-ByjZg3Zy.mjs";
2
+
3
+ init_regex();
4
+ export { EntityType, PIIScanner };