@atscript/db 0.1.163 → 0.1.165
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.cjs +1 -1
- package/dist/agg.d.cts +1 -1
- package/dist/agg.d.mts +1 -1
- package/dist/agg.mjs +1 -1
- package/dist/{aggregate-fns-DupYzFD7.cjs → aggregate-fns-CNtrSxGH.cjs} +1 -1
- package/dist/{aggregate-fns-DsS9UQVB.mjs → aggregate-fns-CmtSNIHm.mjs} +1 -1
- package/dist/{buckets-B8UOod-v.d.cts → buckets-BDxKfCNo.d.cts} +143 -13
- package/dist/{buckets-CHyspjYP.d.mts → buckets-CHUgky8T.d.mts} +143 -13
- package/dist/{column-diff-BfNIlMwX.mjs → column-diff-9pF0d8JP.mjs} +354 -31
- package/dist/{column-diff-BIGX3sRH.cjs → column-diff-DjeUj9kb.cjs} +371 -30
- package/dist/control-B5BJypqL.cjs +16 -0
- package/dist/control-CevYIJC4.mjs +16 -0
- package/dist/{db-error-hraC12B9.mjs → db-error-BMy4-OWq.mjs} +23 -1
- package/dist/{db-error-B_DRL1E9.d.cts → db-error-DQUw2cED.d.cts} +65 -9
- package/dist/{db-error-B_DRL1E9.d.mts → db-error-DQUw2cED.d.mts} +65 -9
- package/dist/{db-error-DmF1_xrz.cjs → db-error-G3sgvL87.cjs} +28 -0
- package/dist/{fk-diff-BNGawZi9.d.cts → fk-diff-DSM7O7hs.d.cts} +1 -1
- package/dist/{fk-diff-rj9N2QLw.d.mts → fk-diff-Dapr5Dk-.d.mts} +1 -1
- package/dist/index.cjs +17 -11
- package/dist/index.d.cts +38 -12
- package/dist/index.d.mts +38 -12
- package/dist/index.mjs +14 -12
- package/dist/{nested-writer-ClUX0DB0.mjs → nested-writer-BraWjS8a.mjs} +177 -26
- package/dist/{nested-writer-yCy6wGuN.cjs → nested-writer-DOvdbYG-.cjs} +194 -31
- package/dist/ops.cjs +1 -1
- package/dist/ops.mjs +1 -1
- package/dist/plugin.cjs +15 -12
- package/dist/plugin.mjs +15 -12
- 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-DloMywNs.d.cts → relation-helpers-C4UNWYQu.d.cts} +1 -1
- package/dist/{relation-helpers-zwt19E7r.d.mts → relation-helpers-hdbuqwjV.d.mts} +1 -1
- package/dist/{relation-loader-Qv3LrQ42.mjs → relation-loader-BOEVhcBe.mjs} +1 -1
- package/dist/{relation-loader-DujL_Mh9.cjs → relation-loader-BrkakVF_.cjs} +1 -1
- package/dist/shared.cjs +3 -1
- package/dist/shared.d.cts +13 -2
- package/dist/shared.d.mts +13 -2
- package/dist/shared.mjs +2 -2
- package/dist/sync.cjs +107 -25
- package/dist/sync.d.cts +53 -6
- package/dist/sync.d.mts +53 -6
- package/dist/sync.mjs +107 -25
- package/dist/{validator-DK9Ijpo7.cjs → validator-D7E7T0Oy.cjs} +1 -1
- package/dist/{validator-DuDWsIdQ.mjs → validator-UpgPhwvH.mjs} +1 -1
- package/dist/validator.cjs +2 -2
- package/dist/validator.d.cts +1 -1
- package/dist/validator.d.mts +1 -1
- package/dist/validator.mjs +2 -2
- package/dist/{view-validation-BiDLCYMw.cjs → view-validation-C0UVLx_H.cjs} +38 -10
- package/dist/{view-validation-BcgDq7M9.mjs → view-validation-KW77xIKn.mjs} +28 -12
- package/package.json +6 -6
- package/dist/control-D5cQmzFj.mjs +0 -14
- package/dist/control-jIVU2hkf.cjs +0 -14
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const require_db_error = require("./db-error-
|
|
2
|
-
const require_aggregate_fns = require("./aggregate-fns-
|
|
3
|
-
const require_nested_writer = require("./nested-writer-
|
|
4
|
-
const require_validator = require("./validator-
|
|
1
|
+
const require_db_error = require("./db-error-G3sgvL87.cjs");
|
|
2
|
+
const require_aggregate_fns = require("./aggregate-fns-CNtrSxGH.cjs");
|
|
3
|
+
const require_nested_writer = require("./nested-writer-DOvdbYG-.cjs");
|
|
4
|
+
const require_validator = require("./validator-D7E7T0Oy.cjs");
|
|
5
5
|
const require_search_fields = require("./search-fields-B49-RCWd.cjs");
|
|
6
6
|
const require_object = require("./object-GjsuPNqo.cjs");
|
|
7
7
|
require("./agg.cjs");
|
|
@@ -10,6 +10,74 @@ const require_ops = require("./ops.cjs");
|
|
|
10
10
|
let _atscript_typescript_utils = require("@atscript/typescript/utils");
|
|
11
11
|
let _uniqu_core = require("@uniqu/core");
|
|
12
12
|
let node_async_hooks = require("node:async_hooks");
|
|
13
|
+
//#region src/shared/with-nesting.ts
|
|
14
|
+
/**
|
|
15
|
+
* Rewrites dotted `$with` names into nesting (since 0.1.165):
|
|
16
|
+
* `{ name: "a.b", filter, controls }` becomes
|
|
17
|
+
* `{ name: "a", controls: { $with: [{ name: "b", filter, controls }] } }`,
|
|
18
|
+
* at every depth. Entries naming the same relation at one level are merged
|
|
19
|
+
* into one — the first one's `filter` / `controls` win, the nested `$with`
|
|
20
|
+
* lists are concatenated — so `$with=a($select=x),a.b` loads `a` once with
|
|
21
|
+
* `b` nested in it. Before, a dotted name was silently skipped.
|
|
22
|
+
*
|
|
23
|
+
* Returns the input array itself when no entry at any level is dotted or a
|
|
24
|
+
* string (the common case allocates nothing). String entries (`"a"`, `"a.b"`
|
|
25
|
+
* — the shorthand the `$with` type admits) are read as `{ name }`.
|
|
26
|
+
*/
|
|
27
|
+
function nestDottedWith(withRelations) {
|
|
28
|
+
if (!withRelations.some(needsRewrite)) return withRelations;
|
|
29
|
+
const out = [];
|
|
30
|
+
const byName = /* @__PURE__ */ new Map();
|
|
31
|
+
for (const raw of withRelations) {
|
|
32
|
+
const rel = typeof raw === "string" ? { name: raw } : raw;
|
|
33
|
+
const dot = rel.name.indexOf(".");
|
|
34
|
+
const head = dot < 0 ? rel.name : rel.name.slice(0, dot);
|
|
35
|
+
const entry = dot < 0 ? { ...rel } : {
|
|
36
|
+
name: head,
|
|
37
|
+
controls: { $with: [{
|
|
38
|
+
...rel,
|
|
39
|
+
name: rel.name.slice(dot + 1)
|
|
40
|
+
}] }
|
|
41
|
+
};
|
|
42
|
+
const existing = byName.get(head);
|
|
43
|
+
if (!existing) {
|
|
44
|
+
byName.set(head, entry);
|
|
45
|
+
out.push(entry);
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const extra = entry.controls?.$with;
|
|
49
|
+
existing.filter ??= entry.filter;
|
|
50
|
+
const controls = {
|
|
51
|
+
...entry.controls,
|
|
52
|
+
...existing.controls
|
|
53
|
+
};
|
|
54
|
+
const nested = [...existing.controls?.$with ?? [], ...extra ?? []];
|
|
55
|
+
if (nested.length > 0) controls.$with = nested;
|
|
56
|
+
existing.controls = controls;
|
|
57
|
+
}
|
|
58
|
+
for (const entry of out) {
|
|
59
|
+
const nested = entry.controls?.$with;
|
|
60
|
+
if (nested?.length) {
|
|
61
|
+
const next = nestDottedWith(nested);
|
|
62
|
+
if (next !== nested) entry.controls = {
|
|
63
|
+
...entry.controls,
|
|
64
|
+
$with: next
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* An entry is rewritten when it (or a nested one) is dotted, or is the
|
|
72
|
+
* string shorthand (typed as allowed, but the loaders read `.name`).
|
|
73
|
+
*/
|
|
74
|
+
function needsRewrite(raw) {
|
|
75
|
+
if (typeof raw === "string") return true;
|
|
76
|
+
if (raw.name.includes(".")) return true;
|
|
77
|
+
const nested = raw.controls?.$with;
|
|
78
|
+
return Array.isArray(nested) && nested.some(needsRewrite);
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
13
81
|
//#region src/logger.ts
|
|
14
82
|
const NoopLogger = {
|
|
15
83
|
error: () => {},
|
|
@@ -511,6 +579,12 @@ var TableMetadata = class {
|
|
|
511
579
|
*/
|
|
512
580
|
onUpdateNow = /* @__PURE__ */ new Set();
|
|
513
581
|
/**
|
|
582
|
+
* Top-level `number.timestamp.created` fields (since 0.1.165): set by the
|
|
583
|
+
* insert only — a replace keeps the stored value, a replace or patch
|
|
584
|
+
* payload's value is dropped.
|
|
585
|
+
*/
|
|
586
|
+
insertOnly = /* @__PURE__ */ new Set();
|
|
587
|
+
/**
|
|
514
588
|
* Top-level fields annotated with `@db.onUpdate.inc`: `0` on insert, `+1` in
|
|
515
589
|
* the same statement on every update and replace.
|
|
516
590
|
* @since 0.1.162
|
|
@@ -848,6 +922,7 @@ var TableMetadata = class {
|
|
|
848
922
|
const resolvedDefault = resolveDefaultFromMetadata(metadata);
|
|
849
923
|
if (resolvedDefault) this.defaults.set(fieldName, resolvedDefault);
|
|
850
924
|
if (metadata.has("db.onUpdate.now")) this.onUpdateNow.add(fieldName);
|
|
925
|
+
else if (!fieldName.includes(".") && hasPrimitiveTag(fieldType, "created")) this.insertOnly.add(fieldName);
|
|
851
926
|
if (metadata.has("db.onUpdate.inc")) {
|
|
852
927
|
if (fieldName.includes(".")) throw new Error(`@db.onUpdate.inc on "${fieldName}" must sit on a top-level field of the table`);
|
|
853
928
|
this.onUpdateInc.add(fieldName);
|
|
@@ -880,9 +955,10 @@ var TableMetadata = class {
|
|
|
880
955
|
if (metadata.has("db.rel.FK")) {
|
|
881
956
|
const raw = metadata.get("db.rel.FK");
|
|
882
957
|
const alias = raw === true ? void 0 : raw;
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
const
|
|
958
|
+
const fkRef = fieldType.ref ?? require_validator.soleUnionMember(fieldType)?.ref;
|
|
959
|
+
if (fkRef) {
|
|
960
|
+
const targetTable = require_nested_writer.tableNameOf(fkRef.type());
|
|
961
|
+
const targetField = fkRef.field;
|
|
886
962
|
const key = alias || `__auto_${fieldName}`;
|
|
887
963
|
const existing = this.foreignKeys.get(key);
|
|
888
964
|
if (existing) {
|
|
@@ -892,7 +968,7 @@ var TableMetadata = class {
|
|
|
892
968
|
fields: [fieldName],
|
|
893
969
|
targetTable,
|
|
894
970
|
targetFields: [targetField],
|
|
895
|
-
targetTypeRef:
|
|
971
|
+
targetTypeRef: fkRef.type,
|
|
896
972
|
alias
|
|
897
973
|
});
|
|
898
974
|
}
|
|
@@ -1242,7 +1318,8 @@ var TableMetadata = class {
|
|
|
1242
1318
|
encrypted: isEncrypted || underEncrypted || void 0,
|
|
1243
1319
|
isGeoPoint: isGeoPointType(colType) || void 0,
|
|
1244
1320
|
derived: this.derivedFields.get(path),
|
|
1245
|
-
computed: computedMeta(rootType, path)
|
|
1321
|
+
computed: computedMeta(rootType, path),
|
|
1322
|
+
insertOnly: this.insertOnly.has(path) || void 0
|
|
1246
1323
|
});
|
|
1247
1324
|
}
|
|
1248
1325
|
const flatCache = /* @__PURE__ */ new Map();
|
|
@@ -1452,6 +1529,16 @@ function computedMeta(rootType, path) {
|
|
|
1452
1529
|
via: Object.freeze(computed.via)
|
|
1453
1530
|
} : void 0;
|
|
1454
1531
|
}
|
|
1532
|
+
/**
|
|
1533
|
+
* Whether the column of `type` is the tagged primitive: the type itself, or
|
|
1534
|
+
* the one value member of `T | null` ({@link columnUnionBase}). Not a
|
|
1535
|
+
* reference to another field (`a: Other.createdAt` does not share its
|
|
1536
|
+
* semantics), nor a wider union (`number.timestamp.created | string`).
|
|
1537
|
+
*/
|
|
1538
|
+
function hasPrimitiveTag(type, tag) {
|
|
1539
|
+
if (type.ref?.field) return false;
|
|
1540
|
+
return (require_validator.columnUnionBase(type) ?? type).type.tags?.has(tag) === true;
|
|
1541
|
+
}
|
|
1455
1542
|
//#endregion
|
|
1456
1543
|
//#region src/shared/search-term.ts
|
|
1457
1544
|
/**
|
|
@@ -1504,6 +1591,16 @@ function describeFulltext(index, textLabel) {
|
|
|
1504
1591
|
function defaultFulltextIndex(indexes) {
|
|
1505
1592
|
return indexes.find((i) => splitFulltextFields(i).text.length > 0) ?? indexes[0];
|
|
1506
1593
|
}
|
|
1594
|
+
/**
|
|
1595
|
+
* The fulltext index a search names (since 0.1.165): the one DECLARED with
|
|
1596
|
+
* that name (`@db.index.fulltext 'name'`), else the one whose internal key
|
|
1597
|
+
* (`atscript__fulltext__<name>`) it is — a deprecated alias kept for callers
|
|
1598
|
+
* that copied the key from an older `getSearchIndexes()`. `undefined` when
|
|
1599
|
+
* none matches; a search naming no index uses {@link defaultFulltextIndex}.
|
|
1600
|
+
*/
|
|
1601
|
+
function resolveFulltextIndex(indexes, indexName) {
|
|
1602
|
+
return indexes.find((i) => i.name === indexName) ?? indexes.find((i) => i.key === indexName);
|
|
1603
|
+
}
|
|
1507
1604
|
//#endregion
|
|
1508
1605
|
//#region src/query/filter-values.ts
|
|
1509
1606
|
const OPAQUE = {
|
|
@@ -4444,10 +4541,26 @@ var AtscriptDbReadable = class {
|
|
|
4444
4541
|
*/
|
|
4445
4542
|
get onUpdateIncPhysical() {
|
|
4446
4543
|
this._ensureBuilt();
|
|
4447
|
-
return this._onUpdateIncPhysical ??=
|
|
4544
|
+
return this._onUpdateIncPhysical ??= this._physicalNames(this._meta.onUpdateInc);
|
|
4448
4545
|
}
|
|
4449
4546
|
_onUpdateIncPhysical;
|
|
4450
4547
|
/**
|
|
4548
|
+
* Physical column names of the insert-only fields (`number.timestamp.created`,
|
|
4549
|
+
* since 0.1.165): a full replace keeps their stored values. Empty when
|
|
4550
|
+
* there are none.
|
|
4551
|
+
*
|
|
4552
|
+
* @internal Adapter-facing surface; not part of the consumer API.
|
|
4553
|
+
*/
|
|
4554
|
+
get insertOnlyPhysical() {
|
|
4555
|
+
this._ensureBuilt();
|
|
4556
|
+
return this._insertOnlyPhysical ??= this._physicalNames(this._meta.insertOnly);
|
|
4557
|
+
}
|
|
4558
|
+
_insertOnlyPhysical;
|
|
4559
|
+
/** The physical columns of a set of top-level logical fields. */
|
|
4560
|
+
_physicalNames(fields) {
|
|
4561
|
+
return [...fields].map((field) => this._meta.columnMap.get(field) ?? field);
|
|
4562
|
+
}
|
|
4563
|
+
/**
|
|
4451
4564
|
* Logical paths of the `@db.column.version.exempt` fields, including objects
|
|
4452
4565
|
* whose direct children are all exempt. A patch that writes only these (and
|
|
4453
4566
|
* no `$cas`) does not bump the version column. Empty when the table has no
|
|
@@ -4606,8 +4719,22 @@ var AtscriptDbReadable = class {
|
|
|
4606
4719
|
*/
|
|
4607
4720
|
_translateRead(query) {
|
|
4608
4721
|
let readQuery = query ?? {};
|
|
4609
|
-
|
|
4610
|
-
|
|
4722
|
+
let controls = readQuery.controls;
|
|
4723
|
+
let withRelations = controls?.$with;
|
|
4724
|
+
if (withRelations?.length) {
|
|
4725
|
+
const nested = nestDottedWith(withRelations);
|
|
4726
|
+
if (nested !== withRelations) {
|
|
4727
|
+
withRelations = nested;
|
|
4728
|
+
controls = {
|
|
4729
|
+
...controls,
|
|
4730
|
+
$with: nested
|
|
4731
|
+
};
|
|
4732
|
+
readQuery = {
|
|
4733
|
+
...readQuery,
|
|
4734
|
+
controls
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4611
4738
|
let widened = [];
|
|
4612
4739
|
if (withRelations?.length && controls?.$select) {
|
|
4613
4740
|
const widen = this._widenSelectForWith(controls.$select, withRelations);
|
|
@@ -5534,8 +5661,8 @@ var AtscriptDbReadable = class {
|
|
|
5534
5661
|
* Public entry point for relation loading. Used by adapters for nested $with delegation.
|
|
5535
5662
|
*/
|
|
5536
5663
|
async loadRelations(rows, withRelations) {
|
|
5537
|
-
const { loadRelationsImpl } = await Promise.resolve().then(() => require("./relation-loader-
|
|
5538
|
-
return loadRelationsImpl(rows, withRelations, this);
|
|
5664
|
+
const { loadRelationsImpl } = await Promise.resolve().then(() => require("./relation-loader-BrkakVF_.cjs")).then((n) => n.relation_loader_exports);
|
|
5665
|
+
return loadRelationsImpl(rows, nestDottedWith(withRelations), this);
|
|
5539
5666
|
}
|
|
5540
5667
|
/**
|
|
5541
5668
|
* Finds the FK entry that connects a `@db.rel.to` relation to its target.
|
|
@@ -6779,10 +6906,7 @@ var ApplicationIntegrity = class extends IntegrityStrategy {
|
|
|
6779
6906
|
if (await target.count(filter) < expectedCount) {
|
|
6780
6907
|
const sample = firstValues.slice(0, 3).join(", ");
|
|
6781
6908
|
const suffix = firstValues.length > 3 ? `, ... (${firstValues.length} total)` : "";
|
|
6782
|
-
throw new require_db_error.DbError("FK_VIOLATION",
|
|
6783
|
-
path: fk.fields.join(", "),
|
|
6784
|
-
message: `FK constraint violation: "${fk.fields.join(", ")}" references non-existent record in "${fk.targetTable}" (values: ${sample}${suffix})`
|
|
6785
|
-
}]);
|
|
6909
|
+
throw new require_db_error.DbError("FK_VIOLATION", require_nested_writer.fkMissingEntries(fk.fields, fk.targetTable, `values: ${sample}${suffix}`));
|
|
6786
6910
|
}
|
|
6787
6911
|
});
|
|
6788
6912
|
}
|
|
@@ -6860,10 +6984,11 @@ var ApplicationIntegrity = class extends IntegrityStrategy {
|
|
|
6860
6984
|
const childFilter = this.buildCascadeChildFilter(records, target.fk);
|
|
6861
6985
|
if (!childFilter) continue;
|
|
6862
6986
|
restrictChecks.push(target.count(childFilter).then((count) => {
|
|
6863
|
-
if (count > 0) throw new require_db_error.DbError("CONFLICT", [{
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6987
|
+
if (count > 0) throw new require_db_error.DbError("CONFLICT", [require_nested_writer.fkRestrictEntry(tableName, {
|
|
6988
|
+
referencedBy: target.childTable,
|
|
6989
|
+
count,
|
|
6990
|
+
fields: target.fk.fields
|
|
6991
|
+
})]);
|
|
6867
6992
|
}));
|
|
6868
6993
|
}
|
|
6869
6994
|
if (restrictChecks.length > 0) await Promise.all(restrictChecks);
|
|
@@ -6953,6 +7078,22 @@ function pinByPrimaryKey(rows, pk) {
|
|
|
6953
7078
|
return out;
|
|
6954
7079
|
}
|
|
6955
7080
|
//#endregion
|
|
7081
|
+
//#region src/table/fk-lookup.ts
|
|
7082
|
+
/**
|
|
7083
|
+
* The FK lookup target of a table: `count` and the key projection `find`
|
|
7084
|
+
* (`$select` of the target fields) that `validateRows({ fk: true })` diffs.
|
|
7085
|
+
* One wrapper for the space's resolver and a table's writable fallback.
|
|
7086
|
+
*/
|
|
7087
|
+
function fkLookupTargetOf(table) {
|
|
7088
|
+
return {
|
|
7089
|
+
count: (filter) => table.count({ filter }),
|
|
7090
|
+
find: (filter, fields) => table.findMany({
|
|
7091
|
+
filter,
|
|
7092
|
+
controls: { $select: [...fields] }
|
|
7093
|
+
})
|
|
7094
|
+
};
|
|
7095
|
+
}
|
|
7096
|
+
//#endregion
|
|
6956
7097
|
//#region src/patch/array-ops-resolver.ts
|
|
6957
7098
|
/**
|
|
6958
7099
|
* Resolves array patch operator keys (`__$insert`, `__$remove`, `__$upsert`,
|
|
@@ -7493,6 +7634,23 @@ const UPDATE_RESULT = {
|
|
|
7493
7634
|
};
|
|
7494
7635
|
/** Upper bound of keys per `touchMany` UPDATE statement (parameter-count safety). */
|
|
7495
7636
|
const TOUCH_MANY_CHUNK = 500;
|
|
7637
|
+
/** Distinct FK key tuples per `validateRows` lookup query (well under every bind limit). */
|
|
7638
|
+
const FK_LOOKUP_CHUNK = 500;
|
|
7639
|
+
/** A key tuple's identity for the in-memory diff — scalar-text, so `1` and `"1"` agree like the engine compares them. */
|
|
7640
|
+
function fkTupleKey(values) {
|
|
7641
|
+
return JSON.stringify(values.map((v) => v instanceof Date ? v.getTime() : String(v)));
|
|
7642
|
+
}
|
|
7643
|
+
/** The write mode a standard validator purpose validates in when called without a context. */
|
|
7644
|
+
/** The validator purpose a write mode validates with (`validateRows`, the write paths). */
|
|
7645
|
+
const MODE_PURPOSE = {
|
|
7646
|
+
insert: "insert",
|
|
7647
|
+
replace: "bulkReplace",
|
|
7648
|
+
patch: "bulkUpdate"
|
|
7649
|
+
};
|
|
7650
|
+
const PURPOSE_MODES = {
|
|
7651
|
+
...Object.fromEntries(Object.entries(MODE_PURPOSE).map(([mode, purpose]) => [purpose, mode])),
|
|
7652
|
+
patch: "patch"
|
|
7653
|
+
};
|
|
7496
7654
|
var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
7497
7655
|
_cascadeResolver;
|
|
7498
7656
|
_fkLookupResolver;
|
|
@@ -7526,13 +7684,175 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
7526
7684
|
* Adapters may define additional purposes.
|
|
7527
7685
|
*/
|
|
7528
7686
|
getValidator(purpose) {
|
|
7529
|
-
|
|
7530
|
-
|
|
7687
|
+
let validator = this.validators.get(purpose);
|
|
7688
|
+
if (!validator) {
|
|
7689
|
+
validator = this._withDefaultContext(this._buildValidator(purpose), purpose);
|
|
7531
7690
|
this.validators.set(purpose, validator);
|
|
7532
7691
|
}
|
|
7533
|
-
return
|
|
7692
|
+
return validator;
|
|
7693
|
+
}
|
|
7694
|
+
/**
|
|
7695
|
+
* A standard purpose's validator called WITHOUT a context validates in that
|
|
7696
|
+
* purpose's write mode (since 0.1.165): `getValidator("insert").validate(row, true)`
|
|
7697
|
+
* accepts what an insert accepts — omitted primary key, defaulted, FK and
|
|
7698
|
+
* navigation fields. Before, the db plugin saw no mode and ran inert while
|
|
7699
|
+
* navigation fields were forced required. An explicit context still wins.
|
|
7700
|
+
*/
|
|
7701
|
+
_withDefaultContext(validator, purpose) {
|
|
7702
|
+
const mode = PURPOSE_MODES[purpose];
|
|
7703
|
+
if (!mode) return validator;
|
|
7704
|
+
const validate = (value, safe, context) => validator.validate(value, safe, context ?? this._validationCtx(mode));
|
|
7705
|
+
return new Proxy(validator, { get: (target, key) => key === "validate" ? validate : Reflect.get(target, key, target) });
|
|
7706
|
+
}
|
|
7707
|
+
/** A fresh validation context of a write `mode` — the one its write paths build. */
|
|
7708
|
+
_validationCtx(mode) {
|
|
7709
|
+
this._ensureBuilt();
|
|
7710
|
+
return {
|
|
7711
|
+
mode,
|
|
7712
|
+
navFields: this._meta.navFields,
|
|
7713
|
+
...mode === "patch" ? { flatMap: this.flatMap } : {}
|
|
7714
|
+
};
|
|
7715
|
+
}
|
|
7716
|
+
/** {@link normalizeWriteErrors} for this table: its constraint errors normalized against its metadata. */
|
|
7717
|
+
_normalizeWrite(fn) {
|
|
7718
|
+
return require_nested_writer.normalizeWriteErrors(this._meta, this.tableName, fn);
|
|
7719
|
+
}
|
|
7720
|
+
/**
|
|
7721
|
+
* Validates rows exactly as a write in `mode` would, without writing
|
|
7722
|
+
* (since 0.1.165): each row is cloned, given the defaults / stamps that
|
|
7723
|
+
* write applies, and validated with that write's validator and context.
|
|
7724
|
+
* Returns one `{ index, path, message }` per problem — `[]` when every row
|
|
7725
|
+
* is valid; the caller's rows are not modified.
|
|
7726
|
+
*
|
|
7727
|
+
* `fk: true` also checks that every foreign key names an existing row
|
|
7728
|
+
* (by lookup, on every adapter — native-FK adapters otherwise leave that to
|
|
7729
|
+
* the engine at write time); a missing target is reported on each of the
|
|
7730
|
+
* key's fields. Rows that fail type validation are not looked up.
|
|
7731
|
+
*
|
|
7732
|
+
* @param mode - `'insert'` (default), `'replace'` or `'patch'`.
|
|
7733
|
+
*/
|
|
7734
|
+
async validateRows(rows, mode = "insert", opts) {
|
|
7735
|
+
this._ensureBuilt();
|
|
7736
|
+
const validator = this.getValidator(MODE_PURPOSE[mode]);
|
|
7737
|
+
const ctx = this._validationCtx(mode);
|
|
7738
|
+
this._applyDepthCtx(ctx, 0);
|
|
7739
|
+
const now = Date.now();
|
|
7740
|
+
const errors = [];
|
|
7741
|
+
const valid = [];
|
|
7742
|
+
for (let index = 0; index < rows.length; index++) {
|
|
7743
|
+
const raw = rows[index];
|
|
7744
|
+
let row = raw;
|
|
7745
|
+
if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
|
|
7746
|
+
const clone = _cloneWritePayload(raw);
|
|
7747
|
+
delete clone.$cas;
|
|
7748
|
+
if (mode !== "patch") this._applyDefaults(clone, now);
|
|
7749
|
+
if (mode !== "insert") this._stampOnUpdate(clone, now, mode === "replace");
|
|
7750
|
+
row = clone;
|
|
7751
|
+
}
|
|
7752
|
+
try {
|
|
7753
|
+
if (validator.validate(row, true, ctx)) {
|
|
7754
|
+
valid.push({
|
|
7755
|
+
index,
|
|
7756
|
+
row
|
|
7757
|
+
});
|
|
7758
|
+
continue;
|
|
7759
|
+
}
|
|
7760
|
+
for (const e of validator.errors) errors.push({
|
|
7761
|
+
index,
|
|
7762
|
+
path: e.path,
|
|
7763
|
+
message: e.message
|
|
7764
|
+
});
|
|
7765
|
+
} catch (error) {
|
|
7766
|
+
errors.push({
|
|
7767
|
+
index,
|
|
7768
|
+
path: error.field ?? "",
|
|
7769
|
+
message: error instanceof Error ? error.message : String(error)
|
|
7770
|
+
});
|
|
7771
|
+
}
|
|
7772
|
+
}
|
|
7773
|
+
if (opts?.fk && valid.length > 0) {
|
|
7774
|
+
errors.push(...await this._missingForeignKeys(valid, mode === "patch"));
|
|
7775
|
+
errors.sort((a, b) => a.index - b.index);
|
|
7776
|
+
}
|
|
7777
|
+
return errors;
|
|
7778
|
+
}
|
|
7779
|
+
/**
|
|
7780
|
+
* {@link validateRows}' FK lookup: one entry per FK field of each row naming
|
|
7781
|
+
* a missing target. Distinct key tuples are looked up in chunks of
|
|
7782
|
+
* {@link FK_LOOKUP_CHUNK} — reading the target keys that exist (`$in` for a
|
|
7783
|
+
* single-column key, `$or` for a composite one) and diffing in memory, so a
|
|
7784
|
+
* batch never exceeds a bind-parameter limit and a miss costs no extra
|
|
7785
|
+
* query. A lookup target without `find` (a custom resolver) is counted per
|
|
7786
|
+
* chunk, and per tuple only in a chunk that comes up short.
|
|
7787
|
+
*/
|
|
7788
|
+
async _missingForeignKeys(rows, partial) {
|
|
7789
|
+
const out = [];
|
|
7790
|
+
for (const fk of this._meta.foreignKeys.values()) {
|
|
7791
|
+
const target = this._fkTargetReader(fk);
|
|
7792
|
+
if (!target) continue;
|
|
7793
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
7794
|
+
for (const { index, row } of rows) {
|
|
7795
|
+
if (partial && !fk.fields.some((f) => f in row)) continue;
|
|
7796
|
+
const values = fk.fields.map((f) => row[f]);
|
|
7797
|
+
if (values.some((v) => v === null || v === void 0)) continue;
|
|
7798
|
+
const key = fkTupleKey(values);
|
|
7799
|
+
const entry = byKey.get(key);
|
|
7800
|
+
if (entry) entry.indexes.push(index);
|
|
7801
|
+
else byKey.set(key, {
|
|
7802
|
+
values,
|
|
7803
|
+
indexes: [index]
|
|
7804
|
+
});
|
|
7805
|
+
}
|
|
7806
|
+
const tuples = [...byKey.values()];
|
|
7807
|
+
const tf = fk.targetFields;
|
|
7808
|
+
const filterOf = (values) => Object.fromEntries(tf.map((f, i) => [f, values[i]]));
|
|
7809
|
+
for (let at = 0; at < tuples.length; at += FK_LOOKUP_CHUNK) {
|
|
7810
|
+
const chunk = tuples.slice(at, at + FK_LOOKUP_CHUNK);
|
|
7811
|
+
const filter = tf.length === 1 ? { [tf[0]]: { $in: chunk.map((t) => t.values[0]) } } : { $or: chunk.map((t) => filterOf(t.values)) };
|
|
7812
|
+
let missing;
|
|
7813
|
+
if (target.find) {
|
|
7814
|
+
const found = new Set((await target.find(filter, tf)).map((r) => fkTupleKey(tf.map((f) => r[f]))));
|
|
7815
|
+
missing = chunk.filter((t) => !found.has(fkTupleKey(t.values)));
|
|
7816
|
+
} else {
|
|
7817
|
+
if (await target.count(filter) >= chunk.length) continue;
|
|
7818
|
+
missing = [];
|
|
7819
|
+
for (const t of chunk) if (await target.count(filterOf(t.values)) === 0) missing.push(t);
|
|
7820
|
+
}
|
|
7821
|
+
for (const tuple of missing) {
|
|
7822
|
+
const shown = tuple.values.map((v) => String(v)).join(", ");
|
|
7823
|
+
for (const index of tuple.indexes) for (const field of fk.fields) out.push({
|
|
7824
|
+
index,
|
|
7825
|
+
path: field,
|
|
7826
|
+
message: `References a non-existent record in "${fk.targetTable}" (${shown})`
|
|
7827
|
+
});
|
|
7828
|
+
}
|
|
7829
|
+
}
|
|
7830
|
+
}
|
|
7831
|
+
return out;
|
|
7832
|
+
}
|
|
7833
|
+
/** How {@link _missingForeignKeys} reads an FK's target table, if it can. */
|
|
7834
|
+
_fkTargetReader(fk) {
|
|
7835
|
+
const lookup = this._fkLookupResolver?.(fk.targetTable);
|
|
7836
|
+
if (lookup) return lookup;
|
|
7837
|
+
if (!fk.targetTypeRef || !this._writeTableResolver) return void 0;
|
|
7838
|
+
const table = this._writeTableResolver(fk.targetTypeRef());
|
|
7839
|
+
return table ? fkLookupTargetOf(table) : void 0;
|
|
7534
7840
|
}
|
|
7535
7841
|
async insertOne(payload, opts) {
|
|
7842
|
+
const { returning, ...insertOpts } = opts ?? {};
|
|
7843
|
+
if (!returning) return this._insertOneRow(payload, insertOpts);
|
|
7844
|
+
return this.adapter.withTransaction(async () => {
|
|
7845
|
+
const result = await this._insertOneRow(payload, insertOpts);
|
|
7846
|
+
if (result.insertedId === void 0) return result;
|
|
7847
|
+
const controls = returning === true ? void 0 : { $select: [...returning] };
|
|
7848
|
+
const row = await this.findById(result.insertedId, controls ? { controls } : void 0);
|
|
7849
|
+
return {
|
|
7850
|
+
...result,
|
|
7851
|
+
row
|
|
7852
|
+
};
|
|
7853
|
+
});
|
|
7854
|
+
}
|
|
7855
|
+
async _insertOneRow(payload, opts) {
|
|
7536
7856
|
const result = await this._insertMany([payload], {
|
|
7537
7857
|
...opts,
|
|
7538
7858
|
_action: "insert"
|
|
@@ -7562,7 +7882,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
7562
7882
|
const canNest = depth < maxDepth && this._writeTableResolver && this._meta.navFields.size > 0;
|
|
7563
7883
|
if (!canNest && this._meta.navFields.size > 0) require_nested_writer.checkDepthOverflow(payloads, maxDepth, this._meta);
|
|
7564
7884
|
const singleStatement = !ignore && this._isSingleStatementWrite(payloads, depth, guard, check);
|
|
7565
|
-
return
|
|
7885
|
+
return this._normalizeWrite(() => this._inWriteTransaction(singleStatement, async () => {
|
|
7566
7886
|
const clone = depth === 0 ? _cloneWritePayload : _shallowPrunedClone;
|
|
7567
7887
|
const now = Date.now();
|
|
7568
7888
|
let items = payloads.map((p) => this._applyDefaults(clone(p), now));
|
|
@@ -7637,7 +7957,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
7637
7957
|
const canNest = depth < maxDepth && this._writeTableResolver && this._meta.navFields.size > 0;
|
|
7638
7958
|
if (!canNest && this._meta.navFields.size > 0) require_nested_writer.checkDepthOverflow(payloads, maxDepth, this._meta);
|
|
7639
7959
|
const singleStatement = this._isSingleStatementWrite(payloads, depth, guard, check);
|
|
7640
|
-
return
|
|
7960
|
+
return this._normalizeWrite(() => this._inWriteTransaction(singleStatement, async () => {
|
|
7641
7961
|
const versionColumn = this.versionColumn;
|
|
7642
7962
|
const expectedVersions = Array.from({ length: payloads.length });
|
|
7643
7963
|
const clone = depth === 0 ? _cloneWritePayload : _shallowPrunedClone;
|
|
@@ -7727,7 +8047,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
7727
8047
|
const canNest = depth < maxDepth && this._writeTableResolver && this._meta.navFields.size > 0;
|
|
7728
8048
|
if (!canNest && this._meta.navFields.size > 0) require_nested_writer.checkDepthOverflow(payloads, maxDepth, this._meta);
|
|
7729
8049
|
const singleStatement = this._isSingleStatementWrite(payloads, depth, guard, check);
|
|
7730
|
-
return
|
|
8050
|
+
return this._normalizeWrite(() => this._inWriteTransaction(singleStatement, async () => {
|
|
7731
8051
|
const versionColumn = this.versionColumn;
|
|
7732
8052
|
const expectedVersions = Array.from({ length: payloads.length });
|
|
7733
8053
|
const clone = depth === 0 ? _cloneWritePayload : _shallowPrunedClone;
|
|
@@ -7987,7 +8307,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
7987
8307
|
modifiedCount: 0
|
|
7988
8308
|
};
|
|
7989
8309
|
}
|
|
7990
|
-
return
|
|
8310
|
+
return this._normalizeWrite(() => {
|
|
7991
8311
|
if (!bound) return this.adapter.updateMany(translatedFilter, update, ops, ...updateOptsArg(keepVersion));
|
|
7992
8312
|
return this._runBounded(bound, UPDATE_RESULT, (f, limit) => this.adapter.updateMany(f, update, ops, ...updateOptsArg(keepVersion, limit)));
|
|
7993
8313
|
});
|
|
@@ -8013,7 +8333,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
8013
8333
|
const versionColumn = this.versionColumn;
|
|
8014
8334
|
if (versionColumn !== void 0) assertNoVersionWrites(dataCopy, versionColumn);
|
|
8015
8335
|
await this._encryptItems([dataCopy], "write");
|
|
8016
|
-
return
|
|
8336
|
+
return this._normalizeWrite(() => this.adapter.replaceMany(this._fieldMapper.translateFilter(filter, this._meta), this._fieldMapper.prepareForWrite(dataCopy, this._meta, this.adapter)));
|
|
8017
8337
|
}
|
|
8018
8338
|
/**
|
|
8019
8339
|
* Deletes every row matching `filter` (application-level cascades run first
|
|
@@ -8532,6 +8852,7 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
8532
8852
|
*/
|
|
8533
8853
|
_stampOnUpdate(data, now, topLevel) {
|
|
8534
8854
|
for (const field of this._meta.onUpdateInc) delete data[field];
|
|
8855
|
+
for (const field of this._meta.insertOnly) delete data[field];
|
|
8535
8856
|
for (const field of this._meta.onUpdateNow) if (!field.includes(".")) if (topLevel) data[field] = now;
|
|
8536
8857
|
else delete data[field];
|
|
8537
8858
|
else if (this._nestedDefaultFillable(field)) stampNested(data, field.split("."), 0, now);
|
|
@@ -8702,6 +9023,8 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
8702
9023
|
* Used by parent tables to validate FROM children before the main insert,
|
|
8703
9024
|
* ensuring errors are caught before the parent is committed.
|
|
8704
9025
|
*
|
|
9026
|
+
* @internal Nested-write plumbing — its FK step is a no-op on native-FK
|
|
9027
|
+
* adapters. To validate rows yourself, use {@link validateRows}.
|
|
8705
9028
|
* @param opts.excludeFkTargetTable - Skip FK validation to this table (the parent).
|
|
8706
9029
|
*/
|
|
8707
9030
|
async preValidateItems(items, opts) {
|
|
@@ -10205,6 +10528,12 @@ Object.defineProperty(exports, "fkKey", {
|
|
|
10205
10528
|
return fkKey;
|
|
10206
10529
|
}
|
|
10207
10530
|
});
|
|
10531
|
+
Object.defineProperty(exports, "fkLookupTargetOf", {
|
|
10532
|
+
enumerable: true,
|
|
10533
|
+
get: function() {
|
|
10534
|
+
return fkLookupTargetOf;
|
|
10535
|
+
}
|
|
10536
|
+
});
|
|
10208
10537
|
Object.defineProperty(exports, "geoIndexNotFoundMessage", {
|
|
10209
10538
|
enumerable: true,
|
|
10210
10539
|
get: function() {
|
|
@@ -10313,6 +10642,12 @@ Object.defineProperty(exports, "narrowerFilterOps", {
|
|
|
10313
10642
|
return narrowerFilterOps;
|
|
10314
10643
|
}
|
|
10315
10644
|
});
|
|
10645
|
+
Object.defineProperty(exports, "nestDottedWith", {
|
|
10646
|
+
enumerable: true,
|
|
10647
|
+
get: function() {
|
|
10648
|
+
return nestDottedWith;
|
|
10649
|
+
}
|
|
10650
|
+
});
|
|
10316
10651
|
Object.defineProperty(exports, "normalizeComputedSelect", {
|
|
10317
10652
|
enumerable: true,
|
|
10318
10653
|
get: function() {
|
|
@@ -10343,6 +10678,12 @@ Object.defineProperty(exports, "resolveDesignType", {
|
|
|
10343
10678
|
return resolveDesignType;
|
|
10344
10679
|
}
|
|
10345
10680
|
});
|
|
10681
|
+
Object.defineProperty(exports, "resolveFulltextIndex", {
|
|
10682
|
+
enumerable: true,
|
|
10683
|
+
get: function() {
|
|
10684
|
+
return resolveFulltextIndex;
|
|
10685
|
+
}
|
|
10686
|
+
});
|
|
10346
10687
|
Object.defineProperty(exports, "sameDefaultValue", {
|
|
10347
10688
|
enumerable: true,
|
|
10348
10689
|
get: function() {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
let _atscript_typescript_utils = require("@atscript/typescript/utils");
|
|
2
|
+
//#region src/schema/control.as
|
|
3
|
+
var AtscriptControl = class {
|
|
4
|
+
static __is_atscript_annotated_type = true;
|
|
5
|
+
static type = {};
|
|
6
|
+
static metadata = /* @__PURE__ */ new Map();
|
|
7
|
+
static id = "AtscriptControl";
|
|
8
|
+
static toJsonSchema() {
|
|
9
|
+
(0, _atscript_typescript_utils.throwFeatureDisabled)("JSON Schema", "jsonSchema", "emit.jsonSchema");
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
(0, _atscript_typescript_utils.$ak)("db.index.fulltext", "db.index.plain", "db.index.unique", "db.view.joins", "expect.pattern");
|
|
13
|
+
(0, _atscript_typescript_utils.$nr)("db.agg.avg", "db.agg.count", "db.agg.countDistinct", "db.agg.max", "db.agg.min", "db.agg.sum", "db.alias", "db.amount.currency.ref", "db.column", "db.column.collate", "db.column.derived", "db.column.dimension", "db.column.filterable", "db.column.measure", "db.column.precision", "db.column.renamed", "db.column.searchable", "db.column.sortable", "db.column.version", "db.column.version.exempt", "db.compute", "db.default", "db.default.increment", "db.default.now", "db.default.uuid", "db.depth.limit", "db.encrypted", "db.http.path", "db.ignore", "db.index.fulltext", "db.index.geo", "db.index.plain", "db.index.unique", "db.json", "db.onUpdate.inc", "db.onUpdate.now", "db.patch.strategy", "db.rel.FK", "db.rel.filter", "db.rel.filterable", "db.rel.from", "db.rel.onDelete", "db.rel.onUpdate", "db.rel.to", "db.rel.via", "db.schema", "db.search.filter", "db.search.vector", "db.search.vector.threshold", "db.sort.nulls", "db.space", "db.sync.method", "db.table", "db.table.filterable", "db.table.preferredId.uniqueIndex", "db.table.renamed", "db.table.sortable", "db.unit.ref", "db.view", "db.view.filter", "db.view.for", "db.view.having", "db.view.joins", "db.view.materialized", "db.view.renamed", "db.writeOnly", "meta.default", "meta.id", "meta.readonly", "meta.required");
|
|
14
|
+
(0, _atscript_typescript_utils.defineAnnotatedType)("object", AtscriptControl).annotate("db.table", "__atscript_control").prop("_id", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").annotate("meta.id", true).$type).prop("value", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("lockedBy", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("lockedAt", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("number").optional().$type).prop("expiresAt", (0, _atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("number").optional().$type);
|
|
15
|
+
//#endregion
|
|
16
|
+
exports.AtscriptControl = AtscriptControl;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { $ak, $nr, defineAnnotatedType, throwFeatureDisabled } from "@atscript/typescript/utils";
|
|
2
|
+
//#region src/schema/control.as
|
|
3
|
+
var AtscriptControl = class {
|
|
4
|
+
static __is_atscript_annotated_type = true;
|
|
5
|
+
static type = {};
|
|
6
|
+
static metadata = /* @__PURE__ */ new Map();
|
|
7
|
+
static id = "AtscriptControl";
|
|
8
|
+
static toJsonSchema() {
|
|
9
|
+
throwFeatureDisabled("JSON Schema", "jsonSchema", "emit.jsonSchema");
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
$ak("db.index.fulltext", "db.index.plain", "db.index.unique", "db.view.joins", "expect.pattern");
|
|
13
|
+
$nr("db.agg.avg", "db.agg.count", "db.agg.countDistinct", "db.agg.max", "db.agg.min", "db.agg.sum", "db.alias", "db.amount.currency.ref", "db.column", "db.column.collate", "db.column.derived", "db.column.dimension", "db.column.filterable", "db.column.measure", "db.column.precision", "db.column.renamed", "db.column.searchable", "db.column.sortable", "db.column.version", "db.column.version.exempt", "db.compute", "db.default", "db.default.increment", "db.default.now", "db.default.uuid", "db.depth.limit", "db.encrypted", "db.http.path", "db.ignore", "db.index.fulltext", "db.index.geo", "db.index.plain", "db.index.unique", "db.json", "db.onUpdate.inc", "db.onUpdate.now", "db.patch.strategy", "db.rel.FK", "db.rel.filter", "db.rel.filterable", "db.rel.from", "db.rel.onDelete", "db.rel.onUpdate", "db.rel.to", "db.rel.via", "db.schema", "db.search.filter", "db.search.vector", "db.search.vector.threshold", "db.sort.nulls", "db.space", "db.sync.method", "db.table", "db.table.filterable", "db.table.preferredId.uniqueIndex", "db.table.renamed", "db.table.sortable", "db.unit.ref", "db.view", "db.view.filter", "db.view.for", "db.view.having", "db.view.joins", "db.view.materialized", "db.view.renamed", "db.writeOnly", "meta.default", "meta.id", "meta.readonly", "meta.required");
|
|
14
|
+
defineAnnotatedType("object", AtscriptControl).annotate("db.table", "__atscript_control").prop("_id", defineAnnotatedType().designType("string").tags("string").annotate("meta.id", true).$type).prop("value", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("lockedBy", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("lockedAt", defineAnnotatedType().designType("number").tags("number").optional().$type).prop("expiresAt", defineAnnotatedType().designType("number").tags("number").optional().$type);
|
|
15
|
+
//#endregion
|
|
16
|
+
export { AtscriptControl };
|
|
@@ -9,6 +9,11 @@ var DbError = class extends Error {
|
|
|
9
9
|
code;
|
|
10
10
|
errors;
|
|
11
11
|
name = "DbError";
|
|
12
|
+
/**
|
|
13
|
+
* Set by {@link constraintViolation}: what the adapter could tell about the
|
|
14
|
+
* violated constraint, for the table layer to normalize. @internal
|
|
15
|
+
*/
|
|
16
|
+
hint;
|
|
12
17
|
constructor(code, errors, message) {
|
|
13
18
|
super(message ?? errors[0]?.message ?? "Database error");
|
|
14
19
|
this.code = code;
|
|
@@ -37,6 +42,23 @@ function isRetryableDbError(error) {
|
|
|
37
42
|
function isConflict(error) {
|
|
38
43
|
return error instanceof DbError && error.code === "CONFLICT";
|
|
39
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* The error an adapter throws for an engine unique / foreign-key violation
|
|
47
|
+
* (since 0.1.165): `CONFLICT` (unique, primary key, blocked delete) or
|
|
48
|
+
* `FK_VIOLATION`, carrying the engine's `raw` text and whatever `hint` the
|
|
49
|
+
* engine gave. The table layer rewrites it into one entry per logical field
|
|
50
|
+
* of the violated constraint, with `code` / `params` and the raw text in
|
|
51
|
+
* `details`.
|
|
52
|
+
*/
|
|
53
|
+
function constraintViolation(code, hint, raw) {
|
|
54
|
+
const error = new DbError(code, [{
|
|
55
|
+
path: hint.columns?.[0] ?? "",
|
|
56
|
+
message: raw,
|
|
57
|
+
details: raw
|
|
58
|
+
}]);
|
|
59
|
+
error.hint = hint;
|
|
60
|
+
return error;
|
|
61
|
+
}
|
|
40
62
|
/** @internal An `INVALID_QUERY` error with a single `{ path, message }` entry. */
|
|
41
63
|
function invalidQuery(path, message) {
|
|
42
64
|
return new DbError("INVALID_QUERY", [{
|
|
@@ -145,4 +167,4 @@ function aggregateExpressionsNotSupported() {
|
|
|
145
167
|
}]);
|
|
146
168
|
}
|
|
147
169
|
//#endregion
|
|
148
|
-
export { aggregateExpressionsNotSupported as a,
|
|
170
|
+
export { aggregateExpressionsNotSupported as a, constraintViolation as c, isRetryableDbError as d, spaceClosedError as f, DepthLimitExceededError as i, invalidQuery as l, CasMismatchError as n, aggregateFailure as o, DbError as r, bucketTimeZoneUnavailable as s, CasExhaustedError as t, isConflict as u };
|