hazo_secure 1.1.0 → 1.3.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/CHANGE_LOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Change Log
2
2
 
3
+ ## 1.3.0 — 2026-06-12
4
+
5
+ ### New: `hazo_secure/secrets` — SecretsProvider
6
+
7
+ - **`SecretsProvider` interface** with `get(name)` / `resolve(names[])` methods.
8
+ - **`EnvSecretsProvider`** — reads from env vars (`${prefix}_${NAME}`; default prefix `HAZO_SECRET`).
9
+ - **`StaticSecretsProvider`** — in-memory map; primary use is tests.
10
+ - **`LookupSecretsProvider`** — caller-supplied `(name) => string|undefined` function.
11
+ - **`SecretsError`** — extends `HazoError`, code `HAZO_SECURE_SECRET_NOT_FOUND` (httpStatus 400, retryable false).
12
+ - New `./secrets` subpath export.
13
+
3
14
  ## 1.1.0 — 2026-05-29
4
15
 
5
16
  ### hazo workspace v2 — Wave 2 migration
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # hazo_secure
2
2
 
3
- Security & compliance primitives for Node.js / Next.js apps. Four independent subpath modules — import only the ones you need.
3
+ Security & compliance primitives for Node.js / Next.js apps. Seven independent subpath modules — import only the ones you need.
4
4
 
5
5
  | Subpath | Purpose |
6
6
  |---|---|
@@ -9,6 +9,8 @@ Security & compliance primitives for Node.js / Next.js apps. Four independent su
9
9
  | `hazo_secure/crypto` | AES-256-GCM field-level encryption with key versioning and AAD support |
10
10
  | `hazo_secure/gdpr` | Exporter / anonymiser registry + lifecycle orchestrator. Uses `hazo_jobs` for async export, `hazo_files` for ZIP delivery |
11
11
  | `hazo_secure/csrf` | Double-submit CSRF protection for Next.js routes — token generation, cookie header, constant-time verification |
12
+ | `hazo_secure/mask` | PII masking and anonymization transforms (redact, hash, tokenize, fake_name, jitter_date, …) |
13
+ | `hazo_secure/secrets` | Provider-pattern secret resolution — env vars, static map, or custom lookup |
12
14
 
13
15
  ## Install
14
16
 
@@ -16,7 +18,7 @@ Security & compliance primitives for Node.js / Next.js apps. Four independent su
16
18
  npm install hazo_secure
17
19
  ```
18
20
 
19
- `hazo_logs` is a required peer dep. `hazo_connect`, `hazo_jobs`, and `hazo_files` are optional peers — `hazo_connect` is needed for `ConnectRateLimitStore`; `hazo_jobs` and `hazo_files` are only needed for `hazo_secure/gdpr`.
21
+ `hazo_core` is a required peer dep. `hazo_logs`, `hazo_connect`, `hazo_jobs`, `hazo_files`, and `hazo_ui` are optional peers — `hazo_connect` is needed for `ConnectRateLimitStore`; `hazo_jobs` and `hazo_files` are only needed for `hazo_secure/gdpr`.
20
22
 
21
23
  ## Quick start
22
24
 
@@ -56,6 +58,24 @@ const stored = await encryptField(plaintext, { keys, aad: `user:${userId}` });
56
58
  const recovered = await decryptField(stored, { keys });
57
59
  ```
58
60
 
61
+ ### Resolve application secrets
62
+
63
+ ```ts
64
+ import { EnvSecretsProvider, StaticSecretsProvider, LookupSecretsProvider } from "hazo_secure/secrets";
65
+
66
+ // Reads from env vars: HAZO_SECRET_DB_PASSWORD, HAZO_SECRET_STRIPE_KEY, …
67
+ const secrets = new EnvSecretsProvider();
68
+ const { db_password, stripe_key } = await secrets.resolve(["db_password", "stripe_key"]);
69
+
70
+ // Tests — in-memory map
71
+ const mock = new StaticSecretsProvider({ api_key: "test-key" });
72
+
73
+ // Custom backend (KMS, HashiCorp Vault, etc.)
74
+ const vault = new LookupSecretsProvider(async (name) => fetchFromVault(name));
75
+ ```
76
+
77
+ `SecretsError` (code `HAZO_SECURE_SECRET_NOT_FOUND`) is thrown when a name can't be resolved. Use a custom prefix: `new EnvSecretsProvider("MY_APP")` → reads `MY_APP_<NAME>`.
78
+
59
79
  ### Register GDPR exporters
