@atscript/db 0.1.39 → 0.1.41

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 (64) hide show
  1. package/README.md +18 -18
  2. package/dist/agg.cjs +8 -3
  3. package/dist/agg.d.cts +7 -0
  4. package/dist/agg.d.mts +7 -0
  5. package/dist/agg.mjs +7 -3
  6. package/dist/control-DRgryKeg.cjs +14 -0
  7. package/dist/{control_as-bjmwe24C.mjs → control-IANbnfjG.mjs} +6 -18
  8. package/dist/db-readable-BQQzfguJ.d.cts +1249 -0
  9. package/dist/db-readable-Bbr4CjMb.d.mts +1249 -0
  10. package/dist/db-space-BUrQ5BFm.d.mts +309 -0
  11. package/dist/db-space-Vxpcnyt5.d.cts +309 -0
  12. package/dist/db-validator-plugin-07kDiis2.d.cts +22 -0
  13. package/dist/db-validator-plugin-CiqsHTI_.d.mts +22 -0
  14. package/dist/db-view-CMI9TOo1.cjs +3096 -0
  15. package/dist/db-view-Esy2fDxw.mjs +2995 -0
  16. package/dist/index.cjs +95 -2801
  17. package/dist/index.d.cts +137 -0
  18. package/dist/index.d.mts +137 -0
  19. package/dist/index.mjs +55 -2761
  20. package/dist/{nested-writer-BkqL7cp3.cjs → nested-writer-BDXsDMPP.cjs} +196 -150
  21. package/dist/{nested-writer-NEN51mnR.mjs → nested-writer-Dmm1gbZV.mjs} +118 -70
  22. package/dist/ops-BdRAFLKY.d.mts +67 -0
  23. package/dist/ops-DXJ4Zw0P.d.cts +67 -0
  24. package/dist/ops.cjs +123 -0
  25. package/dist/ops.d.cts +2 -0
  26. package/dist/ops.d.mts +2 -0
  27. package/dist/ops.mjs +112 -0
  28. package/dist/plugin.cjs +90 -109
  29. package/dist/plugin.d.cts +6 -0
  30. package/dist/plugin.d.mts +6 -0
  31. package/dist/plugin.mjs +29 -49
  32. package/dist/rel.cjs +20 -20
  33. package/dist/rel.d.cts +119 -0
  34. package/dist/rel.d.mts +119 -0
  35. package/dist/rel.mjs +4 -5
  36. package/dist/{relation-helpers-guFL_oRf.cjs → relation-helpers-BYvsE1tR.cjs} +26 -22
  37. package/dist/{relation-helpers-DyBIlQnB.mjs → relation-helpers-CLasawQq.mjs} +11 -6
  38. package/dist/{relation-loader-Dv7qXYq7.mjs → relation-loader-BEOTXNcq.mjs} +63 -43
  39. package/dist/{relation-loader-CpnDRf9k.cjs → relation-loader-CRC5LcqM.cjs} +74 -49
  40. package/dist/shared.cjs +13 -13
  41. package/dist/{shared.d.ts → shared.d.cts} +14 -13
  42. package/dist/shared.d.mts +71 -0
  43. package/dist/shared.mjs +2 -3
  44. package/dist/sync.cjs +300 -252
  45. package/dist/sync.d.cts +369 -0
  46. package/dist/sync.d.mts +369 -0
  47. package/dist/sync.mjs +284 -233
  48. package/dist/{validation-utils-DEoCMmEb.cjs → validation-utils-DVJDijnB.cjs} +141 -109
  49. package/dist/{validation-utils-DhR_mtKa.mjs → validation-utils-DhjIjP1-.mjs} +71 -37
  50. package/package.json +30 -29
  51. package/LICENSE +0 -21
  52. package/dist/agg-BJFJ3dFQ.mjs +0 -8
  53. package/dist/agg-DnUWAOK8.cjs +0 -14
  54. package/dist/agg.d.ts +0 -13
  55. package/dist/chunk-CrpGerW8.cjs +0 -31
  56. package/dist/control_as-BFPERAF_.cjs +0 -28
  57. package/dist/index.d.ts +0 -1706
  58. package/dist/logger-B7oxCfLQ.mjs +0 -12
  59. package/dist/logger-Dt2v_-wb.cjs +0 -18
  60. package/dist/plugin.d.ts +0 -5
  61. package/dist/rel.d.ts +0 -1305
  62. package/dist/relation-loader-D4mTw6yH.cjs +0 -4
  63. package/dist/relation-loader-Ggy1ujwR.mjs +0 -4
  64. package/dist/sync.d.ts +0 -1878
