litedbmodel 2.0.2 → 2.2.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 +7 -0
- package/dist/DBConditions.d.ts.map +1 -1
- package/dist/DBConditions.js +16 -1
- package/dist/DBConditions.js.map +1 -1
- package/dist/DBHandler.d.ts.map +1 -1
- package/dist/DBHandler.js +6 -0
- package/dist/DBHandler.js.map +1 -1
- package/dist/DBModel.d.ts +84 -2
- package/dist/DBModel.d.ts.map +1 -1
- package/dist/DBModel.js +312 -29
- package/dist/DBModel.js.map +1 -1
- package/dist/decorators.d.ts +52 -0
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +127 -24
- package/dist/decorators.js.map +1 -1
- package/dist/drivers/mysql.d.ts.map +1 -1
- package/dist/drivers/mysql.js +12 -2
- package/dist/drivers/mysql.js.map +1 -1
- package/dist/drivers/postgres.d.ts.map +1 -1
- package/dist/drivers/postgres.js +13 -2
- package/dist/drivers/postgres.js.map +1 -1
- package/dist/drivers/sqlite.d.ts +2 -0
- package/dist/drivers/sqlite.d.ts.map +1 -1
- package/dist/drivers/sqlite.js +49 -4
- package/dist/drivers/sqlite.js.map +1 -1
- package/dist/index.cjs +10741 -0
- package/dist/index.d.cts +1 -0
- package/dist/scp/coltype.d.ts +108 -0
- package/dist/scp/coltype.d.ts.map +1 -1
- package/dist/scp/coltype.js +293 -6
- package/dist/scp/coltype.js.map +1 -1
- package/dist/scp/connection-routing.d.ts +312 -0
- package/dist/scp/connection-routing.d.ts.map +1 -0
- package/dist/scp/connection-routing.js +394 -0
- package/dist/scp/connection-routing.js.map +1 -0
- package/dist/scp/dbmodel-runtime.d.ts +101 -0
- package/dist/scp/dbmodel-runtime.d.ts.map +1 -0
- package/dist/scp/dbmodel-runtime.js +174 -0
- package/dist/scp/dbmodel-runtime.js.map +1 -0
- package/dist/scp/decorator-adapter.d.ts +196 -0
- package/dist/scp/decorator-adapter.d.ts.map +1 -0
- package/dist/scp/decorator-adapter.js +286 -0
- package/dist/scp/decorator-adapter.js.map +1 -0
- package/dist/scp/emit/emitter.d.ts +108 -0
- package/dist/scp/emit/emitter.d.ts.map +1 -0
- package/dist/scp/emit/emitter.js +782 -0
- package/dist/scp/emit/emitter.js.map +1 -0
- package/dist/scp/emit/endpoint.d.ts +248 -0
- package/dist/scp/emit/endpoint.d.ts.map +1 -0
- package/dist/scp/emit/endpoint.js +20 -0
- package/dist/scp/emit/endpoint.js.map +1 -0
- package/dist/scp/emit/index.d.ts +11 -0
- package/dist/scp/emit/index.d.ts.map +1 -0
- package/dist/scp/emit/index.js +13 -0
- package/dist/scp/emit/index.js.map +1 -0
- package/dist/scp/errors.d.ts +45 -1
- package/dist/scp/errors.d.ts.map +1 -1
- package/dist/scp/errors.js +47 -1
- package/dist/scp/errors.js.map +1 -1
- package/dist/scp/exec-context.d.ts +390 -0
- package/dist/scp/exec-context.d.ts.map +1 -0
- package/dist/scp/exec-context.js +540 -0
- package/dist/scp/exec-context.js.map +1 -0
- package/dist/scp/grouping.d.ts +32 -0
- package/dist/scp/grouping.d.ts.map +1 -0
- package/dist/scp/grouping.js +0 -0
- package/dist/scp/grouping.js.map +1 -0
- package/dist/scp/index.cjs +6432 -17579
- package/dist/scp/index.d.cts +1 -0
- package/dist/scp/index.d.mts +1 -0
- package/dist/scp/index.d.ts +51 -52
- package/dist/scp/index.d.ts.map +1 -1
- package/dist/scp/index.js +189 -152
- package/dist/scp/index.js.map +1 -1
- package/dist/scp/index.mjs +6067 -3659
- package/dist/scp/leaf-transport.d.ts +50 -0
- package/dist/scp/leaf-transport.d.ts.map +1 -0
- package/dist/scp/leaf-transport.js +92 -0
- package/dist/scp/leaf-transport.js.map +1 -0
- package/dist/scp/leaves.d.ts +174 -0
- package/dist/scp/leaves.d.ts.map +1 -0
- package/dist/scp/leaves.js +287 -0
- package/dist/scp/leaves.js.map +1 -0
- package/dist/scp/limit-config.d.ts +118 -0
- package/dist/scp/limit-config.d.ts.map +1 -0
- package/dist/scp/limit-config.js +126 -0
- package/dist/scp/limit-config.js.map +1 -0
- package/dist/scp/makesql/compile-relation.d.ts +49 -23
- package/dist/scp/makesql/compile-relation.d.ts.map +1 -1
- package/dist/scp/makesql/compile-relation.js +101 -76
- package/dist/scp/makesql/compile-relation.js.map +1 -1
- package/dist/scp/makesql/compile-select.d.ts +45 -10
- package/dist/scp/makesql/compile-select.d.ts.map +1 -1
- package/dist/scp/makesql/compile-select.js +50 -10
- package/dist/scp/makesql/compile-select.js.map +1 -1
- package/dist/scp/makesql/handler.d.ts +1 -1
- package/dist/scp/makesql/handler.d.ts.map +1 -1
- package/dist/scp/makesql/index.d.ts +7 -10
- package/dist/scp/makesql/index.d.ts.map +1 -1
- package/dist/scp/makesql/index.js +19 -30
- package/dist/scp/makesql/index.js.map +1 -1
- package/dist/scp/makesql/json-array.d.ts +65 -0
- package/dist/scp/makesql/json-array.d.ts.map +1 -1
- package/dist/scp/makesql/json-array.js +99 -10
- package/dist/scp/makesql/json-array.js.map +1 -1
- package/dist/scp/makesql/json-batch.d.ts.map +1 -1
- package/dist/scp/makesql/json-batch.js +21 -19
- package/dist/scp/makesql/json-batch.js.map +1 -1
- package/dist/scp/makesql/makesql.d.ts +1 -1
- package/dist/scp/makesql/makesql.d.ts.map +1 -1
- package/dist/scp/makesql/mysql-returning.d.ts +93 -0
- package/dist/scp/makesql/mysql-returning.d.ts.map +1 -0
- package/dist/scp/makesql/mysql-returning.js +215 -0
- package/dist/scp/makesql/mysql-returning.js.map +1 -0
- package/dist/scp/makesql/outtype.d.ts +75 -34
- package/dist/scp/makesql/outtype.d.ts.map +1 -1
- package/dist/scp/makesql/outtype.js +91 -118
- package/dist/scp/makesql/outtype.js.map +1 -1
- package/dist/scp/makesql/pool-executor.d.ts +135 -13
- package/dist/scp/makesql/pool-executor.d.ts.map +1 -1
- package/dist/scp/makesql/pool-executor.js +229 -21
- package/dist/scp/makesql/pool-executor.js.map +1 -1
- package/dist/scp/makesql/tx.d.ts +107 -22
- package/dist/scp/makesql/tx.d.ts.map +1 -1
- package/dist/scp/makesql/tx.js +451 -97
- package/dist/scp/makesql/tx.js.map +1 -1
- package/dist/scp/makesql/writeouttype.d.ts +7 -8
- package/dist/scp/makesql/writeouttype.d.ts.map +1 -1
- package/dist/scp/makesql/writeouttype.js +8 -7
- package/dist/scp/makesql/writeouttype.js.map +1 -1
- package/dist/scp/middleware.d.ts +287 -0
- package/dist/scp/middleware.d.ts.map +1 -0
- package/dist/scp/middleware.js +358 -0
- package/dist/scp/middleware.js.map +1 -0
- package/dist/scp/relation.d.ts +80 -9
- package/dist/scp/relation.d.ts.map +1 -1
- package/dist/scp/relation.js +198 -78
- package/dist/scp/relation.js.map +1 -1
- package/dist/scp/tx-options.d.ts +136 -0
- package/dist/scp/tx-options.d.ts.map +1 -0
- package/dist/scp/tx-options.js +184 -0
- package/dist/scp/tx-options.js.map +1 -0
- package/dist/scp/write-bundle.d.ts +131 -0
- package/dist/scp/write-bundle.d.ts.map +1 -0
- package/dist/scp/write-bundle.js +160 -0
- package/dist/scp/write-bundle.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/docs/BENCHMARK.md +58 -56
- package/docs/benchmark-chart.svg +166 -166
- package/docs/design/phase-a-execution-context.md +139 -0
- package/package.json +16 -9
- package/dist/scp/authoring-sql.d.ts +0 -134
- package/dist/scp/authoring-sql.d.ts.map +0 -1
- package/dist/scp/authoring-sql.js +0 -191
- package/dist/scp/authoring-sql.js.map +0 -1
- package/dist/scp/authoring.d.ts +0 -155
- package/dist/scp/authoring.d.ts.map +0 -1
- package/dist/scp/authoring.js +0 -162
- package/dist/scp/authoring.js.map +0 -1
- package/dist/scp/catalog.d.ts +0 -94
- package/dist/scp/catalog.d.ts.map +0 -1
- package/dist/scp/catalog.js +0 -234
- package/dist/scp/catalog.js.map +0 -1
- package/dist/scp/codegen.d.ts +0 -189
- package/dist/scp/codegen.d.ts.map +0 -1
- package/dist/scp/codegen.js +0 -421
- package/dist/scp/codegen.js.map +0 -1
- package/dist/scp/find-filter-guard.d.ts +0 -68
- package/dist/scp/find-filter-guard.d.ts.map +0 -1
- package/dist/scp/find-filter-guard.js +0 -88
- package/dist/scp/find-filter-guard.js.map +0 -1
- package/dist/scp/guard.d.ts +0 -43
- package/dist/scp/guard.d.ts.map +0 -1
- package/dist/scp/guard.js +0 -134
- package/dist/scp/guard.js.map +0 -1
- package/dist/scp/makesql/authoring-compile.d.ts +0 -90
- package/dist/scp/makesql/authoring-compile.d.ts.map +0 -1
- package/dist/scp/makesql/authoring-compile.js +0 -494
- package/dist/scp/makesql/authoring-compile.js.map +0 -1
- package/dist/scp/makesql/ir.d.ts +0 -42
- package/dist/scp/makesql/ir.d.ts.map +0 -1
- package/dist/scp/makesql/ir.js +0 -66
- package/dist/scp/makesql/ir.js.map +0 -1
- package/dist/scp/makesql/static-bundle.d.ts +0 -187
- package/dist/scp/makesql/static-bundle.d.ts.map +0 -1
- package/dist/scp/makesql/static-bundle.js +0 -1113
- package/dist/scp/makesql/static-bundle.js.map +0 -1
- package/dist/scp/runtime.d.ts +0 -242
- package/dist/scp/runtime.d.ts.map +0 -1
- package/dist/scp/runtime.js +0 -400
- package/dist/scp/runtime.js.map +0 -1
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* litedbmodel v2 SCP — the decorator METADATA adapter (Phase F-1, epic #74, issue #104).
|
|
4
|
+
*
|
|
5
|
+
* litedbmodel's decorators are metadata-only COLLECTORS: `@column` / `@hasMany` / … record
|
|
6
|
+
* `ColumnMeta` / `RelationMeta` / `TABLE_NAME` on the class (`src/decorators.ts`, via `reflect-metadata`
|
|
7
|
+
* + `_columnMeta` / `_relationMeta`). This module READS that registry and translates it into the SCP-level
|
|
8
|
+
* facts the rest of the stack consumes, plus the write bundles a decorated model compiles to. It builds
|
|
9
|
+
* NO SQL structure of its own and holds NO authoring vocabulary — the decorator never emits IR, and the
|
|
10
|
+
* adapter never re-implements composition.
|
|
11
|
+
*
|
|
12
|
+
* ## What the adapter translates
|
|
13
|
+
*
|
|
14
|
+
* 1. **Columns** (the one real translation): `@column.*` `ColumnMeta` (the decorator already knows the
|
|
15
|
+
* type family — bigint / date / boolean / uuid / json / array / …) → the SCP column SoT
|
|
16
|
+
* ({@link ModelColumns}: the SQL-type token per column, validated by `coltype.ts`). See
|
|
17
|
+
* {@link deriveModelColumns} + {@link COLUMN_FAMILY_SQL_TYPE}. The typed-read `outType` and the
|
|
18
|
+
* relation key-array element type both resolve against exactly this SoT.
|
|
19
|
+
* 2. **Relations**: `@hasMany` / `@belongsTo` / `@hasOne` `RelationMeta` → {@link RelationDecl} →
|
|
20
|
+
* `compileRelationOp` (single + composite keys, per-parent `limit` window + `hardLimit`).
|
|
21
|
+
* 3. **Write bundles**: `createMany` (and a `create` WITH `onConflict`) / `updateMany` / `deleteMany` →
|
|
22
|
+
* `compileCreateManyBundle` / `compileUpdateManyBundle` / `compileDeleteManyBundle`, with the
|
|
23
|
+
* model's column SoT threaded as the write de-box resolver.
|
|
24
|
+
*
|
|
25
|
+
* READS are NOT compiled here. A read endpoint is DECLARED on the `@behavior` / `@leaf` authoring
|
|
26
|
+
* surface and lowered by `bc generate`; an ad-hoc runtime-shaped query runs the v1 imperative path.
|
|
27
|
+
*/
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.DEFAULT_UNCAST_SQL_TYPE = exports.COLUMN_FAMILY_SQL_TYPE = void 0;
|
|
30
|
+
exports.columnSqlType = columnSqlType;
|
|
31
|
+
exports.deriveModelColumns = deriveModelColumns;
|
|
32
|
+
exports.tableNameOf = tableNameOf;
|
|
33
|
+
exports.compileCreateBundle = compileCreateBundle;
|
|
34
|
+
exports.compileUpdateBundle = compileUpdateBundle;
|
|
35
|
+
exports.compileDeleteBundle = compileDeleteBundle;
|
|
36
|
+
exports.relationKeyTypeResolver = relationKeyTypeResolver;
|
|
37
|
+
exports.modelColumnResolver = modelColumnResolver;
|
|
38
|
+
exports.deriveRelationDecls = deriveRelationDecls;
|
|
39
|
+
exports.relationDeclOf = relationDeclOf;
|
|
40
|
+
exports.compileRelationOps = compileRelationOps;
|
|
41
|
+
const write_bundle_1 = require("./write-bundle");
|
|
42
|
+
const relation_1 = require("./relation");
|
|
43
|
+
const coltype_1 = require("./coltype");
|
|
44
|
+
const decorators_1 = require("../decorators");
|
|
45
|
+
const Column_1 = require("../Column");
|
|
46
|
+
// ── 1. Column-type mapping (decorator ColumnMeta → the SCP column SQL type) ─────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* The decorator's `ColumnMeta.sqlCast` type-family token → the canonical §4.1 SQL-type token the SCP
|
|
49
|
+
* `static columns` SoT carries (validated by `coltype.ts`). The decorator ALREADY knows the family
|
|
50
|
+
* (it set `sqlCast` from the `@column.*` variant / the `design:type` inference — `src/decorators.ts`),
|
|
51
|
+
* so this is a pure token normalization, no inference. `coltype.ts` accepts every RHS here:
|
|
52
|
+
* BOOLEAN/BIGINT/TIMESTAMP/DATE/UUID/JSONB are scalar; the array tokens de-box as `passthrough`
|
|
53
|
+
* (`sqlTypeToMaterializeClass`).
|
|
54
|
+
*
|
|
55
|
+
* The RHS matches the decorator families 1:1:
|
|
56
|
+
* - `boolean` → `BOOLEAN` (`@column.boolean()` / auto `Boolean`)
|
|
57
|
+
* - `bigint` → `BIGINT` (`@column.bigint()` / auto `BigInt` — read de-box: exact string)
|
|
58
|
+
* - `timestamp` → `TIMESTAMP` (`@column.datetime()` / auto `Date` — read de-box: TZ string)
|
|
59
|
+
* - `date` → `DATE` (`@column.date()` — read de-box: YYYY-MM-DD string)
|
|
60
|
+
* - `uuid` → `UUID` (`@column.uuid()`)
|
|
61
|
+
* - `jsonb` → `JSONB` (`@column.json()`)
|
|
62
|
+
* - `text[]` / `int[]` / `numeric[]` / `boolean[]` → the array token (`@column.*Array()`)
|
|
63
|
+
*/
|
|
64
|
+
exports.COLUMN_FAMILY_SQL_TYPE = {
|
|
65
|
+
boolean: 'BOOLEAN',
|
|
66
|
+
bigint: 'BIGINT',
|
|
67
|
+
timestamp: 'TIMESTAMP',
|
|
68
|
+
date: 'DATE',
|
|
69
|
+
uuid: 'UUID',
|
|
70
|
+
jsonb: 'JSONB',
|
|
71
|
+
'text[]': 'TEXT[]',
|
|
72
|
+
'int[]': 'INT[]',
|
|
73
|
+
'numeric[]': 'NUMERIC[]',
|
|
74
|
+
'boolean[]': 'BOOLEAN[]',
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* The LAST-RESORT SQL type for a column that carries NO `sqlCast` family, NO `baseSqlType` (its
|
|
78
|
+
* `design:type` is absent — no `emitDecoratorMetadata` — or is an Array/Object family), and NO
|
|
79
|
+
* `columnTypes` override. Phase F-2 (#105 option B) made {@link columnSqlType} consult the decorator's
|
|
80
|
+
* `baseSqlType` (derived from the field's TS `design:type`: String→TEXT / Number→INTEGER / Boolean→
|
|
81
|
+
* BOOLEAN / Date→TIMESTAMP / BigInt→BIGINT) BEFORE this default, so a bare `@column() name: string`
|
|
82
|
+
* types as `TEXT` (not `INTEGER`) — the fix for F1's blanket-INTEGER read-de-box defect (it threw
|
|
83
|
+
* `materialize int32` on a live string column). This default now only applies when `design:type` is
|
|
84
|
+
* genuinely unavailable; a REAL/DECIMAL column (a `Number` that is not INT) is pinned via
|
|
85
|
+
* {@link DeriveColumnsOptions.columnTypes} (the escape hatch, unchanged from F1).
|
|
86
|
+
*/
|
|
87
|
+
exports.DEFAULT_UNCAST_SQL_TYPE = 'INTEGER';
|
|
88
|
+
/**
|
|
89
|
+
* Translate ONE column's {@link ColumnMeta} to its §4.1 SQL-type token. Precedence (Phase F-2 / #105
|
|
90
|
+
* option B):
|
|
91
|
+
* 1. an explicit `columnTypes` override (the REAL/DECIMAL/width escape hatch) wins;
|
|
92
|
+
* 2. else the family token from {@link COLUMN_FAMILY_SQL_TYPE} (an explicit `@column.*` `sqlCast`);
|
|
93
|
+
* 3. else the decorator's `baseSqlType` — derived from the field's TS `design:type` (String→TEXT /
|
|
94
|
+
* Number→INTEGER / Boolean→BOOLEAN / Date→TIMESTAMP / BigInt→BIGINT), so a bare `@column()` types
|
|
95
|
+
* correctly for the SCP typed-read de-box (the fix for F1's blanket-INTEGER default);
|
|
96
|
+
* 4. else {@link DEFAULT_UNCAST_SQL_TYPE} (only when `design:type` is unavailable).
|
|
97
|
+
* Fail-closed: a family token the mapping does not know, or a produced token `coltype.ts` rejects,
|
|
98
|
+
* THROWS (naming the column) — never a silent skip (no-assume, no-fallback).
|
|
99
|
+
*/
|
|
100
|
+
function columnSqlType(propKey, meta, override) {
|
|
101
|
+
const sqlType = override ??
|
|
102
|
+
(meta.sqlCast !== undefined ? exports.COLUMN_FAMILY_SQL_TYPE[meta.sqlCast] : undefined) ??
|
|
103
|
+
meta.baseSqlType ??
|
|
104
|
+
exports.DEFAULT_UNCAST_SQL_TYPE;
|
|
105
|
+
if (meta.sqlCast !== undefined && override === undefined && exports.COLUMN_FAMILY_SQL_TYPE[meta.sqlCast] === undefined) {
|
|
106
|
+
throw new Error(`decorator-adapter: column '${propKey}' has decorator sqlCast family '${meta.sqlCast}' with no ` +
|
|
107
|
+
`SCP SQL-type mapping. Add it to COLUMN_FAMILY_SQL_TYPE or pin the column via ` +
|
|
108
|
+
`options.columnTypes['${propKey}']. No-assume, no-fallback.`);
|
|
109
|
+
}
|
|
110
|
+
// Validate the produced token is in the §4.1 vocabulary (throws for an unknown/ambiguous type).
|
|
111
|
+
(0, coltype_1.sqlTypeToMaterializeClass)(sqlType);
|
|
112
|
+
return sqlType;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Derive the SCP `static columns` ({@link ModelColumns}) for a decorated model class from its
|
|
116
|
+
* `@column` `ColumnMeta` registry (the decorator IS the type SoT for v1/v2's decorator surface). The
|
|
117
|
+
* table is the model's `TABLE_NAME` (or the model-name lowercased, matching the decorator's
|
|
118
|
+
* `effectiveTableName`). Each column keys by its DB `columnName` (what a SELECT projects), mapping to
|
|
119
|
+
* its §4.1 SQL type. A model with no `@column` declarations yields no table entry.
|
|
120
|
+
*
|
|
121
|
+
* This is the ONE real translation in the adapter — the read-path de-box (INT→number / BIGINT→string /
|
|
122
|
+
* DATE→string / BOOLEAN→boolean) and the codegen `outType` both consult exactly this SoT.
|
|
123
|
+
*/
|
|
124
|
+
function deriveModelColumns(modelClass, options = {}) {
|
|
125
|
+
const meta = (0, decorators_1.getColumnMeta)(modelClass);
|
|
126
|
+
if (meta === undefined || meta.size === 0)
|
|
127
|
+
return {};
|
|
128
|
+
const table = tableNameOf(modelClass);
|
|
129
|
+
const cols = {};
|
|
130
|
+
for (const [propKey, m] of meta) {
|
|
131
|
+
cols[m.columnName] = columnSqlType(propKey, m, options.columnTypes?.[propKey]);
|
|
132
|
+
}
|
|
133
|
+
return { [table]: cols };
|
|
134
|
+
}
|
|
135
|
+
/** The effective table name (v1 `@model` rule): explicit `TABLE_NAME`, else the model name lowercased. */
|
|
136
|
+
function tableNameOf(modelClass) {
|
|
137
|
+
return modelClass.TABLE_NAME ?? modelClass.name.toLowerCase();
|
|
138
|
+
}
|
|
139
|
+
// ── 2. Write bundles (createMany / updateMany / deleteMany) ─────────────────────────────────────
|
|
140
|
+
/**
|
|
141
|
+
* Compile a decorated model's `createMany` — and a `create` WITH `onConflict` (upsert) — into a batch
|
|
142
|
+
* write {@link SqlBundle} via `compileCreateManyBundle`. This is the UPSERT carry: `onConflict` /
|
|
143
|
+
* `onConflictUpdate` / `onConflictIgnore` are `compileInsertMany` BUILD options (not authored ports),
|
|
144
|
+
* so they carry end-to-end here with NO SCP authoring addition — exactly as v1's `create` and
|
|
145
|
+
* `createMany` share the ONE `DBModel._insert` grouping path (`buildInsert` handles ON CONFLICT for
|
|
146
|
+
* both a single record and a batch). A single-record `records` array is a one-group createMany that
|
|
147
|
+
* emits the SAME statement `_insert` does for a single upsert.
|
|
148
|
+
*
|
|
149
|
+
* Parity scope (the v1/v2 SQL-parity rule, per dialect — NOT a blanket "byte-identical to v1"): on
|
|
150
|
+
* **Postgres** the emitted upsert INSERT is byte-identical to v1 (`compileInsertMany` copies the v1
|
|
151
|
+
* `buildInsert` verbatim; PG stays base-class tuple/placeholder form). On **MySQL / SQLite** the v2
|
|
152
|
+
* form is the JSON-array single-param shape (`json_each` / `JSON_TABLE`), which is DELIBERATELY NOT
|
|
153
|
+
* byte-identical to v1's per-row placeholder expansion — it is the established v2 shape the conformance
|
|
154
|
+
* corpus freezes (#64/#65), executing to the same rows. So: pg = v1-byte-identical; mysql/sqlite = the
|
|
155
|
+
* v2 JSON-array form (equivalent result, distinct text).
|
|
156
|
+
*
|
|
157
|
+
* @param options the `compileCreateManyBundle` options (records / rawRecords / sqlCastMap / onConflict /
|
|
158
|
+
* onConflictUpdate / onConflictIgnore / returning / pk) — already serialized as `DBModel._insert`
|
|
159
|
+
* holds them.
|
|
160
|
+
*/
|
|
161
|
+
function compileCreateBundle(modelClass, name, options, dialect = 'sqlite', columnsOptions) {
|
|
162
|
+
const resolveColumnType = modelColumnResolver(modelClass, columnsOptions);
|
|
163
|
+
return (0, write_bundle_1.compileCreateManyBundle)(name, options, dialect, resolveColumnType);
|
|
164
|
+
}
|
|
165
|
+
/** Compile a decorated model's `updateMany` into a batch write {@link SqlBundle} (`compileUpdateManyBundle`). */
|
|
166
|
+
function compileUpdateBundle(name, options, dialect = 'sqlite') {
|
|
167
|
+
return (0, write_bundle_1.compileUpdateManyBundle)(name, options, dialect);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Compile a decorated model's `deleteMany` into a batch write {@link SqlBundle} (`compileDeleteManyBundle`):
|
|
171
|
+
* a PK-set IN-list DELETE (single key) or one DELETE per composite-key group. `keyColumns` +
|
|
172
|
+
* `returning` carry straight through.
|
|
173
|
+
*/
|
|
174
|
+
function compileDeleteBundle(name, options, dialect = 'sqlite') {
|
|
175
|
+
return (0, write_bundle_1.compileDeleteManyBundle)(name, options, dialect);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Build a {@link KeyTypeResolver} from a decorated model's `static columns` SoT — `(table, column) →`
|
|
179
|
+
* the de-box bc scalar of that key column ({@link import('./coltype').keyArrayElemScalar}). Threaded into
|
|
180
|
+
* {@link relationReadAuthoring} so each relation's `pluck` key array is stamped with the type its
|
|
181
|
+
* READ-materialized key value carries. Returns `undefined` when the model declares no columns.
|
|
182
|
+
*/
|
|
183
|
+
function relationKeyTypeResolver(modelClass, columnsOptions) {
|
|
184
|
+
const columns = deriveModelColumns(modelClass, columnsOptions);
|
|
185
|
+
if (Object.keys(columns).length === 0)
|
|
186
|
+
return undefined;
|
|
187
|
+
return (table, column) => {
|
|
188
|
+
const sqlType = columns[table]?.[column];
|
|
189
|
+
if (sqlType === undefined) {
|
|
190
|
+
throw new Error(`decorator-adapter: relation key column '${table}.${column}' has no declared type in the model's ` +
|
|
191
|
+
`static columns — cannot stamp the key array's de-box element type (no-assume, no-fallback).`);
|
|
192
|
+
}
|
|
193
|
+
return (0, coltype_1.keyArrayElemScalar)(sqlType);
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/** The fail-closed column-type resolver for a decorated model (its `static columns` SoT), or `undefined` if it has no columns. */
|
|
197
|
+
function modelColumnResolver(modelClass, columnsOptions) {
|
|
198
|
+
const columns = deriveModelColumns(modelClass, columnsOptions);
|
|
199
|
+
const tables = Object.keys(columns);
|
|
200
|
+
if (tables.length === 0)
|
|
201
|
+
return undefined;
|
|
202
|
+
const map = new Map();
|
|
203
|
+
for (const [t, cols] of Object.entries(columns))
|
|
204
|
+
map.set(t, new Map(Object.entries(cols)));
|
|
205
|
+
return (0, coltype_1.columnTypeResolverFromColumnMap)(map);
|
|
206
|
+
}
|
|
207
|
+
// ── 4. Relation authoring generation (@hasMany / @belongsTo / @hasOne → RelationDecl → RelationOp) ──
|
|
208
|
+
/**
|
|
209
|
+
* Translate a decorated model's `@hasMany` / `@belongsTo` / `@hasOne` {@link RelationMeta} registry
|
|
210
|
+
* into SCP {@link RelationDecl}s. Single AND composite keys are supported: the decorator's
|
|
211
|
+
* `keysFactory` resolves lazily (forward refs) to `[srcCol, tgtCol]` (single) or `[[…],[…]]`
|
|
212
|
+
* (composite); the target table + projection come from the target model's `@column` metadata (the
|
|
213
|
+
* relation projects the child's OWN columns). Per-parent `order` / `limit` (hasMany window) and
|
|
214
|
+
* `hardLimit` carry from the decorator `options`.
|
|
215
|
+
*
|
|
216
|
+
* @param modelClass the parent (source) `@model` class.
|
|
217
|
+
* @param resolveTargetModel model NAME → the target `@model` class (the decorator records the target
|
|
218
|
+
* model NAME on the key column; the caller supplies the registry — same lazy-resolution shape v1's
|
|
219
|
+
* `_loadRelation` uses).
|
|
220
|
+
*/
|
|
221
|
+
function deriveRelationDecls(modelClass, resolveTargetModel, dialect = 'sqlite') {
|
|
222
|
+
return (0, decorators_1.getRelationMeta)(modelClass).map((rel) => relationDeclOf(rel, resolveTargetModel, dialect).decl);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Translate ONE {@link RelationMeta} → a {@link RelationDecl} (single or composite key) AND the
|
|
226
|
+
* resolved target model (so a caller can bake the child's de-box materializers from the TARGET
|
|
227
|
+
* model's `static columns` — the relation projects the child's own columns).
|
|
228
|
+
*/
|
|
229
|
+
function relationDeclOf(rel, resolveTargetModel, dialect = 'sqlite') {
|
|
230
|
+
const parsed = parseKeys(rel.keysFactory());
|
|
231
|
+
const targetModel = resolveTargetModel(parsed.targetModelName);
|
|
232
|
+
const targetTable = tableNameOf(targetModel);
|
|
233
|
+
const select = targetProjection(targetModel);
|
|
234
|
+
const order = rel.options?.order ? (0, Column_1.orderToString)(rel.options.order()) : undefined;
|
|
235
|
+
const base = {
|
|
236
|
+
name: rel.propertyKey,
|
|
237
|
+
kind: rel.type,
|
|
238
|
+
targetTable,
|
|
239
|
+
select,
|
|
240
|
+
dialect,
|
|
241
|
+
...(order !== undefined ? { order } : {}),
|
|
242
|
+
...(rel.options?.limit !== undefined ? { limit: rel.options.limit } : {}),
|
|
243
|
+
...(rel.options?.hardLimit !== undefined ? { hardLimit: rel.options.hardLimit } : {}),
|
|
244
|
+
};
|
|
245
|
+
const decl = parsed.composite
|
|
246
|
+
? { ...base, parentKeys: parsed.sourceKeys, targetKeys: parsed.targetKeys }
|
|
247
|
+
: { ...base, parentKey: parsed.sourceKeys[0], targetKey: parsed.targetKeys[0] };
|
|
248
|
+
return { decl, targetModel };
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Compile a decorated model's relation registry into ready {@link RelationOp}s (one per relation). Each
|
|
252
|
+
* op's child-column de-box materializers are baked from the TARGET model's `static columns` (the same
|
|
253
|
+
* resolution the primary read uses) — ZERO per-read introspection.
|
|
254
|
+
*/
|
|
255
|
+
function compileRelationOps(modelClass, resolveTargetModel, dialect = 'sqlite', columnsOptions) {
|
|
256
|
+
const ops = {};
|
|
257
|
+
for (const rel of (0, decorators_1.getRelationMeta)(modelClass)) {
|
|
258
|
+
const { decl, targetModel } = relationDeclOf(rel, resolveTargetModel, dialect);
|
|
259
|
+
ops[decl.name] = (0, relation_1.compileRelationOp)(decl, modelColumnResolver(targetModel, columnsOptions));
|
|
260
|
+
}
|
|
261
|
+
return ops;
|
|
262
|
+
}
|
|
263
|
+
/** Parse the decorator `keysFactory()` result (single pair or composite) into src/target column lists. */
|
|
264
|
+
function parseKeys(keys) {
|
|
265
|
+
const arr = keys;
|
|
266
|
+
const composite = Array.isArray(arr[0]);
|
|
267
|
+
if (composite) {
|
|
268
|
+
const pairs = arr;
|
|
269
|
+
return {
|
|
270
|
+
composite: true,
|
|
271
|
+
sourceKeys: pairs.map((p) => p[0].columnName),
|
|
272
|
+
targetKeys: pairs.map((p) => p[1].columnName),
|
|
273
|
+
targetModelName: pairs[0][1].modelName,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
const [src, tgt] = arr;
|
|
277
|
+
return { composite: false, sourceKeys: [src.columnName], targetKeys: [tgt.columnName], targetModelName: tgt.modelName };
|
|
278
|
+
}
|
|
279
|
+
/** The target model's projected columns (its own `@column` DB column names — the relation child props). */
|
|
280
|
+
function targetProjection(targetModel) {
|
|
281
|
+
const meta = (0, decorators_1.getColumnMeta)(targetModel);
|
|
282
|
+
if (meta === undefined)
|
|
283
|
+
return [];
|
|
284
|
+
return Array.from(meta.values()).map((m) => m.columnName);
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=decorator-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorator-adapter.js","sourceRoot":"","sources":["../../src/scp/decorator-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* litedbmodel v2 SCP — the decorator/metadata → SCP-restricted TS LOWERING EMITTER.
|
|
3
|
+
*
|
|
4
|
+
* This is the middle of the settled model (CLAUDE.md §1):
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* ORM user: decorated models + DECLARED endpoints (no SQL) ← src/decorators.ts + ./endpoint
|
|
8
|
+
* ↓ emitBehaviorModule — THIS module
|
|
9
|
+
* SCP-restricted TS: `@behavior static` methods calling the ONE `@leaf` catalog (`Db`), with the
|
|
10
|
+
* dialect SQL already baked in by `makesql`
|
|
11
|
+
* ↓ bc generate --from
|
|
12
|
+
* go / rust / py / php / ts native modules — bc wires the leaves automatically
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* ## It compiles nothing of its own
|
|
16
|
+
*
|
|
17
|
+
* Every artifact the emitter puts in the emitted source comes from an EXISTING aggregation point —
|
|
18
|
+
* the emitter only RENDERS them as TypeScript:
|
|
19
|
+
*
|
|
20
|
+
* | emitted thing | comes from |
|
|
21
|
+
* |------------------------------|-----------------------------------------------------------------|
|
|
22
|
+
* | read SQL + param order | `makesql/compile-select.compileSelect` |
|
|
23
|
+
* | WHERE text (whole / fragment)| `makesql/compile.compileWhere` → `DBConditions` |
|
|
24
|
+
* | static IN-list membership | `makesql/json-array.inListPredicate` (PG `= ANY(?)`, #46) |
|
|
25
|
+
* | write SQL + param order | `makesql/tx.compileWriteNode` (INSERT/UPDATE/DELETE + batch) |
|
|
26
|
+
* | relation batch SQL + keys | `relation.compileRelationOp` (+ `parentKeyCols`/`targetKeyCols`) |
|
|
27
|
+
* | relation hard-limit guard | `relation.relationGuard` (the compiled op's resolved cap) |
|
|
28
|
+
* | row types | `makesql/outtype.deriveReadRow` over the model's `@column` SoT |
|
|
29
|
+
* | column SQL types | `decorator-adapter.deriveModelColumns` / `modelColumnResolver` |
|
|
30
|
+
* | relation declarations | `decorator-adapter.relationDeclOf` |
|
|
31
|
+
* | find hard-limit cap | `limit-config.resolveFindHardLimit` |
|
|
32
|
+
* | the leaf catalog | `leaf-transport.Db` (imported by the emitted module) |
|
|
33
|
+
*
|
|
34
|
+
* ## How a parameter reaches the SQL
|
|
35
|
+
*
|
|
36
|
+
* The builders bind VALUES: they push whatever value a condition/port carried into `params` in the
|
|
37
|
+
* exact order the `?`s appear. The emitter therefore hands them a {@link ParamRef} SENTINEL instead
|
|
38
|
+
* of a value and reads the sentinels back out of `params` — so the emitted `params` array is the
|
|
39
|
+
* builders' own binding order, with each slot rendered as the method parameter it stands for. A slot
|
|
40
|
+
* that came back as a plain value (a QUERY view's own fragment params) renders as a literal.
|
|
41
|
+
*
|
|
42
|
+
* ## SKIP / dynamic WHERE (CLAUDE.md §2 — settled)
|
|
43
|
+
*
|
|
44
|
+
* A predicate declared `optional` is present-or-absent PER CALL, so the final SQL can only be
|
|
45
|
+
* determined at execution time. The emitter therefore does NOT bake it: it passes the WHERE as
|
|
46
|
+
* FRAGMENTS — SQL text + params, and a skipped fragment is simply omitted — and the `executeSQL`
|
|
47
|
+
* leaf assembles the survivors when it runs ({@link import('../leaves').assembleDynamicWhere}); the
|
|
48
|
+
* `?`→`$N` render happens after that, on the final SQL. There is no intermediate IR vocabulary: a
|
|
49
|
+
* fragment is `{sql, params}` and nothing else, and a skipped one evaluates to `null` (bc's `cond` is
|
|
50
|
+
* lazy, so a dropped fragment's params are never evaluated).
|
|
51
|
+
*
|
|
52
|
+
* A statement with NO optional predicate carries no plan at all — its WHERE is lowered into the
|
|
53
|
+
* static `sql` at emit time (native-clean). One path, chosen per statement.
|
|
54
|
+
*/
|
|
55
|
+
import { type DeriveColumnsOptions, type ModelClassLike } from '../decorator-adapter';
|
|
56
|
+
import type { DialectName } from '../dialect';
|
|
57
|
+
import type { EndpointSet } from './endpoint';
|
|
58
|
+
/** What to emit. */
|
|
59
|
+
export interface EmitSpec {
|
|
60
|
+
/** The emitted class name — the `bc generate --behavior` argument. */
|
|
61
|
+
readonly behavior: string;
|
|
62
|
+
/** The dialect whose SQL is baked into the emitted source. */
|
|
63
|
+
readonly dialect: DialectName;
|
|
64
|
+
/**
|
|
65
|
+
* The module specifier the emitted source imports `Db` from — the ONE `@leaf` catalog
|
|
66
|
+
* (`src/scp/leaf-transport`). It must resolve FROM the emitted file's own location, because
|
|
67
|
+
* `bc generate --from` type-checks the emitted source.
|
|
68
|
+
*/
|
|
69
|
+
readonly leafImport: string;
|
|
70
|
+
/** The declared endpoints — one `@behavior static` method each, in declaration order. */
|
|
71
|
+
readonly endpoints: EndpointSet;
|
|
72
|
+
/** Model NAME → model class, for resolving a relation's target model (as `deriveRelationDecls` takes). */
|
|
73
|
+
readonly models?: (modelName: string) => ModelClassLike;
|
|
74
|
+
/** Per-column SQL-type overrides passed through to {@link deriveModelColumns}. */
|
|
75
|
+
readonly columnOptions?: DeriveColumnsOptions;
|
|
76
|
+
}
|
|
77
|
+
/** One emitted endpoint's call contract — what a caller passes and what it gets back. */
|
|
78
|
+
export interface EmittedEndpoint {
|
|
79
|
+
readonly name: string;
|
|
80
|
+
/** The emitted method's parameters, in signature order (the generated module's input scope keys). */
|
|
81
|
+
readonly params: readonly {
|
|
82
|
+
readonly name: string;
|
|
83
|
+
readonly type: string;
|
|
84
|
+
}[];
|
|
85
|
+
/** The emitted method's declared return type. */
|
|
86
|
+
readonly returnType: string;
|
|
87
|
+
/**
|
|
88
|
+
* The `findHardLimit` cap baked into this read's `LIMIT cap + 1` bounded fetch, when one was
|
|
89
|
+
* configured and the endpoint declared no explicit limit. SCP has no throw, so the read boundary
|
|
90
|
+
* enforces it post-fetch with {@link import('../limit-config').assertFindHardLimit} — the same
|
|
91
|
+
* split the relation twin uses (`runRelationOp` throws off the op's baked `hardLimit`).
|
|
92
|
+
*/
|
|
93
|
+
readonly findHardLimit?: number;
|
|
94
|
+
}
|
|
95
|
+
/** The emitter's output. */
|
|
96
|
+
export interface EmitResult {
|
|
97
|
+
/** The SCP-restricted TypeScript source (what `bc generate --from` reads). */
|
|
98
|
+
readonly source: string;
|
|
99
|
+
/** Per-endpoint call contracts, in emission order. */
|
|
100
|
+
readonly endpoints: readonly EmittedEndpoint[];
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Lower a set of declared endpoints to SCP-restricted TypeScript. The result's `source` is ordinary
|
|
104
|
+
* `tsc --strict` TypeScript (that is bc's authoring requirement) whose only imports are bc's
|
|
105
|
+
* authoring markers and the library's ONE leaf catalog.
|
|
106
|
+
*/
|
|
107
|
+
export declare function emitBehaviorModule(spec: EmitSpec): EmitResult;
|
|
108
|
+
//# sourceMappingURL=emitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../../src/scp/emit/emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAcH,OAAO,EAKL,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACpB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAQV,WAAW,EAYZ,MAAM,YAAY,CAAC;AAIpB,oBAAoB;AACpB,MAAM,WAAW,QAAQ;IACvB,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yFAAyF;IACzF,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC;IAChC,0GAA0G;IAC1G,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,cAAc,CAAC;IACxD,kFAAkF;IAClF,QAAQ,CAAC,aAAa,CAAC,EAAE,oBAAoB,CAAC;CAC/C;AAED,yFAAyF;AACzF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qGAAqG;IACrG,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7E,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,4BAA4B;AAC5B,MAAM,WAAW,UAAU;IACzB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;CAChD;AAsBD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAM7D"}
|