@atscript/db 0.1.135 → 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.
Files changed (49) hide show
  1. package/dist/{agg-DKuf_v2L.d.cts → agg-CV7y8nC6.d.cts} +13 -3
  2. package/dist/{agg-BtlGeRfj.d.mts → agg-D5DHsAby.d.mts} +13 -3
  3. package/dist/agg.cjs +35 -3
  4. package/dist/agg.d.cts +2 -2
  5. package/dist/agg.d.mts +2 -2
  6. package/dist/agg.mjs +34 -1
  7. package/dist/aggregate-fns-CGBv3E8S.cjs +87 -0
  8. package/dist/aggregate-fns-CfsveE1w.mjs +58 -0
  9. package/dist/{buckets-GruoVxH5.d.mts → buckets-BFG2RYRW.d.mts} +662 -31
  10. package/dist/{buckets-D6PBXKRJ.d.cts → buckets-C-27xmtq.d.cts} +662 -31
  11. package/dist/column-diff-BmqvgBWw.d.cts +24 -0
  12. package/dist/{db-view-Doh8vPg3.mjs → column-diff-BwOA5101.mjs} +795 -125
  13. package/dist/{db-view-Dm7MpUGB.cjs → column-diff-CgxgFKzx.cjs} +873 -125
  14. package/dist/column-diff-DPkbZIVE.d.mts +24 -0
  15. package/dist/index.cjs +79 -55
  16. package/dist/index.d.cts +12 -10
  17. package/dist/index.d.mts +12 -10
  18. package/dist/index.mjs +30 -11
  19. package/dist/{nested-writer-wk1EFUNY.cjs → nested-writer-BZNCuqI6.cjs} +16 -2
  20. package/dist/{nested-writer-CqL24ojl.mjs → nested-writer-FWD5oOYh.mjs} +11 -3
  21. package/dist/plugin.cjs +217 -77
  22. package/dist/plugin.mjs +217 -78
  23. package/dist/rel.cjs +2 -2
  24. package/dist/rel.d.cts +2 -20
  25. package/dist/rel.d.mts +2 -20
  26. package/dist/rel.mjs +2 -2
  27. package/dist/relation-helpers-D3Zu0Mta.d.mts +30 -0
  28. package/dist/relation-helpers-DxrvS6ar.d.cts +30 -0
  29. package/dist/{relation-loader-BD4xANQJ.cjs → relation-loader-6ZB_5KFq.cjs} +1 -1
  30. package/dist/{relation-loader-B68R1LET.mjs → relation-loader-CTFaZpVa.mjs} +1 -1
  31. package/dist/shared.cjs +5 -1
  32. package/dist/shared.d.cts +16 -3
  33. package/dist/shared.d.mts +16 -3
  34. package/dist/shared.mjs +2 -2
  35. package/dist/sync.cjs +38 -321
  36. package/dist/sync.d.cts +29 -17
  37. package/dist/sync.d.mts +29 -17
  38. package/dist/sync.mjs +6 -289
  39. package/dist/{validation-utils-MWOP1Ts4.mjs → validation-utils-B4h-GW4d.mjs} +29 -7
  40. package/dist/{validation-utils-B7SXPkm7.cjs → validation-utils-Dg0hW6dn.cjs} +52 -6
  41. package/dist/{validator-CewfnGZj.d.mts → validator-Drb2N-YL.d.cts} +1 -1
  42. package/dist/{validator-CewfnGZj.d.cts → validator-Drb2N-YL.d.mts} +1 -1
  43. package/dist/validator.d.cts +1 -1
  44. package/dist/validator.d.mts +1 -1
  45. package/package.json +1 -1
  46. package/dist/agg-CvXDGnKi.mjs +0 -61
  47. package/dist/agg-EcIbAFnJ.cjs +0 -78
  48. package/dist/db-space-9CH5neN7.d.mts +0 -439
  49. package/dist/db-space-BdtBNTeH.d.cts +0 -439
