@atscript/db 0.1.43 → 0.1.45
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/{db-readable-Cc868K54.d.mts → db-readable-D9odJuSf.d.mts} +1 -1
- package/dist/{db-space-crCFv3DF.d.mts → db-space-7bf9_IWC.d.mts} +1 -1
- package/dist/{db-view-DSK76uc9.mjs → db-view-7Y5N4muD.mjs} +19 -266
- package/dist/{db-view-CcUjETIF.cjs → db-view-BMPRfGjB.cjs} +20 -279
- package/dist/index.cjs +16 -3
- package/dist/index.d.cts +3 -28
- package/dist/index.d.mts +6 -31
- package/dist/index.mjs +4 -3
- package/dist/ops.d.mts +1 -1
- package/dist/plugin.cjs +1 -1
- package/dist/plugin.mjs +1 -1
- package/dist/rel.d.mts +2 -2
- package/dist/shared.cjs +1 -1
- package/dist/shared.mjs +1 -1
- package/dist/sync.cjs +4 -3
- package/dist/sync.d.mts +2 -2
- package/dist/sync.mjs +5 -3
- package/dist/validator-0iGuvGOD.cjs +337 -0
- package/dist/validator-BeXlQISk.d.mts +69 -0
- package/dist/validator-D_7Fqzs4.mjs +296 -0
- package/dist/validator-_z_A3cKa.d.cts +69 -0
- package/dist/validator.cjs +19 -0
- package/dist/validator.d.cts +4 -0
- package/dist/validator.d.mts +4 -0
- package/dist/validator.mjs +3 -0
- package/package.json +11 -6
- /package/dist/{control-DRgryKeg.cjs → control-D1QdBO21.cjs} +0 -0
- /package/dist/{control-IANbnfjG.mjs → control-DBd_ff5-.mjs} +0 -0
- /package/dist/{db-validator-plugin-BLMVdi9z.d.mts → db-validator-plugin-KC4aNIQq.d.mts} +0 -0
- /package/dist/{ops-BdRAFLKY.d.mts → ops-DcHDxrjX.d.mts} +0 -0
- /package/dist/{validation-utils-DVJDijnB.cjs → validation-utils-BiG3pLP0.cjs} +0 -0
- /package/dist/{validation-utils-DhjIjP1-.mjs → validation-utils-aNrgK-cj.mjs} +0 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
const require_ops = require("./ops.cjs");
|
|
2
|
+
let _atscript_typescript_utils = require("@atscript/typescript/utils");
|
|
3
|
+
//#region src/patch/patch-types.ts
|
|
4
|
+
/**
|
|
5
|
+
* Extracts `@expect.array.key` properties from an array-of-objects type.
|
|
6
|
+
* These keys uniquely identify an element inside the array and are used
|
|
7
|
+
* for `$update`, `$remove`, and `$upsert` operations.
|
|
8
|
+
*
|
|
9
|
+
* @param def - Atscript array type definition.
|
|
10
|
+
* @returns Set of property names marked as keys; empty set if none.
|
|
11
|
+
*/
|
|
12
|
+
function getKeyProps(def) {
|
|
13
|
+
if (def.type.of.type.kind === "object") {
|
|
14
|
+
const objType = def.type.of.type;
|
|
15
|
+
const keyProps = /* @__PURE__ */ new Set();
|
|
16
|
+
for (const [key, val] of objType.props.entries()) if (val.metadata.get("expect.array.key")) keyProps.add(key);
|
|
17
|
+
return keyProps;
|
|
18
|
+
}
|
|
19
|
+
return /* @__PURE__ */ new Set();
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/db-validator-plugin.ts
|
|
23
|
+
/** Set of recognised array‑patch operator keys. */
|
|
24
|
+
const PATCH_OPS = new Set([
|
|
25
|
+
"$replace",
|
|
26
|
+
"$insert",
|
|
27
|
+
"$upsert",
|
|
28
|
+
"$update",
|
|
29
|
+
"$remove"
|
|
30
|
+
]);
|
|
31
|
+
/**
|
|
32
|
+
* Returns `true` when `value` looks like a patch‑operator object
|
|
33
|
+
* (at least one key is a recognised operator and no unknown keys).
|
|
34
|
+
*/
|
|
35
|
+
function isPatchOperatorObject(value) {
|
|
36
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
37
|
+
const keys = Object.keys(value);
|
|
38
|
+
if (keys.length === 0) return false;
|
|
39
|
+
return keys.every((k) => PATCH_OPS.has(k));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Validator plugin for database operations.
|
|
43
|
+
*
|
|
44
|
+
* Handles navigation field constraints and delegates to the standard validator
|
|
45
|
+
* for type checking. The annotated type tree already includes nav fields with
|
|
46
|
+
* their full target types — this plugin controls WHEN recursion is allowed
|
|
47
|
+
* based on the operation mode (insert/replace/patch).
|
|
48
|
+
*
|
|
49
|
+
* Replaces the old `navFieldsValidatorPlugin` (which blindly skipped all nav
|
|
50
|
+
* fields) and `_checkNavProps()` (which validated constraints separately).
|
|
51
|
+
*/
|
|
52
|
+
function createDbValidatorPlugin() {
|
|
53
|
+
return (ctx, def, value) => {
|
|
54
|
+
const dbCtx = ctx.context;
|
|
55
|
+
if (!dbCtx) return;
|
|
56
|
+
const isTo = def.metadata.has("db.rel.to");
|
|
57
|
+
const isFrom = def.metadata.has("db.rel.from");
|
|
58
|
+
const isVia = def.metadata.has("db.rel.via");
|
|
59
|
+
if (isTo || isFrom || isVia) return handleNavField(ctx, def, value, dbCtx, isTo, isFrom, isVia);
|
|
60
|
+
if (value === void 0 && (dbCtx.mode === "insert" || dbCtx.mode === "replace")) {
|
|
61
|
+
const meta = def.metadata;
|
|
62
|
+
const hasDefault = meta.has("db.default") || meta.has("db.default.increment") || meta.has("db.default.uuid") || meta.has("db.default.now");
|
|
63
|
+
const hasFK = meta.has("db.rel.FK");
|
|
64
|
+
if (hasDefault || hasFK) {
|
|
65
|
+
if (dbCtx.mode === "replace" && meta.has("meta.id") && !isInsideNavField(ctx.path, dbCtx.navFields)) return;
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (dbCtx.mode === "patch") {
|
|
70
|
+
if (require_ops.isDbFieldOp(value)) {
|
|
71
|
+
if (dbCtx.flatMap && !isFieldOpAllowed(ctx.path, dbCtx.flatMap, dbCtx.navFields)) {
|
|
72
|
+
ctx.error("Field operations ($inc/$dec/$mul) are not supported inside @db.json fields or nested objects without @db.patch.strategy \"merge\"");
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if (!(def.type.kind === "" && def.type.designType === "number")) {
|
|
76
|
+
ctx.error("Field operations ($inc/$dec/$mul) can only be applied to numeric fields");
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
if (def.type.kind === "array" && dbCtx.flatMap) {
|
|
82
|
+
const flatEntry = dbCtx.flatMap.get(ctx.path);
|
|
83
|
+
if (flatEntry?.metadata?.has("db.__topLevelArray") && !flatEntry.metadata.has("db.json")) return handleArrayPatch(ctx, def, value);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Checks whether a field op is valid at `path`.
|
|
90
|
+
*
|
|
91
|
+
* Rejects when:
|
|
92
|
+
* - The field itself is `@db.json` (ops on opaque blobs are meaningless).
|
|
93
|
+
* - Any ancestor is `@db.json`.
|
|
94
|
+
* - Any ancestor is a nested object without `@db.patch.strategy "merge"`.
|
|
95
|
+
*
|
|
96
|
+
* Accepts immediately when an ancestor is a navigation field (TO/FROM/VIA) —
|
|
97
|
+
* the nested data is extracted and validated against its own table separately.
|
|
98
|
+
*/
|
|
99
|
+
function isFieldOpAllowed(path, flatMap, navFields) {
|
|
100
|
+
if (flatMap.get(path)?.metadata.has("db.json")) return false;
|
|
101
|
+
let pos = path.length;
|
|
102
|
+
while ((pos = path.lastIndexOf(".", pos - 1)) !== -1) {
|
|
103
|
+
const ancestor = path.slice(0, pos);
|
|
104
|
+
if (navFields?.has(ancestor)) return true;
|
|
105
|
+
const entry = flatMap.get(ancestor);
|
|
106
|
+
if (!entry) continue;
|
|
107
|
+
if (entry.metadata.has("db.json")) return false;
|
|
108
|
+
if (entry.type.kind === "object" && entry.metadata.get("db.patch.strategy") !== "merge") return false;
|
|
109
|
+
}
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
/** Returns true if the given path is nested inside a navigation field. */
|
|
113
|
+
function isInsideNavField(path, navFields) {
|
|
114
|
+
if (!navFields) return false;
|
|
115
|
+
let pos = path.length;
|
|
116
|
+
while ((pos = path.lastIndexOf(".", pos - 1)) !== -1) if (navFields.has(path.slice(0, pos))) return true;
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
function handleNavField(ctx, def, value, dbCtx, isTo, isFrom, isVia) {
|
|
120
|
+
const dotIdx = ctx.path.lastIndexOf(".");
|
|
121
|
+
const fieldName = dotIdx === -1 ? ctx.path : ctx.path.slice(dotIdx + 1);
|
|
122
|
+
if (value === null) {
|
|
123
|
+
ctx.error(`Cannot process null navigation property '${fieldName}'`);
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if (value === void 0) return true;
|
|
127
|
+
if (dbCtx.mode === "patch") {
|
|
128
|
+
if (isFrom || isVia) {
|
|
129
|
+
if (isPatchOperatorObject(value)) return validateNavPatchOps(ctx, def, value, fieldName);
|
|
130
|
+
const relType = isFrom ? "1:N" : "M:N";
|
|
131
|
+
ctx.error(`Cannot patch ${relType} relation '${fieldName}' with a plain value — use patch operators ({ $insert, $remove, $replace, $update, $upsert })`);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (isVia) return true;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Validates patch operator values against the nav field's target array type.
|
|
139
|
+
* Each operator's items are validated against the element type.
|
|
140
|
+
*/
|
|
141
|
+
function validateNavPatchOps(ctx, def, ops, fieldName) {
|
|
142
|
+
if (def.type.kind !== "array") {
|
|
143
|
+
ctx.error(`Cannot use patch operators on non-array relation '${fieldName}'`);
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
const arrayDef = def;
|
|
147
|
+
for (const op of [
|
|
148
|
+
"$replace",
|
|
149
|
+
"$insert",
|
|
150
|
+
"$upsert"
|
|
151
|
+
]) if (ops[op] !== void 0) {
|
|
152
|
+
if (!ctx.validateAnnotatedType(arrayDef, ops[op])) return false;
|
|
153
|
+
}
|
|
154
|
+
for (const op of ["$update", "$remove"]) if (ops[op] !== void 0) {
|
|
155
|
+
if (!validatePartialItems(ctx, arrayDef, ops[op], op, true)) return false;
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Handles patch‑mode validation for top‑level embedded arrays.
|
|
161
|
+
*
|
|
162
|
+
* When the incoming value is:
|
|
163
|
+
* - A plain array → falls through to default array validation ($replace semantics)
|
|
164
|
+
* - A patch operator object → validates each operator's payload individually
|
|
165
|
+
*/
|
|
166
|
+
function handleArrayPatch(ctx, def, value) {
|
|
167
|
+
if (Array.isArray(value)) return;
|
|
168
|
+
if (typeof value !== "object" || value === null) return;
|
|
169
|
+
const ops = value;
|
|
170
|
+
const keys = Object.keys(ops);
|
|
171
|
+
if (keys.length === 0 || !keys.every((k) => PATCH_OPS.has(k))) {
|
|
172
|
+
if (keys.some((k) => PATCH_OPS.has(k))) {
|
|
173
|
+
const unknown = keys.filter((k) => !PATCH_OPS.has(k));
|
|
174
|
+
ctx.error(`Unknown patch operator(s): ${unknown.join(", ")}. Allowed: $replace, $insert, $upsert, $update, $remove`);
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
for (const op of [
|
|
180
|
+
"$replace",
|
|
181
|
+
"$insert",
|
|
182
|
+
"$upsert"
|
|
183
|
+
]) if (ops[op] !== void 0) {
|
|
184
|
+
if (!ctx.validateAnnotatedType(def, ops[op])) return false;
|
|
185
|
+
}
|
|
186
|
+
const isMerge = def.metadata.get("db.patch.strategy") === "merge";
|
|
187
|
+
for (const op of ["$update", "$remove"]) if (ops[op] !== void 0) {
|
|
188
|
+
if (!validatePartialItems(ctx, def, ops[op], op, isMerge)) return false;
|
|
189
|
+
}
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Validates `$update` / `$remove` items.
|
|
194
|
+
*
|
|
195
|
+
* Each item must be an object. For object arrays with `@expect.array.key` fields,
|
|
196
|
+
* key properties are required and non‑key properties are validated but optional.
|
|
197
|
+
* For primitive arrays, items are validated directly against the element type.
|
|
198
|
+
*/
|
|
199
|
+
function validatePartialItems(ctx, arrayDef, items, op, isMerge) {
|
|
200
|
+
if (!Array.isArray(items)) {
|
|
201
|
+
ctx.error(`${op} must be an array`);
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
const elementDef = arrayDef.type.of;
|
|
205
|
+
if (elementDef.type.kind !== "object") return ctx.validateAnnotatedType(arrayDef, items);
|
|
206
|
+
const keyProps = getKeyProps(arrayDef);
|
|
207
|
+
for (let i = 0; i < items.length; i++) {
|
|
208
|
+
const item = items[i];
|
|
209
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) {
|
|
210
|
+
ctx.error(`${op}[${i}]: expected object`);
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
const rec = item;
|
|
214
|
+
if (keyProps.size > 0) {
|
|
215
|
+
for (const kp of keyProps) if (rec[kp] === void 0 || rec[kp] === null) {
|
|
216
|
+
ctx.error(`${op}[${i}]: key field '${kp}' is required`);
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const objType = elementDef.type;
|
|
221
|
+
for (const [key, val] of Object.entries(rec)) {
|
|
222
|
+
const propDef = objType.props.get(key);
|
|
223
|
+
if (propDef) {
|
|
224
|
+
if (!ctx.validateAnnotatedType(propDef, val)) return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (op === "$update" && isMerge !== true) {
|
|
228
|
+
for (const [propName, propDef] of objType.props) if (!propDef.optional && !keyProps.has(propName) && rec[propName] === void 0) {
|
|
229
|
+
ctx.error(`${op}[${i}]: field '${propName}' is required (replace strategy)`);
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
//#endregion
|
|
237
|
+
//#region src/validator.ts
|
|
238
|
+
/** Singleton db validator plugin — stateless, safe to share across all validators. */
|
|
239
|
+
const dbPlugin = createDbValidatorPlugin();
|
|
240
|
+
/**
|
|
241
|
+
* Builds a validator for a given write mode.
|
|
242
|
+
*
|
|
243
|
+
* Shared between server-side (`AtscriptDbTable`) and client-side (`ClientValidator`).
|
|
244
|
+
* Both use the same plugin, same `replace`, same `partial` logic.
|
|
245
|
+
*
|
|
246
|
+
* @param type - The annotated type to validate against.
|
|
247
|
+
* @param mode - The write operation mode.
|
|
248
|
+
* @param extraPlugins - Additional adapter-specific plugins (prepended before the db plugin).
|
|
249
|
+
*/
|
|
250
|
+
function buildDbValidator(type, mode, extraPlugins) {
|
|
251
|
+
const plugins = extraPlugins ? [...extraPlugins, dbPlugin] : [dbPlugin];
|
|
252
|
+
return type.validator({
|
|
253
|
+
plugins,
|
|
254
|
+
partial: mode === "patch",
|
|
255
|
+
replace: forceNavNonOptional
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
/** Returns true if the annotated type is a navigation relation (TO/FROM/VIA). */
|
|
259
|
+
function isNavRelation(type) {
|
|
260
|
+
return !!type.metadata && (type.metadata.has("db.rel.to") || type.metadata.has("db.rel.from") || type.metadata.has("db.rel.via"));
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Forces nav fields non-optional so the plugin handles null/undefined checks.
|
|
264
|
+
* The Validator caches replace results internally, so this allocates at most once per type node.
|
|
265
|
+
*/
|
|
266
|
+
function forceNavNonOptional(type) {
|
|
267
|
+
if (!type.optional) return type;
|
|
268
|
+
if (isNavRelation(type)) return {
|
|
269
|
+
...type,
|
|
270
|
+
optional: false
|
|
271
|
+
};
|
|
272
|
+
return type;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Builds a lightweight validation context from a deserialized Atscript type.
|
|
276
|
+
*
|
|
277
|
+
* This is the client-side equivalent of the server's `TableMetadata.build()`,
|
|
278
|
+
* without any adapter-specific processing (physical columns, index resolution, etc.).
|
|
279
|
+
*
|
|
280
|
+
* @param type - A deserialized annotated type (from `deserializeAnnotatedType(meta.type)`).
|
|
281
|
+
* @returns `flatMap` and `navFields` suitable for passing to `createDbValidatorPlugin` via `DbValidationContext`.
|
|
282
|
+
*/
|
|
283
|
+
function buildValidationContext(type) {
|
|
284
|
+
const navFields = /* @__PURE__ */ new Set();
|
|
285
|
+
return {
|
|
286
|
+
flatMap: (0, _atscript_typescript_utils.flattenAnnotatedType)(type, {
|
|
287
|
+
topLevelArrayTag: "db.__topLevelArray",
|
|
288
|
+
onField: (path, _type, metadata) => {
|
|
289
|
+
if (metadata.has("db.rel.to") || metadata.has("db.rel.from") || metadata.has("db.rel.via")) navFields.add(path);
|
|
290
|
+
}
|
|
291
|
+
}),
|
|
292
|
+
navFields
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
//#endregion
|
|
296
|
+
Object.defineProperty(exports, "buildDbValidator", {
|
|
297
|
+
enumerable: true,
|
|
298
|
+
get: function() {
|
|
299
|
+
return buildDbValidator;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
Object.defineProperty(exports, "buildValidationContext", {
|
|
303
|
+
enumerable: true,
|
|
304
|
+
get: function() {
|
|
305
|
+
return buildValidationContext;
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
Object.defineProperty(exports, "createDbValidatorPlugin", {
|
|
309
|
+
enumerable: true,
|
|
310
|
+
get: function() {
|
|
311
|
+
return createDbValidatorPlugin;
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
Object.defineProperty(exports, "dbPlugin", {
|
|
315
|
+
enumerable: true,
|
|
316
|
+
get: function() {
|
|
317
|
+
return dbPlugin;
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
Object.defineProperty(exports, "forceNavNonOptional", {
|
|
321
|
+
enumerable: true,
|
|
322
|
+
get: function() {
|
|
323
|
+
return forceNavNonOptional;
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
Object.defineProperty(exports, "getKeyProps", {
|
|
327
|
+
enumerable: true,
|
|
328
|
+
get: function() {
|
|
329
|
+
return getKeyProps;
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
Object.defineProperty(exports, "isNavRelation", {
|
|
333
|
+
enumerable: true,
|
|
334
|
+
get: function() {
|
|
335
|
+
return isNavRelation;
|
|
336
|
+
}
|
|
337
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { TAtscriptAnnotatedType, TAtscriptTypeArray, TAtscriptTypeObject, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
|
|
2
|
+
|
|
3
|
+
//#region src/patch/patch-types.d.ts
|
|
4
|
+
interface TArrayPatch<A extends readonly unknown[]> {
|
|
5
|
+
$replace?: A;
|
|
6
|
+
$insert?: A;
|
|
7
|
+
$upsert?: A;
|
|
8
|
+
$update?: Array<Partial<TArrayElement<A>>>;
|
|
9
|
+
$remove?: Array<Partial<TArrayElement<A>>>;
|
|
10
|
+
}
|
|
11
|
+
type TArrayElement<ArrayType extends readonly unknown[]> = ArrayType extends ReadonlyArray<infer ElementType> ? ElementType : never;
|
|
12
|
+
/**
|
|
13
|
+
* Maps each property of T into a patch payload:
|
|
14
|
+
* - Array properties become `TArrayPatch<T[K]>`
|
|
15
|
+
* - Non-array properties become `Partial<T[K]>`
|
|
16
|
+
*/
|
|
17
|
+
type TDbPatch<T> = { [K in keyof T]?: T[K] extends Array<infer _> ? TArrayPatch<T[K]> : Partial<T[K]> };
|
|
18
|
+
/**
|
|
19
|
+
* Extracts `@expect.array.key` properties from an array-of-objects type.
|
|
20
|
+
* These keys uniquely identify an element inside the array and are used
|
|
21
|
+
* for `$update`, `$remove`, and `$upsert` operations.
|
|
22
|
+
*
|
|
23
|
+
* @param def - Atscript array type definition.
|
|
24
|
+
* @returns Set of property names marked as keys; empty set if none.
|
|
25
|
+
*/
|
|
26
|
+
declare function getKeyProps(def: TAtscriptAnnotatedType<TAtscriptTypeArray>): Set<string>;
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region src/validator.d.ts
|
|
29
|
+
/** Write operation mode for validator configuration. */
|
|
30
|
+
type ValidatorMode = "insert" | "patch" | "replace";
|
|
31
|
+
/** Singleton db validator plugin — stateless, safe to share across all validators. */
|
|
32
|
+
declare const dbPlugin: TValidatorPlugin;
|
|
33
|
+
/**
|
|
34
|
+
* Builds a validator for a given write mode.
|
|
35
|
+
*
|
|
36
|
+
* Shared between server-side (`AtscriptDbTable`) and client-side (`ClientValidator`).
|
|
37
|
+
* Both use the same plugin, same `replace`, same `partial` logic.
|
|
38
|
+
*
|
|
39
|
+
* @param type - The annotated type to validate against.
|
|
40
|
+
* @param mode - The write operation mode.
|
|
41
|
+
* @param extraPlugins - Additional adapter-specific plugins (prepended before the db plugin).
|
|
42
|
+
*/
|
|
43
|
+
declare function buildDbValidator(type: TAtscriptAnnotatedType, mode: ValidatorMode, extraPlugins?: TValidatorPlugin[]): Validator<any>;
|
|
44
|
+
/** Returns true if the annotated type is a navigation relation (TO/FROM/VIA). */
|
|
45
|
+
declare function isNavRelation(type: TAtscriptAnnotatedType): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Forces nav fields non-optional so the plugin handles null/undefined checks.
|
|
48
|
+
* The Validator caches replace results internally, so this allocates at most once per type node.
|
|
49
|
+
*/
|
|
50
|
+
declare function forceNavNonOptional(type: TAtscriptAnnotatedType): TAtscriptAnnotatedType;
|
|
51
|
+
/** Result of {@link buildValidationContext}. */
|
|
52
|
+
interface ValidationContext {
|
|
53
|
+
/** Flat map of dotted field paths → annotated types (same shape the server builds). */
|
|
54
|
+
flatMap: Map<string, TAtscriptAnnotatedType>;
|
|
55
|
+
/** Set of field paths that are navigation relations (TO/FROM/VIA). */
|
|
56
|
+
navFields: ReadonlySet<string>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Builds a lightweight validation context from a deserialized Atscript type.
|
|
60
|
+
*
|
|
61
|
+
* This is the client-side equivalent of the server's `TableMetadata.build()`,
|
|
62
|
+
* without any adapter-specific processing (physical columns, index resolution, etc.).
|
|
63
|
+
*
|
|
64
|
+
* @param type - A deserialized annotated type (from `deserializeAnnotatedType(meta.type)`).
|
|
65
|
+
* @returns `flatMap` and `navFields` suitable for passing to `createDbValidatorPlugin` via `DbValidationContext`.
|
|
66
|
+
*/
|
|
67
|
+
declare function buildValidationContext(type: TAtscriptAnnotatedType<TAtscriptTypeObject>): ValidationContext;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { dbPlugin as a, TArrayPatch as c, buildValidationContext as i, TDbPatch as l, ValidatorMode as n, forceNavNonOptional as o, buildDbValidator as r, isNavRelation as s, ValidationContext as t, getKeyProps as u };
|