@atscript/db 0.1.140 → 0.1.142
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agg.d.cts +1 -1
- package/dist/agg.d.mts +1 -1
- package/dist/{buckets-C-27xmtq.d.cts → buckets-Bv4pah66.d.cts} +225 -22
- package/dist/{buckets-BFG2RYRW.d.mts → buckets-CjL7F-hp.d.mts} +225 -22
- package/dist/{column-diff-CgxgFKzx.cjs → column-diff-CfPNcP6e.cjs} +663 -239
- package/dist/{column-diff-BwOA5101.mjs → column-diff-CmFNXV8C.mjs} +638 -220
- package/dist/column-diff-DiBbXyLA.d.cts +211 -0
- package/dist/column-diff-n-k5KY0u.d.mts +211 -0
- package/dist/derived-rules-0sKn4f5C.mjs +44 -0
- package/dist/derived-rules-YstgIxG-.cjs +67 -0
- package/dist/index.cjs +38 -4
- package/dist/index.d.cts +23 -6
- package/dist/index.d.mts +23 -6
- package/dist/index.mjs +34 -4
- package/dist/{nested-writer-FWD5oOYh.mjs → nested-writer-BO3vhbkP.mjs} +8 -4
- package/dist/{nested-writer-BZNCuqI6.cjs → nested-writer-DYsRxZ5f.cjs} +8 -4
- package/dist/object-DSN0h9lB.d.cts +30 -0
- package/dist/object-DSN0h9lB.d.mts +30 -0
- package/dist/plugin.cjs +392 -139
- package/dist/plugin.mjs +392 -139
- package/dist/rel.cjs +2 -2
- package/dist/rel.d.cts +2 -2
- package/dist/rel.d.mts +2 -2
- package/dist/rel.mjs +2 -2
- package/dist/{relation-helpers-D3Zu0Mta.d.mts → relation-helpers-B59to_dG.d.mts} +5 -4
- package/dist/{relation-helpers-DxrvS6ar.d.cts → relation-helpers-DQ_nRsV9.d.cts} +5 -4
- package/dist/{relation-loader-6ZB_5KFq.cjs → relation-loader-CgJ8bK6X.cjs} +1 -1
- package/dist/{relation-loader-CTFaZpVa.mjs → relation-loader-CuhEBzFU.mjs} +1 -1
- package/dist/shared.cjs +6 -1
- package/dist/shared.d.cts +48 -9
- package/dist/shared.d.mts +48 -9
- package/dist/shared.mjs +2 -2
- package/dist/sync.cjs +331 -105
- package/dist/sync.d.cts +62 -163
- package/dist/sync.d.mts +62 -163
- package/dist/sync.mjs +331 -105
- package/dist/{validation-utils-B4h-GW4d.mjs → validation-utils-CMR4fe2M.mjs} +99 -34
- package/dist/{validation-utils-Dg0hW6dn.cjs → validation-utils-DOsB4e6G.cjs} +128 -33
- package/dist/{validator-Drb2N-YL.d.cts → validator-Bw6ks9Hy.d.cts} +1 -11
- package/dist/{validator-Drb2N-YL.d.mts → validator-Bw6ks9Hy.d.mts} +1 -11
- package/dist/{validator-Ch7UIQl9.mjs → validator-D8bPsXPN.mjs} +54 -2
- package/dist/{validator-BtZbcLN2.cjs → validator-DASnXf1j.cjs} +77 -1
- package/dist/validator.cjs +1 -1
- package/dist/validator.d.cts +2 -1
- package/dist/validator.d.mts +2 -1
- package/dist/validator.mjs +1 -1
- package/package.json +6 -6
- package/dist/column-diff-BmqvgBWw.d.cts +0 -24
- package/dist/column-diff-DPkbZIVE.d.mts +0 -24
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as DB_ENTITY_ANNOTATIONS } from "./derived-rules-0sKn4f5C.mjs";
|
|
1
2
|
import { AnnotationSpec, isArray, isInterface, isPrimitive, isQueryComparison, isQueryLogical, isRef, isStructure } from "@atscript/core";
|
|
2
3
|
//#region src/shared/annotation-utils.ts
|
|
3
4
|
/** Asserts the field carrying this annotation does not also carry any of `others`. */
|
|
@@ -177,6 +178,14 @@ function refActionAnnotation(name) {
|
|
|
177
178
|
}
|
|
178
179
|
//#endregion
|
|
179
180
|
//#region src/shared/validation-utils.ts
|
|
181
|
+
/** Whether a declaration node is a `@db.table` or a `@db.view` (managed or external). */
|
|
182
|
+
function isDbSourceDecl(node) {
|
|
183
|
+
return DB_ENTITY_ANNOTATIONS.some((name) => node.countAnnotations(name) > 0);
|
|
184
|
+
}
|
|
185
|
+
/** Whether a declaration node is a `@db.alias` type. @since 0.1.141 */
|
|
186
|
+
function isAliasDecl(node) {
|
|
187
|
+
return node.countAnnotations("db.alias") > 0;
|
|
188
|
+
}
|
|
180
189
|
/**
|
|
181
190
|
* Validate a ref annotation argument against the document's type registry.
|
|
182
191
|
* Returns diagnostic messages for unknown types or fields.
|
|
@@ -204,16 +213,60 @@ function validateRefArgument(token, doc, options) {
|
|
|
204
213
|
return messages;
|
|
205
214
|
}
|
|
206
215
|
}
|
|
207
|
-
if (options?.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
});
|
|
213
|
-
}
|
|
216
|
+
if (options?.accept && decl.node && !options.accept(decl.node)) messages.push({
|
|
217
|
+
severity: 1,
|
|
218
|
+
message: isAliasDecl(decl.node) ? `Type '${typeName}' is a @db.alias — a join alias cannot be used here, reference the aliased table or view.` : `Type '${typeName}' ${options.expected ?? "is not accepted here."}`,
|
|
219
|
+
range: token.range
|
|
220
|
+
});
|
|
214
221
|
return messages;
|
|
215
222
|
}
|
|
216
223
|
/**
|
|
224
|
+
* The declaration a view source name stands for: the named table / view, or
|
|
225
|
+
* — for a `@db.alias` type — the aliased table / view. `undefined` when the
|
|
226
|
+
* name does not resolve (unknown, or an import that is not loaded yet).
|
|
227
|
+
* @since 0.1.141
|
|
228
|
+
*/
|
|
229
|
+
function resolveViewSourceDecl(name, doc) {
|
|
230
|
+
const decl = doc.getDeclarationOwnerNode(name);
|
|
231
|
+
if (!decl?.node) return;
|
|
232
|
+
const aliasTarget = getAnnotationAlias(decl.node, "db.alias");
|
|
233
|
+
if (aliasTarget !== void 0) {
|
|
234
|
+
const target = decl.doc.getDeclarationOwnerNode(aliasTarget);
|
|
235
|
+
return target?.node ? {
|
|
236
|
+
doc: target.doc,
|
|
237
|
+
node: target.node
|
|
238
|
+
} : void 0;
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
doc: decl.doc,
|
|
242
|
+
node: decl.node
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* VW9 — the dependency cycle a view closes, as the list of view names from
|
|
247
|
+
* the view back to itself (`["A", "B", "A"]`), or `undefined`. Walks
|
|
248
|
+
* `@db.view.for` / `@db.view.joins` of the referenced declarations across
|
|
249
|
+
* documents; `@db.alias` sources resolve to their target.
|
|
250
|
+
* @since 0.1.141
|
|
251
|
+
*/
|
|
252
|
+
function findViewCycle(owner, doc) {
|
|
253
|
+
const start = owner.id;
|
|
254
|
+
if (!start) return;
|
|
255
|
+
const visited = /* @__PURE__ */ new Set();
|
|
256
|
+
const walk = (node, nodeDoc, path) => {
|
|
257
|
+
for (const name of viewScopeTypes(node)) {
|
|
258
|
+
const source = resolveViewSourceDecl(name, nodeDoc);
|
|
259
|
+
if (!source) continue;
|
|
260
|
+
if (source.node === owner) return [...path, start];
|
|
261
|
+
if (visited.has(source.node)) continue;
|
|
262
|
+
visited.add(source.node);
|
|
263
|
+
const cycle = walk(source.node, source.doc, [...path, source.node.id ?? name]);
|
|
264
|
+
if (cycle) return cycle;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
return walk(owner, doc, [start]);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
217
270
|
* Find all `@db.rel.FK` fields on a type that reference `targetTypeName`.
|
|
218
271
|
* Resolves `extends` to include inherited fields.
|
|
219
272
|
*/
|
|
@@ -265,6 +318,16 @@ function joinTargets(joins) {
|
|
|
265
318
|
return joins.map((a) => a.args[0]?.text).filter((t) => !!t);
|
|
266
319
|
}
|
|
267
320
|
/**
|
|
321
|
+
* The targets of the `@db.view.joins` declared before `join` among the
|
|
322
|
+
* view's `joins` ({@link viewJoins}) — what a chained join condition may
|
|
323
|
+
* reference besides its target and the entry.
|
|
324
|
+
* @since 0.1.141
|
|
325
|
+
*/
|
|
326
|
+
function earlierJoinTargets(joins, join) {
|
|
327
|
+
const position = joins.indexOf(join);
|
|
328
|
+
return joinTargets(position === -1 ? [] : joins.slice(0, position));
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
268
331
|
* The type names a view predicate (`@db.view.filter`, a conditional
|
|
269
332
|
* `@db.agg.*`) may reference: the `@db.view.for` entry table, then every
|
|
270
333
|
* `@db.view.joins` target.
|
|
@@ -281,19 +344,23 @@ function hasAnyViewAnnotation(node) {
|
|
|
281
344
|
return viewAnnotationNames.some((name) => node.countAnnotations(name) > 0);
|
|
282
345
|
}
|
|
283
346
|
/**
|
|
284
|
-
* Validate that all
|
|
347
|
+
* Validate that all field refs in a query expression are within `scope` —
|
|
348
|
+
* the same {@link TQueryScope} the editor completes (`lsp-scopes`):
|
|
349
|
+
* qualified refs must name one of `scope.allowedTypes`; an unqualified ref
|
|
350
|
+
* (a dotted path included) must be a field of `scope.unqualifiedTarget`, or
|
|
351
|
+
* is rejected outright when that is `null`.
|
|
285
352
|
*
|
|
286
353
|
* @param queryToken - The query arg token (must have .queryNode)
|
|
287
|
-
* @param
|
|
288
|
-
* @param unqualifiedTarget - Type name for resolving unqualified refs, or null to disallow them
|
|
354
|
+
* @param scope - The scope of the argument
|
|
289
355
|
* @param doc - The document for type lookups
|
|
290
356
|
* @param scopeHint - Replaces the default "expected 'A' or 'B'" tail of the out-of-scope message
|
|
291
357
|
*/
|
|
292
|
-
function validateQueryScope(queryToken,
|
|
358
|
+
function validateQueryScope(queryToken, scope, doc, scopeHint) {
|
|
293
359
|
const errors = [];
|
|
294
360
|
const queryNode = queryToken.queryNode;
|
|
295
361
|
if (!queryNode) return errors;
|
|
296
|
-
|
|
362
|
+
const { allowedTypes, unqualifiedTarget } = scope;
|
|
363
|
+
forEachFieldRef(queryNode.expression, (ref) => {
|
|
297
364
|
if (ref.typeRef) {
|
|
298
365
|
const typeName = ref.typeRef.text;
|
|
299
366
|
if (!allowedTypes.includes(typeName)) errors.push({
|
|
@@ -307,29 +374,27 @@ function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc, sc
|
|
|
307
374
|
range: ref.fieldRef.range
|
|
308
375
|
});
|
|
309
376
|
else {
|
|
310
|
-
const
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
message: `Field '${ref.fieldRef.text}' does not exist on '${unqualifiedTarget}'`,
|
|
317
|
-
severity: 1,
|
|
318
|
-
range: ref.fieldRef.range
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
function walkExpr(expr) {
|
|
325
|
-
if (isQueryLogical(expr)) for (const operand of expr.operands) walkExpr(operand);
|
|
326
|
-
else if (isQueryComparison(expr)) {
|
|
327
|
-
walkFieldRef(expr.left);
|
|
328
|
-
if (expr.right && "fieldRef" in expr.right) walkFieldRef(expr.right);
|
|
377
|
+
const targetDef = doc.unwindType(unqualifiedTarget)?.def;
|
|
378
|
+
if (targetDef && (isInterface(targetDef) || isStructure(targetDef)) && !doc.unwindType(unqualifiedTarget, ref.fieldRef.text.split("."))) errors.push({
|
|
379
|
+
message: `Field '${ref.fieldRef.text}' does not exist on '${unqualifiedTarget}'`,
|
|
380
|
+
severity: 1,
|
|
381
|
+
range: ref.fieldRef.range
|
|
382
|
+
});
|
|
329
383
|
}
|
|
330
|
-
}
|
|
331
|
-
walkExpr(queryNode.expression);
|
|
384
|
+
});
|
|
332
385
|
return errors;
|
|
333
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Calls `fn` for every field reference of a query expression — both operands
|
|
389
|
+
* of a field-to-field comparison included.
|
|
390
|
+
* @since 0.1.141
|
|
391
|
+
*/
|
|
392
|
+
function forEachFieldRef(expr, fn) {
|
|
393
|
+
if (isQueryLogical(expr)) for (const operand of expr.operands) forEachFieldRef(operand, fn);
|
|
394
|
+
else if (isQueryComparison(expr)) {
|
|
395
|
+
fn(expr.left);
|
|
396
|
+
if (expr.right && "fieldRef" in expr.right) fn(expr.right);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
334
399
|
//#endregion
|
|
335
|
-
export { validateSiblingStringField as _,
|
|
400
|
+
export { validateSiblingStringField as S, getParentTypeName as _, hasAnyViewAnnotation as a, validateExclusiveWith as b, joinTargets as c, viewJoins as d, viewScopeTypes as f, getParentStruct as g, getNavTargetTypeName as h, forEachFieldRef as i, validateQueryScope as l, getDbTableOwner as m, findFKFieldsPointingTo as n, isAliasDecl as o, getAnnotationAlias as p, findViewCycle as r, isDbSourceDecl as s, earlierJoinTargets as t, validateRefArgument as u, primitiveBaseType as v, validateFieldBaseType as x, refActionAnnotation as y };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const require_derived_rules = require("./derived-rules-YstgIxG-.cjs");
|
|
1
2
|
let _atscript_core = require("@atscript/core");
|
|
2
3
|
//#region src/shared/annotation-utils.ts
|
|
3
4
|
/** Asserts the field carrying this annotation does not also carry any of `others`. */
|
|
@@ -177,6 +178,14 @@ function refActionAnnotation(name) {
|
|
|
177
178
|
}
|
|
178
179
|
//#endregion
|
|
179
180
|
//#region src/shared/validation-utils.ts
|
|
181
|
+
/** Whether a declaration node is a `@db.table` or a `@db.view` (managed or external). */
|
|
182
|
+
function isDbSourceDecl(node) {
|
|
183
|
+
return require_derived_rules.DB_ENTITY_ANNOTATIONS.some((name) => node.countAnnotations(name) > 0);
|
|
184
|
+
}
|
|
185
|
+
/** Whether a declaration node is a `@db.alias` type. @since 0.1.141 */
|
|
186
|
+
function isAliasDecl(node) {
|
|
187
|
+
return node.countAnnotations("db.alias") > 0;
|
|
188
|
+
}
|
|
180
189
|
/**
|
|
181
190
|
* Validate a ref annotation argument against the document's type registry.
|
|
182
191
|
* Returns diagnostic messages for unknown types or fields.
|
|
@@ -204,16 +213,60 @@ function validateRefArgument(token, doc, options) {
|
|
|
204
213
|
return messages;
|
|
205
214
|
}
|
|
206
215
|
}
|
|
207
|
-
if (options?.
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
});
|
|
213
|
-
}
|
|
216
|
+
if (options?.accept && decl.node && !options.accept(decl.node)) messages.push({
|
|
217
|
+
severity: 1,
|
|
218
|
+
message: isAliasDecl(decl.node) ? `Type '${typeName}' is a @db.alias — a join alias cannot be used here, reference the aliased table or view.` : `Type '${typeName}' ${options.expected ?? "is not accepted here."}`,
|
|
219
|
+
range: token.range
|
|
220
|
+
});
|
|
214
221
|
return messages;
|
|
215
222
|
}
|
|
216
223
|
/**
|
|
224
|
+
* The declaration a view source name stands for: the named table / view, or
|
|
225
|
+
* — for a `@db.alias` type — the aliased table / view. `undefined` when the
|
|
226
|
+
* name does not resolve (unknown, or an import that is not loaded yet).
|
|
227
|
+
* @since 0.1.141
|
|
228
|
+
*/
|
|
229
|
+
function resolveViewSourceDecl(name, doc) {
|
|
230
|
+
const decl = doc.getDeclarationOwnerNode(name);
|
|
231
|
+
if (!decl?.node) return;
|
|
232
|
+
const aliasTarget = getAnnotationAlias(decl.node, "db.alias");
|
|
233
|
+
if (aliasTarget !== void 0) {
|
|
234
|
+
const target = decl.doc.getDeclarationOwnerNode(aliasTarget);
|
|
235
|
+
return target?.node ? {
|
|
236
|
+
doc: target.doc,
|
|
237
|
+
node: target.node
|
|
238
|
+
} : void 0;
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
doc: decl.doc,
|
|
242
|
+
node: decl.node
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* VW9 — the dependency cycle a view closes, as the list of view names from
|
|
247
|
+
* the view back to itself (`["A", "B", "A"]`), or `undefined`. Walks
|
|
248
|
+
* `@db.view.for` / `@db.view.joins` of the referenced declarations across
|
|
249
|
+
* documents; `@db.alias` sources resolve to their target.
|
|
250
|
+
* @since 0.1.141
|
|
251
|
+
*/
|
|
252
|
+
function findViewCycle(owner, doc) {
|
|
253
|
+
const start = owner.id;
|
|
254
|
+
if (!start) return;
|
|
255
|
+
const visited = /* @__PURE__ */ new Set();
|
|
256
|
+
const walk = (node, nodeDoc, path) => {
|
|
257
|
+
for (const name of viewScopeTypes(node)) {
|
|
258
|
+
const source = resolveViewSourceDecl(name, nodeDoc);
|
|
259
|
+
if (!source) continue;
|
|
260
|
+
if (source.node === owner) return [...path, start];
|
|
261
|
+
if (visited.has(source.node)) continue;
|
|
262
|
+
visited.add(source.node);
|
|
263
|
+
const cycle = walk(source.node, source.doc, [...path, source.node.id ?? name]);
|
|
264
|
+
if (cycle) return cycle;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
return walk(owner, doc, [start]);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
217
270
|
* Find all `@db.rel.FK` fields on a type that reference `targetTypeName`.
|
|
218
271
|
* Resolves `extends` to include inherited fields.
|
|
219
272
|
*/
|
|
@@ -265,6 +318,16 @@ function joinTargets(joins) {
|
|
|
265
318
|
return joins.map((a) => a.args[0]?.text).filter((t) => !!t);
|
|
266
319
|
}
|
|
267
320
|
/**
|
|
321
|
+
* The targets of the `@db.view.joins` declared before `join` among the
|
|
322
|
+
* view's `joins` ({@link viewJoins}) — what a chained join condition may
|
|
323
|
+
* reference besides its target and the entry.
|
|
324
|
+
* @since 0.1.141
|
|
325
|
+
*/
|
|
326
|
+
function earlierJoinTargets(joins, join) {
|
|
327
|
+
const position = joins.indexOf(join);
|
|
328
|
+
return joinTargets(position === -1 ? [] : joins.slice(0, position));
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
268
331
|
* The type names a view predicate (`@db.view.filter`, a conditional
|
|
269
332
|
* `@db.agg.*`) may reference: the `@db.view.for` entry table, then every
|
|
270
333
|
* `@db.view.joins` target.
|
|
@@ -281,19 +344,23 @@ function hasAnyViewAnnotation(node) {
|
|
|
281
344
|
return viewAnnotationNames.some((name) => node.countAnnotations(name) > 0);
|
|
282
345
|
}
|
|
283
346
|
/**
|
|
284
|
-
* Validate that all
|
|
347
|
+
* Validate that all field refs in a query expression are within `scope` —
|
|
348
|
+
* the same {@link TQueryScope} the editor completes (`lsp-scopes`):
|
|
349
|
+
* qualified refs must name one of `scope.allowedTypes`; an unqualified ref
|
|
350
|
+
* (a dotted path included) must be a field of `scope.unqualifiedTarget`, or
|
|
351
|
+
* is rejected outright when that is `null`.
|
|
285
352
|
*
|
|
286
353
|
* @param queryToken - The query arg token (must have .queryNode)
|
|
287
|
-
* @param
|
|
288
|
-
* @param unqualifiedTarget - Type name for resolving unqualified refs, or null to disallow them
|
|
354
|
+
* @param scope - The scope of the argument
|
|
289
355
|
* @param doc - The document for type lookups
|
|
290
356
|
* @param scopeHint - Replaces the default "expected 'A' or 'B'" tail of the out-of-scope message
|
|
291
357
|
*/
|
|
292
|
-
function validateQueryScope(queryToken,
|
|
358
|
+
function validateQueryScope(queryToken, scope, doc, scopeHint) {
|
|
293
359
|
const errors = [];
|
|
294
360
|
const queryNode = queryToken.queryNode;
|
|
295
361
|
if (!queryNode) return errors;
|
|
296
|
-
|
|
362
|
+
const { allowedTypes, unqualifiedTarget } = scope;
|
|
363
|
+
forEachFieldRef(queryNode.expression, (ref) => {
|
|
297
364
|
if (ref.typeRef) {
|
|
298
365
|
const typeName = ref.typeRef.text;
|
|
299
366
|
if (!allowedTypes.includes(typeName)) errors.push({
|
|
@@ -307,37 +374,53 @@ function validateQueryScope(queryToken, allowedTypes, unqualifiedTarget, doc, sc
|
|
|
307
374
|
range: ref.fieldRef.range
|
|
308
375
|
});
|
|
309
376
|
else {
|
|
310
|
-
const
|
|
311
|
-
if (
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
message: `Field '${ref.fieldRef.text}' does not exist on '${unqualifiedTarget}'`,
|
|
317
|
-
severity: 1,
|
|
318
|
-
range: ref.fieldRef.range
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
function walkExpr(expr) {
|
|
325
|
-
if ((0, _atscript_core.isQueryLogical)(expr)) for (const operand of expr.operands) walkExpr(operand);
|
|
326
|
-
else if ((0, _atscript_core.isQueryComparison)(expr)) {
|
|
327
|
-
walkFieldRef(expr.left);
|
|
328
|
-
if (expr.right && "fieldRef" in expr.right) walkFieldRef(expr.right);
|
|
377
|
+
const targetDef = doc.unwindType(unqualifiedTarget)?.def;
|
|
378
|
+
if (targetDef && ((0, _atscript_core.isInterface)(targetDef) || (0, _atscript_core.isStructure)(targetDef)) && !doc.unwindType(unqualifiedTarget, ref.fieldRef.text.split("."))) errors.push({
|
|
379
|
+
message: `Field '${ref.fieldRef.text}' does not exist on '${unqualifiedTarget}'`,
|
|
380
|
+
severity: 1,
|
|
381
|
+
range: ref.fieldRef.range
|
|
382
|
+
});
|
|
329
383
|
}
|
|
330
|
-
}
|
|
331
|
-
walkExpr(queryNode.expression);
|
|
384
|
+
});
|
|
332
385
|
return errors;
|
|
333
386
|
}
|
|
387
|
+
/**
|
|
388
|
+
* Calls `fn` for every field reference of a query expression — both operands
|
|
389
|
+
* of a field-to-field comparison included.
|
|
390
|
+
* @since 0.1.141
|
|
391
|
+
*/
|
|
392
|
+
function forEachFieldRef(expr, fn) {
|
|
393
|
+
if ((0, _atscript_core.isQueryLogical)(expr)) for (const operand of expr.operands) forEachFieldRef(operand, fn);
|
|
394
|
+
else if ((0, _atscript_core.isQueryComparison)(expr)) {
|
|
395
|
+
fn(expr.left);
|
|
396
|
+
if (expr.right && "fieldRef" in expr.right) fn(expr.right);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
334
399
|
//#endregion
|
|
400
|
+
Object.defineProperty(exports, "earlierJoinTargets", {
|
|
401
|
+
enumerable: true,
|
|
402
|
+
get: function() {
|
|
403
|
+
return earlierJoinTargets;
|
|
404
|
+
}
|
|
405
|
+
});
|
|
335
406
|
Object.defineProperty(exports, "findFKFieldsPointingTo", {
|
|
336
407
|
enumerable: true,
|
|
337
408
|
get: function() {
|
|
338
409
|
return findFKFieldsPointingTo;
|
|
339
410
|
}
|
|
340
411
|
});
|
|
412
|
+
Object.defineProperty(exports, "findViewCycle", {
|
|
413
|
+
enumerable: true,
|
|
414
|
+
get: function() {
|
|
415
|
+
return findViewCycle;
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
Object.defineProperty(exports, "forEachFieldRef", {
|
|
419
|
+
enumerable: true,
|
|
420
|
+
get: function() {
|
|
421
|
+
return forEachFieldRef;
|
|
422
|
+
}
|
|
423
|
+
});
|
|
341
424
|
Object.defineProperty(exports, "getAnnotationAlias", {
|
|
342
425
|
enumerable: true,
|
|
343
426
|
get: function() {
|
|
@@ -374,6 +457,18 @@ Object.defineProperty(exports, "hasAnyViewAnnotation", {
|
|
|
374
457
|
return hasAnyViewAnnotation;
|
|
375
458
|
}
|
|
376
459
|
});
|
|
460
|
+
Object.defineProperty(exports, "isAliasDecl", {
|
|
461
|
+
enumerable: true,
|
|
462
|
+
get: function() {
|
|
463
|
+
return isAliasDecl;
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
Object.defineProperty(exports, "isDbSourceDecl", {
|
|
467
|
+
enumerable: true,
|
|
468
|
+
get: function() {
|
|
469
|
+
return isDbSourceDecl;
|
|
470
|
+
}
|
|
471
|
+
});
|
|
377
472
|
Object.defineProperty(exports, "joinTargets", {
|
|
378
473
|
enumerable: true,
|
|
379
474
|
get: function() {
|
|
@@ -78,16 +78,6 @@ type TDbPatch<T> = { [K in keyof T]?: T[K] extends Array<infer _> ? TArrayPatch<
|
|
|
78
78
|
*/
|
|
79
79
|
declare function getKeyProps(def: TAtscriptAnnotatedType<TAtscriptTypeArray>): Set<string>;
|
|
80
80
|
//#endregion
|
|
81
|
-
//#region src/shared/object.d.ts
|
|
82
|
-
/**
|
|
83
|
-
* Plain object: prototype is `Object.prototype` or `null` — never a class
|
|
84
|
-
* instance (`Date`, `Uint8Array`, `ObjectId`, …) and never an array.
|
|
85
|
-
* Browser-safe; shared by the write paths, the HTTP shape gate and the client.
|
|
86
|
-
*/
|
|
87
|
-
declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
88
|
-
/** Zero-allocation emptiness check for a plain object (`{}` → `true`). */
|
|
89
|
-
declare function isEmptyObject(obj: Record<string, unknown>): boolean;
|
|
90
|
-
//#endregion
|
|
91
81
|
//#region src/validator.d.ts
|
|
92
82
|
/** Write operation mode for validator configuration. */
|
|
93
83
|
type ValidatorMode = "insert" | "patch" | "replace";
|
|
@@ -148,4 +138,4 @@ interface ValidationContext {
|
|
|
148
138
|
*/
|
|
149
139
|
declare function buildValidationContext(type: TAtscriptAnnotatedType<TAtscriptTypeObject>): ValidationContext;
|
|
150
140
|
//#endregion
|
|
151
|
-
export {
|
|
141
|
+
export { buildValidationContext as a, isNavRelation as c, getKeyProps as d, CasExhaustedError as f, bucketTimeZoneUnavailable as g, DbErrorCode as h, buildPatchPartial as i, TArrayPatch as l, DbError as m, ValidatorMode as n, dbPlugin as o, CasMismatchError as p, buildDbValidator as r, forceNavNonOptional as s, ValidationContext as t, TDbPatch as u };
|
|
@@ -78,16 +78,6 @@ type TDbPatch<T> = { [K in keyof T]?: T[K] extends Array<infer _> ? TArrayPatch<
|
|
|
78
78
|
*/
|
|
79
79
|
declare function getKeyProps(def: TAtscriptAnnotatedType<TAtscriptTypeArray>): Set<string>;
|
|
80
80
|
//#endregion
|
|
81
|
-
//#region src/shared/object.d.ts
|
|
82
|
-
/**
|
|
83
|
-
* Plain object: prototype is `Object.prototype` or `null` — never a class
|
|
84
|
-
* instance (`Date`, `Uint8Array`, `ObjectId`, …) and never an array.
|
|
85
|
-
* Browser-safe; shared by the write paths, the HTTP shape gate and the client.
|
|
86
|
-
*/
|
|
87
|
-
declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
88
|
-
/** Zero-allocation emptiness check for a plain object (`{}` → `true`). */
|
|
89
|
-
declare function isEmptyObject(obj: Record<string, unknown>): boolean;
|
|
90
|
-
//#endregion
|
|
91
81
|
//#region src/validator.d.ts
|
|
92
82
|
/** Write operation mode for validator configuration. */
|
|
93
83
|
type ValidatorMode = "insert" | "patch" | "replace";
|
|
@@ -148,4 +138,4 @@ interface ValidationContext {
|
|
|
148
138
|
*/
|
|
149
139
|
declare function buildValidationContext(type: TAtscriptAnnotatedType<TAtscriptTypeObject>): ValidationContext;
|
|
150
140
|
//#endregion
|
|
151
|
-
export {
|
|
141
|
+
export { buildValidationContext as a, isNavRelation as c, getKeyProps as d, CasExhaustedError as f, bucketTimeZoneUnavailable as g, DbErrorCode as h, buildPatchPartial as i, TArrayPatch as l, DbError as m, ValidatorMode as n, dbPlugin as o, CasMismatchError as p, buildDbValidator as r, forceNavNonOptional as s, ValidationContext as t, TDbPatch as u };
|
|
@@ -17,6 +17,54 @@ function isEmptyObject(obj) {
|
|
|
17
17
|
for (const _ in obj) return false;
|
|
18
18
|
return true;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Finds the nearest ancestor of `path` that belongs to `set`.
|
|
22
|
+
* Used by both the build pipeline (in `_classifyFields`) and
|
|
23
|
+
* runtime reconstruction on the Readable.
|
|
24
|
+
*/
|
|
25
|
+
function findAncestorInSet(path, set) {
|
|
26
|
+
let pos = path.length;
|
|
27
|
+
while ((pos = path.lastIndexOf(".", pos - 1)) !== -1) {
|
|
28
|
+
const ancestor = path.slice(0, pos);
|
|
29
|
+
if (set.has(ancestor)) return ancestor;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** `path` itself if it is in `set`, else its nearest ancestor in `set` ({@link findAncestorInSet}). */
|
|
33
|
+
function selfOrAncestor(path, set) {
|
|
34
|
+
return set.has(path) ? path : findAncestorInSet(path, set);
|
|
35
|
+
}
|
|
36
|
+
/** Splits a dot-path once; an already split path is used as-is. */
|
|
37
|
+
function segmentsOf(path) {
|
|
38
|
+
return typeof path === "string" ? path.split(".") : path;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Reads a dot-path (or its segments) off a nested plain object: `undefined`
|
|
42
|
+
* past a missing step, or a step that is not a plain object (arrays are not
|
|
43
|
+
* descended into — no positional indexing).
|
|
44
|
+
* @since 0.1.141
|
|
45
|
+
*/
|
|
46
|
+
function getPath(row, path) {
|
|
47
|
+
let current = row;
|
|
48
|
+
for (const seg of segmentsOf(path)) {
|
|
49
|
+
if (current === null || typeof current !== "object" || Array.isArray(current)) return;
|
|
50
|
+
current = current[seg];
|
|
51
|
+
}
|
|
52
|
+
return current;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Deletes a dot-path (or its segments) from a nested plain object; a no-op
|
|
56
|
+
* when an intermediate step is missing or not a plain object.
|
|
57
|
+
* @since 0.1.141
|
|
58
|
+
*/
|
|
59
|
+
function deletePath(target, path) {
|
|
60
|
+
const segments = segmentsOf(path);
|
|
61
|
+
let current = target;
|
|
62
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
63
|
+
if (current === null || typeof current !== "object" || Array.isArray(current)) return;
|
|
64
|
+
current = current[segments[i]];
|
|
65
|
+
}
|
|
66
|
+
if (current !== null && typeof current === "object" && !Array.isArray(current)) delete current[segments[segments.length - 1]];
|
|
67
|
+
}
|
|
20
68
|
//#endregion
|
|
21
69
|
//#region src/patch/patch-types.ts
|
|
22
70
|
/**
|
|
@@ -92,7 +140,7 @@ function createDbValidatorPlugin() {
|
|
|
92
140
|
if (isTo || isFrom || isVia) return handleNavField(ctx, def, value, dbCtx, isTo, isFrom, isVia);
|
|
93
141
|
if (value === void 0 && (dbCtx.mode === "insert" || dbCtx.mode === "replace")) {
|
|
94
142
|
const meta = def.metadata;
|
|
95
|
-
const serverManaged = meta.has("db.default") || meta.has("db.default.increment") || meta.has("db.default.uuid") || meta.has("db.default.now") || meta.has("db.column.version");
|
|
143
|
+
const serverManaged = meta.has("db.default") || meta.has("db.default.increment") || meta.has("db.default.uuid") || meta.has("db.default.now") || meta.has("db.column.version") || meta.has("db.column.derived");
|
|
96
144
|
const hasFK = meta.has("db.rel.FK");
|
|
97
145
|
if (serverManaged || hasFK) {
|
|
98
146
|
if (dbCtx.mode === "replace" && meta.has("meta.id") && !isInsideNavField(ctx.path, dbCtx.navFields)) return;
|
|
@@ -101,6 +149,10 @@ function createDbValidatorPlugin() {
|
|
|
101
149
|
}
|
|
102
150
|
if (dbCtx.mode === "patch") {
|
|
103
151
|
if (isDbFieldOp(value)) {
|
|
152
|
+
if (def.metadata.has("db.column.derived")) {
|
|
153
|
+
ctx.error("Field operations ($inc/$dec/$mul) are not allowed on a @db.column.derived field — it is computed from its @db.json source; patch the source leaf instead");
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
104
156
|
if (dbCtx.flatMap && !isFieldOpAllowed(ctx.path, dbCtx.flatMap, dbCtx.navFields)) {
|
|
105
157
|
ctx.error("Field operations ($inc/$dec/$mul) are not supported inside @db.json fields or nested objects without @db.patch.strategy \"merge\"");
|
|
106
158
|
return false;
|
|
@@ -400,4 +452,4 @@ function buildValidationContext(type) {
|
|
|
400
452
|
};
|
|
401
453
|
}
|
|
402
454
|
//#endregion
|
|
403
|
-
export { forceNavNonOptional as a, getKeyProps as c, dbPlugin as i,
|
|
455
|
+
export { forceNavNonOptional as a, getKeyProps as c, getPath as d, isEmptyObject as f, dbPlugin as i, deletePath as l, selfOrAncestor as m, buildPatchPartial as n, isNavRelation as o, isPlainObject as p, buildValidationContext as r, createDbValidatorPlugin as s, buildDbValidator as t, findAncestorInSet as u };
|
|
@@ -17,6 +17,54 @@ function isEmptyObject(obj) {
|
|
|
17
17
|
for (const _ in obj) return false;
|
|
18
18
|
return true;
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Finds the nearest ancestor of `path` that belongs to `set`.
|
|
22
|
+
* Used by both the build pipeline (in `_classifyFields`) and
|
|
23
|
+
* runtime reconstruction on the Readable.
|
|
24
|
+
*/
|
|
25
|
+
function findAncestorInSet(path, set) {
|
|
26
|
+
let pos = path.length;
|
|
27
|
+
while ((pos = path.lastIndexOf(".", pos - 1)) !== -1) {
|
|
28
|
+
const ancestor = path.slice(0, pos);
|
|
29
|
+
if (set.has(ancestor)) return ancestor;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** `path` itself if it is in `set`, else its nearest ancestor in `set` ({@link findAncestorInSet}). */
|
|
33
|
+
function selfOrAncestor(path, set) {
|
|
34
|
+
return set.has(path) ? path : findAncestorInSet(path, set);
|
|
35
|
+
}
|
|
36
|
+
/** Splits a dot-path once; an already split path is used as-is. */
|
|
37
|
+
function segmentsOf(path) {
|
|
38
|
+
return typeof path === "string" ? path.split(".") : path;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Reads a dot-path (or its segments) off a nested plain object: `undefined`
|
|
42
|
+
* past a missing step, or a step that is not a plain object (arrays are not
|
|
43
|
+
* descended into — no positional indexing).
|
|
44
|
+
* @since 0.1.141
|
|
45
|
+
*/
|
|
46
|
+
function getPath(row, path) {
|
|
47
|
+
let current = row;
|
|
48
|
+
for (const seg of segmentsOf(path)) {
|
|
49
|
+
if (current === null || typeof current !== "object" || Array.isArray(current)) return;
|
|
50
|
+
current = current[seg];
|
|
51
|
+
}
|
|
52
|
+
return current;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Deletes a dot-path (or its segments) from a nested plain object; a no-op
|
|
56
|
+
* when an intermediate step is missing or not a plain object.
|
|
57
|
+
* @since 0.1.141
|
|
58
|
+
*/
|
|
59
|
+
function deletePath(target, path) {
|
|
60
|
+
const segments = segmentsOf(path);
|
|
61
|
+
let current = target;
|
|
62
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
63
|
+
if (current === null || typeof current !== "object" || Array.isArray(current)) return;
|
|
64
|
+
current = current[segments[i]];
|
|
65
|
+
}
|
|
66
|
+
if (current !== null && typeof current === "object" && !Array.isArray(current)) delete current[segments[segments.length - 1]];
|
|
67
|
+
}
|
|
20
68
|
//#endregion
|
|
21
69
|
//#region src/patch/patch-types.ts
|
|
22
70
|
/**
|
|
@@ -92,7 +140,7 @@ function createDbValidatorPlugin() {
|
|
|
92
140
|
if (isTo || isFrom || isVia) return handleNavField(ctx, def, value, dbCtx, isTo, isFrom, isVia);
|
|
93
141
|
if (value === void 0 && (dbCtx.mode === "insert" || dbCtx.mode === "replace")) {
|
|
94
142
|
const meta = def.metadata;
|
|
95
|
-
const serverManaged = meta.has("db.default") || meta.has("db.default.increment") || meta.has("db.default.uuid") || meta.has("db.default.now") || meta.has("db.column.version");
|
|
143
|
+
const serverManaged = meta.has("db.default") || meta.has("db.default.increment") || meta.has("db.default.uuid") || meta.has("db.default.now") || meta.has("db.column.version") || meta.has("db.column.derived");
|
|
96
144
|
const hasFK = meta.has("db.rel.FK");
|
|
97
145
|
if (serverManaged || hasFK) {
|
|
98
146
|
if (dbCtx.mode === "replace" && meta.has("meta.id") && !isInsideNavField(ctx.path, dbCtx.navFields)) return;
|
|
@@ -101,6 +149,10 @@ function createDbValidatorPlugin() {
|
|
|
101
149
|
}
|
|
102
150
|
if (dbCtx.mode === "patch") {
|
|
103
151
|
if (require_ops.isDbFieldOp(value)) {
|
|
152
|
+
if (def.metadata.has("db.column.derived")) {
|
|
153
|
+
ctx.error("Field operations ($inc/$dec/$mul) are not allowed on a @db.column.derived field — it is computed from its @db.json source; patch the source leaf instead");
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
104
156
|
if (dbCtx.flatMap && !isFieldOpAllowed(ctx.path, dbCtx.flatMap, dbCtx.navFields)) {
|
|
105
157
|
ctx.error("Field operations ($inc/$dec/$mul) are not supported inside @db.json fields or nested objects without @db.patch.strategy \"merge\"");
|
|
106
158
|
return false;
|
|
@@ -430,6 +482,18 @@ Object.defineProperty(exports, "dbPlugin", {
|
|
|
430
482
|
return dbPlugin;
|
|
431
483
|
}
|
|
432
484
|
});
|
|
485
|
+
Object.defineProperty(exports, "deletePath", {
|
|
486
|
+
enumerable: true,
|
|
487
|
+
get: function() {
|
|
488
|
+
return deletePath;
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
Object.defineProperty(exports, "findAncestorInSet", {
|
|
492
|
+
enumerable: true,
|
|
493
|
+
get: function() {
|
|
494
|
+
return findAncestorInSet;
|
|
495
|
+
}
|
|
496
|
+
});
|
|
433
497
|
Object.defineProperty(exports, "forceNavNonOptional", {
|
|
434
498
|
enumerable: true,
|
|
435
499
|
get: function() {
|
|
@@ -442,6 +506,12 @@ Object.defineProperty(exports, "getKeyProps", {
|
|
|
442
506
|
return getKeyProps;
|
|
443
507
|
}
|
|
444
508
|
});
|
|
509
|
+
Object.defineProperty(exports, "getPath", {
|
|
510
|
+
enumerable: true,
|
|
511
|
+
get: function() {
|
|
512
|
+
return getPath;
|
|
513
|
+
}
|
|
514
|
+
});
|
|
445
515
|
Object.defineProperty(exports, "isEmptyObject", {
|
|
446
516
|
enumerable: true,
|
|
447
517
|
get: function() {
|
|
@@ -460,3 +530,9 @@ Object.defineProperty(exports, "isPlainObject", {
|
|
|
460
530
|
return isPlainObject;
|
|
461
531
|
}
|
|
462
532
|
});
|
|
533
|
+
Object.defineProperty(exports, "selfOrAncestor", {
|
|
534
|
+
enumerable: true,
|
|
535
|
+
get: function() {
|
|
536
|
+
return selfOrAncestor;
|
|
537
|
+
}
|
|
538
|
+
});
|
package/dist/validator.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_db_error = require("./db-error-DTkkeu5b.cjs");
|
|
3
|
-
const require_validator = require("./validator-
|
|
3
|
+
const require_validator = require("./validator-DASnXf1j.cjs");
|
|
4
4
|
const require_ops = require("./ops.cjs");
|
|
5
5
|
exports.$cas = require_ops.$cas;
|
|
6
6
|
exports.$dec = require_ops.$dec;
|
package/dist/validator.d.cts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { a as $mul, c as $update, d as TDbFieldOp, g as separateCas, h as reconcileCas, i as $insert, l as $upsert, m as isDbFieldOp, n as $dec, o as $remove, r as $inc, s as $replace, t as $cas, u as TDbCas } from "./ops-AqhV7s9o.cjs";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as isPlainObject, i as isEmptyObject } from "./object-DSN0h9lB.cjs";
|
|
3
|
+
import { a as buildValidationContext, c as isNavRelation, d as getKeyProps, i as buildPatchPartial, l as TArrayPatch, m as DbError, n as ValidatorMode, o as dbPlugin, r as buildDbValidator, s as forceNavNonOptional, t as ValidationContext, u as TDbPatch } from "./validator-Bw6ks9Hy.cjs";
|
|
3
4
|
import { n as createDbValidatorPlugin, t as DbValidationContext } from "./db-validator-plugin-BWy60OvG.cjs";
|
|
4
5
|
export { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, DbError, type DbValidationContext, type TArrayPatch, type TDbCas, type TDbFieldOp, type TDbPatch, ValidationContext, ValidatorMode, buildDbValidator, buildPatchPartial, buildValidationContext, createDbValidatorPlugin, dbPlugin, forceNavNonOptional, getKeyProps, isDbFieldOp, isEmptyObject, isNavRelation, isPlainObject, reconcileCas, separateCas };
|