@voltro/plugin-governance 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,338 +1,94 @@
1
1
  import { consentDescriptor as e, eraseDescriptor as t, exportDescriptor as n, governanceRpcClientImports as r, hasConsentDescriptor as i } from "./rpc.js";
2
- import { Context as a, Effect as o, Layer as s } from "effect";
3
- import { ADMIN_SCOPE as c, definePlugin as l, pluginInstanceName as u, requireScope as d } from "@voltro/protocol";
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";
6
- //#region src/store.ts
7
- var A = (e) => {
8
- let t = e, n = (e, t) => ({
9
- table: void 0,
10
- predicate: void 0,
11
- order: [{
12
- column: "id",
13
- direction: "asc"
14
- }],
15
- take: e,
16
- skip: t,
17
- projection: void 0
18
- });
19
- return {
20
- all: (e) => t.query({
21
- ...n(void 0, void 0),
22
- table: e
23
- }),
24
- where: async (e, r, i) => i.length === 0 ? [] : t.query({
25
- ...n(void 0, void 0),
26
- table: e,
27
- predicate: {
28
- column: r,
29
- op: "in",
30
- value: [...i]
31
- }
32
- }),
33
- page: (e, r, i) => t.query({
34
- ...n(r, i),
35
- table: e
36
- }),
37
- delete: async (e, n) => {
38
- await (t.hardDelete ?? t.delete).call(t, e, n);
39
- },
40
- update: async (e, n, r) => {
41
- await t.update(e, n, r);
42
- }
43
- };
44
- }, j = (e = {}) => {
45
- let t = new Map(Object.entries(e).map(([e, t]) => [e, t.map((e) => ({ ...e }))])), n = (e) => {
46
- let n = t.get(e);
47
- return n || (n = [], t.set(e, n)), n;
48
- };
49
- return {
50
- all: async (e) => [...n(e)],
51
- where: async (e, t, r) => {
52
- if (r.length === 0) return [];
53
- let i = new Set(r.map((e) => String(e)));
54
- return n(e).filter((e) => i.has(String(e[t]))).map((e) => ({ ...e }));
55
- },
56
- page: async (e, t, r) => n(e).slice(r, r + t).map((e) => ({ ...e })),
57
- delete: async (e, t) => {
58
- let r = n(e), i = r.findIndex((e) => String(e.id) === t);
59
- i !== -1 && r.splice(i, 1);
60
- },
61
- update: async (e, t, r) => {
62
- let i = n(e), a = i.findIndex((e) => String(e.id) === t);
63
- a !== -1 && (i[a] = {
64
- ...i[a],
65
- ...r
66
- });
67
- },
68
- dump: () => Object.fromEntries([...t.entries()].map(([e, t]) => [e, t.map((e) => ({ ...e }))]))
69
- };
70
- }, M = 500, N = 5e4, P = (e, t) => {
71
- let n = [];
72
- for (let r = 0; r < e.length; r += t) n.push(e.slice(r, r + t));
73
- return n;
74
- }, F = (e, t) => e.table === t && e.subjectField === "id" ? {
75
- table: e.table,
76
- hops: [],
77
- depth: 0,
78
- via: "declared"
79
- } : {
80
- table: e.table,
81
- hops: [{
82
- table: e.table,
83
- foreignKey: e.subjectField,
84
- parentKey: "id"
85
- }],
86
- depth: 1,
87
- 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 = {
99
- rows: /* @__PURE__ */ new Map(),
100
- truncated: !1
101
- }, i.set(e, n));
102
- for (let e of t) {
103
- if (n.rows.size >= r) {
104
- n.truncated = !0;
105
- break;
106
- }
107
- n.rows.set(String(e.id ?? JSON.stringify(e)), e);
108
- }
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;
121
- }
122
- s(n.table, e), t = [...new Set(e.map((e) => String(e[n.parentKey] ?? e.id)))];
123
- }
124
- }
125
- return [...i.entries()].map(([e, t]) => ({
126
- table: e,
127
- rows: [...t.rows.values()],
128
- ...t.truncated ? { truncated: !0 } : {}
129
- }));
130
- }, L = async (e, t, n) => {
131
- let r = await I(e, t, n), i = {};
132
- for (let e of r) e.rows.length > 0 && (i[e.table] = e.rows);
133
- return i;
134
- }, R = (e, t) => {
135
- 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()) => {
137
- 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();
140
- for (let e of t) {
141
- let t = s.get(e.table);
142
- (t === void 0 || e.depth > t) && s.set(e.table, e.depth);
143
- }
144
- 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) {
146
- 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
- }
156
- l.push({
157
- table: t.table,
158
- count: t.rows.length
159
- });
160
- }
161
- return {
162
- subjectId: n,
163
- at: i(),
164
- mode: a,
165
- affected: l,
166
- ...u.length > 0 ? { truncated: u } : {}
167
- };
168
- }, B = (e, t, n) => {
169
- let r = e[t], i = r instanceof Date ? r.getTime() : typeof r == "string" || typeof r == "number" ? new Date(r).getTime() : NaN;
170
- return Number.isNaN(i) ? null : n - i;
171
- }, V = (e, t, n) => {
172
- let r = t.dateField ?? "createdAt";
173
- return e.filter((e) => {
174
- let i = B(e, r, n);
175
- return i !== null && i > t.ttlMs;
176
- }).map((e) => String(e.id));
177
- }, H = 1e3, U = async (e, t, n = Date.now(), r = H) => {
178
- let i = [];
179
- for (let a of t) {
180
- 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;
195
- }
196
- o += l.length, c = i.length < r;
197
- }
198
- i.push({
199
- table: a.table,
200
- action: t,
201
- affected: o
202
- });
203
- }
204
- return i;
205
- }, W = 1e4, G = (e = W) => {
206
- let t = /* @__PURE__ */ new Map(), n = [], r = (e, t) => `${e}|${t}`;
207
- return {
208
- record: async (i) => {
209
- t.set(r(i.subjectId, i.purpose), i), n.unshift(i), n.length > e && (n.length = e);
210
- },
211
- has: async (e, n) => t.get(r(e, n))?.granted ?? !1,
212
- list: async (e) => n.filter((t) => t.subjectId === e)
213
- };
214
- }, K = "_voltro_consent", q = O(K, {
215
- id: E({ prefix: "consent" }),
216
- subjectId: k(),
217
- purpose: k(),
218
- granted: x(),
219
- at: k(),
220
- tenantId: k().nullable()
221
- }).index("byConsentSubjectPurpose", ["subjectId", "purpose"]).index("byConsentSubject", ["subjectId"]), J = [q], Y = (e) => ({
222
- subjectId: String(e.subjectId),
223
- purpose: String(e.purpose),
224
- granted: e.granted === !0 || e.granted === 1 || e.granted === "true",
225
- at: String(e.at),
226
- tenantId: e.tenantId ?? null
227
- }), X = (e) => {
228
- let t = (t, n) => e.query({
229
- table: K,
230
- predicate: t,
231
- order: [{
232
- column: "at",
233
- direction: "desc"
234
- }],
235
- take: n,
236
- skip: void 0,
237
- projection: void 0
238
- });
239
- return {
240
- record: async (t) => {
241
- await e.insert(K, {
242
- subjectId: t.subjectId,
243
- purpose: t.purpose,
244
- granted: t.granted,
245
- at: t.at,
246
- tenantId: t.tenantId ?? null
247
- });
248
- },
249
- 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;
252
- },
253
- list: async (e) => (await t(w("subjectId", e))).map(Y)
254
- };
255
- }, Z = 1e3, Q = class extends a.Tag("@voltro/plugin-governance/GovernanceService")() {}, $ = "@voltro/plugin-governance", te = (a = {}) => {
256
- let f = u({
257
- base: $,
258
- alias: a.alias,
259
- 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)
264
- }, y = a.subjectScopes ?? [], b = a.deriveSubjectScopes, x = () => {
265
- let e = b?.subjectTable ?? "actors", t = y.map((t) => F(t, e));
266
- if (b === void 0) return {
2
+ import { dataStoreGovStore as a, memoryGovStore as o } from "./store.js";
3
+ import { SUBJECT_KEY_CHUNK as s, SUBJECT_ROW_CAP as c, assertAnonymizable as l, collectSubjectRows as u, eraseSubject as d, exportSubject as f, scopeToPath as p } from "./gdpr.js";
4
+ import { runRetention as m, selectExpired as h } from "./retention.js";
5
+ import { CONSENT_TABLE as g, consentTable as _, consentTables as v, dataStoreConsentStore as y, memoryConsentStore as b } from "./consent.js";
6
+ import { Context as x, Effect as S, Layer as C } from "effect";
7
+ import { ADMIN_SCOPE as w, definePlugin as T, pluginInstanceName as E, requireScope as D } from "@voltro/protocol";
8
+ import { deriveKey as O, makeAesCipher as k, makeFieldCipher as A, makeFieldCipher as j, resolveSecret as M, setFieldCipher as N, setFieldCipher as P } from "@voltro/runtime";
9
+ import { DEFAULT_SUBJECT_GRAPH_DEPTH as F, ENCRYPTED_PREFIX as I, deriveSubjectGraph as L, deriveSubjectGraph as R, formatSubjectGraph as z, isEncrypted as B } from "@voltro/database";
10
+ //#region src/index.ts
11
+ var V = 1e3, H = class extends x.Service()("@voltro/plugin-governance/GovernanceService") {}, U = "@voltro/plugin-governance", W = (o = {}) => {
12
+ let s = E({
13
+ base: U,
14
+ alias: o.alias,
15
+ instance: o.name
16
+ }), c = o.consent === "datastore", u = o.consent && o.consent !== "datastore" ? o.consent : c ? void 0 : b(), h, g = {
17
+ record: (e) => (u ?? h ?? b()).record(e),
18
+ has: (e, t) => (u ?? h ?? b()).has(e, t),
19
+ list: (e) => (u ?? h ?? b()).list(e)
20
+ }, _ = o.subjectScopes ?? [], x = o.deriveSubjectScopes, O = () => {
21
+ let e = x?.subjectTable ?? "actors", t = _.map((t) => p(t, e));
22
+ if (x === void 0) return {
267
23
  subjectTable: e,
268
24
  paths: t,
269
25
  limitations: []
270
26
  };
271
- let n = C({
27
+ let n = R({
272
28
  subjectTable: e,
273
- ...b.maxDepth === void 0 ? {} : { maxDepth: b.maxDepth },
274
- ...b.exclude === void 0 ? {} : { exclude: b.exclude }
29
+ ...x.maxDepth === void 0 ? {} : { maxDepth: x.maxDepth },
30
+ ...x.exclude === void 0 ? {} : { exclude: x.exclude }
275
31
  }), r = new Set(n.paths.map((e) => `${e.table}|${e.hops.map((e) => e.foreignKey).join(">")}`)), i = t.filter((e) => !r.has(`${e.table}|${e.hops.map((e) => e.foreignKey).join(">")}`));
276
32
  return {
277
33
  ...n,
278
34
  paths: [...n.paths, ...i]
279
35
  };
280
- }, 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,
36
+ }, k = () => O().paths;
37
+ for (let e of o.retention ?? []) (e.action ?? "delete") === "anonymize" && l(`retention policy '${e.table}'`, e.anonymizeFields);
38
+ o.erasure?.mode === "anonymize" && l("erasure defaults", o.erasure.anonymizeFields);
39
+ let A = typeof o.fieldEncryption == "object" ? o.fieldEncryption.secretKey ?? "VOLTRO_FIELD_ENCRYPTION_KEY" : "VOLTRO_FIELD_ENCRYPTION_KEY", N = typeof o.fieldEncryption == "object" ? o.fieldEncryption.previousSecretKeys ?? ["VOLTRO_FIELD_ENCRYPTION_KEY_PREVIOUS"] : ["VOLTRO_FIELD_ENCRYPTION_KEY_PREVIOUS"], F = o.fieldEncryption ? [{
40
+ name: A,
285
41
  required: !1,
286
42
  secret: !0,
287
43
  description: "AES-256-GCM key for `.encrypted()` columns (field encryption). Lose the key, lose the data — GCM fails closed."
288
- }, ...T.map((e) => ({
44
+ }, ...N.map((e) => ({
289
45
  name: e,
290
46
  required: !1,
291
47
  secret: !0,
292
48
  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 = {
297
- subjectGraph: x,
298
- exportSubject: (e) => L(I(), S(), e),
49
+ }))] : [], I = [], L, z, B = () => void 0, W = [], G = null, K = !1, q = () => {
50
+ if (!L) throw Error("governance: data store not bound yet");
51
+ return L;
52
+ }, J = {
53
+ subjectGraph: O,
54
+ exportSubject: (e) => f(q(), k(), e),
299
55
  eraseSubject: async (e, t) => {
300
56
  let n = {
301
- ...a.erasure,
57
+ ...o.erasure,
302
58
  ...t
303
- }, r = await z(I(), S(), e, n);
304
- return D.unshift(r), D.length > 1e3 && (D.length = Z), r;
59
+ }, r = await d(q(), k(), e, n);
60
+ return I.unshift(r), I.length > 1e3 && (I.length = V), r;
305
61
  },
306
- recordConsent: (e) => v.record(e),
307
- hasConsent: (e, t) => v.has(e, t),
62
+ recordConsent: (e) => g.record(e),
63
+ hasConsent: (e, t) => g.has(e, t),
308
64
  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 });
65
+ if (!L || !o.retention) return;
66
+ let e = await m(L, o.retention);
67
+ W = e, G = (/* @__PURE__ */ new Date()).toISOString(), B("retention sweep", { report: e });
312
68
  }
313
- }, V = [
69
+ }, Y = [
314
70
  {
315
71
  ...n,
316
72
  description: "GDPR data export for a subject (admin only).",
317
- execute: (e, t) => o.gen(function* () {
318
- return yield* d(t.request.subject, c), yield* o.promise(() => B.exportSubject(e.subjectId));
73
+ execute: (e, t) => S.gen(function* () {
74
+ return yield* D(t.request.subject, w), yield* S.promise(() => J.exportSubject(e.subjectId));
319
75
  })
320
76
  },
321
77
  {
322
78
  ...t,
323
79
  description: "GDPR erasure (right-to-be-forgotten) for a subject (admin only).",
324
- execute: (e, t) => o.gen(function* () {
325
- yield* d(t.request.subject, c);
80
+ execute: (e, t) => S.gen(function* () {
81
+ yield* D(t.request.subject, w);
326
82
  let { subjectId: n, mode: r } = e;
327
- return yield* o.promise(() => B.eraseSubject(n, r ? { mode: r } : void 0));
83
+ return yield* S.promise(() => J.eraseSubject(n, r ? { mode: r } : void 0));
328
84
  })
329
85
  },
330
86
  {
331
87
  ...e,
332
88
  description: "Record the calling subject's consent for a purpose.",
333
- execute: (e, t) => o.promise(async () => {
89
+ execute: (e, t) => S.promise(async () => {
334
90
  let { purpose: n, granted: r } = e, i = t.request.subject;
335
- return await B.recordConsent({
91
+ return await J.recordConsent({
336
92
  subjectId: i.id ?? "anonymous",
337
93
  purpose: n,
338
94
  granted: r,
@@ -344,39 +100,39 @@ var A = (e) => {
344
100
  {
345
101
  ...i,
346
102
  description: "Whether the calling subject has granted consent for a purpose.",
347
- execute: (e, t) => o.promise(async () => {
103
+ execute: (e, t) => S.promise(async () => {
348
104
  let n = t.request.subject;
349
- return { granted: await B.hasConsent(n.id ?? "anonymous", e.purpose) };
105
+ return { granted: await J.hasConsent(n.id ?? "anonymous", e.purpose) };
350
106
  })
351
107
  }
352
- ], H = (e) => ({
108
+ ], X = (e) => ({
353
109
  kind: "json",
354
110
  data: e
355
- }), W = (e, t) => ({
111
+ }), Z = (e, t) => ({
356
112
  kind: "json",
357
113
  status: e,
358
114
  data: { error: t }
359
- }), K = (e, t) => new URLSearchParams(new URL(e, "http://x").search).get(t), q = [
115
+ }), Q = (e, t) => new URLSearchParams(new URL(e, "http://x").search).get(t), $ = [
360
116
  {
361
117
  method: "GET",
362
118
  path: "/status",
363
119
  scope: "process",
364
120
  description: "Retention policies, subject scopes, field-encryption state, last sweep.",
365
- handler: () => o.succeed(H({
366
- retention: (a.retention ?? []).map((e) => ({
121
+ handler: () => S.succeed(X({
122
+ retention: (o.retention ?? []).map((e) => ({
367
123
  table: e.table,
368
124
  ttlMs: e.ttlMs,
369
125
  action: e.action ?? "delete"
370
126
  })),
371
- scopes: y.map((e) => ({
127
+ scopes: _.map((e) => ({
372
128
  table: e.table,
373
129
  subjectField: e.subjectField
374
130
  })),
375
- fieldEncryption: P,
376
- sweepIntervalMs: a.sweepIntervalMs ?? 36e5,
377
- lastSweepAt: N,
378
- lastSweep: M,
379
- erasureCount: D.length
131
+ fieldEncryption: K,
132
+ sweepIntervalMs: o.sweepIntervalMs ?? 36e5,
133
+ lastSweepAt: G,
134
+ lastSweep: W,
135
+ erasureCount: I.length
380
136
  }))
381
137
  },
382
138
  {
@@ -384,18 +140,18 @@ var A = (e) => {
384
140
  path: "/erasures",
385
141
  scope: "process",
386
142
  description: "GDPR erasure log (most recent first).",
387
- handler: () => o.succeed(H({ erasures: D.slice(0, 100) }))
143
+ handler: () => S.succeed(X({ erasures: I.slice(0, 100) }))
388
144
  },
389
145
  {
390
146
  method: "GET",
391
147
  path: "/subject-graph",
392
148
  scope: "declaration",
393
149
  description: "The routes a DSAR export/erasure walks — AND what the derivation cannot see.",
394
- handler: () => o.sync(() => {
395
- let e = x();
396
- return H({
150
+ handler: () => S.sync(() => {
151
+ let e = O();
152
+ return X({
397
153
  subjectTable: e.subjectTable,
398
- derived: b !== void 0,
154
+ derived: x !== void 0,
399
155
  paths: e.paths.map((t) => ({
400
156
  table: t.table,
401
157
  depth: t.depth,
@@ -409,29 +165,29 @@ var A = (e) => {
409
165
  {
410
166
  method: "GET",
411
167
  path: "/consent",
412
- scope: p ? "shared-store" : "process",
168
+ scope: c ? "shared-store" : "process",
413
169
  description: "Consent ledger for a subject (?subjectId=…).",
414
170
  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"));
171
+ let t = Q(e.url, "subjectId");
172
+ return t ? S.promise(async () => X({ records: await g.list(t) })) : S.succeed(Z(400, "subjectId query param required"));
417
173
  }
418
174
  },
419
175
  {
420
176
  method: "POST",
421
177
  path: "/export",
422
178
  description: "Run a GDPR subject export.",
423
- handler: (e) => o.promise(async () => {
179
+ handler: (e) => S.promise(async () => {
424
180
  let t;
425
181
  try {
426
182
  t = JSON.parse(e.body || "{}");
427
183
  } catch {
428
- return W(400, "invalid JSON body");
184
+ return Z(400, "invalid JSON body");
429
185
  }
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");
186
+ if (typeof t != "object" || !t || Array.isArray(t) || !("subjectId" in t) || typeof t.subjectId != "string" || !t.subjectId.trim()) return Z(400, "subjectId must be a non-empty string");
431
187
  try {
432
- return H({ bundle: await B.exportSubject(t.subjectId) });
188
+ return X({ bundle: await J.exportSubject(t.subjectId) });
433
189
  } catch (e) {
434
- return W(500, e.message);
190
+ return Z(500, e.message);
435
191
  }
436
192
  })
437
193
  },
@@ -439,20 +195,20 @@ var A = (e) => {
439
195
  method: "POST",
440
196
  path: "/erase",
441
197
  description: "Run a GDPR erasure (right-to-be-forgotten).",
442
- handler: (e) => o.promise(async () => {
198
+ handler: (e) => S.promise(async () => {
443
199
  let t;
444
200
  try {
445
201
  t = JSON.parse(e.body || "{}");
446
202
  } catch {
447
- return W(400, "invalid JSON body");
203
+ return Z(400, "invalid JSON body");
448
204
  }
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");
205
+ if (typeof t != "object" || !t || Array.isArray(t) || !("subjectId" in t) || typeof t.subjectId != "string" || !t.subjectId.trim()) return Z(400, "subjectId must be a non-empty string");
450
206
  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");
207
+ if (n !== void 0 && n !== "delete" && n !== "anonymize") return Z(400, "mode must be delete or anonymize");
452
208
  try {
453
- return H({ entry: await B.eraseSubject(t.subjectId, n ? { mode: n } : void 0) });
209
+ return X({ entry: await J.eraseSubject(t.subjectId, n ? { mode: n } : void 0) });
454
210
  } catch (e) {
455
- return W(500, e.message);
211
+ return Z(500, e.message);
456
212
  }
457
213
  })
458
214
  },
@@ -460,64 +216,64 @@ var A = (e) => {
460
216
  method: "POST",
461
217
  path: "/sweep",
462
218
  description: "Run the retention sweep now.",
463
- handler: () => o.promise(async () => (await B.runRetentionNow(), H({
464
- lastSweepAt: N,
465
- lastSweep: M
219
+ handler: () => S.promise(async () => (await J.runRetentionNow(), X({
220
+ lastSweepAt: G,
221
+ lastSweep: W
466
222
  })))
467
223
  }
468
224
  ];
469
- return l({
470
- name: f,
471
- baseName: $,
225
+ return T({
226
+ name: s,
227
+ baseName: U,
472
228
  description: "Data governance — retention TTL sweep, GDPR export/erasure, consent ledger.",
473
229
  permissions: [
474
230
  "store:write",
475
231
  "inspect:read",
476
232
  "inspect:write"
477
233
  ],
478
- declaredEnv: E,
479
- services: s.succeed(Q, B),
480
- routes: V,
234
+ declaredEnv: F,
235
+ services: C.succeed(H, J),
236
+ routes: Y,
481
237
  rpcClientDescriptors: r,
482
- inspectEndpoints: q,
483
- ...p ? { extendSchema: { tables: J } } : {},
238
+ inspectEndpoints: $,
239
+ ...c ? { extendSchema: { tables: v } } : {},
484
240
  bindDataStore: (e, t) => {
485
- if (O = A(e), p && (g = X(e)), a.retention && a.retention.length > 0) {
486
- let e = a.sweepIntervalMs ?? 36e5;
487
- k = t?.scheduleCoordinated("governance.retention.sweep", e, () => B.runRetentionNow());
241
+ if (L = a(e), c && (h = y(e)), o.retention && o.retention.length > 0) {
242
+ let e = o.sweepIntervalMs ?? 36e5;
243
+ z = t?.scheduleCoordinated("governance.retention.sweep", e, () => J.runRetentionNow());
488
244
  }
489
245
  },
490
- onActivate: (e) => o.gen(function* () {
491
- j = (t, n) => e.logger.warn(t, n);
246
+ onActivate: (e) => S.gen(function* () {
247
+ B = (t, n) => e.logger.warn(t, n);
492
248
  let t = !1;
493
- if (a.fieldEncryption) {
494
- let e = w, n = yield* o.promise(() => h(e));
495
- 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`));
249
+ if (o.fieldEncryption) {
250
+ let e = A, n = yield* S.promise(() => M(e));
251
+ if (!n) return yield* S.die(/* @__PURE__ */ Error(`governance: fieldEncryption is enabled but secret "${e}" did not resolve — set it via the Secrets-Resolver / env`));
496
252
  let r = [];
497
- for (let e of T) {
498
- let t = yield* o.promise(() => h(e));
253
+ for (let e of N) {
254
+ let t = yield* S.promise(() => M(e));
499
255
  t !== void 0 && t.trim() !== "" && r.push(t);
500
256
  }
501
- _(ee(n, r)), t = !0, P = !0;
257
+ P(j(n, r)), t = !0, K = !0;
502
258
  }
503
- !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
- let n = x();
505
- if (b !== void 0) {
259
+ !c && !o.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.");
260
+ let n = O();
261
+ if (x !== void 0) {
506
262
  let t = n.limitations.filter((e) => e.kind === "unreachable").length;
507
263
  n.paths.length <= 1 && e.logger.warn(`governance: the derived subject scope reached ${n.paths.length} table(s) from '${n.subjectTable}'. A DSAR would export almost nothing. Check that subjectTable names a real table and that the app declares relations() or reference() columns pointing at it.`), t > 0 && e.logger.info(`governance: ${t} registered table(s) are NOT linked to '${n.subjectTable}' and are outside every DSAR. GET /_voltro/inspect/plugins/governance/subject-graph lists them; add subjectScopes for any that hold subject data.`);
508
264
  }
509
265
  e.logger.info("governance active", {
510
- retention: a.retention?.length ?? 0,
266
+ retention: o.retention?.length ?? 0,
511
267
  scopes: n.paths.length,
512
- scopeSource: b === void 0 ? "declared" : "derived",
268
+ scopeSource: x === void 0 ? "declared" : "derived",
513
269
  fieldEncryption: t,
514
- consent: p ? "datastore" : a.consent ? "custom" : "memory"
270
+ consent: c ? "datastore" : o.consent ? "custom" : "memory"
515
271
  });
516
272
  }),
517
- onDeactivate: () => o.sync(() => {
518
- k?.stop(), k = void 0, a.fieldEncryption && _(void 0);
273
+ onDeactivate: () => S.sync(() => {
274
+ z?.stop(), z = void 0, o.fieldEncryption && P(void 0);
519
275
  })
520
276
  });
521
277
  };
522
278
  //#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 };
279
+ export { g as CONSENT_TABLE, F as DEFAULT_SUBJECT_GRAPH_DEPTH, I as ENCRYPTED_PREFIX, V as ERASURE_LOG_CAP, H as GovernanceService, s as SUBJECT_KEY_CHUNK, c as SUBJECT_ROW_CAP, u as collectSubjectRows, _ as consentTable, v as consentTables, y as dataStoreConsentStore, a as dataStoreGovStore, O as deriveKey, L as deriveSubjectGraph, d as eraseSubject, f as exportSubject, z as formatSubjectGraph, W as governancePlugin, B as isEncrypted, k as makeAesCipher, A as makeFieldCipher, b as memoryConsentStore, o as memoryGovStore, m as runRetention, p as scopeToPath, h as selectExpired, N as setFieldCipher };