60
80
 
61
81
  ```ts
@@ -152,6 +172,8 @@ await gdpr.anonymiseUser({ userId });
152
172
  | `hazo_secure/fetch` | ⚠️ pre-flight only | ✅ full protection | Edge: host/IP checks only; Node: + undici connect-time validation |
153
173
  | `hazo_secure/crypto` | ❌ | ✅ | Uses `node:crypto` |
154
174
  | `hazo_secure/gdpr` | ❌ | ✅ | Async iterables + optional hazo_connect |
175
+ | `hazo_secure/mask` | ✅ | ✅ | Pure transforms — no I/O |
176
+ | `hazo_secure/secrets` | ✅ | ✅ | Pure async — no Node built-ins |
155
177
 
156
178
  **Node.js ≥ 18 required** for all Node-only subpaths.
157
179
 
@@ -34,6 +34,20 @@ Default in-memory store works out of the box. For multi-instance deployments, su
34
34
  - **Custom** — implement the `KeyProvider` interface directly
35
35
  3. Decide AAD scheme — usually `domain:entityId` (e.g. `health:${personId}`)
36
36
 
37
+ ### `hazo_secure/secrets`
38
+
39
+ No setup. Choose a provider at startup:
40
+
41
+ - **`EnvSecretsProvider(prefix?)`** — reads from `${prefix}_${NAME}` env vars (default prefix: `HAZO_SECRET`). Set each secret as an env var on your deployment platform.
42
+ - **`StaticSecretsProvider(map)`** — in-memory; use in tests only.
43
+ - **`LookupSecretsProvider(fn)`** — caller-supplied sync or async lookup; useful for KMS / Vault integrations.
44
+
45
+ ```ts
46
+ import { EnvSecretsProvider } from "hazo_secure/secrets";
47
+ const secrets = new EnvSecretsProvider();
48
+ const value = await secrets.get("db_password"); // reads HAZO_SECRET_DB_PASSWORD
49
+ ```
50
+
37
51
  ### `hazo_secure/gdpr`
38
52
 
39
53
  1. Install optional peers if you'll use async export and ZIP delivery:
@@ -65,4 +79,7 @@ node --input-type=module -e "import * as m from 'hazo_secure/crypto'; console.lo
65
79
 
66
80
  # GDPR — expect: createGdprRegistry, GdprRegistryError
67
81
  node --input-type=module -e "import * as m from 'hazo_secure/gdpr'; console.log(Object.keys(m).join(', '))"
82
+
83
+ # Secrets — expect: SecretsError, SecretsProvider (type), EnvSecretsProvider, StaticSecretsProvider, LookupSecretsProvider
84
+ node --input-type=module -e "import * as m from 'hazo_secure/secrets'; console.log(Object.keys(m).join(', '))"
68
85
  ```
@@ -140,8 +140,41 @@ declare class StaticKeyProvider implements KeyProvider {
140
140
  }>;
141
141
  byId(keyId: string): Promise<Uint8Array>;
142
142
  }