package/dist/shared.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { a as getAnnotationAlias, c as getParentStruct, d as validateExclusiveWith, f as validateFieldBaseType, i as validateRefArgument, l as getParentTypeName, n as hasAnyViewAnnotation, o as getDbTableOwner, p as validateSiblingStringField, r as validateQueryScope, s as getNavTargetTypeName, t as findFKFieldsPointingTo, u as refActionAnnotation } from "./validation-utils-MWOP1Ts4.mjs";
2
- export { findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField };
1
+ import { _ as validateSiblingStringField, a as validateRefArgument, c as getAnnotationAlias, d as getParentStruct, f as getParentTypeName, g as validateFieldBaseType, h as validateExclusiveWith, i as validateQueryScope, l as getDbTableOwner, m as refActionAnnotation, n as hasAnyViewAnnotation, o as viewJoins, p as primitiveBaseType, r as joinTargets, s as viewScopeTypes, t as findFKFieldsPointingTo, u as getNavTargetTypeName } from "./validation-utils-B4h-GW4d.mjs";
2
+ export { findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, joinTargets, primitiveBaseType, refActionAnnotation, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField, viewJoins, viewScopeTypes };
package/dist/sync.cjs CHANGED
@@ -1,292 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_db_view = require("./db-view-Dm7MpUGB.cjs");
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 result = {
70
- tableName: view.tableName,
71
- viewType: plan.materialized ? "M" : "V",
72
- entryTable: plan.entryTable,
73
- joinTables: plan.joins.map((j) => ({
74
- targetTable: j.targetTable,
75
- condition: JSON.stringify(canonicalizeQueryNode(j.condition, qualify))
76
- })),
77
- materialized: plan.materialized || void 0,
78
- fields
79
- };
80
- if (plan.filter) result.filterHash = fnv1a(JSON.stringify(canonicalizeQueryNode(plan.filter, qualify)));
81
- if (plan.having) result.havingHash = fnv1a(JSON.stringify(canonicalizeQueryNode(plan.having, qualify)));
82
- return result;
83
- }
84
- /**
85
- * Converts a view predicate (join condition, `@db.view.filter`,
86
- * `@db.view.having`) into a serializable structure whose JSON is a stable
87
- * function of its MEANING: field refs become `qualify(ref)` — the view's
88
- * `resolveFieldRef(ref, (n) => n)`, i.e. `"<table>.<field>"`, so a predicate
89
- * retargeted to another table with the same field name changes — operators
90
- * and literal values are kept as-is, `$and`/`$or` keep declaration order, and
91
- * no function references survive. Two identical models produce byte-identical
92
- * JSON.
93
- * @since 0.1.128
94
- */
95
- function canonicalizeQueryNode(node, qualify) {
96
- if ("$and" in node) return { and: node.$and.map((n) => canonicalizeQueryNode(n, qualify)) };
97
- if ("$or" in node) return { or: node.$or.map((n) => canonicalizeQueryNode(n, qualify)) };
98
- if ("$not" in node) return { not: canonicalizeQueryNode(node.$not, qualify) };
99
- const comp = node;
100
- const out = {
101
- l: qualify(comp.left),
102
- op: comp.op
103
- };
104
- if (comp.right !== void 0) out.r = comp.right !== null && typeof comp.right === "object" && "field" in comp.right ? { f: qualify(comp.right) } : comp.right;
105
- return out;
106
- }
107
- /**
108
- * Computes a deterministic hash string from multiple table snapshots.
109
- * Uses FNV-1a for speed — not cryptographic, just needs stability + collision resistance.
110
- */
111
- function computeSchemaHash(snapshots) {
112
- const sorted = [...snapshots].toSorted((a, b) => a.tableName.localeCompare(b.tableName));
113
- return fnv1a(JSON.stringify(sorted));
114
- }
115
- /**
116
- * Computes a hash for a single table/view snapshot.
117
- * Used for per-table change detection via stored snapshots.
118
- */
119
- function computeTableHash(snapshot) {
120
- return fnv1a(JSON.stringify(snapshot));
121
- }
122
- /**
123
- * Converts stored snapshot fields to `TExistingColumn[]` format
124
- * for use with `computeColumnDiff`. Used by adapters that lack
125
- * native column introspection (e.g., MongoDB).
126
- *
127
- * The `type` field uses `mappedType` when available (adapter-specific),
128
- * falling back to `designType`.
129
- */
130
- function snapshotToExistingColumns(snapshot) {
131
- return snapshot.fields.map((f) => ({
132
- name: f.physicalName,
133
- type: f.mappedType ?? f.designType,
134
- notnull: !f.optional,
135
- pk: f.isPrimaryKey,
136
- dflt_value: serializeDefaultValue(f.defaultValue)
137
- }));
138
- }
139
- /**
140
- * Extracts table options from a stored snapshot for diff comparison.
141
- * Used as fallback when an adapter lacks native table option introspection.
142
- */
143
- function snapshotToExistingTableOptions(snapshot) {
144
- return snapshot.tableOptions ?? [];
145
- }
146
- /** Serializes a TDbDefaultValue to a comparable string. */
147
- function serializeDefaultValue(dv) {
148
- if (!dv) return;
149
- if (dv.kind === "value") return dv.value;
150
- return `fn:${dv.fn}`;
151
- }
152
- /** FNV-1a 32-bit hash → hex string */
153
- function fnv1a(str) {
154
- let hash = 2166136261;
155
- for (let i = 0; i < str.length; i++) {
156
- hash ^= str.codePointAt(i);
157
- hash = Math.imul(hash, 16777619);
158
- }
159
- return Math.trunc(hash).toString(16).padStart(8, "0");
160
- }
161
- //#endregion
162
- //#region src/schema/fk-diff.ts
163
- /** Canonical key for an FK: sorted local field names, comma-joined. */
164
- function fkKey(fields) {
165
- return [...fields].toSorted().join(",");
166
- }
167
- /**
168
- * Compares desired FK constraints against stored snapshot to detect
169
- * additions, removals, and property changes (target table, target fields,
170
- * onDelete, onUpdate).
171
- */
172
- function computeForeignKeyDiff(desired, existingSnapshot) {
173
- const added = [];
174
- const removed = [];
175
- const changed = [];
176
- const existingByKey = /* @__PURE__ */ new Map();
177
- for (const fk of existingSnapshot) existingByKey.set(fkKey(fk.fields), fk);
178
- const desiredKeys = /* @__PURE__ */ new Set();
179
- for (const fk of desired.values()) {
180
- const key = fkKey(fk.fields);
181
- desiredKeys.add(key);
182
- const existing = existingByKey.get(key);
183
- if (!existing) added.push(fk);
184
- else if (fkPropertiesDiffer(fk, existing)) changed.push({
185
- desired: fk,
186
- existing
187
- });
188
- }
189
- for (const [key, fk] of existingByKey) if (!desiredKeys.has(key)) removed.push(fk);
190
- return {
191
- added,
192
- removed,
193
- changed
194
- };
195
- }
196
- /** Whether the FK diff contains any changes. */
197
- function hasForeignKeyChanges(diff) {
198
- return diff.added.length > 0 || diff.removed.length > 0 || diff.changed.length > 0;
199
- }
200
- function fkPropertiesDiffer(desired, existing) {
201
- if (desired.targetTable !== existing.targetTable) return true;
202
- if (fkKey(desired.targetFields) !== fkKey(existing.targetFields)) return true;
203
- if ((desired.onDelete ?? void 0) !== (existing.onDelete ?? void 0)) return true;
204
- if ((desired.onUpdate ?? void 0) !== (existing.onUpdate ?? void 0)) return true;
205
- return false;
206
- }
207
- //#endregion
208
- //#region src/schema/column-diff.ts
209
- /**
210
- * Computes the difference between desired schema fields and existing database columns.
211
- *
212
- * @param desired - Field descriptors from the Atscript type (after flattening).
213
- * @param existing - Columns currently in the database (from introspection).
214
- * @param typeMapper - Optional function to map field metadata to DB-native type strings.
215
- * Receives the full field meta (design type, annotations, PK status, etc.)
216
- * so adapters can produce context-aware types (e.g., `VARCHAR(255)` from maxLength).
217
- * Required for type change detection.
218
- */
219
- function computeColumnDiff(desired, existing, typeMapper) {
220
- const existingByName = new Map(existing.map((c) => [c.name, c]));
221
- const desiredByName = /* @__PURE__ */ new Map();
222
- const renamedOldNames = /* @__PURE__ */ new Set();
223
- const added = [];
224
- const renamed = [];
225
- const typeChanged = [];
226
- const nullableChanged = [];
227
- const defaultChanged = [];
228
- const conflicts = [];
229
- for (const field of desired) {
230
- if (field.ignored) continue;
231
- desiredByName.set(field.physicalName, field);
232
- const existingCol = existingByName.get(field.physicalName);
233
- if (existingCol) if (field.renamedFrom && existingByName.has(field.renamedFrom)) {
234
- conflicts.push({
235
- field,
236
- oldName: field.renamedFrom,
237
- conflictsWith: field.physicalName
238
- });
239
- renamedOldNames.add(field.renamedFrom);
240
- } else {
241
- if (typeMapper) {
242
- if (typeMapper(field).toUpperCase() !== existingCol.type.toUpperCase()) typeChanged.push({
243
- field,
244
- existingType: existingCol.type
245
- });
246
- }
247
- if (!field.isPrimaryKey && !existingCol.pk) {
248
- const desiredNotNull = !field.optional;
249
- if (existingCol.notnull !== desiredNotNull) nullableChanged.push({
250
- field,
251
- wasNullable: !existingCol.notnull
252
- });
253
- }
254
- const desiredDefault = serializeDefaultValue(field.defaultValue);
255
- if (existingCol.dflt_value !== void 0 && existingCol.dflt_value !== desiredDefault) defaultChanged.push({
256
- field,
257
- oldDefault: existingCol.dflt_value,
258
- newDefault: desiredDefault
259
- });
260
- }
261
- else if (field.renamedFrom && existingByName.has(field.renamedFrom)) {
262
- renamed.push({
263
- field,
264
- oldName: field.renamedFrom
265
- });
266
- renamedOldNames.add(field.renamedFrom);
267
- } else added.push(field);
268
- }
269
- const diff = {
270
- added,
271
- removed: existing.filter((c) => !desiredByName.has(c.name) && !renamedOldNames.has(c.name)),
272
- renamed,
273
- typeChanged,
274
- nullableChanged,
275
- defaultChanged,
276
- conflicts
277
- };
278
- if (existing.length > 0) {
279
- const newNameByOld = new Map(renamed.map((r) => [r.oldName, r.field.physicalName]));
280
- const from = existing.filter((c) => c.pk).map((c) => newNameByOld.get(c.name) ?? c.name);
281
- const to = desired.filter((f) => !f.ignored && f.isPrimaryKey).map((f) => f.physicalName);
282
- if (fkKey(from) !== fkKey(to)) diff.primaryKeyChanged = {
283
- from,
284
- to
285
- };
286
- }
287
- return diff;
288
- }
289
- //#endregion
2
+ const require_column_diff = require("./column-diff-CgxgFKzx.cjs");
290
3
  //#region src/schema/table-option-diff.ts
