@vantreeseba/drizzle-graphql 10.0.0 → 10.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -1
- package/dist/index.cjs +382 -247
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +383 -248
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -26,9 +26,9 @@ let drizzle_orm_mysql_core = require("drizzle-orm/mysql-core");
|
|
|
26
26
|
let drizzle_orm_pg_core = require("drizzle-orm/pg-core");
|
|
27
27
|
let drizzle_orm_sqlite_core = require("drizzle-orm/sqlite-core");
|
|
28
28
|
let graphql = require("graphql");
|
|
29
|
+
let graphql_scalars = require("graphql-scalars");
|
|
29
30
|
let pluralize = require("pluralize");
|
|
30
31
|
pluralize = __toESM(pluralize, 1);
|
|
31
|
-
let graphql_scalars = require("graphql-scalars");
|
|
32
32
|
//#region src/util/builders/common/errors.ts
|
|
33
33
|
/**
|
|
34
34
|
* A `GraphQLError` carrying its context as data. The message says what went wrong, in prose
|
|
@@ -115,28 +115,6 @@ const applyErrorMapper = (entities, mapError) => {
|
|
|
115
115
|
for (const tableResolvers of Object.values(entities.fieldResolvers ?? {})) for (const [relationName, resolve] of Object.entries(tableResolvers)) tableResolvers[relationName] = wrap(resolve);
|
|
116
116
|
};
|
|
117
117
|
//#endregion
|
|
118
|
-
//#region src/util/case-ops/index.ts
|
|
119
|
-
const uncapitalize = (input) => input?.length ? `${input[0].toLocaleLowerCase()}${input.length > 1 ? input.slice(1, input.length) : ""}` : input;
|
|
120
|
-
const capitalize = (input) => input?.length ? `${input[0].toLocaleUpperCase()}${input.length > 1 ? input.slice(1, input.length) : ""}` : input;
|
|
121
|
-
/**
|
|
122
|
-
* The `typeNameMapper` preset behind `typeNameMapper: 'singularize'`.
|
|
123
|
-
*
|
|
124
|
-
* Derives the singular and plural forms of a table key with the library's own `pluralize`,
|
|
125
|
-
* which is what nearly every consumer with plural table keys (`export const tasks =
|
|
126
|
-
* pgTable('tasks', …)`) was writing by hand — and adding `pluralize` to their own
|
|
127
|
-
* dependencies to write.
|
|
128
|
-
*
|
|
129
|
-
* `tasks` becomes type `Task`, queries `tasks` / `task`, mutations `createTasks` /
|
|
130
|
-
* `createTask`. A key that is already singular is pluralized for the list side, so `task`
|
|
131
|
-
* yields the same names, and a capitalized key (`Tasks`) yields them too — the field forms
|
|
132
|
-
* are uncapitalized so the generated names follow GraphQL's camelCase convention whatever
|
|
133
|
-
* the schema key looks like. The type name is capitalized by the generator either way.
|
|
134
|
-
*/
|
|
135
|
-
const singularizeMapper = (tableName) => ({
|
|
136
|
-
singular: uncapitalize(pluralize.default.singular(tableName)),
|
|
137
|
-
plural: uncapitalize(pluralize.default.plural(tableName))
|
|
138
|
-
});
|
|
139
|
-
//#endregion
|
|
140
118
|
//#region src/util/scalars/index.ts
|
|
141
119
|
const INVALID_INPUT = "DRIZZLE_INVALID_INPUT_VALUE";
|
|
142
120
|
const UNREPRESENTABLE = "DRIZZLE_UNREPRESENTABLE_VALUE";
|
|
@@ -207,6 +185,28 @@ const GraphQLDecimalString = new graphql.GraphQLScalarType({
|
|
|
207
185
|
}
|
|
208
186
|
});
|
|
209
187
|
//#endregion
|
|
188
|
+
//#region src/util/case-ops/index.ts
|
|
189
|
+
const uncapitalize = (input) => input?.length ? `${input[0].toLocaleLowerCase()}${input.length > 1 ? input.slice(1, input.length) : ""}` : input;
|
|
190
|
+
const capitalize = (input) => input?.length ? `${input[0].toLocaleUpperCase()}${input.length > 1 ? input.slice(1, input.length) : ""}` : input;
|
|
191
|
+
/**
|
|
192
|
+
* The `typeNameMapper` preset behind `typeNameMapper: 'singularize'`.
|
|
193
|
+
*
|
|
194
|
+
* Derives the singular and plural forms of a table key with the library's own `pluralize`,
|
|
195
|
+
* which is what nearly every consumer with plural table keys (`export const tasks =
|
|
196
|
+
* pgTable('tasks', …)`) was writing by hand — and adding `pluralize` to their own
|
|
197
|
+
* dependencies to write.
|
|
198
|
+
*
|
|
199
|
+
* `tasks` becomes type `Task`, queries `tasks` / `task`, mutations `createTasks` /
|
|
200
|
+
* `createTask`. A key that is already singular is pluralized for the list side, so `task`
|
|
201
|
+
* yields the same names, and a capitalized key (`Tasks`) yields them too — the field forms
|
|
202
|
+
* are uncapitalized so the generated names follow GraphQL's camelCase convention whatever
|
|
203
|
+
* the schema key looks like. The type name is capitalized by the generator either way.
|
|
204
|
+
*/
|
|
205
|
+
const singularizeMapper = (tableName) => ({
|
|
206
|
+
singular: uncapitalize(pluralize.default.singular(tableName)),
|
|
207
|
+
plural: uncapitalize(pluralize.default.plural(tableName))
|
|
208
|
+
});
|
|
209
|
+
//#endregion
|
|
210
210
|
//#region src/util/type-converter/index.ts
|
|
211
211
|
const allowedNameChars = /^[a-zA-Z0-9_]+$/;
|
|
212
212
|
/**
|
|
@@ -436,7 +436,7 @@ const normalizeScalarOverride = (override, tableName, columnName) => {
|
|
|
436
436
|
* leaves the column on built-in detection.
|
|
437
437
|
*/
|
|
438
438
|
const registerScalarOverrides = (tables, config) => {
|
|
439
|
-
for (const [tableName, table] of Object.entries(tables)) for (const [columnName, column] of Object.entries((0, drizzle_orm.
|
|
439
|
+
for (const [tableName, table] of Object.entries(tables)) for (const [columnName, column] of Object.entries((0, drizzle_orm.getColumns)(table))) {
|
|
440
440
|
const declared = config.scalars?.[tableName]?.[columnName];
|
|
441
441
|
let resolved;
|
|
442
442
|
if (declared !== void 0) resolved = normalizeScalarOverride(declared, tableName, columnName);
|
|
@@ -475,137 +475,6 @@ const drizzleColumnToGraphQLType = (column, columnName, tableName, forceNullable
|
|
|
475
475
|
return typeDesc;
|
|
476
476
|
};
|
|
477
477
|
//#endregion
|
|
478
|
-
//#region src/util/data-mappers/index.ts
|
|
479
|
-
const isJsonColumn$1 = (column) => (column.dataType ?? "").includes("json") && column.columnType !== "PgGeometryObject";
|
|
480
|
-
const remapToGraphQLCore = (key, value, tableName, column, relationMap) => {
|
|
481
|
-
if (Array.isArray(value)) {
|
|
482
|
-
const relations = relationMap?.[tableName];
|
|
483
|
-
if (relations?.[key]) {
|
|
484
|
-
const rel = relations[key];
|
|
485
|
-
return remapToGraphQLArrayOutput(value, rel.targetTableName, rel.relation?.targetTable ?? rel.relation?.referencedTable, relationMap);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
if (typeof value === "object" && value !== null) {
|
|
489
|
-
const relations = relationMap?.[tableName];
|
|
490
|
-
if (relations?.[key]) {
|
|
491
|
-
const rel = relations[key];
|
|
492
|
-
return remapToGraphQLSingleOutput(value, rel.targetTableName, rel.relation?.targetTable ?? rel.relation?.referencedTable, relationMap);
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
if (!column) return value;
|
|
496
|
-
if (getColumnScalarOverride(column, false)) return value;
|
|
497
|
-
if (isJsonColumn$1(column)) return value;
|
|
498
|
-
if (value instanceof Date) return value.toISOString();
|
|
499
|
-
if (value instanceof Buffer) return Array.from(value);
|
|
500
|
-
if (typeof value === "bigint") return value.toString();
|
|
501
|
-
if (Array.isArray(value)) {
|
|
502
|
-
if (column.columnType === "PgGeometry" || column.columnType === "PgVector") return value;
|
|
503
|
-
return value.map((arrVal) => remapToGraphQLCore(key, arrVal, tableName, column, relationMap));
|
|
504
|
-
}
|
|
505
|
-
if (typeof value === "object" && value !== null) {
|
|
506
|
-
if (column.columnType === "PgGeometryObject") return value;
|
|
507
|
-
return JSON.stringify(value);
|
|
508
|
-
}
|
|
509
|
-
return value;
|
|
510
|
-
};
|
|
511
|
-
const remapToGraphQLSingleOutput = (queryOutput, tableName, table, relationMap) => {
|
|
512
|
-
const columns = (0, drizzle_orm.getTableColumns)(table);
|
|
513
|
-
for (const [key, value] of Object.entries(queryOutput)) {
|
|
514
|
-
if (value === void 0 || value === null) {
|
|
515
|
-
const relEntry = value === null ? relationMap?.[tableName]?.[key] : void 0;
|
|
516
|
-
if (relEntry && (0, drizzle_orm.is)(relEntry.relation, drizzle_orm.One)) {
|
|
517
|
-
queryOutput[key] = null;
|
|
518
|
-
continue;
|
|
519
|
-
}
|
|
520
|
-
delete queryOutput[key];
|
|
521
|
-
continue;
|
|
522
|
-
}
|
|
523
|
-
const column = columns[key];
|
|
524
|
-
if (value === 0n && column && column.columnType === "SQLiteBigInt" && !column.notNull) {
|
|
525
|
-
delete queryOutput[key];
|
|
526
|
-
continue;
|
|
527
|
-
}
|
|
528
|
-
queryOutput[key] = remapToGraphQLCore(key, value, tableName, column, relationMap);
|
|
529
|
-
}
|
|
530
|
-
return queryOutput;
|
|
531
|
-
};
|
|
532
|
-
const remapToGraphQLArrayOutput = (queryOutput, tableName, table, relationMap) => {
|
|
533
|
-
for (const entry of queryOutput) remapToGraphQLSingleOutput(entry, tableName, table, relationMap);
|
|
534
|
-
return queryOutput;
|
|
535
|
-
};
|
|
536
|
-
const remapFromGraphQLCore = (value, column, columnName) => {
|
|
537
|
-
if (getColumnScalarOverride(column, true)) {
|
|
538
|
-
if (typeof value === "object" && value !== null && Object.getPrototypeOf(value) === null) return Object.assign({}, value);
|
|
539
|
-
return value;
|
|
540
|
-
}
|
|
541
|
-
const dataType = column.dataType ?? "";
|
|
542
|
-
const columnType = column.columnType ?? "";
|
|
543
|
-
if (columnType === "SQLiteTimestamp" || columnType === "SQLiteTimestampMs" || columnType === "MySqlTimestamp" || columnType === "MySqlDateTime" || columnType === "PgTimestamp" || columnType === "PgTimestampString") {
|
|
544
|
-
const formatted = new Date(value);
|
|
545
|
-
if (Number.isNaN(formatted.getTime())) throw drizzleError(`Field '${columnName}' is not a valid date!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
546
|
-
return formatted;
|
|
547
|
-
}
|
|
548
|
-
if ((columnType === "MySqlDate" || columnType === "PgDate") && typeof value === "string") {
|
|
549
|
-
const dateOnly = value.includes("T") ? value.split("T")[0] : value;
|
|
550
|
-
const check = new Date(dateOnly);
|
|
551
|
-
if (Number.isNaN(check.getTime())) throw drizzleError(`Field '${columnName}' is not a valid date!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
552
|
-
return dateOnly;
|
|
553
|
-
}
|
|
554
|
-
if (dataType.includes("bigint")) try {
|
|
555
|
-
return BigInt(value);
|
|
556
|
-
} catch {
|
|
557
|
-
throw drizzleError(`Field '${columnName}' is not a BigInt!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
558
|
-
}
|
|
559
|
-
if (dataType.includes("json") && column.columnType !== "PgGeometryObject") return value;
|
|
560
|
-
switch (dataType) {
|
|
561
|
-
case "date": {
|
|
562
|
-
const formatted = new Date(value);
|
|
563
|
-
if (Number.isNaN(formatted.getTime())) throw drizzleError(`Field '${columnName}' is not a valid date!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
564
|
-
return formatted;
|
|
565
|
-
}
|
|
566
|
-
case "buffer":
|
|
567
|
-
if (!Array.isArray(value)) throw drizzleError(`Field '${columnName}' is not an array!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
568
|
-
return Buffer.from(value);
|
|
569
|
-
case "json":
|
|
570
|
-
if (column.columnType === "PgGeometryObject") return value;
|
|
571
|
-
try {
|
|
572
|
-
return JSON.parse(value);
|
|
573
|
-
} catch (e) {
|
|
574
|
-
throw drizzleError(`Invalid JSON in field '${columnName}':\n${e instanceof Error ? e.message : "Unknown error"}`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
575
|
-
}
|
|
576
|
-
case "array":
|
|
577
|
-
if (!Array.isArray(value)) throw drizzleError(`Field '${columnName}' is not an array!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
578
|
-
if (column.columnType === "PgGeometry" && value.length !== 2) throw drizzleError(`Invalid float tuple in field '${columnName}': expected array with length of 2, received ${value.length}`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
579
|
-
return value;
|
|
580
|
-
case "bigint": try {
|
|
581
|
-
return BigInt(value);
|
|
582
|
-
} catch (_error) {
|
|
583
|
-
throw drizzleError(`Field '${columnName}' is not a BigInt!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
584
|
-
}
|
|
585
|
-
default:
|
|
586
|
-
if (typeof value === "object" && value !== null && Object.getPrototypeOf(value) === null) return Object.assign({}, value);
|
|
587
|
-
return value;
|
|
588
|
-
}
|
|
589
|
-
};
|
|
590
|
-
const remapFromGraphQLSingleInput = (queryInput, table, operation = "insert") => {
|
|
591
|
-
for (const [key, value] of Object.entries(queryInput)) if (value === void 0) delete queryInput[key];
|
|
592
|
-
else {
|
|
593
|
-
const column = (0, drizzle_orm.getTableColumns)(table)[key];
|
|
594
|
-
if (!column) throw drizzleError(`Unknown column: ${key}`, { code: "DRIZZLE_UNKNOWN_COLUMN" });
|
|
595
|
-
if (value === null && column.notNull) {
|
|
596
|
-
if (operation === "update") throw drizzleError(`Column '${key}' cannot be set to null.`, { code: "DRIZZLE_NOT_NULL" });
|
|
597
|
-
delete queryInput[key];
|
|
598
|
-
continue;
|
|
599
|
-
}
|
|
600
|
-
queryInput[key] = remapFromGraphQLCore(value, column, key);
|
|
601
|
-
}
|
|
602
|
-
return queryInput;
|
|
603
|
-
};
|
|
604
|
-
const remapFromGraphQLArrayInput = (queryInput, table, operation = "insert") => {
|
|
605
|
-
for (const entry of queryInput) remapFromGraphQLSingleInput(entry, table, operation);
|
|
606
|
-
return queryInput;
|
|
607
|
-
};
|
|
608
|
-
//#endregion
|
|
609
478
|
//#region src/util/builders/common/type-names.ts
|
|
610
479
|
/**
|
|
611
480
|
* The build's naming rule: the mapper's answer where it has one, otherwise the default name
|
|
@@ -648,6 +517,23 @@ const columnDialect = (column) => {
|
|
|
648
517
|
if (ct.startsWith("SQLite")) return "sqlite";
|
|
649
518
|
};
|
|
650
519
|
/**
|
|
520
|
+
* Whether the column stores JSON.
|
|
521
|
+
*
|
|
522
|
+
* Decided on drizzle's `dataType`, its semantic marker, rather than on `columnType`, which
|
|
523
|
+
* names the implementing class. drizzle-orm v1 uses compound dataType strings (`"object
|
|
524
|
+
* json"` for every json/jsonb column across the three dialects), hence the inclusion test.
|
|
525
|
+
*
|
|
526
|
+
* `PgGeometryObject` is excluded explicitly. That is currently redundant — it reports
|
|
527
|
+
* `"object geometry"` — but it is stored as json, has its own object type in the generated
|
|
528
|
+
* schema, and must not be reclassified if drizzle ever spells its dataType differently.
|
|
529
|
+
*
|
|
530
|
+
* One predicate for two callers that must agree: the filter builder, whose `path` and
|
|
531
|
+
* `contains` operators mean structural JSON containment rather than the string operators,
|
|
532
|
+
* and the data mappers, which transport a JSON value as-is instead of remapping it. They
|
|
533
|
+
* previously had a copy each, asking different questions of the column.
|
|
534
|
+
*/
|
|
535
|
+
const isJsonColumn = (column) => (column.dataType ?? "").includes("json") && column.columnType !== "PgGeometryObject";
|
|
536
|
+
/**
|
|
651
537
|
* Maps a Drizzle column to the generic filter type to use.
|
|
652
538
|
*
|
|
653
539
|
* Selection is keyed on the column's data type, never its name: a text column named
|
|
@@ -1078,6 +964,137 @@ const generateColumnFilterValues = (column, tableName, columnName, cacheCtx) =>
|
|
|
1078
964
|
return mainType;
|
|
1079
965
|
};
|
|
1080
966
|
//#endregion
|
|
967
|
+
//#region src/util/data-mappers/index.ts
|
|
968
|
+
const remapToGraphQLCore = (key, value, tableName, column, relationMap) => {
|
|
969
|
+
if (Array.isArray(value)) {
|
|
970
|
+
const relations = relationMap?.[tableName];
|
|
971
|
+
if (relations?.[key]) {
|
|
972
|
+
const rel = relations[key];
|
|
973
|
+
return remapToGraphQLArrayOutput(value, rel.targetTableName, rel.relation?.targetTable ?? rel.relation?.referencedTable, relationMap);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
if (typeof value === "object" && value !== null) {
|
|
977
|
+
const relations = relationMap?.[tableName];
|
|
978
|
+
if (relations?.[key]) {
|
|
979
|
+
const rel = relations[key];
|
|
980
|
+
return remapToGraphQLSingleOutput(value, rel.targetTableName, rel.relation?.targetTable ?? rel.relation?.referencedTable, relationMap);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
if (!column) return value;
|
|
984
|
+
if (getColumnScalarOverride(column, false)) return value;
|
|
985
|
+
if (isJsonColumn(column)) return value;
|
|
986
|
+
if (value instanceof Date) return value.toISOString();
|
|
987
|
+
if (value instanceof Buffer) return Array.from(value);
|
|
988
|
+
if (typeof value === "bigint") return value.toString();
|
|
989
|
+
if (Array.isArray(value)) {
|
|
990
|
+
if (column.columnType === "PgGeometry" || column.columnType === "PgVector") return value;
|
|
991
|
+
return value.map((arrVal) => remapToGraphQLCore(key, arrVal, tableName, column, relationMap));
|
|
992
|
+
}
|
|
993
|
+
if (typeof value === "object" && value !== null) {
|
|
994
|
+
if (column.columnType === "PgGeometryObject") return value;
|
|
995
|
+
return JSON.stringify(value);
|
|
996
|
+
}
|
|
997
|
+
return value;
|
|
998
|
+
};
|
|
999
|
+
const remapToGraphQLSingleOutput = (queryOutput, tableName, table, relationMap) => {
|
|
1000
|
+
const columns = (0, drizzle_orm.getColumns)(table);
|
|
1001
|
+
for (const [key, value] of Object.entries(queryOutput)) {
|
|
1002
|
+
if (value === void 0 || value === null) {
|
|
1003
|
+
const relEntry = value === null ? relationMap?.[tableName]?.[key] : void 0;
|
|
1004
|
+
if (relEntry && (0, drizzle_orm.is)(relEntry.relation, drizzle_orm.One)) {
|
|
1005
|
+
queryOutput[key] = null;
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
delete queryOutput[key];
|
|
1009
|
+
continue;
|
|
1010
|
+
}
|
|
1011
|
+
const column = columns[key];
|
|
1012
|
+
if (value === 0n && column && column.columnType === "SQLiteBigInt" && !column.notNull) {
|
|
1013
|
+
delete queryOutput[key];
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
queryOutput[key] = remapToGraphQLCore(key, value, tableName, column, relationMap);
|
|
1017
|
+
}
|
|
1018
|
+
return queryOutput;
|
|
1019
|
+
};
|
|
1020
|
+
const remapToGraphQLArrayOutput = (queryOutput, tableName, table, relationMap) => {
|
|
1021
|
+
for (const entry of queryOutput) remapToGraphQLSingleOutput(entry, tableName, table, relationMap);
|
|
1022
|
+
return queryOutput;
|
|
1023
|
+
};
|
|
1024
|
+
const remapFromGraphQLCore = (value, column, columnName) => {
|
|
1025
|
+
if (getColumnScalarOverride(column, true)) {
|
|
1026
|
+
if (typeof value === "object" && value !== null && Object.getPrototypeOf(value) === null) return Object.assign({}, value);
|
|
1027
|
+
return value;
|
|
1028
|
+
}
|
|
1029
|
+
const dataType = column.dataType ?? "";
|
|
1030
|
+
const columnType = column.columnType ?? "";
|
|
1031
|
+
if (columnType === "SQLiteTimestamp" || columnType === "SQLiteTimestampMs" || columnType === "MySqlTimestamp" || columnType === "MySqlDateTime" || columnType === "PgTimestamp" || columnType === "PgTimestampString") {
|
|
1032
|
+
const formatted = new Date(value);
|
|
1033
|
+
if (Number.isNaN(formatted.getTime())) throw drizzleError(`Field '${columnName}' is not a valid date!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
1034
|
+
return formatted;
|
|
1035
|
+
}
|
|
1036
|
+
if ((columnType === "MySqlDate" || columnType === "PgDate") && typeof value === "string") {
|
|
1037
|
+
const dateOnly = value.includes("T") ? value.split("T")[0] : value;
|
|
1038
|
+
const check = new Date(dateOnly);
|
|
1039
|
+
if (Number.isNaN(check.getTime())) throw drizzleError(`Field '${columnName}' is not a valid date!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
1040
|
+
return dateOnly;
|
|
1041
|
+
}
|
|
1042
|
+
if (dataType.includes("bigint")) try {
|
|
1043
|
+
return BigInt(value);
|
|
1044
|
+
} catch {
|
|
1045
|
+
throw drizzleError(`Field '${columnName}' is not a BigInt!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
1046
|
+
}
|
|
1047
|
+
if (dataType.includes("json") && column.columnType !== "PgGeometryObject") return value;
|
|
1048
|
+
switch (dataType) {
|
|
1049
|
+
case "date": {
|
|
1050
|
+
const formatted = new Date(value);
|
|
1051
|
+
if (Number.isNaN(formatted.getTime())) throw drizzleError(`Field '${columnName}' is not a valid date!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
1052
|
+
return formatted;
|
|
1053
|
+
}
|
|
1054
|
+
case "buffer":
|
|
1055
|
+
if (!Array.isArray(value)) throw drizzleError(`Field '${columnName}' is not an array!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
1056
|
+
return Buffer.from(value);
|
|
1057
|
+
case "json":
|
|
1058
|
+
if (column.columnType === "PgGeometryObject") return value;
|
|
1059
|
+
try {
|
|
1060
|
+
return JSON.parse(value);
|
|
1061
|
+
} catch (e) {
|
|
1062
|
+
throw drizzleError(`Invalid JSON in field '${columnName}':\n${e instanceof Error ? e.message : "Unknown error"}`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
1063
|
+
}
|
|
1064
|
+
case "array":
|
|
1065
|
+
if (!Array.isArray(value)) throw drizzleError(`Field '${columnName}' is not an array!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
1066
|
+
if (column.columnType === "PgGeometry" && value.length !== 2) throw drizzleError(`Invalid float tuple in field '${columnName}': expected array with length of 2, received ${value.length}`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
1067
|
+
return value;
|
|
1068
|
+
case "bigint": try {
|
|
1069
|
+
return BigInt(value);
|
|
1070
|
+
} catch (_error) {
|
|
1071
|
+
throw drizzleError(`Field '${columnName}' is not a BigInt!`, { code: "DRIZZLE_INVALID_INPUT_VALUE" });
|
|
1072
|
+
}
|
|
1073
|
+
default:
|
|
1074
|
+
if (typeof value === "object" && value !== null && Object.getPrototypeOf(value) === null) return Object.assign({}, value);
|
|
1075
|
+
return value;
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
const remapFromGraphQLSingleInput = (queryInput, table, operation = "insert") => {
|
|
1079
|
+
const columns = (0, drizzle_orm.getColumns)(table);
|
|
1080
|
+
for (const [key, value] of Object.entries(queryInput)) if (value === void 0) delete queryInput[key];
|
|
1081
|
+
else {
|
|
1082
|
+
const column = columns[key];
|
|
1083
|
+
if (!column) throw drizzleError(`Unknown column: ${key}`, { code: "DRIZZLE_UNKNOWN_COLUMN" });
|
|
1084
|
+
if (value === null && column.notNull) {
|
|
1085
|
+
if (operation === "update") throw drizzleError(`Column '${key}' cannot be set to null.`, { code: "DRIZZLE_NOT_NULL" });
|
|
1086
|
+
delete queryInput[key];
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
queryInput[key] = remapFromGraphQLCore(value, column, key);
|
|
1090
|
+
}
|
|
1091
|
+
return queryInput;
|
|
1092
|
+
};
|
|
1093
|
+
const remapFromGraphQLArrayInput = (queryInput, table, operation = "insert") => {
|
|
1094
|
+
for (const entry of queryInput) remapFromGraphQLSingleInput(entry, table, operation);
|
|
1095
|
+
return queryInput;
|
|
1096
|
+
};
|
|
1097
|
+
//#endregion
|
|
1081
1098
|
//#region src/util/builders/common/filters.ts
|
|
1082
1099
|
/**
|
|
1083
1100
|
* Escape character pinned via `ESCAPE` on every generated safe-LIKE predicate. Bound as a
|
|
@@ -1166,11 +1183,6 @@ const lowerMembership = (column, values, negated) => {
|
|
|
1166
1183
|
return negated ? drizzle_orm.sql`lower(${column}) not in (${lowered})` : drizzle_orm.sql`lower(${column}) in (${lowered})`;
|
|
1167
1184
|
};
|
|
1168
1185
|
/**
|
|
1169
|
-
* Whether the column stores JSON — its `contains` operator is structural containment,
|
|
1170
|
-
* not the safe substring operator string columns get.
|
|
1171
|
-
*/
|
|
1172
|
-
const isJsonColumn = (column) => (column.columnType ?? "").includes("Json");
|
|
1173
|
-
/**
|
|
1174
1186
|
* Structural JSON containment for the `contains` operator on json/jsonb columns.
|
|
1175
1187
|
* The value is serialized and bound as a parameter (never interpolated into the SQL text):
|
|
1176
1188
|
* - Postgres: `col @> $1::jsonb` (a plain `json` column is cast through jsonb — `json` has
|
|
@@ -1309,36 +1321,37 @@ const jsonPathCondition = (column, columnName, filter) => {
|
|
|
1309
1321
|
}
|
|
1310
1322
|
return variants.length ? variants.length > 1 ? (0, drizzle_orm.and)(...variants) : variants[0] : void 0;
|
|
1311
1323
|
};
|
|
1324
|
+
const singleValueOps = {
|
|
1325
|
+
eq: drizzle_orm.eq,
|
|
1326
|
+
ne: drizzle_orm.ne,
|
|
1327
|
+
gt: drizzle_orm.gt,
|
|
1328
|
+
gte: drizzle_orm.gte,
|
|
1329
|
+
lt: drizzle_orm.lt,
|
|
1330
|
+
lte: drizzle_orm.lte
|
|
1331
|
+
};
|
|
1332
|
+
const stringValueOps = {
|
|
1333
|
+
like: drizzle_orm.like,
|
|
1334
|
+
notLike: drizzle_orm.notLike,
|
|
1335
|
+
ilike: drizzle_orm.ilike,
|
|
1336
|
+
notIlike: drizzle_orm.notIlike
|
|
1337
|
+
};
|
|
1338
|
+
const arrayValueOps = {
|
|
1339
|
+
inArray: drizzle_orm.inArray,
|
|
1340
|
+
notInArray: drizzle_orm.notInArray
|
|
1341
|
+
};
|
|
1342
|
+
/** Membership operators for array columns: element list → SQL. Empty lists are rejected below. */
|
|
1343
|
+
const arrayMembershipOps = {
|
|
1344
|
+
hasSome: drizzle_orm.arrayOverlaps,
|
|
1345
|
+
hasEvery: drizzle_orm.arrayContains
|
|
1346
|
+
};
|
|
1347
|
+
const nullableOps = {
|
|
1348
|
+
isNull: drizzle_orm.isNull,
|
|
1349
|
+
isNotNull: drizzle_orm.isNotNull
|
|
1350
|
+
};
|
|
1312
1351
|
const extractFiltersColumn = (column, columnName, operators) => {
|
|
1313
1352
|
const { OR, AND, NOT, insensitive, ...restOperators } = operators;
|
|
1314
1353
|
const foldCase = insensitive === true;
|
|
1315
1354
|
const entries = Object.entries(restOperators);
|
|
1316
|
-
const singleValueOps = {
|
|
1317
|
-
eq: drizzle_orm.eq,
|
|
1318
|
-
ne: drizzle_orm.ne,
|
|
1319
|
-
gt: drizzle_orm.gt,
|
|
1320
|
-
gte: drizzle_orm.gte,
|
|
1321
|
-
lt: drizzle_orm.lt,
|
|
1322
|
-
lte: drizzle_orm.lte
|
|
1323
|
-
};
|
|
1324
|
-
const stringValueOps = {
|
|
1325
|
-
like: drizzle_orm.like,
|
|
1326
|
-
notLike: drizzle_orm.notLike,
|
|
1327
|
-
ilike: drizzle_orm.ilike,
|
|
1328
|
-
notIlike: drizzle_orm.notIlike
|
|
1329
|
-
};
|
|
1330
|
-
const arrayValueOps = {
|
|
1331
|
-
inArray: drizzle_orm.inArray,
|
|
1332
|
-
notInArray: drizzle_orm.notInArray
|
|
1333
|
-
};
|
|
1334
|
-
const arrayMembershipOps = {
|
|
1335
|
-
hasSome: drizzle_orm.arrayOverlaps,
|
|
1336
|
-
hasEvery: drizzle_orm.arrayContains
|
|
1337
|
-
};
|
|
1338
|
-
const nullableOps = {
|
|
1339
|
-
isNull: drizzle_orm.isNull,
|
|
1340
|
-
isNotNull: drizzle_orm.isNotNull
|
|
1341
|
-
};
|
|
1342
1355
|
const variants = [];
|
|
1343
1356
|
for (const [operatorName, operatorValue] of entries) {
|
|
1344
1357
|
if (operatorValue === null || operatorValue === false) continue;
|
|
@@ -1524,11 +1537,44 @@ const extractUniqueKeyFilter = (table, tableName, fieldName, members, value) =>
|
|
|
1524
1537
|
};
|
|
1525
1538
|
//#endregion
|
|
1526
1539
|
//#region src/util/builders/common/relation-filters.ts
|
|
1527
|
-
/**
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1540
|
+
/**
|
|
1541
|
+
* The narrowed contexts already handed out, keyed by the build-scoped base and then by
|
|
1542
|
+
* table. The base is created once per `buildSchema` and the result depends on nothing else,
|
|
1543
|
+
* so every call for one table produces the same value — a `WeakMap` on the base keeps them
|
|
1544
|
+
* for exactly as long as that build is reachable.
|
|
1545
|
+
*/
|
|
1546
|
+
const narrowedCtxCache = /* @__PURE__ */ new WeakMap();
|
|
1547
|
+
/**
|
|
1548
|
+
* Narrows the build-scoped relation filter context to the table a resolver is filtering.
|
|
1549
|
+
*
|
|
1550
|
+
* Cached rather than rebuilt: the base carries the build's whole `tables` and `relationMap`,
|
|
1551
|
+
* and the spread copied both on every call — three per parent row in the lazy relation
|
|
1552
|
+
* resolver, one per parent row in the relation aggregate, and up to four per select, several
|
|
1553
|
+
* of them inside callbacks drizzle invokes lazily.
|
|
1554
|
+
*
|
|
1555
|
+
* The one field written after the fact is `aliases`, the counter that keeps subquery aliases
|
|
1556
|
+
* apart ({@link extractRelationFilter}). Sharing it is what makes the caching safe rather
|
|
1557
|
+
* than a hazard: the counter now spans the process instead of restarting per extraction, and
|
|
1558
|
+
* aliases only ever have to be unique *within* one statement, which a number that never
|
|
1559
|
+
* repeats gives more surely than one that restarts.
|
|
1560
|
+
*/
|
|
1561
|
+
const relationFilterCtx = (base, tableKey) => {
|
|
1562
|
+
if (!base) return;
|
|
1563
|
+
let byTable = narrowedCtxCache.get(base);
|
|
1564
|
+
if (!byTable) {
|
|
1565
|
+
byTable = /* @__PURE__ */ new Map();
|
|
1566
|
+
narrowedCtxCache.set(base, byTable);
|
|
1567
|
+
}
|
|
1568
|
+
let ctx = byTable.get(tableKey);
|
|
1569
|
+
if (!ctx) {
|
|
1570
|
+
ctx = {
|
|
1571
|
+
...base,
|
|
1572
|
+
tableKey
|
|
1573
|
+
};
|
|
1574
|
+
byTable.set(tableKey, ctx);
|
|
1575
|
+
}
|
|
1576
|
+
return ctx;
|
|
1577
|
+
};
|
|
1532
1578
|
/**
|
|
1533
1579
|
* Correlates the parent row with the aliased target table using the relation's own join
|
|
1534
1580
|
* columns. Columns are matched by SQL name rather than object identity so this also works when
|
|
@@ -2169,7 +2215,7 @@ const isFilterableRelation = (relation) => !relation.through;
|
|
|
2169
2215
|
* (an object without a `direction`) is rejected with a clear error instead of being
|
|
2170
2216
|
* silently dropped.
|
|
2171
2217
|
*/
|
|
2172
|
-
const orderByEntries = (orderArgs) => Object.entries(orderArgs).sort((a, b) => (b[1]?.priority ?? 0) - (a[1]?.priority ?? 0)).
|
|
2218
|
+
const orderByEntries = (orderArgs) => Object.entries(orderArgs).filter(([, config]) => config).sort((a, b) => (b[1]?.priority ?? 0) - (a[1]?.priority ?? 0)).map(([column, config]) => {
|
|
2173
2219
|
if (typeof config === "object" && config.direction === void 0) throw drizzleError(`ORDER BY ${column}: ordering through a relation is not supported in this query`, { code: "DRIZZLE_INVALID_ORDER_BY" });
|
|
2174
2220
|
if (config.matchFilterOrder) throw drizzleError(`ORDER BY ${column}: 'matchFilterOrder' is not supported in this query`, { code: "DRIZZLE_INVALID_ORDER_BY" });
|
|
2175
2221
|
return [
|
|
@@ -2355,16 +2401,20 @@ const decodeCursorValue = (value) => {
|
|
|
2355
2401
|
return value;
|
|
2356
2402
|
};
|
|
2357
2403
|
/**
|
|
2358
|
-
*
|
|
2359
|
-
*
|
|
2360
|
-
*
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2404
|
+
* `encodeCursor` bound to one ordering, for encoding a whole page.
|
|
2405
|
+
*
|
|
2406
|
+
* The ordering spec is the same for every row of a page, so it is serialized once here and
|
|
2407
|
+
* spliced into each row's payload rather than re-serialized per row. The bytes are identical
|
|
2408
|
+
* to what building the payload object and stringifying it produces — same keys, same order —
|
|
2409
|
+
* so cursors stay interchangeable with ones issued before this was split out.
|
|
2410
|
+
*/
|
|
2411
|
+
const cursorEncoder = (entries) => {
|
|
2412
|
+
const spec = JSON.stringify(entries);
|
|
2413
|
+
const columns = entries.map(([column]) => column);
|
|
2414
|
+
return (row) => {
|
|
2415
|
+
const values = JSON.stringify(columns.map((column) => encodeCursorValue(row[column] ?? null)));
|
|
2416
|
+
return Buffer.from(`{"o":${spec},"v":${values}}`, "utf8").toString("base64url");
|
|
2366
2417
|
};
|
|
2367
|
-
return Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
2368
2418
|
};
|
|
2369
2419
|
/**
|
|
2370
2420
|
* Decodes an `after` cursor and validates it against the ordering the current request pages
|
|
@@ -2454,7 +2504,8 @@ const selectsCursorField = (info, table) => {
|
|
|
2454
2504
|
* dates and bigints into their transport forms.
|
|
2455
2505
|
*/
|
|
2456
2506
|
const attachRowCursors = (rows, entries) => {
|
|
2457
|
-
|
|
2507
|
+
const encode = cursorEncoder(entries);
|
|
2508
|
+
for (const row of rows) row[ROW_CURSOR_PROP] = encode(row);
|
|
2458
2509
|
};
|
|
2459
2510
|
/** Resolver for the `cursor` meta field: reads the value a list resolver attached, if any. */
|
|
2460
2511
|
const rowCursorResolver = (source) => source?.[ROW_CURSOR_PROP] ?? null;
|
|
@@ -2498,6 +2549,31 @@ const withPrimaryKeyColumns = (columns, table, pkNames) => {
|
|
|
2498
2549
|
return columns;
|
|
2499
2550
|
};
|
|
2500
2551
|
/**
|
|
2552
|
+
* Wraps a dialect's `getTableConfig` in a per-table cache.
|
|
2553
|
+
*
|
|
2554
|
+
* The dialect implementations are not lookups: each call re-runs the table's
|
|
2555
|
+
* `extraConfigBuilder` and rebuilds every index, check, unique constraint, primary key and
|
|
2556
|
+
* foreign key it declares. Nothing about a table changes after it is defined, so the result
|
|
2557
|
+
* is the same every time — but a build asks for it many times per table (primary-key names,
|
|
2558
|
+
* unique column sets, conflict targets, one per relation that keys off the target's PK), and
|
|
2559
|
+
* an overriding resolver asks again at request time.
|
|
2560
|
+
*
|
|
2561
|
+
* The cache is a `WeakMap` on the table object, so it costs nothing once the table is gone.
|
|
2562
|
+
* Bind it once per dialect module, at module scope, so the cache outlives a single
|
|
2563
|
+
* `buildSchema` call.
|
|
2564
|
+
*/
|
|
2565
|
+
const memoizeTableConfig = (getTableConfig) => {
|
|
2566
|
+
const cache = /* @__PURE__ */ new WeakMap();
|
|
2567
|
+
return (table) => {
|
|
2568
|
+
let config = cache.get(table);
|
|
2569
|
+
if (config === void 0) {
|
|
2570
|
+
config = getTableConfig(table);
|
|
2571
|
+
cache.set(table, config);
|
|
2572
|
+
}
|
|
2573
|
+
return config;
|
|
2574
|
+
};
|
|
2575
|
+
};
|
|
2576
|
+
/**
|
|
2501
2577
|
* Resolves a table's primary-key property names using a dialect's `getTableConfig` to
|
|
2502
2578
|
* surface table-level composite keys (whose member columns aren't flagged `.primary`).
|
|
2503
2579
|
* Each dialect builder binds this with its own getTableConfig and reuses the binding for
|
|
@@ -3054,48 +3130,55 @@ const relationJoinColumns = (tree, table, selectionCtx) => {
|
|
|
3054
3130
|
}
|
|
3055
3131
|
return needed;
|
|
3056
3132
|
};
|
|
3057
|
-
|
|
3133
|
+
/**
|
|
3134
|
+
* The column the extractors fall back to when a selection names none of its own — a query that
|
|
3135
|
+
* asked only for `__typename`, or only for relation fields. The relational query builder still
|
|
3136
|
+
* needs at least one column, and some SQLite column types crash it when they are the only one
|
|
3137
|
+
* selected, so a column of any other type is preferred and the first column is the last resort.
|
|
3138
|
+
*/
|
|
3139
|
+
const fallbackColumnName = (tableColumns) => {
|
|
3140
|
+
const columnKeys = Object.entries(tableColumns);
|
|
3141
|
+
return columnKeys.find(([, column]) => !rqbCrashTypes.includes(column.columnType))?.[0] ?? columnKeys[0][0];
|
|
3142
|
+
};
|
|
3143
|
+
/**
|
|
3144
|
+
* Property names of the columns a selection reads: the ones it names itself, plus the join
|
|
3145
|
+
* columns its relation fields correlate on, plus the fallback when that leaves nothing.
|
|
3146
|
+
*/
|
|
3147
|
+
const selectedColumnNames = (tree, table, selectionCtx) => {
|
|
3058
3148
|
const tableColumns = (0, drizzle_orm.getColumns)(table);
|
|
3059
|
-
const
|
|
3060
|
-
const
|
|
3061
|
-
for (const [_fieldName, fieldData] of treeEntries) {
|
|
3149
|
+
const names = [];
|
|
3150
|
+
for (const fieldData of Object.values(tree)) {
|
|
3062
3151
|
if (!tableColumns[fieldData.name]) continue;
|
|
3063
|
-
|
|
3064
|
-
}
|
|
3065
|
-
for (const columnName of relationJoinColumns(tree, table, selectionCtx)) selectedColumns.push([columnName, true]);
|
|
3066
|
-
if (!selectedColumns.length) {
|
|
3067
|
-
const columnKeys = Object.entries(tableColumns);
|
|
3068
|
-
const columnName = columnKeys.find((e) => rqbCrashTypes.find((haram) => e[1].columnType !== haram))?.[0] ?? columnKeys[0][0];
|
|
3069
|
-
selectedColumns.push([columnName, true]);
|
|
3152
|
+
names.push(fieldData.name);
|
|
3070
3153
|
}
|
|
3154
|
+
names.push(...relationJoinColumns(tree, table, selectionCtx));
|
|
3155
|
+
if (!names.length) names.push(fallbackColumnName(tableColumns));
|
|
3156
|
+
return names;
|
|
3157
|
+
};
|
|
3158
|
+
/** The requested columns as the relational query builder's `columns` map. */
|
|
3159
|
+
const extractSelectedColumnsFromTree = (tree, table, selectionCtx) => {
|
|
3160
|
+
const selectedColumns = selectedColumnNames(tree, table, selectionCtx).map((columnName) => [columnName, true]);
|
|
3071
3161
|
return Object.fromEntries(selectedColumns);
|
|
3072
3162
|
};
|
|
3073
3163
|
/**
|
|
3164
|
+
* The same columns as a SQL select list.
|
|
3165
|
+
*
|
|
3074
3166
|
* Can't automatically determine column type on type level
|
|
3075
3167
|
* Since drizzle table types extend eachother
|
|
3076
3168
|
*/
|
|
3077
3169
|
const extractSelectedColumnsFromTreeSQLFormat = (tree, table, selectionCtx) => {
|
|
3078
3170
|
const tableColumns = (0, drizzle_orm.getColumns)(table);
|
|
3079
|
-
const
|
|
3080
|
-
const selectedColumns = [];
|
|
3081
|
-
for (const [_fieldName, fieldData] of treeEntries) {
|
|
3082
|
-
if (!tableColumns[fieldData.name]) continue;
|
|
3083
|
-
selectedColumns.push([fieldData.name, tableColumns[fieldData.name]]);
|
|
3084
|
-
}
|
|
3085
|
-
for (const columnName of relationJoinColumns(tree, table, selectionCtx)) selectedColumns.push([columnName, tableColumns[columnName]]);
|
|
3086
|
-
if (!selectedColumns.length) {
|
|
3087
|
-
const columnKeys = Object.entries(tableColumns);
|
|
3088
|
-
const columnName = columnKeys.find((e) => rqbCrashTypes.find((haram) => e[1].columnType !== haram))?.[0] ?? columnKeys[0][0];
|
|
3089
|
-
selectedColumns.push([columnName, tableColumns[columnName]]);
|
|
3090
|
-
}
|
|
3171
|
+
const selectedColumns = selectedColumnNames(tree, table, selectionCtx).map((columnName) => [columnName, tableColumns[columnName]]);
|
|
3091
3172
|
return Object.fromEntries(selectedColumns);
|
|
3092
3173
|
};
|
|
3093
3174
|
//#endregion
|
|
3094
3175
|
//#region src/util/builders/common/relation-params.ts
|
|
3095
|
-
const
|
|
3176
|
+
const extractRelationsParams = (relationMap, tables, tableName, info, typeName, options = {}) => {
|
|
3177
|
+
const { typeNameMapper, filterCtx, limits, scope, defaultOrderBy, resolveName } = options;
|
|
3178
|
+
if (!info) return;
|
|
3096
3179
|
const relationsForTable = relationMap[tableName];
|
|
3097
3180
|
if (!relationsForTable) return;
|
|
3098
|
-
const baseField = Object.entries(
|
|
3181
|
+
const baseField = Object.entries(info.fieldsByTypeName).find(([key, _value]) => key === typeName)?.[1];
|
|
3099
3182
|
if (!baseField) return;
|
|
3100
3183
|
const args = {};
|
|
3101
3184
|
for (const [relName, relEntry] of Object.entries(relationsForTable)) {
|
|
@@ -3132,15 +3215,11 @@ const extractRelationsParamsInner = (relationMap, tables, tableName, typeName, o
|
|
|
3132
3215
|
thisRecord.orderBy = relationArgs?.orderBy ? (aliasedTable) => extractOrderBy(aliasedTable, relationArgs.orderBy, relationFilterCtx(filterCtx, targetTableName), relWhere) : hasPagination && pkNames.length ? (aliasedTable) => primaryKeyOrderExprs(aliasedTable, pkNames) : void 0;
|
|
3133
3216
|
thisRecord.offset = offset;
|
|
3134
3217
|
thisRecord.limit = limit;
|
|
3135
|
-
thisRecord.with =
|
|
3218
|
+
thisRecord.with = extractRelationsParams(relationMap, tables, targetTableName, relationField, relTypeName, options);
|
|
3136
3219
|
args[relName] = thisRecord;
|
|
3137
3220
|
}
|
|
3138
3221
|
return args;
|
|
3139
3222
|
};
|
|
3140
|
-
const extractRelationsParams = (relationMap, tables, tableName, info, typeName, typeNameMapper, filterCtx, limits, scope, defaultOrderBy, resolveName) => {
|
|
3141
|
-
if (!info) return;
|
|
3142
|
-
return extractRelationsParamsInner(relationMap, tables, tableName, typeName, info, typeNameMapper, true, filterCtx, limits, scope, defaultOrderBy, resolveName);
|
|
3143
|
-
};
|
|
3144
3223
|
/**
|
|
3145
3224
|
* Returns a copy of `relationMap` containing only the relations that should be eagerly
|
|
3146
3225
|
* pre-fetched (per the `shouldEagerLoad` predicate). Pass the result wherever a query or
|
|
@@ -3341,7 +3420,13 @@ const hardDeleteArg = {
|
|
|
3341
3420
|
const prepareMutationRelationColumns = (params) => {
|
|
3342
3421
|
const { relationMap, tables, tableName, typeNameMapper, table, pkNames, parsedInfo, resolveName } = params;
|
|
3343
3422
|
const typeName = resolveObjectTypeName(tableName, typeNameMapper, resolveName);
|
|
3344
|
-
const withParams = relationMap[tableName] ? extractRelationsParams(relationMap, tables, tableName, parsedInfo, typeName,
|
|
3423
|
+
const withParams = relationMap[tableName] ? extractRelationsParams(relationMap, tables, tableName, parsedInfo, typeName, {
|
|
3424
|
+
typeNameMapper,
|
|
3425
|
+
limits: params.limits,
|
|
3426
|
+
scope: params.scope,
|
|
3427
|
+
defaultOrderBy: params.defaultOrderBy,
|
|
3428
|
+
resolveName
|
|
3429
|
+
}) : void 0;
|
|
3345
3430
|
const hasRelations = !!(withParams && Object.keys(withParams).length);
|
|
3346
3431
|
const baseColumns = extractSelectedColumnsFromTreeSQLFormat(parsedInfo.fieldsByTypeName[typeName], table, {
|
|
3347
3432
|
tableName,
|
|
@@ -3920,7 +4005,14 @@ const runRelationalSelect = async (opts) => {
|
|
|
3920
4005
|
offset: distinctKeys ? void 0 : offset,
|
|
3921
4006
|
orderBy: distinctKeys ? (aliasedTable) => [...orderBy ? extractOrderBy(aliasedTable, orderBy, relationFilterCtx(filterCtx, tableName), where) : [], ...primaryKeyOrderExprs(aliasedTable, pkNames)] : cursorEntries ? (aliasedTable) => cursorOrderExprs(aliasedTable, cursorEntries) : orderBy ? (aliasedTable) => extractOrderBy(aliasedTable, orderBy, relationFilterCtx(filterCtx, tableName), where) : needsDefaultOrder && pkNames?.length ? (aliasedTable) => primaryKeyOrderExprs(aliasedTable, pkNames) : void 0,
|
|
3922
4007
|
where: distinctKeys ? { RAW: (aliasedTable) => primaryKeyRestriction(aliasedTable, pkNames, distinctKeys) } : where || cursorValues || scope?.has(tableName, deleted) ? { RAW: (aliasedTable) => withScope(scope, tableName, aliasedTable, (0, drizzle_orm.and)(where ? extractFilters(aliasedTable, tableName, where, relationFilterCtx(filterCtx, tableName)) : void 0, cursorValues ? buildCursorCondition(aliasedTable, cursorEntries, cursorValues, opts.nullOrdering ?? "nulls-smallest") : void 0), deleted) } : void 0,
|
|
3923
|
-
with: relationMap[tableName] ? extractRelationsParams(relationMap, tables, tableName, parsedInfo, typeName,
|
|
4008
|
+
with: relationMap[tableName] ? extractRelationsParams(relationMap, tables, tableName, parsedInfo, typeName, {
|
|
4009
|
+
typeNameMapper,
|
|
4010
|
+
filterCtx,
|
|
4011
|
+
limits: opts.limits,
|
|
4012
|
+
scope,
|
|
4013
|
+
defaultOrderBy: opts.defaultOrderBy,
|
|
4014
|
+
resolveName: opts.resolveName
|
|
4015
|
+
}) : void 0
|
|
3924
4016
|
};
|
|
3925
4017
|
if (single) {
|
|
3926
4018
|
const result = await queryBase.findFirst(params);
|
|
@@ -4841,12 +4933,13 @@ const collectSelections = (selections, info, tree, parentType, deep, activeFragm
|
|
|
4841
4933
|
fields = {};
|
|
4842
4934
|
tree[parentTypeName] = fields;
|
|
4843
4935
|
}
|
|
4936
|
+
const parentFields = fieldsOf(parentType);
|
|
4844
4937
|
for (const selection of selections) {
|
|
4845
4938
|
if (isExcluded(info, selection)) continue;
|
|
4846
4939
|
if (selection.kind === graphql.Kind.FIELD) {
|
|
4847
4940
|
const name = selection.name.value;
|
|
4848
4941
|
if (isMetaField(name)) continue;
|
|
4849
|
-
const fieldDef =
|
|
4942
|
+
const fieldDef = parentFields?.[name];
|
|
4850
4943
|
if (!fieldDef) continue;
|
|
4851
4944
|
const fieldType = namedTypeOf(fieldDef.type);
|
|
4852
4945
|
if (!fieldType) continue;
|
|
@@ -4889,7 +4982,10 @@ const collectSelections = (selections, info, tree, parentType, deep, activeFragm
|
|
|
4889
4982
|
}
|
|
4890
4983
|
return tree;
|
|
4891
4984
|
};
|
|
4892
|
-
|
|
4985
|
+
/** The first key of an object, without materializing the whole key array to discard it. */
|
|
4986
|
+
const firstKey = (object) => {
|
|
4987
|
+
for (const key in object) return key;
|
|
4988
|
+
};
|
|
4893
4989
|
/**
|
|
4894
4990
|
* Reads the selection under a resolver's own field out of its `info`, as a tree of
|
|
4895
4991
|
* {@link ResolveTree} nodes.
|
|
@@ -4940,7 +5036,30 @@ const SEP = "__";
|
|
|
4940
5036
|
* arrays (including array-typed number columns), JSON, buffers, and object-shaped columns
|
|
4941
5037
|
* (e.g. geometry) are excluded entirely.
|
|
4942
5038
|
*/
|
|
5039
|
+
/**
|
|
5040
|
+
* Cache for {@link classifyAggregateColumns}. Keyed on the table object, then on the name it
|
|
5041
|
+
* was classified under — the name only reaches `drizzleColumnToGraphQLType` for enum naming,
|
|
5042
|
+
* but a table registered under two names would name its enums differently, so it is part of
|
|
5043
|
+
* the key rather than assumed irrelevant.
|
|
5044
|
+
*
|
|
5045
|
+
* The entries hold this build's GraphQL types and this build's column exclusions, so the
|
|
5046
|
+
* cache lives exactly as long as a build: {@link resetAggregateColumnCache} runs at the start
|
|
5047
|
+
* of every one, alongside the other per-build registries. Kept across builds it would hand a
|
|
5048
|
+
* second build the first build's enum instances, which is a schema with two types of one name.
|
|
5049
|
+
*/
|
|
5050
|
+
let aggregateColumnSetsCache = /* @__PURE__ */ new WeakMap();
|
|
5051
|
+
/** Drops the classifications cached for the previous build. Called once per `generateSchemaData`. */
|
|
5052
|
+
const resetAggregateColumnCache = () => {
|
|
5053
|
+
aggregateColumnSetsCache = /* @__PURE__ */ new WeakMap();
|
|
5054
|
+
};
|
|
4943
5055
|
const classifyAggregateColumns = (table, tableName) => {
|
|
5056
|
+
let byName = aggregateColumnSetsCache.get(table);
|
|
5057
|
+
if (!byName) {
|
|
5058
|
+
byName = /* @__PURE__ */ new Map();
|
|
5059
|
+
aggregateColumnSetsCache.set(table, byName);
|
|
5060
|
+
}
|
|
5061
|
+
const cached = byName.get(tableName);
|
|
5062
|
+
if (cached) return cached;
|
|
4944
5063
|
const numeric = {};
|
|
4945
5064
|
const orderable = {};
|
|
4946
5065
|
const all = {};
|
|
@@ -4958,11 +5077,13 @@ const classifyAggregateColumns = (table, tableName) => {
|
|
|
4958
5077
|
};
|
|
4959
5078
|
if (dataType === "number") numeric[columnName] = column;
|
|
4960
5079
|
}
|
|
4961
|
-
|
|
5080
|
+
const sets = {
|
|
4962
5081
|
numeric,
|
|
4963
5082
|
orderable,
|
|
4964
5083
|
all
|
|
4965
5084
|
};
|
|
5085
|
+
byName.set(tableName, sets);
|
|
5086
|
+
return sets;
|
|
4966
5087
|
};
|
|
4967
5088
|
/**
|
|
4968
5089
|
* Builds the `${typeName}Aggregate` output type for a table:
|
|
@@ -6086,6 +6207,7 @@ const createSchemaBuilder = (adapter) => {
|
|
|
6086
6207
|
registerScalarOverrides(tables, options);
|
|
6087
6208
|
registerEnumConfig(options);
|
|
6088
6209
|
registerColumnExclusions(tables, options.exclude);
|
|
6210
|
+
resetAggregateColumnCache();
|
|
6089
6211
|
const namedRelations = buildNamedRelations(relations ?? {}, tableEntries);
|
|
6090
6212
|
for (const excluded of excludedTables) delete namedRelations[excluded];
|
|
6091
6213
|
attachTargetPrimaryKeys(namedRelations, tables, (table) => primaryKeyPropNames(table));
|
|
@@ -6475,11 +6597,13 @@ const generateDelete = (db, tableName, table, filterArgs, fieldName, single, req
|
|
|
6475
6597
|
}
|
|
6476
6598
|
});
|
|
6477
6599
|
};
|
|
6600
|
+
/** The dialect's table config, cached per table — see {@link memoizeTableConfig}. */
|
|
6601
|
+
const getTableConfig$2 = memoizeTableConfig(drizzle_orm_mysql_core.getTableConfig);
|
|
6478
6602
|
/** Primary-key property names for a MySQL table, including table-level composite keys. */
|
|
6479
|
-
const mysqlPrimaryKeyPropNames = (table) => getPrimaryKeyPropNamesFromConfig(table,
|
|
6603
|
+
const mysqlPrimaryKeyPropNames = (table) => getPrimaryKeyPropNamesFromConfig(table, getTableConfig$2);
|
|
6480
6604
|
const { prepareBuild, addReadFields, finalizeBuild } = createSchemaBuilder({
|
|
6481
6605
|
tableClass: drizzle_orm_mysql_core.MySqlTable,
|
|
6482
|
-
getTableConfig:
|
|
6606
|
+
getTableConfig: getTableConfig$2,
|
|
6483
6607
|
primaryKeyPropNames: mysqlPrimaryKeyPropNames,
|
|
6484
6608
|
nullOrdering: "nulls-smallest",
|
|
6485
6609
|
returnsRows: false,
|
|
@@ -7360,12 +7484,14 @@ const createUpdateManyGenerator = (primaryKeyPropNames, runBatch = runUpdateMany
|
|
|
7360
7484
|
};
|
|
7361
7485
|
//#endregion
|
|
7362
7486
|
//#region src/util/builders/pg.ts
|
|
7487
|
+
/** The dialect's table config, cached per table — see {@link memoizeTableConfig}. */
|
|
7488
|
+
const getTableConfig$1 = memoizeTableConfig(drizzle_orm_pg_core.getTableConfig);
|
|
7363
7489
|
/** Primary-key property names for a PG table, including table-level composite keys. */
|
|
7364
|
-
const pgPrimaryKeyPropNames = (table) => getPrimaryKeyPropNamesFromConfig(table,
|
|
7490
|
+
const pgPrimaryKeyPropNames = (table) => getPrimaryKeyPropNamesFromConfig(table, getTableConfig$1);
|
|
7365
7491
|
const generateUpdateMany$1 = createUpdateManyGenerator(pgPrimaryKeyPropNames);
|
|
7366
7492
|
const pgSchemaData = createSchemaDataGenerator({
|
|
7367
7493
|
tableClass: drizzle_orm_pg_core.PgTable,
|
|
7368
|
-
getTableConfig:
|
|
7494
|
+
getTableConfig: getTableConfig$1,
|
|
7369
7495
|
primaryKeyPropNames: pgPrimaryKeyPropNames,
|
|
7370
7496
|
nullOrdering: "nulls-largest",
|
|
7371
7497
|
returnsRows: true,
|
|
@@ -7374,8 +7500,10 @@ const pgSchemaData = createSchemaDataGenerator({
|
|
|
7374
7500
|
const generateSchemaData$1 = (db, schema, relations, options) => pgSchemaData(db, schema, relations, options);
|
|
7375
7501
|
//#endregion
|
|
7376
7502
|
//#region src/util/builders/sqlite.ts
|
|
7503
|
+
/** The dialect's table config, cached per table — see {@link memoizeTableConfig}. */
|
|
7504
|
+
const getTableConfig = memoizeTableConfig(drizzle_orm_sqlite_core.getTableConfig);
|
|
7377
7505
|
/** Primary-key property names for a SQLite table, including table-level composite keys. */
|
|
7378
|
-
const sqlitePrimaryKeyPropNames = (table) => getPrimaryKeyPropNamesFromConfig(table,
|
|
7506
|
+
const sqlitePrimaryKeyPropNames = (table) => getPrimaryKeyPropNamesFromConfig(table, getTableConfig);
|
|
7379
7507
|
/**
|
|
7380
7508
|
* SQLite's batch runner. A synchronous driver (better-sqlite3) commits the moment its
|
|
7381
7509
|
* transaction callback returns, so that callback must not be async — an awaited statement
|
|
@@ -7420,7 +7548,7 @@ const runSqliteUpdateManyBatch = ({ executor, entries, table, returning, anyNest
|
|
|
7420
7548
|
const generateUpdateMany = createUpdateManyGenerator(sqlitePrimaryKeyPropNames, runSqliteUpdateManyBatch);
|
|
7421
7549
|
const sqliteSchemaData = createSchemaDataGenerator({
|
|
7422
7550
|
tableClass: drizzle_orm_sqlite_core.SQLiteTable,
|
|
7423
|
-
getTableConfig
|
|
7551
|
+
getTableConfig,
|
|
7424
7552
|
primaryKeyPropNames: sqlitePrimaryKeyPropNames,
|
|
7425
7553
|
nullOrdering: "nulls-smallest",
|
|
7426
7554
|
returnsRows: true,
|
|
@@ -7434,11 +7562,14 @@ const generateSchemaData = (db, schema, relations, options) => sqliteSchemaData(
|
|
|
7434
7562
|
//#endregion
|
|
7435
7563
|
//#region src/util/selection.ts
|
|
7436
7564
|
const contextCache = /* @__PURE__ */ new WeakMap();
|
|
7565
|
+
const getMySqlTableConfig = memoizeTableConfig(drizzle_orm_mysql_core.getTableConfig);
|
|
7566
|
+
const getPgTableConfig = memoizeTableConfig(drizzle_orm_pg_core.getTableConfig);
|
|
7567
|
+
const getSQLiteTableConfig = memoizeTableConfig(drizzle_orm_sqlite_core.getTableConfig);
|
|
7437
7568
|
/** The dialect's `getTableConfig`, which is the only place a composite primary key shows up. */
|
|
7438
7569
|
const tableConfigFor = (db) => {
|
|
7439
|
-
if ((0, drizzle_orm.is)(db, drizzle_orm_mysql_core.MySqlAsyncDatabase)) return
|
|
7440
|
-
if ((0, drizzle_orm.is)(db, drizzle_orm_pg_core.PgAsyncDatabase)) return
|
|
7441
|
-
if ((0, drizzle_orm.is)(db, drizzle_orm_sqlite_core.SQLiteAsyncDatabase)) return
|
|
7570
|
+
if ((0, drizzle_orm.is)(db, drizzle_orm_mysql_core.MySqlAsyncDatabase)) return getMySqlTableConfig;
|
|
7571
|
+
if ((0, drizzle_orm.is)(db, drizzle_orm_pg_core.PgAsyncDatabase)) return getPgTableConfig;
|
|
7572
|
+
if ((0, drizzle_orm.is)(db, drizzle_orm_sqlite_core.SQLiteAsyncDatabase)) return getSQLiteTableConfig;
|
|
7442
7573
|
throw new Error("Drizzle-GraphQL Error: unsupported database instance — expected a PostgreSQL, MySQL or SQLite one.");
|
|
7443
7574
|
};
|
|
7444
7575
|
const contextFor = (db) => {
|
|
@@ -7519,7 +7650,11 @@ const selectionToWith = (info, options) => {
|
|
|
7519
7650
|
if (!tables[table]) throw new Error(`Drizzle-GraphQL Error: '${table}' is not a table in the Drizzle schema.`);
|
|
7520
7651
|
if (!relationMap[table]) return;
|
|
7521
7652
|
const parsed = parseInfo(info);
|
|
7522
|
-
const params = extractRelationsParams(relationMap, tables, table, parsed, resolveObjectTypeName(table, typeNameMapper, resolveName),
|
|
7653
|
+
const params = extractRelationsParams(relationMap, tables, table, parsed, resolveObjectTypeName(table, typeNameMapper, resolveName), {
|
|
7654
|
+
typeNameMapper,
|
|
7655
|
+
filterCtx,
|
|
7656
|
+
resolveName
|
|
7657
|
+
});
|
|
7523
7658
|
return params && Object.keys(params).length ? params : void 0;
|
|
7524
7659
|
};
|
|
7525
7660
|
/**
|