litedbmodel 1.2.9 → 2.0.0
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 +1 -1
- package/dist/DBModel.d.ts.map +1 -1
- package/dist/DBModel.js +6 -0
- package/dist/DBModel.js.map +1 -1
- package/dist/scp/authoring-sql.d.ts +56 -0
- package/dist/scp/authoring-sql.d.ts.map +1 -0
- package/dist/scp/authoring-sql.js +83 -0
- package/dist/scp/authoring-sql.js.map +1 -0
- package/dist/scp/authoring.d.ts +155 -0
- package/dist/scp/authoring.d.ts.map +1 -0
- package/dist/scp/authoring.js +162 -0
- package/dist/scp/authoring.js.map +1 -0
- package/dist/scp/bridge.d.ts +64 -0
- package/dist/scp/bridge.d.ts.map +1 -0
- package/dist/scp/bridge.js +329 -0
- package/dist/scp/bridge.js.map +1 -0
- package/dist/scp/catalog.d.ts +91 -0
- package/dist/scp/catalog.d.ts.map +1 -0
- package/dist/scp/catalog.js +203 -0
- package/dist/scp/catalog.js.map +1 -0
- package/dist/scp/codegen.d.ts +131 -0
- package/dist/scp/codegen.d.ts.map +1 -0
- package/dist/scp/codegen.js +137 -0
- package/dist/scp/codegen.js.map +1 -0
- package/dist/scp/compile-dialect.d.ts +39 -0
- package/dist/scp/compile-dialect.d.ts.map +1 -0
- package/dist/scp/compile-dialect.js +62 -0
- package/dist/scp/compile-dialect.js.map +1 -0
- package/dist/scp/compile-sqlite.d.ts +114 -0
- package/dist/scp/compile-sqlite.d.ts.map +1 -0
- package/dist/scp/compile-sqlite.js +149 -0
- package/dist/scp/compile-sqlite.js.map +1 -0
- package/dist/scp/dialect.d.ts +141 -0
- package/dist/scp/dialect.d.ts.map +1 -0
- package/dist/scp/dialect.js +194 -0
- package/dist/scp/dialect.js.map +1 -0
- package/dist/scp/errors.d.ts +36 -0
- package/dist/scp/errors.d.ts.map +1 -0
- package/dist/scp/errors.js +72 -0
- package/dist/scp/errors.js.map +1 -0
- package/dist/scp/guard.d.ts +47 -0
- package/dist/scp/guard.d.ts.map +1 -0
- package/dist/scp/guard.js +137 -0
- package/dist/scp/guard.js.map +1 -0
- package/dist/scp/index.cjs +4744 -0
- package/dist/scp/index.d.ts +49 -0
- package/dist/scp/index.d.ts.map +1 -0
- package/dist/scp/index.js +164 -0
- package/dist/scp/index.js.map +1 -0
- package/dist/scp/index.mjs +2000 -0
- package/dist/scp/ir.d.ts +92 -0
- package/dist/scp/ir.d.ts.map +1 -0
- package/dist/scp/ir.js +28 -0
- package/dist/scp/ir.js.map +1 -0
- package/dist/scp/relation.d.ts +136 -0
- package/dist/scp/relation.d.ts.map +1 -0
- package/dist/scp/relation.js +191 -0
- package/dist/scp/relation.js.map +1 -0
- package/dist/scp/render.d.ts +40 -0
- package/dist/scp/render.d.ts.map +1 -0
- package/dist/scp/render.js +170 -0
- package/dist/scp/render.js.map +1 -0
- package/dist/scp/runtime.d.ts +295 -0
- package/dist/scp/runtime.d.ts.map +1 -0
- package/dist/scp/runtime.js +665 -0
- package/dist/scp/runtime.js.map +1 -0
- package/dist/scp/typed-object.d.ts +88 -0
- package/dist/scp/typed-object.d.ts.map +1 -0
- package/dist/scp/typed-object.js +163 -0
- package/dist/scp/typed-object.js.map +1 -0
- package/dist/scp/write-plan.d.ts +161 -0
- package/dist/scp/write-plan.d.ts.map +1 -0
- package/dist/scp/write-plan.js +528 -0
- package/dist/scp/write-plan.js.map +1 -0
- package/dist/scp/write-runtime.d.ts +80 -0
- package/dist/scp/write-runtime.d.ts.map +1 -0
- package/dist/scp/write-runtime.js +167 -0
- package/dist/scp/write-runtime.js.map +1 -0
- package/dist/scp/writes.d.ts +251 -0
- package/dist/scp/writes.d.ts.map +1 -0
- package/dist/scp/writes.js +176 -0
- package/dist/scp/writes.js.map +1 -0
- package/docs/proposal/contracts-architecture-feasibility.md +328 -0
- package/docs/proposal/contracts-architecture.md +579 -0
- package/docs/proposal/litedbmodel-v2-scp-architecture.md +382 -0
- package/docs/proposal/sql-dynamic-expansion-spec.md +175 -0
- package/package.json +27 -5
- package/docs/README_REVIEW.md +0 -23
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* litedbmodel v2 SCP — write-time transaction runtime (WS5, #25; spec §6 / §3 / §11).
|
|
4
|
+
*
|
|
5
|
+
* Executes a derived {@link TransactionPlan} against REAL SQLite as ONE transaction with
|
|
6
|
+
* gate-first short-circuit (spec §6). It renders each ordered statement with the SAME normative
|
|
7
|
+
* {@link renderOperation} the read path uses (fragment tree + Expression-IR param slots), and
|
|
8
|
+
* drives an explicit `BEGIN` / `COMMIT` / `ROLLBACK` envelope through the SAME synchronous
|
|
9
|
+
* driver seam ({@link SqliteDb}) the WS3 runtime uses — no mock, no separate interpreter.
|
|
10
|
+
*
|
|
11
|
+
* ## The transaction envelope (spec §6) — Tx is derived, not authored
|
|
12
|
+
*
|
|
13
|
+
* BEGIN;
|
|
14
|
+
* for each ordered statement:
|
|
15
|
+
* render (input + accumulated $.entity row) → execute REAL SQL
|
|
16
|
+
* if it is a GATE and its rule FAILS → ROLLBACK and stop (remaining SQL never runs)
|
|
17
|
+
* COMMIT; (or ROLLBACK on a driver failure / gate fail)
|
|
18
|
+
*
|
|
19
|
+
* The body write's RETURNING row is captured and exposed to the derive/edges/emits stages under
|
|
20
|
+
* `$.entity.*` ({@link ENTITY_ROOT}) — the §6 example's `$.entity.id`.
|
|
21
|
+
*
|
|
22
|
+
* ## Gate-first is a REAL behavior (spec §6 "Gate First"), proven, not textual
|
|
23
|
+
*
|
|
24
|
+
* A gate statement's {@link GateRule} is evaluated on its actual driver result:
|
|
25
|
+
* - `existsElseRollback` — `SELECT 1 …` returned ZERO rows ⇒ the required row is absent ⇒
|
|
26
|
+
* ROLLBACK; the body + all later statements are NEVER prepared/executed (a real query-count
|
|
27
|
+
* drop, observable via a counting driver, and the DB is unchanged).
|
|
28
|
+
* - `insertedElseRollback` — a `unique` guard `INSERT … ON CONFLICT DO NOTHING` affected ZERO
|
|
29
|
+
* rows ⇒ a collision ⇒ ROLLBACK.
|
|
30
|
+
* - `insertedElseNoop` — an `idempotency` token INSERT affected ZERO rows ⇒ a DUPLICATE request
|
|
31
|
+
* ⇒ short-circuit as a no-op: ROLLBACK the (empty-so-far) transaction, so a duplicate
|
|
32
|
+
* `request_id` performs NO double write (idempotent).
|
|
33
|
+
*
|
|
34
|
+
* A short-circuit returns a structured {@link TransactionResult} (`committed:false`) rather than
|
|
35
|
+
* throwing — the caller distinguishes a legitimate gate outcome (absent requires / unique
|
|
36
|
+
* collision / duplicate) from a driver Failure (which IS mapped + thrown, spec §11 item 5).
|
|
37
|
+
*/
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.executeTransaction = executeTransaction;
|
|
40
|
+
exports.countingDriver = countingDriver;
|
|
41
|
+
const render_1 = require("./render");
|
|
42
|
+
const errors_1 = require("./errors");
|
|
43
|
+
const writes_1 = require("./writes");
|
|
44
|
+
const dialect_1 = require("./dialect");
|
|
45
|
+
/** bc evaluates ints to bigint; convert a rendered param to a driver-bindable value. */
|
|
46
|
+
function toDriverParam(v) {
|
|
47
|
+
if (typeof v === 'bigint') {
|
|
48
|
+
if (v >= BigInt(Number.MIN_SAFE_INTEGER) && v <= BigInt(Number.MAX_SAFE_INTEGER))
|
|
49
|
+
return Number(v);
|
|
50
|
+
return v;
|
|
51
|
+
}
|
|
52
|
+
// An emit payload evaluates to a plain object (`{obj:{…}}`); serialize it to the outbox JSON
|
|
53
|
+
// text column. Arrays are IN-list elements (not used in the write path); pass through.
|
|
54
|
+
if (v !== null && typeof v === 'object' && !Array.isArray(v))
|
|
55
|
+
return JSON.stringify(v);
|
|
56
|
+
return v;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Execute one statement's compiled op against the driver in the given scope, returning the
|
|
60
|
+
* rows (SELECT / RETURNING) plus the affected-row count (non-returning writes). A driver error
|
|
61
|
+
* is mapped and thrown (the caller ROLLBACKs). This is the SAME render→bind→execute pipeline as
|
|
62
|
+
* the WS3 read/write handlers; only the transaction envelope + gate interpretation is new.
|
|
63
|
+
*/
|
|
64
|
+
function execStatement(db, op, scope, dialect) {
|
|
65
|
+
const rendered = (0, render_1.renderOperation)(op, scope, dialect);
|
|
66
|
+
const params = rendered.params.map(toDriverParam);
|
|
67
|
+
const stmt = db.prepare(rendered.sql);
|
|
68
|
+
const hasReturn = op.component === 'Select' || /\breturning\b/i.test(rendered.sql);
|
|
69
|
+
if (hasReturn) {
|
|
70
|
+
const rows = stmt.all(...params);
|
|
71
|
+
return { rows, changes: rows.length };
|
|
72
|
+
}
|
|
73
|
+
const info = stmt.run(...params);
|
|
74
|
+
return { rows: [], changes: info.changes };
|
|
75
|
+
}
|
|
76
|
+
/** Evaluate a gate rule on a statement result → the short-circuit reason, or null to continue. */
|
|
77
|
+
function gateShortCircuit(gate, result) {
|
|
78
|
+
switch (gate) {
|
|
79
|
+
case 'existsElseRollback':
|
|
80
|
+
return result.rows.length === 0 ? 'requires_absent' : null;
|
|
81
|
+
case 'insertedElseRollback':
|
|
82
|
+
return result.changes === 0 ? 'unique_collision' : null;
|
|
83
|
+
case 'insertedElseNoop':
|
|
84
|
+
return result.changes === 0 ? 'idempotent_duplicate' : null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Execute a derived {@link TransactionPlan} as ONE real SQLite transaction with gate-first
|
|
89
|
+
* short-circuit (spec §6). Statements run in the plan's fixed order (requires → idempotency →
|
|
90
|
+
* unique → body → derive → edges → emits); a failing gate ROLLBACKs and the remaining statements
|
|
91
|
+
* are never executed. On success COMMITs and returns the `$.entity` RETURNING row.
|
|
92
|
+
*
|
|
93
|
+
* @param db the synchronous SQLite driver (better-sqlite3 `Database`), supporting `BEGIN` /
|
|
94
|
+
* `COMMIT` / `ROLLBACK` via `prepare(...).run()`.
|
|
95
|
+
* @param plan the derived transaction plan (pure JSON; from `deriveTransactionPlan` or the bundle).
|
|
96
|
+
* @param input the Command input scope (`$.input.*` = bc flat scope).
|
|
97
|
+
* @throws {SqlFailure} a mapped driver failure (the transaction is ROLLBACKed first, spec §11).
|
|
98
|
+
*/
|
|
99
|
+
function executeTransaction(db, plan, input, dialect = dialect_1.SQLITE) {
|
|
100
|
+
db.prepare('BEGIN').run();
|
|
101
|
+
const executed = [];
|
|
102
|
+
// The evolving scope: input names at the top level (bc flat scope) + the body RETURNING row
|
|
103
|
+
// exposed under `__entity` once the body runs. Defaults live in the declaration/schema, so no
|
|
104
|
+
// ad-hoc code default is injected here.
|
|
105
|
+
const scope = { ...input };
|
|
106
|
+
let entity = null;
|
|
107
|
+
try {
|
|
108
|
+
for (const stmt of plan.statements) {
|
|
109
|
+
const result = runOne(db, stmt, scope, executed, dialect);
|
|
110
|
+
// Gate-first: a failing gate short-circuits — ROLLBACK and STOP (tail never executes).
|
|
111
|
+
if (stmt.gate !== undefined) {
|
|
112
|
+
const reason = gateShortCircuit(stmt.gate, result);
|
|
113
|
+
if (reason !== null) {
|
|
114
|
+
db.prepare('ROLLBACK').run();
|
|
115
|
+
return { committed: false, shortCircuit: { statementId: stmt.id, reason }, entity: null, executed };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Capture the SOLE body RETURNING row as `$.entity` (WS5 single-write back-compat).
|
|
119
|
+
if (stmt.id === plan.entityFrom) {
|
|
120
|
+
entity = result.rows.length > 0 ? result.rows[0] : null;
|
|
121
|
+
if (entity !== null)
|
|
122
|
+
scope[writes_1.ENTITY_ROOT] = entity;
|
|
123
|
+
}
|
|
124
|
+
// Composite (WS8a): bind THIS statement's RETURNING row under its `binds` name so a later
|
|
125
|
+
// `$.ref.<binds>.<field>` resolves against it (the DAG's data-dependency edge). Self-
|
|
126
|
+
// describing: the runtime just binds the row the plan told it to — no re-derivation.
|
|
127
|
+
if (stmt.binds !== undefined && result.rows.length > 0) {
|
|
128
|
+
scope[stmt.binds] = result.rows[0];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
db.prepare('COMMIT').run();
|
|
132
|
+
return { committed: true, entity, executed };
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
// A driver failure: ROLLBACK, then map + re-throw the structured SqlFailure (spec §11).
|
|
136
|
+
try {
|
|
137
|
+
db.prepare('ROLLBACK').run();
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
/* ROLLBACK best-effort; surface the original failure below */
|
|
141
|
+
}
|
|
142
|
+
throw (0, errors_1.mapSqliteError)(e);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/** Render + execute one statement, recording it as executed. */
|
|
146
|
+
function runOne(db, stmt, scope, executed, dialect) {
|
|
147
|
+
const result = execStatement(db, stmt.op, scope, dialect);
|
|
148
|
+
executed.push(stmt.id);
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* A counting {@link SqliteDb} wrapper: forwards every call to the wrapped driver but records
|
|
153
|
+
* each PREPARED SQL string. Tests use it to PROVE gate-first short-circuit — after an absent
|
|
154
|
+
* `requires`, the body/derive/edges/emits SQL is never prepared (a real, observable query-count
|
|
155
|
+
* drop), not merely absent from a text list. This is a test aid living beside the runtime.
|
|
156
|
+
*/
|
|
157
|
+
function countingDriver(db) {
|
|
158
|
+
const prepared = [];
|
|
159
|
+
const wrapped = {
|
|
160
|
+
prepare(sql) {
|
|
161
|
+
prepared.push(sql);
|
|
162
|
+
return db.prepare(sql);
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
return { db: wrapped, prepared };
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=write-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-runtime.js","sourceRoot":"","sources":["../../src/scp/write-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* litedbmodel v2 SCP — write-time relations declaration vocabulary (WS5, #25; spec §6 / §2.2).
|
|
3
|
+
*
|
|
4
|
+
* `entityWrites` / `edgeWrites` declare the **write-side invariants / effects required for
|
|
5
|
+
* an entity to stay consistent** when it is created / updated / removed — the SQL-backend
|
|
6
|
+
* counterpart of graphddb's `entityWrites` save contract (`graphddb/src/define/entity-writes.ts`,
|
|
7
|
+
* same shape: a per-lifecycle map of `w.lifecycle({requires,unique,derive,edges,emits,idempotency})`).
|
|
8
|
+
* This module produces a PORTABLE declaration (pure data, no closures): every value that
|
|
9
|
+
* references the write is a **path-rooted string** (`$.input.<field>` = the mutation input,
|
|
10
|
+
* `$.entity.<field>` = the written row's RETURNING result). {@link ../write-plan} lowers the
|
|
11
|
+
* declaration + a base write op into an ordered, gate-first SQL transaction plan (§6 table).
|
|
12
|
+
*
|
|
13
|
+
* ## Path roots (spec §6 example) — the SSoT for every derived value
|
|
14
|
+
*
|
|
15
|
+
* A leaf value binds to an explicit root so its source is unambiguous and no engine-code
|
|
16
|
+
* fallback is ever needed (hard rule: defaults live in the declaration):
|
|
17
|
+
* - `$.input.<field>` — the Command's input port value (bc flat scope: `{ref:['<field>']}`).
|
|
18
|
+
* - `$.entity.<field>` — the just-written row (the body write's RETURNING row), exposed to
|
|
19
|
+
* the derive/edges/emits stages under the reserved `{@link ENTITY_ROOT}` binding.
|
|
20
|
+
*
|
|
21
|
+
* The declaration is INITIAL-scope (spec §6 / §13): single-statement Command + fixed-order
|
|
22
|
+
* write-time relations. It does NOT model the complex DAG derivation (WS8).
|
|
23
|
+
*/
|
|
24
|
+
/** The reserved scope binding under which the body write's RETURNING row is exposed (`$.entity.*`). */
|
|
25
|
+
export declare const ENTITY_ROOT = "__entity";
|
|
26
|
+
/**
|
|
27
|
+
* The path root a write-relation value binds from:
|
|
28
|
+
* - `input` — the Command input port (bc flat scope).
|
|
29
|
+
* - `entity` — the SOLE body write's RETURNING row (the WS5 single-base-write shorthand),
|
|
30
|
+
* bound under {@link ENTITY_ROOT}.
|
|
31
|
+
* - `ref` — a NAMED upstream write's RETURNING row (`$.ref.<writeName>.<field>`, WS8a
|
|
32
|
+
* composite/DAG scope). `entity` is exactly `ref` targeting the sole base write; keeping
|
|
33
|
+
* it distinct preserves the WS5 vocabulary while composite writes address writes by name.
|
|
34
|
+
*/
|
|
35
|
+
export type PathRoot = 'input' | 'entity' | 'ref';
|
|
36
|
+
/** A parsed, path-rooted write-relation value (`$.input.x` / `$.entity.x` / `$.ref.w.x`). */
|
|
37
|
+
export interface EffectPath {
|
|
38
|
+
readonly root: PathRoot;
|
|
39
|
+
/**
|
|
40
|
+
* For `ref`: the upstream write's NAME (the statement whose RETURNING row is referenced).
|
|
41
|
+
* Absent for `input` / `entity` (those have a single implicit source).
|
|
42
|
+
*/
|
|
43
|
+
readonly writeName?: string;
|
|
44
|
+
/** The field name after the root (a single physical column; no nested paths in α). */
|
|
45
|
+
readonly field: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Parse a path-rooted write-relation value string into an {@link EffectPath}. Fail-closed on
|
|
49
|
+
* a malformed path — a write-relation value MUST be `$.input.<field>`, `$.entity.<field>`, or
|
|
50
|
+
* `$.ref.<writeName>.<field>` (no free-form paths, no implicit root), so a typo is a loud build
|
|
51
|
+
* error, never a silent default (hard rule).
|
|
52
|
+
*/
|
|
53
|
+
export declare function parseEffectPath(value: string): EffectPath;
|
|
54
|
+
/**
|
|
55
|
+
* A referential-integrity requirement (§6 `requires`): the named `table` must contain a row
|
|
56
|
+
* whose columns equal the bound keys. Lowered to a gate-first `SELECT 1 FROM <table> WHERE …`
|
|
57
|
+
* existence guard that ROLLBACKs when absent (spec §6 example line 1).
|
|
58
|
+
*/
|
|
59
|
+
export interface RequiresEffect {
|
|
60
|
+
readonly kind: 'requires';
|
|
61
|
+
/** The table whose row must exist. */
|
|
62
|
+
readonly table: string;
|
|
63
|
+
/** target column → path-rooted source value (the existence key). */
|
|
64
|
+
readonly keys: Readonly<Record<string, string>>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A uniqueness guard (§6 `unique`): the combined `fields` must be unique within `scope`.
|
|
68
|
+
* Lowered to a guard-row `INSERT INTO <guardTable> … ON CONFLICT DO NOTHING` whose affected-
|
|
69
|
+
* row count is checked (0 rows ⇒ collision ⇒ ROLLBACK) — the SQL idiom of §6 (`ON CONFLICT
|
|
70
|
+
* DO NOTHING` + affected 検査).
|
|
71
|
+
*/
|
|
72
|
+
export interface UniqueEffect {
|
|
73
|
+
readonly kind: 'unique';
|
|
74
|
+
/** The guard-row name (its discriminator column value in the guard table). */
|
|
75
|
+
readonly name: string;
|
|
76
|
+
/** The guard table that materializes the uniqueness rows. */
|
|
77
|
+
readonly guardTable: string;
|
|
78
|
+
/** The scope the uniqueness is partitioned by (path-rooted values). */
|
|
79
|
+
readonly scope: readonly string[];
|
|
80
|
+
/** The fields whose combined value must be unique within the scope (path-rooted values). */
|
|
81
|
+
readonly fields: readonly string[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* A derived / cascading counter update (§6 `derive`): `SET <attribute> = <attribute> ± amount`
|
|
85
|
+
* on the `table` row keyed by `keys`. Lowered to `UPDATE <table> SET c = c + :amount WHERE …`
|
|
86
|
+
* (spec §6 example line "derive"). `amount` is the SSoT for the increment — never a code default.
|
|
87
|
+
*/
|
|
88
|
+
export interface DeriveEffect {
|
|
89
|
+
readonly kind: 'derive';
|
|
90
|
+
/** The table whose counter attribute is derived-updated. */
|
|
91
|
+
readonly table: string;
|
|
92
|
+
/** target column → path-rooted source value (the updated row's key). */
|
|
93
|
+
readonly keys: Readonly<Record<string, string>>;
|
|
94
|
+
/** The counter column to increment. */
|
|
95
|
+
readonly attribute: string;
|
|
96
|
+
/** The signed amount added to the counter (the declaration IS the source of truth). */
|
|
97
|
+
readonly amount: number;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* An edge write (§6 `edges`). A many-to-many edge materializes as an intermediate-table
|
|
101
|
+
* `INSERT` (create) / `DELETE` (remove); a one-to-many edge as a foreign-key column `UPDATE`
|
|
102
|
+
* on the related table. Lowered per {@link EdgeEffect.relation} (spec §6 table row `edges`).
|
|
103
|
+
*/
|
|
104
|
+
export interface EdgeEffect {
|
|
105
|
+
readonly kind: 'edge';
|
|
106
|
+
/** `m2m` → intermediate-table INSERT/DELETE; `fk` → related-table FK column UPDATE. */
|
|
107
|
+
readonly relation: 'm2m' | 'fk';
|
|
108
|
+
/** The write action for this lifecycle: `set` (create/link) or `unset` (remove/unlink). */
|
|
109
|
+
readonly action: 'set' | 'unset';
|
|
110
|
+
/**
|
|
111
|
+
* For `m2m`: the intermediate (join) table. For `fk`: the related table whose FK column is set.
|
|
112
|
+
*/
|
|
113
|
+
readonly table: string;
|
|
114
|
+
/**
|
|
115
|
+
* For `m2m`: the join-row columns → path-rooted source values (both endpoint keys).
|
|
116
|
+
* For `fk`: the FK column → path-rooted source value AND the matched-row key columns.
|
|
117
|
+
*/
|
|
118
|
+
readonly columns: Readonly<Record<string, string>>;
|
|
119
|
+
/**
|
|
120
|
+
* For `fk` only: the WHERE key columns (matched-row identity) → path-rooted source values.
|
|
121
|
+
* `columns` then holds ONLY the FK column(s) to SET. For `m2m` this is absent.
|
|
122
|
+
*/
|
|
123
|
+
readonly where?: Readonly<Record<string, string>>;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* A domain event (§6 `emits`): an outbox `INSERT INTO <outboxTable>(type, payload)` in the
|
|
127
|
+
* SAME transaction (spec §6 example line "emits"). The payload is a path-rooted map serialized
|
|
128
|
+
* to a JSON text column at render time.
|
|
129
|
+
*/
|
|
130
|
+
export interface EmitEffect {
|
|
131
|
+
readonly kind: 'emit';
|
|
132
|
+
/** The event type discriminator (e.g. `PostCreated`). */
|
|
133
|
+
readonly name: string;
|
|
134
|
+
/** The outbox table the event row is inserted into. */
|
|
135
|
+
readonly outboxTable: string;
|
|
136
|
+
/** The event payload: field → path-rooted source value. */
|
|
137
|
+
readonly payload: Readonly<Record<string, string>>;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* An idempotency guard (§6 `idempotency`): a client-token guard `INSERT INTO <table>(token)`
|
|
141
|
+
* whose UNIQUE violation detects a duplicate request (spec §6 example line "idempotency").
|
|
142
|
+
* Runs gate-first: a duplicate token short-circuits the whole transaction (no double write).
|
|
143
|
+
*/
|
|
144
|
+
export interface IdempotencyEffect {
|
|
145
|
+
readonly kind: 'idempotency';
|
|
146
|
+
/** The idempotency-key table (a single UNIQUE token column). */
|
|
147
|
+
readonly table: string;
|
|
148
|
+
/** The token column name. */
|
|
149
|
+
readonly column: string;
|
|
150
|
+
/** The path-rooted client-token value (e.g. `$.input.request_id`). */
|
|
151
|
+
readonly token: string;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The §6 effect set a {@link LifecycleContract} carries. Every field is optional; an omitted
|
|
155
|
+
* field is the empty effect set. Fixed derivation order (spec §6 example): gate-first
|
|
156
|
+
* (requires → idempotency → unique), body, then derive → edges → emits.
|
|
157
|
+
*/
|
|
158
|
+
export interface LifecycleEffects {
|
|
159
|
+
/** Referential-integrity requirements (→ gate-first existence guard). */
|
|
160
|
+
readonly requires?: readonly RequiresEffect[];
|
|
161
|
+
/** Idempotency guard (→ gate-first token INSERT; duplicate short-circuits). */
|
|
162
|
+
readonly idempotency?: IdempotencyEffect;
|
|
163
|
+
/** Uniqueness guards (→ gate-first guard-row INSERT ON CONFLICT DO NOTHING + affected check). */
|
|
164
|
+
readonly unique?: readonly UniqueEffect[];
|
|
165
|
+
/** Derived / cascading counter updates (→ UPDATE … SET c = c ± n). */
|
|
166
|
+
readonly derive?: readonly DeriveEffect[];
|
|
167
|
+
/** Edge writes (→ M:N intermediate INSERT/DELETE or 1:N FK UPDATE). */
|
|
168
|
+
readonly edges?: readonly EdgeEffect[];
|
|
169
|
+
/** Domain events (→ outbox INSERT, same tx). */
|
|
170
|
+
readonly emits?: readonly EmitEffect[];
|
|
171
|
+
}
|
|
172
|
+
/** One lifecycle's save contract — the §6 effect set for a single phase. */
|
|
173
|
+
export interface LifecycleContract {
|
|
174
|
+
readonly effects: LifecycleEffects;
|
|
175
|
+
}
|
|
176
|
+
/** The lifecycle phase a save-contract entry declares (the Command intent selects it). */
|
|
177
|
+
export type WriteLifecyclePhase = 'create' | 'update' | 'remove';
|
|
178
|
+
/** A model's per-lifecycle save contract (`entityWrites(...)` result). */
|
|
179
|
+
export interface EntityWritesDefinition {
|
|
180
|
+
readonly create?: LifecycleContract;
|
|
181
|
+
readonly update?: LifecycleContract;
|
|
182
|
+
readonly remove?: LifecycleContract;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* The recorder handed to the {@link entityWrites} / {@link edgeWrites} builder. Each method
|
|
186
|
+
* builds ONE §6 effect (pure data). `w.lifecycle({...})` gathers the effect arrays into a
|
|
187
|
+
* {@link LifecycleContract}. Every path-rooted value is validated eagerly ({@link parseEffectPath}).
|
|
188
|
+
*/
|
|
189
|
+
export interface WriteRecorder {
|
|
190
|
+
/** Build one lifecycle's save contract from its §6 effect arrays (each field optional). */
|
|
191
|
+
lifecycle(effects?: LifecycleEffects): LifecycleContract;
|
|
192
|
+
/** `requires` — a referential-integrity existence guard (§6). */
|
|
193
|
+
exists(table: string, keys: Readonly<Record<string, string>>): RequiresEffect;
|
|
194
|
+
/** `unique` — a composite-scope uniqueness guard (§6). */
|
|
195
|
+
unique(spec: {
|
|
196
|
+
readonly name: string;
|
|
197
|
+
readonly guardTable: string;
|
|
198
|
+
readonly scope: readonly string[];
|
|
199
|
+
readonly fields: readonly string[];
|
|
200
|
+
}): UniqueEffect;
|
|
201
|
+
/** `derive` — a cascade counter increment (§6). `amount` is the SSoT (no code default). */
|
|
202
|
+
increment(table: string, keys: Readonly<Record<string, string>>, attribute: string, amount: number): DeriveEffect;
|
|
203
|
+
/** `edges` — a many-to-many intermediate-table link/unlink (§6). */
|
|
204
|
+
edge(spec: {
|
|
205
|
+
readonly relation: 'm2m';
|
|
206
|
+
readonly action: 'set' | 'unset';
|
|
207
|
+
readonly table: string;
|
|
208
|
+
readonly columns: Readonly<Record<string, string>>;
|
|
209
|
+
}): EdgeEffect;
|
|
210
|
+
/** `edges` — a one-to-many foreign-key column set/unset on a related row (§6). */
|
|
211
|
+
edge(spec: {
|
|
212
|
+
readonly relation: 'fk';
|
|
213
|
+
readonly action: 'set' | 'unset';
|
|
214
|
+
readonly table: string;
|
|
215
|
+
readonly columns: Readonly<Record<string, string>>;
|
|
216
|
+
readonly where: Readonly<Record<string, string>>;
|
|
217
|
+
}): EdgeEffect;
|
|
218
|
+
/** `emits` — a domain event into an outbox table, same tx (§6). */
|
|
219
|
+
event(name: string, outboxTable: string, payload: Readonly<Record<string, string>>): EmitEffect;
|
|
220
|
+
/** `idempotency` — a client-token duplicate guard (§6). */
|
|
221
|
+
idempotentBy(table: string, column: string, token: string): IdempotencyEffect;
|
|
222
|
+
}
|
|
223
|
+
/** The per-lifecycle map an {@link entityWrites} builder returns. */
|
|
224
|
+
export interface EntityWritesShape {
|
|
225
|
+
readonly create?: LifecycleContract;
|
|
226
|
+
readonly update?: LifecycleContract;
|
|
227
|
+
readonly remove?: LifecycleContract;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Declare a model's reusable write-time-relations save contract (spec §2.2 / §6): a
|
|
231
|
+
* per-lifecycle map `{ create?, update?, remove? }`, each value a `w.lifecycle({...})`
|
|
232
|
+
* carrying the §6 effect arrays. Stored on the model as
|
|
233
|
+
* `static readonly writes = entityWrites<Model>((w) => ({ … }))`.
|
|
234
|
+
*
|
|
235
|
+
* @typeParam M documentary model type (the recorder is structural).
|
|
236
|
+
* @throws if no lifecycle is declared, or a lifecycle is not built with `w.lifecycle(...)`.
|
|
237
|
+
*/
|
|
238
|
+
export declare function entityWrites<M = unknown>(builder: (w: WriteRecorder) => EntityWritesShape): EntityWritesDefinition;
|
|
239
|
+
/**
|
|
240
|
+
* Declare an adjacency entity's edge-only write contract (spec §2.2 / §6 — the `edgeWrites`
|
|
241
|
+
* counterpart of graphddb's #82). It is a thin specialization of {@link entityWrites} whose
|
|
242
|
+
* lifecycles carry ONLY `edges` effects — the write side of a many-to-many / one-to-many
|
|
243
|
+
* relation entity. Kept distinct so an author signals intent (an edge entity vs a full
|
|
244
|
+
* save contract), matching graphddb's two coexisting `writes` forms.
|
|
245
|
+
*
|
|
246
|
+
* @throws if a lifecycle declares a non-edge effect (an edge entity has only edge writes).
|
|
247
|
+
*/
|
|
248
|
+
export declare function edgeWrites<M = unknown>(builder: (w: WriteRecorder) => EntityWritesShape): EntityWritesDefinition;
|
|
249
|
+
/** Resolve the {@link LifecycleContract} for a Command intent, or undefined if not declared. */
|
|
250
|
+
export declare function lifecycleFor(def: EntityWritesDefinition, phase: WriteLifecyclePhase): LifecycleContract | undefined;
|
|
251
|
+
//# sourceMappingURL=writes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writes.d.ts","sourceRoot":"","sources":["../../src/scp/writes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* litedbmodel v2 SCP — write-time relations declaration vocabulary (WS5, #25; spec §6 / §2.2).
|
|
4
|
+
*
|
|
5
|
+
* `entityWrites` / `edgeWrites` declare the **write-side invariants / effects required for
|
|
6
|
+
* an entity to stay consistent** when it is created / updated / removed — the SQL-backend
|
|
7
|
+
* counterpart of graphddb's `entityWrites` save contract (`graphddb/src/define/entity-writes.ts`,
|
|
8
|
+
* same shape: a per-lifecycle map of `w.lifecycle({requires,unique,derive,edges,emits,idempotency})`).
|
|
9
|
+
* This module produces a PORTABLE declaration (pure data, no closures): every value that
|
|
10
|
+
* references the write is a **path-rooted string** (`$.input.<field>` = the mutation input,
|
|
11
|
+
* `$.entity.<field>` = the written row's RETURNING result). {@link ../write-plan} lowers the
|
|
12
|
+
* declaration + a base write op into an ordered, gate-first SQL transaction plan (§6 table).
|
|
13
|
+
*
|
|
14
|
+
* ## Path roots (spec §6 example) — the SSoT for every derived value
|
|
15
|
+
*
|
|
16
|
+
* A leaf value binds to an explicit root so its source is unambiguous and no engine-code
|
|
17
|
+
* fallback is ever needed (hard rule: defaults live in the declaration):
|
|
18
|
+
* - `$.input.<field>` — the Command's input port value (bc flat scope: `{ref:['<field>']}`).
|
|
19
|
+
* - `$.entity.<field>` — the just-written row (the body write's RETURNING row), exposed to
|
|
20
|
+
* the derive/edges/emits stages under the reserved `{@link ENTITY_ROOT}` binding.
|
|
21
|
+
*
|
|
22
|
+
* The declaration is INITIAL-scope (spec §6 / §13): single-statement Command + fixed-order
|
|
23
|
+
* write-time relations. It does NOT model the complex DAG derivation (WS8).
|
|
24
|
+
*/
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.ENTITY_ROOT = void 0;
|
|
27
|
+
exports.parseEffectPath = parseEffectPath;
|
|
28
|
+
exports.entityWrites = entityWrites;
|
|
29
|
+
exports.edgeWrites = edgeWrites;
|
|
30
|
+
exports.lifecycleFor = lifecycleFor;
|
|
31
|
+
/** The reserved scope binding under which the body write's RETURNING row is exposed (`$.entity.*`). */
|
|
32
|
+
exports.ENTITY_ROOT = '__entity';
|
|
33
|
+
const PATH_RE = /^\$\.(input|entity)\.([A-Za-z_][A-Za-z0-9_]*)$/;
|
|
34
|
+
const REF_PATH_RE = /^\$\.ref\.([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)$/;
|
|
35
|
+
/**
|
|
36
|
+
* Parse a path-rooted write-relation value string into an {@link EffectPath}. Fail-closed on
|
|
37
|
+
* a malformed path — a write-relation value MUST be `$.input.<field>`, `$.entity.<field>`, or
|
|
38
|
+
* `$.ref.<writeName>.<field>` (no free-form paths, no implicit root), so a typo is a loud build
|
|
39
|
+
* error, never a silent default (hard rule).
|
|
40
|
+
*/
|
|
41
|
+
function parseEffectPath(value) {
|
|
42
|
+
const m = PATH_RE.exec(value);
|
|
43
|
+
if (m !== null)
|
|
44
|
+
return { root: m[1], field: m[2] };
|
|
45
|
+
const r = REF_PATH_RE.exec(value);
|
|
46
|
+
if (r !== null)
|
|
47
|
+
return { root: 'ref', writeName: r[1], field: r[2] };
|
|
48
|
+
throw new Error(`write-time relations: '${value}' is not a valid path-rooted value; use ` +
|
|
49
|
+
`'$.input.<field>' (the Command input), '$.entity.<field>' (the sole written row), or ` +
|
|
50
|
+
`'$.ref.<writeName>.<field>' (a named upstream write's RETURNING row).`);
|
|
51
|
+
}
|
|
52
|
+
/** Assert every value in a path-rooted map parses (fail-closed on a malformed path). */
|
|
53
|
+
function assertPaths(map) {
|
|
54
|
+
for (const v of Object.values(map))
|
|
55
|
+
parseEffectPath(v); // throws on malformed
|
|
56
|
+
}
|
|
57
|
+
const recorder = {
|
|
58
|
+
lifecycle(effects = {}) {
|
|
59
|
+
// Validate every declared effect's paths eagerly (build-time fail-closed).
|
|
60
|
+
for (const r of effects.requires ?? [])
|
|
61
|
+
assertPaths(r.keys);
|
|
62
|
+
if (effects.idempotency !== undefined)
|
|
63
|
+
parseEffectPath(effects.idempotency.token);
|
|
64
|
+
for (const u of effects.unique ?? []) {
|
|
65
|
+
u.scope.forEach((p) => parseEffectPath(p));
|
|
66
|
+
u.fields.forEach((p) => parseEffectPath(p));
|
|
67
|
+
}
|
|
68
|
+
for (const d of effects.derive ?? [])
|
|
69
|
+
assertPaths(d.keys);
|
|
70
|
+
for (const e of effects.edges ?? []) {
|
|
71
|
+
assertPaths(e.columns);
|
|
72
|
+
if (e.where !== undefined)
|
|
73
|
+
assertPaths(e.where);
|
|
74
|
+
}
|
|
75
|
+
for (const em of effects.emits ?? [])
|
|
76
|
+
assertPaths(em.payload);
|
|
77
|
+
return { effects: freezeEffects(effects) };
|
|
78
|
+
},
|
|
79
|
+
exists(table, keys) {
|
|
80
|
+
return { kind: 'requires', table, keys };
|
|
81
|
+
},
|
|
82
|
+
unique(spec) {
|
|
83
|
+
return { kind: 'unique', name: spec.name, guardTable: spec.guardTable, scope: spec.scope, fields: spec.fields };
|
|
84
|
+
},
|
|
85
|
+
increment(table, keys, attribute, amount) {
|
|
86
|
+
if (!Number.isFinite(amount)) {
|
|
87
|
+
throw new Error(`entityWrites: increment amount for '${table}.${attribute}' must be a finite number (got ${String(amount)})`);
|
|
88
|
+
}
|
|
89
|
+
return { kind: 'derive', table, keys, attribute, amount };
|
|
90
|
+
},
|
|
91
|
+
edge(spec) {
|
|
92
|
+
if (spec.relation === 'fk' && spec.where === undefined) {
|
|
93
|
+
throw new Error(`entityWrites: a 'fk' edge on '${spec.table}' requires a 'where' key map (the matched row's identity)`);
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
kind: 'edge',
|
|
97
|
+
relation: spec.relation,
|
|
98
|
+
action: spec.action,
|
|
99
|
+
table: spec.table,
|
|
100
|
+
columns: spec.columns,
|
|
101
|
+
...(spec.where !== undefined ? { where: spec.where } : {}),
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
event(name, outboxTable, payload) {
|
|
105
|
+
return { kind: 'emit', name, outboxTable, payload };
|
|
106
|
+
},
|
|
107
|
+
idempotentBy(table, column, token) {
|
|
108
|
+
return { kind: 'idempotency', table, column, token };
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
/** Keep only the present effect fields (an empty `w.lifecycle()` carries an empty set). */
|
|
112
|
+
function freezeEffects(effects) {
|
|
113
|
+
const out = {};
|
|
114
|
+
if (effects.requires !== undefined)
|
|
115
|
+
out.requires = effects.requires;
|
|
116
|
+
if (effects.idempotency !== undefined)
|
|
117
|
+
out.idempotency = effects.idempotency;
|
|
118
|
+
if (effects.unique !== undefined)
|
|
119
|
+
out.unique = effects.unique;
|
|
120
|
+
if (effects.derive !== undefined)
|
|
121
|
+
out.derive = effects.derive;
|
|
122
|
+
if (effects.edges !== undefined)
|
|
123
|
+
out.edges = effects.edges;
|
|
124
|
+
if (effects.emits !== undefined)
|
|
125
|
+
out.emits = effects.emits;
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Declare a model's reusable write-time-relations save contract (spec §2.2 / §6): a
|
|
130
|
+
* per-lifecycle map `{ create?, update?, remove? }`, each value a `w.lifecycle({...})`
|
|
131
|
+
* carrying the §6 effect arrays. Stored on the model as
|
|
132
|
+
* `static readonly writes = entityWrites<Model>((w) => ({ … }))`.
|
|
133
|
+
*
|
|
134
|
+
* @typeParam M documentary model type (the recorder is structural).
|
|
135
|
+
* @throws if no lifecycle is declared, or a lifecycle is not built with `w.lifecycle(...)`.
|
|
136
|
+
*/
|
|
137
|
+
function entityWrites(builder) {
|
|
138
|
+
void undefined;
|
|
139
|
+
const shape = builder(recorder);
|
|
140
|
+
if (shape.create === undefined && shape.update === undefined && shape.remove === undefined) {
|
|
141
|
+
throw new Error('entityWrites(...) must declare at least one lifecycle (`create` / `update` / `remove`).');
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
...(shape.create !== undefined ? { create: shape.create } : {}),
|
|
145
|
+
...(shape.update !== undefined ? { update: shape.update } : {}),
|
|
146
|
+
...(shape.remove !== undefined ? { remove: shape.remove } : {}),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Declare an adjacency entity's edge-only write contract (spec §2.2 / §6 — the `edgeWrites`
|
|
151
|
+
* counterpart of graphddb's #82). It is a thin specialization of {@link entityWrites} whose
|
|
152
|
+
* lifecycles carry ONLY `edges` effects — the write side of a many-to-many / one-to-many
|
|
153
|
+
* relation entity. Kept distinct so an author signals intent (an edge entity vs a full
|
|
154
|
+
* save contract), matching graphddb's two coexisting `writes` forms.
|
|
155
|
+
*
|
|
156
|
+
* @throws if a lifecycle declares a non-edge effect (an edge entity has only edge writes).
|
|
157
|
+
*/
|
|
158
|
+
function edgeWrites(builder) {
|
|
159
|
+
const def = entityWrites(builder);
|
|
160
|
+
for (const phase of ['create', 'update', 'remove']) {
|
|
161
|
+
const c = def[phase];
|
|
162
|
+
if (c === undefined)
|
|
163
|
+
continue;
|
|
164
|
+
const e = c.effects;
|
|
165
|
+
if (e.requires !== undefined || e.unique !== undefined || e.derive !== undefined || e.emits !== undefined || e.idempotency !== undefined) {
|
|
166
|
+
throw new Error(`edgeWrites: the '${phase}' lifecycle may declare ONLY 'edges' effects ` +
|
|
167
|
+
`(an edge entity carries only edge writes; use entityWrites for a full save contract).`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return def;
|
|
171
|
+
}
|
|
172
|
+
/** Resolve the {@link LifecycleContract} for a Command intent, or undefined if not declared. */
|
|
173
|
+
function lifecycleFor(def, phase) {
|
|
174
|
+
return def[phase];
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=writes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writes.js","sourceRoot":"","sources":["../../src/scp/writes.ts"],"names":[],"mappings":"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"}
|