@vantreeseba/drizzle-graphql 5.1.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 +111 -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 +113 -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,52 +812,95 @@ var innerOrder = new GraphQLInputObjectType2({
|
|
|
813
812
|
}
|
|
814
813
|
}
|
|
815
814
|
});
|
|
816
|
-
var
|
|
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
|
+
}
|
|
817
839
|
if (columnGraphQLType.type === GraphQLUUID) {
|
|
818
|
-
return "Id";
|
|
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) {
|
|
851
|
-
return "Int";
|
|
859
|
+
return { name: "Int", kind: "scalar" };
|
|
852
860
|
}
|
|
853
861
|
if (columnGraphQLType.type === GraphQLFloat2) {
|
|
854
|
-
return "Float";
|
|
862
|
+
return { name: "Float", kind: "scalar" };
|
|
855
863
|
}
|
|
856
|
-
return "String";
|
|
864
|
+
return { name: "String", kind: "scalar" };
|
|
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
|
+
};
|
|
857
899
|
};
|
|
858
900
|
var FILTERS_WITHOUT_STRING_OPS = /* @__PURE__ */ new Set(["Id", "Int", "Float", "BigInt", "Decimal"]);
|
|
859
901
|
var generateColumnFilterValues = (column, tableName, columnName, cacheCtx) => {
|
|
860
902
|
const columnGraphQLType = drizzleColumnToGraphQLType(column, columnName, tableName, true, false, true);
|
|
861
|
-
const genericName =
|
|
903
|
+
const { name: genericName, kind } = resolveGenericFilterDescriptor(column, columnGraphQLType);
|
|
862
904
|
const cached = cacheCtx.genericFilterCache.get(genericName);
|
|
863
905
|
if (cached) {
|
|
864
906
|
return cached;
|
|
@@ -867,7 +909,7 @@ var generateColumnFilterValues = (column, tableName, columnName, cacheCtx) => {
|
|
|
867
909
|
const colDesc = columnGraphQLType.description;
|
|
868
910
|
const colArr = new GraphQLList2(new GraphQLNonNull2(colType));
|
|
869
911
|
const omitStringOps = FILTERS_WITHOUT_STRING_OPS.has(genericName);
|
|
870
|
-
const baseFields = {
|
|
912
|
+
const baseFields = kind === "json" ? jsonFilterFields(column, colType) : kind === "array" ? arrayFilterFields(colType, colArr, colDesc) : {
|
|
871
913
|
eq: { type: colType, description: colDesc },
|
|
872
914
|
ne: { type: colType, description: colDesc },
|
|
873
915
|
lt: { type: colType, description: colDesc },
|
|
@@ -1440,12 +1482,28 @@ var safeLikeCondition = (column, pattern, insensitive) => {
|
|
|
1440
1482
|
const isPg = (column.columnType ?? "").startsWith("Pg");
|
|
1441
1483
|
return isPg ? sql`${column} ilike ${pattern} escape ${LIKE_ESCAPE_CHAR}` : sql`lower(${column}) like ${pattern.toLowerCase()} escape ${LIKE_ESCAPE_CHAR}`;
|
|
1442
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
|
+
};
|
|
1443
1497
|
var extractFiltersColumn = (column, columnName, operators) => {
|
|
1444
1498
|
const { OR, AND, NOT, ...restOperators } = operators;
|
|
1445
1499
|
const entries = Object.entries(restOperators);
|
|
1446
1500
|
const singleValueOps = { eq, ne, gt, gte, lt, lte };
|
|
1447
1501
|
const stringValueOps = { like, notLike, ilike, notIlike };
|
|
1448
1502
|
const arrayValueOps = { inArray, notInArray };
|
|
1503
|
+
const arrayMembershipOps = {
|
|
1504
|
+
hasSome: arrayOverlaps,
|
|
1505
|
+
hasEvery: arrayContains
|
|
1506
|
+
};
|
|
1449
1507
|
const nullableOps = { isNull, isNotNull };
|
|
1450
1508
|
const variants = [];
|
|
1451
1509
|
for (const [operatorName, operatorValue] of entries) {
|
|
@@ -1457,6 +1515,8 @@ var extractFiltersColumn = (column, columnName, operators) => {
|
|
|
1457
1515
|
variants.push(singleValueOps[operatorName](column, singleValue));
|
|
1458
1516
|
} else if (operatorName in stringValueOps) {
|
|
1459
1517
|
variants.push(stringValueOps[operatorName](column, operatorValue));
|
|
1518
|
+
} else if (operatorName === "contains" && isJsonColumn2(column)) {
|
|
1519
|
+
variants.push(jsonContains(column, columnName, operatorValue));
|
|
1460
1520
|
} else if (operatorName in safeLikeOps) {
|
|
1461
1521
|
const { buildPattern, insensitive } = safeLikeOps[operatorName];
|
|
1462
1522
|
variants.push(safeLikeCondition(column, buildPattern(operatorValue), insensitive));
|
|
@@ -1466,6 +1526,15 @@ var extractFiltersColumn = (column, columnName, operators) => {
|
|
|
1466
1526
|
}
|
|
1467
1527
|
const arrayValue = operatorValue.map((val) => remapFromGraphQLCore(val, column, columnName));
|
|
1468
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`);
|
|
1469
1538
|
} else if (operatorName in nullableOps) {
|
|
1470
1539
|
variants.push(nullableOps[operatorName](column));
|
|
1471
1540
|
} else {
|