@voltro/plugin-governance 0.83.1 → 0.85.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
@@ -2,9 +2,9 @@ import { consentDescriptor as e, eraseDescriptor as t, exportDescriptor as n, go
2
2
  import { Context as a, Effect as o, Layer as s } from "effect";
3
3
  import { ADMIN_SCOPE as c, definePlugin as l, pluginInstanceName as u, requireScope as d } from "@voltro/protocol";
4
4
  import { deriveKey as f, makeAesCipher as p, makeFieldCipher as m, makeFieldCipher as ee, resolveSecret as h, setFieldCipher as g, setFieldCipher as _ } from "@voltro/runtime";
5
- import { DEFAULT_SUBJECT_GRAPH_DEPTH as v, ENCRYPTED_PREFIX as y, and as b, boolean as x, deriveSubjectGraph as S, deriveSubjectGraph as C, eq as w, formatSubjectGraph as T, id as E, isEncrypted as D, table as O, text as k } from "@voltro/database";
5
+ import { DEFAULT_SUBJECT_GRAPH_DEPTH as v, ENCRYPTED_PREFIX as y, and as b, boolean as x, deriveSubjectGraph as S, deriveSubjectGraph as te, eq as C, formatSubjectGraph as w, getTable as T, gt as E, id as D, inSet as O, isEncrypted as k, lt as A, table as j, text as M } from "@voltro/database";
6
6
  //#region src/store.ts
7
- var A = (e) => {
7
+ var N = (e) => {
8
8
  let t = e, n = (e, t) => ({
9
9
  table: void 0,
10
10
  predicate: void 0,
@@ -34,6 +34,25 @@ var A = (e) => {
34
34
  ...n(r, i),
35
35
  table: e
36
36
  }),
37
+ scan: (e, r) => {
38
+ if (r.column !== void 0 && r.values?.length === 0) return Promise.resolve([]);
39
+ let i = [];
40
+ if (r.after !== void 0 && i.push(E("id", r.after)), r.column !== void 0) {
41
+ if (r.values === void 0) throw Error("governance scan needs values for its subject column");
42
+ i.push(O(r.column, r.values));
43
+ }
44
+ return r.expired && T(e)?.fields[r.expired.column]?.type === "timestamp" && i.push(A(r.expired.column, r.expired.before)), t.query({
45
+ ...n(r.limit, void 0),
46
+ table: e,
47
+ predicate: i.length ? b(...i) : void 0,
48
+ projection: r.projection
49
+ });
50
+ },
51
+ ...t.deleteMany && t.updateMany ? { writeMany: async (e, n, r) => {
52
+ if (n.length === 0) return 0;
53
+ let i = O("id", n);
54
+ return r === void 0 ? t.deleteMany(e, { where: i }) : t.updateMany(e, r, { where: i });
55
+ } } : {},
37
56
  delete: async (e, n) => {
38
57
  await (t.hardDelete ?? t.delete).call(t, e, n);
39
58
  },
@@ -41,7 +60,7 @@ var A = (e) => {
41
60
  await t.update(e, n, r);
42
61
  }
43
62
  };
44
- }, j = (e = {}) => {
63
+ }, P = (e = {}) => {
45
64
  let t = new Map(Object.entries(e).map(([e, t]) => [e, t.map((e) => ({ ...e }))])), n = (e) => {
46
65
  let n = t.get(e);
47
66
  return n || (n = [], t.set(e, n)), n;
@@ -54,6 +73,21 @@ var A = (e) => {
54
73
  return n(e).filter((e) => i.has(String(e[t]))).map((e) => ({ ...e }));
55
74
  },
56
75
  page: async (e, t, r) => n(e).slice(r, r + t).map((e) => ({ ...e })),
76
+ scan: async (e, t) => {
77
+ let r = t.values === void 0 ? void 0 : new Set(t.values.map(String));
78
+ return n(e).filter((e) => (t.after === void 0 || String(e.id) > String(t.after)) && (t.column === void 0 || r?.has(String(e[t.column])))).sort((e, t) => String(e.id) < String(t.id) ? -1 : +(String(e.id) > String(t.id))).slice(0, t.limit).map((e) => t.projection ? Object.fromEntries(t.projection.map((t) => [t, e[t]])) : { ...e });
79
+ },
80
+ writeMany: async (e, r, i) => {
81
+ let a = new Set(r), o = n(e), s = 0;
82
+ if (i === void 0) {
83
+ let n = o.filter((e) => !a.has(String(e.id)) || (s++, !1));
84
+ t.set(e, n);
85
+ } else for (let e = 0; e < o.length; e++) a.has(String(o[e].id)) && (o[e] = {
86
+ ...o[e],
87
+ ...i
88
+ }, s++);
89
+ return s;
90
+ },
57
91
  delete: async (e, t) => {
58
92
  let r = n(e), i = r.findIndex((e) => String(e.id) === t);
59
93
  i !== -1 && r.splice(i, 1);
@@ -67,11 +101,29 @@ var A = (e) => {
67
101
  },
68
102
  dump: () => Object.fromEntries([...t.entries()].map(([e, t]) => [e, t.map((e) => ({ ...e }))]))
69
103
  };
70
- }, M = 500, N = 5e4, P = (e, t) => {
104
+ }, F = async (e, t, n, r) => {
105
+ let i = 0;
106
+ if (e.writeMany) {
107
+ for (let a = 0; a < n.length; a += 500) i += await e.writeMany(t, n.slice(a, a + 500), r);
108
+ return i;
109
+ }
110
+ let a = 0, o = !1, s = await Promise.allSettled(Array.from({ length: Math.min(8, n.length) }, async () => {
111
+ for (; !o && a < n.length;) {
112
+ let s = n[a++];
113
+ try {
114
+ r === void 0 ? await e.delete(t, s) : await e.update(t, s, r), i++;
115
+ } catch (e) {
116
+ throw o = !0, e;
117
+ }
118
+ }
119
+ }));
120
+ for (let e of s) if (e.status === "rejected") throw e.reason;
121
+ return i;
122
+ }, I = 500, L = 5e4, R = (e, t) => {
71
123
  let n = [];
72
124
  for (let r = 0; r < e.length; r += t) n.push(e.slice(r, r + t));
73
125
  return n;
74
- }, F = (e, t) => e.table === t && e.subjectField === "id" ? {
126
+ }, z = (e, t) => e.table === t && e.subjectField === "id" ? {
75
127
  table: e.table,
76
128
  hops: [],
77
129
  depth: 0,
@@ -85,77 +137,96 @@ var A = (e) => {
85
137
  }],
86
138
  depth: 1,
87
139
  via: "declared"
88
- }, I = async (e, t, n, r = N) => {
89
- let i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map(), o = async (t, n, r) => {
90
- if (r.length === 0) return [];
91
- let i = `${t}\u0000${n}\u0000${r.join(",")}`, o = a.get(i);
92
- if (o !== void 0) return o;
93
- let s = [];
94
- for (let i of P(r, 500)) s.push(...await e.where(t, n, i));
95
- return a.set(i, s), s;
96
- }, s = (e, t) => {
97
- let n = i.get(e);
98
- n === void 0 && (n = {
140
+ }, B = async (e, t, n, r = L) => {
141
+ let i = /* @__PURE__ */ new Map();
142
+ if (!Number.isSafeInteger(r) || r < 1) throw Error("subject rowCap must be a positive safe integer");
143
+ let a = /* @__PURE__ */ new Map();
144
+ for (let e of t) {
145
+ let t = e.hops.length ? e.hops : [{
146
+ table: e.table,
147
+ foreignKey: "id",
148
+ parentKey: "id"
149
+ }], n = a;
150
+ for (let e of t) {
151
+ let t = JSON.stringify([
152
+ e.table,
153
+ e.foreignKey,
154
+ e.parentKey
155
+ ]), r = n.get(t);
156
+ r || (r = {
157
+ hop: e,
158
+ children: /* @__PURE__ */ new Map()
159
+ }, n.set(t, r)), n = r.children;
160
+ }
161
+ }
162
+ let o = (e) => {
163
+ let t = i.get(e);
164
+ return t || (t = {
99
165
  rows: /* @__PURE__ */ new Map(),
100
166
  truncated: !1
101
- }, i.set(e, n));
167
+ }, i.set(e, t)), t;
168
+ }, s = (e, t) => {
169
+ let n = o(e);
102
170
  for (let e of t) {
103
- if (n.rows.size >= r) {
104
- n.truncated = !0;
105
- break;
171
+ let t = String(e.id ?? JSON.stringify(e));
172
+ if (!n.rows.has(t)) {
173
+ if (n.rows.size >= r) {
174
+ n.truncated = !0;
175
+ continue;
176
+ }
177
+ n.rows.set(t, e);
106
178
  }
107
- n.rows.set(String(e.id ?? JSON.stringify(e)), e);
108
179
  }
109
- };
110
- for (let e of t) {
111
- if (e.hops.length === 0) {
112
- s(e.table, await o(e.table, "id", [n]));
113
- continue;
114
- }
115
- let t = [n];
116
- for (let n of e.hops) {
117
- let e = await o(n.table, n.foreignKey, t);
118
- if (e.length === 0) {
119
- t = [];
120
- break;
180
+ }, c = async (t, n) => {
181
+ let { hop: i, children: a } = t, l = o(i.table);
182
+ for (let t of R(n, 500)) {
183
+ let n;
184
+ for (;;) {
185
+ if (a.size === 0 && l.truncated) return;
186
+ let o = a.size ? 500 : Math.min(500, r - l.rows.size + 1), u = e.scan ? await e.scan(i.table, {
187
+ column: i.foreignKey,
188
+ values: t,
189
+ limit: o,
190
+ ...n === void 0 ? {} : { after: n }
191
+ }) : await e.where(i.table, i.foreignKey, t);
192
+ if (s(i.table, u), u.length === 0) break;
193
+ let d = [...new Set(u.map((e) => String(e[i.parentKey] ?? e.id)))];
194
+ for (let e of a.values()) await c(e, d);
195
+ if (!e.scan || u.length < o) break;
196
+ let f = u[u.length - 1].id;
197
+ if (f === void 0 || f === n) throw Error("governance scan must advance a stable id cursor");
198
+ n = f;
121
199
  }
122
- s(n.table, e), t = [...new Set(e.map((e) => String(e[n.parentKey] ?? e.id)))];
123
200
  }
124
- }
201
+ };
202
+ for (let e of a.values()) await c(e, [n]);
125
203
  return [...i.entries()].map(([e, t]) => ({
126
204
  table: e,
127
205
  rows: [...t.rows.values()],
128
206
  ...t.truncated ? { truncated: !0 } : {}
129
207
  }));
130
- }, L = async (e, t, n) => {
131
- let r = await I(e, t, n), i = {};
208
+ }, V = async (e, t, n) => {
209
+ let r = await B(e, t, n), i = {};
132
210
  for (let e of r) e.rows.length > 0 && (i[e.table] = e.rows);
133
211
  return i;
134
- }, R = (e, t) => {
212
+ }, H = (e, t) => {
135
213
  if (t === void 0 || t.length === 0) throw Error(`${e}: mode 'anonymize' needs a non-empty 'anonymizeFields' — with none, nothing is written and the result would report rows as affected that were not changed`);
136
- }, z = async (e, t, n, r = {}, i = () => (/* @__PURE__ */ new Date()).toISOString()) => {
214
+ }, U = async (e, t, n, r = {}, i = () => (/* @__PURE__ */ new Date()).toISOString()) => {
137
215
  let a = r.mode ?? "delete";
138
- a === "anonymize" && R("governance erasure", r.anonymizeFields);
139
- let o = await I(e, t, n), s = /* @__PURE__ */ new Map();
216
+ a === "anonymize" && H("governance erasure", r.anonymizeFields);
217
+ let o = await B(e, t, n), s = /* @__PURE__ */ new Map();
140
218
  for (let e of t) {
141
219
  let t = s.get(e.table);
142
220
  (t === void 0 || e.depth > t) && s.set(e.table, e.depth);
143
221
  }
144
222
  let c = [...o].sort((e, t) => (s.get(t.table) ?? 0) - (s.get(e.table) ?? 0)), l = [], u = [];
145
- for (let t of c) if (t.rows.length !== 0) {
223
+ for (let t of c) {
224
+ if (t.rows.length === 0) continue;
146
225
  t.truncated === !0 && u.push(t.table);
147
- for (let n of t.rows) {
148
- let i = String(n.id);
149
- if (a === "delete") await e.delete(t.table, i);
150
- else {
151
- let n = {};
152
- for (let e of r.anonymizeFields ?? []) n[e] = null;
153
- await e.update(t.table, i, n);
154
- }
155
- }
226
+ let n = a === "delete" ? void 0 : Object.fromEntries((r.anonymizeFields ?? []).map((e) => [e, null])), i = await F(e, t.table, t.rows.map((e) => String(e.id)), n);
156
227
  l.push({
157
228
  table: t.table,
158
- count: t.rows.length
229
+ count: i
159
230
  });
160
231
  }
161
232
  return {
@@ -165,35 +236,44 @@ var A = (e) => {
165
236
  affected: l,
166
237
  ...u.length > 0 ? { truncated: u } : {}
167
238
  };
168
- }, B = (e, t, n) => {
239
+ }, W = (e, t, n) => {
169
240
  let r = e[t], i = r instanceof Date ? r.getTime() : typeof r == "string" || typeof r == "number" ? new Date(r).getTime() : NaN;
170
241
  return Number.isNaN(i) ? null : n - i;
171
- }, V = (e, t, n) => {
242
+ }, G = (e, t, n) => {
172
243
  let r = t.dateField ?? "createdAt";
173
244
  return e.filter((e) => {
174
- let i = B(e, r, n);
245
+ let i = W(e, r, n);
175
246
  return i !== null && i > t.ttlMs;
176
247
  }).map((e) => String(e.id));
177
- }, H = 1e3, U = async (e, t, n = Date.now(), r = H) => {
248
+ }, ne = 1e3, K = async (e, t, n = Date.now(), r = ne) => {
249
+ if (!Number.isSafeInteger(r) || r < 1) throw Error("retention batchSize must be a positive safe integer");
178
250
  let i = [];
179
251
  for (let a of t) {
180
252
  let t = a.action ?? "delete";
181
- t === "anonymize" && R(`retention policy '${a.table}'`, a.anonymizeFields);
182
- let o = 0, s = 0, c = !1;
183
- for (; !c;) {
184
- let i = await e.page(a.table, r, s), l = V(i, a, n);
185
- if (t === "delete") {
186
- for (let t of l) await e.delete(a.table, t);
187
- s += i.length - l.length;
188
- } else {
189
- for (let t of l) {
190
- let n = {};
191
- for (let e of a.anonymizeFields ?? []) n[e] = null;
192
- await e.update(a.table, t, n);
193
- }
194
- s += i.length;
253
+ t === "anonymize" && H(`retention policy '${a.table}'`, a.anonymizeFields);
254
+ let o = 0, s = 0, c, l = !1;
255
+ for (; !l;) {
256
+ let i = a.dateField ?? "createdAt", u = new Date(Math.ceil(n - a.ttlMs)), d = e.scan ? await e.scan(a.table, {
257
+ limit: r,
258
+ ...c === void 0 ? {} : { after: c },
259
+ projection: ["id", i],
260
+ ...Number.isNaN(u.getTime()) ? {} : { expired: {
261
+ column: i,
262
+ before: u
263
+ } }
264
+ }) : await e.page(a.table, r, s), f = G(d, a, n);
265
+ if (t === "delete") o += await F(e, a.table, f), s += d.length - f.length;
266
+ else {
267
+ let t = {};
268
+ for (let e of a.anonymizeFields ?? []) t[e] = null;
269
+ o += await F(e, a.table, f, t), s += d.length;
270
+ }
271
+ if (d.length && e.scan) {
272
+ let e = d[d.length - 1].id;
273
+ if (e === void 0 || e === c) throw Error("retention scan must advance a stable id cursor");
274
+ c = e;
195
275
  }
196
- o += l.length, c = i.length < r;
276
+ l = d.length < r;
197
277
  }
198
278
  i.push({
199
279
  table: a.table,
@@ -202,7 +282,7 @@ var A = (e) => {
202
282
  });
203
283
  }
204
284
  return i;
205
- }, W = 1e4, G = (e = W) => {
285
+ }, re = 1e4, q = (e = re) => {
206
286
  let t = /* @__PURE__ */ new Map(), n = [], r = (e, t) => `${e}|${t}`;
207
287
  return {
208
288
  record: async (i) => {
@@ -211,22 +291,22 @@ var A = (e) => {
211
291
  has: async (e, n) => t.get(r(e, n))?.granted ?? !1,
212
292
  list: async (e) => n.filter((t) => t.subjectId === e)
213
293
  };
214
- }, K = "_voltro_consent", q = O(K, {
215
- id: E({ prefix: "consent" }),
216
- subjectId: k(),
217
- purpose: k(),
294
+ }, J = "_voltro_consent", Y = j(J, {
295
+ id: D({ prefix: "consent" }),
296
+ subjectId: M(),
297
+ purpose: M(),
218
298
  granted: x(),
219
- at: k(),
220
- tenantId: k().nullable()
221
- }).index("byConsentSubjectPurpose", ["subjectId", "purpose"]).index("byConsentSubject", ["subjectId"]), J = [q], Y = (e) => ({
299
+ at: M(),
300
+ tenantId: M().nullable()
301
+ }).index("byConsentSubjectPurpose", ["subjectId", "purpose"]).index("byConsentSubject", ["subjectId"]), X = [Y], Z = (e) => ({
222
302
  subjectId: String(e.subjectId),
223
303
  purpose: String(e.purpose),
224
304
  granted: e.granted === !0 || e.granted === 1 || e.granted === "true",
225
305
  at: String(e.at),
226
306
  tenantId: e.tenantId ?? null
227
- }), X = (e) => {
307
+ }), Q = (e) => {
228
308
  let t = (t, n) => e.query({
229
- table: K,
309
+ table: J,
230
310
  predicate: t,
231
311
  order: [{
232
312
  column: "at",
@@ -238,7 +318,7 @@ var A = (e) => {
238
318
  });
239
319
  return {
240
320
  record: async (t) => {
241
- await e.insert(K, {
321
+ await e.insert(J, {
242
322
  subjectId: t.subjectId,
243
323
  purpose: t.purpose,
244
324
  granted: t.granted,
@@ -247,28 +327,28 @@ var A = (e) => {
247
327
  });
248
328
  },
249
329
  has: async (e, n) => {
250
- let r = await t(b(w("subjectId", e), w("purpose", n)), 1);
251
- return r[0] ? Y(r[0]).granted : !1;
330
+ let r = await t(b(C("subjectId", e), C("purpose", n)), 1);
331
+ return r[0] ? Z(r[0]).granted : !1;
252
332
  },
253
- list: async (e) => (await t(w("subjectId", e))).map(Y)
333
+ list: async (e) => (await t(C("subjectId", e))).map(Z)
254
334
  };
255
- }, Z = 1e3, Q = class extends a.Tag("@voltro/plugin-governance/GovernanceService")() {}, $ = "@voltro/plugin-governance", te = (a = {}) => {
335
+ }, $ = 1e3, ie = class extends a.Service()("@voltro/plugin-governance/GovernanceService") {}, ae = "@voltro/plugin-governance", oe = (a = {}) => {
256
336
  let f = u({
257
- base: $,
337
+ base: ae,
258
338
  alias: a.alias,
259
339
  instance: a.name
260
- }), p = a.consent === "datastore", m = a.consent && a.consent !== "datastore" ? a.consent : p ? void 0 : G(), g, v = {
261
- record: (e) => (m ?? g ?? G()).record(e),
262
- has: (e, t) => (m ?? g ?? G()).has(e, t),
263
- list: (e) => (m ?? g ?? G()).list(e)
340
+ }), p = a.consent === "datastore", m = a.consent && a.consent !== "datastore" ? a.consent : p ? void 0 : q(), g, v = {
341
+ record: (e) => (m ?? g ?? q()).record(e),
342
+ has: (e, t) => (m ?? g ?? q()).has(e, t),
343
+ list: (e) => (m ?? g ?? q()).list(e)
264
344
  }, y = a.subjectScopes ?? [], b = a.deriveSubjectScopes, x = () => {
265
- let e = b?.subjectTable ?? "actors", t = y.map((t) => F(t, e));
345
+ let e = b?.subjectTable ?? "actors", t = y.map((t) => z(t, e));
266
346
  if (b === void 0) return {
267
347
  subjectTable: e,
268
348
  paths: t,
269
349
  limitations: []
270
350
  };
271
- let n = C({
351
+ let n = te({
272
352
  subjectTable: e,
273
353
  ...b.maxDepth === void 0 ? {} : { maxDepth: b.maxDepth },
274
354
  ...b.exclude === void 0 ? {} : { exclude: b.exclude }
@@ -278,44 +358,44 @@ var A = (e) => {
278
358
  paths: [...n.paths, ...i]
279
359
  };
280
360
  }, S = () => x().paths;
281
- for (let e of a.retention ?? []) (e.action ?? "delete") === "anonymize" && R(`retention policy '${e.table}'`, e.anonymizeFields);
282
- a.erasure?.mode === "anonymize" && R("erasure defaults", a.erasure.anonymizeFields);
283
- let w = typeof a.fieldEncryption == "object" ? a.fieldEncryption.secretKey ?? "VOLTRO_FIELD_ENCRYPTION_KEY" : "VOLTRO_FIELD_ENCRYPTION_KEY", T = typeof a.fieldEncryption == "object" ? a.fieldEncryption.previousSecretKeys ?? ["VOLTRO_FIELD_ENCRYPTION_KEY_PREVIOUS"] : ["VOLTRO_FIELD_ENCRYPTION_KEY_PREVIOUS"], E = a.fieldEncryption ? [{
284
- name: w,
361
+ for (let e of a.retention ?? []) (e.action ?? "delete") === "anonymize" && H(`retention policy '${e.table}'`, e.anonymizeFields);
362
+ a.erasure?.mode === "anonymize" && H("erasure defaults", a.erasure.anonymizeFields);
363
+ let C = typeof a.fieldEncryption == "object" ? a.fieldEncryption.secretKey ?? "VOLTRO_FIELD_ENCRYPTION_KEY" : "VOLTRO_FIELD_ENCRYPTION_KEY", w = typeof a.fieldEncryption == "object" ? a.fieldEncryption.previousSecretKeys ?? ["VOLTRO_FIELD_ENCRYPTION_KEY_PREVIOUS"] : ["VOLTRO_FIELD_ENCRYPTION_KEY_PREVIOUS"], T = a.fieldEncryption ? [{
364
+ name: C,
285
365
  required: !1,
286
366
  secret: !0,
287
367
  description: "AES-256-GCM key for `.encrypted()` columns (field encryption). Lose the key, lose the data — GCM fails closed."
288
- }, ...T.map((e) => ({
368
+ }, ...w.map((e) => ({
289
369
  name: e,
290
370
  required: !1,
291
371
  secret: !0,
292
372
  description: "A retired field-encryption key, read during a rotation so values written under it stay readable. Remove once they are re-encrypted."
293
- }))] : [], D = [], O, k, j = () => void 0, M = [], N = null, P = !1, I = () => {
294
- if (!O) throw Error("governance: data store not bound yet");
295
- return O;
296
- }, B = {
373
+ }))] : [], E = [], D, O, k = () => void 0, A = [], j = null, M = !1, P = () => {
374
+ if (!D) throw Error("governance: data store not bound yet");
375
+ return D;
376
+ }, F = {
297
377
  subjectGraph: x,
298
- exportSubject: (e) => L(I(), S(), e),
378
+ exportSubject: (e) => V(P(), S(), e),
299
379
  eraseSubject: async (e, t) => {
300
380
  let n = {
301
381
  ...a.erasure,
302
382
  ...t
303
- }, r = await z(I(), S(), e, n);
304
- return D.unshift(r), D.length > 1e3 && (D.length = Z), r;
383
+ }, r = await U(P(), S(), e, n);
384
+ return E.unshift(r), E.length > 1e3 && (E.length = $), r;
305
385
  },
306
386
  recordConsent: (e) => v.record(e),
307
387
  hasConsent: (e, t) => v.has(e, t),
308
388
  runRetentionNow: async () => {
309
- if (!O || !a.retention) return;
310
- let e = await U(O, a.retention);
311
- M = e, N = (/* @__PURE__ */ new Date()).toISOString(), j("retention sweep", { report: e });
389
+ if (!D || !a.retention) return;
390
+ let e = await K(D, a.retention);
391
+ A = e, j = (/* @__PURE__ */ new Date()).toISOString(), k("retention sweep", { report: e });
312
392
  }
313
- }, V = [
393
+ }, I = [
314
394
  {
315
395
  ...n,
316
396
  description: "GDPR data export for a subject (admin only).",
317
397
  execute: (e, t) => o.gen(function* () {
318
- return yield* d(t.request.subject, c), yield* o.promise(() => B.exportSubject(e.subjectId));
398
+ return yield* d(t.request.subject, c), yield* o.promise(() => F.exportSubject(e.subjectId));
319
399
  })
320
400
  },
321
401
  {
@@ -324,7 +404,7 @@ var A = (e) => {
324
404
  execute: (e, t) => o.gen(function* () {
325
405
  yield* d(t.request.subject, c);
326
406
  let { subjectId: n, mode: r } = e;
327
- return yield* o.promise(() => B.eraseSubject(n, r ? { mode: r } : void 0));
407
+ return yield* o.promise(() => F.eraseSubject(n, r ? { mode: r } : void 0));
328
408
  })
329
409
  },
330
410
  {
@@ -332,7 +412,7 @@ var A = (e) => {
332
412
  description: "Record the calling subject's consent for a purpose.",
333
413
  execute: (e, t) => o.promise(async () => {
334
414
  let { purpose: n, granted: r } = e, i = t.request.subject;
335
- return await B.recordConsent({
415
+ return await F.recordConsent({
336
416
  subjectId: i.id ?? "anonymous",
337
417
  purpose: n,
338
418
  granted: r,
@@ -346,23 +426,23 @@ var A = (e) => {
346
426
  description: "Whether the calling subject has granted consent for a purpose.",
347
427
  execute: (e, t) => o.promise(async () => {
348
428
  let n = t.request.subject;
349
- return { granted: await B.hasConsent(n.id ?? "anonymous", e.purpose) };
429
+ return { granted: await F.hasConsent(n.id ?? "anonymous", e.purpose) };
350
430
  })
351
431
  }
352
- ], H = (e) => ({
432
+ ], L = (e) => ({
353
433
  kind: "json",
354
434
  data: e
355
- }), W = (e, t) => ({
435
+ }), R = (e, t) => ({
356
436
  kind: "json",
357
437
  status: e,
358
438
  data: { error: t }
359
- }), K = (e, t) => new URLSearchParams(new URL(e, "http://x").search).get(t), q = [
439
+ }), B = (e, t) => new URLSearchParams(new URL(e, "http://x").search).get(t), W = [
360
440
  {
361
441
  method: "GET",
362
442
  path: "/status",
363
443
  scope: "process",
364
444
  description: "Retention policies, subject scopes, field-encryption state, last sweep.",
365
- handler: () => o.succeed(H({
445
+ handler: () => o.succeed(L({
366
446
  retention: (a.retention ?? []).map((e) => ({
367
447
  table: e.table,
368
448
  ttlMs: e.ttlMs,
@@ -372,11 +452,11 @@ var A = (e) => {
372
452
  table: e.table,
373
453
  subjectField: e.subjectField
374
454
  })),
375
- fieldEncryption: P,
455
+ fieldEncryption: M,
376
456
  sweepIntervalMs: a.sweepIntervalMs ?? 36e5,
377
- lastSweepAt: N,
378
- lastSweep: M,
379
- erasureCount: D.length
457
+ lastSweepAt: j,
458
+ lastSweep: A,
459
+ erasureCount: E.length
380
460
  }))
381
461
  },
382
462
  {
@@ -384,7 +464,7 @@ var A = (e) => {
384
464
  path: "/erasures",
385
465
  scope: "process",
386
466
  description: "GDPR erasure log (most recent first).",
387
- handler: () => o.succeed(H({ erasures: D.slice(0, 100) }))
467
+ handler: () => o.succeed(L({ erasures: E.slice(0, 100) }))
388
468
  },
389
469
  {
390
470
  method: "GET",
@@ -393,7 +473,7 @@ var A = (e) => {
393
473
  description: "The routes a DSAR export/erasure walks — AND what the derivation cannot see.",
394
474
  handler: () => o.sync(() => {
395
475
  let e = x();
396
- return H({
476
+ return L({
397
477
  subjectTable: e.subjectTable,
398
478
  derived: b !== void 0,
399
479
  paths: e.paths.map((t) => ({
@@ -412,8 +492,8 @@ var A = (e) => {
412
492
  scope: p ? "shared-store" : "process",
413
493
  description: "Consent ledger for a subject (?subjectId=…).",
414
494
  handler: (e) => {
415
- let t = K(e.url, "subjectId");
416
- return t ? o.promise(async () => H({ records: await v.list(t) })) : o.succeed(W(400, "subjectId query param required"));
495
+ let t = B(e.url, "subjectId");
496
+ return t ? o.promise(async () => L({ records: await v.list(t) })) : o.succeed(R(400, "subjectId query param required"));
417
497
  }
418
498
  },
419
499
  {
@@ -425,13 +505,13 @@ var A = (e) => {
425
505
  try {
426
506
  t = JSON.parse(e.body || "{}");
427
507
  } catch {
428
- return W(400, "invalid JSON body");
508
+ return R(400, "invalid JSON body");
429
509
  }
430
- if (typeof t != "object" || !t || Array.isArray(t) || !("subjectId" in t) || typeof t.subjectId != "string" || !t.subjectId.trim()) return W(400, "subjectId must be a non-empty string");
510
+ if (typeof t != "object" || !t || Array.isArray(t) || !("subjectId" in t) || typeof t.subjectId != "string" || !t.subjectId.trim()) return R(400, "subjectId must be a non-empty string");
431
511
  try {
432
- return H({ bundle: await B.exportSubject(t.subjectId) });
512
+ return L({ bundle: await F.exportSubject(t.subjectId) });
433
513
  } catch (e) {
434
- return W(500, e.message);
514
+ return R(500, e.message);
435
515
  }
436
516
  })
437
517
  },
@@ -444,15 +524,15 @@ var A = (e) => {
444
524
  try {
445
525
  t = JSON.parse(e.body || "{}");
446
526
  } catch {
447
- return W(400, "invalid JSON body");
527
+ return R(400, "invalid JSON body");
448
528
  }
449
- if (typeof t != "object" || !t || Array.isArray(t) || !("subjectId" in t) || typeof t.subjectId != "string" || !t.subjectId.trim()) return W(400, "subjectId must be a non-empty string");
529
+ if (typeof t != "object" || !t || Array.isArray(t) || !("subjectId" in t) || typeof t.subjectId != "string" || !t.subjectId.trim()) return R(400, "subjectId must be a non-empty string");
450
530
  let n = "mode" in t ? t.mode : void 0;
451
- if (n !== void 0 && n !== "delete" && n !== "anonymize") return W(400, "mode must be delete or anonymize");
531
+ if (n !== void 0 && n !== "delete" && n !== "anonymize") return R(400, "mode must be delete or anonymize");
452
532
  try {
453
- return H({ entry: await B.eraseSubject(t.subjectId, n ? { mode: n } : void 0) });
533
+ return L({ entry: await F.eraseSubject(t.subjectId, n ? { mode: n } : void 0) });
454
534
  } catch (e) {
455
- return W(500, e.message);
535
+ return R(500, e.message);
456
536
  }
457
537
  })
458
538
  },
@@ -460,45 +540,45 @@ var A = (e) => {
460
540
  method: "POST",
461
541
  path: "/sweep",
462
542
  description: "Run the retention sweep now.",
463
- handler: () => o.promise(async () => (await B.runRetentionNow(), H({
464
- lastSweepAt: N,
465
- lastSweep: M
543
+ handler: () => o.promise(async () => (await F.runRetentionNow(), L({
544
+ lastSweepAt: j,
545
+ lastSweep: A
466
546
  })))
467
547
  }
468
548
  ];
469
549
  return l({
470
550
  name: f,
471
- baseName: $,
551
+ baseName: ae,
472
552
  description: "Data governance — retention TTL sweep, GDPR export/erasure, consent ledger.",
473
553
  permissions: [
474
554
  "store:write",
475
555
  "inspect:read",
476
556
  "inspect:write"
477
557
  ],
478
- declaredEnv: E,
479
- services: s.succeed(Q, B),
480
- routes: V,
558
+ declaredEnv: T,
559
+ services: s.succeed(ie, F),
560
+ routes: I,
481
561
  rpcClientDescriptors: r,
482
- inspectEndpoints: q,
483
- ...p ? { extendSchema: { tables: J } } : {},
562
+ inspectEndpoints: W,
563
+ ...p ? { extendSchema: { tables: X } } : {},
484
564
  bindDataStore: (e, t) => {
485
- if (O = A(e), p && (g = X(e)), a.retention && a.retention.length > 0) {
565
+ if (D = N(e), p && (g = Q(e)), a.retention && a.retention.length > 0) {
486
566
  let e = a.sweepIntervalMs ?? 36e5;
487
- k = t?.scheduleCoordinated("governance.retention.sweep", e, () => B.runRetentionNow());
567
+ O = t?.scheduleCoordinated("governance.retention.sweep", e, () => F.runRetentionNow());
488
568
  }
489
569
  },
490
570
  onActivate: (e) => o.gen(function* () {
491
- j = (t, n) => e.logger.warn(t, n);
571
+ k = (t, n) => e.logger.warn(t, n);
492
572
  let t = !1;
493
573
  if (a.fieldEncryption) {
494
- let e = w, n = yield* o.promise(() => h(e));
574
+ let e = C, n = yield* o.promise(() => h(e));
495
575
  if (!n) return yield* o.die(/* @__PURE__ */ Error(`governance: fieldEncryption is enabled but secret "${e}" did not resolve — set it via the Secrets-Resolver / env`));
496
576
  let r = [];
497
- for (let e of T) {
577
+ for (let e of w) {
498
578
  let t = yield* o.promise(() => h(e));
499
579
  t !== void 0 && t.trim() !== "" && r.push(t);
500
580
  }
501
- _(ee(n, r)), t = !0, P = !0;
581
+ _(ee(n, r)), t = !0, M = !0;
502
582
  }
503
583
  !p && !a.consent && e.logger.warn("governance: consent ledger uses the in-memory store — per-process + restart-cleared. Under >1 replica a consent recorded on one replica is invisible to others. Pass consent: 'datastore' (durable, cross-instance) in production.");
504
584
  let n = x();
@@ -515,9 +595,9 @@ var A = (e) => {
515
595
  });
516
596
  }),
517
597
  onDeactivate: () => o.sync(() => {
518
- k?.stop(), k = void 0, a.fieldEncryption && _(void 0);
598
+ O?.stop(), O = void 0, a.fieldEncryption && _(void 0);
519
599
  })
520
600
  });
521
601
  };
522
602
  //#endregion
523
- export { K as CONSENT_TABLE, v as DEFAULT_SUBJECT_GRAPH_DEPTH, y as ENCRYPTED_PREFIX, Z as ERASURE_LOG_CAP, Q as GovernanceService, M as SUBJECT_KEY_CHUNK, N as SUBJECT_ROW_CAP, I as collectSubjectRows, q as consentTable, J as consentTables, X as dataStoreConsentStore, A as dataStoreGovStore, f as deriveKey, S as deriveSubjectGraph, z as eraseSubject, L as exportSubject, T as formatSubjectGraph, te as governancePlugin, D as isEncrypted, p as makeAesCipher, m as makeFieldCipher, G as memoryConsentStore, j as memoryGovStore, U as runRetention, F as scopeToPath, V as selectExpired, g as setFieldCipher };
603
+ export { J as CONSENT_TABLE, v as DEFAULT_SUBJECT_GRAPH_DEPTH, y as ENCRYPTED_PREFIX, $ as ERASURE_LOG_CAP, ie as GovernanceService, I as SUBJECT_KEY_CHUNK, L as SUBJECT_ROW_CAP, B as collectSubjectRows, Y as consentTable, X as consentTables, Q as dataStoreConsentStore, N as dataStoreGovStore, f as deriveKey, S as deriveSubjectGraph, U as eraseSubject, V as exportSubject, w as formatSubjectGraph, oe as governancePlugin, k as isEncrypted, p as makeAesCipher, m as makeFieldCipher, q as memoryConsentStore, P as memoryGovStore, K as runRetention, z as scopeToPath, G as selectExpired, g as setFieldCipher };