turbine-orm 0.81.0 → 0.90.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 +26 -25
- package/dist/checkout.d.ts +27 -2
- package/dist/checkout.js +75 -4
- package/dist/cjs/checkout.d.ts +27 -2
- package/dist/cjs/checkout.js +76 -3
- package/dist/cjs/cli/code-first-tags.d.ts +62 -0
- package/dist/cjs/cli/code-first-tags.js +124 -0
- package/dist/cjs/cli/compile-query.js +9 -0
- package/dist/cjs/cli/config.d.ts +35 -6
- package/dist/cjs/cli/config.js +17 -1
- package/dist/cjs/cli/destructive.d.ts +7 -4
- package/dist/cjs/cli/destructive.js +224 -20
- package/dist/cjs/cli/error-catalog.js +36 -0
- package/dist/cjs/cli/error-text.d.ts +15 -0
- package/dist/cjs/cli/error-text.js +40 -0
- package/dist/cjs/cli/index.d.ts +62 -4
- package/dist/cjs/cli/index.js +568 -108
- package/dist/cjs/cli/local-server-guard.d.ts +53 -0
- package/dist/cjs/cli/local-server-guard.js +119 -0
- package/dist/cjs/cli/local-server-output.d.ts +41 -0
- package/dist/cjs/cli/local-server-output.js +103 -0
- package/dist/cjs/cli/mcp.d.ts +8 -0
- package/dist/cjs/cli/mcp.js +121 -32
- package/dist/cjs/cli/migrate.d.ts +113 -5
- package/dist/cjs/cli/migrate.js +270 -21
- package/dist/cjs/cli/observe.d.ts +6 -1
- package/dist/cjs/cli/observe.js +85 -55
- package/dist/cjs/cli/pii-predicate-guard.d.ts +43 -23
- package/dist/cjs/cli/pii-predicate-guard.js +98 -37
- package/dist/cjs/cli/prisma-resolve.js +17 -13
- package/dist/cjs/cli/prisma-schema.js +12 -4
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/cli/studio.d.ts +14 -2
- package/dist/cjs/cli/studio.js +244 -69
- package/dist/cjs/cli/ui.d.ts +1 -1
- package/dist/cjs/client.d.ts +558 -85
- package/dist/cjs/client.js +1270 -395
- package/dist/cjs/connection-guard.d.ts +13 -0
- package/dist/cjs/connection-guard.js +28 -0
- package/dist/cjs/connection-url.d.ts +51 -16
- package/dist/cjs/connection-url.js +67 -34
- package/dist/cjs/diagnostics.d.ts +81 -0
- package/dist/cjs/diagnostics.js +147 -0
- package/dist/cjs/dialect.d.ts +449 -20
- package/dist/cjs/dialect.js +117 -76
- package/dist/cjs/engine-config.d.ts +7 -6
- package/dist/cjs/errors.d.ts +126 -6
- package/dist/cjs/errors.js +256 -13
- package/dist/cjs/generate.d.ts +57 -7
- package/dist/cjs/generate.js +635 -279
- package/dist/cjs/global-filters-config.d.ts +59 -0
- package/dist/cjs/global-filters-config.js +367 -0
- package/dist/cjs/index.d.ts +9 -4
- package/dist/cjs/index.js +10 -2
- package/dist/cjs/introspect.d.ts +31 -6
- package/dist/cjs/introspect.js +108 -50
- package/dist/cjs/log-codes.d.ts +12 -0
- package/dist/cjs/log-codes.js +77 -0
- package/dist/cjs/logger.d.ts +87 -0
- package/dist/cjs/logger.js +98 -0
- package/dist/cjs/mssql.d.ts +8 -5
- package/dist/cjs/mssql.js +352 -65
- package/dist/cjs/mysql.d.ts +3 -5
- package/dist/cjs/mysql.js +420 -70
- package/dist/cjs/nested-write.d.ts +3 -0
- package/dist/cjs/nested-write.js +179 -114
- package/dist/cjs/pipeline-submittable.d.ts +12 -0
- package/dist/cjs/pipeline-submittable.js +118 -44
- package/dist/cjs/pipeline.js +21 -26
- package/dist/cjs/powdb-introspect.d.ts +5 -2
- package/dist/cjs/powdb-introspect.js +20 -15
- package/dist/cjs/powdb-shared.d.ts +36 -5
- package/dist/cjs/powdb-shared.js +67 -8
- package/dist/cjs/powdb.d.ts +4 -0
- package/dist/cjs/powdb.js +183 -51
- package/dist/cjs/powql.d.ts +394 -51
- package/dist/cjs/powql.js +1829 -284
- package/dist/cjs/prisma-compat.d.ts +23 -15
- package/dist/cjs/prisma-compat.js +507 -372
- package/dist/cjs/query/aggregates.d.ts +17 -1
- package/dist/cjs/query/aggregates.js +261 -29
- package/dist/cjs/query/batched-loader.d.ts +25 -6
- package/dist/cjs/query/batched-loader.js +103 -20
- package/dist/cjs/query/builder.d.ts +325 -184
- package/dist/cjs/query/builder.js +1452 -490
- package/dist/cjs/query/column-ref.d.ts +61 -0
- package/dist/cjs/query/column-ref.js +73 -0
- package/dist/cjs/query/compound-unique.d.ts +21 -0
- package/dist/cjs/query/compound-unique.js +98 -18
- package/dist/cjs/query/deferred.d.ts +53 -16
- package/dist/cjs/query/expr.d.ts +217 -0
- package/dist/cjs/query/expr.js +859 -0
- package/dist/cjs/query/filters.d.ts +155 -53
- package/dist/cjs/query/filters.js +613 -76
- package/dist/cjs/query/index.d.ts +2 -0
- package/dist/cjs/query/index.js +7 -1
- package/dist/cjs/query/json-value.d.ts +43 -0
- package/dist/cjs/query/json-value.js +59 -0
- package/dist/cjs/query/normalize-args.d.ts +87 -0
- package/dist/cjs/query/normalize-args.js +527 -0
- package/dist/cjs/query/option-surface.d.ts +16 -4
- package/dist/cjs/query/option-surface.js +57 -14
- package/dist/cjs/query/relation-names.d.ts +14 -0
- package/dist/cjs/query/relation-names.js +77 -18
- package/dist/cjs/query/relations.d.ts +30 -3
- package/dist/cjs/query/relations.js +219 -63
- package/dist/cjs/query/row-lock.d.ts +49 -0
- package/dist/cjs/query/row-lock.js +138 -0
- package/dist/cjs/query/sql-comment.d.ts +161 -0
- package/dist/cjs/query/sql-comment.js +381 -0
- package/dist/cjs/query/stream-idle.d.ts +132 -0
- package/dist/cjs/query/stream-idle.js +212 -0
- package/dist/cjs/query/table-client.d.ts +101 -0
- package/dist/cjs/query/table-client.js +74 -0
- package/dist/cjs/query/types.d.ts +677 -298
- package/dist/cjs/query/utils.d.ts +105 -6
- package/dist/cjs/query/utils.js +426 -19
- package/dist/cjs/query/warn-registry.d.ts +18 -0
- package/dist/cjs/query/warn-registry.js +18 -0
- package/dist/cjs/query/where-compile.d.ts +138 -14
- package/dist/cjs/query/where-compile.js +513 -29
- package/dist/cjs/query/where.d.ts +159 -59
- package/dist/cjs/query/where.js +716 -246
- package/dist/cjs/query/writes.d.ts +112 -50
- package/dist/cjs/query/writes.js +1498 -200
- package/dist/cjs/query-events.d.ts +4 -2
- package/dist/cjs/query-events.js +3 -1
- package/dist/cjs/realtime.d.ts +7 -3
- package/dist/cjs/realtime.js +13 -2
- package/dist/cjs/schema-builder.d.ts +55 -4
- package/dist/cjs/schema-builder.js +382 -18
- package/dist/cjs/schema-metadata.d.ts +21 -1
- package/dist/cjs/schema-metadata.js +29 -7
- package/dist/cjs/schema-sql.d.ts +75 -5
- package/dist/cjs/schema-sql.js +497 -45
- package/dist/cjs/schema.d.ts +26 -5
- package/dist/cjs/schema.js +110 -13
- package/dist/cjs/sql-placeholders.d.ts +56 -0
- package/dist/cjs/sql-placeholders.js +204 -0
- package/dist/cjs/sqlite.d.ts +75 -20
- package/dist/cjs/sqlite.js +593 -95
- package/dist/cjs/supplied-pool-types.d.ts +48 -0
- package/dist/cjs/supplied-pool-types.js +256 -0
- package/dist/cjs/transaction-control.d.ts +51 -0
- package/dist/cjs/transaction-control.js +316 -0
- package/dist/cjs/transaction-state.d.ts +342 -0
- package/dist/cjs/transaction-state.js +707 -0
- package/dist/cjs/typed-sql.d.ts +37 -3
- package/dist/cjs/typed-sql.js +102 -8
- package/dist/cli/code-first-tags.d.ts +62 -0
- package/dist/cli/code-first-tags.js +120 -0
- package/dist/cli/compile-query.js +9 -0
- package/dist/cli/config.d.ts +35 -6
- package/dist/cli/config.js +16 -1
- package/dist/cli/destructive.d.ts +7 -4
- package/dist/cli/destructive.js +224 -20
- package/dist/cli/error-catalog.js +36 -0
- package/dist/cli/error-text.d.ts +15 -0
- package/dist/cli/error-text.js +37 -0
- package/dist/cli/index.d.ts +62 -4
- package/dist/cli/index.js +569 -112
- package/dist/cli/local-server-guard.d.ts +53 -0
- package/dist/cli/local-server-guard.js +113 -0
- package/dist/cli/local-server-output.d.ts +41 -0
- package/dist/cli/local-server-output.js +98 -0
- package/dist/cli/mcp.d.ts +8 -0
- package/dist/cli/mcp.js +121 -34
- package/dist/cli/migrate.d.ts +113 -5
- package/dist/cli/migrate.js +261 -21
- package/dist/cli/observe.d.ts +6 -1
- package/dist/cli/observe.js +84 -54
- package/dist/cli/pii-predicate-guard.d.ts +43 -23
- package/dist/cli/pii-predicate-guard.js +100 -35
- package/dist/cli/prisma-resolve.js +15 -11
- package/dist/cli/prisma-schema.js +12 -4
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/cli/studio.d.ts +14 -2
- package/dist/cli/studio.js +246 -71
- package/dist/cli/ui.d.ts +1 -1
- package/dist/client.d.ts +558 -85
- package/dist/client.js +1275 -400
- package/dist/connection-guard.d.ts +13 -0
- package/dist/connection-guard.js +27 -0
- package/dist/connection-url.d.ts +51 -16
- package/dist/connection-url.js +63 -34
- package/dist/diagnostics.d.ts +81 -0
- package/dist/diagnostics.js +144 -0
- package/dist/dialect.d.ts +449 -20
- package/dist/dialect.js +115 -43
- package/dist/engine-config.d.ts +7 -6
- package/dist/errors.d.ts +126 -6
- package/dist/errors.js +252 -12
- package/dist/generate.d.ts +57 -7
- package/dist/generate.js +635 -281
- package/dist/global-filters-config.d.ts +59 -0
- package/dist/global-filters-config.js +361 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.js +4 -2
- package/dist/introspect.d.ts +31 -6
- package/dist/introspect.js +106 -50
- package/dist/log-codes.d.ts +12 -0
- package/dist/log-codes.js +74 -0
- package/dist/logger.d.ts +87 -0
- package/dist/logger.js +93 -0
- package/dist/mssql.d.ts +8 -5
- package/dist/mssql.js +354 -67
- package/dist/mysql.d.ts +3 -5
- package/dist/mysql.js +423 -73
- package/dist/nested-write.d.ts +3 -0
- package/dist/nested-write.js +177 -112
- package/dist/pipeline-submittable.d.ts +12 -0
- package/dist/pipeline-submittable.js +117 -44
- package/dist/pipeline.js +21 -26
- package/dist/powdb-introspect.d.ts +5 -2
- package/dist/powdb-introspect.js +21 -16
- package/dist/powdb-shared.d.ts +36 -5
- package/dist/powdb-shared.js +65 -8
- package/dist/powdb.d.ts +4 -0
- package/dist/powdb.js +183 -51
- package/dist/powql.d.ts +394 -51
- package/dist/powql.js +1836 -291
- package/dist/prisma-compat.d.ts +23 -15
- package/dist/prisma-compat.js +508 -373
- package/dist/query/aggregates.d.ts +17 -1
- package/dist/query/aggregates.js +263 -32
- package/dist/query/batched-loader.d.ts +25 -6
- package/dist/query/batched-loader.js +101 -21
- package/dist/query/builder.d.ts +325 -184
- package/dist/query/builder.js +1458 -495
- package/dist/query/column-ref.d.ts +61 -0
- package/dist/query/column-ref.js +66 -0
- package/dist/query/compound-unique.d.ts +21 -0
- package/dist/query/compound-unique.js +97 -19
- package/dist/query/deferred.d.ts +53 -16
- package/dist/query/expr.d.ts +217 -0
- package/dist/query/expr.js +848 -0
- package/dist/query/filters.d.ts +155 -53
- package/dist/query/filters.js +597 -72
- package/dist/query/index.d.ts +2 -0
- package/dist/query/index.js +4 -0
- package/dist/query/json-value.d.ts +43 -0
- package/dist/query/json-value.js +54 -0
- package/dist/query/normalize-args.d.ts +87 -0
- package/dist/query/normalize-args.js +517 -0
- package/dist/query/option-surface.d.ts +16 -4
- package/dist/query/option-surface.js +56 -13
- package/dist/query/relation-names.d.ts +14 -0
- package/dist/query/relation-names.js +77 -19
- package/dist/query/relations.d.ts +30 -3
- package/dist/query/relations.js +220 -67
- package/dist/query/row-lock.d.ts +49 -0
- package/dist/query/row-lock.js +131 -0
- package/dist/query/sql-comment.d.ts +161 -0
- package/dist/query/sql-comment.js +374 -0
- package/dist/query/stream-idle.d.ts +132 -0
- package/dist/query/stream-idle.js +203 -0
- package/dist/query/table-client.d.ts +101 -0
- package/dist/query/table-client.js +70 -0
- package/dist/query/types.d.ts +677 -298
- package/dist/query/utils.d.ts +105 -6
- package/dist/query/utils.js +414 -18
- package/dist/query/warn-registry.d.ts +18 -0
- package/dist/query/warn-registry.js +18 -0
- package/dist/query/where-compile.d.ts +138 -14
- package/dist/query/where-compile.js +508 -31
- package/dist/query/where.d.ts +159 -59
- package/dist/query/where.js +711 -248
- package/dist/query/writes.d.ts +112 -50
- package/dist/query/writes.js +1496 -202
- package/dist/query-events.d.ts +4 -2
- package/dist/query-events.js +3 -1
- package/dist/realtime.d.ts +7 -3
- package/dist/realtime.js +13 -2
- package/dist/schema-builder.d.ts +55 -4
- package/dist/schema-builder.js +381 -18
- package/dist/schema-metadata.d.ts +21 -1
- package/dist/schema-metadata.js +28 -7
- package/dist/schema-sql.d.ts +75 -5
- package/dist/schema-sql.js +494 -44
- package/dist/schema.d.ts +26 -5
- package/dist/schema.js +109 -13
- package/dist/sql-placeholders.d.ts +56 -0
- package/dist/sql-placeholders.js +200 -0
- package/dist/sqlite.d.ts +75 -20
- package/dist/sqlite.js +597 -99
- package/dist/supplied-pool-types.d.ts +48 -0
- package/dist/supplied-pool-types.js +247 -0
- package/dist/transaction-control.d.ts +51 -0
- package/dist/transaction-control.js +311 -0
- package/dist/transaction-state.d.ts +342 -0
- package/dist/transaction-state.js +696 -0
- package/dist/typed-sql.d.ts +37 -3
- package/dist/typed-sql.js +100 -8
- package/package.json +7 -4
- package/skills/turbine-orm/SKILL.md +56 -6
|
@@ -68,16 +68,22 @@
|
|
|
68
68
|
* - Fluent relation chaining (`prisma.user.findUnique().posts()`).
|
|
69
69
|
* - Accelerate / Pulse / driver-adapter preview features, the Mongo API, and the
|
|
70
70
|
* `prisma migrate`/`db` CLI family (Turbine ships its own migrations).
|
|
71
|
-
* - **
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* `
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* - **
|
|
80
|
-
*
|
|
71
|
+
* - **Negative `take`** (take-from-end) throws: Turbine has no reverse-take.
|
|
72
|
+
* A **`cursor` on a nested relation include** throws: `with` has no cursor
|
|
73
|
+
* (`take` / `skip` there page each parent's rows, as in Prisma).
|
|
74
|
+
* - **A filtered relation count** (`_count: { select: { posts: { where } } }`)
|
|
75
|
+
* and **`distinct` on a nested include** throw `UnsupportedFeatureError`:
|
|
76
|
+
* core counts and includes a relation whole, so honoring either would mean
|
|
77
|
+
* returning a wrong number or duplicate rows. The message names the
|
|
78
|
+
* alternative (a `count` / `findMany` on the related model).
|
|
79
|
+
* - **Nothing is accepted and dropped.** Every option on an included relation
|
|
80
|
+
* and every key under `_count` is translated, refused by name (the two
|
|
81
|
+
* above, `cursor`), or refused as unknown (`ValidationError`, with a
|
|
82
|
+
* suggestion): a misspelled `wher`, a Turbine spelling like `limit`, a
|
|
83
|
+
* relation `_count` does not know. The same holds for `$transaction` options
|
|
84
|
+
* (`maxWait` is accepted, warned about once, and not applied: the pool's
|
|
85
|
+
* `connectionTimeoutMs` bounds the wait) and for the keys of a nested write's
|
|
86
|
+
* `{ where, data }` / `{ where, create, update }` payloads.
|
|
81
87
|
* - **`limit` on `updateMany` / `deleteMany`** (Prisma 6.7+) throws. Turbine has
|
|
82
88
|
* no row-bounded mass mutation, and dropping a SAFETY BOUND with a warning
|
|
83
89
|
* would turn "change at most 10 rows" into "change every matching row".
|
|
@@ -88,9 +94,10 @@
|
|
|
88
94
|
* either (or any `include` on a write) THROWS, since a write returns its own
|
|
89
95
|
* row only. `omit` on READS is translated to core's own `omit`, see
|
|
90
96
|
* {@link translateOmit}.
|
|
91
|
-
* - **`
|
|
92
|
-
*
|
|
93
|
-
*
|
|
97
|
+
* - **`take` / `skip` / `cursor` on `count` and `aggregate`** throw: Prisma
|
|
98
|
+
* computes those over one page of rows and Turbine has no paged count. An
|
|
99
|
+
* `orderBy` alone changes nothing there and is accepted. `count({ select })`
|
|
100
|
+
* returns Prisma's per-field object (`{ _all, field }`).
|
|
94
101
|
*
|
|
95
102
|
* ## Type dependencies (0.41.0)
|
|
96
103
|
*
|
|
@@ -156,10 +163,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
156
163
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
157
164
|
exports.CLIENT_RESERVED_KEYS = exports.COMPAT_DEFERRED = exports.PRISMA_ARG_KEYS = exports.Prisma = void 0;
|
|
158
165
|
exports.createPrismaCompatClient = createPrismaCompatClient;
|
|
166
|
+
const diagnostics_js_1 = require("./diagnostics.js");
|
|
159
167
|
const errors_js_1 = require("./errors.js");
|
|
168
|
+
const logger_js_1 = require("./logger.js");
|
|
160
169
|
const nested_write_js_1 = require("./nested-write.js");
|
|
161
170
|
const prisma_schema_fingerprint_js_1 = require("./prisma-schema-fingerprint.js");
|
|
171
|
+
const filters_js_1 = require("./query/filters.js");
|
|
162
172
|
const index_js_1 = require("./query/index.js");
|
|
173
|
+
const normalize_args_js_1 = require("./query/normalize-args.js");
|
|
163
174
|
const utils_js_1 = require("./query/utils.js");
|
|
164
175
|
const warn_registry_js_1 = require("./query/warn-registry.js");
|
|
165
176
|
const where_compile_js_1 = require("./query/where-compile.js");
|
|
@@ -270,19 +281,21 @@ function timeStringToDate(v) {
|
|
|
270
281
|
return new Date(Date.UTC(1970, 0, 1, Number(m[1]), Number(m[2]), Number(m[3]), ms));
|
|
271
282
|
}
|
|
272
283
|
function buildLookups(ctx, mm) {
|
|
273
|
-
|
|
284
|
+
// Keyed by field and relation names, so prototype-less: a field called
|
|
285
|
+
// `constructor` is an own key here, never Object.prototype's member.
|
|
286
|
+
const reverseFields = Object.create(null);
|
|
274
287
|
let identityFields = true;
|
|
275
288
|
for (const [prismaField, turbineField] of Object.entries(mm.fields)) {
|
|
276
289
|
reverseFields[turbineField] = prismaField;
|
|
277
290
|
if (prismaField !== turbineField)
|
|
278
291
|
identityFields = false;
|
|
279
292
|
}
|
|
280
|
-
const reverseRelations =
|
|
293
|
+
const reverseRelations = Object.create(null);
|
|
281
294
|
for (const [prismaRel, rel] of Object.entries(mm.relations)) {
|
|
282
295
|
reverseRelations[rel.name] = { prismaName: prismaRel, cardinality: rel.cardinality };
|
|
283
296
|
}
|
|
284
297
|
const timeFields = new Set();
|
|
285
|
-
for (const col of ctx.schema.tables
|
|
298
|
+
for (const col of (0, utils_js_1.ownLookup)(ctx.schema.tables, mm.table)?.columns ?? []) {
|
|
286
299
|
if (TIME_PG_TYPES.has(col.pgType))
|
|
287
300
|
timeFields.add(col.field);
|
|
288
301
|
}
|
|
@@ -297,62 +310,18 @@ function lookupsFor(ctx, mm) {
|
|
|
297
310
|
return l;
|
|
298
311
|
}
|
|
299
312
|
/** Resolve a turbine relation's target Prisma model map (for nested translation). */
|
|
300
|
-
/**
|
|
301
|
-
* A `where` fragment no row satisfies, for Prisma's empty `OR`.
|
|
302
|
-
*
|
|
303
|
-
* `{ <key>: { in: [] } }`, which core compiles to `= ANY($n)` with an empty
|
|
304
|
-
* array: false for every row, NULL-valued ones included, and one ordinary bound
|
|
305
|
-
* param rather than a literal spliced into the SQL. The column is the first
|
|
306
|
-
* primary-key column, else the table's first column, so it is always a real
|
|
307
|
-
* column of this table and the predicate stays a plain scalar comparison the
|
|
308
|
-
* cache fingerprints like any other.
|
|
309
|
-
*
|
|
310
|
-
* A table this layer cannot name a column of THROWS. The fallback used to be
|
|
311
|
-
* `{}`, an empty `where` fragment - which is not "no rows" but its exact
|
|
312
|
-
* opposite, EVERY row, and it would have been merged in silently beside the
|
|
313
|
-
* caller's other predicates. A sentinel builder that cannot build a sentinel
|
|
314
|
-
* has not succeeded, and the one shape it exists to compile is the one where
|
|
315
|
-
* answering "all of them" instead of "none of them" is worst.
|
|
316
|
-
*/
|
|
317
|
-
function matchNothing(ctx, mm) {
|
|
318
|
-
const meta = ctx.schema.tables[mm.table];
|
|
319
|
-
const column = meta?.primaryKey?.[0] ?? meta?.allColumns?.[0];
|
|
320
|
-
if (!meta || !column) {
|
|
321
|
-
throw new errors_js_1.ValidationError(`An empty \`OR\` on model ${mm.table} means "no rows", and compiling that needs one real column ` +
|
|
322
|
-
`of the table, but the schema metadata for "${mm.table}" lists none. Regenerate the client ` +
|
|
323
|
-
'(`npx turbine generate`) so the table carries its columns, or drop the empty `OR`.');
|
|
324
|
-
}
|
|
325
|
-
return { [meta.reverseColumnMap?.[column] ?? column]: { in: [] } };
|
|
326
|
-
}
|
|
327
313
|
function relTargetModel(ctx, mm, turbineRel) {
|
|
328
|
-
const
|
|
314
|
+
const relations = (0, utils_js_1.ownLookup)(ctx.schema.tables, mm.table)?.relations;
|
|
315
|
+
const rd = relations ? (0, utils_js_1.ownLookup)(relations, turbineRel) : undefined;
|
|
329
316
|
if (!rd)
|
|
330
317
|
return undefined;
|
|
331
318
|
const modelName = ctx.tableToModel.get(rd.to);
|
|
332
|
-
return modelName ? ctx.map.models
|
|
319
|
+
return modelName ? (0, utils_js_1.ownLookup)(ctx.map.models, modelName) : undefined;
|
|
333
320
|
}
|
|
334
321
|
// ---------------------------------------------------------------------------
|
|
335
322
|
// Argument translation
|
|
336
323
|
// ---------------------------------------------------------------------------
|
|
337
324
|
const COMBINATORS = new Set(['AND', 'OR', 'NOT']);
|
|
338
|
-
/**
|
|
339
|
-
* The to-many half of the relation-filter wrapper list.
|
|
340
|
-
*
|
|
341
|
-
* DRIFT RISK, and this is the third copy of that list in the tree. The canonical
|
|
342
|
-
* NAMED one is `RELATION_FILTER_WRAPPERS` in `cli/pii-predicate-guard.ts`
|
|
343
|
-
* (`some`/`none`/`every`/`is`/`isNot`), and a second is inlined in
|
|
344
|
-
* `query/where-compile.ts` as `'some' in x || 'every' in x || …`. This copy is
|
|
345
|
-
* the worst of the three because it is SPLIT: the to-many quantifiers are here,
|
|
346
|
-
* and `is` / `isNot` are two inline `k === …` tests in
|
|
347
|
-
* {@link translateRelationFilter}, so half of it does not read as a list and a
|
|
348
|
-
* grep for the list will not find it. A wrapper the compiler learns and this set
|
|
349
|
-
* does not is a relation body that reaches core untranslated, i.e. with Prisma
|
|
350
|
-
* field names still in it.
|
|
351
|
-
*
|
|
352
|
-
* The fix is an export from `query/filters.ts` that all three import; until that
|
|
353
|
-
* exists, these comments are what hold them in step.
|
|
354
|
-
*/
|
|
355
|
-
const RELATION_QUANTIFIERS = new Set(['some', 'every', 'none']);
|
|
356
325
|
/**
|
|
357
326
|
* Refuse a translation walk that has nested past {@link MAX_WHERE_DEPTH}.
|
|
358
327
|
*
|
|
@@ -392,16 +361,8 @@ function assertTranslateDepth(depth, clause) {
|
|
|
392
361
|
`query needs, and an unbounded walk over caller-supplied nesting is a stack-overflow surface, so it is ` +
|
|
393
362
|
`refused. If this is a generated argument, flatten it: a single array of N entries is one level, not N.`);
|
|
394
363
|
}
|
|
395
|
-
/** Whether a value is a plain object usable as a compound-unique selector. */
|
|
396
|
-
function isPlainObject(v) {
|
|
397
|
-
return (typeof v === 'object' &&
|
|
398
|
-
v !== null &&
|
|
399
|
-
!Array.isArray(v) &&
|
|
400
|
-
!(v instanceof Date) &&
|
|
401
|
-
!(typeof Buffer !== 'undefined' && Buffer.isBuffer(v)));
|
|
402
|
-
}
|
|
403
364
|
function renameField(mm, prismaField) {
|
|
404
|
-
return mm.fields
|
|
365
|
+
return (0, utils_js_1.ownLookup)(mm.fields, prismaField) ?? prismaField;
|
|
405
366
|
}
|
|
406
367
|
/** The turbine arg surface each operation compiles into. */
|
|
407
368
|
const OPERATION_TABLES = {
|
|
@@ -521,8 +482,8 @@ const KNOWN_KEYS = (() => {
|
|
|
521
482
|
return out;
|
|
522
483
|
})();
|
|
523
484
|
/**
|
|
524
|
-
*
|
|
525
|
-
* no meaning for.
|
|
485
|
+
* Per-query advice for a key on a delegate's args object that this operation
|
|
486
|
+
* has no meaning for.
|
|
526
487
|
*
|
|
527
488
|
* An unrecognized key is silently ignored (JavaScript objects have no schema),
|
|
528
489
|
* which makes a typo, a turbine spelling, and a genuinely missing feature all
|
|
@@ -534,13 +495,14 @@ const KNOWN_KEYS = (() => {
|
|
|
534
495
|
* into a failing one on upgrade over one stray key, and a key from a Prisma
|
|
535
496
|
* major newer than {@link PRISMA_ARG_KEYS} would be exactly that key. The whole
|
|
536
497
|
* body is wrapped so a hostile or exotic args object (a Proxy whose `ownKeys`
|
|
537
|
-
* throws) can never be the reason a query fails.
|
|
538
|
-
* `
|
|
498
|
+
* throws) can never be the reason a query fails. Off when the client's
|
|
499
|
+
* `diagnostics` turn advice off, and once per `model.operation.key` per
|
|
500
|
+
* process, like every other advisory here.
|
|
539
501
|
*/
|
|
540
|
-
function warnUnknownQueryOptions(model, op, args) {
|
|
541
|
-
if (
|
|
502
|
+
function warnUnknownQueryOptions(log, model, op, args) {
|
|
503
|
+
if (!log.diagnostics.advice)
|
|
542
504
|
return;
|
|
543
|
-
if (!
|
|
505
|
+
if (!(0, filters_js_1.isPlainRecord)(args))
|
|
544
506
|
return;
|
|
545
507
|
try {
|
|
546
508
|
const known = KNOWN_KEYS[op];
|
|
@@ -556,13 +518,19 @@ function warnUnknownQueryOptions(model, op, args) {
|
|
|
556
518
|
if (!(0, warn_registry_js_1.shouldWarnOnce)(warn_registry_js_1.WARN_NS.unknownQueryOption, `${model}.${op}.${key}`))
|
|
557
519
|
continue;
|
|
558
520
|
if (alias) {
|
|
559
|
-
|
|
560
|
-
` prisma-compat takes Prisma's "${alias}". (${model}.${op})
|
|
521
|
+
(0, logger_js_1.logTo)(log.logger, 'warn', `prisma-compat: "${key}" is Turbine's spelling and is ignored here;` +
|
|
522
|
+
` prisma-compat takes Prisma's "${alias}". (${model}.${op})`, { code: 'prisma-compat-native-spelling', table: model, operation: op, key, prismaKey: alias });
|
|
561
523
|
continue;
|
|
562
524
|
}
|
|
563
525
|
const suggestion = (0, utils_js_1.suggestKey)(key, known);
|
|
564
|
-
|
|
565
|
-
(suggestion ? ` Did you mean "${suggestion}"?` : '')
|
|
526
|
+
(0, logger_js_1.logTo)(log.logger, 'warn', `prisma-compat: unknown option "${key}" in ${model}.${op}(), it is ignored.` +
|
|
527
|
+
(suggestion ? ` Did you mean "${suggestion}"?` : ''), {
|
|
528
|
+
code: 'prisma-compat-unknown-option',
|
|
529
|
+
table: model,
|
|
530
|
+
operation: op,
|
|
531
|
+
key,
|
|
532
|
+
...(suggestion ? { suggestion } : {}),
|
|
533
|
+
});
|
|
566
534
|
}
|
|
567
535
|
}
|
|
568
536
|
catch {
|
|
@@ -571,8 +539,8 @@ function warnUnknownQueryOptions(model, op, args) {
|
|
|
571
539
|
}
|
|
572
540
|
}
|
|
573
541
|
/**
|
|
574
|
-
*
|
|
575
|
-
*
|
|
542
|
+
* Notice that the `prisma-map.ts` driving this client no longer matches the
|
|
543
|
+
* Prisma schema it was generated from.
|
|
576
544
|
*
|
|
577
545
|
* Nothing re-runs `turbine migrate-from-prisma`. When the schema moves on, the
|
|
578
546
|
* map does not, and the adapter keeps translating the names it has: a model
|
|
@@ -587,19 +555,26 @@ function warnUnknownQueryOptions(model, op, args) {
|
|
|
587
555
|
* - **Any failure is silence.** A missing file is the NORMAL deployed state
|
|
588
556
|
* (nobody ships `prisma/` to production) and so are a bundled runtime with no
|
|
589
557
|
* `node:fs` and a read that is refused. None of them is evidence of drift.
|
|
590
|
-
* - **
|
|
591
|
-
*
|
|
592
|
-
* builds a client per request
|
|
558
|
+
* - **Always on**, like every construction warning: a stale map loses models
|
|
559
|
+
* in production too. In production mode the file is READ once per process
|
|
560
|
+
* per path, so a process that builds a client per request does not read it
|
|
561
|
+
* per request; in development it is re-read per client, so a schema edited
|
|
562
|
+
* under a running dev server is still noticed.
|
|
563
|
+
* - **Warned once per process per path**, via the shared registry.
|
|
593
564
|
*
|
|
594
565
|
* @param map - the map under test; a map with no `source` (hand-written, or
|
|
595
566
|
* emitted before 0.60) is skipped, since absence carries no information.
|
|
596
567
|
*/
|
|
597
|
-
function warnStalePrismaMap(map) {
|
|
568
|
+
function warnStalePrismaMap(map, log) {
|
|
598
569
|
const source = map.source;
|
|
599
570
|
if (!source || typeof source.path !== 'string' || typeof source.hash !== 'string')
|
|
600
571
|
return;
|
|
601
|
-
|
|
572
|
+
// No `process`, no `node:fs`: an edge runtime has no file to compare.
|
|
573
|
+
if (typeof process === 'undefined')
|
|
602
574
|
return;
|
|
575
|
+
if (log.diagnostics.mode === 'production' && !(0, warn_registry_js_1.shouldWarnOnce)(warn_registry_js_1.WARN_NS.stalePrismaMap, `checked:${source.path}`)) {
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
603
578
|
void (async () => {
|
|
604
579
|
try {
|
|
605
580
|
// Dynamic so a bundle that never reaches this line never needs `node:fs`.
|
|
@@ -609,9 +584,9 @@ function warnStalePrismaMap(map) {
|
|
|
609
584
|
return;
|
|
610
585
|
if (!(0, warn_registry_js_1.shouldWarnOnce)(warn_registry_js_1.WARN_NS.stalePrismaMap, source.path))
|
|
611
586
|
return;
|
|
612
|
-
|
|
587
|
+
(0, logger_js_1.logTo)(log.logger, 'warn', `prisma-compat: ${source.path} has changed since prisma-map.ts was generated,` +
|
|
613
588
|
' so any model, field, relation or compound-unique added or renamed since then is missing' +
|
|
614
|
-
' from the compat client. Re-run: turbine migrate-from-prisma');
|
|
589
|
+
' from the compat client. Re-run: turbine migrate-from-prisma', { code: 'prisma-map-stale', path: source.path });
|
|
615
590
|
}
|
|
616
591
|
catch {
|
|
617
592
|
// No file, no `node:fs`, no permission. None of these means the map is
|
|
@@ -627,7 +602,7 @@ function warnStalePrismaMap(map) {
|
|
|
627
602
|
* bound turns "change at most 10 rows" into "change every row".
|
|
628
603
|
*/
|
|
629
604
|
function refuseRowLimit(model, op, args) {
|
|
630
|
-
if (!
|
|
605
|
+
if (!(0, filters_js_1.isPlainRecord)(args) || args.limit === undefined)
|
|
631
606
|
return;
|
|
632
607
|
throw new errors_js_1.UnsupportedFeatureError(`\`limit\` on ${op} (row-bounded mass mutation)`, 'prisma-compat', `Turbine has no row-bounded ${op}; select the rows you mean with \`where\`` +
|
|
633
608
|
` (on ${model}.${op}), or page them and mutate by primary key.`);
|
|
@@ -661,33 +636,11 @@ function mapRelationLoadStrategy(value) {
|
|
|
661
636
|
*/
|
|
662
637
|
function translateWhere(ctx, mm, where, depth = 0) {
|
|
663
638
|
assertTranslateDepth(depth, 'where');
|
|
664
|
-
if (!
|
|
639
|
+
if (!(0, filters_js_1.isPlainRecord)(where))
|
|
665
640
|
return where;
|
|
666
641
|
const out = {};
|
|
667
|
-
// The empty-`OR` sentinel, held aside rather than merged into `out`. At most
|
|
668
|
-
// one per level, since `OR` is a single object key.
|
|
669
|
-
let matchNone;
|
|
670
642
|
for (const [key, val] of Object.entries(where)) {
|
|
671
643
|
if (COMBINATORS.has(key)) {
|
|
672
|
-
// An EMPTY `OR` is false in Prisma (no branch can match), while an empty
|
|
673
|
-
// `AND` / `NOT` is true. Core drops an empty combinator entirely, so the
|
|
674
|
-
// same call returned EVERY row here: the classic trigger is
|
|
675
|
-
// `OR: ids.map(...)` over a selection that turned out to be empty, i.e.
|
|
676
|
-
// "none of these" answered with "all of them". Compiled to a predicate
|
|
677
|
-
// that matches nothing so this layer keeps Prisma's meaning; core's own
|
|
678
|
-
// semantics are unchanged and documented separately.
|
|
679
|
-
//
|
|
680
|
-
// Held aside, NOT `Object.assign`ed onto `out`: the sentinel is keyed on
|
|
681
|
-
// a real column of the table, so merging it made two predicates on one
|
|
682
|
-
// key collide and JavaScript's key ORDER decided which survived.
|
|
683
|
-
// `{ OR: [], id: 5 }` kept the caller's `id = 5` and dropped the
|
|
684
|
-
// sentinel (returning the row Prisma excludes); `{ id: 5, OR: [] }` kept
|
|
685
|
-
// the sentinel and dropped the caller's `id = 5`. Same query, same
|
|
686
|
-
// meaning, opposite predicate.
|
|
687
|
-
if (key === 'OR' && Array.isArray(val) && val.length === 0) {
|
|
688
|
-
matchNone = matchNothing(ctx, mm);
|
|
689
|
-
continue;
|
|
690
|
-
}
|
|
691
644
|
// An `AND` / `OR` array of N conditions is ONE level, not N: the elements
|
|
692
645
|
// are siblings, so they all translate at the same incremented depth.
|
|
693
646
|
out[key] = Array.isArray(val)
|
|
@@ -696,18 +649,18 @@ function translateWhere(ctx, mm, where, depth = 0) {
|
|
|
696
649
|
continue;
|
|
697
650
|
}
|
|
698
651
|
// Compound-unique selector (custom or default Prisma name).
|
|
699
|
-
const compound = mm.compoundUniques
|
|
700
|
-
if (compound &&
|
|
652
|
+
const compound = (0, utils_js_1.ownLookup)(mm.compoundUniques, key);
|
|
653
|
+
if (compound && (0, filters_js_1.isPlainRecord)(val) && !(0, utils_js_1.ownLookup)(mm.fields, key) && !(0, utils_js_1.ownLookup)(mm.relations, key)) {
|
|
701
654
|
const inner = {};
|
|
702
655
|
for (const [pk, pv] of Object.entries(val))
|
|
703
|
-
inner
|
|
656
|
+
(0, filters_js_1.setOwn)(inner, renameField(mm, pk), pv);
|
|
704
657
|
// Turbine's core derives the selector name from the underscore-join of the
|
|
705
658
|
// member FIELD names, so re-key custom names onto that canonical form.
|
|
706
|
-
out
|
|
659
|
+
(0, filters_js_1.setOwn)(out, compound.join('_'), inner);
|
|
707
660
|
continue;
|
|
708
661
|
}
|
|
709
662
|
// Relation filter.
|
|
710
|
-
const rel = mm.relations
|
|
663
|
+
const rel = (0, utils_js_1.ownLookup)(mm.relations, key);
|
|
711
664
|
if (rel) {
|
|
712
665
|
// Prisma spells "this to-one relation has no related row" as a bare
|
|
713
666
|
// `null` on the relation key; core spells it `{ is: null }` and answers a
|
|
@@ -717,44 +670,28 @@ function translateWhere(ctx, mm, where, depth = 0) {
|
|
|
717
670
|
// has no bare-null shape there, so core still refuses them by name rather
|
|
718
671
|
// than this layer inventing a meaning.
|
|
719
672
|
if (val === null && rel.cardinality === 'one') {
|
|
720
|
-
out
|
|
673
|
+
(0, filters_js_1.setOwn)(out, rel.name, { is: null });
|
|
721
674
|
continue;
|
|
722
675
|
}
|
|
723
676
|
const target = relTargetModel(ctx, mm, rel.name);
|
|
724
|
-
out
|
|
677
|
+
(0, filters_js_1.setOwn)(out, rel.name, translateRelationFilter(ctx, target, val, depth + 1));
|
|
725
678
|
continue;
|
|
726
679
|
}
|
|
727
680
|
// Scalar field, key renamed, value (literal or operator object) passes
|
|
728
681
|
// through unchanged (Prisma operator names match Turbine's).
|
|
729
|
-
out
|
|
682
|
+
(0, filters_js_1.setOwn)(out, renameField(mm, key), val);
|
|
730
683
|
}
|
|
731
|
-
|
|
732
|
-
return out;
|
|
733
|
-
// Nothing else at this level: the sentinel IS the where, no wrapper needed.
|
|
734
|
-
if (Object.keys(out).length === 0)
|
|
735
|
-
return matchNone;
|
|
736
|
-
// Otherwise conjoin, so neither half can overwrite the other whatever order
|
|
737
|
-
// the caller wrote the keys in. BRANDED, because an `AND` array is the shape
|
|
738
|
-
// that makes a statement unnamed (a caller-written combinator has a
|
|
739
|
-
// caller-sized arity); this one is Turbine's, with a fixed arity of two, and
|
|
740
|
-
// an unbranded wrapper here would quietly take every compat query carrying an
|
|
741
|
-
// empty `OR` off named prepared statements. The caller's own combinators sit
|
|
742
|
-
// INSIDE `out` and are walked at the next level with their own brand check,
|
|
743
|
-
// so they still count.
|
|
744
|
-
return (0, utils_js_1.markInternalCombinator)({ AND: [out, matchNone] });
|
|
684
|
+
return out;
|
|
745
685
|
}
|
|
746
686
|
function translateRelationFilter(ctx, target, val, depth) {
|
|
747
|
-
if (!
|
|
687
|
+
if (!(0, filters_js_1.isPlainRecord)(val))
|
|
748
688
|
return val;
|
|
749
689
|
const keys = Object.keys(val);
|
|
750
|
-
const hasQuantifier = keys.some((k) =>
|
|
690
|
+
const hasQuantifier = keys.some((k) => filters_js_1.RELATION_FILTER_WRAPPER_SET.has(k));
|
|
751
691
|
if (hasQuantifier) {
|
|
752
692
|
const out = {};
|
|
753
693
|
for (const [k, v] of Object.entries(val)) {
|
|
754
|
-
out
|
|
755
|
-
target && (RELATION_QUANTIFIERS.has(k) || k === 'is' || k === 'isNot')
|
|
756
|
-
? translateWhere(ctx, target, v, depth + 1)
|
|
757
|
-
: v;
|
|
694
|
+
(0, filters_js_1.setOwn)(out, k, target && filters_js_1.RELATION_FILTER_WRAPPER_SET.has(k) ? translateWhere(ctx, target, v, depth + 1) : v);
|
|
758
695
|
}
|
|
759
696
|
return out;
|
|
760
697
|
}
|
|
@@ -778,7 +715,7 @@ function translateOrderBy(ctx, mm, ob, depth = 0) {
|
|
|
778
715
|
assertTranslateDepth(depth, 'orderBy');
|
|
779
716
|
if (Array.isArray(ob))
|
|
780
717
|
return ob.map((o) => translateOrderBy(ctx, mm, o, depth + 1));
|
|
781
|
-
if (!
|
|
718
|
+
if (!(0, filters_js_1.isPlainRecord)(ob))
|
|
782
719
|
return ob;
|
|
783
720
|
const out = {};
|
|
784
721
|
for (const [key, val] of Object.entries(ob)) {
|
|
@@ -786,20 +723,23 @@ function translateOrderBy(ctx, mm, ob, depth = 0) {
|
|
|
786
723
|
out._count = val;
|
|
787
724
|
continue;
|
|
788
725
|
}
|
|
789
|
-
const rel = mm.relations
|
|
726
|
+
const rel = (0, utils_js_1.ownLookup)(mm.relations, key);
|
|
790
727
|
if (rel) {
|
|
791
728
|
const target = relTargetModel(ctx, mm, rel.name);
|
|
792
729
|
out[rel.name] =
|
|
793
|
-
|
|
730
|
+
(0, filters_js_1.isPlainRecord)(val) && !('_count' in val) && target ? translateOrderBy(ctx, target, val, depth + 1) : val;
|
|
794
731
|
continue;
|
|
795
732
|
}
|
|
796
|
-
out
|
|
733
|
+
(0, filters_js_1.setOwn)(out, renameField(mm, key), val);
|
|
797
734
|
}
|
|
798
735
|
return out;
|
|
799
736
|
}
|
|
800
|
-
/**
|
|
737
|
+
/**
|
|
738
|
+
* Map a Prisma `take` to a Turbine `limit`. Negative take is unsupported. Any
|
|
739
|
+
* other value goes to core as written, which refuses a non-integer.
|
|
740
|
+
*/
|
|
801
741
|
function mapTake(take) {
|
|
802
|
-
if (take < 0) {
|
|
742
|
+
if (typeof take === 'number' && take < 0) {
|
|
803
743
|
throw new errors_js_1.UnsupportedFeatureError('negative take (take-from-end pagination)', 'prisma-compat', 'Turbine has no reverse-take; reverse your orderBy and use a positive take instead.');
|
|
804
744
|
}
|
|
805
745
|
return take;
|
|
@@ -824,11 +764,11 @@ function translateOmit(ctx, mm, omit) {
|
|
|
824
764
|
for (const [key, val] of Object.entries(omit)) {
|
|
825
765
|
if (val === false || val == null)
|
|
826
766
|
continue;
|
|
827
|
-
if (mm.relations
|
|
767
|
+
if ((0, utils_js_1.ownLookup)(mm.relations, key)) {
|
|
828
768
|
throw new errors_js_1.ValidationError(`prisma-compat: \`omit\` on model "${modelName(ctx, mm)}" takes scalar fields, but "${key}" ` +
|
|
829
769
|
'is a relation. Leave the relation out of `include` instead.');
|
|
830
770
|
}
|
|
831
|
-
out
|
|
771
|
+
(0, filters_js_1.setOwn)(out, renameField(mm, key), true);
|
|
832
772
|
any = true;
|
|
833
773
|
}
|
|
834
774
|
return any ? out : undefined;
|
|
@@ -865,15 +805,15 @@ function translateProjection(ctx, mm, args, depth = 0) {
|
|
|
865
805
|
if (val === false || val == null)
|
|
866
806
|
continue;
|
|
867
807
|
if (key === '_count') {
|
|
868
|
-
withClause._count = translateCountSelect(mm, val);
|
|
808
|
+
withClause._count = translateCountSelect(ctx, mm, val);
|
|
869
809
|
hasWith = true;
|
|
870
810
|
continue;
|
|
871
811
|
}
|
|
872
|
-
const rel = mm.relations
|
|
812
|
+
const rel = (0, utils_js_1.ownLookup)(mm.relations, key);
|
|
873
813
|
if (!rel) {
|
|
874
814
|
throw new errors_js_1.ValidationError(`prisma-compat: unknown relation "${key}" in include on model "${modelName(ctx, mm)}".`);
|
|
875
815
|
}
|
|
876
|
-
withClause
|
|
816
|
+
(0, filters_js_1.setOwn)(withClause, rel.name, translateWithOption(ctx, mm, rel.name, val, depth + 1));
|
|
877
817
|
hasWith = true;
|
|
878
818
|
}
|
|
879
819
|
return { omit, with: hasWith ? withClause : undefined };
|
|
@@ -885,17 +825,17 @@ function translateProjection(ctx, mm, args, depth = 0) {
|
|
|
885
825
|
if (val === false || val == null)
|
|
886
826
|
continue;
|
|
887
827
|
if (key === '_count') {
|
|
888
|
-
withClause._count = translateCountSelect(mm, val);
|
|
828
|
+
withClause._count = translateCountSelect(ctx, mm, val);
|
|
889
829
|
hasWith = true;
|
|
890
830
|
continue;
|
|
891
831
|
}
|
|
892
|
-
const rel = mm.relations
|
|
832
|
+
const rel = (0, utils_js_1.ownLookup)(mm.relations, key);
|
|
893
833
|
if (rel) {
|
|
894
|
-
withClause
|
|
834
|
+
(0, filters_js_1.setOwn)(withClause, rel.name, translateWithOption(ctx, mm, rel.name, val, depth + 1));
|
|
895
835
|
hasWith = true;
|
|
896
836
|
continue;
|
|
897
837
|
}
|
|
898
|
-
scalar
|
|
838
|
+
(0, filters_js_1.setOwn)(scalar, renameField(mm, key), true);
|
|
899
839
|
hasScalar = true;
|
|
900
840
|
}
|
|
901
841
|
return { select: hasScalar ? scalar : undefined, with: hasWith ? withClause : undefined };
|
|
@@ -977,7 +917,7 @@ function resolveWriteProjection(ctx, mm, op, args) {
|
|
|
977
917
|
for (const [key, val] of Object.entries(select)) {
|
|
978
918
|
if (val === false || val == null)
|
|
979
919
|
continue;
|
|
980
|
-
if (key === '_count' || mm.relations
|
|
920
|
+
if (key === '_count' || (0, utils_js_1.ownLookup)(mm.relations, key)) {
|
|
981
921
|
throw new errors_js_1.UnsupportedFeatureError(`select of "${key}" on ${op}`, 'prisma-compat', `"${key}" is a relation on model "${modelName(ctx, mm)}"; a write returns its own row only, so read ` +
|
|
982
922
|
'it back with a separate findUnique inside the same $transaction.');
|
|
983
923
|
}
|
|
@@ -994,7 +934,7 @@ function resolveWriteProjection(ctx, mm, op, args) {
|
|
|
994
934
|
for (const [key, val] of Object.entries(omitArg)) {
|
|
995
935
|
if (val === false || val == null)
|
|
996
936
|
continue;
|
|
997
|
-
if (mm.relations
|
|
937
|
+
if ((0, utils_js_1.ownLookup)(mm.relations, key)) {
|
|
998
938
|
throw new errors_js_1.ValidationError(`prisma-compat: \`omit\` on model "${modelName(ctx, mm)}" takes scalar fields, but "${key}" ` +
|
|
999
939
|
'is a relation.');
|
|
1000
940
|
}
|
|
@@ -1023,13 +963,13 @@ function coreWriteProjection(mm, proj) {
|
|
|
1023
963
|
}
|
|
1024
964
|
/** Apply a {@link WriteProjection} to one already-reshaped write result row. */
|
|
1025
965
|
function applyWriteProjection(proj, row) {
|
|
1026
|
-
if (!
|
|
966
|
+
if (!(0, filters_js_1.isPlainRecord)(row))
|
|
1027
967
|
return row;
|
|
1028
968
|
if (proj.pick) {
|
|
1029
969
|
const out = {};
|
|
1030
970
|
for (const key of proj.pick) {
|
|
1031
|
-
if (key
|
|
1032
|
-
out
|
|
971
|
+
if (Object.hasOwn(row, key))
|
|
972
|
+
(0, filters_js_1.setOwn)(out, key, row[key]);
|
|
1033
973
|
}
|
|
1034
974
|
return out;
|
|
1035
975
|
}
|
|
@@ -1041,9 +981,65 @@ function applyWriteProjection(proj, row) {
|
|
|
1041
981
|
}
|
|
1042
982
|
return row;
|
|
1043
983
|
}
|
|
984
|
+
/** What a caller wrote, named for a message: `an array`, `null`, `a string`. */
|
|
985
|
+
function describeValue(value) {
|
|
986
|
+
if (value === undefined)
|
|
987
|
+
return 'undefined';
|
|
988
|
+
if (value === null)
|
|
989
|
+
return 'null';
|
|
990
|
+
if (Array.isArray(value))
|
|
991
|
+
return 'an array';
|
|
992
|
+
return typeof value === 'object' ? 'an object' : `a ${typeof value}`;
|
|
993
|
+
}
|
|
994
|
+
/** The Prisma spelling of a turbine relation name, for a message. */
|
|
995
|
+
function prismaRelationName(ctx, mm, turbineRel) {
|
|
996
|
+
return (0, utils_js_1.ownLookup)(lookupsFor(ctx, mm).reverseRelations, turbineRel)?.prismaName ?? turbineRel;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* The options Prisma takes on an included relation. `cursor` and `distinct` are
|
|
1000
|
+
* here because they are Prisma's, and are refused by name (E017) rather than
|
|
1001
|
+
* called unknown: Turbine's `with` clause cannot honor either.
|
|
1002
|
+
*/
|
|
1003
|
+
const NESTED_RELATION_KEYS = new Set([
|
|
1004
|
+
'where',
|
|
1005
|
+
'orderBy',
|
|
1006
|
+
'take',
|
|
1007
|
+
'skip',
|
|
1008
|
+
'cursor',
|
|
1009
|
+
'distinct',
|
|
1010
|
+
'select',
|
|
1011
|
+
'include',
|
|
1012
|
+
'omit',
|
|
1013
|
+
]);
|
|
1014
|
+
/**
|
|
1015
|
+
* Refuse a key on an included relation's options that this layer does not
|
|
1016
|
+
* translate. Every key it does not read used to be dropped, so a misspelled
|
|
1017
|
+
* `wher` or a Turbine spelling (`limit`) returned unfiltered, unlimited rows
|
|
1018
|
+
* with no signal. An `undefined` value is absent, as everywhere else.
|
|
1019
|
+
*/
|
|
1020
|
+
function assertNestedRelationKeys(label, opts) {
|
|
1021
|
+
for (const key of Object.keys(opts)) {
|
|
1022
|
+
if (opts[key] === undefined || NESTED_RELATION_KEYS.has(key))
|
|
1023
|
+
continue;
|
|
1024
|
+
const alias = ALIAS_HINT[key];
|
|
1025
|
+
if (alias) {
|
|
1026
|
+
throw new errors_js_1.ValidationError(`prisma-compat: "${key}" in the options of relation "${label}" is Turbine's spelling and would be ignored. ` +
|
|
1027
|
+
`prisma-compat takes Prisma's "${alias}".`);
|
|
1028
|
+
}
|
|
1029
|
+
const suggestion = (0, utils_js_1.suggestKey)(key, NESTED_RELATION_KEYS);
|
|
1030
|
+
throw new errors_js_1.ValidationError(`prisma-compat: unknown option "${key}" in the options of relation "${label}", it would be ignored.` +
|
|
1031
|
+
(suggestion ? ` Did you mean "${suggestion}"?` : '') +
|
|
1032
|
+
` Valid options: ${[...NESTED_RELATION_KEYS].join(', ')}.`);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1044
1035
|
/**
|
|
1045
1036
|
* Translate a Prisma relation include payload into a Turbine `WithOptions`.
|
|
1046
1037
|
*
|
|
1038
|
+
* Every option the payload carries is either translated, refused by name, or
|
|
1039
|
+
* refused as unknown: none is dropped. `cursor` and `distinct` are Prisma's
|
|
1040
|
+
* and have no per-relation form in a `with` clause (E017); any other key is
|
|
1041
|
+
* E003 (see {@link assertNestedRelationKeys}).
|
|
1042
|
+
*
|
|
1047
1043
|
* `depth` is the relation hop count carried down from {@link translateProjection}
|
|
1048
1044
|
* and is charged to the nested `where` / `orderBy` too, so a payload that pads
|
|
1049
1045
|
* one arg with relation hops and then nests the other cannot spend two separate
|
|
@@ -1052,8 +1048,11 @@ function applyWriteProjection(proj, row) {
|
|
|
1052
1048
|
function translateWithOption(ctx, mm, turbineRel, val, depth = 0) {
|
|
1053
1049
|
if (val === true)
|
|
1054
1050
|
return true;
|
|
1055
|
-
|
|
1056
|
-
|
|
1051
|
+
const label = `${modelName(ctx, mm)}.${prismaRelationName(ctx, mm, turbineRel)}`;
|
|
1052
|
+
if (!(0, filters_js_1.isPlainRecord)(val)) {
|
|
1053
|
+
throw new errors_js_1.ValidationError(`prisma-compat: the value for relation "${label}" must be true or an options object, received ${describeValue(val)}.`);
|
|
1054
|
+
}
|
|
1055
|
+
assertNestedRelationKeys(label, val);
|
|
1057
1056
|
const target = relTargetModel(ctx, mm, turbineRel);
|
|
1058
1057
|
const opt = {};
|
|
1059
1058
|
if (val.where !== undefined)
|
|
@@ -1062,10 +1061,26 @@ function translateWithOption(ctx, mm, turbineRel, val, depth = 0) {
|
|
|
1062
1061
|
opt.orderBy = target ? translateOrderBy(ctx, target, val.orderBy, depth) : val.orderBy;
|
|
1063
1062
|
if (val.take !== undefined)
|
|
1064
1063
|
opt.limit = mapTake(val.take);
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
if (
|
|
1064
|
+
// Per parent, as in Prisma; core refuses it on a to-one relation.
|
|
1065
|
+
if (val.skip !== undefined)
|
|
1066
|
+
opt.offset = val.skip;
|
|
1067
|
+
if (val.cursor !== undefined) {
|
|
1068
|
+
throw new errors_js_1.UnsupportedFeatureError('cursor on a nested relation include', 'prisma-compat', "Turbine's `with` clause has no cursor. Page the relation with `take` / `skip`, or with a separate " +
|
|
1069
|
+
'findMany on the related model.');
|
|
1070
|
+
}
|
|
1071
|
+
// `distinct: []` names no field and removes nothing, so it is not a request.
|
|
1072
|
+
if (val.distinct !== undefined && !(Array.isArray(val.distinct) && val.distinct.length === 0)) {
|
|
1073
|
+
throw new errors_js_1.UnsupportedFeatureError(`distinct on a nested relation include ("${label}")`, 'prisma-compat', "Turbine's `with` clause has no distinct. Run a separate findMany on the related model with the same " +
|
|
1074
|
+
'where / orderBy / distinct, or de-duplicate the included rows in application code.');
|
|
1075
|
+
}
|
|
1076
|
+
if (val.select !== undefined || val.include !== undefined || val.omit !== undefined) {
|
|
1077
|
+
// Without the target model there are no field names to translate with, and
|
|
1078
|
+
// dropping the projection would return every column of the child rows.
|
|
1079
|
+
if (!target) {
|
|
1080
|
+
throw new errors_js_1.ValidationError(`prisma-compat: relation "${label}" points at a table that is not in the compat map, so its ` +
|
|
1081
|
+
'`select` / `include` / `omit` cannot be translated. Regenerate the map with ' +
|
|
1082
|
+
'`turbine migrate-from-prisma`, or drop the projection.');
|
|
1083
|
+
}
|
|
1069
1084
|
const proj = translateProjection(ctx, target, val, depth);
|
|
1070
1085
|
if (proj.select)
|
|
1071
1086
|
opt.select = proj.select;
|
|
@@ -1078,30 +1093,104 @@ function translateWithOption(ctx, mm, turbineRel, val, depth = 0) {
|
|
|
1078
1093
|
}
|
|
1079
1094
|
return opt;
|
|
1080
1095
|
}
|
|
1081
|
-
/**
|
|
1082
|
-
function
|
|
1096
|
+
/** A count `where` that filters nothing: absent, or an object with no defined key. */
|
|
1097
|
+
function isUnfilteredCountWhere(where) {
|
|
1098
|
+
return where === undefined || ((0, filters_js_1.isPlainRecord)(where) && Object.values(where).every((v) => v === undefined));
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Translate a Prisma `_count: { select: { rel: true } }` / `true` into Turbine
|
|
1102
|
+
* `with._count`.
|
|
1103
|
+
*
|
|
1104
|
+
* Core counts a relation whole: `with._count` takes `true` or `{ rel: true }`.
|
|
1105
|
+
* So a Prisma filtered count (`{ rel: { where } }`) is refused (E017) naming the
|
|
1106
|
+
* alternative, a relation that is not on the model is E003, and so is any key
|
|
1107
|
+
* but `select` under `_count` (a misspelled `selec` used to count EVERY relation,
|
|
1108
|
+
* and a misspelled relation counted nothing, both without a word).
|
|
1109
|
+
*/
|
|
1110
|
+
function translateCountSelect(ctx, mm, val) {
|
|
1083
1111
|
if (val === true)
|
|
1084
1112
|
return true;
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1113
|
+
const model = modelName(ctx, mm);
|
|
1114
|
+
if (!(0, filters_js_1.isPlainRecord)(val)) {
|
|
1115
|
+
throw new errors_js_1.ValidationError(`prisma-compat: \`_count\` on model "${model}" takes true or { select: { <relation>: true } }, ` +
|
|
1116
|
+
`received ${describeValue(val)}.`);
|
|
1117
|
+
}
|
|
1118
|
+
for (const key of Object.keys(val)) {
|
|
1119
|
+
if (key === 'select' || val[key] === undefined)
|
|
1120
|
+
continue;
|
|
1121
|
+
const suggestion = (0, utils_js_1.suggestKey)(key, ['select']);
|
|
1122
|
+
throw new errors_js_1.ValidationError(`prisma-compat: unknown key "${key}" in \`_count\` on model "${model}", it would be ignored.` +
|
|
1123
|
+
(suggestion ? ` Did you mean "${suggestion}"?` : '') +
|
|
1124
|
+
' The only key is "select".');
|
|
1125
|
+
}
|
|
1126
|
+
if (val.select === undefined)
|
|
1127
|
+
return true;
|
|
1128
|
+
if (!(0, filters_js_1.isPlainRecord)(val.select)) {
|
|
1129
|
+
throw new errors_js_1.ValidationError(`prisma-compat: \`_count.select\` on model "${model}" takes { <relation>: true }, ` +
|
|
1130
|
+
`received ${describeValue(val.select)}.`);
|
|
1131
|
+
}
|
|
1132
|
+
const out = {};
|
|
1133
|
+
for (const [key, v] of Object.entries(val.select)) {
|
|
1134
|
+
if (v === undefined || v === false || v === null)
|
|
1135
|
+
continue;
|
|
1136
|
+
const rel = (0, utils_js_1.ownLookup)(mm.relations, key);
|
|
1137
|
+
if (!rel) {
|
|
1138
|
+
const known = Object.keys(mm.relations);
|
|
1139
|
+
const suggestion = (0, utils_js_1.suggestKey)(key, known);
|
|
1140
|
+
throw new errors_js_1.ValidationError(`prisma-compat: unknown relation "${key}" in \`_count.select\` on model "${model}", it would not be counted.` +
|
|
1141
|
+
(suggestion ? ` Did you mean "${suggestion}"?` : '') +
|
|
1142
|
+
` Relations: ${known.join(', ') || '(none)'}.`);
|
|
1143
|
+
}
|
|
1144
|
+
if (v === true) {
|
|
1145
|
+
(0, filters_js_1.setOwn)(out, rel.name, true);
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1148
|
+
if (!(0, filters_js_1.isPlainRecord)(v)) {
|
|
1149
|
+
throw new errors_js_1.ValidationError(`prisma-compat: \`_count.select.${key}\` on model "${model}" takes true, false or { where }, ` +
|
|
1150
|
+
`received ${describeValue(v)}.`);
|
|
1151
|
+
}
|
|
1152
|
+
for (const k of Object.keys(v)) {
|
|
1153
|
+
if (k === 'where' || v[k] === undefined)
|
|
1089
1154
|
continue;
|
|
1090
|
-
const
|
|
1091
|
-
|
|
1092
|
-
|
|
1155
|
+
const hint = (0, utils_js_1.suggestKey)(k, ['where']);
|
|
1156
|
+
throw new errors_js_1.ValidationError(`prisma-compat: unknown key "${k}" in \`_count.select.${key}\` on model "${model}", it would be ignored.` +
|
|
1157
|
+
(hint ? ` Did you mean "${hint}"?` : '') +
|
|
1158
|
+
' The only key is "where".');
|
|
1093
1159
|
}
|
|
1094
|
-
|
|
1160
|
+
if (!isUnfilteredCountWhere(v.where)) {
|
|
1161
|
+
const target = relTargetModel(ctx, mm, rel.name);
|
|
1162
|
+
const targetName = target ? modelName(ctx, target) : undefined;
|
|
1163
|
+
const delegate = targetName ? (prismaPropertyAlias(targetName) ?? targetName) : '<model>';
|
|
1164
|
+
throw new errors_js_1.UnsupportedFeatureError(`a filtered relation count (\`_count.select.${key}.where\` on model "${model}")`, 'prisma-compat', 'Turbine counts every row of a relation, so the filter would be dropped and the number would be wrong. ' +
|
|
1165
|
+
`Count the filtered rows through the related model instead: prisma.${delegate}.count({ where: ... }) ` +
|
|
1166
|
+
`with the foreign key and your filter, or prisma.${delegate}.groupBy({ by: [<foreign key>], _count: true }) ` +
|
|
1167
|
+
'for many parents at once.');
|
|
1168
|
+
}
|
|
1169
|
+
(0, filters_js_1.setOwn)(out, rel.name, true);
|
|
1095
1170
|
}
|
|
1096
|
-
return
|
|
1171
|
+
return out;
|
|
1097
1172
|
}
|
|
1098
1173
|
function modelName(ctx, mm) {
|
|
1099
1174
|
return ctx.tableToModel.get(mm.table) ?? mm.table;
|
|
1100
1175
|
}
|
|
1101
1176
|
/**
|
|
1102
|
-
*
|
|
1103
|
-
* `
|
|
1104
|
-
* a
|
|
1177
|
+
* A Prisma field list that is written either as one name or as an array of
|
|
1178
|
+
* names (`distinct`, groupBy `by`), renamed into turbine fields. Anything else
|
|
1179
|
+
* is refused: a bare string used to be dropped (`distinct: 'status'` returned
|
|
1180
|
+
* every row, and `by: 'status'` crashed with a TypeError).
|
|
1181
|
+
*/
|
|
1182
|
+
function translateFieldList(ctx, mm, option, val) {
|
|
1183
|
+
const names = typeof val === 'string' ? [val] : val;
|
|
1184
|
+
if (!Array.isArray(names) || names.some((n) => typeof n !== 'string')) {
|
|
1185
|
+
throw new errors_js_1.ValidationError(`prisma-compat: \`${option}\` on model "${modelName(ctx, mm)}" takes a field name or an array of field names, ` +
|
|
1186
|
+
`received ${describeValue(val)}.`);
|
|
1187
|
+
}
|
|
1188
|
+
return names.map((f) => renameField(mm, f));
|
|
1189
|
+
}
|
|
1190
|
+
/**
|
|
1191
|
+
* Assemble Turbine findMany-family args from Prisma read args. `where` /
|
|
1192
|
+
* `orderBy` are translated first, so the cursor step works in turbine-field
|
|
1193
|
+
* space.
|
|
1105
1194
|
*/
|
|
1106
1195
|
function translateReadArgs(ctx, mm, prismaArgs, kind) {
|
|
1107
1196
|
const t = {};
|
|
@@ -1123,19 +1212,11 @@ function translateReadArgs(ctx, mm, prismaArgs, kind) {
|
|
|
1123
1212
|
t.omit = proj.omit;
|
|
1124
1213
|
if (proj.with)
|
|
1125
1214
|
t.with = proj.with;
|
|
1126
|
-
if (
|
|
1127
|
-
t.distinct =
|
|
1128
|
-
}
|
|
1215
|
+
if (prismaArgs.distinct !== undefined)
|
|
1216
|
+
t.distinct = translateFieldList(ctx, mm, 'distinct', prismaArgs.distinct);
|
|
1129
1217
|
if (prismaArgs.relationLoadStrategy !== undefined) {
|
|
1130
1218
|
t.relationLoadStrategy = mapRelationLoadStrategy(prismaArgs.relationLoadStrategy);
|
|
1131
1219
|
}
|
|
1132
|
-
// ORDER IS LOAD-BEARING: translateCursor MUST run BEFORE applyImplicitPkOrder.
|
|
1133
|
-
// The cursor translation reads `t.orderBy` to decide the seek direction and to
|
|
1134
|
-
// validate that a bare inclusive cursor names the sort key; it must see the
|
|
1135
|
-
// USER's ordering, never an ordering this layer invented. Swap these two lines
|
|
1136
|
-
// and a bare inclusive cursor on a non-PK field silently compiles against the
|
|
1137
|
-
// injected primary-key order instead of throwing. See the guard test
|
|
1138
|
-
// "cursor decisions are made against the user's own orderBy".
|
|
1139
1220
|
translateCursor(ctx, mm, prismaArgs, t);
|
|
1140
1221
|
applyImplicitPkOrder(ctx, mm, t, kind);
|
|
1141
1222
|
return t;
|
|
@@ -1182,15 +1263,9 @@ function translateReadArgs(ctx, mm, prismaArgs, kind) {
|
|
|
1182
1263
|
* with `column "id" does not exist`. Injecting also lifts the `LIMIT` outside
|
|
1183
1264
|
* the `DISTINCT ON`, so the inner scan stops being limited. `distinct` reads
|
|
1184
1265
|
* keep the exact SQL they emitted before this ordering existed.
|
|
1185
|
-
* - `cursor`:
|
|
1186
|
-
*
|
|
1187
|
-
*
|
|
1188
|
-
* primary-key order. The injection is safe because it runs strictly AFTER
|
|
1189
|
-
* {@link translateCursor}: the cursor comparison direction was already
|
|
1190
|
-
* resolved from the user's own `orderBy` (ascending by default, matching an
|
|
1191
|
-
* ascending primary key), and the bare-inclusive-cursor branch has already
|
|
1192
|
-
* set or demanded an `orderBy`, so only the exclusive `cursor` + `skip`
|
|
1193
|
-
* branch reaches here. It gains an `ORDER BY <pk> ASC` and nothing else.
|
|
1266
|
+
* - `cursor`: not excluded, and harmless either way. Core orders every cursor
|
|
1267
|
+
* page itself (the caller's order plus a primary-key tiebreaker), and with no
|
|
1268
|
+
* `orderBy` that is exactly the `ORDER BY <pk> ASC` written here.
|
|
1194
1269
|
*/
|
|
1195
1270
|
function applyImplicitPkOrder(ctx, mm, t, kind) {
|
|
1196
1271
|
if (kind === 'unique')
|
|
@@ -1201,134 +1276,59 @@ function applyImplicitPkOrder(ctx, mm, t, kind) {
|
|
|
1201
1276
|
return;
|
|
1202
1277
|
if (kind === 'many' && t.limit === undefined && t.offset === undefined)
|
|
1203
1278
|
return;
|
|
1204
|
-
const meta = ctx.schema.tables
|
|
1279
|
+
const meta = (0, utils_js_1.ownLookup)(ctx.schema.tables, mm.table);
|
|
1205
1280
|
const pk = meta?.primaryKey ?? [];
|
|
1206
1281
|
if (pk.length === 0)
|
|
1207
1282
|
return;
|
|
1208
1283
|
const fields = pk.map((col) => meta?.reverseColumnMap?.[col] ?? col);
|
|
1209
1284
|
t.orderBy = fields.length === 1 ? { [fields[0]]: 'asc' } : fields.map((f) => ({ [f]: 'asc' }));
|
|
1210
1285
|
}
|
|
1211
|
-
/** turbine field names of the model's single-column primary key, if any. */
|
|
1212
|
-
function singleColumnPkField(ctx, mm) {
|
|
1213
|
-
const pk = ctx.schema.tables[mm.table]?.primaryKey;
|
|
1214
|
-
if (pk && pk.length === 1) {
|
|
1215
|
-
const col = pk[0];
|
|
1216
|
-
return ctx.schema.tables[mm.table]?.reverseColumnMap?.[col] ?? col;
|
|
1217
|
-
}
|
|
1218
|
-
return undefined;
|
|
1219
|
-
}
|
|
1220
1286
|
/**
|
|
1221
1287
|
* Translate Prisma cursor + take/skip onto Turbine args (operating on already
|
|
1222
1288
|
* translated `t.where` / `t.orderBy` in turbine-field space).
|
|
1223
1289
|
*
|
|
1224
1290
|
* - **No cursor:** `skip`→`offset`, `take`→`limit`.
|
|
1225
|
-
* - **Cursor
|
|
1226
|
-
*
|
|
1227
|
-
*
|
|
1228
|
-
*
|
|
1229
|
-
*
|
|
1230
|
-
*
|
|
1231
|
-
*
|
|
1232
|
-
* single-column PK). Any other shape THROWS rather than emit a wrong page -
|
|
1233
|
-
* pair the cursor with `skip: 1` for the exact exclusive translation.
|
|
1291
|
+
* - **Cursor:** core's cursor with the {@link CURSOR_INCLUSIVE} flag, plus
|
|
1292
|
+
* `offset: skip`. That is Prisma's definition exactly: the page starts AT the
|
|
1293
|
+
* cursor row, in the query's order (core completes it with a primary-key
|
|
1294
|
+
* tiebreaker), and `skip` counts from there, so the `skip: 1` idiom drops the
|
|
1295
|
+
* cursor row. Core places the page from the row the cursor names by a unique
|
|
1296
|
+
* key, which is what a Prisma cursor always is, so a cursor whose row has
|
|
1297
|
+
* been deleted gives an empty page, as it does in Prisma.
|
|
1234
1298
|
*/
|
|
1235
|
-
function translateCursor(
|
|
1299
|
+
function translateCursor(_ctx, mm, prismaArgs, t) {
|
|
1236
1300
|
const cursor = prismaArgs.cursor;
|
|
1237
1301
|
const skip = prismaArgs.skip;
|
|
1238
1302
|
const take = prismaArgs.take;
|
|
1239
1303
|
if (take !== undefined)
|
|
1240
1304
|
t.limit = mapTake(take);
|
|
1241
|
-
if (
|
|
1242
|
-
|
|
1243
|
-
|
|
1305
|
+
if (skip !== undefined)
|
|
1306
|
+
t.offset = skip;
|
|
1307
|
+
if (cursor === undefined)
|
|
1308
|
+
return;
|
|
1309
|
+
// A cursor that is not an object (`null`) reaches core as written, which
|
|
1310
|
+
// refuses it: read as "no cursor" it would return the first page.
|
|
1311
|
+
if (!(0, filters_js_1.isPlainRecord)(cursor)) {
|
|
1312
|
+
t.cursor = cursor;
|
|
1244
1313
|
return;
|
|
1245
1314
|
}
|
|
1246
1315
|
// Translate cursor field names (and expand a compound-unique selector cursor).
|
|
1316
|
+
// An `undefined` value is carried through rather than dropped, so a cursor
|
|
1317
|
+
// whose every value was `undefined` reaches core's args seam as one and is
|
|
1318
|
+
// refused there (E003), instead of becoming `{}` and paging from the start.
|
|
1247
1319
|
const tcursor = {};
|
|
1248
1320
|
for (const [key, val] of Object.entries(cursor)) {
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
const compound = mm.compoundUniques[key];
|
|
1252
|
-
if (compound && isPlainObject(val)) {
|
|
1321
|
+
const compound = (0, utils_js_1.ownLookup)(mm.compoundUniques, key);
|
|
1322
|
+
if (compound && (0, filters_js_1.isPlainRecord)(val)) {
|
|
1253
1323
|
for (const [pk, pv] of Object.entries(val))
|
|
1254
|
-
tcursor
|
|
1324
|
+
(0, filters_js_1.setOwn)(tcursor, renameField(mm, pk), pv);
|
|
1255
1325
|
}
|
|
1256
1326
|
else {
|
|
1257
|
-
tcursor
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
const cursorFields = Object.keys(tcursor);
|
|
1261
|
-
if (skip !== undefined && skip >= 1) {
|
|
1262
|
-
t.cursor = tcursor;
|
|
1263
|
-
t.offset = skip - 1;
|
|
1264
|
-
return;
|
|
1265
|
-
}
|
|
1266
|
-
// Bare inclusive cursor (skip absent or 0).
|
|
1267
|
-
if (cursorFields.length !== 1) {
|
|
1268
|
-
throw new errors_js_1.UnsupportedFeatureError('inclusive multi-field cursor without skip', 'prisma-compat', 'Pair the cursor with `skip: 1` (the Prisma idiom) so it maps to an exact exclusive cursor.');
|
|
1269
|
-
}
|
|
1270
|
-
const field = cursorFields[0];
|
|
1271
|
-
const value = tcursor[field];
|
|
1272
|
-
const obEntries = orderByPairs(t.orderBy);
|
|
1273
|
-
let desc = false;
|
|
1274
|
-
if (obEntries.length === 0) {
|
|
1275
|
-
const pkField = singleColumnPkField(ctx, mm);
|
|
1276
|
-
if (!pkField || field !== pkField) {
|
|
1277
|
-
throw new errors_js_1.UnsupportedFeatureError('inclusive cursor without orderBy on a non-PK field', 'prisma-compat', `A bare Prisma cursor is inclusive; translating it needs the cursor field "${field}" to be the single-column primary key, or to be the single orderBy field. Pair the cursor with \`skip: 1\`.`);
|
|
1278
|
-
}
|
|
1279
|
-
t.orderBy = { [field]: 'asc' };
|
|
1280
|
-
}
|
|
1281
|
-
else {
|
|
1282
|
-
if (obEntries.length !== 1 || obEntries[0][0] !== field) {
|
|
1283
|
-
throw new errors_js_1.UnsupportedFeatureError('inclusive cursor whose field is not the sort key', 'prisma-compat', `A bare Prisma cursor is inclusive; translating it needs the cursor field "${field}" to be the single orderBy field. Order by "${field}", or pair the cursor with \`skip: 1\`.`);
|
|
1327
|
+
(0, filters_js_1.setOwn)(tcursor, renameField(mm, key), val);
|
|
1284
1328
|
}
|
|
1285
|
-
desc = obEntries[0][1];
|
|
1286
1329
|
}
|
|
1287
|
-
|
|
1288
|
-
t.
|
|
1289
|
-
}
|
|
1290
|
-
/**
|
|
1291
|
-
* Flatten a Turbine orderBy (object, or an array of them) into [field, isDesc]
|
|
1292
|
-
* pairs.
|
|
1293
|
-
*
|
|
1294
|
-
* EVERY array element is read. Reading only an array of length one returned
|
|
1295
|
-
* `[]` for a two-element array, and `[]` is also what "no orderBy at all"
|
|
1296
|
-
* returns, so {@link translateCursor} took its no-orderBy branch: it found a
|
|
1297
|
-
* bare cursor on the primary key, wrote `orderBy = { id: 'asc' }` over the
|
|
1298
|
-
* caller's array, and returned the rows following the anchor in PK order
|
|
1299
|
-
* instead of in the caller's order. The single-sort-key check downstream is
|
|
1300
|
-
* what refuses a shape a bare (inclusive) cursor cannot express, and it can
|
|
1301
|
-
* only fire if the pairs it counts are all there.
|
|
1302
|
-
*/
|
|
1303
|
-
function orderByPairs(ob) {
|
|
1304
|
-
const entries = Array.isArray(ob) ? ob : [ob];
|
|
1305
|
-
const out = [];
|
|
1306
|
-
for (const entry of entries) {
|
|
1307
|
-
if (!isPlainObject(entry))
|
|
1308
|
-
continue;
|
|
1309
|
-
for (const [k, v] of Object.entries(entry)) {
|
|
1310
|
-
const dir = isPlainObject(v) ? v.sort : v;
|
|
1311
|
-
out.push([k, dir === 'desc']);
|
|
1312
|
-
}
|
|
1313
|
-
}
|
|
1314
|
-
return out;
|
|
1315
|
-
}
|
|
1316
|
-
/** Merge a `{ field: { gte|lte: value } }` keyset predicate into a where object. */
|
|
1317
|
-
function mergeKeyset(where, field, op, value) {
|
|
1318
|
-
const merged = { ...where };
|
|
1319
|
-
const existing = merged[field];
|
|
1320
|
-
if (isPlainObject(existing)) {
|
|
1321
|
-
merged[field] = { ...existing, [op]: value };
|
|
1322
|
-
}
|
|
1323
|
-
else if (existing !== undefined) {
|
|
1324
|
-
const prevAnd = merged.AND;
|
|
1325
|
-
const andList = Array.isArray(prevAnd) ? prevAnd : prevAnd !== undefined ? [prevAnd] : [];
|
|
1326
|
-
merged.AND = [...andList, { [field]: { [op]: value } }];
|
|
1327
|
-
}
|
|
1328
|
-
else {
|
|
1329
|
-
merged[field] = { [op]: value };
|
|
1330
|
-
}
|
|
1331
|
-
return merged;
|
|
1330
|
+
t.cursor = tcursor;
|
|
1331
|
+
t[normalize_args_js_1.CURSOR_INCLUSIVE] = true;
|
|
1332
1332
|
}
|
|
1333
1333
|
// --- write-data translation (nested writes) -------------------------------
|
|
1334
1334
|
const NESTED_WRITE_OPS = new Set([
|
|
@@ -1352,20 +1352,36 @@ const NESTED_WRITE_OPS = new Set([
|
|
|
1352
1352
|
*/
|
|
1353
1353
|
function translateWriteData(ctx, mm, data, depth = 0) {
|
|
1354
1354
|
assertTranslateDepth(depth, 'data');
|
|
1355
|
-
if (!
|
|
1355
|
+
if (!(0, filters_js_1.isPlainRecord)(data))
|
|
1356
1356
|
return data;
|
|
1357
1357
|
const out = {};
|
|
1358
1358
|
for (const [key, val] of Object.entries(data)) {
|
|
1359
|
-
|
|
1360
|
-
|
|
1359
|
+
if (isExpressionNode(val)) {
|
|
1360
|
+
// The field names inside an expression are Turbine's, and this layer
|
|
1361
|
+
// renames only the keys it can see, so one would be compiled against
|
|
1362
|
+
// the wrong names wherever a Prisma field is mapped.
|
|
1363
|
+
throw new errors_js_1.UnsupportedFeatureError(`an update expression (expr / col()) in "${key}" on model "${modelName(ctx, mm)}"`, 'prisma-compat', 'Expressions are a Turbine-native API: run this write through the Turbine client, where field names ' +
|
|
1364
|
+
'are its own, or compute the value in JavaScript.');
|
|
1365
|
+
}
|
|
1366
|
+
const rel = (0, utils_js_1.ownLookup)(mm.relations, key);
|
|
1367
|
+
if (rel && (0, filters_js_1.isPlainRecord)(val) && Object.keys(val).some((k) => NESTED_WRITE_OPS.has(k))) {
|
|
1361
1368
|
const target = relTargetModel(ctx, mm, rel.name);
|
|
1362
|
-
out
|
|
1369
|
+
(0, filters_js_1.setOwn)(out, rel.name, translateNestedWrite(ctx, target, val, depth + 1));
|
|
1363
1370
|
continue;
|
|
1364
1371
|
}
|
|
1365
|
-
out
|
|
1372
|
+
(0, filters_js_1.setOwn)(out, renameField(mm, key), val);
|
|
1366
1373
|
}
|
|
1367
1374
|
return out;
|
|
1368
1375
|
}
|
|
1376
|
+
/**
|
|
1377
|
+
* An update expression (`expr.*`) or column reference (`col()`), read by the
|
|
1378
|
+
* brands' registry keys so this shim imports neither builder module.
|
|
1379
|
+
*/
|
|
1380
|
+
function isExpressionNode(value) {
|
|
1381
|
+
return (typeof value === 'object' &&
|
|
1382
|
+
value !== null &&
|
|
1383
|
+
(Symbol.for('turbine-orm.sqlExpr') in value || Symbol.for('turbine-orm.columnRef') in value));
|
|
1384
|
+
}
|
|
1369
1385
|
/**
|
|
1370
1386
|
* One relation's nested-write ops. `depth` is threaded into EVERY payload
|
|
1371
1387
|
* translator, the `where` ones included, so the budget is spent on total
|
|
@@ -1393,10 +1409,8 @@ function translateNestedWrite(ctx, target, ops, depth) {
|
|
|
1393
1409
|
break;
|
|
1394
1410
|
case 'deleteMany':
|
|
1395
1411
|
case 'updateMany':
|
|
1396
|
-
out[op] = mapMaybeArray(payload, (p) => translateWhereDataPair(ctx, target, p, depth + 1));
|
|
1397
|
-
break;
|
|
1398
1412
|
case 'update':
|
|
1399
|
-
out[op] = mapMaybeArray(payload, (p) => translateWhereDataPair(ctx, target, p, depth + 1));
|
|
1413
|
+
out[op] = mapMaybeArray(payload, (p) => translateWhereDataPair(ctx, target, op, p, depth + 1));
|
|
1400
1414
|
break;
|
|
1401
1415
|
case 'connectOrCreate':
|
|
1402
1416
|
out[op] = mapMaybeArray(payload, (p) => translateConnectOrCreate(ctx, target, p, depth + 1));
|
|
@@ -1405,7 +1419,7 @@ function translateNestedWrite(ctx, target, ops, depth) {
|
|
|
1405
1419
|
out[op] = mapMaybeArray(payload, (p) => translateUpsertNested(ctx, target, p, depth + 1));
|
|
1406
1420
|
break;
|
|
1407
1421
|
default:
|
|
1408
|
-
out
|
|
1422
|
+
(0, filters_js_1.setOwn)(out, op, payload);
|
|
1409
1423
|
}
|
|
1410
1424
|
}
|
|
1411
1425
|
return out;
|
|
@@ -1413,11 +1427,27 @@ function translateNestedWrite(ctx, target, ops, depth) {
|
|
|
1413
1427
|
function mapMaybeArray(val, fn) {
|
|
1414
1428
|
return Array.isArray(val) ? val.map(fn) : fn(val);
|
|
1415
1429
|
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Refuse a key on a nested write's `{ where, data }` / `{ where, create, update }`
|
|
1432
|
+
* payload that this layer does not read: it was dropped, so the part of the
|
|
1433
|
+
* write the caller meant never ran. An `undefined` value is absent.
|
|
1434
|
+
*/
|
|
1435
|
+
function assertNestedPayloadKeys(op, p, allowed) {
|
|
1436
|
+
for (const key of Object.keys(p)) {
|
|
1437
|
+
if (p[key] === undefined || allowed.includes(key))
|
|
1438
|
+
continue;
|
|
1439
|
+
const suggestion = (0, utils_js_1.suggestKey)(key, allowed);
|
|
1440
|
+
throw new errors_js_1.ValidationError(`prisma-compat: unknown key "${key}" in a nested ${op}, it would be ignored.` +
|
|
1441
|
+
(suggestion ? ` Did you mean "${suggestion}"?` : '') +
|
|
1442
|
+
` Valid keys: ${allowed.join(', ')}.`);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1416
1445
|
/** A `{ where?, data }` pair (nested update/updateMany), or a bare data object. */
|
|
1417
|
-
function translateWhereDataPair(ctx, target, p, depth) {
|
|
1418
|
-
if (!
|
|
1446
|
+
function translateWhereDataPair(ctx, target, op, p, depth) {
|
|
1447
|
+
if (!(0, filters_js_1.isPlainRecord)(p))
|
|
1419
1448
|
return p;
|
|
1420
1449
|
if ('data' in p || 'where' in p) {
|
|
1450
|
+
assertNestedPayloadKeys(op, p, ['where', 'data']);
|
|
1421
1451
|
const out = {};
|
|
1422
1452
|
if (p.where !== undefined)
|
|
1423
1453
|
out.where = target ? translateWhere(ctx, target, p.where, depth) : p.where;
|
|
@@ -1428,8 +1458,9 @@ function translateWhereDataPair(ctx, target, p, depth) {
|
|
|
1428
1458
|
return target ? translateWriteData(ctx, target, p, depth) : p;
|
|
1429
1459
|
}
|
|
1430
1460
|
function translateConnectOrCreate(ctx, target, p, depth) {
|
|
1431
|
-
if (!
|
|
1461
|
+
if (!(0, filters_js_1.isPlainRecord)(p))
|
|
1432
1462
|
return p;
|
|
1463
|
+
assertNestedPayloadKeys('connectOrCreate', p, ['where', 'create']);
|
|
1433
1464
|
const out = {};
|
|
1434
1465
|
if (p.where !== undefined)
|
|
1435
1466
|
out.where = target ? translateWhere(ctx, target, p.where, depth) : p.where;
|
|
@@ -1438,8 +1469,9 @@ function translateConnectOrCreate(ctx, target, p, depth) {
|
|
|
1438
1469
|
return out;
|
|
1439
1470
|
}
|
|
1440
1471
|
function translateUpsertNested(ctx, target, p, depth) {
|
|
1441
|
-
if (!
|
|
1472
|
+
if (!(0, filters_js_1.isPlainRecord)(p))
|
|
1442
1473
|
return p;
|
|
1474
|
+
assertNestedPayloadKeys('upsert', p, ['where', 'create', 'update']);
|
|
1443
1475
|
const out = {};
|
|
1444
1476
|
if (p.where !== undefined)
|
|
1445
1477
|
out.where = target ? translateWhere(ctx, target, p.where, depth) : p.where;
|
|
@@ -1451,8 +1483,24 @@ function translateUpsertNested(ctx, target, p, depth) {
|
|
|
1451
1483
|
}
|
|
1452
1484
|
// --- aggregate / groupBy translation --------------------------------------
|
|
1453
1485
|
const AGG_FIELD_BLOCKS = ['_sum', '_avg', '_min', '_max'];
|
|
1486
|
+
/**
|
|
1487
|
+
* Prisma's `count` and `aggregate` with `take` / `skip` / `cursor` compute over
|
|
1488
|
+
* that PAGE of rows. Turbine has no paged count or aggregate, and answering
|
|
1489
|
+
* over every matching row instead is a different number, so it is refused.
|
|
1490
|
+
* `orderBy` on its own changes no count or aggregate and is accepted.
|
|
1491
|
+
*/
|
|
1492
|
+
function refusePagedAggregate(op, args) {
|
|
1493
|
+
const paged = ['take', 'skip', 'cursor'].filter((k) => args[k] !== undefined);
|
|
1494
|
+
if (paged.length === 0)
|
|
1495
|
+
return;
|
|
1496
|
+
throw new errors_js_1.UnsupportedFeatureError(`${paged.join(' / ')} on ${op}`, 'prisma-compat', `Prisma computes this ${op} over one page of rows, and Turbine has no paged ${op}. Fetch the page with ` +
|
|
1497
|
+
`findMany (same where / orderBy / ${paged.join(' / ')}) and compute it from the rows, or drop ` +
|
|
1498
|
+
`${paged.join(' / ')} to ${op} every matching row.`);
|
|
1499
|
+
}
|
|
1454
1500
|
function translateAggregateArgs(ctx, mm, args, isGroupBy) {
|
|
1455
1501
|
const t = {};
|
|
1502
|
+
if (!isGroupBy)
|
|
1503
|
+
refusePagedAggregate('aggregate', args);
|
|
1456
1504
|
(0, index_js_1.applyNativeOptions)(isGroupBy ? index_js_1.GROUP_BY_OPTIONS : index_js_1.AGGREGATE_OPTIONS, args, t);
|
|
1457
1505
|
if (args.where !== undefined)
|
|
1458
1506
|
t.where = translateWhere(ctx, mm, args.where);
|
|
@@ -1463,17 +1511,19 @@ function translateAggregateArgs(ctx, mm, args, isGroupBy) {
|
|
|
1463
1511
|
t[block] = renameAggBlock(mm, args[block], false);
|
|
1464
1512
|
}
|
|
1465
1513
|
if (isGroupBy) {
|
|
1466
|
-
if (
|
|
1467
|
-
|
|
1514
|
+
if (args.by === undefined) {
|
|
1515
|
+
throw new errors_js_1.ValidationError(`prisma-compat: groupBy on model "${modelName(ctx, mm)}" requires \`by\`, a field name or an array of field names.`);
|
|
1516
|
+
}
|
|
1517
|
+
t.by = translateFieldList(ctx, mm, 'by', args.by);
|
|
1468
1518
|
if (args.orderBy !== undefined)
|
|
1469
1519
|
t.orderBy = translateOrderBy(ctx, mm, args.orderBy);
|
|
1470
1520
|
if (args.having !== undefined)
|
|
1471
1521
|
t.having = renameHaving(mm, args.having);
|
|
1472
1522
|
if (args.distinctOn !== undefined)
|
|
1473
1523
|
t.distinctOn = translateDistinctOn(ctx, mm, args.distinctOn);
|
|
1474
|
-
if (
|
|
1524
|
+
if (args.take !== undefined)
|
|
1475
1525
|
t.limit = mapTake(args.take);
|
|
1476
|
-
if (
|
|
1526
|
+
if (args.skip !== undefined)
|
|
1477
1527
|
t.offset = args.skip;
|
|
1478
1528
|
}
|
|
1479
1529
|
return t;
|
|
@@ -1486,7 +1536,7 @@ function translateAggregateArgs(ctx, mm, args, isGroupBy) {
|
|
|
1486
1536
|
* silently wrong on one with a `@map`.
|
|
1487
1537
|
*/
|
|
1488
1538
|
function translateDistinctOn(ctx, mm, val) {
|
|
1489
|
-
if (!
|
|
1539
|
+
if (!(0, filters_js_1.isPlainRecord)(val))
|
|
1490
1540
|
return val;
|
|
1491
1541
|
const out = { ...val };
|
|
1492
1542
|
if (Array.isArray(val.columns))
|
|
@@ -1497,7 +1547,7 @@ function translateDistinctOn(ctx, mm, val) {
|
|
|
1497
1547
|
}
|
|
1498
1548
|
/** Rename field keys inside an aggregate block; `_count`'s `_all` passes through. */
|
|
1499
1549
|
function renameAggBlock(mm, block, isCount) {
|
|
1500
|
-
if (block === true || !
|
|
1550
|
+
if (block === true || !(0, filters_js_1.isPlainRecord)(block))
|
|
1501
1551
|
return block;
|
|
1502
1552
|
const out = {};
|
|
1503
1553
|
for (const [key, val] of Object.entries(block)) {
|
|
@@ -1505,7 +1555,7 @@ function renameAggBlock(mm, block, isCount) {
|
|
|
1505
1555
|
out._all = val;
|
|
1506
1556
|
continue;
|
|
1507
1557
|
}
|
|
1508
|
-
out
|
|
1558
|
+
(0, filters_js_1.setOwn)(out, renameField(mm, key), val);
|
|
1509
1559
|
}
|
|
1510
1560
|
return out;
|
|
1511
1561
|
}
|
|
@@ -1520,7 +1570,7 @@ function renameAggBlock(mm, block, isCount) {
|
|
|
1520
1570
|
*/
|
|
1521
1571
|
function renameHaving(mm, having, depth = 0) {
|
|
1522
1572
|
assertTranslateDepth(depth, 'having');
|
|
1523
|
-
if (!
|
|
1573
|
+
if (!(0, filters_js_1.isPlainRecord)(having))
|
|
1524
1574
|
return having;
|
|
1525
1575
|
const out = {};
|
|
1526
1576
|
for (const [key, val] of Object.entries(having)) {
|
|
@@ -1532,7 +1582,7 @@ function renameHaving(mm, having, depth = 0) {
|
|
|
1532
1582
|
out._count = val;
|
|
1533
1583
|
continue;
|
|
1534
1584
|
}
|
|
1535
|
-
out
|
|
1585
|
+
(0, filters_js_1.setOwn)(out, renameField(mm, key), val);
|
|
1536
1586
|
}
|
|
1537
1587
|
return out;
|
|
1538
1588
|
}
|
|
@@ -1546,7 +1596,7 @@ function reshapeRowOrNull(ctx, mm, row) {
|
|
|
1546
1596
|
return row == null ? null : reshapeRow(ctx, mm, row);
|
|
1547
1597
|
}
|
|
1548
1598
|
function reshapeRow(ctx, mm, row) {
|
|
1549
|
-
if (!
|
|
1599
|
+
if (!(0, filters_js_1.isPlainRecord)(row))
|
|
1550
1600
|
return row;
|
|
1551
1601
|
const l = lookupsFor(ctx, mm);
|
|
1552
1602
|
const out = {};
|
|
@@ -1555,7 +1605,7 @@ function reshapeRow(ctx, mm, row) {
|
|
|
1555
1605
|
out._count = reshapeCount(l, val);
|
|
1556
1606
|
continue;
|
|
1557
1607
|
}
|
|
1558
|
-
const rel = l.reverseRelations
|
|
1608
|
+
const rel = (0, utils_js_1.ownLookup)(l.reverseRelations, key);
|
|
1559
1609
|
if (rel) {
|
|
1560
1610
|
const target = relTargetModel(ctx, mm, key);
|
|
1561
1611
|
let rv;
|
|
@@ -1565,25 +1615,25 @@ function reshapeRow(ctx, mm, row) {
|
|
|
1565
1615
|
// map's cardinality is authoritative → first element or null.
|
|
1566
1616
|
rv = rel.cardinality === 'one' ? (mapped.length ? mapped[0] : null) : mapped;
|
|
1567
1617
|
}
|
|
1568
|
-
else if (
|
|
1618
|
+
else if ((0, filters_js_1.isPlainRecord)(val)) {
|
|
1569
1619
|
rv = target ? reshapeRow(ctx, target, val) : val;
|
|
1570
1620
|
}
|
|
1571
1621
|
else {
|
|
1572
1622
|
rv = val; // null to-one
|
|
1573
1623
|
}
|
|
1574
|
-
out
|
|
1624
|
+
(0, filters_js_1.setOwn)(out, rel.prismaName, rv);
|
|
1575
1625
|
continue;
|
|
1576
1626
|
}
|
|
1577
1627
|
let sv = val;
|
|
1578
1628
|
if (typeof sv === 'string' && l.timeFields.has(key)) {
|
|
1579
1629
|
sv = timeStringToDate(sv) ?? sv;
|
|
1580
1630
|
}
|
|
1581
|
-
out
|
|
1631
|
+
(0, filters_js_1.setOwn)(out, l.identityFields ? key : ((0, utils_js_1.ownLookup)(l.reverseFields, key) ?? key), sv);
|
|
1582
1632
|
}
|
|
1583
1633
|
return out;
|
|
1584
1634
|
}
|
|
1585
1635
|
function reshapeCount(l, count) {
|
|
1586
|
-
if (!
|
|
1636
|
+
if (!(0, filters_js_1.isPlainRecord)(count))
|
|
1587
1637
|
return count;
|
|
1588
1638
|
const out = {};
|
|
1589
1639
|
for (const [key, val] of Object.entries(count)) {
|
|
@@ -1591,13 +1641,13 @@ function reshapeCount(l, count) {
|
|
|
1591
1641
|
out._all = val;
|
|
1592
1642
|
continue;
|
|
1593
1643
|
}
|
|
1594
|
-
out
|
|
1644
|
+
(0, filters_js_1.setOwn)(out, (0, utils_js_1.ownLookup)(l.reverseRelations, key)?.prismaName ?? key, val);
|
|
1595
1645
|
}
|
|
1596
1646
|
return out;
|
|
1597
1647
|
}
|
|
1598
1648
|
/** Reshape an aggregate result: field keys inside blocks → prisma names. */
|
|
1599
1649
|
function reshapeAggregate(ctx, mm, res) {
|
|
1600
|
-
if (!
|
|
1650
|
+
if (!(0, filters_js_1.isPlainRecord)(res))
|
|
1601
1651
|
return res;
|
|
1602
1652
|
const l = lookupsFor(ctx, mm);
|
|
1603
1653
|
const out = {};
|
|
@@ -1614,12 +1664,12 @@ function reshapeAggregate(ctx, mm, res) {
|
|
|
1614
1664
|
if (typeof sv === 'string' && l.timeFields.has(key)) {
|
|
1615
1665
|
sv = timeStringToDate(sv) ?? sv;
|
|
1616
1666
|
}
|
|
1617
|
-
out
|
|
1667
|
+
(0, filters_js_1.setOwn)(out, l.identityFields ? key : ((0, utils_js_1.ownLookup)(l.reverseFields, key) ?? key), sv);
|
|
1618
1668
|
}
|
|
1619
1669
|
return out;
|
|
1620
1670
|
}
|
|
1621
1671
|
function reshapeAggFieldBlock(l, block, isCount) {
|
|
1622
|
-
if (!
|
|
1672
|
+
if (!(0, filters_js_1.isPlainRecord)(block))
|
|
1623
1673
|
return block;
|
|
1624
1674
|
const out = {};
|
|
1625
1675
|
for (const [key, val] of Object.entries(block)) {
|
|
@@ -1627,13 +1677,13 @@ function reshapeAggFieldBlock(l, block, isCount) {
|
|
|
1627
1677
|
out._all = val;
|
|
1628
1678
|
continue;
|
|
1629
1679
|
}
|
|
1630
|
-
out
|
|
1680
|
+
(0, filters_js_1.setOwn)(out, (0, utils_js_1.ownLookup)(l.reverseFields, key) ?? key, val);
|
|
1631
1681
|
}
|
|
1632
1682
|
return out;
|
|
1633
1683
|
}
|
|
1634
1684
|
/** Reshape a groupBy row: by-field keys + aggregate blocks → prisma names. */
|
|
1635
1685
|
function reshapeGroupRow(ctx, mm, row) {
|
|
1636
|
-
if (!
|
|
1686
|
+
if (!(0, filters_js_1.isPlainRecord)(row))
|
|
1637
1687
|
return row;
|
|
1638
1688
|
const l = lookupsFor(ctx, mm);
|
|
1639
1689
|
const out = {};
|
|
@@ -1650,7 +1700,7 @@ function reshapeGroupRow(ctx, mm, row) {
|
|
|
1650
1700
|
if (typeof sv === 'string' && l.timeFields.has(key)) {
|
|
1651
1701
|
sv = timeStringToDate(sv) ?? sv;
|
|
1652
1702
|
}
|
|
1653
|
-
out
|
|
1703
|
+
(0, filters_js_1.setOwn)(out, l.identityFields ? key : ((0, utils_js_1.ownLookup)(l.reverseFields, key) ?? key), sv);
|
|
1654
1704
|
}
|
|
1655
1705
|
return out;
|
|
1656
1706
|
}
|
|
@@ -1691,6 +1741,55 @@ class CompatPromise {
|
|
|
1691
1741
|
function batchableOf(v) {
|
|
1692
1742
|
return v instanceof CompatPromise ? v[exports.COMPAT_DEFERRED] : undefined;
|
|
1693
1743
|
}
|
|
1744
|
+
/** Warn-once namespace for a `$transaction` option this layer accepts and does not apply. */
|
|
1745
|
+
const TX_OPTION_WARN_NS = 'prismaCompatTxOption';
|
|
1746
|
+
/**
|
|
1747
|
+
* Every `$transaction` option this layer understands: Prisma's three, plus the
|
|
1748
|
+
* Turbine-native ones it forwards to core as written.
|
|
1749
|
+
*/
|
|
1750
|
+
const TX_OPTION_KEYS = new Set([
|
|
1751
|
+
'isolationLevel',
|
|
1752
|
+
'timeout',
|
|
1753
|
+
'maxWait',
|
|
1754
|
+
'sessionContext',
|
|
1755
|
+
'allowBuiltinSettings',
|
|
1756
|
+
'readOnly',
|
|
1757
|
+
]);
|
|
1758
|
+
/**
|
|
1759
|
+
* Check a `$transaction` options object and drop the one key core has no use
|
|
1760
|
+
* for. A key that is not an option used to be forwarded and ignored, so a
|
|
1761
|
+
* misspelled `isolationLevl` ran the transaction at the default isolation.
|
|
1762
|
+
* `maxWait` bounds how long Prisma waits for a pooled connection; here the
|
|
1763
|
+
* pool's own `connectionTimeoutMs` does that, so it is accepted, warned about
|
|
1764
|
+
* once, and not forwarded.
|
|
1765
|
+
*/
|
|
1766
|
+
function resolveTxOptions(log, options) {
|
|
1767
|
+
if (options === undefined || options === null)
|
|
1768
|
+
return undefined;
|
|
1769
|
+
if (!(0, filters_js_1.isPlainRecord)(options)) {
|
|
1770
|
+
throw new errors_js_1.ValidationError(`prisma-compat: $transaction options must be an object, received ${describeValue(options)}.`);
|
|
1771
|
+
}
|
|
1772
|
+
const out = {};
|
|
1773
|
+
for (const [key, value] of Object.entries(options)) {
|
|
1774
|
+
if (value === undefined)
|
|
1775
|
+
continue;
|
|
1776
|
+
if (!TX_OPTION_KEYS.has(key)) {
|
|
1777
|
+
const suggestion = (0, utils_js_1.suggestKey)(key, TX_OPTION_KEYS);
|
|
1778
|
+
throw new errors_js_1.ValidationError(`prisma-compat: unknown $transaction option "${key}", it would be ignored.` +
|
|
1779
|
+
(suggestion ? ` Did you mean "${suggestion}"?` : '') +
|
|
1780
|
+
` Valid options: ${[...TX_OPTION_KEYS].join(', ')}.`);
|
|
1781
|
+
}
|
|
1782
|
+
if (key === 'maxWait') {
|
|
1783
|
+
if (log.diagnostics.advice && (0, warn_registry_js_1.shouldWarnOnce)(TX_OPTION_WARN_NS, key)) {
|
|
1784
|
+
(0, logger_js_1.logTo)(log.logger, 'warn', 'prisma-compat: the $transaction option "maxWait" has no effect here. Waiting for a pooled connection ' +
|
|
1785
|
+
"is bounded by the client's `connectionTimeoutMs` instead.", { code: 'prisma-compat-unknown-option', operation: '$transaction', key });
|
|
1786
|
+
}
|
|
1787
|
+
continue;
|
|
1788
|
+
}
|
|
1789
|
+
out[key] = value;
|
|
1790
|
+
}
|
|
1791
|
+
return out;
|
|
1792
|
+
}
|
|
1694
1793
|
/**
|
|
1695
1794
|
* Prisma's client-property spelling of a model name: first letter lowercased
|
|
1696
1795
|
* (`model User` -> `prisma.user`). Returns null when the spelling is identical
|
|
@@ -1736,7 +1835,7 @@ function clientDefaultValue(kind) {
|
|
|
1736
1835
|
/** Fill missing create-side client defaults (uuid / cuid / now / updatedAt), by Prisma field name. */
|
|
1737
1836
|
function applyCreateDefaults(mm, data) {
|
|
1738
1837
|
const cd = mm.clientDefaults;
|
|
1739
|
-
if (!cd || !
|
|
1838
|
+
if (!cd || !(0, filters_js_1.isPlainRecord)(data))
|
|
1740
1839
|
return data;
|
|
1741
1840
|
let out = data;
|
|
1742
1841
|
for (const [field, kind] of Object.entries(cd)) {
|
|
@@ -1751,7 +1850,7 @@ function applyCreateDefaults(mm, data) {
|
|
|
1751
1850
|
/** Touch @updatedAt fields on the update side (Prisma sets them on every update). */
|
|
1752
1851
|
function applyUpdateTouch(mm, data) {
|
|
1753
1852
|
const cd = mm.clientDefaults;
|
|
1754
|
-
if (!cd || !
|
|
1853
|
+
if (!cd || !(0, filters_js_1.isPlainRecord)(data))
|
|
1755
1854
|
return data;
|
|
1756
1855
|
let out = data;
|
|
1757
1856
|
for (const [field, kind] of Object.entries(cd)) {
|
|
@@ -1765,8 +1864,8 @@ function applyUpdateTouch(mm, data) {
|
|
|
1765
1864
|
}
|
|
1766
1865
|
/** Whether TRANSLATED write data carries relation keys (nested-write shapes). */
|
|
1767
1866
|
function hasNestedKeys(ctx, mm, data) {
|
|
1768
|
-
const rels = ctx.schema.tables
|
|
1769
|
-
if (!rels || !
|
|
1867
|
+
const rels = (0, utils_js_1.ownLookup)(ctx.schema.tables, mm.table)?.relations;
|
|
1868
|
+
if (!rels || !(0, filters_js_1.isPlainRecord)(data))
|
|
1770
1869
|
return false;
|
|
1771
1870
|
return Object.keys(data).some((k) => {
|
|
1772
1871
|
if (!Object.hasOwn(rels, k))
|
|
@@ -1827,7 +1926,7 @@ function makeDelegate(ctx, mm, getQI, runInTx) {
|
|
|
1827
1926
|
// itself, for the same reason (see the comment above): a compat promise that
|
|
1828
1927
|
// is built and never awaited must produce no output at all.
|
|
1829
1928
|
const translate = () => {
|
|
1830
|
-
warnUnknownQueryOptions(modelName(ctx, mm), op, rawArgs);
|
|
1929
|
+
warnUnknownQueryOptions(ctx.log, modelName(ctx, mm), op, rawArgs);
|
|
1831
1930
|
return translateRaw();
|
|
1832
1931
|
};
|
|
1833
1932
|
const batchable = batch
|
|
@@ -1894,10 +1993,15 @@ function makeDelegate(ctx, mm, getQI, runInTx) {
|
|
|
1894
1993
|
});
|
|
1895
1994
|
},
|
|
1896
1995
|
createMany: (args) => defer('createMany', args, () => {
|
|
1996
|
+
// Prisma takes one row object or an array of them. Anything else used
|
|
1997
|
+
// to become zero rows, so the call "succeeded" with `count: 0`.
|
|
1897
1998
|
const data = args.data;
|
|
1898
|
-
const
|
|
1899
|
-
|
|
1900
|
-
:
|
|
1999
|
+
const list = Array.isArray(data) ? data : (0, filters_js_1.isPlainRecord)(data) ? [data] : undefined;
|
|
2000
|
+
if (!list) {
|
|
2001
|
+
throw new errors_js_1.ValidationError(`prisma-compat: createMany on model "${modelName(ctx, mm)}" requires \`data\`, a row object or an ` +
|
|
2002
|
+
`array of them, received ${describeValue(data)}.`);
|
|
2003
|
+
}
|
|
2004
|
+
const rows = list.map((d) => translateWriteData(ctx, mm, applyCreateDefaults(mm, d)));
|
|
1901
2005
|
const t = { data: rows };
|
|
1902
2006
|
(0, index_js_1.applyNativeOptions)(index_js_1.CREATE_MANY_OPTIONS, args, t);
|
|
1903
2007
|
if (args.skipDuplicates)
|
|
@@ -1935,7 +2039,7 @@ function makeDelegate(ctx, mm, getQI, runInTx) {
|
|
|
1935
2039
|
// `optimisticLock.field` is a FIELD NAME, so it is renamed rather than
|
|
1936
2040
|
// copied: a blind passthrough would send the Prisma spelling into core
|
|
1937
2041
|
// and break on any model whose column is `@map`ped.
|
|
1938
|
-
if (
|
|
2042
|
+
if ((0, filters_js_1.isPlainRecord)(a.optimisticLock)) {
|
|
1939
2043
|
t.optimisticLock = {
|
|
1940
2044
|
...a.optimisticLock,
|
|
1941
2045
|
field: renameField(mm, String(a.optimisticLock.field)),
|
|
@@ -2011,13 +2115,31 @@ function makeDelegate(ctx, mm, getQI, runInTx) {
|
|
|
2011
2115
|
execInTx: async (table, t) => shape(await table(mm.table).upsert(t)),
|
|
2012
2116
|
});
|
|
2013
2117
|
},
|
|
2014
|
-
count: (args = {}) =>
|
|
2015
|
-
const
|
|
2016
|
-
(
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2118
|
+
count: (args = {}) => {
|
|
2119
|
+
const a = args;
|
|
2120
|
+
// `count({ select })` answers per field (`{ _all, title }`, a non-null
|
|
2121
|
+
// count per field), which is core's aggregate `_count` record form.
|
|
2122
|
+
if ((0, filters_js_1.isPlainRecord)(a.select)) {
|
|
2123
|
+
const countOf = (raw) => reshapeAggregate(ctx, mm, raw)._count;
|
|
2124
|
+
return defer('count', args, () => {
|
|
2125
|
+
refusePagedAggregate('count', a);
|
|
2126
|
+
const t = {};
|
|
2127
|
+
(0, index_js_1.applyNativeOptions)(index_js_1.AGGREGATE_OPTIONS, a, t);
|
|
2128
|
+
if (a.where !== undefined)
|
|
2129
|
+
t.where = translateWhere(ctx, mm, a.where);
|
|
2130
|
+
t._count = renameAggBlock(mm, a.select, true);
|
|
2131
|
+
return t;
|
|
2132
|
+
}, (qi, t) => qi.aggregate(t).then(countOf), { build: (qi, t) => qi.buildAggregate(t), reshape: countOf });
|
|
2133
|
+
}
|
|
2134
|
+
return defer('count', args, () => {
|
|
2135
|
+
refusePagedAggregate('count', a);
|
|
2136
|
+
const t = {};
|
|
2137
|
+
(0, index_js_1.applyNativeOptions)(index_js_1.COUNT_OPTIONS, a, t);
|
|
2138
|
+
if (a.where !== undefined)
|
|
2139
|
+
t.where = translateWhere(ctx, mm, a.where);
|
|
2140
|
+
return t;
|
|
2141
|
+
}, (qi, t) => qi.count(t), { build: (qi, t) => qi.buildCount(t), reshape: (raw) => raw });
|
|
2142
|
+
},
|
|
2021
2143
|
aggregate: (args) => defer('aggregate', args, () => translateAggregateArgs(ctx, mm, args, false), (qi, t) => qi.aggregate(t).then((r) => reshapeAggregate(ctx, mm, r)), { build: (qi, t) => qi.buildAggregate(t), reshape: (raw) => reshapeAggregate(ctx, mm, raw) }),
|
|
2022
2144
|
groupBy: (args) => defer('groupBy', args, () => translateAggregateArgs(ctx, mm, args, true), (qi, t) => qi.groupBy(t).then((rows) => rows.map((r) => reshapeGroupRow(ctx, mm, r))), {
|
|
2023
2145
|
build: (qi, t) => qi.buildGroupBy(t),
|
|
@@ -2132,8 +2254,9 @@ const JUNCTION_WARN_NS = 'prismaCompatJunction';
|
|
|
2132
2254
|
* `$transaction` must not turn the transaction surface into a delegate).
|
|
2133
2255
|
*
|
|
2134
2256
|
* Every skip except the mapped-table one loses a capability the caller might
|
|
2135
|
-
* expect, so those warn once per junction name
|
|
2136
|
-
* already maps to is not a loss (the model IS
|
|
2257
|
+
* expect, so those warn once per junction name (a construction warning, so
|
|
2258
|
+
* always on); a table that a model already maps to is not a loss (the model IS
|
|
2259
|
+
* that table's accessor).
|
|
2137
2260
|
*/
|
|
2138
2261
|
function junctionModels(ctx, map, tableToModel) {
|
|
2139
2262
|
const out = [];
|
|
@@ -2154,16 +2277,16 @@ function junctionModels(ctx, map, tableToModel) {
|
|
|
2154
2277
|
if (rel.type !== 'manyToMany')
|
|
2155
2278
|
continue;
|
|
2156
2279
|
const name = rel.through?.table;
|
|
2157
|
-
if (!name || !ctx.schema.tables
|
|
2280
|
+
if (!name || !(0, utils_js_1.ownLookup)(ctx.schema.tables, name) || tableToModel.has(name))
|
|
2158
2281
|
continue;
|
|
2159
2282
|
if (registered.has(name))
|
|
2160
2283
|
continue;
|
|
2161
2284
|
if (seen.has(name)) {
|
|
2162
|
-
if (
|
|
2163
|
-
|
|
2285
|
+
if ((0, warn_registry_js_1.shouldWarnOnce)(JUNCTION_WARN_NS, name)) {
|
|
2286
|
+
(0, logger_js_1.logTo)(ctx.log.logger, 'warn', `prisma-compat: the many-to-many junction table "${name}" collides with an ` +
|
|
2164
2287
|
'existing client member of the same name, so no junction accessor was created for it. ' +
|
|
2165
2288
|
'Reach its rows through the owning model relation (a nested connect / disconnect / set on the ' +
|
|
2166
|
-
'related model), or through $queryRaw / $executeRaw.');
|
|
2289
|
+
'related model), or through $queryRaw / $executeRaw.', { code: 'prisma-compat-junction-collision', table: name });
|
|
2167
2290
|
}
|
|
2168
2291
|
continue;
|
|
2169
2292
|
}
|
|
@@ -2298,15 +2421,19 @@ function createPrismaCompatClient(client, map, options = {}) {
|
|
|
2298
2421
|
// the real client satisfies it structurally (the cast just relaxes the strict
|
|
2299
2422
|
// callback-param variance on `$transaction`).
|
|
2300
2423
|
const db = client;
|
|
2301
|
-
//
|
|
2302
|
-
//
|
|
2303
|
-
|
|
2424
|
+
// Log through the wrapped client's logger and diagnostics. A stand-in client
|
|
2425
|
+
// without them (a test stub) gets the console and the environment's mode.
|
|
2426
|
+
const log = client[logger_js_1.CLIENT_LOG_CONTEXT] ?? { logger: logger_js_1.consoleLogger, diagnostics: (0, diagnostics_js_1.resolveDiagnostics)() };
|
|
2427
|
+
// Fire-and-forget, once per process: is this map still describing the schema
|
|
2428
|
+
// on disk? Nothing else in the system ever asks.
|
|
2429
|
+
warnStalePrismaMap(map, log);
|
|
2304
2430
|
const tableToModel = new Map();
|
|
2305
2431
|
for (const [prismaModel, mm] of Object.entries(map.models))
|
|
2306
2432
|
tableToModel.set(mm.table, prismaModel);
|
|
2307
2433
|
const ctx = {
|
|
2308
2434
|
map,
|
|
2309
2435
|
schema: db.schema,
|
|
2436
|
+
log,
|
|
2310
2437
|
tableToModel,
|
|
2311
2438
|
lookups: new Map(),
|
|
2312
2439
|
options: {
|
|
@@ -2398,7 +2525,15 @@ function createPrismaCompatClient(client, map, options = {}) {
|
|
|
2398
2525
|
}
|
|
2399
2526
|
const base = {
|
|
2400
2527
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2401
|
-
$transaction: ((arg,
|
|
2528
|
+
$transaction: ((arg, rawTxOptions) => {
|
|
2529
|
+
// Rejected, never thrown: Prisma's $transaction is always thenable.
|
|
2530
|
+
let txOptions;
|
|
2531
|
+
try {
|
|
2532
|
+
txOptions = resolveTxOptions(ctx.log, rawTxOptions);
|
|
2533
|
+
}
|
|
2534
|
+
catch (err) {
|
|
2535
|
+
return Promise.reject(decorate(err, ctx.options.prismaErrorCodes));
|
|
2536
|
+
}
|
|
2402
2537
|
// Array (lazy batch) form. Wrapped so validation/build errors REJECT the
|
|
2403
2538
|
// returned promise (Prisma's $transaction is always thenable) rather than
|
|
2404
2539
|
// throwing synchronously.
|
|
@@ -2425,7 +2560,7 @@ function createPrismaCompatClient(client, map, options = {}) {
|
|
|
2425
2560
|
}, txOptions);
|
|
2426
2561
|
}
|
|
2427
2562
|
const deferreds = batchables.map((b) => b.build());
|
|
2428
|
-
const results = (await db.$transaction(deferreds));
|
|
2563
|
+
const results = (await db.$transaction(deferreds, txOptions));
|
|
2429
2564
|
return results.map((raw, i) => batchables[i].reshape(raw));
|
|
2430
2565
|
}
|
|
2431
2566
|
catch (err) {
|
|
@@ -2438,10 +2573,10 @@ function createPrismaCompatClient(client, map, options = {}) {
|
|
|
2438
2573
|
return db.$transaction((tx) => {
|
|
2439
2574
|
const txDelegates = {};
|
|
2440
2575
|
for (const [prismaModel, mm] of delegateModels) {
|
|
2441
|
-
txDelegates
|
|
2576
|
+
(0, filters_js_1.setOwn)(txDelegates, prismaModel, extendDelegate(makeDelegate(ctx, mm, () => tx.table(mm.table), (fn) => fn((n) => tx.table(n))), prismaModel, exts));
|
|
2442
2577
|
const alias = prismaPropertyAlias(prismaModel);
|
|
2443
|
-
if (alias && !(
|
|
2444
|
-
txDelegates
|
|
2578
|
+
if (alias && !Object.hasOwn(map.models, alias) && !Object.hasOwn(txDelegates, alias)) {
|
|
2579
|
+
(0, filters_js_1.setOwn)(txDelegates, alias, (0, utils_js_1.ownLookup)(txDelegates, prismaModel));
|
|
2445
2580
|
}
|
|
2446
2581
|
}
|
|
2447
2582
|
// Raw SQL on the transaction's own connection. Prisma's tx client
|
|
@@ -2468,13 +2603,13 @@ function createPrismaCompatClient(client, map, options = {}) {
|
|
|
2468
2603
|
// key from the map, so no dynamic-access proxy is needed.
|
|
2469
2604
|
const result = { ...base };
|
|
2470
2605
|
for (const [prismaModel, delegate] of delegates)
|
|
2471
|
-
result
|
|
2606
|
+
(0, filters_js_1.setOwn)(result, prismaModel, delegate);
|
|
2472
2607
|
// Prisma-spelling aliases (`prisma.user` for `model User`). Skipped when the
|
|
2473
2608
|
// lowercased name is itself a model or already taken (never shadow a real key).
|
|
2474
2609
|
for (const [prismaModel, delegate] of delegates) {
|
|
2475
2610
|
const alias = prismaPropertyAlias(prismaModel);
|
|
2476
|
-
if (alias && !(alias
|
|
2477
|
-
result
|
|
2611
|
+
if (alias && !Object.hasOwn(result, alias))
|
|
2612
|
+
(0, filters_js_1.setOwn)(result, alias, delegate);
|
|
2478
2613
|
}
|
|
2479
2614
|
// Extension `client` members last: every name was checked against the real
|
|
2480
2615
|
// client keys in applyExtension, so this can never overwrite a delegate.
|