@atscript/db 0.1.136 → 0.1.137
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/agg.d.cts +1 -1
- package/dist/agg.d.mts +1 -1
- package/dist/{buckets-y4EzoX7D.d.mts → buckets-BFG2RYRW.d.mts} +603 -15
- package/dist/{buckets-kKY0RKYN.d.cts → buckets-C-27xmtq.d.cts} +603 -15
- package/dist/column-diff-BmqvgBWw.d.cts +24 -0
- package/dist/{db-view-BrAtbaOx.mjs → column-diff-BwOA5101.mjs} +423 -46
- package/dist/{db-view-BAzhEUxB.cjs → column-diff-CgxgFKzx.cjs} +494 -45
- package/dist/column-diff-DPkbZIVE.d.mts +24 -0
- package/dist/index.cjs +64 -54
- package/dist/index.d.cts +4 -4
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +18 -9
- package/dist/plugin.cjs +2 -2
- package/dist/plugin.mjs +2 -2
- package/dist/rel.d.cts +2 -2
- package/dist/rel.d.mts +2 -2
- package/dist/{relation-helpers-BVjQflL_.d.cts → relation-helpers-D3Zu0Mta.d.mts} +1 -1
- package/dist/{relation-helpers-D4haLo1n.d.mts → relation-helpers-DxrvS6ar.d.cts} +1 -1
- package/dist/sync.cjs +33 -343
- package/dist/sync.d.cts +7 -15
- package/dist/sync.d.mts +7 -15
- package/dist/sync.mjs +1 -311
- package/package.json +1 -1
- package/dist/db-space-BX4K3vb6.d.mts +0 -558
- package/dist/db-space-ClG41vJh.d.cts +0 -558
package/dist/sync.cjs
CHANGED
|
@@ -1,315 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
//#region src/schema/schema-hash.ts
|
|
4
|
-
/** Extracts sorted field snapshots from a readable's field descriptors. */
|
|
5
|
-
function extractFieldSnapshots(fields, typeMapper) {
|
|
6
|
-
return fields.filter((f) => !f.ignored).map((f) => {
|
|
7
|
-
const snap = {
|
|
8
|
-
physicalName: f.physicalName,
|
|
9
|
-
designType: f.designType,
|
|
10
|
-
optional: f.optional,
|
|
11
|
-
isPrimaryKey: f.isPrimaryKey,
|
|
12
|
-
storage: f.storage
|
|
13
|
-
};
|
|
14
|
-
if (f.defaultValue) snap.defaultValue = f.defaultValue;
|
|
15
|
-
if (typeMapper) snap.mappedType = typeMapper(f);
|
|
16
|
-
if (f.encrypted) snap.encrypted = true;
|
|
17
|
-
return snap;
|
|
18
|
-
}).toSorted((a, b) => a.physicalName.localeCompare(b.physicalName));
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Extracts a canonical, serializable snapshot from a readable's metadata.
|
|
22
|
-
* Sorted deterministically so the hash is stable across runs.
|
|
23
|
-
*
|
|
24
|
-
* @param readable - The table/view readable.
|
|
25
|
-
* @param typeMapper - Optional adapter-specific type mapper. When provided,
|
|
26
|
-
* each field's mapped type (e.g., "VARCHAR(255)") is stored in the snapshot
|
|
27
|
-
* for precise type change detection.
|
|
28
|
-
*/
|
|
29
|
-
function computeTableSnapshot(readable, typeMapper, tableOptions) {
|
|
30
|
-
const fields = extractFieldSnapshots(readable.fieldDescriptors, typeMapper);
|
|
31
|
-
const indexes = [...readable.indexes.values()].map((idx) => ({
|
|
32
|
-
key: idx.key,
|
|
33
|
-
type: idx.type,
|
|
34
|
-
fields: idx.fields.map((f) => ({
|
|
35
|
-
name: f.name,
|
|
36
|
-
sort: f.sort
|
|
37
|
-
}))
|
|
38
|
-
})).toSorted((a, b) => a.key.localeCompare(b.key));
|
|
39
|
-
const foreignKeys = [...readable.foreignKeys.values()].map((fk) => ({
|
|
40
|
-
fields: [...fk.fields].toSorted(),
|
|
41
|
-
targetTable: fk.targetTable,
|
|
42
|
-
targetFields: [...fk.targetFields].toSorted(),
|
|
43
|
-
onDelete: fk.onDelete,
|
|
44
|
-
onUpdate: fk.onUpdate
|
|
45
|
-
})).toSorted((a, b) => a.fields.join(",").localeCompare(b.fields.join(",")));
|
|
46
|
-
const snapshot = {
|
|
47
|
-
tableName: readable.tableName,
|
|
48
|
-
fields,
|
|
49
|
-
indexes,
|
|
50
|
-
foreignKeys
|
|
51
|
-
};
|
|
52
|
-
if (tableOptions?.length) snapshot.tableOptions = [...tableOptions].toSorted((a, b) => a.key.localeCompare(b.key));
|
|
53
|
-
return snapshot;
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Extracts a canonical, serializable snapshot from a view's metadata.
|
|
57
|
-
* Captures view plan (entry table, joins, filter, materialization) for
|
|
58
|
-
* detecting view definition changes.
|
|
59
|
-
*/
|
|
60
|
-
function computeViewSnapshot(view) {
|
|
61
|
-
const fields = extractFieldSnapshots(view.fieldDescriptors);
|
|
62
|
-
if (view.isExternal) return {
|
|
63
|
-
tableName: view.tableName,
|
|
64
|
-
viewType: "E",
|
|
65
|
-
fields
|
|
66
|
-
};
|
|
67
|
-
const plan = view.viewPlan;
|
|
68
|
-
const qualify = (ref) => view.resolveFieldRef(ref, (n) => n);
|
|
69
|
-
const canonical = (node) => JSON.stringify(canonicalizeQueryNode(node, qualify));
|
|
70
|
-
const columns = view.getViewColumnMappings().map((m) => {
|
|
71
|
-
const col = {
|
|
72
|
-
column: m.viewColumn,
|
|
73
|
-
sourceTable: m.sourceTable,
|
|
74
|
-
sourceColumn: m.sourceColumn
|
|
75
|
-
};
|
|
76
|
-
if (m.json) {
|
|
77
|
-
col.jsonPath = JSON.stringify(m.json.path);
|
|
78
|
-
col.jsonType = m.json.type;
|
|
79
|
-
}
|
|
80
|
-
if (m.aggFn) col.aggFn = m.aggFn;
|
|
81
|
-
if (m.aggField) col.aggField = m.aggField;
|
|
82
|
-
if (m.aggFilter) col.aggFilter = canonical(m.aggFilter);
|
|
83
|
-
return col;
|
|
84
|
-
}).toSorted((a, b) => a.column < b.column ? -1 : a.column > b.column ? 1 : 0);
|
|
85
|
-
const result = {
|
|
86
|
-
tableName: view.tableName,
|
|
87
|
-
viewType: plan.materialized ? "M" : "V",
|
|
88
|
-
entryTable: plan.entryTable,
|
|
89
|
-
joinTables: plan.joins.map((j) => {
|
|
90
|
-
const join = {
|
|
91
|
-
targetTable: j.targetTable,
|
|
92
|
-
condition: canonical(j.condition)
|
|
93
|
-
};
|
|
94
|
-
if (j.kind === "left") join.kind = "left";
|
|
95
|
-
return join;
|
|
96
|
-
}),
|
|
97
|
-
columns
|
|
98
|
-
};
|
|
99
|
-
if (plan.filter) result.filterHash = fnv1a(canonical(plan.filter));
|
|
100
|
-
if (plan.having) result.havingHash = fnv1a(canonical(plan.having));
|
|
101
|
-
if (plan.materialized) result.materialized = true;
|
|
102
|
-
return {
|
|
103
|
-
...result,
|
|
104
|
-
fields
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Converts a view predicate (join condition, `@db.view.filter`,
|
|
109
|
-
* `@db.view.having`) into a serializable structure whose JSON is a stable
|
|
110
|
-
* function of its MEANING: field refs become `qualify(ref)` — the view's
|
|
111
|
-
* `resolveFieldRef(ref, (n) => n)`, i.e. `"<table>.<field>"`, so a predicate
|
|
112
|
-
* retargeted to another table with the same field name changes — operators
|
|
113
|
-
* and literal values are kept as-is, `$and`/`$or` keep declaration order, and
|
|
114
|
-
* no function references survive. Two identical models produce byte-identical
|
|
115
|
-
* JSON.
|
|
116
|
-
* @since 0.1.128
|
|
117
|
-
*/
|
|
118
|
-
function canonicalizeQueryNode(node, qualify) {
|
|
119
|
-
if ("$and" in node) return { and: node.$and.map((n) => canonicalizeQueryNode(n, qualify)) };
|
|
120
|
-
if ("$or" in node) return { or: node.$or.map((n) => canonicalizeQueryNode(n, qualify)) };
|
|
121
|
-
if ("$not" in node) return { not: canonicalizeQueryNode(node.$not, qualify) };
|
|
122
|
-
const comp = node;
|
|
123
|
-
const out = {
|
|
124
|
-
l: qualify(comp.left),
|
|
125
|
-
op: comp.op
|
|
126
|
-
};
|
|
127
|
-
if (comp.right !== void 0) out.r = comp.right !== null && typeof comp.right === "object" && "field" in comp.right ? { f: qualify(comp.right) } : comp.right;
|
|
128
|
-
return out;
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Computes a deterministic hash string from multiple table snapshots.
|
|
132
|
-
* Uses FNV-1a for speed — not cryptographic, just needs stability + collision resistance.
|
|
133
|
-
*/
|
|
134
|
-
function computeSchemaHash(snapshots) {
|
|
135
|
-
const sorted = [...snapshots].toSorted((a, b) => a.tableName.localeCompare(b.tableName));
|
|
136
|
-
return fnv1a(JSON.stringify(sorted));
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Computes a hash for a single table/view snapshot.
|
|
140
|
-
* Used for per-table change detection via stored snapshots.
|
|
141
|
-
*/
|
|
142
|
-
function computeTableHash(snapshot) {
|
|
143
|
-
return fnv1a(JSON.stringify(snapshot));
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* Converts stored snapshot fields to `TExistingColumn[]` format
|
|
147
|
-
* for use with `computeColumnDiff`. Used by adapters that lack
|
|
148
|
-
* native column introspection (e.g., MongoDB).
|
|
149
|
-
*
|
|
150
|
-
* The `type` field uses `mappedType` when available (adapter-specific),
|
|
151
|
-
* falling back to `designType`.
|
|
152
|
-
*/
|
|
153
|
-
function snapshotToExistingColumns(snapshot) {
|
|
154
|
-
return snapshot.fields.map((f) => ({
|
|
155
|
-
name: f.physicalName,
|
|
156
|
-
type: f.mappedType ?? f.designType,
|
|
157
|
-
notnull: !f.optional,
|
|
158
|
-
pk: f.isPrimaryKey,
|
|
159
|
-
dflt_value: serializeDefaultValue(f.defaultValue)
|
|
160
|
-
}));
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Extracts table options from a stored snapshot for diff comparison.
|
|
164
|
-
* Used as fallback when an adapter lacks native table option introspection.
|
|
165
|
-
*/
|
|
166
|
-
function snapshotToExistingTableOptions(snapshot) {
|
|
167
|
-
return snapshot.tableOptions ?? [];
|
|
168
|
-
}
|
|
169
|
-
/** Serializes a TDbDefaultValue to a comparable string. */
|
|
170
|
-
function serializeDefaultValue(dv) {
|
|
171
|
-
if (!dv) return;
|
|
172
|
-
if (dv.kind === "value") return dv.value;
|
|
173
|
-
return `fn:${dv.fn}`;
|
|
174
|
-
}
|
|
175
|
-
/** FNV-1a 32-bit hash → hex string */
|
|
176
|
-
function fnv1a(str) {
|
|
177
|
-
let hash = 2166136261;
|
|
178
|
-
for (let i = 0; i < str.length; i++) {
|
|
179
|
-
hash ^= str.codePointAt(i);
|
|
180
|
-
hash = Math.imul(hash, 16777619);
|
|
181
|
-
}
|
|
182
|
-
return Math.trunc(hash).toString(16).padStart(8, "0");
|
|
183
|
-
}
|
|
184
|
-
//#endregion
|
|
185
|
-
//#region src/schema/fk-diff.ts
|
|
186
|
-
/** Canonical key for an FK: sorted local field names, comma-joined. */
|
|
187
|
-
function fkKey(fields) {
|
|
188
|
-
return [...fields].toSorted().join(",");
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Compares desired FK constraints against stored snapshot to detect
|
|
192
|
-
* additions, removals, and property changes (target table, target fields,
|
|
193
|
-
* onDelete, onUpdate).
|
|
194
|
-
*/
|
|
195
|
-
function computeForeignKeyDiff(desired, existingSnapshot) {
|
|
196
|
-
const added = [];
|
|
197
|
-
const removed = [];
|
|
198
|
-
const changed = [];
|
|
199
|
-
const existingByKey = /* @__PURE__ */ new Map();
|
|
200
|
-
for (const fk of existingSnapshot) existingByKey.set(fkKey(fk.fields), fk);
|
|
201
|
-
const desiredKeys = /* @__PURE__ */ new Set();
|
|
202
|
-
for (const fk of desired.values()) {
|
|
203
|
-
const key = fkKey(fk.fields);
|
|
204
|
-
desiredKeys.add(key);
|
|
205
|
-
const existing = existingByKey.get(key);
|
|
206
|
-
if (!existing) added.push(fk);
|
|
207
|
-
else if (fkPropertiesDiffer(fk, existing)) changed.push({
|
|
208
|
-
desired: fk,
|
|
209
|
-
existing
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
for (const [key, fk] of existingByKey) if (!desiredKeys.has(key)) removed.push(fk);
|
|
213
|
-
return {
|
|
214
|
-
added,
|
|
215
|
-
removed,
|
|
216
|
-
changed
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
/** Whether the FK diff contains any changes. */
|
|
220
|
-
function hasForeignKeyChanges(diff) {
|
|
221
|
-
return diff.added.length > 0 || diff.removed.length > 0 || diff.changed.length > 0;
|
|
222
|
-
}
|
|
223
|
-
function fkPropertiesDiffer(desired, existing) {
|
|
224
|
-
if (desired.targetTable !== existing.targetTable) return true;
|
|
225
|
-
if (fkKey(desired.targetFields) !== fkKey(existing.targetFields)) return true;
|
|
226
|
-
if ((desired.onDelete ?? void 0) !== (existing.onDelete ?? void 0)) return true;
|
|
227
|
-
if ((desired.onUpdate ?? void 0) !== (existing.onUpdate ?? void 0)) return true;
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
230
|
-
//#endregion
|
|
231
|
-
//#region src/schema/column-diff.ts
|
|
232
|
-
/**
|
|
233
|
-
* Computes the difference between desired schema fields and existing database columns.
|
|
234
|
-
*
|
|
235
|
-
* @param desired - Field descriptors from the Atscript type (after flattening).
|
|
236
|
-
* @param existing - Columns currently in the database (from introspection).
|
|
237
|
-
* @param typeMapper - Optional function to map field metadata to DB-native type strings.
|
|
238
|
-
* Receives the full field meta (design type, annotations, PK status, etc.)
|
|
239
|
-
* so adapters can produce context-aware types (e.g., `VARCHAR(255)` from maxLength).
|
|
240
|
-
* Required for type change detection.
|
|
241
|
-
*/
|
|
242
|
-
function computeColumnDiff(desired, existing, typeMapper) {
|
|
243
|
-
const existingByName = new Map(existing.map((c) => [c.name, c]));
|
|
244
|
-
const desiredByName = /* @__PURE__ */ new Map();
|
|
245
|
-
const renamedOldNames = /* @__PURE__ */ new Set();
|
|
246
|
-
const added = [];
|
|
247
|
-
const renamed = [];
|
|
248
|
-
const typeChanged = [];
|
|
249
|
-
const nullableChanged = [];
|
|
250
|
-
const defaultChanged = [];
|
|
251
|
-
const conflicts = [];
|
|
252
|
-
for (const field of desired) {
|
|
253
|
-
if (field.ignored) continue;
|
|
254
|
-
desiredByName.set(field.physicalName, field);
|
|
255
|
-
const existingCol = existingByName.get(field.physicalName);
|
|
256
|
-
if (existingCol) if (field.renamedFrom && existingByName.has(field.renamedFrom)) {
|
|
257
|
-
conflicts.push({
|
|
258
|
-
field,
|
|
259
|
-
oldName: field.renamedFrom,
|
|
260
|
-
conflictsWith: field.physicalName
|
|
261
|
-
});
|
|
262
|
-
renamedOldNames.add(field.renamedFrom);
|
|
263
|
-
} else {
|
|
264
|
-
if (typeMapper) {
|
|
265
|
-
if (typeMapper(field).toUpperCase() !== existingCol.type.toUpperCase()) typeChanged.push({
|
|
266
|
-
field,
|
|
267
|
-
existingType: existingCol.type
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
if (!field.isPrimaryKey && !existingCol.pk) {
|
|
271
|
-
const desiredNotNull = !field.optional;
|
|
272
|
-
if (existingCol.notnull !== desiredNotNull) nullableChanged.push({
|
|
273
|
-
field,
|
|
274
|
-
wasNullable: !existingCol.notnull
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
const desiredDefault = serializeDefaultValue(field.defaultValue);
|
|
278
|
-
if (existingCol.dflt_value !== void 0 && existingCol.dflt_value !== desiredDefault) defaultChanged.push({
|
|
279
|
-
field,
|
|
280
|
-
oldDefault: existingCol.dflt_value,
|
|
281
|
-
newDefault: desiredDefault
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
else if (field.renamedFrom && existingByName.has(field.renamedFrom)) {
|
|
285
|
-
renamed.push({
|
|
286
|
-
field,
|
|
287
|
-
oldName: field.renamedFrom
|
|
288
|
-
});
|
|
289
|
-
renamedOldNames.add(field.renamedFrom);
|
|
290
|
-
} else added.push(field);
|
|
291
|
-
}
|
|
292
|
-
const diff = {
|
|
293
|
-
added,
|
|
294
|
-
removed: existing.filter((c) => !desiredByName.has(c.name) && !renamedOldNames.has(c.name)),
|
|
295
|
-
renamed,
|
|
296
|
-
typeChanged,
|
|
297
|
-
nullableChanged,
|
|
298
|
-
defaultChanged,
|
|
299
|
-
conflicts
|
|
300
|
-
};
|
|
301
|
-
if (existing.length > 0) {
|
|
302
|
-
const newNameByOld = new Map(renamed.map((r) => [r.oldName, r.field.physicalName]));
|
|
303
|
-
const from = existing.filter((c) => c.pk).map((c) => newNameByOld.get(c.name) ?? c.name);
|
|
304
|
-
const to = desired.filter((f) => !f.ignored && f.isPrimaryKey).map((f) => f.physicalName);
|
|
305
|
-
if (fkKey(from) !== fkKey(to)) diff.primaryKeyChanged = {
|
|
306
|
-
from,
|
|
307
|
-
to
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
return diff;
|
|
311
|
-
}
|
|
312
|
-
//#endregion
|
|
2
|
+
const require_column_diff = require("./column-diff-CgxgFKzx.cjs");
|
|
313
3
|
//#region src/schema/table-option-diff.ts
|
|
314
4
|
/**
|
|
315
5
|
* Computes the difference between desired and existing table options.
|
|
@@ -839,8 +529,8 @@ var SyncEntry = class SyncEntry {
|
|
|
839
529
|
async function viewDefinitionChanged(view, store) {
|
|
840
530
|
const storedSnapshot = await store.readTableSnapshot(view.tableName, true);
|
|
841
531
|
if (!storedSnapshot) return false;
|
|
842
|
-
const currentHash = computeTableHash(computeViewSnapshot(view));
|
|
843
|
-
return computeTableHash(storedSnapshot) !== currentHash;
|
|
532
|
+
const currentHash = require_column_diff.computeTableHash(require_column_diff.computeViewSnapshot(view));
|
|
533
|
+
return require_column_diff.computeTableHash(storedSnapshot) !== currentHash;
|
|
844
534
|
}
|
|
845
535
|
/**
|
|
846
536
|
* The plan entry of one table as `plan()` reports it (and as `run()` reports
|
|
@@ -935,14 +625,14 @@ async function executeSyncTable(facts, safe, deps, exec) {
|
|
|
935
625
|
init.status = "alter";
|
|
936
626
|
}
|
|
937
627
|
phase = "Column sync";
|
|
938
|
-
const hasFkChanges = fkDiff ? hasForeignKeyChanges(fkDiff) : false;
|
|
628
|
+
const hasFkChanges = fkDiff ? require_column_diff.hasForeignKeyChanges(fkDiff) : false;
|
|
939
629
|
if (adapter.getExistingColumns && adapter.syncColumns) {
|
|
940
630
|
const existing = facts.existing ?? await adapter.getExistingColumns();
|
|
941
631
|
if (existing.length === 0 && !init.renamedFrom) {
|
|
942
632
|
await adapter.ensureTable(ensureOpts);
|
|
943
633
|
init.status = "create";
|
|
944
634
|
} else if (existing.length > 0) {
|
|
945
|
-
const diff = facts.diff ?? computeColumnDiff(readable.fieldDescriptors, existing, adapter.typeMapper?.bind(adapter));
|
|
635
|
+
const diff = facts.diff ?? require_column_diff.computeColumnDiff(readable.fieldDescriptors, existing, adapter.typeMapper?.bind(adapter));
|
|
946
636
|
if (hasFkChanges && !adapter.syncForeignKeys && adapter.recreateTable) {
|
|
947
637
|
await adapter.recreateTable();
|
|
948
638
|
init.recreated = true;
|
|
@@ -953,7 +643,7 @@ async function executeSyncTable(facts, safe, deps, exec) {
|
|
|
953
643
|
};
|
|
954
644
|
} else {
|
|
955
645
|
if (hasFkChanges && fkDiff && adapter.dropForeignKeys) {
|
|
956
|
-
const keysToDrop = [...fkDiff.removed.map((fk) => fkKey(fk.fields)), ...fkDiff.changed.map((fk) => fkKey(fk.desired.fields))];
|
|
646
|
+
const keysToDrop = [...fkDiff.removed.map((fk) => require_column_diff.fkKey(fk.fields)), ...fkDiff.changed.map((fk) => require_column_diff.fkKey(fk.desired.fields))];
|
|
957
647
|
if (keysToDrop.length > 0) {
|
|
958
648
|
await adapter.dropForeignKeys(keysToDrop);
|
|
959
649
|
droppedFks.push(...keysToDrop);
|
|
@@ -967,7 +657,7 @@ async function executeSyncTable(facts, safe, deps, exec) {
|
|
|
967
657
|
const existed = adapter.tableExists ? await adapter.tableExists() : false;
|
|
968
658
|
await adapter.ensureTable(ensureOpts);
|
|
969
659
|
if (!existed) init.status = "create";
|
|
970
|
-
} else await applyColumnDiff(adapter, readable, facts.diff ?? computeColumnDiff(readable.fieldDescriptors, snapshotToExistingColumns(storedSnapshot), deps.resolveTypeMapper(adapter)), init, safe, deps.logger, ensureOpts);
|
|
660
|
+
} else await applyColumnDiff(adapter, readable, facts.diff ?? require_column_diff.computeColumnDiff(readable.fieldDescriptors, require_column_diff.snapshotToExistingColumns(storedSnapshot), deps.resolveTypeMapper(adapter)), init, safe, deps.logger, ensureOpts);
|
|
971
661
|
else {
|
|
972
662
|
const existed = adapter.tableExists ? await adapter.tableExists() : true;
|
|
973
663
|
if (!init.recreated) {
|
|
@@ -1118,7 +808,7 @@ function safeModeSkips(facts) {
|
|
|
1118
808
|
const adapter = readable.dbAdapter;
|
|
1119
809
|
const out = [];
|
|
1120
810
|
if (facts.planEntry.status === "error") return out;
|
|
1121
|
-
if (fkDiff && hasForeignKeyChanges(fkDiff) && !adapter.syncForeignKeys && adapter.recreateTable) return out;
|
|
811
|
+
if (fkDiff && require_column_diff.hasForeignKeyChanges(fkDiff) && !adapter.syncForeignKeys && adapter.recreateTable) return out;
|
|
1122
812
|
const strategy = diff && diff.typeChanged.length > 0 ? typeChangeStrategy(readable, diff) : "modify";
|
|
1123
813
|
if (strategy === "drop") out.push("recreate");
|
|
1124
814
|
else if (strategy === "recreate") return out;
|
|
@@ -1321,7 +1011,7 @@ var SchemaSync = class {
|
|
|
1321
1011
|
logger;
|
|
1322
1012
|
constructor(space, logger) {
|
|
1323
1013
|
this.space = space;
|
|
1324
|
-
this.logger = logger ||
|
|
1014
|
+
this.logger = logger || require_column_diff.NoopLogger;
|
|
1325
1015
|
this.store = new SyncStore(space);
|
|
1326
1016
|
}
|
|
1327
1017
|
/**
|
|
@@ -1348,20 +1038,20 @@ var SchemaSync = class {
|
|
|
1348
1038
|
const snapshots = [];
|
|
1349
1039
|
for (const r of allReadables) {
|
|
1350
1040
|
if (r.isView) {
|
|
1351
|
-
snapshots.push(computeViewSnapshot(r));
|
|
1041
|
+
snapshots.push(require_column_diff.computeViewSnapshot(r));
|
|
1352
1042
|
continue;
|
|
1353
1043
|
}
|
|
1354
1044
|
r.fieldDescriptors;
|
|
1355
1045
|
await r.dbAdapter.prepareTypeMapper?.();
|
|
1356
1046
|
const tm = r.dbAdapter.typeMapper?.bind(r.dbAdapter);
|
|
1357
1047
|
const opts = r.dbAdapter.getDesiredTableOptions?.();
|
|
1358
|
-
snapshots.push(computeTableSnapshot(r, tm, opts));
|
|
1048
|
+
snapshots.push(require_column_diff.computeTableSnapshot(r, tm, opts));
|
|
1359
1049
|
}
|
|
1360
1050
|
return {
|
|
1361
1051
|
tables,
|
|
1362
1052
|
views,
|
|
1363
1053
|
externalViews,
|
|
1364
|
-
hash: computeSchemaHash(snapshots)
|
|
1054
|
+
hash: require_column_diff.computeSchemaHash(snapshots)
|
|
1365
1055
|
};
|
|
1366
1056
|
}
|
|
1367
1057
|
/**
|
|
@@ -1669,7 +1359,7 @@ var SchemaSync = class {
|
|
|
1669
1359
|
pendingRename,
|
|
1670
1360
|
dbName,
|
|
1671
1361
|
storedSnapshot,
|
|
1672
|
-
fkDiff: storedSnapshot ? computeForeignKeyDiff(readable.foreignKeys, storedSnapshot.foreignKeys) : void 0,
|
|
1362
|
+
fkDiff: storedSnapshot ? require_column_diff.computeForeignKeyDiff(readable.foreignKeys, storedSnapshot.foreignKeys) : void 0,
|
|
1673
1363
|
dropsTable: false,
|
|
1674
1364
|
planEntry: init
|
|
1675
1365
|
};
|
|
@@ -1681,7 +1371,7 @@ var SchemaSync = class {
|
|
|
1681
1371
|
init.columnsToAdd = readable.fieldDescriptors.filter((f) => !f.ignored);
|
|
1682
1372
|
} else if (facts.existing && facts.existing.length > 0) {
|
|
1683
1373
|
const typeMapper = adapter.typeMapper?.bind(adapter);
|
|
1684
|
-
facts.diff = computeColumnDiff(readable.fieldDescriptors, facts.existing, typeMapper);
|
|
1374
|
+
facts.diff = require_column_diff.computeColumnDiff(readable.fieldDescriptors, facts.existing, typeMapper);
|
|
1685
1375
|
this.populatePlanFromDiff(facts.diff, init, readable, safe);
|
|
1686
1376
|
}
|
|
1687
1377
|
} else if (adapter.syncColumns) if (!storedSnapshot) {
|
|
@@ -1692,8 +1382,8 @@ var SchemaSync = class {
|
|
|
1692
1382
|
}
|
|
1693
1383
|
}
|
|
1694
1384
|
} else {
|
|
1695
|
-
const existing = snapshotToExistingColumns(storedSnapshot);
|
|
1696
|
-
facts.diff = computeColumnDiff(readable.fieldDescriptors, existing, this.resolveTypeMapper(adapter));
|
|
1385
|
+
const existing = require_column_diff.snapshotToExistingColumns(storedSnapshot);
|
|
1386
|
+
facts.diff = require_column_diff.computeColumnDiff(readable.fieldDescriptors, existing, this.resolveTypeMapper(adapter));
|
|
1697
1387
|
this.populatePlanFromDiff(facts.diff, init, readable, safe);
|
|
1698
1388
|
}
|
|
1699
1389
|
else if (adapter.tableExists) {
|
|
@@ -1708,7 +1398,7 @@ var SchemaSync = class {
|
|
|
1708
1398
|
if (optionDiff.changed.some((c) => c.destructive)) init.recreated = true;
|
|
1709
1399
|
}
|
|
1710
1400
|
}
|
|
1711
|
-
if (init.status !== "create" && init.status !== "error" && facts.fkDiff && hasForeignKeyChanges(facts.fkDiff)) {
|
|
1401
|
+
if (init.status !== "create" && init.status !== "error" && facts.fkDiff && require_column_diff.hasForeignKeyChanges(facts.fkDiff)) {
|
|
1712
1402
|
const fkDiff = facts.fkDiff;
|
|
1713
1403
|
init.status = "alter";
|
|
1714
1404
|
init.fkAdded = fkDiff.added.map((fk) => ({
|
|
@@ -1760,7 +1450,7 @@ var SchemaSync = class {
|
|
|
1760
1450
|
addRefusal(refusals, t.name, `Primary key of "${t.name}" changed ${label} but the referencing table "${child.name}" is renamed in the same run (from "${child.pendingRename}") — rename it in a separate run first, then retarget the foreign key and re-run.`);
|
|
1761
1451
|
continue;
|
|
1762
1452
|
}
|
|
1763
|
-
if (!(child !== void 0 && [...child.readable.foreignKeys.values()].some((desired) => desired.targetTable === t.name && fkKey(desired.fields) === fkKey(fk.fields) && fkKey(desired.targetFields) === fkKey(pk.to)))) addRefusal(refusals, t.name, `Primary key of "${t.name}" changed ${label} but "${fk.table}.${fk.fields.join(",")}" still references the old key — retarget the foreign key (or migrate manually) and re-run.`);
|
|
1453
|
+
if (!(child !== void 0 && [...child.readable.foreignKeys.values()].some((desired) => desired.targetTable === t.name && require_column_diff.fkKey(desired.fields) === require_column_diff.fkKey(fk.fields) && require_column_diff.fkKey(desired.targetFields) === require_column_diff.fkKey(pk.to)))) addRefusal(refusals, t.name, `Primary key of "${t.name}" changed ${label} but "${fk.table}.${fk.fields.join(",")}" still references the old key — retarget the foreign key (or migrate manually) and re-run.`);
|
|
1764
1454
|
}
|
|
1765
1455
|
for (const f of t.readable.fieldDescriptors) if (!f.ignored && !f.isPrimaryKey && f.defaultValue?.kind === "fn" && f.defaultValue.fn === "increment") addRefusal(refusals, t.name, `"${t.name}.${f.physicalName}" is auto-increment but no longer part of the primary key; auto-increment columns must be primary-key columns.`);
|
|
1766
1456
|
}
|
|
@@ -1909,7 +1599,7 @@ var SchemaSync = class {
|
|
|
1909
1599
|
if (!safe) for (const t of d.tables) {
|
|
1910
1600
|
if (!t.diff?.primaryKeyChanged) continue;
|
|
1911
1601
|
const keysByChild = /* @__PURE__ */ new Map();
|
|
1912
|
-
for (const fk of t.inboundFks ?? []) keysByChild.set(fk.table, [...keysByChild.get(fk.table) ?? [], fkKey(fk.fields)]);
|
|
1602
|
+
for (const fk of t.inboundFks ?? []) keysByChild.set(fk.table, [...keysByChild.get(fk.table) ?? [], require_column_diff.fkKey(fk.fields)]);
|
|
1913
1603
|
for (const [childName, fkKeys] of keysByChild) {
|
|
1914
1604
|
const child = d.tableByDbName.get(childName);
|
|
1915
1605
|
if (child?.readable.dbAdapter.dropForeignKeys) {
|
|
@@ -1992,7 +1682,7 @@ var SchemaSync = class {
|
|
|
1992
1682
|
const adapter = readable.dbAdapter;
|
|
1993
1683
|
const tm = adapter.typeMapper?.bind(adapter);
|
|
1994
1684
|
const opts = adapter.getDesiredTableOptions?.();
|
|
1995
|
-
const snapshot = readable.isView ? computeViewSnapshot(readable) : computeTableSnapshot(readable, tm, opts);
|
|
1685
|
+
const snapshot = readable.isView ? require_column_diff.computeViewSnapshot(readable) : require_column_diff.computeTableSnapshot(readable, tm, opts);
|
|
1996
1686
|
await this.store.writeTableSnapshot(readable.tableName, snapshot);
|
|
1997
1687
|
}
|
|
1998
1688
|
for (const name of droppedNames) await this.store.deleteTableSnapshot(name);
|
|
@@ -2057,7 +1747,7 @@ var SchemaSync = class {
|
|
|
2057
1747
|
*/
|
|
2058
1748
|
reportOutcome(result, onError) {
|
|
2059
1749
|
if (onError === "silent" || result.entries.length === 0) return;
|
|
2060
|
-
const out = this.logger ===
|
|
1750
|
+
const out = this.logger === require_column_diff.NoopLogger ? console : this.logger;
|
|
2061
1751
|
const counts = /* @__PURE__ */ new Map();
|
|
2062
1752
|
for (const entry of result.entries) counts.set(entry.status, (counts.get(entry.status) ?? 0) + 1);
|
|
2063
1753
|
const summary = [...counts.entries()].map(([status, n]) => `${n} ${status}`).join(", ");
|
|
@@ -2152,7 +1842,7 @@ var SchemaSync = class {
|
|
|
2152
1842
|
if (adapter.getExistingTableOptions) existing = await adapter.getExistingTableOptions(tableName);
|
|
2153
1843
|
else {
|
|
2154
1844
|
const snapshot = await this.store.readTableSnapshot(tableName ?? readable.tableName);
|
|
2155
|
-
existing = snapshot ? snapshotToExistingTableOptions(snapshot) : [];
|
|
1845
|
+
existing = snapshot ? require_column_diff.snapshotToExistingTableOptions(snapshot) : [];
|
|
2156
1846
|
}
|
|
2157
1847
|
if (existing.length === 0) return null;
|
|
2158
1848
|
const destructiveKeys = adapter.destructiveOptionKeys?.();
|
|
@@ -2219,19 +1909,19 @@ async function planSchema(space, types, opts) {
|
|
|
2219
1909
|
//#endregion
|
|
2220
1910
|
exports.SchemaSync = SchemaSync;
|
|
2221
1911
|
exports.SyncEntry = SyncEntry;
|
|
2222
|
-
exports.canonicalizeQueryNode = canonicalizeQueryNode;
|
|
2223
|
-
exports.computeColumnDiff = computeColumnDiff;
|
|
2224
|
-
exports.computeForeignKeyDiff = computeForeignKeyDiff;
|
|
2225
|
-
exports.computeSchemaHash = computeSchemaHash;
|
|
2226
|
-
exports.computeTableHash = computeTableHash;
|
|
1912
|
+
exports.canonicalizeQueryNode = require_column_diff.canonicalizeQueryNode;
|
|
1913
|
+
exports.computeColumnDiff = require_column_diff.computeColumnDiff;
|
|
1914
|
+
exports.computeForeignKeyDiff = require_column_diff.computeForeignKeyDiff;
|
|
1915
|
+
exports.computeSchemaHash = require_column_diff.computeSchemaHash;
|
|
1916
|
+
exports.computeTableHash = require_column_diff.computeTableHash;
|
|
2227
1917
|
exports.computeTableOptionDiff = computeTableOptionDiff;
|
|
2228
|
-
exports.computeTableSnapshot = computeTableSnapshot;
|
|
2229
|
-
exports.computeViewSnapshot = computeViewSnapshot;
|
|
2230
|
-
exports.fkKey = fkKey;
|
|
2231
|
-
exports.hasForeignKeyChanges = hasForeignKeyChanges;
|
|
1918
|
+
exports.computeTableSnapshot = require_column_diff.computeTableSnapshot;
|
|
1919
|
+
exports.computeViewSnapshot = require_column_diff.computeViewSnapshot;
|
|
1920
|
+
exports.fkKey = require_column_diff.fkKey;
|
|
1921
|
+
exports.hasForeignKeyChanges = require_column_diff.hasForeignKeyChanges;
|
|
2232
1922
|
exports.planSchema = planSchema;
|
|
2233
1923
|
exports.readStoredSnapshot = readStoredSnapshot;
|
|
2234
|
-
exports.snapshotToExistingColumns = snapshotToExistingColumns;
|
|
2235
|
-
exports.snapshotToExistingTableOptions = snapshotToExistingTableOptions;
|
|
1924
|
+
exports.snapshotToExistingColumns = require_column_diff.snapshotToExistingColumns;
|
|
1925
|
+
exports.snapshotToExistingTableOptions = require_column_diff.snapshotToExistingTableOptions;
|
|
2236
1926
|
exports.syncSchema = syncSchema;
|
|
2237
1927
|
exports.topoOrder = topoOrder;
|
package/dist/sync.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { $t as AtscriptDbReadable, Ft as DbSpace, G as TDbObjectKind, I as TDbDefaultValue, R as TDbFieldMeta, Rt as AtscriptDbView, Ut as AtscriptQueryFieldRef$1, Wt as AtscriptQueryNode$1, X as TDbStorageType, at as TExistingTableOption, ht as TReferencingForeignKey, it as TExistingForeignKey, mt as TPrimaryKeyChange, nt as TEnsureTableOptions, rt as TExistingColumn, sn as TGenericLogger, yt as TTableOptionDiff, z as TDbForeignKey } from "./buckets-C-27xmtq.cjs";
|
|
2
|
+
import { t as computeColumnDiff } from "./column-diff-BmqvgBWw.cjs";
|
|
3
3
|
import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
|
|
4
4
|
|
|
5
5
|
//#region src/schema/sync-entry.d.ts
|
|
@@ -305,6 +305,11 @@ interface TViewSnapshot {
|
|
|
305
305
|
/** @since 0.1.128 — hash of the canonical `@db.view.having` predicate. */
|
|
306
306
|
havingHash?: string;
|
|
307
307
|
materialized?: boolean;
|
|
308
|
+
/**
|
|
309
|
+
* @since 0.1.137 — the adapter's `viewRenderRevision()`, present only when
|
|
310
|
+
* the adapter defines one (managed views only).
|
|
311
|
+
*/
|
|
312
|
+
renderRevision?: string;
|
|
308
313
|
fields: TFieldSnapshot[];
|
|
309
314
|
}
|
|
310
315
|
/**
|
|
@@ -554,19 +559,6 @@ declare class SchemaSync {
|
|
|
554
559
|
private buildExecutorDeps;
|
|
555
560
|
}
|
|
556
561
|
//#endregion
|
|
557
|
-
//#region src/schema/column-diff.d.ts
|
|
558
|
-
/**
|
|
559
|
-
* Computes the difference between desired schema fields and existing database columns.
|
|
560
|
-
*
|
|
561
|
-
* @param desired - Field descriptors from the Atscript type (after flattening).
|
|
562
|
-
* @param existing - Columns currently in the database (from introspection).
|
|
563
|
-
* @param typeMapper - Optional function to map field metadata to DB-native type strings.
|
|
564
|
-
* Receives the full field meta (design type, annotations, PK status, etc.)
|
|
565
|
-
* so adapters can produce context-aware types (e.g., `VARCHAR(255)` from maxLength).
|
|
566
|
-
* Required for type change detection.
|
|
567
|
-
*/
|
|
568
|
-
declare function computeColumnDiff(desired: readonly TDbFieldMeta[], existing: TExistingColumn[], typeMapper?: (field: TDbFieldMeta) => string): TColumnDiff;
|
|
569
|
-
//#endregion
|
|
570
562
|
//#region src/schema/table-option-diff.d.ts
|
|
571
563
|
/**
|
|
572
564
|
* Computes the difference between desired and existing table options.
|
package/dist/sync.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { $t as AtscriptDbReadable, Ft as DbSpace, G as TDbObjectKind, I as TDbDefaultValue, R as TDbFieldMeta, Rt as AtscriptDbView, Ut as AtscriptQueryFieldRef$1, Wt as AtscriptQueryNode$1, X as TDbStorageType, at as TExistingTableOption, ht as TReferencingForeignKey, it as TExistingForeignKey, mt as TPrimaryKeyChange, nt as TEnsureTableOptions, rt as TExistingColumn, sn as TGenericLogger, yt as TTableOptionDiff, z as TDbForeignKey } from "./buckets-BFG2RYRW.mjs";
|
|
2
|
+
import { t as computeColumnDiff } from "./column-diff-DPkbZIVE.mjs";
|
|
3
3
|
import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
|
|
4
4
|
|
|
5
5
|
//#region src/schema/sync-entry.d.ts
|
|
@@ -305,6 +305,11 @@ interface TViewSnapshot {
|
|
|
305
305
|
/** @since 0.1.128 — hash of the canonical `@db.view.having` predicate. */
|
|
306
306
|
havingHash?: string;
|
|
307
307
|
materialized?: boolean;
|
|
308
|
+
/**
|
|
309
|
+
* @since 0.1.137 — the adapter's `viewRenderRevision()`, present only when
|
|
310
|
+
* the adapter defines one (managed views only).
|
|
311
|
+
*/
|
|
312
|
+
renderRevision?: string;
|
|
308
313
|
fields: TFieldSnapshot[];
|
|
309
314
|
}
|
|
310
315
|
/**
|
|
@@ -554,19 +559,6 @@ declare class SchemaSync {
|
|
|
554
559
|
private buildExecutorDeps;
|
|
555
560
|
}
|
|
556
561
|
//#endregion
|
|
557
|
-
//#region src/schema/column-diff.d.ts
|
|
558
|
-
/**
|
|
559
|
-
* Computes the difference between desired schema fields and existing database columns.
|
|
560
|
-
*
|
|
561
|
-
* @param desired - Field descriptors from the Atscript type (after flattening).
|
|
562
|
-
* @param existing - Columns currently in the database (from introspection).
|
|
563
|
-
* @param typeMapper - Optional function to map field metadata to DB-native type strings.
|
|
564
|
-
* Receives the full field meta (design type, annotations, PK status, etc.)
|
|
565
|
-
* so adapters can produce context-aware types (e.g., `VARCHAR(255)` from maxLength).
|
|
566
|
-
* Required for type change detection.
|
|
567
|
-
*/
|
|
568
|
-
declare function computeColumnDiff(desired: readonly TDbFieldMeta[], existing: TExistingColumn[], typeMapper?: (field: TDbFieldMeta) => string): TColumnDiff;
|
|
569
|
-
//#endregion
|
|
570
562
|
//#region src/schema/table-option-diff.d.ts
|
|
571
563
|
/**
|
|
572
564
|
* Computes the difference between desired and existing table options.
|