graphddb 0.7.10 → 0.8.1
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/README.md +6 -6
- package/dist/cdc/index.d.ts +389 -5
- package/dist/cdc/index.js +4 -4
- package/dist/{chunk-AD6ZQTTE.js → chunk-GS4C5VGO.js} +2 -6
- package/dist/{chunk-DFUKGU2Q.js → chunk-HNY2EJPV.js} +216 -229
- package/dist/{chunk-3ZU2VW3L.js → chunk-L2NEDS7U.js} +582 -781
- package/dist/chunk-L4QRCHRQ.js +278 -0
- package/dist/chunk-LAT64YCZ.js +1987 -0
- package/dist/chunk-S2NI4PBW.js +187 -0
- package/dist/{chunk-EOJDN3SA.js → chunk-T44OB5GU.js} +3757 -6138
- package/dist/{chunk-PDUVTYC5.js → chunk-XTWXMOHD.js} +0 -1
- package/dist/cli.js +63 -254
- package/dist/index.d.ts +23 -1550
- package/dist/index.js +94 -1850
- package/dist/internal/index.d.ts +84 -0
- package/dist/internal/index.js +701 -0
- package/dist/{maintenance-view-adapter-BAZ9uBGe.d.ts → key-DZtjAQDh.d.ts} +573 -1817
- package/dist/linter/index.d.ts +39 -7
- package/dist/linter/index.js +22 -4
- package/dist/{registry-LWE54Sdc.d.ts → linter-DQY7gUEk.d.ts} +22 -22
- package/dist/prepared-artifact-HFealr1q.d.ts +281 -0
- package/dist/spec/index.d.ts +506 -5
- package/dist/spec/index.js +22 -18
- package/dist/testing/index.d.ts +2 -3
- package/dist/testing/index.js +4 -4
- package/dist/transform/index.d.ts +1 -1
- package/dist/transform/index.js +4 -4
- package/dist/{types-BQLzTEqh.d.ts → types-2PMXEn5x.d.ts} +8 -10
- package/dist/types-DW__-Icc.d.ts +450 -0
- package/docs/cdc-projection.md +5 -5
- package/docs/class-hydration.md +1 -1
- package/docs/cqrs-contract.md +79 -20
- package/docs/design-patterns.md +5 -5
- package/docs/docs-generation.md +6 -6
- package/docs/middleware.md +15 -15
- package/docs/mutation-command-derivation.md +52 -42
- package/docs/prepared-statements.md +14 -14
- package/docs/python-bridge.md +96 -65
- package/docs/spec.md +153 -124
- package/docs/testing.md +9 -8
- package/package.json +14 -4
- package/dist/chunk-3UD3XIF2.js +0 -860
- package/dist/chunk-MMVHOUM4.js +0 -24
- package/dist/from-change-Ty95KA8C.d.ts +0 -327
- package/dist/index-Dc7d8mWI.d.ts +0 -1089
- package/dist/relation-depth-BRS513Tq.d.ts +0 -36
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
// src/spec/types.ts
|
|
2
|
+
var SPEC_VERSION = "1.1";
|
|
3
|
+
var SPEC_VERSION_SCP = "1.2";
|
|
4
|
+
var SPEC_VERSION_SUPPORTED = SPEC_VERSION_SCP;
|
|
5
|
+
var EXPR_VERSION = 1;
|
|
6
|
+
var MARKER_ROW_ENTITY = "__marker__";
|
|
7
|
+
|
|
8
|
+
// src/define/transaction.ts
|
|
9
|
+
var TX_REF_BRAND = /* @__PURE__ */ Symbol("graphddb:txref");
|
|
10
|
+
function isTransactionRef(value) {
|
|
11
|
+
return typeof value === "object" && value !== null && value[TX_REF_BRAND] === true;
|
|
12
|
+
}
|
|
13
|
+
var MARKER_TAGS = [
|
|
14
|
+
// pass 0: short, begins with 'A'.
|
|
15
|
+
"Axqv",
|
|
16
|
+
// pass 1: long, begins with 'Z'. The two tags share NO common prefix and NO
|
|
17
|
+
// common substring of length >= 2 (their alphabets are disjoint), and differ
|
|
18
|
+
// in length, so any fragment-extracting / length-dependent transform
|
|
19
|
+
// (`slice(0,n)`, `[i]`, `charAt`, `padStart`, `padEnd`) yields *different*
|
|
20
|
+
// fragments across the two passes -- the differential check then rejects it.
|
|
21
|
+
// (Convergence-to-constant escapes that even disjoint markers cannot separate
|
|
22
|
+
// -- `slice(0,0)` -> '' in both passes, `includes('x')` -> a constant boolean
|
|
23
|
+
// -- are caught instead by the per-access coercion ledger; see
|
|
24
|
+
// {@link makeFieldRef} and {@link defineTransaction}.)
|
|
25
|
+
"Zmnoprstuwy0123456789WIDEMARKERBODYFILLERZ"
|
|
26
|
+
];
|
|
27
|
+
var MARKER_PASS_COUNT = MARKER_TAGS.length;
|
|
28
|
+
var when = {
|
|
29
|
+
eq(left, right) {
|
|
30
|
+
assertRef(left, "when.eq");
|
|
31
|
+
return { op: "eq", left, right };
|
|
32
|
+
},
|
|
33
|
+
ne(left, right) {
|
|
34
|
+
assertRef(left, "when.ne");
|
|
35
|
+
return { op: "ne", left, right };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
function assertRef(value, context) {
|
|
39
|
+
if (!isTransactionRef(value)) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`defineTransaction: ${context} expects a field reference (e.g. \`p.status\` or an element field) on its left-hand side.`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function isTransactionDefinition(value) {
|
|
46
|
+
return typeof value === "object" && value !== null && value.__isTransactionDefinition === true;
|
|
47
|
+
}
|
|
48
|
+
function isMultiWriteBody(def) {
|
|
49
|
+
let writes = 0;
|
|
50
|
+
for (const instr of def.instructions) {
|
|
51
|
+
if (instr.kind === "forEach") {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (instr.operation === "conditionCheck") return true;
|
|
55
|
+
writes += 1;
|
|
56
|
+
if (writes > 1) return true;
|
|
57
|
+
}
|
|
58
|
+
return writes > 1;
|
|
59
|
+
}
|
|
60
|
+
var SCP_LOWERED_MARKER = "graphddb:scp-lowered/v1";
|
|
61
|
+
|
|
62
|
+
// src/spec/expression.ts
|
|
63
|
+
var UNARY_OPS = /* @__PURE__ */ new Set(["neg", "not", "len"]);
|
|
64
|
+
var BINARY_OPS = /* @__PURE__ */ new Set([
|
|
65
|
+
"add",
|
|
66
|
+
"sub",
|
|
67
|
+
"mul",
|
|
68
|
+
"div",
|
|
69
|
+
"mod",
|
|
70
|
+
"concat",
|
|
71
|
+
"eq",
|
|
72
|
+
"ne",
|
|
73
|
+
"lt",
|
|
74
|
+
"le",
|
|
75
|
+
"gt",
|
|
76
|
+
"ge",
|
|
77
|
+
"and",
|
|
78
|
+
"or",
|
|
79
|
+
"coalesce"
|
|
80
|
+
]);
|
|
81
|
+
var TERNARY_OPS = /* @__PURE__ */ new Set(["cond"]);
|
|
82
|
+
var ALL_OPS = /* @__PURE__ */ new Set([...UNARY_OPS, ...BINARY_OPS, ...TERNARY_OPS]);
|
|
83
|
+
var I64_MIN = -(2n ** 63n);
|
|
84
|
+
var I64_MAX = 2n ** 63n - 1n;
|
|
85
|
+
function cmpCodePoints(a, b) {
|
|
86
|
+
const ia = a[Symbol.iterator]();
|
|
87
|
+
const ib = b[Symbol.iterator]();
|
|
88
|
+
for (; ; ) {
|
|
89
|
+
const ra = ia.next();
|
|
90
|
+
const rb = ib.next();
|
|
91
|
+
if (ra.done && rb.done) return 0;
|
|
92
|
+
if (ra.done) return -1;
|
|
93
|
+
if (rb.done) return 1;
|
|
94
|
+
const ca = ra.value.codePointAt(0);
|
|
95
|
+
const cb = rb.value.codePointAt(0);
|
|
96
|
+
if (ca !== cb) return ca < cb ? -1 : 1;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function invalid(context, path, message) {
|
|
100
|
+
throw new Error(`${context}: invalid SCP expression at ${path}: ${message}`);
|
|
101
|
+
}
|
|
102
|
+
function isPlainObject(v) {
|
|
103
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
|
|
104
|
+
const proto = Object.getPrototypeOf(v);
|
|
105
|
+
return proto === Object.prototype || proto === null;
|
|
106
|
+
}
|
|
107
|
+
function canonicalizeNode(node, context, path) {
|
|
108
|
+
if (node === null) return null;
|
|
109
|
+
if (typeof node === "string" || typeof node === "boolean") return node;
|
|
110
|
+
if (typeof node === "number") {
|
|
111
|
+
if (!Number.isFinite(node)) {
|
|
112
|
+
invalid(context, path, `non-finite number literal (${node}).`);
|
|
113
|
+
}
|
|
114
|
+
if (Number.isInteger(node) && !Number.isSafeInteger(node)) {
|
|
115
|
+
invalid(
|
|
116
|
+
context,
|
|
117
|
+
path,
|
|
118
|
+
`integral number literal ${node} exceeds the safe-integer range; carry it as {int:"${node}"} (expression-ir.md \xA72.3).`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return node;
|
|
122
|
+
}
|
|
123
|
+
if (Array.isArray(node)) {
|
|
124
|
+
invalid(context, path, "a bare array is not an expression (use {arr:[\u2026]}).");
|
|
125
|
+
}
|
|
126
|
+
if (!isPlainObject(node)) {
|
|
127
|
+
invalid(context, path, `expected a literal or a single-key node object.`);
|
|
128
|
+
}
|
|
129
|
+
const keys = Object.keys(node);
|
|
130
|
+
if (keys.length !== 1) {
|
|
131
|
+
invalid(
|
|
132
|
+
context,
|
|
133
|
+
path,
|
|
134
|
+
`a node object must have exactly one key, got [${keys.join(", ")}].`
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
const key = keys[0];
|
|
138
|
+
const arg = node[key];
|
|
139
|
+
if (key === "int") {
|
|
140
|
+
if (typeof arg !== "string" || !/^-?[0-9]+$/.test(arg)) {
|
|
141
|
+
invalid(context, `${path}.int`, '{int:"\u2026"} expects an integer string.');
|
|
142
|
+
}
|
|
143
|
+
const v = BigInt(arg);
|
|
144
|
+
if (v < I64_MIN || v > I64_MAX) {
|
|
145
|
+
invalid(context, `${path}.int`, `int literal ${arg} exceeds i64.`);
|
|
146
|
+
}
|
|
147
|
+
if (v >= BigInt(Number.MIN_SAFE_INTEGER) && v <= BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
148
|
+
return Number(v);
|
|
149
|
+
}
|
|
150
|
+
return { int: v.toString() };
|
|
151
|
+
}
|
|
152
|
+
if (key === "float") {
|
|
153
|
+
if (typeof arg !== "number" || !Number.isFinite(arg)) {
|
|
154
|
+
invalid(context, `${path}.float`, "{float:n} expects a finite number.");
|
|
155
|
+
}
|
|
156
|
+
return Number.isInteger(arg) ? { float: arg } : arg;
|
|
157
|
+
}
|
|
158
|
+
if (key === "ref" || key === "refOpt") {
|
|
159
|
+
if (!Array.isArray(arg) || arg.length === 0) {
|
|
160
|
+
invalid(context, `${path}.${key}`, `{${key}:[\u2026]} expects a non-empty path array.`);
|
|
161
|
+
}
|
|
162
|
+
const segs = arg.map((seg, i) => {
|
|
163
|
+
if (typeof seg !== "string" || seg.length === 0) {
|
|
164
|
+
invalid(
|
|
165
|
+
context,
|
|
166
|
+
`${path}.${key}[${i}]`,
|
|
167
|
+
"a reference path segment must be a non-empty string."
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
return seg;
|
|
171
|
+
});
|
|
172
|
+
return key === "ref" ? { ref: segs } : { refOpt: segs };
|
|
173
|
+
}
|
|
174
|
+
if (key === "obj") {
|
|
175
|
+
if (!isPlainObject(arg)) {
|
|
176
|
+
invalid(context, `${path}.obj`, "{obj:{\u2026}} expects a plain object.");
|
|
177
|
+
}
|
|
178
|
+
const out = {};
|
|
179
|
+
for (const k of Object.keys(arg).sort(cmpCodePoints)) {
|
|
180
|
+
out[k] = canonicalizeNode(arg[k], context, `${path}.obj.${k}`);
|
|
181
|
+
}
|
|
182
|
+
return { obj: out };
|
|
183
|
+
}
|
|
184
|
+
if (key === "arr") {
|
|
185
|
+
if (!Array.isArray(arg)) {
|
|
186
|
+
invalid(context, `${path}.arr`, "{arr:[\u2026]} expects an array.");
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
arr: arg.map((el, i) => canonicalizeNode(el, context, `${path}.arr[${i}]`))
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (!ALL_OPS.has(key)) {
|
|
193
|
+
invalid(
|
|
194
|
+
context,
|
|
195
|
+
path,
|
|
196
|
+
`unknown operator '${key}' \u2014 outside the Expression IR v1 closed set.`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
const op = key;
|
|
200
|
+
if (!Array.isArray(arg)) {
|
|
201
|
+
invalid(context, `${path}.${op}`, `'${op}' expects an argument array.`);
|
|
202
|
+
}
|
|
203
|
+
const arity = UNARY_OPS.has(op) ? 1 : TERNARY_OPS.has(op) ? 3 : 2;
|
|
204
|
+
if (arg.length !== arity) {
|
|
205
|
+
invalid(
|
|
206
|
+
context,
|
|
207
|
+
`${path}.${op}`,
|
|
208
|
+
`'${op}' expects ${arity} argument(s), got ${arg.length}.`
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
const args = arg.map((a, i) => canonicalizeNode(a, context, `${path}.${op}[${i}]`));
|
|
212
|
+
return { [op]: args };
|
|
213
|
+
}
|
|
214
|
+
function canonicalizeExpressionSpec(input, context) {
|
|
215
|
+
if (!isPlainObject(input)) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`${context}: an SCP expression must be a plain \`{ exprVersion: 1, expr: <node> }\` object.`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
const keys = Object.keys(input).sort();
|
|
221
|
+
if (keys.length !== 2 || keys[0] !== "expr" || keys[1] !== "exprVersion") {
|
|
222
|
+
throw new Error(
|
|
223
|
+
`${context}: an SCP expression envelope must have exactly the keys \`exprVersion\` and \`expr\`; got [${Object.keys(input).join(", ")}].`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
if (input.exprVersion !== EXPR_VERSION) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
`${context}: unsupported exprVersion ${JSON.stringify(input.exprVersion)} \u2014 this build understands exprVersion ${EXPR_VERSION} only (fail-closed).`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
exprVersion: EXPR_VERSION,
|
|
233
|
+
expr: canonicalizeNode(input.expr, context, "$")
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
function isScpExprConditionInput(condition) {
|
|
237
|
+
return "scpExpr" in condition;
|
|
238
|
+
}
|
|
239
|
+
function conditionHasScp(condition) {
|
|
240
|
+
return condition?.kind === "scpExpr";
|
|
241
|
+
}
|
|
242
|
+
function operationsContainScpNodes(doc) {
|
|
243
|
+
for (const command of Object.values(doc.commands)) {
|
|
244
|
+
if (conditionHasScp(command.condition)) return true;
|
|
245
|
+
}
|
|
246
|
+
for (const transaction of Object.values(doc.transactions ?? {})) {
|
|
247
|
+
for (const item of transaction.items) {
|
|
248
|
+
if (item.guard !== void 0 || conditionHasScp(item.condition)) return true;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
for (const contract of Object.values(doc.contracts ?? {})) {
|
|
252
|
+
if (contract.kind !== "command") continue;
|
|
253
|
+
for (const method of Object.values(contract.methods)) {
|
|
254
|
+
if (method.guard !== void 0) return true;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
function operationsSpecVersion(doc) {
|
|
260
|
+
return operationsContainScpNodes(doc) ? SPEC_VERSION_SCP : SPEC_VERSION;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export {
|
|
264
|
+
SPEC_VERSION,
|
|
265
|
+
SPEC_VERSION_SCP,
|
|
266
|
+
SPEC_VERSION_SUPPORTED,
|
|
267
|
+
EXPR_VERSION,
|
|
268
|
+
MARKER_ROW_ENTITY,
|
|
269
|
+
isTransactionRef,
|
|
270
|
+
when,
|
|
271
|
+
isTransactionDefinition,
|
|
272
|
+
isMultiWriteBody,
|
|
273
|
+
SCP_LOWERED_MARKER,
|
|
274
|
+
canonicalizeExpressionSpec,
|
|
275
|
+
isScpExprConditionInput,
|
|
276
|
+
operationsContainScpNodes,
|
|
277
|
+
operationsSpecVersion
|
|
278
|
+
};
|