@atscript/db 0.1.140 → 0.1.141

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.d.cts +1 -1
  2. package/dist/agg.d.mts +1 -1
  3. package/dist/{buckets-C-27xmtq.d.cts → buckets-Bv4pah66.d.cts} +225 -22
  4. package/dist/{buckets-BFG2RYRW.d.mts → buckets-CjL7F-hp.d.mts} +225 -22
  5. package/dist/{column-diff-CgxgFKzx.cjs → column-diff-CfPNcP6e.cjs} +663 -239
  6. package/dist/{column-diff-BwOA5101.mjs → column-diff-CmFNXV8C.mjs} +638 -220
  7. package/dist/column-diff-DiBbXyLA.d.cts +211 -0
  8. package/dist/column-diff-n-k5KY0u.d.mts +211 -0
  9. package/dist/derived-rules-0sKn4f5C.mjs +44 -0
  10. package/dist/derived-rules-YstgIxG-.cjs +67 -0
  11. package/dist/index.cjs +38 -4
  12. package/dist/index.d.cts +23 -6
  13. package/dist/index.d.mts +23 -6
  14. package/dist/index.mjs +34 -4
  15. package/dist/{nested-writer-FWD5oOYh.mjs → nested-writer-BO3vhbkP.mjs} +8 -4
  16. package/dist/{nested-writer-BZNCuqI6.cjs → nested-writer-DYsRxZ5f.cjs} +8 -4
  17. package/dist/object-DSN0h9lB.d.cts +30 -0
  18. package/dist/object-DSN0h9lB.d.mts +30 -0
  19. package/dist/plugin.cjs +392 -139
  20. package/dist/plugin.mjs +392 -139
  21. package/dist/rel.cjs +2 -2
  22. package/dist/rel.d.cts +2 -2
  23. package/dist/rel.d.mts +2 -2
  24. package/dist/rel.mjs +2 -2
  25. package/dist/{relation-helpers-D3Zu0Mta.d.mts → relation-helpers-B59to_dG.d.mts} +5 -4
  26. package/dist/{relation-helpers-DxrvS6ar.d.cts → relation-helpers-DQ_nRsV9.d.cts} +5 -4
  27. package/dist/{relation-loader-6ZB_5KFq.cjs → relation-loader-CgJ8bK6X.cjs} +1 -1
  28. package/dist/{relation-loader-CTFaZpVa.mjs → relation-loader-CuhEBzFU.mjs} +1 -1
  29. package/dist/shared.cjs +6 -1
  30. package/dist/shared.d.cts +48 -9
  31. package/dist/shared.d.mts +48 -9
  32. package/dist/shared.mjs +2 -2
  33. package/dist/sync.cjs +331 -105
  34. package/dist/sync.d.cts +62 -163
  35. package/dist/sync.d.mts +62 -163
  36. package/dist/sync.mjs +331 -105
  37. package/dist/{validation-utils-B4h-GW4d.mjs → validation-utils-CMR4fe2M.mjs} +99 -34
  38. package/dist/{validation-utils-Dg0hW6dn.cjs → validation-utils-DOsB4e6G.cjs} +128 -33
  39. package/dist/{validator-Drb2N-YL.d.cts → validator-Bw6ks9Hy.d.cts} +1 -11
  40. package/dist/{validator-Drb2N-YL.d.mts → validator-Bw6ks9Hy.d.mts} +1 -11
  41. package/dist/{validator-Ch7UIQl9.mjs → validator-D8bPsXPN.mjs} +54 -2
  42. package/dist/{validator-BtZbcLN2.cjs → validator-DASnXf1j.cjs} +77 -1
  43. package/dist/validator.cjs +1 -1
  44. package/dist/validator.d.cts +2 -1
  45. package/dist/validator.d.mts +2 -1
  46. package/dist/validator.mjs +1 -1
  47. package/package.json +6 -6
  48. package/dist/column-diff-BmqvgBWw.d.cts +0 -24
  49. package/dist/column-diff-DPkbZIVE.d.mts +0 -24
