@vantreeseba/drizzle-graphql 5.0.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +118 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +120 -44
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -57,12 +57,24 @@ type FilterColumnOperatorsCore<TColumn extends Column, TColType = GetColumnGqlDa
|
|
|
57
57
|
notIlike: string;
|
|
58
58
|
startsWith: string;
|
|
59
59
|
endsWith: string;
|
|
60
|
-
|
|
60
|
+
/**
|
|
61
|
+
* String columns: safe substring match (`%`, `_` and `\` matched literally).
|
|
62
|
+
* JSON columns: structural containment (Postgres `@>` / MySQL JSON_CONTAINS).
|
|
63
|
+
*/
|
|
64
|
+
contains: string | TColType;
|
|
61
65
|
iStartsWith: string;
|
|
62
66
|
iEndsWith: string;
|
|
63
67
|
iContains: string;
|
|
64
68
|
inArray: Array<TColType>;
|
|
65
69
|
notInArray: Array<TColType>;
|
|
70
|
+
/** Array columns only: the array contains this element. */
|
|
71
|
+
has: TColType extends Array<infer TElement> ? TElement : never;
|
|
72
|
+
/** Array columns only: the array overlaps these elements (`&&`). */
|
|
73
|
+
hasSome: TColType extends Array<infer TElement> ? Array<TElement> : never;
|
|
74
|
+
/** Array columns only: the array contains all of these elements (`@>`). */
|
|
75
|
+
hasEvery: TColType extends Array<infer TElement> ? Array<TElement> : never;
|
|
76
|
+
/** Array columns only: when true, matches arrays with no elements. */
|
|
77
|
+
isEmpty: boolean;
|
|
66
78
|
isNull: boolean;
|
|
67
79
|
isNotNull: boolean;
|
|
68
80
|
}>;
|
package/dist/index.d.ts
CHANGED
|
@@ -57,12 +57,24 @@ type FilterColumnOperatorsCore<TColumn extends Column, TColType = GetColumnGqlDa
|
|
|
57
57
|
notIlike: string;
|
|
58
58
|
startsWith: string;
|
|
59
59
|
endsWith: string;
|
|
60
|
-
|
|
60
|
+
/**
|
|
61
|
+
* String columns: safe substring match (`%`, `_` and `\` matched literally).
|
|
62
|
+
* JSON columns: structural containment (Postgres `@>` / MySQL JSON_CONTAINS).
|
|
63
|
+
*/
|
|
64
|
+
contains: string | TColType;
|
|
61
65
|
iStartsWith: string;
|
|
62
66
|
iEndsWith: string;
|
|
63
67
|
iContains: string;
|
|
64
68
|
inArray: Array<TColType>;
|
|
65
69
|
notInArray: Array<TColType>;
|
|
70
|
+
/** Array columns only: the array contains this element. */
|
|
71
|
+
has: TColType extends Array<infer TElement> ? TElement : never;
|
|
72
|
+
/** Array columns only: the array overlaps these elements (`&&`). */
|
|
73
|
+
hasSome: TColType extends Array<infer TElement> ? Array<TElement> : never;
|
|
74
|
+
/** Array columns only: the array contains all of these elements (`@>`). */
|
|
75
|
+
hasEvery: TColType extends Array<infer TElement> ? Array<TElement> : never;
|
|
76
|
+
/** Array columns only: when true, matches arrays with no elements. */
|
|
77
|
+
isEmpty: boolean;
|
|
66
78
|
isNull: boolean;
|
|
67
79
|
isNotNull: boolean;
|
|
68
80
|
}>;
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
aliasedTable,
|
|
14
14
|
and,
|
|
15
|
+
arrayContains,
|
|
16
|
+
arrayOverlaps,
|
|
15
17
|
asc,
|
|
16
18
|
desc,
|
|
17
19
|
eq,
|
|
@@ -416,6 +418,21 @@ var geoXyInputType = new GraphQLInputObjectType({
|
|
|
416
418
|
}
|
|
417
419
|
});
|
|
418
420
|
var columnToGraphQLCore = (column, columnName, tableName, isInput) => {
|
|
421
|
+
const dimensions = column.dimensions;
|
|
422
|
+
if (dimensions !== void 0 && dimensions > 0) {
|
|
423
|
+
const inner = columnBaseToGraphQLCore(column, columnName, tableName, isInput);
|
|
424
|
+
const innerDesc = inner.description ?? inner.type.name;
|
|
425
|
+
let type = inner.type;
|
|
426
|
+
let description = innerDesc;
|
|
427
|
+
for (let i = 0; i < dimensions; i++) {
|
|
428
|
+
type = new GraphQLList(new GraphQLNonNull(type));
|
|
429
|
+
description = `Array<${description}>`;
|
|
430
|
+
}
|
|
431
|
+
return { type, description };
|
|
432
|
+
}
|
|
433
|
+
return columnBaseToGraphQLCore(column, columnName, tableName, isInput);
|
|
434
|
+
};
|
|
435
|
+
var columnBaseToGraphQLCore = (column, columnName, tableName, isInput) => {
|
|
419
436
|
const { type: baseType, constraint } = extractExtendedColumnType(column);
|
|
420
437
|
switch (baseType) {
|
|
421
438
|
case "boolean":
|
|
@@ -447,28 +464,10 @@ var columnToGraphQLCore = (column, columnName, tableName, isInput) => {
|
|
|
447
464
|
if (column instanceof PgDateString) {
|
|
448
465
|
return isInput ? { type: GraphQLString, description: "Date" } : { type: GraphQLDate, description: "Date" };
|
|
449
466
|
}
|
|
450
|
-
{
|
|
451
|
-
const dims = column.dimensions;
|
|
452
|
-
if (dims !== void 0 && dims > 0) {
|
|
453
|
-
return {
|
|
454
|
-
type: new GraphQLList(new GraphQLNonNull(GraphQLString)),
|
|
455
|
-
description: "Array<String>"
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
467
|
return { type: GraphQLString, description: "String" };
|
|
460
468
|
case "bigint":
|
|
461
469
|
return { type: GraphQLBigIntString, description: "BigInt" };
|
|
462
470
|
case "number": {
|
|
463
|
-
const dims = column.dimensions;
|
|
464
|
-
if (dims !== void 0 && dims > 0) {
|
|
465
|
-
const baseDesc = is2(column, PgInteger) || is2(column, PgSerial) ? "Integer" : "Float";
|
|
466
|
-
const baseType2 = baseDesc === "Integer" ? GraphQLInt : GraphQLFloat;
|
|
467
|
-
return {
|
|
468
|
-
type: new GraphQLList(new GraphQLNonNull(baseType2)),
|
|
469
|
-
description: `Array<${baseDesc}>`
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
471
|
return is2(column, PgInteger) || is2(column, PgSerial) || is2(column, MySqlInt) || is2(column, MySqlSerial) || is2(column, SQLiteInteger) ? { type: GraphQLInt, description: "Integer" } : { type: GraphQLFloat, description: "Float" };
|
|
473
472
|
}
|
|
474
473
|
case "array": {
|
|
@@ -813,45 +812,95 @@ var innerOrder = new GraphQLInputObjectType2({
|
|
|
813
812
|
}
|
|
814
813
|
}
|
|
815
814
|
});
|
|
816
|
-
var
|
|
817
|
-
|
|
818
|
-
|
|
815
|
+
var columnDialect = (column) => {
|
|
816
|
+
const ct = column.columnType ?? "";
|
|
817
|
+
if (ct.startsWith("Pg")) {
|
|
818
|
+
return "pg";
|
|
819
|
+
}
|
|
820
|
+
if (ct.startsWith("MySql")) {
|
|
821
|
+
return "mysql";
|
|
822
|
+
}
|
|
823
|
+
if (ct.startsWith("SQLite")) {
|
|
824
|
+
return "sqlite";
|
|
825
|
+
}
|
|
826
|
+
return void 0;
|
|
827
|
+
};
|
|
828
|
+
var resolveGenericFilterDescriptor = (column, columnGraphQLType) => {
|
|
829
|
+
if (columnGraphQLType.type === GraphQLJSON) {
|
|
830
|
+
return { name: "JSON", kind: "json" };
|
|
831
|
+
}
|
|
832
|
+
if (columnGraphQLType.type instanceof GraphQLList2) {
|
|
833
|
+
let element = columnGraphQLType.type.ofType;
|
|
834
|
+
if (element instanceof GraphQLNonNull2) {
|
|
835
|
+
element = element.ofType;
|
|
836
|
+
}
|
|
837
|
+
return { name: `${element.name}Array`, kind: "array" };
|
|
838
|
+
}
|
|
839
|
+
if (columnGraphQLType.type === GraphQLUUID) {
|
|
840
|
+
return { name: "Id", kind: "scalar" };
|
|
819
841
|
}
|
|
820
842
|
if (columnGraphQLType.type === GraphQLBoolean2) {
|
|
821
|
-
return "Boolean";
|
|
843
|
+
return { name: "Boolean", kind: "scalar" };
|
|
822
844
|
}
|
|
823
845
|
if (columnGraphQLType.type instanceof GraphQLEnumType2) {
|
|
824
|
-
return columnGraphQLType.type.name;
|
|
846
|
+
return { name: columnGraphQLType.type.name, kind: "scalar" };
|
|
825
847
|
}
|
|
826
848
|
if (columnGraphQLType.type === GraphQLBigIntString) {
|
|
827
|
-
return "BigInt";
|
|
849
|
+
return { name: "BigInt", kind: "scalar" };
|
|
828
850
|
}
|
|
829
851
|
if (columnGraphQLType.type === GraphQLDecimalString) {
|
|
830
|
-
return "Decimal";
|
|
831
|
-
}
|
|
832
|
-
if (columnGraphQLType.type instanceof GraphQLList2) {
|
|
833
|
-
const desc2 = columnGraphQLType.description ?? "";
|
|
834
|
-
let inner = columnGraphQLType.type.ofType;
|
|
835
|
-
if (inner instanceof GraphQLNonNull2) {
|
|
836
|
-
inner = inner.ofType;
|
|
837
|
-
}
|
|
838
|
-
if (desc2.includes("Integer") || inner === GraphQLInt2) {
|
|
839
|
-
return "IntArray";
|
|
840
|
-
}
|
|
841
|
-
if (desc2.includes("Float") || inner === GraphQLFloat2) {
|
|
842
|
-
return "FloatArray";
|
|
843
|
-
}
|
|
844
|
-
return "StringArray";
|
|
852
|
+
return { name: "Decimal", kind: "scalar" };
|
|
845
853
|
}
|
|
846
854
|
const ct = column.columnType ?? "";
|
|
847
855
|
if (ct === "PgTimestamp" || ct === "PgTimestampString" || ct === "PgDate") {
|
|
848
|
-
return "DateTime";
|
|
856
|
+
return { name: "DateTime", kind: "scalar" };
|
|
849
857
|
}
|
|
850
|
-
|
|
858
|
+
if (columnGraphQLType.type === GraphQLInt2) {
|
|
859
|
+
return { name: "Int", kind: "scalar" };
|
|
860
|
+
}
|
|
861
|
+
if (columnGraphQLType.type === GraphQLFloat2) {
|
|
862
|
+
return { name: "Float", kind: "scalar" };
|
|
863
|
+
}
|
|
864
|
+
return { name: "String", kind: "scalar" };
|
|
851
865
|
};
|
|
866
|
+
var jsonFilterFields = (column, colType) => {
|
|
867
|
+
const dialect = columnDialect(column);
|
|
868
|
+
return {
|
|
869
|
+
eq: { type: colType, description: "JSON equality on the whole value" },
|
|
870
|
+
ne: { type: colType, description: "JSON inequality on the whole value" },
|
|
871
|
+
...dialect === "pg" || dialect === "mysql" ? {
|
|
872
|
+
contains: {
|
|
873
|
+
type: colType,
|
|
874
|
+
description: "Value structurally contains this JSON (Postgres `@>` / MySQL JSON_CONTAINS)"
|
|
875
|
+
}
|
|
876
|
+
} : {},
|
|
877
|
+
isNull: { type: GraphQLBoolean2 },
|
|
878
|
+
isNotNull: { type: GraphQLBoolean2 }
|
|
879
|
+
};
|
|
880
|
+
};
|
|
881
|
+
var arrayFilterFields = (colType, colArr, colDesc) => {
|
|
882
|
+
let element = colType.ofType;
|
|
883
|
+
if (element instanceof GraphQLNonNull2) {
|
|
884
|
+
element = element.ofType;
|
|
885
|
+
}
|
|
886
|
+
const elementList = new GraphQLList2(new GraphQLNonNull2(element));
|
|
887
|
+
return {
|
|
888
|
+
eq: { type: colType, description: colDesc },
|
|
889
|
+
ne: { type: colType, description: colDesc },
|
|
890
|
+
has: { type: element, description: "Array contains this element" },
|
|
891
|
+
hasSome: { type: elementList, description: "Array contains at least one of these elements (overlap, `&&`)" },
|
|
892
|
+
hasEvery: { type: elementList, description: "Array contains every one of these elements (containment, `@>`)" },
|
|
893
|
+
isEmpty: { type: GraphQLBoolean2, description: "When true, matches arrays with no elements" },
|
|
894
|
+
inArray: { type: colArr, description: `Array<${colDesc}>` },
|
|
895
|
+
notInArray: { type: colArr, description: `Array<${colDesc}>` },
|
|
896
|
+
isNull: { type: GraphQLBoolean2 },
|
|
897
|
+
isNotNull: { type: GraphQLBoolean2 }
|
|
898
|
+
};
|
|
899
|
+
};
|
|
900
|
+
var FILTERS_WITHOUT_STRING_OPS = /* @__PURE__ */ new Set(["Id", "Int", "Float", "BigInt", "Decimal"]);
|
|
852
901
|
var generateColumnFilterValues = (column, tableName, columnName, cacheCtx) => {
|
|
853
902
|
const columnGraphQLType = drizzleColumnToGraphQLType(column, columnName, tableName, true, false, true);
|
|
854
|
-
const genericName =
|
|
903
|
+
const { name: genericName, kind } = resolveGenericFilterDescriptor(column, columnGraphQLType);
|
|
855
904
|
const cached = cacheCtx.genericFilterCache.get(genericName);
|
|
856
905
|
if (cached) {
|
|
857
906
|
return cached;
|
|
@@ -859,8 +908,8 @@ var generateColumnFilterValues = (column, tableName, columnName, cacheCtx) => {
|
|
|
859
908
|
const colType = columnGraphQLType.type;
|
|
860
909
|
const colDesc = columnGraphQLType.description;
|
|
861
910
|
const colArr = new GraphQLList2(new GraphQLNonNull2(colType));
|
|
862
|
-
const omitStringOps = genericName
|
|
863
|
-
const baseFields = {
|
|
911
|
+
const omitStringOps = FILTERS_WITHOUT_STRING_OPS.has(genericName);
|
|
912
|
+
const baseFields = kind === "json" ? jsonFilterFields(column, colType) : kind === "array" ? arrayFilterFields(colType, colArr, colDesc) : {
|
|
864
913
|
eq: { type: colType, description: colDesc },
|
|
865
914
|
ne: { type: colType, description: colDesc },
|
|
866
915
|
lt: { type: colType, description: colDesc },
|
|
@@ -1433,12 +1482,28 @@ var safeLikeCondition = (column, pattern, insensitive) => {
|
|
|
1433
1482
|
const isPg = (column.columnType ?? "").startsWith("Pg");
|
|
1434
1483
|
return isPg ? sql`${column} ilike ${pattern} escape ${LIKE_ESCAPE_CHAR}` : sql`lower(${column}) like ${pattern.toLowerCase()} escape ${LIKE_ESCAPE_CHAR}`;
|
|
1435
1484
|
};
|
|
1485
|
+
var isJsonColumn2 = (column) => (column.columnType ?? "").includes("Json");
|
|
1486
|
+
var jsonContains = (column, columnName, value) => {
|
|
1487
|
+
const serialized = JSON.stringify(value);
|
|
1488
|
+
switch (columnDialect(column)) {
|
|
1489
|
+
case "pg":
|
|
1490
|
+
return column.columnType === "PgJson" ? sql`${column}::jsonb @> ${serialized}::jsonb` : sql`${column} @> ${serialized}::jsonb`;
|
|
1491
|
+
case "mysql":
|
|
1492
|
+
return sql`json_contains(${column}, ${serialized})`;
|
|
1493
|
+
default:
|
|
1494
|
+
throw new GraphQLError3(`WHERE ${columnName}: Operator 'contains' is not supported for this dialect!`);
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1436
1497
|
var extractFiltersColumn = (column, columnName, operators) => {
|
|
1437
1498
|
const { OR, AND, NOT, ...restOperators } = operators;
|
|
1438
1499
|
const entries = Object.entries(restOperators);
|
|
1439
1500
|
const singleValueOps = { eq, ne, gt, gte, lt, lte };
|
|
1440
1501
|
const stringValueOps = { like, notLike, ilike, notIlike };
|
|
1441
1502
|
const arrayValueOps = { inArray, notInArray };
|
|
1503
|
+
const arrayMembershipOps = {
|
|
1504
|
+
hasSome: arrayOverlaps,
|
|
1505
|
+
hasEvery: arrayContains
|
|
1506
|
+
};
|
|
1442
1507
|
const nullableOps = { isNull, isNotNull };
|
|
1443
1508
|
const variants = [];
|
|
1444
1509
|
for (const [operatorName, operatorValue] of entries) {
|
|
@@ -1450,6 +1515,8 @@ var extractFiltersColumn = (column, columnName, operators) => {
|
|
|
1450
1515
|
variants.push(singleValueOps[operatorName](column, singleValue));
|
|
1451
1516
|
} else if (operatorName in stringValueOps) {
|
|
1452
1517
|
variants.push(stringValueOps[operatorName](column, operatorValue));
|
|
1518
|
+
} else if (operatorName === "contains" && isJsonColumn2(column)) {
|
|
1519
|
+
variants.push(jsonContains(column, columnName, operatorValue));
|
|
1453
1520
|
} else if (operatorName in safeLikeOps) {
|
|
1454
1521
|
const { buildPattern, insensitive } = safeLikeOps[operatorName];
|
|
1455
1522
|
variants.push(safeLikeCondition(column, buildPattern(operatorValue), insensitive));
|
|
@@ -1459,6 +1526,15 @@ var extractFiltersColumn = (column, columnName, operators) => {
|
|
|
1459
1526
|
}
|
|
1460
1527
|
const arrayValue = operatorValue.map((val) => remapFromGraphQLCore(val, column, columnName));
|
|
1461
1528
|
variants.push(arrayValueOps[operatorName](column, arrayValue));
|
|
1529
|
+
} else if (operatorName === "has") {
|
|
1530
|
+
variants.push(arrayContains(column, [operatorValue]));
|
|
1531
|
+
} else if (operatorName in arrayMembershipOps) {
|
|
1532
|
+
if (!operatorValue.length) {
|
|
1533
|
+
throw new GraphQLError3(`WHERE ${columnName}: Unable to use operator ${operatorName} with an empty array!`);
|
|
1534
|
+
}
|
|
1535
|
+
variants.push(arrayMembershipOps[operatorName](column, operatorValue));
|
|
1536
|
+
} else if (operatorName === "isEmpty") {
|
|
1537
|
+
variants.push(sql`cardinality(${column}) = 0`);
|
|
1462
1538
|
} else if (operatorName in nullableOps) {
|
|
1463
1539
|
variants.push(nullableOps[operatorName](column));
|
|
1464
1540
|
} else {
|