143
+ /**
144
+ * Lookup-function-backed `KeyProvider` for config-file (INI) key sources.
145
+ * Reads keys from a caller-supplied synchronous lookup function, decoding
146
+ * each value from base64 to a 32-byte AES-256 key.
147
+ *
148
+ * Expected key naming convention (mirrors hazo_config INI layout):
149
+ * `current` → the current key id (e.g. `"v1"`)
150
+ * `key_<id>` → base64-encoded 32-byte key (e.g. `key_v1=<base64>`)
151
+ *
152
+ * Example backed by hazo_config:
153
+ * ```ts
154
+ * const cfg = getConfig('crypto.myapp'); // hazo_config, not imported here
155
+ * const keys = new LookupKeyProvider(name => cfg.get(name));
156
+ * ```
157
+ *
158
+ * hazo_secure takes no dependency on hazo_config — the caller supplies the
159
+ * lookup, keeping this library config-agnostic. `Buffer.from(value, 'base64')`
160
+ * handles standard base64; keys decoded to anything other than 32 bytes are
161
+ * rejected with `invalid_key`.
162
+ *
163
+ * Decoded keys are memoised per id so the lookup runs once per key per
164
+ * process lifetime.
165
+ */
166
+ declare class LookupKeyProvider implements KeyProvider {
167
+ private readonly lookup;
168
+ private readonly cache;
169
+ constructor(lookup: (name: string) => string | undefined);
170
+ current(): Promise<{
171
+ keyId: string;
172
+ key: Uint8Array;
173
+ }>;
174
+ byId(keyId: string): Promise<Uint8Array>;
175
+ }
143
176
  /** Helper: build a `aad` string with a stable, parser-friendly shape.
144
177
  * Convention: `<table>:<entityId>:<field>` — collision-free across tables. */
145
178
  declare function aadFor(table: string, entityId: string, field: string): string;
146
179
 
147
- export { CryptoError, type CryptoErrorCode, type EncryptedField, EnvKeyProvider, HttpKeyProvider, type HttpKeyProviderOptions, type KeyProvider, StaticKeyProvider, aadFor, decryptField, encryptField };
180
+ export { CryptoError, type CryptoErrorCode, type EncryptedField, EnvKeyProvider, HttpKeyProvider, type HttpKeyProviderOptions, type KeyProvider, LookupKeyProvider, StaticKeyProvider, aadFor, decryptField, encryptField };
@@ -218,6 +218,44 @@ var StaticKeyProvider = class {
218
218
  return key;
219
219
  }
220
220
  };
221
+ var LookupKeyProvider = class {
222
+ constructor(lookup) {
223
+ this.lookup = lookup;
224
+ }
225
+ lookup;
226
+ cache = /* @__PURE__ */ new Map();
227
+ async current() {
228
+ const keyId = this.lookup("current");
229
+ if (!keyId) {
230
+ throw new CryptoError(
231
+ "LookupKeyProvider: no 'current' key id from lookup",
232
+ "missing_env_key"
233
+ );
234
+ }
235
+ const key = await this.byId(keyId);
236
+ return { keyId, key };
237
+ }
238
+ async byId(keyId) {
239
+ if (this.cache.has(keyId)) return this.cache.get(keyId);
240
+ const lookupName = `key_${keyId}`;
241
+ const raw = this.lookup(lookupName);
242
+ if (!raw) {
243
+ throw new CryptoError(
244
+ `LookupKeyProvider: no key material for id ${keyId} (looked up "${lookupName}")`,
245
+ "key_not_found"
246
+ );
247
+ }
248
+ const buf = Buffer.from(raw, "base64");
249
+ if (buf.byteLength !== KEY_LEN2) {
250
+ throw new CryptoError(
251
+ `${lookupName} decodes to ${buf.byteLength} bytes; AES-256-GCM needs ${KEY_LEN2}`,
252
+ "invalid_key"
253
+ );
254
+ }
255
+ this.cache.set(keyId, buf);
256
+ return buf;
257
+ }
258
+ };
221
259
  function aadFor(table, entityId, field) {
222
260
  return `${table}:${entityId}:${field}`;
223
261
  }