@@ -0,0 +1,211 @@
1
+ import { Bt as AtscriptDbView, D as TColumnDiff, Gt as AtscriptQueryFieldRef, I as TDbDefaultValue, Kt as AtscriptQueryNode, R as TDbFieldMeta, X as TDbStorageType, at as TExistingColumn, rt as TDerivedColumn, st as TExistingTableOption, tn as AtscriptDbReadable } from "./buckets-Bv4pah66.cjs";
2
+
3
+ //#region src/schema/schema-hash.d.ts
4
+ interface TFieldSnapshot {
5
+ physicalName: string;
6
+ designType: string;
7
+ optional: boolean;
8
+ isPrimaryKey: boolean;
9
+ storage: TDbStorageType;
10
+ defaultValue?: TDbDefaultValue;
11
+ /** Adapter-specific mapped type (e.g., "VARCHAR(255)", "INTEGER"). */
12
+ mappedType?: string;
13
+ /** `@db.encrypted` — toggling encryption changes the snapshot hash on every adapter. */
14
+ encrypted?: boolean;
15
+ /**
16
+ * `@db.column.derived` — what the generated column reads (physical JSON
17
+ * column, path, leaf type). Present for derived fields only, so a table
18
+ * without one hashes exactly as before; the column diff compares it with
19
+ * the model to detect an expression change (engines normalize the stored
20
+ * expression text, so the snapshot is the baseline).
21
+ * @since 0.1.141
22
+ */
23
+ derived?: Omit<TDerivedColumn, "sourcePath">;
24
+ }
25
+ interface TIndexSnapshot {
26
+ key: string;
27
+ type: string;
28
+ fields: Array<{
29
+ name: string;
30
+ sort: string;
31
+ }>;
32
+ }
33
+ interface TForeignKeySnapshot {
34
+ fields: string[];
35
+ targetTable: string;
36
+ targetFields: string[];
37
+ onDelete?: string;
38
+ onUpdate?: string;
39
+ }
40
+ interface TTableSnapshot {
41
+ tableName: string;
42
+ fields: TFieldSnapshot[];
43
+ indexes: TIndexSnapshot[];
44
+ foreignKeys: TForeignKeySnapshot[];
45
+ /** Adapter-specific table-level options (e.g., MySQL engine/charset, MongoDB capped). */
46
+ tableOptions?: TExistingTableOption[];
47
+ }
48
+ /**
49
+ * One join of a managed view as stored in its snapshot.
50
+ * @since 0.1.128 — `joinTables` elements were bare target-table names before;
51
+ * the ON predicate is now part of the view definition.
52
+ */
53
+ interface TViewJoinSnapshot {
54
+ /**
55
+ * Scope name of the join: the physical table / view, or the `@db.alias`
56
+ * type name when the join is aliased (then {@link table} holds the physical name).
57
+ */
58
+ targetTable: string;
59
+ /**
60
+ * Physical table / view of an aliased join. Emitted only when the target
61
+ * is a `@db.alias` type — a plain join serializes exactly as before.
62
+ * @since 0.1.141
63
+ */
64
+ table?: string;
65
+ /** Canonical JSON of the join condition (see {@link canonicalizeQueryNode}). */
66
+ condition: string;
67
+ /** Emitted only for `"left"` — an inner join (the default) carries no key. @since 0.1.136 */
68
+ kind?: "inner" | "left";
69
+ }
70
+ /**
71
+ * One column of a managed view as stored in its snapshot — the PHYSICAL
72
+ * source it reads, so a source rename (`@db.column`, flattening, a moved
73
+ * JSON leaf) or an aggregate change recreates the view.
74
+ * @since 0.1.136
75
+ */
76
+ interface TViewColumnSnapshot {
77
+ column: string;
78
+ sourceTable: string;
79
+ sourceColumn: string;
80
+ /** JSON array of the path segments inside a JSON source column. */
81
+ jsonPath?: string;
82
+ jsonType?: string;
83
+ aggFn?: string;
84
+ aggField?: string;
85
+ /** Canonical JSON of a conditional aggregate's predicate. */
86
+ aggFilter?: string;
87
+ }
88
+ interface TViewSnapshot {
89
+ tableName: string;
90
+ viewType: "V" | "M" | "E";
91
+ entryTable?: string;
92
+ /**
93
+ * Joins in declaration order. The key keeps its historical name so a
94
+ * join-less view (`[]`) serializes byte-identically to older snapshots.
95
+ */
96
+ joinTables?: TViewJoinSnapshot[];
97
+ /** @since 0.1.136 — view columns and their physical sources, sorted by `column`. */
98
+ columns?: TViewColumnSnapshot[];
99
+ filterHash?: string;
100
+ /** @since 0.1.128 — hash of the canonical `@db.view.having` predicate. */
101
+ havingHash?: string;
102
+ materialized?: boolean;
103
+ /**
104
+ * @since 0.1.137 — the adapter's `viewRenderRevision()`, present only when
105
+ * the adapter defines one (managed views only).
106
+ */
107
+ renderRevision?: string;
108
+ fields: TFieldSnapshot[];
109
+ }
110
+ /**
111
+ * Extracts a canonical, serializable snapshot from a readable's metadata.
112
+ * Sorted deterministically so the hash is stable across runs.
113
+ *
114
+ * @param readable - The table/view readable.
115
+ * @param typeMapper - Optional adapter-specific type mapper. When provided,
116
+ * each field's mapped type (e.g., "VARCHAR(255)") is stored in the snapshot
117
+ * for precise type change detection.
118
+ */
119
+ declare function computeTableSnapshot(readable: AtscriptDbReadable, typeMapper?: (field: TDbFieldMeta) => string, tableOptions?: TExistingTableOption[]): TTableSnapshot;
120
+ /**
121
+ * Extracts a canonical, serializable snapshot from a view's metadata.
122
+ * Captures view plan (entry table, joins, filter, materialization) for
123
+ * detecting view definition changes.
124
+ */
125
+ declare function computeViewSnapshot(view: AtscriptDbView): TViewSnapshot;
126
+ /** Canonical (table-qualified, fixed-key-order) form of a view predicate. */
127
+ type TCanonicalQueryNode = {
128
+ and: TCanonicalQueryNode[];
129
+ } | {
130
+ or: TCanonicalQueryNode[];
131
+ } | {
132
+ not: TCanonicalQueryNode;
133
+ } /** `r` is `{ f: "<table>.<field>" }` for a field-to-field comparison, else the literal. */ | {
134
+ l: string;
135
+ op: string;
136
+ r?: unknown;
137
+ };
138
+ /**
139
+ * Converts a view predicate (join condition, `@db.view.filter`,
140
+ * `@db.view.having`) into a serializable structure whose JSON is a stable
141
+ * function of its MEANING: field refs become `qualify(ref)` — the view's
142
+ * `resolveFieldRef(ref, (n) => n)`, i.e. `"<table>.<field>"`, so a predicate
143
+ * retargeted to another table with the same field name changes — operators
144
+ * and literal values are kept as-is, `$and`/`$or` keep declaration order, and
145
+ * no function references survive. Two identical models produce byte-identical
146
+ * JSON.
147
+ * @since 0.1.128
148
+ */
149
+ declare function canonicalizeQueryNode(node: AtscriptQueryNode, qualify: (ref: AtscriptQueryFieldRef) => string): TCanonicalQueryNode;
150
+ /**
151
+ * Computes a deterministic hash string from multiple table snapshots.
152
+ * Uses FNV-1a for speed — not cryptographic, just needs stability + collision resistance.
153
+ */
154
+ declare function computeSchemaHash(snapshots: Array<TTableSnapshot | TViewSnapshot>): string;
155
+ /**
156
+ * Computes a hash for a single table/view snapshot.
157
+ * Used for per-table change detection via stored snapshots.
158
+ */
159
+ declare function computeTableHash(snapshot: TTableSnapshot | TViewSnapshot): string;
160
+ /**
161
+ * Converts stored snapshot fields to `TExistingColumn[]` format
162
+ * for use with `computeColumnDiff`. Used by adapters that lack
163
+ * native column introspection (e.g., MongoDB).
164
+ *
165
+ * The `type` field uses `mappedType` when available (adapter-specific),
166
+ * falling back to `designType`. Derived fields are left out (since 0.1.141):
167
+ * an adapter without column introspection stores no derived column, and a
168
+ * `physicalName` of one is its SOURCE path — reporting it as an existing
169
+ * column would have the diff drop the source leaf.
170
+ *
171
+ * With `readable`, the subfields of its navigation properties are left out
172
+ * too: a document adapter's snapshot lists them (for hash stability — see
173
+ * `snapshotFields`) although nothing is stored under a nav field — they are
174
+ * not columns to drop.
175
+ */
176
+ declare function snapshotToExistingColumns(snapshot: TTableSnapshot, readable?: AtscriptDbReadable): TExistingColumn[];
177
+ /**
178
+ * Extracts table options from a stored snapshot for diff comparison.
179
+ * Used as fallback when an adapter lacks native table option introspection.
180
+ */
181
+ declare function snapshotToExistingTableOptions(snapshot: TTableSnapshot): TExistingTableOption[];
182
+ //#endregion
183
+ //#region src/schema/column-diff.d.ts
184
+ /**
185
+ * Whether a live column's type differs from the type the adapter's
186
+ * `typeMapper` gives the field — the one rule schema sync diffs column types
187
+ * by (case-insensitive). Exported for adapters that must agree with it (the
188
+ * PostgreSQL recreate converts exactly the columns this reports changed).
189
+ * @since 0.1.137
190
+ */
191
+ declare function isColumnTypeChanged(existingType: string, expectedType: string): boolean;
192
+ /**
193
+ * Computes the difference between desired schema fields and existing database columns.
194
+ *
195
+ * @param desired - Field descriptors from the Atscript type (after flattening) —
196
+ * the readable's `columnDescriptors`; ignored descriptors are skipped.
197
+ * @param existing - Columns currently in the database (from introspection).
198
+ * @param typeMapper - Optional function to map field metadata to DB-native type strings.
199
+ * Receives the full field meta (design type, annotations, PK status, etc.)
200
+ * so adapters can produce context-aware types (e.g., `VARCHAR(255)` from maxLength).
201
+ * Required for type change detection.
202
+ * @param opts.snapshot - The table's stored snapshot (since 0.1.141) — the baseline a
203
+ * derived column's expression is compared with (the engine normalizes
204
+ * the expression it stores, so the live column cannot be). Without
205
+ * one, only a kind or type drift is seen.
206
+ */
207
+ declare function computeColumnDiff(desired: readonly TDbFieldMeta[], existing: TExistingColumn[], typeMapper?: (field: TDbFieldMeta) => string, opts?: {
208
+ snapshot?: TTableSnapshot | null;
209
+ }): TColumnDiff;
210
+ //#endregion
211
+ export { TTableSnapshot as a, TViewSnapshot as c, computeTableHash as d, computeTableSnapshot as f, snapshotToExistingTableOptions as h, TForeignKeySnapshot as i, canonicalizeQueryNode as l, snapshotToExistingColumns as m, isColumnTypeChanged as n, TViewColumnSnapshot as o, computeViewSnapshot as p, TFieldSnapshot as r, TViewJoinSnapshot as s, computeColumnDiff as t, computeSchemaHash as u };
@@ -0,0 +1,211 @@
1
+ import { Bt as AtscriptDbView, D as TColumnDiff, Gt as AtscriptQueryFieldRef, I as TDbDefaultValue, Kt as AtscriptQueryNode, R as TDbFieldMeta, X as TDbStorageType, at as TExistingColumn, rt as TDerivedColumn, st as TExistingTableOption, tn as AtscriptDbReadable } from "./buckets-CjL7F-hp.mjs";
2
+
3
+ //#region src/schema/schema-hash.d.ts
4
+ interface TFieldSnapshot {
5
+ physicalName: string;
6
+ designType: string;
7
+ optional: boolean;
8
+ isPrimaryKey: boolean;
9
+ storage: TDbStorageType;
10
+ defaultValue?: TDbDefaultValue;
11
+ /** Adapter-specific mapped type (e.g., "VARCHAR(255)", "INTEGER"). */
12
+ mappedType?: string;
13
+ /** `@db.encrypted` — toggling encryption changes the snapshot hash on every adapter. */
14
+ encrypted?: boolean;
15
+ /**
16
+ * `@db.column.derived` — what the generated column reads (physical JSON
17
+ * column, path, leaf type). Present for derived fields only, so a table
18
+ * without one hashes exactly as before; the column diff compares it with
19
+ * the model to detect an expression change (engines normalize the stored
20
+ * expression text, so the snapshot is the baseline).
21
+ * @since 0.1.141
22
+ */
23
+ derived?: Omit<TDerivedColumn, "sourcePath">;
24
+ }
25
+ interface TIndexSnapshot {
26
+ key: string;
27
+ type: string;
28
+ fields: Array<{
29
+ name: string;
30
+ sort: string;
31
+ }>;
32
+ }
33
+ interface TForeignKeySnapshot {
34
+ fields: string[];
35
+ targetTable: string;
36
+ targetFields: string[];
37
+ onDelete?: string;
38
+ onUpdate?: string;
39
+ }
40
+ interface TTableSnapshot {
41
+ tableName: string;
42
+ fields: TFieldSnapshot[];
43
+ indexes: TIndexSnapshot[];
44
+ foreignKeys: TForeignKeySnapshot[];
45
+ /** Adapter-specific table-level options (e.g., MySQL engine/charset, MongoDB capped). */
46
+ tableOptions?: TExistingTableOption[];
47
+ }
48
+ /**
49
+ * One join of a managed view as stored in its snapshot.
50
+ * @since 0.1.128 — `joinTables` elements were bare target-table names before;
51
+ * the ON predicate is now part of the view definition.
52
+ */
53
+ interface TViewJoinSnapshot {
54
+ /**
55
+ * Scope name of the join: the physical table / view, or the `@db.alias`
56
+ * type name when the join is aliased (then {@link table} holds the physical name).
57
+ */
58
+ targetTable: string;
59
+ /**
60
+ * Physical table / view of an aliased join. Emitted only when the target
61
+ * is a `@db.alias` type — a plain join serializes exactly as before.
62
+ * @since 0.1.141
63
+ */
64
+ table?: string;
65
+ /** Canonical JSON of the join condition (see {@link canonicalizeQueryNode}). */
66
+ condition: string;
67
+ /** Emitted only for `"left"` — an inner join (the default) carries no key. @since 0.1.136 */
68
+ kind?: "inner" | "left";
69
+ }
70
+ /**
71
+ * One column of a managed view as stored in its snapshot — the PHYSICAL
72
+ * source it reads, so a source rename (`@db.column`, flattening, a moved
73
+ * JSON leaf) or an aggregate change recreates the view.
74
+ * @since 0.1.136
75
+ */
76
+ interface TViewColumnSnapshot {
77
+ column: string;
78
+ sourceTable: string;
79
+ sourceColumn: string;
80
+ /** JSON array of the path segments inside a JSON source column. */
81
+ jsonPath?: string;
82
+ jsonType?: string;
83
+ aggFn?: string;
84
+ aggField?: string;
85
+ /** Canonical JSON of a conditional aggregate's predicate. */
86
+ aggFilter?: string;
87
+ }
88
+ interface TViewSnapshot {
89
+ tableName: string;
90
+ viewType: "V" | "M" | "E";
91
+ entryTable?: string;
92
+ /**
93
+ * Joins in declaration order. The key keeps its historical name so a
94
+ * join-less view (`[]`) serializes byte-identically to older snapshots.
95
+ */
96
+ joinTables?: TViewJoinSnapshot[];
97
+ /** @since 0.1.136 — view columns and their physical sources, sorted by `column`. */
98
+ columns?: TViewColumnSnapshot[];
99
+ filterHash?: string;
100
+ /** @since 0.1.128 — hash of the canonical `@db.view.having` predicate. */
101
+ havingHash?: string;
102
+ materialized?: boolean;
103
+ /**
104
+ * @since 0.1.137 — the adapter's `viewRenderRevision()`, present only when
105
+ * the adapter defines one (managed views only).
106
+ */
107
+ renderRevision?: string;
108
+ fields: TFieldSnapshot[];
109
+ }
110
+ /**
111
+ * Extracts a canonical, serializable snapshot from a readable's metadata.
112
+ * Sorted deterministically so the hash is stable across runs.
113
+ *
114
+ * @param readable - The table/view readable.
115
+ * @param typeMapper - Optional adapter-specific type mapper. When provided,
116
+ * each field's mapped type (e.g., "VARCHAR(255)") is stored in the snapshot
117
+ * for precise type change detection.
118
+ */
119
+ declare function computeTableSnapshot(readable: AtscriptDbReadable, typeMapper?: (field: TDbFieldMeta) => string, tableOptions?: TExistingTableOption[]): TTableSnapshot;
120
+ /**
121
+ * Extracts a canonical, serializable snapshot from a view's metadata.
122
+ * Captures view plan (entry table, joins, filter, materialization) for
123
+ * detecting view definition changes.
124
+ */
125
+ declare function computeViewSnapshot(view: AtscriptDbView): TViewSnapshot;
126
+ /** Canonical (table-qualified, fixed-key-order) form of a view predicate. */
127
+ type TCanonicalQueryNode = {
128
+ and: TCanonicalQueryNode[];
129
+ } | {
130
+ or: TCanonicalQueryNode[];
131
+ } | {
132
+ not: TCanonicalQueryNode;
133
+ } /** `r` is `{ f: "<table>.<field>" }` for a field-to-field comparison, else the literal. */ | {
134
+ l: string;
135
+ op: string;
136
+ r?: unknown;
137
+ };
138
+ /**
139
+ * Converts a view predicate (join condition, `@db.view.filter`,
140
+ * `@db.view.having`) into a serializable structure whose JSON is a stable
141
+ * function of its MEANING: field refs become `qualify(ref)` — the view's
142
+ * `resolveFieldRef(ref, (n) => n)`, i.e. `"<table>.<field>"`, so a predicate
143
+ * retargeted to another table with the same field name changes — operators
144
+ * and literal values are kept as-is, `$and`/`$or` keep declaration order, and
145
+ * no function references survive. Two identical models produce byte-identical
146
+ * JSON.
147
+ * @since 0.1.128
148
+ */
149
+ declare function canonicalizeQueryNode(node: AtscriptQueryNode, qualify: (ref: AtscriptQueryFieldRef) => string): TCanonicalQueryNode;
150
+ /**
151
+ * Computes a deterministic hash string from multiple table snapshots.
152
+ * Uses FNV-1a for speed — not cryptographic, just needs stability + collision resistance.
153
+ */
154
+ declare function computeSchemaHash(snapshots: Array<TTableSnapshot | TViewSnapshot>): string;
155
+ /**
156
+ * Computes a hash for a single table/view snapshot.
157
+ * Used for per-table change detection via stored snapshots.
158
+ */
159
+ declare function computeTableHash(snapshot: TTableSnapshot | TViewSnapshot): string;
160
+ /**
161
+ * Converts stored snapshot fields to `TExistingColumn[]` format
162
+ * for use with `computeColumnDiff`. Used by adapters that lack
163
+ * native column introspection (e.g., MongoDB).
164
+ *
165
+ * The `type` field uses `mappedType` when available (adapter-specific),
166
+ * falling back to `designType`. Derived fields are left out (since 0.1.141):
167
+ * an adapter without column introspection stores no derived column, and a
168
+ * `physicalName` of one is its SOURCE path — reporting it as an existing
169
+ * column would have the diff drop the source leaf.
170
+ *
171
+ * With `readable`, the subfields of its navigation properties are left out
172
+ * too: a document adapter's snapshot lists them (for hash stability — see
173
+ * `snapshotFields`) although nothing is stored under a nav field — they are
174
+ * not columns to drop.
175
+ */
176
+ declare function snapshotToExistingColumns(snapshot: TTableSnapshot, readable?: AtscriptDbReadable): TExistingColumn[];
177
+ /**
178
+ * Extracts table options from a stored snapshot for diff comparison.
179
+ * Used as fallback when an adapter lacks native table option introspection.
180
+ */
181
+ declare function snapshotToExistingTableOptions(snapshot: TTableSnapshot): TExistingTableOption[];
182
+ //#endregion
183
+ //#region src/schema/column-diff.d.ts
184
+ /**
185
+ * Whether a live column's type differs from the type the adapter's
186
+ * `typeMapper` gives the field — the one rule schema sync diffs column types
187
+ * by (case-insensitive). Exported for adapters that must agree with it (the
188
+ * PostgreSQL recreate converts exactly the columns this reports changed).
189
+ * @since 0.1.137
190
+ */
191
+ declare function isColumnTypeChanged(existingType: string, expectedType: string): boolean;
192
+ /**
193
+ * Computes the difference between desired schema fields and existing database columns.
194
+ *
195
+ * @param desired - Field descriptors from the Atscript type (after flattening) —
196
+ * the readable's `columnDescriptors`; ignored descriptors are skipped.
197
+ * @param existing - Columns currently in the database (from introspection).
198
+ * @param typeMapper - Optional function to map field metadata to DB-native type strings.
199
+ * Receives the full field meta (design type, annotations, PK status, etc.)
200
+ * so adapters can produce context-aware types (e.g., `VARCHAR(255)` from maxLength).
201
+ * Required for type change detection.
202
+ * @param opts.snapshot - The table's stored snapshot (since 0.1.141) — the baseline a
203
+ * derived column's expression is compared with (the engine normalizes
204
+ * the expression it stores, so the live column cannot be). Without
205
+ * one, only a kind or type drift is seen.
206
+ */
207
+ declare function computeColumnDiff(desired: readonly TDbFieldMeta[], existing: TExistingColumn[], typeMapper?: (field: TDbFieldMeta) => string, opts?: {
208
+ snapshot?: TTableSnapshot | null;
209
+ }): TColumnDiff;
210
+ //#endregion
211
+ export { TTableSnapshot as a, TViewSnapshot as c, computeTableHash as d, computeTableSnapshot as f, snapshotToExistingTableOptions as h, TForeignKeySnapshot as i, canonicalizeQueryNode as l, snapshotToExistingColumns as m, isColumnTypeChanged as n, TViewColumnSnapshot as o, computeViewSnapshot as p, TFieldSnapshot as r, TViewJoinSnapshot as s, computeColumnDiff as t, computeSchemaHash as u };
@@ -0,0 +1,44 @@
1
+ //#region src/shared/derived-rules.ts
2
+ /**
3
+ * The rules the compiler plugin and the runtime share about entities and
4
+ * `@db.column.derived` fields — one dependency-free list each, so a
5
+ * compile-time diagnostic and its runtime mirror can never disagree.
6
+ * @since 0.1.141
7
+ */
8
+ /** The annotations that make a declaration a DB entity: a table, or a managed / external view. */
9
+ const DB_ENTITY_ANNOTATIONS = [
10
+ "db.table",
11
+ "db.view",
12
+ "db.view.for"
13
+ ];
14
+ /** Primitive leaf types a JSON-stored path may end at (view JSON leaves, derived columns). */
15
+ const JSON_LEAF_TYPES = new Set([
16
+ "string",
17
+ "number",
18
+ "boolean"
19
+ ]);
20
+ /** Whether `type` (a resolved design type) is one of {@link JSON_LEAF_TYPES}. */
21
+ function isJsonLeafType(type) {
22
+ return JSON_LEAF_TYPES.has(type);
23
+ }
24
+ /** Annotations a `@db.column.derived` field cannot carry (D8), with the reason. */
25
+ const DERIVED_INCOMPATIBLE = [
26
+ ["meta.id", "a computed column cannot identify the row"],
27
+ ["db.rel.FK", "a foreign key needs a stored, writable column"],
28
+ ["db.default", "the value is computed, never defaulted"],
29
+ ["db.default.increment", "the value is computed, never defaulted"],
30
+ ["db.default.uuid", "the value is computed, never defaulted"],
31
+ ["db.default.now", "the value is computed, never defaulted"],
32
+ ["db.column.version", "the version column is adapter-managed"],
33
+ ["db.encrypted", "the value is a cleartext extraction of its source"],
34
+ ["db.json", "a derived column is a primitive leaf, not a JSON value"],
35
+ ["db.ignore", "an ignored field has no column to derive"],
36
+ ["db.writeOnly", "a derived column is never written"],
37
+ ["db.index.fulltext", "fulltext indexes need a stored text column"],
38
+ ["db.index.geo", "a geo index needs a db.geoPoint column"],
39
+ ["db.search.vector", "a vector index needs a stored embedding column"],
40
+ ["db.mongo.search.text", "Atlas Search indexes the stored document, which holds no derived field"],
41
+ ["db.mongo.search.autocomplete", "Atlas Search indexes the stored document, which holds no derived field"]
42
+ ];
43
+ //#endregion
44
+ export { isJsonLeafType as i, DERIVED_INCOMPATIBLE as n, JSON_LEAF_TYPES as r, DB_ENTITY_ANNOTATIONS as t };
@@ -0,0 +1,67 @@
1
+ //#region src/shared/derived-rules.ts
2
+ /**
3
+ * The rules the compiler plugin and the runtime share about entities and
4
+ * `@db.column.derived` fields — one dependency-free list each, so a
5
+ * compile-time diagnostic and its runtime mirror can never disagree.
6
+ * @since 0.1.141
7
+ */
8
+ /** The annotations that make a declaration a DB entity: a table, or a managed / external view. */
9
+ const DB_ENTITY_ANNOTATIONS = [
10
+ "db.table",
11
+ "db.view",
12
+ "db.view.for"
13
+ ];
14
+ /** Primitive leaf types a JSON-stored path may end at (view JSON leaves, derived columns). */
15
+ const JSON_LEAF_TYPES = new Set([
16
+ "string",
17
+ "number",
18
+ "boolean"
19
+ ]);
20
+ /** Whether `type` (a resolved design type) is one of {@link JSON_LEAF_TYPES}. */
21
+ function isJsonLeafType(type) {
22
+ return JSON_LEAF_TYPES.has(type);
23
+ }
24
+ /** Annotations a `@db.column.derived` field cannot carry (D8), with the reason. */
25
+ const DERIVED_INCOMPATIBLE = [
26
+ ["meta.id", "a computed column cannot identify the row"],
27
+ ["db.rel.FK", "a foreign key needs a stored, writable column"],
28
+ ["db.default", "the value is computed, never defaulted"],
29
+ ["db.default.increment", "the value is computed, never defaulted"],
30
+ ["db.default.uuid", "the value is computed, never defaulted"],
31
+ ["db.default.now", "the value is computed, never defaulted"],
32
+ ["db.column.version", "the version column is adapter-managed"],
33
+ ["db.encrypted", "the value is a cleartext extraction of its source"],
34
+ ["db.json", "a derived column is a primitive leaf, not a JSON value"],
35
+ ["db.ignore", "an ignored field has no column to derive"],
36
+ ["db.writeOnly", "a derived column is never written"],
37
+ ["db.index.fulltext", "fulltext indexes need a stored text column"],
38
+ ["db.index.geo", "a geo index needs a db.geoPoint column"],
39
+ ["db.search.vector", "a vector index needs a stored embedding column"],
40
+ ["db.mongo.search.text", "Atlas Search indexes the stored document, which holds no derived field"],
41
+ ["db.mongo.search.autocomplete", "Atlas Search indexes the stored document, which holds no derived field"]
42
+ ];
43
+ //#endregion
44
+ Object.defineProperty(exports, "DB_ENTITY_ANNOTATIONS", {
45
+ enumerable: true,
46
+ get: function() {
47
+ return DB_ENTITY_ANNOTATIONS;
48
+ }
49
+ });
50
+ Object.defineProperty(exports, "DERIVED_INCOMPATIBLE", {
51
+ enumerable: true,
52
+ get: function() {
53
+ return DERIVED_INCOMPATIBLE;
54
+ }
55
+ });
56
+ Object.defineProperty(exports, "JSON_LEAF_TYPES", {
57
+ enumerable: true,
58
+ get: function() {
59
+ return JSON_LEAF_TYPES;
60
+ }
61
+ });
62
+ Object.defineProperty(exports, "isJsonLeafType", {
63
+ enumerable: true,
64
+ get: function() {
65
+ return isJsonLeafType;
66
+ }
67
+ });
package/dist/index.cjs CHANGED
@@ -1,11 +1,13 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_db_error = require("./db-error-DTkkeu5b.cjs");
3
- const require_column_diff = require("./column-diff-CgxgFKzx.cjs");
3
+ const require_column_diff = require("./column-diff-CfPNcP6e.cjs");
4
4
  const require_aggregate_fns = require("./aggregate-fns-CGBv3E8S.cjs");
