@directive-run/core 1.12.0 → 1.14.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.
Files changed (94) hide show
  1. package/dist/adapter-utils.cjs +1 -1
  2. package/dist/adapter-utils.d.cts +2 -2
  3. package/dist/adapter-utils.d.ts +2 -2
  4. package/dist/adapter-utils.js +1 -1
  5. package/dist/adapter-utils.js.map +1 -1
  6. package/dist/chunk-2FF6QGOA.js +2 -0
  7. package/dist/chunk-2FF6QGOA.js.map +1 -0
  8. package/dist/chunk-4MNQDXH7.cjs +3 -0
  9. package/dist/chunk-4MNQDXH7.cjs.map +1 -0
  10. package/dist/chunk-644QZVTT.js +16 -0
  11. package/dist/{chunk-26Z5VNPZ.js.map → chunk-644QZVTT.js.map} +1 -1
  12. package/dist/chunk-AV6S27PU.js +2 -0
  13. package/dist/chunk-AV6S27PU.js.map +1 -0
  14. package/dist/chunk-ENZEHIL7.cjs +3 -0
  15. package/dist/chunk-ENZEHIL7.cjs.map +1 -0
  16. package/dist/chunk-I722BZA5.js +7 -0
  17. package/dist/chunk-I722BZA5.js.map +1 -0
  18. package/dist/chunk-NPX5EKPP.cjs +16 -0
  19. package/dist/{chunk-EX3XG667.cjs.map → chunk-NPX5EKPP.cjs.map} +1 -1
  20. package/dist/chunk-PXRV64PA.js +3 -0
  21. package/dist/chunk-PXRV64PA.js.map +1 -0
  22. package/dist/chunk-Q2VZPURY.cjs +2 -0
  23. package/dist/chunk-Q2VZPURY.cjs.map +1 -0
  24. package/dist/chunk-R2GHSCTR.js +3 -0
  25. package/dist/chunk-R2GHSCTR.js.map +1 -0
  26. package/dist/chunk-T4TRJEJN.cjs +2 -0
  27. package/dist/chunk-T4TRJEJN.cjs.map +1 -0
  28. package/dist/chunk-X7G7UBXU.cjs +7 -0
  29. package/dist/chunk-X7G7UBXU.cjs.map +1 -0
  30. package/dist/index-C8M6pj9H.d.ts +406 -0
  31. package/dist/index-DErb8sV3.d.cts +406 -0
  32. package/dist/index.cjs +2 -2
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +214 -391
  35. package/dist/index.d.ts +214 -391
  36. package/dist/index.js +2 -2
  37. package/dist/index.js.map +1 -1
  38. package/dist/internals.cjs +1 -1
  39. package/dist/internals.d.cts +5 -5
  40. package/dist/internals.d.ts +5 -5
  41. package/dist/internals.js +1 -1
  42. package/dist/plugins/index.cjs +2 -2
  43. package/dist/plugins/index.cjs.map +1 -1
  44. package/dist/plugins/index.d.cts +2 -2
  45. package/dist/plugins/index.d.ts +2 -2
  46. package/dist/plugins/index.js +1 -1
  47. package/dist/plugins/index.js.map +1 -1
  48. package/dist/{plugins-Ykl_sAPE.d.ts → plugins-BIzXaYbg.d.cts} +15 -1
  49. package/dist/{plugins-Ykl_sAPE.d.cts → plugins-BIzXaYbg.d.ts} +15 -1
  50. package/dist/predicate-Bnx3LN7P.d.cts +655 -0
  51. package/dist/predicate-BxQVf0ug.d.ts +655 -0
  52. package/dist/system-A6VYKLVF.js +2 -0
  53. package/dist/{system-VZWB6WXX.js.map → system-A6VYKLVF.js.map} +1 -1
  54. package/dist/system-CDJMD5O5.cjs +2 -0
  55. package/dist/{system-GK3NSFQH.cjs.map → system-CDJMD5O5.cjs.map} +1 -1
  56. package/dist/testing.cjs +1 -1
  57. package/dist/testing.cjs.map +1 -1
  58. package/dist/testing.d.cts +1 -1
  59. package/dist/testing.d.ts +1 -1
  60. package/dist/testing.js +1 -1
  61. package/dist/testing.js.map +1 -1
  62. package/dist/{utils-BnQajqPu.d.cts → utils-BZBBwS5A.d.cts} +36 -1
  63. package/dist/{utils-BnQajqPu.d.ts → utils-BZBBwS5A.d.ts} +36 -1
  64. package/dist/worker.cjs +1 -1
  65. package/dist/worker.cjs.map +1 -1
  66. package/dist/worker.d.cts +24 -7
  67. package/dist/worker.d.ts +24 -7
  68. package/dist/worker.js +1 -1
  69. package/dist/worker.js.map +1 -1
  70. package/package.json +1 -1
  71. package/dist/audit-ledger-9IElAHH9.d.ts +0 -205
  72. package/dist/audit-ledger-qMjEBqiP.d.cts +0 -205
  73. package/dist/chunk-26Z5VNPZ.js +0 -16
  74. package/dist/chunk-4VZOZWXM.cjs +0 -2
  75. package/dist/chunk-4VZOZWXM.cjs.map +0 -1
  76. package/dist/chunk-7NMXRATK.cjs +0 -3
  77. package/dist/chunk-7NMXRATK.cjs.map +0 -1
  78. package/dist/chunk-7TSYQEN3.js +0 -2
  79. package/dist/chunk-7TSYQEN3.js.map +0 -1
  80. package/dist/chunk-EOLY64E6.cjs +0 -3
  81. package/dist/chunk-EOLY64E6.cjs.map +0 -1
  82. package/dist/chunk-EX3XG667.cjs +0 -16
  83. package/dist/chunk-N4KTCKOI.cjs +0 -7
  84. package/dist/chunk-N4KTCKOI.cjs.map +0 -1
  85. package/dist/chunk-T6IJUWYR.js +0 -3
  86. package/dist/chunk-T6IJUWYR.js.map +0 -1
  87. package/dist/chunk-TPOKS4RY.js +0 -3
  88. package/dist/chunk-TPOKS4RY.js.map +0 -1
  89. package/dist/chunk-TZHC4E6S.js +0 -7
  90. package/dist/chunk-TZHC4E6S.js.map +0 -1
  91. package/dist/helpers-D2pfb6vT.d.ts +0 -235
  92. package/dist/helpers-hh6UanB1.d.cts +0 -235
  93. package/dist/system-GK3NSFQH.cjs +0 -2
  94. package/dist/system-VZWB6WXX.js +0 -2
