@voltro/plugin-governance 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.
@@ -0,0 +1,55 @@
1
+ import { and as e, boolean as t, eq as n, id as r, table as i, text as a } from "@voltro/database";
2
+ //#region src/consent.ts
3
+ var o = 1e4, s = (e = o) => {
4
+ let t = /* @__PURE__ */ new Map(), n = [], r = (e, t) => `${e}|${t}`;
5
+ return {
6
+ record: async (i) => {
7
+ t.set(r(i.subjectId, i.purpose), i), n.unshift(i), n.length > e && (n.length = e);
8
+ },
9
+ has: async (e, n) => t.get(r(e, n))?.granted ?? !1,
10
+ list: async (e) => n.filter((t) => t.subjectId === e)
11
+ };
12
+ }, c = "_voltro_consent", l = i(c, {
13
+ id: r({ prefix: "consent" }),
14
+ subjectId: a(),
15
+ purpose: a(),
16
+ granted: t(),
17
+ at: a(),
18
+ tenantId: a().nullable()
19
+ }).index("byConsentSubjectPurpose", ["subjectId", "purpose"]).index("byConsentSubject", ["subjectId"]), u = [l], d = (e) => ({
20
+ subjectId: String(e.subjectId),
21
+ purpose: String(e.purpose),
22
+ granted: e.granted === !0 || e.granted === 1 || e.granted === "true",
23
+ at: String(e.at),
24
+ tenantId: e.tenantId ?? null
25
+ }), f = (t) => {
26
+ let r = (e, n) => t.query({
27
+ table: c,
28
+ predicate: e,
29
+ order: [{
30
+ column: "at",
31
+ direction: "desc"
32
+ }],
33
+ take: n,
34
+ skip: void 0,
35
+ projection: void 0
36
+ });
37
+ return {
38
+ record: async (e) => {
39
+ await t.insert(c, {
40
+ subjectId: e.subjectId,
41
+ purpose: e.purpose,
42
+ granted: e.granted,
43
+ at: e.at,
44
+ tenantId: e.tenantId ?? null
45
+ });
46
+ },
47
+ has: async (t, i) => {
48
+ let a = await r(e(n("subjectId", t), n("purpose", i)), 1);
49
+ return a[0] ? d(a[0]).granted : !1;
50
+ },
51
+ list: async (e) => (await r(n("subjectId", e))).map(d)
52
+ };
53
+ };
54
+ //#endregion
55
+ export { o as CONSENT_HISTORY_CAP, c as CONSENT_TABLE, l as consentTable, u as consentTables, f as dataStoreConsentStore, s as memoryConsentStore };
package/dist/gdpr.js ADDED
@@ -0,0 +1,118 @@
1
+ import { writeGovernanceRows as e } from "./store.js";
2
+ //#region src/gdpr.ts
3
+ var t = 500, n = 5e4, r = (e, t) => {
4
+ let n = [];
5
+ for (let r = 0; r < e.length; r += t) n.push(e.slice(r, r + t));
6
+ return n;
7
+ }, i = (e, t) => e.table === t && e.subjectField === "id" ? {
8
+ table: e.table,
9
+ hops: [],
10
+ depth: 0,
11
+ via: "declared"
12
+ } : {
13
+ table: e.table,
14
+ hops: [{
15
+ table: e.table,
16
+ foreignKey: e.subjectField,
17
+ parentKey: "id"
18
+ }],
19
+ depth: 1,
20
+ via: "declared"
21
+ }, a = async (e, t, i, a = n) => {
22
+ let o = /* @__PURE__ */ new Map();
23
+ if (!Number.isSafeInteger(a) || a < 1) throw Error("subject rowCap must be a positive safe integer");
24
+ let s = /* @__PURE__ */ new Map();
25
+ for (let e of t) {
26
+ let t = e.hops.length ? e.hops : [{
27
+ table: e.table,
28
+ foreignKey: "id",
29
+ parentKey: "id"
30
+ }], n = s;
31
+ for (let e of t) {
32
+ let t = JSON.stringify([
33
+ e.table,
34
+ e.foreignKey,
35
+ e.parentKey
36
+ ]), r = n.get(t);
37
+ r || (r = {
38
+ hop: e,
39
+ children: /* @__PURE__ */ new Map()
40
+ }, n.set(t, r)), n = r.children;
41
+ }
42
+ }
43
+ let c = (e) => {
44
+ let t = o.get(e);
45
+ return t || (t = {
46
+ rows: /* @__PURE__ */ new Map(),
47
+ truncated: !1
48
+ }, o.set(e, t)), t;
49
+ }, l = (e, t) => {
50
+ let n = c(e);
51
+ for (let e of t) {
52
+ let t = String(e.id ?? JSON.stringify(e));
53
+ n.rows.has(t) || (n.rows.size >= a ? n.truncated = !0 : n.rows.set(t, e));
54
+ }
55
+ }, u = async (t, n) => {
56
+ let { hop: i, children: o } = t, s = c(i.table);
57
+ for (let t of r(n, 500)) {
58
+ let n, r = !1;
59
+ for (;;) {
60
+ if (o.size === 0 && s.truncated) return;
61
+ let c = o.size || r ? 500 : Math.min(500, a - s.rows.size + 1), d = e.scan ? await e.scan(i.table, {
62
+ column: i.foreignKey,
63
+ values: t,
64
+ limit: c,
65
+ ...n === void 0 ? {} : { after: n }
66
+ }) : await e.where(i.table, i.foreignKey, t), f = s.rows.size;
67
+ if (l(i.table, d), r = d.length > 0 && s.rows.size === f, d.length === 0) break;
68
+ let p = [...new Set(d.map((e) => String(e[i.parentKey] ?? e.id)))];
69
+ for (let e of o.values()) await u(e, p);
70
+ if (!e.scan || d.length < c) break;
71
+ let m = d[d.length - 1].id;
72
+ if (m === void 0 || m === n) throw Error("governance scan must advance a stable id cursor");
73
+ n = m;
74
+ }
75
+ }
76
+ };
77
+ for (let e of s.values()) await u(e, [i]);
78
+ return [...o.entries()].map(([e, t]) => ({
79
+ table: e,
80
+ rows: [...t.rows.values()],
81
+ ...t.truncated ? { truncated: !0 } : {}
82
+ }));
83
+ }, o = async (e, t, n) => {
84
+ let r = await a(e, t, n);
85
+ if (r.some((e) => e.truncated === !0)) throw Error("governance: subject export exceeds the per-table row limit; no bundle was returned");
86
+ let i = {};
87
+ for (let e of r) e.rows.length > 0 && (i[e.table] = e.rows);
88
+ return i;
89
+ }, s = (e, t) => {
90
+ 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`);
91
+ }, c = async (t, n, r, i = {}, o = () => (/* @__PURE__ */ new Date()).toISOString()) => {
92
+ let c = i.mode ?? "delete";
93
+ c === "anonymize" && s("governance erasure", i.anonymizeFields);
94
+ let l = await a(t, n, r), u = /* @__PURE__ */ new Map();
95
+ for (let e of n) {
96
+ let t = u.get(e.table);
97
+ (t === void 0 || e.depth > t) && u.set(e.table, e.depth);
98
+ }
99
+ let d = [...l].sort((e, t) => (u.get(t.table) ?? 0) - (u.get(e.table) ?? 0)), f = [], p = [];
100
+ for (let n of d) {
101
+ if (n.rows.length === 0) continue;
102
+ n.truncated === !0 && p.push(n.table);
103
+ let r = c === "delete" ? void 0 : Object.fromEntries((i.anonymizeFields ?? []).map((e) => [e, null])), a = await e(t, n.table, n.rows.map((e) => String(e.id)), r);
104
+ f.push({
105
+ table: n.table,
106
+ count: a
107
+ });
108
+ }
109
+ return {
110
+ subjectId: r,
111
+ at: o(),
112
+ mode: c,
113
+ affected: f,
114
+ ...p.length > 0 ? { truncated: p } : {}
115
+ };
116
+ };
117
+ //#endregion
118
+ export { t as SUBJECT_KEY_CHUNK, n as SUBJECT_ROW_CAP, s as assertAnonymizable, a as collectSubjectRows, c as eraseSubject, o as exportSubject, i as scopeToPath };
package/dist/index.d.ts CHANGED
@@ -5,10 +5,13 @@ import { deriveSubjectGraph } from '@voltro/database';
5
5
  import { DeriveSubjectGraphInput } from '@voltro/database';
6
6
  import { ENCRYPTED_PREFIX } from '@voltro/database';
7
7
  import { FieldCipher } from '@voltro/database';
8
+ import { fieldKeyIdOf } from '@voltro/runtime';
8
9
  import { formatSubjectGraph } from '@voltro/database';
9
10
  import { isEncrypted } from '@voltro/database';
10
11
  import { makeAesCipher } from '@voltro/runtime';
11
12
  import { makeFieldCipher } from '@voltro/runtime';
13
+ import { makeFieldKeyring } from '@voltro/runtime';
14
+ import { resolveFieldKeyring } from '@voltro/runtime';
12
15
  import { SchemaTable } from '@voltro/database';
13
16
  import { setFieldCipher } from '@voltro/runtime';
14
17
  import { SubjectGraph } from '@voltro/database';
@@ -155,6 +158,8 @@ export declare const exportSubject: (store: GovStore, paths: ReadonlyArray<Subje
155
158
 
156
159
  export { FieldCipher }
157
160
 
161
+ export { fieldKeyIdOf }
162
+
158
163
  export { formatSubjectGraph }
159
164
 
160
165
  export declare const governancePlugin: (options?: GovernancePluginOptions) => VoltroPlugin;
@@ -334,6 +339,8 @@ export { makeAesCipher }
334
339
 
335
340
  export { makeFieldCipher }
336
341
 
342
+ export { makeFieldKeyring }
343
+
337
344
  /**
338
345
  * In-memory consent store (default + test). Latest-write-wins per
339
346
  * (subject,purpose).
@@ -354,6 +361,8 @@ export declare const memoryGovStore: (seed?: Record<string, Array<Record<string,
354
361
  dump: () => Record<string, Array<Record<string, unknown>>>;
355
362
  };
356
363
 
364
+ export { resolveFieldKeyring }
365
+
357
366
  export declare interface RetentionPolicy {
358
367
  readonly table: string;
359
368
  /** Time-to-live in ms (e.g. 90 days = 90*86_400_000). */