asherah 4.0.34 → 4.0.35

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.
Files changed (4) hide show
  1. package/README.md +274 -186
  2. package/index.d.ts +343 -16
  3. package/npm/index.js +1 -0
  4. package/package.json +9 -9
package/README.md CHANGED
@@ -14,260 +14,348 @@ npm install asherah
14
14
 
15
15
  Requires Node.js >= 18.
16
16
 
17
- ## Quick Start (Static API)
17
+ ## Choosing an API style
18
18
 
19
- The static API uses a global singleton. Call `setup()` once, then `encrypt`/`decrypt` from anywhere.
19
+ Two API styles are exposed; both are fully supported and produce the same
20
+ wire format. New code should prefer the **Factory / Session API**.
21
+
22
+ | Style | When to use |
23
+ |---|---|
24
+ | **Static / module-level** (`asherah.setup`, `asherah.encrypt`, …) | Drop-in compatibility with the canonical `godaddy/asherah-node` package. Simplest call surface. Singleton lifecycle (`setup()` once, `shutdown()` once). |
25
+ | **Factory / Session** (`new SessionFactory(...)`, `factory.getSession(...)`) | Recommended for new code. Explicit lifecycle, no hidden singleton, multi-tenant isolation is obvious in code. |
26
+
27
+ A complete runnable example exercising both styles plus async, log hook, and
28
+ metrics hook is in [`samples/node/index.mjs`](../samples/node/index.mjs).
29
+
30
+ ## Quick start (static API)
20
31
 
21
32
  ```js
22
33
  const asherah = require('asherah');
23
34
 
24
- // Static master key for local development only.
25
- // In production, use kms: 'aws' with a region map.
26
- process.env.STATIC_MASTER_KEY_HEX = '22'.repeat(32);
35
+ process.env.STATIC_MASTER_KEY_HEX = '22'.repeat(32); // testing only
27
36
 
28
37
  asherah.setup({
29
38
  serviceName: 'my-service',
30
39
  productId: 'my-product',
31
- metastore: 'memory', // testing only
32
- kms: 'static', // testing only
33
- enableSessionCaching: true,
40
+ metastore: 'memory', // testing only — use 'rdbms' or 'dynamodb' in production
41
+ kms: 'static', // testing only — use 'aws' in production
34
42
  });
35
43
 
36
- // Encrypt raw bytes
37
- const ciphertext = asherah.encrypt('my-partition', Buffer.from('secret data'));
38
- const plaintext = asherah.decrypt('my-partition', ciphertext);
39
- console.log(plaintext.toString()); // 'secret data'
40
-
41
- // Or use the string convenience methods
42
- const ct = asherah.encryptString('my-partition', 'hello world');
43
- const pt = asherah.decryptString('my-partition', ct);
44
- console.log(pt); // 'hello world'
44
+ const ct = asherah.encryptString('user-42', 'secret');
45
+ const pt = asherah.decryptString('user-42', ct);
45
46
 
46
47
  asherah.shutdown();
47
48
  ```
48
49
 
49
- ## Session-Based API
50
-
51
- The `SessionFactory` / `AsherahSession` pattern is preferred for production. It
52
- avoids the global singleton and gives you explicit control over session
53
- lifetimes.
50
+ ## Quick start (factory / session API)
54
51
 
55
52
  ```js
56
53
  const { SessionFactory } = require('asherah');
57
54
 
58
- process.env.STATIC_MASTER_KEY_HEX = '22'.repeat(32);
59
-
60
55
  const factory = new SessionFactory({
61
56
  serviceName: 'my-service',
62
57
  productId: 'my-product',
63
- metastore: 'memory', // testing only
64
- kms: 'static', // testing only
58
+ metastore: 'memory',
59
+ kms: 'static',
65
60
  });
61
+ const session = factory.getSession('user-42');
62
+ try {
63
+ const ct = session.encryptString('secret');
64
+ const pt = session.decryptString(ct);
65
+ } finally {
66
+ session.close();
67
+ factory.close();
68
+ }
69
+ ```
66
70
 
67
- const session = factory.getSession('my-partition');
68
-
69
- const ct = session.encrypt(Buffer.from('secret'));
70
- const pt = session.decrypt(ct);
71
- console.log(pt.toString()); // 'secret'
71
+ ## Async API
72
72
 
73
- // String variants
74
- const ct2 = session.encryptString('hello');
75
- const pt2 = session.decryptString(ct2);
76
- console.log(pt2); // 'hello'
73
+ Every sync function has a `*Async` counterpart that returns a `Promise` and
74
+ runs on the Rust tokio runtime — the Node event loop is not blocked.
77
75
 
78
- session.close();
79
- factory.close();
76
+ ```js
77
+ await asherah.setupAsync(config);
78
+ const ct = await asherah.encryptStringAsync('user-42', 'secret');
79
+ const pt = await asherah.decryptStringAsync('user-42', ct);
80
+ await asherah.shutdownAsync();
80
81
  ```
81
82
 
82
- You can also create a factory from environment variables:
83
+ | Metastore | Async path | Blocks event loop? |
84
+ |-----------|------------|---------------------|
85
+ | In-memory | tokio worker thread | No |
86
+ | DynamoDB | true async AWS SDK calls on tokio | No |
87
+ | MySQL | `spawn_blocking` (sync driver on tokio thread pool) | No |
88
+ | Postgres | `spawn_blocking` (sync driver on tokio thread pool) | No |
83
89
 
84
- ```js
85
- const factory = SessionFactory.fromEnv();
86
- ```
90
+ Tradeoff: ~12µs async vs ~1µs sync per call (hot cache, 64 B payload). Use
91
+ sync in tight loops where latency matters; async when you need to keep the
92
+ event loop responsive.
87
93
 
88
- ## Async API
94
+ ## Observability hooks
95
+
96
+ ### Log hook
89
97
 
90
- Every sync function has an async counterpart that returns a Promise and never
91
- blocks the Node.js event loop.
98
+ Receive every log event from the Rust core (encrypt/decrypt path, metastore
99
+ drivers, KMS clients).
92
100
 
93
101
  ```js
94
- const asherah = require('asherah');
102
+ asherah.setLogHook((event) => {
103
+ // event = { level, message, target }
104
+ // level ∈ 'trace' | 'debug' | 'info' | 'warn' | 'error'
105
+ if (event.level === 'warn' || event.level === 'error') {
106
+ console.error(`[asherah ${event.level}] ${event.message}`);
107
+ }
108
+ });
95
109
 
96
- process.env.STATIC_MASTER_KEY_HEX = '22'.repeat(32);
110
+ // later, to deregister:
111
+ asherah.setLogHook(null);
112
+ ```
97
113
 
