litedbmodel 2.0.0 → 2.0.2
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/scp/authoring-sql.d.ts +78 -0
- package/dist/scp/authoring-sql.d.ts.map +1 -1
- package/dist/scp/authoring-sql.js +110 -2
- package/dist/scp/authoring-sql.js.map +1 -1
- package/dist/scp/catalog.d.ts +5 -2
- package/dist/scp/catalog.d.ts.map +1 -1
- package/dist/scp/catalog.js +32 -1
- package/dist/scp/catalog.js.map +1 -1
- package/dist/scp/codegen.d.ts +143 -85
- package/dist/scp/codegen.d.ts.map +1 -1
- package/dist/scp/codegen.js +374 -90
- package/dist/scp/codegen.js.map +1 -1
- package/dist/scp/coltype.d.ts +41 -0
- package/dist/scp/coltype.d.ts.map +1 -0
- package/dist/scp/coltype.js +188 -0
- package/dist/scp/coltype.js.map +1 -0
- package/dist/scp/dialect.d.ts +12 -3
- package/dist/scp/dialect.d.ts.map +1 -1
- package/dist/scp/dialect.js +12 -3
- package/dist/scp/dialect.js.map +1 -1
- package/dist/scp/find-filter-guard.d.ts +68 -0
- package/dist/scp/find-filter-guard.d.ts.map +1 -0
- package/dist/scp/find-filter-guard.js +88 -0
- package/dist/scp/find-filter-guard.js.map +1 -0
- package/dist/scp/fragment/assemble.d.ts +61 -0
- package/dist/scp/fragment/assemble.d.ts.map +1 -0
- package/dist/scp/fragment/assemble.js +156 -0
- package/dist/scp/fragment/assemble.js.map +1 -0
- package/dist/scp/fragment/compile-pg.d.ts +107 -0
- package/dist/scp/fragment/compile-pg.d.ts.map +1 -0
- package/dist/scp/fragment/compile-pg.js +142 -0
- package/dist/scp/fragment/compile-pg.js.map +1 -0
- package/dist/scp/fragment/index.d.ts +16 -0
- package/dist/scp/fragment/index.d.ts.map +1 -0
- package/dist/scp/fragment/index.js +34 -0
- package/dist/scp/fragment/index.js.map +1 -0
- package/dist/scp/fragment/model.d.ts +119 -0
- package/dist/scp/fragment/model.d.ts.map +1 -0
- package/dist/scp/fragment/model.js +66 -0
- package/dist/scp/fragment/model.js.map +1 -0
- package/dist/scp/fragment/pg-array-type.d.ts +13 -0
- package/dist/scp/fragment/pg-array-type.d.ts.map +1 -0
- package/dist/scp/fragment/pg-array-type.js +34 -0
- package/dist/scp/fragment/pg-array-type.js.map +1 -0
- package/dist/scp/guard.d.ts +2 -6
- package/dist/scp/guard.d.ts.map +1 -1
- package/dist/scp/guard.js +19 -22
- package/dist/scp/guard.js.map +1 -1
- package/dist/scp/index.cjs +18695 -3921
- package/dist/scp/index.d.ts +41 -24
- package/dist/scp/index.d.ts.map +1 -1
- package/dist/scp/index.js +134 -49
- package/dist/scp/index.js.map +1 -1
- package/dist/scp/index.mjs +4830 -1432
- package/dist/scp/makesql/authoring-compile.d.ts +90 -0
- package/dist/scp/makesql/authoring-compile.d.ts.map +1 -0
- package/dist/scp/makesql/authoring-compile.js +494 -0
- package/dist/scp/makesql/authoring-compile.js.map +1 -0
- package/dist/scp/makesql/compile-crud.d.ts +133 -0
- package/dist/scp/makesql/compile-crud.d.ts.map +1 -0
- package/dist/scp/makesql/compile-crud.js +288 -0
- package/dist/scp/makesql/compile-crud.js.map +1 -0
- package/dist/scp/makesql/compile-relation.d.ts +157 -0
- package/dist/scp/makesql/compile-relation.d.ts.map +1 -0
- package/dist/scp/makesql/compile-relation.js +425 -0
- package/dist/scp/makesql/compile-relation.js.map +1 -0
- package/dist/scp/makesql/compile-select.d.ts +52 -0
- package/dist/scp/makesql/compile-select.d.ts.map +1 -0
- package/dist/scp/makesql/compile-select.js +58 -0
- package/dist/scp/makesql/compile-select.js.map +1 -0
- package/dist/scp/makesql/compile.d.ts +63 -0
- package/dist/scp/makesql/compile.d.ts.map +1 -0
- package/dist/scp/makesql/compile.js +99 -0
- package/dist/scp/makesql/compile.js.map +1 -0
- package/dist/scp/makesql/handler.d.ts +58 -0
- package/dist/scp/makesql/handler.d.ts.map +1 -0
- package/dist/scp/makesql/handler.js +113 -0
- package/dist/scp/makesql/handler.js.map +1 -0
- package/dist/scp/makesql/index.d.ts +33 -0
- package/dist/scp/makesql/index.d.ts.map +1 -0
- package/dist/scp/makesql/index.js +114 -0
- package/dist/scp/makesql/index.js.map +1 -0
- package/dist/scp/makesql/ir.d.ts +42 -0
- package/dist/scp/makesql/ir.d.ts.map +1 -0
- package/dist/scp/makesql/ir.js +66 -0
- package/dist/scp/makesql/ir.js.map +1 -0
- package/dist/scp/makesql/json-array.d.ts +84 -0
- package/dist/scp/makesql/json-array.d.ts.map +1 -0
- package/dist/scp/makesql/json-array.js +118 -0
- package/dist/scp/makesql/json-array.js.map +1 -0
- package/dist/scp/makesql/json-batch.d.ts +102 -0
- package/dist/scp/makesql/json-batch.d.ts.map +1 -0
- package/dist/scp/makesql/json-batch.js +269 -0
- package/dist/scp/makesql/json-batch.js.map +1 -0
- package/dist/scp/makesql/makesql.d.ts +99 -0
- package/dist/scp/makesql/makesql.d.ts.map +1 -0
- package/dist/scp/makesql/makesql.js +122 -0
- package/dist/scp/makesql/makesql.js.map +1 -0
- package/dist/scp/makesql/outtype.d.ts +44 -0
- package/dist/scp/makesql/outtype.d.ts.map +1 -0
- package/dist/scp/makesql/outtype.js +197 -0
- package/dist/scp/makesql/outtype.js.map +1 -0
- package/dist/scp/makesql/pool-executor.d.ts +47 -0
- package/dist/scp/makesql/pool-executor.d.ts.map +1 -0
- package/dist/scp/makesql/pool-executor.js +50 -0
- package/dist/scp/makesql/pool-executor.js.map +1 -0
- package/dist/scp/makesql/static-bundle.d.ts +187 -0
- package/dist/scp/makesql/static-bundle.d.ts.map +1 -0
- package/dist/scp/makesql/static-bundle.js +1113 -0
- package/dist/scp/makesql/static-bundle.js.map +1 -0
- package/dist/scp/makesql/tx.d.ts +216 -0
- package/dist/scp/makesql/tx.d.ts.map +1 -0
- package/dist/scp/makesql/tx.js +788 -0
- package/dist/scp/makesql/tx.js.map +1 -0
- package/dist/scp/makesql/writeouttype.d.ts +66 -0
- package/dist/scp/makesql/writeouttype.d.ts.map +1 -0
- package/dist/scp/makesql/writeouttype.js +195 -0
- package/dist/scp/makesql/writeouttype.js.map +1 -0
- package/dist/scp/relation.d.ts +86 -74
- package/dist/scp/relation.d.ts.map +1 -1
- package/dist/scp/relation.js +186 -120
- package/dist/scp/relation.js.map +1 -1
- package/dist/scp/runtime.d.ts +158 -211
- package/dist/scp/runtime.d.ts.map +1 -1
- package/dist/scp/runtime.js +250 -515
- package/dist/scp/runtime.js.map +1 -1
- package/dist/scp/typed-object.d.ts +12 -1
- package/dist/scp/typed-object.d.ts.map +1 -1
- package/dist/scp/typed-object.js +25 -4
- package/dist/scp/typed-object.js.map +1 -1
- package/docs/proposal/litedbmodel-v2-scp-architecture.md +51 -0
- package/docs/proposal/v0-coverage-matrix.md +157 -0
- package/docs/proposal/v2-completeness-plan.md +90 -0
- package/docs/proposal/v2-sql-parity-checklist.md +162 -0
- package/package.json +3 -3
- package/dist/scp/bridge.d.ts +0 -64
- package/dist/scp/bridge.d.ts.map +0 -1
- package/dist/scp/bridge.js +0 -329
- package/dist/scp/bridge.js.map +0 -1
- package/dist/scp/compile-dialect.d.ts +0 -39
- package/dist/scp/compile-dialect.d.ts.map +0 -1
- package/dist/scp/compile-dialect.js +0 -62
- package/dist/scp/compile-dialect.js.map +0 -1
- package/dist/scp/compile-sqlite.d.ts +0 -114
- package/dist/scp/compile-sqlite.d.ts.map +0 -1
- package/dist/scp/compile-sqlite.js +0 -149
- package/dist/scp/compile-sqlite.js.map +0 -1
- package/dist/scp/ir.d.ts +0 -92
- package/dist/scp/ir.d.ts.map +0 -1
- package/dist/scp/ir.js +0 -28
- package/dist/scp/ir.js.map +0 -1
- package/dist/scp/render.d.ts +0 -40
- package/dist/scp/render.d.ts.map +0 -1
- package/dist/scp/render.js +0 -170
- package/dist/scp/render.js.map +0 -1
- package/dist/scp/write-plan.d.ts +0 -161
- package/dist/scp/write-plan.d.ts.map +0 -1
- package/dist/scp/write-plan.js +0 -528
- package/dist/scp/write-plan.js.map +0 -1
- package/dist/scp/write-runtime.d.ts +0 -80
- package/dist/scp/write-runtime.d.ts.map +0 -1
- package/dist/scp/write-runtime.js +0 -167
- package/dist/scp/write-runtime.js.map +0 -1
package/dist/scp/render.js
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* litedbmodel v2 SCP — fragment-tree render + param assembly (WS1, #21).
|
|
4
|
-
*
|
|
5
|
-
* The NORMATIVE reference implementation of `docs/proposal/sql-dynamic-expansion-spec.md`.
|
|
6
|
-
* Given a compiled operation and a bound input scope, it deterministically produces the
|
|
7
|
-
* final SQLite SQL text (`?` placeholders) and the flat params array. Every multi-language
|
|
8
|
-
* runtime (WS3) must reproduce this byte-for-byte; this TS implementation is the golden
|
|
9
|
-
* reference the conformance harness pins.
|
|
10
|
-
*
|
|
11
|
-
* The three moving parts (dynamic-expansion spec):
|
|
12
|
-
* §2 SKIP → fragment existence: a fragment with `when` is present iff `when` evaluates
|
|
13
|
-
* to a present (non-null / true) binding; an absent fragment contributes NO SQL and
|
|
14
|
-
* NO params.
|
|
15
|
-
* §3 empty-WHERE degeneration: if no fragment is present the whole `{where}` splice
|
|
16
|
-
* (including the ` WHERE ` keyword) collapses to the empty string.
|
|
17
|
-
* §4 AND/OR structure + parenthesization: a nested tree renders `(… <connector> …)`.
|
|
18
|
-
* §5 IN-list array expansion: a fragment slot marked `expand` turns its `(?)` into
|
|
19
|
-
* `(?, ?, …)` for each element of the bound array (0 elements → the `1 = 0`
|
|
20
|
-
* always-false degeneration, byte-identical to litedbmodel v1's empty-IN handling).
|
|
21
|
-
*/
|
|
22
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.renderOperation = renderOperation;
|
|
24
|
-
const behavior_contracts_1 = require("behavior-contracts");
|
|
25
|
-
const ir_1 = require("./ir");
|
|
26
|
-
const dialect_1 = require("./dialect");
|
|
27
|
-
/** A fragment tree carries a `connector`; a fragment carries `sql`. */
|
|
28
|
-
function isTree(node) {
|
|
29
|
-
return 'connector' in node;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* SKIP existence rule (dynamic-expansion spec §2): a fragment is present iff it is
|
|
33
|
-
* `always`, or its `when` Expression evaluates to a PRESENT binding — `null` and `false`
|
|
34
|
-
* are absent; everything else (including `0`, `""`) is present. This mirrors bc's
|
|
35
|
-
* strict-bool discipline: `when` is expected to be an explicit presence/bool Expression
|
|
36
|
-
* (e.g. `{ne:[{refOpt:["status"]}, null]}` in bc's flat scope), evaluated fail-closed by
|
|
37
|
-
* evaluateExpression.
|
|
38
|
-
*/
|
|
39
|
-
function fragmentPresent(f, scope) {
|
|
40
|
-
if (f.always === true)
|
|
41
|
-
return true;
|
|
42
|
-
if (f.when === undefined)
|
|
43
|
-
return false; // fail-closed: neither always nor when
|
|
44
|
-
const v = (0, behavior_contracts_1.evaluateExpression)(f.when, scope);
|
|
45
|
-
return v !== null && v !== false;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Render one leaf fragment's SQL + params into the accumulators. Handles IN-list
|
|
49
|
-
* expansion (spec §5): the slot at `expand` is an array; its `(?)` in `sql` becomes
|
|
50
|
-
* `(?, ?, …)` and each element is pushed as its own param.
|
|
51
|
-
*/
|
|
52
|
-
function renderFragment(f, scope, params) {
|
|
53
|
-
if (f.expand === undefined) {
|
|
54
|
-
for (const slot of f.params)
|
|
55
|
-
params.push((0, behavior_contracts_1.evaluateExpression)(slot, scope));
|
|
56
|
-
return f.sql;
|
|
57
|
-
}
|
|
58
|
-
// IN-list expansion. Evaluate all slots; the `expand` slot must be an array.
|
|
59
|
-
let sql = f.sql;
|
|
60
|
-
for (let i = 0; i < f.params.length; i++) {
|
|
61
|
-
const v = (0, behavior_contracts_1.evaluateExpression)(f.params[i], scope);
|
|
62
|
-
if (i === f.expand) {
|
|
63
|
-
if (!Array.isArray(v)) {
|
|
64
|
-
throw new Error(`IN-list expansion slot ${i} did not bind to an array (got ${v === null ? 'null' : typeof v})`);
|
|
65
|
-
}
|
|
66
|
-
if (v.length === 0) {
|
|
67
|
-
// Empty-array degeneration (dynamic-expansion spec §5): the whole `col IN (?)`
|
|
68
|
-
// fragment collapses to the always-false sentinel `1 = 0` — byte-identical to
|
|
69
|
-
// litedbmodel v1's DBConditions (an empty IN pushes NO params). The `col IN`
|
|
70
|
-
// prefix and the `(?)` slot are BOTH replaced.
|
|
71
|
-
sql = '1 = 0';
|
|
72
|
-
// no params pushed for this slot
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
// Replace the single `(?)` with `(?, ?, …)` sized to the array; push each element.
|
|
76
|
-
sql = sql.replace('(?)', `(${v.map(() => '?').join(', ')})`);
|
|
77
|
-
for (const el of v)
|
|
78
|
-
params.push(el);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
else {
|
|
82
|
-
params.push(v);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return sql;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Render a fragment tree into a WHERE clause body (no leading ` WHERE ` keyword).
|
|
89
|
-
* Present fragments are joined by ` <connector> `; a nested tree is parenthesized
|
|
90
|
-
* (spec §4). Returns the empty string when NO fragment is present (degeneration, §3).
|
|
91
|
-
*
|
|
92
|
-
* Each fragment's `sql` already carries its own leading connector prefix when authored as
|
|
93
|
-
* a chained condition (e.g. `" AND status = ?"`), so this function STRIPS the accumulated
|
|
94
|
-
* connector from the first present fragment to avoid a dangling leading `AND`/`OR`, then
|
|
95
|
-
* joins the rest as-authored. To keep the golden output identical to litedbmodel v1 (which
|
|
96
|
-
* builds `parts.join(' AND ')` with no leading connector), fragment `sql` at the top level
|
|
97
|
-
* is authored WITHOUT a leading connector and joined here.
|
|
98
|
-
*/
|
|
99
|
-
function renderTree(tree, scope, params) {
|
|
100
|
-
const parts = [];
|
|
101
|
-
for (const node of tree.fragments) {
|
|
102
|
-
if (isTree(node)) {
|
|
103
|
-
const inner = renderTree(node, scope, params);
|
|
104
|
-
if (inner !== '')
|
|
105
|
-
parts.push(`(${inner})`);
|
|
106
|
-
}
|
|
107
|
-
else if (fragmentPresent(node, scope)) {
|
|
108
|
-
parts.push(renderFragment(node, scope, params));
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (parts.length === 0)
|
|
112
|
-
return '';
|
|
113
|
-
return parts.join(` ${tree.connector} `);
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Render a compiled operation to final SQLite SQL + params for a bound input scope.
|
|
117
|
-
*
|
|
118
|
-
* Param order (matches SQL text order, dynamic-expansion spec §6): the compiled `sql`
|
|
119
|
-
* splices the WHERE tree at `{where}`; static params before the marker are emitted first,
|
|
120
|
-
* then the fragment params in tree order, then static params after the marker. WS1's CRUD
|
|
121
|
-
* shapes place all pre-WHERE statics (Insert values, Update SET) before `{where}` and all
|
|
122
|
-
* post-WHERE statics (LIMIT / OFFSET) after — so a single left-to-right walk of the
|
|
123
|
-
* spliced SQL yields the canonical `?` order.
|
|
124
|
-
*/
|
|
125
|
-
function renderOperation(op, input, dialect = dialect_1.SQLITE) {
|
|
126
|
-
const params = [];
|
|
127
|
-
const markerIdx = op.sql.indexOf(ir_1.WHERE_SLOT);
|
|
128
|
-
if (markerIdx === -1) {
|
|
129
|
-
// No dynamic WHERE: all params are static, in position order.
|
|
130
|
-
for (const slot of op.params)
|
|
131
|
-
params.push((0, behavior_contracts_1.evaluateExpression)(slot, input));
|
|
132
|
-
return { sql: finalize(op.sql, dialect), params };
|
|
133
|
-
}
|
|
134
|
-
const before = op.sql.slice(0, markerIdx);
|
|
135
|
-
const after = op.sql.slice(markerIdx + ir_1.WHERE_SLOT.length);
|
|
136
|
-
// Static params are partitioned by whether their `?` sits before or after the marker.
|
|
137
|
-
const beforeQ = countPlaceholders(before);
|
|
138
|
-
const preStatics = op.params.slice(0, beforeQ);
|
|
139
|
-
const postStatics = op.params.slice(beforeQ);
|
|
140
|
-
for (const slot of preStatics)
|
|
141
|
-
params.push((0, behavior_contracts_1.evaluateExpression)(slot, input));
|
|
142
|
-
let whereSql = '';
|
|
143
|
-
if (op.where !== null) {
|
|
144
|
-
const body = renderTree(op.where, input, params);
|
|
145
|
-
if (body !== '')
|
|
146
|
-
whereSql = ` WHERE ${body}`; // degeneration §3: drop keyword when empty
|
|
147
|
-
}
|
|
148
|
-
for (const slot of postStatics)
|
|
149
|
-
params.push((0, behavior_contracts_1.evaluateExpression)(slot, input));
|
|
150
|
-
return { sql: finalize(before + whereSql + after, dialect), params };
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Apply the dialect's final placeholder pass over the FULLY-assembled SQL text (spec §8): for
|
|
154
|
-
* Postgres this converts `?`→`$1,$2,…` in ONE left-to-right pass (the number-reassignment
|
|
155
|
-
* problem is designed out — it runs once, after IN-list expansion + fragment splicing are all
|
|
156
|
-
* done); for SQLite/MySQL it is identity. This is the single place the `?`→`$N` conversion
|
|
157
|
-
* happens, so `params` order (already position-ordered by the walk above) matches `$N` order.
|
|
158
|
-
*/
|
|
159
|
-
function finalize(sql, dialect) {
|
|
160
|
-
return dialect.finalizePlaceholders(sql);
|
|
161
|
-
}
|
|
162
|
-
/** Count `?` placeholders in a static SQL segment (no fragment markers present). */
|
|
163
|
-
function countPlaceholders(sql) {
|
|
164
|
-
let n = 0;
|
|
165
|
-
for (const ch of sql)
|
|
166
|
-
if (ch === '?')
|
|
167
|
-
n++;
|
|
168
|
-
return n;
|
|
169
|
-
}
|
|
170
|
-
//# sourceMappingURL=render.js.map
|
package/dist/scp/render.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/scp/render.ts"],"names":[],"mappings":"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"}
|
package/dist/scp/write-plan.d.ts
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* litedbmodel v2 SCP — write-time relations → ordered SQL transaction plan (WS5, #25; spec §6).
|
|
3
|
-
*
|
|
4
|
-
* Derives a declarative {@link LifecycleContract} (the §6 effect arrays) + a base write op
|
|
5
|
-
* (the Command's `Insert`/`Update`/`Delete`, e.g. `Insert(Post, {onWrite: Post.writes.create})`)
|
|
6
|
-
* into ONE ordered, gate-first multi-statement transaction plan — exactly the §6 example:
|
|
7
|
-
*
|
|
8
|
-
* ```
|
|
9
|
-
* BEGIN;
|
|
10
|
-
* -- Gate First (short-circuit + ROLLBACK before any body write)
|
|
11
|
-
* requires: SELECT 1 FROM users WHERE id = :author_id; -- absent ⇒ fail
|
|
12
|
-
* idempotency: INSERT INTO idem(token) VALUES(:request_id); -- duplicate ⇒ short-circuit
|
|
13
|
-
* unique: INSERT INTO uniq(...) ON CONFLICT DO NOTHING; … -- collision ⇒ fail
|
|
14
|
-
* -- Body
|
|
15
|
-
* INSERT INTO posts(author_id,title) VALUES(:author_id,:title) RETURNING id;
|
|
16
|
-
* derive: UPDATE users SET post_count = post_count + 1 WHERE id = :author_id;
|
|
17
|
-
* edges: … (M:N intermediate INSERT/DELETE or 1:N FK UPDATE) …
|
|
18
|
-
* emits: INSERT INTO outbox(type,payload) VALUES('PostCreated', :payload);
|
|
19
|
-
* COMMIT;
|
|
20
|
-
* ```
|
|
21
|
-
*
|
|
22
|
-
* The plan is PURE JSON (each statement is a WS1 {@link CompiledOperation} rendered by the
|
|
23
|
-
* SAME {@link import('./render').renderOperation}), so it serializes into the §8 bundle and a
|
|
24
|
-
* thin per-language runtime honors the SAME ordering + gate-first (no re-derivation, spec §6:
|
|
25
|
-
* "多言語 runtime は同一計画を honor"). `Transaction` は公開仕様にしない — this plan is DERIVED
|
|
26
|
-
* from the Command's declared intent, never authored.
|
|
27
|
-
*
|
|
28
|
-
* ## Gate-first is a real execution behavior, not a text ordering (spec §6 "Gate First")
|
|
29
|
-
*
|
|
30
|
-
* Each gate statement carries a {@link GateRule} the runtime evaluates AFTER executing it: a
|
|
31
|
-
* failing gate short-circuits — the remaining statements (body + derive + edges + emits) never
|
|
32
|
-
* execute and the transaction ROLLBACKs. This is semantically-invariant early termination
|
|
33
|
-
* ({@link import('./write-runtime').executeTransaction} proves it with real query counts + DB state).
|
|
34
|
-
*
|
|
35
|
-
* ## Path lowering (spec §6) — `$.input.*` / `$.entity.*` → closed-set Expression IR
|
|
36
|
-
*
|
|
37
|
-
* A `$.input.<field>` value lowers to `{ref:['<field>']}` (bc flat input scope); a
|
|
38
|
-
* `$.entity.<field>` value lowers to `{ref:['__entity','<field>']}` (the body write's
|
|
39
|
-
* RETURNING row, exposed to the derive/edges/emits stages under {@link ENTITY_ROOT}). Only
|
|
40
|
-
* bc's closed operator set (`ref`) is emitted — no invented opcode (hard rule).
|
|
41
|
-
*
|
|
42
|
-
* ## Transaction derivation (spec §6 / §14) — tx-DAG + gate-first
|
|
43
|
-
*
|
|
44
|
-
* A Command carries one or more named base writes, each with its §6 effects. Statements form a
|
|
45
|
-
* data-dependency DAG (`$.ref.<writeName>.<field>` consumes an earlier write's RETURNING row,
|
|
46
|
-
* exposed via `TxStatement.binds`), with a gate-first constraint (every gate precedes every
|
|
47
|
-
* body/derive/edge/emit). The DAG is topologically ordered (Kahn, stable ascending declaration
|
|
48
|
-
* `seq` tie-break) into a single-transaction statement plan. Underivable shapes (dependency
|
|
49
|
-
* cycle, dangling `$.ref`, referenced write without RETURNING, duplicate bind, composite
|
|
50
|
-
* `$.entity`) are LOUD rejects — never a silently mis-ordered plan.
|
|
51
|
-
*/
|
|
52
|
-
import type { CompiledOperation } from './ir';
|
|
53
|
-
import { type Dialect } from './dialect';
|
|
54
|
-
import { type LifecycleContract, type LifecycleEffects, type WriteLifecyclePhase } from './writes';
|
|
55
|
-
/** The role a transaction statement plays (drives the runtime's gate-first interpretation). */
|
|
56
|
-
export type StatementRole = 'gate:requires' | 'gate:idempotency' | 'gate:unique' | 'body' | 'derive' | 'edge' | 'emit';
|
|
57
|
-
/**
|
|
58
|
-
* The gate rule the runtime evaluates on a gate statement's result to decide short-circuit
|
|
59
|
-
* (spec §6 "Gate First"):
|
|
60
|
-
* - `existsElseRollback` — the statement is a `SELECT 1 …` existence probe; ZERO rows ⇒
|
|
61
|
-
* the required row is absent ⇒ short-circuit + ROLLBACK (`requires`).
|
|
62
|
-
* - `insertedElseRollback` — the statement is a guard `INSERT … ON CONFLICT DO NOTHING`;
|
|
63
|
-
* ZERO affected rows ⇒ a collision ⇒ short-circuit + ROLLBACK (`unique`).
|
|
64
|
-
* - `insertedElseNoop` — the statement is an idempotency-token `INSERT … ON CONFLICT DO
|
|
65
|
-
* NOTHING`; ZERO affected rows ⇒ a DUPLICATE request ⇒ short-circuit WITHOUT error
|
|
66
|
-
* (idempotent: the prior write already happened; the tx commits the no-op / rolls back
|
|
67
|
-
* the empty body per {@link TransactionPlan.onIdempotentHit}).
|
|
68
|
-
*/
|
|
69
|
-
export type GateRule = 'existsElseRollback' | 'insertedElseRollback' | 'insertedElseNoop';
|
|
70
|
-
/** One ordered statement of a transaction plan (pure JSON — a WS1 compiled op + its role). */
|
|
71
|
-
export interface TxStatement {
|
|
72
|
-
/** Stable statement id (ordering key + diagnostics). */
|
|
73
|
-
readonly id: string;
|
|
74
|
-
/** The statement's role in the §6 derivation order. */
|
|
75
|
-
readonly role: StatementRole;
|
|
76
|
-
/** The compiled SQL op (rendered by the normative {@link import('./render').renderOperation}). */
|
|
77
|
-
readonly op: CompiledOperation;
|
|
78
|
-
/** For a gate statement: the short-circuit rule. Absent for body/derive/edge/emit. */
|
|
79
|
-
readonly gate?: GateRule;
|
|
80
|
-
/**
|
|
81
|
-
* For a composite (multi-write) body statement: the scope binding under which THIS statement's
|
|
82
|
-
* RETURNING row is exposed to LATER statements — a downstream `$.ref.<binds>.<field>` resolves
|
|
83
|
-
* to `scope[<binds>][<field>]` (spec §6 nested write). This is the DAG's data-dependency edge,
|
|
84
|
-
* self-describing so the 5 runtimes just bind the row and continue (no re-derivation). Absent for
|
|
85
|
-
* a statement no later statement depends on; the WS5 single-base write additionally exposes its
|
|
86
|
-
* row under {@link TransactionPlan.entityFrom} / {@link ENTITY_ROOT} for `$.entity.*` back-compat.
|
|
87
|
-
*/
|
|
88
|
-
readonly binds?: string;
|
|
89
|
-
/** Human label (diagnostics; e.g. `requires users`, `derive users.post_count`). */
|
|
90
|
-
readonly label: string;
|
|
91
|
-
}
|
|
92
|
-
/** How the runtime resolves an idempotency short-circuit hit (a duplicate request). */
|
|
93
|
-
export type IdempotentHitPolicy = 'rollback';
|
|
94
|
-
/**
|
|
95
|
-
* A derived write-time-relations transaction plan (spec §6 / §8 `transaction plan`). Pure JSON:
|
|
96
|
-
* ordered statements + gate-first rules + the body-write's `entity` exposure. Serializes into
|
|
97
|
-
* the §8 bundle so a WS7 runtime executes the SAME plan.
|
|
98
|
-
*/
|
|
99
|
-
export interface TransactionPlan {
|
|
100
|
-
/** The lifecycle phase this plan realizes (`create` / `update` / `remove`). */
|
|
101
|
-
readonly phase: WriteLifecyclePhase;
|
|
102
|
-
/**
|
|
103
|
-
* The statement id whose RETURNING row is exposed to later stages under `$.entity.*`
|
|
104
|
-
* ({@link ENTITY_ROOT}). Always the body write. `null` when the body has no RETURNING
|
|
105
|
-
* (then no later stage may reference `$.entity.*` — validated at derivation time).
|
|
106
|
-
*/
|
|
107
|
-
readonly entityFrom: string | null;
|
|
108
|
-
/** The ordered statements (gate-first, then body, then derive/edges/emits). */
|
|
109
|
-
readonly statements: readonly TxStatement[];
|
|
110
|
-
/** What a duplicate-idempotency-token short-circuit does (α: rollback the empty tx). */
|
|
111
|
-
readonly onIdempotentHit: IdempotentHitPolicy;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* The base write op the Command declares (`Insert`/`Update`/`Delete` with `onWrite`). This is
|
|
115
|
-
* the Command body statement the plan wraps in a transaction. A Command may declare ONE base
|
|
116
|
-
* write (WS5 single-statement scope) OR several (WS8a composite / nested write, spec §6:
|
|
117
|
-
* "多対多や nested write(親作成と同時に子作成)は edges/追加 write で表現し、同一 tx にまとめる").
|
|
118
|
-
*/
|
|
119
|
-
export interface BaseWrite {
|
|
120
|
-
/** The compiled base write op (from the bridge, e.g. `compileNode` of the Insert node). */
|
|
121
|
-
readonly op: CompiledOperation;
|
|
122
|
-
/** The body statement's role (always `body`). */
|
|
123
|
-
readonly label: string;
|
|
124
|
-
/**
|
|
125
|
-
* The write's stable NAME (WS8a composite scope) — later statements reference its RETURNING row
|
|
126
|
-
* as `$.ref.<name>.<field>`, and the derived body statement `binds` this name. Omitted for the
|
|
127
|
-
* WS5 single-base write (its row is addressed as `$.entity.*` under {@link ENTITY_ROOT}).
|
|
128
|
-
*/
|
|
129
|
-
readonly name?: string;
|
|
130
|
-
/**
|
|
131
|
-
* The write's OWN save-contract effects (WS8a composite scope): its gates/derive/edges/emits.
|
|
132
|
-
* For the WS5 single-base write, the lifecycle is passed as the top-level `lifecycle` arg and
|
|
133
|
-
* this is omitted. When a base write carries `effects`, the derivation treats it as one DAG
|
|
134
|
-
* node group (its gates → its body → its after-effects), then topologically orders ALL groups.
|
|
135
|
-
*/
|
|
136
|
-
readonly effects?: LifecycleEffects;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Derive the ORDERED, gate-first {@link TransactionPlan} from a Command's base write(s) + its
|
|
140
|
-
* lifecycle save contract (spec §6 / §14).
|
|
141
|
-
*
|
|
142
|
-
* ## Single base write (WS5 scope)
|
|
143
|
-
*
|
|
144
|
-
* One base write + a top-level `lifecycle`; the order is the fixed §6 group
|
|
145
|
-
* (requires → idempotency → unique → BODY → derive → edges → emits). `$.entity.*` references in
|
|
146
|
-
* derive/edges/emits require the body write to RETURN the entity row (else a loud build error).
|
|
147
|
-
*
|
|
148
|
-
* ## Composite / nested write (WS8a scope) — real DAG derivation
|
|
149
|
-
*
|
|
150
|
-
* Multiple base writes, each carrying its OWN {@link BaseWrite.effects} and a `name`. A later
|
|
151
|
-
* write references an earlier write's RETURNING row via `$.ref.<name>.<field>` (e.g. a child
|
|
152
|
-
* INSERT keyed by the parent's returned id). The derivation builds the data-dependency graph
|
|
153
|
-
* (statement → the writes it references) + the gate-first constraint, TOPOLOGICALLY orders it
|
|
154
|
-
* (deterministic, stable tie-break), and emits ONE ordered gate-first transaction plan. A cycle
|
|
155
|
-
* or dangling reference is ESCALATED (no silent mis-derivation).
|
|
156
|
-
*
|
|
157
|
-
* @throws if a `$.entity.*` reference lacks a RETURNING body, a `$.ref.*` is dangling, two writes
|
|
158
|
-
* bind the same name, or the dependency graph has a cycle.
|
|
159
|
-
*/
|
|
160
|
-
export declare function deriveTransactionPlan(phase: WriteLifecyclePhase, bases: readonly BaseWrite[], lifecycle: LifecycleContract, dialect?: Dialect): TransactionPlan;
|
|
161
|
-
//# sourceMappingURL=write-plan.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"write-plan.d.ts","sourceRoot":"","sources":["../../src/scp/write-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAY,MAAM,MAAM,CAAC;AAGxD,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAOL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAGrB,KAAK,mBAAmB,EACzB,MAAM,UAAU,CAAC;AAElB,+FAA+F;AAC/F,MAAM,MAAM,aAAa,GACrB,eAAe,GACf,kBAAkB,GAClB,aAAa,GACb,MAAM,GACN,QAAQ,GACR,MAAM,GACN,MAAM,CAAC;AAEX;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,QAAQ,GAAG,oBAAoB,GAAG,sBAAsB,GAAG,kBAAkB,CAAC;AAE1F,8FAA8F;AAC9F,MAAM,WAAW,WAAW;IAC1B,wDAAwD;IACxD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,kGAAkG;IAClG,QAAQ,CAAC,EAAE,EAAE,iBAAiB,CAAC;IAC/B,sFAAsF;IACtF,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC;IACzB;;;;;;;OAOG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,uFAAuF;AACvF,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC;AAE7C;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,+EAA+E;IAC/E,QAAQ,CAAC,UAAU,EAAE,SAAS,WAAW,EAAE,CAAC;IAC5C,wFAAwF;IACxF,QAAQ,CAAC,eAAe,EAAE,mBAAmB,CAAC;CAC/C;AAiMD;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACxB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE,EAAE,iBAAiB,CAAC;IAC/B,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAuJD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,mBAAmB,EAC1B,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,SAAS,EAAE,iBAAiB,EAC5B,OAAO,GAAE,OAAgB,GACxB,eAAe,CA2DjB"}
|