@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.
- package/dist/{agg-DKuf_v2L.d.cts → agg-CV7y8nC6.d.cts} +13 -3
- package/dist/{agg-BtlGeRfj.d.mts → agg-D5DHsAby.d.mts} +13 -3
- package/dist/agg.cjs +35 -3
- package/dist/agg.d.cts +2 -2
- package/dist/agg.d.mts +2 -2
- package/dist/agg.mjs +34 -1
- package/dist/aggregate-fns-CGBv3E8S.cjs +87 -0
- package/dist/aggregate-fns-CfsveE1w.mjs +58 -0
- package/dist/{buckets-GruoVxH5.d.mts → buckets-BFG2RYRW.d.mts} +662 -31
- package/dist/{buckets-D6PBXKRJ.d.cts → buckets-C-27xmtq.d.cts} +662 -31
- package/dist/column-diff-BmqvgBWw.d.cts +24 -0
- package/dist/{db-view-Doh8vPg3.mjs → column-diff-BwOA5101.mjs} +795 -125
- package/dist/{db-view-Dm7MpUGB.cjs → column-diff-CgxgFKzx.cjs} +873 -125
- package/dist/column-diff-DPkbZIVE.d.mts +24 -0
- package/dist/index.cjs +79 -55
- package/dist/index.d.cts +12 -10
- package/dist/index.d.mts +12 -10
- package/dist/index.mjs +30 -11
- package/dist/{nested-writer-wk1EFUNY.cjs → nested-writer-BZNCuqI6.cjs} +16 -2
- package/dist/{nested-writer-CqL24ojl.mjs → nested-writer-FWD5oOYh.mjs} +11 -3
- package/dist/plugin.cjs +217 -77
- package/dist/plugin.mjs +217 -78
- package/dist/rel.cjs +2 -2
- package/dist/rel.d.cts +2 -20
- package/dist/rel.d.mts +2 -20
- package/dist/rel.mjs +2 -2
- package/dist/relation-helpers-D3Zu0Mta.d.mts +30 -0
- package/dist/relation-helpers-DxrvS6ar.d.cts +30 -0
- package/dist/{relation-loader-BD4xANQJ.cjs → relation-loader-6ZB_5KFq.cjs} +1 -1
- package/dist/{relation-loader-B68R1LET.mjs → relation-loader-CTFaZpVa.mjs} +1 -1
- package/dist/shared.cjs +5 -1
- package/dist/shared.d.cts +16 -3
- package/dist/shared.d.mts +16 -3
- package/dist/shared.mjs +2 -2
- package/dist/sync.cjs +38 -321
- package/dist/sync.d.cts +29 -17
- package/dist/sync.d.mts +29 -17
- package/dist/sync.mjs +6 -289
- package/dist/{validation-utils-MWOP1Ts4.mjs → validation-utils-B4h-GW4d.mjs} +29 -7
- package/dist/{validation-utils-B7SXPkm7.cjs → validation-utils-Dg0hW6dn.cjs} +52 -6
- package/dist/{validator-CewfnGZj.d.mts → validator-Drb2N-YL.d.cts} +1 -1
- package/dist/{validator-CewfnGZj.d.cts → validator-Drb2N-YL.d.mts} +1 -1
- package/dist/validator.d.cts +1 -1
- package/dist/validator.d.mts +1 -1
- package/package.json +1 -1
- package/dist/agg-CvXDGnKi.mjs +0 -61
- package/dist/agg-EcIbAFnJ.cjs +0 -78
- package/dist/db-space-9CH5neN7.d.mts +0 -439
- package/dist/db-space-BdtBNTeH.d.cts +0 -439
package/dist/plugin.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { i as SUPPORTED_AGGREGATE_FNS, r as NULL_WHEN_EMPTY_AGGREGATE_FNS } from "./aggregate-fns-CfsveE1w.mjs";
|
|
1
2
|
import "./consts-C_-5_pFq.mjs";
|
|
2
|
-
import { a as
|
|
3
|
+
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";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { AnnotationSpec, DEFAULT_FORMAT, isArray, isInterface, isPrimitive, isRef, isStructure } from "@atscript/core";
|
|
6
|
+
import { AnnotationSpec, DEFAULT_FORMAT, isArray, isInterface, isPrimitive, isProp, isRef, isStructure } from "@atscript/core";
|
|
6
7
|
//#region src/plugin/manifest.ts
|
|
7
8
|
const DB_ENTITY_ANNOTATIONS = [
|
|
8
9
|
"db.table",
|
|
@@ -95,67 +96,98 @@ async function generateModelManifest(options, output, format, repo) {
|
|
|
95
96
|
}
|
|
96
97
|
//#endregion
|
|
97
98
|
//#region src/plugin/annotations/agg.ts
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
99
|
+
/**
|
|
100
|
+
* A conditional aggregate that is NULL when no row matches (so its field
|
|
101
|
+
* must be optional): every NULL-when-empty function but `sum`, whose
|
|
102
|
+
* conditional form is `COALESCE(…, 0)`.
|
|
103
|
+
*/
|
|
104
|
+
function conditionalIsNullable(name) {
|
|
105
|
+
return name !== "sum" && NULL_WHEN_EMPTY_AGGREGATE_FNS.has(name);
|
|
106
|
+
}
|
|
107
|
+
/** The optional 2nd argument every `@db.agg.*` takes. */
|
|
108
|
+
const CONDITION_ARG = {
|
|
109
|
+
name: "condition",
|
|
110
|
+
type: "query",
|
|
111
|
+
optional: true,
|
|
112
|
+
description: "Row predicate of a conditional aggregate: only rows where it holds are aggregated (SQL `FN(CASE WHEN … THEN field END)`). May reference the entry table and every join; unqualified fields resolve to the entry table."
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* The rules every `@db.agg.*` shares: `'*'` is `count`'s only, and a
|
|
116
|
+
* condition (2nd argument) must stay within the view's tables (entry + every
|
|
117
|
+
* join, like `@db.view.filter`) and — for the aggregates that are NULL when
|
|
118
|
+
* no row matches (avg / min / max) — sit on an optional field.
|
|
119
|
+
*/
|
|
120
|
+
function validateAggArgs(name, token, args, doc) {
|
|
121
|
+
const errors = [];
|
|
122
|
+
const annotation = `@db.agg.${name}`;
|
|
123
|
+
if (args[0]?.text === "*" && name !== "count") errors.push({
|
|
124
|
+
message: `${annotation} needs a field — only @db.agg.count accepts '*'`,
|
|
125
|
+
severity: 1,
|
|
126
|
+
range: args[0].range
|
|
127
|
+
});
|
|
128
|
+
const condition = args[1];
|
|
129
|
+
if (!condition?.queryNode) return errors;
|
|
130
|
+
const owner = getDbTableOwner(token);
|
|
131
|
+
const entryTypeName = owner ? getAnnotationAlias(owner, "db.view.for") : void 0;
|
|
132
|
+
if (!owner || !entryTypeName) {
|
|
133
|
+
errors.push({
|
|
134
|
+
message: `A conditional ${annotation} requires @db.view.for on the view`,
|
|
135
|
+
severity: 1,
|
|
136
|
+
range: condition.range
|
|
137
|
+
});
|
|
138
|
+
return errors;
|
|
139
|
+
}
|
|
140
|
+
errors.push(...validateQueryScope(condition, viewScopeTypes(owner), entryTypeName, doc));
|
|
141
|
+
const prop = token.parentNode;
|
|
142
|
+
if (conditionalIsNullable(name) && prop && !prop.has("optional")) {
|
|
143
|
+
const field = prop.id ?? "field";
|
|
144
|
+
errors.push({
|
|
145
|
+
message: `Field "${field}" has a conditional ${annotation} and must be optional (${field}?: …) — it is NULL when no row matches`,
|
|
146
|
+
severity: 1,
|
|
147
|
+
range: token.range
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return errors;
|
|
151
|
+
}
|
|
152
|
+
const CONDITIONAL = " An optional 2nd argument (a query) makes it conditional: only rows where it holds are aggregated.";
|
|
153
|
+
/** What a conditional `name` yields when no row matches (from `NULL_WHEN_EMPTY_AGGREGATE_FNS`). */
|
|
154
|
+
function conditionalNote(name) {
|
|
155
|
+
if (!NULL_WHEN_EMPTY_AGGREGATE_FNS.has(name)) return "";
|
|
156
|
+
const fn = name.toUpperCase();
|
|
157
|
+
return conditionalIsNullable(name) ? ` A conditional ${fn} is NULL when no row matches, so its field must be optional.` : ` A conditional ${fn} is 0 (not NULL) when no row matches.`;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* One `@db.agg.<name>` spec: `[field, condition?]` (field optional for count
|
|
161
|
+
* only). The description gets the conditional-form sentences appended.
|
|
162
|
+
*/
|
|
163
|
+
function aggSpec(name, description, field, types, extra = "") {
|
|
164
|
+
return new AnnotationSpec({
|
|
165
|
+
description: description + CONDITIONAL + conditionalNote(name) + extra,
|
|
151
166
|
nodeType: ["prop"],
|
|
152
167
|
passedWhenReferred: false,
|
|
153
|
-
argument: {
|
|
168
|
+
argument: [{
|
|
154
169
|
name: "field",
|
|
155
170
|
type: "string",
|
|
156
|
-
|
|
171
|
+
optional: field.optional,
|
|
172
|
+
description: field.description
|
|
173
|
+
}, CONDITION_ARG],
|
|
174
|
+
validate(token, args, doc) {
|
|
175
|
+
const errors = validateAggArgs(name, token, args, doc);
|
|
176
|
+
if (types) errors.push(...validateFieldBaseType(token, doc, `@db.agg.${name}`, types));
|
|
177
|
+
return errors;
|
|
157
178
|
}
|
|
158
|
-
})
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
const dbAggAnnotations = { agg: {
|
|
182
|
+
sum: aggSpec("sum", "Declares a view field as SUM of a source column.", { description: "Source column name to sum." }, ["number", "decimal"]),
|
|
183
|
+
avg: aggSpec("avg", "Declares a view field as AVG of a source column.", { description: "Source column name to average." }, ["number", "decimal"]),
|
|
184
|
+
count: aggSpec("count", "Declares a view field as COUNT. Without argument (or with `'*'`): COUNT(*). With field name argument: COUNT(field) (non-null count).", {
|
|
185
|
+
optional: true,
|
|
186
|
+
description: "Source column name to count non-null values. Omit (or `'*'`) for COUNT(*)."
|
|
187
|
+
}, ["number"], " A conditional COUNT(*) is spelled `@db.agg.count '*', <query>`."),
|
|
188
|
+
countDistinct: aggSpec("countDistinct", "Declares a view field as COUNT(DISTINCT field): the number of distinct non-null values of a source column.", { description: "Source column name whose distinct non-null values are counted." }, ["number"]),
|
|
189
|
+
min: aggSpec("min", "Declares a view field as MIN of a source column.", { description: "Source column name." }),
|
|
190
|
+
max: aggSpec("max", "Declares a view field as MAX of a source column.", { description: "Source column name." })
|
|
159
191
|
} };
|
|
160
192
|
//#endregion
|
|
161
193
|
//#region src/plugin/annotations/amount.ts
|
|
@@ -245,7 +277,7 @@ const dbColumnAnnotations = {
|
|
|
245
277
|
}) },
|
|
246
278
|
column: {
|
|
247
279
|
$self: new AnnotationSpec({
|
|
248
|
-
description: "Overrides the physical column name in the database. For nested (flattened) fields, the parent prefix is still prepended automatically.\n\n**Example:**\n```atscript\n@db.column \"first_name\"\nfirstName: string\n// → physical column: first_name\n\n// Nested:\naddress: {\n @db.column \"zip_code\"\n zip: string\n}\n// → physical column: address__zip_code\n```\n",
|
|
280
|
+
description: "Overrides the physical column name in the database. For nested (flattened) fields, the parent prefix is still prepended automatically. Document storage (MongoDB) renames top-level fields only — a nested field keeps its name there.\n\n**Example:**\n```atscript\n@db.column \"first_name\"\nfirstName: string\n// → physical column: first_name\n\n// Nested:\naddress: {\n @db.column \"zip_code\"\n zip: string\n}\n// → physical column: address__zip_code\n```\n",
|
|
249
281
|
nodeType: ["prop"],
|
|
250
282
|
passedWhenReferred: false,
|
|
251
283
|
argument: {
|
|
@@ -255,7 +287,7 @@ const dbColumnAnnotations = {
|
|
|
255
287
|
}
|
|
256
288
|
}),
|
|
257
289
|
renamed: new AnnotationSpec({
|
|
258
|
-
description: "Specifies the previous local field name for column rename migration. The sync engine generates ALTER TABLE RENAME COLUMN instead of drop+add.\n\n**Example:**\n```atscript\n@db.column.renamed \"zip\"\npostalCode: string\n// Renames address__zip → address__postalCode\n```\n",
|
|
290
|
+
description: "Specifies the previous local field name for column rename migration. The sync engine generates ALTER TABLE RENAME COLUMN instead of drop+add (on document storage, top-level fields only).\n\n**Example:**\n```atscript\n@db.column.renamed \"zip\"\npostalCode: string\n// Renames address__zip → address__postalCode\n```\n",
|
|
259
291
|
nodeType: ["prop"],
|
|
260
292
|
passedWhenReferred: false,
|
|
261
293
|
argument: {
|
|
@@ -1261,6 +1293,86 @@ const dbUnitAnnotations = { unit: {
|
|
|
1261
1293
|
})
|
|
1262
1294
|
} };
|
|
1263
1295
|
//#endregion
|
|
1296
|
+
//#region src/shared/view-validation.ts
|
|
1297
|
+
/** `@db.agg.*` annotations that are never NULL (the counts: 0 over no value). */
|
|
1298
|
+
const NULL_SAFE_AGG_ANNOTATIONS = SUPPORTED_AGGREGATE_FNS.filter((fn) => !NULL_WHEN_EMPTY_AGGREGATE_FNS.has(fn)).map((fn) => `db.agg.${fn}`);
|
|
1299
|
+
/** Primitive leaf types a JSON-stored path may end at. */
|
|
1300
|
+
const JSON_LEAF_TYPES = new Set([
|
|
1301
|
+
"string",
|
|
1302
|
+
"number",
|
|
1303
|
+
"boolean"
|
|
1304
|
+
]);
|
|
1305
|
+
/** Props of a view interface (its own structure; views don't use `extends`). */
|
|
1306
|
+
function viewProps(owner) {
|
|
1307
|
+
if (isInterface(owner)) return owner.props;
|
|
1308
|
+
const def = owner.getDefinition();
|
|
1309
|
+
return def && isStructure(def) ? def.props : void 0;
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* VW8: a chain ref that passes through a `@db.json` or array node reads a
|
|
1313
|
+
* JSON leaf, which views can only extract as a string / number / boolean.
|
|
1314
|
+
* Intermediate nodes that `unwindType` can't reach are skipped (sync-time
|
|
1315
|
+
* resolution reports them).
|
|
1316
|
+
*/
|
|
1317
|
+
function validateJsonChain(fieldName, ref, doc, range) {
|
|
1318
|
+
const typeName = ref.id;
|
|
1319
|
+
const chain = ref.chain.map((t) => t.text);
|
|
1320
|
+
if (!typeName || chain.length < 2) return [];
|
|
1321
|
+
let throughJson = false;
|
|
1322
|
+
for (let i = 1; i < chain.length; i++) {
|
|
1323
|
+
const step = doc.unwindType(typeName, chain.slice(0, i));
|
|
1324
|
+
if (!step) return [];
|
|
1325
|
+
const stepNode = step.node;
|
|
1326
|
+
if (stepNode && isProp(stepNode) && stepNode.countAnnotations("db.json") > 0 || isArray(step.def)) {
|
|
1327
|
+
throughJson = true;
|
|
1328
|
+
break;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
if (!throughJson) return [];
|
|
1332
|
+
const leaf = doc.unwindType(typeName, chain)?.def;
|
|
1333
|
+
const leafType = primitiveBaseType(leaf);
|
|
1334
|
+
if (leafType !== void 0 && JSON_LEAF_TYPES.has(leafType)) return [];
|
|
1335
|
+
return [{
|
|
1336
|
+
message: `Field "${fieldName}" reads "${typeName}.${chain.join(".")}" inside a JSON-stored field — it must end at a string, number or boolean leaf`,
|
|
1337
|
+
severity: 1,
|
|
1338
|
+
range
|
|
1339
|
+
}];
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Whole-view checks, run once per `@db.view.for` interface:
|
|
1343
|
+
*
|
|
1344
|
+
* - VW7 — a field reading from a left-joined table must be optional (the
|
|
1345
|
+
* join yields NULL for unmatched rows); `@db.agg.count` / `countDistinct`
|
|
1346
|
+
* fields are exempt (they count 0, never NULL).
|
|
1347
|
+
* - VW8 — a chain ref through a `@db.json` or array node must end at a
|
|
1348
|
+
* primitive string / number / boolean leaf.
|
|
1349
|
+
* @since 0.1.136
|
|
1350
|
+
*/
|
|
1351
|
+
function validateViewInterface(owner, doc) {
|
|
1352
|
+
const errors = [];
|
|
1353
|
+
const props = viewProps(owner);
|
|
1354
|
+
if (!props) return errors;
|
|
1355
|
+
const leftJoined = /* @__PURE__ */ new Set();
|
|
1356
|
+
for (const join of viewJoins(owner)) {
|
|
1357
|
+
const target = join.args[0]?.text;
|
|
1358
|
+
if (target && join.args[2]?.text === "left") leftJoined.add(target);
|
|
1359
|
+
}
|
|
1360
|
+
for (const [fieldName, prop] of props) {
|
|
1361
|
+
const def = prop.getDefinition();
|
|
1362
|
+
if (!def || !isRef(def)) continue;
|
|
1363
|
+
const ref = def;
|
|
1364
|
+
const range = (prop.token("identifier") ?? ref.token("identifier"))?.range;
|
|
1365
|
+
if (!range) continue;
|
|
1366
|
+
if (ref.id && leftJoined.has(ref.id) && !prop.has("optional") && !NULL_SAFE_AGG_ANNOTATIONS.some((name) => prop.countAnnotations(name) > 0)) errors.push({
|
|
1367
|
+
message: `Field "${fieldName}" reads from left-joined "${ref.id}" and must be optional (${fieldName}?: …)`,
|
|
1368
|
+
severity: 1,
|
|
1369
|
+
range
|
|
1370
|
+
});
|
|
1371
|
+
errors.push(...validateJsonChain(fieldName, ref, doc, range));
|
|
1372
|
+
}
|
|
1373
|
+
return errors;
|
|
1374
|
+
}
|
|
1375
|
+
//#endregion
|
|
1264
1376
|
//#region src/plugin/annotations/view.ts
|
|
1265
1377
|
const dbViewAnnotations = { view: {
|
|
1266
1378
|
$self: new AnnotationSpec({
|
|
@@ -1292,29 +1404,41 @@ const dbViewAnnotations = { view: {
|
|
|
1292
1404
|
},
|
|
1293
1405
|
validate(token, args, doc) {
|
|
1294
1406
|
const errors = [];
|
|
1295
|
-
|
|
1407
|
+
const owner = token.parentNode;
|
|
1408
|
+
if (owner.countAnnotations("db.table") > 0) errors.push({
|
|
1296
1409
|
message: "An interface cannot be both a @db.table and a @db.view",
|
|
1297
1410
|
severity: 1,
|
|
1298
1411
|
range: token.range
|
|
1299
1412
|
});
|
|
1300
1413
|
if (args[0]) errors.push(...validateRefArgument(args[0], doc, { requireDbTable: true }));
|
|
1414
|
+
errors.push(...validateViewInterface(owner, doc));
|
|
1301
1415
|
return errors;
|
|
1302
1416
|
}
|
|
1303
1417
|
}),
|
|
1304
1418
|
joins: new AnnotationSpec({
|
|
1305
|
-
description: "Declares an explicit join for a view.
|
|
1419
|
+
description: "Declares an explicit join for a view. Joins are INNER by default — pass `'left'` as the third argument to keep entry rows without a match (fields read from a left-joined table must be optional). A join condition may reference the entry table and joins declared before it (chained joins); a table can be joined once (no aliases / self-joins).\n\n**Example:**\n```atscript\n@db.view.for Order\n@db.view.joins Customer, `Customer.id = Order.customerId`\n@db.view.joins Region, `Region.id = Customer.regionId`, 'left'\nexport interface OrderRegion { ... }\n```\n",
|
|
1306
1420
|
nodeType: ["interface"],
|
|
1307
1421
|
multiple: true,
|
|
1308
1422
|
mergeStrategy: "append",
|
|
1309
|
-
argument: [
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1423
|
+
argument: [
|
|
1424
|
+
{
|
|
1425
|
+
name: "target",
|
|
1426
|
+
type: "ref",
|
|
1427
|
+
description: "The table type to join (must have @db.table)."
|
|
1428
|
+
},
|
|
1429
|
+
{
|
|
1430
|
+
name: "condition",
|
|
1431
|
+
type: "query",
|
|
1432
|
+
description: "Join condition expression."
|
|
1433
|
+
},
|
|
1434
|
+
{
|
|
1435
|
+
name: "kind",
|
|
1436
|
+
type: "string",
|
|
1437
|
+
optional: true,
|
|
1438
|
+
description: "`\"inner\"` (default) drops entry rows without a match; `\"left\"` keeps them with NULLs.",
|
|
1439
|
+
values: ["inner", "left"]
|
|
1440
|
+
}
|
|
1441
|
+
],
|
|
1318
1442
|
validate(token, args, doc) {
|
|
1319
1443
|
const errors = [];
|
|
1320
1444
|
const owner = token.parentNode;
|
|
@@ -1336,9 +1460,29 @@ const dbViewAnnotations = { view: {
|
|
|
1336
1460
|
});
|
|
1337
1461
|
return errors;
|
|
1338
1462
|
}
|
|
1463
|
+
const allJoins = viewJoins(owner);
|
|
1464
|
+
const position = allJoins.findIndex((a) => a.token === token);
|
|
1465
|
+
const earlier = joinTargets(position === -1 ? [] : allJoins.slice(0, position));
|
|
1466
|
+
if (args[0]) {
|
|
1467
|
+
const target = args[0].text;
|
|
1468
|
+
if (target === entryTypeName) errors.push({
|
|
1469
|
+
message: `@db.view.joins cannot join the entry table '${target}' — no join aliases / self-joins yet`,
|
|
1470
|
+
severity: 1,
|
|
1471
|
+
range: args[0].range
|
|
1472
|
+
});
|
|
1473
|
+
else if (earlier.includes(target)) errors.push({
|
|
1474
|
+
message: `'${target}' is joined more than once — no join aliases / self-joins yet`,
|
|
1475
|
+
severity: 1,
|
|
1476
|
+
range: args[0].range
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1339
1479
|
if (args[1]?.queryNode && args[0]) {
|
|
1340
1480
|
const joinTargetName = args[0].text;
|
|
1341
|
-
errors.push(...validateQueryScope(args[1], [
|
|
1481
|
+
errors.push(...validateQueryScope(args[1], [
|
|
1482
|
+
joinTargetName,
|
|
1483
|
+
entryTypeName,
|
|
1484
|
+
...earlier
|
|
1485
|
+
], entryTypeName, doc, "a join may reference the entry table and joins declared before it"));
|
|
1342
1486
|
}
|
|
1343
1487
|
return errors;
|
|
1344
1488
|
}
|
|
@@ -1372,12 +1516,7 @@ const dbViewAnnotations = { view: {
|
|
|
1372
1516
|
});
|
|
1373
1517
|
return errors;
|
|
1374
1518
|
}
|
|
1375
|
-
|
|
1376
|
-
const joinsAnnotations = owner.annotations?.filter((a) => a.name === "db.view.joins");
|
|
1377
|
-
if (joinsAnnotations) {
|
|
1378
|
-
for (const join of joinsAnnotations) if (join.args[0]) allowedTypes.push(join.args[0].text);
|
|
1379
|
-
}
|
|
1380
|
-
errors.push(...validateQueryScope(args[0], allowedTypes, entryTypeName, doc));
|
|
1519
|
+
errors.push(...validateQueryScope(args[0], viewScopeTypes(owner), entryTypeName, doc));
|
|
1381
1520
|
return errors;
|
|
1382
1521
|
}
|
|
1383
1522
|
}),
|
package/dist/rel.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_nested_writer = require("./nested-writer-
|
|
3
|
-
const require_relation_loader = require("./relation-loader-
|
|
2
|
+
const require_nested_writer = require("./nested-writer-BZNCuqI6.cjs");
|
|
3
|
+
const require_relation_loader = require("./relation-loader-6ZB_5KFq.cjs");
|
|
4
4
|
exports.batchInsertNestedFrom = require_nested_writer.batchInsertNestedFrom;
|
|
5
5
|
exports.batchInsertNestedTo = require_nested_writer.batchInsertNestedTo;
|
|
6
6
|
exports.batchInsertNestedVia = require_nested_writer.batchInsertNestedVia;
|
package/dist/rel.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Pt as BaseDbAdapter, bt as TTableResolver, kt as TableMetadata, q as TDbRelation, sn as TGenericLogger, wt as TWriteTableResolver, z as TDbForeignKey } from "./buckets-C-27xmtq.cjs";
|
|
2
2
|
import { t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.cjs";
|
|
3
|
+
import { n as findRemoteFK, r as resolveRelationTargetTable, t as findFKForRelation } from "./relation-helpers-DxrvS6ar.cjs";
|
|
3
4
|
import { FilterExpr, WithRelation } from "@uniqu/core";
|
|
4
5
|
import { Validator } from "@atscript/typescript/utils";
|
|
5
6
|
|
|
@@ -20,25 +21,6 @@ interface TRelationLoaderHost {
|
|
|
20
21
|
*/
|
|
21
22
|
declare function loadRelationsImpl(rows: Array<Record<string, unknown>>, withRelations: WithRelation[], host: TRelationLoaderHost): Promise<void>;
|
|
22
23
|
//#endregion
|
|
23
|
-
//#region src/rel/relation-helpers.d.ts
|
|
24
|
-
/**
|
|
25
|
-
* Finds the FK entry that connects a `@db.rel.to` relation to its target.
|
|
26
|
-
*/
|
|
27
|
-
declare function findFKForRelation(relation: TDbRelation, foreignKeys: ReadonlyMap<string, TDbForeignKey>): {
|
|
28
|
-
localFields: string[];
|
|
29
|
-
targetFields: string[];
|
|
30
|
-
} | undefined;
|
|
31
|
-
/**
|
|
32
|
-
* Finds a FK on a remote table that points back to a given table name.
|
|
33
|
-
*/
|
|
34
|
-
declare function findRemoteFK(targetTable: {
|
|
35
|
-
foreignKeys: ReadonlyMap<string, TDbForeignKey>;
|
|
36
|
-
}, thisTableName: string, alias?: string): TDbForeignKey | undefined;
|
|
37
|
-
/**
|
|
38
|
-
* Resolves the target table name from a relation's target type metadata.
|
|
39
|
-
*/
|
|
40
|
-
declare function resolveRelationTargetTable(relation: TDbRelation): string;
|
|
41
|
-
//#endregion
|
|
42
24
|
//#region src/rel/nested-writer.d.ts
|
|
43
25
|
/**
|
|
44
26
|
* Properties the nested writer functions need from the table instance.
|
package/dist/rel.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Pt as BaseDbAdapter, bt as TTableResolver, kt as TableMetadata, q as TDbRelation, sn as TGenericLogger, wt as TWriteTableResolver, z as TDbForeignKey } from "./buckets-BFG2RYRW.mjs";
|
|
2
2
|
import { t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.mjs";
|
|
3
|
+
import { n as findRemoteFK, r as resolveRelationTargetTable, t as findFKForRelation } from "./relation-helpers-D3Zu0Mta.mjs";
|
|
3
4
|
import { Validator } from "@atscript/typescript/utils";
|
|
4
5
|
import { FilterExpr, WithRelation } from "@uniqu/core";
|
|
5
6
|
|
|
@@ -20,25 +21,6 @@ interface TRelationLoaderHost {
|
|
|
20
21
|
*/
|
|
21
22
|
declare function loadRelationsImpl(rows: Array<Record<string, unknown>>, withRelations: WithRelation[], host: TRelationLoaderHost): Promise<void>;
|
|
22
23
|
//#endregion
|
|
23
|
-
//#region src/rel/relation-helpers.d.ts
|
|
24
|
-
/**
|
|
25
|
-
* Finds the FK entry that connects a `@db.rel.to` relation to its target.
|
|
26
|
-
*/
|
|
27
|
-
declare function findFKForRelation(relation: TDbRelation, foreignKeys: ReadonlyMap<string, TDbForeignKey>): {
|
|
28
|
-
localFields: string[];
|
|
29
|
-
targetFields: string[];
|
|
30
|
-
} | undefined;
|
|
31
|
-
/**
|
|
32
|
-
* Finds a FK on a remote table that points back to a given table name.
|
|
33
|
-
*/
|
|
34
|
-
declare function findRemoteFK(targetTable: {
|
|
35
|
-
foreignKeys: ReadonlyMap<string, TDbForeignKey>;
|
|
36
|
-
}, thisTableName: string, alias?: string): TDbForeignKey | undefined;
|
|
37
|
-
/**
|
|
38
|
-
* Resolves the target table name from a relation's target type metadata.
|
|
39
|
-
*/
|
|
40
|
-
declare function resolveRelationTargetTable(relation: TDbRelation): string;
|
|
41
|
-
//#endregion
|
|
42
24
|
//#region src/rel/nested-writer.d.ts
|
|
43
25
|
/**
|
|
44
26
|
* Properties the nested writer functions need from the table instance.
|
package/dist/rel.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as loadRelationsImpl } from "./relation-loader-
|
|
2
|
-
import { _ as resolveRelationTargetTable, a as batchPatchNestedTo, c as batchReplaceNestedTo, d as preValidateNestedFrom, f as validateBatch, g as findRemoteFK, h as findFKForRelation, i as batchPatchNestedFrom, l as batchReplaceNestedVia, n as batchInsertNestedTo, o as batchPatchNestedVia, r as batchInsertNestedVia, s as batchReplaceNestedFrom, t as batchInsertNestedFrom, u as checkDepthOverflow } from "./nested-writer-
|
|
1
|
+
import { t as loadRelationsImpl } from "./relation-loader-CTFaZpVa.mjs";
|
|
2
|
+
import { _ as resolveRelationTargetTable, a as batchPatchNestedTo, c as batchReplaceNestedTo, d as preValidateNestedFrom, f as validateBatch, g as findRemoteFK, h as findFKForRelation, i as batchPatchNestedFrom, l as batchReplaceNestedVia, n as batchInsertNestedTo, o as batchPatchNestedVia, r as batchInsertNestedVia, s as batchReplaceNestedFrom, t as batchInsertNestedFrom, u as checkDepthOverflow } from "./nested-writer-FWD5oOYh.mjs";
|
|
3
3
|
export { batchInsertNestedFrom, batchInsertNestedTo, batchInsertNestedVia, batchPatchNestedFrom, batchPatchNestedTo, batchPatchNestedVia, batchReplaceNestedFrom, batchReplaceNestedTo, batchReplaceNestedVia, checkDepthOverflow, findFKForRelation, findRemoteFK, loadRelationsImpl, preValidateNestedFrom, resolveRelationTargetTable, validateBatch };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { q as TDbRelation, z as TDbForeignKey } from "./buckets-BFG2RYRW.mjs";
|
|
2
|
+
import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
|
|
3
|
+
|
|
4
|
+
//#region src/rel/relation-helpers.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Finds the FK entry that connects a `@db.rel.to` relation to its target.
|
|
7
|
+
*/
|
|
8
|
+
declare function findFKForRelation(relation: TDbRelation, foreignKeys: ReadonlyMap<string, TDbForeignKey>): {
|
|
9
|
+
localFields: string[];
|
|
10
|
+
targetFields: string[];
|
|
11
|
+
} | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Finds a FK on a remote table that points back to a given table name.
|
|
14
|
+
*/
|
|
15
|
+
declare function findRemoteFK(targetTable: {
|
|
16
|
+
foreignKeys: ReadonlyMap<string, TDbForeignKey>;
|
|
17
|
+
}, thisTableName: string, alias?: string): TDbForeignKey | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Table name of an annotated type — its `@db.table`, else its type id (`""`
|
|
20
|
+
* when neither is set). The rule relations, foreign keys and view plans use
|
|
21
|
+
* to name a referenced table.
|
|
22
|
+
* @since 0.1.136
|
|
23
|
+
*/
|
|
24
|
+
declare function tableNameOf(type: TAtscriptAnnotatedType | undefined): string;
|
|
25
|
+
/**
|
|
26
|
+
* Resolves the target table name from a relation's target type metadata.
|
|
27
|
+
*/
|
|
28
|
+
declare function resolveRelationTargetTable(relation: TDbRelation): string;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { tableNameOf as i, findRemoteFK as n, resolveRelationTargetTable as r, findFKForRelation as t };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { q as TDbRelation, z as TDbForeignKey } from "./buckets-C-27xmtq.cjs";
|
|
2
|
+
import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
|
|
3
|
+
|
|
4
|
+
//#region src/rel/relation-helpers.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Finds the FK entry that connects a `@db.rel.to` relation to its target.
|
|
7
|
+
*/
|
|
8
|
+
declare function findFKForRelation(relation: TDbRelation, foreignKeys: ReadonlyMap<string, TDbForeignKey>): {
|
|
9
|
+
localFields: string[];
|
|
10
|
+
targetFields: string[];
|
|
11
|
+
} | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Finds a FK on a remote table that points back to a given table name.
|
|
14
|
+
*/
|
|
15
|
+
declare function findRemoteFK(targetTable: {
|
|
16
|
+
foreignKeys: ReadonlyMap<string, TDbForeignKey>;
|
|
17
|
+
}, thisTableName: string, alias?: string): TDbForeignKey | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Table name of an annotated type — its `@db.table`, else its type id (`""`
|
|
20
|
+
* when neither is set). The rule relations, foreign keys and view plans use
|
|
21
|
+
* to name a referenced table.
|
|
22
|
+
* @since 0.1.136
|
|
23
|
+
*/
|
|
24
|
+
declare function tableNameOf(type: TAtscriptAnnotatedType | undefined): string;
|
|
25
|
+
/**
|
|
26
|
+
* Resolves the target table name from a relation's target type metadata.
|
|
27
|
+
*/
|
|
28
|
+
declare function resolveRelationTargetTable(relation: TDbRelation): string;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { tableNameOf as i, findRemoteFK as n, resolveRelationTargetTable as r, findFKForRelation as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_nested_writer = require("./nested-writer-
|
|
1
|
+
const require_nested_writer = require("./nested-writer-BZNCuqI6.cjs");
|
|
2
2
|
//#region src/rel/relation-loader.ts
|
|
3
3
|
var relation_loader_exports = /* @__PURE__ */ require("./plugin.cjs").__exportAll({
|
|
4
4
|
ensureSelectIncludesFields: () => ensureSelectIncludesFields,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as resolveRelationTargetTable, g as findRemoteFK, h as findFKForRelation } from "./nested-writer-
|
|
1
|
+
import { _ as resolveRelationTargetTable, g as findRemoteFK, h as findFKForRelation } from "./nested-writer-FWD5oOYh.mjs";
|
|
2
2
|
//#region \0rolldown/runtime.js
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __exportAll = (all, no_symbols) => {
|
package/dist/shared.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_validation_utils = require("./validation-utils-
|
|
2
|
+
const require_validation_utils = require("./validation-utils-Dg0hW6dn.cjs");
|
|
3
3
|
exports.findFKFieldsPointingTo = require_validation_utils.findFKFieldsPointingTo;
|
|
4
4
|
exports.getAnnotationAlias = require_validation_utils.getAnnotationAlias;
|
|
5
5
|
exports.getDbTableOwner = require_validation_utils.getDbTableOwner;
|
|
@@ -7,9 +7,13 @@ exports.getNavTargetTypeName = require_validation_utils.getNavTargetTypeName;
|
|
|
7
7
|
exports.getParentStruct = require_validation_utils.getParentStruct;
|
|
8
8
|
exports.getParentTypeName = require_validation_utils.getParentTypeName;
|
|
9
9
|
exports.hasAnyViewAnnotation = require_validation_utils.hasAnyViewAnnotation;
|
|
10
|
+
exports.joinTargets = require_validation_utils.joinTargets;
|
|
11
|
+
exports.primitiveBaseType = require_validation_utils.primitiveBaseType;
|
|
10
12
|
exports.refActionAnnotation = require_validation_utils.refActionAnnotation;
|
|
11
13
|
exports.validateExclusiveWith = require_validation_utils.validateExclusiveWith;
|
|
12
14
|
exports.validateFieldBaseType = require_validation_utils.validateFieldBaseType;
|
|
13
15
|
exports.validateQueryScope = require_validation_utils.validateQueryScope;
|
|
14
16
|
exports.validateRefArgument = require_validation_utils.validateRefArgument;
|
|
15
17
|
exports.validateSiblingStringField = require_validation_utils.validateSiblingStringField;
|
|
18
|
+
exports.viewJoins = require_validation_utils.viewJoins;
|
|
19
|
+
exports.viewScopeTypes = require_validation_utils.viewScopeTypes;
|
package/dist/shared.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnnotationSpec, AtscriptDoc, SemanticInterfaceNode, SemanticNode, SemanticPropNode, SemanticStructureNode, TMessages, Token } from "@atscript/core";
|
|
1
|
+
import { AnnotationSpec, AtscriptDoc, SemanticInterfaceNode, SemanticNode, SemanticPropNode, SemanticStructureNode, TAnnotationTokens, TMessages, Token } from "@atscript/core";
|
|
2
2
|
|
|
3
3
|
//#region src/shared/annotation-utils.d.ts
|
|
4
4
|
/** Asserts the field carrying this annotation does not also carry any of `others`. */
|
|
@@ -6,6 +6,8 @@ declare function validateExclusiveWith(token: Token, selfName: string, others: A
|
|
|
6
6
|
key: string;
|
|
7
7
|
displayName?: string;
|
|
8
8
|
}>): TMessages;
|
|
9
|
+
/** Primitive base type (`string`, `number`, `decimal`, …) of a resolved definition, if it is a primitive. */
|
|
10
|
+
declare function primitiveBaseType(def: SemanticNode | undefined): string | undefined;
|
|
9
11
|
/** Asserts `args[0]` names a sibling property whose primitive base type is `string`. */
|
|
10
12
|
declare function validateSiblingStringField(token: Token, args: Token[], doc: AtscriptDoc, selfName: string): TMessages;
|
|
11
13
|
/**
|
|
@@ -61,6 +63,16 @@ interface TFKFieldMatch {
|
|
|
61
63
|
* Resolves `extends` to include inherited fields.
|
|
62
64
|
*/
|
|
63
65
|
declare function findFKFieldsPointingTo(doc: AtscriptDoc, iface: SemanticInterfaceNode | SemanticStructureNode, targetTypeName: string, alias?: string): TFKFieldMatch[];
|
|
66
|
+
/** The `@db.view.joins` annotations of a view interface, in declaration order. */
|
|
67
|
+
declare function viewJoins(owner: SemanticNode): TAnnotationTokens[];
|
|
68
|
+
/** The target type names of `@db.view.joins` annotations. */
|
|
69
|
+
declare function joinTargets(joins: readonly TAnnotationTokens[]): string[];
|
|
70
|
+
/**
|
|
71
|
+
* The type names a view predicate (`@db.view.filter`, a conditional
|
|
72
|
+
* `@db.agg.*`) may reference: the `@db.view.for` entry table, then every
|
|
73
|
+
* `@db.view.joins` target.
|
|
74
|
+
*/
|
|
75
|
+
declare function viewScopeTypes(owner: SemanticNode): string[];
|
|
64
76
|
/**
|
|
65
77
|
* Check if a node has any @db.view.* annotation.
|
|
66
78
|
*/
|
|
@@ -72,7 +84,8 @@ declare function hasAnyViewAnnotation(node: SemanticNode): boolean;
|
|
|
72
84
|
* @param allowedTypes - Type names allowed as qualified refs
|
|
73
85
|
* @param unqualifiedTarget - Type name for resolving unqualified refs, or null to disallow them
|
|
74
86
|
* @param doc - The document for type lookups
|
|
87
|
+
* @param scopeHint - Replaces the default "expected 'A' or 'B'" tail of the out-of-scope message
|
|
75
88
|
*/
|
|
76
|
-
declare function validateQueryScope(queryToken: Token, allowedTypes: string[], unqualifiedTarget: string | null, doc: AtscriptDoc): TMessages;
|
|
89
|
+
declare function validateQueryScope(queryToken: Token, allowedTypes: string[], unqualifiedTarget: string | null, doc: AtscriptDoc, scopeHint?: string): TMessages;
|
|
77
90
|
//#endregion
|
|
78
|
-
export { TFKFieldMatch, findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField };
|
|
91
|
+
export { TFKFieldMatch, findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, joinTargets, primitiveBaseType, refActionAnnotation, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField, viewJoins, viewScopeTypes };
|
package/dist/shared.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AnnotationSpec, AtscriptDoc, SemanticInterfaceNode, SemanticNode, SemanticPropNode, SemanticStructureNode, TMessages, Token } from "@atscript/core";
|
|
1
|
+
import { AnnotationSpec, AtscriptDoc, SemanticInterfaceNode, SemanticNode, SemanticPropNode, SemanticStructureNode, TAnnotationTokens, TMessages, Token } from "@atscript/core";
|
|
2
2
|
|
|
3
3
|
//#region src/shared/annotation-utils.d.ts
|
|
4
4
|
/** Asserts the field carrying this annotation does not also carry any of `others`. */
|
|
@@ -6,6 +6,8 @@ declare function validateExclusiveWith(token: Token, selfName: string, others: A
|
|
|
6
6
|
key: string;
|
|
7
7
|
displayName?: string;
|
|
8
8
|
}>): TMessages;
|
|
9
|
+
/** Primitive base type (`string`, `number`, `decimal`, …) of a resolved definition, if it is a primitive. */
|
|
10
|
+
declare function primitiveBaseType(def: SemanticNode | undefined): string | undefined;
|
|
9
11
|
/** Asserts `args[0]` names a sibling property whose primitive base type is `string`. */
|
|
10
12
|
declare function validateSiblingStringField(token: Token, args: Token[], doc: AtscriptDoc, selfName: string): TMessages;
|
|
11
13
|
/**
|
|
@@ -61,6 +63,16 @@ interface TFKFieldMatch {
|
|
|
61
63
|
* Resolves `extends` to include inherited fields.
|
|
62
64
|
*/
|
|
63
65
|
declare function findFKFieldsPointingTo(doc: AtscriptDoc, iface: SemanticInterfaceNode | SemanticStructureNode, targetTypeName: string, alias?: string): TFKFieldMatch[];
|
|
66
|
+
/** The `@db.view.joins` annotations of a view interface, in declaration order. */
|
|
67
|
+
declare function viewJoins(owner: SemanticNode): TAnnotationTokens[];
|
|
68
|
+
/** The target type names of `@db.view.joins` annotations. */
|
|
69
|
+
declare function joinTargets(joins: readonly TAnnotationTokens[]): string[];
|
|
70
|
+
/**
|
|
71
|
+
* The type names a view predicate (`@db.view.filter`, a conditional
|
|
72
|
+
* `@db.agg.*`) may reference: the `@db.view.for` entry table, then every
|
|
73
|
+
* `@db.view.joins` target.
|
|
74
|
+
*/
|
|
75
|
+
declare function viewScopeTypes(owner: SemanticNode): string[];
|
|
64
76
|
/**
|
|
65
77
|
* Check if a node has any @db.view.* annotation.
|
|
66
78
|
*/
|
|
@@ -72,7 +84,8 @@ declare function hasAnyViewAnnotation(node: SemanticNode): boolean;
|
|
|
72
84
|
* @param allowedTypes - Type names allowed as qualified refs
|
|
73
85
|
* @param unqualifiedTarget - Type name for resolving unqualified refs, or null to disallow them
|
|
74
86
|
* @param doc - The document for type lookups
|
|
87
|
+
* @param scopeHint - Replaces the default "expected 'A' or 'B'" tail of the out-of-scope message
|
|
75
88
|
*/
|
|
76
|
-
declare function validateQueryScope(queryToken: Token, allowedTypes: string[], unqualifiedTarget: string | null, doc: AtscriptDoc): TMessages;
|
|
89
|
+
declare function validateQueryScope(queryToken: Token, allowedTypes: string[], unqualifiedTarget: string | null, doc: AtscriptDoc, scopeHint?: string): TMessages;
|
|
77
90
|
//#endregion
|
|
78
|
-
export { TFKFieldMatch, findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, refActionAnnotation, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField };
|
|
91
|
+
export { TFKFieldMatch, findFKFieldsPointingTo, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, joinTargets, primitiveBaseType, refActionAnnotation, validateExclusiveWith, validateFieldBaseType, validateQueryScope, validateRefArgument, validateSiblingStringField, viewJoins, viewScopeTypes };
|