@atscript/db 0.1.154 → 0.1.155
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-jy8BfswO.d.cts → buckets-DcWm3SLt.d.cts} +120 -10
- package/dist/{buckets-CBKL_ZWE.d.mts → buckets-goCoky8v.d.mts} +120 -10
- package/dist/{column-diff-DAIAq6XR.mjs → column-diff-C68V_eYM.mjs} +346 -65
- package/dist/{column-diff-qnzx1o3c.cjs → column-diff-DMkGYAGM.cjs} +357 -64
- package/dist/{fk-diff-BIJ8l_hF.d.mts → fk-diff-BYLNWoF9.d.mts} +2 -1
- package/dist/{fk-diff-D1mrLMjU.d.cts → fk-diff-DblOPBLS.d.cts} +2 -1
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +10 -5
- package/dist/index.d.mts +10 -5
- package/dist/index.mjs +2 -2
- package/dist/plugin.cjs +3 -3
- package/dist/plugin.mjs +3 -3
- package/dist/rel.d.cts +2 -2
- package/dist/rel.d.mts +2 -2
- package/dist/{relation-helpers-BUsxNNGU.d.mts → relation-helpers-B9MVBrtx.d.mts} +1 -1
- package/dist/{relation-helpers-D_8bydc2.d.cts → relation-helpers-D9kMFfum.d.cts} +1 -1
- package/dist/shared.cjs +2 -1
- package/dist/shared.d.cts +10 -3
- package/dist/shared.d.mts +10 -3
- package/dist/shared.mjs +2 -2
- package/dist/sync.cjs +288 -30
- package/dist/sync.d.cts +34 -5
- package/dist/sync.d.mts +34 -5
- package/dist/sync.mjs +288 -30
- package/dist/{validation-utils-DqZAH6aT.mjs → validation-utils-DxHIqa36.mjs} +23 -5
- package/dist/{validation-utils-debIYpq0.cjs → validation-utils-_PA7iSLm.cjs} +28 -4
- package/dist/{validator-BACewoRj.mjs → validator-BC3nwFi4.mjs} +178 -10
- package/dist/{validator-BxWwh3wf.cjs → validator-DHwSyBej.cjs} +201 -9
- package/dist/validator.cjs +1 -1
- package/dist/validator.mjs +1 -1
- package/package.json +6 -6
|
@@ -1,6 +1,110 @@
|
|
|
1
1
|
import { i as DepthLimitExceededError } from "./db-error-eSsQX7rt.mjs";
|
|
2
2
|
import { getDbFieldOp } from "./ops.mjs";
|
|
3
3
|
import { createAnnotatedTypeNode, flattenAnnotatedType } from "@atscript/typescript/utils";
|
|
4
|
+
//#region src/shared/union-shape.ts
|
|
5
|
+
/** `null` / `undefined` as a type: a union member that only adds NULL. */
|
|
6
|
+
function isNullishType(type) {
|
|
7
|
+
const def = type.type;
|
|
8
|
+
return def.kind === "" && (def.designType === "null" || def.designType === "undefined");
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The value members of a union — nested unions expanded (a flat-union entry
|
|
12
|
+
* of `flattenAnnotatedType` holds the prop's own union besides its members,
|
|
13
|
+
* an alias member may be a union itself), `null` / `undefined` left out,
|
|
14
|
+
* each member once. `nullable`: a `null` / `undefined` member was left out.
|
|
15
|
+
*/
|
|
16
|
+
function unionValueMembers(type) {
|
|
17
|
+
const members = /* @__PURE__ */ new Set();
|
|
18
|
+
let nullable = false;
|
|
19
|
+
const visit = (t, depth) => {
|
|
20
|
+
if (t.type.kind === "union" && depth < 8) for (const item of t.type.items) visit(item, depth + 1);
|
|
21
|
+
else if (isNullishType(t)) nullable = true;
|
|
22
|
+
else members.add(t);
|
|
23
|
+
};
|
|
24
|
+
visit(type, 0);
|
|
25
|
+
return {
|
|
26
|
+
members: [...members],
|
|
27
|
+
nullable
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The one value member of a union (`T | null` → `T`), or `undefined` when
|
|
32
|
+
* `type` is no union or has several value members. A column of a nullable
|
|
33
|
+
* union is the column `T` alone would get (its type tags, size, precision).
|
|
34
|
+
*/
|
|
35
|
+
function soleUnionMember(type) {
|
|
36
|
+
if (type.type.kind !== "union") return void 0;
|
|
37
|
+
const { members } = unionValueMembers(type);
|
|
38
|
+
return members.length === 1 ? members[0] : void 0;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The `T` of a field typed `T | null` whose db annotations (`@db.default.now`
|
|
42
|
+
* of `number.timestamp.created`) describe the field's column like those of a
|
|
43
|
+
* field typed `T` — {@link soleUnionMember}, except for a reference to another
|
|
44
|
+
* field (`closedAt: Order.closedAt`): it shares that field's union, whose db
|
|
45
|
+
* annotations stay with the referenced field.
|
|
46
|
+
*/
|
|
47
|
+
function columnUnionBase(type) {
|
|
48
|
+
return type.ref?.field ? void 0 : soleUnionMember(type);
|
|
49
|
+
}
|
|
50
|
+
const RANK = {
|
|
51
|
+
required: 0,
|
|
52
|
+
nullable: 1,
|
|
53
|
+
partial: 2
|
|
54
|
+
};
|
|
55
|
+
function weaker(a, b) {
|
|
56
|
+
return RANK[a] >= RANK[b] ? a : b;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The {@link TPathPresence} of the dot-path `path` below the object type
|
|
60
|
+
* `root`, or `undefined` when the path crosses an array or a tuple (its
|
|
61
|
+
* elements' fields are no columns of their own on relational storage;
|
|
62
|
+
* callers keep the field's own `optional` there).
|
|
63
|
+
*/
|
|
64
|
+
function pathPresence(root, path) {
|
|
65
|
+
return presenceAt(root, path.split("."), 0);
|
|
66
|
+
}
|
|
67
|
+
function presenceAt(type, segs, i) {
|
|
68
|
+
const def = type.type;
|
|
69
|
+
switch (def.kind) {
|
|
70
|
+
case "union": {
|
|
71
|
+
let result = "required";
|
|
72
|
+
for (const item of def.items) {
|
|
73
|
+
if (isNullishType(item)) {
|
|
74
|
+
result = weaker(result, "nullable");
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const member = presenceAt(item, segs, i);
|
|
78
|
+
if (member === void 0) return void 0;
|
|
79
|
+
result = weaker(result, member);
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
case "intersection": {
|
|
84
|
+
let result = "partial";
|
|
85
|
+
for (const item of def.items) {
|
|
86
|
+
const member = presenceAt(item, segs, i);
|
|
87
|
+
if (member === void 0) return void 0;
|
|
88
|
+
if (RANK[member] < RANK[result]) result = member;
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
case "object": {
|
|
93
|
+
if (i === segs.length) return "required";
|
|
94
|
+
const prop = def.props.get(segs[i]);
|
|
95
|
+
if (!prop) return "partial";
|
|
96
|
+
const rest = presenceAt(prop, segs, i + 1);
|
|
97
|
+
if (rest === void 0) return void 0;
|
|
98
|
+
return prop.optional ? weaker(rest, "nullable") : rest;
|
|
99
|
+
}
|
|
100
|
+
case "array":
|
|
101
|
+
case "tuple": return i === segs.length ? "required" : void 0;
|
|
102
|
+
default:
|
|
103
|
+
if (i < segs.length) return "partial";
|
|
104
|
+
return isNullishType(type) ? "nullable" : "required";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
4
108
|
//#region src/patch/patch-types.ts
|
|
5
109
|
/**
|
|
6
110
|
* Extracts `@expect.array.key` properties from an array-of-objects type.
|
|
@@ -50,6 +154,73 @@ function markComplexItems(def) {
|
|
|
50
154
|
if (kind === "union" || kind === "tuple" || kind === "intersection") for (const item of def.type.items) complexItems.add(item);
|
|
51
155
|
}
|
|
52
156
|
/**
|
|
157
|
+
* Per validation context: the paths (array indices as `*`) of the tuples and
|
|
158
|
+
* the unions of several types the validator visited. Which item a field
|
|
159
|
+
* below one belongs to is not known when defaults are applied, so its
|
|
160
|
+
* default is not filled (`AtscriptDbTable._applyDefaults`) — and the field
|
|
161
|
+
* is not optional on insert either.
|
|
162
|
+
*/
|
|
163
|
+
const ambiguousPaths = /* @__PURE__ */ new WeakMap();
|
|
164
|
+
/** `items.3.at` → `items.*.at` */
|
|
165
|
+
function schemaPath(path) {
|
|
166
|
+
return path.replace(/(^|\.)\d+(?=\.|$)/g, "$1*");
|
|
167
|
+
}
|
|
168
|
+
function markAmbiguous(dbCtx, def, path) {
|
|
169
|
+
const kind = def.type.kind;
|
|
170
|
+
if (kind === "tuple" || kind === "union" && !soleUnionMember(def)) {
|
|
171
|
+
let paths = ambiguousPaths.get(dbCtx);
|
|
172
|
+
if (!paths) ambiguousPaths.set(dbCtx, paths = /* @__PURE__ */ new Set());
|
|
173
|
+
paths.add(schemaPath(path));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/** Whether `path` lies below a tuple or a union of several types (see {@link ambiguousPaths}). */
|
|
177
|
+
function belowAmbiguous(dbCtx, path) {
|
|
178
|
+
const paths = ambiguousPaths.get(dbCtx);
|
|
179
|
+
if (!paths) return false;
|
|
180
|
+
const schema = schemaPath(path);
|
|
181
|
+
for (let dot = schema.lastIndexOf("."); dot !== -1; dot = schema.lastIndexOf(".", dot - 1)) if (paths.has(schema.slice(0, dot))) return true;
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
/** Per validated root type: schema path → whether it lies below a tuple / union of several types. */
|
|
185
|
+
const ambiguousByRoot = /* @__PURE__ */ new WeakMap();
|
|
186
|
+
/**
|
|
187
|
+
* {@link belowAmbiguous} read from the type itself: walks `path` from `root`
|
|
188
|
+
* (the validator's own type) and answers whether a tuple or a union of
|
|
189
|
+
* several types lies on the way. The visit-based answer misses a union the
|
|
190
|
+
* plugin was not consulted for — a caller that runs this plugin only for
|
|
191
|
+
* absent values (a form's server-managed check) never shows it the union
|
|
192
|
+
* holding the value.
|
|
193
|
+
*/
|
|
194
|
+
function pathBelowAmbiguous(root, path) {
|
|
195
|
+
let cache = ambiguousByRoot.get(root);
|
|
196
|
+
if (!cache) ambiguousByRoot.set(root, cache = /* @__PURE__ */ new Map());
|
|
197
|
+
const schema = schemaPath(path);
|
|
198
|
+
let answer = cache.get(schema);
|
|
199
|
+
if (answer === void 0) {
|
|
200
|
+
answer = false;
|
|
201
|
+
let type = root;
|
|
202
|
+
for (const seg of schema.split(".")) {
|
|
203
|
+
if (type.type.kind === "union") {
|
|
204
|
+
const member = soleUnionMember(type);
|
|
205
|
+
if (!member) {
|
|
206
|
+
answer = true;
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
type = member;
|
|
210
|
+
}
|
|
211
|
+
const kind = type.type.kind;
|
|
212
|
+
if (kind === "tuple") {
|
|
213
|
+
answer = true;
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
type = kind === "array" ? seg === "*" ? type.type.of : void 0 : kind === "object" ? type.type.props.get(seg) : void 0;
|
|
217
|
+
if (!type) break;
|
|
218
|
+
}
|
|
219
|
+
cache.set(schema, answer);
|
|
220
|
+
}
|
|
221
|
+
return answer;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
53
224
|
* Validator plugin for database operations.
|
|
54
225
|
*
|
|
55
226
|
* Handles navigation field constraints and delegates to the standard validator
|
|
@@ -65,6 +236,7 @@ function createDbValidatorPlugin() {
|
|
|
65
236
|
const dbCtx = ctx.context;
|
|
66
237
|
if (!dbCtx) return;
|
|
67
238
|
markComplexItems(def);
|
|
239
|
+
markAmbiguous(dbCtx, def, ctx.path);
|
|
68
240
|
if (def.type.tags?.has("geoPoint") && value !== void 0 && value !== null) {
|
|
69
241
|
if (!Array.isArray(value) || value.length !== 2 || typeof value[0] !== "number" || typeof value[1] !== "number" || !Number.isFinite(value[0]) || !Number.isFinite(value[1])) {
|
|
70
242
|
ctx.error("Expected a [lng, lat] tuple of two finite numbers (GeoJSON order)");
|
|
@@ -86,10 +258,13 @@ function createDbValidatorPlugin() {
|
|
|
86
258
|
if (isTo || isFrom || isVia) return handleNavField(ctx, def, value, dbCtx, isTo, isFrom, isVia);
|
|
87
259
|
if (value === void 0 && (dbCtx.mode === "insert" || dbCtx.mode === "replace") && !complexItems.has(def)) {
|
|
88
260
|
const meta = def.metadata;
|
|
89
|
-
const
|
|
261
|
+
const baseMeta = columnUnionBase(def)?.metadata;
|
|
262
|
+
const defaulted = !(belowAmbiguous(dbCtx, ctx.path) || pathBelowAmbiguous(ctx.root, ctx.path));
|
|
263
|
+
const has = (key) => defaulted && (meta.has(key) || baseMeta?.has(key) === true);
|
|
264
|
+
const serverManaged = has("db.default") || has("db.default.increment") || has("db.default.uuid") || has("db.default.now") || meta.has("db.column.version") || meta.has("db.column.derived");
|
|
90
265
|
const hasFK = meta.has("db.rel.FK");
|
|
91
266
|
if (serverManaged || hasFK) {
|
|
92
|
-
if (dbCtx.mode === "replace" && meta.has("meta.id") && !
|
|
267
|
+
if (dbCtx.mode === "replace" && meta.has("meta.id") && !ctx.path.includes(".")) return;
|
|
93
268
|
return true;
|
|
94
269
|
}
|
|
95
270
|
}
|
|
@@ -149,13 +324,6 @@ function isFieldOpAllowed(path, flatMap, navFields) {
|
|
|
149
324
|
}
|
|
150
325
|
return true;
|
|
151
326
|
}
|
|
152
|
-
/** Returns true if the given path is nested inside a navigation field. */
|
|
153
|
-
function isInsideNavField(path, navFields) {
|
|
154
|
-
if (!navFields) return false;
|
|
155
|
-
let pos = path.length;
|
|
156
|
-
while ((pos = path.lastIndexOf(".", pos - 1)) !== -1) if (navFields.has(path.slice(0, pos))) return true;
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
327
|
function handleNavField(ctx, def, value, dbCtx, isTo, isFrom, isVia) {
|
|
160
328
|
const dotIdx = ctx.path.lastIndexOf(".");
|
|
161
329
|
const fieldName = dotIdx === -1 ? ctx.path : ctx.path.slice(dotIdx + 1);
|
|
@@ -437,4 +605,4 @@ function buildValidationContext(type) {
|
|
|
437
605
|
};
|
|
438
606
|
}
|
|
439
607
|
//#endregion
|
|
440
|
-
export { forceNavNonOptional as a, getKeyProps as c, dbPlugin as i, buildPatchPartial as n, isNavRelation as o, buildValidationContext as r, createDbValidatorPlugin as s, buildDbValidator as t };
|
|
608
|
+
export { forceNavNonOptional as a, getKeyProps as c, soleUnionMember as d, unionValueMembers as f, dbPlugin as i, columnUnionBase as l, buildPatchPartial as n, isNavRelation as o, buildValidationContext as r, createDbValidatorPlugin as s, buildDbValidator as t, pathPresence as u };
|
|
@@ -1,6 +1,110 @@
|
|
|
1
1
|
const require_db_error = require("./db-error-JcfJqqpk.cjs");
|
|
2
2
|
const require_ops = require("./ops.cjs");
|
|
3
3
|
let _atscript_typescript_utils = require("@atscript/typescript/utils");
|
|
4
|
+
//#region src/shared/union-shape.ts
|
|
5
|
+
/** `null` / `undefined` as a type: a union member that only adds NULL. */
|
|
6
|
+
function isNullishType(type) {
|
|
7
|
+
const def = type.type;
|
|
8
|
+
return def.kind === "" && (def.designType === "null" || def.designType === "undefined");
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The value members of a union — nested unions expanded (a flat-union entry
|
|
12
|
+
* of `flattenAnnotatedType` holds the prop's own union besides its members,
|
|
13
|
+
* an alias member may be a union itself), `null` / `undefined` left out,
|
|
14
|
+
* each member once. `nullable`: a `null` / `undefined` member was left out.
|
|
15
|
+
*/
|
|
16
|
+
function unionValueMembers(type) {
|
|
17
|
+
const members = /* @__PURE__ */ new Set();
|
|
18
|
+
let nullable = false;
|
|
19
|
+
const visit = (t, depth) => {
|
|
20
|
+
if (t.type.kind === "union" && depth < 8) for (const item of t.type.items) visit(item, depth + 1);
|
|
21
|
+
else if (isNullishType(t)) nullable = true;
|
|
22
|
+
else members.add(t);
|
|
23
|
+
};
|
|
24
|
+
visit(type, 0);
|
|
25
|
+
return {
|
|
26
|
+
members: [...members],
|
|
27
|
+
nullable
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The one value member of a union (`T | null` → `T`), or `undefined` when
|
|
32
|
+
* `type` is no union or has several value members. A column of a nullable
|
|
33
|
+
* union is the column `T` alone would get (its type tags, size, precision).
|
|
34
|
+
*/
|
|
35
|
+
function soleUnionMember(type) {
|
|
36
|
+
if (type.type.kind !== "union") return void 0;
|
|
37
|
+
const { members } = unionValueMembers(type);
|
|
38
|
+
return members.length === 1 ? members[0] : void 0;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The `T` of a field typed `T | null` whose db annotations (`@db.default.now`
|
|
42
|
+
* of `number.timestamp.created`) describe the field's column like those of a
|
|
43
|
+
* field typed `T` — {@link soleUnionMember}, except for a reference to another
|
|
44
|
+
* field (`closedAt: Order.closedAt`): it shares that field's union, whose db
|
|
45
|
+
* annotations stay with the referenced field.
|
|
46
|
+
*/
|
|
47
|
+
function columnUnionBase(type) {
|
|
48
|
+
return type.ref?.field ? void 0 : soleUnionMember(type);
|
|
49
|
+
}
|
|
50
|
+
const RANK = {
|
|
51
|
+
required: 0,
|
|
52
|
+
nullable: 1,
|
|
53
|
+
partial: 2
|
|
54
|
+
};
|
|
55
|
+
function weaker(a, b) {
|
|
56
|
+
return RANK[a] >= RANK[b] ? a : b;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The {@link TPathPresence} of the dot-path `path` below the object type
|
|
60
|
+
* `root`, or `undefined` when the path crosses an array or a tuple (its
|
|
61
|
+
* elements' fields are no columns of their own on relational storage;
|
|
62
|
+
* callers keep the field's own `optional` there).
|
|
63
|
+
*/
|
|
64
|
+
function pathPresence(root, path) {
|
|
65
|
+
return presenceAt(root, path.split("."), 0);
|
|
66
|
+
}
|
|
67
|
+
function presenceAt(type, segs, i) {
|
|
68
|
+
const def = type.type;
|
|
69
|
+
switch (def.kind) {
|
|
70
|
+
case "union": {
|
|
71
|
+
let result = "required";
|
|
72
|
+
for (const item of def.items) {
|
|
73
|
+
if (isNullishType(item)) {
|
|
74
|
+
result = weaker(result, "nullable");
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const member = presenceAt(item, segs, i);
|
|
78
|
+
if (member === void 0) return void 0;
|
|
79
|
+
result = weaker(result, member);
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
case "intersection": {
|
|
84
|
+
let result = "partial";
|
|
85
|
+
for (const item of def.items) {
|
|
86
|
+
const member = presenceAt(item, segs, i);
|
|
87
|
+
if (member === void 0) return void 0;
|
|
88
|
+
if (RANK[member] < RANK[result]) result = member;
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
case "object": {
|
|
93
|
+
if (i === segs.length) return "required";
|
|
94
|
+
const prop = def.props.get(segs[i]);
|
|
95
|
+
if (!prop) return "partial";
|
|
96
|
+
const rest = presenceAt(prop, segs, i + 1);
|
|
97
|
+
if (rest === void 0) return void 0;
|
|
98
|
+
return prop.optional ? weaker(rest, "nullable") : rest;
|
|
99
|
+
}
|
|
100
|
+
case "array":
|
|
101
|
+
case "tuple": return i === segs.length ? "required" : void 0;
|
|
102
|
+
default:
|
|
103
|
+
if (i < segs.length) return "partial";
|
|
104
|
+
return isNullishType(type) ? "nullable" : "required";
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
4
108
|
//#region src/patch/patch-types.ts
|
|
5
109
|
/**
|
|
6
110
|
* Extracts `@expect.array.key` properties from an array-of-objects type.
|
|
@@ -50,6 +154,73 @@ function markComplexItems(def) {
|
|
|
50
154
|
if (kind === "union" || kind === "tuple" || kind === "intersection") for (const item of def.type.items) complexItems.add(item);
|
|
51
155
|
}
|
|
52
156
|
/**
|
|
157
|
+
* Per validation context: the paths (array indices as `*`) of the tuples and
|
|
158
|
+
* the unions of several types the validator visited. Which item a field
|
|
159
|
+
* below one belongs to is not known when defaults are applied, so its
|
|
160
|
+
* default is not filled (`AtscriptDbTable._applyDefaults`) — and the field
|
|
161
|
+
* is not optional on insert either.
|
|
162
|
+
*/
|
|
163
|
+
const ambiguousPaths = /* @__PURE__ */ new WeakMap();
|
|
164
|
+
/** `items.3.at` → `items.*.at` */
|
|
165
|
+
function schemaPath(path) {
|
|
166
|
+
return path.replace(/(^|\.)\d+(?=\.|$)/g, "$1*");
|
|
167
|
+
}
|
|
168
|
+
function markAmbiguous(dbCtx, def, path) {
|
|
169
|
+
const kind = def.type.kind;
|
|
170
|
+
if (kind === "tuple" || kind === "union" && !soleUnionMember(def)) {
|
|
171
|
+
let paths = ambiguousPaths.get(dbCtx);
|
|
172
|
+
if (!paths) ambiguousPaths.set(dbCtx, paths = /* @__PURE__ */ new Set());
|
|
173
|
+
paths.add(schemaPath(path));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/** Whether `path` lies below a tuple or a union of several types (see {@link ambiguousPaths}). */
|
|
177
|
+
function belowAmbiguous(dbCtx, path) {
|
|
178
|
+
const paths = ambiguousPaths.get(dbCtx);
|
|
179
|
+
if (!paths) return false;
|
|
180
|
+
const schema = schemaPath(path);
|
|
181
|
+
for (let dot = schema.lastIndexOf("."); dot !== -1; dot = schema.lastIndexOf(".", dot - 1)) if (paths.has(schema.slice(0, dot))) return true;
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
/** Per validated root type: schema path → whether it lies below a tuple / union of several types. */
|
|
185
|
+
const ambiguousByRoot = /* @__PURE__ */ new WeakMap();
|
|
186
|
+
/**
|
|
187
|
+
* {@link belowAmbiguous} read from the type itself: walks `path` from `root`
|
|
188
|
+
* (the validator's own type) and answers whether a tuple or a union of
|
|
189
|
+
* several types lies on the way. The visit-based answer misses a union the
|
|
190
|
+
* plugin was not consulted for — a caller that runs this plugin only for
|
|
191
|
+
* absent values (a form's server-managed check) never shows it the union
|
|
192
|
+
* holding the value.
|
|
193
|
+
*/
|
|
194
|
+
function pathBelowAmbiguous(root, path) {
|
|
195
|
+
let cache = ambiguousByRoot.get(root);
|
|
196
|
+
if (!cache) ambiguousByRoot.set(root, cache = /* @__PURE__ */ new Map());
|
|
197
|
+
const schema = schemaPath(path);
|
|
198
|
+
let answer = cache.get(schema);
|
|
199
|
+
if (answer === void 0) {
|
|
200
|
+
answer = false;
|
|
201
|
+
let type = root;
|
|
202
|
+
for (const seg of schema.split(".")) {
|
|
203
|
+
if (type.type.kind === "union") {
|
|
204
|
+
const member = soleUnionMember(type);
|
|
205
|
+
if (!member) {
|
|
206
|
+
answer = true;
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
type = member;
|
|
210
|
+
}
|
|
211
|
+
const kind = type.type.kind;
|
|
212
|
+
if (kind === "tuple") {
|
|
213
|
+
answer = true;
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
type = kind === "array" ? seg === "*" ? type.type.of : void 0 : kind === "object" ? type.type.props.get(seg) : void 0;
|
|
217
|
+
if (!type) break;
|
|
218
|
+
}
|
|
219
|
+
cache.set(schema, answer);
|
|
220
|
+
}
|
|
221
|
+
return answer;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
53
224
|
* Validator plugin for database operations.
|
|
54
225
|
*
|
|
55
226
|
* Handles navigation field constraints and delegates to the standard validator
|
|
@@ -65,6 +236,7 @@ function createDbValidatorPlugin() {
|
|
|
65
236
|
const dbCtx = ctx.context;
|
|
66
237
|
if (!dbCtx) return;
|
|
67
238
|
markComplexItems(def);
|
|
239
|
+
markAmbiguous(dbCtx, def, ctx.path);
|
|
68
240
|
if (def.type.tags?.has("geoPoint") && value !== void 0 && value !== null) {
|
|
69
241
|
if (!Array.isArray(value) || value.length !== 2 || typeof value[0] !== "number" || typeof value[1] !== "number" || !Number.isFinite(value[0]) || !Number.isFinite(value[1])) {
|
|
70
242
|
ctx.error("Expected a [lng, lat] tuple of two finite numbers (GeoJSON order)");
|
|
@@ -86,10 +258,13 @@ function createDbValidatorPlugin() {
|
|
|
86
258
|
if (isTo || isFrom || isVia) return handleNavField(ctx, def, value, dbCtx, isTo, isFrom, isVia);
|
|
87
259
|
if (value === void 0 && (dbCtx.mode === "insert" || dbCtx.mode === "replace") && !complexItems.has(def)) {
|
|
88
260
|
const meta = def.metadata;
|
|
89
|
-
const
|
|
261
|
+
const baseMeta = columnUnionBase(def)?.metadata;
|
|
262
|
+
const defaulted = !(belowAmbiguous(dbCtx, ctx.path) || pathBelowAmbiguous(ctx.root, ctx.path));
|
|
263
|
+
const has = (key) => defaulted && (meta.has(key) || baseMeta?.has(key) === true);
|
|
264
|
+
const serverManaged = has("db.default") || has("db.default.increment") || has("db.default.uuid") || has("db.default.now") || meta.has("db.column.version") || meta.has("db.column.derived");
|
|
90
265
|
const hasFK = meta.has("db.rel.FK");
|
|
91
266
|
if (serverManaged || hasFK) {
|
|
92
|
-
if (dbCtx.mode === "replace" && meta.has("meta.id") && !
|
|
267
|
+
if (dbCtx.mode === "replace" && meta.has("meta.id") && !ctx.path.includes(".")) return;
|
|
93
268
|
return true;
|
|
94
269
|
}
|
|
95
270
|
}
|
|
@@ -149,13 +324,6 @@ function isFieldOpAllowed(path, flatMap, navFields) {
|
|
|
149
324
|
}
|
|
150
325
|
return true;
|
|
151
326
|
}
|
|
152
|
-
/** Returns true if the given path is nested inside a navigation field. */
|
|
153
|
-
function isInsideNavField(path, navFields) {
|
|
154
|
-
if (!navFields) return false;
|
|
155
|
-
let pos = path.length;
|
|
156
|
-
while ((pos = path.lastIndexOf(".", pos - 1)) !== -1) if (navFields.has(path.slice(0, pos))) return true;
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
327
|
function handleNavField(ctx, def, value, dbCtx, isTo, isFrom, isVia) {
|
|
160
328
|
const dotIdx = ctx.path.lastIndexOf(".");
|
|
161
329
|
const fieldName = dotIdx === -1 ? ctx.path : ctx.path.slice(dotIdx + 1);
|
|
@@ -455,6 +623,12 @@ Object.defineProperty(exports, "buildValidationContext", {
|
|
|
455
623
|
return buildValidationContext;
|
|
456
624
|
}
|
|
457
625
|
});
|
|
626
|
+
Object.defineProperty(exports, "columnUnionBase", {
|
|
627
|
+
enumerable: true,
|
|
628
|
+
get: function() {
|
|
629
|
+
return columnUnionBase;
|
|
630
|
+
}
|
|
631
|
+
});
|
|
458
632
|
Object.defineProperty(exports, "createDbValidatorPlugin", {
|
|
459
633
|
enumerable: true,
|
|
460
634
|
get: function() {
|
|
@@ -485,3 +659,21 @@ Object.defineProperty(exports, "isNavRelation", {
|
|
|
485
659
|
return isNavRelation;
|
|
486
660
|
}
|
|
487
661
|
});
|
|
662
|
+
Object.defineProperty(exports, "pathPresence", {
|
|
663
|
+
enumerable: true,
|
|
664
|
+
get: function() {
|
|
665
|
+
return pathPresence;
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
Object.defineProperty(exports, "soleUnionMember", {
|
|
669
|
+
enumerable: true,
|
|
670
|
+
get: function() {
|
|
671
|
+
return soleUnionMember;
|
|
672
|
+
}
|
|
673
|
+
});
|
|
674
|
+
Object.defineProperty(exports, "unionValueMembers", {
|
|
675
|
+
enumerable: true,
|
|
676
|
+
get: function() {
|
|
677
|
+
return unionValueMembers;
|
|
678
|
+
}
|
|
679
|
+
});
|
package/dist/validator.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_db_error = require("./db-error-JcfJqqpk.cjs");
|
|
3
|
+
const require_validator = require("./validator-DHwSyBej.cjs");
|
|
3
4
|
const require_object = require("./object-Djg28csK.cjs");
|
|
4
5
|
const require_ops = require("./ops.cjs");
|
|
5
|
-
const require_validator = require("./validator-BxWwh3wf.cjs");
|
|
6
6
|
exports.$cas = require_ops.$cas;
|
|
7
7
|
exports.$dec = require_ops.$dec;
|
|
8
8
|
exports.$inc = require_ops.$inc;
|
package/dist/validator.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as DbError } from "./db-error-eSsQX7rt.mjs";
|
|
2
|
+
import { a as forceNavNonOptional, c as getKeyProps, i as dbPlugin, n as buildPatchPartial, o as isNavRelation, r as buildValidationContext, s as createDbValidatorPlugin, t as buildDbValidator } from "./validator-BC3nwFi4.mjs";
|
|
2
3
|
import { a as isPlainObject, i as isEmptyObject } from "./object-TkiJQ-Dp.mjs";
|
|
3
4
|
import { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, isDbFieldOp, reconcileCas, separateCas } from "./ops.mjs";
|
|
4
|
-
import { a as forceNavNonOptional, c as getKeyProps, i as dbPlugin, n as buildPatchPartial, o as isNavRelation, r as buildValidationContext, s as createDbValidatorPlugin, t as buildDbValidator } from "./validator-BACewoRj.mjs";
|
|
5
5
|
export { $cas, $dec, $inc, $insert, $mul, $remove, $replace, $update, $upsert, DbError, buildDbValidator, buildPatchPartial, buildValidationContext, createDbValidatorPlugin, dbPlugin, forceNavNonOptional, getKeyProps, isDbFieldOp, isEmptyObject, isNavRelation, isPlainObject, reconcileCas, separateCas };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/db",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.155",
|
|
4
4
|
"description": "Database adapter utilities for atscript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"atscript",
|
|
@@ -71,14 +71,14 @@
|
|
|
71
71
|
"access": "public"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@atscript/core": "^0.1.
|
|
75
|
-
"@atscript/typescript": "^0.1.
|
|
74
|
+
"@atscript/core": "^0.1.105",
|
|
75
|
+
"@atscript/typescript": "^0.1.105",
|
|
76
76
|
"@uniqu/core": "^0.1.15",
|
|
77
|
-
"unplugin-atscript": "^0.1.
|
|
77
|
+
"unplugin-atscript": "^0.1.105"
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
|
-
"@atscript/core": "^0.1.
|
|
81
|
-
"@atscript/typescript": "^0.1.
|
|
80
|
+
"@atscript/core": "^0.1.105",
|
|
81
|
+
"@atscript/typescript": "^0.1.105",
|
|
82
82
|
"@uniqu/core": "^0.1.15"
|
|
83
83
|
},
|
|
84
84
|
"scripts": {
|