291
4
  /**
292
5
  * Computes the difference between desired and existing table options.
@@ -816,8 +529,8 @@ var SyncEntry = class SyncEntry {
816
529
  async function viewDefinitionChanged(view, store) {
817
530
  const storedSnapshot = await store.readTableSnapshot(view.tableName, true);
818
531
  if (!storedSnapshot) return false;
819
- const currentHash = computeTableHash(computeViewSnapshot(view));
820
- return computeTableHash(storedSnapshot) !== currentHash;
532
+ const currentHash = require_column_diff.computeTableHash(require_column_diff.computeViewSnapshot(view));
533
+ return require_column_diff.computeTableHash(storedSnapshot) !== currentHash;
821
534
  }
822
535
  /**
823
536
  * The plan entry of one table as `plan()` reports it (and as `run()` reports
@@ -912,14 +625,14 @@ async function executeSyncTable(facts, safe, deps, exec) {
912
625
  init.status = "alter";
913
626
  }
914
627
  phase = "Column sync";
915
- const hasFkChanges = fkDiff ? hasForeignKeyChanges(fkDiff) : false;
628
+ const hasFkChanges = fkDiff ? require_column_diff.hasForeignKeyChanges(fkDiff) : false;
916
629
  if (adapter.getExistingColumns && adapter.syncColumns) {
917
630
  const existing = facts.existing ?? await adapter.getExistingColumns();
918
631
  if (existing.length === 0 && !init.renamedFrom) {
919
632
  await adapter.ensureTable(ensureOpts);
920
633
  init.status = "create";
921
634
  } else if (existing.length > 0) {
922
- 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));
923
636
  if (hasFkChanges && !adapter.syncForeignKeys && adapter.recreateTable) {
924
637
  await adapter.recreateTable();
925
638
  init.recreated = true;
@@ -930,7 +643,7 @@ async function executeSyncTable(facts, safe, deps, exec) {
930
643
  };
931
644
  } else {
932
645
  if (hasFkChanges && fkDiff && adapter.dropForeignKeys) {
933
- 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))];
934
647
  if (keysToDrop.length > 0) {
935
648
  await adapter.dropForeignKeys(keysToDrop);
936
649
  droppedFks.push(...keysToDrop);
@@ -944,7 +657,7 @@ async function executeSyncTable(facts, safe, deps, exec) {
944
657
  const existed = adapter.tableExists ? await adapter.tableExists() : false;
945
658
  await adapter.ensureTable(ensureOpts);
946
659
  if (!existed) init.status = "create";
947
- } 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);
948
661
  else {
949
662
  const existed = adapter.tableExists ? await adapter.tableExists() : true;
950
663
  if (!init.recreated) {
@@ -1095,7 +808,7 @@ function safeModeSkips(facts) {
1095
808
  const adapter = readable.dbAdapter;
1096
809
  const out = [];
1097
810
  if (facts.planEntry.status === "error") return out;
1098
- if (fkDiff && hasForeignKeyChanges(fkDiff) && !adapter.syncForeignKeys && adapter.recreateTable) return out;
811
+ if (fkDiff && require_column_diff.hasForeignKeyChanges(fkDiff) && !adapter.syncForeignKeys && adapter.recreateTable) return out;
1099
812
  const strategy = diff && diff.typeChanged.length > 0 ? typeChangeStrategy(readable, diff) : "modify";
1100
813
  if (strategy === "drop") out.push("recreate");
1101
814
  else if (strategy === "recreate") return out;
@@ -1298,7 +1011,7 @@ var SchemaSync = class {
1298
1011
  logger;
1299
1012
  constructor(space, logger) {
1300
1013
  this.space = space;
1301
- this.logger = logger || require_db_view.NoopLogger;
1014
+ this.logger = logger || require_column_diff.NoopLogger;
1302
1015
  this.store = new SyncStore(space);
1303
1016
  }
1304
1017
  /**
@@ -1325,20 +1038,20 @@ var SchemaSync = class {
1325
1038
  const snapshots = [];
1326
1039
  for (const r of allReadables) {
1327
1040
  if (r.isView) {
1328
- snapshots.push(computeViewSnapshot(r));
1041
+ snapshots.push(require_column_diff.computeViewSnapshot(r));
1329
1042
  continue;
1330
1043
  }
1331
1044
  r.fieldDescriptors;
1332
1045
  await r.dbAdapter.prepareTypeMapper?.();
1333
1046
  const tm = r.dbAdapter.typeMapper?.bind(r.dbAdapter);
1334
1047
  const opts = r.dbAdapter.getDesiredTableOptions?.();
1335
- snapshots.push(computeTableSnapshot(r, tm, opts));
1048
+ snapshots.push(require_column_diff.computeTableSnapshot(r, tm, opts));
1336
1049
  }
1337
1050
  return {
1338
1051
  tables,
1339
1052
  views,
1340
1053
  externalViews,
1341
- hash: computeSchemaHash(snapshots)
1054
+ hash: require_column_diff.computeSchemaHash(snapshots)
1342
1055
  };
1343
1056
  }
1344
1057
  /**
@@ -1646,7 +1359,7 @@ var SchemaSync = class {
1646
1359
  pendingRename,
1647
1360
  dbName,
1648
1361
  storedSnapshot,
1649
- fkDiff: storedSnapshot ? computeForeignKeyDiff(readable.foreignKeys, storedSnapshot.foreignKeys) : void 0,
1362
+ fkDiff: storedSnapshot ? require_column_diff.computeForeignKeyDiff(readable.foreignKeys, storedSnapshot.foreignKeys) : void 0,
1650
1363
  dropsTable: false,
1651
1364
  planEntry: init
1652
1365
  };
@@ -1658,7 +1371,7 @@ var SchemaSync = class {
1658
1371
  init.columnsToAdd = readable.fieldDescriptors.filter((f) => !f.ignored);
1659
1372
  } else if (facts.existing && facts.existing.length > 0) {
1660
1373
  const typeMapper = adapter.typeMapper?.bind(adapter);
1661
- facts.diff = computeColumnDiff(readable.fieldDescriptors, facts.existing, typeMapper);
1374
+ facts.diff = require_column_diff.computeColumnDiff(readable.fieldDescriptors, facts.existing, typeMapper);
1662
1375
  this.populatePlanFromDiff(facts.diff, init, readable, safe);
1663
1376
  }
1664
1377
  } else if (adapter.syncColumns) if (!storedSnapshot) {
@@ -1669,8 +1382,8 @@ var SchemaSync = class {
1669
1382
  }
1670
1383
  }
1671
1384
  } else {
1672
- const existing = snapshotToExistingColumns(storedSnapshot);
1673
- 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));
1674
1387
  this.populatePlanFromDiff(facts.diff, init, readable, safe);
1675
1388
  }
1676
1389
  else if (adapter.tableExists) {
@@ -1685,7 +1398,7 @@ var SchemaSync = class {
1685
1398
  if (optionDiff.changed.some((c) => c.destructive)) init.recreated = true;
1686
1399
  }
1687
1400
  }
1688
- 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)) {
1689
1402
  const fkDiff = facts.fkDiff;
1690
1403
  init.status = "alter";
1691
1404
  init.fkAdded = fkDiff.added.map((fk) => ({
@@ -1737,7 +1450,7 @@ var SchemaSync = class {
1737
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.`);
1738
1451
  continue;
1739
1452
  }
1740
- 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.`);
1741
1454
  }
1742
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.`);
1743
1456
  }
@@ -1747,7 +1460,11 @@ var SchemaSync = class {
1747
1460
  if (d.externalTargets.get(fk.targetTable) === false) addRefusal(refusals, t.name, `FK ${t.name}.${fk.fields.join(",")} references "${fk.targetTable}" which is neither in the sync inventory nor present in the database`);
1748
1461
  }
1749
1462
  }
1750
- for (const view of d.views) if (d.viewObjectKinds.get(view.tableName) === "table") addRefusal(refusals, view.tableName, `A physical table "${view.tableName}" exists where managed view "${view.tableName}" is declared — drop or rename it`);
1463
+ for (const view of d.views) {
1464
+ if (d.viewObjectKinds.get(view.tableName) === "table") addRefusal(refusals, view.tableName, `A physical table "${view.tableName}" exists where managed view "${view.tableName}" is declared — drop or rename it`);
1465
+ const fns = view.dbAdapter.aggregateFns();
1466
+ for (const col of view.getViewColumnMappings()) if (col.aggFn && !fns.has(col.aggFn)) addRefusal(refusals, view.tableName, `View "${view.tableName}" field "${col.viewPath}": aggregate "${col.aggFn}" is not supported by this adapter (aggregateFns())`);
1467
+ }
1751
1468
  if (!safe) for (const r of d.removed) {
1752
1469
  if (r.isView) continue;
1753
1470
  const referencers = [];
@@ -1882,7 +1599,7 @@ var SchemaSync = class {
1882
1599
  if (!safe) for (const t of d.tables) {
1883
1600
  if (!t.diff?.primaryKeyChanged) continue;
1884
1601
  const keysByChild = /* @__PURE__ */ new Map();
