@adminiumjs/engine 0.1.0-rc.1

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.
Files changed (94) hide show
  1. package/LICENSE +661 -0
  2. package/dist/adapter-registry.d.ts +62 -0
  3. package/dist/adapter-registry.d.ts.map +1 -0
  4. package/dist/adapter-registry.js +75 -0
  5. package/dist/adapter-registry.js.map +1 -0
  6. package/dist/adapter.d.ts +384 -0
  7. package/dist/adapter.d.ts.map +1 -0
  8. package/dist/adapter.js +54 -0
  9. package/dist/adapter.js.map +1 -0
  10. package/dist/capabilities.d.ts +57 -0
  11. package/dist/capabilities.d.ts.map +1 -0
  12. package/dist/capabilities.js +131 -0
  13. package/dist/capabilities.js.map +1 -0
  14. package/dist/classify/columns.d.ts +57 -0
  15. package/dist/classify/columns.d.ts.map +1 -0
  16. package/dist/classify/columns.js +535 -0
  17. package/dist/classify/columns.js.map +1 -0
  18. package/dist/classify/index.d.ts +29 -0
  19. package/dist/classify/index.d.ts.map +1 -0
  20. package/dist/classify/index.js +44 -0
  21. package/dist/classify/index.js.map +1 -0
  22. package/dist/classify/names.d.ts +11 -0
  23. package/dist/classify/names.d.ts.map +1 -0
  24. package/dist/classify/names.js +19 -0
  25. package/dist/classify/names.js.map +1 -0
  26. package/dist/classify/pii.d.ts +29 -0
  27. package/dist/classify/pii.d.ts.map +1 -0
  28. package/dist/classify/pii.js +55 -0
  29. package/dist/classify/pii.js.map +1 -0
  30. package/dist/classify/tables.d.ts +64 -0
  31. package/dist/classify/tables.d.ts.map +1 -0
  32. package/dist/classify/tables.js +303 -0
  33. package/dist/classify/tables.js.map +1 -0
  34. package/dist/config-schema/envelope.d.ts +83 -0
  35. package/dist/config-schema/envelope.d.ts.map +1 -0
  36. package/dist/config-schema/envelope.js +87 -0
  37. package/dist/config-schema/envelope.js.map +1 -0
  38. package/dist/config-schema/index.d.ts +14 -0
  39. package/dist/config-schema/index.d.ts.map +1 -0
  40. package/dist/config-schema/index.js +15 -0
  41. package/dist/config-schema/index.js.map +1 -0
  42. package/dist/config-schema/migrations.d.ts +44 -0
  43. package/dist/config-schema/migrations.d.ts.map +1 -0
  44. package/dist/config-schema/migrations.js +62 -0
  45. package/dist/config-schema/migrations.js.map +1 -0
  46. package/dist/generate/archetype.d.ts +76 -0
  47. package/dist/generate/archetype.d.ts.map +1 -0
  48. package/dist/generate/archetype.js +250 -0
  49. package/dist/generate/archetype.js.map +1 -0
  50. package/dist/generate/crud.d.ts +36 -0
  51. package/dist/generate/crud.d.ts.map +1 -0
  52. package/dist/generate/crud.js +45 -0
  53. package/dist/generate/crud.js.map +1 -0
  54. package/dist/generate/dashboard.d.ts +43 -0
  55. package/dist/generate/dashboard.d.ts.map +1 -0
  56. package/dist/generate/dashboard.js +61 -0
  57. package/dist/generate/dashboard.js.map +1 -0
  58. package/dist/generate/domains.d.ts +29 -0
  59. package/dist/generate/domains.d.ts.map +1 -0
  60. package/dist/generate/domains.js +145 -0
  61. package/dist/generate/domains.js.map +1 -0
  62. package/dist/generate/index.d.ts +78 -0
  63. package/dist/generate/index.d.ts.map +1 -0
  64. package/dist/generate/index.js +301 -0
  65. package/dist/generate/index.js.map +1 -0
  66. package/dist/generate/util.d.ts +46 -0
  67. package/dist/generate/util.d.ts.map +1 -0
  68. package/dist/generate/util.js +123 -0
  69. package/dist/generate/util.js.map +1 -0
  70. package/dist/index.d.ts +8 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +8 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/schema-model.d.ts +1853 -0
  75. package/dist/schema-model.d.ts.map +1 -0
  76. package/dist/schema-model.js +463 -0
  77. package/dist/schema-model.js.map +1 -0
  78. package/dist/snapshot/diff.d.ts +258 -0
  79. package/dist/snapshot/diff.d.ts.map +1 -0
  80. package/dist/snapshot/diff.js +151 -0
  81. package/dist/snapshot/diff.js.map +1 -0
  82. package/dist/snapshot/hash.d.ts +23 -0
  83. package/dist/snapshot/hash.d.ts.map +1 -0
  84. package/dist/snapshot/hash.js +47 -0
  85. package/dist/snapshot/hash.js.map +1 -0
  86. package/dist/snapshot/index.d.ts +28 -0
  87. package/dist/snapshot/index.d.ts.map +1 -0
  88. package/dist/snapshot/index.js +26 -0
  89. package/dist/snapshot/index.js.map +1 -0
  90. package/dist/snapshot/sha256.d.ts +14 -0
  91. package/dist/snapshot/sha256.d.ts.map +1 -0
  92. package/dist/snapshot/sha256.js +99 -0
  93. package/dist/snapshot/sha256.js.map +1 -0
  94. package/package.json +37 -0
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Snapshot diff engine — 05-introspection-engine.md §9 `diffModels`.
3
+ *
4
+ * Pure structural diff between two `DatabaseModel`s. All output arrays are
5
+ * sorted (tables/relations by id, columns by name) so the diff itself is
6
+ * canonical and safe to hash/golden-test.
7
+ *
8
+ * NOT IMPLEMENTED HERE (deliberately, per 05 §9): rename detection.
9
+ * `rename-detect.ts` is a separate M3-follow-up — a removed+added column
10
+ * pair with identical logicalType+nullability and (same ordinal or
11
+ * Levenshtein ≤ 2 or snake/camel variant) becomes a `renamedColumns`
12
+ * suggestion at confidence 0.7, never auto-applied. Until it lands,
13
+ * `renamedColumns` is always `[]` and a drop+add reads as exactly that.
14
+ */
15
+ import { z } from 'zod';
16
+ import { type DatabaseModel } from '../schema-model.js';
17
+ export declare const changedTableSchema: z.ZodObject<{
18
+ addedColumns: z.ZodArray<z.ZodString>;
19
+ removedColumns: z.ZodArray<z.ZodString>;
20
+ typeChanged: z.ZodArray<z.ZodObject<{
21
+ column: z.ZodString;
22
+ from: z.ZodEnum<{
23
+ bigint: "bigint";
24
+ boolean: "boolean";
25
+ text: "text";
26
+ varchar: "varchar";
27
+ integer: "integer";
28
+ decimal: "decimal";
29
+ float: "float";
30
+ date: "date";
31
+ time: "time";
32
+ timestamp: "timestamp";
33
+ timestamptz: "timestamptz";
34
+ interval: "interval";
35
+ uuid: "uuid";
36
+ json: "json";
37
+ binary: "binary";
38
+ enum: "enum";
39
+ geometry: "geometry";
40
+ inet: "inet";
41
+ unknown: "unknown";
42
+ }>;
43
+ to: z.ZodEnum<{
44
+ bigint: "bigint";
45
+ boolean: "boolean";
46
+ text: "text";
47
+ varchar: "varchar";
48
+ integer: "integer";
49
+ decimal: "decimal";
50
+ float: "float";
51
+ date: "date";
52
+ time: "time";
53
+ timestamp: "timestamp";
54
+ timestamptz: "timestamptz";
55
+ interval: "interval";
56
+ uuid: "uuid";
57
+ json: "json";
58
+ binary: "binary";
59
+ enum: "enum";
60
+ geometry: "geometry";
61
+ inet: "inet";
62
+ unknown: "unknown";
63
+ }>;
64
+ }, z.core.$strict>>;
65
+ nullabilityChanged: z.ZodArray<z.ZodObject<{
66
+ column: z.ZodString;
67
+ nowNullable: z.ZodBoolean;
68
+ }, z.core.$strict>>;
69
+ enumValuesChanged: z.ZodArray<z.ZodObject<{
70
+ enumId: z.ZodString;
71
+ added: z.ZodArray<z.ZodString>;
72
+ removed: z.ZodArray<z.ZodString>;
73
+ }, z.core.$strict>>;
74
+ pkChanged: z.ZodBoolean;
75
+ renamedColumns: z.ZodArray<z.ZodObject<{
76
+ from: z.ZodString;
77
+ to: z.ZodString;
78
+ confidence: z.ZodNumber;
79
+ }, z.core.$strict>>;
80
+ }, z.core.$strict>;
81
+ export type ChangedTable = z.infer<typeof changedTableSchema>;
82
+ export declare const schemaDiffSchema: z.ZodObject<{
83
+ addedTables: z.ZodArray<z.ZodString>;
84
+ removedTables: z.ZodArray<z.ZodString>;
85
+ changedTables: z.ZodRecord<z.ZodString, z.ZodObject<{
86
+ addedColumns: z.ZodArray<z.ZodString>;
87
+ removedColumns: z.ZodArray<z.ZodString>;
88
+ typeChanged: z.ZodArray<z.ZodObject<{
89
+ column: z.ZodString;
90
+ from: z.ZodEnum<{
91
+ bigint: "bigint";
92
+ boolean: "boolean";
93
+ text: "text";
94
+ varchar: "varchar";
95
+ integer: "integer";
96
+ decimal: "decimal";
97
+ float: "float";
98
+ date: "date";
99
+ time: "time";
100
+ timestamp: "timestamp";
101
+ timestamptz: "timestamptz";
102
+ interval: "interval";
103
+ uuid: "uuid";
104
+ json: "json";
105
+ binary: "binary";
106
+ enum: "enum";
107
+ geometry: "geometry";
108
+ inet: "inet";
109
+ unknown: "unknown";
110
+ }>;
111
+ to: z.ZodEnum<{
112
+ bigint: "bigint";
113
+ boolean: "boolean";
114
+ text: "text";
115
+ varchar: "varchar";
116
+ integer: "integer";
117
+ decimal: "decimal";
118
+ float: "float";
119
+ date: "date";
120
+ time: "time";
121
+ timestamp: "timestamp";
122
+ timestamptz: "timestamptz";
123
+ interval: "interval";
124
+ uuid: "uuid";
125
+ json: "json";
126
+ binary: "binary";
127
+ enum: "enum";
128
+ geometry: "geometry";
129
+ inet: "inet";
130
+ unknown: "unknown";
131
+ }>;
132
+ }, z.core.$strict>>;
133
+ nullabilityChanged: z.ZodArray<z.ZodObject<{
134
+ column: z.ZodString;
135
+ nowNullable: z.ZodBoolean;
136
+ }, z.core.$strict>>;
137
+ enumValuesChanged: z.ZodArray<z.ZodObject<{
138
+ enumId: z.ZodString;
139
+ added: z.ZodArray<z.ZodString>;
140
+ removed: z.ZodArray<z.ZodString>;
141
+ }, z.core.$strict>>;
142
+ pkChanged: z.ZodBoolean;
143
+ renamedColumns: z.ZodArray<z.ZodObject<{
144
+ from: z.ZodString;
145
+ to: z.ZodString;
146
+ confidence: z.ZodNumber;
147
+ }, z.core.$strict>>;
148
+ }, z.core.$strict>>;
149
+ relationChanges: z.ZodObject<{
150
+ added: z.ZodArray<z.ZodObject<{
151
+ id: z.ZodString;
152
+ kind: z.ZodEnum<{
153
+ override: "override";
154
+ "declared-fk": "declared-fk";
155
+ "inferred-name": "inferred-name";
156
+ "inferred-join-table": "inferred-join-table";
157
+ manifest: "manifest";
158
+ }>;
159
+ cardinality: z.ZodEnum<{
160
+ "one-to-one": "one-to-one";
161
+ "one-to-many": "one-to-many";
162
+ "many-to-many": "many-to-many";
163
+ }>;
164
+ from: z.ZodObject<{
165
+ tableId: z.ZodString;
166
+ columns: z.ZodArray<z.ZodString>;
167
+ }, z.core.$strict>;
168
+ to: z.ZodObject<{
169
+ tableId: z.ZodString;
170
+ columns: z.ZodArray<z.ZodString>;
171
+ }, z.core.$strict>;
172
+ through: z.ZodDefault<z.ZodNullable<z.ZodObject<{
173
+ tableId: z.ZodString;
174
+ fromColumns: z.ZodArray<z.ZodString>;
175
+ toColumns: z.ZodArray<z.ZodString>;
176
+ }, z.core.$strict>>>;
177
+ onDelete: z.ZodDefault<z.ZodNullable<z.ZodEnum<{
178
+ cascade: "cascade";
179
+ restrict: "restrict";
180
+ "set-null": "set-null";
181
+ "set-default": "set-default";
182
+ "no-action": "no-action";
183
+ }>>>;
184
+ onUpdate: z.ZodDefault<z.ZodNullable<z.ZodEnum<{
185
+ cascade: "cascade";
186
+ restrict: "restrict";
187
+ "set-null": "set-null";
188
+ "set-default": "set-default";
189
+ "no-action": "no-action";
190
+ }>>>;
191
+ selfReferential: z.ZodDefault<z.ZodBoolean>;
192
+ confidence: z.ZodDefault<z.ZodNumber>;
193
+ }, z.core.$strict>>;
194
+ removed: z.ZodArray<z.ZodObject<{
195
+ id: z.ZodString;
196
+ kind: z.ZodEnum<{
197
+ override: "override";
198
+ "declared-fk": "declared-fk";
199
+ "inferred-name": "inferred-name";
200
+ "inferred-join-table": "inferred-join-table";
201
+ manifest: "manifest";
202
+ }>;
203
+ cardinality: z.ZodEnum<{
204
+ "one-to-one": "one-to-one";
205
+ "one-to-many": "one-to-many";
206
+ "many-to-many": "many-to-many";
207
+ }>;
208
+ from: z.ZodObject<{
209
+ tableId: z.ZodString;
210
+ columns: z.ZodArray<z.ZodString>;
211
+ }, z.core.$strict>;
212
+ to: z.ZodObject<{
213
+ tableId: z.ZodString;
214
+ columns: z.ZodArray<z.ZodString>;
215
+ }, z.core.$strict>;
216
+ through: z.ZodDefault<z.ZodNullable<z.ZodObject<{
217
+ tableId: z.ZodString;
218
+ fromColumns: z.ZodArray<z.ZodString>;
219
+ toColumns: z.ZodArray<z.ZodString>;
220
+ }, z.core.$strict>>>;
221
+ onDelete: z.ZodDefault<z.ZodNullable<z.ZodEnum<{
222
+ cascade: "cascade";
223
+ restrict: "restrict";
224
+ "set-null": "set-null";
225
+ "set-default": "set-default";
226
+ "no-action": "no-action";
227
+ }>>>;
228
+ onUpdate: z.ZodDefault<z.ZodNullable<z.ZodEnum<{
229
+ cascade: "cascade";
230
+ restrict: "restrict";
231
+ "set-null": "set-null";
232
+ "set-default": "set-default";
233
+ "no-action": "no-action";
234
+ }>>>;
235
+ selfReferential: z.ZodDefault<z.ZodBoolean>;
236
+ confidence: z.ZodDefault<z.ZodNumber>;
237
+ }, z.core.$strict>>;
238
+ }, z.core.$strict>;
239
+ breaking: z.ZodBoolean;
240
+ }, z.core.$strict>;
241
+ export type SchemaDiff = z.infer<typeof schemaDiffSchema>;
242
+ export interface DiffOptions {
243
+ /**
244
+ * Is this target path (`schema.table` or `schema.table.column`) referenced
245
+ * by current pages/overrides? `breaking` is only set for referenced
246
+ * removals/type changes. Callers without page knowledge (the engine in
247
+ * isolation) get the conservative default: everything is referenced.
248
+ */
249
+ isReferenced?: (targetPath: string) => boolean;
250
+ }
251
+ /**
252
+ * §9 `diffModels(a, b)`: what changed going FROM `a` TO `b`. Pure; stable
253
+ * ordering throughout; empty diff = `{ addedTables: [], … breaking: false }`.
254
+ */
255
+ export declare function diffModels(a: DatabaseModel, b: DatabaseModel, options?: DiffOptions): SchemaDiff;
256
+ /** True when the diff carries no change at all. */
257
+ export declare function isEmptyDiff(diff: SchemaDiff): boolean;
258
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/snapshot/diff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAGL,KAAK,aAAa,EAInB,MAAM,oBAAoB,CAAC;AAM5B,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAe7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAU3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;CAChD;AAiFD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,CAAC,EAAE,aAAa,EAChB,CAAC,EAAE,aAAa,EAChB,OAAO,GAAE,WAAgB,GACxB,UAAU,CA2CZ;AAED,mDAAmD;AACnD,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAQrD"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Snapshot diff engine — 05-introspection-engine.md §9 `diffModels`.
3
+ *
4
+ * Pure structural diff between two `DatabaseModel`s. All output arrays are
5
+ * sorted (tables/relations by id, columns by name) so the diff itself is
6
+ * canonical and safe to hash/golden-test.
7
+ *
8
+ * NOT IMPLEMENTED HERE (deliberately, per 05 §9): rename detection.
9
+ * `rename-detect.ts` is a separate M3-follow-up — a removed+added column
10
+ * pair with identical logicalType+nullability and (same ordinal or
11
+ * Levenshtein ≤ 2 or snake/camel variant) becomes a `renamedColumns`
12
+ * suggestion at confidence 0.7, never auto-applied. Until it lands,
13
+ * `renamedColumns` is always `[]` and a drop+add reads as exactly that.
14
+ */
15
+ import { z } from 'zod';
16
+ import { logicalTypeSchema, relationSchema, } from '../schema-model.js';
17
+ // ---------------------------------------------------------------------------
18
+ // Diff shape (§9) — Zod schema so the §11 diff route can validate responses
19
+ // ---------------------------------------------------------------------------
20
+ export const changedTableSchema = z.strictObject({
21
+ addedColumns: z.array(z.string()),
22
+ removedColumns: z.array(z.string()),
23
+ typeChanged: z.array(z.strictObject({ column: z.string(), from: logicalTypeSchema, to: logicalTypeSchema })),
24
+ nullabilityChanged: z.array(z.strictObject({ column: z.string(), nowNullable: z.boolean() })),
25
+ enumValuesChanged: z.array(z.strictObject({ enumId: z.string(), added: z.array(z.string()), removed: z.array(z.string()) })),
26
+ pkChanged: z.boolean(),
27
+ /** Suggestions only; always [] until rename-detect.ts lands (see header). */
28
+ renamedColumns: z.array(z.strictObject({ from: z.string(), to: z.string(), confidence: z.number().min(0).max(1) })),
29
+ });
30
+ export const schemaDiffSchema = z.strictObject({
31
+ addedTables: z.array(z.string()),
32
+ removedTables: z.array(z.string()),
33
+ changedTables: z.record(z.string(), changedTableSchema),
34
+ relationChanges: z.strictObject({
35
+ added: z.array(relationSchema),
36
+ removed: z.array(relationSchema),
37
+ }),
38
+ /** Any removal/typeChange referenced by current pages/overrides (§9). */
39
+ breaking: z.boolean(),
40
+ });
41
+ // ---------------------------------------------------------------------------
42
+ // Implementation
43
+ // ---------------------------------------------------------------------------
44
+ const sortStrings = (values) => [...values].sort((a, b) => a.localeCompare(b));
45
+ const byId = (a, b) => a.id.localeCompare(b.id);
46
+ function diffTable(a, b, modelA, modelB) {
47
+ const aColumns = new Map(a.columns.map((c) => [c.name, c]));
48
+ const bColumns = new Map(b.columns.map((c) => [c.name, c]));
49
+ const addedColumns = sortStrings([...bColumns.keys()].filter((name) => !aColumns.has(name)));
50
+ const removedColumns = sortStrings([...aColumns.keys()].filter((name) => !bColumns.has(name)));
51
+ const typeChanged = [];
52
+ const nullabilityChanged = [];
53
+ const enumValuesChanged = [];
54
+ const enumsA = new Map(modelA.enums.map((e) => [e.id, e]));
55
+ const enumsB = new Map(modelB.enums.map((e) => [e.id, e]));
56
+ const seenEnumIds = new Set();
57
+ for (const name of sortStrings([...aColumns.keys()].filter((n) => bColumns.has(n)))) {
58
+ const before = aColumns.get(name);
59
+ const after = bColumns.get(name);
60
+ if (before.logicalType !== after.logicalType) {
61
+ typeChanged.push({ column: name, from: before.logicalType, to: after.logicalType });
62
+ }
63
+ if (before.nullable !== after.nullable) {
64
+ nullabilityChanged.push({ column: name, nowNullable: after.nullable });
65
+ }
66
+ // Enum drift is reported on the table(s) whose columns reference the enum.
67
+ const enumId = after.enumRef ?? before.enumRef;
68
+ if (enumId !== null && !seenEnumIds.has(enumId)) {
69
+ seenEnumIds.add(enumId);
70
+ const valuesA = enumsA.get(before.enumRef ?? enumId)?.values ?? [];
71
+ const valuesB = enumsB.get(after.enumRef ?? enumId)?.values ?? [];
72
+ const setA = new Set(valuesA);
73
+ const setB = new Set(valuesB);
74
+ const added = sortStrings(valuesB.filter((v) => !setA.has(v)));
75
+ const removed = sortStrings(valuesA.filter((v) => !setB.has(v)));
76
+ if (added.length > 0 || removed.length > 0) {
77
+ enumValuesChanged.push({ enumId, added, removed });
78
+ }
79
+ }
80
+ }
81
+ enumValuesChanged.sort((x, y) => x.enumId.localeCompare(y.enumId));
82
+ const pkChanged = a.primaryKey.length !== b.primaryKey.length ||
83
+ a.primaryKey.some((column, i) => column !== b.primaryKey[i]);
84
+ if (addedColumns.length === 0 &&
85
+ removedColumns.length === 0 &&
86
+ typeChanged.length === 0 &&
87
+ nullabilityChanged.length === 0 &&
88
+ enumValuesChanged.length === 0 &&
89
+ !pkChanged) {
90
+ return null;
91
+ }
92
+ return {
93
+ addedColumns,
94
+ removedColumns,
95
+ typeChanged,
96
+ nullabilityChanged,
97
+ enumValuesChanged,
98
+ pkChanged,
99
+ renamedColumns: [], // rename detection deferred — see module header
100
+ };
101
+ }
102
+ /**
103
+ * §9 `diffModels(a, b)`: what changed going FROM `a` TO `b`. Pure; stable
104
+ * ordering throughout; empty diff = `{ addedTables: [], … breaking: false }`.
105
+ */
106
+ export function diffModels(a, b, options = {}) {
107
+ const isReferenced = options.isReferenced ?? (() => true);
108
+ const tablesA = new Map(a.tables.map((t) => [t.id, t]));
109
+ const tablesB = new Map(b.tables.map((t) => [t.id, t]));
110
+ const addedTables = sortStrings([...tablesB.keys()].filter((id) => !tablesA.has(id)));
111
+ const removedTables = sortStrings([...tablesA.keys()].filter((id) => !tablesB.has(id)));
112
+ const changedTables = {};
113
+ for (const id of sortStrings([...tablesA.keys()].filter((tid) => tablesB.has(tid)))) {
114
+ const changed = diffTable(tablesA.get(id), tablesB.get(id), a, b);
115
+ if (changed !== null)
116
+ changedTables[id] = changed;
117
+ }
118
+ const relationsA = new Map(a.relations.map((r) => [r.id, r]));
119
+ const relationsB = new Map(b.relations.map((r) => [r.id, r]));
120
+ const added = [...relationsB.values()].filter((r) => !relationsA.has(r.id)).sort(byId);
121
+ const removed = [...relationsA.values()]
122
+ .filter((r) => !relationsB.has(r.id))
123
+ .sort(byId);
124
+ let breaking = removedTables.some((id) => isReferenced(id));
125
+ if (!breaking) {
126
+ for (const [id, changed] of Object.entries(changedTables)) {
127
+ if (changed.removedColumns.some((column) => isReferenced(`${id}.${column}`)) ||
128
+ changed.typeChanged.some((change) => isReferenced(`${id}.${change.column}`)) ||
129
+ (changed.pkChanged && isReferenced(id))) {
130
+ breaking = true;
131
+ break;
132
+ }
133
+ }
134
+ }
135
+ return {
136
+ addedTables,
137
+ removedTables,
138
+ changedTables,
139
+ relationChanges: { added, removed },
140
+ breaking,
141
+ };
142
+ }
143
+ /** True when the diff carries no change at all. */
144
+ export function isEmptyDiff(diff) {
145
+ return (diff.addedTables.length === 0 &&
146
+ diff.removedTables.length === 0 &&
147
+ Object.keys(diff.changedTables).length === 0 &&
148
+ diff.relationChanges.added.length === 0 &&
149
+ diff.relationChanges.removed.length === 0);
150
+ }
151
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/snapshot/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Canonical model JSON + sha256 — 05-introspection-engine.md §9.
3
+ *
4
+ * `model_hash` in `adminium_schema_snapshots` is "sha256 over canonical
5
+ * JSON: sorted keys, stripped volatile fields
6
+ * introspectedAt/stats/warnings/rowCountEstimate/activity/sizeBytes".
7
+ * Identical hash to the latest snapshot → the meta-store writes no new row.
8
+ *
9
+ * Beyond the doc's letter we also sort the model's collection arrays by
10
+ * their stable keys (tables/relations/enums by id, columns/indexes by name)
11
+ * so two introspections that enumerate the catalog in a different order
12
+ * still hash identically.
13
+ */
14
+ import type { DatabaseModel } from '../schema-model.js';
15
+ /**
16
+ * The canonical (hash-input) JSON string for a model: volatile fields
17
+ * stripped, keys sorted, collections sorted by stable key. Pure — the input
18
+ * model is never mutated (operates on a deep clone).
19
+ */
20
+ export declare function canonicalModelJson(model: DatabaseModel): string;
21
+ /** sha256 hex digest of {@link canonicalModelJson}. */
22
+ export declare function hashModel(model: DatabaseModel): string;
23
+ //# sourceMappingURL=hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/snapshot/hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAwBxD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAgB/D;AAED,uDAAuD;AACvD,wBAAgB,SAAS,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAEtD"}
@@ -0,0 +1,47 @@
1
+ import { sha256Hex } from './sha256.js';
2
+ /** Top-level fields stripped before hashing (§9). */
3
+ const VOLATILE_MODEL_FIELDS = ['introspectedAt', 'stats', 'warnings'];
4
+ /** Per-table fields stripped before hashing (§9). */
5
+ const VOLATILE_TABLE_FIELDS = ['rowCountEstimate', 'activity', 'sizeBytes'];
6
+ function sortKeysDeep(value) {
7
+ if (Array.isArray(value))
8
+ return value.map(sortKeysDeep);
9
+ if (value !== null && typeof value === 'object') {
10
+ const record = value;
11
+ const sorted = {};
12
+ for (const key of Object.keys(record).sort()) {
13
+ sorted[key] = sortKeysDeep(record[key]);
14
+ }
15
+ return sorted;
16
+ }
17
+ return value;
18
+ }
19
+ const byId = (a, b) => a.id.localeCompare(b.id);
20
+ const byName = (a, b) => a.name.localeCompare(b.name);
21
+ /**
22
+ * The canonical (hash-input) JSON string for a model: volatile fields
23
+ * stripped, keys sorted, collections sorted by stable key. Pure — the input
24
+ * model is never mutated (operates on a deep clone).
25
+ */
26
+ export function canonicalModelJson(model) {
27
+ const plain = JSON.parse(JSON.stringify(model));
28
+ for (const field of VOLATILE_MODEL_FIELDS)
29
+ delete plain[field];
30
+ const tables = (plain['tables'] ?? []);
31
+ for (const table of tables) {
32
+ for (const field of VOLATILE_TABLE_FIELDS)
33
+ delete table[field];
34
+ table['columns']?.sort(byName);
35
+ table['indexes']?.sort(byName);
36
+ }
37
+ tables.sort(byId);
38
+ plain['relations']?.sort(byId);
39
+ plain['enums']?.sort(byId);
40
+ plain['schemas']?.sort();
41
+ return JSON.stringify(sortKeysDeep(plain));
42
+ }
43
+ /** sha256 hex digest of {@link canonicalModelJson}. */
44
+ export function hashModel(model) {
45
+ return sha256Hex(canonicalModelJson(model));
46
+ }
47
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/snapshot/hash.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,qDAAqD;AACrD,MAAM,qBAAqB,GAAG,CAAC,gBAAgB,EAAE,OAAO,EAAE,UAAU,CAAU,CAAC;AAC/E,qDAAqD;AACrD,MAAM,qBAAqB,GAAG,CAAC,kBAAkB,EAAE,UAAU,EAAE,WAAW,CAAU,CAAC;AAErF,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACzD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,MAAM,GAAG,KAAgC,CAAC;QAChD,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YAC7C,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,IAAI,GAAG,CAAC,CAAiB,EAAE,CAAiB,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAChF,MAAM,MAAM,GAAG,CAAC,CAAmB,EAAE,CAAmB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAE1F;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAoB;IACrD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAA4B,CAAC;IAC3E,KAAK,MAAM,KAAK,IAAI,qBAAqB;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC;IAE/D,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAiD,CAAC;IACvF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,qBAAqB;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC;QAC9D,KAAK,CAAC,SAAS,CAAoC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAClE,KAAK,CAAC,SAAS,CAAoC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,KAAK,CAAC,WAAW,CAAkC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAChE,KAAK,CAAC,OAAO,CAAkC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5D,KAAK,CAAC,SAAS,CAA0B,EAAE,IAAI,EAAE,CAAC;IAEnD,OAAO,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,SAAS,CAAC,KAAoB;IAC5C,OAAO,SAAS,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Snapshot module — 05-introspection-engine.md §9 (M3-T02 diff half /
3
+ * 05-T09 engine side).
4
+ *
5
+ * The engine computes checksums and diffs; PERSISTENCE is owned by
6
+ * `@adminium/meta` (`adminium_schema_snapshots` — 07-meta-store.md). The
7
+ * store's dedupe rule uses `Snapshot.checksum`: identical checksum to the
8
+ * latest snapshot → no new row, just touch `checked_at`.
9
+ */
10
+ import type { DatabaseModel } from '../schema-model.js';
11
+ import { type DiffOptions, type SchemaDiff } from './diff.js';
12
+ export { canonicalModelJson, hashModel } from './hash.js';
13
+ export { sha256Hex } from './sha256.js';
14
+ export { changedTableSchema, diffModels, isEmptyDiff, schemaDiffSchema, type ChangedTable, type DiffOptions, type SchemaDiff, } from './diff.js';
15
+ /** An in-memory snapshot: the model plus its canonical sha256 checksum. */
16
+ export interface Snapshot {
17
+ /** sha256 hex over canonical JSON (volatile fields stripped — hash.ts). */
18
+ checksum: string;
19
+ model: DatabaseModel;
20
+ }
21
+ /** Build a snapshot for a model. Pure; the model is not copied or mutated. */
22
+ export declare function snapshotFromModel(model: DatabaseModel): Snapshot;
23
+ /**
24
+ * Diff two snapshots (FROM `a` TO `b`). Equal checksums short-circuit to an
25
+ * empty diff without walking the models.
26
+ */
27
+ export declare function diffSnapshots(a: Snapshot, b: Snapshot, options?: DiffOptions): SchemaDiff;
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/snapshot/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAc,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AAE1E,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AAEnB,2EAA2E;AAC3E,MAAM,WAAW,QAAQ;IACvB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,QAAQ,CAEhE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,UAAU,CAWzF"}
@@ -0,0 +1,26 @@
1
+ import { hashModel } from './hash.js';
2
+ import { diffModels } from './diff.js';
3
+ export { canonicalModelJson, hashModel } from './hash.js';
4
+ export { sha256Hex } from './sha256.js';
5
+ export { changedTableSchema, diffModels, isEmptyDiff, schemaDiffSchema, } from './diff.js';
6
+ /** Build a snapshot for a model. Pure; the model is not copied or mutated. */
7
+ export function snapshotFromModel(model) {
8
+ return { checksum: hashModel(model), model };
9
+ }
10
+ /**
11
+ * Diff two snapshots (FROM `a` TO `b`). Equal checksums short-circuit to an
12
+ * empty diff without walking the models.
13
+ */
14
+ export function diffSnapshots(a, b, options) {
15
+ if (a.checksum === b.checksum) {
16
+ return {
17
+ addedTables: [],
18
+ removedTables: [],
19
+ changedTables: {},
20
+ relationChanges: { added: [], removed: [] },
21
+ breaking: false,
22
+ };
23
+ }
24
+ return diffModels(a.model, b.model, options);
25
+ }
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/snapshot/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,UAAU,EAAqC,MAAM,WAAW,CAAC;AAE1E,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,WAAW,EACX,gBAAgB,GAIjB,MAAM,WAAW,CAAC;AASnB,8EAA8E;AAC9E,MAAM,UAAU,iBAAiB,CAAC,KAAoB;IACpD,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,CAAW,EAAE,CAAW,EAAE,OAAqB;IAC3E,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC9B,OAAO;YACL,WAAW,EAAE,EAAE;YACf,aAAa,EAAE,EAAE;YACjB,aAAa,EAAE,EAAE;YACjB,eAAe,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;YAC3C,QAAQ,EAAE,KAAK;SAChB,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Minimal, dependency-free SHA-256 (FIPS 180-4) over a UTF-8 string.
3
+ *
4
+ * Why not `node:crypto`: `@adminium/engine` src is environment-agnostic —
5
+ * it declares no node types and its subpaths may be bundled client-side
6
+ * (01-architecture.md §2.3.2). Web Crypto's `subtle.digest` is async, which
7
+ * would poison the pure/sync snapshot API for no benefit at these input
8
+ * sizes (a 500-table model is < 10 MB; SHA-256 of that is milliseconds).
9
+ *
10
+ * Verified against the FIPS test vectors in test/snapshot.test.ts.
11
+ */
12
+ /** SHA-256 hex digest of the UTF-8 encoding of `input`. */
13
+ export declare function sha256Hex(input: string): string;
14
+ //# sourceMappingURL=sha256.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sha256.d.ts","sourceRoot":"","sources":["../../src/snapshot/sha256.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAyCH,2DAA2D;AAC3D,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA0D/C"}