@@ -0,0 +1,406 @@
1
+ import { F as FactPredicate, C as ClauseResult, P as Plugin, M as ModuleSchema } from './plugins-BIzXaYbg.js';
2
+
3
+ /**
4
+ * Audit-ledger type definitions — entry shapes, sink interface, query
5
+ * filter, verify result, and plugin options. Pure types + the two
6
+ * schema-version constants (`HASH_ALGO`, `SCHEMA_VERSION`) that are
7
+ * referenced by the entry shape.
8
+ *
9
+ * Kept free of runtime imports so the hash/freeze/sink modules can pull
10
+ * types from here without a cycle.
11
+ */
12
+
13
+ /** Hash algorithm tag — bumped if canonicalization or hash function changes. */
14
+ declare const HASH_ALGO: "djb2-1";
15
+ /**
16
+ * Entry schema version. Bumped if `AuditEntry` field shape changes in
17
+ * a way that breaks back-compat parsers. Persisted on every entry so
18
+ * exports remain self-describing across library upgrades. (F-5)
19
+ */
20
+ declare const SCHEMA_VERSION: 1;
21
+ type AuditEntryKind = "constraint.evaluate" | "resolver.write.rejected" | "fact.change" | "resolver.complete" | "resolver.error" | "system.init" | "system.start" | "system.stop" | "system.destroy" | "system.snapshot" | "system.history.navigate" | "system.truncated" | "system.entry-erased" | "system.subject-erased";
22
+ /**
23
+ * Internal sentinel symbol type. The actual symbol VALUE lives in
24
+ * `hash.ts` and is never exported from this folder's public surface —
25
+ * but the TYPE must be referenceable here so `AuditEntryBase` can
26
+ * declare the optional `__internal` field.
27
+ *
28
+ * We use `symbol` rather than `typeof LEDGER_INTERNAL_TOKEN` because
29
+ * importing the symbol value into types.ts would either re-export it
30
+ * (defeating the defense) or create a circular import. The runtime
31
+ * check in `verify()` compares against the actual symbol reference.
32
+ */
33
+ type LedgerInternalSentinel = symbol;
34
+ interface AuditEntryBase {
35
+ /** Monotonic sequence number, starting at 0. */
36
+ readonly seq: number;
37
+ /** Wall-clock timestamp (ms epoch). */
38
+ readonly ts: number;
39
+ /** Discriminator. */
40
+ readonly kind: AuditEntryKind;
41
+ /** Hash of the previous entry's full payload. null on the genesis entry. */
42
+ readonly prevHash: string | null;
43
+ /**
44
+ * Hash algorithm tag identifying the canonicalization + hash
45
+ * function in use. Bumped if the algorithm or canonical form
46
+ * changes, so exports remain verifiable across versions.
47
+ */
48
+ readonly hashAlgo: typeof HASH_ALGO;
49
+ /**
50
+ * Entry schema version — bumped if any `AuditEntry` field shape
51
+ * changes in a way that breaks back-compat. Pair with `hashAlgo`
52
+ * when migrating older exports. (F-5)
53
+ */
54
+ readonly schemaVersion: typeof SCHEMA_VERSION;
55
+ /**
56
+ * Private sentinel — present (and equal to the in-module token) only
57
+ * on legitimate tombstones minted by `ledger.erase()`. Filtered out
58
+ * of all public read paths (`query`, `recent`, `toJSON`, etc.) so
59
+ * consumers never see or copy it. (N7)
60
+ *
61
+ * NOT serialized. NOT exported. Forging this from outside the module
62
+ * is impossible without the symbol reference; `verify()` rejects any
63
+ * `system.entry-erased` entry that lacks it.
64
+ *
65
+ * @internal
66
+ */
67
+ readonly __internal?: LedgerInternalSentinel;
68
+ }
69
+ type AuditEntry = (AuditEntryBase & {
70
+ kind: "constraint.evaluate";
71
+ constraintId: string;
72
+ active: boolean;
73
+ /** Cached at ledger start from `system.inspect().constraints[].whenSpec`. Refreshed on `register()`/`assign()`/`unregister()`. May be undefined for function-form constraints (see `whenSource`). PII operands redacted unless `capturePII: true`. */
74
+ whenSpec?: FactPredicate<unknown>;
75
+ whenExplain?: readonly ClauseResult[];
76
+ /**
77
+ * For function-form constraints (no `whenSpec`), a tamper-evident
78
+ * identity for the function. We DO NOT capture the raw source —
79
+ * closures routinely reference secrets, API keys, or PII (e.g.
80
+ * `if (apiKey === "sk-live-xxx")`) and a preview would leak them
81
+ * into the audit log. Instead, we capture a djb2 hash of the
82
+ * stringified function (`hashObject(String(fn))`). Auditors can
83
+ * detect "the function changed between deploys" by comparing
84
+ * hashes across entries, without ever seeing the function body.
85
+ *
86
+ * Informational only — NOT replayable. (N5, M22)
87
+ */
88
+ whenSource?: {
89
+ kind: "function";
90
+ sourceHash: string;
91
+ };
92
+ }) | (AuditEntryBase & {
93
+ kind: "resolver.write.rejected";
94
+ rejection: "rejection" | "summary";
95
+ resolverId: string;
96
+ requirementId: string;
97
+ reason: string;
98
+ fact?: string;
99
+ expected?: unknown;
100
+ actual?: unknown;
101
+ dropped?: number;
102
+ }) | (AuditEntryBase & {
103
+ kind: "fact.change";
104
+ key: string;
105
+ prior: unknown;
106
+ next: unknown;
107
+ }) | (AuditEntryBase & {
108
+ kind: "resolver.complete";
109
+ resolverId: string;
110
+ requirementId: string;
111
+ duration: number;
112
+ }) | (AuditEntryBase & {
113
+ kind: "resolver.error";
114
+ resolverId: string;
115
+ requirementId: string;
116
+ error: string;
117
+ }) | (AuditEntryBase & {
118
+ kind: "system.init" | "system.start" | "system.stop" | "system.destroy";
119
+ }) | (AuditEntryBase & {
120
+ kind: "system.snapshot";
121
+ snapshotId: number;
122
+ trigger: string;
123
+ }) | (AuditEntryBase & {
124
+ kind: "system.history.navigate";
125
+ from: number;
126
+ to: number;
127
+ }) | (AuditEntryBase & {
128
+ kind: "system.truncated";
129
+ droppedSeq: number;
130
+ droppedCount: number;
131
+ }) | (AuditEntryBase & {
132
+ kind: "system.entry-erased";
133
+ originalKind: AuditEntryKind;
134
+ erasedAt: number;
135
+ }) | (AuditEntryBase & {
136
+ kind: "system.subject-erased";
137
+ /**
138
+ * djb2 hash of the filter (via `hashObject(filter)`). PII-safe —
139
+ * the raw filter values never land in the ledger. Pair with
140
+ * `filterShape` to see which filter fields were used. (N2)
141
+ */
142
+ filterHash: string;
143
+ /**
144
+ * Stripped-values shape of the filter — captures WHICH fields were
145
+ * present without recording their values. (N2)
146
+ */
147
+ filterShape: {
148
+ factPath: boolean;
149
+ constraintId: boolean;
150
+ kind: AuditEntryKind | readonly AuditEntryKind[] | undefined;
151
+ changedBetween: "[range]" | undefined;
152
+ };
153
+ erased: number;
154
+ });
155
+ interface QueryFilter {
156
+ /** Exact-match fact path. */
157
+ factPath?: string;
158
+ /** Filter by constraint id. */
159
+ constraintId?: string;
160
+ /** Filter by entry kind. */
161
+ kind?: AuditEntryKind | readonly AuditEntryKind[];
162
+ /** Time range as `[startMs, endMs]`, ISO strings, or epoch numbers. */
163
+ changedBetween?: [string | number | Date, string | number | Date];
164
+ /** Maximum entries returned. Default 1000. */
165
+ limit?: number;
166
+ }
167
+ /**
168
+ * Verify result — chain valid OR a break with full context for tamper visualization.
169
+ *
170
+ * Erased entries (via `ledger.erase()`) appear as legitimate chain breaks —
171
+ * `verify()` reports them in `erasedSeqs` and continues the walk from the
172
+ * tombstone's own hash. Real tamper still surfaces as `valid: false`.
173
+ *
174
+ * Forged tombstones (a caller writes `kind: "system.entry-erased"`
175
+ * directly via `sink.write()` to mask tamper as erasure) are detected:
176
+ * legitimate tombstones carry an in-module sentinel that forgeries
177
+ * cannot mint, so `verify()` reports them as tamper. (N7)
178
+ */
179
+ type VerifyResult = {
180
+ valid: true;
181
+ entryCount: number;
182
+ /**
183
+ * Seq numbers of entries legitimately broken by `erase()`
184
+ * tombstones. NOT timestamps — each entry pairs this seq with
185
+ * the per-entry `system.entry-erased.erasedAt` (ms epoch) for
186
+ * the timestamp. Empty unless the chain contains erasures.
187
+ * (N1 + M1; renamed from `erasedAt` in R3)
188
+ */
189
+ erasedSeqs?: number[];
190
+ } | {
191
+ valid: false;
192
+ brokenAt: number;
193
+ expectedHash: string;
194
+ actualHash: string;
195
+ entry: AuditEntry;
196
+ /**
197
+ * Human-readable reason for the break — populated for cases
198
+ * where the cause is more specific than "hash mismatch" (e.g.
199
+ * tombstone forgery detected via missing sentinel).
200
+ */
201
+ reason?: string;
202
+ };
203
+ interface AuditLedgerSink {
204
+ write(entry: AuditEntry): void;
205
+ query(filter: QueryFilter): readonly AuditEntry[];
206
+ recent(n: number): readonly AuditEntry[];
207
+ forFact(path: string, opts?: {
208
+ limit?: number;
209
+ }): readonly AuditEntry[];
210
+ forConstraint(id: string, opts?: {
211
+ limit?: number;
212
+ }): readonly AuditEntry[];
213
+ toJSON(): {
214
+ entries: readonly AuditEntry[];
215
+ capturedAt: number;
216
+ };
217
+ clear(): void;
218
+ destroy(): void;
219
+ /**
220
+ * Replace matching entries with marker entries IN PLACE (preserving seq +
221
+ * prevHash so the hash chain still verifies — the marker is a
222
+ * tombstone in chain terms; the chain break is what makes erasure
223
+ * visible to `verify()`). v1 implementation matches on the same
224
+ * `QueryFilter` shape used by `query()`. Returns the count of entries
225
+ * replaced.
226
+ *
227
+ * WARNING: erases only from this sink. Any external copies (toJSON
228
+ * exports, downstream pipelines) must be erased separately.
229
+ */
230
+ erase?(filter: QueryFilter, markerEntryFactory: (e: AuditEntry) => AuditEntry): number;
231
+ /**
232
+ * Optional hook fired by the sink BEFORE shifting the oldest entry
233
+ * out of a bounded ring buffer. The ledger plugin uses this to emit
234
+ * a `system.truncated` marker so an auditor sees that the log was
235
+ * truncated and where. (M23)
236
+ */
237
+ onTruncate?(handler: (droppedSeq: number, droppedCount: number) => void): void;
238
+ }
239
+ interface AuditLedgerOptions {
240
+ /** Sink to write entries to. Default: in-memory ring buffer (capacity 10k). */
241
+ sink?: AuditLedgerSink;
242
+ /**
243
+ * Whether to capture raw fact values (`prior`/`next` on fact.change,
244
+ * `actual` in whenExplain). Default `false` — PII-tagged facts are
245
+ * redacted by default. Set `true` to opt out of redaction.
246
+ */
247
+ capturePII?: boolean;
248
+ /**
249
+ * Optional caller-supplied redactor. Runs AFTER the default
250
+ * pii-tag-based redaction. Useful for additional sanitization.
251
+ */
252
+ redact?: (entry: AuditEntry) => AuditEntry;
253
+ }
254
+ interface AuditLedger {
255
+ /** The plugin to pass to `createSystem({ plugins: [...] })`. */
256
+ readonly plugin: Plugin<ModuleSchema>;
257
+ /** Query entries matching the filter. */
258
+ query(filter?: QueryFilter): readonly AuditEntry[];
259
+ /** Most recent N entries (chronological). */
260
+ recent(n: number): readonly AuditEntry[];
261
+ /** All entries that touch this fact path (exact match). */
262
+ forFact(path: string, opts?: {
263
+ limit?: number;
264
+ }): readonly AuditEntry[];
265
+ /** All entries for this constraint id. */
266
+ forConstraint(id: string, opts?: {
267
+ limit?: number;
268
+ }): readonly AuditEntry[];
269
+ /** Full ledger snapshot for export / serialization. */
270
+ toJSON(): {
271
+ entries: readonly AuditEntry[];
272
+ capturedAt: number;
273
+ };
274
+ /**
275
+ * Walk the hash chain genesis → tip. Returns `{ valid: true }` iff
276
+ * every entry's `prevHash` matches the (sync, djb2-based) hash of
277
+ * the previous entry. On break, returns the index of the first
278
+ * broken link plus the expected vs actual hashes — feed into a
279
+ * "TAMPERED" visualization.
280
+ *
281
+ * Erased entries (via `ledger.erase()`) appear as legitimate chain
282
+ * breaks — `verify()` reports them in `erasedSeqs` and continues
283
+ * the walk from the tombstone's actual hash. Real tamper still
284
+ * surfaces as `valid: false`. (N1 + M1)
285
+ *
286
+ * Forged tombstones — `kind: "system.entry-erased"` entries written
287
+ * directly via `sink.write()` to mask tamper — are detected as
288
+ * forgery. Legitimate tombstones carry an in-module sentinel that
289
+ * forgeries cannot mint. (N7)
290
+ *
291
+ * v1 ships sync djb2 only. `verify({ strong: true })` is reserved
292
+ * for v2 (SHA-256) and THROWS today — there is no silent fallback.
293
+ * Call `verify()` (no args) for tamper detection.
294
+ */
295
+ verify(opts?: {
296
+ strong?: boolean;
297
+ }): VerifyResult;
298
+ /**
299
+ * Per-subject erasure (GDPR Art. 17 stub). Replaces matching entries
300
+ * in this sink with `system.entry-erased` tombstones (preserving
301
+ * seq + prevHash so verify() can resync), then appends a chained
302
+ * `system.subject-erased` marker entry that summarises the erasure.
303
+ *
304
+ * Returns `{ erased, markerEntry }` — `markerEntry` is the chained
305
+ * `system.subject-erased` summary (the N per-entry tombstones live
306
+ * in the sink, not on the return value). (M7)
307
+ *
308
+ * When `erased === 0` (filter matched nothing), `markerEntry` is
309
+ * `null` and no marker is emitted into the chain — avoids polluting
310
+ * the audit trail with empty "erased: 0" records. (MAJOR-3)
311
+ *
312
+ * WARNING: v1 erases only from THIS sink. External copies (toJSON
313
+ * exports, downstream pipelines, persisted backups) must be erased
314
+ * separately. (C8)
315
+ */
316
+ erase(filter: QueryFilter): {
317
+ erased: number;
318
+ markerEntry: AuditEntry | null;
319
+ };
320
+ /** Empty the sink. */
321
+ clear(): void;
322
+ /** Unsubscribe + drop the sink. */
323
+ destroy(): void;
324
+ }
325
+
326
+ /**
327
+ * `memorySink()` — bounded ring-buffer implementation of
328
+ * `AuditLedgerSink`. Drops the oldest entry past `capacity`
329
+ * (default 10,000), exposes a truncation hook so the ledger plugin
330
+ * can emit a `system.truncated` marker before the shift, and
331
+ * supports in-place tombstone erasure for GDPR Art. 17.
332
+ *
333
+ * Pure storage layer — no hash chain awareness, no PII redaction.
334
+ * The plugin in `./index.ts` owns those concerns and routes them
335
+ * through this sink via `write()` / `erase()`.
336
+ */
337
+
338
+ /**
339
+ * In-memory bounded ring-buffer sink. Drops oldest entries past
340
+ * `capacity` (default 10,000). Use this as the default sink for dev,
341
+ * tests, and StackBlitz demos.
342
+ */
343
+ declare function memorySink(opts?: {
344
+ capacity?: number;
345
+ }): AuditLedgerSink;
346
+
347
+ /**
348
+ * createAuditLedger — append-only, queryable, hash-chained
349
+ * (djb2; SHA-256 reserved for v2) audit of every state change. For
350
+ * forensics and "show me why this user got that decision."
351
+ *
352
+ * Captures (per observation event):
353
+ *
354
+ * - `constraint.evaluate` → { whenSpec, whenExplain, active, whenSource? }
355
+ * - `resolver.write.rejected` (rejection + summary kinds)
356
+ * - `fact.change` → { key, prior, next }
357
+ * - `resolver.complete` → { resolverId, requirementId, duration }
358
+ * - `system.init` / `system.start` / `system.stop` / `system.destroy`
359
+ * - `system.snapshot` / `system.history.navigate` (lifecycle markers)
360
+ * - `system.truncated` (ring-buffer overflow marker)
361
+ * - `system.entry-erased` / `system.subject-erased` (GDPR Art.17 stub)
362
+ *
363
+ * PII redaction: by default, fact keys whose meta carries the `pii`
364
+ * tag (via `system.meta.byTag("pii")`) have their values replaced with
365
+ * `"[redacted]"` in `whenExplain.actual`, `fact.change.prior`,
366
+ * `fact.change.next`, and the cached `whenSpec` operands. Opt out with
367
+ * `capturePII: true`.
368
+ *
369
+ * Folder layout:
370
+ * types.ts — public type surface + version stamps
371
+ * hash.ts — djb2 dispatch + freeze helper + INTERNAL TOKEN
372
+ * sink.ts — memorySink() ring buffer
373
+ * predicate-redact.ts — PII-aware redactWhenSpec
374
+ * verify.ts — tombstone-aware chain walk
375
+ * index.ts (this) — createAuditLedger factory + plugin wiring
376
+ */
377
+
378
+ /**
379
+ * Create an audit ledger that subscribes to the given system's
380
+ * observation stream. Returns a `Plugin` to install + a query/verify
381
+ * API for the ledger.
382
+ *
383
+ * @example
384
+ * ```ts
385
+ * import { createAuditLedger } from "@directive-run/core/plugins";
386
+ *
387
+ * const ledger = createAuditLedger();
388
+ * const system = createSystem({ module, plugins: [ledger.plugin] });
389
+ * system.start();
390
+ *
391
+ * // Six months later — auditor asks "what changed cart-total in March?"
392
+ * ledger.query({
393
+ * factPath: "cartTotal",
394
+ * changedBetween: ["2026-03-01", "2026-04-01"],
395
+ * });
396
+ *
397
+ * // Verify nobody tampered with the ledger
398
+ * const verdict = await ledger.verify();
399
+ * if (!verdict.valid) {
400
+ * console.error("Tamper at entry", verdict.brokenAt);
401
+ * }
402
+ * ```
403
+ */
404
+ declare function createAuditLedger(opts?: AuditLedgerOptions): AuditLedger;
405
+
406
+ export { type AuditEntry as A, type QueryFilter as Q, type VerifyResult as V, type AuditEntryKind as a, type AuditLedger as b, type AuditLedgerOptions as c, type AuditLedgerSink as d, createAuditLedger as e, memorySink as m };