@zenstackhq/runtime 3.0.0-beta.3 → 3.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{contract-CusA0mQO.d.cts → contract-BJce14-p.d.cts} +58 -17
- package/dist/{contract-CusA0mQO.d.ts → contract-BJce14-p.d.ts} +58 -17
- package/dist/index.cjs +1401 -786
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1286 -671
- package/dist/index.js.map +1 -1
- package/dist/plugins/policy/index.cjs +965 -375
- package/dist/plugins/policy/index.cjs.map +1 -1
- package/dist/plugins/policy/index.d.cts +24 -4
- package/dist/plugins/policy/index.d.ts +24 -4
- package/dist/plugins/policy/index.js +880 -301
- package/dist/plugins/policy/index.js.map +1 -1
- package/dist/plugins/policy/plugin.zmodel +10 -0
- package/dist/schema.cjs +3 -0
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.d.cts +1 -0
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +3 -0
- package/dist/schema.js.map +1 -1
- package/package.json +11 -8
package/dist/index.js
CHANGED
|
@@ -7,13 +7,14 @@ var __export = (target, all) => {
|
|
|
7
7
|
|
|
8
8
|
// src/client/client-impl.ts
|
|
9
9
|
import { invariant as invariant15 } from "@zenstackhq/common-helpers";
|
|
10
|
-
import { CompiledQuery, DefaultConnectionProvider, DefaultQueryExecutor as DefaultQueryExecutor2, Kysely, Log, sql as
|
|
10
|
+
import { CompiledQuery, DefaultConnectionProvider, DefaultQueryExecutor as DefaultQueryExecutor2, Kysely, Log, sql as sql10, Transaction } from "kysely";
|
|
11
11
|
|
|
12
12
|
// src/client/crud/operations/aggregate.ts
|
|
13
|
-
import { sql as
|
|
13
|
+
import { sql as sql6 } from "kysely";
|
|
14
14
|
import { match as match10 } from "ts-pattern";
|
|
15
15
|
|
|
16
16
|
// src/client/query-utils.ts
|
|
17
|
+
import { invariant } from "@zenstackhq/common-helpers";
|
|
17
18
|
import { match } from "ts-pattern";
|
|
18
19
|
|
|
19
20
|
// src/schema/expression.ts
|
|
@@ -81,6 +82,9 @@ var ExpressionUtils = {
|
|
|
81
82
|
or: /* @__PURE__ */ __name((expr2, ...expressions) => {
|
|
82
83
|
return expressions.reduce((acc, exp) => ExpressionUtils.binary(acc, "||", exp), expr2);
|
|
83
84
|
}, "or"),
|
|
85
|
+
not: /* @__PURE__ */ __name((expr2) => {
|
|
86
|
+
return ExpressionUtils.unary("!", expr2);
|
|
87
|
+
}, "not"),
|
|
84
88
|
is: /* @__PURE__ */ __name((value, kind) => {
|
|
85
89
|
return !!value && typeof value === "object" && "kind" in value && value.kind === kind;
|
|
86
90
|
}, "is"),
|
|
@@ -141,22 +145,26 @@ var NotFoundError = class extends Error {
|
|
|
141
145
|
static {
|
|
142
146
|
__name(this, "NotFoundError");
|
|
143
147
|
}
|
|
144
|
-
constructor(model) {
|
|
145
|
-
super(`Entity not found for model "${model}"`);
|
|
148
|
+
constructor(model, details) {
|
|
149
|
+
super(`Entity not found for model "${model}"${details ? `: ${details}` : ""}`);
|
|
146
150
|
}
|
|
147
151
|
};
|
|
148
152
|
|
|
149
153
|
// src/client/query-utils.ts
|
|
150
154
|
function getModel(schema, model) {
|
|
151
|
-
return schema.models
|
|
155
|
+
return Object.values(schema.models).find((m) => m.name.toLowerCase() === model.toLowerCase());
|
|
152
156
|
}
|
|
153
157
|
__name(getModel, "getModel");
|
|
158
|
+
function getTypeDef(schema, type) {
|
|
159
|
+
return schema.typeDefs?.[type];
|
|
160
|
+
}
|
|
161
|
+
__name(getTypeDef, "getTypeDef");
|
|
154
162
|
function requireModel(schema, model) {
|
|
155
|
-
const
|
|
156
|
-
if (!
|
|
163
|
+
const modelDef = getModel(schema, model);
|
|
164
|
+
if (!modelDef) {
|
|
157
165
|
throw new QueryError(`Model "${model}" not found in schema`);
|
|
158
166
|
}
|
|
159
|
-
return
|
|
167
|
+
return modelDef;
|
|
160
168
|
}
|
|
161
169
|
__name(requireModel, "requireModel");
|
|
162
170
|
function getField(schema, model, field) {
|
|
@@ -164,19 +172,40 @@ function getField(schema, model, field) {
|
|
|
164
172
|
return modelDef?.fields[field];
|
|
165
173
|
}
|
|
166
174
|
__name(getField, "getField");
|
|
167
|
-
function requireField(schema,
|
|
168
|
-
const modelDef =
|
|
169
|
-
if (
|
|
170
|
-
|
|
175
|
+
function requireField(schema, modelOrType, field) {
|
|
176
|
+
const modelDef = getModel(schema, modelOrType);
|
|
177
|
+
if (modelDef) {
|
|
178
|
+
if (!modelDef.fields[field]) {
|
|
179
|
+
throw new QueryError(`Field "${field}" not found in model "${modelOrType}"`);
|
|
180
|
+
} else {
|
|
181
|
+
return modelDef.fields[field];
|
|
182
|
+
}
|
|
171
183
|
}
|
|
172
|
-
|
|
184
|
+
const typeDef = getTypeDef(schema, modelOrType);
|
|
185
|
+
if (typeDef) {
|
|
186
|
+
if (!typeDef.fields[field]) {
|
|
187
|
+
throw new QueryError(`Field "${field}" not found in type "${modelOrType}"`);
|
|
188
|
+
} else {
|
|
189
|
+
return typeDef.fields[field];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
throw new QueryError(`Model or type "${modelOrType}" not found in schema`);
|
|
173
193
|
}
|
|
174
194
|
__name(requireField, "requireField");
|
|
175
195
|
function getIdFields(schema, model) {
|
|
176
|
-
const modelDef =
|
|
196
|
+
const modelDef = getModel(schema, model);
|
|
177
197
|
return modelDef?.idFields;
|
|
178
198
|
}
|
|
179
199
|
__name(getIdFields, "getIdFields");
|
|
200
|
+
function requireIdFields(schema, model) {
|
|
201
|
+
const modelDef = requireModel(schema, model);
|
|
202
|
+
const result = modelDef?.idFields;
|
|
203
|
+
if (!result) {
|
|
204
|
+
throw new InternalError(`Model "${model}" does not have ID field(s)`);
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
__name(requireIdFields, "requireIdFields");
|
|
180
209
|
function getRelationForeignKeyFieldPairs(schema, model, relationField) {
|
|
181
210
|
const fieldDef = requireField(schema, model, relationField);
|
|
182
211
|
if (!fieldDef?.relation) {
|
|
@@ -290,7 +319,7 @@ function buildFieldRef(schema, model, field, options, eb, modelAlias, inlineComp
|
|
|
290
319
|
throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
|
|
291
320
|
}
|
|
292
321
|
return computer(eb, {
|
|
293
|
-
|
|
322
|
+
modelAlias
|
|
294
323
|
});
|
|
295
324
|
}
|
|
296
325
|
}
|
|
@@ -325,7 +354,7 @@ function buildJoinPairs(schema, model, modelAlias, relationField, relationModelA
|
|
|
325
354
|
}
|
|
326
355
|
__name(buildJoinPairs, "buildJoinPairs");
|
|
327
356
|
function makeDefaultOrderBy(schema, model) {
|
|
328
|
-
const idFields =
|
|
357
|
+
const idFields = requireIdFields(schema, model);
|
|
329
358
|
return idFields.map((f) => ({
|
|
330
359
|
[f]: "asc"
|
|
331
360
|
}));
|
|
@@ -364,11 +393,17 @@ function getManyToManyRelation(schema, model, field) {
|
|
|
364
393
|
"A"
|
|
365
394
|
];
|
|
366
395
|
}
|
|
396
|
+
const modelIdFields = requireIdFields(schema, model);
|
|
397
|
+
invariant(modelIdFields.length === 1, "Only single-field ID is supported for many-to-many relation");
|
|
398
|
+
const otherIdFields = requireIdFields(schema, fieldDef.type);
|
|
399
|
+
invariant(otherIdFields.length === 1, "Only single-field ID is supported for many-to-many relation");
|
|
367
400
|
return {
|
|
368
401
|
parentFkName: orderedFK[0],
|
|
402
|
+
parentPKName: modelIdFields[0],
|
|
369
403
|
otherModel: fieldDef.type,
|
|
370
404
|
otherField: fieldDef.relation.opposite,
|
|
371
405
|
otherFkName: orderedFK[1],
|
|
406
|
+
otherPKName: otherIdFields[0],
|
|
372
407
|
joinTable: fieldDef.relation.name ? `_${fieldDef.relation.name}` : `_${sortedModelNames[0]}To${sortedModelNames[1]}`
|
|
373
408
|
};
|
|
374
409
|
} else {
|
|
@@ -407,7 +442,7 @@ function ensureArray(value) {
|
|
|
407
442
|
}
|
|
408
443
|
__name(ensureArray, "ensureArray");
|
|
409
444
|
function extractIdFields(entity, schema, model) {
|
|
410
|
-
const idFields =
|
|
445
|
+
const idFields = requireIdFields(schema, model);
|
|
411
446
|
return extractFields(entity, idFields);
|
|
412
447
|
}
|
|
413
448
|
__name(extractIdFields, "extractIdFields");
|
|
@@ -444,35 +479,94 @@ __name(aggregate, "aggregate");
|
|
|
444
479
|
|
|
445
480
|
// src/client/crud/operations/base.ts
|
|
446
481
|
import { createId } from "@paralleldrive/cuid2";
|
|
447
|
-
import { invariant as
|
|
448
|
-
import { expressionBuilder as
|
|
482
|
+
import { invariant as invariant9, isPlainObject as isPlainObject3 } from "@zenstackhq/common-helpers";
|
|
483
|
+
import { expressionBuilder as expressionBuilder4, sql as sql5 } from "kysely";
|
|
449
484
|
import { nanoid } from "nanoid";
|
|
450
485
|
import { match as match9 } from "ts-pattern";
|
|
451
486
|
import { ulid } from "ulid";
|
|
452
487
|
import * as uuid from "uuid";
|
|
453
488
|
|
|
454
489
|
// src/plugins/policy/errors.ts
|
|
490
|
+
var RejectedByPolicyReason = /* @__PURE__ */ function(RejectedByPolicyReason2) {
|
|
491
|
+
RejectedByPolicyReason2["NO_ACCESS"] = "no-access";
|
|
492
|
+
RejectedByPolicyReason2["CANNOT_READ_BACK"] = "cannot-read-back";
|
|
493
|
+
RejectedByPolicyReason2["OTHER"] = "other";
|
|
494
|
+
return RejectedByPolicyReason2;
|
|
495
|
+
}({});
|
|
455
496
|
var RejectedByPolicyError = class extends Error {
|
|
456
497
|
static {
|
|
457
498
|
__name(this, "RejectedByPolicyError");
|
|
458
499
|
}
|
|
459
500
|
model;
|
|
460
501
|
reason;
|
|
461
|
-
constructor(model, reason) {
|
|
462
|
-
super(
|
|
502
|
+
constructor(model, reason = "no-access", message) {
|
|
503
|
+
super(message ?? `Operation rejected by policy${model ? ": " + model : ""}`), this.model = model, this.reason = reason;
|
|
463
504
|
}
|
|
464
505
|
};
|
|
465
506
|
|
|
507
|
+
// src/plugins/policy/functions.ts
|
|
508
|
+
import { invariant as invariant8 } from "@zenstackhq/common-helpers";
|
|
509
|
+
import { ExpressionWrapper as ExpressionWrapper2, ValueNode as ValueNode4 } from "kysely";
|
|
510
|
+
|
|
511
|
+
// src/client/contract.ts
|
|
512
|
+
var TransactionIsolationLevel = /* @__PURE__ */ function(TransactionIsolationLevel2) {
|
|
513
|
+
TransactionIsolationLevel2["ReadUncommitted"] = "read uncommitted";
|
|
514
|
+
TransactionIsolationLevel2["ReadCommitted"] = "read committed";
|
|
515
|
+
TransactionIsolationLevel2["RepeatableRead"] = "repeatable read";
|
|
516
|
+
TransactionIsolationLevel2["Serializable"] = "serializable";
|
|
517
|
+
TransactionIsolationLevel2["Snapshot"] = "snapshot";
|
|
518
|
+
return TransactionIsolationLevel2;
|
|
519
|
+
}({});
|
|
520
|
+
var CRUD = [
|
|
521
|
+
"create",
|
|
522
|
+
"read",
|
|
523
|
+
"update",
|
|
524
|
+
"delete"
|
|
525
|
+
];
|
|
526
|
+
|
|
527
|
+
// src/client/kysely-utils.ts
|
|
528
|
+
import { AliasNode, ColumnNode, ReferenceNode, TableNode } from "kysely";
|
|
529
|
+
function stripAlias(node) {
|
|
530
|
+
if (AliasNode.is(node)) {
|
|
531
|
+
return {
|
|
532
|
+
alias: node.alias,
|
|
533
|
+
node: node.node
|
|
534
|
+
};
|
|
535
|
+
} else {
|
|
536
|
+
return {
|
|
537
|
+
alias: void 0,
|
|
538
|
+
node
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
__name(stripAlias, "stripAlias");
|
|
543
|
+
function extractModelName(node) {
|
|
544
|
+
const { node: innerNode } = stripAlias(node);
|
|
545
|
+
return TableNode.is(innerNode) ? innerNode.table.identifier.name : void 0;
|
|
546
|
+
}
|
|
547
|
+
__name(extractModelName, "extractModelName");
|
|
548
|
+
function extractFieldName(node) {
|
|
549
|
+
if (ReferenceNode.is(node) && ColumnNode.is(node.column)) {
|
|
550
|
+
return node.column.column.name;
|
|
551
|
+
} else if (ColumnNode.is(node)) {
|
|
552
|
+
return node.column.name;
|
|
553
|
+
} else {
|
|
554
|
+
return void 0;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
__name(extractFieldName, "extractFieldName");
|
|
558
|
+
|
|
466
559
|
// src/plugins/policy/policy-handler.ts
|
|
467
|
-
import { invariant as
|
|
468
|
-
import { AliasNode as
|
|
560
|
+
import { invariant as invariant7 } from "@zenstackhq/common-helpers";
|
|
561
|
+
import { AliasNode as AliasNode4, BinaryOperationNode as BinaryOperationNode3, ColumnNode as ColumnNode3, DeleteQueryNode, expressionBuilder as expressionBuilder3, ExpressionWrapper, FromNode as FromNode2, FunctionNode as FunctionNode3, IdentifierNode as IdentifierNode2, InsertQueryNode, OperationNodeTransformer, OperatorNode as OperatorNode3, ParensNode as ParensNode2, PrimitiveValueListNode, RawNode, ReturningNode, SelectionNode as SelectionNode2, SelectQueryNode as SelectQueryNode2, sql as sql4, TableNode as TableNode4, UpdateQueryNode, ValueListNode as ValueListNode2, ValueNode as ValueNode3, ValuesNode, WhereNode as WhereNode2 } from "kysely";
|
|
469
562
|
import { match as match8 } from "ts-pattern";
|
|
470
563
|
|
|
471
564
|
// src/client/crud/dialects/index.ts
|
|
472
565
|
import { match as match5 } from "ts-pattern";
|
|
473
566
|
|
|
474
567
|
// src/client/crud/dialects/postgresql.ts
|
|
475
|
-
import { invariant as
|
|
568
|
+
import { invariant as invariant3 } from "@zenstackhq/common-helpers";
|
|
569
|
+
import Decimal from "decimal.js";
|
|
476
570
|
import { sql as sql2 } from "kysely";
|
|
477
571
|
import { match as match3 } from "ts-pattern";
|
|
478
572
|
|
|
@@ -497,8 +591,8 @@ var AGGREGATE_OPERATORS = [
|
|
|
497
591
|
"_max"
|
|
498
592
|
];
|
|
499
593
|
|
|
500
|
-
// src/client/crud/dialects/base.ts
|
|
501
|
-
import { invariant, isPlainObject } from "@zenstackhq/common-helpers";
|
|
594
|
+
// src/client/crud/dialects/base-dialect.ts
|
|
595
|
+
import { invariant as invariant2, isPlainObject } from "@zenstackhq/common-helpers";
|
|
502
596
|
import { expressionBuilder, sql } from "kysely";
|
|
503
597
|
import { match as match2, P } from "ts-pattern";
|
|
504
598
|
|
|
@@ -516,7 +610,7 @@ function enumerate(x) {
|
|
|
516
610
|
}
|
|
517
611
|
__name(enumerate, "enumerate");
|
|
518
612
|
|
|
519
|
-
// src/client/crud/dialects/base.ts
|
|
613
|
+
// src/client/crud/dialects/base-dialect.ts
|
|
520
614
|
var BaseCrudDialect = class {
|
|
521
615
|
static {
|
|
522
616
|
__name(this, "BaseCrudDialect");
|
|
@@ -530,6 +624,9 @@ var BaseCrudDialect = class {
|
|
|
530
624
|
transformPrimitive(value, _type, _forArrayField) {
|
|
531
625
|
return value;
|
|
532
626
|
}
|
|
627
|
+
transformOutput(value, _type) {
|
|
628
|
+
return value;
|
|
629
|
+
}
|
|
533
630
|
// #region common query builders
|
|
534
631
|
buildSelectModel(eb, model, modelAlias) {
|
|
535
632
|
const modelDef = requireModel(this.schema, model);
|
|
@@ -697,9 +794,11 @@ var BaseCrudDialect = class {
|
|
|
697
794
|
const buildPkFkWhereRefs = /* @__PURE__ */ __name((eb2) => {
|
|
698
795
|
const m2m = getManyToManyRelation(this.schema, model, field);
|
|
699
796
|
if (m2m) {
|
|
700
|
-
const
|
|
701
|
-
|
|
702
|
-
|
|
797
|
+
const modelIdFields = requireIdFields(this.schema, model);
|
|
798
|
+
invariant2(modelIdFields.length === 1, "many-to-many relation must have exactly one id field");
|
|
799
|
+
const relationIdFields = requireIdFields(this.schema, relationModel);
|
|
800
|
+
invariant2(relationIdFields.length === 1, "many-to-many relation must have exactly one id field");
|
|
801
|
+
return eb2(sql.ref(`${relationFilterSelectAlias}.${relationIdFields[0]}`), "in", eb2.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(sql.ref(`${m2m.joinTable}.${m2m.parentFkName}`), "=", sql.ref(`${modelAlias}.${modelIdFields[0]}`)));
|
|
703
802
|
} else {
|
|
704
803
|
const relationKeyPairs = getRelationForeignKeyFieldPairs(this.schema, model, field);
|
|
705
804
|
let result2 = this.true(eb2);
|
|
@@ -809,14 +908,14 @@ var BaseCrudDialect = class {
|
|
|
809
908
|
}
|
|
810
909
|
const rhs = Array.isArray(value) ? value.map(getRhs) : getRhs(value);
|
|
811
910
|
const condition = match2(op).with("equals", () => rhs === null ? eb(lhs, "is", null) : eb(lhs, "=", rhs)).with("in", () => {
|
|
812
|
-
|
|
911
|
+
invariant2(Array.isArray(rhs), "right hand side must be an array");
|
|
813
912
|
if (rhs.length === 0) {
|
|
814
913
|
return this.false(eb);
|
|
815
914
|
} else {
|
|
816
915
|
return eb(lhs, "in", rhs);
|
|
817
916
|
}
|
|
818
917
|
}).with("notIn", () => {
|
|
819
|
-
|
|
918
|
+
invariant2(Array.isArray(rhs), "right hand side must be an array");
|
|
820
919
|
if (rhs.length === 0) {
|
|
821
920
|
return this.true(eb);
|
|
822
921
|
} else {
|
|
@@ -934,18 +1033,18 @@ var BaseCrudDialect = class {
|
|
|
934
1033
|
"_min",
|
|
935
1034
|
"_max"
|
|
936
1035
|
].includes(field)) {
|
|
937
|
-
|
|
1036
|
+
invariant2(value && typeof value === "object", `invalid orderBy value for field "${field}"`);
|
|
938
1037
|
for (const [k, v] of Object.entries(value)) {
|
|
939
|
-
|
|
1038
|
+
invariant2(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
940
1039
|
result = result.orderBy((eb) => aggregate(eb, this.fieldRef(model, k, eb, modelAlias), field), sql.raw(this.negateSort(v, negated)));
|
|
941
1040
|
}
|
|
942
1041
|
continue;
|
|
943
1042
|
}
|
|
944
1043
|
switch (field) {
|
|
945
1044
|
case "_count": {
|
|
946
|
-
|
|
1045
|
+
invariant2(value && typeof value === "object", 'invalid orderBy value for field "_count"');
|
|
947
1046
|
for (const [k, v] of Object.entries(value)) {
|
|
948
|
-
|
|
1047
|
+
invariant2(v === "asc" || v === "desc", `invalid orderBy value for field "${field}"`);
|
|
949
1048
|
result = result.orderBy((eb) => eb.fn.count(this.fieldRef(model, k, eb, modelAlias)), sql.raw(this.negateSort(v, negated)));
|
|
950
1049
|
}
|
|
951
1050
|
continue;
|
|
@@ -968,7 +1067,7 @@ var BaseCrudDialect = class {
|
|
|
968
1067
|
throw new QueryError(`invalid orderBy value for field "${field}"`);
|
|
969
1068
|
}
|
|
970
1069
|
if ("_count" in value) {
|
|
971
|
-
|
|
1070
|
+
invariant2(value._count === "asc" || value._count === "desc", 'invalid orderBy value for field "_count"');
|
|
972
1071
|
const sort = this.negateSort(value._count, negated);
|
|
973
1072
|
result = result.orderBy((eb) => {
|
|
974
1073
|
const subQueryAlias = `${modelAlias}$orderBy$${field}$count`;
|
|
@@ -1040,7 +1139,7 @@ var BaseCrudDialect = class {
|
|
|
1040
1139
|
}
|
|
1041
1140
|
}
|
|
1042
1141
|
buildDelegateJoin(thisModel, thisModelAlias, otherModelAlias, query) {
|
|
1043
|
-
const idFields =
|
|
1142
|
+
const idFields = requireIdFields(this.schema, thisModel);
|
|
1044
1143
|
query = query.leftJoin(otherModelAlias, (qb) => {
|
|
1045
1144
|
for (const idField of idFields) {
|
|
1046
1145
|
qb = qb.onRef(`${thisModelAlias}.${idField}`, "=", `${otherModelAlias}.${idField}`);
|
|
@@ -1062,10 +1161,16 @@ var BaseCrudDialect = class {
|
|
|
1062
1161
|
for (const [field, value] of Object.entries(selections.select)) {
|
|
1063
1162
|
const fieldDef = requireField(this.schema, model, field);
|
|
1064
1163
|
const fieldModel = fieldDef.type;
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
fieldCountQuery =
|
|
1164
|
+
let fieldCountQuery;
|
|
1165
|
+
const m2m = getManyToManyRelation(this.schema, model, field);
|
|
1166
|
+
if (m2m) {
|
|
1167
|
+
fieldCountQuery = eb.selectFrom(fieldModel).innerJoin(m2m.joinTable, (join) => join.onRef(`${m2m.joinTable}.${m2m.otherFkName}`, "=", `${fieldModel}.${m2m.otherPKName}`).onRef(`${m2m.joinTable}.${m2m.parentFkName}`, "=", `${parentAlias}.${m2m.parentPKName}`)).select(eb.fn.countAll().as(`_count$${field}`));
|
|
1168
|
+
} else {
|
|
1169
|
+
fieldCountQuery = eb.selectFrom(fieldModel).select(eb.fn.countAll().as(`_count$${field}`));
|
|
1170
|
+
const joinPairs = buildJoinPairs(this.schema, model, parentAlias, field, fieldModel);
|
|
1171
|
+
for (const [left, right] of joinPairs) {
|
|
1172
|
+
fieldCountQuery = fieldCountQuery.whereRef(left, "=", right);
|
|
1173
|
+
}
|
|
1069
1174
|
}
|
|
1070
1175
|
if (value && typeof value === "object" && "where" in value && value.where && typeof value.where === "object") {
|
|
1071
1176
|
const filter = this.buildFilter(eb, fieldModel, fieldModel, value.where);
|
|
@@ -1145,6 +1250,9 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1145
1250
|
static {
|
|
1146
1251
|
__name(this, "PostgresCrudDialect");
|
|
1147
1252
|
}
|
|
1253
|
+
constructor(schema, options) {
|
|
1254
|
+
super(schema, options);
|
|
1255
|
+
}
|
|
1148
1256
|
get provider() {
|
|
1149
1257
|
return "postgresql";
|
|
1150
1258
|
}
|
|
@@ -1159,9 +1267,41 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1159
1267
|
return value.map((v) => this.transformPrimitive(v, type, false));
|
|
1160
1268
|
}
|
|
1161
1269
|
} else {
|
|
1162
|
-
return match3(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
|
|
1270
|
+
return match3(type).with("DateTime", () => value instanceof Date ? value.toISOString() : typeof value === "string" ? new Date(value).toISOString() : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
transformOutput(value, type) {
|
|
1274
|
+
if (value === null || value === void 0) {
|
|
1275
|
+
return value;
|
|
1276
|
+
}
|
|
1277
|
+
return match3(type).with("DateTime", () => this.transformOutputDate(value)).with("Bytes", () => this.transformOutputBytes(value)).with("BigInt", () => this.transformOutputBigInt(value)).with("Decimal", () => this.transformDecimal(value)).otherwise(() => super.transformOutput(value, type));
|
|
1278
|
+
}
|
|
1279
|
+
transformOutputBigInt(value) {
|
|
1280
|
+
if (typeof value === "bigint") {
|
|
1281
|
+
return value;
|
|
1282
|
+
}
|
|
1283
|
+
invariant3(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
|
|
1284
|
+
return BigInt(value);
|
|
1285
|
+
}
|
|
1286
|
+
transformDecimal(value) {
|
|
1287
|
+
if (value instanceof Decimal) {
|
|
1288
|
+
return value;
|
|
1289
|
+
}
|
|
1290
|
+
invariant3(typeof value === "string" || typeof value === "number" || value instanceof Decimal, `Expected string, number or Decimal, got ${typeof value}`);
|
|
1291
|
+
return new Decimal(value);
|
|
1292
|
+
}
|
|
1293
|
+
transformOutputDate(value) {
|
|
1294
|
+
if (typeof value === "string") {
|
|
1295
|
+
return new Date(value);
|
|
1296
|
+
} else if (value instanceof Date && this.options.fixPostgresTimezone !== false) {
|
|
1297
|
+
return new Date(value.getTime() - value.getTimezoneOffset() * 60 * 1e3);
|
|
1298
|
+
} else {
|
|
1299
|
+
return value;
|
|
1163
1300
|
}
|
|
1164
1301
|
}
|
|
1302
|
+
transformOutputBytes(value) {
|
|
1303
|
+
return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
|
|
1304
|
+
}
|
|
1165
1305
|
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
1166
1306
|
const relationResultName = `${parentAlias}$${relationField}`;
|
|
1167
1307
|
const joinedQuery = this.buildRelationJSON(model, query, relationField, parentAlias, payload, relationResultName);
|
|
@@ -1192,10 +1332,10 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1192
1332
|
buildRelationJoinFilter(query, model, relationField, relationModel, relationModelAlias, parentAlias) {
|
|
1193
1333
|
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
1194
1334
|
if (m2m) {
|
|
1195
|
-
const parentIds =
|
|
1196
|
-
const relationIds =
|
|
1197
|
-
|
|
1198
|
-
|
|
1335
|
+
const parentIds = requireIdFields(this.schema, model);
|
|
1336
|
+
const relationIds = requireIdFields(this.schema, relationModel);
|
|
1337
|
+
invariant3(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1338
|
+
invariant3(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1199
1339
|
query = query.where((eb) => eb(eb.ref(`${relationModelAlias}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentAlias}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
|
|
1200
1340
|
} else {
|
|
1201
1341
|
const joinPairs = buildJoinPairs(this.schema, model, parentAlias, relationField, relationModelAlias);
|
|
@@ -1307,10 +1447,32 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
1307
1447
|
get supportInsertWithDefault() {
|
|
1308
1448
|
return true;
|
|
1309
1449
|
}
|
|
1450
|
+
getFieldSqlType(fieldDef) {
|
|
1451
|
+
if (fieldDef.relation) {
|
|
1452
|
+
throw new QueryError("Cannot get SQL type of a relation field");
|
|
1453
|
+
}
|
|
1454
|
+
let result;
|
|
1455
|
+
if (this.schema.enums?.[fieldDef.type]) {
|
|
1456
|
+
result = "text";
|
|
1457
|
+
} else {
|
|
1458
|
+
result = match3(fieldDef.type).with("String", () => "text").with("Boolean", () => "boolean").with("Int", () => "integer").with("BigInt", () => "bigint").with("Float", () => "double precision").with("Decimal", () => "decimal").with("DateTime", () => "timestamp").with("Bytes", () => "bytea").with("Json", () => "jsonb").otherwise(() => "text");
|
|
1459
|
+
}
|
|
1460
|
+
if (fieldDef.array) {
|
|
1461
|
+
result += "[]";
|
|
1462
|
+
}
|
|
1463
|
+
return result;
|
|
1464
|
+
}
|
|
1465
|
+
getStringCasingBehavior() {
|
|
1466
|
+
return {
|
|
1467
|
+
supportsILike: true,
|
|
1468
|
+
likeCaseSensitive: true
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1310
1471
|
};
|
|
1311
1472
|
|
|
1312
1473
|
// src/client/crud/dialects/sqlite.ts
|
|
1313
|
-
import { invariant as
|
|
1474
|
+
import { invariant as invariant4 } from "@zenstackhq/common-helpers";
|
|
1475
|
+
import Decimal2 from "decimal.js";
|
|
1314
1476
|
import { sql as sql3 } from "kysely";
|
|
1315
1477
|
import { match as match4 } from "ts-pattern";
|
|
1316
1478
|
var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
@@ -1330,9 +1492,57 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1330
1492
|
if (this.schema.typeDefs && type in this.schema.typeDefs) {
|
|
1331
1493
|
return JSON.stringify(value);
|
|
1332
1494
|
} else {
|
|
1333
|
-
return match4(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).with("Json", () => JSON.stringify(value)).otherwise(() => value);
|
|
1495
|
+
return match4(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : typeof value === "string" ? new Date(value).toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).with("Json", () => JSON.stringify(value)).otherwise(() => value);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
transformOutput(value, type) {
|
|
1500
|
+
if (value === null || value === void 0) {
|
|
1501
|
+
return value;
|
|
1502
|
+
} else if (this.schema.typeDefs && type in this.schema.typeDefs) {
|
|
1503
|
+
return this.transformOutputJson(value);
|
|
1504
|
+
} else {
|
|
1505
|
+
return match4(type).with("Boolean", () => this.transformOutputBoolean(value)).with("DateTime", () => this.transformOutputDate(value)).with("Bytes", () => this.transformOutputBytes(value)).with("Decimal", () => this.transformOutputDecimal(value)).with("BigInt", () => this.transformOutputBigInt(value)).with("Json", () => this.transformOutputJson(value)).otherwise(() => super.transformOutput(value, type));
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
transformOutputDecimal(value) {
|
|
1509
|
+
if (value instanceof Decimal2) {
|
|
1510
|
+
return value;
|
|
1511
|
+
}
|
|
1512
|
+
invariant4(typeof value === "string" || typeof value === "number" || value instanceof Decimal2, `Expected string, number or Decimal, got ${typeof value}`);
|
|
1513
|
+
return new Decimal2(value);
|
|
1514
|
+
}
|
|
1515
|
+
transformOutputBigInt(value) {
|
|
1516
|
+
if (typeof value === "bigint") {
|
|
1517
|
+
return value;
|
|
1518
|
+
}
|
|
1519
|
+
invariant4(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
|
|
1520
|
+
return BigInt(value);
|
|
1521
|
+
}
|
|
1522
|
+
transformOutputBoolean(value) {
|
|
1523
|
+
return !!value;
|
|
1524
|
+
}
|
|
1525
|
+
transformOutputDate(value) {
|
|
1526
|
+
if (typeof value === "number") {
|
|
1527
|
+
return new Date(value);
|
|
1528
|
+
} else if (typeof value === "string") {
|
|
1529
|
+
return new Date(value);
|
|
1530
|
+
} else {
|
|
1531
|
+
return value;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
transformOutputBytes(value) {
|
|
1535
|
+
return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
|
|
1536
|
+
}
|
|
1537
|
+
transformOutputJson(value) {
|
|
1538
|
+
if (typeof value === "string") {
|
|
1539
|
+
try {
|
|
1540
|
+
return JSON.parse(value);
|
|
1541
|
+
} catch (e) {
|
|
1542
|
+
throw new QueryError("Invalid JSON returned", e);
|
|
1334
1543
|
}
|
|
1335
1544
|
}
|
|
1545
|
+
return value;
|
|
1336
1546
|
}
|
|
1337
1547
|
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
1338
1548
|
return query.select((eb) => this.buildRelationJSON(model, eb, relationField, parentAlias, payload).as(relationField));
|
|
@@ -1415,10 +1625,10 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1415
1625
|
const relationModel = fieldDef.type;
|
|
1416
1626
|
const m2m = getManyToManyRelation(this.schema, model, relationField);
|
|
1417
1627
|
if (m2m) {
|
|
1418
|
-
const parentIds =
|
|
1419
|
-
const relationIds =
|
|
1420
|
-
|
|
1421
|
-
|
|
1628
|
+
const parentIds = requireIdFields(this.schema, model);
|
|
1629
|
+
const relationIds = requireIdFields(this.schema, relationModel);
|
|
1630
|
+
invariant4(parentIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1631
|
+
invariant4(relationIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
1422
1632
|
selectModelQuery = selectModelQuery.where((eb) => eb(eb.ref(`${relationModelAlias}.${relationIds[0]}`), "in", eb.selectFrom(m2m.joinTable).select(`${m2m.joinTable}.${m2m.otherFkName}`).whereRef(`${parentAlias}.${parentIds[0]}`, "=", `${m2m.joinTable}.${m2m.parentFkName}`)));
|
|
1423
1633
|
} else {
|
|
1424
1634
|
const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
|
|
@@ -1470,6 +1680,24 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
1470
1680
|
get supportInsertWithDefault() {
|
|
1471
1681
|
return false;
|
|
1472
1682
|
}
|
|
1683
|
+
getFieldSqlType(fieldDef) {
|
|
1684
|
+
if (fieldDef.relation) {
|
|
1685
|
+
throw new QueryError("Cannot get SQL type of a relation field");
|
|
1686
|
+
}
|
|
1687
|
+
if (fieldDef.array) {
|
|
1688
|
+
throw new QueryError("SQLite does not support scalar list type");
|
|
1689
|
+
}
|
|
1690
|
+
if (this.schema.enums?.[fieldDef.type]) {
|
|
1691
|
+
return "text";
|
|
1692
|
+
}
|
|
1693
|
+
return match4(fieldDef.type).with("String", () => "text").with("Boolean", () => "integer").with("Int", () => "integer").with("BigInt", () => "integer").with("Float", () => "real").with("Decimal", () => "decimal").with("DateTime", () => "numeric").with("Bytes", () => "blob").with("Json", () => "jsonb").otherwise(() => "text");
|
|
1694
|
+
}
|
|
1695
|
+
getStringCasingBehavior() {
|
|
1696
|
+
return {
|
|
1697
|
+
supportsILike: false,
|
|
1698
|
+
likeCaseSensitive: false
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1473
1701
|
};
|
|
1474
1702
|
|
|
1475
1703
|
// src/client/crud/dialects/index.ts
|
|
@@ -1797,12 +2025,12 @@ var ColumnCollector = class extends DefaultOperationNodeVisitor {
|
|
|
1797
2025
|
};
|
|
1798
2026
|
|
|
1799
2027
|
// src/plugins/policy/expression-transformer.ts
|
|
1800
|
-
import { invariant as
|
|
1801
|
-
import { AliasNode as
|
|
2028
|
+
import { invariant as invariant6 } from "@zenstackhq/common-helpers";
|
|
2029
|
+
import { AliasNode as AliasNode3, BinaryOperationNode as BinaryOperationNode2, ColumnNode as ColumnNode2, expressionBuilder as expressionBuilder2, FromNode, FunctionNode as FunctionNode2, IdentifierNode, OperatorNode as OperatorNode2, ReferenceNode as ReferenceNode3, SelectionNode, SelectQueryNode, TableNode as TableNode3, ValueListNode, ValueNode as ValueNode2, WhereNode } from "kysely";
|
|
1802
2030
|
import { match as match7 } from "ts-pattern";
|
|
1803
2031
|
|
|
1804
2032
|
// src/plugins/policy/expression-evaluator.ts
|
|
1805
|
-
import { invariant as
|
|
2033
|
+
import { invariant as invariant5 } from "@zenstackhq/common-helpers";
|
|
1806
2034
|
import { match as match6 } from "ts-pattern";
|
|
1807
2035
|
var ExpressionEvaluator = class {
|
|
1808
2036
|
static {
|
|
@@ -1846,18 +2074,18 @@ var ExpressionEvaluator = class {
|
|
|
1846
2074
|
const right = this.evaluate(expr2.right, context);
|
|
1847
2075
|
return match6(expr2.op).with("==", () => left === right).with("!=", () => left !== right).with(">", () => left > right).with(">=", () => left >= right).with("<", () => left < right).with("<=", () => left <= right).with("&&", () => left && right).with("||", () => left || right).with("in", () => {
|
|
1848
2076
|
const _right = right ?? [];
|
|
1849
|
-
|
|
2077
|
+
invariant5(Array.isArray(_right), 'expected array for "in" operator');
|
|
1850
2078
|
return _right.includes(left);
|
|
1851
2079
|
}).exhaustive();
|
|
1852
2080
|
}
|
|
1853
2081
|
evaluateCollectionPredicate(expr2, context) {
|
|
1854
2082
|
const op = expr2.op;
|
|
1855
|
-
|
|
2083
|
+
invariant5(op === "?" || op === "!" || op === "^", 'expected "?" or "!" or "^" operator');
|
|
1856
2084
|
const left = this.evaluate(expr2.left, context);
|
|
1857
2085
|
if (!left) {
|
|
1858
2086
|
return false;
|
|
1859
2087
|
}
|
|
1860
|
-
|
|
2088
|
+
invariant5(Array.isArray(left), "expected array");
|
|
1861
2089
|
return match6(op).with("?", () => left.some((item) => this.evaluate(expr2.right, {
|
|
1862
2090
|
...context,
|
|
1863
2091
|
thisValue: item
|
|
@@ -1872,7 +2100,7 @@ var ExpressionEvaluator = class {
|
|
|
1872
2100
|
};
|
|
1873
2101
|
|
|
1874
2102
|
// src/plugins/policy/utils.ts
|
|
1875
|
-
import { AliasNode, AndNode, BinaryOperationNode, FunctionNode, OperatorNode, OrNode, ParensNode, ReferenceNode, TableNode, UnaryOperationNode, ValueNode } from "kysely";
|
|
2103
|
+
import { AliasNode as AliasNode2, AndNode, BinaryOperationNode, FunctionNode, OperatorNode, OrNode, ParensNode, ReferenceNode as ReferenceNode2, TableNode as TableNode2, UnaryOperationNode, ValueNode } from "kysely";
|
|
1876
2104
|
function trueNode(dialect) {
|
|
1877
2105
|
return ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
|
|
1878
2106
|
}
|
|
@@ -1890,6 +2118,12 @@ function isFalseNode(node) {
|
|
|
1890
2118
|
}
|
|
1891
2119
|
__name(isFalseNode, "isFalseNode");
|
|
1892
2120
|
function conjunction(dialect, nodes) {
|
|
2121
|
+
if (nodes.length === 0) {
|
|
2122
|
+
return trueNode(dialect);
|
|
2123
|
+
}
|
|
2124
|
+
if (nodes.length === 1) {
|
|
2125
|
+
return nodes[0];
|
|
2126
|
+
}
|
|
1893
2127
|
if (nodes.some(isFalseNode)) {
|
|
1894
2128
|
return falseNode(dialect);
|
|
1895
2129
|
}
|
|
@@ -1897,10 +2131,16 @@ function conjunction(dialect, nodes) {
|
|
|
1897
2131
|
if (items.length === 0) {
|
|
1898
2132
|
return trueNode(dialect);
|
|
1899
2133
|
}
|
|
1900
|
-
return items.reduce((acc, node) =>
|
|
2134
|
+
return items.reduce((acc, node) => AndNode.create(wrapParensIf(acc, OrNode.is), wrapParensIf(node, OrNode.is)));
|
|
1901
2135
|
}
|
|
1902
2136
|
__name(conjunction, "conjunction");
|
|
1903
2137
|
function disjunction(dialect, nodes) {
|
|
2138
|
+
if (nodes.length === 0) {
|
|
2139
|
+
return falseNode(dialect);
|
|
2140
|
+
}
|
|
2141
|
+
if (nodes.length === 1) {
|
|
2142
|
+
return nodes[0];
|
|
2143
|
+
}
|
|
1904
2144
|
if (nodes.some(isTrueNode)) {
|
|
1905
2145
|
return trueNode(dialect);
|
|
1906
2146
|
}
|
|
@@ -1908,13 +2148,23 @@ function disjunction(dialect, nodes) {
|
|
|
1908
2148
|
if (items.length === 0) {
|
|
1909
2149
|
return falseNode(dialect);
|
|
1910
2150
|
}
|
|
1911
|
-
return items.reduce((acc, node) =>
|
|
2151
|
+
return items.reduce((acc, node) => OrNode.create(wrapParensIf(acc, AndNode.is), wrapParensIf(node, AndNode.is)));
|
|
1912
2152
|
}
|
|
1913
2153
|
__name(disjunction, "disjunction");
|
|
1914
|
-
function logicalNot(node) {
|
|
1915
|
-
|
|
2154
|
+
function logicalNot(dialect, node) {
|
|
2155
|
+
if (isTrueNode(node)) {
|
|
2156
|
+
return falseNode(dialect);
|
|
2157
|
+
}
|
|
2158
|
+
if (isFalseNode(node)) {
|
|
2159
|
+
return trueNode(dialect);
|
|
2160
|
+
}
|
|
2161
|
+
return UnaryOperationNode.create(OperatorNode.create("not"), wrapParensIf(node, (n) => AndNode.is(n) || OrNode.is(n)));
|
|
1916
2162
|
}
|
|
1917
2163
|
__name(logicalNot, "logicalNot");
|
|
2164
|
+
function wrapParensIf(node, predicate) {
|
|
2165
|
+
return predicate(node) ? ParensNode.create(node) : node;
|
|
2166
|
+
}
|
|
2167
|
+
__name(wrapParensIf, "wrapParensIf");
|
|
1918
2168
|
function buildIsFalse(node, dialect) {
|
|
1919
2169
|
if (isFalseNode(node)) {
|
|
1920
2170
|
return trueNode(dialect);
|
|
@@ -1936,11 +2186,11 @@ function getTableName(node) {
|
|
|
1936
2186
|
if (!node) {
|
|
1937
2187
|
return node;
|
|
1938
2188
|
}
|
|
1939
|
-
if (
|
|
2189
|
+
if (TableNode2.is(node)) {
|
|
1940
2190
|
return node.table.identifier.name;
|
|
1941
|
-
} else if (
|
|
2191
|
+
} else if (AliasNode2.is(node)) {
|
|
1942
2192
|
return getTableName(node.node);
|
|
1943
|
-
} else if (
|
|
2193
|
+
} else if (ReferenceNode2.is(node) && node.table) {
|
|
1944
2194
|
return getTableName(node.table);
|
|
1945
2195
|
}
|
|
1946
2196
|
return void 0;
|
|
@@ -1973,16 +2223,21 @@ var ExpressionTransformer = class {
|
|
|
1973
2223
|
static {
|
|
1974
2224
|
__name(this, "ExpressionTransformer");
|
|
1975
2225
|
}
|
|
1976
|
-
|
|
1977
|
-
clientOptions;
|
|
1978
|
-
auth;
|
|
2226
|
+
client;
|
|
1979
2227
|
dialect;
|
|
1980
|
-
constructor(
|
|
1981
|
-
this.
|
|
1982
|
-
this.clientOptions = clientOptions;
|
|
1983
|
-
this.auth = auth;
|
|
2228
|
+
constructor(client) {
|
|
2229
|
+
this.client = client;
|
|
1984
2230
|
this.dialect = getCrudDialect(this.schema, this.clientOptions);
|
|
1985
2231
|
}
|
|
2232
|
+
get schema() {
|
|
2233
|
+
return this.client.$schema;
|
|
2234
|
+
}
|
|
2235
|
+
get clientOptions() {
|
|
2236
|
+
return this.client.$options;
|
|
2237
|
+
}
|
|
2238
|
+
get auth() {
|
|
2239
|
+
return this.client.$auth;
|
|
2240
|
+
}
|
|
1986
2241
|
get authType() {
|
|
1987
2242
|
if (!this.schema.authType) {
|
|
1988
2243
|
throw new InternalError('Schema does not have an "authType" specified');
|
|
@@ -2005,11 +2260,7 @@ var ExpressionTransformer = class {
|
|
|
2005
2260
|
_field(expr2, context) {
|
|
2006
2261
|
const fieldDef = requireField(this.schema, context.model, expr2.field);
|
|
2007
2262
|
if (!fieldDef.relation) {
|
|
2008
|
-
|
|
2009
|
-
return context.thisEntity[expr2.field];
|
|
2010
|
-
} else {
|
|
2011
|
-
return this.createColumnRef(expr2.field, context);
|
|
2012
|
-
}
|
|
2263
|
+
return this.createColumnRef(expr2.field, context);
|
|
2013
2264
|
} else {
|
|
2014
2265
|
const { memberFilter, memberSelect, ...restContext } = context;
|
|
2015
2266
|
const relation = this.transformRelationAccess(expr2.field, fieldDef.type, restContext);
|
|
@@ -2050,14 +2301,15 @@ var ExpressionTransformer = class {
|
|
|
2050
2301
|
]);
|
|
2051
2302
|
}
|
|
2052
2303
|
if (this.isAuthCall(expr2.left) || this.isAuthCall(expr2.right)) {
|
|
2053
|
-
return this.transformAuthBinary(expr2);
|
|
2304
|
+
return this.transformAuthBinary(expr2, context);
|
|
2054
2305
|
}
|
|
2055
2306
|
const op = expr2.op;
|
|
2056
2307
|
if (op === "?" || op === "!" || op === "^") {
|
|
2057
2308
|
return this.transformCollectionPredicate(expr2, context);
|
|
2058
2309
|
}
|
|
2059
|
-
const
|
|
2060
|
-
const
|
|
2310
|
+
const { normalizedLeft, normalizedRight } = this.normalizeBinaryOperationOperands(expr2, context);
|
|
2311
|
+
const left = this.transform(normalizedLeft, context);
|
|
2312
|
+
const right = this.transform(normalizedRight, context);
|
|
2061
2313
|
if (op === "in") {
|
|
2062
2314
|
if (this.isNullNode(left)) {
|
|
2063
2315
|
return this.transformValue(false, "Boolean");
|
|
@@ -2072,29 +2324,65 @@ var ExpressionTransformer = class {
|
|
|
2072
2324
|
}
|
|
2073
2325
|
}
|
|
2074
2326
|
if (this.isNullNode(right)) {
|
|
2075
|
-
return
|
|
2327
|
+
return this.transformNullCheck(left, expr2.op);
|
|
2076
2328
|
} else if (this.isNullNode(left)) {
|
|
2077
|
-
return
|
|
2329
|
+
return this.transformNullCheck(right, expr2.op);
|
|
2330
|
+
} else {
|
|
2331
|
+
return BinaryOperationNode2.create(left, this.transformOperator(op), right);
|
|
2332
|
+
}
|
|
2333
|
+
}
|
|
2334
|
+
transformNullCheck(expr2, operator) {
|
|
2335
|
+
invariant6(operator === "==" || operator === "!=", 'operator must be "==" or "!=" for null comparison');
|
|
2336
|
+
if (ValueNode2.is(expr2)) {
|
|
2337
|
+
if (expr2.value === null) {
|
|
2338
|
+
return operator === "==" ? trueNode(this.dialect) : falseNode(this.dialect);
|
|
2339
|
+
} else {
|
|
2340
|
+
return operator === "==" ? falseNode(this.dialect) : trueNode(this.dialect);
|
|
2341
|
+
}
|
|
2342
|
+
} else {
|
|
2343
|
+
return operator === "==" ? BinaryOperationNode2.create(expr2, OperatorNode2.create("is"), ValueNode2.createImmediate(null)) : BinaryOperationNode2.create(expr2, OperatorNode2.create("is not"), ValueNode2.createImmediate(null));
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
normalizeBinaryOperationOperands(expr2, context) {
|
|
2347
|
+
let normalizedLeft = expr2.left;
|
|
2348
|
+
if (this.isRelationField(expr2.left, context.model)) {
|
|
2349
|
+
invariant6(ExpressionUtils.isNull(expr2.right), "only null comparison is supported for relation field");
|
|
2350
|
+
const leftRelDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
|
|
2351
|
+
invariant6(leftRelDef, "failed to get relation field definition");
|
|
2352
|
+
const idFields = requireIdFields(this.schema, leftRelDef.type);
|
|
2353
|
+
normalizedLeft = this.makeOrAppendMember(normalizedLeft, idFields[0]);
|
|
2354
|
+
}
|
|
2355
|
+
let normalizedRight = expr2.right;
|
|
2356
|
+
if (this.isRelationField(expr2.right, context.model)) {
|
|
2357
|
+
invariant6(ExpressionUtils.isNull(expr2.left), "only null comparison is supported for relation field");
|
|
2358
|
+
const rightRelDef = this.getFieldDefFromFieldRef(expr2.right, context.model);
|
|
2359
|
+
invariant6(rightRelDef, "failed to get relation field definition");
|
|
2360
|
+
const idFields = requireIdFields(this.schema, rightRelDef.type);
|
|
2361
|
+
normalizedRight = this.makeOrAppendMember(normalizedRight, idFields[0]);
|
|
2078
2362
|
}
|
|
2079
|
-
return
|
|
2363
|
+
return {
|
|
2364
|
+
normalizedLeft,
|
|
2365
|
+
normalizedRight
|
|
2366
|
+
};
|
|
2080
2367
|
}
|
|
2081
2368
|
transformCollectionPredicate(expr2, context) {
|
|
2082
|
-
|
|
2369
|
+
invariant6(expr2.op === "?" || expr2.op === "!" || expr2.op === "^", 'expected "?" or "!" or "^" operator');
|
|
2083
2370
|
if (this.isAuthCall(expr2.left) || this.isAuthMember(expr2.left)) {
|
|
2084
2371
|
const value = new ExpressionEvaluator().evaluate(expr2, {
|
|
2085
2372
|
auth: this.auth
|
|
2086
2373
|
});
|
|
2087
2374
|
return this.transformValue(value, "Boolean");
|
|
2088
2375
|
}
|
|
2089
|
-
|
|
2376
|
+
invariant6(ExpressionUtils.isField(expr2.left) || ExpressionUtils.isMember(expr2.left), "left operand must be field or member access");
|
|
2090
2377
|
let newContextModel;
|
|
2091
|
-
|
|
2092
|
-
|
|
2378
|
+
const fieldDef = this.getFieldDefFromFieldRef(expr2.left, context.model);
|
|
2379
|
+
if (fieldDef) {
|
|
2380
|
+
invariant6(fieldDef.relation, `field is not a relation: ${JSON.stringify(expr2.left)}`);
|
|
2093
2381
|
newContextModel = fieldDef.type;
|
|
2094
2382
|
} else {
|
|
2095
|
-
|
|
2096
|
-
const
|
|
2097
|
-
newContextModel =
|
|
2383
|
+
invariant6(ExpressionUtils.isMember(expr2.left) && ExpressionUtils.isField(expr2.left.receiver), "left operand must be member access with field receiver");
|
|
2384
|
+
const fieldDef2 = requireField(this.schema, context.model, expr2.left.receiver.field);
|
|
2385
|
+
newContextModel = fieldDef2.type;
|
|
2098
2386
|
for (const member of expr2.left.members) {
|
|
2099
2387
|
const memberDef = requireField(this.schema, newContextModel, member);
|
|
2100
2388
|
newContextModel = memberDef.type;
|
|
@@ -2103,11 +2391,10 @@ var ExpressionTransformer = class {
|
|
|
2103
2391
|
let predicateFilter = this.transform(expr2.right, {
|
|
2104
2392
|
...context,
|
|
2105
2393
|
model: newContextModel,
|
|
2106
|
-
alias: void 0
|
|
2107
|
-
thisEntity: void 0
|
|
2394
|
+
alias: void 0
|
|
2108
2395
|
});
|
|
2109
2396
|
if (expr2.op === "!") {
|
|
2110
|
-
predicateFilter = logicalNot(predicateFilter);
|
|
2397
|
+
predicateFilter = logicalNot(this.dialect, predicateFilter);
|
|
2111
2398
|
}
|
|
2112
2399
|
const count = FunctionNode2.create("count", [
|
|
2113
2400
|
ValueNode2.createImmediate(1)
|
|
@@ -2115,32 +2402,66 @@ var ExpressionTransformer = class {
|
|
|
2115
2402
|
const predicateResult = match7(expr2.op).with("?", () => BinaryOperationNode2.create(count, OperatorNode2.create(">"), ValueNode2.createImmediate(0))).with("!", () => BinaryOperationNode2.create(count, OperatorNode2.create("="), ValueNode2.createImmediate(0))).with("^", () => BinaryOperationNode2.create(count, OperatorNode2.create("="), ValueNode2.createImmediate(0))).exhaustive();
|
|
2116
2403
|
return this.transform(expr2.left, {
|
|
2117
2404
|
...context,
|
|
2118
|
-
memberSelect: SelectionNode.create(
|
|
2405
|
+
memberSelect: SelectionNode.create(AliasNode3.create(predicateResult, IdentifierNode.create("$t"))),
|
|
2119
2406
|
memberFilter: predicateFilter
|
|
2120
2407
|
});
|
|
2121
2408
|
}
|
|
2122
|
-
transformAuthBinary(expr2) {
|
|
2409
|
+
transformAuthBinary(expr2, context) {
|
|
2123
2410
|
if (expr2.op !== "==" && expr2.op !== "!=") {
|
|
2124
|
-
throw new
|
|
2411
|
+
throw new QueryError(`Unsupported operator for \`auth()\` in policy of model "${context.model}": ${expr2.op}`);
|
|
2125
2412
|
}
|
|
2413
|
+
let authExpr;
|
|
2126
2414
|
let other;
|
|
2127
2415
|
if (this.isAuthCall(expr2.left)) {
|
|
2416
|
+
authExpr = expr2.left;
|
|
2128
2417
|
other = expr2.right;
|
|
2129
2418
|
} else {
|
|
2419
|
+
authExpr = expr2.right;
|
|
2130
2420
|
other = expr2.left;
|
|
2131
2421
|
}
|
|
2132
2422
|
if (ExpressionUtils.isNull(other)) {
|
|
2133
2423
|
return this.transformValue(expr2.op === "==" ? !this.auth : !!this.auth, "Boolean");
|
|
2134
2424
|
} else {
|
|
2135
|
-
|
|
2425
|
+
const authModel = getModel(this.schema, this.authType);
|
|
2426
|
+
if (!authModel) {
|
|
2427
|
+
throw new QueryError(`Unsupported use of \`auth()\` in policy of model "${context.model}", comparing with \`auth()\` is only possible when auth type is a model`);
|
|
2428
|
+
}
|
|
2429
|
+
const idFields = Object.values(authModel.fields).filter((f) => f.id).map((f) => f.name);
|
|
2430
|
+
invariant6(idFields.length > 0, "auth type model must have at least one id field");
|
|
2431
|
+
const conditions = idFields.map((fieldName) => ExpressionUtils.binary(ExpressionUtils.member(authExpr, [
|
|
2432
|
+
fieldName
|
|
2433
|
+
]), "==", this.makeOrAppendMember(other, fieldName)));
|
|
2434
|
+
let result = this.buildAnd(conditions);
|
|
2435
|
+
if (expr2.op === "!=") {
|
|
2436
|
+
result = this.buildLogicalNot(result);
|
|
2437
|
+
}
|
|
2438
|
+
return this.transform(result, context);
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
makeOrAppendMember(other, fieldName) {
|
|
2442
|
+
if (ExpressionUtils.isMember(other)) {
|
|
2443
|
+
return ExpressionUtils.member(other.receiver, [
|
|
2444
|
+
...other.members,
|
|
2445
|
+
fieldName
|
|
2446
|
+
]);
|
|
2447
|
+
} else {
|
|
2448
|
+
return ExpressionUtils.member(other, [
|
|
2449
|
+
fieldName
|
|
2450
|
+
]);
|
|
2136
2451
|
}
|
|
2137
2452
|
}
|
|
2138
2453
|
transformValue(value, type) {
|
|
2139
|
-
|
|
2454
|
+
if (value === true) {
|
|
2455
|
+
return trueNode(this.dialect);
|
|
2456
|
+
} else if (value === false) {
|
|
2457
|
+
return falseNode(this.dialect);
|
|
2458
|
+
} else {
|
|
2459
|
+
return ValueNode2.create(this.dialect.transformPrimitive(value, type, false) ?? null);
|
|
2460
|
+
}
|
|
2140
2461
|
}
|
|
2141
2462
|
_unary(expr2, context) {
|
|
2142
|
-
|
|
2143
|
-
return
|
|
2463
|
+
invariant6(expr2.op === "!", 'only "!" operator is supported');
|
|
2464
|
+
return logicalNot(this.dialect, this.transform(expr2.operand, context));
|
|
2144
2465
|
}
|
|
2145
2466
|
transformOperator(op) {
|
|
2146
2467
|
const mappedOp = match7(op).with("==", () => "=").otherwise(() => op);
|
|
@@ -2151,23 +2472,37 @@ var ExpressionTransformer = class {
|
|
|
2151
2472
|
return result.toOperationNode();
|
|
2152
2473
|
}
|
|
2153
2474
|
transformCall(expr2, context) {
|
|
2154
|
-
const func = this.
|
|
2475
|
+
const func = this.getFunctionImpl(expr2.function);
|
|
2155
2476
|
if (!func) {
|
|
2156
2477
|
throw new QueryError(`Function not implemented: ${expr2.function}`);
|
|
2157
2478
|
}
|
|
2158
2479
|
const eb = expressionBuilder2();
|
|
2159
2480
|
return func(eb, (expr2.args ?? []).map((arg) => this.transformCallArg(eb, arg, context)), {
|
|
2481
|
+
client: this.client,
|
|
2160
2482
|
dialect: this.dialect,
|
|
2161
2483
|
model: context.model,
|
|
2484
|
+
modelAlias: context.alias ?? context.model,
|
|
2162
2485
|
operation: context.operation
|
|
2163
2486
|
});
|
|
2164
2487
|
}
|
|
2488
|
+
getFunctionImpl(functionName) {
|
|
2489
|
+
let func = this.clientOptions.functions?.[functionName];
|
|
2490
|
+
if (!func) {
|
|
2491
|
+
for (const plugin of this.clientOptions.plugins ?? []) {
|
|
2492
|
+
if (plugin.functions?.[functionName]) {
|
|
2493
|
+
func = plugin.functions[functionName];
|
|
2494
|
+
break;
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
return func;
|
|
2499
|
+
}
|
|
2165
2500
|
transformCallArg(eb, arg, context) {
|
|
2166
2501
|
if (ExpressionUtils.isLiteral(arg)) {
|
|
2167
2502
|
return eb.val(arg.value);
|
|
2168
2503
|
}
|
|
2169
2504
|
if (ExpressionUtils.isField(arg)) {
|
|
2170
|
-
return
|
|
2505
|
+
return eb.ref(arg.field);
|
|
2171
2506
|
}
|
|
2172
2507
|
if (ExpressionUtils.isCall(arg)) {
|
|
2173
2508
|
return this.transformCall(arg, context);
|
|
@@ -2182,14 +2517,32 @@ var ExpressionTransformer = class {
|
|
|
2182
2517
|
if (this.isAuthCall(expr2.receiver)) {
|
|
2183
2518
|
return this.valueMemberAccess(this.auth, expr2, this.authType);
|
|
2184
2519
|
}
|
|
2185
|
-
|
|
2520
|
+
invariant6(ExpressionUtils.isField(expr2.receiver) || ExpressionUtils.isThis(expr2.receiver), 'expect receiver to be field expression or "this"');
|
|
2521
|
+
let members = expr2.members;
|
|
2522
|
+
let receiver;
|
|
2186
2523
|
const { memberFilter, memberSelect, ...restContext } = context;
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2524
|
+
if (ExpressionUtils.isThis(expr2.receiver)) {
|
|
2525
|
+
if (expr2.members.length === 1) {
|
|
2526
|
+
return this._field(ExpressionUtils.field(expr2.members[0]), context);
|
|
2527
|
+
} else {
|
|
2528
|
+
const firstMemberFieldDef = requireField(this.schema, context.model, expr2.members[0]);
|
|
2529
|
+
receiver = this.transformRelationAccess(expr2.members[0], firstMemberFieldDef.type, restContext);
|
|
2530
|
+
members = expr2.members.slice(1);
|
|
2531
|
+
}
|
|
2532
|
+
} else {
|
|
2533
|
+
receiver = this.transform(expr2.receiver, restContext);
|
|
2534
|
+
}
|
|
2535
|
+
invariant6(SelectQueryNode.is(receiver), "expected receiver to be select query");
|
|
2536
|
+
let startType;
|
|
2537
|
+
if (ExpressionUtils.isField(expr2.receiver)) {
|
|
2538
|
+
const receiverField = requireField(this.schema, context.model, expr2.receiver.field);
|
|
2539
|
+
startType = receiverField.type;
|
|
2540
|
+
} else {
|
|
2541
|
+
startType = context.model;
|
|
2542
|
+
}
|
|
2190
2543
|
const memberFields = [];
|
|
2191
|
-
let currType =
|
|
2192
|
-
for (const member of
|
|
2544
|
+
let currType = startType;
|
|
2545
|
+
for (const member of members) {
|
|
2193
2546
|
const fieldDef = requireField(this.schema, currType, member);
|
|
2194
2547
|
memberFields.push({
|
|
2195
2548
|
fieldDef,
|
|
@@ -2198,22 +2551,21 @@ var ExpressionTransformer = class {
|
|
|
2198
2551
|
currType = fieldDef.type;
|
|
2199
2552
|
}
|
|
2200
2553
|
let currNode = void 0;
|
|
2201
|
-
for (let i =
|
|
2202
|
-
const member =
|
|
2554
|
+
for (let i = members.length - 1; i >= 0; i--) {
|
|
2555
|
+
const member = members[i];
|
|
2203
2556
|
const { fieldDef, fromModel } = memberFields[i];
|
|
2204
2557
|
if (fieldDef.relation) {
|
|
2205
2558
|
const relation = this.transformRelationAccess(member, fieldDef.type, {
|
|
2206
2559
|
...restContext,
|
|
2207
2560
|
model: fromModel,
|
|
2208
|
-
alias: void 0
|
|
2209
|
-
thisEntity: void 0
|
|
2561
|
+
alias: void 0
|
|
2210
2562
|
});
|
|
2211
2563
|
if (currNode) {
|
|
2212
|
-
|
|
2564
|
+
invariant6(SelectQueryNode.is(currNode), "expected select query node");
|
|
2213
2565
|
currNode = {
|
|
2214
2566
|
...relation,
|
|
2215
2567
|
selections: [
|
|
2216
|
-
SelectionNode.create(
|
|
2568
|
+
SelectionNode.create(AliasNode3.create(currNode, IdentifierNode.create(members[i + 1])))
|
|
2217
2569
|
]
|
|
2218
2570
|
};
|
|
2219
2571
|
} else {
|
|
@@ -2226,15 +2578,15 @@ var ExpressionTransformer = class {
|
|
|
2226
2578
|
};
|
|
2227
2579
|
}
|
|
2228
2580
|
} else {
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
currNode =
|
|
2581
|
+
invariant6(i === members.length - 1, "plain field access must be the last segment");
|
|
2582
|
+
invariant6(!currNode, "plain field access must be the last segment");
|
|
2583
|
+
currNode = ColumnNode2.create(member);
|
|
2232
2584
|
}
|
|
2233
2585
|
}
|
|
2234
2586
|
return {
|
|
2235
2587
|
...receiver,
|
|
2236
2588
|
selections: [
|
|
2237
|
-
SelectionNode.create(
|
|
2589
|
+
SelectionNode.create(AliasNode3.create(currNode, IdentifierNode.create("$t")))
|
|
2238
2590
|
]
|
|
2239
2591
|
};
|
|
2240
2592
|
}
|
|
@@ -2251,40 +2603,33 @@ var ExpressionTransformer = class {
|
|
|
2251
2603
|
return this.transformValue(fieldValue, fieldDef.type);
|
|
2252
2604
|
}
|
|
2253
2605
|
transformRelationAccess(field, relationModel, context) {
|
|
2606
|
+
const m2m = getManyToManyRelation(this.schema, context.model, field);
|
|
2607
|
+
if (m2m) {
|
|
2608
|
+
return this.transformManyToManyRelationAccess(m2m, context);
|
|
2609
|
+
}
|
|
2254
2610
|
const fromModel = context.model;
|
|
2255
2611
|
const { keyPairs, ownedByModel } = getRelationForeignKeyFieldPairs(this.schema, fromModel, field);
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode2.create(ColumnNode.create(pk), TableNode2.create(relationModel)), OperatorNode2.create("="), context.thisEntity[fk])));
|
|
2260
|
-
} else {
|
|
2261
|
-
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode2.create(ColumnNode.create(fk), TableNode2.create(relationModel)), OperatorNode2.create("="), context.thisEntity[pk])));
|
|
2262
|
-
}
|
|
2263
|
-
return {
|
|
2264
|
-
kind: "SelectQueryNode",
|
|
2265
|
-
from: FromNode.create([
|
|
2266
|
-
TableNode2.create(relationModel)
|
|
2267
|
-
]),
|
|
2268
|
-
where: WhereNode.create(condition)
|
|
2269
|
-
};
|
|
2612
|
+
let condition;
|
|
2613
|
+
if (ownedByModel) {
|
|
2614
|
+
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode3.create(ColumnNode2.create(fk), TableNode3.create(context.alias ?? fromModel)), OperatorNode2.create("="), ReferenceNode3.create(ColumnNode2.create(pk), TableNode3.create(relationModel)))));
|
|
2270
2615
|
} else {
|
|
2271
|
-
|
|
2272
|
-
if (ownedByModel) {
|
|
2273
|
-
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode2.create(ColumnNode.create(fk), TableNode2.create(context.alias ?? fromModel)), OperatorNode2.create("="), ReferenceNode2.create(ColumnNode.create(pk), TableNode2.create(relationModel)))));
|
|
2274
|
-
} else {
|
|
2275
|
-
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode2.create(ColumnNode.create(pk), TableNode2.create(context.alias ?? fromModel)), OperatorNode2.create("="), ReferenceNode2.create(ColumnNode.create(fk), TableNode2.create(relationModel)))));
|
|
2276
|
-
}
|
|
2277
|
-
return {
|
|
2278
|
-
kind: "SelectQueryNode",
|
|
2279
|
-
from: FromNode.create([
|
|
2280
|
-
TableNode2.create(relationModel)
|
|
2281
|
-
]),
|
|
2282
|
-
where: WhereNode.create(condition)
|
|
2283
|
-
};
|
|
2616
|
+
condition = conjunction(this.dialect, keyPairs.map(({ fk, pk }) => BinaryOperationNode2.create(ReferenceNode3.create(ColumnNode2.create(pk), TableNode3.create(context.alias ?? fromModel)), OperatorNode2.create("="), ReferenceNode3.create(ColumnNode2.create(fk), TableNode3.create(relationModel)))));
|
|
2284
2617
|
}
|
|
2618
|
+
return {
|
|
2619
|
+
kind: "SelectQueryNode",
|
|
2620
|
+
from: FromNode.create([
|
|
2621
|
+
TableNode3.create(relationModel)
|
|
2622
|
+
]),
|
|
2623
|
+
where: WhereNode.create(condition)
|
|
2624
|
+
};
|
|
2625
|
+
}
|
|
2626
|
+
transformManyToManyRelationAccess(m2m, context) {
|
|
2627
|
+
const eb = expressionBuilder2();
|
|
2628
|
+
const relationQuery = eb.selectFrom(m2m.otherModel).innerJoin(m2m.joinTable, (join) => join.onRef(`${m2m.otherModel}.${m2m.otherPKName}`, "=", `${m2m.joinTable}.${m2m.otherFkName}`).onRef(`${m2m.joinTable}.${m2m.parentFkName}`, "=", `${context.alias ?? context.model}.${m2m.parentPKName}`));
|
|
2629
|
+
return relationQuery.toOperationNode();
|
|
2285
2630
|
}
|
|
2286
2631
|
createColumnRef(column, context) {
|
|
2287
|
-
return
|
|
2632
|
+
return ReferenceNode3.create(ColumnNode2.create(column), TableNode3.create(context.alias ?? context.model));
|
|
2288
2633
|
}
|
|
2289
2634
|
isAuthCall(value) {
|
|
2290
2635
|
return ExpressionUtils.isCall(value) && value.function === "auth";
|
|
@@ -2295,6 +2640,31 @@ var ExpressionTransformer = class {
|
|
|
2295
2640
|
isNullNode(node) {
|
|
2296
2641
|
return ValueNode2.is(node) && node.value === null;
|
|
2297
2642
|
}
|
|
2643
|
+
buildLogicalNot(result) {
|
|
2644
|
+
return ExpressionUtils.unary("!", result);
|
|
2645
|
+
}
|
|
2646
|
+
buildAnd(conditions) {
|
|
2647
|
+
if (conditions.length === 0) {
|
|
2648
|
+
return ExpressionUtils.literal(true);
|
|
2649
|
+
} else if (conditions.length === 1) {
|
|
2650
|
+
return conditions[0];
|
|
2651
|
+
} else {
|
|
2652
|
+
return conditions.reduce((acc, condition) => ExpressionUtils.binary(acc, "&&", condition));
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
isRelationField(expr2, model) {
|
|
2656
|
+
const fieldDef = this.getFieldDefFromFieldRef(expr2, model);
|
|
2657
|
+
return !!fieldDef?.relation;
|
|
2658
|
+
}
|
|
2659
|
+
getFieldDefFromFieldRef(expr2, model) {
|
|
2660
|
+
if (ExpressionUtils.isField(expr2)) {
|
|
2661
|
+
return requireField(this.schema, model, expr2.field);
|
|
2662
|
+
} else if (ExpressionUtils.isMember(expr2) && expr2.members.length === 1 && ExpressionUtils.isThis(expr2.receiver)) {
|
|
2663
|
+
return requireField(this.schema, model, expr2.members[0]);
|
|
2664
|
+
} else {
|
|
2665
|
+
return void 0;
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2298
2668
|
};
|
|
2299
2669
|
_ts_decorate([
|
|
2300
2670
|
expr("literal"),
|
|
@@ -2381,103 +2751,288 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2381
2751
|
}
|
|
2382
2752
|
async handle(node, proceed) {
|
|
2383
2753
|
if (!this.isCrudQueryNode(node)) {
|
|
2384
|
-
throw new RejectedByPolicyError(void 0, "non-CRUD queries are not allowed");
|
|
2754
|
+
throw new RejectedByPolicyError(void 0, RejectedByPolicyReason.OTHER, "non-CRUD queries are not allowed");
|
|
2385
2755
|
}
|
|
2386
2756
|
if (!this.isMutationQueryNode(node)) {
|
|
2387
2757
|
return proceed(this.transformNode(node));
|
|
2388
2758
|
}
|
|
2389
|
-
|
|
2390
|
-
const mutationModel = this.getMutationModel(node);
|
|
2759
|
+
const { mutationModel } = this.getMutationModel(node);
|
|
2391
2760
|
if (InsertQueryNode.is(node)) {
|
|
2392
|
-
const
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2761
|
+
const isManyToManyJoinTable = this.isManyToManyJoinTable(mutationModel);
|
|
2762
|
+
let needCheckPreCreate = true;
|
|
2763
|
+
if (!isManyToManyJoinTable) {
|
|
2764
|
+
const constCondition = this.tryGetConstantPolicy(mutationModel, "create");
|
|
2765
|
+
if (constCondition === true) {
|
|
2766
|
+
needCheckPreCreate = false;
|
|
2767
|
+
} else if (constCondition === false) {
|
|
2768
|
+
throw new RejectedByPolicyError(mutationModel);
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
if (needCheckPreCreate) {
|
|
2772
|
+
await this.enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed);
|
|
2397
2773
|
}
|
|
2398
2774
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
await this.enforcePreCreatePolicy(node, proceed);
|
|
2404
|
-
}
|
|
2405
|
-
const transformedNode = this.transformNode(node);
|
|
2406
|
-
const result = await proceed(transformedNode);
|
|
2407
|
-
if (!this.onlyReturningId(node)) {
|
|
2775
|
+
const result = await proceed(this.transformNode(node));
|
|
2776
|
+
if (!node.returning || this.onlyReturningId(node)) {
|
|
2777
|
+
return result;
|
|
2778
|
+
} else {
|
|
2408
2779
|
const readBackResult = await this.processReadBack(node, result, proceed);
|
|
2409
2780
|
if (readBackResult.rows.length !== result.rows.length) {
|
|
2410
|
-
throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
|
|
2781
|
+
throw new RejectedByPolicyError(mutationModel, RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
|
|
2411
2782
|
}
|
|
2412
2783
|
return readBackResult;
|
|
2413
|
-
}
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
// #region overrides
|
|
2787
|
+
transformSelectQuery(node) {
|
|
2788
|
+
let whereNode = this.transformNode(node.where);
|
|
2789
|
+
const policyFilter = this.createPolicyFilterForFrom(node.from);
|
|
2790
|
+
if (policyFilter) {
|
|
2791
|
+
whereNode = WhereNode2.create(whereNode?.where ? conjunction(this.dialect, [
|
|
2792
|
+
whereNode.where,
|
|
2793
|
+
policyFilter
|
|
2794
|
+
]) : policyFilter);
|
|
2795
|
+
}
|
|
2796
|
+
const baseResult = super.transformSelectQuery({
|
|
2797
|
+
...node,
|
|
2798
|
+
where: void 0
|
|
2799
|
+
});
|
|
2800
|
+
return {
|
|
2801
|
+
...baseResult,
|
|
2802
|
+
where: whereNode
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
transformJoin(node) {
|
|
2806
|
+
const table = this.extractTableName(node.table);
|
|
2807
|
+
if (!table) {
|
|
2808
|
+
return super.transformJoin(node);
|
|
2809
|
+
}
|
|
2810
|
+
const filter = this.buildPolicyFilter(table.model, table.alias, "read");
|
|
2811
|
+
const nestedSelect = {
|
|
2812
|
+
kind: "SelectQueryNode",
|
|
2813
|
+
from: FromNode2.create([
|
|
2814
|
+
node.table
|
|
2815
|
+
]),
|
|
2816
|
+
selections: [
|
|
2817
|
+
SelectionNode2.createSelectAll()
|
|
2818
|
+
],
|
|
2819
|
+
where: WhereNode2.create(filter)
|
|
2820
|
+
};
|
|
2821
|
+
return {
|
|
2822
|
+
...node,
|
|
2823
|
+
table: AliasNode4.create(ParensNode2.create(nestedSelect), IdentifierNode2.create(table.alias ?? table.model))
|
|
2824
|
+
};
|
|
2825
|
+
}
|
|
2826
|
+
transformInsertQuery(node) {
|
|
2827
|
+
let onConflict = node.onConflict;
|
|
2828
|
+
if (onConflict?.updates) {
|
|
2829
|
+
const { mutationModel, alias } = this.getMutationModel(node);
|
|
2830
|
+
const filter = this.buildPolicyFilter(mutationModel, alias, "update");
|
|
2831
|
+
if (onConflict.updateWhere) {
|
|
2832
|
+
onConflict = {
|
|
2833
|
+
...onConflict,
|
|
2834
|
+
updateWhere: WhereNode2.create(conjunction(this.dialect, [
|
|
2835
|
+
onConflict.updateWhere.where,
|
|
2836
|
+
filter
|
|
2837
|
+
]))
|
|
2838
|
+
};
|
|
2839
|
+
} else {
|
|
2840
|
+
onConflict = {
|
|
2841
|
+
...onConflict,
|
|
2842
|
+
updateWhere: WhereNode2.create(filter)
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
const processedNode = onConflict ? {
|
|
2847
|
+
...node,
|
|
2848
|
+
onConflict
|
|
2849
|
+
} : node;
|
|
2850
|
+
const result = super.transformInsertQuery(processedNode);
|
|
2851
|
+
if (!node.returning) {
|
|
2414
2852
|
return result;
|
|
2415
2853
|
}
|
|
2854
|
+
if (this.onlyReturningId(node)) {
|
|
2855
|
+
return result;
|
|
2856
|
+
} else {
|
|
2857
|
+
const { mutationModel } = this.getMutationModel(node);
|
|
2858
|
+
const idFields = requireIdFields(this.client.$schema, mutationModel);
|
|
2859
|
+
return {
|
|
2860
|
+
...result,
|
|
2861
|
+
returning: ReturningNode.create(idFields.map((field) => SelectionNode2.create(ColumnNode3.create(field))))
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
transformUpdateQuery(node) {
|
|
2866
|
+
const result = super.transformUpdateQuery(node);
|
|
2867
|
+
const { mutationModel, alias } = this.getMutationModel(node);
|
|
2868
|
+
let filter = this.buildPolicyFilter(mutationModel, alias, "update");
|
|
2869
|
+
if (node.from) {
|
|
2870
|
+
const joinFilter = this.createPolicyFilterForFrom(node.from);
|
|
2871
|
+
if (joinFilter) {
|
|
2872
|
+
filter = conjunction(this.dialect, [
|
|
2873
|
+
filter,
|
|
2874
|
+
joinFilter
|
|
2875
|
+
]);
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
return {
|
|
2879
|
+
...result,
|
|
2880
|
+
where: WhereNode2.create(result.where ? conjunction(this.dialect, [
|
|
2881
|
+
result.where.where,
|
|
2882
|
+
filter
|
|
2883
|
+
]) : filter)
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
transformDeleteQuery(node) {
|
|
2887
|
+
const result = super.transformDeleteQuery(node);
|
|
2888
|
+
const { mutationModel, alias } = this.getMutationModel(node);
|
|
2889
|
+
let filter = this.buildPolicyFilter(mutationModel, alias, "delete");
|
|
2890
|
+
if (node.using) {
|
|
2891
|
+
const joinFilter = this.createPolicyFilterForTables(node.using.tables);
|
|
2892
|
+
if (joinFilter) {
|
|
2893
|
+
filter = conjunction(this.dialect, [
|
|
2894
|
+
filter,
|
|
2895
|
+
joinFilter
|
|
2896
|
+
]);
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
return {
|
|
2900
|
+
...result,
|
|
2901
|
+
where: WhereNode2.create(result.where ? conjunction(this.dialect, [
|
|
2902
|
+
result.where.where,
|
|
2903
|
+
filter
|
|
2904
|
+
]) : filter)
|
|
2905
|
+
};
|
|
2416
2906
|
}
|
|
2907
|
+
// #endregion
|
|
2908
|
+
// #region helpers
|
|
2417
2909
|
onlyReturningId(node) {
|
|
2418
2910
|
if (!node.returning) {
|
|
2419
2911
|
return true;
|
|
2420
2912
|
}
|
|
2421
|
-
const
|
|
2913
|
+
const { mutationModel } = this.getMutationModel(node);
|
|
2914
|
+
const idFields = requireIdFields(this.client.$schema, mutationModel);
|
|
2422
2915
|
const collector = new ColumnCollector();
|
|
2423
2916
|
const selectedColumns = collector.collect(node.returning);
|
|
2424
2917
|
return selectedColumns.every((c) => idFields.includes(c));
|
|
2425
2918
|
}
|
|
2426
|
-
async enforcePreCreatePolicy(node, proceed) {
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
const fields = node.columns.map((c) => c.column.name);
|
|
2432
|
-
const valueRows = this.unwrapCreateValueRows(node.values, model, fields);
|
|
2919
|
+
async enforcePreCreatePolicy(node, mutationModel, isManyToManyJoinTable, proceed) {
|
|
2920
|
+
const fields = node.columns?.map((c) => c.column.name) ?? [];
|
|
2921
|
+
const valueRows = node.values ? this.unwrapCreateValueRows(node.values, mutationModel, fields, isManyToManyJoinTable) : [
|
|
2922
|
+
[]
|
|
2923
|
+
];
|
|
2433
2924
|
for (const values of valueRows) {
|
|
2434
|
-
|
|
2925
|
+
if (isManyToManyJoinTable) {
|
|
2926
|
+
await this.enforcePreCreatePolicyForManyToManyJoinTable(mutationModel, fields, values.map((v) => v.node), proceed);
|
|
2927
|
+
} else {
|
|
2928
|
+
await this.enforcePreCreatePolicyForOne(mutationModel, fields, values.map((v) => v.node), proceed);
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
async enforcePreCreatePolicyForManyToManyJoinTable(tableName, fields, values, proceed) {
|
|
2933
|
+
const m2m = this.resolveManyToManyJoinTable(tableName);
|
|
2934
|
+
invariant7(m2m);
|
|
2935
|
+
invariant7(fields.includes("A") && fields.includes("B"), "many-to-many join table must have A and B fk fields");
|
|
2936
|
+
const aIndex = fields.indexOf("A");
|
|
2937
|
+
const aNode = values[aIndex];
|
|
2938
|
+
const bIndex = fields.indexOf("B");
|
|
2939
|
+
const bNode = values[bIndex];
|
|
2940
|
+
invariant7(ValueNode3.is(aNode) && ValueNode3.is(bNode), "A and B values must be ValueNode");
|
|
2941
|
+
const aValue = aNode.value;
|
|
2942
|
+
const bValue = bNode.value;
|
|
2943
|
+
invariant7(aValue !== null && aValue !== void 0, "A value cannot be null or undefined");
|
|
2944
|
+
invariant7(bValue !== null && bValue !== void 0, "B value cannot be null or undefined");
|
|
2945
|
+
const eb = expressionBuilder3();
|
|
2946
|
+
const filterA = this.buildPolicyFilter(m2m.firstModel, void 0, "update");
|
|
2947
|
+
const queryA = eb.selectFrom(m2m.firstModel).where(eb(eb.ref(`${m2m.firstModel}.${m2m.firstIdField}`), "=", aValue)).select(() => new ExpressionWrapper(filterA).as("$t"));
|
|
2948
|
+
const filterB = this.buildPolicyFilter(m2m.secondModel, void 0, "update");
|
|
2949
|
+
const queryB = eb.selectFrom(m2m.secondModel).where(eb(eb.ref(`${m2m.secondModel}.${m2m.secondIdField}`), "=", bValue)).select(() => new ExpressionWrapper(filterB).as("$t"));
|
|
2950
|
+
const queryNode = {
|
|
2951
|
+
kind: "SelectQueryNode",
|
|
2952
|
+
selections: [
|
|
2953
|
+
SelectionNode2.create(AliasNode4.create(queryA.toOperationNode(), IdentifierNode2.create("$conditionA"))),
|
|
2954
|
+
SelectionNode2.create(AliasNode4.create(queryB.toOperationNode(), IdentifierNode2.create("$conditionB")))
|
|
2955
|
+
]
|
|
2956
|
+
};
|
|
2957
|
+
const result = await proceed(queryNode);
|
|
2958
|
+
if (!result.rows[0]?.$conditionA) {
|
|
2959
|
+
throw new RejectedByPolicyError(m2m.firstModel, RejectedByPolicyReason.CANNOT_READ_BACK, `many-to-many relation participant model "${m2m.firstModel}" not updatable`);
|
|
2960
|
+
}
|
|
2961
|
+
if (!result.rows[0]?.$conditionB) {
|
|
2962
|
+
throw new RejectedByPolicyError(m2m.secondModel, RejectedByPolicyReason.NO_ACCESS, `many-to-many relation participant model "${m2m.secondModel}" not updatable`);
|
|
2435
2963
|
}
|
|
2436
2964
|
}
|
|
2437
|
-
async enforcePreCreatePolicyForOne(model, fields, values,
|
|
2438
|
-
const
|
|
2439
|
-
const
|
|
2440
|
-
for (
|
|
2441
|
-
|
|
2442
|
-
|
|
2965
|
+
async enforcePreCreatePolicyForOne(model, fields, values, proceed) {
|
|
2966
|
+
const allFields = Object.entries(requireModel(this.client.$schema, model).fields).filter(([, def]) => !def.relation);
|
|
2967
|
+
const allValues = [];
|
|
2968
|
+
for (const [name, _def] of allFields) {
|
|
2969
|
+
const index = fields.indexOf(name);
|
|
2970
|
+
if (index >= 0) {
|
|
2971
|
+
allValues.push(values[index]);
|
|
2972
|
+
} else {
|
|
2973
|
+
allValues.push(ValueNode3.createImmediate(null));
|
|
2974
|
+
}
|
|
2443
2975
|
}
|
|
2444
|
-
const
|
|
2976
|
+
const eb = expressionBuilder3();
|
|
2977
|
+
const constTable = {
|
|
2978
|
+
kind: "SelectQueryNode",
|
|
2979
|
+
from: FromNode2.create([
|
|
2980
|
+
AliasNode4.create(ParensNode2.create(ValuesNode.create([
|
|
2981
|
+
ValueListNode2.create(allValues)
|
|
2982
|
+
])), IdentifierNode2.create("$t"))
|
|
2983
|
+
]),
|
|
2984
|
+
selections: allFields.map(([name, def], index) => {
|
|
2985
|
+
const castedColumnRef = sql4`CAST(${eb.ref(`column${index + 1}`)} as ${sql4.raw(this.dialect.getFieldSqlType(def))})`.as(name);
|
|
2986
|
+
return SelectionNode2.create(castedColumnRef.toOperationNode());
|
|
2987
|
+
})
|
|
2988
|
+
};
|
|
2989
|
+
const filter = this.buildPolicyFilter(model, void 0, "create");
|
|
2445
2990
|
const preCreateCheck = {
|
|
2446
2991
|
kind: "SelectQueryNode",
|
|
2992
|
+
from: FromNode2.create([
|
|
2993
|
+
AliasNode4.create(constTable, IdentifierNode2.create(model))
|
|
2994
|
+
]),
|
|
2447
2995
|
selections: [
|
|
2448
|
-
SelectionNode2.create(
|
|
2449
|
-
|
|
2996
|
+
SelectionNode2.create(AliasNode4.create(BinaryOperationNode3.create(FunctionNode3.create("COUNT", [
|
|
2997
|
+
ValueNode3.createImmediate(1)
|
|
2998
|
+
]), OperatorNode3.create(">"), ValueNode3.createImmediate(0)), IdentifierNode2.create("$condition")))
|
|
2999
|
+
],
|
|
3000
|
+
where: WhereNode2.create(filter)
|
|
2450
3001
|
};
|
|
2451
3002
|
const result = await proceed(preCreateCheck);
|
|
2452
3003
|
if (!result.rows[0]?.$condition) {
|
|
2453
3004
|
throw new RejectedByPolicyError(model);
|
|
2454
3005
|
}
|
|
2455
3006
|
}
|
|
2456
|
-
unwrapCreateValueRows(node, model, fields) {
|
|
3007
|
+
unwrapCreateValueRows(node, model, fields, isManyToManyJoinTable) {
|
|
2457
3008
|
if (ValuesNode.is(node)) {
|
|
2458
|
-
return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields));
|
|
3009
|
+
return node.values.map((v) => this.unwrapCreateValueRow(v.values, model, fields, isManyToManyJoinTable));
|
|
2459
3010
|
} else if (PrimitiveValueListNode.is(node)) {
|
|
2460
3011
|
return [
|
|
2461
|
-
this.unwrapCreateValueRow(node.values, model, fields)
|
|
3012
|
+
this.unwrapCreateValueRow(node.values, model, fields, isManyToManyJoinTable)
|
|
2462
3013
|
];
|
|
2463
3014
|
} else {
|
|
2464
3015
|
throw new InternalError(`Unexpected node kind: ${node.kind} for unwrapping create values`);
|
|
2465
3016
|
}
|
|
2466
3017
|
}
|
|
2467
|
-
unwrapCreateValueRow(data, model, fields) {
|
|
2468
|
-
|
|
3018
|
+
unwrapCreateValueRow(data, model, fields, isImplicitManyToManyJoinTable) {
|
|
3019
|
+
invariant7(data.length === fields.length, "data length must match fields length");
|
|
2469
3020
|
const result = [];
|
|
2470
3021
|
for (let i = 0; i < data.length; i++) {
|
|
2471
3022
|
const item = data[i];
|
|
2472
|
-
const fieldDef = requireField(this.client.$schema, model, fields[i]);
|
|
2473
3023
|
if (typeof item === "object" && item && "kind" in item) {
|
|
2474
|
-
|
|
3024
|
+
const fieldDef = requireField(this.client.$schema, model, fields[i]);
|
|
3025
|
+
invariant7(item.kind === "ValueNode", "expecting a ValueNode");
|
|
2475
3026
|
result.push({
|
|
2476
3027
|
node: ValueNode3.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
|
|
2477
3028
|
raw: item.value
|
|
2478
3029
|
});
|
|
2479
3030
|
} else {
|
|
2480
|
-
|
|
3031
|
+
let value = item;
|
|
3032
|
+
if (!isImplicitManyToManyJoinTable) {
|
|
3033
|
+
const fieldDef = requireField(this.client.$schema, model, fields[i]);
|
|
3034
|
+
value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
|
|
3035
|
+
}
|
|
2481
3036
|
if (Array.isArray(value)) {
|
|
2482
3037
|
result.push({
|
|
2483
3038
|
node: RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
|
|
@@ -2521,16 +3076,13 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2521
3076
|
if (!this.isMutationQueryNode(node) || !node.returning) {
|
|
2522
3077
|
return result;
|
|
2523
3078
|
}
|
|
2524
|
-
const
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
}
|
|
2528
|
-
const idConditions = this.buildIdConditions(table, result.rows);
|
|
2529
|
-
const policyFilter = this.buildPolicyFilter(table, void 0, "read");
|
|
3079
|
+
const { mutationModel } = this.getMutationModel(node);
|
|
3080
|
+
const idConditions = this.buildIdConditions(mutationModel, result.rows);
|
|
3081
|
+
const policyFilter = this.buildPolicyFilter(mutationModel, void 0, "read");
|
|
2530
3082
|
const select = {
|
|
2531
3083
|
kind: "SelectQueryNode",
|
|
2532
3084
|
from: FromNode2.create([
|
|
2533
|
-
|
|
3085
|
+
TableNode4.create(mutationModel)
|
|
2534
3086
|
]),
|
|
2535
3087
|
where: WhereNode2.create(conjunction(this.dialect, [
|
|
2536
3088
|
idConditions,
|
|
@@ -2542,15 +3094,31 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2542
3094
|
return selectResult;
|
|
2543
3095
|
}
|
|
2544
3096
|
buildIdConditions(table, rows) {
|
|
2545
|
-
const idFields =
|
|
2546
|
-
return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => BinaryOperationNode3.create(
|
|
3097
|
+
const idFields = requireIdFields(this.client.$schema, table);
|
|
3098
|
+
return disjunction(this.dialect, rows.map((row) => conjunction(this.dialect, idFields.map((field) => BinaryOperationNode3.create(ColumnNode3.create(field), OperatorNode3.create("="), ValueNode3.create(row[field]))))));
|
|
2547
3099
|
}
|
|
2548
3100
|
getMutationModel(node) {
|
|
2549
|
-
const r = match8(node).when(InsertQueryNode.is, (node2) =>
|
|
3101
|
+
const r = match8(node).when(InsertQueryNode.is, (node2) => ({
|
|
3102
|
+
mutationModel: getTableName(node2.into),
|
|
3103
|
+
alias: void 0
|
|
3104
|
+
})).when(UpdateQueryNode.is, (node2) => {
|
|
3105
|
+
if (!node2.table) {
|
|
3106
|
+
throw new QueryError("Update query must have a table");
|
|
3107
|
+
}
|
|
3108
|
+
const r2 = this.extractTableName(node2.table);
|
|
3109
|
+
return r2 ? {
|
|
3110
|
+
mutationModel: r2.model,
|
|
3111
|
+
alias: r2.alias
|
|
3112
|
+
} : void 0;
|
|
3113
|
+
}).when(DeleteQueryNode.is, (node2) => {
|
|
2550
3114
|
if (node2.from.froms.length !== 1) {
|
|
2551
|
-
throw new
|
|
3115
|
+
throw new QueryError("Only one from table is supported for delete");
|
|
2552
3116
|
}
|
|
2553
|
-
|
|
3117
|
+
const r2 = this.extractTableName(node2.from.froms[0]);
|
|
3118
|
+
return r2 ? {
|
|
3119
|
+
mutationModel: r2.model,
|
|
3120
|
+
alias: r2.alias
|
|
3121
|
+
} : void 0;
|
|
2554
3122
|
}).exhaustive();
|
|
2555
3123
|
if (!r) {
|
|
2556
3124
|
throw new InternalError(`Unable to get table name for query node: ${node}`);
|
|
@@ -2563,13 +3131,17 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2563
3131
|
isMutationQueryNode(node) {
|
|
2564
3132
|
return InsertQueryNode.is(node) || UpdateQueryNode.is(node) || DeleteQueryNode.is(node);
|
|
2565
3133
|
}
|
|
2566
|
-
buildPolicyFilter(model, alias, operation
|
|
3134
|
+
buildPolicyFilter(model, alias, operation) {
|
|
3135
|
+
const m2mFilter = this.getModelPolicyFilterForManyToManyJoinTable(model, alias, operation);
|
|
3136
|
+
if (m2mFilter) {
|
|
3137
|
+
return m2mFilter;
|
|
3138
|
+
}
|
|
2567
3139
|
const policies = this.getModelPolicies(model, operation);
|
|
2568
3140
|
if (policies.length === 0) {
|
|
2569
3141
|
return falseNode(this.dialect);
|
|
2570
3142
|
}
|
|
2571
|
-
const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.
|
|
2572
|
-
const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.
|
|
3143
|
+
const allows = policies.filter((policy) => policy.kind === "allow").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
|
|
3144
|
+
const denies = policies.filter((policy) => policy.kind === "deny").map((policy) => this.compilePolicyCondition(model, alias, operation, policy));
|
|
2573
3145
|
let combinedPolicy;
|
|
2574
3146
|
if (allows.length === 0) {
|
|
2575
3147
|
combinedPolicy = falseNode(this.dialect);
|
|
@@ -2577,110 +3149,67 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2577
3149
|
combinedPolicy = disjunction(this.dialect, allows);
|
|
2578
3150
|
if (denies.length !== 0) {
|
|
2579
3151
|
const combinedDenies = conjunction(this.dialect, denies.map((d) => buildIsFalse(d, this.dialect)));
|
|
2580
|
-
combinedPolicy = conjunction(this.dialect, [
|
|
2581
|
-
combinedPolicy,
|
|
2582
|
-
combinedDenies
|
|
2583
|
-
]);
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
return combinedPolicy;
|
|
2587
|
-
}
|
|
2588
|
-
transformSelectQuery(node) {
|
|
2589
|
-
let whereNode = node.where;
|
|
2590
|
-
node.from?.froms.forEach((from) => {
|
|
2591
|
-
const extractResult = this.extractTableName(from);
|
|
2592
|
-
if (extractResult) {
|
|
2593
|
-
const { model, alias } = extractResult;
|
|
2594
|
-
const filter = this.buildPolicyFilter(model, alias, "read");
|
|
2595
|
-
whereNode = WhereNode2.create(whereNode?.where ? conjunction(this.dialect, [
|
|
2596
|
-
whereNode.where,
|
|
2597
|
-
filter
|
|
2598
|
-
]) : filter);
|
|
2599
|
-
}
|
|
2600
|
-
});
|
|
2601
|
-
const baseResult = super.transformSelectQuery({
|
|
2602
|
-
...node,
|
|
2603
|
-
where: void 0
|
|
2604
|
-
});
|
|
2605
|
-
return {
|
|
2606
|
-
...baseResult,
|
|
2607
|
-
where: whereNode
|
|
2608
|
-
};
|
|
2609
|
-
}
|
|
2610
|
-
transformInsertQuery(node) {
|
|
2611
|
-
const result = super.transformInsertQuery(node);
|
|
2612
|
-
if (!node.returning) {
|
|
2613
|
-
return result;
|
|
2614
|
-
}
|
|
2615
|
-
if (this.onlyReturningId(node)) {
|
|
2616
|
-
return result;
|
|
2617
|
-
} else {
|
|
2618
|
-
const idFields = getIdFields(this.client.$schema, this.getMutationModel(node));
|
|
2619
|
-
return {
|
|
2620
|
-
...result,
|
|
2621
|
-
returning: ReturningNode.create(idFields.map((field) => SelectionNode2.create(ColumnNode2.create(field))))
|
|
2622
|
-
};
|
|
3152
|
+
combinedPolicy = conjunction(this.dialect, [
|
|
3153
|
+
combinedPolicy,
|
|
3154
|
+
combinedDenies
|
|
3155
|
+
]);
|
|
3156
|
+
}
|
|
2623
3157
|
}
|
|
3158
|
+
return combinedPolicy;
|
|
2624
3159
|
}
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
const mutationModel = this.getMutationModel(node);
|
|
2628
|
-
const filter = this.buildPolicyFilter(mutationModel, void 0, "update");
|
|
2629
|
-
return {
|
|
2630
|
-
...result,
|
|
2631
|
-
where: WhereNode2.create(result.where ? conjunction(this.dialect, [
|
|
2632
|
-
result.where.where,
|
|
2633
|
-
filter
|
|
2634
|
-
]) : filter)
|
|
2635
|
-
};
|
|
2636
|
-
}
|
|
2637
|
-
transformDeleteQuery(node) {
|
|
2638
|
-
const result = super.transformDeleteQuery(node);
|
|
2639
|
-
const mutationModel = this.getMutationModel(node);
|
|
2640
|
-
const filter = this.buildPolicyFilter(mutationModel, void 0, "delete");
|
|
2641
|
-
return {
|
|
2642
|
-
...result,
|
|
2643
|
-
where: WhereNode2.create(result.where ? conjunction(this.dialect, [
|
|
2644
|
-
result.where.where,
|
|
2645
|
-
filter
|
|
2646
|
-
]) : filter)
|
|
2647
|
-
};
|
|
2648
|
-
}
|
|
2649
|
-
extractTableName(from) {
|
|
2650
|
-
if (TableNode3.is(from)) {
|
|
3160
|
+
extractTableName(node) {
|
|
3161
|
+
if (TableNode4.is(node)) {
|
|
2651
3162
|
return {
|
|
2652
|
-
model:
|
|
3163
|
+
model: node.table.identifier.name
|
|
2653
3164
|
};
|
|
2654
3165
|
}
|
|
2655
|
-
if (
|
|
2656
|
-
const inner = this.extractTableName(
|
|
3166
|
+
if (AliasNode4.is(node)) {
|
|
3167
|
+
const inner = this.extractTableName(node.node);
|
|
2657
3168
|
if (!inner) {
|
|
2658
3169
|
return void 0;
|
|
2659
3170
|
}
|
|
2660
3171
|
return {
|
|
2661
3172
|
model: inner.model,
|
|
2662
|
-
alias: IdentifierNode2.is(
|
|
3173
|
+
alias: IdentifierNode2.is(node.alias) ? node.alias.name : void 0
|
|
2663
3174
|
};
|
|
2664
3175
|
} else {
|
|
2665
3176
|
return void 0;
|
|
2666
3177
|
}
|
|
2667
3178
|
}
|
|
2668
|
-
|
|
2669
|
-
|
|
3179
|
+
createPolicyFilterForFrom(node) {
|
|
3180
|
+
if (!node) {
|
|
3181
|
+
return void 0;
|
|
3182
|
+
}
|
|
3183
|
+
return this.createPolicyFilterForTables(node.froms);
|
|
3184
|
+
}
|
|
3185
|
+
createPolicyFilterForTables(tables) {
|
|
3186
|
+
return tables.reduce((acc, table) => {
|
|
3187
|
+
const extractResult = this.extractTableName(table);
|
|
3188
|
+
if (extractResult) {
|
|
3189
|
+
const { model, alias } = extractResult;
|
|
3190
|
+
const filter = this.buildPolicyFilter(model, alias, "read");
|
|
3191
|
+
return acc ? conjunction(this.dialect, [
|
|
3192
|
+
acc,
|
|
3193
|
+
filter
|
|
3194
|
+
]) : filter;
|
|
3195
|
+
}
|
|
3196
|
+
return acc;
|
|
3197
|
+
}, void 0);
|
|
3198
|
+
}
|
|
3199
|
+
compilePolicyCondition(model, alias, operation, policy) {
|
|
3200
|
+
return new ExpressionTransformer(this.client).transform(policy.condition, {
|
|
2670
3201
|
model,
|
|
2671
3202
|
alias,
|
|
2672
3203
|
operation,
|
|
2673
|
-
thisEntity,
|
|
2674
|
-
thisEntityRaw,
|
|
2675
3204
|
auth: this.client.$auth
|
|
2676
3205
|
});
|
|
2677
3206
|
}
|
|
2678
|
-
getModelPolicies(
|
|
2679
|
-
const modelDef = requireModel(this.client.$schema,
|
|
3207
|
+
getModelPolicies(model, operation) {
|
|
3208
|
+
const modelDef = requireModel(this.client.$schema, model);
|
|
2680
3209
|
const result = [];
|
|
2681
3210
|
const extractOperations = /* @__PURE__ */ __name((expr2) => {
|
|
2682
|
-
|
|
2683
|
-
|
|
3211
|
+
invariant7(ExpressionUtils.isLiteral(expr2), "expecting a literal");
|
|
3212
|
+
invariant7(typeof expr2.value === "string", "expecting a string literal");
|
|
2684
3213
|
return expr2.value.split(",").filter((v) => !!v).map((v) => v.trim());
|
|
2685
3214
|
}, "extractOperations");
|
|
2686
3215
|
if (modelDef.attributes) {
|
|
@@ -2692,8 +3221,84 @@ var PolicyHandler = class extends OperationNodeTransformer {
|
|
|
2692
3221
|
}
|
|
2693
3222
|
return result;
|
|
2694
3223
|
}
|
|
3224
|
+
resolveManyToManyJoinTable(tableName) {
|
|
3225
|
+
for (const model of Object.values(this.client.$schema.models)) {
|
|
3226
|
+
for (const field of Object.values(model.fields)) {
|
|
3227
|
+
const m2m = getManyToManyRelation(this.client.$schema, model.name, field.name);
|
|
3228
|
+
if (m2m?.joinTable === tableName) {
|
|
3229
|
+
const sortedRecord = [
|
|
3230
|
+
{
|
|
3231
|
+
model: model.name,
|
|
3232
|
+
field: field.name
|
|
3233
|
+
},
|
|
3234
|
+
{
|
|
3235
|
+
model: m2m.otherModel,
|
|
3236
|
+
field: m2m.otherField
|
|
3237
|
+
}
|
|
3238
|
+
].sort(this.manyToManySorter);
|
|
3239
|
+
const firstIdFields = requireIdFields(this.client.$schema, sortedRecord[0].model);
|
|
3240
|
+
const secondIdFields = requireIdFields(this.client.$schema, sortedRecord[1].model);
|
|
3241
|
+
invariant7(firstIdFields.length === 1 && secondIdFields.length === 1, "only single-field id is supported for implicit many-to-many join table");
|
|
3242
|
+
return {
|
|
3243
|
+
firstModel: sortedRecord[0].model,
|
|
3244
|
+
firstField: sortedRecord[0].field,
|
|
3245
|
+
firstIdField: firstIdFields[0],
|
|
3246
|
+
secondModel: sortedRecord[1].model,
|
|
3247
|
+
secondField: sortedRecord[1].field,
|
|
3248
|
+
secondIdField: secondIdFields[0]
|
|
3249
|
+
};
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
}
|
|
3253
|
+
return void 0;
|
|
3254
|
+
}
|
|
3255
|
+
manyToManySorter(a, b) {
|
|
3256
|
+
return a.model !== b.model ? a.model.localeCompare(b.model) : a.field.localeCompare(b.field);
|
|
3257
|
+
}
|
|
3258
|
+
isManyToManyJoinTable(tableName) {
|
|
3259
|
+
return !!this.resolveManyToManyJoinTable(tableName);
|
|
3260
|
+
}
|
|
3261
|
+
getModelPolicyFilterForManyToManyJoinTable(tableName, alias, operation) {
|
|
3262
|
+
const m2m = this.resolveManyToManyJoinTable(tableName);
|
|
3263
|
+
if (!m2m) {
|
|
3264
|
+
return void 0;
|
|
3265
|
+
}
|
|
3266
|
+
const checkForOperation = operation === "read" ? "read" : "update";
|
|
3267
|
+
const eb = expressionBuilder3();
|
|
3268
|
+
const joinTable = alias ?? tableName;
|
|
3269
|
+
const aQuery = eb.selectFrom(m2m.firstModel).whereRef(`${m2m.firstModel}.${m2m.firstIdField}`, "=", `${joinTable}.A`).select(() => new ExpressionWrapper(this.buildPolicyFilter(m2m.firstModel, void 0, checkForOperation)).as("$conditionA"));
|
|
3270
|
+
const bQuery = eb.selectFrom(m2m.secondModel).whereRef(`${m2m.secondModel}.${m2m.secondIdField}`, "=", `${joinTable}.B`).select(() => new ExpressionWrapper(this.buildPolicyFilter(m2m.secondModel, void 0, checkForOperation)).as("$conditionB"));
|
|
3271
|
+
return eb.and([
|
|
3272
|
+
aQuery,
|
|
3273
|
+
bQuery
|
|
3274
|
+
]).toOperationNode();
|
|
3275
|
+
}
|
|
2695
3276
|
};
|
|
2696
3277
|
|
|
3278
|
+
// src/plugins/policy/functions.ts
|
|
3279
|
+
var check = /* @__PURE__ */ __name((eb, args, { client, model, modelAlias, operation }) => {
|
|
3280
|
+
invariant8(args.length === 1 || args.length === 2, '"check" function requires 1 or 2 arguments');
|
|
3281
|
+
const arg1Node = args[0].toOperationNode();
|
|
3282
|
+
const arg2Node = args.length === 2 ? args[1].toOperationNode() : void 0;
|
|
3283
|
+
if (arg2Node) {
|
|
3284
|
+
invariant8(ValueNode4.is(arg2Node) && typeof arg2Node.value === "string", '"operation" parameter must be a string literal when provided');
|
|
3285
|
+
invariant8(CRUD.includes(arg2Node.value), '"operation" parameter must be one of "create", "read", "update", "delete"');
|
|
3286
|
+
}
|
|
3287
|
+
const fieldName = extractFieldName(arg1Node);
|
|
3288
|
+
invariant8(fieldName, 'Failed to extract field name from the first argument of "check" function');
|
|
3289
|
+
const fieldDef = requireField(client.$schema, model, fieldName);
|
|
3290
|
+
invariant8(fieldDef.relation, `Field "${fieldName}" is not a relation field in model "${model}"`);
|
|
3291
|
+
invariant8(!fieldDef.array, `Field "${fieldName}" is a to-many relation, which is not supported by "check"`);
|
|
3292
|
+
const relationModel = fieldDef.type;
|
|
3293
|
+
const op = arg2Node ? arg2Node.value : operation;
|
|
3294
|
+
const policyHandler = new PolicyHandler(client);
|
|
3295
|
+
const joinPairs = buildJoinPairs(client.$schema, model, modelAlias, fieldName, relationModel);
|
|
3296
|
+
const joinCondition = joinPairs.length === 1 ? eb(eb.ref(joinPairs[0][0]), "=", eb.ref(joinPairs[0][1])) : eb.and(joinPairs.map(([left, right]) => eb(eb.ref(left), "=", eb.ref(right))));
|
|
3297
|
+
const policyCondition = policyHandler.buildPolicyFilter(relationModel, void 0, op);
|
|
3298
|
+
const result = eb.selectFrom(relationModel).where(joinCondition).select(new ExpressionWrapper2(policyCondition).as("$condition"));
|
|
3299
|
+
return result;
|
|
3300
|
+
}, "check");
|
|
3301
|
+
|
|
2697
3302
|
// src/plugins/policy/plugin.ts
|
|
2698
3303
|
var PolicyPlugin = class {
|
|
2699
3304
|
static {
|
|
@@ -2708,6 +3313,11 @@ var PolicyPlugin = class {
|
|
|
2708
3313
|
get description() {
|
|
2709
3314
|
return "Enforces access policies defined in the schema.";
|
|
2710
3315
|
}
|
|
3316
|
+
get functions() {
|
|
3317
|
+
return {
|
|
3318
|
+
check
|
|
3319
|
+
};
|
|
3320
|
+
}
|
|
2711
3321
|
onKyselyQuery({
|
|
2712
3322
|
query,
|
|
2713
3323
|
client,
|
|
@@ -2743,16 +3353,6 @@ function clone(value) {
|
|
|
2743
3353
|
}
|
|
2744
3354
|
__name(clone, "clone");
|
|
2745
3355
|
|
|
2746
|
-
// src/client/contract.ts
|
|
2747
|
-
var TransactionIsolationLevel = /* @__PURE__ */ function(TransactionIsolationLevel2) {
|
|
2748
|
-
TransactionIsolationLevel2["ReadUncommitted"] = "read uncommitted";
|
|
2749
|
-
TransactionIsolationLevel2["ReadCommitted"] = "read committed";
|
|
2750
|
-
TransactionIsolationLevel2["RepeatableRead"] = "repeatable read";
|
|
2751
|
-
TransactionIsolationLevel2["Serializable"] = "serializable";
|
|
2752
|
-
TransactionIsolationLevel2["Snapshot"] = "snapshot";
|
|
2753
|
-
return TransactionIsolationLevel2;
|
|
2754
|
-
}({});
|
|
2755
|
-
|
|
2756
3356
|
// src/client/crud/operations/base.ts
|
|
2757
3357
|
var BaseOperationHandler = class {
|
|
2758
3358
|
static {
|
|
@@ -2797,7 +3397,7 @@ var BaseOperationHandler = class {
|
|
|
2797
3397
|
return getField(this.schema, model, field);
|
|
2798
3398
|
}
|
|
2799
3399
|
async exists(kysely, model, filter) {
|
|
2800
|
-
const idFields =
|
|
3400
|
+
const idFields = requireIdFields(this.schema, model);
|
|
2801
3401
|
const _filter = flattenCompoundUniqueFilters(this.schema, model, filter);
|
|
2802
3402
|
const query = kysely.selectFrom(model).where((eb) => eb.and(_filter)).select(idFields.map((f) => kysely.dynamic.ref(f))).limit(1).modifyEnd(this.makeContextComment({
|
|
2803
3403
|
model,
|
|
@@ -2806,7 +3406,7 @@ var BaseOperationHandler = class {
|
|
|
2806
3406
|
return this.executeQueryTakeFirst(kysely, query, "exists");
|
|
2807
3407
|
}
|
|
2808
3408
|
async read(kysely, model, args) {
|
|
2809
|
-
let query = this.dialect.buildSelectModel(
|
|
3409
|
+
let query = this.dialect.buildSelectModel(expressionBuilder4(), model, model);
|
|
2810
3410
|
if (args) {
|
|
2811
3411
|
query = this.dialect.buildFilterSortTake(model, args, query, model);
|
|
2812
3412
|
}
|
|
@@ -2878,26 +3478,21 @@ var BaseOperationHandler = class {
|
|
|
2878
3478
|
throw new QueryError(`Model "${this.model}" is a delegate and cannot be created directly.`);
|
|
2879
3479
|
}
|
|
2880
3480
|
let createFields = {};
|
|
2881
|
-
let
|
|
3481
|
+
let updateParent = void 0;
|
|
2882
3482
|
let m2m = void 0;
|
|
2883
3483
|
if (fromRelation) {
|
|
2884
3484
|
m2m = getManyToManyRelation(this.schema, fromRelation.model, fromRelation.field);
|
|
2885
3485
|
if (!m2m) {
|
|
2886
3486
|
const { ownedByModel, keyPairs } = getRelationForeignKeyFieldPairs(this.schema, fromRelation?.model ?? "", fromRelation?.field ?? "");
|
|
2887
3487
|
if (!ownedByModel) {
|
|
2888
|
-
const parentFkFields = this.buildFkAssignments(fromRelation.model, fromRelation.field, fromRelation.ids);
|
|
3488
|
+
const parentFkFields = await this.buildFkAssignments(kysely, fromRelation.model, fromRelation.field, fromRelation.ids);
|
|
2889
3489
|
Object.assign(createFields, parentFkFields);
|
|
2890
3490
|
} else {
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
model: fromRelation.model,
|
|
2897
|
-
operation: "update"
|
|
2898
|
-
}));
|
|
2899
|
-
return this.executeQuery(kysely, query2, "update");
|
|
2900
|
-
}, "parentUpdateTask");
|
|
3491
|
+
updateParent = /* @__PURE__ */ __name((entity) => {
|
|
3492
|
+
for (const { fk, pk } of keyPairs) {
|
|
3493
|
+
fromRelation.parentUpdates[fk] = entity[pk];
|
|
3494
|
+
}
|
|
3495
|
+
}, "updateParent");
|
|
2901
3496
|
}
|
|
2902
3497
|
}
|
|
2903
3498
|
}
|
|
@@ -2929,24 +3524,23 @@ var BaseOperationHandler = class {
|
|
|
2929
3524
|
const baseCreateResult = await this.processBaseModelCreate(kysely, modelDef.baseModel, createFields, model);
|
|
2930
3525
|
createFields = baseCreateResult.remainingFields;
|
|
2931
3526
|
}
|
|
2932
|
-
const updatedData = this.
|
|
2933
|
-
const idFields =
|
|
3527
|
+
const updatedData = this.fillGeneratedAndDefaultValues(modelDef, createFields);
|
|
3528
|
+
const idFields = requireIdFields(this.schema, model);
|
|
2934
3529
|
const query = kysely.insertInto(model).$if(Object.keys(updatedData).length === 0, (qb) => qb.defaultValues()).$if(Object.keys(updatedData).length > 0, (qb) => qb.values(updatedData)).returning(idFields).modifyEnd(this.makeContextComment({
|
|
2935
3530
|
model,
|
|
2936
3531
|
operation: "create"
|
|
2937
3532
|
}));
|
|
2938
3533
|
const createdEntity = await this.executeQueryTakeFirst(kysely, query, "create");
|
|
2939
3534
|
if (Object.keys(postCreateRelations).length > 0) {
|
|
2940
|
-
const
|
|
2941
|
-
|
|
2942
|
-
}
|
|
2943
|
-
await Promise.all(relationPromises);
|
|
3535
|
+
for (const [field, subPayload] of Object.entries(postCreateRelations)) {
|
|
3536
|
+
await this.processNoneOwnedRelationForCreate(kysely, model, field, subPayload, createdEntity);
|
|
3537
|
+
}
|
|
2944
3538
|
}
|
|
2945
3539
|
if (fromRelation && m2m) {
|
|
2946
3540
|
await this.handleManyToManyRelation(kysely, "connect", fromRelation.model, fromRelation.field, fromRelation.ids, m2m.otherModel, m2m.otherField, createdEntity, m2m.joinTable);
|
|
2947
3541
|
}
|
|
2948
|
-
if (
|
|
2949
|
-
|
|
3542
|
+
if (updateParent) {
|
|
3543
|
+
updateParent(createdEntity);
|
|
2950
3544
|
}
|
|
2951
3545
|
return createdEntity;
|
|
2952
3546
|
}
|
|
@@ -2962,7 +3556,7 @@ var BaseOperationHandler = class {
|
|
|
2962
3556
|
}
|
|
2963
3557
|
});
|
|
2964
3558
|
const discriminatorField = getDiscriminatorField(this.schema, model);
|
|
2965
|
-
|
|
3559
|
+
invariant9(discriminatorField, `Base model "${model}" must have a discriminator field`);
|
|
2966
3560
|
thisCreateFields[discriminatorField] = forModel;
|
|
2967
3561
|
const baseEntity = await this.create(kysely, model, thisCreateFields, void 0, true);
|
|
2968
3562
|
const idValues = extractIdFields(baseEntity, this.schema, model);
|
|
@@ -2972,14 +3566,24 @@ var BaseOperationHandler = class {
|
|
|
2972
3566
|
remainingFields
|
|
2973
3567
|
};
|
|
2974
3568
|
}
|
|
2975
|
-
buildFkAssignments(model, relationField, entity) {
|
|
3569
|
+
async buildFkAssignments(kysely, model, relationField, entity) {
|
|
2976
3570
|
const parentFkFields = {};
|
|
2977
|
-
|
|
2978
|
-
|
|
3571
|
+
invariant9(relationField, "parentField must be defined if parentModel is defined");
|
|
3572
|
+
invariant9(entity, "parentEntity must be defined if parentModel is defined");
|
|
2979
3573
|
const { keyPairs } = getRelationForeignKeyFieldPairs(this.schema, model, relationField);
|
|
2980
3574
|
for (const pair of keyPairs) {
|
|
2981
3575
|
if (!(pair.pk in entity)) {
|
|
2982
|
-
|
|
3576
|
+
const extraRead = await this.readUnique(kysely, model, {
|
|
3577
|
+
where: entity,
|
|
3578
|
+
select: {
|
|
3579
|
+
[pair.pk]: true
|
|
3580
|
+
}
|
|
3581
|
+
});
|
|
3582
|
+
if (!extraRead) {
|
|
3583
|
+
throw new QueryError(`Field "${pair.pk}" not found in parent created data`);
|
|
3584
|
+
} else {
|
|
3585
|
+
Object.assign(entity, extraRead);
|
|
3586
|
+
}
|
|
2983
3587
|
}
|
|
2984
3588
|
Object.assign(parentFkFields, {
|
|
2985
3589
|
[pair.fk]: entity[pair.pk]
|
|
@@ -3000,15 +3604,15 @@ var BaseOperationHandler = class {
|
|
|
3000
3604
|
entity: rightEntity
|
|
3001
3605
|
}
|
|
3002
3606
|
].sort((a, b) => (
|
|
3003
|
-
// the
|
|
3607
|
+
// the implicit m2m join table's "A", "B" fk fields' order is determined
|
|
3004
3608
|
// by model name's sort order, and when identical (for self-relations),
|
|
3005
3609
|
// field name's sort order
|
|
3006
3610
|
a.model !== b.model ? a.model.localeCompare(b.model) : a.field.localeCompare(b.field)
|
|
3007
3611
|
));
|
|
3008
|
-
const firstIds =
|
|
3009
|
-
const secondIds =
|
|
3010
|
-
|
|
3011
|
-
|
|
3612
|
+
const firstIds = requireIdFields(this.schema, sortedRecords[0].model);
|
|
3613
|
+
const secondIds = requireIdFields(this.schema, sortedRecords[1].model);
|
|
3614
|
+
invariant9(firstIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
3615
|
+
invariant9(secondIds.length === 1, "many-to-many relation must have exactly one id field");
|
|
3012
3616
|
if (action === "connect") {
|
|
3013
3617
|
const result = await kysely.insertInto(joinTable).values({
|
|
3014
3618
|
A: sortedRecords[0].entity[firstIds[0]],
|
|
@@ -3019,17 +3623,17 @@ var BaseOperationHandler = class {
|
|
|
3019
3623
|
]).doNothing()).execute();
|
|
3020
3624
|
return result[0];
|
|
3021
3625
|
} else {
|
|
3022
|
-
const eb =
|
|
3626
|
+
const eb = expressionBuilder4();
|
|
3023
3627
|
const result = await kysely.deleteFrom(joinTable).where(eb(`${joinTable}.A`, "=", sortedRecords[0].entity[firstIds[0]])).where(eb(`${joinTable}.B`, "=", sortedRecords[1].entity[secondIds[0]])).execute();
|
|
3024
3628
|
return result[0];
|
|
3025
3629
|
}
|
|
3026
3630
|
}
|
|
3027
3631
|
resetManyToManyRelation(kysely, model, field, parentIds) {
|
|
3028
|
-
|
|
3632
|
+
invariant9(Object.keys(parentIds).length === 1, "parentIds must have exactly one field");
|
|
3029
3633
|
const parentId = Object.values(parentIds)[0];
|
|
3030
3634
|
const m2m = getManyToManyRelation(this.schema, model, field);
|
|
3031
|
-
|
|
3032
|
-
const eb =
|
|
3635
|
+
invariant9(m2m, "not a many-to-many relation");
|
|
3636
|
+
const eb = expressionBuilder4();
|
|
3033
3637
|
return kysely.deleteFrom(m2m.joinTable).where(eb(`${m2m.joinTable}.${m2m.parentFkName}`, "=", parentId)).execute();
|
|
3034
3638
|
}
|
|
3035
3639
|
async processOwnedRelationForCreate(kysely, relationField, payload) {
|
|
@@ -3050,7 +3654,7 @@ var BaseOperationHandler = class {
|
|
|
3050
3654
|
}
|
|
3051
3655
|
case "connect": {
|
|
3052
3656
|
const referencedPkFields = relationField.relation.references;
|
|
3053
|
-
|
|
3657
|
+
invariant9(referencedPkFields, "relation must have fields info");
|
|
3054
3658
|
const extractedFks = extractFields(subPayload, referencedPkFields);
|
|
3055
3659
|
if (Object.keys(extractedFks).length === referencedPkFields.length) {
|
|
3056
3660
|
result = extractedFks;
|
|
@@ -3060,7 +3664,7 @@ var BaseOperationHandler = class {
|
|
|
3060
3664
|
select: fieldsToSelectObject(referencedPkFields)
|
|
3061
3665
|
});
|
|
3062
3666
|
if (!relationEntity) {
|
|
3063
|
-
throw new NotFoundError(`Could not find the entity
|
|
3667
|
+
throw new NotFoundError(relationModel, `Could not find the entity to connect for the relation "${relationField.name}"`);
|
|
3064
3668
|
}
|
|
3065
3669
|
result = relationEntity;
|
|
3066
3670
|
}
|
|
@@ -3082,14 +3686,14 @@ var BaseOperationHandler = class {
|
|
|
3082
3686
|
}
|
|
3083
3687
|
return result;
|
|
3084
3688
|
}
|
|
3085
|
-
processNoneOwnedRelationForCreate(kysely, contextModel, relationFieldName, payload, parentEntity) {
|
|
3689
|
+
async processNoneOwnedRelationForCreate(kysely, contextModel, relationFieldName, payload, parentEntity) {
|
|
3086
3690
|
const relationFieldDef = this.requireField(contextModel, relationFieldName);
|
|
3087
3691
|
const relationModel = relationFieldDef.type;
|
|
3088
|
-
const tasks = [];
|
|
3089
3692
|
const fromRelationContext = {
|
|
3090
3693
|
model: contextModel,
|
|
3091
3694
|
field: relationFieldName,
|
|
3092
|
-
ids: parentEntity
|
|
3695
|
+
ids: parentEntity,
|
|
3696
|
+
parentUpdates: {}
|
|
3093
3697
|
};
|
|
3094
3698
|
for (const [action, subPayload] of Object.entries(payload)) {
|
|
3095
3699
|
if (!subPayload) {
|
|
@@ -3097,39 +3701,35 @@ var BaseOperationHandler = class {
|
|
|
3097
3701
|
}
|
|
3098
3702
|
switch (action) {
|
|
3099
3703
|
case "create": {
|
|
3100
|
-
|
|
3704
|
+
for (const item of enumerate(subPayload)) {
|
|
3705
|
+
await this.create(kysely, relationModel, item, fromRelationContext);
|
|
3706
|
+
}
|
|
3101
3707
|
break;
|
|
3102
3708
|
}
|
|
3103
3709
|
case "createMany": {
|
|
3104
|
-
|
|
3105
|
-
|
|
3710
|
+
invariant9(relationFieldDef.array, "relation must be an array for createMany");
|
|
3711
|
+
await this.createMany(kysely, relationModel, subPayload, false, fromRelationContext);
|
|
3106
3712
|
break;
|
|
3107
3713
|
}
|
|
3108
3714
|
case "connect": {
|
|
3109
|
-
|
|
3110
|
-
model: contextModel,
|
|
3111
|
-
field: relationFieldName,
|
|
3112
|
-
ids: parentEntity
|
|
3113
|
-
}));
|
|
3715
|
+
await this.connectRelation(kysely, relationModel, subPayload, fromRelationContext);
|
|
3114
3716
|
break;
|
|
3115
3717
|
}
|
|
3116
3718
|
case "connectOrCreate": {
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
}))));
|
|
3719
|
+
for (const item of enumerate(subPayload)) {
|
|
3720
|
+
const found = await this.exists(kysely, relationModel, item.where);
|
|
3721
|
+
if (!found) {
|
|
3722
|
+
await this.create(kysely, relationModel, item.create, fromRelationContext);
|
|
3723
|
+
} else {
|
|
3724
|
+
await this.connectRelation(kysely, relationModel, found, fromRelationContext);
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3126
3727
|
break;
|
|
3127
3728
|
}
|
|
3128
3729
|
default:
|
|
3129
3730
|
throw new QueryError(`Invalid relation action: ${action}`);
|
|
3130
3731
|
}
|
|
3131
3732
|
}
|
|
3132
|
-
return Promise.all(tasks);
|
|
3133
3733
|
}
|
|
3134
3734
|
async createMany(kysely, model, input, returnData, fromRelation) {
|
|
3135
3735
|
if (!input.data || Array.isArray(input.data) && input.data.length === 0) {
|
|
@@ -3150,7 +3750,7 @@ var BaseOperationHandler = class {
|
|
|
3150
3750
|
const newItem = {};
|
|
3151
3751
|
for (const [name, value] of Object.entries(item)) {
|
|
3152
3752
|
const fieldDef = this.requireField(model, name);
|
|
3153
|
-
|
|
3753
|
+
invariant9(!fieldDef.relation, "createMany does not support relations");
|
|
3154
3754
|
newItem[name] = this.dialect.transformPrimitive(value, fieldDef.type, !!fieldDef.array);
|
|
3155
3755
|
}
|
|
3156
3756
|
if (fromRelation) {
|
|
@@ -3158,7 +3758,7 @@ var BaseOperationHandler = class {
|
|
|
3158
3758
|
newItem[fk] = fromRelation.ids[pk];
|
|
3159
3759
|
}
|
|
3160
3760
|
}
|
|
3161
|
-
return this.
|
|
3761
|
+
return this.fillGeneratedAndDefaultValues(modelDef, newItem);
|
|
3162
3762
|
});
|
|
3163
3763
|
if (!this.dialect.supportInsertWithDefault) {
|
|
3164
3764
|
const allPassedFields = createData.reduce((acc, item) => {
|
|
@@ -3200,7 +3800,7 @@ var BaseOperationHandler = class {
|
|
|
3200
3800
|
count: Number(result.numAffectedRows)
|
|
3201
3801
|
};
|
|
3202
3802
|
} else {
|
|
3203
|
-
const idFields =
|
|
3803
|
+
const idFields = requireIdFields(this.schema, model);
|
|
3204
3804
|
const result = await query.returning(idFields).execute();
|
|
3205
3805
|
return result;
|
|
3206
3806
|
}
|
|
@@ -3209,7 +3809,7 @@ var BaseOperationHandler = class {
|
|
|
3209
3809
|
const thisCreateRows = [];
|
|
3210
3810
|
const remainingFieldRows = [];
|
|
3211
3811
|
const discriminatorField = getDiscriminatorField(this.schema, model);
|
|
3212
|
-
|
|
3812
|
+
invariant9(discriminatorField, `Base model "${model}" must have a discriminator field`);
|
|
3213
3813
|
for (const createFields of createRows) {
|
|
3214
3814
|
const thisCreateFields = {};
|
|
3215
3815
|
const remainingFields = {};
|
|
@@ -3238,7 +3838,7 @@ var BaseOperationHandler = class {
|
|
|
3238
3838
|
remainingFieldRows
|
|
3239
3839
|
};
|
|
3240
3840
|
}
|
|
3241
|
-
|
|
3841
|
+
fillGeneratedAndDefaultValues(modelDef, data) {
|
|
3242
3842
|
const fields = modelDef.fields;
|
|
3243
3843
|
const values = clone(data);
|
|
3244
3844
|
for (const [field, fieldDef] of Object.entries(fields)) {
|
|
@@ -3253,6 +3853,16 @@ var BaseOperationHandler = class {
|
|
|
3253
3853
|
}
|
|
3254
3854
|
} else if (fields[field]?.updatedAt) {
|
|
3255
3855
|
values[field] = this.dialect.transformPrimitive(/* @__PURE__ */ new Date(), "DateTime", false);
|
|
3856
|
+
} else if (fields[field]?.default !== void 0) {
|
|
3857
|
+
let value = fields[field].default;
|
|
3858
|
+
if (fieldDef.type === "Json") {
|
|
3859
|
+
if (fieldDef.array && Array.isArray(value)) {
|
|
3860
|
+
value = value.map((v) => typeof v === "string" ? JSON.parse(v) : v);
|
|
3861
|
+
} else if (typeof value === "string") {
|
|
3862
|
+
value = JSON.parse(value);
|
|
3863
|
+
}
|
|
3864
|
+
}
|
|
3865
|
+
values[field] = this.dialect.transformPrimitive(value, fields[field].type, !!fields[field].array);
|
|
3256
3866
|
}
|
|
3257
3867
|
}
|
|
3258
3868
|
}
|
|
@@ -3295,7 +3905,7 @@ var BaseOperationHandler = class {
|
|
|
3295
3905
|
}
|
|
3296
3906
|
} else {
|
|
3297
3907
|
const fromRelationFieldDef = this.requireField(fromRelation.model, fromRelation.field);
|
|
3298
|
-
|
|
3908
|
+
invariant9(fromRelationFieldDef.relation?.opposite);
|
|
3299
3909
|
parentWhere[fromRelationFieldDef.relation.opposite] = {
|
|
3300
3910
|
some: fromRelation.ids
|
|
3301
3911
|
};
|
|
@@ -3353,10 +3963,7 @@ var BaseOperationHandler = class {
|
|
|
3353
3963
|
throw new QueryError(`Relation update not allowed for field "${field}"`);
|
|
3354
3964
|
}
|
|
3355
3965
|
if (!thisEntity) {
|
|
3356
|
-
thisEntity = await this.
|
|
3357
|
-
where: combinedWhere,
|
|
3358
|
-
select: this.makeIdSelect(model)
|
|
3359
|
-
});
|
|
3966
|
+
thisEntity = await this.getEntityIds(kysely, model, combinedWhere);
|
|
3360
3967
|
if (!thisEntity) {
|
|
3361
3968
|
if (throwIfNotFound) {
|
|
3362
3969
|
throw new NotFoundError(model);
|
|
@@ -3365,13 +3972,16 @@ var BaseOperationHandler = class {
|
|
|
3365
3972
|
}
|
|
3366
3973
|
}
|
|
3367
3974
|
}
|
|
3368
|
-
await this.processRelationUpdates(kysely, model, field, fieldDef, thisEntity, finalData[field], throwIfNotFound);
|
|
3975
|
+
const parentUpdates = await this.processRelationUpdates(kysely, model, field, fieldDef, thisEntity, finalData[field], throwIfNotFound);
|
|
3976
|
+
if (Object.keys(parentUpdates).length > 0) {
|
|
3977
|
+
Object.assign(updateFields, parentUpdates);
|
|
3978
|
+
}
|
|
3369
3979
|
}
|
|
3370
3980
|
}
|
|
3371
3981
|
if (Object.keys(updateFields).length === 0) {
|
|
3372
3982
|
return combinedWhere;
|
|
3373
3983
|
} else {
|
|
3374
|
-
const idFields =
|
|
3984
|
+
const idFields = requireIdFields(this.schema, model);
|
|
3375
3985
|
const query = kysely.updateTable(model).where((eb) => this.dialect.buildFilter(eb, model, model, combinedWhere)).set(updateFields).returning(idFields).modifyEnd(this.makeContextComment({
|
|
3376
3986
|
model,
|
|
3377
3987
|
operation: "update"
|
|
@@ -3416,7 +4026,7 @@ var BaseOperationHandler = class {
|
|
|
3416
4026
|
if (!filter || typeof filter !== "object") {
|
|
3417
4027
|
return false;
|
|
3418
4028
|
}
|
|
3419
|
-
const idFields =
|
|
4029
|
+
const idFields = requireIdFields(this.schema, model);
|
|
3420
4030
|
return idFields.length === Object.keys(filter).length && idFields.every((field) => field in filter);
|
|
3421
4031
|
}
|
|
3422
4032
|
async processBaseModelUpdate(kysely, model, where, updateFields, throwIfNotFound) {
|
|
@@ -3437,20 +4047,20 @@ var BaseOperationHandler = class {
|
|
|
3437
4047
|
};
|
|
3438
4048
|
}
|
|
3439
4049
|
transformIncrementalUpdate(model, field, fieldDef, payload) {
|
|
3440
|
-
|
|
4050
|
+
invariant9(Object.keys(payload).length === 1, 'Only one of "set", "increment", "decrement", "multiply", or "divide" can be provided');
|
|
3441
4051
|
const key = Object.keys(payload)[0];
|
|
3442
4052
|
const value = this.dialect.transformPrimitive(payload[key], fieldDef.type, false);
|
|
3443
|
-
const eb =
|
|
4053
|
+
const eb = expressionBuilder4();
|
|
3444
4054
|
const fieldRef = this.dialect.fieldRef(model, field, eb);
|
|
3445
4055
|
return match9(key).with("set", () => value).with("increment", () => eb(fieldRef, "+", value)).with("decrement", () => eb(fieldRef, "-", value)).with("multiply", () => eb(fieldRef, "*", value)).with("divide", () => eb(fieldRef, "/", value)).otherwise(() => {
|
|
3446
4056
|
throw new InternalError(`Invalid incremental update operation: ${key}`);
|
|
3447
4057
|
});
|
|
3448
4058
|
}
|
|
3449
4059
|
transformScalarListUpdate(model, field, fieldDef, payload) {
|
|
3450
|
-
|
|
4060
|
+
invariant9(Object.keys(payload).length === 1, 'Only one of "set", "push" can be provided');
|
|
3451
4061
|
const key = Object.keys(payload)[0];
|
|
3452
4062
|
const value = this.dialect.transformPrimitive(payload[key], fieldDef.type, true);
|
|
3453
|
-
const eb =
|
|
4063
|
+
const eb = expressionBuilder4();
|
|
3454
4064
|
const fieldRef = this.dialect.fieldRef(model, field, eb);
|
|
3455
4065
|
return match9(key).with("set", () => value).with("push", () => {
|
|
3456
4066
|
return eb(fieldRef, "||", eb.val(ensureArray(value)));
|
|
@@ -3462,7 +4072,7 @@ var BaseOperationHandler = class {
|
|
|
3462
4072
|
return NUMERIC_FIELD_TYPES.includes(fieldDef.type) && !fieldDef.array;
|
|
3463
4073
|
}
|
|
3464
4074
|
makeContextComment(_context) {
|
|
3465
|
-
return
|
|
4075
|
+
return sql5``;
|
|
3466
4076
|
}
|
|
3467
4077
|
async updateMany(kysely, model, where, data, limit, returnData, filterModel) {
|
|
3468
4078
|
if (typeof data !== "object") {
|
|
@@ -3521,7 +4131,7 @@ var BaseOperationHandler = class {
|
|
|
3521
4131
|
count: Number(result.numAffectedRows)
|
|
3522
4132
|
};
|
|
3523
4133
|
} else {
|
|
3524
|
-
const idFields =
|
|
4134
|
+
const idFields = requireIdFields(this.schema, model);
|
|
3525
4135
|
const result = await query.returning(idFields).execute();
|
|
3526
4136
|
return result;
|
|
3527
4137
|
}
|
|
@@ -3544,82 +4154,87 @@ var BaseOperationHandler = class {
|
|
|
3544
4154
|
};
|
|
3545
4155
|
}
|
|
3546
4156
|
buildIdFieldRefs(kysely, model) {
|
|
3547
|
-
const idFields =
|
|
4157
|
+
const idFields = requireIdFields(this.schema, model);
|
|
3548
4158
|
return idFields.map((f) => kysely.dynamic.ref(`${model}.${f}`));
|
|
3549
4159
|
}
|
|
3550
4160
|
async processRelationUpdates(kysely, model, field, fieldDef, parentIds, args, throwIfNotFound) {
|
|
3551
|
-
const tasks = [];
|
|
3552
4161
|
const fieldModel = fieldDef.type;
|
|
3553
4162
|
const fromRelationContext = {
|
|
3554
4163
|
model,
|
|
3555
4164
|
field,
|
|
3556
|
-
ids: parentIds
|
|
4165
|
+
ids: parentIds,
|
|
4166
|
+
parentUpdates: {}
|
|
3557
4167
|
};
|
|
3558
4168
|
for (const [key, value] of Object.entries(args)) {
|
|
3559
4169
|
switch (key) {
|
|
3560
4170
|
case "create": {
|
|
3561
|
-
|
|
3562
|
-
|
|
4171
|
+
invariant9(!Array.isArray(value) || fieldDef.array, "relation must be an array if create is an array");
|
|
4172
|
+
for (const item of enumerate(value)) {
|
|
4173
|
+
await this.create(kysely, fieldModel, item, fromRelationContext);
|
|
4174
|
+
}
|
|
3563
4175
|
break;
|
|
3564
4176
|
}
|
|
3565
4177
|
case "createMany": {
|
|
3566
|
-
|
|
3567
|
-
|
|
4178
|
+
invariant9(fieldDef.array, "relation must be an array for createMany");
|
|
4179
|
+
await this.createMany(kysely, fieldModel, value, false, fromRelationContext);
|
|
3568
4180
|
break;
|
|
3569
4181
|
}
|
|
3570
4182
|
case "connect": {
|
|
3571
|
-
|
|
4183
|
+
await this.connectRelation(kysely, fieldModel, value, fromRelationContext);
|
|
3572
4184
|
break;
|
|
3573
4185
|
}
|
|
3574
4186
|
case "connectOrCreate": {
|
|
3575
|
-
|
|
4187
|
+
await this.connectOrCreateRelation(kysely, fieldModel, value, fromRelationContext);
|
|
3576
4188
|
break;
|
|
3577
4189
|
}
|
|
3578
4190
|
case "disconnect": {
|
|
3579
|
-
|
|
4191
|
+
await this.disconnectRelation(kysely, fieldModel, value, fromRelationContext);
|
|
3580
4192
|
break;
|
|
3581
4193
|
}
|
|
3582
4194
|
case "set": {
|
|
3583
|
-
|
|
3584
|
-
|
|
4195
|
+
invariant9(fieldDef.array, "relation must be an array");
|
|
4196
|
+
await this.setRelation(kysely, fieldModel, value, fromRelationContext);
|
|
3585
4197
|
break;
|
|
3586
4198
|
}
|
|
3587
4199
|
case "update": {
|
|
3588
|
-
|
|
4200
|
+
for (const _item of enumerate(value)) {
|
|
4201
|
+
const item = _item;
|
|
3589
4202
|
let where;
|
|
3590
4203
|
let data;
|
|
3591
|
-
if ("
|
|
4204
|
+
if ("data" in item && typeof item.data === "object") {
|
|
3592
4205
|
where = item.where;
|
|
3593
4206
|
data = item.data;
|
|
3594
4207
|
} else {
|
|
3595
4208
|
where = void 0;
|
|
3596
4209
|
data = item;
|
|
3597
4210
|
}
|
|
3598
|
-
|
|
3599
|
-
}
|
|
4211
|
+
await this.update(kysely, fieldModel, where, data, fromRelationContext, true, throwIfNotFound);
|
|
4212
|
+
}
|
|
3600
4213
|
break;
|
|
3601
4214
|
}
|
|
3602
4215
|
case "upsert": {
|
|
3603
|
-
|
|
4216
|
+
for (const _item of enumerate(value)) {
|
|
4217
|
+
const item = _item;
|
|
3604
4218
|
const updated = await this.update(kysely, fieldModel, item.where, item.update, fromRelationContext, true, false);
|
|
3605
|
-
if (updated) {
|
|
3606
|
-
|
|
3607
|
-
} else {
|
|
3608
|
-
return this.create(kysely, fieldModel, item.create, fromRelationContext);
|
|
4219
|
+
if (!updated) {
|
|
4220
|
+
await this.create(kysely, fieldModel, item.create, fromRelationContext);
|
|
3609
4221
|
}
|
|
3610
|
-
}
|
|
4222
|
+
}
|
|
3611
4223
|
break;
|
|
3612
4224
|
}
|
|
3613
4225
|
case "updateMany": {
|
|
3614
|
-
|
|
4226
|
+
for (const _item of enumerate(value)) {
|
|
4227
|
+
const item = _item;
|
|
4228
|
+
await this.update(kysely, fieldModel, item.where, item.data, fromRelationContext, false, false);
|
|
4229
|
+
}
|
|
3615
4230
|
break;
|
|
3616
4231
|
}
|
|
3617
4232
|
case "delete": {
|
|
3618
|
-
|
|
4233
|
+
await this.deleteRelation(kysely, fieldModel, value, fromRelationContext, true);
|
|
3619
4234
|
break;
|
|
3620
4235
|
}
|
|
3621
4236
|
case "deleteMany": {
|
|
3622
|
-
|
|
4237
|
+
await this.deleteRelation(kysely, fieldModel, value, fromRelationContext, false);
|
|
3623
4238
|
break;
|
|
3624
4239
|
}
|
|
3625
4240
|
default: {
|
|
@@ -3627,7 +4242,7 @@ var BaseOperationHandler = class {
|
|
|
3627
4242
|
}
|
|
3628
4243
|
}
|
|
3629
4244
|
}
|
|
3630
|
-
|
|
4245
|
+
return fromRelationContext.parentUpdates;
|
|
3631
4246
|
}
|
|
3632
4247
|
// #region relation manipulation
|
|
3633
4248
|
async connectRelation(kysely, model, data, fromRelation) {
|
|
@@ -3637,37 +4252,35 @@ var BaseOperationHandler = class {
|
|
|
3637
4252
|
}
|
|
3638
4253
|
const m2m = getManyToManyRelation(this.schema, fromRelation.model, fromRelation.field);
|
|
3639
4254
|
if (m2m) {
|
|
3640
|
-
const
|
|
4255
|
+
const results = [];
|
|
4256
|
+
for (const d of _data) {
|
|
3641
4257
|
const ids = await this.getEntityIds(kysely, model, d);
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
4258
|
+
if (!ids) {
|
|
4259
|
+
throw new NotFoundError(model);
|
|
4260
|
+
}
|
|
4261
|
+
const r = await this.handleManyToManyRelation(kysely, "connect", fromRelation.model, fromRelation.field, fromRelation.ids, m2m.otherModel, m2m.otherField, ids, m2m.joinTable);
|
|
4262
|
+
results.push(r);
|
|
4263
|
+
}
|
|
3645
4264
|
if (_data.length > results.filter((r) => !!r).length) {
|
|
3646
4265
|
throw new NotFoundError(model);
|
|
3647
4266
|
}
|
|
3648
4267
|
} else {
|
|
3649
4268
|
const { ownedByModel, keyPairs } = getRelationForeignKeyFieldPairs(this.schema, fromRelation.model, fromRelation.field);
|
|
3650
|
-
let updateResult;
|
|
3651
4269
|
if (ownedByModel) {
|
|
3652
|
-
|
|
4270
|
+
invariant9(_data.length === 1, "only one entity can be connected");
|
|
3653
4271
|
const target = await this.readUnique(kysely, model, {
|
|
3654
4272
|
where: _data[0]
|
|
3655
4273
|
});
|
|
3656
4274
|
if (!target) {
|
|
3657
4275
|
throw new NotFoundError(model);
|
|
3658
4276
|
}
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
}), {})).modifyEnd(this.makeContextComment({
|
|
3663
|
-
model: fromRelation.model,
|
|
3664
|
-
operation: "update"
|
|
3665
|
-
}));
|
|
3666
|
-
updateResult = await this.executeQuery(kysely, query, "connect");
|
|
4277
|
+
for (const { fk, pk } of keyPairs) {
|
|
4278
|
+
fromRelation.parentUpdates[fk] = target[pk];
|
|
4279
|
+
}
|
|
3667
4280
|
} else {
|
|
3668
4281
|
const relationFieldDef = this.requireField(fromRelation.model, fromRelation.field);
|
|
3669
4282
|
if (!relationFieldDef.array) {
|
|
3670
|
-
const query2 = kysely.updateTable(model).where((eb) => eb.and(keyPairs.map(({ fk, pk }) => eb(
|
|
4283
|
+
const query2 = kysely.updateTable(model).where((eb) => eb.and(keyPairs.map(({ fk, pk }) => eb(sql5.ref(fk), "=", fromRelation.ids[pk])))).set(keyPairs.reduce((acc, { fk }) => ({
|
|
3671
4284
|
...acc,
|
|
3672
4285
|
[fk]: null
|
|
3673
4286
|
}), {})).modifyEnd(this.makeContextComment({
|
|
@@ -3683,10 +4296,10 @@ var BaseOperationHandler = class {
|
|
|
3683
4296
|
model,
|
|
3684
4297
|
operation: "update"
|
|
3685
4298
|
}));
|
|
3686
|
-
updateResult = await this.executeQuery(kysely, query, "connect");
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
4299
|
+
const updateResult = await this.executeQuery(kysely, query, "connect");
|
|
4300
|
+
if (!updateResult.numAffectedRows || _data.length > updateResult.numAffectedRows) {
|
|
4301
|
+
throw new NotFoundError(model);
|
|
4302
|
+
}
|
|
3690
4303
|
}
|
|
3691
4304
|
}
|
|
3692
4305
|
}
|
|
@@ -3695,16 +4308,16 @@ var BaseOperationHandler = class {
|
|
|
3695
4308
|
if (_data.length === 0) {
|
|
3696
4309
|
return;
|
|
3697
4310
|
}
|
|
3698
|
-
|
|
4311
|
+
for (const { where, create } of _data) {
|
|
3699
4312
|
const existing = await this.exists(kysely, model, where);
|
|
3700
4313
|
if (existing) {
|
|
3701
|
-
|
|
4314
|
+
await this.connectRelation(kysely, model, [
|
|
3702
4315
|
where
|
|
3703
4316
|
], fromRelation);
|
|
3704
4317
|
} else {
|
|
3705
|
-
|
|
4318
|
+
await this.create(kysely, model, create, fromRelation);
|
|
3706
4319
|
}
|
|
3707
|
-
}
|
|
4320
|
+
}
|
|
3708
4321
|
}
|
|
3709
4322
|
async disconnectRelation(kysely, model, data, fromRelation) {
|
|
3710
4323
|
let disconnectConditions = [];
|
|
@@ -3727,33 +4340,51 @@ var BaseOperationHandler = class {
|
|
|
3727
4340
|
}
|
|
3728
4341
|
const m2m = getManyToManyRelation(this.schema, fromRelation.model, fromRelation.field);
|
|
3729
4342
|
if (m2m) {
|
|
3730
|
-
const
|
|
4343
|
+
for (const d of disconnectConditions) {
|
|
3731
4344
|
const ids = await this.getEntityIds(kysely, model, d);
|
|
3732
4345
|
if (!ids) {
|
|
3733
4346
|
return;
|
|
3734
4347
|
}
|
|
3735
|
-
|
|
3736
|
-
}
|
|
3737
|
-
await Promise.all(actions);
|
|
4348
|
+
await this.handleManyToManyRelation(kysely, "disconnect", fromRelation.model, fromRelation.field, fromRelation.ids, m2m.otherModel, m2m.otherField, ids, m2m.joinTable);
|
|
4349
|
+
}
|
|
3738
4350
|
} else {
|
|
3739
4351
|
const { ownedByModel, keyPairs } = getRelationForeignKeyFieldPairs(this.schema, fromRelation.model, fromRelation.field);
|
|
3740
|
-
const eb =
|
|
4352
|
+
const eb = expressionBuilder4();
|
|
3741
4353
|
if (ownedByModel) {
|
|
3742
|
-
|
|
4354
|
+
invariant9(disconnectConditions.length === 1, "only one entity can be disconnected");
|
|
3743
4355
|
const condition = disconnectConditions[0];
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
4356
|
+
if (condition === true) {
|
|
4357
|
+
for (const { fk } of keyPairs) {
|
|
4358
|
+
fromRelation.parentUpdates[fk] = null;
|
|
4359
|
+
}
|
|
4360
|
+
} else {
|
|
4361
|
+
const fromEntity = await this.readUnique(kysely, fromRelation.model, {
|
|
4362
|
+
where: fromRelation.ids,
|
|
4363
|
+
select: fieldsToSelectObject(keyPairs.map(({ fk }) => fk))
|
|
4364
|
+
});
|
|
4365
|
+
if (!fromEntity || keyPairs.some(({ fk }) => fromEntity[fk] == null)) {
|
|
4366
|
+
return;
|
|
4367
|
+
}
|
|
4368
|
+
const relationFilter = {
|
|
4369
|
+
AND: [
|
|
4370
|
+
condition,
|
|
4371
|
+
Object.fromEntries(keyPairs.map(({ fk, pk }) => [
|
|
4372
|
+
pk,
|
|
4373
|
+
fromEntity[fk]
|
|
4374
|
+
]))
|
|
4375
|
+
]
|
|
4376
|
+
};
|
|
4377
|
+
const targetExists = await this.read(kysely, model, {
|
|
4378
|
+
where: relationFilter,
|
|
4379
|
+
take: 1,
|
|
4380
|
+
select: this.makeIdSelect(model)
|
|
4381
|
+
});
|
|
4382
|
+
if (targetExists.length > 0) {
|
|
4383
|
+
for (const { fk } of keyPairs) {
|
|
4384
|
+
fromRelation.parentUpdates[fk] = null;
|
|
4385
|
+
}
|
|
4386
|
+
}
|
|
4387
|
+
}
|
|
3757
4388
|
} else {
|
|
3758
4389
|
const query = kysely.updateTable(model).where(eb.and([
|
|
3759
4390
|
// fk filter
|
|
@@ -3779,11 +4410,14 @@ var BaseOperationHandler = class {
|
|
|
3779
4410
|
const m2m = getManyToManyRelation(this.schema, fromRelation.model, fromRelation.field);
|
|
3780
4411
|
if (m2m) {
|
|
3781
4412
|
await this.resetManyToManyRelation(kysely, fromRelation.model, fromRelation.field, fromRelation.ids);
|
|
3782
|
-
const
|
|
4413
|
+
const results = [];
|
|
4414
|
+
for (const d of _data) {
|
|
3783
4415
|
const ids = await this.getEntityIds(kysely, model, d);
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
4416
|
+
if (!ids) {
|
|
4417
|
+
throw new NotFoundError(model);
|
|
4418
|
+
}
|
|
4419
|
+
results.push(await this.handleManyToManyRelation(kysely, "connect", fromRelation.model, fromRelation.field, fromRelation.ids, m2m.otherModel, m2m.otherField, ids, m2m.joinTable));
|
|
4420
|
+
}
|
|
3787
4421
|
if (_data.length > results.filter((r) => !!r).length) {
|
|
3788
4422
|
throw new NotFoundError(model);
|
|
3789
4423
|
}
|
|
@@ -3818,7 +4452,7 @@ var BaseOperationHandler = class {
|
|
|
3818
4452
|
operation: "update"
|
|
3819
4453
|
}));
|
|
3820
4454
|
const r = await this.executeQuery(kysely, query2, "connect");
|
|
3821
|
-
if (_data.length > r.numAffectedRows) {
|
|
4455
|
+
if (!r.numAffectedRows || _data.length > r.numAffectedRows) {
|
|
3822
4456
|
throw new NotFoundError(model);
|
|
3823
4457
|
}
|
|
3824
4458
|
}
|
|
@@ -3844,10 +4478,12 @@ var BaseOperationHandler = class {
|
|
|
3844
4478
|
expectedDeleteCount = deleteConditions.length;
|
|
3845
4479
|
}
|
|
3846
4480
|
let deleteResult;
|
|
4481
|
+
let deleteFromModel;
|
|
3847
4482
|
const m2m = getManyToManyRelation(this.schema, fromRelation.model, fromRelation.field);
|
|
3848
4483
|
if (m2m) {
|
|
4484
|
+
deleteFromModel = model;
|
|
3849
4485
|
const fieldDef = this.requireField(fromRelation.model, fromRelation.field);
|
|
3850
|
-
|
|
4486
|
+
invariant9(fieldDef.relation?.opposite);
|
|
3851
4487
|
deleteResult = await this.delete(kysely, model, {
|
|
3852
4488
|
AND: [
|
|
3853
4489
|
{
|
|
@@ -3863,14 +4499,15 @@ var BaseOperationHandler = class {
|
|
|
3863
4499
|
} else {
|
|
3864
4500
|
const { ownedByModel, keyPairs } = getRelationForeignKeyFieldPairs(this.schema, fromRelation.model, fromRelation.field);
|
|
3865
4501
|
if (ownedByModel) {
|
|
4502
|
+
deleteFromModel = fromRelation.model;
|
|
3866
4503
|
const fromEntity = await this.readUnique(kysely, fromRelation.model, {
|
|
3867
4504
|
where: fromRelation.ids
|
|
3868
4505
|
});
|
|
3869
4506
|
if (!fromEntity) {
|
|
3870
|
-
throw new NotFoundError(model);
|
|
4507
|
+
throw new NotFoundError(fromRelation.model);
|
|
3871
4508
|
}
|
|
3872
4509
|
const fieldDef = this.requireField(fromRelation.model, fromRelation.field);
|
|
3873
|
-
|
|
4510
|
+
invariant9(fieldDef.relation?.opposite);
|
|
3874
4511
|
deleteResult = await this.delete(kysely, model, {
|
|
3875
4512
|
AND: [
|
|
3876
4513
|
// filter for parent
|
|
@@ -3884,6 +4521,7 @@ var BaseOperationHandler = class {
|
|
|
3884
4521
|
]
|
|
3885
4522
|
});
|
|
3886
4523
|
} else {
|
|
4524
|
+
deleteFromModel = model;
|
|
3887
4525
|
deleteResult = await this.delete(kysely, model, {
|
|
3888
4526
|
AND: [
|
|
3889
4527
|
Object.fromEntries(keyPairs.map(({ fk, pk }) => [
|
|
@@ -3898,7 +4536,7 @@ var BaseOperationHandler = class {
|
|
|
3898
4536
|
}
|
|
3899
4537
|
}
|
|
3900
4538
|
if (throwForNotFound && expectedDeleteCount > deleteResult.count) {
|
|
3901
|
-
throw new NotFoundError(
|
|
4539
|
+
throw new NotFoundError(deleteFromModel);
|
|
3902
4540
|
}
|
|
3903
4541
|
}
|
|
3904
4542
|
normalizeRelationManipulationInput(model, data) {
|
|
@@ -3999,7 +4637,7 @@ var BaseOperationHandler = class {
|
|
|
3999
4637
|
// reused the filter if it's a complete id filter (without extra fields)
|
|
4000
4638
|
// otherwise, read the entity by the filter
|
|
4001
4639
|
getEntityIds(kysely, model, uniqueFilter) {
|
|
4002
|
-
const idFields =
|
|
4640
|
+
const idFields = requireIdFields(this.schema, model);
|
|
4003
4641
|
if (
|
|
4004
4642
|
// all id fields are provided
|
|
4005
4643
|
idFields.every((f) => f in uniqueFilter && uniqueFilter[f] !== void 0) && // no non-id filter exists
|
|
@@ -4101,7 +4739,7 @@ var AggregateOperationHandler = class extends BaseOperationHandler {
|
|
|
4101
4739
|
if (field === "_all") {
|
|
4102
4740
|
query = query.select((eb) => eb.cast(eb.fn.countAll(), "integer").as(`_count._all`));
|
|
4103
4741
|
} else {
|
|
4104
|
-
query = query.select((eb) => eb.cast(eb.fn.count(
|
|
4742
|
+
query = query.select((eb) => eb.cast(eb.fn.count(sql6.ref(`$sub.${field}`)), "integer").as(`${key}.${field}`));
|
|
4105
4743
|
}
|
|
4106
4744
|
}
|
|
4107
4745
|
});
|
|
@@ -4116,7 +4754,7 @@ var AggregateOperationHandler = class extends BaseOperationHandler {
|
|
|
4116
4754
|
if (val === true) {
|
|
4117
4755
|
query = query.select((eb) => {
|
|
4118
4756
|
const fn = match10(key).with("_sum", () => eb.fn.sum).with("_avg", () => eb.fn.avg).with("_max", () => eb.fn.max).with("_min", () => eb.fn.min).exhaustive();
|
|
4119
|
-
return fn(
|
|
4757
|
+
return fn(sql6.ref(`$sub.${field}`)).as(`${key}.${field}`);
|
|
4120
4758
|
});
|
|
4121
4759
|
}
|
|
4122
4760
|
});
|
|
@@ -4163,7 +4801,7 @@ var AggregateOperationHandler = class extends BaseOperationHandler {
|
|
|
4163
4801
|
};
|
|
4164
4802
|
|
|
4165
4803
|
// src/client/crud/operations/count.ts
|
|
4166
|
-
import { sql as
|
|
4804
|
+
import { sql as sql7 } from "kysely";
|
|
4167
4805
|
var CountOperationHandler = class extends BaseOperationHandler {
|
|
4168
4806
|
static {
|
|
4169
4807
|
__name(this, "CountOperationHandler");
|
|
@@ -4187,7 +4825,7 @@ var CountOperationHandler = class extends BaseOperationHandler {
|
|
|
4187
4825
|
return subQuery.as(subQueryName);
|
|
4188
4826
|
});
|
|
4189
4827
|
if (parsedArgs?.select && typeof parsedArgs.select === "object") {
|
|
4190
|
-
query = query.select((eb) => Object.keys(parsedArgs.select).map((key) => key === "_all" ? eb.cast(eb.fn.countAll(), "integer").as("_all") : eb.cast(eb.fn.count(
|
|
4828
|
+
query = query.select((eb) => Object.keys(parsedArgs.select).map((key) => key === "_all" ? eb.cast(eb.fn.countAll(), "integer").as("_all") : eb.cast(eb.fn.count(sql7.ref(`${subQueryName}.${key}`)), "integer").as(key)));
|
|
4191
4829
|
const result = await this.executeQuery(this.kysely, query, "count");
|
|
4192
4830
|
return result.rows[0];
|
|
4193
4831
|
} else {
|
|
@@ -4223,7 +4861,7 @@ var CreateOperationHandler = class extends BaseOperationHandler {
|
|
|
4223
4861
|
});
|
|
4224
4862
|
});
|
|
4225
4863
|
if (!result && this.hasPolicyEnabled) {
|
|
4226
|
-
throw new RejectedByPolicyError(this.model, `result is not allowed to be read back`);
|
|
4864
|
+
throw new RejectedByPolicyError(this.model, RejectedByPolicyReason.CANNOT_READ_BACK, `result is not allowed to be read back`);
|
|
4227
4865
|
}
|
|
4228
4866
|
return result;
|
|
4229
4867
|
}
|
|
@@ -4269,15 +4907,15 @@ var DeleteOperationHandler = class extends BaseOperationHandler {
|
|
|
4269
4907
|
omit: args.omit,
|
|
4270
4908
|
where: args.where
|
|
4271
4909
|
});
|
|
4272
|
-
if (!existing) {
|
|
4273
|
-
throw new NotFoundError(this.model);
|
|
4274
|
-
}
|
|
4275
4910
|
await this.safeTransaction(async (tx) => {
|
|
4276
4911
|
const result = await this.delete(tx, this.model, args.where);
|
|
4277
4912
|
if (result.count === 0) {
|
|
4278
4913
|
throw new NotFoundError(this.model);
|
|
4279
4914
|
}
|
|
4280
4915
|
});
|
|
4916
|
+
if (!existing && this.hasPolicyEnabled) {
|
|
4917
|
+
throw new RejectedByPolicyError(this.model, RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
|
|
4918
|
+
}
|
|
4281
4919
|
return existing;
|
|
4282
4920
|
}
|
|
4283
4921
|
async runDeleteMany(args) {
|
|
@@ -4311,7 +4949,7 @@ var FindOperationHandler = class extends BaseOperationHandler {
|
|
|
4311
4949
|
};
|
|
4312
4950
|
|
|
4313
4951
|
// src/client/crud/operations/group-by.ts
|
|
4314
|
-
import { expressionBuilder as
|
|
4952
|
+
import { expressionBuilder as expressionBuilder5 } from "kysely";
|
|
4315
4953
|
import { match as match13 } from "ts-pattern";
|
|
4316
4954
|
var GroupByOperationHandler = class extends BaseOperationHandler {
|
|
4317
4955
|
static {
|
|
@@ -4333,7 +4971,7 @@ var GroupByOperationHandler = class extends BaseOperationHandler {
|
|
|
4333
4971
|
subQuery = this.dialect.buildOrderBy(subQuery, this.model, this.model, void 0, skip !== void 0 || take !== void 0, negateOrderBy);
|
|
4334
4972
|
return subQuery.as("$sub");
|
|
4335
4973
|
});
|
|
4336
|
-
const fieldRef = /* @__PURE__ */ __name((field) => this.dialect.fieldRef(this.model, field,
|
|
4974
|
+
const fieldRef = /* @__PURE__ */ __name((field) => this.dialect.fieldRef(this.model, field, expressionBuilder5(), "$sub"), "fieldRef");
|
|
4337
4975
|
const bys = typeof parsedArgs.by === "string" ? [
|
|
4338
4976
|
parsedArgs.by
|
|
4339
4977
|
] : parsedArgs.by;
|
|
@@ -4448,7 +5086,7 @@ var UpdateOperationHandler = class extends BaseOperationHandler {
|
|
|
4448
5086
|
});
|
|
4449
5087
|
if (!readBackResult) {
|
|
4450
5088
|
if (this.hasPolicyEnabled) {
|
|
4451
|
-
throw new RejectedByPolicyError(this.model, "result is not allowed to be read back");
|
|
5089
|
+
throw new RejectedByPolicyError(this.model, RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
|
|
4452
5090
|
} else {
|
|
4453
5091
|
return null;
|
|
4454
5092
|
}
|
|
@@ -4465,16 +5103,24 @@ var UpdateOperationHandler = class extends BaseOperationHandler {
|
|
|
4465
5103
|
if (!args) {
|
|
4466
5104
|
return [];
|
|
4467
5105
|
}
|
|
4468
|
-
|
|
4469
|
-
const
|
|
4470
|
-
|
|
5106
|
+
const { readBackResult, updateResult } = await this.safeTransaction(async (tx) => {
|
|
5107
|
+
const updateResult2 = await this.updateMany(tx, this.model, args.where, args.data, args.limit, true);
|
|
5108
|
+
const readBackResult2 = await this.read(tx, this.model, {
|
|
4471
5109
|
select: args.select,
|
|
4472
5110
|
omit: args.omit,
|
|
4473
5111
|
where: {
|
|
4474
|
-
OR:
|
|
5112
|
+
OR: updateResult2.map((item) => getIdValues(this.schema, this.model, item))
|
|
4475
5113
|
}
|
|
4476
5114
|
});
|
|
5115
|
+
return {
|
|
5116
|
+
readBackResult: readBackResult2,
|
|
5117
|
+
updateResult: updateResult2
|
|
5118
|
+
};
|
|
4477
5119
|
});
|
|
5120
|
+
if (readBackResult.length < updateResult.length && this.hasPolicyEnabled) {
|
|
5121
|
+
throw new RejectedByPolicyError(this.model, RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
|
|
5122
|
+
}
|
|
5123
|
+
return readBackResult;
|
|
4478
5124
|
}
|
|
4479
5125
|
async runUpsert(args) {
|
|
4480
5126
|
const result = await this.safeTransaction(async (tx) => {
|
|
@@ -4490,18 +5136,32 @@ var UpdateOperationHandler = class extends BaseOperationHandler {
|
|
|
4490
5136
|
});
|
|
4491
5137
|
});
|
|
4492
5138
|
if (!result && this.hasPolicyEnabled) {
|
|
4493
|
-
throw new RejectedByPolicyError(this.model, "result is not allowed to be read back");
|
|
5139
|
+
throw new RejectedByPolicyError(this.model, RejectedByPolicyReason.CANNOT_READ_BACK, "result is not allowed to be read back");
|
|
4494
5140
|
}
|
|
4495
5141
|
return result;
|
|
4496
5142
|
}
|
|
4497
5143
|
};
|
|
4498
5144
|
|
|
4499
5145
|
// src/client/crud/validator.ts
|
|
4500
|
-
import { invariant as
|
|
4501
|
-
import
|
|
5146
|
+
import { invariant as invariant10 } from "@zenstackhq/common-helpers";
|
|
5147
|
+
import Decimal3 from "decimal.js";
|
|
4502
5148
|
import stableStringify from "json-stable-stringify";
|
|
4503
5149
|
import { match as match15, P as P2 } from "ts-pattern";
|
|
4504
5150
|
import { z } from "zod";
|
|
5151
|
+
|
|
5152
|
+
// src/utils/zod-utils.ts
|
|
5153
|
+
import { fromError as fromError3 } from "zod-validation-error/v3";
|
|
5154
|
+
import { fromError as fromError4 } from "zod-validation-error/v4";
|
|
5155
|
+
function formatError(error) {
|
|
5156
|
+
if ("_zod" in error) {
|
|
5157
|
+
return fromError4(error).toString();
|
|
5158
|
+
} else {
|
|
5159
|
+
return fromError3(error).toString();
|
|
5160
|
+
}
|
|
5161
|
+
}
|
|
5162
|
+
__name(formatError, "formatError");
|
|
5163
|
+
|
|
5164
|
+
// src/client/crud/validator.ts
|
|
4505
5165
|
var InputValidator = class {
|
|
4506
5166
|
static {
|
|
4507
5167
|
__name(this, "InputValidator");
|
|
@@ -4563,7 +5223,7 @@ var InputValidator = class {
|
|
|
4563
5223
|
}
|
|
4564
5224
|
const { error } = schema.safeParse(args);
|
|
4565
5225
|
if (error) {
|
|
4566
|
-
throw new InputValidationError(`Invalid ${operation} args: ${error
|
|
5226
|
+
throw new InputValidationError(`Invalid ${operation} args: ${formatError(error)}`, error);
|
|
4567
5227
|
}
|
|
4568
5228
|
return args;
|
|
4569
5229
|
}
|
|
@@ -4607,7 +5267,7 @@ var InputValidator = class {
|
|
|
4607
5267
|
z.bigint()
|
|
4608
5268
|
])).with("Decimal", () => z.union([
|
|
4609
5269
|
z.number(),
|
|
4610
|
-
z.instanceof(
|
|
5270
|
+
z.instanceof(Decimal3),
|
|
4611
5271
|
z.string()
|
|
4612
5272
|
])).with("DateTime", () => z.union([
|
|
4613
5273
|
z.date(),
|
|
@@ -4622,7 +5282,7 @@ var InputValidator = class {
|
|
|
4622
5282
|
return schema;
|
|
4623
5283
|
}
|
|
4624
5284
|
const typeDef = this.schema.typeDefs?.[type];
|
|
4625
|
-
|
|
5285
|
+
invariant10(typeDef, `Type definition "${type}" not found in schema`);
|
|
4626
5286
|
schema = z.object(Object.fromEntries(Object.entries(typeDef.fields).map(([field, def]) => {
|
|
4627
5287
|
let fieldSchema = this.makePrimitiveSchema(def.type);
|
|
4628
5288
|
if (def.array) {
|
|
@@ -4640,10 +5300,7 @@ var InputValidator = class {
|
|
|
4640
5300
|
return schema;
|
|
4641
5301
|
}
|
|
4642
5302
|
makeWhereSchema(model, unique, withoutRelationFields = false, withAggregations = false) {
|
|
4643
|
-
const modelDef =
|
|
4644
|
-
if (!modelDef) {
|
|
4645
|
-
throw new QueryError(`Model "${model}" not found in schema`);
|
|
4646
|
-
}
|
|
5303
|
+
const modelDef = requireModel(this.schema, model);
|
|
4647
5304
|
const fields = {};
|
|
4648
5305
|
for (const field of Object.keys(modelDef.fields)) {
|
|
4649
5306
|
const fieldDef = requireField(this.schema, model, field);
|
|
@@ -4693,7 +5350,7 @@ var InputValidator = class {
|
|
|
4693
5350
|
for (const uniqueField of uniqueFields) {
|
|
4694
5351
|
if ("defs" in uniqueField) {
|
|
4695
5352
|
fields[uniqueField.name] = z.object(Object.fromEntries(Object.entries(uniqueField.defs).map(([key, def]) => {
|
|
4696
|
-
|
|
5353
|
+
invariant10(!def.relation, "unique field cannot be a relation");
|
|
4697
5354
|
let fieldSchema;
|
|
4698
5355
|
const enumDef = getEnum(this.schema, def.type);
|
|
4699
5356
|
if (enumDef) {
|
|
@@ -4899,9 +5556,16 @@ var InputValidator = class {
|
|
|
4899
5556
|
fields[field] = z.boolean().optional();
|
|
4900
5557
|
}
|
|
4901
5558
|
}
|
|
5559
|
+
const _countSchema = this.makeCountSelectionSchema(modelDef);
|
|
5560
|
+
if (_countSchema) {
|
|
5561
|
+
fields["_count"] = _countSchema;
|
|
5562
|
+
}
|
|
5563
|
+
return z.strictObject(fields);
|
|
5564
|
+
}
|
|
5565
|
+
makeCountSelectionSchema(modelDef) {
|
|
4902
5566
|
const toManyRelations = Object.values(modelDef.fields).filter((def) => def.relation && def.array);
|
|
4903
5567
|
if (toManyRelations.length > 0) {
|
|
4904
|
-
|
|
5568
|
+
return z.union([
|
|
4905
5569
|
z.literal(true),
|
|
4906
5570
|
z.strictObject({
|
|
4907
5571
|
select: z.strictObject(toManyRelations.reduce((acc, fieldDef) => ({
|
|
@@ -4915,8 +5579,9 @@ var InputValidator = class {
|
|
|
4915
5579
|
}), {}))
|
|
4916
5580
|
})
|
|
4917
5581
|
]).optional();
|
|
5582
|
+
} else {
|
|
5583
|
+
return void 0;
|
|
4918
5584
|
}
|
|
4919
|
-
return z.strictObject(fields);
|
|
4920
5585
|
}
|
|
4921
5586
|
makeRelationSelectIncludeSchema(fieldDef) {
|
|
4922
5587
|
let objSchema = z.strictObject({
|
|
@@ -4963,6 +5628,10 @@ var InputValidator = class {
|
|
|
4963
5628
|
fields[field] = this.makeRelationSelectIncludeSchema(fieldDef).optional();
|
|
4964
5629
|
}
|
|
4965
5630
|
}
|
|
5631
|
+
const _countSchema = this.makeCountSelectionSchema(modelDef);
|
|
5632
|
+
if (_countSchema) {
|
|
5633
|
+
fields["_count"] = _countSchema;
|
|
5634
|
+
}
|
|
4966
5635
|
return z.strictObject(fields);
|
|
4967
5636
|
}
|
|
4968
5637
|
makeOrderBySchema(model, withRelation, WithAggregation) {
|
|
@@ -5029,13 +5698,15 @@ var InputValidator = class {
|
|
|
5029
5698
|
// #region Create
|
|
5030
5699
|
makeCreateSchema(model) {
|
|
5031
5700
|
const dataSchema = this.makeCreateDataSchema(model, false);
|
|
5032
|
-
|
|
5701
|
+
let schema = z.strictObject({
|
|
5033
5702
|
data: dataSchema,
|
|
5034
5703
|
select: this.makeSelectSchema(model).optional(),
|
|
5035
5704
|
include: this.makeIncludeSchema(model).optional(),
|
|
5036
5705
|
omit: this.makeOmitSchema(model).optional()
|
|
5037
5706
|
});
|
|
5038
|
-
|
|
5707
|
+
schema = this.refineForSelectIncludeMutuallyExclusive(schema);
|
|
5708
|
+
schema = this.refineForSelectOmitMutuallyExclusive(schema);
|
|
5709
|
+
return schema;
|
|
5039
5710
|
}
|
|
5040
5711
|
makeCreateManySchema(model) {
|
|
5041
5712
|
return this.makeCreateManyDataSchema(model, []).optional();
|
|
@@ -5160,11 +5831,11 @@ var InputValidator = class {
|
|
|
5160
5831
|
fields["delete"] = this.makeDeleteRelationDataSchema(fieldType, array, true).optional();
|
|
5161
5832
|
}
|
|
5162
5833
|
fields["update"] = array ? this.orArray(z.strictObject({
|
|
5163
|
-
where: this.makeWhereSchema(fieldType, true),
|
|
5834
|
+
where: this.makeWhereSchema(fieldType, true).optional(),
|
|
5164
5835
|
data: this.makeUpdateDataSchema(fieldType, withoutFields)
|
|
5165
5836
|
}), true).optional() : z.union([
|
|
5166
5837
|
z.strictObject({
|
|
5167
|
-
where: this.makeWhereSchema(fieldType, true),
|
|
5838
|
+
where: this.makeWhereSchema(fieldType, true).optional(),
|
|
5168
5839
|
data: this.makeUpdateDataSchema(fieldType, withoutFields)
|
|
5169
5840
|
}),
|
|
5170
5841
|
this.makeUpdateDataSchema(fieldType, withoutFields)
|
|
@@ -5224,14 +5895,16 @@ var InputValidator = class {
|
|
|
5224
5895
|
// #endregion
|
|
5225
5896
|
// #region Update
|
|
5226
5897
|
makeUpdateSchema(model) {
|
|
5227
|
-
|
|
5898
|
+
let schema = z.strictObject({
|
|
5228
5899
|
where: this.makeWhereSchema(model, true),
|
|
5229
5900
|
data: this.makeUpdateDataSchema(model),
|
|
5230
5901
|
select: this.makeSelectSchema(model).optional(),
|
|
5231
5902
|
include: this.makeIncludeSchema(model).optional(),
|
|
5232
5903
|
omit: this.makeOmitSchema(model).optional()
|
|
5233
5904
|
});
|
|
5234
|
-
|
|
5905
|
+
schema = this.refineForSelectIncludeMutuallyExclusive(schema);
|
|
5906
|
+
schema = this.refineForSelectOmitMutuallyExclusive(schema);
|
|
5907
|
+
return schema;
|
|
5235
5908
|
}
|
|
5236
5909
|
makeUpdateManySchema(model) {
|
|
5237
5910
|
return z.strictObject({
|
|
@@ -5242,14 +5915,15 @@ var InputValidator = class {
|
|
|
5242
5915
|
}
|
|
5243
5916
|
makeUpdateManyAndReturnSchema(model) {
|
|
5244
5917
|
const base = this.makeUpdateManySchema(model);
|
|
5245
|
-
|
|
5918
|
+
let schema = base.extend({
|
|
5246
5919
|
select: this.makeSelectSchema(model).optional(),
|
|
5247
5920
|
omit: this.makeOmitSchema(model).optional()
|
|
5248
5921
|
});
|
|
5249
|
-
|
|
5922
|
+
schema = this.refineForSelectOmitMutuallyExclusive(schema);
|
|
5923
|
+
return schema;
|
|
5250
5924
|
}
|
|
5251
5925
|
makeUpsertSchema(model) {
|
|
5252
|
-
|
|
5926
|
+
let schema = z.strictObject({
|
|
5253
5927
|
where: this.makeWhereSchema(model, true),
|
|
5254
5928
|
create: this.makeCreateDataSchema(model, false),
|
|
5255
5929
|
update: this.makeUpdateDataSchema(model),
|
|
@@ -5257,7 +5931,9 @@ var InputValidator = class {
|
|
|
5257
5931
|
include: this.makeIncludeSchema(model).optional(),
|
|
5258
5932
|
omit: this.makeOmitSchema(model).optional()
|
|
5259
5933
|
});
|
|
5260
|
-
|
|
5934
|
+
schema = this.refineForSelectIncludeMutuallyExclusive(schema);
|
|
5935
|
+
schema = this.refineForSelectOmitMutuallyExclusive(schema);
|
|
5936
|
+
return schema;
|
|
5261
5937
|
}
|
|
5262
5938
|
makeUpdateDataSchema(model, withoutFields = [], withoutRelationFields = false) {
|
|
5263
5939
|
const uncheckedVariantFields = {};
|
|
@@ -5334,12 +6010,14 @@ var InputValidator = class {
|
|
|
5334
6010
|
// #endregion
|
|
5335
6011
|
// #region Delete
|
|
5336
6012
|
makeDeleteSchema(model) {
|
|
5337
|
-
|
|
6013
|
+
let schema = z.strictObject({
|
|
5338
6014
|
where: this.makeWhereSchema(model, true),
|
|
5339
6015
|
select: this.makeSelectSchema(model).optional(),
|
|
5340
6016
|
include: this.makeIncludeSchema(model).optional()
|
|
5341
6017
|
});
|
|
5342
|
-
|
|
6018
|
+
schema = this.refineForSelectIncludeMutuallyExclusive(schema);
|
|
6019
|
+
schema = this.refineForSelectOmitMutuallyExclusive(schema);
|
|
6020
|
+
return schema;
|
|
5343
6021
|
}
|
|
5344
6022
|
makeDeleteManySchema(model) {
|
|
5345
6023
|
return z.object({
|
|
@@ -5689,32 +6367,13 @@ function performanceNow() {
|
|
|
5689
6367
|
__name(performanceNow, "performanceNow");
|
|
5690
6368
|
|
|
5691
6369
|
// src/client/executor/zenstack-query-executor.ts
|
|
5692
|
-
import { invariant as
|
|
5693
|
-
import { AndNode as AndNode2, DefaultQueryExecutor, DeleteQueryNode as DeleteQueryNode2, InsertQueryNode as InsertQueryNode2, ReturningNode as ReturningNode2, SelectionNode as SelectionNode4, SingleConnectionProvider, TableNode as
|
|
6370
|
+
import { invariant as invariant12 } from "@zenstackhq/common-helpers";
|
|
6371
|
+
import { AndNode as AndNode2, DefaultQueryExecutor, DeleteQueryNode as DeleteQueryNode2, InsertQueryNode as InsertQueryNode2, ReturningNode as ReturningNode2, SelectionNode as SelectionNode4, SingleConnectionProvider, TableNode as TableNode6, UpdateQueryNode as UpdateQueryNode2, WhereNode as WhereNode3 } from "kysely";
|
|
5694
6372
|
import { match as match16 } from "ts-pattern";
|
|
5695
6373
|
|
|
5696
|
-
// src/client/executor/kysely-utils.ts
|
|
5697
|
-
import { invariant as invariant9 } from "@zenstackhq/common-helpers";
|
|
5698
|
-
import { AliasNode as AliasNode4, IdentifierNode as IdentifierNode3 } from "kysely";
|
|
5699
|
-
function stripAlias(node) {
|
|
5700
|
-
if (AliasNode4.is(node)) {
|
|
5701
|
-
invariant9(IdentifierNode3.is(node.alias), "Expected identifier as alias");
|
|
5702
|
-
return {
|
|
5703
|
-
alias: node.alias.name,
|
|
5704
|
-
node: node.node
|
|
5705
|
-
};
|
|
5706
|
-
} else {
|
|
5707
|
-
return {
|
|
5708
|
-
alias: void 0,
|
|
5709
|
-
node
|
|
5710
|
-
};
|
|
5711
|
-
}
|
|
5712
|
-
}
|
|
5713
|
-
__name(stripAlias, "stripAlias");
|
|
5714
|
-
|
|
5715
6374
|
// src/client/executor/name-mapper.ts
|
|
5716
|
-
import { invariant as
|
|
5717
|
-
import { AliasNode as AliasNode5, ColumnNode as
|
|
6375
|
+
import { invariant as invariant11 } from "@zenstackhq/common-helpers";
|
|
6376
|
+
import { AliasNode as AliasNode5, ColumnNode as ColumnNode4, FromNode as FromNode3, IdentifierNode as IdentifierNode3, OperationNodeTransformer as OperationNodeTransformer2, ReferenceNode as ReferenceNode4, SelectAllNode, SelectionNode as SelectionNode3, TableNode as TableNode5 } from "kysely";
|
|
5718
6377
|
var QueryNameMapper = class extends OperationNodeTransformer2 {
|
|
5719
6378
|
static {
|
|
5720
6379
|
__name(this, "QueryNameMapper");
|
|
@@ -5783,7 +6442,7 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
|
|
|
5783
6442
|
};
|
|
5784
6443
|
}
|
|
5785
6444
|
transformReference(node) {
|
|
5786
|
-
if (!
|
|
6445
|
+
if (!ColumnNode4.is(node.column)) {
|
|
5787
6446
|
return super.transformReference(node);
|
|
5788
6447
|
}
|
|
5789
6448
|
const scope = this.resolveFieldFromScopes(node.column.column.name, node.table?.table.identifier.name);
|
|
@@ -5791,12 +6450,12 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
|
|
|
5791
6450
|
const mappedFieldName = this.mapFieldName(scope.model, node.column.column.name);
|
|
5792
6451
|
let mappedTableName = node.table?.table.identifier.name;
|
|
5793
6452
|
if (mappedTableName) {
|
|
5794
|
-
if (scope.alias === mappedTableName) {
|
|
6453
|
+
if (scope.alias && IdentifierNode3.is(scope.alias) && scope.alias.name === mappedTableName) {
|
|
5795
6454
|
} else if (scope.model === mappedTableName) {
|
|
5796
6455
|
mappedTableName = this.mapTableName(scope.model);
|
|
5797
6456
|
}
|
|
5798
6457
|
}
|
|
5799
|
-
return
|
|
6458
|
+
return ReferenceNode4.create(ColumnNode4.create(mappedFieldName), mappedTableName ? TableNode5.create(mappedTableName) : void 0);
|
|
5800
6459
|
} else {
|
|
5801
6460
|
return super.transformReference(node);
|
|
5802
6461
|
}
|
|
@@ -5807,14 +6466,14 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
|
|
|
5807
6466
|
return super.transformColumn(node);
|
|
5808
6467
|
}
|
|
5809
6468
|
const mappedName = this.mapFieldName(scope.model, node.column.name);
|
|
5810
|
-
return
|
|
6469
|
+
return ColumnNode4.create(mappedName);
|
|
5811
6470
|
}
|
|
5812
6471
|
transformUpdateQuery(node) {
|
|
5813
6472
|
if (!node.table) {
|
|
5814
6473
|
return super.transformUpdateQuery(node);
|
|
5815
6474
|
}
|
|
5816
6475
|
const { alias, node: innerTable } = stripAlias(node.table);
|
|
5817
|
-
if (!innerTable || !
|
|
6476
|
+
if (!innerTable || !TableNode5.is(innerTable)) {
|
|
5818
6477
|
return super.transformUpdateQuery(node);
|
|
5819
6478
|
}
|
|
5820
6479
|
return this.withScope({
|
|
@@ -5832,14 +6491,14 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
|
|
|
5832
6491
|
const scopes = node.from.froms.map((node2) => {
|
|
5833
6492
|
const { alias, node: innerNode } = stripAlias(node2);
|
|
5834
6493
|
return {
|
|
5835
|
-
model:
|
|
6494
|
+
model: extractModelName(innerNode),
|
|
5836
6495
|
alias,
|
|
5837
6496
|
namesMapped: false
|
|
5838
6497
|
};
|
|
5839
6498
|
});
|
|
5840
6499
|
const froms = node.from.froms.map((from) => {
|
|
5841
6500
|
const { alias, node: innerNode } = stripAlias(from);
|
|
5842
|
-
if (
|
|
6501
|
+
if (TableNode5.is(innerNode)) {
|
|
5843
6502
|
return this.wrapAlias(this.processTableRef(innerNode), alias);
|
|
5844
6503
|
} else {
|
|
5845
6504
|
return super.transformNode(from);
|
|
@@ -5864,15 +6523,15 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
|
|
|
5864
6523
|
} else {
|
|
5865
6524
|
selections.push(super.transformSelection(selection));
|
|
5866
6525
|
}
|
|
5867
|
-
} else if (
|
|
6526
|
+
} else if (ReferenceNode4.is(selection.selection) || ColumnNode4.is(selection.selection)) {
|
|
5868
6527
|
const transformed = this.transformNode(selection.selection);
|
|
5869
6528
|
if (AliasNode5.is(transformed)) {
|
|
5870
6529
|
selections.push(SelectionNode3.create(transformed));
|
|
5871
6530
|
} else {
|
|
5872
|
-
const origFieldName =
|
|
5873
|
-
const fieldName =
|
|
6531
|
+
const origFieldName = extractFieldName(selection.selection);
|
|
6532
|
+
const fieldName = extractFieldName(transformed);
|
|
5874
6533
|
if (fieldName !== origFieldName) {
|
|
5875
|
-
selections.push(SelectionNode3.create(this.wrapAlias(transformed, origFieldName)));
|
|
6534
|
+
selections.push(SelectionNode3.create(this.wrapAlias(transformed, origFieldName ? IdentifierNode3.create(origFieldName) : void 0)));
|
|
5876
6535
|
} else {
|
|
5877
6536
|
selections.push(SelectionNode3.create(transformed));
|
|
5878
6537
|
}
|
|
@@ -5888,7 +6547,7 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
|
|
|
5888
6547
|
const scope = this.scopes[i];
|
|
5889
6548
|
if (qualifier) {
|
|
5890
6549
|
if (scope.alias) {
|
|
5891
|
-
if (scope.alias === qualifier) {
|
|
6550
|
+
if (scope.alias && IdentifierNode3.is(scope.alias) && scope.alias.name === qualifier) {
|
|
5892
6551
|
return scope;
|
|
5893
6552
|
} else {
|
|
5894
6553
|
continue;
|
|
@@ -5934,16 +6593,16 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
|
|
|
5934
6593
|
}
|
|
5935
6594
|
}
|
|
5936
6595
|
wrapAlias(node, alias) {
|
|
5937
|
-
return alias ? AliasNode5.create(node,
|
|
6596
|
+
return alias ? AliasNode5.create(node, alias) : node;
|
|
5938
6597
|
}
|
|
5939
6598
|
processTableRef(node) {
|
|
5940
6599
|
if (!node) {
|
|
5941
6600
|
return node;
|
|
5942
6601
|
}
|
|
5943
|
-
if (!
|
|
6602
|
+
if (!TableNode5.is(node)) {
|
|
5944
6603
|
return super.transformNode(node);
|
|
5945
6604
|
}
|
|
5946
|
-
return
|
|
6605
|
+
return TableNode5.create(this.mapTableName(node.table.identifier.name));
|
|
5947
6606
|
}
|
|
5948
6607
|
getMappedName(def) {
|
|
5949
6608
|
const mapAttr = def.attributes?.find((attr) => attr.name === "@@map" || attr.name === "@map");
|
|
@@ -5979,14 +6638,14 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
|
|
|
5979
6638
|
// convert a "from" node to a nested query if there are columns with name mapping
|
|
5980
6639
|
processSelectTable(node) {
|
|
5981
6640
|
const { alias, node: innerNode } = stripAlias(node);
|
|
5982
|
-
if (innerNode &&
|
|
6641
|
+
if (innerNode && TableNode5.is(innerNode)) {
|
|
5983
6642
|
const modelName = innerNode.table.identifier.name;
|
|
5984
6643
|
const mappedName = this.mapTableName(modelName);
|
|
5985
|
-
const finalAlias = alias ?? (mappedName !== modelName ? modelName : void 0);
|
|
6644
|
+
const finalAlias = alias ?? (mappedName !== modelName ? IdentifierNode3.create(modelName) : void 0);
|
|
5986
6645
|
return {
|
|
5987
|
-
node: this.wrapAlias(
|
|
6646
|
+
node: this.wrapAlias(TableNode5.create(mappedName), finalAlias),
|
|
5988
6647
|
scope: {
|
|
5989
|
-
alias: alias ?? modelName,
|
|
6648
|
+
alias: alias ?? IdentifierNode3.create(modelName),
|
|
5990
6649
|
model: modelName,
|
|
5991
6650
|
namesMapped: !this.hasMappedColumns(modelName)
|
|
5992
6651
|
}
|
|
@@ -6006,9 +6665,9 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
|
|
|
6006
6665
|
const modelDef = requireModel(this.schema, model);
|
|
6007
6666
|
return this.getModelFields(modelDef).map((fieldDef) => {
|
|
6008
6667
|
const columnName = this.mapFieldName(model, fieldDef.name);
|
|
6009
|
-
const columnRef =
|
|
6668
|
+
const columnRef = ReferenceNode4.create(ColumnNode4.create(columnName), alias && IdentifierNode3.is(alias) ? TableNode5.create(alias.name) : void 0);
|
|
6010
6669
|
if (columnName !== fieldDef.name) {
|
|
6011
|
-
const aliased = AliasNode5.create(columnRef,
|
|
6670
|
+
const aliased = AliasNode5.create(columnRef, IdentifierNode3.create(fieldDef.name));
|
|
6012
6671
|
return SelectionNode3.create(aliased);
|
|
6013
6672
|
} else {
|
|
6014
6673
|
return SelectionNode3.create(columnRef);
|
|
@@ -6037,37 +6696,24 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
|
|
|
6037
6696
|
processSelection(node) {
|
|
6038
6697
|
let alias;
|
|
6039
6698
|
if (!AliasNode5.is(node)) {
|
|
6040
|
-
alias =
|
|
6699
|
+
alias = extractFieldName(node);
|
|
6041
6700
|
}
|
|
6042
6701
|
const result = super.transformNode(node);
|
|
6043
|
-
return this.wrapAlias(result, alias);
|
|
6702
|
+
return this.wrapAlias(result, alias ? IdentifierNode3.create(alias) : void 0);
|
|
6044
6703
|
}
|
|
6045
6704
|
processSelectAll(node) {
|
|
6046
6705
|
const scope = this.scopes[this.scopes.length - 1];
|
|
6047
|
-
|
|
6706
|
+
invariant11(scope);
|
|
6048
6707
|
if (!scope.model || !this.hasMappedColumns(scope.model)) {
|
|
6049
6708
|
return super.transformSelectAll(node);
|
|
6050
6709
|
}
|
|
6051
6710
|
const modelDef = requireModel(this.schema, scope.model);
|
|
6052
6711
|
return this.getModelFields(modelDef).map((fieldDef) => {
|
|
6053
6712
|
const columnName = this.mapFieldName(modelDef.name, fieldDef.name);
|
|
6054
|
-
const columnRef =
|
|
6055
|
-
return columnName !== fieldDef.name ? this.wrapAlias(columnRef, fieldDef.name) : columnRef;
|
|
6713
|
+
const columnRef = ReferenceNode4.create(ColumnNode4.create(columnName));
|
|
6714
|
+
return columnName !== fieldDef.name ? this.wrapAlias(columnRef, IdentifierNode3.create(fieldDef.name)) : columnRef;
|
|
6056
6715
|
});
|
|
6057
6716
|
}
|
|
6058
|
-
extractModelName(node) {
|
|
6059
|
-
const { node: innerNode } = stripAlias(node);
|
|
6060
|
-
return TableNode4.is(innerNode) ? innerNode.table.identifier.name : void 0;
|
|
6061
|
-
}
|
|
6062
|
-
extractFieldName(node) {
|
|
6063
|
-
if (ReferenceNode3.is(node) && ColumnNode3.is(node.column)) {
|
|
6064
|
-
return node.column.column.name;
|
|
6065
|
-
} else if (ColumnNode3.is(node)) {
|
|
6066
|
-
return node.column.name;
|
|
6067
|
-
} else {
|
|
6068
|
-
return void 0;
|
|
6069
|
-
}
|
|
6070
|
-
}
|
|
6071
6717
|
};
|
|
6072
6718
|
|
|
6073
6719
|
// src/client/executor/zenstack-query-executor.ts
|
|
@@ -6114,7 +6760,6 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
|
|
|
6114
6760
|
const hookResult = await hook({
|
|
6115
6761
|
client: this.client,
|
|
6116
6762
|
schema: this.client.$schema,
|
|
6117
|
-
kysely: this.kysely,
|
|
6118
6763
|
query,
|
|
6119
6764
|
proceed: _p
|
|
6120
6765
|
});
|
|
@@ -6273,17 +6918,17 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
|
|
|
6273
6918
|
}
|
|
6274
6919
|
getMutationModel(queryNode) {
|
|
6275
6920
|
return match16(queryNode).when(InsertQueryNode2.is, (node) => {
|
|
6276
|
-
|
|
6921
|
+
invariant12(node.into, "InsertQueryNode must have an into clause");
|
|
6277
6922
|
return node.into.table.identifier.name;
|
|
6278
6923
|
}).when(UpdateQueryNode2.is, (node) => {
|
|
6279
|
-
|
|
6924
|
+
invariant12(node.table, "UpdateQueryNode must have a table");
|
|
6280
6925
|
const { node: tableNode } = stripAlias(node.table);
|
|
6281
|
-
|
|
6926
|
+
invariant12(TableNode6.is(tableNode), "UpdateQueryNode must use a TableNode");
|
|
6282
6927
|
return tableNode.table.identifier.name;
|
|
6283
6928
|
}).when(DeleteQueryNode2.is, (node) => {
|
|
6284
|
-
|
|
6929
|
+
invariant12(node.from.froms.length === 1, "Delete query must have exactly one from table");
|
|
6285
6930
|
const { node: tableNode } = stripAlias(node.from.froms[0]);
|
|
6286
|
-
|
|
6931
|
+
invariant12(TableNode6.is(tableNode), "DeleteQueryNode must use a TableNode");
|
|
6287
6932
|
return tableNode.table.identifier.name;
|
|
6288
6933
|
}).otherwise((node) => {
|
|
6289
6934
|
throw new InternalError(`Invalid query node: ${node}`);
|
|
@@ -6381,53 +7026,58 @@ __export(functions_exports, {
|
|
|
6381
7026
|
search: () => search,
|
|
6382
7027
|
startsWith: () => startsWith
|
|
6383
7028
|
});
|
|
6384
|
-
import { invariant as
|
|
6385
|
-
import { sql as
|
|
7029
|
+
import { invariant as invariant13, lowerCaseFirst, upperCaseFirst } from "@zenstackhq/common-helpers";
|
|
7030
|
+
import { sql as sql8, ValueNode as ValueNode5 } from "kysely";
|
|
6386
7031
|
import { match as match17 } from "ts-pattern";
|
|
6387
|
-
var contains = /* @__PURE__ */ __name((eb, args) =>
|
|
6388
|
-
const [field, search2, caseInsensitive = false] = args;
|
|
6389
|
-
if (!field) {
|
|
6390
|
-
throw new Error('"field" parameter is required');
|
|
6391
|
-
}
|
|
6392
|
-
if (!search2) {
|
|
6393
|
-
throw new Error('"search" parameter is required');
|
|
6394
|
-
}
|
|
6395
|
-
const searchExpr = eb.fn("CONCAT", [
|
|
6396
|
-
sql7.lit("%"),
|
|
6397
|
-
search2,
|
|
6398
|
-
sql7.lit("%")
|
|
6399
|
-
]);
|
|
6400
|
-
return eb(field, caseInsensitive ? "ilike" : "like", searchExpr);
|
|
6401
|
-
}, "contains");
|
|
7032
|
+
var contains = /* @__PURE__ */ __name((eb, args, context) => textMatch(eb, args, context, "contains"), "contains");
|
|
6402
7033
|
var search = /* @__PURE__ */ __name((_eb, _args) => {
|
|
6403
7034
|
throw new Error(`"search" function is not implemented yet`);
|
|
6404
7035
|
}, "search");
|
|
6405
|
-
var startsWith = /* @__PURE__ */ __name((eb, args) =>
|
|
6406
|
-
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
}
|
|
6410
|
-
if (!search2) {
|
|
6411
|
-
throw new Error('"search" parameter is required');
|
|
6412
|
-
}
|
|
6413
|
-
return eb(field, "like", eb.fn("CONCAT", [
|
|
6414
|
-
search2,
|
|
6415
|
-
sql7.lit("%")
|
|
6416
|
-
]));
|
|
6417
|
-
}, "startsWith");
|
|
6418
|
-
var endsWith = /* @__PURE__ */ __name((eb, args) => {
|
|
6419
|
-
const [field, search2] = args;
|
|
7036
|
+
var startsWith = /* @__PURE__ */ __name((eb, args, context) => textMatch(eb, args, context, "startsWith"), "startsWith");
|
|
7037
|
+
var endsWith = /* @__PURE__ */ __name((eb, args, context) => textMatch(eb, args, context, "endsWith"), "endsWith");
|
|
7038
|
+
var textMatch = /* @__PURE__ */ __name((eb, args, { dialect }, method) => {
|
|
7039
|
+
const [field, search2, caseInsensitive = void 0] = args;
|
|
6420
7040
|
if (!field) {
|
|
6421
7041
|
throw new Error('"field" parameter is required');
|
|
6422
7042
|
}
|
|
6423
7043
|
if (!search2) {
|
|
6424
7044
|
throw new Error('"search" parameter is required');
|
|
6425
7045
|
}
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
7046
|
+
const casingBehavior = dialect.getStringCasingBehavior();
|
|
7047
|
+
const caseInsensitiveValue = readBoolean(caseInsensitive, false);
|
|
7048
|
+
let op;
|
|
7049
|
+
let fieldExpr = field;
|
|
7050
|
+
let searchExpr = search2;
|
|
7051
|
+
if (caseInsensitiveValue) {
|
|
7052
|
+
if (casingBehavior.supportsILike) {
|
|
7053
|
+
op = "ilike";
|
|
7054
|
+
} else {
|
|
7055
|
+
op = "like";
|
|
7056
|
+
if (casingBehavior.likeCaseSensitive === true) {
|
|
7057
|
+
fieldExpr = eb.fn("LOWER", [
|
|
7058
|
+
fieldExpr
|
|
7059
|
+
]);
|
|
7060
|
+
searchExpr = eb.fn("LOWER", [
|
|
7061
|
+
searchExpr
|
|
7062
|
+
]);
|
|
7063
|
+
}
|
|
7064
|
+
}
|
|
7065
|
+
} else {
|
|
7066
|
+
op = "like";
|
|
7067
|
+
}
|
|
7068
|
+
searchExpr = match17(method).with("contains", () => eb.fn("CONCAT", [
|
|
7069
|
+
sql8.lit("%"),
|
|
7070
|
+
sql8`CAST(${searchExpr} as text)`,
|
|
7071
|
+
sql8.lit("%")
|
|
7072
|
+
])).with("startsWith", () => eb.fn("CONCAT", [
|
|
7073
|
+
sql8`CAST(${searchExpr} as text)`,
|
|
7074
|
+
sql8.lit("%")
|
|
7075
|
+
])).with("endsWith", () => eb.fn("CONCAT", [
|
|
7076
|
+
sql8.lit("%"),
|
|
7077
|
+
sql8`CAST(${searchExpr} as text)`
|
|
7078
|
+
])).exhaustive();
|
|
7079
|
+
return eb(fieldExpr, op, searchExpr);
|
|
7080
|
+
}, "textMatch");
|
|
6431
7081
|
var has = /* @__PURE__ */ __name((eb, args) => {
|
|
6432
7082
|
const [field, search2] = args;
|
|
6433
7083
|
if (!field) {
|
|
@@ -6465,18 +7115,16 @@ var isEmpty = /* @__PURE__ */ __name((eb, args, { dialect }) => {
|
|
|
6465
7115
|
if (!field) {
|
|
6466
7116
|
throw new Error('"field" parameter is required');
|
|
6467
7117
|
}
|
|
6468
|
-
return eb(dialect.buildArrayLength(eb, field), "=",
|
|
7118
|
+
return eb(dialect.buildArrayLength(eb, field), "=", sql8.lit(0));
|
|
6469
7119
|
}, "isEmpty");
|
|
6470
|
-
var now = /* @__PURE__ */ __name((
|
|
6471
|
-
return match17(dialect.provider).with("postgresql", () => eb.fn("now")).with("sqlite", () => sql7.raw("CURRENT_TIMESTAMP")).exhaustive();
|
|
6472
|
-
}, "now");
|
|
7120
|
+
var now = /* @__PURE__ */ __name(() => sql8.raw("CURRENT_TIMESTAMP"), "now");
|
|
6473
7121
|
var currentModel = /* @__PURE__ */ __name((_eb, args, { model }) => {
|
|
6474
7122
|
let result = model;
|
|
6475
7123
|
const [casing] = args;
|
|
6476
7124
|
if (casing) {
|
|
6477
7125
|
result = processCasing(casing, result, model);
|
|
6478
7126
|
}
|
|
6479
|
-
return
|
|
7127
|
+
return sql8.lit(result);
|
|
6480
7128
|
}, "currentModel");
|
|
6481
7129
|
var currentOperation = /* @__PURE__ */ __name((_eb, args, { operation }) => {
|
|
6482
7130
|
let result = operation;
|
|
@@ -6484,21 +7132,30 @@ var currentOperation = /* @__PURE__ */ __name((_eb, args, { operation }) => {
|
|
|
6484
7132
|
if (casing) {
|
|
6485
7133
|
result = processCasing(casing, result, operation);
|
|
6486
7134
|
}
|
|
6487
|
-
return
|
|
7135
|
+
return sql8.lit(result);
|
|
6488
7136
|
}, "currentOperation");
|
|
6489
7137
|
function processCasing(casing, result, model) {
|
|
6490
7138
|
const opNode = casing.toOperationNode();
|
|
6491
|
-
|
|
7139
|
+
invariant13(ValueNode5.is(opNode) && typeof opNode.value === "string", '"casting" parameter must be a string value');
|
|
6492
7140
|
result = match17(opNode.value).with("original", () => model).with("upper", () => result.toUpperCase()).with("lower", () => result.toLowerCase()).with("capitalize", () => upperCaseFirst(result)).with("uncapitalize", () => lowerCaseFirst(result)).otherwise(() => {
|
|
6493
7141
|
throw new Error(`Invalid casing value: ${opNode.value}. Must be "original", "upper", "lower", "capitalize", or "uncapitalize".`);
|
|
6494
7142
|
});
|
|
6495
7143
|
return result;
|
|
6496
7144
|
}
|
|
6497
7145
|
__name(processCasing, "processCasing");
|
|
7146
|
+
function readBoolean(expr2, defaultValue) {
|
|
7147
|
+
if (expr2 === void 0) {
|
|
7148
|
+
return defaultValue;
|
|
7149
|
+
}
|
|
7150
|
+
const opNode = expr2.toOperationNode();
|
|
7151
|
+
invariant13(ValueNode5.is(opNode), "expression must be a literal value");
|
|
7152
|
+
return !!opNode.value;
|
|
7153
|
+
}
|
|
7154
|
+
__name(readBoolean, "readBoolean");
|
|
6498
7155
|
|
|
6499
7156
|
// src/client/helpers/schema-db-pusher.ts
|
|
6500
|
-
import { invariant as
|
|
6501
|
-
import { sql as
|
|
7157
|
+
import { invariant as invariant14 } from "@zenstackhq/common-helpers";
|
|
7158
|
+
import { sql as sql9 } from "kysely";
|
|
6502
7159
|
import toposort from "toposort";
|
|
6503
7160
|
import { match as match18 } from "ts-pattern";
|
|
6504
7161
|
var SchemaDbPusher = class {
|
|
@@ -6519,7 +7176,8 @@ var SchemaDbPusher = class {
|
|
|
6519
7176
|
await createEnum.execute();
|
|
6520
7177
|
}
|
|
6521
7178
|
}
|
|
6522
|
-
const
|
|
7179
|
+
const models = Object.values(this.schema.models).filter((m) => !m.isView);
|
|
7180
|
+
const sortedModels = this.sortModels(models);
|
|
6523
7181
|
for (const modelDef of sortedModels) {
|
|
6524
7182
|
const createTable = this.createModelTable(tx, modelDef);
|
|
6525
7183
|
await createTable.execute();
|
|
@@ -6528,7 +7186,7 @@ var SchemaDbPusher = class {
|
|
|
6528
7186
|
}
|
|
6529
7187
|
sortModels(models) {
|
|
6530
7188
|
const graph = [];
|
|
6531
|
-
for (const model of
|
|
7189
|
+
for (const model of models) {
|
|
6532
7190
|
let added = false;
|
|
6533
7191
|
if (model.baseModel) {
|
|
6534
7192
|
const baseDef = requireModel(this.schema, model.baseModel);
|
|
@@ -6613,7 +7271,7 @@ var SchemaDbPusher = class {
|
|
|
6613
7271
|
}
|
|
6614
7272
|
addUniqueConstraint(table, modelDef) {
|
|
6615
7273
|
for (const [key, value] of Object.entries(modelDef.uniqueFields)) {
|
|
6616
|
-
|
|
7274
|
+
invariant14(typeof value === "object", "expecting an object");
|
|
6617
7275
|
if ("type" in value) {
|
|
6618
7276
|
const fieldDef = modelDef.fields[key];
|
|
6619
7277
|
if (fieldDef.unique) {
|
|
@@ -6636,7 +7294,7 @@ var SchemaDbPusher = class {
|
|
|
6636
7294
|
if (fieldDef.default !== void 0) {
|
|
6637
7295
|
if (typeof fieldDef.default === "object" && "kind" in fieldDef.default) {
|
|
6638
7296
|
if (ExpressionUtils.isCall(fieldDef.default) && fieldDef.default.function === "now") {
|
|
6639
|
-
col = col.defaultTo(
|
|
7297
|
+
col = col.defaultTo(sql9`CURRENT_TIMESTAMP`);
|
|
6640
7298
|
}
|
|
6641
7299
|
} else {
|
|
6642
7300
|
col = col.defaultTo(fieldDef.default);
|
|
@@ -6656,7 +7314,7 @@ var SchemaDbPusher = class {
|
|
|
6656
7314
|
}
|
|
6657
7315
|
mapFieldType(fieldDef) {
|
|
6658
7316
|
if (this.schema.enums?.[fieldDef.type]) {
|
|
6659
|
-
return this.schema.provider.type === "postgresql" ?
|
|
7317
|
+
return this.schema.provider.type === "postgresql" ? sql9.ref(fieldDef.type) : "text";
|
|
6660
7318
|
}
|
|
6661
7319
|
if (this.isAutoIncrement(fieldDef) && this.schema.provider.type === "postgresql") {
|
|
6662
7320
|
return "serial";
|
|
@@ -6669,7 +7327,7 @@ var SchemaDbPusher = class {
|
|
|
6669
7327
|
throw new Error(`Unsupported field type: ${type}`);
|
|
6670
7328
|
});
|
|
6671
7329
|
if (fieldDef.array) {
|
|
6672
|
-
return
|
|
7330
|
+
return sql9.raw(`${result}[]`);
|
|
6673
7331
|
} else {
|
|
6674
7332
|
return result;
|
|
6675
7333
|
}
|
|
@@ -6681,7 +7339,7 @@ var SchemaDbPusher = class {
|
|
|
6681
7339
|
return fieldDef.default && ExpressionUtils.isCall(fieldDef.default) && fieldDef.default.function === "autoincrement";
|
|
6682
7340
|
}
|
|
6683
7341
|
addForeignKeyConstraint(table, model, fieldName, fieldDef) {
|
|
6684
|
-
|
|
7342
|
+
invariant14(fieldDef.relation, "field must be a relation");
|
|
6685
7343
|
if (!fieldDef.relation.fields || !fieldDef.relation.references) {
|
|
6686
7344
|
return table;
|
|
6687
7345
|
}
|
|
@@ -6738,16 +7396,15 @@ function valueToPromise(thing) {
|
|
|
6738
7396
|
__name(valueToPromise, "valueToPromise");
|
|
6739
7397
|
|
|
6740
7398
|
// src/client/result-processor.ts
|
|
6741
|
-
import { invariant as invariant14 } from "@zenstackhq/common-helpers";
|
|
6742
|
-
import Decimal2 from "decimal.js";
|
|
6743
|
-
import { match as match19 } from "ts-pattern";
|
|
6744
7399
|
var ResultProcessor = class {
|
|
6745
7400
|
static {
|
|
6746
7401
|
__name(this, "ResultProcessor");
|
|
6747
7402
|
}
|
|
6748
7403
|
schema;
|
|
6749
|
-
|
|
7404
|
+
dialect;
|
|
7405
|
+
constructor(schema, options) {
|
|
6750
7406
|
this.schema = schema;
|
|
7407
|
+
this.dialect = getCrudDialect(schema, options);
|
|
6751
7408
|
}
|
|
6752
7409
|
processResult(data, model, args) {
|
|
6753
7410
|
const result = this.doProcessResult(data, model);
|
|
@@ -6776,7 +7433,7 @@ var ResultProcessor = class {
|
|
|
6776
7433
|
}
|
|
6777
7434
|
if (key.startsWith(DELEGATE_JOINED_FIELD_PREFIX)) {
|
|
6778
7435
|
if (value) {
|
|
6779
|
-
const subRow = this.
|
|
7436
|
+
const subRow = this.dialect.transformOutput(value, "Json");
|
|
6780
7437
|
const subModel = key.slice(DELEGATE_JOINED_FIELD_PREFIX.length);
|
|
6781
7438
|
const idValues = getIdValues(this.schema, subModel, subRow);
|
|
6782
7439
|
if (Object.values(idValues).some((v) => v === null || v === void 0)) {
|
|
@@ -6810,10 +7467,10 @@ var ResultProcessor = class {
|
|
|
6810
7467
|
processFieldValue(value, fieldDef) {
|
|
6811
7468
|
const type = fieldDef.type;
|
|
6812
7469
|
if (Array.isArray(value)) {
|
|
6813
|
-
value.forEach((v, i) => value[i] = this.
|
|
7470
|
+
value.forEach((v, i) => value[i] = this.dialect.transformOutput(v, type));
|
|
6814
7471
|
return value;
|
|
6815
7472
|
} else {
|
|
6816
|
-
return this.
|
|
7473
|
+
return this.dialect.transformOutput(value, type);
|
|
6817
7474
|
}
|
|
6818
7475
|
}
|
|
6819
7476
|
processRelation(value, fieldDef) {
|
|
@@ -6827,42 +7484,6 @@ var ResultProcessor = class {
|
|
|
6827
7484
|
}
|
|
6828
7485
|
return this.doProcessResult(relationData, fieldDef.type);
|
|
6829
7486
|
}
|
|
6830
|
-
transformScalar(value, type) {
|
|
6831
|
-
if (this.schema.typeDefs && type in this.schema.typeDefs) {
|
|
6832
|
-
return this.transformJson(value);
|
|
6833
|
-
} else {
|
|
6834
|
-
return match19(type).with("Boolean", () => this.transformBoolean(value)).with("DateTime", () => this.transformDate(value)).with("Bytes", () => this.transformBytes(value)).with("Decimal", () => this.transformDecimal(value)).with("BigInt", () => this.transformBigInt(value)).with("Json", () => this.transformJson(value)).otherwise(() => value);
|
|
6835
|
-
}
|
|
6836
|
-
}
|
|
6837
|
-
transformDecimal(value) {
|
|
6838
|
-
if (value instanceof Decimal2) {
|
|
6839
|
-
return value;
|
|
6840
|
-
}
|
|
6841
|
-
invariant14(typeof value === "string" || typeof value === "number" || value instanceof Decimal2, `Expected string, number or Decimal, got ${typeof value}`);
|
|
6842
|
-
return new Decimal2(value);
|
|
6843
|
-
}
|
|
6844
|
-
transformBigInt(value) {
|
|
6845
|
-
if (typeof value === "bigint") {
|
|
6846
|
-
return value;
|
|
6847
|
-
}
|
|
6848
|
-
invariant14(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
|
|
6849
|
-
return BigInt(value);
|
|
6850
|
-
}
|
|
6851
|
-
transformBoolean(value) {
|
|
6852
|
-
return !!value;
|
|
6853
|
-
}
|
|
6854
|
-
transformDate(value) {
|
|
6855
|
-
if (typeof value === "number") {
|
|
6856
|
-
return new Date(value);
|
|
6857
|
-
} else if (typeof value === "string") {
|
|
6858
|
-
return new Date(Date.parse(value));
|
|
6859
|
-
} else {
|
|
6860
|
-
return value;
|
|
6861
|
-
}
|
|
6862
|
-
}
|
|
6863
|
-
transformBytes(value) {
|
|
6864
|
-
return Buffer.isBuffer(value) ? Uint8Array.from(value) : value;
|
|
6865
|
-
}
|
|
6866
7487
|
fixReversedResult(data, model, args) {
|
|
6867
7488
|
if (!data) {
|
|
6868
7489
|
return;
|
|
@@ -6887,12 +7508,6 @@ var ResultProcessor = class {
|
|
|
6887
7508
|
}
|
|
6888
7509
|
}
|
|
6889
7510
|
}
|
|
6890
|
-
transformJson(value) {
|
|
6891
|
-
return match19(this.schema.provider.type).with("sqlite", () => {
|
|
6892
|
-
invariant14(typeof value === "string", "Expected string, got " + typeof value);
|
|
6893
|
-
return JSON.parse(value);
|
|
6894
|
-
}).otherwise(() => value);
|
|
6895
|
-
}
|
|
6896
7511
|
};
|
|
6897
7512
|
|
|
6898
7513
|
// src/client/client-impl.ts
|
|
@@ -6915,7 +7530,7 @@ var ClientImpl = class _ClientImpl {
|
|
|
6915
7530
|
this.schema = schema;
|
|
6916
7531
|
this.options = options;
|
|
6917
7532
|
this.$schema = schema;
|
|
6918
|
-
this.$options = options
|
|
7533
|
+
this.$options = options;
|
|
6919
7534
|
this.$options.functions = {
|
|
6920
7535
|
...functions_exports,
|
|
6921
7536
|
...this.$options.functions
|
|
@@ -7082,7 +7697,7 @@ var ClientImpl = class _ClientImpl {
|
|
|
7082
7697
|
}
|
|
7083
7698
|
$executeRaw(query, ...values) {
|
|
7084
7699
|
return createZenStackPromise(async () => {
|
|
7085
|
-
const result = await
|
|
7700
|
+
const result = await sql10(query, ...values).execute(this.kysely);
|
|
7086
7701
|
return Number(result.numAffectedRows ?? 0);
|
|
7087
7702
|
});
|
|
7088
7703
|
}
|
|
@@ -7095,7 +7710,7 @@ var ClientImpl = class _ClientImpl {
|
|
|
7095
7710
|
}
|
|
7096
7711
|
$queryRaw(query, ...values) {
|
|
7097
7712
|
return createZenStackPromise(async () => {
|
|
7098
|
-
const result = await
|
|
7713
|
+
const result = await sql10(query, ...values).execute(this.kysely);
|
|
7099
7714
|
return result.rows;
|
|
7100
7715
|
});
|
|
7101
7716
|
}
|
|
@@ -7116,7 +7731,7 @@ var ClientImpl = class _ClientImpl {
|
|
|
7116
7731
|
};
|
|
7117
7732
|
function createClientProxy(client) {
|
|
7118
7733
|
const inputValidator = new InputValidator(client.$schema);
|
|
7119
|
-
const resultProcessor = new ResultProcessor(client.$schema);
|
|
7734
|
+
const resultProcessor = new ResultProcessor(client.$schema, client.$options);
|
|
7120
7735
|
return new Proxy(client, {
|
|
7121
7736
|
get: /* @__PURE__ */ __name((target, prop, receiver) => {
|
|
7122
7737
|
if (typeof prop === "string" && prop.startsWith("$")) {
|
|
@@ -7234,7 +7849,7 @@ function definePlugin(plugin) {
|
|
|
7234
7849
|
__name(definePlugin, "definePlugin");
|
|
7235
7850
|
|
|
7236
7851
|
// src/client/index.ts
|
|
7237
|
-
import { sql as
|
|
7852
|
+
import { sql as sql11 } from "kysely";
|
|
7238
7853
|
export {
|
|
7239
7854
|
InputValidationError,
|
|
7240
7855
|
InternalError,
|
|
@@ -7242,6 +7857,6 @@ export {
|
|
|
7242
7857
|
QueryError,
|
|
7243
7858
|
ZenStackClient,
|
|
7244
7859
|
definePlugin,
|
|
7245
|
-
|
|
7860
|
+
sql11 as sql
|
|
7246
7861
|
};
|
|
7247
7862
|
//# sourceMappingURL=index.js.map
|