98
- await asherah.setupAsync({
99
- serviceName: 'my-service',
100
- productId: 'my-product',
101
- metastore: 'memory', // testing only
102
- kms: 'static', // testing only
114
+ The snake_case alias `set_log_hook` also accepts the canonical
115
+ `(level: number, message: string)` signature for backward compatibility with
116
+ the Go-based `asherah` npm package.
117
+
118
+ ```js
119
+ asherah.set_log_hook((level, message) => {
120
+ // level is a number 0..4 (0=trace, 1=debug, 2=info, 3=warn, 4=error)
121
+ console.log(`[level ${level}] ${message}`);
103
122
  });
123
+ ```
104
124
 
105
- const ct = await asherah.encryptStringAsync('my-partition', 'secret');
106
- const pt = await asherah.decryptStringAsync('my-partition', ct);
107
- console.log(pt); // 'secret'
125
+ Log events are delivered via N-API ThreadsafeFunction — they run on the Node
126
+ main thread, so synchronous code in the callback is safe.
108
127
 
109
- await asherah.shutdownAsync();
128
+ ### Metrics hook
129
+
130
+ Receive timing events for encrypt/decrypt/store/load and counter events for
131
+ cache hit/miss/stale.
132
+
133
+ ```js
134
+ asherah.setMetricsHook((event) => {
135
+ switch (event.type) {
136
+ case 'encrypt':
137
+ case 'decrypt':
138
+ case 'store':
139
+ case 'load':
140
+ // event = { type, durationNs }
141
+ myHistogram.observe(event.type, event.durationNs / 1e6);
142
+ break;
143
+ case 'cache_hit':
144
+ case 'cache_miss':
145
+ case 'cache_stale':
146
+ // event = { type, name }
147
+ myCounter.inc({ result: event.type, cache: event.name });
148
+ break;
149
+ }
150
+ });
151
+
152
+ // later:
153
+ asherah.setMetricsHook(null);
110
154
  ```
111
155
 
112
- ## Async Behavior
156
+ Metrics collection is enabled automatically when a hook is installed, and
157
+ disabled when cleared.
113
158
 
114
- Async operations run on a Rust tokio runtime, separate from the Node.js event
115
- loop. The exact execution strategy depends on the metastore:
159
+ ## Input contract
116
160
 
117
- | Metastore | Async Encrypt/Decrypt | Blocks Event Loop? |
118
- |-----------|----------------------|-------------------|
119
- | In-Memory | Runs on tokio worker thread | No |
120
- | DynamoDB | True async AWS SDK calls on tokio | No |
121
- | MySQL | `spawn_blocking` (sync driver on tokio thread pool) | No |
122
- | Postgres | `spawn_blocking` (sync driver on tokio thread pool) | No |
161
+ **Partition ID** (`null`, `undefined`, `""`): always rejected as
162
+ programming errors with `TypeError` (sync) or rejected `Promise`
163
+ (async). No row is ever written to the metastore under a degenerate
164
+ partition ID.
165
+
166
+ **Plaintext** to encrypt:
167
+ - `null` / `undefined` → `TypeError` from N-API marshalling (sync) or
168
+ rejected `Promise` (async).
169
+ - Empty `string` (`""`) and `Buffer.alloc(0)` are **valid** plaintexts.
170
+ `encrypt(...)` / `encryptString(...)` produces a real `DataRowRecord`
171
+ envelope; the matching `decrypt(...)` returns exactly `""` or an
172
+ empty `Buffer`.
123
173
 
124
- **Async never blocks the Node.js event loop.** The tradeoff is ~12us overhead
125
- per async call vs ~1us for sync (hot cache, 64B payload). Use sync in tight
126
- loops where latency matters; use async when you need to keep the event loop
127
- responsive.
174
+ **Ciphertext** to decrypt:
175
+ - `null` / `undefined` `TypeError`.
176
+ - Empty `string` / empty `Buffer` `Error` from native layer (not
177
+ valid `DataRowRecord` JSON).
178
+
179
+ **Do not short-circuit empty plaintext encryption in caller code** —
180
+ empty data is real data, encrypting it produces a genuine envelope, and
181
+ skipping encryption leaks the fact that the value was empty. See
182
+ [docs/input-contract.md](../docs/input-contract.md) for the full
183
+ rationale.
128
184
 
129
185
  ## Configuration
130
186
 
131
- Pass a config object to `setup()`, `setupAsync()`, or the `SessionFactory`
132
- constructor. Both camelCase and PascalCase field names are accepted (PascalCase
133
- is auto-mapped for backward compatibility with the canonical Go-based package).
187
+ All fields can be passed in `camelCase` (native) or `PascalCase` (canonical Go
188
+ SDK) both are auto-mapped. Pass to `setup()`, `setupAsync()`, or the
189
+ `SessionFactory` constructor.
134
190
 
135
191
  | Field | Type | Default | Description |
136
192
  |-------|------|---------|-------------|
137
- | `serviceName` | `string` | **(required)** | Service identifier for key hierarchy |
138
- | `productId` | `string` | **(required)** | Product identifier for key hierarchy |
139
- | `metastore` | `string` | **(required)** | `"rdbms"`, `"dynamodb"`, `"memory"` (testing) |
140
- | `kms` | `string` | `"static"` | `"static"` or `"aws"` |
141
- | `connectionString` | `string` | | Connection string for sqlite or rdbms metastore |
142
- | `sqlMetastoreDBType` | `string` | | `"mysql"` or `"postgres"` (for rdbms metastore) |
143
- | `enableSessionCaching` | `boolean` | `true` | Cache sessions by partition ID |
144
- | `sessionCacheMaxSize` | `number` | `1000` | Max cached sessions |
145
- | `sessionCacheDuration` | `number` | | Session cache TTL in milliseconds |
146
- | `regionMap` | `object` | | `{ "us-west-2": "arn:aws:kms:..." }` for AWS KMS multi-region |
147
- | `preferredRegion` | `string` | | Preferred AWS region for KMS |
148
- | `enableRegionSuffix` | `boolean` | | Append region suffix to system key IDs |
149
- | `expireAfter` | `number` | | Key expiration in milliseconds |
150
- | `checkInterval` | `number` | | Key rotation check interval in milliseconds |
151
- | `dynamoDBEndpoint` | `string` | | Custom DynamoDB endpoint URL |
152
- | `dynamoDBRegion` | `string` | | DynamoDB region |
153
- | `dynamoDBTableName` | `string` | | DynamoDB table name |
154
- | `replicaReadConsistency` | `string` | | DynamoDB read consistency |
155
- | `verbose` | `boolean` | `false` | Enable debug logging |
156
- | `enableCanaries` | `boolean` | | Enable canary key verification |
157
- | `disableZeroCopy` | `boolean` | | Disable zero-copy optimizations |
158
- | `nullDataCheck` | `boolean` | | Verify data is not null before decrypt |
159
- | `poolMaxOpen` | `number` | `0` | Max open DB connections (0 = unlimited) |
160
- | `poolMaxIdle` | `number` | `2` | Max idle connections to retain |
161
- | `poolMaxLifetime` | `number` | `0` | Max connection lifetime in seconds (0 = unlimited) |
162
- | `poolMaxIdleTime` | `number` | `0` | Max idle time per connection in seconds (0 = unlimited) |
163
-
164
- ### Environment Variables
165
-
166
- - `STATIC_MASTER_KEY_HEX` -- 64 hex chars (32 bytes) for static KMS. **Testing only.**
167
- - `ASHERAH_NODE_DEBUG=1` -- Enable native debug logging.
168
- - `ASHERAH_POOL_MAX_OPEN` -- Max open DB connections (overrides config).
169
- - `ASHERAH_POOL_MAX_IDLE` -- Max idle connections (overrides config).
170
- - `ASHERAH_POOL_MAX_LIFETIME` -- Max connection lifetime in seconds (overrides config).
171
- - `ASHERAH_POOL_MAX_IDLE_TIME` -- Max idle time per connection in seconds (overrides config).
193
+ | `serviceName` | `string` | **required** | Service identifier for the key hierarchy. |
194
+ | `productId` | `string` | **required** | Product identifier for the key hierarchy. |
195
+ | `metastore` | `'memory' \| 'rdbms' \| 'dynamodb'` | **required** | `'memory'` is testing-only and does not persist across processes. |
196
+ | `kms` | `'static' \| 'aws'` | `'static'` | `'static'` is testing-only (uses a hard-coded master key). |
197
+ | `connectionString` | `string` | | Connection string for `rdbms` metastore. |
198
+ | `sqlMetastoreDbType` | `'mysql' \| 'postgres'` | | SQL driver. |
199
+ | `enableSessionCaching` | `boolean` | `true` | Cache `Session` objects by partition ID. |
200
+ | `sessionCacheMaxSize` | `number` | `1000` | Max cached sessions. |
201
+ | `sessionCacheDuration` | `number` | | Session cache TTL in seconds. |
202
+ | `regionMap` | `Record<string, string>` | | AWS KMS multi-region key ARN map. |
203
+ | `preferredRegion` | `string` | | Preferred AWS region from `regionMap`. |
204
+ | `enableRegionSuffix` | `boolean` | | Append AWS region suffix to key IDs. |
205
+ | `expireAfter` | `number` | 90 days | Intermediate-key expiration in seconds. |
206
+ | `checkInterval` | `number` | 60 minutes | Revoke-check interval in seconds. |
207
+ | `dynamoDbEndpoint` | `string` | | DynamoDB endpoint URL (for local DynamoDB). |
208
+ | `dynamoDbRegion` | `string` | | AWS region for DynamoDB. |
209
+ | `dynamoDbTableName` | `string` | `'EncryptionKey'` | DynamoDB table name. |
210
+ | `dynamoDbSigningRegion` | `string` | | Region used for SigV4 signing. |
211
+ | `replicaReadConsistency` | `'eventual' \| 'global' \| 'session'` | | DynamoDB read consistency. |
212
+ | `verbose` | `boolean` | `false` | Emit verbose log events (use a log hook to consume). |
213
+ | `enableCanaries` | `boolean` | `false` | Enable in-memory canary buffers around plaintexts. |
214
+ | `disableZeroCopy` | `boolean` | | Compatibility shim accepted but no effect. |
215
+ | `nullDataCheck` | `boolean` | | Compatibility shim accepted but no effect. |
216
+ | `poolMaxOpen` | `number` | `0` | Max open DB connections (0 = unlimited). |
217
+ | `poolMaxIdle` | `number` | `2` | Max idle DB connections to retain. |
218
+ | `poolMaxLifetime` | `number` | `0` | Max connection lifetime in seconds (0 = unlimited). |
219
+ | `poolMaxIdleTime` | `number` | `0` | Max idle time in seconds per connection (0 = unlimited). |
220
+
221
+ ### Environment variables
222
+
223
+ | Variable | Effect |
224
+ |---|---|
225
+ | `STATIC_MASTER_KEY_HEX` | 64 hex chars (32 bytes) for static KMS. **Testing only.** |
226
+ | `ASHERAH_NODE_DEBUG=1` | Enable native-side debug logging. |
227
+ | `ASHERAH_POOL_MAX_OPEN` | Override `poolMaxOpen`. |
228
+ | `ASHERAH_POOL_MAX_IDLE` | Override `poolMaxIdle`. |
229
+ | `ASHERAH_POOL_MAX_LIFETIME` | Override `poolMaxLifetime`. |
230
+ | `ASHERAH_POOL_MAX_IDLE_TIME` | Override `poolMaxIdleTime`. |
172
231
 
173
232
  ## Performance
174
233
 
175
- This is a native Rust implementation compiled via napi-rs. Typical latencies on
176
- Apple M4 Max (in-memory metastore, session caching enabled, 64-byte payload):
234
+ Native Rust implementation compiled via napi-rs. Typical latencies on Apple
235
+ M4 Max (in-memory metastore, session caching enabled, 64-byte payload):
177
236
 
178
237
  | Operation | Sync | Async |
179
238
  |-----------|------|-------|
180
- | Encrypt | ~970 ns | ~12 us |
181
- | Decrypt | ~1,200 ns | ~12 us |
239
+ | Encrypt | ~970 ns | ~12 µs |
240
+ | Decrypt | ~1.2 µs | ~12 µs |
182
241
 
183
242
  See `scripts/benchmark.sh` for head-to-head comparisons with the canonical
184
243
  Go-based implementation.
185
244
 
186
- ## Migration from Canonical (v3.x)
245
+ ## Migration from the canonical Go-based `asherah` (v3.x)
187
246
 
188
- This package is a drop-in replacement for the Go-based `asherah` npm package
189
- (v3.x). The JavaScript wrapper provides full backward compatibility:
247
+ Drop-in replacement. The npm wrapper provides full backward compatibility:
190
248
 
191
- - **PascalCase config** -- `ServiceName`, `ProductID`, `Metastore`, etc. are
192
- auto-mapped to camelCase equivalents.
193
- - **snake_case function aliases** -- `set_log_hook`, `get_setup_status`,
194
- `encrypt_string`, `decrypt_string_async`, etc. all work.
195
- - **Metastore/KMS aliases** -- `"test-debug-memory"`, `"test-debug-static"`,
196
- etc. are normalized to their short forms.
197
- - **`set_log_hook` callback signature** -- Both the canonical
198
- `(level: number, message: string)` and the native
199
- `(event: { level, message, target })` signatures are supported.
249
+ - **PascalCase config** `ServiceName`, `ProductID`, `Metastore`, etc. are
250
+ auto-mapped to camelCase.
251
+ - **snake_case function aliases** `set_log_hook`, `set_metrics_hook`,
252
+ `get_setup_status`, `encrypt_string`, `decrypt_string_async`, etc.
253
+ - **Metastore/KMS aliases** `'test-debug-memory'`, `'test-debug-static'`
254
+ normalize to the short forms.
255
+ - **`set_log_hook` signature variants** both the canonical
256
+ `(level: number, message: string)` and the structured
257
+ `(event: { level, message, target })` are supported.
200
258
 
201
- To migrate, update your package version. No code changes required.
259
+ To migrate: change your dependency from `asherah@^3` to this package. No code
260
+ changes required.
202
261
 
203
- ## Supported Platforms
262
+ ## Supported platforms
204
263
 
205
264
  | Platform | Architecture | Notes |
206
- |----------|-------------|-------|
207
- | Linux | x64 | glibc (most distros) |
208
- | Linux | x64 | musl (Alpine) |
209
- | Linux | ARM64 | glibc |
210
- | Linux | ARM64 | musl (Alpine) |
211
- | macOS | x64 | Intel Macs |
212
- | macOS | ARM64 | Apple Silicon |
213
- | Windows | x64 | MSVC |
214
- | Windows | ARM64 | MSVC |
265
+ |----------|--------------|-------|
266
+ | Linux | x64 | glibc (most distros) |
267
+ | Linux | x64 | musl (Alpine) |
268
+ | Linux | ARM64 | glibc |
269
+ | Linux | ARM64 | musl (Alpine) |
270
+ | macOS | x64 | Intel |
271
+ | macOS | ARM64 | Apple Silicon |
272
+ | Windows | x64 | MSVC |
273
+ | Windows | ARM64 | MSVC |
215
274
 
216
275
  ## API Reference
217
276
 
218
- ### Setup / Teardown
219
-
220
- - `setup(config)` -- Initialize the global Asherah instance.
221
- - `setupAsync(config)` -- Async variant of `setup`.
222
- - `shutdown()` -- Shut down and release all resources.
223
- - `shutdownAsync()` -- Async variant of `shutdown`.
224
- - `getSetupStatus()` -- Returns `true` if `setup` has been called.
225
-
226
- ### Encrypt / Decrypt (Static API)
227
-
228
- - `encrypt(partitionId, data: Buffer)` -- Returns JSON string (DataRowRecord).
229
- - `encryptAsync(partitionId, data: Buffer)` -- Async variant.
230
- - `encryptString(partitionId, data: string)` -- String-in, string-out convenience.
231
- - `encryptStringAsync(partitionId, data: string)` -- Async variant.
232
- - `decrypt(partitionId, dataRowRecord: string | Buffer)` -- Returns `Buffer`.
233
- - `decryptAsync(partitionId, dataRowRecord: string | Buffer)` -- Async variant.
234
- - `decryptString(partitionId, dataRowRecord: string)` -- Returns `string`.
235
- - `decryptStringAsync(partitionId, dataRowRecord: string)` -- Async variant.
236
-
237
- ### Session-Based API
238
-
239
- - `new SessionFactory(config)` -- Create a factory with explicit config.
240
- - `SessionFactory.fromEnv()` -- Create a factory from environment variables.
241
- - `factory.getSession(partitionId)` -- Get a session for a partition.
242
- - `factory.close()` -- Close the factory and release resources.
243
- - `session.encrypt(data: Buffer)` -- Returns JSON string.
244
- - `session.encryptString(data: string)` -- String convenience.
245
- - `session.decrypt(dataRowRecord: string)` -- Returns `Buffer`.
246
- - `session.decryptString(dataRowRecord: string)` -- Returns `string`.
247
- - `session.close()` -- Close the session.
248
-
249
- ### Hooks
250
-
251
- - `setLogHook(callback)` / `set_log_hook(callback)` -- Receive log events.
252
- Pass `null` to disable.
253
- - `setMetricsHook(callback)` -- Receive metrics events
254
- (`{ type, durationNs?, name? }`). Pass `null` to disable.
255
-
256
- ### Utility
257
-
258
- - `setenv(lines: string)` -- Set environment variables from `KEY=VALUE` lines.
259
- - `setMaxStackAllocItemSize(n)` -- No-op (compatibility stub).
260
- - `setSafetyPaddingOverhead(n)` -- No-op (compatibility stub).
261
-
262
- ## Features
263
-
264
- - Synchronous and asynchronous encrypt/decrypt APIs
265
- - Session-based API with factory pattern
266
- - Compatible with Go, Python, Ruby, Java, and .NET Asherah implementations
267
- - SQLite, MySQL, PostgreSQL, and DynamoDB metastore support
268
- - AWS KMS and static key management
269
- - Log and metrics hooks
270
- - Automatic key rotation with configurable intervals
277
+ > Full TSDoc lives in `index.d.ts` and surfaces in your IDE on hover. The
278
+ > tables below summarize each API; the type file is the source of truth.
279
+
280
+ ### Static / module-level API (legacy compatibility)
281
+
282
+ #### Lifecycle
283
+
284
+ | Function | Description |
285
+ |---|---|
286
+ | `setup(config)` | Initialize the global instance. Throws if already configured. |
287
+ | `setupAsync(config)` | Async variant. Returns `Promise<void>`. |
288
+ | `shutdown()` | Tear down the global instance and clear cached sessions. Idempotent. |
289
+ | `shutdownAsync()` | Async variant. Returns `Promise<void>`. |
290
+ | `getSetupStatus()` | `boolean` — true if `setup()` has been called and `shutdown()` has not. |
291
+ | `setenv(envJson)` | Apply env vars from a JSON string before `setup()`. Mirrors the canonical SDK. |
292
+
293
+ #### Encrypt / decrypt
294
+
295
+ | Function | Param 1 | Param 2 | Returns |
296
+ |---|---|---|---|
297
+ | `encrypt(partitionId, data)` | `string` (non-empty) | `Buffer` (empty OK) | `string` (DRR JSON) |
298
+ | `encryptAsync(partitionId, data)` | `string` | `Buffer` | `Promise<string>` |
299
+ | `encryptString(partitionId, data)` | `string` | `string` (empty OK) | `string` (DRR JSON) |
300
+ | `encryptStringAsync(partitionId, data)` | `string` | `string` | `Promise<string>` |
301
+ | `decrypt(partitionId, drr)` | `string` | `string` (DRR JSON) | `Buffer` |
302
+ | `decryptAsync(partitionId, drr)` | `string` | `string` | `Promise<Buffer>` |
303
+ | `decryptString(partitionId, drr)` | `string` | `string` | `string` |
304
+ | `decryptStringAsync(partitionId, drr)` | `string` | `string` | `Promise<string>` |
305
+
306
+ All accept the snake_case aliases `encrypt_async`, `encrypt_string`,
307
+ `encrypt_string_async`, `decrypt_async`, `decrypt_string`,
308
+ `decrypt_string_async`, `setup_async`, `shutdown_async`, `get_setup_status`.
309
+
310
+ #### Hooks
311
+
312
+ | Function | Description |
313
+ |---|---|
314
+ | `setLogHook(cb)` / `set_log_hook(cb)` | Register a structured-event log callback. Pass `null` to deregister. The snake_case alias also accepts the canonical `(level, message)` signature. |
315
+ | `setMetricsHook(cb)` / `set_metrics_hook(cb)` | Register a metrics callback. Pass `null` to deregister. |
316
+
317
+ ### Factory / Session API (recommended)
318
+
319
+ #### `class SessionFactory`
320
+
321
+ | Member | Description |
322
+ |---|---|
323
+ | `new SessionFactory(config)` | Construct from inline config. |
324
+ | `static SessionFactory.fromEnv()` | Construct from environment variables. |
325
+ | `factory.getSession(partitionId)` | Get a per-partition session. Throws on null/empty partition. |
326
+ | `factory.close()` | Release native resources. After close, `getSession()` throws. |
327
+
328
+ #### `class AsherahSession`
329
+
330
+ | Member | Description |
331
+ |---|---|
332
+ | `session.encrypt(data)` | `Buffer` → DRR JSON `string`. Empty `Buffer` is valid. |
333
+ | `session.encryptString(data)` | `string` → DRR JSON `string`. Empty `string` is valid. |
334
+ | `session.decrypt(drr)` | DRR JSON `string` → `Buffer`. |
335
+ | `session.decryptString(drr)` | DRR JSON `string` → `string`. |
336
+ | `session.close()` | Release native resources. |
337
+
338
+ ### Type aliases
339
+
340
+ ```ts
341
+ type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';
342
+
343
+ type LogEvent = {
344
+ level: LogLevel;
345
+ message: string;
346
+ target: string;
347
+ };
348
+
349
+ type MetricsEvent =
350
+ | { type: 'encrypt' | 'decrypt' | 'store' | 'load'; durationNs: number }
351
+ | { type: 'cache_hit' | 'cache_miss' | 'cache_stale'; name: string };
352
+ ```
353
+
354
+ ### Compatibility shims
355
+
356
+ `setMaxStackAllocItemSize(n)` and `setSafetyPaddingOverhead(n)` are accepted
357
+ for parity with the canonical Go-based asherah-node package but have no
358
+ effect in this Rust binding.
271
359
 
272
360
  ## License
273
361
 
package/index.d.ts CHANGED
@@ -1,40 +1,129 @@
1
1
  /// <reference types="node" />
2
2
 
3
+ // ============================================================================
4
+ //
5
+ // Asherah for Node.js
6
+ //
7
+ // Application-layer envelope encryption with automatic key rotation and a
8
+ // pluggable KMS / metastore. Drop-in compatible with the canonical
9
+ // `asherah` npm package (PascalCase config + snake_case API aliases) and
10
+ // significantly faster (Rust core via napi-rs).
11
+ //
12
+ // Two API styles are exposed; both are fully supported and produce the
13
+ // same wire format:
14
+ //
15
+ // 1. **Static / module-level API** (legacy): `setup()` once at process
16
+ // startup, then call free `encrypt()` / `decrypt()` functions on the
17
+ // module. This mirrors the canonical `godaddy/asherah-node` API and
18
+ // is the easiest path for existing callers to migrate.
19
+ //
20
+ // 2. **Factory / Session API** (recommended for new code): construct a
21
+ // `SessionFactory`, hold one or more `AsherahSession` instances, and
22
+ // call `encrypt()` / `decrypt()` on the session. This avoids the
23
+ // hidden-singleton lifecycle of the static API and makes session
24
+ // isolation explicit per partition.
25
+ //
26
+ // See `samples/node/index.mjs` for a runnable end-to-end example covering
27
+ // both styles plus async, log hook, metrics hook, and config variants.
28
+ //
29
+ // ============================================================================
30
+
31
+ // ─── Configuration types ────────────────────────────────────────────────────
32
+
33
+ /**
34
+ * Asherah configuration object using camelCase field names. This is the
35
+ * native shape; it is passed to `setup()`, `setupAsync()`, and the
36
+ * `SessionFactory` constructor.
37
+ *
38
+ * The PascalCase variant ({@link AsherahConfigCompat}) is also accepted
39
+ * everywhere this type is — fields are auto-mapped for compatibility with
40
+ * the canonical Go-based asherah package.
41
+ */
3
42
  export type AsherahConfig = {
43
+ /** Service name. Forms part of the key hierarchy partition path. Required. */
4
44
  serviceName: string;
45
+
46
+ /** Product ID. Forms part of the key hierarchy partition path. Required. */
5
47
  productId: string;
48
+
49
+ /** Intermediate-key expiration in seconds. Default: 90 days. */
6
50
  expireAfter?: number | null;
51
+
52
+ /** Revoke-check interval in seconds — how often to re-read parent keys
53
+ * to pick up revocation. Default: 60 minutes. */
7
54
  checkInterval?: number | null;
55
+
56
+ /** Metastore backend. `'memory'` is testing-only and will not persist
57
+ * across processes. */
8
58
  metastore: 'memory' | 'rdbms' | 'dynamodb';
59
+
60
+ /** SQL connection string when `metastore` is `'rdbms'`. Format depends
61
+ * on `sqlMetastoreDbType`. */
9
62
  connectionString?: string | null;
63
+
64
+ /** RDBMS replica read consistency: `'eventual'`, `'global'`, or `'session'`. */
10
65
  replicaReadConsistency?: string | null;
66
+
67
+ /** DynamoDB endpoint URL (typically only set for local DynamoDB). */
11
68
  dynamoDbEndpoint?: string | null;
69
+ /** AWS region for DynamoDB. */
12
70
  dynamoDbRegion?: string | null;
71
+ /** DynamoDB table name. Default: `EncryptionKey`. */
13
72
  dynamoDbTableName?: string | null;
14
- /** @deprecated Use dynamoDbEndpoint */
73
+ /** AWS region used for SigV4 signing of DynamoDB requests. */
74
+ dynamoDbSigningRegion?: string | null;
75
+
76
+ /** @deprecated Use `dynamoDbEndpoint` (lowercase `b`). */
15
77
  dynamoDBEndpoint?: string | null;
16
- /** @deprecated Use dynamoDbRegion */
78
+ /** @deprecated Use `dynamoDbRegion` (lowercase `b`). */
17
79
  dynamoDBRegion?: string | null;
18
- /** @deprecated Use dynamoDbTableName */
80
+ /** @deprecated Use `dynamoDbTableName` (lowercase `b`). */
19
81
  dynamoDBTableName?: string | null;
82
+
83
+ /** Maximum number of cached sessions. */
20
84
  sessionCacheMaxSize?: number | null;
85
+ /** Session cache TTL in seconds. */
21
86
  sessionCacheDuration?: number | null;
87
+
88
+ /** KMS backend. `'static'` is testing-only (uses a hard-coded master key). */
22
89
  kms?: 'aws' | 'static' | null;
90
+
91
+ /** AWS KMS region-to-key-ARN map for multi-region KMS. */
23
92
  regionMap?: Record<string, string> | null;
93
+ /** Preferred AWS region when `regionMap` is set. */
24
94
  preferredRegion?: string | null;
95
+ /** Append the AWS region as a suffix to the key ID. */
25
96
  enableRegionSuffix?: boolean | null;
97
+
98
+ /** Cache `Session` objects by partition ID. Default: `true`. */
26
99
  enableSessionCaching?: boolean | null;
100
+
101
+ /** Emit verbose log events at the `info`/`debug` level. Use a log hook
102
+ * ({@link setLogHook}) to consume them. */
27
103
  verbose?: boolean | null;
28
- dynamoDbSigningRegion?: string | null;
104
+
105
+ /** SQL driver: `'mysql'` or `'postgres'` (used with `metastore: 'rdbms'`). */
29
106
  sqlMetastoreDbType?: string | null;
30
- /** @deprecated Use sqlMetastoreDbType */
107
+ /** @deprecated Use `sqlMetastoreDbType` (lowercase `b`). */
31
108
  sqlMetastoreDBType?: string | null;
109
+
110
+ /** Compatibility shim for the canonical Go-based asherah-node package.
111
+ * Has no effect in this Rust-based binding (which is always zero-copy
112
+ * where possible). */
32
113
  disableZeroCopy?: boolean | null;
114
+ /** Compatibility shim — accepted but has no effect (this binding always
115
+ * validates inputs). */
33
116
  nullDataCheck?: boolean | null;
117
+ /** Enable in-memory canary buffers around plaintexts. Costs a small
118
+ * amount of allocation overhead per operation. */
34
119
  enableCanaries?: boolean | null;
35
120
  };
36
121
 
37
- /** Canonical godaddy/asherah-node PascalCase config format */
122
+ /**
123
+ * Asherah configuration in canonical PascalCase format, matching the
124
+ * canonical `asherah` npm package. Accepted everywhere {@link AsherahConfig}
125
+ * is — fields are auto-mapped on the way in.
126
+ */
38
127
  export type AsherahConfigCompat = {
39
128
  readonly ServiceName: string;
40
129
  readonly ProductID: string;
@@ -60,67 +149,305 @@ export type AsherahConfigCompat = {
60
149
  readonly EnableCanaries?: boolean | null;
61
150
  };
62
151
 
63
- /** Canonical godaddy/asherah-node log hook callback: (level: number, message: string) => void */
64
- export type LogHookCallback = (level: number, message: string) => void;
152
+ // ─── Static / module-level API (legacy) ─────────────────────────────────────
65
153
 
154
+ /**
155
+ * Initialize the global Asherah instance. Must be called once before any
156
+ * `encrypt()` / `decrypt()` call on the static API.
157
+ *
158
+ * Subsequent calls to `setup()` without an intervening `shutdown()` throw.
159
+ * For new code, prefer the {@link SessionFactory} API, which avoids the
160
+ * hidden-singleton lifecycle.
161
+ *
162
+ * @example
163
+ * ```js
164
+ * const asherah = require('asherah');
165
+ * asherah.setup({
166
+ * serviceName: 'my-svc',
167
+ * productId: 'my-prod',
168
+ * metastore: 'memory', // production: 'rdbms' or 'dynamodb'
169
+ * kms: 'static', // production: 'aws'
170
+ * });
171
+ * ```
172
+ *
173
+ * @throws if Asherah is already configured (call {@link shutdown} first)
174
+ * or if the config is invalid.
175
+ */
66
176
  export declare function setup(config: AsherahConfig | AsherahConfigCompat): void;
177
+
178
+ /**
179
+ * Async variant of {@link setup}. Resolves once the global instance is
180
+ * configured and the metastore/KMS are reachable. Safe to call from an
181
+ * async context — does not block the Node event loop on KMS/SDK setup.
182
+ */
67
183
  export declare function setupAsync(config: AsherahConfig | AsherahConfigCompat): Promise<void>;
184
+
185
+ /**
186
+ * Tear down the global Asherah instance. Releases the native factory and
187
+ * clears any cached sessions. Idempotent — calling on an already-shut-down
188
+ * instance is a no-op.
189
+ */
68
190
  export declare function shutdown(): void;
191
+
192
+ /** Async variant of {@link shutdown}. */
69
193
  export declare function shutdownAsync(): Promise<void>;
194
+
195
+ /** Returns `true` when {@link setup} has been called and {@link shutdown}
196
+ * has not yet been called. */
70
197
  export declare function getSetupStatus(): boolean;
198
+
199
+ /**
200
+ * Apply a JSON object of environment variables before {@link setup} is
201
+ * called. Equivalent to `process.env[k] = v` for each entry, but evaluated
202
+ * by the native side so configuration via env-var works identically to
203
+ * the canonical SDK.
204
+ *
205
+ * @param env JSON string. Keys must be strings; values may be strings or
206
+ * `null` (a `null` value unsets the variable).
207
+ */
71
208
  export declare function setenv(env: string): void;
72
209
 
210
+ /**
211
+ * Encrypt `data` for the given partition. Returns a `DataRowRecord` JSON
212
+ * string suitable for storing in a database column.
213
+ *
214
+ * @param partitionId Tenant / user / record-owner identifier. Must be
215
+ * non-empty — `null`, `undefined`, and `""` are
216
+ * rejected as programming errors.
217
+ * @param data Plaintext bytes. Empty `Buffer` is **valid** and round-trips
218
+ * to an empty `Buffer` on decrypt — do not short-circuit empty
219
+ * inputs in caller code (see docs/input-contract.md).
220
+ * @returns The full `DataRowRecord` JSON envelope (Key, Data,
221
+ * ParentKeyMeta).
222
+ * @throws TypeError if `partitionId` or `data` is null/undefined.
223
+ * @throws Error from the native layer on encryption failure.
224
+ *
225
+ * @example
226
+ * ```js
227
+ * const drr = asherah.encrypt('user-42', Buffer.from('secret'));
228
+ * // store drr in your database
229
+ * ```
230
+ */
73
231
  export declare function encrypt(partitionId: string, data: Buffer): string;
232
+
233
+ /** Async variant of {@link encrypt}. The work runs on the Rust tokio
234
+ * runtime; the Node event loop is NOT blocked. */
74
235
  export declare function encryptAsync(partitionId: string, data: Buffer): Promise<string>;
236
+
237
+ /**
238
+ * Decrypt a `DataRowRecord` JSON string produced by {@link encrypt}.
239
+ *
240
+ * @param partitionId Must match the partition the value was encrypted
241
+ * under. Non-empty.
242
+ * @param dataRowRecordJson The DRR JSON envelope as a string.
243
+ * @returns Plaintext as a `Buffer`. Length 0 if the original plaintext
244
+ * was empty — empty round-trips to empty.
245
+ * @throws TypeError if either argument is null/undefined.
246
+ * @throws Error if the JSON is malformed, the partition doesn't match,
247
+ * the parent key has been revoked, or the AEAD tag fails.
248
+ */
75
249
  export declare function decrypt(partitionId: string, dataRowRecordJson: string): Buffer;
250
+
251
+ /** Async variant of {@link decrypt}. */
76
252
  export declare function decryptAsync(partitionId: string, dataRowRecordJson: string): Promise<Buffer>;
77
253
 
254
+ /** UTF-8 string-typed wrapper around {@link encrypt}. Empty `string`
255
+ * ("") is valid and round-trips. */
78
256
  export declare function encryptString(partitionId: string, data: string): string;
257
+
258
+ /** Async variant of {@link encryptString}. */
79
259
  export declare function encryptStringAsync(partitionId: string, data: string): Promise<string>;
260
+
261
+ /** UTF-8 string-typed wrapper around {@link decrypt}. */
80
262
  export declare function decryptString(partitionId: string, dataRowRecordJson: string): string;
263
+
264
+ /** Async variant of {@link decryptString}. */
81
265
  export declare function decryptStringAsync(partitionId: string, dataRowRecordJson: string): Promise<string>;
82
266
 
83
- export declare function setMaxStackAllocItemSize(n: number): void;
84
- export declare function setSafetyPaddingOverhead(n: number): void;
267
+ // ─── Factory / Session API (recommended) ────────────────────────────────────
85
268
 
269
+ /**
270
+ * Factory for creating per-partition `AsherahSession` instances. Holding
271
+ * a long-lived factory is cheaper than calling `setup()`/`shutdown()`
272
+ * repeatedly and makes session isolation explicit.
273
+ *
274
+ * @example
275
+ * ```js
276
+ * const factory = new asherah.SessionFactory({
277
+ * serviceName: 'my-svc',
278
+ * productId: 'my-prod',
279
+ * metastore: 'memory',
280
+ * kms: 'static',
281
+ * });
282
+ * const session = factory.getSession('user-42');
283
+ * try {
284
+ * const ct = session.encryptString('secret');
285
+ * const pt = session.decryptString(ct);
286
+ * } finally {
287
+ * session.close();
288
+ * factory.close();
289
+ * }
290
+ * ```
291
+ */
86
292
  export declare class SessionFactory {
293
+ /** Construct a factory from an inline config object. */
87
294
  constructor(config: AsherahConfig | AsherahConfigCompat);
295
+
296
+ /** Construct a factory from environment variables (for parity with the
297
+ * canonical Go-based asherah module). */
88
298
  static fromEnv(): SessionFactory;
299
+
300
+ /**
301
+ * Get a session for the given partition. Sessions returned for the same
302
+ * partition share the underlying intermediate key; different partitions
303
+ * are cryptographically isolated.
304
+ *
305
+ * @param partitionId Non-empty tenant / record-owner identifier.
306
+ * @throws TypeError if `partitionId` is null/undefined.
307
+ * @throws Error if `partitionId` is the empty string.
308
+ */
89
309
  getSession(partitionId: string): AsherahSession;
310
+
311
+ /** Release native resources. After `close()`, `getSession()` will throw. */
90
312
  close(): void;
91
313
  }
92
314
 
315
+ /**
316
+ * Per-partition encrypt/decrypt session. Created via
317
+ * {@link SessionFactory.getSession}. Always pair with `close()` to release
318
+ * native resources promptly.
319
+ */
93
320
  export declare class AsherahSession {
321
+ /** Encrypt a `Buffer` and return the DRR JSON. Empty `Buffer` is valid. */
94
322
  encrypt(data: Buffer): string;
323
+ /** Encrypt a UTF-8 string and return the DRR JSON. Empty string is valid. */
95
324
  encryptString(data: string): string;
325
+ /** Decrypt a DRR JSON string and return the plaintext as a `Buffer`. */
96
326
  decrypt(dataRowRecordJson: string): Buffer;
327
+ /** Decrypt a DRR JSON string and return the plaintext as a UTF-8 string. */
97
328
  decryptString(dataRowRecordJson: string): string;
329
+ /** Release native resources. */
98
330
  close(): void;
99
331
  }
100
332
 
333
+ // ─── Observability hooks ────────────────────────────────────────────────────
334
+
335
+ /** Log severity strings carried in {@link LogEvent.level}. */
336
+ export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error';
337
+
338
+ /**
339
+ * Structured log event delivered to a log hook. Includes the source
340
+ * `target` (typically the Rust module path that emitted the log) and the
341
+ * formatted `message`.
342
+ */
101
343
  export type LogEvent = {
102
- level: 'trace' | 'debug' | 'info' | 'warn' | 'error';
344
+ /** Severity. Mirrors the Rust `log::Level`. */
345
+ level: LogLevel;
346
+ /** Formatted log message. */
103
347
  message: string;
348
+ /** Source module / target string. Useful for filtering. */
104
349
  target: string;
105
350
  };
106
351
 
352
+ /** Numeric-level callback for compatibility with the canonical
353
+ * `godaddy/asherah-node` log hook signature. New code should prefer the
354
+ * structured {@link LogEvent} variant. */
355
+ export type LogHookCallback = (level: number, message: string) => void;
356
+
357
+ /**
358
+ * Metrics event delivered to a metrics hook. Timing events
359
+ * (`encrypt`/`decrypt`/`store`/`load`) carry `durationNs`; cache events
360
+ * (`cache_hit`/`cache_miss`/`cache_stale`) carry the cache `name`.
361
+ */
107
362
  export type MetricsEvent =
108
363
  | { type: 'encrypt' | 'decrypt' | 'store' | 'load'; durationNs: number }
109
- | { type: 'cache_hit' | 'cache_miss'; name: string };
364
+ | { type: 'cache_hit' | 'cache_miss' | 'cache_stale'; name: string };
365
+
366
+ /**
367
+ * Install a callback that fires for every log event emitted by the Rust
368
+ * core (encrypt/decrypt path, metastore drivers, KMS clients, etc.).
369
+ *
370
+ * Pass `null` to deregister.
371
+ *
372
+ * Callbacks may fire from any thread (Rust tokio worker threads, DB
373
+ * driver threads). The N-API ThreadsafeFunction layer marshals each
374
+ * event back to the Node event loop, so the callback runs on the main
375
+ * thread — synchronous code in the callback is safe.
376
+ *
377
+ * @example
378
+ * ```js
379
+ * asherah.setLogHook((event) => {
380
+ * if (event.level === 'warn' || event.level === 'error') {
381
+ * console.error(`[asherah ${event.level}] ${event.message}`);
382
+ * }
383
+ * });
384
+ * ```
385
+ */
386
+ export declare function setLogHook(
387
+ hook: ((event: LogEvent) => void) | LogHookCallback | null,
388
+ ): void;
389
+
390
+ /**
391
+ * Install a callback that fires for every metrics event (encrypt/decrypt
392
+ * timings, store/load timings, cache hit/miss/stale counters).
393
+ *
394
+ * Pass `null` to deregister. Same threading semantics as
395
+ * {@link setLogHook}.
396
+ *
397
+ * @example
398
+ * ```js
399
+ * asherah.setMetricsHook((event) => {
400
+ * if (event.type === 'encrypt') {
401
+ * histogram.observe(event.durationNs / 1e6); // ms
402
+ * } else if (event.type === 'cache_miss') {
403
+ * counter.inc({ cache: event.name });
404
+ * }
405
+ * });
406
+ * ```
407
+ */
408
+ export declare function setMetricsHook(
409
+ hook: ((event: MetricsEvent) => void) | null,
410
+ ): void;
411
+
412
+ // ─── Performance tuning ─────────────────────────────────────────────────────
413
+
414
+ /** Compatibility shim — accepted but has no effect in the Rust binding
415
+ * (which manages its own buffer allocation strategy). Provided for API
416
+ * parity with the canonical Go-based asherah-node package. */
417
+ export declare function setMaxStackAllocItemSize(n: number): void;
418
+
419
+ /** Compatibility shim — accepted but has no effect. */
420
+ export declare function setSafetyPaddingOverhead(n: number): void;
110
421
 
111
- export declare function setLogHook(hook: ((event: LogEvent) => void) | LogHookCallback | null): void;
112
- export declare function setMetricsHook(hook: ((event: MetricsEvent) => void) | null): void;
422
+ // ─── snake_case aliases (canonical asherah-node compatibility) ──────────────
113
423
 
114
- // Canonical godaddy/asherah-node snake_case aliases
424
+ /** Alias for {@link setupAsync}. */
115
425
  export declare function setup_async(config: AsherahConfig | AsherahConfigCompat): Promise<void>;
426
+ /** Alias for {@link shutdownAsync}. */
116
427
  export declare function shutdown_async(): Promise<void>;
428
+ /** Alias for {@link encryptAsync}. */
117
429
  export declare function encrypt_async(partitionId: string, data: Buffer): Promise<string>;
430
+ /** Alias for {@link encryptString}. */
118
431
  export declare function encrypt_string(partitionId: string, data: string): string;
432
+ /** Alias for {@link encryptStringAsync}. */
119
433
  export declare function encrypt_string_async(partitionId: string, data: string): Promise<string>;
434
+ /** Alias for {@link decryptAsync}. */
120
435
  export declare function decrypt_async(partitionId: string, dataRowRecordJson: string): Promise<Buffer>;
436
+ /** Alias for {@link decryptString}. */
121
437
  export declare function decrypt_string(partitionId: string, dataRowRecordJson: string): string;
438
+ /** Alias for {@link decryptStringAsync}. */
122
439
  export declare function decrypt_string_async(partitionId: string, dataRowRecordJson: string): Promise<string>;
440
+ /** Alias for {@link setMaxStackAllocItemSize}. */
123
441
  export declare function set_max_stack_alloc_item_size(n: number): void;
442
+ /** Alias for {@link setSafetyPaddingOverhead}. */
124
443
  export declare function set_safety_padding_overhead(n: number): void;
125
- export declare function set_log_hook(hook: ((event: LogEvent) => void) | LogHookCallback | null): void;
444
+ /** Alias for {@link setLogHook}. */
445
+ export declare function set_log_hook(
446
+ hook: ((event: LogEvent) => void) | LogHookCallback | null,
447
+ ): void;
448
+ /** Alias for {@link setMetricsHook}. */
449
+ export declare function set_metrics_hook(
450
+ hook: ((event: MetricsEvent) => void) | null,
451
+ ): void;
452
+ /** Alias for {@link getSetupStatus}. */
126
453
  export declare function get_setup_status(): boolean;
package/npm/index.js CHANGED
@@ -284,4 +284,5 @@ module.exports.decrypt_string_async = native.decryptStringAsync;
284
284
  module.exports.set_max_stack_alloc_item_size = native.setMaxStackAllocItemSize;
285
285
  module.exports.set_safety_padding_overhead = native.setSafetyPaddingOverhead;
286
286
  module.exports.set_log_hook = set_log_hook;
287
+ module.exports.set_metrics_hook = native.setMetricsHook;
287
288
  module.exports.get_setup_status = native.getSetupStatus;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "asherah",
3
- "version": "4.0.34",
3
+ "version": "4.0.35",
4
4
  "private": false,
5
5
  "description": "Asherah application-layer encryption for Node.js with automatic key rotation, powered by the native Rust implementation.",
6
6
  "author": "Jay Gowdy",
@@ -70,13 +70,13 @@
70
70
  "node": ">= 18"
71
71
  },
72
72
  "optionalDependencies": {
73
- "asherah-darwin-arm64": "4.0.34",
74
- "asherah-darwin-x64": "4.0.34",
75
- "asherah-linux-x64-gnu": "4.0.34",
76
- "asherah-linux-arm64-gnu": "4.0.34",
77
- "asherah-linux-x64-musl": "4.0.34",
78
- "asherah-linux-arm64-musl": "4.0.34",
79
- "asherah-windows-x64": "4.0.34",
80
- "asherah-windows-arm64": "4.0.34"
73
+ "asherah-darwin-arm64": "4.0.35",
74
+ "asherah-darwin-x64": "4.0.35",
75
+ "asherah-linux-x64-gnu": "4.0.35",
76
+ "asherah-linux-arm64-gnu": "4.0.35",
77
+ "asherah-linux-x64-musl": "4.0.35",
78
+ "asherah-linux-arm64-musl": "4.0.35",
79
+ "asherah-windows-x64": "4.0.35",
80
+ "asherah-windows-arm64": "4.0.35"
81
81
  }
82
82
  }