5
- const require_nested_writer = require("./nested-writer-BZNCuqI6.cjs");
6
- const require_validator = require("./validator-BtZbcLN2.cjs");
5
+ const require_nested_writer = require("./nested-writer-DYsRxZ5f.cjs");
6
+ const require_derived_rules = require("./derived-rules-YstgIxG-.cjs");
7
+ const require_validator = require("./validator-DASnXf1j.cjs");
7
8
  const require_consts = require("./consts-BzRfCcH2.cjs");
8
9
  const require_ops = require("./ops.cjs");
10
+ let _atscript_typescript_utils = require("@atscript/typescript/utils");
9
11
  let _uniqu_core = require("@uniqu/core");
10
12
  let node_crypto = require("node:crypto");
11
13
  //#region src/encryption.ts
@@ -203,6 +205,14 @@ async function withOptimisticRetry(table, filter, mutator, opts) {
203
205
  //#endregion
204
206
  //#region src/table/db-space.ts
205
207
  /**
208
+ * A `@db.alias` type only names a join scope inside a view definition — it
209
+ * is never a table or view of its own (since 0.1.141).
210
+ */
211
+ function assertNotAlias(type) {
212
+ const target = require_column_diff.aliasTargetOf(type);
213
+ if (target) throw new Error(`"${type.id ?? ""}" is a @db.alias of "${target.id ?? ""}" — a join scope, not a table or view; register "${target.id ?? ""}" instead`);
214
+ }
215
+ /**
206
216
  * A database space — a registry of tables and views sharing the same adapter type and driver.
207
217
  *
208
218
  * `DbSpace` solves the cross-table discovery problem: when table A has a relation
@@ -259,6 +269,7 @@ var DbSpace = class {
259
269
  getTable(type, logger) {
260
270
  let readable = this._readables.get(type);
261
271
  if (!readable) {
272
+ assertNotAlias(type);
262
273
  readable = new require_column_diff.AtscriptDbTable(type, this._createAdapter(), logger || this.logger, (t) => this.get(t), (t) => {
263
274
  const resolved = this.get(t);
264
275
  return resolved instanceof require_column_diff.AtscriptDbTable ? resolved : void 0;
@@ -278,6 +289,7 @@ var DbSpace = class {
278
289
  getView(type, logger) {
279
290
  let readable = this._readables.get(type);
280
291
  if (!readable) {
292
+ assertNotAlias(type);
281
293
  readable = new require_column_diff.AtscriptDbView(type, this._createAdapter(), logger || this.logger, (t) => this.get(t));
282
294
  readable.setEncryption(this._encryption);
283
295
  this._readables.set(type, readable);
@@ -392,6 +404,24 @@ function translateQueryTree(node, resolveField) {
392
404
  return { [leftField]: { [comp.op]: comp.right } };
393
405
  }
394
406
  //#endregion
407
+ //#region src/table/db-entity.ts
408
+ /**
409
+ * Whether `value` is a compiled type a `DbSpace` accepts: an annotated type
410
+ * whose own declaration carries `@db.table`, `@db.view` or `@db.view.for`.
411
+ * A `@db.alias` type is not one, and neither is a plain `export type X = Table`
412
+ * or a field typed with a table (`customer: Customer`) — since 0.1.141 the
413
+ * entity annotations stay on the declaring interface instead of travelling
414
+ * with every reference.
415
+ *
416
+ * Filters a module namespace before a sync:
417
+ * `syncSchema(db, Object.values(models).filter(isDbEntityType))`.
418
+ * @since 0.1.141
419
+ */
420
+ function isDbEntityType(value) {
421
+ if (!(0, _atscript_typescript_utils.isAnnotatedType)(value)) return false;
422
+ return require_derived_rules.DB_ENTITY_ANNOTATIONS.some((name) => value.metadata.has(name));
423
+ }
424
+ //#endregion
395
425
  exports.$cas = require_ops.$cas;
396
426
  exports.$dec = require_ops.$dec;
397
427
  exports.$inc = require_ops.$inc;
@@ -425,6 +455,7 @@ exports.RelationalFieldMapper = require_column_diff.RelationalFieldMapper;
425
455
  exports.TableMetadata = require_column_diff.TableMetadata;
426
456
  exports.UniquSelect = require_column_diff.UniquSelect;
427
457
  exports.acceptedOperatorsHint = require_column_diff.acceptedOperatorsHint;
458
+ exports.aliasTargetOf = require_column_diff.aliasTargetOf;
428
459
  exports.assertGeoPoint = require_column_diff.assertGeoPoint;
429
460
  exports.assertNoVersionWrites = require_column_diff.assertNoVersionWrites;
430
461
  exports.bucketSourceVerdict = require_column_diff.bucketSourceVerdict;
@@ -444,10 +475,12 @@ Object.defineProperty(exports, "computeInsights", {
444
475
  exports.createDbValidatorPlugin = require_validator.createDbValidatorPlugin;
445
476
  exports.createFailureCollector = require_column_diff.createFailureCollector;
446
477
  exports.decomposePatch = require_column_diff.decomposePatch;
447
- exports.findAncestorInSet = require_column_diff.findAncestorInSet;
478
+ exports.deletePath = require_validator.deletePath;
479
+ exports.findAncestorInSet = require_validator.findAncestorInSet;
448
480
  exports.forceNavNonOptional = require_validator.forceNavNonOptional;
449
481
  exports.getDbFieldOp = require_ops.getDbFieldOp;
450
482
  exports.getKeyProps = require_validator.getKeyProps;
483
+ exports.getPath = require_validator.getPath;
451
484
  exports.guardAggregate = require_column_diff.guardAggregate;
452
485
  exports.guardFilter = require_column_diff.guardFilter;
453
486
  exports.guardPath = require_column_diff.guardPath;
@@ -456,6 +489,7 @@ exports.guardQuery = require_column_diff.guardQuery;
456
489
  exports.isAtscriptDbView = require_column_diff.isAtscriptDbView;
457
490
  exports.isBucketableField = require_column_diff.isBucketableField;
458
491
  exports.isColumnTypeChanged = require_column_diff.isColumnTypeChanged;
492
+ exports.isDbEntityType = isDbEntityType;
459
493
  exports.isDbFieldOp = require_ops.isDbFieldOp;
460
494
  exports.isEmptyObject = require_validator.isEmptyObject;
461
495
  exports.isFieldRef = isFieldRef;