@voltro/plugin-audit 0.84.0 → 0.86.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,403 +1,266 @@
1
- import { audit as e } from "./mixin.js";
2
- import { Effect as t } from "effect";
3
- import { anonymousSubject as n, definePlugin as r, pluginInstanceName as i, rawImpersonationMark as a } from "@voltro/protocol";
4
- import { createLogger as o } from "@voltro/logger";
5
- import { and as s, eq as c, id as l, integer as u, json as d, registerRetention as ee, resolveActorSnapshot as f, retentionTtlMsFromEnv as p, scopeKeyOf as m, table as h, text as g, timestamp as _ } from "@voltro/database";
6
- import { createHash as v, createHmac as y, randomBytes as b } from "node:crypto";
7
- //#region src/redactionShape.ts
8
- var x = 4, S = 32, C = 64, w = /^[A-Za-z_$][A-Za-z0-9_$]*$/, T = (e, t) => e === 0 || t <= 0 || e >= t ? `string(${e})` : `string(<${t})`, E = (e, t) => e.length <= C && w.test(e) ? e : `<key:${T(e.length, t)}>`, D = (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 T(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 >= x) return Array.isArray(e) ? "array(…)" : "object(…)";
16
- if (Array.isArray(e)) {
17
- let r = e.slice(0, S).map((e) => D(e, t + 1, n));
18
- return e.length > S ? [...r, `…${e.length - S} more`] : r;
19
- }
20
- if (r === "object") {
21
- let r = Object.entries(e), i = {};
22
- for (let [e, a] of r.slice(0, S)) i[E(e, n)] = D(a, t + 1, n);
23
- return r.length > S && (i["…"] = `${r.length - S} more key(s)`), i;
24
- }
25
- return r;
26
- }, O = (e, t = 16) => ({ __redacted: D(e, 0, t) }), k = "_voltro_audit_log", A = [
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
- ], j = (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(j).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)}:${j(t)}`).join(",")}}` : JSON.stringify(String(e));
46
- }, M = (e) => e instanceof Date ? e.toISOString() : e, N = (e) => j(Object.fromEntries(A.map((t) => [t, M(e[t] ?? null)]))), P = (e) => {
47
- let t = process.env.VOLTRO_AUDIT_CHAIN_SECRET;
48
- return t ? y("sha256", t).update(e, "utf8").digest("hex") : v("sha256").update(e, "utf8").digest("hex");
49
- }, F = (e, t) => P(JSON.stringify([
50
- t.chainId,
51
- t.seq,
52
- t.prevHash,
53
- N(e)
54
- ])), I = (e = b(12).toString("base64url")) => {
55
- let t = 0, n = null;
56
- return {
57
- chainId: e,
58
- link: (r) => {
59
- t += 1;
60
- let i = F(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
- }, L = async (e, t = {}) => {
75
- let n = t.chainId === void 0 ? void 0 : c("chainId", t.chainId), r = await e.query({
76
- table: k,
77
- ...n ? { predicate: n } : {},
78
- order: [{
79
- column: "chainId",
80
- direction: "asc"
81
- }, {
82
- column: "seq",
83
- direction: "asc"
84
- }],
85
- ...t.limit === void 0 ? {} : { take: t.limit }
86
- }), i = [], a = [], o = 0, s = 0, l = null, u = () => {
87
- l &&= (a.push({
88
- chainId: l.chainId,
89
- from: l.from,
90
- to: l.last,
91
- tip: l.tip,
92
- prunedPrefix: l.from > 1
93
- }), null);
94
- };
95
- for (let e of r) {
96
- let t = e.chainId, n = e.hash, r = e.seq;
97
- if (typeof t != "string" || typeof n != "string" || r == null) {
98
- o += 1;
99
- continue;
100
- }
101
- let a = Number(r), c = typeof e.prevHash == "string" ? e.prevHash : null, d = typeof e.id == "string" ? e.id : void 0;
102
- s += 1, !l || l.chainId !== t ? (u(), l = {
103
- chainId: t,
104
- from: a,
105
- last: a,
106
- tip: n
107
- }) : (a === l.last + 1 ? c !== l.tip && i.push({
108
- kind: "broken-link",
109
- chainId: t,
110
- seq: a,
111
- ...d ? { rowId: d } : {},
112
- detail: `prevHash does not match the hash of seq ${l.last}`
113
- }) : i.push({
114
- kind: "gap",
115
- chainId: t,
116
- seq: l.last + 1,
117
- detail: `chain jumps from seq ${l.last} to ${a} — ${a - l.last - 1} row(s) missing`
118
- }), l.last = a, l.tip = n), F(e, {
119
- chainId: t,
120
- seq: a,
121
- prevHash: c
122
- }) !== n && i.push({
123
- kind: "tampered",
124
- chainId: t,
125
- seq: a,
126
- ...d ? { rowId: d } : {},
127
- detail: "the row content does not hash to its stored hash — a column was altered"
128
- });
129
- }
130
- return u(), {
131
- ok: i.length === 0,
132
- rowsChecked: s,
133
- unchainedRows: o,
134
- chains: a,
135
- issues: i,
136
- keyed: !!process.env.VOLTRO_AUDIT_CHAIN_SECRET
137
- };
138
- }, R = h(k, {
139
- id: l({ prefix: "audit" }),
140
- tag: g(),
141
- at: _(),
142
- subjectId: g().nullable(),
143
- tenantId: g().nullable(),
144
- traceId: g(),
145
- status: g(),
146
- durationMs: g(),
147
- subject: d(),
148
- actor: d().nullable(),
149
- scope: d().nullable(),
150
- scopeKey: g().nullable(),
151
- metadata: d().nullable(),
152
- impersonation: d().nullable(),
153
- input: d(),
154
- outcome: d(),
155
- errorTag: g().nullable(),
156
- chainId: g().nullable(),
157
- seq: u().nullable(),
158
- prevHash: g().nullable(),
159
- hash: g().nullable()
160
- }).index("byAuditTag", ["tag"]).index("byAuditTrace", ["traceId"]).index("byAuditSubjectStatus", [
161
- "subjectId",
162
- "status",
163
- "at"
164
- ]).index("byAuditTenantStatus", [
165
- "tenantId",
166
- "status",
167
- "at"
168
- ]).index("byAuditScope", ["scopeKey", "at"]).index("byAuditChain", ["chainId", "seq"]), z = [R], B = (e) => ({
169
- tag: e.tag,
170
- at: new Date(e.ts),
171
- subjectId: e.subject.id ?? null,
172
- tenantId: e.subject.tenantId ?? null,
173
- traceId: e.traceId,
174
- status: e.outcome.kind,
175
- durationMs: String(e.outcome.durationMs),
176
- subject: e.subject,
177
- actor: e.actor ?? null,
178
- scope: e.scope ?? null,
179
- scopeKey: m(e.scope),
180
- metadata: e.metadata ?? null,
181
- impersonation: e.impersonation ?? null,
182
- input: e.input,
183
- outcome: e.outcome,
184
- errorTag: e.outcome.kind === "error" ? V(e.outcome.error) : null
185
- }), V = (e) => {
186
- if (typeof e != "object" || !e) return null;
187
- let t = e._tag;
188
- return typeof t == "string" && t !== "" ? t : null;
189
- }, H = async (e, t) => e.query({
190
- table: k,
191
- predicate: c("traceId", t),
192
- order: [{
193
- column: "at",
194
- direction: "asc"
195
- }]
196
- }), U = async (e, t, n = {}) => {
197
- let r = m(t);
198
- if (r === null) return [];
199
- let i = c("scopeKey", r);
200
- return e.query({
201
- table: k,
202
- predicate: n.status === void 0 ? i : s(i, c("status", n.status)),
203
- order: [{
204
- column: "at",
205
- direction: "desc"
206
- }],
207
- take: n.limit ?? 100
208
- });
209
- }, W = async (e, t, n = {}) => {
210
- let r = c("subjectId", t);
211
- return e.query({
212
- table: k,
213
- predicate: n.status === void 0 ? r : s(r, c("status", n.status)),
214
- order: [{
215
- column: "at",
216
- direction: "desc"
217
- }],
218
- take: n.limit ?? 100
219
- });
220
- }, G = (e) => {
221
- let t = I();
222
- return async (n) => {
223
- let r = n.actor !== void 0 || typeof e.query != "function" ? n : {
224
- ...n,
225
- actor: await f(e, n.subject.id ?? null, n.subject.type)
226
- };
227
- await e.insert(k, t.link(B(r)));
228
- };
229
- }, K = o({ scope: "@voltro/plugin-audit" }), q = 1e3, J = [], Y = () => [...J], X = () => {
230
- J.length = 0;
231
- }, Z = (e) => {
232
- J.push(e), J.length > q && J.splice(0, J.length - q);
233
- }, Q = (e) => {
234
- 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 = {
235
19
  subject: t,
236
20
  tenant: e.subject.tenantId ?? null,
237
21
  status: n,
238
22
  durationMs: e.outcome.durationMs,
239
23
  traceId: e.traceId
240
24
  };
241
- n === "error" ? K.warn(e.tag, r) : K.info(e.tag, r);
242
- }, te = (e, n) => typeof e == "function" ? (n) => t.suspend(() => {
243
- let r = e(n);
244
- return r === void 0 ? t.void : t.isEffect(r) ? r : t.tryPromise({
245
- 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),
246
30
  catch: (e) => e
247
31
  });
248
- }) : e === "datastore" ? (e) => t.suspend(() => {
249
- let r = n();
250
- return r ? t.tryPromise({
251
- try: () => r(e),
32
+ }) : e === "datastore" ? (e) => S.suspend(() => {
33
+ let n = t();
34
+ return n ? S.tryPromise({
35
+ try: () => n(e),
252
36
  catch: (e) => e
253
- }) : t.void;
254
- }) : e === "memory" ? (e) => t.sync(() => Z(e)) : (e) => t.sync(() => Q(e)), ne = (e) => {
255
- let t = [], n = [], r = (e) => {
256
- if (e._tag !== "Empty") {
257
- if (e._tag === "Fail") {
258
- t.push(e.error);
259
- return;
260
- }
261
- if (e._tag === "Die") {
262
- n.push(e.defect);
263
- return;
264
- }
265
- if (e._tag !== "Interrupt") {
266
- if (e._tag === "Sequential") {
267
- r(e.left), r(e.right);
268
- return;
269
- }
270
- if (e._tag === "Parallel") {
271
- r(e.left), r(e.right);
272
- return;
273
- }
274
- }
275
- }
276
- };
277
- return r(e), t[0] ?? n[0] ?? e;
278
- }, $ = "@voltro/plugin-audit", re = (e = {}) => {
279
- let o = e.sink === "datastore";
280
- o && 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({
281
52
  source: "plugin",
282
- table: k,
53
+ table: t,
283
54
  timeColumn: "at",
284
- ttlMs: p(process.env.VOLTRO_AUDIT_LOG_TTL_HOURS, 8760)
55
+ ttlMs: O(process.env.VOLTRO_AUDIT_LOG_TTL_HOURS, 8760)
285
56
  });
286
- let s, c = te(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)), m = (t) => {
287
- let n = e.record ?? "all";
288
- return n === "all" ? !0 : n === "errors" ? t.outcome.kind === "error" : n(t);
289
- }, h = { __redacted: "all" }, g = e.redactionLengthFloor ?? 16, _ = (t) => {
290
- let n = e.redactInput ?? "all";
291
- 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" ? {
292
63
  ...t,
293
- input: h
294
- } : n === "shape" ? {
64
+ input: B
65
+ } : r === "shape" ? {
295
66
  ...t,
296
- input: O(t.input, g)
67
+ input: e(t.input, V)
297
68
  } : {
298
69
  ...t,
299
- input: n(t)
70
+ input: r(t)
300
71
  };
301
- }, v = (t) => {
302
- let n = e.redactOutcome ?? "all";
303
- if (n === "none") return t;
304
- let r = t.outcome, i = n === "all" ? h : n === "shape" ? ((e) => O(e, g))(r.kind === "ok" ? r.value : r.error) : n(t);
305
- 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 {
306
78
  ...t,
307
79
  outcome: {
308
- ...r,
309
- value: i
80
+ ...i,
81
+ value: a
310
82
  }
311
83
  };
312
- 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;
313
85
  return {
314
86
  ...t,
315
87
  outcome: {
316
- ...r,
317
- error: o === void 0 ? i : {
318
- _tag: o,
319
- ...i
88
+ ...i,
89
+ error: s === void 0 ? a : {
90
+ _tag: s,
91
+ ...a
320
92
  }
321
93
  }
322
94
  };
323
- }, y = (t) => {
324
- let n = e.redactSubject ?? "metadata";
325
- if (n === "none") return t;
326
- 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 {
327
99
  ...t,
328
- subject: n(t.subject)
100
+ subject: r(t.subject)
329
101
  };
330
- let r = t.subject;
331
- if (r.metadata === void 0) return t;
332
- let { metadata: i, ...a } = r, o = n === "metadata-shape" ? O(i, g) : h;
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;
333
105
  return {
334
106
  ...t,
335
107
  subject: {
336
- ...a,
337
- metadata: o
108
+ ...o,
109
+ metadata: s
338
110
  }
339
111
  };
340
- }, b = (e) => {
341
- let t = a(e.subject);
112
+ }, G = (e) => {
113
+ let t = ie(e.subject);
342
114
  return t === null ? e : {
343
115
  ...e,
344
116
  impersonation: t
345
117
  };
346
- }, x = (e) => m(e) ? c(v(y(_(b(e))))) : t.void, S = (t) => {
347
- if (e.resolveScope !== void 0) try {
348
- 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);
349
143
  } catch {
350
144
  return;
351
145
  }
352
- }, C = (n, r) => e.metadata === void 0 ? t.succeed(void 0) : t.tryPromise(async () => e.metadata({
353
- tag: n.tag,
354
- subject: n.subject,
355
- input: n.input,
356
- outcome: r
357
- })).pipe(t.catchAllCause(() => t.succeed(void 0))), w = (e, n) => f(n.tag) ? t.suspend(() => {
358
- let r = Date.now(), i = S(n);
359
- return e.pipe(t.tap((e) => C(n, {
360
- kind: "ok",
361
- value: e
362
- }).pipe(t.flatMap((t) => x({
363
- ts: r,
364
- tag: n.tag,
365
- subject: n.subject,
366
- ...i === void 0 ? {} : { scope: i },
367
- ...t === void 0 ? {} : { metadata: t },
368
- traceId: n.traceId,
369
- input: n.input,
370
- outcome: {
371
- kind: "ok",
372
- value: e,
373
- durationMs: Date.now() - r
374
- }
375
- }))).pipe(t.catchAllCause(() => t.void))), t.tapErrorCause((e) => {
376
- let a = ne(e);
377
- return C(n, {
378
- kind: "error",
379
- error: a
380
- }).pipe(t.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({
381
189
  ts: r,
382
- tag: n.tag,
383
- subject: n.subject,
384
- ...i === void 0 ? {} : { scope: i },
385
- ...e === void 0 ? {} : { metadata: e },
386
- traceId: n.traceId,
387
- input: n.input,
388
- 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, {
389
234
  kind: "error",
390
- error: a,
391
- durationMs: Date.now() - r
392
- }
393
- }))).pipe(t.catchAllCause(() => t.void));
394
- }));
395
- }) : e, T = (e) => {
396
- let r = `http.${e.method.toUpperCase()} ${e.path}`;
397
- f(r) && t.runPromise(x({
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) => {
259
+ let t = `http.${e.method.toUpperCase()} ${e.path}`;
260
+ F(t) && S.runPromise(J({
398
261
  ts: Date.now() - e.durationMs,
399
- tag: r,
400
- subject: n(null),
262
+ tag: t,
263
+ subject: C(null),
401
264
  traceId: e.traceId,
402
265
  input: {
403
266
  method: e.method,
@@ -420,35 +283,98 @@ var x = 4, S = 32, C = 64, w = /^[A-Za-z_$][A-Za-z0-9_$]*$/, T = (e, t) => e ===
420
283
  value: { status: e.status },
421
284
  durationMs: e.durationMs
422
285
  }
423
- }).pipe(t.catchAllCause(() => t.void)));
424
- }, E = w, D = w, A = w, j = w;
425
- return r({
426
- name: i({
427
- base: $,
428
- alias: e.alias
429
- }),
430
- 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,
431
348
  description: "Records every mutation invocation; ships an audit() schema mixin for row-level metadata.",
432
349
  permissions: [
433
350
  "rpc:intercept:mutation",
434
351
  "rpc:intercept:action",
435
352
  "rpc:intercept:workflow",
436
353
  "http:intercept",
437
- ...o ? ["store:write"] : [],
438
- ...e.recordQueries === !0 ? ["rpc:intercept:query"] : []
354
+ ...r ? ["store:write"] : [],
355
+ ...l ? ["outbox:handle"] : [],
356
+ ...n.recordQueries === !0 ? ["rpc:intercept:query"] : []
439
357
  ],
440
- ...o ? {
441
- ...e.tables === !1 ? {} : { extendSchema: { tables: z } },
358
+ ...r ? {
359
+ ...n.tables === !1 ? {} : { extendSchema: { tables: d } },
442
360
  bindDataStore: (e) => {
443
- s = G(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);
444
364
  }
445
365
  } : {},
446
- interceptMutation: E,
447
- interceptAction: D,
448
- interceptWorkflow: A,
449
- onHttpRoute: T,
450
- ...e.recordQueries === !0 ? { interceptQuery: j } : {}
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 } : {}
451
377
  });
452
378
  };
453
379
  //#endregion
454
- export { k as AUDIT_LOG_TABLE, e as audit, U as auditByScope, W as auditBySubject, H as auditByTrace, B as auditEventToRow, R as auditLogTable, z as auditLogTables, re as auditPlugin, F as auditRowHash, X as clearAuditBuffer, G as dataStoreAuditSink, I as makeAuditChain, Y as readAuditBuffer, L 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 };