@@ -225,6 +263,7 @@ export {
225
263
  CryptoError,
226
264
  EnvKeyProvider,
227
265
  HttpKeyProvider,
266
+ LookupKeyProvider,
228
267
  StaticKeyProvider,
229
268
  aadFor,
230
269
  decryptField,
@@ -0,0 +1,48 @@
1
+ /**
2
+ * mask_email — replace with deterministic masked email.
3
+ * alice@example.com → a3f9b12c1d2e@masked.example
4
+ * Same source → same masked value (FK survival).
5
+ */
6
+ declare function mask_email(value: unknown, _col: string, maskKey: string): unknown;
7
+ /**
8
+ * mask_phone — replace with deterministic masked phone.
9
+ * Preserves leading + and length pattern. +1-555-0001 → +1-XXX-a3f9b1
10
+ */
11
+ declare function mask_phone(value: unknown, _col: string, maskKey: string): unknown;
12
+ declare function fake_name(value: unknown, _col: string, maskKey: string): unknown;
13
+ /**
14
+ * jitter_date — add a deterministic date offset (±0–180 days) to a date string.
15
+ * Preserves approximate era; different source values → different offsets.
16
+ */
17
+ declare function jitter_date(value: unknown, _col: string, maskKey: string): unknown;
18
+ /**
19
+ * hash — replace with HMAC-SHA256 hex hash of value.
20
+ * Fully opaque; same source → same hash.
21
+ */
22
+ declare function hash(value: unknown, col: string, maskKey: string): unknown;
23
+ /**
24
+ * tokenize — replace with a stable short token (FK-safe).
25
+ * tok_<12 hex chars> — same source → same token.
26
+ */
27
+ declare function tokenize(value: unknown, col: string, maskKey: string): unknown;
28
+ /**
29
+ * drop — always returns null (secret dropped, not masked).
30
+ * Used for passwords, tokens, etc.
31
+ */
32
+ declare function drop(_value: unknown, _col: string, _maskKey: string): unknown;
33
+ /**
34
+ * nullify — replace with null (less aggressive than drop; preserves column semantics).
35
+ */
36
+ declare function nullify(_value: unknown, _col: string, _maskKey: string): unknown;
37
+ /**
38
+ * redact_pii — NER-based PII redaction (wink-nlp lite model).
39
+ * Detects named entities (dates, cardinals, etc.) and replaces them with [REDACTED_TYPE].
40
+ * Deterministic: same input + same model → same output.
41
+ * maskKey is accepted but not used (redaction is model-driven, not key-driven).
42
+ */
43
+ declare function redact_pii(value: unknown, _col: string, _maskKey: string): unknown;
44
+ /** Registry of all built-in transforms */
45
+ declare const BUILTIN_TRANSFORMS: Record<string, (value: unknown, col: string, maskKey: string) => unknown>;
46
+ type MaskTransformFn = (value: unknown, col: string, maskKey: string) => unknown;
47
+
48
+ export { BUILTIN_TRANSFORMS, type MaskTransformFn, drop, fake_name, hash, jitter_date, mask_email, mask_phone, nullify, redact_pii, tokenize };
@@ -0,0 +1,115 @@
1
+ // src/mask/index.ts
2
+ import { createHmac } from "crypto";
3
+ import { createRequire } from "module";
4
+ function hmacHex(value, maskKey, context) {
5
+ return createHmac("sha256", maskKey).update(`${context}:${value}`).digest("hex");
6
+ }
7
+ function hmacToken(value, maskKey, context) {
8
+ return hmacHex(value, maskKey, context).slice(0, 12);
9
+ }
10
+ function mask_email(value, _col, maskKey) {
11
+ if (value === null || value === void 0) return value;
12
+ const str = String(value);
13
+ const atIdx = str.indexOf("@");
14
+ const domain = atIdx >= 0 ? str.slice(atIdx + 1) : "example.com";
15
+ const token = hmacToken(str, maskKey, "email");
16
+ return `${token}@masked.${domain}`;
17
+ }
18
+ function mask_phone(value, _col, maskKey) {
19
+ if (value === null || value === void 0) return value;
20
+ const str = String(value);
21
+ const token = hmacToken(str, maskKey, "phone").slice(0, 6);
22
+ const hasPlus = str.startsWith("+");
23
+ const digits = str.replace(/[^\d]/g, "");
24
+ const prefix = digits.length > 4 ? digits.slice(0, 2) : "00";
25
+ return `${hasPlus ? "+" : ""}${prefix}-XXX-${token}`;
26
+ }
27
+ var FIRST_NAMES = ["Alex", "Blake", "Casey", "Dana", "Ellis", "Finley", "Gray", "Harper", "Indigo", "Jordan"];
28
+ var LAST_NAMES = ["Smith", "Jones", "Williams", "Brown", "Davis", "Miller", "Wilson", "Moore", "Taylor", "Anderson"];
29
+ function fake_name(value, _col, maskKey) {
30
+ if (value === null || value === void 0) return value;
31
+ const str = String(value);
32
+ const h = hmacHex(str, maskKey, "name");
33
+ const firstIdx = parseInt(h.slice(0, 2), 16) % FIRST_NAMES.length;
34
+ const lastIdx = parseInt(h.slice(2, 4), 16) % LAST_NAMES.length;
35
+ return `${FIRST_NAMES[firstIdx]} ${LAST_NAMES[lastIdx]}`;
36
+ }
37
+ function jitter_date(value, _col, maskKey) {
38
+ if (value === null || value === void 0) return value;
39
+ const str = String(value);
40
+ const h = hmacHex(str, maskKey, "date");
41
+ const jitterDays = parseInt(h.slice(0, 4), 16) % 361 - 180;
42
+ const date = new Date(str);
43
+ if (isNaN(date.getTime())) return value;
44
+ date.setDate(date.getDate() + jitterDays);
45
+ return date.toISOString().slice(0, 10);
46
+ }
47
+ function hash(value, col, maskKey) {
48
+ if (value === null || value === void 0) return value;
49
+ return hmacHex(String(value), maskKey, col);
50
+ }
51
+ function tokenize(value, col, maskKey) {
52
+ if (value === null || value === void 0) return value;
53
+ return `tok_${hmacToken(String(value), maskKey, col)}`;
54
+ }
55
+ function drop(_value, _col, _maskKey) {
56
+ return null;
57
+ }
58
+ function nullify(_value, _col, _maskKey) {
59
+ return null;
60
+ }
61
+ var _esmReq = createRequire(import.meta.url);
62
+ var _nlpInstance = null;
63
+ var _nlpIts = null;
64
+ function getNlp() {
65
+ if (_nlpInstance) return { nlp: _nlpInstance, its: _nlpIts };
66
+ try {
67
+ const winkNLP = _esmReq("wink-nlp");
68
+ const model = _esmReq("wink-eng-lite-web-model");
69
+ const nlp = winkNLP(model, ["ner"]);
70
+ _nlpInstance = nlp;
71
+ _nlpIts = nlp.its;
72
+ return { nlp, its: nlp.its };
73
+ } catch {
74
+ return null;
75
+ }
76
+ }
77
+ function redact_pii(value, _col, _maskKey) {
78
+ if (value === null || value === void 0) return value;
79
+ const text = String(value);
80
+ const ctx = getNlp();
81
+ if (!ctx) return value;
82
+ const doc = ctx.nlp.readDoc(text);
83
+ const replacements = [];
84
+ doc.entities().each((e) => {
85
+ replacements.push({ text: e.out(), type: e.out(ctx.its.type) });
86
+ });
87
+ let result = text;
88
+ for (const { text: entityText, type } of replacements) {
89
+ result = result.replaceAll(entityText, `[REDACTED_${type}]`);
90
+ }
91
+ return result;
92
+ }
93
+ var BUILTIN_TRANSFORMS = {
94
+ mask_email,
95
+ mask_phone,
96
+ fake_name,
97
+ jitter_date,
98
+ hash,
99
+ tokenize,
100
+ drop,
101
+ nullify,
102
+ redact_pii
103
+ };
104
+ export {
105
+ BUILTIN_TRANSFORMS,
106
+ drop,
107
+ fake_name,
108
+ hash,
109
+ jitter_date,
110
+ mask_email,
111
+ mask_phone,
112
+ nullify,
113
+ redact_pii,
114
+ tokenize
115
+ };
@@ -0,0 +1,41 @@
1
+ import { HazoError } from 'hazo_core';
2
+
3
+ type SecretsErrorCode = 'HAZO_SECURE_SECRET_NOT_FOUND';
4
+ declare class SecretsError extends HazoError {
5
+ readonly code: SecretsErrorCode;
6
+ constructor(code: SecretsErrorCode, message: string);
7
+ }
8
+ interface SecretsProvider {
9
+ /** Resolve multiple secret names at once. Returns all resolved values.
10
+ * Throws SecretsError (HAZO_SECURE_SECRET_NOT_FOUND) for any missing name. */
11
+ resolve(names: string[]): Promise<Record<string, string>>;
12
+ /** Resolve a single secret. Throws if absent. */
13
+ get(name: string): Promise<string>;
14
+ }
15
+ /**
16
+ * Reads secrets from environment variables.
17
+ * Variable name = `${prefix}_${NAME.toUpperCase()}` (underscores preserved).
18
+ * Default prefix: `HAZO_SECRET`
19
+ */
20
+ declare class EnvSecretsProvider implements SecretsProvider {
21
+ private readonly prefix;
22
+ constructor(prefix?: string);
23
+ get(name: string): Promise<string>;
24
+ resolve(names: string[]): Promise<Record<string, string>>;
25
+ }
26
+ /** In-memory map. Primary use: tests. */
27
+ declare class StaticSecretsProvider implements SecretsProvider {
28
+ private readonly map;
29
+ constructor(secrets: Record<string, string>);
30
+ get(name: string): Promise<string>;
31
+ resolve(names: string[]): Promise<Record<string, string>>;
32
+ }
33
+ /** Caller-supplied lookup function. Useful for custom secret backends. */
34
+ declare class LookupSecretsProvider implements SecretsProvider {
35
+ private readonly lookup;
36
+ constructor(lookup: (name: string) => string | undefined | Promise<string | undefined>);
37
+ get(name: string): Promise<string>;
38
+ resolve(names: string[]): Promise<Record<string, string>>;
39
+ }
40
+
41
+ export { EnvSecretsProvider, LookupSecretsProvider, SecretsError, type SecretsProvider, StaticSecretsProvider };
@@ -0,0 +1,84 @@
1
+ // src/secrets/index.ts
2
+ import { HazoError } from "hazo_core";
3
+ var SecretsError = class extends HazoError {
4
+ constructor(code, message) {
5
+ super({ code, pkg: "hazo_secure", message, httpStatus: 400, retryable: false });
6
+ this.name = "SecretsError";
7
+ }
8
+ };
9
+ var EnvSecretsProvider = class {
10
+ prefix;
11
+ constructor(prefix = "HAZO_SECRET") {
12
+ this.prefix = prefix;
13
+ }
14
+ async get(name) {
15
+ const envKey = `${this.prefix}_${name.toUpperCase()}`;
16
+ const val = process.env[envKey];
17
+ if (val === void 0) {
18
+ throw new SecretsError(
19
+ "HAZO_SECURE_SECRET_NOT_FOUND",
20
+ `Secret "${name}" not found (checked env var ${envKey})`
21
+ );
22
+ }
23
+ return val;
24
+ }
25
+ async resolve(names) {
26
+ const out = {};
27
+ for (const name of names) {
28
+ out[name] = await this.get(name);
29
+ }
30
+ return out;
31
+ }
32
+ };
33
+ var StaticSecretsProvider = class {
34
+ map;
35
+ constructor(secrets) {
36
+ this.map = new Map(Object.entries(secrets));
37
+ }
38
+ async get(name) {
39
+ const val = this.map.get(name);
40
+ if (val === void 0) {
41
+ throw new SecretsError(
42
+ "HAZO_SECURE_SECRET_NOT_FOUND",
43
+ `Secret "${name}" not found in static map`
44
+ );
45
+ }
46
+ return val;
47
+ }
48
+ async resolve(names) {
49
+ const out = {};
50
+ for (const name of names) {
51
+ out[name] = await this.get(name);
52
+ }
53
+ return out;
54
+ }
55
+ };
56
+ var LookupSecretsProvider = class {
57
+ lookup;
58
+ constructor(lookup) {
59
+ this.lookup = lookup;
60
+ }
61
+ async get(name) {
62
+ const val = await this.lookup(name);
63
+ if (val === void 0) {
64
+ throw new SecretsError(
65
+ "HAZO_SECURE_SECRET_NOT_FOUND",
66
+ `Secret "${name}" not found via lookup`
67
+ );
68
+ }
69
+ return val;
70
+ }
71
+ async resolve(names) {
72
+ const out = {};
73
+ for (const name of names) {
74
+ out[name] = await this.get(name);
75
+ }
76
+ return out;
77
+ }
78
+ };
79
+ export {
80
+ EnvSecretsProvider,
81
+ LookupSecretsProvider,
82
+ SecretsError,
83
+ StaticSecretsProvider
84
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_secure",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Security & compliance primitives — SSRF-safe fetch, rate limiting, field-level encryption, GDPR orchestration, CSRF. Five subpath modules, pick the ones you need.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -36,6 +36,16 @@
36
36
  "types": "./dist/csrf/index.d.ts",
37
37
  "import": "./dist/csrf/index.js",
38
38
  "default": "./dist/csrf/index.js"
39
+ },
40
+ "./mask": {
41
+ "types": "./dist/mask/index.d.ts",
42
+ "import": "./dist/mask/index.js",
43
+ "default": "./dist/mask/index.js"
44
+ },
45
+ "./secrets": {
46
+ "types": "./dist/secrets/index.d.ts",
47
+ "import": "./dist/secrets/index.js",
48
+ "default": "./dist/secrets/index.js"
39
49
  }
40
50
  },