@@ -1,33 +1,40 @@
1
- "use strict";
2
- const require_chunk = require('./chunk-CrpGerW8.cjs');
3
- const __atscript_core = require_chunk.__toESM(require("@atscript/core"));
4
-
5
- //#region packages/db/src/shared/annotation-utils.ts
1
+ let _atscript_core = require("@atscript/core");
2
+ //#region src/shared/annotation-utils.ts
3
+ /**
4
+ * Traverse from annotation token → prop → structure → interface
5
+ * to check if the parent interface has @db.table.
6
+ */
6
7
  function getDbTableOwner(token) {
7
- const field = token.parentNode;
8
- const struct = field.ownerNode;
9
- if (!struct || !(0, __atscript_core.isStructure)(struct)) return undefined;
8
+ const struct = token.parentNode.ownerNode;
9
+ if (!struct || !(0, _atscript_core.isStructure)(struct)) return;
10
10
  const iface = struct.ownerNode;
11
- return iface && (0, __atscript_core.isInterface)(iface) ? iface : struct;
11
+ return iface && (0, _atscript_core.isInterface)(iface) ? iface : struct;
12
12
  }
13
+ /**
14
+ * Get the parent structure node from an annotation token.
15
+ */
13
16
  function getParentStruct(token) {
14
- const field = token.parentNode;
15
- const struct = field.ownerNode;
16
- return struct && (0, __atscript_core.isStructure)(struct) ? struct : undefined;
17
+ const struct = token.parentNode.ownerNode;
18
+ return struct && (0, _atscript_core.isStructure)(struct) ? struct : void 0;
17
19
  }
20
+ /**
21
+ * Get the parent interface name (for error messages and cross-type resolution).
22
+ */
18
23
  function getParentTypeName(token) {
19
24
  const struct = getParentStruct(token);
20
- if (!struct) return undefined;
25
+ if (!struct) return;
21
26
  const iface = struct.ownerNode;
22
- return iface && (0, __atscript_core.isInterface)(iface) ? iface.id : struct.id;
27
+ return iface && (0, _atscript_core.isInterface)(iface) ? iface.id : struct.id;
23
28
  }
29
+ /**
30
+ * Validate that an annotation is on a field with the expected base type.
31
+ */
24
32
  function validateFieldBaseType(token, doc, annotationName, expectedType) {
25
33
  const errors = [];
26
- const field = token.parentNode;
27
- const definition = field.getDefinition();
28
- if (!definition || !(0, __atscript_core.isRef)(definition)) return errors;
34
+ const definition = token.parentNode.getDefinition();
35
+ if (!definition || !(0, _atscript_core.isRef)(definition)) return errors;
29
36
  const unwound = doc.unwindType(definition.id, definition.chain);
30
- if (!unwound || !(0, __atscript_core.isPrimitive)(unwound.def)) return errors;
37
+ if (!unwound || !(0, _atscript_core.isPrimitive)(unwound.def)) return errors;
31
38
  const ct = unwound.def.config.type;
32
39
  const baseType = typeof ct === "object" ? ct.kind === "final" ? ct.value : ct.kind : ct;
33
40
  const allowed = Array.isArray(expectedType) ? expectedType : [expectedType];
@@ -38,20 +45,35 @@ function validateFieldBaseType(token, doc, annotationName, expectedType) {
38
45
  });
39
46
  return errors;
40
47
  }
48
+ /**
49
+ * Extract target type name from a navigational field definition.
50
+ * Unwraps arrays (e.g., `Post[]` → `Post`).
51
+ */
41
52
  function getNavTargetTypeName(field) {
42
53
  let def = field.getDefinition();
43
- if ((0, __atscript_core.isArray)(def)) def = def?.getDefinition();
44
- if ((0, __atscript_core.isRef)(def)) return def.id;
45
- return undefined;
54
+ if ((0, _atscript_core.isArray)(def)) def = def?.getDefinition();
55
+ if ((0, _atscript_core.isRef)(def)) return def.id;
46
56
  }
57
+ /**
58
+ * Get the alias argument from an annotation on a field.
59
+ */
47
60
  function getAnnotationAlias(prop, annotationName) {
48
61
  const annotations = prop.annotations?.filter((a) => a.name === annotationName);
49
- if (!annotations || annotations.length === 0) return undefined;
50
- return annotations[0].args.length > 0 ? annotations[0].args[0].text : undefined;
62
+ if (!annotations || annotations.length === 0) return;
63
+ return annotations[0].args.length > 0 ? annotations[0].args[0].text : void 0;
51
64
  }
