@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
package/README.md
CHANGED
|
@@ -1,141 +1,84 @@
|
|
|
1
1
|
# @vibeorm/runtime
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Part of **[VibeORM](https://github.com/vibeorm/vibeorm)** — a type-safe TypeScript ORM for Bun and Node. Prisma-schema or TypeScript-DSL input, a canonical schema IR, a generated client, and a dialect-aware SQL layer over PostgreSQL, PGlite, SQLite and MySQL.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The query engine behind the generated client: it turns delegate calls into SQL statements, loads relations, runs nested writes and moves values across the wire through the codec table. It is its own package because it is driver-agnostic — it talks to databases only through the `DatabaseAdapter` contract, so adapters and the generator can depend on it without depending on each other.
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
bun add @vibeorm/runtime
|
|
8
|
+
bun add @vibeorm/runtime@alpha
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
> **Pre-release.** `2.0.0-alpha.x`, published under the `alpha` dist-tag. `npm i vibeorm` still resolves to the stable v1 line.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
bun add @vibeorm/adapter-bun # for bun:sql
|
|
15
|
-
# or
|
|
16
|
-
bun add @vibeorm/adapter-pg pg # for node-postgres
|
|
17
|
-
```
|
|
13
|
+
Most users never install this directly — it ships as a dependency of [`vibeorm`](https://www.npmjs.com/package/vibeorm) and of the generated client. Reach for it when you are writing an adapter or tooling against the runtime.
|
|
18
14
|
|
|
19
|
-
##
|
|
15
|
+
## What it does
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
`createClient({ schema, adapter, options })` builds a `DynamicClient` from the Schema IR at runtime: model delegates keyed by both camelCase client name and model name (`$models`), plus `$transaction`, `$queryRaw`, `$executeRaw`, `$queryRawUnsafe`, `$executeRawUnsafe`, `$on`, `$connect` and `$disconnect`. The delegates are deliberately broad here (`ClientRow` in, `ClientRow` out); the generated `.d.ts` declares the precise per-model types on top.
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
// The generated client wires everything together:
|
|
25
|
-
import { VibeClient } from "./generated/vibeorm/index.ts";
|
|
26
|
-
|
|
27
|
-
const db = VibeClient({
|
|
28
|
-
relationStrategy: "query",
|
|
29
|
-
log: false,
|
|
30
|
-
});
|
|
31
|
-
```
|
|
19
|
+
Each `ModelDelegate` implements 16 methods:
|
|
32
20
|
|
|
33
|
-
|
|
21
|
+
| Group | Methods |
|
|
22
|
+
|---|---|
|
|
23
|
+
| Read | `findMany` · `findFirst` · `findFirstOrThrow` · `findUnique` · `findUniqueOrThrow` |
|
|
24
|
+
| Create | `create` · `createMany` · `createManyAndReturn` |
|
|
25
|
+
| Update | `update` · `updateMany` · `upsert` |
|
|
26
|
+
| Delete | `delete` · `deleteMany` |
|
|
27
|
+
| Aggregate | `count` · `aggregate` · `groupBy` |
|
|
34
28
|
|
|
35
|
-
|
|
29
|
+
The statement-building pieces are exported too, for tooling that needs SQL without executing it: `buildQuery`, `compileWhere`, `compileSelect`, `compileOrderBy`, `resolveResultShape`, and the runtime metadata builders `buildRuntimeMeta`, `getModelMeta`, `getFieldMeta`.
|
|
36
30
|
|
|
37
|
-
|
|
31
|
+
## The adapter contract
|
|
38
32
|
|
|
39
|
-
`
|
|
33
|
+
`DatabaseAdapter` is frozen. Implement it and any driver works with the whole ORM:
|
|
40
34
|
|
|
41
35
|
```ts
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
36
|
+
import type { DatabaseAdapter, QueryResult, TransactionOptions } from "@vibeorm/runtime";
|
|
37
|
+
|
|
38
|
+
const adapter: DatabaseAdapter = {
|
|
39
|
+
dialect: "postgres",
|
|
40
|
+
provider: "postgresql",
|
|
41
|
+
async execute(params: { text: string; values: unknown[] }): Promise<Record<string, unknown>[]> { /* … */ },
|
|
42
|
+
async executeUnsafe(params: { text: string; values?: unknown[] }): Promise<QueryResult> { /* … */ },
|
|
43
|
+
async transaction<T>(fn: (tx: DatabaseAdapter) => Promise<T>, options?: TransactionOptions): Promise<T> { /* … */ },
|
|
44
|
+
async connect(): Promise<void> { /* … */ },
|
|
45
|
+
async disconnect(): Promise<void> { /* … */ },
|
|
46
|
+
formatArrayParam(values: unknown[]): unknown { /* … */ },
|
|
47
|
+
};
|
|
46
48
|
```
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- Default behavior: full scalar row is returned (same as before)
|
|
55
|
-
- With `select`: only selected scalar columns are returned (plus internal keys when needed)
|
|
56
|
-
- With `returning: true`: force full scalar `RETURNING` payload even when `select` is present
|
|
50
|
+
- `execute` is the ORM hot path (row-returning, statement-cached by the adapter). `executeUnsafe` is the raw/DDL path and is the only place `affectedRows` surfaces.
|
|
51
|
+
- `transaction` nests: an inner call creates a savepoint. `isolationLevel` and `timeout` are top-level only — a nested call must refuse both with `VIBE_VALIDATION`.
|
|
52
|
+
- Adapters never let raw driver errors escape. Each maps its driver's failures to `VibeError` codes (`VIBE_UNIQUE_VIOLATION`, `VIBE_FK_VIOLATION`, `VIBE_TRANSACTION`, `VIBE_ADAPTER`) through a per-driver table, with the driver error as `cause`.
|
|
53
|
+
- `formatArrayParam` converts a JS array into the driver's preferred scalar-array representation; the ORM path binds arrays through it, the raw path does not.
|
|
54
|
+
- The optional `wire` declaration (`WireFidelity`) tells the runtime what the driver already delivers for non-list scalars, so provably no-op decoders can be skipped.
|
|
57
55
|
|
|
58
|
-
|
|
56
|
+
`toSqlExecutor({ adapter })` bridges an adapter to the `SqlExecutor` shape `@vibeorm/migrate` runs on.
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
### `withRetry(fn, options?)`
|
|
63
|
-
|
|
64
|
-
Retry utility with exponential backoff and jitter. Only retries `VibeTransientError` instances (connection failures, deadlocks, serialization conflicts, statement timeouts, pool exhaustion). Deterministic errors (`VibeRequestError`) are never retried.
|
|
58
|
+
## Relation strategies
|
|
65
59
|
|
|
66
60
|
```ts
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// Basic — 3 retries, exponential backoff with jitter
|
|
70
|
-
const users = await withRetry(() =>
|
|
71
|
-
db.user.findMany({ where: { active: true } })
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
// Custom options
|
|
75
|
-
const user = await withRetry(
|
|
76
|
-
() => db.user.create({ data: { email: "new@example.com" } }),
|
|
77
|
-
{ maxRetries: 5, baseDelay: 100, maxDelay: 10000 },
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
// Retry only specific error codes
|
|
81
|
-
const result = await withRetry(
|
|
82
|
-
() => db.$transaction(async (tx) => {
|
|
83
|
-
/* ... */
|
|
84
|
-
}, { isolationLevel: "Serializable" }),
|
|
85
|
-
{ retryOn: ["SERIALIZATION_FAILURE", "DEADLOCK"] },
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
// With abort signal
|
|
89
|
-
const controller = new AbortController();
|
|
90
|
-
const result = await withRetry(
|
|
91
|
-
() => db.user.findFirst({ where: { id: 1 } }),
|
|
92
|
-
{ signal: controller.signal },
|
|
93
|
-
);
|
|
61
|
+
const db = createClient({ schema, adapter, options: { relationStrategy: "join" } });
|
|
94
62
|
```
|
|
95
63
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
| Option | Default | Description |
|
|
99
|
-
|--------|---------|-------------|
|
|
100
|
-
| `maxRetries` | `3` | Max retry attempts (total executions = maxRetries + 1) |
|
|
101
|
-
| `baseDelay` | `50` | Base delay in ms for exponential backoff |
|
|
102
|
-
| `maxDelay` | `5000` | Maximum delay cap in ms |
|
|
103
|
-
| `maxJitter` | `50` | Random jitter added per retry to prevent thundering herd |
|
|
104
|
-
| `signal` | — | `AbortSignal` to cancel pending retries |
|
|
105
|
-
| `retryOn` | — | Filter to retry only specific `VibeTransientErrorCode`s |
|
|
106
|
-
| `onRetry` | — | Callback `({ error, attempt, delay }) => void` for logging/metrics |
|
|
107
|
-
|
|
108
|
-
Backoff formula: `min(baseDelay * 2^attempt + random(0, maxJitter), maxDelay)`
|
|
109
|
-
|
|
110
|
-
### `VibeValidationError`
|
|
64
|
+
- `"query"` (default): one query per relation, batched with `WHERE ... IN`. Works on every dialect.
|
|
65
|
+
- `"join"`: a single statement using `LEFT JOIN LATERAL` with JSON aggregation. PostgreSQL and PGlite only; other dialects throw `VibeError` with code `VIBE_UNSUPPORTED_CAPABILITY`.
|
|
111
66
|
|
|
112
|
-
|
|
67
|
+
A `relationStrategy` argument on a find overrides the client default. Relation trees are resolved by `resolveRelationSelection` / `resolveRelationLink` and attached by `attachRelations` (query strategy) or `attachLateralRelations` (join strategy).
|
|
113
68
|
|
|
114
|
-
|
|
69
|
+
## Codecs
|
|
115
70
|
|
|
116
|
-
|
|
71
|
+
Value encoding and decoding lives in one table per dialect: `POSTGRES_CODECS`, `SQLITE_CODECS`, `MYSQL_CODECS`, indexed by `DIALECT_CODECS` and reachable through `getCodec({ dialect, type })`. Nothing else in the codebase coerces values — scattered coercion is what produced v1's BigInt, enum-array and timestamp bugs.
|
|
117
72
|
|
|
118
73
|
```ts
|
|
119
|
-
|
|
120
|
-
execute(params: { text: string; values: unknown[] }): Promise<Record<string, unknown>[]>;
|
|
121
|
-
executeRaw(params: { text: string; values: unknown[] }): Promise<QueryResult>;
|
|
122
|
-
transaction<T>(params: { fn: (adapter: DatabaseAdapter) => Promise<T> }): Promise<T>;
|
|
123
|
-
connect(): Promise<void>;
|
|
124
|
-
disconnect(): Promise<void>;
|
|
125
|
-
};
|
|
74
|
+
import { decodeRows, encodeValue, getCodec } from "@vibeorm/runtime";
|
|
126
75
|
```
|
|
127
76
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
| Strategy | Method | Best for |
|
|
131
|
-
|----------|--------|----------|
|
|
132
|
-
| `"query"` | Parent query + batched `WHERE IN` queries | Deep/nested includes |
|
|
133
|
-
| `"join"` | `LEFT JOIN LATERAL` + JSON aggregation | Flat includes, fewer round-trips |
|
|
134
|
-
|
|
135
|
-
### Exported Types
|
|
136
|
-
|
|
137
|
-
`DatabaseAdapter`, `QueryResult`, `SqlExecutor`, `VibeClientOptions`, `ModelMeta`, `ModelMetaMap`, `ModelSchemas`, `ValidationSchema`, `QueryProfile`, `RelationProfile`, `ScalarFieldMeta`, `RelationFieldMeta`, `SqlQuery`, `Operation`, `RetryOptions`.
|
|
77
|
+
`encodeValue` runs on every parameter, `decodeValue` / `decodeRow` / `decodeRows` on every row materialization point — including relation children and join-strategy rows, not only top-level parents. `decodeAggregateValue` handles aggregate columns, whose result type differs from the underlying field's.
|
|
138
78
|
|
|
139
|
-
##
|
|
79
|
+
## Links
|
|
140
80
|
|
|
141
|
-
[
|
|
81
|
+
- [Documentation](https://github.com/vibeorm/vibeorm/tree/master/docs)
|
|
82
|
+
- [Query API](https://github.com/vibeorm/vibeorm/blob/master/docs/queries.md)
|
|
83
|
+
- [Repository and issues](https://github.com/vibeorm/vibeorm)
|
|
84
|
+
- MIT licensed
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database adapter contract for VibeORM v2. FROZEN INTERFACE — the runtime is
|
|
3
|
+
* driver-agnostic and talks to databases exclusively through this type;
|
|
4
|
+
* adapter packages (adapter-bun, adapter-pg, adapter-pglite, adapter-sqlite,
|
|
5
|
+
* adapter-mysql) implement it.
|
|
6
|
+
*
|
|
7
|
+
* ERROR CONTRACT: adapters never let raw driver errors escape. Every failure
|
|
8
|
+
* is mapped to a VibeError with a stable code via a per-driver table
|
|
9
|
+
* (unique violation → VIBE_UNIQUE_VIOLATION, FK violation → VIBE_FK_VIOLATION,
|
|
10
|
+
* serialization/deadlock → VIBE_TRANSACTION, connection/other → VIBE_ADAPTER),
|
|
11
|
+
* with the driver error attached as `cause`.
|
|
12
|
+
*/
|
|
13
|
+
import type { Dialect, Provider } from "@vibeorm/schema";
|
|
14
|
+
import type { WireFidelity } from "@vibeorm/sql";
|
|
15
|
+
/** Transaction isolation levels (mapped per dialect by the adapter). */
|
|
16
|
+
export type IsolationLevel = "ReadCommitted" | "RepeatableRead" | "Serializable";
|
|
17
|
+
/**
|
|
18
|
+
* Options for `$transaction` / `adapter.transaction`.
|
|
19
|
+
*
|
|
20
|
+
* `timeout` is milliseconds, enforced database-side where the engine can
|
|
21
|
+
* genuinely cancel a running statement (postgres servers via `SET LOCAL
|
|
22
|
+
* statement_timeout`, mysql's SELECT-only `MAX_EXECUTION_TIME`). Dialect
|
|
23
|
+
* honesty: an adapter whose engine CANNOT enforce it refuses loudly instead
|
|
24
|
+
* of no-opping — sqlite adapters throw `VIBE_VALIDATION` (busy_timeout only
|
|
25
|
+
* bounds lock acquisition), pglite throws `VIBE_UNSUPPORTED_CAPABILITY` (the
|
|
26
|
+
* WASM build accepts statement_timeout but never fires it).
|
|
27
|
+
*
|
|
28
|
+
* Options are TOP-LEVEL only: a nested `transaction()` call (a savepoint)
|
|
29
|
+
* refuses BOTH fields with `VIBE_VALIDATION` on every adapter — a savepoint
|
|
30
|
+
* cannot change the isolation level of the transaction it joins, and a
|
|
31
|
+
* nested timeout is not enforceable.
|
|
32
|
+
*/
|
|
33
|
+
export type TransactionOptions = {
|
|
34
|
+
isolationLevel?: IsolationLevel;
|
|
35
|
+
timeout?: number;
|
|
36
|
+
};
|
|
37
|
+
/** Result of an unsafe/raw execution: rows plus affected-row count. */
|
|
38
|
+
export type QueryResult = {
|
|
39
|
+
rows: Record<string, unknown>[];
|
|
40
|
+
affectedRows: number;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The primary interface every database adapter implements. Adapters own
|
|
44
|
+
* connection management, statement caching, transactions (including nested →
|
|
45
|
+
* savepoints), driver-specific parameter formatting and driver-error mapping.
|
|
46
|
+
*/
|
|
47
|
+
export type DatabaseAdapter = {
|
|
48
|
+
/** SQL dialect this adapter's database speaks (selects the @vibeorm/sql dialect). */
|
|
49
|
+
readonly dialect: Dialect;
|
|
50
|
+
/** Concrete provider (pglite and postgresql both speak `postgres`). */
|
|
51
|
+
readonly provider: Provider;
|
|
52
|
+
/**
|
|
53
|
+
* What this DRIVER actually delivers for non-list scalar columns on the
|
|
54
|
+
* result path (board #30) — lets the runtime's decode plans skip decoders
|
|
55
|
+
* that are provably no-ops for this driver (e.g. node-postgres already hands
|
|
56
|
+
* `Date` for timestamptz and parsed values for jsonb). OPTIONAL and purely
|
|
57
|
+
* an optimization contract: an adapter that declares nothing gets the full
|
|
58
|
+
* idempotency-guarded decode path. Declare only what the driver verifiably
|
|
59
|
+
* does; transactional adapters must carry the same declaration.
|
|
60
|
+
*/
|
|
61
|
+
readonly wire?: WireFidelity;
|
|
62
|
+
/**
|
|
63
|
+
* Execute a parameterized query on the ORM hot path and return rows.
|
|
64
|
+
* The adapter owns prepared-statement caching and ORM-path parameter
|
|
65
|
+
* serialization (e.g. tagged scalar-list params vs JSON values).
|
|
66
|
+
*/
|
|
67
|
+
execute(params: {
|
|
68
|
+
text: string;
|
|
69
|
+
values: unknown[];
|
|
70
|
+
}): Promise<Record<string, unknown>[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Execute a raw query (`$queryRaw*`, `$executeRaw*`, DDL). No statement
|
|
73
|
+
* caching; raw-path parameter serialization (arrays pass through to the
|
|
74
|
+
* driver where it supports them — see LEARNINGS: raw vs ORM paths differ).
|
|
75
|
+
*
|
|
76
|
+
* OMITTING `values` changes semantics on the sqlite adapters (board #31):
|
|
77
|
+
* `adapter-sqlite` and `adapter-better-sqlite3` route no-`values` text
|
|
78
|
+
* through the driver's multi-statement script mode (`db.run`/`exec` — the
|
|
79
|
+
* shape @vibeorm/migrate emits for DDL/PRAGMA batches), which CANNOT return
|
|
80
|
+
* rows: a parameterless SELECT yields `rows: []` there, while pg/pglite/
|
|
81
|
+
* mysql/bun return its result set either way. Callers that want rows from a
|
|
82
|
+
* single parameterless statement must pass `values: []` — the client's
|
|
83
|
+
* `$queryRawUnsafe` always does. Rule of thumb: `values` present (even
|
|
84
|
+
* empty) = one prepared statement with rows; `values` absent = script mode,
|
|
85
|
+
* rows not guaranteed.
|
|
86
|
+
*/
|
|
87
|
+
executeUnsafe(params: {
|
|
88
|
+
text: string;
|
|
89
|
+
values?: unknown[];
|
|
90
|
+
}): Promise<QueryResult>;
|
|
91
|
+
/**
|
|
92
|
+
* Run `fn` inside a transaction; the callback receives a transactional
|
|
93
|
+
* adapter with this same interface. Nested calls create savepoints. Throwing
|
|
94
|
+
* rolls back (the savepoint or the whole transaction) and rethrows.
|
|
95
|
+
*
|
|
96
|
+
* `options` is honored on TOP-LEVEL calls only. A NESTED call refuses any
|
|
97
|
+
* `isolationLevel` or `timeout` with `VIBE_VALIDATION` (`meta.nested:
|
|
98
|
+
* true`), identically on every adapter: a savepoint cannot change the
|
|
99
|
+
* isolation level of the transaction it joins, and a nested timeout is not
|
|
100
|
+
* enforceable. Silently dropping the option was the pre-#3 behaviour of the
|
|
101
|
+
* pg/pglite/mysql/bun adapters and is forbidden by constitution rule 4.
|
|
102
|
+
*/
|
|
103
|
+
transaction<T>(fn: (txAdapter: DatabaseAdapter) => Promise<T>, options?: TransactionOptions): Promise<T>;
|
|
104
|
+
/** Eagerly open/verify connectivity (pool warm-up or first connection). */
|
|
105
|
+
connect(): Promise<void>;
|
|
106
|
+
/** Gracefully close all connections. */
|
|
107
|
+
disconnect(): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Convert a JS array into the driver's preferred representation for a
|
|
110
|
+
* scalar-array parameter (postgres `= ANY($n)`): PG array literal string for
|
|
111
|
+
* drivers that send strings, the raw array for drivers with native support.
|
|
112
|
+
*/
|
|
113
|
+
formatArrayParam(values: unknown[]): unknown;
|
|
114
|
+
};
|
|
115
|
+
/** Minimal SQL execution interface used by @vibeorm/migrate. */
|
|
116
|
+
export type SqlExecutor = (params: {
|
|
117
|
+
text: string;
|
|
118
|
+
values?: unknown[];
|
|
119
|
+
}) => Promise<Record<string, unknown>[]>;
|
|
120
|
+
/** Bridge a DatabaseAdapter to the migrate package's SqlExecutor shape. */
|
|
121
|
+
export declare function toSqlExecutor(params: {
|
|
122
|
+
adapter: DatabaseAdapter;
|
|
123
|
+
}): SqlExecutor;
|
|
124
|
+
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAIjD,wEAAwE;AACxE,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,gBAAgB,GAAG,cAAc,CAAC;AAEjF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAIF,uEAAuE;AACvE,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAIF;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,qFAAqF;IACrF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAE5B;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC;IAE7B;;;;OAIG;IACH,OAAO,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAEzF;;;;;;;;;;;;;;;OAeG;IACH,aAAa,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAElF;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAEzG,2EAA2E;IAC3E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzB,wCAAwC;IACxC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5B;;;;OAIG;IACH,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;CAC9C,CAAC;AAIF,gEAAgE;AAChE,MAAM,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;CACpB,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AAEzC,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,eAAe,CAAA;CAAE,GAAG,WAAW,CAS/E"}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dynamic client: model delegates over a `DatabaseAdapter`, built from the
|
|
3
|
+
* Schema IR at runtime.
|
|
4
|
+
*
|
|
5
|
+
* This is the engine the generated client wraps in P2 proper. It is deliberately
|
|
6
|
+
* untyped-per-model — the generator emits a `.d.ts` that declares each delegate
|
|
7
|
+
* with precise argument and result types, and casts to it. Nothing about the
|
|
8
|
+
* shape of a query lives here: `query-builder.ts` builds statements,
|
|
9
|
+
* `@vibeorm/sql` renders them, `codecs.ts` moves values across the wire,
|
|
10
|
+
* `relation-loader.ts` attaches included relations, `nested-writes.ts`
|
|
11
|
+
* orchestrates relation verbs inside create/update data.
|
|
12
|
+
*
|
|
13
|
+
* Execution split:
|
|
14
|
+
* - row-returning statements use `adapter.execute` (the cached hot path),
|
|
15
|
+
* - count-returning mutations (createMany/updateMany/deleteMany) use
|
|
16
|
+
* `adapter.executeUnsafe`, the only place the frozen adapter contract
|
|
17
|
+
* surfaces `affectedRows`.
|
|
18
|
+
*/
|
|
19
|
+
import type { SchemaIR } from "@vibeorm/schema";
|
|
20
|
+
import type { DatabaseAdapter, TransactionOptions } from "./adapter.ts";
|
|
21
|
+
import type { RelationStrategy } from "./query-builder.ts";
|
|
22
|
+
import type { ClientComputedOptions } from "./computed.ts";
|
|
23
|
+
import type { ClientExtensionsOptions } from "./extensions.ts";
|
|
24
|
+
import type { DefineViewFn } from "./views.ts";
|
|
25
|
+
/** One executed statement, reported to `ClientOptions.onQuery`. */
|
|
26
|
+
export type QueryEvent = {
|
|
27
|
+
/** Model name, or `null` for raw queries. */
|
|
28
|
+
readonly model: string | null;
|
|
29
|
+
/** ORM method, or `"$queryRaw"` / `"$executeRaw"`. */
|
|
30
|
+
readonly method: string;
|
|
31
|
+
readonly text: string;
|
|
32
|
+
readonly values: readonly unknown[];
|
|
33
|
+
readonly durationMs: number;
|
|
34
|
+
};
|
|
35
|
+
export type ClientOptions = {
|
|
36
|
+
/** Called after every statement — logging, tracing, test assertions. */
|
|
37
|
+
readonly onQuery?: (event: QueryEvent) => void;
|
|
38
|
+
/**
|
|
39
|
+
* Default relation-loading strategy: `"query"` (batched WHERE-IN, portable,
|
|
40
|
+
* the default) or `"join"` (LATERAL + JSON aggregation, postgres/pglite
|
|
41
|
+
* only). A per-query `relationStrategy` argument overrides it.
|
|
42
|
+
*/
|
|
43
|
+
readonly relationStrategy?: RelationStrategy;
|
|
44
|
+
/**
|
|
45
|
+
* Append the primary key to ORDER BY when a find has no explicit orderBy
|
|
46
|
+
* (default false) — deterministic row order at the cost of a sort. v1 parity.
|
|
47
|
+
*/
|
|
48
|
+
readonly defaultOrderByPk?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Append the primary key as a tie-breaker to `distinct` ordering (default
|
|
51
|
+
* TRUE) — without it, WHICH row represents each distinct key is unspecified.
|
|
52
|
+
* v1 parity.
|
|
53
|
+
*/
|
|
54
|
+
readonly distinctOrderByPk?: boolean;
|
|
55
|
+
};
|
|
56
|
+
/** Rows are plain objects keyed by field name; the generated client narrows them. */
|
|
57
|
+
export type ClientRow = Record<string, unknown>;
|
|
58
|
+
/** Result of the count-returning batch methods. */
|
|
59
|
+
export type BatchResult = {
|
|
60
|
+
readonly count: number;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* A model's methods. Arguments and results are intentionally broad here — the
|
|
64
|
+
* generated `.d.ts` replaces this surface with precise per-model types, and
|
|
65
|
+
* keeping the runtime free of inference is what holds the type-cost budget
|
|
66
|
+
* (constitution rule 2).
|
|
67
|
+
*/
|
|
68
|
+
export type ModelDelegate = {
|
|
69
|
+
readonly findMany: (args?: ClientRow) => Promise<ClientRow[]>;
|
|
70
|
+
readonly findFirst: (args?: ClientRow) => Promise<ClientRow | null>;
|
|
71
|
+
readonly findFirstOrThrow: (args?: ClientRow) => Promise<ClientRow>;
|
|
72
|
+
readonly findUnique: (args: ClientRow) => Promise<ClientRow | null>;
|
|
73
|
+
readonly findUniqueOrThrow: (args: ClientRow) => Promise<ClientRow>;
|
|
74
|
+
readonly create: (args: ClientRow) => Promise<ClientRow>;
|
|
75
|
+
readonly createMany: (args: ClientRow) => Promise<BatchResult>;
|
|
76
|
+
readonly createManyAndReturn: (args: ClientRow) => Promise<ClientRow[]>;
|
|
77
|
+
readonly update: (args: ClientRow) => Promise<ClientRow>;
|
|
78
|
+
readonly updateMany: (args: ClientRow) => Promise<BatchResult>;
|
|
79
|
+
readonly upsert: (args: ClientRow) => Promise<ClientRow>;
|
|
80
|
+
readonly delete: (args: ClientRow) => Promise<ClientRow>;
|
|
81
|
+
readonly deleteMany: (args?: ClientRow) => Promise<BatchResult>;
|
|
82
|
+
/** Plain form returns a number; `{ select: { _all, field } }` returns per-key counts. */
|
|
83
|
+
readonly count: (args?: ClientRow) => Promise<number | Record<string, number>>;
|
|
84
|
+
readonly aggregate: (args: ClientRow) => Promise<ClientRow>;
|
|
85
|
+
readonly groupBy: (args: ClientRow) => Promise<ClientRow[]>;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* The client. Delegates are also attached as own properties under their
|
|
89
|
+
* camelCase names (`client.user.findMany()`); the type surfaces them through
|
|
90
|
+
* `$models` because a plain index signature would erase the `$`-methods.
|
|
91
|
+
* Model names can never collide with those: every reserved client name is
|
|
92
|
+
* `$`-prefixed (see `RESERVED_CLIENT_NAMES` in @vibeorm/schema).
|
|
93
|
+
*/
|
|
94
|
+
export type DynamicClient = {
|
|
95
|
+
/** Delegates keyed by BOTH camelCase client name and model name. */
|
|
96
|
+
readonly $models: Readonly<Record<string, ModelDelegate>>;
|
|
97
|
+
/**
|
|
98
|
+
* Subscribe to client events (`"query"` fires after every statement, with
|
|
99
|
+
* the same payload as `ClientOptions.onQuery` — both fire). Returns an
|
|
100
|
+
* unsubscribe function. Transaction clients share the parent's listeners.
|
|
101
|
+
*/
|
|
102
|
+
readonly $on: (event: "query", listener: (event: QueryEvent) => void) => () => void;
|
|
103
|
+
/**
|
|
104
|
+
* Run work in ONE transaction. Callback form: `fn` receives a transaction
|
|
105
|
+
* client; everything it runs rides the same BEGIN/COMMIT and a throw rolls
|
|
106
|
+
* back. Array form (Prisma parity): UN-AWAITED delegate calls — delegate
|
|
107
|
+
* methods return LAZY query promises that only dispatch on await — run
|
|
108
|
+
* SEQUENTIALLY inside one transaction, results returned positionally.
|
|
109
|
+
*/
|
|
110
|
+
readonly $transaction: {
|
|
111
|
+
<T>(fn: (tx: DynamicClient) => Promise<T>, options?: TransactionOptions): Promise<T>;
|
|
112
|
+
<T extends readonly unknown[]>(operations: readonly [...T], options?: TransactionOptions): Promise<{
|
|
113
|
+
-readonly [K in keyof T]: Awaited<T[K]>;
|
|
114
|
+
}>;
|
|
115
|
+
};
|
|
116
|
+
/** Tagged template — interpolations become bound parameters, never text. */
|
|
117
|
+
readonly $queryRaw: (strings: TemplateStringsArray, ...values: unknown[]) => Promise<ClientRow[]>;
|
|
118
|
+
/** Tagged template returning the affected-row count. */
|
|
119
|
+
readonly $executeRaw: (strings: TemplateStringsArray, ...values: unknown[]) => Promise<number>;
|
|
120
|
+
readonly $queryRawUnsafe: (text: string, ...values: unknown[]) => Promise<ClientRow[]>;
|
|
121
|
+
readonly $executeRawUnsafe: (text: string, ...values: unknown[]) => Promise<number>;
|
|
122
|
+
readonly $connect: () => Promise<void>;
|
|
123
|
+
readonly $disconnect: () => Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Field-masking views (v1 `defineView` parity, board #11) — a read-only
|
|
126
|
+
* scoped client restricted to the definition's models and fields. Attached
|
|
127
|
+
* by `attachDefineView` after construction (hence optional in the type);
|
|
128
|
+
* it is always present on a built client, transaction clients included.
|
|
129
|
+
*/
|
|
130
|
+
readonly $defineView?: DefineViewFn;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Build a client for a schema over an adapter.
|
|
134
|
+
*
|
|
135
|
+
* The adapter chooses the dialect: the same IR runs on postgres, sqlite or
|
|
136
|
+
* mysql, and every dialect difference is resolved by `@vibeorm/sql` and the
|
|
137
|
+
* codec table rather than by branching here.
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* const db = createClient({ schema, adapter });
|
|
141
|
+
* const users = await db.$models.user.findMany({ where: { active: true } });
|
|
142
|
+
*/
|
|
143
|
+
export declare function createClient(params: {
|
|
144
|
+
schema: SchemaIR;
|
|
145
|
+
adapter: DatabaseAdapter;
|
|
146
|
+
options?: ClientOptions;
|
|
147
|
+
/** Extension wiring — baked mounts + manifest from the generated client, live instances from the caller. */
|
|
148
|
+
extensions?: ClientExtensionsOptions;
|
|
149
|
+
/** Computed-field wiring — baked manifest from the generated client, live specs from the caller. */
|
|
150
|
+
computed?: ClientComputedOptions;
|
|
151
|
+
}): DynamicClient;
|
|
152
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/codecs.d.ts
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The codec entry points — constitution rule 6.
|
|
3
|
+
*
|
|
4
|
+
* The PURE per-dialect tables (dialect × scalar type) live in @vibeorm/sql
|
|
5
|
+
* (`codecs.ts` there) and are re-exported here so consumers keep one import
|
|
6
|
+
* site. This module owns the FieldMeta-aware entry points: list handling,
|
|
7
|
+
* enum-to-String routing and row materialization.
|
|
8
|
+
*
|
|
9
|
+
* The two rules that make it work:
|
|
10
|
+
* - EVERY parameter bound by the query builder passes through `encodeValue`.
|
|
11
|
+
* - EVERY row materialized by the client passes through `decodeRow`/
|
|
12
|
+
* `decodeRows` — including every relation-loaded child row, at every
|
|
13
|
+
* materialization point (the v1 lesson: BigInt-as-string and enum-array
|
|
14
|
+
* bugs came from children skipping coercion).
|
|
15
|
+
*/
|
|
16
|
+
import type { Dialect, FieldType } from "@vibeorm/schema";
|
|
17
|
+
import type { ScalarCodec, WireFidelity } from "@vibeorm/sql";
|
|
18
|
+
import type { FieldMeta, ModelMeta } from "./model-meta.ts";
|
|
19
|
+
export { DIALECT_CODECS, MYSQL_CODECS, POSTGRES_CODECS, SQLITE_CODECS, decodeIsNoop, } from "@vibeorm/sql";
|
|
20
|
+
export type { CodecTable, ScalarCodec, WireFidelity } from "@vibeorm/sql";
|
|
21
|
+
/** The codec for a field type on a dialect. Enum references use the String codec. */
|
|
22
|
+
export declare function getCodec(params: {
|
|
23
|
+
dialect: Dialect;
|
|
24
|
+
type: FieldType;
|
|
25
|
+
}): ScalarCodec;
|
|
26
|
+
/**
|
|
27
|
+
* Encode one value for parameter binding. `null`/`undefined` pass through
|
|
28
|
+
* untouched (SQL NULL). List fields encode element-wise; the adapter's
|
|
29
|
+
* `formatArrayParam` then owns the array's wire representation — except for
|
|
30
|
+
* postgres ENUM arrays, which become an array literal here (no driver can
|
|
31
|
+
* serialize them, see {@link isPostgresEnumList}).
|
|
32
|
+
*/
|
|
33
|
+
export declare function encodeValue(params: {
|
|
34
|
+
dialect: Dialect;
|
|
35
|
+
field: FieldMeta;
|
|
36
|
+
value: unknown;
|
|
37
|
+
}): unknown;
|
|
38
|
+
/**
|
|
39
|
+
* Decode one driver value. List fields decode element-wise.
|
|
40
|
+
*
|
|
41
|
+
* postgres returns ENUM arrays as a raw array literal string, because no driver
|
|
42
|
+
* knows a user-defined enum's array OID (LEARNINGS.md) — that literal is parsed
|
|
43
|
+
* here, next to the other coercion rules.
|
|
44
|
+
*/
|
|
45
|
+
export declare function decodeValue(params: {
|
|
46
|
+
dialect: Dialect;
|
|
47
|
+
field: FieldMeta;
|
|
48
|
+
value: unknown;
|
|
49
|
+
}): unknown;
|
|
50
|
+
/**
|
|
51
|
+
* Encode one COMPARISON operand (WHERE / cursor bounds). Identical to
|
|
52
|
+
* {@link encodeValue} everywhere except sqlite Decimal (SQL review B4):
|
|
53
|
+
* Decimal STORES as exact TEXT there, but text comparison is lexicographic —
|
|
54
|
+
* so every compare site casts the column (`CAST(col AS REAL)`) and binds the
|
|
55
|
+
* operand as a NUMBER. Comparison precision is float64-bounded on sqlite
|
|
56
|
+
* (docs/dialects.md); storage and read-back stay exact.
|
|
57
|
+
*/
|
|
58
|
+
export declare function encodeFilterValue(params: {
|
|
59
|
+
dialect: Dialect;
|
|
60
|
+
field: FieldMeta;
|
|
61
|
+
value: unknown;
|
|
62
|
+
}): unknown;
|
|
63
|
+
/**
|
|
64
|
+
* Encode one AGGREGATE-predicate operand (HAVING). On sqlite an aggregate
|
|
65
|
+
* expression has NO column affinity, so a TEXT operand never converts and the
|
|
66
|
+
* predicate is constant-false (SQL review M5, live-proven) — operands bind in
|
|
67
|
+
* raw numeric form there: Decimal as a number (the aggregate is over
|
|
68
|
+
* `CAST(col AS REAL)`), BigInt as a bigint (bun:sqlite binds int64 natively).
|
|
69
|
+
* Other dialects keep the field codec's wire form.
|
|
70
|
+
*/
|
|
71
|
+
export declare function encodeAggregateOperand(params: {
|
|
72
|
+
dialect: Dialect;
|
|
73
|
+
field: FieldMeta;
|
|
74
|
+
value: unknown;
|
|
75
|
+
}): unknown;
|
|
76
|
+
/** Decode one non-null driver value (list handling prebound). */
|
|
77
|
+
type RowDecoder = (value: unknown) => unknown;
|
|
78
|
+
/**
|
|
79
|
+
* The prebound encoder for one field on one dialect, or `null` when its codec
|
|
80
|
+
* encode is the identity. Mirrors `encodeValue` exactly (see `fieldDecoder`).
|
|
81
|
+
*/
|
|
82
|
+
export declare function fieldEncoder(params: {
|
|
83
|
+
dialect: Dialect;
|
|
84
|
+
field: FieldMeta;
|
|
85
|
+
}): RowDecoder | null;
|
|
86
|
+
/**
|
|
87
|
+
* The prebound decoder for one field on one dialect, or `null` when its codec
|
|
88
|
+
* decode is the identity (nothing to do). Mirrors `decodeValue` exactly:
|
|
89
|
+
* elements of a list decode element-wise (null elements pass), a non-array
|
|
90
|
+
* value on a list field goes through the element decoder as-is.
|
|
91
|
+
*
|
|
92
|
+
* With a `wire` declaration (board #30 — the ADAPTER's promise about what its
|
|
93
|
+
* driver delivers), decoders that are provably no-ops for that wire are pruned
|
|
94
|
+
* too ({@link decodeIsNoop}): non-list DateTime/Json/Int/Float columns on
|
|
95
|
+
* drivers that already deliver native forms. Without a declaration the full
|
|
96
|
+
* idempotency-guarded decoder set applies — exactly the pre-#30 behaviour.
|
|
97
|
+
*/
|
|
98
|
+
export declare function fieldDecoder(params: {
|
|
99
|
+
dialect: Dialect;
|
|
100
|
+
field: FieldMeta;
|
|
101
|
+
wire?: WireFidelity;
|
|
102
|
+
}): RowDecoder | null;
|
|
103
|
+
/**
|
|
104
|
+
* Materialize one driver row: column names become field names, values pass
|
|
105
|
+
* through their codecs. Columns the model does not know (aggregate aliases, raw
|
|
106
|
+
* extras) are carried over verbatim.
|
|
107
|
+
*/
|
|
108
|
+
export declare function decodeRow(params: {
|
|
109
|
+
dialect: Dialect;
|
|
110
|
+
model: ModelMeta;
|
|
111
|
+
row: Record<string, unknown>;
|
|
112
|
+
/** The executing adapter's wire declaration — prunes provably-no-op decoders (board #30). */
|
|
113
|
+
wire?: WireFidelity;
|
|
114
|
+
}): Record<string, unknown>;
|
|
115
|
+
/** `decodeRow` over a result set. */
|
|
116
|
+
export declare function decodeRows(params: {
|
|
117
|
+
dialect: Dialect;
|
|
118
|
+
model: ModelMeta;
|
|
119
|
+
rows: readonly Record<string, unknown>[];
|
|
120
|
+
/** The executing adapter's wire declaration — prunes provably-no-op decoders (board #30). */
|
|
121
|
+
wire?: WireFidelity;
|
|
122
|
+
}): Record<string, unknown>[];
|
|
123
|
+
/**
|
|
124
|
+
* `decodeRows` for rows the caller OWNS (fresh driver output): when no column
|
|
125
|
+
* is renamed the rows are decoded in place — only columns whose codec does
|
|
126
|
+
* something are touched — and the same array returns. With renames it falls
|
|
127
|
+
* back to rebuilding rows. Every decoder is idempotent (each checks its input
|
|
128
|
+
* type), so re-materializing an already-decoded row is harmless.
|
|
129
|
+
*
|
|
130
|
+
* Internal to the runtime (client + relation loader) — the exported
|
|
131
|
+
* `decodeRow`/`decodeRows` keep their copy semantics.
|
|
132
|
+
*/
|
|
133
|
+
export declare function materializeRows(params: {
|
|
134
|
+
dialect: Dialect;
|
|
135
|
+
model: ModelMeta;
|
|
136
|
+
rows: Record<string, unknown>[];
|
|
137
|
+
/** The executing adapter's wire declaration — prunes provably-no-op decoders (board #30). */
|
|
138
|
+
wire?: WireFidelity;
|
|
139
|
+
}): Record<string, unknown>[];
|
|
140
|
+
/**
|
|
141
|
+
* `materializeRows` for JSON-transport rows — the join strategy's child rows
|
|
142
|
+
* after `JSON.parse`. Postgres-only by construction (lateral joins are).
|
|
143
|
+
*/
|
|
144
|
+
export declare function materializeJsonRows(params: {
|
|
145
|
+
model: ModelMeta;
|
|
146
|
+
rows: Record<string, unknown>[];
|
|
147
|
+
}): Record<string, unknown>[];
|
|
148
|
+
/**
|
|
149
|
+
* Normalize one aggregate value — the contract, tested live on PGlite:
|
|
150
|
+
*
|
|
151
|
+
* | aggregate | field type | JS result |
|
|
152
|
+
* |-----------|-----------------|-------------------------------|
|
|
153
|
+
* | `_count` | any / `_all` | `number` (0 over an empty set)|
|
|
154
|
+
* | `_avg` | Int/BigInt/Float| `number` (pg numeric → Number)|
|
|
155
|
+
* | `_avg` | Decimal | `string` (no float rounding) |
|
|
156
|
+
* | `_sum` | Int / Float | `number` |
|
|
157
|
+
* | `_sum` | BigInt | `bigint` |
|
|
158
|
+
* | `_sum` | Decimal | `string` |
|
|
159
|
+
* | `_min/_max`| any | the field's own codec type |
|
|
160
|
+
*
|
|
161
|
+
* Every non-`_count` aggregate over an empty set is `null` (SQL semantics,
|
|
162
|
+
* kept deliberately — 0 would be a lie for MIN/AVG).
|
|
163
|
+
*/
|
|
164
|
+
export declare function decodeAggregateValue(params: {
|
|
165
|
+
dialect: Dialect;
|
|
166
|
+
key: "_count" | "_avg" | "_sum" | "_min" | "_max";
|
|
167
|
+
field: FieldMeta | null;
|
|
168
|
+
value: unknown;
|
|
169
|
+
}): unknown;
|
|
170
|
+
//# sourceMappingURL=codecs.d.ts.map
|