turbine-orm 0.49.0 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -39
- package/dist/cjs/adapters/cockroachdb.d.ts +39 -0
- package/dist/cjs/adapters/index.d.ts +110 -0
- package/dist/cjs/adapters/yugabytedb.d.ts +51 -0
- package/dist/cjs/cli/config.d.ts +181 -0
- package/dist/cjs/cli/config.js +32 -6
- package/dist/cjs/cli/destructive.d.ts +38 -0
- package/dist/cjs/cli/index.d.ts +359 -0
- package/dist/cjs/cli/index.js +228 -56
- package/dist/cjs/cli/loader.d.ts +61 -0
- package/dist/cjs/cli/mcp.d.ts +42 -0
- package/dist/cjs/cli/migrate.d.ts +356 -0
- package/dist/cjs/cli/migrate.js +131 -40
- package/dist/cjs/cli/observe-ui.d.ts +1 -0
- package/dist/cjs/cli/observe-ui.js +14 -5
- package/dist/cjs/cli/observe.d.ts +25 -0
- package/dist/cjs/cli/observe.js +49 -12
- package/dist/cjs/cli/pii-tags.d.ts +53 -0
- package/dist/cjs/cli/prisma-report.d.ts +33 -0
- package/dist/cjs/cli/prisma-report.js +73 -0
- package/dist/cjs/cli/prisma-resolve.d.ts +106 -0
- package/dist/cjs/cli/prisma-resolve.js +1 -0
- package/dist/cjs/cli/prisma-schema.d.ts +176 -0
- package/dist/cjs/cli/prisma-schema.js +82 -4
- package/dist/cjs/cli/rate-limit.d.ts +32 -0
- package/dist/cjs/cli/rate-limit.js +45 -0
- package/dist/cjs/cli/studio-demo.d.ts +43 -0
- package/dist/cjs/cli/studio-ui.generated.d.ts +1 -0
- package/dist/cjs/cli/studio.d.ts +207 -0
- package/dist/cjs/cli/studio.js +136 -71
- package/dist/cjs/cli/ui.d.ts +73 -0
- package/dist/cjs/cli/ui.js +51 -9
- package/dist/cjs/client.d.ts +837 -0
- package/dist/cjs/client.js +3 -0
- package/dist/cjs/dialect.d.ts +516 -0
- package/dist/cjs/dialect.js +37 -12
- package/dist/cjs/errors.d.ts +370 -0
- package/dist/cjs/generate.d.ts +137 -0
- package/dist/cjs/generate.js +39 -6
- package/dist/cjs/index-advisor.d.ts +153 -0
- package/dist/cjs/index-stats.d.ts +384 -0
- package/dist/cjs/index.d.ts +55 -0
- package/dist/cjs/index.js +7 -2
- package/dist/cjs/introspect.d.ts +269 -0
- package/dist/cjs/mssql.d.ts +232 -0
- package/dist/cjs/mssql.js +6 -0
- package/dist/cjs/mysql.d.ts +173 -0
- package/dist/cjs/mysql.js +16 -0
- package/dist/cjs/nested-write.d.ts +96 -0
- package/dist/cjs/nested-write.js +414 -24
- package/dist/cjs/observe.d.ts +115 -0
- package/dist/cjs/optional-peer-import.d.cts +72 -0
- package/dist/cjs/pipeline-submittable.d.ts +93 -0
- package/dist/cjs/pipeline.d.ts +71 -0
- package/dist/cjs/powdb-introspect.d.ts +84 -0
- package/dist/cjs/powdb.d.ts +931 -0
- package/dist/cjs/powdb.js +106 -21
- package/dist/cjs/powql.d.ts +592 -0
- package/dist/cjs/powql.js +42 -6
- package/dist/cjs/prisma-compat.d.ts +283 -0
- package/dist/cjs/prisma-compat.js +167 -9
- package/dist/cjs/query/aggregates.d.ts +92 -0
- package/dist/cjs/query/aggregates.js +7 -3
- package/dist/cjs/query/batched-loader.d.ts +193 -0
- package/dist/cjs/query/builder.d.ts +849 -0
- package/dist/cjs/query/builder.js +571 -65
- package/dist/cjs/query/compound-unique.d.ts +51 -0
- package/dist/cjs/query/deferred.d.ts +223 -0
- package/dist/cjs/query/filters.d.ts +201 -0
- package/dist/cjs/query/index.d.ts +14 -0
- package/dist/cjs/query/index.js +6 -1
- package/dist/cjs/query/relations.d.ts +609 -0
- package/dist/cjs/query/relations.js +693 -46
- package/dist/cjs/query/types.d.ts +1300 -0
- package/dist/cjs/query/utils.d.ts +209 -0
- package/dist/cjs/query/utils.js +208 -1
- package/dist/cjs/query/warn-registry.d.ts +68 -0
- package/dist/cjs/query/warn-registry.js +9 -0
- package/dist/cjs/query/where-compile.d.ts +139 -0
- package/dist/cjs/query/where.d.ts +548 -0
- package/dist/cjs/query/where.js +58 -22
- package/dist/cjs/query/writes.d.ts +172 -0
- package/dist/cjs/query/writes.js +105 -12
- package/dist/cjs/realtime.d.ts +70 -0
- package/dist/cjs/schema-builder.d.ts +354 -0
- package/dist/cjs/schema-metadata.d.ts +83 -0
- package/dist/cjs/schema-sql.d.ts +217 -0
- package/dist/cjs/schema-sql.js +23 -5
- package/dist/cjs/schema.d.ts +356 -0
- package/dist/cjs/schema.js +125 -0
- package/dist/cjs/seed.d.ts +15 -0
- package/dist/cjs/serverless.d.ts +142 -0
- package/dist/cjs/sqlite.d.ts +143 -0
- package/dist/cjs/sqlite.js +4 -0
- package/dist/cjs/typed-sql.d.ts +102 -0
- package/dist/cli/config.d.ts +18 -4
- package/dist/cli/config.js +31 -6
- package/dist/cli/index.d.ts +123 -0
- package/dist/cli/index.js +223 -58
- package/dist/cli/migrate.d.ts +59 -10
- package/dist/cli/migrate.js +128 -41
- package/dist/cli/observe-ui.d.ts +1 -1
- package/dist/cli/observe-ui.js +14 -5
- package/dist/cli/observe.d.ts +7 -1
- package/dist/cli/observe.js +48 -12
- package/dist/cli/prisma-report.d.ts +14 -0
- package/dist/cli/prisma-report.js +72 -0
- package/dist/cli/prisma-resolve.d.ts +6 -0
- package/dist/cli/prisma-resolve.js +1 -0
- package/dist/cli/prisma-schema.d.ts +62 -2
- package/dist/cli/prisma-schema.js +81 -4
- package/dist/cli/rate-limit.d.ts +32 -0
- package/dist/cli/rate-limit.js +40 -0
- package/dist/cli/studio.d.ts +5 -5
- package/dist/cli/studio.js +135 -70
- package/dist/cli/ui.d.ts +1 -1
- package/dist/cli/ui.js +51 -9
- package/dist/client.d.ts +40 -0
- package/dist/client.js +3 -0
- package/dist/dialect.d.ts +17 -1
- package/dist/dialect.js +37 -12
- package/dist/generate.js +40 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/mssql.js +6 -0
- package/dist/mysql.js +16 -0
- package/dist/nested-write.d.ts +2 -0
- package/dist/nested-write.js +415 -25
- package/dist/powdb.d.ts +4 -2
- package/dist/powdb.js +106 -21
- package/dist/powql.d.ts +5 -0
- package/dist/powql.js +42 -6
- package/dist/prisma-compat.d.ts +2 -0
- package/dist/prisma-compat.js +166 -8
- package/dist/query/aggregates.js +7 -3
- package/dist/query/builder.d.ts +292 -21
- package/dist/query/builder.js +570 -64
- package/dist/query/deferred.d.ts +39 -0
- package/dist/query/index.d.ts +1 -1
- package/dist/query/index.js +1 -1
- package/dist/query/relations.d.ts +173 -5
- package/dist/query/relations.js +688 -47
- package/dist/query/types.d.ts +123 -39
- package/dist/query/utils.d.ts +116 -0
- package/dist/query/utils.js +198 -0
- package/dist/query/warn-registry.d.ts +9 -0
- package/dist/query/warn-registry.js +9 -0
- package/dist/query/where.d.ts +38 -1
- package/dist/query/where.js +58 -23
- package/dist/query/writes.d.ts +42 -1
- package/dist/query/writes.js +104 -13
- package/dist/schema-sql.d.ts +14 -0
- package/dist/schema-sql.js +23 -5
- package/dist/schema.d.ts +38 -0
- package/dist/schema.js +123 -0
- package/dist/sqlite.js +4 -0
- package/package.json +77 -28
package/dist/cjs/powql.js
CHANGED
|
@@ -143,6 +143,21 @@ const OPERATOR_KEYS = new Set([
|
|
|
143
143
|
'endsWith',
|
|
144
144
|
'mode',
|
|
145
145
|
]);
|
|
146
|
+
/**
|
|
147
|
+
* Aggregate keys accepted inside a per-field `having` filter, mapped to the
|
|
148
|
+
* PowQL function each one emits. The set is FIXED: any other key is rejected by
|
|
149
|
+
* {@link PowqlInterface.buildHaving} rather than interpolated into the query
|
|
150
|
+
* text (the SQL builder holds the same contract, see `aggFnByKey` in
|
|
151
|
+
* query/aggregates.ts). A Map is used deliberately so an inherited object key
|
|
152
|
+
* (`constructor`, `toString`, `__proto__`) can never resolve to a function name.
|
|
153
|
+
*/
|
|
154
|
+
const POWQL_HAVING_AGG_FNS = new Map([
|
|
155
|
+
['_sum', 'sum'],
|
|
156
|
+
['_avg', 'avg'],
|
|
157
|
+
['_min', 'min'],
|
|
158
|
+
['_max', 'max'],
|
|
159
|
+
['_count', 'count'],
|
|
160
|
+
]);
|
|
146
161
|
/** Filters that have no PowDB representation and must throw E017. */
|
|
147
162
|
function rejectUnsupportedFilter(value, field) {
|
|
148
163
|
if ('distance' in value || 'metric' in value) {
|
|
@@ -242,7 +257,7 @@ class PowqlInterface {
|
|
|
242
257
|
// json document column: a JS object/array is serialized to canonical JSON
|
|
243
258
|
// text and stored as a json document (a JS string passes through raw, same
|
|
244
259
|
// contract as pg jsonb; `null` stays `null`).
|
|
245
|
-
tagged = new powdb_js_1.PowdbJsonParam(value);
|
|
260
|
+
tagged = new powdb_js_1.PowdbJsonParam(value, col.name);
|
|
246
261
|
}
|
|
247
262
|
params.push(tagged);
|
|
248
263
|
return `$${params.length}`;
|
|
@@ -2496,6 +2511,11 @@ class PowqlInterface {
|
|
|
2496
2511
|
* with the projection); a per-field aggregate re-emits its inner expression
|
|
2497
2512
|
* (from `aggInner` when the field is a requested aggregate, so a JSON-path
|
|
2498
2513
|
* aggregate reuses its bound placeholders, else `.field` for a plain column).
|
|
2514
|
+
*
|
|
2515
|
+
* Every token that reaches the PowQL text is builder-controlled: the field is
|
|
2516
|
+
* validated by `ref()`, the aggregate function comes from
|
|
2517
|
+
* {@link POWQL_HAVING_AGG_FNS} (unknown keys throw E003), the operator from a
|
|
2518
|
+
* fixed map, and every compared value is bound as a `$N` param.
|
|
2499
2519
|
*/
|
|
2500
2520
|
buildHaving(having, params, aggInner) {
|
|
2501
2521
|
if (!having)
|
|
@@ -2505,10 +2525,19 @@ class PowqlInterface {
|
|
|
2505
2525
|
if (typeof filter === 'number')
|
|
2506
2526
|
return `${expr} = ${this.param(filter, params)}`;
|
|
2507
2527
|
const f = filter;
|
|
2508
|
-
|
|
2528
|
+
// Map (not an object literal) so an inherited key such as `constructor`
|
|
2529
|
+
// cannot resolve to a truthy value and reach the emitted PowQL.
|
|
2530
|
+
const ops = new Map([
|
|
2531
|
+
['equals', '='],
|
|
2532
|
+
['gt', '>'],
|
|
2533
|
+
['gte', '>='],
|
|
2534
|
+
['lt', '<'],
|
|
2535
|
+
['lte', '<='],
|
|
2536
|
+
['not', '!='],
|
|
2537
|
+
]);
|
|
2509
2538
|
return Object.entries(f)
|
|
2510
|
-
.filter(([k]) => ops
|
|
2511
|
-
.map(([k, v]) => `${expr} ${ops
|
|
2539
|
+
.filter(([k]) => ops.has(k))
|
|
2540
|
+
.map(([k, v]) => `${expr} ${ops.get(k)} ${this.param(v, params)}`)
|
|
2512
2541
|
.join(' and ');
|
|
2513
2542
|
};
|
|
2514
2543
|
for (const [key, spec] of Object.entries(having)) {
|
|
@@ -2518,11 +2547,18 @@ class PowqlInterface {
|
|
|
2518
2547
|
conds.push(cmp('count(*)', spec));
|
|
2519
2548
|
}
|
|
2520
2549
|
else {
|
|
2521
|
-
for (const [
|
|
2550
|
+
for (const [aggKey, filter] of Object.entries(spec)) {
|
|
2522
2551
|
if (filter == null)
|
|
2523
2552
|
continue;
|
|
2553
|
+
// The function token is emitted verbatim into the PowQL text, so it
|
|
2554
|
+
// must come from the fixed allowlist, never from the caller's key.
|
|
2555
|
+
const fn = POWQL_HAVING_AGG_FNS.get(aggKey);
|
|
2556
|
+
if (!fn) {
|
|
2557
|
+
throw new errors_js_1.ValidationError(`[turbine] Unknown aggregate "${aggKey}" in having for field "${key}" on table "${this.table}". ` +
|
|
2558
|
+
`Supported: ${[...POWQL_HAVING_AGG_FNS.keys()].join(', ')}.`);
|
|
2559
|
+
}
|
|
2524
2560
|
const inner = aggInner.get(key) ?? this.ref(key);
|
|
2525
|
-
conds.push(cmp(`${fn
|
|
2561
|
+
conds.push(cmp(`${fn}(${inner})`, filter));
|
|
2526
2562
|
}
|
|
2527
2563
|
}
|
|
2528
2564
|
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* turbine-orm/prisma-compat, a typed PrismaClient-surface adapter over a
|
|
3
|
+
* {@link TurbineClient}.
|
|
4
|
+
*
|
|
5
|
+
* This subpath lets a codebase that speaks Prisma's `db.model.findMany(...)`
|
|
6
|
+
* surface run on Turbine with minimal churn. It is a **pure TypeScript shim**:
|
|
7
|
+
* zero new runtime dependencies, never imported by Turbine core, and driven
|
|
8
|
+
* entirely by the {@link PrismaCompatMap} that `turbine migrate-from-prisma`
|
|
9
|
+
* emits (`prisma-map.ts`). Every Prisma model / field / relation / compound
|
|
10
|
+
* unique name in the map was resolved against live introspected metadata, so
|
|
11
|
+
* the adapter only ever translates names it can prove exist.
|
|
12
|
+
*
|
|
13
|
+
* ## What it does
|
|
14
|
+
*
|
|
15
|
+
* - **Model delegates** under both the Prisma model name (`compat.User`) and
|
|
16
|
+
* Prisma's client-property spelling with the first letter lowercased
|
|
17
|
+
* (`compat.user`, what generated Prisma call sites actually use),
|
|
18
|
+
* translating args recursively: `include`→`with`, `select` split into scalar
|
|
19
|
+
* selection + relations, field/relation renames both ways through the map,
|
|
20
|
+
* `take`/`skip`→`limit`/`offset`, cursor pagination, and compound-unique
|
|
21
|
+
* selectors (including custom `@@unique(name:)` names the core `findUnique`
|
|
22
|
+
* derivation cannot know).
|
|
23
|
+
* - **`$transaction`** in both forms: a callback (`$transaction(async (tx) => …)`)
|
|
24
|
+
* and Prisma's lazy array batching (`$transaction([a.create(...), b.update(...)])`)
|
|
25
|
+
* , the un-awaited delegate calls defer to Turbine's `build*()` methods and
|
|
26
|
+
* run atomically through the core batch `$transaction([...])` path.
|
|
27
|
+
* - **Raw SQL**: `$queryRaw` / `$executeRaw` tagged templates (with
|
|
28
|
+
* `Prisma.sql`-style nested-fragment flattening) and the `*Unsafe` variants.
|
|
29
|
+
* - **Result reshaping**: `_count` objects keyed back to Prisma relation names,
|
|
30
|
+
* and to-one relations surfaced as `object | null`.
|
|
31
|
+
*
|
|
32
|
+
* ## What it deliberately does NOT do (documented divergences)
|
|
33
|
+
*
|
|
34
|
+
* These cannot be faithfully translated and are not attempted; each throws or is
|
|
35
|
+
* documented rather than silently returning wrong data:
|
|
36
|
+
*
|
|
37
|
+
* - `$extends` / client extensions, `$use` with Prisma's middleware param shape.
|
|
38
|
+
* - `instanceof PrismaClientKnownRequestError`, `.meta`/message byte parity
|
|
39
|
+
* (opt into `prismaErrorCodes` for a `.code` like `P2002`, without pretending
|
|
40
|
+
* `instanceof` identity).
|
|
41
|
+
* - `Prisma.join` / `Prisma.raw` composition beyond plain fragment flattening.
|
|
42
|
+
* - Fluent relation chaining (`prisma.user.findUnique().posts()`).
|
|
43
|
+
* - Accelerate / Pulse / driver-adapter preview features, the Mongo API, and the
|
|
44
|
+
* `prisma migrate`/`db` CLI family (Turbine ships its own migrations).
|
|
45
|
+
* - **Inclusive bare cursors whose field is not the sort key**, see
|
|
46
|
+
* {@link translateCursor}. A bare Prisma cursor is inclusive; translating it
|
|
47
|
+
* correctly needs the anchor row's sort-key value. When the cursor field is
|
|
48
|
+
* the single `orderBy` field (or the cursor is the single-column PK with no
|
|
49
|
+
* `orderBy`) it compiles to a `gte`/`lte` keyset predicate; **otherwise it
|
|
50
|
+
* throws** an {@link UnsupportedFeatureError} rather than emit an off-by-one
|
|
51
|
+
* page. Pair the cursor with `skip: 1` (Prisma's idiom) for the exact
|
|
52
|
+
* exclusive-cursor + `offset` translation.
|
|
53
|
+
* - **Negative `take`** (take-from-end) and **`skip` on a nested relation
|
|
54
|
+
* include** throw, Turbine's `with` clause has no offset and no reverse-take.
|
|
55
|
+
*
|
|
56
|
+
* ## Type dependencies (0.41.0)
|
|
57
|
+
*
|
|
58
|
+
* - Field-name identity fast path pairs with the generator's
|
|
59
|
+
* `--keep-column-names` output (byte-shaped like a snake_case Prisma client).
|
|
60
|
+
* - Compound-unique default selectors are handled by the core
|
|
61
|
+
* `findUnique`-family derivation; this adapter only translates custom
|
|
62
|
+
* `@@unique(name:)` names on top.
|
|
63
|
+
* - To-one relations return `object | null` natively once the unique-FK hasOne
|
|
64
|
+
* introspection default is in effect (0.41.0). Until a schema is regenerated,
|
|
65
|
+
* the map's `cardinality: 'one'` still drives the first-element-or-null guard,
|
|
66
|
+
* so the surface is correct either way.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```ts
|
|
70
|
+
* import { TurbineClient } from 'turbine-orm';
|
|
71
|
+
* import { createPrismaCompatClient } from 'turbine-orm/prisma-compat';
|
|
72
|
+
* import { SCHEMA } from './generated/turbine/metadata.js';
|
|
73
|
+
* import { PRISMA_MAP } from './generated/turbine/prisma-map.js';
|
|
74
|
+
*
|
|
75
|
+
* const db = new TurbineClient({ connectionString: process.env.DATABASE_URL }, SCHEMA);
|
|
76
|
+
* const prisma = createPrismaCompatClient(db, PRISMA_MAP);
|
|
77
|
+
*
|
|
78
|
+
* const users = await prisma.User.findMany({
|
|
79
|
+
* where: { email: { contains: '@acme.com' } },
|
|
80
|
+
* include: { posts: { orderBy: { createdAt: 'desc' }, take: 5 } },
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
import type { TurbineClient } from './client.js';
|
|
85
|
+
import type { DeferredQuery } from './query/index.js';
|
|
86
|
+
import type { PrismaCompatMap, SchemaMetadata } from './schema.js';
|
|
87
|
+
/** A build-only query object with the `build*` methods the adapter drives. */
|
|
88
|
+
export interface CompatQueryInterface {
|
|
89
|
+
findMany(args?: Record<string, unknown>): Promise<unknown>;
|
|
90
|
+
findFirst(args?: Record<string, unknown>): Promise<unknown>;
|
|
91
|
+
findUnique(args: Record<string, unknown>): Promise<unknown>;
|
|
92
|
+
findFirstOrThrow(args?: Record<string, unknown>): Promise<unknown>;
|
|
93
|
+
findUniqueOrThrow(args: Record<string, unknown>): Promise<unknown>;
|
|
94
|
+
create(args: Record<string, unknown>): Promise<unknown>;
|
|
95
|
+
createMany(args: Record<string, unknown>): Promise<unknown[]>;
|
|
96
|
+
update(args: Record<string, unknown>): Promise<unknown>;
|
|
97
|
+
updateMany(args: Record<string, unknown>): Promise<{
|
|
98
|
+
count: number;
|
|
99
|
+
}>;
|
|
100
|
+
delete(args: Record<string, unknown>): Promise<unknown>;
|
|
101
|
+
deleteMany(args: Record<string, unknown>): Promise<{
|
|
102
|
+
count: number;
|
|
103
|
+
}>;
|
|
104
|
+
upsert(args: Record<string, unknown>): Promise<unknown>;
|
|
105
|
+
count(args?: Record<string, unknown>): Promise<number>;
|
|
106
|
+
aggregate(args: Record<string, unknown>): Promise<unknown>;
|
|
107
|
+
groupBy(args: Record<string, unknown>): Promise<unknown[]>;
|
|
108
|
+
buildFindMany(args?: Record<string, unknown>): DeferredQuery<unknown>;
|
|
109
|
+
buildFindFirst(args?: Record<string, unknown>): DeferredQuery<unknown>;
|
|
110
|
+
buildFindUnique(args: Record<string, unknown>): DeferredQuery<unknown>;
|
|
111
|
+
buildFindFirstOrThrow(args?: Record<string, unknown>): DeferredQuery<unknown>;
|
|
112
|
+
buildFindUniqueOrThrow(args: Record<string, unknown>): DeferredQuery<unknown>;
|
|
113
|
+
buildCreate(args: Record<string, unknown>): DeferredQuery<unknown>;
|
|
114
|
+
buildCreateMany(args: Record<string, unknown>): DeferredQuery<unknown[]>;
|
|
115
|
+
buildUpdate(args: Record<string, unknown>): DeferredQuery<unknown>;
|
|
116
|
+
buildUpdateMany(args: Record<string, unknown>): DeferredQuery<{
|
|
117
|
+
count: number;
|
|
118
|
+
}>;
|
|
119
|
+
buildDelete(args: Record<string, unknown>): DeferredQuery<unknown>;
|
|
120
|
+
buildDeleteMany(args: Record<string, unknown>): DeferredQuery<{
|
|
121
|
+
count: number;
|
|
122
|
+
}>;
|
|
123
|
+
buildUpsert(args: Record<string, unknown>): DeferredQuery<unknown>;
|
|
124
|
+
buildCount(args?: Record<string, unknown>): DeferredQuery<number>;
|
|
125
|
+
buildAggregate(args: Record<string, unknown>): DeferredQuery<unknown>;
|
|
126
|
+
buildGroupBy(args: Record<string, unknown>): DeferredQuery<unknown[]>;
|
|
127
|
+
}
|
|
128
|
+
/** A transaction-scoped client handed to a `$transaction(callback)`. */
|
|
129
|
+
export interface CompatTransactionClient {
|
|
130
|
+
table(name: string): CompatQueryInterface;
|
|
131
|
+
}
|
|
132
|
+
/** The minimal `TurbineClient` surface the adapter consumes. */
|
|
133
|
+
export interface CompatTurbineClient extends CompatTransactionClient {
|
|
134
|
+
readonly schema: SchemaMetadata;
|
|
135
|
+
$transaction<R>(fn: (tx: CompatTransactionClient) => Promise<R>, options?: unknown): Promise<R>;
|
|
136
|
+
$transaction(queries: readonly DeferredQuery<unknown>[]): Promise<unknown[]>;
|
|
137
|
+
}
|
|
138
|
+
declare const SQL_FRAGMENT: unique symbol;
|
|
139
|
+
/** A composable SQL fragment, the local stand-in for `Prisma.Sql`. */
|
|
140
|
+
export interface Sql {
|
|
141
|
+
/** Literal string segments; `strings.length === values.length + 1`. */
|
|
142
|
+
readonly strings: readonly string[];
|
|
143
|
+
/** Interpolated values, one between each pair of `strings`. */
|
|
144
|
+
readonly values: readonly unknown[];
|
|
145
|
+
readonly [SQL_FRAGMENT]: true;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* `Prisma`-compatible raw-SQL helpers, a minimal local implementation so
|
|
149
|
+
* migrated `Prisma.sql\`…\`` / `Prisma.join(...)` / `Prisma.raw(...)` calls keep
|
|
150
|
+
* composing. Fragments are flattened at execution time into a single
|
|
151
|
+
* parameterized statement (values become `$N`), so composition is injection-safe
|
|
152
|
+
* by construction.
|
|
153
|
+
*/
|
|
154
|
+
export declare const Prisma: {
|
|
155
|
+
/** Tagged-template fragment: `Prisma.sql\`id = ${id}\``. */
|
|
156
|
+
sql(strings: TemplateStringsArray, ...values: unknown[]): Sql;
|
|
157
|
+
/**
|
|
158
|
+
* Join fragments/values with a separator: `Prisma.join([1, 2, 3])` →
|
|
159
|
+
* `$1, $2, $3`. Each element that is not already a fragment becomes a bound
|
|
160
|
+
* value.
|
|
161
|
+
*/
|
|
162
|
+
join(items: readonly unknown[], separator?: string, prefix?: string, suffix?: string): Sql;
|
|
163
|
+
/**
|
|
164
|
+
* A raw, unparameterized SQL fragment. The string is spliced verbatim, never
|
|
165
|
+
* pass user input here (matches Prisma's `Prisma.raw` contract).
|
|
166
|
+
*/
|
|
167
|
+
raw(sql: string): Sql;
|
|
168
|
+
/** An empty fragment. */
|
|
169
|
+
empty: Sql;
|
|
170
|
+
};
|
|
171
|
+
/** Options for {@link createPrismaCompatClient}. */
|
|
172
|
+
export interface PrismaCompatOptions {
|
|
173
|
+
/**
|
|
174
|
+
* When `true`, every to-many `with` relation lacking an explicit `orderBy` is
|
|
175
|
+
* loaded ordered by the target table's primary key ascending (Prisma's
|
|
176
|
+
* relation rows come back in a stable order). This passes through to Turbine's
|
|
177
|
+
* core `stableRelationOrder` flag, it never re-walks the `with` tree. An
|
|
178
|
+
* explicit per-relation `orderBy` always wins. Default `false`.
|
|
179
|
+
*/
|
|
180
|
+
stablePkOrder?: boolean;
|
|
181
|
+
/**
|
|
182
|
+
* When `true`, thrown {@link TurbineError}s are decorated with a `.code` equal
|
|
183
|
+
* to the nearest Prisma error code (e.g. `P2002` for a unique violation),
|
|
184
|
+
* WITHOUT pretending `instanceof PrismaClientKnownRequestError`. Default
|
|
185
|
+
* `false` (Turbine's own `TURBINE_E0NN` codes are preserved untouched).
|
|
186
|
+
*/
|
|
187
|
+
prismaErrorCodes?: boolean;
|
|
188
|
+
}
|
|
189
|
+
/** Symbol under which a lazy delegate call exposes its batchable plan. */
|
|
190
|
+
export declare const COMPAT_DEFERRED: unique symbol;
|
|
191
|
+
/**
|
|
192
|
+
* The per-model type bundle a consumer supplies to type a model delegate.
|
|
193
|
+
* Populate `Row` (and optionally the arg shapes) from the generated entity
|
|
194
|
+
* types; unspecified members fall back to permissive shapes.
|
|
195
|
+
*/
|
|
196
|
+
export interface PrismaModelTypes {
|
|
197
|
+
Row: object;
|
|
198
|
+
Create?: object;
|
|
199
|
+
Update?: object;
|
|
200
|
+
Where?: object;
|
|
201
|
+
OrderBy?: object;
|
|
202
|
+
Select?: object;
|
|
203
|
+
Include?: object;
|
|
204
|
+
}
|
|
205
|
+
type Args = Record<string, unknown>;
|
|
206
|
+
/** A typed model delegate mirroring Prisma's `db.model.*` surface. */
|
|
207
|
+
export interface PrismaModelDelegate<M extends PrismaModelTypes> {
|
|
208
|
+
findMany(args?: Args): Promise<M['Row'][]>;
|
|
209
|
+
findFirst(args?: Args): Promise<M['Row'] | null>;
|
|
210
|
+
findUnique(args: Args): Promise<M['Row'] | null>;
|
|
211
|
+
findFirstOrThrow(args?: Args): Promise<M['Row']>;
|
|
212
|
+
findUniqueOrThrow(args: Args): Promise<M['Row']>;
|
|
213
|
+
create(args: Args): Promise<M['Row']>;
|
|
214
|
+
createMany(args: Args): Promise<{
|
|
215
|
+
count: number;
|
|
216
|
+
}>;
|
|
217
|
+
update(args: Args): Promise<M['Row']>;
|
|
218
|
+
updateMany(args: Args): Promise<{
|
|
219
|
+
count: number;
|
|
220
|
+
}>;
|
|
221
|
+
delete(args: Args): Promise<M['Row']>;
|
|
222
|
+
deleteMany(args?: Args): Promise<{
|
|
223
|
+
count: number;
|
|
224
|
+
}>;
|
|
225
|
+
upsert(args: Args): Promise<M['Row']>;
|
|
226
|
+
count(args?: Args): Promise<number>;
|
|
227
|
+
aggregate(args: Args): Promise<Record<string, unknown>>;
|
|
228
|
+
groupBy(args: Args): Promise<Record<string, unknown>[]>;
|
|
229
|
+
}
|
|
230
|
+
/** The client-level surface (`$transaction` / raw), added to the model map. */
|
|
231
|
+
export interface PrismaCompatClientBase<S extends Record<string, PrismaModelTypes> = Record<string, PrismaModelTypes>> {
|
|
232
|
+
$transaction<R>(fn: (tx: PrismaCompatTransactionClient<S>) => Promise<R>, options?: PrismaCompatTxOptions): Promise<R>;
|
|
233
|
+
$transaction<P extends readonly PromiseLike<unknown>[]>(promises: readonly [...P]): Promise<{
|
|
234
|
+
[K in keyof P]: Awaited<P[K]>;
|
|
235
|
+
}>;
|
|
236
|
+
$queryRaw<T = unknown>(strings: TemplateStringsArray, ...values: unknown[]): Promise<T[]>;
|
|
237
|
+
$queryRawUnsafe<T = unknown>(sql: string, ...params: unknown[]): Promise<T[]>;
|
|
238
|
+
$executeRaw(strings: TemplateStringsArray, ...values: unknown[]): Promise<number>;
|
|
239
|
+
$executeRawUnsafe(sql: string, ...params: unknown[]): Promise<number>;
|
|
240
|
+
$connect(): Promise<void>;
|
|
241
|
+
$disconnect(): Promise<void>;
|
|
242
|
+
}
|
|
243
|
+
/** Options accepted by the callback form of `$transaction`. */
|
|
244
|
+
export interface PrismaCompatTxOptions {
|
|
245
|
+
isolationLevel?: 'ReadUncommitted' | 'ReadCommitted' | 'RepeatableRead' | 'Serializable';
|
|
246
|
+
timeout?: number;
|
|
247
|
+
maxWait?: number;
|
|
248
|
+
}
|
|
249
|
+
/** The transaction-scoped client handed to a `$transaction(callback)`. */
|
|
250
|
+
export type PrismaCompatTransactionClient<S extends Record<string, PrismaModelTypes> = Record<string, PrismaModelTypes>> = {
|
|
251
|
+
[K in keyof S]: PrismaModelDelegate<S[K]>;
|
|
252
|
+
};
|
|
253
|
+
/**
|
|
254
|
+
* The full typed compat client: a model delegate per Prisma model name, plus the
|
|
255
|
+
* client-level `$transaction` / raw surface. Parameterize `S` with your
|
|
256
|
+
* generated entity types for full autocompletion.
|
|
257
|
+
*/
|
|
258
|
+
export type PrismaCompatClient<S extends Record<string, PrismaModelTypes> = Record<string, PrismaModelTypes>> = {
|
|
259
|
+
[K in keyof S]: PrismaModelDelegate<S[K]>;
|
|
260
|
+
} & {
|
|
261
|
+
[K in keyof S as Uncapitalize<K & string>]: PrismaModelDelegate<S[K]>;
|
|
262
|
+
} & PrismaCompatClientBase<S>;
|
|
263
|
+
/** @internal Names on a compat client that are NOT model delegates. */
|
|
264
|
+
export declare const CLIENT_RESERVED_KEYS: ReadonlySet<string>;
|
|
265
|
+
/**
|
|
266
|
+
* Create a PrismaClient-surface adapter over a {@link TurbineClient}, driven by a
|
|
267
|
+
* {@link PrismaCompatMap} (the `prisma-map.ts` that `turbine
|
|
268
|
+
* migrate-from-prisma` emits).
|
|
269
|
+
*
|
|
270
|
+
* The returned object exposes a delegate per Prisma model name (by the map's
|
|
271
|
+
* keys) plus the client-level `$transaction` / `$queryRaw` / `$executeRaw`
|
|
272
|
+
* surface. Model and field names are translated through the map in both
|
|
273
|
+
* directions; when field names are identity (the `--keep-column-names` pairing)
|
|
274
|
+
* result rekeying is skipped entirely.
|
|
275
|
+
*
|
|
276
|
+
* @typeParam S - Per-model type bundles (from your generated entity types) for
|
|
277
|
+
* full autocompletion. Defaults to a permissive shape.
|
|
278
|
+
* @param client - The TurbineClient (or generated subclass / `turbineHttp` client).
|
|
279
|
+
* @param map - The resolved `PRISMA_MAP`.
|
|
280
|
+
* @param options - {@link PrismaCompatOptions}.
|
|
281
|
+
*/
|
|
282
|
+
export declare function createPrismaCompatClient<S extends Record<string, PrismaModelTypes> = Record<string, PrismaModelTypes>>(client: TurbineClient, map: PrismaCompatMap, options?: PrismaCompatOptions): PrismaCompatClient<S>;
|
|
283
|
+
export {};
|
|
@@ -83,9 +83,10 @@
|
|
|
83
83
|
* ```
|
|
84
84
|
*/
|
|
85
85
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
86
|
-
exports.COMPAT_DEFERRED = exports.Prisma = void 0;
|
|
86
|
+
exports.CLIENT_RESERVED_KEYS = exports.COMPAT_DEFERRED = exports.Prisma = void 0;
|
|
87
87
|
exports.createPrismaCompatClient = createPrismaCompatClient;
|
|
88
88
|
const errors_js_1 = require("./errors.js");
|
|
89
|
+
const warn_registry_js_1 = require("./query/warn-registry.js");
|
|
89
90
|
// ---------------------------------------------------------------------------
|
|
90
91
|
// Prisma.sql-style raw fragments (local, minimal, never imports @prisma/client)
|
|
91
92
|
// ---------------------------------------------------------------------------
|
|
@@ -403,7 +404,7 @@ function modelName(ctx, mm) {
|
|
|
403
404
|
* `where` / `orderBy` are translated first so the cursor step (which may inject
|
|
404
405
|
* a keyset predicate) operates in the translated turbine-field space.
|
|
405
406
|
*/
|
|
406
|
-
function translateReadArgs(ctx, mm, prismaArgs) {
|
|
407
|
+
function translateReadArgs(ctx, mm, prismaArgs, kind) {
|
|
407
408
|
const t = {};
|
|
408
409
|
if (prismaArgs.where !== undefined)
|
|
409
410
|
t.where = translateWhere(ctx, mm, prismaArgs.where);
|
|
@@ -428,9 +429,85 @@ function translateReadArgs(ctx, mm, prismaArgs) {
|
|
|
428
429
|
t.includePii = prismaArgs.includePii;
|
|
429
430
|
if (ctx.options.stablePkOrder)
|
|
430
431
|
t.stableRelationOrder = true;
|
|
432
|
+
// ORDER IS LOAD-BEARING: translateCursor MUST run BEFORE applyImplicitPkOrder.
|
|
433
|
+
// The cursor translation reads `t.orderBy` to decide the seek direction and to
|
|
434
|
+
// validate that a bare inclusive cursor names the sort key; it must see the
|
|
435
|
+
// USER's ordering, never an ordering this layer invented. Swap these two lines
|
|
436
|
+
// and a bare inclusive cursor on a non-PK field silently compiles against the
|
|
437
|
+
// injected primary-key order instead of throwing. See the guard test
|
|
438
|
+
// "cursor decisions are made against the user's own orderBy".
|
|
431
439
|
translateCursor(ctx, mm, prismaArgs, t);
|
|
440
|
+
applyImplicitPkOrder(ctx, mm, t, kind);
|
|
432
441
|
return t;
|
|
433
442
|
}
|
|
443
|
+
/**
|
|
444
|
+
* Prisma appends an implicit `ORDER BY <primary key> ASC` to a paginated read;
|
|
445
|
+
* Turbine emits a bare `LIMIT`. An unordered `LIMIT` is not stable in Postgres:
|
|
446
|
+
* once the heap changes underneath it (autovacuum, a heap rewrite, a plan flip)
|
|
447
|
+
* successive pages can repeat a row or skip one entirely, and the unordered
|
|
448
|
+
* plan is also the slower one (a seq scan instead of a primary-key index scan).
|
|
449
|
+
*
|
|
450
|
+
* So a top-level compat read whose page is not already ordered gets the model's
|
|
451
|
+
* primary key ascending, matching Prisma. An explicit `orderBy` always wins, a
|
|
452
|
+
* composite primary key orders on every column in declaration order, and a model
|
|
453
|
+
* with no primary key is left exactly as it was (nothing is invented).
|
|
454
|
+
*
|
|
455
|
+
* ## Which reads this covers
|
|
456
|
+
*
|
|
457
|
+
* - `findMany` only when it paginates (`take` / `skip`): without a limit there
|
|
458
|
+
* is no page to be unstable.
|
|
459
|
+
* - `findFirst` / `findFirstOrThrow` ALWAYS: core compiles both to a bare
|
|
460
|
+
* `LIMIT 1`, which is the single most common nondeterministic read shape in
|
|
461
|
+
* Prisma-shaped code ("give me any one row" quietly becoming "give me
|
|
462
|
+
* whichever row the heap hands back today").
|
|
463
|
+
* - `findUnique` / `findUniqueOrThrow` NEVER: they are addressed by a unique
|
|
464
|
+
* key, so at most one row matches and an ordering is pure overhead.
|
|
465
|
+
* - Nested relation includes (`include: { posts: { take: 3 } }`) are NOT
|
|
466
|
+
* touched here, deliberately. Relation ordering is a core concern (core owns
|
|
467
|
+
* the target table's metadata and the subquery shape), and core already
|
|
468
|
+
* exposes it as `stableRelationOrder`, which this layer forwards from the
|
|
469
|
+
* {@link PrismaCompatOptions.stablePkOrder} option. Re-walking the `with` tree
|
|
470
|
+
* here would duplicate that mechanism and fight it inside the subquery.
|
|
471
|
+
*
|
|
472
|
+
* ## Mirrored exclusions (keep in sync with core `isUnorderedPage`)
|
|
473
|
+
*
|
|
474
|
+
* Core's `QueryInterface.isUnorderedPage` (query/builder.ts) excludes both
|
|
475
|
+
* `distinct` and `cursor`. This layer mirrors them one at a time rather than
|
|
476
|
+
* copying the predicate, because the two layers do NOT have the same default:
|
|
477
|
+
*
|
|
478
|
+
* - `distinct`: EXCLUDED, same as core, and this one is a hard requirement.
|
|
479
|
+
* Adding an `orderBy` flips core's `DISTINCT ON` out of its plain branch into
|
|
480
|
+
* the two-level derived-table rewrite whose OUTER `ORDER BY` can only see the
|
|
481
|
+
* projected columns, so `distinct` + `select` + `take` would fail at runtime
|
|
482
|
+
* with `column "id" does not exist`. Injecting also lifts the `LIMIT` outside
|
|
483
|
+
* the `DISTINCT ON`, so the inner scan stops being limited. `distinct` reads
|
|
484
|
+
* keep the exact SQL they emitted before this ordering existed.
|
|
485
|
+
* - `cursor`: NOT excluded here, unlike core. Core is a general-purpose layer
|
|
486
|
+
* whose cursor callers chose their own ordering; this layer's contract is
|
|
487
|
+
* Prisma parity, and Prisma pairs cursor pagination with the same implicit
|
|
488
|
+
* primary-key order. The injection is safe because it runs strictly AFTER
|
|
489
|
+
* {@link translateCursor}: the cursor comparison direction was already
|
|
490
|
+
* resolved from the user's own `orderBy` (ascending by default, matching an
|
|
491
|
+
* ascending primary key), and the bare-inclusive-cursor branch has already
|
|
492
|
+
* set or demanded an `orderBy`, so only the exclusive `cursor` + `skip`
|
|
493
|
+
* branch reaches here. It gains an `ORDER BY <pk> ASC` and nothing else.
|
|
494
|
+
*/
|
|
495
|
+
function applyImplicitPkOrder(ctx, mm, t, kind) {
|
|
496
|
+
if (kind === 'unique')
|
|
497
|
+
return;
|
|
498
|
+
if (t.orderBy !== undefined)
|
|
499
|
+
return;
|
|
500
|
+
if (t.distinct !== undefined)
|
|
501
|
+
return;
|
|
502
|
+
if (kind === 'many' && t.limit === undefined && t.offset === undefined)
|
|
503
|
+
return;
|
|
504
|
+
const meta = ctx.schema.tables[mm.table];
|
|
505
|
+
const pk = meta?.primaryKey ?? [];
|
|
506
|
+
if (pk.length === 0)
|
|
507
|
+
return;
|
|
508
|
+
const fields = pk.map((col) => meta?.reverseColumnMap?.[col] ?? col);
|
|
509
|
+
t.orderBy = fields.length === 1 ? { [fields[0]]: 'asc' } : fields.map((f) => ({ [f]: 'asc' }));
|
|
510
|
+
}
|
|
434
511
|
/** turbine field names of the model's single-column primary key, if any. */
|
|
435
512
|
function singleColumnPkField(ctx, mm) {
|
|
436
513
|
const pk = ctx.schema.tables[mm.table]?.primaryKey;
|
|
@@ -1043,11 +1120,11 @@ function makeDelegate(ctx, mm, getQI, runInTx) {
|
|
|
1043
1120
|
return args;
|
|
1044
1121
|
};
|
|
1045
1122
|
return {
|
|
1046
|
-
findMany: (args = {}) => defer(() => translateReadArgs(ctx, mm, args), (qi, t) => qi.findMany(t).then((r) => reshapeRows(ctx, mm, r)), { build: (qi, t) => qi.buildFindMany(t), reshape: (raw) => reshapeRows(ctx, mm, raw) }),
|
|
1047
|
-
findFirst: (args = {}) => defer(() => translateReadArgs(ctx, mm, args), (qi, t) => qi.findFirst(t).then((r) => reshapeRowOrNull(ctx, mm, r)), { build: (qi, t) => qi.buildFindFirst(t), reshape: (raw) => reshapeRowOrNull(ctx, mm, raw) }),
|
|
1048
|
-
findUnique: (args) => defer(() => translateReadArgs(ctx, mm, requireWhere(args, 'findUnique')), (qi, t) => qi.findUnique(t).then((r) => reshapeRowOrNull(ctx, mm, r)), { build: (qi, t) => qi.buildFindUnique(t), reshape: (raw) => reshapeRowOrNull(ctx, mm, raw) }),
|
|
1049
|
-
findFirstOrThrow: (args = {}) => defer(() => translateReadArgs(ctx, mm, args), (qi, t) => qi.findFirstOrThrow(t).then((r) => reshapeRow(ctx, mm, r)), { build: (qi, t) => qi.buildFindFirstOrThrow(t), reshape: (raw) => reshapeRow(ctx, mm, raw) }),
|
|
1050
|
-
findUniqueOrThrow: (args) => defer(() => translateReadArgs(ctx, mm, requireWhere(args, 'findUniqueOrThrow')), (qi, t) => qi.findUniqueOrThrow(t).then((r) => reshapeRow(ctx, mm, r)), { build: (qi, t) => qi.buildFindUniqueOrThrow(t), reshape: (raw) => reshapeRow(ctx, mm, raw) }),
|
|
1123
|
+
findMany: (args = {}) => defer(() => translateReadArgs(ctx, mm, args, 'many'), (qi, t) => qi.findMany(t).then((r) => reshapeRows(ctx, mm, r)), { build: (qi, t) => qi.buildFindMany(t), reshape: (raw) => reshapeRows(ctx, mm, raw) }),
|
|
1124
|
+
findFirst: (args = {}) => defer(() => translateReadArgs(ctx, mm, args, 'first'), (qi, t) => qi.findFirst(t).then((r) => reshapeRowOrNull(ctx, mm, r)), { build: (qi, t) => qi.buildFindFirst(t), reshape: (raw) => reshapeRowOrNull(ctx, mm, raw) }),
|
|
1125
|
+
findUnique: (args) => defer(() => translateReadArgs(ctx, mm, requireWhere(args, 'findUnique'), 'unique'), (qi, t) => qi.findUnique(t).then((r) => reshapeRowOrNull(ctx, mm, r)), { build: (qi, t) => qi.buildFindUnique(t), reshape: (raw) => reshapeRowOrNull(ctx, mm, raw) }),
|
|
1126
|
+
findFirstOrThrow: (args = {}) => defer(() => translateReadArgs(ctx, mm, args, 'first'), (qi, t) => qi.findFirstOrThrow(t).then((r) => reshapeRow(ctx, mm, r)), { build: (qi, t) => qi.buildFindFirstOrThrow(t), reshape: (raw) => reshapeRow(ctx, mm, raw) }),
|
|
1127
|
+
findUniqueOrThrow: (args) => defer(() => translateReadArgs(ctx, mm, requireWhere(args, 'findUniqueOrThrow'), 'unique'), (qi, t) => qi.findUniqueOrThrow(t).then((r) => reshapeRow(ctx, mm, r)), { build: (qi, t) => qi.buildFindUniqueOrThrow(t), reshape: (raw) => reshapeRow(ctx, mm, raw) }),
|
|
1051
1128
|
create: (args) => defer(() => {
|
|
1052
1129
|
const t = { data: translateWriteData(ctx, mm, applyCreateDefaults(mm, args.data)) };
|
|
1053
1130
|
if (typeof args.timeout === 'number')
|
|
@@ -1185,6 +1262,79 @@ function flattenTemplate(strings, values, ph) {
|
|
|
1185
1262
|
// ---------------------------------------------------------------------------
|
|
1186
1263
|
// createPrismaCompatClient
|
|
1187
1264
|
// ---------------------------------------------------------------------------
|
|
1265
|
+
/**
|
|
1266
|
+
* The client-level members every compat client carries, as a total map over
|
|
1267
|
+
* {@link PrismaCompatClientBase}. Declaring it `Record<keyof …, true>` makes the
|
|
1268
|
+
* compiler keep it in sync: adding a `$method` to the interface without listing
|
|
1269
|
+
* it here is a type error, and listing one that does not exist is too. The
|
|
1270
|
+
* derived key set is what {@link junctionModels} refuses to shadow.
|
|
1271
|
+
*/
|
|
1272
|
+
const CLIENT_RESERVED_KEY_MAP = {
|
|
1273
|
+
$transaction: true,
|
|
1274
|
+
$queryRaw: true,
|
|
1275
|
+
$queryRawUnsafe: true,
|
|
1276
|
+
$executeRaw: true,
|
|
1277
|
+
$executeRawUnsafe: true,
|
|
1278
|
+
$connect: true,
|
|
1279
|
+
$disconnect: true,
|
|
1280
|
+
};
|
|
1281
|
+
/** @internal Names on a compat client that are NOT model delegates. */
|
|
1282
|
+
exports.CLIENT_RESERVED_KEYS = new Set(Object.keys(CLIENT_RESERVED_KEY_MAP));
|
|
1283
|
+
/** Warn-once namespace for junction accessors dropped on a name collision. */
|
|
1284
|
+
const JUNCTION_WARN_NS = 'prismaCompatJunction';
|
|
1285
|
+
/**
|
|
1286
|
+
* Synthesize a model map per implicit many-to-many JUNCTION table, keyed by the
|
|
1287
|
+
* junction's raw table name (`_ReportToSchedule`, `post_tags`, ...).
|
|
1288
|
+
*
|
|
1289
|
+
* Prisma's schema has no model for an implicit junction, so `PRISMA_MAP` has no
|
|
1290
|
+
* entry for it and the adapter would expose no accessor: a compat caller had no
|
|
1291
|
+
* way to touch link rows at all, not even inside a `$transaction`. The junction
|
|
1292
|
+
* tables ARE in the Turbine schema metadata (`through.table` on every
|
|
1293
|
+
* many-to-many relation), so the delegate is a plain identity mapping over the
|
|
1294
|
+
* real table: no field renames, no relations, no compound uniques.
|
|
1295
|
+
*
|
|
1296
|
+
* A junction accessor is an ESCAPE HATCH, never worth breaking a real member of
|
|
1297
|
+
* the client for, so it FAILS SAFE: the name is skipped (leaving the existing
|
|
1298
|
+
* member intact) whenever it collides with anything the client already exposes,
|
|
1299
|
+
* which is a Prisma model name, a table some model maps to, a model's
|
|
1300
|
+
* lowercased Prisma-property alias (`compat.user` for `model User`), or one of
|
|
1301
|
+
* the client-level {@link CLIENT_RESERVED_KEYS} (a junction table named
|
|
1302
|
+
* `$transaction` must not turn the transaction surface into a delegate).
|
|
1303
|
+
*
|
|
1304
|
+
* Every skip except the mapped-table one loses a capability the caller might
|
|
1305
|
+
* expect, so those warn once per junction name in dev; a table that a model
|
|
1306
|
+
* already maps to is not a loss (the model IS that table's accessor).
|
|
1307
|
+
*/
|
|
1308
|
+
function junctionModels(ctx, map, tableToModel) {
|
|
1309
|
+
const out = [];
|
|
1310
|
+
const seen = new Set(exports.CLIENT_RESERVED_KEYS);
|
|
1311
|
+
for (const model of Object.keys(map.models)) {
|
|
1312
|
+
seen.add(model);
|
|
1313
|
+
const alias = prismaPropertyAlias(model);
|
|
1314
|
+
if (alias)
|
|
1315
|
+
seen.add(alias);
|
|
1316
|
+
}
|
|
1317
|
+
for (const table of Object.values(ctx.schema.tables)) {
|
|
1318
|
+
for (const rel of Object.values(table.relations ?? {})) {
|
|
1319
|
+
if (rel.type !== 'manyToMany')
|
|
1320
|
+
continue;
|
|
1321
|
+
const name = rel.through?.table;
|
|
1322
|
+
if (!name || !ctx.schema.tables[name] || tableToModel.has(name))
|
|
1323
|
+
continue;
|
|
1324
|
+
if (seen.has(name)) {
|
|
1325
|
+
if (process.env.NODE_ENV !== 'production' && (0, warn_registry_js_1.shouldWarnOnce)(JUNCTION_WARN_NS, name)) {
|
|
1326
|
+
console.warn(`[turbine] prisma-compat: the many-to-many junction table "${name}" collides with an ` +
|
|
1327
|
+
'existing client member of the same name, so no junction accessor was created for it. ' +
|
|
1328
|
+
'Reach its rows through $queryRaw / $executeRaw, or through the owning model relation.');
|
|
1329
|
+
}
|
|
1330
|
+
continue;
|
|
1331
|
+
}
|
|
1332
|
+
seen.add(name);
|
|
1333
|
+
out.push([name, { table: name, accessor: name, fields: {}, relations: {}, compoundUniques: {} }]);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
return out;
|
|
1337
|
+
}
|
|
1188
1338
|
/**
|
|
1189
1339
|
* Create a PrismaClient-surface adapter over a {@link TurbineClient}, driven by a
|
|
1190
1340
|
* {@link PrismaCompatMap} (the `prisma-map.ts` that `turbine
|
|
@@ -1220,9 +1370,17 @@ function createPrismaCompatClient(client, map, options = {}) {
|
|
|
1220
1370
|
prismaErrorCodes: options.prismaErrorCodes ?? false,
|
|
1221
1371
|
},
|
|
1222
1372
|
};
|
|
1373
|
+
// Every model the client exposes: the map's Prisma models, plus the implicit
|
|
1374
|
+
// many-to-many junction tables (see below). Both delegate loops (base and
|
|
1375
|
+
// transaction-scoped) build from this ONE list, so an accessor can never
|
|
1376
|
+
// exist on the client but be missing inside `$transaction`.
|
|
1377
|
+
const delegateModels = [
|
|
1378
|
+
...Object.entries(map.models),
|
|
1379
|
+
...junctionModels(ctx, map, tableToModel),
|
|
1380
|
+
];
|
|
1223
1381
|
// Delegates bound to the base client (each call reads db.table(...) lazily).
|
|
1224
1382
|
const delegates = new Map();
|
|
1225
|
-
for (const [prismaModel, mm] of
|
|
1383
|
+
for (const [prismaModel, mm] of delegateModels) {
|
|
1226
1384
|
delegates.set(prismaModel, makeDelegate(ctx, mm, () => db.table(mm.table), (fn) => db.$transaction((tx) => fn((n) => tx.table(n)))));
|
|
1227
1385
|
}
|
|
1228
1386
|
const ph = placeholderOf(db);
|
|
@@ -1275,7 +1433,7 @@ function createPrismaCompatClient(client, map, options = {}) {
|
|
|
1275
1433
|
const fn = arg;
|
|
1276
1434
|
return db.$transaction((tx) => {
|
|
1277
1435
|
const txDelegates = {};
|
|
1278
|
-
for (const [prismaModel, mm] of
|
|
1436
|
+
for (const [prismaModel, mm] of delegateModels) {
|
|
1279
1437
|
txDelegates[prismaModel] = makeDelegate(ctx, mm, () => tx.table(mm.table), (fn) => fn((n) => tx.table(n)));
|
|
1280
1438
|
const alias = prismaPropertyAlias(prismaModel);
|
|
1281
1439
|
if (alias && !(alias in map.models) && !(alias in txDelegates)) {
|