@voltro/plugin-audit 0.85.0 → 0.87.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/dist/index.js CHANGED
@@ -1,434 +1,266 @@
1
- import { audit as e } from "./mixin.js";
2
- import { Cause as t, Effect as n } from "effect";
3
- import { anonymousSubject as r, definePlugin as i, pluginInstanceName as a, rawImpersonationMark as o } from "@voltro/protocol";
4
- import { createLogger as s } from "@voltro/logger";
5
- import { and as c, column as l, eq as u, gt as d, id as f, integer as p, isNotNull as m, isNull as h, json as g, lte as _, or as v, registerRetention as ee, resolveActorSnapshot as y, retentionTtlMsFromEnv as b, scopeKeyOf as x, table as S, text as C, timestamp as w } from "@voltro/database";
6
- import { createHash as T, createHmac as E, randomBytes as D } from "node:crypto";
7
- //#region src/redactionShape.ts
8
- var O = 4, k = 32, A = 64, te = /^[A-Za-z_$][A-Za-z0-9_$]*$/, j = (e, t) => e === 0 || t <= 0 || e >= t ? `string(${e})` : `string(<${t})`, M = (e, t) => e.length <= A && te.test(e) ? e : `<key:${j(e.length, t)}>`, N = (e, t = 0, n = 16) => {
9
- if (e === null) return "null";
10
- if (e === void 0) return "undefined";
11
- let r = typeof e;
12
- if (r === "string") return j(e.length, n);
13
- if (r === "number" || r === "boolean" || r === "bigint" || r === "symbol" || r === "function") return r;
14
- if (e instanceof Date) return "date";
15
- if (t >= O) return Array.isArray(e) ? "array(…)" : "object(…)";
16
- if (Array.isArray(e)) {
17
- let r = e.slice(0, k).map((e) => N(e, t + 1, n));
18
- return e.length > k ? [...r, `…${e.length - k} more`] : r;
19
- }
20
- if (r === "object") {
21
- let r = Object.entries(e), i = {};
22
- for (let [e, a] of r.slice(0, k)) i[M(e, n)] = N(a, t + 1, n);
23
- return r.length > k && (i["…"] = `${r.length - k} more key(s)`), i;
24
- }
25
- return r;
26
- }, P = (e, t = 16) => ({ __redacted: N(e, 0, t) }), F = "_voltro_audit_log", I = [
27
- "tag",
28
- "at",
29
- "subjectId",
30
- "tenantId",
31
- "traceId",
32
- "status",
33
- "durationMs",
34
- "subject",
35
- "actor",
36
- "scope",
37
- "metadata",
38
- "input",
39
- "outcome",
40
- "errorTag"
41
- ], L = (e) => {
42
- if (e == null) return "null";
43
- if (e instanceof Date) return JSON.stringify(e.toISOString());
44
- let t = typeof e;
45
- return t === "number" || t === "boolean" || t === "string" ? JSON.stringify(e) : t === "bigint" ? JSON.stringify(String(e)) : Array.isArray(e) ? `[${e.map(L).join(",")}]` : t === "object" ? `{${Object.entries(e).filter(([, e]) => e !== void 0).sort(([e], [t]) => e < t ? -1 : +(e > t)).map(([e, t]) => `${JSON.stringify(e)}:${L(t)}`).join(",")}}` : JSON.stringify(String(e));
46
- }, ne = (e) => e instanceof Date ? e.toISOString() : e, re = (e) => L(Object.fromEntries(I.map((t) => [t, ne(e[t] ?? null)]))), ie = (e) => {
47
- let t = process.env.VOLTRO_AUDIT_CHAIN_SECRET;
48
- return t ? E("sha256", t).update(e, "utf8").digest("hex") : T("sha256").update(e, "utf8").digest("hex");
49
- }, R = (e, t) => ie(JSON.stringify([
50
- t.chainId,
51
- t.seq,
52
- t.prevHash,
53
- re(e)
54
- ])), z = (e = D(12).toString("base64url")) => {
55
- let t = 0, n = null;
56
- return {
57
- chainId: e,
58
- link: (r) => {
59
- t += 1;
60
- let i = R(r, {
61
- chainId: e,
62
- seq: t,
63
- prevHash: n
64
- }), a = {
65
- ...r,
66
- chainId: e,
67
- seq: t,
68
- prevHash: n,
69
- hash: i
70
- };
71
- return n = i, a;
72
- }
73
- };
74
- };
75
- async function* B(e, t) {
76
- let n = t.chainId === void 0 ? void 0 : u("chainId", t.chainId), r = (e, t, n) => ({
77
- table: F,
78
- predicate: e,
79
- order: n,
80
- take: t,
81
- skip: void 0,
82
- projection: void 0
83
- });
84
- if (t.limit !== void 0) {
85
- yield* await e.query(r(n, t.limit, [{
86
- column: "chainId",
87
- direction: "asc"
88
- }, {
89
- column: "seq",
90
- direction: "asc"
91
- }]));
92
- return;
93
- }
94
- let i = [...await e.query({
95
- ...r(n, void 0, [{
96
- column: "chainId",
97
- direction: "asc"
98
- }]),
99
- groupBy: ["chainId"],
100
- aggregations: [
101
- l("chainId"),
102
- {
103
- op: "max",
104
- column: "seq",
105
- alias: "lastSeq"
106
- },
107
- {
108
- op: "max",
109
- column: "id",
110
- alias: "lastId"
111
- }
112
- ]
113
- })].sort((e, t) => String(e.chainId) < String(t.chainId) ? -1 : +(String(e.chainId) > String(t.chainId)));
114
- for (let t of i) {
115
- let n = t.chainId, i = n == null ? h("chainId") : u("chainId", n), a = n == null ? [!1] : [!1, !0];
116
- for (let o of a) {
117
- if (o && t.lastSeq == null) continue;
118
- let a = [i, _("id", t.lastId)];
119
- n != null && a.push(o ? c(m("seq"), _("seq", t.lastSeq)) : h("seq"));
120
- let s;
121
- for (;;) {
122
- let t = s === void 0 ? void 0 : o ? v(d("seq", s.seq), c(u("seq", s.seq), d("id", s.id))) : d("id", s.id), n = await e.query(r(c(...a, ...t ? [t] : []), 1e3, [...o ? [{
123
- column: "seq",
124
- direction: "asc"
125
- }] : [], {
126
- column: "id",
127
- direction: "asc"
128
- }]));
129
- if (yield* n, n.length < 1e3) break;
130
- let i = n[n.length - 1];
131
- if (i.id === void 0 || i.id === s?.id) throw Error("audit verification cursor did not advance");
132
- s = i;
133
- }
134
- }
135
- }
136
- }
137
- var V = async (e, t = {}) => {
138
- let n = [], r = [], i = 0, a = 0, o = null, s = () => {
139
- o &&= (r.push({
140
- chainId: o.chainId,
141
- from: o.from,
142
- to: o.last,
143
- tip: o.tip,
144
- prunedPrefix: o.from > 1
145
- }), null);
146
- };
147
- for await (let r of B(e, t)) {
148
- let e = r.chainId, t = r.hash, c = r.seq;
149
- if (typeof e != "string" || typeof t != "string" || c == null) {
150
- i += 1;
151
- continue;
152
- }
153
- let l = Number(c), u = typeof r.prevHash == "string" ? r.prevHash : null, d = typeof r.id == "string" ? r.id : void 0;
154
- a += 1, !o || o.chainId !== e ? (s(), o = {
155
- chainId: e,
156
- from: l,
157
- last: l,
158
- tip: t
159
- }) : (l === o.last + 1 ? u !== o.tip && n.push({
160
- kind: "broken-link",
161
- chainId: e,
162
- seq: l,
163
- ...d ? { rowId: d } : {},
164
- detail: `prevHash does not match the hash of seq ${o.last}`
165
- }) : n.push({
166
- kind: "gap",
167
- chainId: e,
168
- seq: o.last + 1,
169
- detail: `chain jumps from seq ${o.last} to ${l} — ${l - o.last - 1} row(s) missing`
170
- }), o.last = l, o.tip = t), R(r, {
171
- chainId: e,
172
- seq: l,
173
- prevHash: u
174
- }) !== t && n.push({
175
- kind: "tampered",
176
- chainId: e,
177
- seq: l,
178
- ...d ? { rowId: d } : {},
179
- detail: "the row content does not hash to its stored hash — a column was altered"
180
- });
181
- }
182
- return s(), {
183
- ok: n.length === 0,
184
- rowsChecked: a,
185
- unchainedRows: i,
186
- chains: r,
187
- issues: n,
188
- keyed: !!process.env.VOLTRO_AUDIT_CHAIN_SECRET
189
- };
190
- }, H = S(F, {
191
- id: f({ prefix: "audit" }),
192
- tag: C(),
193
- at: w(),
194
- subjectId: C().nullable(),
195
- tenantId: C().nullable(),
196
- traceId: C(),
197
- status: C(),
198
- durationMs: C(),
199
- subject: g(),
200
- actor: g().nullable(),
201
- scope: g().nullable(),
202
- scopeKey: C().nullable(),
203
- metadata: g().nullable(),
204
- impersonation: g().nullable(),
205
- input: g(),
206
- outcome: g(),
207
- errorTag: C().nullable(),
208
- chainId: C().nullable(),
209
- seq: p().nullable(),
210
- prevHash: C().nullable(),
211
- hash: C().nullable()
212
- }).index("byAuditTag", ["tag"]).index("byAuditTrace", ["traceId"]).index("byAuditSubjectStatus", [
213
- "subjectId",
214
- "status",
215
- "at"
216
- ]).index("byAuditTenantStatus", [
217
- "tenantId",
218
- "status",
219
- "at"
220
- ]).index("byAuditScope", ["scopeKey", "at"]).index("byAuditChain", ["chainId", "seq"]), U = [H], W = (e) => ({
221
- tag: e.tag,
222
- at: new Date(e.ts),
223
- subjectId: e.subject.id ?? null,
224
- tenantId: e.subject.tenantId ?? null,
225
- traceId: e.traceId,
226
- status: e.outcome.kind,
227
- durationMs: String(e.outcome.durationMs),
228
- subject: e.subject,
229
- actor: e.actor ?? null,
230
- scope: e.scope ?? null,
231
- scopeKey: x(e.scope),
232
- metadata: e.metadata ?? null,
233
- impersonation: e.impersonation ?? null,
234
- input: e.input,
235
- outcome: e.outcome,
236
- errorTag: e.outcome.kind === "error" ? G(e.outcome.error) : null
237
- }), G = (e) => {
238
- if (typeof e != "object" || !e) return null;
239
- let t = e._tag;
240
- return typeof t == "string" && t !== "" ? t : null;
241
- }, K = async (e, t) => e.query({
242
- table: F,
243
- predicate: u("traceId", t),
244
- order: [{
245
- column: "at",
246
- direction: "asc"
247
- }]
248
- }), q = async (e, t, n = {}) => {
249
- let r = x(t);
250
- if (r === null) return [];
251
- let i = u("scopeKey", r);
252
- return e.query({
253
- table: F,
254
- predicate: n.status === void 0 ? i : c(i, u("status", n.status)),
255
- order: [{
256
- column: "at",
257
- direction: "desc"
258
- }],
259
- take: n.limit ?? 100
260
- });
261
- }, J = async (e, t, n = {}) => {
262
- let r = u("subjectId", t);
263
- return e.query({
264
- table: F,
265
- predicate: n.status === void 0 ? r : c(r, u("status", n.status)),
266
- order: [{
267
- column: "at",
268
- direction: "desc"
269
- }],
270
- take: n.limit ?? 100
271
- });
272
- }, Y = (e) => {
273
- let t = z();
274
- return async (n) => {
275
- let r = n.actor !== void 0 || typeof e.query != "function" ? n : {
276
- ...n,
277
- actor: await y(e, n.subject.id ?? null, n.subject.type)
278
- };
279
- await e.insert(F, t.link(W(r)));
280
- };
281
- }, X = s({ scope: "@voltro/plugin-audit" }), Z = 1e3, Q = [], ae = () => [...Q], oe = () => {
282
- Q.length = 0;
283
- }, se = (e) => {
284
- Q.push(e), Q.length > Z && Q.splice(0, Q.length - Z);
285
- }, ce = (e) => {
286
- let t = e.subject.id ? `${e.subject.type}:${e.subject.id}` : e.subject.type, n = e.outcome.kind === "ok" ? "ok" : "error", r = {
1
+ import { shapeRedaction as e } from "./redactionShape.js";
2
+ import { AUDIT_LOG_TABLE as t } from "./tableName.js";
3
+ import { auditRowHash as n, makeAuditChain as r, verifyAuditChain as i } from "./chain.js";
4
+ import { assertAuditReceipt as a, auditByScope as o, auditBySubject as s, auditByTrace as c, auditEventToRow as l, auditLogTable as u, auditLogTables as d, dataStoreAuditSink as f } from "./store.js";
5
+ import { decodeAuditAdmissionIntent as ee, decodeAuditFailureIntent as te, decodeAuditIntent as p, decodeAuditInvocationOutcomeIntent as m, freezeAuditAdmissionIntent as h, freezeAuditFailureIntent as ne, freezeAuditIntent as g, freezeAuditInvocationOutcomeIntent as _ } from "./transactionalCapture.js";
6
+ import { audit as v } from "./mixin.js";
7
+ import { auditInvocationIdentity as y, auditInvocationQuery as b } from "./invocationQuery.js";
8
+ import { Cause as x, Effect as S } from "effect";
9
+ import { anonymousSubject as C, definePlugin as w, pluginInstanceName as re, rawImpersonationMark as ie } from "@voltro/protocol";
10
+ import { createLogger as T } from "@voltro/logger";
11
+ import { eq as ae, generateId as E, registerRetention as D, resolveActorSnapshot as oe, retentionTtlMsFromEnv as O } from "@voltro/database";
12
+ //#region src/index.ts
13
+ var k = T({ scope: "@voltro/plugin-audit" }), A = 1e3, j = [], M = () => [...j], N = () => {
14
+ j.length = 0;
15
+ }, P = (e) => {
16
+ j.push(e), j.length > A && j.splice(0, j.length - A);
17
+ }, F = (e) => {
18
+ let t = e.subject.id ? `${e.subject.type}:${e.subject.id}` : e.subject.type, n = e.outcome.kind, r = {
287
19
  subject: t,
288
20
  tenant: e.subject.tenantId ?? null,
289
21
  status: n,
290
22
  durationMs: e.outcome.durationMs,
291
23
  traceId: e.traceId
292
24
  };
293
- n === "error" ? X.warn(e.tag, r) : X.info(e.tag, r);
294
- }, le = (e, t) => typeof e == "function" ? (t) => n.suspend(() => {
295
- let r = e(t);
296
- return r === void 0 ? n.void : n.isEffect(r) ? r : n.tryPromise({
297
- try: () => Promise.resolve(r),
25
+ n === "error" ? k.warn(e.tag, r) : k.info(e.tag, r);
26
+ }, I = (e, t) => typeof e == "function" ? (t) => S.suspend(() => {
27
+ let n = e(t);
28
+ return n === void 0 ? S.void : S.isEffect(n) ? n : S.tryPromise({
29
+ try: () => Promise.resolve(n),
298
30
  catch: (e) => e
299
31
  });
300
- }) : e === "datastore" ? (e) => n.suspend(() => {
301
- let r = t();
302
- return r ? n.tryPromise({
303
- try: () => r(e),
32
+ }) : e === "datastore" ? (e) => S.suspend(() => {
33
+ let n = t();
34
+ return n ? S.tryPromise({
35
+ try: () => n(e),
304
36
  catch: (e) => e
305
- }) : n.void;
306
- }) : e === "memory" ? (e) => n.sync(() => se(e)) : (e) => n.sync(() => ce(e)), ue = (e) => {
307
- let n = e.reasons.filter(t.isFailReason).map((e) => e.error), r = e.reasons.filter(t.isDieReason).map((e) => e.defect);
308
- return n[0] ?? r[0] ?? e;
309
- }, $ = "@voltro/plugin-audit", de = (e = {}) => {
310
- let t = e.sink === "datastore";
311
- t && ee({
37
+ }) : S.void;
38
+ }) : e === "memory" ? (e) => S.sync(() => P(e)) : (e) => S.sync(() => F(e)), L = (e) => {
39
+ let t = e.reasons.filter(x.isFailReason).map((e) => e.error), n = e.reasons.filter(x.isDieReason).map((e) => e.defect);
40
+ return t[0] ?? n[0] ?? e;
41
+ }, R = "@voltro/plugin-audit", z = (n = {}) => {
42
+ let r = n.sink === "datastore", i = n.mutationCapture === "transactional-outbox", o = n.failureCapture === "outbox", s = n.successCapture === "outbox", c = n.invocationCapture === "required", l = i || o || s;
43
+ if (o && !r) throw TypeError("Outbox failure capture requires datastore sink");
44
+ if (s && !r) throw TypeError("Outbox success capture requires datastore sink");
45
+ if (i && !r) throw TypeError("Transactional audit capture requires the datastore sink");
46
+ if (c && (!r || !s || !o || n.record !== void 0 && n.record !== "all")) throw TypeError("Required invocation capture needs datastore sink, success/failure outbox capture and record: all");
47
+ let u = re({
48
+ base: R,
49
+ alias: n.alias
50
+ }), v = u + "/append", y = u + "/append-failure", b = u + "/append-invocation-outcome";
51
+ r && D({
312
52
  source: "plugin",
313
- table: F,
53
+ table: t,
314
54
  timeColumn: "at",
315
- ttlMs: b(process.env.VOLTRO_AUDIT_LOG_TTL_HOURS, 8760)
55
+ ttlMs: O(process.env.VOLTRO_AUDIT_LOG_TTL_HOURS, 8760)
316
56
  });
317
- let s, c = le(e.sink, () => s), l = (e) => e === void 0 || !e.global && !e.sticky ? e : new RegExp(e.source, e.flags.replace(/[gy]/g, "")), u = l(e.exclude), d = l(e.include), f = (e) => !(u?.test(e) || d && !d.test(e)), p = (t) => {
318
- let n = e.record ?? "all";
319
- return n === "all" ? !0 : n === "errors" ? t.outcome.kind === "error" : n(t);
320
- }, m = { __redacted: "all" }, h = e.redactionLengthFloor ?? 16, g = (t) => {
321
- let n = e.redactInput ?? "all";
322
- return n === "none" ? t : n === "all" ? {
57
+ let T, A, j = I(n.sink, () => T), M = (e) => e === void 0 || !e.global && !e.sticky ? e : new RegExp(e.source, e.flags.replace(/[gy]/g, "")), N = M(n.exclude), P = M(n.include), F = (e) => !(N?.test(e) || P && !P.test(e)), z = (e) => {
58
+ let t = n.record ?? "all";
59
+ return t === "all" ? !0 : t === "errors" ? e.outcome.kind === "error" : t(e);
60
+ }, B = { __redacted: "all" }, V = n.redactionLengthFloor ?? 16, H = (t) => {
61
+ let r = n.redactInput ?? "all";
62
+ return r === "none" ? t : r === "all" ? {
323
63
  ...t,
324
- input: m
325
- } : n === "shape" ? {
64
+ input: B
65
+ } : r === "shape" ? {
326
66
  ...t,
327
- input: P(t.input, h)
67
+ input: e(t.input, V)
328
68
  } : {
329
69
  ...t,
330
- input: n(t)
70
+ input: r(t)
331
71
  };
332
- }, _ = (t) => {
333
- let n = e.redactOutcome ?? "all";
334
- if (n === "none") return t;
335
- let r = t.outcome, i = n === "all" ? m : n === "shape" ? ((e) => P(e, h))(r.kind === "ok" ? r.value : r.error) : n(t);
336
- if (r.kind === "ok") return {
72
+ }, U = (t) => {
73
+ if (t.outcome.kind === "unknown") return t;
74
+ let r = n.redactOutcome ?? "all";
75
+ if (r === "none") return t;
76
+ let i = { ...t.outcome }, a = r === "all" ? B : r === "shape" ? ((t) => e(t, V))(i.kind === "ok" ? i.value : i.error) : r(t);
77
+ if (i.kind === "ok") return {
337
78
  ...t,
338
79
  outcome: {
339
- ...r,
340
- value: i
80
+ ...i,
81
+ value: a
341
82
  }
342
83
  };
343
- let a = r.error, o = typeof a == "object" && a ? a._tag : void 0;
84
+ let o = i.error, s = typeof o == "object" && o ? o._tag : void 0;
344
85
  return {
345
86
  ...t,
346
87
  outcome: {
347
- ...r,
348
- error: o === void 0 ? i : {
349
- _tag: o,
350
- ...i
88
+ ...i,
89
+ error: s === void 0 ? a : {
90
+ _tag: s,
91
+ ...a
351
92
  }
352
93
  }
353
94
  };
354
- }, v = (t) => {
355
- let n = e.redactSubject ?? "metadata";
356
- if (n === "none") return t;
357
- if (typeof n == "function") return {
95
+ }, W = (t) => {
96
+ let r = n.redactSubject ?? "metadata";
97
+ if (r === "none") return t;
98
+ if (typeof r == "function") return {
358
99
  ...t,
359
- subject: n(t.subject)
100
+ subject: r(t.subject)
360
101
  };
361
- let r = t.subject;
362
- if (r.metadata === void 0) return t;
363
- let { metadata: i, ...a } = r, o = n === "metadata-shape" ? P(i, h) : m;
102
+ let i = t.subject;
103
+ if (i.metadata === void 0) return t;
104
+ let { metadata: a, ...o } = i, s = r === "metadata-shape" ? e(a, V) : B;
364
105
  return {
365
106
  ...t,
366
107
  subject: {
367
- ...a,
368
- metadata: o
108
+ ...o,
109
+ metadata: s
369
110
  }
370
111
  };
371
- }, y = (e) => {
372
- let t = o(e.subject);
112
+ }, G = (e) => {
113
+ let t = ie(e.subject);
373
114
  return t === null ? e : {
374
115
  ...e,
375
116
  impersonation: t
376
117
  };
377
- }, x = (e) => p(e) ? c(_(v(g(y(e))))) : n.void, S = (t) => {
378
- if (e.resolveScope !== void 0) try {
379
- return e.resolveScope(t);
118
+ }, K = (e) => U(W(H(G(e)))), se = (e) => {
119
+ if (e == null) return null;
120
+ if (n.redactActor === void 0) return e;
121
+ let { id: t, type: r } = e, i = n.redactActor(Object.freeze({ ...e }));
122
+ if (typeof i != "object" || !i) throw TypeError("Actor redaction must return displayName and email labels");
123
+ let { displayName: a, email: o } = i;
124
+ if (a !== null && typeof a != "string" || o !== null && typeof o != "string") throw TypeError("Actor redaction labels must be strings or null");
125
+ return {
126
+ id: t,
127
+ type: r,
128
+ displayName: a,
129
+ email: o
130
+ };
131
+ }, q = async (e, t) => {
132
+ let n = e.actor === void 0 ? t === void 0 ? null : await oe(t, e.subject.id, e.subject.type) : e.actor;
133
+ return {
134
+ ...K(e),
135
+ actor: se(n)
136
+ };
137
+ }, J = (e) => z(e) ? n.redactActor === void 0 ? j(K(e)) : S.tryPromise({
138
+ try: () => q(e, A),
139
+ catch: (e) => e
140
+ }).pipe(S.flatMap(j)) : S.void, Y = (e) => {
141
+ if (n.resolveScope !== void 0) try {
142
+ return n.resolveScope(e);
380
143
  } catch {
381
144
  return;
382
145
  }
383
- }, C = (t, r) => e.metadata === void 0 ? n.succeed(void 0) : n.tryPromise(async () => e.metadata({
384
- tag: t.tag,
385
- subject: t.subject,
386
- input: t.input,
387
- outcome: r
388
- })).pipe(n.catchCause(() => n.succeed(void 0))), w = (e, t) => f(t.tag) ? n.suspend(() => {
389
- let r = Date.now(), i = S(t);
390
- return e.pipe(n.tap((e) => C(t, {
391
- kind: "ok",
392
- value: e
393
- }).pipe(n.flatMap((n) => x({
394
- ts: r,
395
- tag: t.tag,
396
- subject: t.subject,
397
- ...i === void 0 ? {} : { scope: i },
398
- ...n === void 0 ? {} : { metadata: n },
399
- traceId: t.traceId,
400
- input: t.input,
401
- outcome: {
402
- kind: "ok",
403
- value: e,
404
- durationMs: Date.now() - r
405
- }
406
- }))).pipe(n.catchCause(() => n.void))), n.tapCause((e) => {
407
- let a = ue(e);
408
- return C(t, {
409
- kind: "error",
410
- error: a
411
- }).pipe(n.flatMap((e) => x({
146
+ }, X = (e, t) => n.metadata === void 0 ? S.succeed(void 0) : S.tryPromise(async () => n.metadata({
147
+ tag: e.tag,
148
+ subject: e.subject,
149
+ input: e.input,
150
+ outcome: t
151
+ })).pipe(S.catchCause(() => S.succeed(void 0))), Z = (e, t, r, i) => S.tryPromise({
152
+ try: async () => {
153
+ let a = i === void 0 ? n.resolveScope?.(e) : i.scope, o = {
154
+ ...r,
155
+ durationMs: Date.now() - t,
156
+ ...i === void 0 ? {} : { invocationId: i.outcome.invocationId }
157
+ }, s = await n.metadata?.({
158
+ ...e,
159
+ outcome: { ...o }
160
+ }), c = {
161
+ ts: t,
162
+ tag: e.tag,
163
+ subject: e.subject,
164
+ traceId: e.traceId,
165
+ input: e.input,
166
+ outcome: o,
167
+ ...a === void 0 ? {} : { scope: a },
168
+ ...s === void 0 ? {} : { metadata: s }
169
+ };
170
+ if (!z(c)) return;
171
+ if (e.enqueueOutcome === void 0 || A === void 0) throw TypeError("Required audit outcome capture is unavailable");
172
+ let l = i === void 0 ? await q(c, A) : U({
173
+ ...i,
174
+ outcome: o,
175
+ ...s === void 0 ? {} : { metadata: s }
176
+ }), u = i === void 0 ? r.kind === "error" ? ne(l) : g(l) : _(l);
177
+ await e.enqueueOutcome(i === void 0 ? r.kind === "error" ? y : v : b, u);
178
+ },
179
+ catch: (e) => e
180
+ }), ce = (e, r) => S.tryPromise({
181
+ try: async () => {
182
+ if (e.enqueueOutcome === void 0 || A === void 0 || T === void 0) throw TypeError("Required audit invocation capture is unavailable");
183
+ let i = E({
184
+ kind: "typeid",
185
+ prefix: "audit"
186
+ }, t);
187
+ if (i === null) throw TypeError("Audit invocation identity is unavailable");
188
+ let a = n.resolveScope?.(e), o = await q({
412
189
  ts: r,
413
- tag: t.tag,
414
- subject: t.subject,
415
- ...i === void 0 ? {} : { scope: i },
416
- ...e === void 0 ? {} : { metadata: e },
417
- traceId: t.traceId,
418
- input: t.input,
419
- outcome: {
190
+ tag: e.tag,
191
+ subject: e.subject,
192
+ traceId: e.traceId,
193
+ input: e.input,
194
+ ...a === void 0 ? {} : { scope: a },
195
+ outcome: Object.freeze({
196
+ kind: "unknown",
197
+ invocationId: i,
198
+ durationMs: 0
199
+ })
200
+ }, A), s = ee(h(o));
201
+ return await T(s, i), s;
202
+ },
203
+ catch: (e) => e
204
+ }), Q = (e, t) => {
205
+ if (!F(t.tag)) return e;
206
+ let n = i && t.kind === "mutation", r = o && t.kind !== "query", a = s && (t.kind === "action" || t.kind === "workflow"), l = c && (t.kind === "action" || t.kind === "workflow");
207
+ return S.suspend(() => {
208
+ let i = Date.now(), o = n || r || a ? void 0 : Y(t), s = (s) => e.pipe(S.tap((e) => n ? S.void : a ? Z(t, i, {
209
+ kind: "ok",
210
+ value: e
211
+ }, s).pipe(S.catchCause(() => (k.warn("Audit success capture could not be queued", { tag: t.tag }), S.void))) : S.suspend(() => {
212
+ let n = r ? Y(t) : o;
213
+ return X(t, {
214
+ kind: "ok",
215
+ value: e
216
+ }).pipe(S.flatMap((r) => J({
217
+ ts: i,
218
+ tag: t.tag,
219
+ subject: t.subject,
220
+ ...n === void 0 ? {} : { scope: n },
221
+ ...r === void 0 ? {} : { metadata: r },
222
+ traceId: t.traceId,
223
+ input: t.input,
224
+ outcome: {
225
+ kind: "ok",
226
+ value: e,
227
+ durationMs: Date.now() - i
228
+ }
229
+ }))).pipe(S.catchCause(() => S.void));
230
+ })), S.tapCause((e) => {
231
+ if (s !== void 0 && x.hasInterrupts(e)) return S.void;
232
+ let c = L(e);
233
+ if (r) return Z(t, i, {
420
234
  kind: "error",
421
- error: a,
422
- durationMs: Date.now() - r
423
- }
424
- }))).pipe(n.catchCause(() => n.void));
425
- }));
426
- }) : e, T = (e) => {
235
+ error: c
236
+ }, s).pipe(S.catchCause(() => (k.warn("Audit failure capture could not be queued", { tag: t.tag }), S.void)));
237
+ let l = n || a ? Y(t) : o;
238
+ return X(t, {
239
+ kind: "error",
240
+ error: c
241
+ }).pipe(S.flatMap((e) => J({
242
+ ts: i,
243
+ tag: t.tag,
244
+ subject: t.subject,
245
+ ...l === void 0 ? {} : { scope: l },
246
+ ...e === void 0 ? {} : { metadata: e },
247
+ traceId: t.traceId,
248
+ input: t.input,
249
+ outcome: {
250
+ kind: "error",
251
+ error: c,
252
+ durationMs: Date.now() - i
253
+ }
254
+ }))).pipe(S.catchCause(() => S.void));
255
+ }));
256
+ return l ? ce(t, i).pipe(S.flatMap(s)) : s();
257
+ });
258
+ }, le = (e) => {
427
259
  let t = `http.${e.method.toUpperCase()} ${e.path}`;
428
- f(t) && n.runPromise(x({
260
+ F(t) && S.runPromise(J({
429
261
  ts: Date.now() - e.durationMs,
430
262
  tag: t,
431
- subject: r(null),
263
+ subject: C(null),
432
264
  traceId: e.traceId,
433
265
  input: {
434
266
  method: e.method,
@@ -451,35 +283,98 @@ var V = async (e, t = {}) => {
451
283
  value: { status: e.status },
452
284
  durationMs: e.durationMs
453
285
  }
454
- }).pipe(n.catchCause(() => n.void)));
455
- }, E = w, D = w, O = w, k = w;
456
- return i({
457
- name: a({
458
- base: $,
459
- alias: e.alias
460
- }),
461
- baseName: $,
286
+ }).pipe(S.catchCause(() => S.void)));
287
+ }, ue = Q, de = Q, fe = Q, pe = Q, me = (e) => S.tryPromise({
288
+ try: async () => {
289
+ if (!F(e.tag)) return;
290
+ if (e.outbox === void 0) throw TypeError("Required audit transaction outbox is unavailable");
291
+ if (e.startedAt === void 0 || e.durationMs === void 0) throw TypeError("Required audit execution timing is unavailable");
292
+ let t = n.resolveScope?.(e), r = {
293
+ kind: "ok",
294
+ value: e.output,
295
+ durationMs: e.durationMs
296
+ }, i = await n.metadata?.({
297
+ ...e,
298
+ outcome: r
299
+ }), a = {
300
+ ts: e.startedAt,
301
+ tag: e.tag,
302
+ subject: e.subject,
303
+ traceId: e.traceId,
304
+ input: e.input,
305
+ outcome: r,
306
+ ...t === void 0 ? {} : { scope: t },
307
+ ...i === void 0 ? {} : { metadata: i }
308
+ };
309
+ if (!z(a)) return;
310
+ let o = await q(a, e.store), s = g(o);
311
+ await e.outbox.enqueue(v, s);
312
+ },
313
+ catch: (e) => e
314
+ }), $ = (e, n) => ({
315
+ effect: e,
316
+ handler: (r) => S.tryPromise({
317
+ try: async () => {
318
+ let i = n(r.payload);
319
+ if (r.subjectId !== i.subject.id || r.tenantId !== i.subject.tenantId || r.traceId !== i.traceId) throw TypeError("Audit intent does not match its captured delivery authority");
320
+ if (T === void 0) throw TypeError("Required audit datastore is not bound");
321
+ if (e === b) {
322
+ let e = i.outcome.invocationId;
323
+ if (e === void 0 || A === void 0) throw TypeError("Audit invocation admission is unavailable");
324
+ let n = (await A.query({
325
+ table: t,
326
+ predicate: ae("id", e),
327
+ order: [],
328
+ take: 1
329
+ })).find((t) => t.id === e);
330
+ if (n === void 0) throw TypeError("Audit invocation admission is missing");
331
+ a({
332
+ ...i,
333
+ metadata: void 0,
334
+ outcome: {
335
+ kind: "unknown",
336
+ invocationId: e,
337
+ durationMs: 0
338
+ }
339
+ }, n), await T(i, e + ":outcome");
340
+ } else await T(i, r.outboxId);
341
+ },
342
+ catch: (e) => e
343
+ })
344
+ });
345
+ return w({
346
+ name: u,
347
+ baseName: R,
462
348
  description: "Records every mutation invocation; ships an audit() schema mixin for row-level metadata.",
463
349
  permissions: [
464
350
  "rpc:intercept:mutation",
465
351
  "rpc:intercept:action",
466
352
  "rpc:intercept:workflow",
467
353
  "http:intercept",
468
- ...t ? ["store:write"] : [],
469
- ...e.recordQueries === !0 ? ["rpc:intercept:query"] : []
354
+ ...r ? ["store:write"] : [],
355
+ ...l ? ["outbox:handle"] : [],
356
+ ...n.recordQueries === !0 ? ["rpc:intercept:query"] : []
470
357
  ],
471
- ...t ? {
472
- ...e.tables === !1 ? {} : { extendSchema: { tables: U } },
358
+ ...r ? {
359
+ ...n.tables === !1 ? {} : { extendSchema: { tables: d } },
473
360
  bindDataStore: (e) => {
474
- s = Y(e);
361
+ if (o && (typeof e != "object" || !e || !("query" in e) || typeof e.query != "function")) throw TypeError("Audit failure capture requires readable persistence");
362
+ if (s && (typeof e != "object" || !e || !("query" in e) || typeof e.query != "function")) throw TypeError("Audit success capture requires readable persistence");
363
+ T = f(e), typeof e == "object" && e && "query" in e && typeof e.query == "function" && (A = e);
475
364
  }
476
365
  } : {},
477
- interceptMutation: E,
478
- interceptAction: D,
479
- interceptWorkflow: O,
480
- onHttpRoute: T,
481
- ...e.recordQueries === !0 ? { interceptQuery: k } : {}
366
+ ...i ? { beforeMutationCommit: me } : {},
367
+ ...l ? { outboxHandlers: [
368
+ ...i || s ? [$(v, p)] : [],
369
+ ...o ? [$(y, te)] : [],
370
+ ...c ? [$(b, m)] : []
371
+ ] } : {},
372
+ interceptMutation: ue,
373
+ interceptAction: de,
374
+ interceptWorkflow: fe,
375
+ onHttpRoute: le,
376
+ ...n.recordQueries === !0 ? { interceptQuery: pe } : {}
482
377
  });
483
378
  };
484
379
  //#endregion
485
- export { F as AUDIT_LOG_TABLE, e as audit, q as auditByScope, J as auditBySubject, K as auditByTrace, W as auditEventToRow, H as auditLogTable, U as auditLogTables, de as auditPlugin, R as auditRowHash, oe as clearAuditBuffer, Y as dataStoreAuditSink, z as makeAuditChain, ae as readAuditBuffer, V as verifyAuditChain };
380
+ export { t as AUDIT_LOG_TABLE, v as audit, o as auditByScope, s as auditBySubject, c as auditByTrace, l as auditEventToRow, y as auditInvocationIdentity, b as auditInvocationQuery, u as auditLogTable, d as auditLogTables, z as auditPlugin, n as auditRowHash, N as clearAuditBuffer, f as dataStoreAuditSink, r as makeAuditChain, M as readAuditBuffer, i as verifyAuditChain };