41
51
  "files": [
@@ -53,23 +63,25 @@
53
63
  "build": "tsup",
54
64
  "dev": "tsup --watch",
55
65
  "lint": "tsc --noEmit",
56
- "test": "jest",
57
- "test:watch": "jest --watch",
66
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest",
67
+ "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch",
58
68
  "dev:test-app": "npm run build && cd test-app && npm run dev",
59
69
  "build:test-app": "npm run build && cd test-app && npm run build",
60
70
  "install:test-app": "cd test-app && npm install",
61
71
  "prepublishOnly": "npm run build"
62
72
  },
63
73
  "dependencies": {
64
- "undici": "^7.3.0"
74
+ "undici": "^7.3.0",
75
+ "wink-nlp": "^2.3.1",
76
+ "wink-eng-lite-web-model": "^1.7.0"
65
77
  },
66
78
  "peerDependencies": {
67
- "hazo_core": "^1.0.0",
68
- "hazo_logs": "^2.0.0",
69
- "hazo_connect": "^3.0.0",
79
+ "hazo_core": "^1.1.0",
80
+ "hazo_logs": "^2.0.3",
81
+ "hazo_connect": "^3.5.1",
70
82
  "hazo_jobs": "^0.12.0",
71
- "hazo_files": "^3.0.0",
72
- "hazo_ui": "^3.1.0"
83
+ "hazo_files": "^3.1.0",
84
+ "hazo_ui": "^3.5.0"
73
85
  },
74
86
  "peerDependenciesMeta": {
75
87
  "hazo_logs": {
@@ -91,16 +103,18 @@
91
103
  "devDependencies": {
92
104
  "@types/jest": "^30.0.0",
93
105
  "@types/node": "^22.10.0",
94
- "hazo_core": "^1.0.0",
95
- "hazo_logs": "^2.0.1",
96
- "hazo_connect": "^3.0.0",
106
+ "hazo_core": "^1.1.0",
107
+ "hazo_logs": "^2.0.3",
108
+ "hazo_connect": "^3.5.1",
97
109
  "hazo_jobs": "^0.12.0",
98
- "hazo_files": "^3.0.0",
99
- "hazo_ui": "^3.1.0",
110
+ "hazo_files": "^3.1.0",
111
+ "hazo_ui": "^3.5.0",
100
112
  "jest": "^30.2.0",
101
113
  "ts-jest": "^29.4.5",
102
114
  "tsup": "^8.5.1",
103
115
  "typescript": "^5.7.2",
116
+ "wink-nlp": "^2.3.1",
117
+ "wink-eng-lite-web-model": "^1.7.0",
104
118
  "zod": "^3.23.8"
105
119
  },
106
120
  "author": "Pubs Abayasiri",