litedbmodel 2.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +1 -1
- package/dist/scp/catalog.d.ts.map +1 -1
- package/dist/scp/catalog.js +27 -0
- package/dist/scp/catalog.js.map +1 -1
- package/dist/scp/codegen.d.ts +46 -82
- package/dist/scp/codegen.d.ts.map +1 -1
- package/dist/scp/codegen.js +37 -80
- package/dist/scp/codegen.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 +148 -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 +11284 -3826
- package/dist/scp/index.d.ts +38 -23
- package/dist/scp/index.d.ts.map +1 -1
- package/dist/scp/index.js +119 -45
- package/dist/scp/index.js.map +1 -1
- package/dist/scp/index.mjs +4231 -1412
- 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 +45 -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/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 +185 -0
- package/dist/scp/makesql/static-bundle.d.ts.map +1 -0
- package/dist/scp/makesql/static-bundle.js +1024 -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 +743 -0
- package/dist/scp/makesql/tx.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 +146 -210
- package/dist/scp/runtime.d.ts.map +1 -1
- package/dist/scp/runtime.js +228 -512
- 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/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
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* litedbmodel v2 SCP — PostgreSQL compile (build time, TS).
|
|
3
|
+
*
|
|
4
|
+
* Produces the fragment list for a query. Every fragment's `sql` text is emitted by
|
|
5
|
+
* REUSING the ORIGINAL tuned builders (`DBConditions`, `buildSelectSQL`,
|
|
6
|
+
* `LazyRelation`) so that ANY SKIP combination reassembles to exactly what the
|
|
7
|
+
* original builder would produce for that combination. The compiler runs at build
|
|
8
|
+
* time (TS); its OUTPUT (the fragment list) is the pure-JSON portable bundle.
|
|
9
|
+
*
|
|
10
|
+
* NO abstract IR is produced. Only `{ sql, params, skip }` fragments (+ nested
|
|
11
|
+
* fragment params where a subquery/sub-expression is needed).
|
|
12
|
+
*/
|
|
13
|
+
import type { Fragment, Node, ValueSpec } from './model';
|
|
14
|
+
/**
|
|
15
|
+
* An authored optional WHERE equality: `<column> = <value from input.path>`,
|
|
16
|
+
* present only when `input.path` is non-null (else the fragment is skipped).
|
|
17
|
+
*/
|
|
18
|
+
export interface OptionalEq {
|
|
19
|
+
column: string;
|
|
20
|
+
inputPath: string[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Compile one optional equality to a fragment carrying a `skip`.
|
|
24
|
+
*
|
|
25
|
+
* The condition core text (`<col> = ?`) is emitted by the ORIGINAL `DBConditions`,
|
|
26
|
+
* so it is byte-identical to the tuned builder. The leading `" AND "` is the exact
|
|
27
|
+
* connector the original uses when joining a present condition after an earlier
|
|
28
|
+
* (mandatory) part — see the byte-match invariant in {@link compileSelect}.
|
|
29
|
+
*/
|
|
30
|
+
export declare function compileOptionalEq(cond: OptionalEq): Fragment;
|
|
31
|
+
/**
|
|
32
|
+
* belongsTo / single-key batch (PG): `SELECT … FROM t WHERE t.key = ANY(?::type[])`.
|
|
33
|
+
*
|
|
34
|
+
* This is byte-identical to `LazyRelation.batchLoadWithAnyArray`. The array of keys
|
|
35
|
+
* binds as ONE param (`keys` value-spec) — the `?::type[]` cast text is static,
|
|
36
|
+
* chosen at compile time by the ORIGINAL `inferPgArrayType`. Optional relation
|
|
37
|
+
* `where`-filters (config.conditions) are appended as `compileOptionalEq` fragments.
|
|
38
|
+
*/
|
|
39
|
+
export declare function compileBelongsTo(opts: {
|
|
40
|
+
tableName: string;
|
|
41
|
+
selectColumn?: string;
|
|
42
|
+
targetKey: string;
|
|
43
|
+
/** compile-time sample values used ONLY for cast-type inference. */
|
|
44
|
+
sampleKeys: unknown[];
|
|
45
|
+
/** value-spec producing the runtime key array (bound as one param). */
|
|
46
|
+
keys: ValueSpec;
|
|
47
|
+
sqlCast?: string;
|
|
48
|
+
filters?: OptionalEq[];
|
|
49
|
+
}): Fragment[];
|
|
50
|
+
/**
|
|
51
|
+
* hasMany + per-parent limit (PG): `CROSS JOIN LATERAL`.
|
|
52
|
+
*
|
|
53
|
+
* Byte-identical to `LazyRelation.batchLoadWithLateral`. The inner SELECT is emitted
|
|
54
|
+
* by the ORIGINAL `buildSelectSQL` (reproduced here as the same template it yields:
|
|
55
|
+
* `SELECT <sel> FROM t WHERE t.key = _keys.key[ AND <filters>] ORDER BY <order> LIMIT n`).
|
|
56
|
+
* The outer wraps it in `unnest(?::type[]) AS _keys(key) CROSS JOIN LATERAL (…)`.
|
|
57
|
+
*
|
|
58
|
+
* `limit` is inlined into the text (the original inlines it: `LIMIT ${limit}`), so it
|
|
59
|
+
* is NOT a param. The key array binds as ONE param.
|
|
60
|
+
*/
|
|
61
|
+
export declare function compileHasManyLimited(opts: {
|
|
62
|
+
tableName: string;
|
|
63
|
+
selectColumn?: string;
|
|
64
|
+
targetKey: string;
|
|
65
|
+
sampleKeys: unknown[];
|
|
66
|
+
keys: ValueSpec;
|
|
67
|
+
order: string;
|
|
68
|
+
limit: number;
|
|
69
|
+
sqlCast?: string;
|
|
70
|
+
filters?: OptionalEq[];
|
|
71
|
+
}): Fragment[];
|
|
72
|
+
/**
|
|
73
|
+
* hasMany / single-key batch WITHOUT limit (PG): `= ANY(?::type[])` + ORDER BY.
|
|
74
|
+
* Byte-identical to `batchLoadWithAnyArray` (used for hasMany when no limit) —
|
|
75
|
+
* same shape as {@link compileBelongsTo} plus a trailing ORDER BY carried on the
|
|
76
|
+
* base fragment (so optional filters slot BEFORE the ORDER BY, matching the
|
|
77
|
+
* original which appends conditions before the order clause).
|
|
78
|
+
*/
|
|
79
|
+
export declare function compileHasManyAny(opts: {
|
|
80
|
+
tableName: string;
|
|
81
|
+
selectColumn?: string;
|
|
82
|
+
targetKey: string;
|
|
83
|
+
sampleKeys: unknown[];
|
|
84
|
+
keys: ValueSpec;
|
|
85
|
+
order?: string;
|
|
86
|
+
sqlCast?: string;
|
|
87
|
+
filters?: OptionalEq[];
|
|
88
|
+
}): Fragment[];
|
|
89
|
+
/**
|
|
90
|
+
* A plain `SELECT <sel> FROM t` with a WHERE built only from optional conditions.
|
|
91
|
+
*
|
|
92
|
+
* The WHERE is a {@link WhereGroup}: each member fragment carries the BARE condition
|
|
93
|
+
* core (`"status = ?"`, NO leading connector), emitted by the ORIGINAL
|
|
94
|
+
* `DBConditions`, and its own `skip`. At assemble time the group joins the SURVIVORS
|
|
95
|
+
* with `" AND "` and prefixes `" WHERE "` — byte-identical to the original
|
|
96
|
+
* `DBConditions.compile` → `parts.join(' AND ')` for EVERY present/absent subset,
|
|
97
|
+
* including when the first condition is skipped (the group's join, not a static
|
|
98
|
+
* connector, decides the leading text). No new SQL-structure vocabulary: the join is
|
|
99
|
+
* pure text glue, exactly the original's algorithm.
|
|
100
|
+
*/
|
|
101
|
+
export declare function compileBaseSelect(opts: {
|
|
102
|
+
tableName: string;
|
|
103
|
+
selectColumn?: string;
|
|
104
|
+
/** ordered optional equality conditions */
|
|
105
|
+
conditions: OptionalEq[];
|
|
106
|
+
}): Node[];
|
|
107
|
+
//# sourceMappingURL=compile-pg.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-pg.d.ts","sourceRoot":"","sources":["../../../src/scp/fragment/compile-pg.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAqB,SAAS,EAAE,MAAM,SAAS,CAAC;AAM5E;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,CAU5D;AAMD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,UAAU,EAAE,OAAO,EAAE,CAAC;IACtB,uEAAuE;IACvE,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;CACxB,GAAG,QAAQ,EAAE,CAQb;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,EAAE,CAAC;IACtB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;CACxB,GAAG,QAAQ,EAAE,CAsBb;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,EAAE,CAAC;IACtB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;CACxB,GAAG,QAAQ,EAAE,CAUb;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,UAAU,EAAE,UAAU,EAAE,CAAC;CAC1B,GAAG,IAAI,EAAE,CAeT"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* litedbmodel v2 SCP — PostgreSQL compile (build time, TS).
|
|
4
|
+
*
|
|
5
|
+
* Produces the fragment list for a query. Every fragment's `sql` text is emitted by
|
|
6
|
+
* REUSING the ORIGINAL tuned builders (`DBConditions`, `buildSelectSQL`,
|
|
7
|
+
* `LazyRelation`) so that ANY SKIP combination reassembles to exactly what the
|
|
8
|
+
* original builder would produce for that combination. The compiler runs at build
|
|
9
|
+
* time (TS); its OUTPUT (the fragment list) is the pure-JSON portable bundle.
|
|
10
|
+
*
|
|
11
|
+
* NO abstract IR is produced. Only `{ sql, params, skip }` fragments (+ nested
|
|
12
|
+
* fragment params where a subquery/sub-expression is needed).
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.compileOptionalEq = compileOptionalEq;
|
|
16
|
+
exports.compileBelongsTo = compileBelongsTo;
|
|
17
|
+
exports.compileHasManyLimited = compileHasManyLimited;
|
|
18
|
+
exports.compileHasManyAny = compileHasManyAny;
|
|
19
|
+
exports.compileBaseSelect = compileBaseSelect;
|
|
20
|
+
const DBConditions_1 = require("../../DBConditions");
|
|
21
|
+
const pg_array_type_1 = require("./pg-array-type");
|
|
22
|
+
/**
|
|
23
|
+
* Compile one optional equality to a fragment carrying a `skip`.
|
|
24
|
+
*
|
|
25
|
+
* The condition core text (`<col> = ?`) is emitted by the ORIGINAL `DBConditions`,
|
|
26
|
+
* so it is byte-identical to the tuned builder. The leading `" AND "` is the exact
|
|
27
|
+
* connector the original uses when joining a present condition after an earlier
|
|
28
|
+
* (mandatory) part — see the byte-match invariant in {@link compileSelect}.
|
|
29
|
+
*/
|
|
30
|
+
function compileOptionalEq(cond) {
|
|
31
|
+
const probe = [];
|
|
32
|
+
const core = new DBConditions_1.DBConditions({ [cond.column]: '__probe__' }).compile(probe);
|
|
33
|
+
// core === `<col> = ?` (source of the text is the original builder)
|
|
34
|
+
const param = { input: cond.inputPath };
|
|
35
|
+
return {
|
|
36
|
+
sql: ` AND ${core}`,
|
|
37
|
+
params: [param],
|
|
38
|
+
skip: { absent: cond.inputPath },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// Relation batch-load fragments — text emitted by the ORIGINAL LazyRelation paths.
|
|
43
|
+
// ============================================================================
|
|
44
|
+
/**
|
|
45
|
+
* belongsTo / single-key batch (PG): `SELECT … FROM t WHERE t.key = ANY(?::type[])`.
|
|
46
|
+
*
|
|
47
|
+
* This is byte-identical to `LazyRelation.batchLoadWithAnyArray`. The array of keys
|
|
48
|
+
* binds as ONE param (`keys` value-spec) — the `?::type[]` cast text is static,
|
|
49
|
+
* chosen at compile time by the ORIGINAL `inferPgArrayType`. Optional relation
|
|
50
|
+
* `where`-filters (config.conditions) are appended as `compileOptionalEq` fragments.
|
|
51
|
+
*/
|
|
52
|
+
function compileBelongsTo(opts) {
|
|
53
|
+
const select = opts.selectColumn ?? '*';
|
|
54
|
+
const pgType = (0, pg_array_type_1.inferPgArrayTypeForCompile)(opts.sampleKeys, opts.sqlCast);
|
|
55
|
+
const base = {
|
|
56
|
+
sql: `SELECT ${select} FROM ${opts.tableName} WHERE ${opts.tableName}.${opts.targetKey} = ANY(?::${pgType})`,
|
|
57
|
+
params: [opts.keys],
|
|
58
|
+
};
|
|
59
|
+
return [base, ...(opts.filters ?? []).map(compileOptionalEq)];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* hasMany + per-parent limit (PG): `CROSS JOIN LATERAL`.
|
|
63
|
+
*
|
|
64
|
+
* Byte-identical to `LazyRelation.batchLoadWithLateral`. The inner SELECT is emitted
|
|
65
|
+
* by the ORIGINAL `buildSelectSQL` (reproduced here as the same template it yields:
|
|
66
|
+
* `SELECT <sel> FROM t WHERE t.key = _keys.key[ AND <filters>] ORDER BY <order> LIMIT n`).
|
|
67
|
+
* The outer wraps it in `unnest(?::type[]) AS _keys(key) CROSS JOIN LATERAL (…)`.
|
|
68
|
+
*
|
|
69
|
+
* `limit` is inlined into the text (the original inlines it: `LIMIT ${limit}`), so it
|
|
70
|
+
* is NOT a param. The key array binds as ONE param.
|
|
71
|
+
*/
|
|
72
|
+
function compileHasManyLimited(opts) {
|
|
73
|
+
const select = opts.selectColumn ?? '*';
|
|
74
|
+
const pgType = (0, pg_array_type_1.inferPgArrayTypeForCompile)(opts.sampleKeys, opts.sqlCast);
|
|
75
|
+
const filterFrags = (opts.filters ?? []).map(compileOptionalEq);
|
|
76
|
+
// The full inner+outer text (byte-identical to batchLoadWithLateral) is split into
|
|
77
|
+
// three fragments so optional filters slot in AFTER the mandatory `t.key=_keys.key`
|
|
78
|
+
// predicate and BEFORE the ORDER BY, exactly where the original appends them:
|
|
79
|
+
// head: ... CROSS JOIN LATERAL (SELECT * FROM t WHERE t.key = _keys.key
|
|
80
|
+
// [filters] each `" AND <core>"` (skip-guarded)
|
|
81
|
+
// tail: ORDER BY <order> LIMIT <n>) t
|
|
82
|
+
const head = {
|
|
83
|
+
sql: `SELECT ${opts.tableName}.* FROM unnest(?::${pgType}) AS _keys(key) CROSS JOIN LATERAL (` +
|
|
84
|
+
`SELECT ${select} FROM ${opts.tableName} WHERE ${opts.tableName}.${opts.targetKey} = _keys.key`,
|
|
85
|
+
params: [opts.keys],
|
|
86
|
+
};
|
|
87
|
+
const tail = {
|
|
88
|
+
sql: ` ORDER BY ${opts.order} LIMIT ${opts.limit}) ${opts.tableName}`,
|
|
89
|
+
params: [],
|
|
90
|
+
};
|
|
91
|
+
return [head, ...filterFrags, tail];
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* hasMany / single-key batch WITHOUT limit (PG): `= ANY(?::type[])` + ORDER BY.
|
|
95
|
+
* Byte-identical to `batchLoadWithAnyArray` (used for hasMany when no limit) —
|
|
96
|
+
* same shape as {@link compileBelongsTo} plus a trailing ORDER BY carried on the
|
|
97
|
+
* base fragment (so optional filters slot BEFORE the ORDER BY, matching the
|
|
98
|
+
* original which appends conditions before the order clause).
|
|
99
|
+
*/
|
|
100
|
+
function compileHasManyAny(opts) {
|
|
101
|
+
const select = opts.selectColumn ?? '*';
|
|
102
|
+
const pgType = (0, pg_array_type_1.inferPgArrayTypeForCompile)(opts.sampleKeys, opts.sqlCast);
|
|
103
|
+
const head = {
|
|
104
|
+
sql: `SELECT ${select} FROM ${opts.tableName} WHERE ${opts.tableName}.${opts.targetKey} = ANY(?::${pgType})`,
|
|
105
|
+
params: [opts.keys],
|
|
106
|
+
};
|
|
107
|
+
const filterFrags = (opts.filters ?? []).map(compileOptionalEq);
|
|
108
|
+
const tail = opts.order ? [{ sql: ` ORDER BY ${opts.order}`, params: [] }] : [];
|
|
109
|
+
return [head, ...filterFrags, ...tail];
|
|
110
|
+
}
|
|
111
|
+
// ============================================================================
|
|
112
|
+
// Base SELECT + WHERE (all-optional conditions, no mandatory predicate).
|
|
113
|
+
// ============================================================================
|
|
114
|
+
/**
|
|
115
|
+
* A plain `SELECT <sel> FROM t` with a WHERE built only from optional conditions.
|
|
116
|
+
*
|
|
117
|
+
* The WHERE is a {@link WhereGroup}: each member fragment carries the BARE condition
|
|
118
|
+
* core (`"status = ?"`, NO leading connector), emitted by the ORIGINAL
|
|
119
|
+
* `DBConditions`, and its own `skip`. At assemble time the group joins the SURVIVORS
|
|
120
|
+
* with `" AND "` and prefixes `" WHERE "` — byte-identical to the original
|
|
121
|
+
* `DBConditions.compile` → `parts.join(' AND ')` for EVERY present/absent subset,
|
|
122
|
+
* including when the first condition is skipped (the group's join, not a static
|
|
123
|
+
* connector, decides the leading text). No new SQL-structure vocabulary: the join is
|
|
124
|
+
* pure text glue, exactly the original's algorithm.
|
|
125
|
+
*/
|
|
126
|
+
function compileBaseSelect(opts) {
|
|
127
|
+
const select = opts.selectColumn ?? '*';
|
|
128
|
+
const base = { sql: `SELECT ${select} FROM ${opts.tableName}`, params: [] };
|
|
129
|
+
const group = {
|
|
130
|
+
where: opts.conditions.map((c) => {
|
|
131
|
+
const probe = [];
|
|
132
|
+
const core = new DBConditions_1.DBConditions({ [c.column]: '__probe__' }).compile(probe);
|
|
133
|
+
return {
|
|
134
|
+
sql: core, // bare core, e.g. `status = ?` — connector belongs to the join
|
|
135
|
+
params: [{ input: c.inputPath }],
|
|
136
|
+
skip: { absent: c.inputPath },
|
|
137
|
+
};
|
|
138
|
+
}),
|
|
139
|
+
};
|
|
140
|
+
return [base, group];
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=compile-pg.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-pg.js","sourceRoot":"","sources":["../../../src/scp/fragment/compile-pg.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* litedbmodel v2 SCP — the MINIMAL fragment model (epic #43 reset).
|
|
3
|
+
*
|
|
4
|
+
* Public surface: the fragment types, the compile helpers (build-time TS, reuse the
|
|
5
|
+
* original tuned builders), and the runtime assembler (thin, language-portable).
|
|
6
|
+
*
|
|
7
|
+
* There is NO abstract IR here. The portable artifact is ONLY a combination of
|
|
8
|
+
* `{ sql, params, skip }` fragments, with params being either value-specs or nested
|
|
9
|
+
* fragments. SQL structure (`= ANY`, `CROSS JOIN LATERAL`, `UNNEST`, subquery, cast)
|
|
10
|
+
* is TEXT inside `sql`, never a modeled construct.
|
|
11
|
+
*/
|
|
12
|
+
export * from './model';
|
|
13
|
+
export * from './assemble';
|
|
14
|
+
export * from './compile-pg';
|
|
15
|
+
export { inferPgArrayTypeForCompile } from './pg-array-type';
|
|
16
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/scp/fragment/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* litedbmodel v2 SCP — the MINIMAL fragment model (epic #43 reset).
|
|
4
|
+
*
|
|
5
|
+
* Public surface: the fragment types, the compile helpers (build-time TS, reuse the
|
|
6
|
+
* original tuned builders), and the runtime assembler (thin, language-portable).
|
|
7
|
+
*
|
|
8
|
+
* There is NO abstract IR here. The portable artifact is ONLY a combination of
|
|
9
|
+
* `{ sql, params, skip }` fragments, with params being either value-specs or nested
|
|
10
|
+
* fragments. SQL structure (`= ANY`, `CROSS JOIN LATERAL`, `UNNEST`, subquery, cast)
|
|
11
|
+
* is TEXT inside `sql`, never a modeled construct.
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
25
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
26
|
+
};
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.inferPgArrayTypeForCompile = void 0;
|
|
29
|
+
__exportStar(require("./model"), exports);
|
|
30
|
+
__exportStar(require("./assemble"), exports);
|
|
31
|
+
__exportStar(require("./compile-pg"), exports);
|
|
32
|
+
var pg_array_type_1 = require("./pg-array-type");
|
|
33
|
+
Object.defineProperty(exports, "inferPgArrayTypeForCompile", { enumerable: true, get: function () { return pg_array_type_1.inferPgArrayTypeForCompile; } });
|
|
34
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/scp/fragment/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;;;;;;;;;;;;;AAEH,0CAAwB;AACxB,6CAA2B;AAC3B,+CAA6B;AAC7B,iDAA6D;AAApD,2HAAA,0BAA0B,OAAA"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* litedbmodel v2 SCP — the MINIMAL fragment model (epic #43 reset).
|
|
3
|
+
*
|
|
4
|
+
* ## The locked vocabulary (owner-specified — nothing else exists here)
|
|
5
|
+
*
|
|
6
|
+
* The portable artifact is ONLY a combination of **fragments**. There is NO
|
|
7
|
+
* abstract "Expression IR", NO relation-op / operator kinds for SQL structure,
|
|
8
|
+
* NO `FragmentTree` connector algebra, NO `{where}` splice marker. SQL structure
|
|
9
|
+
* (`= ANY`, `CROSS JOIN LATERAL`, `UNNEST`, subquery, cast, batch shapes) lives
|
|
10
|
+
* ENTIRELY as TEXT inside `sql`.
|
|
11
|
+
*
|
|
12
|
+
* fragment = { sql: string, params: Param[], skip?: PresenceCondition }
|
|
13
|
+
*
|
|
14
|
+
* - A query is an ordered list of fragments.
|
|
15
|
+
* - Assembling = concatenate the `sql` of every PRESENT fragment, and concatenate
|
|
16
|
+
* their `params` in order. If a fragment's `skip` is satisfied, BOTH its `sql`
|
|
17
|
+
* and its `params` are omitted.
|
|
18
|
+
*
|
|
19
|
+
* Param = EITHER
|
|
20
|
+
* (a) a **value-spec** — how to compute the bound value from input at runtime:
|
|
21
|
+
* an input-ref / wire-ref / a small closed value-expression
|
|
22
|
+
* (e.g. `coalesce(input.limit, 20)`). This computes the VALUE only.
|
|
23
|
+
* OR
|
|
24
|
+
* (b) a **nested fragment** — a raw SQL sub-piece with its own params. This is
|
|
25
|
+
* how subqueries / sub-expressions are represented: NOT as IR constructs,
|
|
26
|
+
* just a nested { sql, params }.
|
|
27
|
+
*
|
|
28
|
+
* Array-parameter placeholder expansion is DRIVER-side, not modeled here. On
|
|
29
|
+
* PostgreSQL an array param binds directly (`= ANY(?::int[])`), so the text is
|
|
30
|
+
* static and the array is ONE param. On MySQL/SQLite (no array binding) the DRIVER
|
|
31
|
+
* expands an array param into `(?, ?, …)` at bind time. The bundle stays
|
|
32
|
+
* "string + params" regardless.
|
|
33
|
+
*
|
|
34
|
+
* The whole model is plain JSON / serializable so a thin runtime in any language
|
|
35
|
+
* reads it identically.
|
|
36
|
+
*/
|
|
37
|
+
/** Read a value from runtime input by path, e.g. `{ input: ["authorId"] }`. */
|
|
38
|
+
export interface InputRef {
|
|
39
|
+
input: string[];
|
|
40
|
+
}
|
|
41
|
+
/** Read a value produced by an upstream node/wire, e.g. `{ wire: ["node1", "id"] }`. */
|
|
42
|
+
export interface WireRef {
|
|
43
|
+
wire: string[];
|
|
44
|
+
}
|
|
45
|
+
/** A literal constant value baked into the bundle (e.g. an array of keys, or 20). */
|
|
46
|
+
export interface LiteralValue {
|
|
47
|
+
literal: unknown;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A small CLOSED value-expression. It computes a value from other value-specs.
|
|
51
|
+
* This is deliberately tiny: `coalesce` and `add` are enough for the LIMIT / key
|
|
52
|
+
* shapes in this slice. It is NOT an SQL-structure IR — it never emits SQL.
|
|
53
|
+
*/
|
|
54
|
+
export interface ValueExpr {
|
|
55
|
+
op: 'coalesce' | 'add';
|
|
56
|
+
args: ValueSpec[];
|
|
57
|
+
}
|
|
58
|
+
/** Param variant (a): a value-spec. */
|
|
59
|
+
export type ValueSpec = InputRef | WireRef | LiteralValue | ValueExpr;
|
|
60
|
+
/**
|
|
61
|
+
* Param variant (b): a nested fragment. A subquery / sub-expression is just a
|
|
62
|
+
* `{ sql, params }` embedded as a param — its `sql` is spliced into the parent's
|
|
63
|
+
* `?` position, its params flow into the parent's param stream in order. This is
|
|
64
|
+
* how subqueries are represented WITHOUT any IR construct.
|
|
65
|
+
*/
|
|
66
|
+
export type Param = ValueSpec | Fragment;
|
|
67
|
+
/**
|
|
68
|
+
* A presence condition for `skip`. When SATISFIED, the fragment (sql + params)
|
|
69
|
+
* is OMITTED. Deliberately closed and tiny: "the input at this path is absent /
|
|
70
|
+
* null / undefined". SKIP is not an operator — it is the ABSENCE of the fragment.
|
|
71
|
+
*/
|
|
72
|
+
export interface PresenceCondition {
|
|
73
|
+
/** Skip the fragment when the input at this path is null/undefined. */
|
|
74
|
+
absent: string[];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The one and only structural type: `{ sql, params, skip? }`.
|
|
78
|
+
*
|
|
79
|
+
* `sql` is literal SQL text (leading connector included where relevant, e.g.
|
|
80
|
+
* `" AND status = ?"`) with `?` placeholders. `params` are 1:1 with the `?`
|
|
81
|
+
* (a value-spec fills one `?`; a nested-fragment param splices its own `sql`,
|
|
82
|
+
* which itself may contain further `?`). `skip`, when present and satisfied,
|
|
83
|
+
* omits the whole fragment.
|
|
84
|
+
*/
|
|
85
|
+
export interface Fragment {
|
|
86
|
+
sql: string;
|
|
87
|
+
params: Param[];
|
|
88
|
+
skip?: PresenceCondition;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* A WHERE clause is an ordered list of condition fragments joined by the SAME text
|
|
92
|
+
* glue the ORIGINAL `DBConditions.compile` uses: present members are joined with
|
|
93
|
+
* `" AND "` and the whole is prefixed with `" WHERE "` — and if NO member is
|
|
94
|
+
* present, the clause degenerates to the empty string (exactly the original's
|
|
95
|
+
* `parts.length === 0 → ''`).
|
|
96
|
+
*
|
|
97
|
+
* This is the "tree" form of the fragment model (a query is an ordered list/tree of
|
|
98
|
+
* fragments). It carries NO SQL-structure vocabulary — the join is pure text glue,
|
|
99
|
+
* identical to the original builder's `parts.join(' AND ')`. Each member fragment's
|
|
100
|
+
* `sql` is the bare condition core (`"status = ?"`, NO leading connector); the
|
|
101
|
+
* connector belongs to the join, so it is SKIP-stable regardless of which members
|
|
102
|
+
* survive.
|
|
103
|
+
*
|
|
104
|
+
* Members carry their own `skip`; a skipped member contributes neither text nor
|
|
105
|
+
* params, and the join simply closes over the survivors (so the first survivor never
|
|
106
|
+
* emits a stray leading `" AND "`).
|
|
107
|
+
*/
|
|
108
|
+
export interface WhereGroup {
|
|
109
|
+
where: Fragment[];
|
|
110
|
+
}
|
|
111
|
+
/** A member of an assembled query: a plain fragment or a WHERE group. */
|
|
112
|
+
export type Node = Fragment | WhereGroup;
|
|
113
|
+
export declare function isFragment(p: Param | Node): p is Fragment;
|
|
114
|
+
export declare function isWhereGroup(n: Node): n is WhereGroup;
|
|
115
|
+
export declare function isInputRef(v: ValueSpec): v is InputRef;
|
|
116
|
+
export declare function isWireRef(v: ValueSpec): v is WireRef;
|
|
117
|
+
export declare function isLiteral(v: ValueSpec): v is LiteralValue;
|
|
118
|
+
export declare function isValueExpr(v: ValueSpec): v is ValueExpr;
|
|
119
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/scp/fragment/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAOH,+EAA+E;AAC/E,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,wFAAwF;AACxF,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,qFAAqF;AACrF,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB;AAED,uCAAuC;AACvC,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,YAAY,GAAG,SAAS,CAAC;AAMtE;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAC;AAMzC;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,uEAAuE;IACvE,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,yEAAyE;AACzE,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,UAAU,CAAC;AAMzC,wBAAgB,UAAU,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,QAAQ,CAEzD;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,IAAI,UAAU,CAErD;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,IAAI,QAAQ,CAEtD;AACD,wBAAgB,SAAS,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,IAAI,OAAO,CAEpD;AACD,wBAAgB,SAAS,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,IAAI,YAAY,CAEzD;AACD,wBAAgB,WAAW,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,IAAI,SAAS,CAExD"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* litedbmodel v2 SCP — the MINIMAL fragment model (epic #43 reset).
|
|
4
|
+
*
|
|
5
|
+
* ## The locked vocabulary (owner-specified — nothing else exists here)
|
|
6
|
+
*
|
|
7
|
+
* The portable artifact is ONLY a combination of **fragments**. There is NO
|
|
8
|
+
* abstract "Expression IR", NO relation-op / operator kinds for SQL structure,
|
|
9
|
+
* NO `FragmentTree` connector algebra, NO `{where}` splice marker. SQL structure
|
|
10
|
+
* (`= ANY`, `CROSS JOIN LATERAL`, `UNNEST`, subquery, cast, batch shapes) lives
|
|
11
|
+
* ENTIRELY as TEXT inside `sql`.
|
|
12
|
+
*
|
|
13
|
+
* fragment = { sql: string, params: Param[], skip?: PresenceCondition }
|
|
14
|
+
*
|
|
15
|
+
* - A query is an ordered list of fragments.
|
|
16
|
+
* - Assembling = concatenate the `sql` of every PRESENT fragment, and concatenate
|
|
17
|
+
* their `params` in order. If a fragment's `skip` is satisfied, BOTH its `sql`
|
|
18
|
+
* and its `params` are omitted.
|
|
19
|
+
*
|
|
20
|
+
* Param = EITHER
|
|
21
|
+
* (a) a **value-spec** — how to compute the bound value from input at runtime:
|
|
22
|
+
* an input-ref / wire-ref / a small closed value-expression
|
|
23
|
+
* (e.g. `coalesce(input.limit, 20)`). This computes the VALUE only.
|
|
24
|
+
* OR
|
|
25
|
+
* (b) a **nested fragment** — a raw SQL sub-piece with its own params. This is
|
|
26
|
+
* how subqueries / sub-expressions are represented: NOT as IR constructs,
|
|
27
|
+
* just a nested { sql, params }.
|
|
28
|
+
*
|
|
29
|
+
* Array-parameter placeholder expansion is DRIVER-side, not modeled here. On
|
|
30
|
+
* PostgreSQL an array param binds directly (`= ANY(?::int[])`), so the text is
|
|
31
|
+
* static and the array is ONE param. On MySQL/SQLite (no array binding) the DRIVER
|
|
32
|
+
* expands an array param into `(?, ?, …)` at bind time. The bundle stays
|
|
33
|
+
* "string + params" regardless.
|
|
34
|
+
*
|
|
35
|
+
* The whole model is plain JSON / serializable so a thin runtime in any language
|
|
36
|
+
* reads it identically.
|
|
37
|
+
*/
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.isFragment = isFragment;
|
|
40
|
+
exports.isWhereGroup = isWhereGroup;
|
|
41
|
+
exports.isInputRef = isInputRef;
|
|
42
|
+
exports.isWireRef = isWireRef;
|
|
43
|
+
exports.isLiteral = isLiteral;
|
|
44
|
+
exports.isValueExpr = isValueExpr;
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Type guards (structural — a nested-fragment param has `sql`; value-specs don't)
|
|
47
|
+
// ============================================================================
|
|
48
|
+
function isFragment(p) {
|
|
49
|
+
return typeof p.sql === 'string';
|
|
50
|
+
}
|
|
51
|
+
function isWhereGroup(n) {
|
|
52
|
+
return Array.isArray(n.where);
|
|
53
|
+
}
|
|
54
|
+
function isInputRef(v) {
|
|
55
|
+
return Array.isArray(v.input);
|
|
56
|
+
}
|
|
57
|
+
function isWireRef(v) {
|
|
58
|
+
return Array.isArray(v.wire);
|
|
59
|
+
}
|
|
60
|
+
function isLiteral(v) {
|
|
61
|
+
return 'literal' in v;
|
|
62
|
+
}
|
|
63
|
+
function isValueExpr(v) {
|
|
64
|
+
return typeof v.op === 'string' && Array.isArray(v.args);
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/scp/fragment/model.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;;AAyGH,gCAEC;AAED,oCAEC;AAED,gCAEC;AACD,8BAEC;AACD,8BAEC;AACD,kCAEC;AAvBD,+EAA+E;AAC/E,kFAAkF;AAClF,+EAA+E;AAE/E,SAAgB,UAAU,CAAC,CAAe;IACxC,OAAO,OAAQ,CAAc,CAAC,GAAG,KAAK,QAAQ,CAAC;AACjD,CAAC;AAED,SAAgB,YAAY,CAAC,CAAO;IAClC,OAAO,KAAK,CAAC,OAAO,CAAE,CAAgB,CAAC,KAAK,CAAC,CAAC;AAChD,CAAC;AAED,SAAgB,UAAU,CAAC,CAAY;IACrC,OAAO,KAAK,CAAC,OAAO,CAAE,CAAc,CAAC,KAAK,CAAC,CAAC;AAC9C,CAAC;AACD,SAAgB,SAAS,CAAC,CAAY;IACpC,OAAO,KAAK,CAAC,OAAO,CAAE,CAAa,CAAC,IAAI,CAAC,CAAC;AAC5C,CAAC;AACD,SAAgB,SAAS,CAAC,CAAY;IACpC,OAAO,SAAS,IAAK,CAAkB,CAAC;AAC1C,CAAC;AACD,SAAgB,WAAW,CAAC,CAAY;IACtC,OAAO,OAAQ,CAAe,CAAC,EAAE,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAE,CAAe,CAAC,IAAI,CAAC,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgreSQL array-type inference for compile-time cast text.
|
|
3
|
+
*
|
|
4
|
+
* VERBATIM reuse of the ORIGINAL `LazyRelation.inferPgArrayType` logic so the
|
|
5
|
+
* `?::<type>[]` cast text a fragment carries is byte-identical to the tuned
|
|
6
|
+
* builder. sqlCast (from the target column's metadata) wins; otherwise the element
|
|
7
|
+
* type is inferred from the compile-time sample values.
|
|
8
|
+
*
|
|
9
|
+
* (This is TEXT chosen at build time; it is NOT an IR "kind". The array itself is
|
|
10
|
+
* bound as ONE param on PostgreSQL.)
|
|
11
|
+
*/
|
|
12
|
+
export declare function inferPgArrayTypeForCompile(values: unknown[], sqlCast?: string): string;
|
|
13
|
+
//# sourceMappingURL=pg-array-type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pg-array-type.d.ts","sourceRoot":"","sources":["../../../src/scp/fragment/pg-array-type.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAYtF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.inferPgArrayTypeForCompile = inferPgArrayTypeForCompile;
|
|
4
|
+
/**
|
|
5
|
+
* PostgreSQL array-type inference for compile-time cast text.
|
|
6
|
+
*
|
|
7
|
+
* VERBATIM reuse of the ORIGINAL `LazyRelation.inferPgArrayType` logic so the
|
|
8
|
+
* `?::<type>[]` cast text a fragment carries is byte-identical to the tuned
|
|
9
|
+
* builder. sqlCast (from the target column's metadata) wins; otherwise the element
|
|
10
|
+
* type is inferred from the compile-time sample values.
|
|
11
|
+
*
|
|
12
|
+
* (This is TEXT chosen at build time; it is NOT an IR "kind". The array itself is
|
|
13
|
+
* bound as ONE param on PostgreSQL.)
|
|
14
|
+
*/
|
|
15
|
+
function inferPgArrayTypeForCompile(values, sqlCast) {
|
|
16
|
+
if (sqlCast)
|
|
17
|
+
return `${sqlCast}[]`;
|
|
18
|
+
if (values.length === 0)
|
|
19
|
+
return 'text[]';
|
|
20
|
+
const sample = values[0];
|
|
21
|
+
if (typeof sample === 'number') {
|
|
22
|
+
if (values.every((v) => Number.isInteger(v)))
|
|
23
|
+
return 'int[]';
|
|
24
|
+
return 'numeric[]';
|
|
25
|
+
}
|
|
26
|
+
if (typeof sample === 'bigint')
|
|
27
|
+
return 'bigint[]';
|
|
28
|
+
if (typeof sample === 'boolean')
|
|
29
|
+
return 'boolean[]';
|
|
30
|
+
if (sample instanceof Date)
|
|
31
|
+
return 'timestamp[]';
|
|
32
|
+
return 'text[]';
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=pg-array-type.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pg-array-type.js","sourceRoot":"","sources":["../../../src/scp/fragment/pg-array-type.ts"],"names":[],"mappings":";;AAWA,gEAYC;AAvBD;;;;;;;;;;GAUG;AACH,SAAgB,0BAA0B,CAAC,MAAiB,EAAE,OAAgB;IAC5E,IAAI,OAAO;QAAE,OAAO,GAAG,OAAO,IAAI,CAAC;IACnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,QAAQ,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACzB,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO,OAAO,CAAC;QAC7D,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAC;IAClD,IAAI,OAAO,MAAM,KAAK,SAAS;QAAE,OAAO,WAAW,CAAC;IACpD,IAAI,MAAM,YAAY,IAAI;QAAE,OAAO,aAAa,CAAC;IACjD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/dist/scp/guard.d.ts
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
* plus the operator-set check.
|
|
10
10
|
*/
|
|
11
11
|
import { type Component, type ComponentGraphIR } from 'behavior-contracts';
|
|
12
|
-
|
|
12
|
+
/** A bc Expression IR node (closed operator set). Structural alias — a JSON expression tree. */
|
|
13
|
+
export type ExprNode = unknown;
|
|
13
14
|
/**
|
|
14
15
|
* Assert one Expression IR node is portable: no residue (functions/Date/…) and every
|
|
15
16
|
* operator-node key is in bc's closed set. Literals (number/string/bool/null) and arrays
|
|
@@ -23,11 +24,6 @@ import type { CompiledOperation, ExprNode } from './ir';
|
|
|
23
24
|
* semantics exactly — see {@link walkExpr} for the `obj` field-map special case.
|
|
24
25
|
*/
|
|
25
26
|
export declare function assertExprPortable(node: ExprNode, path?: string): void;
|
|
26
|
-
/**
|
|
27
|
-
* Assert a whole compiled operation emits only portable Expression IR (static params +
|
|
28
|
-
* fragment tree). Throws {@link PortabilityError} on the first non-portable construct.
|
|
29
|
-
*/
|
|
30
|
-
export declare function assertOperationPortable(op: CompiledOperation): void;
|
|
31
27
|
/**
|
|
32
28
|
* Assert every Expression IR node in ONE lowered component's port wiring is portable
|
|
33
29
|
* (WS2 AC). Walks every `componentRef`/`map` node's `ports` (and `map.over` / `map.when`)
|
package/dist/scp/guard.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/scp/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAmF,KAAK,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/scp/guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAmF,KAAK,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE5J,gGAAgG;AAChG,MAAM,MAAM,QAAQ,GAAG,OAAO,CAAC;AAE/B;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,SAAM,GAAG,IAAI,CAGnE;AAgDD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAgBlE;AAoBD;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,gBAAgB,GAAG,IAAI,CAEvE"}
|