@vibeorm/runtime 1.3.0 → 2.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -107
- package/dist/adapter.d.ts +124 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/client.d.ts +152 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/codecs.d.ts +170 -0
- package/dist/codecs.d.ts.map +1 -0
- package/dist/computed.d.ts +43 -0
- package/dist/computed.d.ts.map +1 -0
- package/dist/extensions.d.ts +102 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6625 -0
- package/dist/index.js.map +21 -0
- package/dist/model-meta.d.ts +156 -0
- package/dist/model-meta.d.ts.map +1 -0
- package/dist/nested-writes.d.ts +100 -0
- package/dist/nested-writes.d.ts.map +1 -0
- package/dist/query-builder.d.ts +250 -0
- package/dist/query-builder.d.ts.map +1 -0
- package/dist/relation-loader.d.ts +75 -0
- package/dist/relation-loader.d.ts.map +1 -0
- package/dist/relation-plan.d.ts +103 -0
- package/dist/relation-plan.d.ts.map +1 -0
- package/dist/render-cache.d.ts +48 -0
- package/dist/render-cache.d.ts.map +1 -0
- package/dist/views.d.ts +97 -0
- package/dist/views.d.ts.map +1 -0
- package/package.json +31 -26
- package/src/adapter.ts +0 -146
- package/src/client.ts +0 -2172
- package/src/coerce.ts +0 -184
- package/src/count-loader.ts +0 -152
- package/src/errors.ts +0 -492
- package/src/id-generators.ts +0 -151
- package/src/index.ts +0 -55
- package/src/lateral-join-builder.ts +0 -1053
- package/src/query-builder.ts +0 -1832
- package/src/relation-loader.ts +0 -534
- package/src/retry.ts +0 -183
- package/src/types.ts +0 -317
- package/src/view.ts +0 -629
- package/src/where-builder.ts +0 -772
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime model metadata: the pre-resolved view of the Schema IR the query
|
|
3
|
+
* builder and client run on.
|
|
4
|
+
*
|
|
5
|
+
* Built once per client (`buildRuntimeMeta`), then read-only. Everything the
|
|
6
|
+
* hot path needs is precomputed here — column names, field lookup maps, primary
|
|
7
|
+
* keys — so no query ever walks the IR. Relations are carried verbatim: the P2
|
|
8
|
+
* core slice does not load them, P3 does.
|
|
9
|
+
*
|
|
10
|
+
* Also the home of app-side default generation (uuid/cuid/nanoid/ulid/now),
|
|
11
|
+
* which is dialect-free by design: the id exists before the INSERT, which is
|
|
12
|
+
* what lets dialects without RETURNING re-select the row they just wrote.
|
|
13
|
+
*/
|
|
14
|
+
import type { ComputedConfig, DefaultValueIR, FieldType, RelationIR, ScalarType, SchemaIR } from "@vibeorm/schema";
|
|
15
|
+
import type { SqlExpr } from "@vibeorm/sql";
|
|
16
|
+
/** Where a field's default value is produced. */
|
|
17
|
+
export type DefaultOrigin = "app" | "database" | "none";
|
|
18
|
+
/** A scalar or enum column, resolved for the runtime. */
|
|
19
|
+
export type FieldMeta = {
|
|
20
|
+
/** Client-facing field name. */
|
|
21
|
+
readonly name: string;
|
|
22
|
+
/** Database column name (`dbName ?? name`). */
|
|
23
|
+
readonly columnName: string;
|
|
24
|
+
readonly type: FieldType;
|
|
25
|
+
/** The scalar type, or `null` when the field is an enum reference. */
|
|
26
|
+
readonly scalarType: ScalarType | null;
|
|
27
|
+
/** The enum name, or `null` when the field is a scalar. */
|
|
28
|
+
readonly enumName: string | null;
|
|
29
|
+
readonly isList: boolean;
|
|
30
|
+
readonly isOptional: boolean;
|
|
31
|
+
readonly isId: boolean;
|
|
32
|
+
readonly isUnique: boolean;
|
|
33
|
+
readonly isUpdatedAt: boolean;
|
|
34
|
+
readonly default: DefaultValueIR | null;
|
|
35
|
+
/**
|
|
36
|
+
* `"app"` — the runtime generates the value and sends it (uuid/cuid/nanoid/
|
|
37
|
+
* ulid/now). `"database"` — the column's DEFAULT clause applies and the field
|
|
38
|
+
* is omitted from INSERTs (autoincrement, dbgenerated, literal, enum value).
|
|
39
|
+
*/
|
|
40
|
+
readonly defaultOrigin: DefaultOrigin;
|
|
41
|
+
};
|
|
42
|
+
/** One computed field, resolved for the runtime (config-declared, see @vibeorm/schema computed.ts). */
|
|
43
|
+
export type ComputedFieldMeta = {
|
|
44
|
+
readonly name: string;
|
|
45
|
+
/** The declared TypeScript type (informational at runtime; the generator wrote it into the .d.ts). */
|
|
46
|
+
readonly type: string;
|
|
47
|
+
/** Scalar fields the compute reads — fetched with the computed field, stripped unless selected. */
|
|
48
|
+
readonly needs: readonly string[];
|
|
49
|
+
readonly compute: (row: Record<string, unknown>) => unknown;
|
|
50
|
+
};
|
|
51
|
+
/** A model, resolved for the runtime. */
|
|
52
|
+
export type ModelMeta = {
|
|
53
|
+
/** IR model name, e.g. `"User"`. */
|
|
54
|
+
readonly name: string;
|
|
55
|
+
/** Delegate key on the client, e.g. `"user"`. */
|
|
56
|
+
readonly clientName: string;
|
|
57
|
+
/** Table name (`dbName ?? name`). */
|
|
58
|
+
readonly tableName: string;
|
|
59
|
+
readonly fields: readonly FieldMeta[];
|
|
60
|
+
readonly fieldByName: ReadonlyMap<string, FieldMeta>;
|
|
61
|
+
/** Reverse map for row materialization (driver rows are keyed by column). */
|
|
62
|
+
readonly fieldByColumn: ReadonlyMap<string, FieldMeta>;
|
|
63
|
+
/** Primary-key fields: the single `@id` field, or the `@@id` fields in order. */
|
|
64
|
+
readonly primaryKey: readonly FieldMeta[];
|
|
65
|
+
/** Fields that identify a row on their own (`@id` or `@unique`). */
|
|
66
|
+
readonly uniqueFields: readonly FieldMeta[];
|
|
67
|
+
/**
|
|
68
|
+
* COMPOSITE ways to address one row, keyed exactly like the generated
|
|
69
|
+
* `…WhereUniqueInput` property — member field names joined with `_`
|
|
70
|
+
* (`projectId_userId`). The composite `@@id` comes first, then every
|
|
71
|
+
* multi-field `@@unique`, in schema order. Empty for models without one.
|
|
72
|
+
*/
|
|
73
|
+
readonly compoundUniques: ReadonlyMap<string, readonly FieldMeta[]>;
|
|
74
|
+
/** Carried verbatim from the IR — consumed by relation loading in P3. */
|
|
75
|
+
readonly relations: readonly RelationIR[];
|
|
76
|
+
readonly relationByName: ReadonlyMap<string, RelationIR>;
|
|
77
|
+
readonly isView: boolean;
|
|
78
|
+
/** Computed fields configured for this model, or `null` (the common case). */
|
|
79
|
+
readonly computed: ReadonlyMap<string, ComputedFieldMeta> | null;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* A pre-bound extension where-operator: compiles one use (`{ similarTo: … }`)
|
|
83
|
+
* into a SqlExpr fragment. Built by `buildExtensionOperatorMap` in
|
|
84
|
+
* extensions.ts with dialect/provider/options already curried in.
|
|
85
|
+
*/
|
|
86
|
+
export type ExtensionOperatorFn = (params: {
|
|
87
|
+
/** Identifier path of the column (qualified when inside a subquery). */
|
|
88
|
+
readonly column: readonly string[];
|
|
89
|
+
readonly operand: unknown;
|
|
90
|
+
readonly model: string;
|
|
91
|
+
readonly field: string;
|
|
92
|
+
}) => SqlExpr;
|
|
93
|
+
/** All runtime metadata for one schema. */
|
|
94
|
+
export type RuntimeMeta = {
|
|
95
|
+
readonly models: readonly ModelMeta[];
|
|
96
|
+
/** Keyed by BOTH camelCase client name (`user`) and model name (`User`). */
|
|
97
|
+
readonly modelByName: ReadonlyMap<string, ModelMeta>;
|
|
98
|
+
/** Enum name → its values, for validation and codec decisions. */
|
|
99
|
+
readonly enums: ReadonlyMap<string, readonly string[]>;
|
|
100
|
+
/**
|
|
101
|
+
* Extension where-operators (extension sql plane), keyed by operator name.
|
|
102
|
+
* Rides on the meta so the WHERE compiler reaches it at every site without
|
|
103
|
+
* threading a parameter through the hot path.
|
|
104
|
+
*/
|
|
105
|
+
readonly extensionOperators?: ReadonlyMap<string, ExtensionOperatorFn>;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Delegate key for a model name: the first character lowercased, nothing else
|
|
109
|
+
* (the Prisma convention — `User` → `user`, `URLMap` → `uRLMap`). Keeping the
|
|
110
|
+
* rest untouched makes the mapping unambiguous and reversible.
|
|
111
|
+
*/
|
|
112
|
+
export declare function toClientName(params: {
|
|
113
|
+
name: string;
|
|
114
|
+
}): string;
|
|
115
|
+
/** RFC 4122 v4 UUID. */
|
|
116
|
+
export declare function generateUuid(): string;
|
|
117
|
+
/**
|
|
118
|
+
* Collision-resistant, sortable-ish id in the CUID spirit: `"c"` + a base-36
|
|
119
|
+
* millisecond timestamp + 16 random base-36 characters. Starts with a letter so
|
|
120
|
+
* it is safe as an HTML id and in every database identifier context.
|
|
121
|
+
*/
|
|
122
|
+
export declare function generateCuid(): string;
|
|
123
|
+
/** 21-character NanoID over the standard URL-safe alphabet (64 chars, unbiased 6-bit mask). */
|
|
124
|
+
export declare function generateNanoid(): string;
|
|
125
|
+
/**
|
|
126
|
+
* ULID: 10 characters of millisecond timestamp + 16 of randomness, Crockford
|
|
127
|
+
* base32, lexicographically sortable by creation time. 256 % 32 === 0, so the
|
|
128
|
+
* byte-to-symbol fold is unbiased.
|
|
129
|
+
*/
|
|
130
|
+
export declare function generateUlid(): string;
|
|
131
|
+
/**
|
|
132
|
+
* The value the runtime sends for an app-side default, or `undefined` when the
|
|
133
|
+
* database owns it (autoincrement, dbgenerated, literal, enum value — those
|
|
134
|
+
* ride the column's DEFAULT clause so the database stays the single source of
|
|
135
|
+
* truth for them).
|
|
136
|
+
*/
|
|
137
|
+
export declare function generateDefaultValue(params: {
|
|
138
|
+
spec: DefaultValueIR;
|
|
139
|
+
}): unknown;
|
|
140
|
+
/** Resolve a whole schema into the runtime's read-only metadata. */
|
|
141
|
+
export declare function buildRuntimeMeta(params: {
|
|
142
|
+
schema: SchemaIR;
|
|
143
|
+
computed?: ComputedConfig;
|
|
144
|
+
}): RuntimeMeta;
|
|
145
|
+
/** Look a model up by client name or model name; unknown names are a caller error. */
|
|
146
|
+
export declare function getModelMeta(params: {
|
|
147
|
+
meta: RuntimeMeta;
|
|
148
|
+
model: string;
|
|
149
|
+
}): ModelMeta;
|
|
150
|
+
/** Look a field up on a model; unknown names are a caller error naming the field. */
|
|
151
|
+
export declare function getFieldMeta(params: {
|
|
152
|
+
model: ModelMeta;
|
|
153
|
+
field: string;
|
|
154
|
+
context: string;
|
|
155
|
+
}): FieldMeta;
|
|
156
|
+
//# sourceMappingURL=model-meta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-meta.d.ts","sourceRoot":"","sources":["../src/model-meta.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nested writes (Prisma-shaped, v1 parity plus implicit-M2M support):
|
|
3
|
+
*
|
|
4
|
+
* create data: relation: { create | connect | connectOrCreate }
|
|
5
|
+
* update data: … plus { update | updateMany | upsert | set | disconnect |
|
|
6
|
+
* delete | deleteMany }
|
|
7
|
+
*
|
|
8
|
+
* STATEMENT ORDER (deterministic, pinned by unit tests):
|
|
9
|
+
* create: [owner-side to-one resolutions: SELECT/INSERT per verb] →
|
|
10
|
+
* parent INSERT → [list/M2M ops in data-key order, item order;
|
|
11
|
+
* each M2M child op is followed immediately by its join-row INSERT].
|
|
12
|
+
* PLAIN multi-item FK-side `create` lists collapse into ONE
|
|
13
|
+
* multi-row INSERT (v1 parity — one fewer round-trip per item) when
|
|
14
|
+
* no item carries nested ops of its own and all items write the
|
|
15
|
+
* same columns (so no NULL-vs-database-default drift can appear).
|
|
16
|
+
* update: parent pre-SELECT (findUnique) → [owner-side to-one resolutions]
|
|
17
|
+
* → parent UPDATE (scalars + FK changes, skipped when empty) →
|
|
18
|
+
* [deferred owner-side child DELETEs] → [list/M2M ops in data-key
|
|
19
|
+
* order] — the returned row is the UPDATE's result (or the
|
|
20
|
+
* pre-SELECT when nothing on the parent changed).
|
|
21
|
+
*
|
|
22
|
+
* TRANSACTIONS: any nested write that produces more than one statement wraps
|
|
23
|
+
* in `adapter.transaction` automatically; inside an outer `$transaction` that
|
|
24
|
+
* nests as a savepoint (the adapter contract). A mid-write failure therefore
|
|
25
|
+
* rolls the whole write back.
|
|
26
|
+
*
|
|
27
|
+
* Implicit M2M goes through the ONE shared join-table rule (`implicitJoinTable`
|
|
28
|
+
* via relation-plan's link resolution); `connect` writes join rows with
|
|
29
|
+
* conflict-skip, so re-connecting an existing pair is a no-op.
|
|
30
|
+
*
|
|
31
|
+
* Unknown verbs are loud: VIBE_VALIDATION naming the verb (v1 silently
|
|
32
|
+
* ignored them — that was a bug class, not a feature).
|
|
33
|
+
*/
|
|
34
|
+
import type { SqlDialect, SqlStatement } from "@vibeorm/sql";
|
|
35
|
+
import type { ModelMeta, RuntimeMeta } from "./model-meta.ts";
|
|
36
|
+
import type { QueryMethod } from "./query-builder.ts";
|
|
37
|
+
export type ClientRowLike = Record<string, unknown>;
|
|
38
|
+
/**
|
|
39
|
+
* Executes ORM statements against ONE adapter (plain or transactional).
|
|
40
|
+
* `single` runs a single-row mutation with the client's full RETURNING /
|
|
41
|
+
* re-select machinery (mysql-safe); `statement` runs a raw AST statement
|
|
42
|
+
* (join-table work has no model).
|
|
43
|
+
*/
|
|
44
|
+
export type StatementRunner = {
|
|
45
|
+
readonly single: (params: {
|
|
46
|
+
model: string;
|
|
47
|
+
method: QueryMethod;
|
|
48
|
+
args: Record<string, unknown>;
|
|
49
|
+
}) => Promise<ClientRowLike>;
|
|
50
|
+
readonly rows: (params: {
|
|
51
|
+
model: string;
|
|
52
|
+
method: QueryMethod;
|
|
53
|
+
args: Record<string, unknown>;
|
|
54
|
+
}) => Promise<ClientRowLike[]>;
|
|
55
|
+
readonly affected: (params: {
|
|
56
|
+
model: string;
|
|
57
|
+
method: QueryMethod;
|
|
58
|
+
args: Record<string, unknown>;
|
|
59
|
+
}) => Promise<number>;
|
|
60
|
+
readonly statement: (params: {
|
|
61
|
+
statement: SqlStatement;
|
|
62
|
+
model: string;
|
|
63
|
+
method: string;
|
|
64
|
+
}) => Promise<ClientRowLike[]>;
|
|
65
|
+
};
|
|
66
|
+
/** What the client hands the nested-write orchestrator. */
|
|
67
|
+
export type WriteContext = {
|
|
68
|
+
readonly meta: RuntimeMeta;
|
|
69
|
+
readonly dialect: SqlDialect;
|
|
70
|
+
/** Runner on the client's current adapter (may already be transactional). */
|
|
71
|
+
readonly run: StatementRunner;
|
|
72
|
+
/** Runs `fn` with a runner bound to a (nested) transaction. */
|
|
73
|
+
readonly transactional: <T>(fn: (run: StatementRunner) => Promise<T>) => Promise<T>;
|
|
74
|
+
};
|
|
75
|
+
/** Does this `data` object carry nested relation operations? */
|
|
76
|
+
export declare function hasNestedWrites(params: {
|
|
77
|
+
model: ModelMeta;
|
|
78
|
+
data: unknown;
|
|
79
|
+
}): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* `create` with nested relation operations. Returns the parent row as the
|
|
82
|
+
* INSERT produced it (relation loading/projection are the client's follow-up).
|
|
83
|
+
*/
|
|
84
|
+
export declare function createWithNested(params: {
|
|
85
|
+
ctx: WriteContext;
|
|
86
|
+
model: ModelMeta;
|
|
87
|
+
data: Record<string, unknown>;
|
|
88
|
+
}): Promise<ClientRowLike>;
|
|
89
|
+
/**
|
|
90
|
+
* `update` with nested relation operations. Always transactional (the flow is
|
|
91
|
+
* multi-statement by construction). Returns the parent row after its UPDATE
|
|
92
|
+
* (or the pre-read row when only children changed).
|
|
93
|
+
*/
|
|
94
|
+
export declare function updateWithNested(params: {
|
|
95
|
+
ctx: WriteContext;
|
|
96
|
+
model: ModelMeta;
|
|
97
|
+
where: Record<string, unknown>;
|
|
98
|
+
data: Record<string, unknown>;
|
|
99
|
+
}): Promise<ClientRowLike>;
|
|
100
|
+
//# sourceMappingURL=nested-writes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nested-writes.d.ts","sourceRoot":"","sources":["../src/nested-writes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE7D,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAMtD,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,WAAW,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC/B,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE;QACtB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,WAAW,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC/B,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE;QAC1B,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,WAAW,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAC/B,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE;QAC3B,SAAS,EAAE,YAAY,CAAC;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;CAChC,CAAC;AAEF,2DAA2D;AAC3D,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,GAAG,EAAE,eAAe,CAAC;IAC9B,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CACrF,CAAC;AAmCF,gEAAgE;AAChE,wBAAgB,eAAe,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAQpF;AAgcD;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,GAAG,EAAE,YAAY,CAAC;IAClB,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,GAAG,OAAO,CAAC,aAAa,CAAC,CAOzB;AAoeD;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE;IAC7C,GAAG,EAAE,YAAY,CAAC;IAClB,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B,GAAG,OAAO,CAAC,aAAa,CAAC,CAoCzB"}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The query builder: `{ model, method, args }` → a SQL AST statement, purely.
|
|
3
|
+
*
|
|
4
|
+
* Nothing here touches a database, a clock-free input always produces the same
|
|
5
|
+
* statement, and every branch is reachable from the query catalog
|
|
6
|
+
* (`tests/query-catalog/`) — which pins the rendered SQL for all three dialects
|
|
7
|
+
* and exports it to the reviewable root `sql-catalog.md`.
|
|
8
|
+
*
|
|
9
|
+
* Division of labour:
|
|
10
|
+
* - shapes and SQL text live in @vibeorm/sql (this file never writes SQL),
|
|
11
|
+
* - value transport lives in codecs.ts (this file never coerces),
|
|
12
|
+
* - dialect gaps become VIBE_UNSUPPORTED_CAPABILITY here, at build time,
|
|
13
|
+
* which is the earliest knowable moment for a query-level feature,
|
|
14
|
+
* - bad input becomes VIBE_VALIDATION naming the model, field and operator.
|
|
15
|
+
*/
|
|
16
|
+
import type { OrderByColumn, SqlDialect, SqlExpr, SqlStatement } from "@vibeorm/sql";
|
|
17
|
+
import type { ModelMeta, RuntimeMeta } from "./model-meta.ts";
|
|
18
|
+
import type { RelationSelection, RelationTreeNode } from "./relation-plan.ts";
|
|
19
|
+
/** Every ORM method the query builder builds SQL for. */
|
|
20
|
+
export type QueryMethod = "findMany" | "findFirst" | "findUnique" | "create" | "createMany" | "createManyAndReturn" | "update" | "updateMany" | "upsert" | "delete" | "deleteMany" | "count" | "aggregate" | "groupBy";
|
|
21
|
+
/** Client-level options that shape SQL (subset of ClientOptions the builder needs). */
|
|
22
|
+
export type BuilderClientOptions = {
|
|
23
|
+
/** Append the primary key to ORDER BY when no explicit orderBy was given (default false). */
|
|
24
|
+
readonly defaultOrderByPk?: boolean;
|
|
25
|
+
/** Append the primary key as a `distinct` tie-breaker for determinism (default true). */
|
|
26
|
+
readonly distinctOrderByPk?: boolean;
|
|
27
|
+
};
|
|
28
|
+
/** The five aggregate selector keys, in the deterministic order they project. */
|
|
29
|
+
export type AggregateKey = "_count" | "_avg" | "_sum" | "_min" | "_max";
|
|
30
|
+
/**
|
|
31
|
+
* One projected aggregate. `field: null` is `_all` (COUNT(*)). The alias is the
|
|
32
|
+
* deterministic `<key>__<selector>` scheme (`_count___all`, `_avg__score`) —
|
|
33
|
+
* results are read back by construction from these aliases, never parsed.
|
|
34
|
+
*/
|
|
35
|
+
export type AggregateSelection = {
|
|
36
|
+
readonly key: AggregateKey;
|
|
37
|
+
readonly field: string | null;
|
|
38
|
+
readonly alias: string;
|
|
39
|
+
};
|
|
40
|
+
/** Everything the client needs to decode an aggregate/groupBy/count-select row. */
|
|
41
|
+
export type AggregateSpec = {
|
|
42
|
+
readonly selections: readonly AggregateSelection[];
|
|
43
|
+
/** `_count: true` — the result's `_count` is a bare number, not an object. */
|
|
44
|
+
readonly countShorthand: boolean;
|
|
45
|
+
};
|
|
46
|
+
/** How included relations are fetched. */
|
|
47
|
+
export type RelationStrategy = "query" | "join";
|
|
48
|
+
/**
|
|
49
|
+
* A built query plus everything the client needs after execution. The plan is
|
|
50
|
+
* data only: the client decides how to run it.
|
|
51
|
+
*/
|
|
52
|
+
export type QueryPlan = {
|
|
53
|
+
readonly method: QueryMethod;
|
|
54
|
+
readonly model: ModelMeta;
|
|
55
|
+
readonly statement: SqlStatement;
|
|
56
|
+
/**
|
|
57
|
+
* Field names the statement projects, in order, or `null` when it projects
|
|
58
|
+
* every scalar field. Mutations RETURNING extra primary-key columns are
|
|
59
|
+
* narrowed back down to this list by the client.
|
|
60
|
+
*/
|
|
61
|
+
readonly selectedFields: readonly string[] | null;
|
|
62
|
+
/**
|
|
63
|
+
* What the CALLER should see: the user's scalar select plus relation names
|
|
64
|
+
* and `_count`, or `null` for "all scalars (plus attached relations)".
|
|
65
|
+
* Differs from `selectedFields` when stitching keys were added to the SQL
|
|
66
|
+
* projection — the client narrows back down to this after relation loading.
|
|
67
|
+
*/
|
|
68
|
+
readonly resultFields: readonly string[] | null;
|
|
69
|
+
/**
|
|
70
|
+
* Computed fields to attach to result rows (config-declared), resolved from
|
|
71
|
+
* select/omit — always a concrete name list, `null` when none apply. Their
|
|
72
|
+
* `needs` ride `selectedFields` like stitch keys and are narrowed away by
|
|
73
|
+
* `resultFields` unless selected themselves.
|
|
74
|
+
*/
|
|
75
|
+
readonly computed: readonly string[] | null;
|
|
76
|
+
/** Relations to load after the base rows, or `null` when none were asked for. */
|
|
77
|
+
readonly relations: RelationSelection | null;
|
|
78
|
+
/**
|
|
79
|
+
* How relations load. With `"join"` on a find method the base statement IS
|
|
80
|
+
* the lateral statement (level-1 relations ride along as JSON columns);
|
|
81
|
+
* deeper levels always fall back to `"query"` batches (v1 semantics).
|
|
82
|
+
*/
|
|
83
|
+
readonly relationStrategy: RelationStrategy;
|
|
84
|
+
/** True when `take` produced a LIMIT. */
|
|
85
|
+
readonly takeApplied: boolean;
|
|
86
|
+
/** Alias the COUNT(*) value is projected under (`count` only). */
|
|
87
|
+
readonly countAlias: string | null;
|
|
88
|
+
/**
|
|
89
|
+
* Unique WHERE identifying the affected row, for the re-select a dialect
|
|
90
|
+
* without RETURNING needs. `null` when the row cannot be identified before
|
|
91
|
+
* the write (an autoincrement key the database assigns).
|
|
92
|
+
*/
|
|
93
|
+
readonly refetchWhere: Readonly<Record<string, unknown>> | null;
|
|
94
|
+
/**
|
|
95
|
+
* Upsert only: the unique where WITHOUT the update-arm overlay. The INSERT
|
|
96
|
+
* arm's row answers this key (its create row binds the conflict target — the
|
|
97
|
+
* B1 native-eligibility gate), so the mysql read-back tries the overlaid
|
|
98
|
+
* `refetchWhere` first, then this. `null` on every other method.
|
|
99
|
+
*/
|
|
100
|
+
readonly refetchWhereBase: Readonly<Record<string, unknown>> | null;
|
|
101
|
+
/**
|
|
102
|
+
* createMany/createManyAndReturn only: when the call compiles to MORE than
|
|
103
|
+
* one statement (sqlite's heterogeneous column-set groups — M4 — or the M7
|
|
104
|
+
* parameter-budget chunks), every statement in order (`statement` is the
|
|
105
|
+
* first). The client executes them in ONE transaction, summing counts /
|
|
106
|
+
* concatenating returned rows. Absent for single-statement calls.
|
|
107
|
+
*/
|
|
108
|
+
readonly batch?: readonly SqlStatement[];
|
|
109
|
+
/** The caller's `select`, replayed on that re-select. */
|
|
110
|
+
readonly selectArg: Readonly<Record<string, unknown>> | null;
|
|
111
|
+
/** How to decode aggregate/groupBy/count-select rows; `null` elsewhere. */
|
|
112
|
+
readonly aggregateSpec: AggregateSpec | null;
|
|
113
|
+
/** groupBy's `by` field names, for row assembly; `null` elsewhere. */
|
|
114
|
+
readonly groupByFields: readonly string[] | null;
|
|
115
|
+
/** Negative `take`: the SQL ordering was flipped; re-reverse rows after fetch. */
|
|
116
|
+
readonly reverseRows: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* `distinct` + an explicit user orderBy (N1, board #22): the arrangement
|
|
119
|
+
* DISTINCT ON needs demotes the user's order behind the distinct fields, so
|
|
120
|
+
* the client re-sorts the FINAL rows by these keys — Prisma's presentation
|
|
121
|
+
* order, on every dialect. `nulls` is resolved at build time to the
|
|
122
|
+
* dialect's own NULL placement so the re-sort mirrors what the SQL order
|
|
123
|
+
* would have produced. `null` when no re-sort is needed.
|
|
124
|
+
*/
|
|
125
|
+
readonly resortBy: readonly ResortKey[] | null;
|
|
126
|
+
};
|
|
127
|
+
/** One client-side re-sort key (see QueryPlan.resortBy). */
|
|
128
|
+
export type ResortKey = {
|
|
129
|
+
readonly field: string;
|
|
130
|
+
readonly direction: "asc" | "desc";
|
|
131
|
+
readonly nulls: "first" | "last";
|
|
132
|
+
/** B4 mirror: Decimal ranks numerically — the SQL side ordered a number. */
|
|
133
|
+
readonly decimal: boolean;
|
|
134
|
+
};
|
|
135
|
+
/** Column alias for COUNT(*) — postgres and sqlite/mysql name it differently otherwise. */
|
|
136
|
+
export declare const COUNT_ALIAS: string;
|
|
137
|
+
/**
|
|
138
|
+
* Compile a Prisma-shaped where object; `undefined` when it constrains nothing.
|
|
139
|
+
*
|
|
140
|
+
* `qualify` table-qualifies every column reference (used inside lateral and
|
|
141
|
+
* EXISTS subqueries); `aliasSeq` threads the statement-wide subquery-alias
|
|
142
|
+
* counter so nested relation filters never collide.
|
|
143
|
+
*/
|
|
144
|
+
export declare function compileWhere(params: {
|
|
145
|
+
dialect: SqlDialect;
|
|
146
|
+
meta: RuntimeMeta;
|
|
147
|
+
model: ModelMeta;
|
|
148
|
+
where: Record<string, unknown>;
|
|
149
|
+
qualify?: string;
|
|
150
|
+
aliasSeq?: {
|
|
151
|
+
n: number;
|
|
152
|
+
};
|
|
153
|
+
}): SqlExpr | undefined;
|
|
154
|
+
/** Compile `orderBy` (a single object or an array of them) into order terms. */
|
|
155
|
+
export declare function compileOrderBy(params: {
|
|
156
|
+
model: ModelMeta;
|
|
157
|
+
orderBy: unknown;
|
|
158
|
+
qualify?: string;
|
|
159
|
+
/** When given, B4's sqlite Decimal ordering cast applies (`CAST … AS REAL`). */
|
|
160
|
+
dialect?: SqlDialect;
|
|
161
|
+
}): OrderByColumn[];
|
|
162
|
+
/**
|
|
163
|
+
* Compile `select` into scalar field names, projecting EXACTLY what was asked
|
|
164
|
+
* — the primary key is not smuggled in. Relation keys and `_count` are the
|
|
165
|
+
* relation resolver's business (`relation-plan.ts`) and are skipped here;
|
|
166
|
+
* `allowEmpty` permits a select of ONLY relations (the stitching key is added
|
|
167
|
+
* separately and stripped from the result).
|
|
168
|
+
*/
|
|
169
|
+
export declare function compileSelect(params: {
|
|
170
|
+
model: ModelMeta;
|
|
171
|
+
select: unknown;
|
|
172
|
+
allowEmpty?: boolean;
|
|
173
|
+
}): string[];
|
|
174
|
+
/**
|
|
175
|
+
* Whether `upsert({ where, create, update })` can ride ONE native
|
|
176
|
+
* `INSERT … ON CONFLICT`/`ON DUPLICATE KEY` statement — Prisma's own
|
|
177
|
+
* criterion: the create data must bind every conflict-target column with the
|
|
178
|
+
* SAME value the where names. Otherwise the conflict can never fire on the
|
|
179
|
+
* addressed row (postgres/sqlite would silently insert instead of update;
|
|
180
|
+
* mysql's ODKU could update an unrelated row — SQL review B1) and the client
|
|
181
|
+
* must run the transactional find-then-write fallback instead.
|
|
182
|
+
*/
|
|
183
|
+
/**
|
|
184
|
+
* Whether `upsert({ where, create, update })` can ride ONE native
|
|
185
|
+
* `INSERT … ON CONFLICT`/`ON DUPLICATE KEY` statement — Prisma's own
|
|
186
|
+
* criterion, two halves:
|
|
187
|
+
* 1. the create data binds every conflict-target column with the SAME value
|
|
188
|
+
* the where names (otherwise the conflict can never fire on the
|
|
189
|
+
* addressed row — postgres/sqlite would silently insert instead of
|
|
190
|
+
* update; mysql's ODKU could update an unrelated row — SQL review B1);
|
|
191
|
+
* 2. the where carries NO fields beyond the conflict target (PG-2): a guard
|
|
192
|
+
* field (`where: { email, active: true }`) is invisible to ON CONFLICT —
|
|
193
|
+
* the update arm would fire even when the guard fails, where update()
|
|
194
|
+
* honors the same guard.
|
|
195
|
+
* Ineligible shapes run the client's transactional find-then-write fallback,
|
|
196
|
+
* whose probe honors the FULL where.
|
|
197
|
+
*/
|
|
198
|
+
export declare function upsertUsesNativeStatement(params: {
|
|
199
|
+
model: ModelMeta;
|
|
200
|
+
args: Record<string, unknown>;
|
|
201
|
+
}): boolean;
|
|
202
|
+
/** The column a relation's JSON payload is exposed under in the outer row. */
|
|
203
|
+
export declare function lateralColumnAlias(params: {
|
|
204
|
+
relation: string;
|
|
205
|
+
}): string;
|
|
206
|
+
/**
|
|
207
|
+
* PG-3 (SQL review round 2): order fields the lateral subselect must project
|
|
208
|
+
* HIDDEN — the aggregate's explicit `ORDER BY "__sub".…` (N2) can only
|
|
209
|
+
* reference projected columns, and a per-relation orderBy on a field outside
|
|
210
|
+
* the child select otherwise left the aggregate relying on "usually preserves
|
|
211
|
+
* subquery order". The loader deletes these after parsing (the stitch-key
|
|
212
|
+
* pattern). Empty when the projection is full or no orderBy was given.
|
|
213
|
+
*/
|
|
214
|
+
export declare function lateralHiddenOrderFields(params: {
|
|
215
|
+
node: RelationTreeNode;
|
|
216
|
+
}): string[];
|
|
217
|
+
/**
|
|
218
|
+
* The relation selection + caller-visible field list for one call, WITHOUT
|
|
219
|
+
* building a statement — the nested-write path orchestrates its own
|
|
220
|
+
* statements but still needs the validated result shape for relation loading
|
|
221
|
+
* and projection.
|
|
222
|
+
*/
|
|
223
|
+
export declare function resolveResultShape(params: {
|
|
224
|
+
meta: RuntimeMeta;
|
|
225
|
+
model: ModelMeta;
|
|
226
|
+
method: QueryMethod;
|
|
227
|
+
args: Record<string, unknown>;
|
|
228
|
+
}): {
|
|
229
|
+
relations: RelationSelection | null;
|
|
230
|
+
resultFields: readonly string[] | null;
|
|
231
|
+
computed: readonly string[] | null;
|
|
232
|
+
};
|
|
233
|
+
/**
|
|
234
|
+
* Build the SQL plan for one ORM call. Pure: same inputs, same statement —
|
|
235
|
+
* except for app-generated defaults (uuid/cuid/nanoid/ulid/now), which are by
|
|
236
|
+
* definition fresh per call.
|
|
237
|
+
*
|
|
238
|
+
* `defaultRelationStrategy` is the client-level option; a per-query
|
|
239
|
+
* `relationStrategy` argument (find methods only) overrides it.
|
|
240
|
+
*/
|
|
241
|
+
export declare function buildQuery(params: {
|
|
242
|
+
meta: RuntimeMeta;
|
|
243
|
+
dialect: SqlDialect;
|
|
244
|
+
model: string;
|
|
245
|
+
method: QueryMethod;
|
|
246
|
+
args?: Record<string, unknown>;
|
|
247
|
+
defaultRelationStrategy?: RelationStrategy;
|
|
248
|
+
clientOptions?: BuilderClientOptions;
|
|
249
|
+
}): QueryPlan;
|
|
250
|
+
//# sourceMappingURL=query-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-builder.d.ts","sourceRoot":"","sources":["../src/query-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relation LOADING: attach included relations onto already-materialized rows.
|
|
3
|
+
*
|
|
4
|
+
* The portable "query" strategy (default, all dialects): one batched statement
|
|
5
|
+
* per relation per level (`WHERE childKey IN (parent keys)`), grouped and
|
|
6
|
+
* stitched in memory — never one query per parent. Implicit M2M loads in ONE
|
|
7
|
+
* statement per level (board #12, v1's shape): children INNER JOINed to their
|
|
8
|
+
* join-table membership rows, the join table's parent-key column projected
|
|
9
|
+
* under a reserved alias and decoded through the PARENT key's codec (rule 6).
|
|
10
|
+
* Deeper levels recurse over the level's loaded children.
|
|
11
|
+
*
|
|
12
|
+
* PER-PARENT take/skip MECHANISM (differs from v1's SQL, matches its
|
|
13
|
+
* semantics): v1 windowed with `ROW_NUMBER() OVER (PARTITION BY fk)`; v2 loads
|
|
14
|
+
* the batch fully ordered (the user's orderBy, or the child PK when paginating
|
|
15
|
+
* without one) and applies each parent's skip/take at stitch time. Same
|
|
16
|
+
* observable result for any deterministic ordering, no window functions in the
|
|
17
|
+
* AST. The trade-off — overfetching children that fall outside a page — is
|
|
18
|
+
* accepted for 2.0 and confined to this module.
|
|
19
|
+
*
|
|
20
|
+
* The "join" strategy's rows arrive with `__rel_<name>` JSON columns (built by
|
|
21
|
+
* the query builder's lateral statement); `attachLateralRelations` parses,
|
|
22
|
+
* DECODES (codecs on every child row — to_jsonb strings timestamps and drops
|
|
23
|
+
* BigInt-ness, LEARNINGS) and stitches them, then recurses deeper levels
|
|
24
|
+
* through the query strategy exactly like v1.
|
|
25
|
+
*
|
|
26
|
+
* Statements run SEQUENTIALLY in relation-declaration order, so the statement
|
|
27
|
+
* stream is deterministic (scripted-adapter tests pin it; real adapters get
|
|
28
|
+
* predictable logs).
|
|
29
|
+
*/
|
|
30
|
+
import type { SelectStatement, SqlDialect, WireFidelity } from "@vibeorm/sql";
|
|
31
|
+
import type { ModelMeta, RuntimeMeta } from "./model-meta.ts";
|
|
32
|
+
import type { RelationSelection } from "./relation-plan.ts";
|
|
33
|
+
/** A materialized row relations are stitched onto. */
|
|
34
|
+
export type LoadedRow = Record<string, unknown>;
|
|
35
|
+
/**
|
|
36
|
+
* Executes one SELECT and returns RAW driver rows — the loader decodes them
|
|
37
|
+
* itself (the child model's codecs, at every materialization point). The
|
|
38
|
+
* client provides this bound to its adapter + onQuery reporting.
|
|
39
|
+
*/
|
|
40
|
+
export type RelationExecutor = (params: {
|
|
41
|
+
statement: SelectStatement;
|
|
42
|
+
model: string;
|
|
43
|
+
method: string;
|
|
44
|
+
}) => Promise<Record<string, unknown>[]>;
|
|
45
|
+
/**
|
|
46
|
+
* "query"-strategy loading: batched statements per relation, in declaration
|
|
47
|
+
* order, recursively. Mutates `rows` in place (relations become properties).
|
|
48
|
+
*/
|
|
49
|
+
export declare function attachRelations(params: {
|
|
50
|
+
dialect: SqlDialect;
|
|
51
|
+
meta: RuntimeMeta;
|
|
52
|
+
model: ModelMeta;
|
|
53
|
+
rows: LoadedRow[];
|
|
54
|
+
selection: RelationSelection;
|
|
55
|
+
execute: RelationExecutor;
|
|
56
|
+
/** The executing adapter's wire declaration (board #30). */
|
|
57
|
+
wire?: WireFidelity;
|
|
58
|
+
}): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* "join"-strategy post-processing: parse each row's `__rel_<name>` JSON
|
|
61
|
+
* column, decode the children through the child model's codecs, stitch, then
|
|
62
|
+
* recurse deeper levels through the query strategy (v1 semantics) and load
|
|
63
|
+
* `_count` separately.
|
|
64
|
+
*/
|
|
65
|
+
export declare function attachLateralRelations(params: {
|
|
66
|
+
dialect: SqlDialect;
|
|
67
|
+
meta: RuntimeMeta;
|
|
68
|
+
model: ModelMeta;
|
|
69
|
+
rows: LoadedRow[];
|
|
70
|
+
selection: RelationSelection;
|
|
71
|
+
execute: RelationExecutor;
|
|
72
|
+
/** The executing adapter's wire declaration (board #30 — deeper levels recurse through the query strategy). */
|
|
73
|
+
wire?: WireFidelity;
|
|
74
|
+
}): Promise<void>;
|
|
75
|
+
//# sourceMappingURL=relation-loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relation-loader.d.ts","sourceRoot":"","sources":["../src/relation-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAW,YAAY,EAAE,MAAM,cAAc,CAAC;AAGvF,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9D,OAAO,KAAK,EAAgB,iBAAiB,EAAoB,MAAM,oBAAoB,CAAC;AAI5F,sDAAsD;AACtD,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEhD;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE;IACtC,SAAS,EAAE,eAAe,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AA2czC;;;GAGG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,4DAA4D;IAC5D,IAAI,CAAC,EAAE,YAAY,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAShB;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,MAAM,EAAE;IACnD,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,SAAS,CAAC;IACjB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,+GAA+G;IAC/G,IAAI,CAAC,EAAE,YAAY,CAAC;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CA4DhB"}
|