1885
- 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)]);
1886
1603
  for (const [childName, fkKeys] of keysByChild) {
1887
1604
  const child = d.tableByDbName.get(childName);
1888
1605
  if (child?.readable.dbAdapter.dropForeignKeys) {
@@ -1965,7 +1682,7 @@ var SchemaSync = class {
1965
1682
  const adapter = readable.dbAdapter;
1966
1683
  const tm = adapter.typeMapper?.bind(adapter);
1967
1684
  const opts = adapter.getDesiredTableOptions?.();
1968
- 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);
1969
1686
  await this.store.writeTableSnapshot(readable.tableName, snapshot);
1970
1687
  }
1971
1688
  for (const name of droppedNames) await this.store.deleteTableSnapshot(name);
@@ -2030,7 +1747,7 @@ var SchemaSync = class {
2030
1747
  */
2031
1748
  reportOutcome(result, onError) {
2032
1749
  if (onError === "silent" || result.entries.length === 0) return;
2033
- const out = this.logger === require_db_view.NoopLogger ? console : this.logger;
1750
+ const out = this.logger === require_column_diff.NoopLogger ? console : this.logger;
2034
1751
  const counts = /* @__PURE__ */ new Map();
2035
1752
  for (const entry of result.entries) counts.set(entry.status, (counts.get(entry.status) ?? 0) + 1);
2036
1753
  const summary = [...counts.entries()].map(([status, n]) => `${n} ${status}`).join(", ");
@@ -2125,7 +1842,7 @@ var SchemaSync = class {
2125
1842
  if (adapter.getExistingTableOptions) existing = await adapter.getExistingTableOptions(tableName);
2126
1843
  else {
2127
1844
  const snapshot = await this.store.readTableSnapshot(tableName ?? readable.tableName);
2128
- existing = snapshot ? snapshotToExistingTableOptions(snapshot) : [];
1845
+ existing = snapshot ? require_column_diff.snapshotToExistingTableOptions(snapshot) : [];
2129
1846
  }
2130
1847
  if (existing.length === 0) return null;
2131
1848
  const destructiveKeys = adapter.destructiveOptionKeys?.();
@@ -2192,19 +1909,19 @@ async function planSchema(space, types, opts) {
2192
1909
  //#endregion
2193
1910
  exports.SchemaSync = SchemaSync;
2194
1911
  exports.SyncEntry = SyncEntry;
2195
- exports.canonicalizeQueryNode = canonicalizeQueryNode;
2196
- exports.computeColumnDiff = computeColumnDiff;
2197
- exports.computeForeignKeyDiff = computeForeignKeyDiff;
2198
- exports.computeSchemaHash = computeSchemaHash;
2199
- 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;
2200
1917
  exports.computeTableOptionDiff = computeTableOptionDiff;
2201
- exports.computeTableSnapshot = computeTableSnapshot;
2202
- exports.computeViewSnapshot = computeViewSnapshot;
2203
- exports.fkKey = fkKey;
2204
- 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;
2205
1922
  exports.planSchema = planSchema;
2206
1923
  exports.readStoredSnapshot = readStoredSnapshot;
2207
- exports.snapshotToExistingColumns = snapshotToExistingColumns;
2208
- exports.snapshotToExistingTableOptions = snapshotToExistingTableOptions;
1924
+ exports.snapshotToExistingColumns = require_column_diff.snapshotToExistingColumns;
1925
+ exports.snapshotToExistingTableOptions = require_column_diff.snapshotToExistingTableOptions;
2209
1926
  exports.syncSchema = syncSchema;
2210
1927
  exports.topoOrder = topoOrder;
package/dist/sync.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { E as TColumnDiff, F as TDbDefaultValue, Ht as TGenericLogger, L as TDbFieldMeta, Pt as AtscriptDbReadable, R as TDbForeignKey, W as TDbObjectKind, Y as TDbStorageType, it as TExistingTableOption, mt as TReferencingForeignKey, nt as TExistingColumn, pt as TPrimaryKeyChange, rt as TExistingForeignKey, tt as TEnsureTableOptions, vt as TTableOptionDiff } from "./buckets-D6PBXKRJ.cjs";
2
- import { c as AtscriptQueryFieldRef$1, i as AtscriptDbView, l as AtscriptQueryNode$1, t as DbSpace } from "./db-space-BdtBNTeH.cjs";
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
@@ -269,9 +269,27 @@ interface TViewJoinSnapshot {
269
269
  targetTable: string;
270
270
  /** Canonical JSON of the join condition (see {@link canonicalizeQueryNode}). */
271
271
  condition: string;
272
- /** Reserved for optional joins; absent today so it does not perturb the hash. */
272
+ /** Emitted only for `"left"` — an inner join (the default) carries no key. @since 0.1.136 */
273
273
  kind?: "inner" | "left";
274
274
  }
275
+ /**
276
+ * One column of a managed view as stored in its snapshot — the PHYSICAL
277
+ * source it reads, so a source rename (`@db.column`, flattening, a moved
278
+ * JSON leaf) or an aggregate change recreates the view.
279
+ * @since 0.1.136
280
+ */
281
+ interface TViewColumnSnapshot {
282
+ column: string;
283
+ sourceTable: string;
284
+ sourceColumn: string;
285
+ /** JSON array of the path segments inside a JSON source column. */
286
+ jsonPath?: string;
287
+ jsonType?: string;
288
+ aggFn?: string;
289
+ aggField?: string;
290
+ /** Canonical JSON of a conditional aggregate's predicate. */
291
+ aggFilter?: string;
292
+ }
275
293
  interface TViewSnapshot {
276
294
  tableName: string;
277
295
  viewType: "V" | "M" | "E";
@@ -281,10 +299,17 @@ interface TViewSnapshot {
281
299
  * join-less view (`[]`) serializes byte-identically to older snapshots.
282
300
  */
283
301
  joinTables?: TViewJoinSnapshot[];
302
+ /** @since 0.1.136 — view columns and their physical sources, sorted by `column`. */
303
+ columns?: TViewColumnSnapshot[];
284
304
  filterHash?: string;
285
305
  /** @since 0.1.128 — hash of the canonical `@db.view.having` predicate. */
286
306
  havingHash?: string;
287
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;
288
313
  fields: TFieldSnapshot[];
289
314
  }
290
315
  /**
@@ -534,19 +559,6 @@ declare class SchemaSync {
534
559
  private buildExecutorDeps;
535
560
  }
536
561
  //#endregion
537
- //#region src/schema/column-diff.d.ts
538
- /**
539
- * Computes the difference between desired schema fields and existing database columns.
540
- *
541
- * @param desired - Field descriptors from the Atscript type (after flattening).
542
- * @param existing - Columns currently in the database (from introspection).
543
- * @param typeMapper - Optional function to map field metadata to DB-native type strings.
544
- * Receives the full field meta (design type, annotations, PK status, etc.)
545
- * so adapters can produce context-aware types (e.g., `VARCHAR(255)` from maxLength).
546
- * Required for type change detection.
547
- */
548
- declare function computeColumnDiff(desired: readonly TDbFieldMeta[], existing: TExistingColumn[], typeMapper?: (field: TDbFieldMeta) => string): TColumnDiff;
549
- //#endregion
550
562
  //#region src/schema/table-option-diff.d.ts
551
563
  /**
552
564
  * Computes the difference between desired and existing table options.
@@ -649,4 +661,4 @@ declare function syncSchema(space: DbSpace, types: readonly TAtscriptAnnotatedTy
649
661
  */
650
662
  declare function planSchema(space: DbSpace, types: readonly TAtscriptAnnotatedType[], opts?: Pick<TSyncOptions, "force" | "safe">): Promise<TSyncPlan>;
651
663
  //#endregion
652
- export { SchemaSync, SyncEntry, type TDbObjectKind, type TDependencyEdge, type TEnsureTableOptions, type TExistingForeignKey, type TFieldSnapshot, type TForeignKeyDiff, type TForeignKeySnapshot, type TPrimaryKeyChange, type TReferencingForeignKey, type TSyncColors, type TSyncEntryInit, type TSyncEntryStatus, type TSyncOptions, type TSyncPlan, type TSyncResult, type TSyncSkippedWork, type TTableSnapshot, type TViewJoinSnapshot, type TViewSnapshot, canonicalizeQueryNode, computeColumnDiff, computeForeignKeyDiff, computeSchemaHash, computeTableHash, computeTableOptionDiff, computeTableSnapshot, computeViewSnapshot, fkKey, hasForeignKeyChanges, planSchema, readStoredSnapshot, snapshotToExistingColumns, snapshotToExistingTableOptions, syncSchema, topoOrder };
664
+ export { SchemaSync, SyncEntry, type TDbObjectKind, type TDependencyEdge, type TEnsureTableOptions, type TExistingForeignKey, type TFieldSnapshot, type TForeignKeyDiff, type TForeignKeySnapshot, type TPrimaryKeyChange, type TReferencingForeignKey, type TSyncColors, type TSyncEntryInit, type TSyncEntryStatus, type TSyncOptions, type TSyncPlan, type TSyncResult, type TSyncSkippedWork, type TTableSnapshot, type TViewColumnSnapshot, type TViewJoinSnapshot, type TViewSnapshot, canonicalizeQueryNode, computeColumnDiff, computeForeignKeyDiff, computeSchemaHash, computeTableHash, computeTableOptionDiff, computeTableSnapshot, computeViewSnapshot, fkKey, hasForeignKeyChanges, planSchema, readStoredSnapshot, snapshotToExistingColumns, snapshotToExistingTableOptions, syncSchema, topoOrder };