@atscript/db 0.1.164 → 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
package/dist/plugin.cjs
CHANGED
|
@@ -33,10 +33,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
33
33
|
enumerable: true
|
|
34
34
|
}) : target, mod));
|
|
35
35
|
//#endregion
|
|
36
|
-
const require_aggregate_fns = require("./aggregate-fns-
|
|
36
|
+
const require_aggregate_fns = require("./aggregate-fns-CNtrSxGH.cjs");
|
|
37
37
|
const require_search_fields = require("./search-fields-B49-RCWd.cjs");
|
|
38
38
|
require("./consts-BzRfCcH2.cjs");
|
|
39
|
-
const require_view_validation = require("./view-validation-
|
|
39
|
+
const require_view_validation = require("./view-validation-C0UVLx_H.cjs");
|
|
40
40
|
let _atscript_core = require("@atscript/core");
|
|
41
41
|
let node_path = require("node:path");
|
|
42
42
|
node_path = __toESM(node_path, 1);
|
|
@@ -1083,6 +1083,12 @@ const dbIndexAnnotations = { index: {
|
|
|
1083
1083
|
description: (0, _atscript_core.annotationExample)("Full-text search index. Fields sharing the same index name form a composite full-text index. A string member is part of the engine's text index. An integer member (`number.int` and its sizes, `@expect.int`, `@db.default.increment`) is never part of the physical text index: when the whole search term is a whole number (`2946`, `-12`; no leading zeros), rows whose member equals it exactly also match (OR'd with the text match). An integer member must be index-backed — the primary key (first `@meta.id`) or the first field of a `@db.index.plain` / `@db.index.unique`. Floats, decimals and timestamps are refused.", "@db.index.fulltext \"ft_content\"", "title: string", "", "@db.index.fulltext \"ft_content\", 5", "bio: string", "", "@db.index.fulltext \"ft_content\"", "@db.index.unique", "refNo: number.int"),
|
|
1084
1084
|
validate(token, args, doc) {
|
|
1085
1085
|
const field = token.parentNode;
|
|
1086
|
+
const indexName = args[0]?.text;
|
|
1087
|
+
if (indexName?.startsWith("atscript__")) return [{
|
|
1088
|
+
message: `@db.index.fulltext name "${indexName}" is reserved — names starting with "atscript__" are internal index keys`,
|
|
1089
|
+
severity: 1,
|
|
1090
|
+
range: args[0].range
|
|
1091
|
+
}];
|
|
1086
1092
|
const sealed = require_view_validation.sealedSearchProblem(token, doc, "@db.index.fulltext");
|
|
1087
1093
|
if (sealed) return [sealed];
|
|
1088
1094
|
const verdict = require_view_validation.searchFieldVerdict(field, doc);
|
|
@@ -1156,8 +1162,8 @@ function checkCompositeFkCoverage(token, alias, refTypeName, targetStruct, targe
|
|
|
1156
1162
|
for (const [, sibling] of hostStruct.props) {
|
|
1157
1163
|
if (sibling.countAnnotations("db.rel.FK") === 0) continue;
|
|
1158
1164
|
if (require_view_validation.getAnnotationAlias(sibling, "db.rel.FK") !== alias) continue;
|
|
1159
|
-
const siblingDef = sibling.getDefinition();
|
|
1160
|
-
if (siblingDef &&
|
|
1165
|
+
const siblingDef = require_view_validation.fkChainRef(sibling.getDefinition());
|
|
1166
|
+
if (siblingDef && siblingDef.id === refTypeName) {
|
|
1161
1167
|
if (siblingDef.chain.length === 1) covered.add(siblingDef.chain[0].text);
|
|
1162
1168
|
}
|
|
1163
1169
|
}
|
|
@@ -1183,16 +1189,15 @@ const dbRelAnnotations = { rel: {
|
|
|
1183
1189
|
severity: 1,
|
|
1184
1190
|
range: token.range
|
|
1185
1191
|
});
|
|
1186
|
-
const
|
|
1187
|
-
if (!
|
|
1192
|
+
const ref = require_view_validation.fkChainRef(field.getDefinition());
|
|
1193
|
+
if (!ref) {
|
|
1188
1194
|
errors.push({
|
|
1189
|
-
message: `@db.rel.FK requires a chain reference type (e.g. User.id), got scalar type`,
|
|
1195
|
+
message: `@db.rel.FK requires a chain reference type (e.g. User.id or User.id | null), got scalar type`,
|
|
1190
1196
|
severity: 1,
|
|
1191
1197
|
range: token.range
|
|
1192
1198
|
});
|
|
1193
1199
|
return errors;
|
|
1194
1200
|
}
|
|
1195
|
-
const ref = definition;
|
|
1196
1201
|
const refTypeName = ref.id;
|
|
1197
1202
|
const chainFields = ref.chain.map((c) => c.text);
|
|
1198
1203
|
const targetUnwound = doc.unwindType(refTypeName);
|
|
@@ -1236,10 +1241,8 @@ const dbRelAnnotations = { rel: {
|
|
|
1236
1241
|
let sameTargetCount = 0;
|
|
1237
1242
|
for (const [, prop] of struct.props) {
|
|
1238
1243
|
if (prop.countAnnotations("db.rel.FK") === 0) continue;
|
|
1239
|
-
const
|
|
1240
|
-
if (!
|
|
1241
|
-
const r = def;
|
|
1242
|
-
if (!r.hasChain) continue;
|
|
1244
|
+
const r = require_view_validation.fkChainRef(prop.getDefinition());
|
|
1245
|
+
if (!r) continue;
|
|
1243
1246
|
if (r.id === refTypeName) {
|
|
1244
1247
|
if (!require_view_validation.getAnnotationAlias(prop, "db.rel.FK")) sameTargetCount++;
|
|
1245
1248
|
}
|
package/dist/plugin.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { i as NULL_WHEN_EMPTY_AGGREGATE_FNS, t as AGG_ANNOTATIONS } from "./aggregate-fns-
|
|
1
|
+
import { i as NULL_WHEN_EMPTY_AGGREGATE_FNS, t as AGG_ANNOTATIONS } from "./aggregate-fns-CmtSNIHm.mjs";
|
|
2
2
|
import { c as DERIVED_INCOMPATIBLE, l as JSON_LEAF_TYPES, o as numericTypeProblem, s as DB_ENTITY_ANNOTATIONS, t as ENCRYPTED_INCOMPATIBLE } from "./search-fields-BPNsxrCk.mjs";
|
|
3
3
|
import "./consts-C_-5_pFq.mjs";
|
|
4
|
-
import { A as
|
|
4
|
+
import { A as getParentStruct, B as validateFieldBaseType, C as validateRefArgument, D as getAnnotationAlias, E as effectiveAnnotationNames, H as validateSiblingStringField, I as primitiveBaseType, L as refActionAnnotation, M as integerMemberIndexProblem, O as getDbTableOwner, P as isNullableUnion, R as searchFieldVerdict, S as validateQueryScope, T as viewScopeTypes, V as validateIncompatible, _ as forEachFieldRef, b as isDbSourceDecl, c as refuseCiphertext, d as viewFieldSeals, f as viewProps, g as fkChainRef, h as findViewCycle, i as encryptedTypeSearchProblem, j as getParentTypeName, k as getNavTargetTypeName, l as sealedSearchProblem, m as findFKFieldsPointingTo, o as isNavProp, p as earlierJoinTargets, s as jsonChainInfo, u as validateViewInterface, v as hasAnyViewAnnotation, w as viewJoins, y as isAliasDecl, z as validateExclusiveWith } from "./view-validation-KW77xIKn.mjs";
|
|
5
5
|
import { AnnotationSpec, DEFAULT_FORMAT, annotationExample, getFieldsForType, isArray, isInterface, isPrimitive, isProp, isQueryComparison, isQueryLogical, isRef, isStructure } from "@atscript/core";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
@@ -1047,6 +1047,12 @@ const dbIndexAnnotations = { index: {
|
|
|
1047
1047
|
description: annotationExample("Full-text search index. Fields sharing the same index name form a composite full-text index. A string member is part of the engine's text index. An integer member (`number.int` and its sizes, `@expect.int`, `@db.default.increment`) is never part of the physical text index: when the whole search term is a whole number (`2946`, `-12`; no leading zeros), rows whose member equals it exactly also match (OR'd with the text match). An integer member must be index-backed — the primary key (first `@meta.id`) or the first field of a `@db.index.plain` / `@db.index.unique`. Floats, decimals and timestamps are refused.", "@db.index.fulltext \"ft_content\"", "title: string", "", "@db.index.fulltext \"ft_content\", 5", "bio: string", "", "@db.index.fulltext \"ft_content\"", "@db.index.unique", "refNo: number.int"),
|
|
1048
1048
|
validate(token, args, doc) {
|
|
1049
1049
|
const field = token.parentNode;
|
|
1050
|
+
const indexName = args[0]?.text;
|
|
1051
|
+
if (indexName?.startsWith("atscript__")) return [{
|
|
1052
|
+
message: `@db.index.fulltext name "${indexName}" is reserved — names starting with "atscript__" are internal index keys`,
|
|
1053
|
+
severity: 1,
|
|
1054
|
+
range: args[0].range
|
|
1055
|
+
}];
|
|
1050
1056
|
const sealed = sealedSearchProblem(token, doc, "@db.index.fulltext");
|
|
1051
1057
|
if (sealed) return [sealed];
|
|
1052
1058
|
const verdict = searchFieldVerdict(field, doc);
|
|
@@ -1120,8 +1126,8 @@ function checkCompositeFkCoverage(token, alias, refTypeName, targetStruct, targe
|
|
|
1120
1126
|
for (const [, sibling] of hostStruct.props) {
|
|
1121
1127
|
if (sibling.countAnnotations("db.rel.FK") === 0) continue;
|
|
1122
1128
|
if (getAnnotationAlias(sibling, "db.rel.FK") !== alias) continue;
|
|
1123
|
-
const siblingDef = sibling.getDefinition();
|
|
1124
|
-
if (siblingDef &&
|
|
1129
|
+
const siblingDef = fkChainRef(sibling.getDefinition());
|
|
1130
|
+
if (siblingDef && siblingDef.id === refTypeName) {
|
|
1125
1131
|
if (siblingDef.chain.length === 1) covered.add(siblingDef.chain[0].text);
|
|
1126
1132
|
}
|
|
1127
1133
|
}
|
|
@@ -1147,16 +1153,15 @@ const dbRelAnnotations = { rel: {
|
|
|
1147
1153
|
severity: 1,
|
|
1148
1154
|
range: token.range
|
|
1149
1155
|
});
|
|
1150
|
-
const
|
|
1151
|
-
if (!
|
|
1156
|
+
const ref = fkChainRef(field.getDefinition());
|
|
1157
|
+
if (!ref) {
|
|
1152
1158
|
errors.push({
|
|
1153
|
-
message: `@db.rel.FK requires a chain reference type (e.g. User.id), got scalar type`,
|
|
1159
|
+
message: `@db.rel.FK requires a chain reference type (e.g. User.id or User.id | null), got scalar type`,
|
|
1154
1160
|
severity: 1,
|
|
1155
1161
|
range: token.range
|
|
1156
1162
|
});
|
|
1157
1163
|
return errors;
|
|
1158
1164
|
}
|
|
1159
|
-
const ref = definition;
|
|
1160
1165
|
const refTypeName = ref.id;
|
|
1161
1166
|
const chainFields = ref.chain.map((c) => c.text);
|
|
1162
1167
|
const targetUnwound = doc.unwindType(refTypeName);
|
|
@@ -1200,10 +1205,8 @@ const dbRelAnnotations = { rel: {
|
|
|
1200
1205
|
let sameTargetCount = 0;
|
|
1201
1206
|
for (const [, prop] of struct.props) {
|
|
1202
1207
|
if (prop.countAnnotations("db.rel.FK") === 0) continue;
|
|
1203
|
-
const
|
|
1204
|
-
if (!
|
|
1205
|
-
const r = def;
|
|
1206
|
-
if (!r.hasChain) continue;
|
|
1208
|
+
const r = fkChainRef(prop.getDefinition());
|
|
1209
|
+
if (!r) continue;
|
|
1207
1210
|
if (r.id === refTypeName) {
|
|
1208
1211
|
if (!getAnnotationAlias(prop, "db.rel.FK")) sameTargetCount++;
|
|
1209
1212
|
}
|
package/dist/rel.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_nested_writer = require("./nested-writer-
|
|
3
|
-
const require_relation_loader = require("./relation-loader-
|
|
2
|
+
const require_nested_writer = require("./nested-writer-DOvdbYG-.cjs");
|
|
3
|
+
const require_relation_loader = require("./relation-loader-BrkakVF_.cjs");
|
|
4
4
|
exports.applyPatchNestedTo = require_nested_writer.applyPatchNestedTo;
|
|
5
5
|
exports.batchInsertNestedFrom = require_nested_writer.batchInsertNestedFrom;
|
|
6
6
|
exports.batchInsertNestedTo = require_nested_writer.batchInsertNestedTo;
|
package/dist/rel.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { K as TDbForeignKey, Yt as TTableResolver, ct as TDbRelation, fr as TGenericLogger, h as AtscriptDbWritable, hn as BaseDbAdapter, m as AtscriptDbTableLike, nn as TWriteTableResolver, sn as TableMetadata } from "./buckets-BDxKfCNo.cjs";
|
|
2
2
|
import { t as DbValidationContext } from "./db-validator-plugin-wRqMYNBI.cjs";
|
|
3
|
-
import { n as findRemoteFK, r as resolveRelationTargetTable, t as findFKForRelation } from "./relation-helpers-
|
|
3
|
+
import { n as findRemoteFK, r as resolveRelationTargetTable, t as findFKForRelation } from "./relation-helpers-C4UNWYQu.cjs";
|
|
4
4
|
import { FilterExpr, WithRelation } from "@uniqu/core";
|
|
5
5
|
import { Validator } from "@atscript/typescript/utils";
|
|
6
6
|
|
package/dist/rel.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { K as TDbForeignKey, Yt as TTableResolver, ct as TDbRelation, fr as TGenericLogger, h as AtscriptDbWritable, hn as BaseDbAdapter, m as AtscriptDbTableLike, nn as TWriteTableResolver, sn as TableMetadata } from "./buckets-CHUgky8T.mjs";
|
|
2
2
|
import { t as DbValidationContext } from "./db-validator-plugin-wRqMYNBI.mjs";
|
|
3
|
-
import { n as findRemoteFK, r as resolveRelationTargetTable, t as findFKForRelation } from "./relation-helpers-
|
|
3
|
+
import { n as findRemoteFK, r as resolveRelationTargetTable, t as findFKForRelation } from "./relation-helpers-hdbuqwjV.mjs";
|
|
4
4
|
import { Validator } from "@atscript/typescript/utils";
|
|
5
5
|
import { FilterExpr, WithRelation } from "@uniqu/core";
|
|
6
6
|
|
package/dist/rel.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as loadRelationsImpl } from "./relation-loader-
|
|
2
|
-
import { B as resolveRelationTargetTable,
|
|
1
|
+
import { t as loadRelationsImpl } from "./relation-loader-BOEVhcBe.mjs";
|
|
2
|
+
import { B as findFKForRelation, H as resolveRelationTargetTable, V as findRemoteFK, a as batchPatchNestedFrom, c as batchReplaceNestedFrom, d as checkDepthOverflow, f as planNestedFromVia, h as validateBatch, i as batchInsertNestedVia, l as batchReplaceNestedTo, m as preValidateNestedFrom, n as batchInsertNestedFrom, o as batchPatchNestedTo, p as planPatchNestedTo, r as batchInsertNestedTo, s as batchPatchNestedVia, t as applyPatchNestedTo, u as batchReplaceNestedVia } from "./nested-writer-BraWjS8a.mjs";
|
|
3
3
|
export { applyPatchNestedTo, batchInsertNestedFrom, batchInsertNestedTo, batchInsertNestedVia, batchPatchNestedFrom, batchPatchNestedTo, batchPatchNestedVia, batchReplaceNestedFrom, batchReplaceNestedTo, batchReplaceNestedVia, checkDepthOverflow, findFKForRelation, findRemoteFK, loadRelationsImpl, planNestedFromVia, planPatchNestedTo, preValidateNestedFrom, resolveRelationTargetTable, validateBatch };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { K as TDbForeignKey, ct as TDbRelation } from "./buckets-
|
|
1
|
+
import { K as TDbForeignKey, ct as TDbRelation } from "./buckets-BDxKfCNo.cjs";
|
|
2
2
|
import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
|
|
3
3
|
|
|
4
4
|
//#region src/rel/relation-helpers.d.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { K as TDbForeignKey, ct as TDbRelation } from "./buckets-
|
|
1
|
+
import { K as TDbForeignKey, ct as TDbRelation } from "./buckets-CHUgky8T.mjs";
|
|
2
2
|
import { TAtscriptAnnotatedType } from "@atscript/typescript/utils";
|
|
3
3
|
|
|
4
4
|
//#region src/rel/relation-helpers.d.ts
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as
|
|
1
|
+
import { B as findFKForRelation, H as resolveRelationTargetTable, I as relationStaticFilter, O as andFilters, R as compositeKey, U as slicePerGroup, V as findRemoteFK } from "./nested-writer-BraWjS8a.mjs";
|
|
2
2
|
//#region \0rolldown/runtime.js
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const require_nested_writer = require("./nested-writer-
|
|
1
|
+
const require_nested_writer = require("./nested-writer-DOvdbYG-.cjs");
|
|
2
2
|
//#region src/rel/relation-loader.ts
|
|
3
3
|
var relation_loader_exports = /* @__PURE__ */ require("./plugin.cjs").__exportAll({
|
|
4
4
|
ensureSelectIncludesFields: () => ensureSelectIncludesFields,
|
package/dist/shared.cjs
CHANGED
|
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_search_fields = require("./search-fields-B49-RCWd.cjs");
|
|
3
3
|
const require_object = require("./object-GjsuPNqo.cjs");
|
|
4
4
|
const require_aggregate_rules = require("./aggregate-rules-D_bsCpUI.cjs");
|
|
5
|
-
const require_view_validation = require("./view-validation-
|
|
5
|
+
const require_view_validation = require("./view-validation-C0UVLx_H.cjs");
|
|
6
6
|
exports.ENCRYPTED_INCOMPATIBLE = require_search_fields.ENCRYPTED_INCOMPATIBLE;
|
|
7
7
|
exports.ENCRYPTED_SEARCH_FEATURES = require_search_fields.ENCRYPTED_SEARCH_FEATURES;
|
|
8
8
|
exports.NAV_ANNOTATIONS = require_view_validation.NAV_ANNOTATIONS;
|
|
@@ -15,6 +15,7 @@ exports.encryptedTypeSearchProblem = require_view_validation.encryptedTypeSearch
|
|
|
15
15
|
exports.findFKFieldsPointingTo = require_view_validation.findFKFieldsPointingTo;
|
|
16
16
|
exports.findStoredField = require_view_validation.findStoredField;
|
|
17
17
|
exports.findViewCycle = require_view_validation.findViewCycle;
|
|
18
|
+
exports.fkChainRef = require_view_validation.fkChainRef;
|
|
18
19
|
exports.forEachFieldRef = require_view_validation.forEachFieldRef;
|
|
19
20
|
exports.getAnnotationAlias = require_view_validation.getAnnotationAlias;
|
|
20
21
|
exports.getDbTableOwner = require_view_validation.getDbTableOwner;
|
|
@@ -26,6 +27,7 @@ exports.integerMemberIndexProblem = require_view_validation.integerMemberIndexPr
|
|
|
26
27
|
exports.isAliasDecl = require_view_validation.isAliasDecl;
|
|
27
28
|
exports.isDbSourceDecl = require_view_validation.isDbSourceDecl;
|
|
28
29
|
exports.isNavProp = require_view_validation.isNavProp;
|
|
30
|
+
exports.isNullMember = require_view_validation.isNullMember;
|
|
29
31
|
exports.isNullableUnion = require_view_validation.isNullableUnion;
|
|
30
32
|
exports.isStrictAggregateStruct = require_aggregate_rules.isStrictAggregateStruct;
|
|
31
33
|
exports.isViewField = require_view_validation.isViewField;
|
package/dist/shared.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { s as stableJson } from "./object-Czia_qD4.cjs";
|
|
2
2
|
import { n as ENCRYPTED_SEARCH_FEATURES, t as ENCRYPTED_INCOMPATIBLE } from "./search-fields-CHqqcC3W.cjs";
|
|
3
|
-
import { AnnotationSpec, AtscriptDoc, SemanticInterfaceNode, SemanticNode, SemanticPropNode, SemanticQueryExprNode, SemanticQueryFieldRefNode, SemanticStructureNode, TAnnotationSpecConfig, TAnnotationTokens, TMessages, TQueryScope, Token } from "@atscript/core";
|
|
3
|
+
import { AnnotationSpec, AtscriptDoc, SemanticInterfaceNode, SemanticNode, SemanticPropNode, SemanticQueryExprNode, SemanticQueryFieldRefNode, SemanticRefNode, SemanticStructureNode, TAnnotationSpecConfig, TAnnotationTokens, TMessages, TQueryScope, Token } from "@atscript/core";
|
|
4
4
|
|
|
5
5
|
//#region src/shared/annotation-utils.d.ts
|
|
6
6
|
/**
|
|
@@ -32,6 +32,11 @@ declare function validateIncompatible(token: Token, tag: string, pairs: Readonly
|
|
|
32
32
|
declare function isViewField(field: SemanticNode): boolean;
|
|
33
33
|
/** Primitive base type (`string`, `number`, `decimal`, …) of a resolved definition, if it is a primitive. */
|
|
34
34
|
declare function primitiveBaseType(def: SemanticNode | undefined): string | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* `null` in a type expression: the `null` reference or the `null` primitive
|
|
37
|
+
* (whose `config.type` is `"null"`).
|
|
38
|
+
*/
|
|
39
|
+
declare function isNullMember(node: SemanticNode): boolean;
|
|
35
40
|
/**
|
|
36
41
|
* Whether a definition is a union with a `null` member — `T | null`, or a
|
|
37
42
|
* ref to an alias of one (compile-time mirror of the runtime nullable union,
|
|
@@ -130,6 +135,12 @@ interface TFKFieldMatch {
|
|
|
130
135
|
field: string;
|
|
131
136
|
};
|
|
132
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* The chain reference (`User.id`) an `@db.rel.FK` field's type names, else
|
|
140
|
+
* `undefined`. `User.id | null` names it too (since 0.1.165) — the same
|
|
141
|
+
* optional foreign key as `field?: User.id`; any other union does not.
|
|
142
|
+
*/
|
|
143
|
+
declare function fkChainRef(def: SemanticNode | undefined): SemanticRefNode | undefined;
|
|
133
144
|
/**
|
|
134
145
|
* Find all `@db.rel.FK` fields on a type that reference `targetTypeName`.
|
|
135
146
|
* Resolves `extends` to include inherited fields.
|
|
@@ -258,4 +269,4 @@ declare function encryptedTypeSearchProblem(token: Token, doc: AtscriptDoc): TMe
|
|
|
258
269
|
/** Name of the nearest `@db.encrypted` prop enclosing `field` (inline nested objects), if any. */
|
|
259
270
|
declare function encryptedAncestorName(field: SemanticNode): string | undefined;
|
|
260
271
|
//#endregion
|
|
261
|
-
export { ENCRYPTED_INCOMPATIBLE, ENCRYPTED_SEARCH_FEATURES, NAV_ANNOTATIONS, NOT_DIMENSION_REASON, TFKFieldMatch, TRefArgumentOptions, type TViewFieldSeals, earlierJoinTargets, effectiveAnnotationNames, encryptedAncestorName, encryptedSearchProblem, encryptedTypeSearchProblem, findFKFieldsPointingTo, findStoredField, findViewCycle, forEachFieldRef, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, integerMemberIndexProblem, isAliasDecl, isDbSourceDecl, isNavProp, isNullableUnion, isStrictAggregateStruct, isViewField, joinTargets, primitiveBaseType, refActionAnnotation, refuseCiphertext, searchFieldVerdict, stableJson, validateExclusiveWith, validateFieldBaseType, validateIncompatible, validateQueryScope, validateRefArgument, validateSiblingStringField, viewFieldSeals, viewJoins, viewScopeTypes };
|
|
272
|
+
export { ENCRYPTED_INCOMPATIBLE, ENCRYPTED_SEARCH_FEATURES, NAV_ANNOTATIONS, NOT_DIMENSION_REASON, TFKFieldMatch, TRefArgumentOptions, type TViewFieldSeals, earlierJoinTargets, effectiveAnnotationNames, encryptedAncestorName, encryptedSearchProblem, encryptedTypeSearchProblem, findFKFieldsPointingTo, findStoredField, findViewCycle, fkChainRef, forEachFieldRef, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, integerMemberIndexProblem, isAliasDecl, isDbSourceDecl, isNavProp, isNullMember, isNullableUnion, isStrictAggregateStruct, isViewField, joinTargets, primitiveBaseType, refActionAnnotation, refuseCiphertext, searchFieldVerdict, stableJson, validateExclusiveWith, validateFieldBaseType, validateIncompatible, validateQueryScope, validateRefArgument, validateSiblingStringField, viewFieldSeals, viewJoins, viewScopeTypes };
|
package/dist/shared.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { s as stableJson } from "./object-Czia_qD4.mjs";
|
|
2
2
|
import { n as ENCRYPTED_SEARCH_FEATURES, t as ENCRYPTED_INCOMPATIBLE } from "./search-fields-CHqqcC3W.mjs";
|
|
3
|
-
import { AnnotationSpec, AtscriptDoc, SemanticInterfaceNode, SemanticNode, SemanticPropNode, SemanticQueryExprNode, SemanticQueryFieldRefNode, SemanticStructureNode, TAnnotationSpecConfig, TAnnotationTokens, TMessages, TQueryScope, Token } from "@atscript/core";
|
|
3
|
+
import { AnnotationSpec, AtscriptDoc, SemanticInterfaceNode, SemanticNode, SemanticPropNode, SemanticQueryExprNode, SemanticQueryFieldRefNode, SemanticRefNode, SemanticStructureNode, TAnnotationSpecConfig, TAnnotationTokens, TMessages, TQueryScope, Token } from "@atscript/core";
|
|
4
4
|
|
|
5
5
|
//#region src/shared/annotation-utils.d.ts
|
|
6
6
|
/**
|
|
@@ -32,6 +32,11 @@ declare function validateIncompatible(token: Token, tag: string, pairs: Readonly
|
|
|
32
32
|
declare function isViewField(field: SemanticNode): boolean;
|
|
33
33
|
/** Primitive base type (`string`, `number`, `decimal`, …) of a resolved definition, if it is a primitive. */
|
|
34
34
|
declare function primitiveBaseType(def: SemanticNode | undefined): string | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* `null` in a type expression: the `null` reference or the `null` primitive
|
|
37
|
+
* (whose `config.type` is `"null"`).
|
|
38
|
+
*/
|
|
39
|
+
declare function isNullMember(node: SemanticNode): boolean;
|
|
35
40
|
/**
|
|
36
41
|
* Whether a definition is a union with a `null` member — `T | null`, or a
|
|
37
42
|
* ref to an alias of one (compile-time mirror of the runtime nullable union,
|
|
@@ -130,6 +135,12 @@ interface TFKFieldMatch {
|
|
|
130
135
|
field: string;
|
|
131
136
|
};
|
|
132
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* The chain reference (`User.id`) an `@db.rel.FK` field's type names, else
|
|
140
|
+
* `undefined`. `User.id | null` names it too (since 0.1.165) — the same
|
|
141
|
+
* optional foreign key as `field?: User.id`; any other union does not.
|
|
142
|
+
*/
|
|
143
|
+
declare function fkChainRef(def: SemanticNode | undefined): SemanticRefNode | undefined;
|
|
133
144
|
/**
|
|
134
145
|
* Find all `@db.rel.FK` fields on a type that reference `targetTypeName`.
|
|
135
146
|
* Resolves `extends` to include inherited fields.
|
|
@@ -258,4 +269,4 @@ declare function encryptedTypeSearchProblem(token: Token, doc: AtscriptDoc): TMe
|
|
|
258
269
|
/** Name of the nearest `@db.encrypted` prop enclosing `field` (inline nested objects), if any. */
|
|
259
270
|
declare function encryptedAncestorName(field: SemanticNode): string | undefined;
|
|
260
271
|
//#endregion
|
|
261
|
-
export { ENCRYPTED_INCOMPATIBLE, ENCRYPTED_SEARCH_FEATURES, NAV_ANNOTATIONS, NOT_DIMENSION_REASON, TFKFieldMatch, TRefArgumentOptions, type TViewFieldSeals, earlierJoinTargets, effectiveAnnotationNames, encryptedAncestorName, encryptedSearchProblem, encryptedTypeSearchProblem, findFKFieldsPointingTo, findStoredField, findViewCycle, forEachFieldRef, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, integerMemberIndexProblem, isAliasDecl, isDbSourceDecl, isNavProp, isNullableUnion, isStrictAggregateStruct, isViewField, joinTargets, primitiveBaseType, refActionAnnotation, refuseCiphertext, searchFieldVerdict, stableJson, validateExclusiveWith, validateFieldBaseType, validateIncompatible, validateQueryScope, validateRefArgument, validateSiblingStringField, viewFieldSeals, viewJoins, viewScopeTypes };
|
|
272
|
+
export { ENCRYPTED_INCOMPATIBLE, ENCRYPTED_SEARCH_FEATURES, NAV_ANNOTATIONS, NOT_DIMENSION_REASON, TFKFieldMatch, TRefArgumentOptions, type TViewFieldSeals, earlierJoinTargets, effectiveAnnotationNames, encryptedAncestorName, encryptedSearchProblem, encryptedTypeSearchProblem, findFKFieldsPointingTo, findStoredField, findViewCycle, fkChainRef, forEachFieldRef, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, integerMemberIndexProblem, isAliasDecl, isDbSourceDecl, isNavProp, isNullMember, isNullableUnion, isStrictAggregateStruct, isViewField, joinTargets, primitiveBaseType, refActionAnnotation, refuseCiphertext, searchFieldVerdict, stableJson, validateExclusiveWith, validateFieldBaseType, validateIncompatible, validateQueryScope, validateRefArgument, validateSiblingStringField, viewFieldSeals, viewJoins, viewScopeTypes };
|
package/dist/shared.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as ENCRYPTED_SEARCH_FEATURES, t as ENCRYPTED_INCOMPATIBLE } from "./search-fields-BPNsxrCk.mjs";
|
|
2
2
|
import { s as stableJson } from "./object-PQ1J8jri.mjs";
|
|
3
3
|
import { n as isStrictAggregateStruct, t as NOT_DIMENSION_REASON } from "./aggregate-rules-jdPrxqWa.mjs";
|
|
4
|
-
import { A as
|
|
5
|
-
export { ENCRYPTED_INCOMPATIBLE, ENCRYPTED_SEARCH_FEATURES, NAV_ANNOTATIONS, NOT_DIMENSION_REASON, earlierJoinTargets, effectiveAnnotationNames, encryptedAncestorName, encryptedSearchProblem, encryptedTypeSearchProblem, findFKFieldsPointingTo, findStoredField, findViewCycle, forEachFieldRef, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, integerMemberIndexProblem, isAliasDecl, isDbSourceDecl, isNavProp, isNullableUnion, isStrictAggregateStruct, isViewField, joinTargets, primitiveBaseType, refActionAnnotation, refuseCiphertext, searchFieldVerdict, stableJson, validateExclusiveWith, validateFieldBaseType, validateIncompatible, validateQueryScope, validateRefArgument, validateSiblingStringField, viewFieldSeals, viewJoins, viewScopeTypes };
|
|
4
|
+
import { A as getParentStruct, B as validateFieldBaseType, C as validateRefArgument, D as getAnnotationAlias, E as effectiveAnnotationNames, F as isViewField, H as validateSiblingStringField, I as primitiveBaseType, L as refActionAnnotation, M as integerMemberIndexProblem, N as isNullMember, O as getDbTableOwner, P as isNullableUnion, R as searchFieldVerdict, S as validateQueryScope, T as viewScopeTypes, V as validateIncompatible, _ as forEachFieldRef, a as findStoredField, b as isDbSourceDecl, c as refuseCiphertext, d as viewFieldSeals, g as fkChainRef, h as findViewCycle, i as encryptedTypeSearchProblem, j as getParentTypeName, k as getNavTargetTypeName, m as findFKFieldsPointingTo, n as encryptedAncestorName, o as isNavProp, p as earlierJoinTargets, r as encryptedSearchProblem, t as NAV_ANNOTATIONS, v as hasAnyViewAnnotation, w as viewJoins, x as joinTargets, y as isAliasDecl, z as validateExclusiveWith } from "./view-validation-KW77xIKn.mjs";
|
|
5
|
+
export { ENCRYPTED_INCOMPATIBLE, ENCRYPTED_SEARCH_FEATURES, NAV_ANNOTATIONS, NOT_DIMENSION_REASON, earlierJoinTargets, effectiveAnnotationNames, encryptedAncestorName, encryptedSearchProblem, encryptedTypeSearchProblem, findFKFieldsPointingTo, findStoredField, findViewCycle, fkChainRef, forEachFieldRef, getAnnotationAlias, getDbTableOwner, getNavTargetTypeName, getParentStruct, getParentTypeName, hasAnyViewAnnotation, integerMemberIndexProblem, isAliasDecl, isDbSourceDecl, isNavProp, isNullMember, isNullableUnion, isStrictAggregateStruct, isViewField, joinTargets, primitiveBaseType, refActionAnnotation, refuseCiphertext, searchFieldVerdict, stableJson, validateExclusiveWith, validateFieldBaseType, validateIncompatible, validateQueryScope, validateRefArgument, validateSiblingStringField, viewFieldSeals, viewJoins, viewScopeTypes };
|
package/dist/sync.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
const require_validator = require("./validator-
|
|
2
|
+
const require_column_diff = require("./column-diff-DjeUj9kb.cjs");
|
|
3
|
+
const require_db_error = require("./db-error-G3sgvL87.cjs");
|
|
4
|
+
const require_validator = require("./validator-D7E7T0Oy.cjs");
|
|
5
5
|
//#region src/schema/json-column-copy.ts
|
|
6
6
|
const NONE = {
|
|
7
7
|
copies: [],
|
|
@@ -323,11 +323,28 @@ var SyncStore = class {
|
|
|
323
323
|
}
|
|
324
324
|
async ensureControlTable() {
|
|
325
325
|
if (!this.controlTable) {
|
|
326
|
-
const { AtscriptControl } = await Promise.resolve().then(() => require("./control-
|
|
326
|
+
const { AtscriptControl } = await Promise.resolve().then(() => require("./control-B5BJypqL.cjs"));
|
|
327
327
|
this.controlTable = this.space.getTable(AtscriptControl);
|
|
328
328
|
}
|
|
329
329
|
await this.controlTable.ensureTable();
|
|
330
330
|
}
|
|
331
|
+
/**
|
|
332
|
+
* Runs `fn` in a transaction of the control table's adapter family — the
|
|
333
|
+
* space's tables on the same driver join it (since 0.1.165, data steps).
|
|
334
|
+
*/
|
|
335
|
+
async inTransaction(fn) {
|
|
336
|
+
const { AtscriptControl } = await Promise.resolve().then(() => require("./control-B5BJypqL.cjs"));
|
|
337
|
+
return this.space.getAdapter(AtscriptControl).withTransaction(fn);
|
|
338
|
+
}
|
|
339
|
+
/** Which of `ids` have a control row — one query (since 0.1.165, data steps). */
|
|
340
|
+
async presentControlIds(ids) {
|
|
341
|
+
if (ids.length === 0) return /* @__PURE__ */ new Set();
|
|
342
|
+
const rows = await this.controlTable.findMany({
|
|
343
|
+
filter: { _id: { $in: [...ids] } },
|
|
344
|
+
controls: { $select: ["_id"] }
|
|
345
|
+
});
|
|
346
|
+
return new Set(rows.map((r) => r._id));
|
|
347
|
+
}
|
|
331
348
|
async readControlValue(_id) {
|
|
332
349
|
return (await this.controlTable.findOne({
|
|
333
350
|
filter: { _id: { $eq: _id } },
|
|
@@ -495,7 +512,7 @@ function isExpired(lock, now) {
|
|
|
495
512
|
return expiresAt > 0 && expiresAt < now;
|
|
496
513
|
}
|
|
497
514
|
async function readStoredSnapshot(space, tableName, _asView) {
|
|
498
|
-
const { AtscriptControl } = await Promise.resolve().then(() => require("./control-
|
|
515
|
+
const { AtscriptControl } = await Promise.resolve().then(() => require("./control-B5BJypqL.cjs"));
|
|
499
516
|
const table = space.getTable(AtscriptControl);
|
|
500
517
|
await table.ensureTable();
|
|
501
518
|
const value = (await table.findOne({
|
|
@@ -1571,6 +1588,33 @@ function removedBlockers(t, removedTables, removedTableSet, dropEdges) {
|
|
|
1571
1588
|
} else for (const r of removedTables) if (r.snapshot && "foreignKeys" in r.snapshot && r.snapshot.foreignKeys.some((fk) => fk.targetTable === t.dbName)) direct.push(r.name);
|
|
1572
1589
|
return reachable(direct, dropEdges);
|
|
1573
1590
|
}
|
|
1591
|
+
/** The `__atscript_control` key a data step is recorded under. */
|
|
1592
|
+
function dataStepKey(id) {
|
|
1593
|
+
return `data_step:${id}`;
|
|
1594
|
+
}
|
|
1595
|
+
/** The steps as given, after checking their ids are non-empty and unique. */
|
|
1596
|
+
function checkDataSteps(steps) {
|
|
1597
|
+
if (!steps?.length) return [];
|
|
1598
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1599
|
+
for (const step of steps) {
|
|
1600
|
+
if (typeof step.id !== "string" || step.id === "") throw new Error("syncSchema: every data step needs a non-empty id");
|
|
1601
|
+
if (seen.has(step.id)) throw new Error(`syncSchema: duplicate data step id "${step.id}"`);
|
|
1602
|
+
seen.add(step.id);
|
|
1603
|
+
}
|
|
1604
|
+
return steps;
|
|
1605
|
+
}
|
|
1606
|
+
/** `result` with its `dataSteps` report — unchanged when no steps were passed. */
|
|
1607
|
+
function withSteps(result, steps, ran) {
|
|
1608
|
+
if (steps.length === 0) return result;
|
|
1609
|
+
const done = new Set(ran);
|
|
1610
|
+
return {
|
|
1611
|
+
...result,
|
|
1612
|
+
dataSteps: {
|
|
1613
|
+
ran: [...ran],
|
|
1614
|
+
pending: steps.map((s) => s.id).filter((id) => !done.has(id))
|
|
1615
|
+
}
|
|
1616
|
+
};
|
|
1617
|
+
}
|
|
1574
1618
|
var SchemaSync = class {
|
|
1575
1619
|
space;
|
|
1576
1620
|
store;
|
|
@@ -1588,7 +1632,7 @@ var SchemaSync = class {
|
|
|
1588
1632
|
const tables = [];
|
|
1589
1633
|
const views = [];
|
|
1590
1634
|
const externalViews = [];
|
|
1591
|
-
for (const type of types) {
|
|
1635
|
+
for (const type of new Set(types)) {
|
|
1592
1636
|
if (require_column_diff.aliasTargetOf(type)) continue;
|
|
1593
1637
|
const readable = this.space.get(type);
|
|
1594
1638
|
if (readable.isView) {
|
|
@@ -1716,7 +1760,8 @@ var SchemaSync = class {
|
|
|
1716
1760
|
/**
|
|
1717
1761
|
* Acquires the sync lock, waiting for a peer that holds it. Resolves
|
|
1718
1762
|
* `"acquired"`, or `"synced-by-peer"` when a non-forced run finds, after a
|
|
1719
|
-
* wait, that the peer stored this schema's hash
|
|
1763
|
+
* wait, that the peer stored this schema's hash (and `peerDone` agrees —
|
|
1764
|
+
* no data step left pending). A forced run never
|
|
1720
1765
|
* short-circuits — it waits its turn and runs. Losing the race for a freed
|
|
1721
1766
|
* lock to another waiter just waits again, within the same `waitTimeoutMs`.
|
|
1722
1767
|
*/
|
|
@@ -1724,7 +1769,7 @@ var SchemaSync = class {
|
|
|
1724
1769
|
const deadline = Date.now() + opts.waitTimeoutMs;
|
|
1725
1770
|
while (!await this.store.tryAcquireLock(podId, opts.lockTtlMs)) {
|
|
1726
1771
|
if (!await this.store.waitForLock(deadline - Date.now(), opts.pollIntervalMs)) throw new Error(`Schema sync lock wait timed out after ${opts.waitTimeoutMs}ms`);
|
|
1727
|
-
if (!opts.force && await this.store.readHash() === hash) return "synced-by-peer";
|
|
1772
|
+
if (!opts.force && await this.store.readHash() === hash && (!opts.peerDone || await opts.peerDone())) return "synced-by-peer";
|
|
1728
1773
|
}
|
|
1729
1774
|
return "acquired";
|
|
1730
1775
|
}
|
|
@@ -1744,6 +1789,30 @@ var SchemaSync = class {
|
|
|
1744
1789
|
this.logger.warn("[schema-sync] Failed to release the sync lock — peers wait until it expires:", error instanceof Error ? error.message : error);
|
|
1745
1790
|
}
|
|
1746
1791
|
}
|
|
1792
|
+
/** Ids of the steps not recorded yet, in order. */
|
|
1793
|
+
async pendingDataSteps(steps) {
|
|
1794
|
+
const done = await this.store.presentControlIds(steps.map((step) => dataStepKey(step.id)));
|
|
1795
|
+
return steps.filter((step) => !done.has(dataStepKey(step.id))).map((step) => step.id);
|
|
1796
|
+
}
|
|
1797
|
+
/**
|
|
1798
|
+
* Runs the pending steps in order, each with its record in one transaction.
|
|
1799
|
+
* A throwing step propagates (rolled back, not recorded). Returns the ids run.
|
|
1800
|
+
*/
|
|
1801
|
+
async runDataSteps(steps, getAbortReason) {
|
|
1802
|
+
const ran = [];
|
|
1803
|
+
const pending = new Set(await this.pendingDataSteps(steps));
|
|
1804
|
+
for (const step of steps) {
|
|
1805
|
+
if (!pending.has(step.id)) continue;
|
|
1806
|
+
this.assertLockHeld(getAbortReason);
|
|
1807
|
+
await this.store.inTransaction(async () => {
|
|
1808
|
+
await step.run(this.space);
|
|
1809
|
+
await this.store.writeControlValue(dataStepKey(step.id), JSON.stringify({ ranAt: Date.now() }));
|
|
1810
|
+
});
|
|
1811
|
+
this.logger.info?.(`[schema-sync] data step "${step.id}" applied`);
|
|
1812
|
+
ran.push(step.id);
|
|
1813
|
+
}
|
|
1814
|
+
return ran;
|
|
1815
|
+
}
|
|
1747
1816
|
/**
|
|
1748
1817
|
* Runs schema synchronization with distributed locking, in three phases:
|
|
1749
1818
|
*
|
|
@@ -1768,21 +1837,25 @@ var SchemaSync = class {
|
|
|
1768
1837
|
const force = opts?.force ?? false;
|
|
1769
1838
|
const safe = opts?.safe ?? false;
|
|
1770
1839
|
const onError = opts?.onError ?? "warn";
|
|
1840
|
+
const steps = checkDataSteps(opts?.dataSteps);
|
|
1771
1841
|
const resolved = await this.resolveAndHash(types);
|
|
1772
1842
|
const { hash } = resolved;
|
|
1773
1843
|
await this.store.ensureControlTable();
|
|
1774
1844
|
if (!force) {
|
|
1775
|
-
if (await this.store.readHash() === hash)
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1845
|
+
if (await this.store.readHash() === hash) {
|
|
1846
|
+
if ((await this.pendingDataSteps(steps)).length === 0) return withSteps({
|
|
1847
|
+
status: "up-to-date",
|
|
1848
|
+
schemaHash: hash,
|
|
1849
|
+
entries: []
|
|
1850
|
+
}, steps, []);
|
|
1851
|
+
}
|
|
1780
1852
|
}
|
|
1781
1853
|
if (await this.acquireLock(podId, hash, {
|
|
1782
1854
|
lockTtlMs,
|
|
1783
1855
|
waitTimeoutMs,
|
|
1784
1856
|
pollIntervalMs,
|
|
1785
|
-
force
|
|
1857
|
+
force,
|
|
1858
|
+
peerDone: async () => (await this.pendingDataSteps(steps)).length === 0
|
|
1786
1859
|
}) === "synced-by-peer") return {
|
|
1787
1860
|
status: "synced-by-peer",
|
|
1788
1861
|
schemaHash: hash,
|
|
@@ -1791,11 +1864,14 @@ var SchemaSync = class {
|
|
|
1791
1864
|
const heartbeat = this.startHeartbeat(podId, lockTtlMs);
|
|
1792
1865
|
try {
|
|
1793
1866
|
if (!force) {
|
|
1794
|
-
if (await this.store.readHash() === hash)
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1867
|
+
if (await this.store.readHash() === hash) {
|
|
1868
|
+
const ran = await this.runDataSteps(steps, heartbeat.getAbortReason);
|
|
1869
|
+
return withSteps({
|
|
1870
|
+
status: ran.length > 0 ? "synced" : "synced-by-peer",
|
|
1871
|
+
schemaHash: hash,
|
|
1872
|
+
entries: []
|
|
1873
|
+
}, steps, ran);
|
|
1874
|
+
}
|
|
1799
1875
|
}
|
|
1800
1876
|
const d = await this.discover(resolved, safe);
|
|
1801
1877
|
this.assertLockHeld(heartbeat.getAbortReason);
|
|
@@ -1808,11 +1884,11 @@ var SchemaSync = class {
|
|
|
1808
1884
|
entries: this.buildPlanEntries(d, safe, refusals)
|
|
1809
1885
|
};
|
|
1810
1886
|
this.reportOutcome(result, onError);
|
|
1811
|
-
return result;
|
|
1887
|
+
return withSteps(result, steps, []);
|
|
1812
1888
|
}
|
|
1813
1889
|
const result = await this.execute(d, safe, heartbeat.getAbortReason);
|
|
1814
1890
|
this.reportOutcome(result, onError);
|
|
1815
|
-
return result;
|
|
1891
|
+
return withSteps(result, steps, result.entries.some((e) => e.status === "error") ? [] : await this.runDataSteps(steps, heartbeat.getAbortReason));
|
|
1816
1892
|
} finally {
|
|
1817
1893
|
await heartbeat.stop();
|
|
1818
1894
|
await this.releaseLock(podId);
|
|
@@ -2437,6 +2513,7 @@ var SchemaSync = class {
|
|
|
2437
2513
|
* as `error` entries (`refused: true`) exactly as `run()` would report them.
|
|
2438
2514
|
*/
|
|
2439
2515
|
async plan(types, opts) {
|
|
2516
|
+
const steps = checkDataSteps(opts?.dataSteps);
|
|
2440
2517
|
const force = opts?.force ?? false;
|
|
2441
2518
|
const safe = opts?.safe ?? false;
|
|
2442
2519
|
const resolved = await this.resolveAndHash(types);
|
|
@@ -2446,18 +2523,23 @@ var SchemaSync = class {
|
|
|
2446
2523
|
const refusals = this.preflight(d, safe);
|
|
2447
2524
|
await this.planLiveWork(d, safe);
|
|
2448
2525
|
const entries = this.buildPlanEntries(d, safe, refusals);
|
|
2526
|
+
const pending = steps.length > 0 ? await this.pendingDataSteps(steps) : void 0;
|
|
2527
|
+
const withPending = (plan) => pending ? {
|
|
2528
|
+
...plan,
|
|
2529
|
+
pendingDataSteps: pending
|
|
2530
|
+
} : plan;
|
|
2449
2531
|
if (!force) {
|
|
2450
|
-
if (await this.store.readHash() === hash) return {
|
|
2532
|
+
if (await this.store.readHash() === hash && !pending?.length) return withPending({
|
|
2451
2533
|
status: "up-to-date",
|
|
2452
2534
|
schemaHash: hash,
|
|
2453
2535
|
entries
|
|
2454
|
-
};
|
|
2536
|
+
});
|
|
2455
2537
|
}
|
|
2456
|
-
return {
|
|
2538
|
+
return withPending({
|
|
2457
2539
|
status: "changes-needed",
|
|
2458
2540
|
schemaHash: hash,
|
|
2459
2541
|
entries
|
|
2460
|
-
};
|
|
2542
|
+
});
|
|
2461
2543
|
}
|
|
2462
2544
|
/** Fallback typeMapper for snapshot-based Path B: compares designType directly, skips unions. */
|
|
2463
2545
|
resolveTypeMapper(adapter) {
|