65
+ /**
66
+ * Factory for @db.rel.onDelete / @db.rel.onUpdate — identical validation logic,
67
+ * only the annotation name and description verb differ.
68
+ */
52
69
  function refActionAnnotation(name) {
53
- return new __atscript_core.AnnotationSpec({
54
- description: `Referential action when the target ${name === "onDelete" ? "row is deleted" : "key is updated"}. Only valid on @db.rel.FK fields.\n\n` + "**Example:**\n" + "```atscript\n" + "@db.rel.FK\n" + `@db.rel.${name} "cascade"\n` + "authorId: User.id\n" + "```\n",
70
+ return new _atscript_core.AnnotationSpec({
71
+ description: `Referential action when the target ${name === "onDelete" ? "row is deleted" : "key is updated"}. Only valid on @db.rel.FK fields.\n\n**Example:**
72
+ \`\`\`atscript
73
+ @db.rel.FK
74
+ @db.rel.${name} "cascade"\nauthorId: User.id
75
+ \`\`\`
76
+ `,
55
77
  nodeType: ["prop"],
56
78
  argument: {
57
79
  name: "action",
@@ -65,7 +87,7 @@ function refActionAnnotation(name) {
65
87
  ],
66
88
  description: "Referential action: \"cascade\", \"restrict\", \"noAction\", \"setNull\", or \"setDefault\"."
67
89
  },
68
- validate(token, args, doc) {
90
+ validate(token, args, _doc) {
69
91
  const errors = [];
70
92
  const field = token.parentNode;
71
93
  if (field.countAnnotations("db.rel.FK") === 0) errors.push({
@@ -95,8 +117,7 @@ function refActionAnnotation(name) {
95
117
  for (const [, prop] of struct.props) {
96
118
  if (prop.countAnnotations("db.rel.FK") === 0) continue;
97
119
  if (prop.countAnnotations(annotationName) === 0) continue;
98
- const propFkAlias = getAnnotationAlias(prop, "db.rel.FK");
99
- if (propFkAlias === fkAlias) count++;
120
+ if (getAnnotationAlias(prop, "db.rel.FK") === fkAlias) count++;
100
121
  }
101
122
  if (count > 1) errors.push({
102
123
  message: `Composite FK '${fkAlias}' has @db.rel.${name} on multiple fields — declare it on exactly one`,
@@ -109,17 +130,18 @@ function refActionAnnotation(name) {
109
130
  }
110
131
  });
111
132
  }
112
-
113
133
  //#endregion
114
- //#region packages/db/src/shared/validation-utils.ts
134
+ //#region src/shared/validation-utils.ts
135
+ /**
136
+ * Validate a ref annotation argument against the document's type registry.
137
+ * Returns diagnostic messages for unknown types or fields.
138
+ */
115
139
  function validateRefArgument(token, doc, options) {
116
140
  const messages = [];
117
- const text = token.text;
118
- const [typeName, ...chain] = text.split(".");
141
+ const [typeName, ...chain] = token.text.split(".");
119
142
  const decl = doc.getDeclarationOwnerNode(typeName);
120
143
  if (!decl) {
121
- const regDef = doc.registry.definitions.get(typeName);
122
- if (regDef?.imported) return messages;
144
+ if (doc.registry.definitions.get(typeName)?.imported) return messages;
123
145
  messages.push({
124
146
  severity: 1,
125
147
  message: `Unknown type '${typeName}'.`,
@@ -128,8 +150,7 @@ function validateRefArgument(token, doc, options) {
128
150
  return messages;
129
151
  }
130
152
  if (chain.length > 0) {
131
- const unwound = doc.unwindType(typeName, chain);
132
- if (!unwound) {
153
+ if (!doc.unwindType(typeName, chain)) {
133
154
  messages.push({
134
155
  severity: 1,
135
156
  message: `Field '${chain.join(".")}' does not exist on type '${typeName}'.`,
@@ -139,8 +160,7 @@ function validateRefArgument(token, doc, options) {
139
160
  }
140
161
  }
141
162
  if (options?.requireDbTable && decl.node) {
142
- const hasDbTable = decl.node.countAnnotations("db.table") > 0;
143
- if (!hasDbTable) messages.push({
163
+ if (!(decl.node.countAnnotations("db.table") > 0)) messages.push({
144
164
  severity: 1,
145
165
  message: `Type '${typeName}' must have @db.table annotation.`,
146
166
  range: token.range
@@ -148,28 +168,30 @@ function validateRefArgument(token, doc, options) {
148
168
  }
149
169
  return messages;
150
170
  }
171
+ /**
172
+ * Find all `@db.rel.FK` fields on a type that reference `targetTypeName`.
173
+ * Resolves `extends` to include inherited fields.
174
+ */
151
175
  function findFKFieldsPointingTo(doc, iface, targetTypeName, alias) {
152
176
  const results = [];
153
177
  let struct;
154
- if ((0, __atscript_core.isInterface)(iface) && iface.hasExtends) {
178
+ if ((0, _atscript_core.isInterface)(iface) && iface.hasExtends) {
155
179
  const resolved = doc.resolveInterfaceExtends(iface);
156
- if (resolved && (0, __atscript_core.isStructure)(resolved)) struct = resolved;
180
+ if (resolved && (0, _atscript_core.isStructure)(resolved)) struct = resolved;
157
181
  }
158
- if (!struct) struct = (0, __atscript_core.isStructure)(iface) ? iface : (0, __atscript_core.isInterface)(iface) && (0, __atscript_core.isStructure)(iface.getDefinition()) ? iface.getDefinition() : undefined;
182
+ if (!struct) struct = (0, _atscript_core.isStructure)(iface) ? iface : (0, _atscript_core.isInterface)(iface) && (0, _atscript_core.isStructure)(iface.getDefinition()) ? iface.getDefinition() : void 0;
159
183
  if (!struct) return results;
160
184
  for (const [name, prop] of struct.props) {
161
185
  if (prop.countAnnotations("db.rel.FK") === 0) continue;
162
186
  const def = prop.getDefinition();
163
- if (!def || !(0, __atscript_core.isRef)(def)) continue;
187
+ if (!def || !(0, _atscript_core.isRef)(def)) continue;
164
188
  const ref = def;
165
189
  if (!ref.hasChain) continue;
166
190
  const refTypeName = ref.id;
167
191
  const refField = ref.chain.map((c) => c.text).join(".");
168
192
  if (refTypeName !== targetTypeName) continue;
169
- if (alias !== undefined) {
170
- const fkAnnotations = prop.annotations?.filter((a) => a.name === "db.rel.FK");
171
- const hasMatchingAlias = fkAnnotations?.some((a) => a.args.length > 0 && a.args[0].text === alias);
172
- if (!hasMatchingAlias) continue;
193
+ if (alias !== void 0) {
194
+ if (!(prop.annotations?.filter((a) => a.name === "db.rel.FK"))?.some((a) => a.args.length > 0 && a.args[0].text === alias)) continue;
173
195
  }
174
196
  results.push({
175
197
  name,
@@ -189,9 +211,20 @@ const viewAnnotationNames = [
189
211
  "db.view.filter",
190
212
  "db.view.materialized"
191
213
  ];
214
+ /**
215
+ * Check if a node has any @db.view.* annotation.
216
+ */
192
217
  function hasAnyViewAnnotation(node) {
193
218
  return viewAnnotationNames.some((name) => node.countAnnotations(name) > 0);
194
219
  }
220
+ /**
221
+ * Validate that all type refs in a query expression are within the allowed scope.
222
+ *
223
+ * @param queryToken - The query arg token (must have .queryNode)
224
+ * @param allowedTypes - Type names allowed as qualified refs
225
+ * @param unqualifiedTarget - Type name for resolving unqualified refs, or null to disallow them
226
+ * @param doc - The document for type lookups
227
+ */
195
228
  function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc) {
196
229
  const errors = [];
197
230
  const queryNode = queryToken.queryNode;
@@ -209,13 +242,13 @@ function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc) {
209
242
  severity: 1,
210
243
  range: ref.fieldRef.range
211
244
  });
212
- else {
245
+ else {
213
246
  const unwound = doc.unwindType(unqualifiedTarget);
214
247
  if (unwound) {
215
248
  const targetDef = unwound.def;
216
- if ((0, __atscript_core.isInterface)(targetDef) || (0, __atscript_core.isStructure)(targetDef)) {
217
- const struct = (0, __atscript_core.isInterface)(targetDef) ? targetDef.getDefinition() : targetDef;
218
- if (struct && (0, __atscript_core.isStructure)(struct) && !struct.props.has(ref.fieldRef.text)) errors.push({
249
+ if ((0, _atscript_core.isInterface)(targetDef) || (0, _atscript_core.isStructure)(targetDef)) {
250
+ const struct = (0, _atscript_core.isInterface)(targetDef) ? targetDef.getDefinition() : targetDef;
251
+ if (struct && (0, _atscript_core.isStructure)(struct) && !struct.props.has(ref.fieldRef.text)) errors.push({
219
252
  message: `Field '${ref.fieldRef.text}' does not exist on '${unqualifiedTarget}'`,
220
253
  severity: 1,
221
254
  range: ref.fieldRef.range
@@ -225,8 +258,8 @@ else {
225
258
  }
226
259
  }
227
260
  function walkExpr(expr) {
228
- if ((0, __atscript_core.isQueryLogical)(expr)) for (const operand of expr.operands) walkExpr(operand);
229
- else if ((0, __atscript_core.isQueryComparison)(expr)) {
261
+ if ((0, _atscript_core.isQueryLogical)(expr)) for (const operand of expr.operands) walkExpr(operand);
262
+ else if ((0, _atscript_core.isQueryComparison)(expr)) {
230
263
  walkFieldRef(expr.left);
231
264
  if (expr.right && "fieldRef" in expr.right) walkFieldRef(expr.right);
232
265
  }
@@ -234,71 +267,70 @@ else if ((0, __atscript_core.isQueryComparison)(expr)) {
234
267
  walkExpr(queryNode.expression);
235
268
  return errors;
236
269
  }
237
-
238
270
  //#endregion
239
- Object.defineProperty(exports, 'findFKFieldsPointingTo', {
240
- enumerable: true,
241
- get: function () {
242
- return findFKFieldsPointingTo;
243
- }
271
+ Object.defineProperty(exports, "findFKFieldsPointingTo", {
272
+ enumerable: true,
273
+ get: function() {
274
+ return findFKFieldsPointingTo;
275
+ }
276
+ });
277
+ Object.defineProperty(exports, "getAnnotationAlias", {
278
+ enumerable: true,
279
+ get: function() {
280
+ return getAnnotationAlias;
281
+ }
244
282
  });
245
- Object.defineProperty(exports, 'getAnnotationAlias', {
246
- enumerable: true,
247
- get: function () {
248
- return getAnnotationAlias;
249
- }
283
+ Object.defineProperty(exports, "getDbTableOwner", {
284
+ enumerable: true,
285
+ get: function() {
286
+ return getDbTableOwner;
287
+ }
250
288
  });
251
- Object.defineProperty(exports, 'getDbTableOwner', {
252
- enumerable: true,
253
- get: function () {
254
- return getDbTableOwner;
255
- }
289
+ Object.defineProperty(exports, "getNavTargetTypeName", {
290
+ enumerable: true,
291
+ get: function() {
292
+ return getNavTargetTypeName;
293
+ }
256
294
  });
257
- Object.defineProperty(exports, 'getNavTargetTypeName', {
258
- enumerable: true,
259
- get: function () {
260
- return getNavTargetTypeName;
261
- }
295
+ Object.defineProperty(exports, "getParentStruct", {
296
+ enumerable: true,
297
+ get: function() {
298
+ return getParentStruct;
299
+ }
262
300
  });
263
- Object.defineProperty(exports, 'getParentStruct', {
264
- enumerable: true,
265
- get: function () {
266
- return getParentStruct;
267
- }
301
+ Object.defineProperty(exports, "getParentTypeName", {
302
+ enumerable: true,
303
+ get: function() {
304
+ return getParentTypeName;
305
+ }
268
306
  });
269
- Object.defineProperty(exports, 'getParentTypeName', {
270
- enumerable: true,
271
- get: function () {
272
- return getParentTypeName;
273
- }
307
+ Object.defineProperty(exports, "hasAnyViewAnnotation", {
308
+ enumerable: true,
309
+ get: function() {
310
+ return hasAnyViewAnnotation;
311
+ }
274
312
  });
275
- Object.defineProperty(exports, 'hasAnyViewAnnotation', {
276
- enumerable: true,
277
- get: function () {
278
- return hasAnyViewAnnotation;
279
- }
313
+ Object.defineProperty(exports, "refActionAnnotation", {
314
+ enumerable: true,
315
+ get: function() {
316
+ return refActionAnnotation;
317
+ }
280
318
  });
281
- Object.defineProperty(exports, 'refActionAnnotation', {
282
- enumerable: true,
283
- get: function () {
284
- return refActionAnnotation;
285
- }
319
+ Object.defineProperty(exports, "validateFieldBaseType", {
320
+ enumerable: true,
321
+ get: function() {
322
+ return validateFieldBaseType;
323
+ }
286
324
  });
287
- Object.defineProperty(exports, 'validateFieldBaseType', {
288
- enumerable: true,
289
- get: function () {
290
- return validateFieldBaseType;
291
- }
325
+ Object.defineProperty(exports, "validateQueryScope", {
326
+ enumerable: true,
327
+ get: function() {
328
+ return validateQueryScope;
329
+ }
292
330
  });
293
- Object.defineProperty(exports, 'validateQueryScope', {
294
- enumerable: true,
295
- get: function () {
296
- return validateQueryScope;
297
- }
331
+ Object.defineProperty(exports, "validateRefArgument", {
332
+ enumerable: true,
333
+ get: function() {
334
+ return validateRefArgument;
335
+ }
298
336
  });
299
- Object.defineProperty(exports, 'validateRefArgument', {
300
- enumerable: true,
301
- get: function () {
302
- return validateRefArgument;
303
- }
304
- });
@@ -1,28 +1,37 @@
1
1
  import { AnnotationSpec, isArray, isInterface, isPrimitive, isQueryComparison, isQueryLogical, isRef, isStructure } from "@atscript/core";
2
-
3
- //#region packages/db/src/shared/annotation-utils.ts
2
+ //#region src/shared/annotation-utils.ts
3
+ /**
4
+ * Traverse from annotation token → prop → structure → interface
5
+ * to check if the parent interface has @db.table.
6
+ */
4
7
  function getDbTableOwner(token) {
5
- const field = token.parentNode;
6
- const struct = field.ownerNode;
7
- if (!struct || !isStructure(struct)) return undefined;
8
+ const struct = token.parentNode.ownerNode;
9
+ if (!struct || !isStructure(struct)) return;
8
10
  const iface = struct.ownerNode;
9
11
  return iface && isInterface(iface) ? iface : struct;
10
12
  }
13
+ /**
14
+ * Get the parent structure node from an annotation token.
15
+ */
11
16
  function getParentStruct(token) {
12
- const field = token.parentNode;
13
- const struct = field.ownerNode;
14
- return struct && isStructure(struct) ? struct : undefined;
17
+ const struct = token.parentNode.ownerNode;
18
+ return struct && isStructure(struct) ? struct : void 0;
15
19
  }
20
+ /**
21
+ * Get the parent interface name (for error messages and cross-type resolution).
22
+ */
16
23
  function getParentTypeName(token) {
17
24
  const struct = getParentStruct(token);
18
- if (!struct) return undefined;
25
+ if (!struct) return;
19
26
  const iface = struct.ownerNode;
20
27
  return iface && isInterface(iface) ? iface.id : struct.id;
21
28
  }
29
+ /**
30
+ * Validate that an annotation is on a field with the expected base type.
31
+ */
22
32
  function validateFieldBaseType(token, doc, annotationName, expectedType) {
23
33
  const errors = [];
24
- const field = token.parentNode;
25
- const definition = field.getDefinition();
34
+ const definition = token.parentNode.getDefinition();
26
35
  if (!definition || !isRef(definition)) return errors;
27
36
  const unwound = doc.unwindType(definition.id, definition.chain);
28
37
  if (!unwound || !isPrimitive(unwound.def)) return errors;
@@ -36,20 +45,35 @@ function validateFieldBaseType(token, doc, annotationName, expectedType) {
36
45
  });
37
46
  return errors;
38
47
  }
48
+ /**
49
+ * Extract target type name from a navigational field definition.
50
+ * Unwraps arrays (e.g., `Post[]` → `Post`).
51
+ */
39
52
  function getNavTargetTypeName(field) {
40
53
  let def = field.getDefinition();
41
54
  if (isArray(def)) def = def?.getDefinition();
42
55
  if (isRef(def)) return def.id;
43
- return undefined;
44
56
  }
57
+ /**
58
+ * Get the alias argument from an annotation on a field.
59
+ */
45
60
  function getAnnotationAlias(prop, annotationName) {
46
61
  const annotations = prop.annotations?.filter((a) => a.name === annotationName);
47
- if (!annotations || annotations.length === 0) return undefined;
48
- return annotations[0].args.length > 0 ? annotations[0].args[0].text : undefined;
62
+ if (!annotations || annotations.length === 0) return;
63
+ return annotations[0].args.length > 0 ? annotations[0].args[0].text : void 0;
49
64
  }
65
+ /**
66
+ * Factory for @db.rel.onDelete / @db.rel.onUpdate — identical validation logic,
67
+ * only the annotation name and description verb differ.
68
+ */
50
69
  function refActionAnnotation(name) {
51
70
  return new AnnotationSpec({
52
- description: `Referential action when the target ${name === "onDelete" ? "row is deleted" : "key is updated"}. Only valid on @db.rel.FK fields.\n\n` + "**Example:**\n" + "```atscript\n" + "@db.rel.FK\n" + `@db.rel.${name} "cascade"\n` + "authorId: User.id\n" + "```\n",
71
+ description: `Referential action when the target ${name === "onDelete" ? "row is deleted" : "key is updated"}. Only valid on @db.rel.FK fields.\n\n**Example:**
72
+ \`\`\`atscript
73
+ @db.rel.FK
74
+ @db.rel.${name} "cascade"\nauthorId: User.id
75
+ \`\`\`
76
+ `,
53
77
  nodeType: ["prop"],
54
78
  argument: {
55
79
  name: "action",
@@ -63,7 +87,7 @@ function refActionAnnotation(name) {
63
87
  ],
64
88
  description: "Referential action: \"cascade\", \"restrict\", \"noAction\", \"setNull\", or \"setDefault\"."
65
89
  },
66
- validate(token, args, doc) {
90
+ validate(token, args, _doc) {
67
91
  const errors = [];
68
92
  const field = token.parentNode;
69
93
  if (field.countAnnotations("db.rel.FK") === 0) errors.push({
@@ -93,8 +117,7 @@ function refActionAnnotation(name) {
93
117
  for (const [, prop] of struct.props) {
94
118
  if (prop.countAnnotations("db.rel.FK") === 0) continue;
95
119
  if (prop.countAnnotations(annotationName) === 0) continue;
96
- const propFkAlias = getAnnotationAlias(prop, "db.rel.FK");
97
- if (propFkAlias === fkAlias) count++;
120
+ if (getAnnotationAlias(prop, "db.rel.FK") === fkAlias) count++;
98
121
  }
99
122
  if (count > 1) errors.push({
100
123
  message: `Composite FK '${fkAlias}' has @db.rel.${name} on multiple fields — declare it on exactly one`,
@@ -107,17 +130,18 @@ function refActionAnnotation(name) {
107
130
  }
108
131
  });
109
132
  }
110
-
111
133
  //#endregion
112
- //#region packages/db/src/shared/validation-utils.ts
134
+ //#region src/shared/validation-utils.ts
135
+ /**
136
+ * Validate a ref annotation argument against the document's type registry.
137
+ * Returns diagnostic messages for unknown types or fields.
138
+ */
113
139
  function validateRefArgument(token, doc, options) {
114
140
  const messages = [];
115
- const text = token.text;
116
- const [typeName, ...chain] = text.split(".");
141
+ const [typeName, ...chain] = token.text.split(".");
117
142
  const decl = doc.getDeclarationOwnerNode(typeName);
118
143
  if (!decl) {
119
- const regDef = doc.registry.definitions.get(typeName);
120
- if (regDef?.imported) return messages;
144
+ if (doc.registry.definitions.get(typeName)?.imported) return messages;
121
145
  messages.push({
122
146
  severity: 1,
123
147
  message: `Unknown type '${typeName}'.`,
@@ -126,8 +150,7 @@ function validateRefArgument(token, doc, options) {
126
150
  return messages;
127
151
  }
128
152
  if (chain.length > 0) {
129
- const unwound = doc.unwindType(typeName, chain);
130
- if (!unwound) {
153
+ if (!doc.unwindType(typeName, chain)) {
131
154
  messages.push({
132
155
  severity: 1,
133
156
  message: `Field '${chain.join(".")}' does not exist on type '${typeName}'.`,
@@ -137,8 +160,7 @@ function validateRefArgument(token, doc, options) {
137
160
  }
138
161
  }
139
162
  if (options?.requireDbTable && decl.node) {
140
- const hasDbTable = decl.node.countAnnotations("db.table") > 0;
141
- if (!hasDbTable) messages.push({
163
+ if (!(decl.node.countAnnotations("db.table") > 0)) messages.push({
142
164
  severity: 1,
143
165
  message: `Type '${typeName}' must have @db.table annotation.`,
144
166
  range: token.range
@@ -146,6 +168,10 @@ function validateRefArgument(token, doc, options) {
146
168
  }
147
169
  return messages;
148
170
  }
171
+ /**
172
+ * Find all `@db.rel.FK` fields on a type that reference `targetTypeName`.
173
+ * Resolves `extends` to include inherited fields.
174
+ */
149
175
  function findFKFieldsPointingTo(doc, iface, targetTypeName, alias) {
150
176
  const results = [];
151
177
  let struct;
@@ -153,7 +179,7 @@ function findFKFieldsPointingTo(doc, iface, targetTypeName, alias) {
153
179
  const resolved = doc.resolveInterfaceExtends(iface);
154
180
  if (resolved && isStructure(resolved)) struct = resolved;
155
181
  }
156
- if (!struct) struct = isStructure(iface) ? iface : isInterface(iface) && isStructure(iface.getDefinition()) ? iface.getDefinition() : undefined;
182
+ if (!struct) struct = isStructure(iface) ? iface : isInterface(iface) && isStructure(iface.getDefinition()) ? iface.getDefinition() : void 0;
157
183
  if (!struct) return results;
158
184
  for (const [name, prop] of struct.props) {
159
185
  if (prop.countAnnotations("db.rel.FK") === 0) continue;
@@ -164,10 +190,8 @@ function findFKFieldsPointingTo(doc, iface, targetTypeName, alias) {
164
190
  const refTypeName = ref.id;
165
191
  const refField = ref.chain.map((c) => c.text).join(".");
166
192
  if (refTypeName !== targetTypeName) continue;
167
- if (alias !== undefined) {
168
- const fkAnnotations = prop.annotations?.filter((a) => a.name === "db.rel.FK");
169
- const hasMatchingAlias = fkAnnotations?.some((a) => a.args.length > 0 && a.args[0].text === alias);
170
- if (!hasMatchingAlias) continue;
193
+ if (alias !== void 0) {
194
+ if (!(prop.annotations?.filter((a) => a.name === "db.rel.FK"))?.some((a) => a.args.length > 0 && a.args[0].text === alias)) continue;
171
195
  }
172
196
  results.push({
173
197
  name,
@@ -187,9 +211,20 @@ const viewAnnotationNames = [
187
211
  "db.view.filter",
188
212
  "db.view.materialized"
189
213
  ];
214
+ /**
215
+ * Check if a node has any @db.view.* annotation.
216
+ */
190
217
  function hasAnyViewAnnotation(node) {
191
218
  return viewAnnotationNames.some((name) => node.countAnnotations(name) > 0);
192
219
  }
220
+ /**
221
+ * Validate that all type refs in a query expression are within the allowed scope.
222
+ *
223
+ * @param queryToken - The query arg token (must have .queryNode)
224
+ * @param allowedTypes - Type names allowed as qualified refs
225
+ * @param unqualifiedTarget - Type name for resolving unqualified refs, or null to disallow them
226
+ * @param doc - The document for type lookups
227
+ */
193
228
  function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc) {
194
229
  const errors = [];
195
230
  const queryNode = queryToken.queryNode;
@@ -207,7 +242,7 @@ function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc) {
207
242
  severity: 1,
208
243
  range: ref.fieldRef.range
209
244
  });
210
- else {
245
+ else {
211
246
  const unwound = doc.unwindType(unqualifiedTarget);
212
247
  if (unwound) {
213
248
  const targetDef = unwound.def;
@@ -224,7 +259,7 @@ else {
224
259
  }
225
260
  function walkExpr(expr) {
226
261
  if (isQueryLogical(expr)) for (const operand of expr.operands) walkExpr(operand);
227
- else if (isQueryComparison(expr)) {
262
+ else if (isQueryComparison(expr)) {
228
263
  walkFieldRef(expr.left);
229
264
  if (expr.right && "fieldRef" in expr.right) walkFieldRef(expr.right);
230
265
  }
@@ -232,6 +267,5 @@ else if (isQueryComparison(expr)) {
232
267
  walkExpr(queryNode.expression);
233
268
  return errors;
234
269
  }
235
-
236
270
  //#endregion
237
- export { findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateFieldBaseType, validateQueryScope, validateRefArgument };
271
+ export { getAnnotationAlias as a, getParentStruct as c, validateFieldBaseType as d, validateRefArgument as i, getParentTypeName as l, hasAnyViewAnnotation as n, getDbTableOwner as o, validateQueryScope as r, getNavTargetTypeName as s, findFKFieldsPointingTo as t, refActionAnnotation as u };