@m1212e/rumble 0.22.2 → 0.23.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/out/index.cjs +500 -103
- package/out/index.cjs.map +1 -1
- package/out/index.d.cts +112 -15
- package/out/index.d.cts.map +1 -1
- package/out/index.d.mts +112 -15
- package/out/index.d.mts.map +1 -1
- package/out/index.mjs +503 -106
- package/out/index.mjs.map +1 -1
- package/package.json +1 -1
package/out/index.cjs
CHANGED
|
@@ -33,9 +33,9 @@ let graphql_yoga = require("graphql-yoga");
|
|
|
33
33
|
let drizzle_orm = require("drizzle-orm");
|
|
34
34
|
let drizzle_orm_casing = require("drizzle-orm/casing");
|
|
35
35
|
let drizzle_orm_pg_core = require("drizzle-orm/pg-core");
|
|
36
|
+
let drizzle_orm_mysql_core = require("drizzle-orm/mysql-core");
|
|
36
37
|
let pluralize = require("pluralize");
|
|
37
38
|
pluralize = __toESM(pluralize, 1);
|
|
38
|
-
let drizzle_orm_mysql_core = require("drizzle-orm/mysql-core");
|
|
39
39
|
let drizzle_orm_sqlite_core = require("drizzle-orm/sqlite-core");
|
|
40
40
|
let _pothos_core = require("@pothos/core");
|
|
41
41
|
_pothos_core = __toESM(_pothos_core, 1);
|
|
@@ -208,7 +208,7 @@ function mapNullFieldsToUndefined(obj) {
|
|
|
208
208
|
}
|
|
209
209
|
//#endregion
|
|
210
210
|
//#region package.json
|
|
211
|
-
var version = "0.
|
|
211
|
+
var version = "0.23.0";
|
|
212
212
|
//#endregion
|
|
213
213
|
//#region lib/helpers/mergeFilters.ts
|
|
214
214
|
function mergeFilters(filterA, filterB, mode = "AND") {
|
|
@@ -227,23 +227,47 @@ function mergeFilters(filterA, filterB, mode = "AND") {
|
|
|
227
227
|
}
|
|
228
228
|
//#endregion
|
|
229
229
|
//#region lib/helpers/sqlTypes/types.ts
|
|
230
|
+
/**
|
|
231
|
+
* Strips parameter suffixes like `(256)`, `(10,2)` and timezone modifiers
|
|
232
|
+
* (`with time zone`, `without time zone`) from raw getSQLType() output so
|
|
233
|
+
* every classifier works on the base type name only.
|
|
234
|
+
*/
|
|
235
|
+
function normalizeSQLType(raw) {
|
|
236
|
+
return raw.replace(/\s*with(?:out)?\s+time\s+zone/i, "").replace(/\s*\([^)]*\)/, "").trim().toLowerCase();
|
|
237
|
+
}
|
|
230
238
|
const intLikeSQLTypeStrings = [
|
|
231
239
|
"serial",
|
|
232
240
|
"int",
|
|
233
241
|
"integer",
|
|
234
242
|
"tinyint",
|
|
235
243
|
"smallint",
|
|
236
|
-
"mediumint"
|
|
244
|
+
"mediumint",
|
|
245
|
+
"int2",
|
|
246
|
+
"int4",
|
|
247
|
+
"smallserial"
|
|
237
248
|
];
|
|
238
249
|
function isIntLikeSQLTypeString(sqlType) {
|
|
239
250
|
return intLikeSQLTypeStrings.includes(sqlType);
|
|
240
251
|
}
|
|
252
|
+
const bigIntLikeSQLTypeStrings = [
|
|
253
|
+
"bigint",
|
|
254
|
+
"bigserial",
|
|
255
|
+
"int8",
|
|
256
|
+
"bigint unsigned"
|
|
257
|
+
];
|
|
258
|
+
function isBigIntLikeSQLTypeString(sqlType) {
|
|
259
|
+
return bigIntLikeSQLTypeStrings.includes(sqlType);
|
|
260
|
+
}
|
|
241
261
|
const floatLikeSQLTypeStrings = [
|
|
242
262
|
"real",
|
|
243
263
|
"decimal",
|
|
244
264
|
"double",
|
|
245
265
|
"float",
|
|
246
|
-
"numeric"
|
|
266
|
+
"numeric",
|
|
267
|
+
"double precision",
|
|
268
|
+
"float4",
|
|
269
|
+
"float8",
|
|
270
|
+
"money"
|
|
247
271
|
];
|
|
248
272
|
function isFloatLikeSQLTypeString(sqlType) {
|
|
249
273
|
return floatLikeSQLTypeStrings.includes(sqlType);
|
|
@@ -253,7 +277,20 @@ const stringLikeSQLTypeStrings = [
|
|
|
253
277
|
"text",
|
|
254
278
|
"varchar",
|
|
255
279
|
"char",
|
|
256
|
-
"
|
|
280
|
+
"character varying",
|
|
281
|
+
"character",
|
|
282
|
+
"inet",
|
|
283
|
+
"cidr",
|
|
284
|
+
"macaddr",
|
|
285
|
+
"macaddr8",
|
|
286
|
+
"interval",
|
|
287
|
+
"time",
|
|
288
|
+
"year",
|
|
289
|
+
"binary",
|
|
290
|
+
"varbinary",
|
|
291
|
+
"tinytext",
|
|
292
|
+
"mediumtext",
|
|
293
|
+
"longtext"
|
|
257
294
|
];
|
|
258
295
|
function isStringLikeSQLTypeString(sqlType) {
|
|
259
296
|
return stringLikeSQLTypeStrings.includes(sqlType);
|
|
@@ -262,7 +299,7 @@ const IDLikeSQLTypeStrings = ["uuid"];
|
|
|
262
299
|
function isIDLikeSQLTypeString(sqlType) {
|
|
263
300
|
return IDLikeSQLTypeStrings.includes(sqlType);
|
|
264
301
|
}
|
|
265
|
-
const booleanSQLTypeStrings = ["boolean"];
|
|
302
|
+
const booleanSQLTypeStrings = ["boolean", "bool"];
|
|
266
303
|
function isBooleanSQLTypeString(sqlType) {
|
|
267
304
|
return booleanSQLTypeStrings.includes(sqlType);
|
|
268
305
|
}
|
|
@@ -278,16 +315,23 @@ const jsonLikeSQLTypeStrings = ["json", "jsonb"];
|
|
|
278
315
|
function isJSONLikeSQLTypeString(sqlType) {
|
|
279
316
|
return jsonLikeSQLTypeStrings.includes(sqlType);
|
|
280
317
|
}
|
|
281
|
-
[
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
318
|
+
const jsonFallbackSQLTypeStrings = [
|
|
319
|
+
"point",
|
|
320
|
+
"line",
|
|
321
|
+
"lseg",
|
|
322
|
+
"box",
|
|
323
|
+
"path",
|
|
324
|
+
"polygon",
|
|
325
|
+
"circle",
|
|
326
|
+
"geometry"
|
|
290
327
|
];
|
|
328
|
+
function isJSONFallbackSQLTypeString(sqlType) {
|
|
329
|
+
return jsonFallbackSQLTypeStrings.includes(sqlType) || sqlType.startsWith("geometry");
|
|
330
|
+
}
|
|
331
|
+
const bytesSQLTypeStrings = ["bytea", "blob"];
|
|
332
|
+
function isBytesSQLTypeString(sqlType) {
|
|
333
|
+
return bytesSQLTypeStrings.includes(sqlType);
|
|
334
|
+
}
|
|
291
335
|
const UnknownTypeRumbleError = (sqlType, additionalInfo) => new RumbleError(`RumbleError: Unknown SQL type '${sqlType}'. Please open an issue (https://github.com/m1212e/rumble/issues) so it can be added. (${additionalInfo})`);
|
|
292
336
|
//#endregion
|
|
293
337
|
//#region lib/helpers/sqlTypes/distinctValuesFromSQLType.ts
|
|
@@ -402,16 +446,17 @@ function isDynamicQueryFilter(filter) {
|
|
|
402
446
|
function isStaticQueryFilter(filter) {
|
|
403
447
|
return typeof filter !== "function";
|
|
404
448
|
}
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
`);
|
|
449
|
+
const makeNothingRegisteredWarner = (logger) => (0, es_toolkit.debounce)((model, action) => {
|
|
450
|
+
const msg = `No abilities registered for ${model}/${action} — blocking everything. Register the ability or ignore this warning if intentional.`;
|
|
451
|
+
if (logger) logger.warn({
|
|
452
|
+
"rumble.table": model,
|
|
453
|
+
"rumble.action": action
|
|
454
|
+
}, msg);
|
|
455
|
+
else console.warn(msg);
|
|
413
456
|
}, 1e3);
|
|
414
|
-
const createAbilityBuilder = ({ db, actions, defaultLimit, otel }) => {
|
|
457
|
+
const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerConfig }) => {
|
|
458
|
+
const log = loggerConfig?.enabled ? loggerConfig.logger : void 0;
|
|
459
|
+
const nothingRegisteredWarningLogger = makeNothingRegisteredWarner(log);
|
|
415
460
|
let hasBeenBuilt = false;
|
|
416
461
|
const createBuilderForTable = () => {
|
|
417
462
|
const queryFilters = /* @__PURE__ */ new Map();
|
|
@@ -701,14 +746,38 @@ by: (explicitFilter) => {
|
|
|
701
746
|
span?.setAttribute("abilities.status", "applied");
|
|
702
747
|
return transformToResponse(mergedFilters);
|
|
703
748
|
};
|
|
704
|
-
if (otel?.enabled && otel.tracer) return otel.tracer.startActiveSpan(`
|
|
749
|
+
if (otel?.enabled && otel.tracer) return otel.tracer.startActiveSpan(`rumble.abilities.prepare`, (span) => {
|
|
750
|
+
span.setAttribute("rumble.action", action);
|
|
751
|
+
span.setAttribute("rumble.table", tableName.toString());
|
|
705
752
|
try {
|
|
706
|
-
|
|
753
|
+
const attrs = {
|
|
754
|
+
"rumble.table": tableName.toString(),
|
|
755
|
+
"rumble.action": action
|
|
756
|
+
};
|
|
757
|
+
const result = assembleAbilities(new Proxy(span, { get(target, prop) {
|
|
758
|
+
if (prop === "setAttribute") return (k, v) => {
|
|
759
|
+
attrs[k] = v;
|
|
760
|
+
return target.setAttribute(k, v);
|
|
761
|
+
};
|
|
762
|
+
return target[prop];
|
|
763
|
+
} }));
|
|
764
|
+
log?.debug(attrs, "abilities prepared");
|
|
765
|
+
return result;
|
|
707
766
|
} finally {
|
|
708
767
|
span.end();
|
|
709
768
|
}
|
|
710
769
|
});
|
|
711
|
-
else
|
|
770
|
+
else {
|
|
771
|
+
const attrs = {
|
|
772
|
+
"rumble.table": tableName.toString(),
|
|
773
|
+
"rumble.action": action
|
|
774
|
+
};
|
|
775
|
+
const result = assembleAbilities(log ? { setAttribute: (k, v) => {
|
|
776
|
+
attrs[k] = v;
|
|
777
|
+
} } : void 0);
|
|
778
|
+
log?.debug(attrs, "abilities prepared");
|
|
779
|
+
return result;
|
|
780
|
+
}
|
|
712
781
|
} };
|
|
713
782
|
} };
|
|
714
783
|
};
|
|
@@ -737,29 +806,14 @@ const createOrderArgImplementer = ({ db, schemaBuilder }) => {
|
|
|
737
806
|
if (ret) return ret;
|
|
738
807
|
const implement = () => {
|
|
739
808
|
return schemaBuilder.inputType(inputTypeName, { fields: (t) => {
|
|
740
|
-
|
|
809
|
+
return Object.entries(tableSchema.columns).reduce((acc, [key]) => {
|
|
810
|
+
if (key.startsWith("__")) return acc;
|
|
741
811
|
acc[key] = t.field({
|
|
742
812
|
type: sortingParameterEnumRef(),
|
|
743
813
|
required: false
|
|
744
814
|
});
|
|
745
815
|
return acc;
|
|
746
816
|
}, {});
|
|
747
|
-
const relations = Object.entries(tableSchema.relations ?? {}).reduce((acc, [key, value]) => {
|
|
748
|
-
const targetTsName = value.targetTableName;
|
|
749
|
-
const referenceModel = orderArgImplementer({ dbName: tableHelper({
|
|
750
|
-
db,
|
|
751
|
-
table: targetTsName
|
|
752
|
-
}).dbName });
|
|
753
|
-
acc[key] = t.field({
|
|
754
|
-
type: referenceModel,
|
|
755
|
-
required: false
|
|
756
|
-
});
|
|
757
|
-
return acc;
|
|
758
|
-
}, {});
|
|
759
|
-
return {
|
|
760
|
-
...fields,
|
|
761
|
-
...relations
|
|
762
|
-
};
|
|
763
817
|
} });
|
|
764
818
|
};
|
|
765
819
|
ret = implement();
|
|
@@ -771,10 +825,10 @@ const createOrderArgImplementer = ({ db, schemaBuilder }) => {
|
|
|
771
825
|
//#endregion
|
|
772
826
|
//#region lib/enum.ts
|
|
773
827
|
/**
|
|
774
|
-
* Checks if a column is a
|
|
828
|
+
* Checks if a column is a database enum column (Postgres or MySQL).
|
|
775
829
|
*/
|
|
776
830
|
function isEnumSchema(schemaType) {
|
|
777
|
-
return (0, drizzle_orm.is)(schemaType, drizzle_orm_pg_core.PgEnumColumn) || (0, drizzle_orm.is)(schemaType, drizzle_orm_pg_core.PgEnumObjectColumn);
|
|
831
|
+
return (0, drizzle_orm.is)(schemaType, drizzle_orm_pg_core.PgEnumColumn) || (0, drizzle_orm.is)(schemaType, drizzle_orm_pg_core.PgEnumObjectColumn) || (0, drizzle_orm.is)(schemaType, drizzle_orm_mysql_core.MySqlEnumColumn) || (0, drizzle_orm.is)(schemaType, drizzle_orm_mysql_core.MySqlEnumObjectColumn);
|
|
778
832
|
}
|
|
779
833
|
const createEnumImplementer = ({ schema, schemaBuilder }) => {
|
|
780
834
|
const referenceStorage = /* @__PURE__ */ new Map();
|
|
@@ -806,7 +860,12 @@ const createEnumImplementer = ({ schema, schemaBuilder }) => {
|
|
|
806
860
|
if (enumObject) {
|
|
807
861
|
enumSchemaName = enumObject.enumName;
|
|
808
862
|
enumValues = enumObject.enumValues;
|
|
809
|
-
} else if (enumColumn) {
|
|
863
|
+
} else if (enumColumn) if ((0, drizzle_orm.is)(enumColumn, drizzle_orm_mysql_core.MySqlEnumColumn) || (0, drizzle_orm.is)(enumColumn, drizzle_orm_mysql_core.MySqlEnumObjectColumn)) {
|
|
864
|
+
enumValues = enumColumn.enumValues;
|
|
865
|
+
const colTable = enumColumn.table;
|
|
866
|
+
const tableName = colTable && (0, drizzle_orm.isTable)(colTable) ? (0, drizzle_orm.getTableName)(colTable) : void 0;
|
|
867
|
+
enumSchemaName = tableName ? `${tableName}_${enumColumn.name}` : enumColumn.name;
|
|
868
|
+
} else {
|
|
810
869
|
const pgEnum = enumColumn.enum;
|
|
811
870
|
if (!pgEnum) throw new RumbleError(`Could not find enum definition on column. Make sure the column is a pgEnum column.`);
|
|
812
871
|
enumSchemaName = pgEnum.enumName;
|
|
@@ -825,16 +884,21 @@ const createEnumImplementer = ({ schema, schemaBuilder }) => {
|
|
|
825
884
|
//#endregion
|
|
826
885
|
//#region lib/helpers/sqlTypes/mapSQLTypeToTSType.ts
|
|
827
886
|
function mapSQLTypeToGraphQLType({ sqlType, fieldName }) {
|
|
887
|
+
if (/^enum\(/.test(sqlType)) return "String";
|
|
888
|
+
const normalized = normalizeSQLType(sqlType);
|
|
828
889
|
let ret;
|
|
829
|
-
if (isIntLikeSQLTypeString(
|
|
830
|
-
if (
|
|
831
|
-
if (
|
|
890
|
+
if (isIntLikeSQLTypeString(normalized)) ret = "Int";
|
|
891
|
+
if (isBigIntLikeSQLTypeString(normalized)) ret = "BigInt";
|
|
892
|
+
if (isFloatLikeSQLTypeString(normalized)) ret = "Float";
|
|
893
|
+
if (isStringLikeSQLTypeString(normalized)) if (fieldName && (fieldName.toLowerCase().endsWith("_id") || fieldName.toLowerCase().endsWith("id"))) ret = "ID";
|
|
832
894
|
else ret = "String";
|
|
833
|
-
if (isIDLikeSQLTypeString(
|
|
834
|
-
if (isBooleanSQLTypeString(
|
|
835
|
-
if (isDateTimeLikeSQLTypeString(
|
|
836
|
-
if (isDateLikeSQLTypeString(
|
|
837
|
-
if (isJSONLikeSQLTypeString(
|
|
895
|
+
if (isIDLikeSQLTypeString(normalized)) ret = "ID";
|
|
896
|
+
if (isBooleanSQLTypeString(normalized)) ret = "Boolean";
|
|
897
|
+
if (isDateTimeLikeSQLTypeString(normalized)) ret = "DateTime";
|
|
898
|
+
if (isDateLikeSQLTypeString(normalized)) ret = "Date";
|
|
899
|
+
if (isJSONLikeSQLTypeString(normalized)) ret = "JSON";
|
|
900
|
+
if (isJSONFallbackSQLTypeString(normalized)) ret = "JSON";
|
|
901
|
+
if (isBytesSQLTypeString(normalized)) ret = "Bytes";
|
|
838
902
|
if (ret !== void 0) return ret;
|
|
839
903
|
throw UnknownTypeRumbleError(sqlType, "SQL to GQL");
|
|
840
904
|
}
|
|
@@ -844,6 +908,60 @@ const makeDefaultName = (dbName) => `${(0, es_toolkit.capitalize)((0, drizzle_or
|
|
|
844
908
|
const createWhereArgImplementer = ({ db, schemaBuilder, enumImplementer }) => {
|
|
845
909
|
const referenceStorage = /* @__PURE__ */ new Map();
|
|
846
910
|
const enumArrayWhereInputStorage = /* @__PURE__ */ new Map();
|
|
911
|
+
const scalarArrayWhereInputStorage = /* @__PURE__ */ new Map();
|
|
912
|
+
const scalarArrayWhereInputName = (gqlType) => `${gqlType}ArrayWhereInputArgument`;
|
|
913
|
+
const getScalarArrayWhereInput = (gqlType) => {
|
|
914
|
+
const typeName = scalarArrayWhereInputName(gqlType);
|
|
915
|
+
const existing = scalarArrayWhereInputStorage.get(typeName);
|
|
916
|
+
if (existing) return existing;
|
|
917
|
+
const ret = schemaBuilder.inputRef(typeName);
|
|
918
|
+
scalarArrayWhereInputStorage.set(typeName, ret);
|
|
919
|
+
ret.implement({ fields: (t) => ({
|
|
920
|
+
eq: t.field({
|
|
921
|
+
type: [gqlType],
|
|
922
|
+
required: false
|
|
923
|
+
}),
|
|
924
|
+
ne: t.field({
|
|
925
|
+
type: [gqlType],
|
|
926
|
+
required: false
|
|
927
|
+
}),
|
|
928
|
+
in: t.field({
|
|
929
|
+
type: [[gqlType]],
|
|
930
|
+
required: false
|
|
931
|
+
}),
|
|
932
|
+
notIn: t.field({
|
|
933
|
+
type: [[gqlType]],
|
|
934
|
+
required: false
|
|
935
|
+
}),
|
|
936
|
+
isNull: t.boolean({ required: false }),
|
|
937
|
+
isNotNull: t.boolean({ required: false }),
|
|
938
|
+
arrayOverlaps: t.field({
|
|
939
|
+
type: [gqlType],
|
|
940
|
+
required: false
|
|
941
|
+
}),
|
|
942
|
+
arrayContained: t.field({
|
|
943
|
+
type: [gqlType],
|
|
944
|
+
required: false
|
|
945
|
+
}),
|
|
946
|
+
arrayContains: t.field({
|
|
947
|
+
type: [gqlType],
|
|
948
|
+
required: false
|
|
949
|
+
}),
|
|
950
|
+
AND: t.field({
|
|
951
|
+
type: [ret],
|
|
952
|
+
required: false
|
|
953
|
+
}),
|
|
954
|
+
OR: t.field({
|
|
955
|
+
type: [ret],
|
|
956
|
+
required: false
|
|
957
|
+
}),
|
|
958
|
+
NOT: t.field({
|
|
959
|
+
type: ret,
|
|
960
|
+
required: false
|
|
961
|
+
})
|
|
962
|
+
}) });
|
|
963
|
+
return ret;
|
|
964
|
+
};
|
|
847
965
|
const getEnumArrayWhereInput = (enumImpl) => {
|
|
848
966
|
const typeName = `${enumImpl.name}WhereInputArgument`;
|
|
849
967
|
const existing = enumArrayWhereInputStorage.get(typeName);
|
|
@@ -907,31 +1025,73 @@ const createWhereArgImplementer = ({ db, schemaBuilder, enumImplementer }) => {
|
|
|
907
1025
|
const ret = schemaBuilder.inputRef(inputTypeName);
|
|
908
1026
|
referenceStorage.set(inputTypeName, ret);
|
|
909
1027
|
ret.implement({ fields: (t) => {
|
|
910
|
-
const mapSQLTypeStringToInputPothosType = (sqlType, fieldName) => {
|
|
1028
|
+
const mapSQLTypeStringToInputPothosType = (sqlType, fieldName, isArray) => {
|
|
911
1029
|
const gqlType = mapSQLTypeToGraphQLType({
|
|
912
1030
|
sqlType,
|
|
913
1031
|
fieldName
|
|
914
1032
|
});
|
|
1033
|
+
if (isArray) return t.field({
|
|
1034
|
+
type: getScalarArrayWhereInput(gqlType),
|
|
1035
|
+
required: false
|
|
1036
|
+
});
|
|
915
1037
|
switch (gqlType) {
|
|
916
|
-
case "Int": return t.field({
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
case "
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1038
|
+
case "Int": return t.field({
|
|
1039
|
+
type: "IntWhereInputArgument",
|
|
1040
|
+
required: false
|
|
1041
|
+
});
|
|
1042
|
+
case "BigInt": return t.field({
|
|
1043
|
+
type: "BigIntWhereInputArgument",
|
|
1044
|
+
required: false
|
|
1045
|
+
});
|
|
1046
|
+
case "String": return t.field({
|
|
1047
|
+
type: "StringWhereInputArgument",
|
|
1048
|
+
required: false
|
|
1049
|
+
});
|
|
1050
|
+
case "Boolean": return t.field({
|
|
1051
|
+
type: "BooleanWhereInputArgument",
|
|
1052
|
+
required: false
|
|
1053
|
+
});
|
|
1054
|
+
case "Date": return t.field({
|
|
1055
|
+
type: "DateWhereInputArgument",
|
|
1056
|
+
required: false
|
|
1057
|
+
});
|
|
1058
|
+
case "DateTime": return t.field({
|
|
1059
|
+
type: "DateTimeWhereInputArgument",
|
|
1060
|
+
required: false
|
|
1061
|
+
});
|
|
1062
|
+
case "Float": return t.field({
|
|
1063
|
+
type: "FloatWhereInputArgument",
|
|
1064
|
+
required: false
|
|
1065
|
+
});
|
|
1066
|
+
case "ID": return t.field({
|
|
1067
|
+
type: "IDWhereInputArgument",
|
|
1068
|
+
required: false
|
|
1069
|
+
});
|
|
1070
|
+
case "JSON": return t.field({
|
|
1071
|
+
type: "JSONWhereInputArgument",
|
|
1072
|
+
required: false
|
|
1073
|
+
});
|
|
1074
|
+
case "Bytes": return null;
|
|
924
1075
|
default: throw new RumbleError(`Unsupported argument type ${gqlType} for column ${sqlType}`);
|
|
925
1076
|
}
|
|
926
1077
|
};
|
|
927
1078
|
const fields = Object.entries(tableSchema.columns).reduce((acc, [key, value]) => {
|
|
1079
|
+
if (key.startsWith("__")) return acc;
|
|
1080
|
+
let isArray = false;
|
|
1081
|
+
if ((0, drizzle_orm.is)(value, drizzle_orm_pg_core.PgColumn)) {
|
|
1082
|
+
const dims = value.dimensions;
|
|
1083
|
+
if (dims && dims > 0) isArray = true;
|
|
1084
|
+
}
|
|
928
1085
|
if (isEnumSchema(value)) {
|
|
929
1086
|
const enumImpl = enumImplementer({ enumColumn: value });
|
|
930
1087
|
acc[key] = t.field({
|
|
931
|
-
type:
|
|
1088
|
+
type: isArray ? getEnumArrayWhereInput(enumImpl) : enumImpl,
|
|
932
1089
|
required: false
|
|
933
1090
|
});
|
|
934
|
-
} else
|
|
1091
|
+
} else {
|
|
1092
|
+
const field = mapSQLTypeStringToInputPothosType(value.getSQLType(), key, isArray);
|
|
1093
|
+
if (field !== null) acc[key] = field;
|
|
1094
|
+
}
|
|
935
1095
|
return acc;
|
|
936
1096
|
}, {});
|
|
937
1097
|
const relations = Object.entries(tableSchema.relations ?? {}).reduce((acc, [key, value]) => {
|
|
@@ -1145,6 +1305,11 @@ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, n
|
|
|
1145
1305
|
});
|
|
1146
1306
|
switch (gqlType) {
|
|
1147
1307
|
case "Int": return isArray ? builder.exposeIntList(fieldName, { nullable }) : builder.exposeInt(fieldName, { nullable });
|
|
1308
|
+
case "BigInt": return builder.field({
|
|
1309
|
+
type: isArray ? ["BigInt"] : "BigInt",
|
|
1310
|
+
resolve: (element) => element[fieldName],
|
|
1311
|
+
nullable
|
|
1312
|
+
});
|
|
1148
1313
|
case "String": return isArray ? builder.exposeStringList(fieldName, { nullable }) : builder.exposeString(fieldName, { nullable });
|
|
1149
1314
|
case "Boolean": return isArray ? builder.exposeBooleanList(fieldName, { nullable }) : builder.exposeBoolean(fieldName, { nullable });
|
|
1150
1315
|
case "Date": return builder.field({
|
|
@@ -1154,7 +1319,11 @@ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, n
|
|
|
1154
1319
|
});
|
|
1155
1320
|
case "DateTime": return builder.field({
|
|
1156
1321
|
type: isArray ? ["DateTime"] : "DateTime",
|
|
1157
|
-
resolve: (element) =>
|
|
1322
|
+
resolve: (element) => {
|
|
1323
|
+
const v = element[fieldName];
|
|
1324
|
+
if (v == null) return v;
|
|
1325
|
+
return v instanceof Date ? v : new Date(v);
|
|
1326
|
+
},
|
|
1158
1327
|
nullable
|
|
1159
1328
|
});
|
|
1160
1329
|
case "Float": return isArray ? builder.exposeFloatList(fieldName, { nullable }) : builder.exposeFloat(fieldName, { nullable });
|
|
@@ -1164,6 +1333,15 @@ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, n
|
|
|
1164
1333
|
resolve: (element) => element[fieldName],
|
|
1165
1334
|
nullable
|
|
1166
1335
|
});
|
|
1336
|
+
case "Bytes": return builder.field({
|
|
1337
|
+
type: "Bytes",
|
|
1338
|
+
resolve: (element) => {
|
|
1339
|
+
const v = element[fieldName];
|
|
1340
|
+
if (v == null) return v;
|
|
1341
|
+
return Buffer.isBuffer(v) ? v.toString("base64") : String(v);
|
|
1342
|
+
},
|
|
1343
|
+
nullable
|
|
1344
|
+
});
|
|
1167
1345
|
default: throw new RumbleError(`Unsupported object type ${gqlType} for column ${fieldName}`);
|
|
1168
1346
|
}
|
|
1169
1347
|
}
|
|
@@ -1195,14 +1373,17 @@ function isPostgresDB(db) {
|
|
|
1195
1373
|
//#endregion
|
|
1196
1374
|
//#region lib/search.ts
|
|
1197
1375
|
async function initSearchIfApplicable(input) {
|
|
1376
|
+
const log = input.logger?.enabled ? input.logger.logger : void 0;
|
|
1198
1377
|
if (!isPostgresDB(input.db)) {
|
|
1199
|
-
|
|
1378
|
+
const msg = "Database dialect is not compatible with search, skipping search initialization. Only PostgreSQL is supported.";
|
|
1379
|
+
log ? log.info({}, msg) : console.info(msg);
|
|
1200
1380
|
return;
|
|
1201
1381
|
}
|
|
1202
1382
|
try {
|
|
1203
1383
|
await input.db.execute(drizzle_orm.sql`CREATE EXTENSION IF NOT EXISTS pg_trgm;`);
|
|
1204
1384
|
} catch (error) {
|
|
1205
|
-
|
|
1385
|
+
const msg = "Failed to create pg_trgm extension. Search functionality may not work. Ensure the database user has CREATE privilege or the extension is pre-installed.";
|
|
1386
|
+
log ? log.error({ err: error }, msg) : console.error(msg, error);
|
|
1206
1387
|
return;
|
|
1207
1388
|
}
|
|
1208
1389
|
if (input.search?.threshold) {
|
|
@@ -1261,13 +1442,17 @@ const isProbablyAConfigObject = (t) => {
|
|
|
1261
1442
|
].find((e) => e === k))) return true;
|
|
1262
1443
|
return false;
|
|
1263
1444
|
};
|
|
1264
|
-
const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance, whereArgImplementer, orderArgImplementer, enumImplementer, abilityBuilder }) => {
|
|
1445
|
+
const createObjectImplementer = ({ db, search, logger: loggerConfig, schemaBuilder, makePubSubInstance, whereArgImplementer, orderArgImplementer, enumImplementer, abilityBuilder }) => {
|
|
1265
1446
|
return ({ table, refName, readAction = "read", adjust }) => {
|
|
1266
1447
|
const tableSchema = tableHelper({
|
|
1267
1448
|
db,
|
|
1268
1449
|
table
|
|
1269
1450
|
});
|
|
1270
|
-
if (Object.keys(tableSchema.primaryKey).length === 0)
|
|
1451
|
+
if (Object.keys(tableSchema.primaryKey).length === 0) {
|
|
1452
|
+
const log = loggerConfig?.enabled ? loggerConfig.logger : void 0;
|
|
1453
|
+
const msg = `Could not find primary key for ${table.toString()}. Cannot register subscriptions!`;
|
|
1454
|
+
log ? log.warn({ "rumble.table": table.toString() }, msg) : console.warn(msg);
|
|
1455
|
+
}
|
|
1271
1456
|
const primaryKey = Object.values(tableSchema.primaryKey)[0];
|
|
1272
1457
|
const { registerOnInstance } = makePubSubInstance({ table });
|
|
1273
1458
|
return schemaBuilder.drizzleObject(table, {
|
|
@@ -1276,7 +1461,9 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
|
|
|
1276
1461
|
if (!primaryKey) return;
|
|
1277
1462
|
const primaryKeyValue = element[primaryKey.name];
|
|
1278
1463
|
if (!primaryKeyValue) {
|
|
1279
|
-
|
|
1464
|
+
const log = loggerConfig?.enabled ? loggerConfig.logger : void 0;
|
|
1465
|
+
const msg = `Could not find primary key value for element on ${table.toString()}. Cannot register subscription!`;
|
|
1466
|
+
log ? log.warn({ "rumble.table": table.toString() }, msg) : console.warn(msg);
|
|
1280
1467
|
return;
|
|
1281
1468
|
}
|
|
1282
1469
|
registerOnInstance({
|
|
@@ -1307,6 +1494,10 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
|
|
|
1307
1494
|
};
|
|
1308
1495
|
} })) ?? {};
|
|
1309
1496
|
const fields = Object.entries(columns).reduce((acc, [key, column]) => {
|
|
1497
|
+
if (key.startsWith("__")) {
|
|
1498
|
+
console.warn(`Skipping column "${key}" on table "${tableSchema.tsName}": names starting with "__" are reserved by GraphQL introspection. Use a custom adjust() call if you really need to expose this column.`);
|
|
1499
|
+
return acc;
|
|
1500
|
+
}
|
|
1310
1501
|
if (userAdjustments[key]) {
|
|
1311
1502
|
const { params, creatorFunction, configObject } = configMap.get(userAdjustments[key]);
|
|
1312
1503
|
if (typeof configObject.nullable !== "boolean") configObject.nullable = !column.notNull;
|
|
@@ -1507,7 +1698,8 @@ const createPubSubInstance = ({ subscriptions }) => {
|
|
|
1507
1698
|
};
|
|
1508
1699
|
//#endregion
|
|
1509
1700
|
//#region lib/query.ts
|
|
1510
|
-
const createQueryImplementer = ({ db, schemaBuilder, search, whereArgImplementer, orderArgImplementer, makePubSubInstance }) => {
|
|
1701
|
+
const createQueryImplementer = ({ db, schemaBuilder, search, logger: loggerConfig, whereArgImplementer, orderArgImplementer, makePubSubInstance }) => {
|
|
1702
|
+
const registeredQueryFieldNames = /* @__PURE__ */ new Set();
|
|
1511
1703
|
return ({ table, readAction = "read", listAction = "read" }) => {
|
|
1512
1704
|
const WhereArg = whereArgImplementer({ table });
|
|
1513
1705
|
const OrderArg = orderArgImplementer({ table });
|
|
@@ -1517,6 +1709,12 @@ const createQueryImplementer = ({ db, schemaBuilder, search, whereArgImplementer
|
|
|
1517
1709
|
});
|
|
1518
1710
|
const primaryKeyField = Object.values(tableSchema.primaryKey)[0];
|
|
1519
1711
|
const { registerOnInstance } = makePubSubInstance({ table });
|
|
1712
|
+
const pluralName = pluralize.default.plural(table.toString());
|
|
1713
|
+
const singularName = pluralize.default.singular(table.toString());
|
|
1714
|
+
for (const name of [pluralName, singularName]) {
|
|
1715
|
+
if (registeredQueryFieldNames.has(name)) throw new RumbleError(`Duplicate query field "${name}": the table "${table.toString()}" generates a query field that collides with one already registered. Use unique table names (or refNames) to avoid plural/singular collisions (e.g. "post" and "posts").`);
|
|
1716
|
+
registeredQueryFieldNames.add(name);
|
|
1717
|
+
}
|
|
1520
1718
|
return schemaBuilder.queryFields((t) => {
|
|
1521
1719
|
const manyArgs = {
|
|
1522
1720
|
where: t.arg({
|
|
@@ -1533,11 +1731,11 @@ const createQueryImplementer = ({ db, schemaBuilder, search, whereArgImplementer
|
|
|
1533
1731
|
};
|
|
1534
1732
|
if (!search?.enabled) delete manyArgs.search;
|
|
1535
1733
|
return {
|
|
1536
|
-
[
|
|
1734
|
+
[pluralName]: t.drizzleField({
|
|
1537
1735
|
type: [table],
|
|
1538
1736
|
nullable: false,
|
|
1539
1737
|
smartSubscription: true,
|
|
1540
|
-
description: `List all ${
|
|
1738
|
+
description: `List all ${pluralName}`,
|
|
1541
1739
|
subscribe: (subscriptions, _root, _args, _ctx, _info) => {
|
|
1542
1740
|
registerOnInstance({
|
|
1543
1741
|
instance: subscriptions,
|
|
@@ -1565,17 +1763,21 @@ const createQueryImplementer = ({ db, schemaBuilder, search, whereArgImplementer
|
|
|
1565
1763
|
if (filter.columns) queryInstance.columns = filter.columns;
|
|
1566
1764
|
if (search?.enabled && args.search && args.search.length > 0 && search?.cpu_operator_cost) return db.transaction(async (tx) => {
|
|
1567
1765
|
if (isPostgresDB(tx)) await tx.execute(drizzle_orm.sql.raw(`SET cpu_operator_cost = ${search.cpu_operator_cost};`));
|
|
1568
|
-
else
|
|
1766
|
+
else {
|
|
1767
|
+
const msg = "Database dialect is not postgresql, cannot set cpu_operator_cost.";
|
|
1768
|
+
const log = loggerConfig?.enabled ? loggerConfig.logger : void 0;
|
|
1769
|
+
log ? log.info({}, msg) : console.info(msg);
|
|
1770
|
+
}
|
|
1569
1771
|
return tx.query[table].findMany(queryInstance);
|
|
1570
1772
|
});
|
|
1571
1773
|
return db.query[table].findMany(queryInstance);
|
|
1572
1774
|
}
|
|
1573
1775
|
}),
|
|
1574
|
-
[
|
|
1776
|
+
[singularName]: t.drizzleField({
|
|
1575
1777
|
type: table,
|
|
1576
1778
|
nullable: false,
|
|
1577
1779
|
smartSubscription: true,
|
|
1578
|
-
description: `Get a single ${
|
|
1780
|
+
description: `Get a single ${singularName} by ID`,
|
|
1579
1781
|
args: { id: t.arg.id({ required: true }) },
|
|
1580
1782
|
resolve: (query, _root, args, ctx, _info) => {
|
|
1581
1783
|
deepSetProto(args);
|
|
@@ -1751,6 +1953,96 @@ function implementDefaultWhereInputArgs(schemaBuilder) {
|
|
|
1751
1953
|
OR: t.field({ type: [JSONWhereInputArgument] }),
|
|
1752
1954
|
NOT: t.field({ type: JSONWhereInputArgument })
|
|
1753
1955
|
}) });
|
|
1956
|
+
const BigIntWhereInputArgument = schemaBuilder.inputRef("BigIntWhereInputArgument").implement({ fields: (t) => ({
|
|
1957
|
+
eq: t.field({
|
|
1958
|
+
type: "BigInt",
|
|
1959
|
+
required: false
|
|
1960
|
+
}),
|
|
1961
|
+
ne: t.field({
|
|
1962
|
+
type: "BigInt",
|
|
1963
|
+
required: false
|
|
1964
|
+
}),
|
|
1965
|
+
gt: t.field({
|
|
1966
|
+
type: "BigInt",
|
|
1967
|
+
required: false
|
|
1968
|
+
}),
|
|
1969
|
+
gte: t.field({
|
|
1970
|
+
type: "BigInt",
|
|
1971
|
+
required: false
|
|
1972
|
+
}),
|
|
1973
|
+
lt: t.field({
|
|
1974
|
+
type: "BigInt",
|
|
1975
|
+
required: false
|
|
1976
|
+
}),
|
|
1977
|
+
lte: t.field({
|
|
1978
|
+
type: "BigInt",
|
|
1979
|
+
required: false
|
|
1980
|
+
}),
|
|
1981
|
+
in: t.field({
|
|
1982
|
+
type: ["BigInt"],
|
|
1983
|
+
required: false
|
|
1984
|
+
}),
|
|
1985
|
+
notIn: t.field({
|
|
1986
|
+
type: ["BigInt"],
|
|
1987
|
+
required: false
|
|
1988
|
+
}),
|
|
1989
|
+
isNull: t.boolean(),
|
|
1990
|
+
isNotNull: t.boolean(),
|
|
1991
|
+
AND: t.field({ type: [BigIntWhereInputArgument] }),
|
|
1992
|
+
OR: t.field({ type: [BigIntWhereInputArgument] }),
|
|
1993
|
+
NOT: t.field({ type: BigIntWhereInputArgument })
|
|
1994
|
+
}) });
|
|
1995
|
+
const DateTimeWhereInputArgument = schemaBuilder.inputRef("DateTimeWhereInputArgument").implement({ fields: (t) => ({
|
|
1996
|
+
eq: t.field({
|
|
1997
|
+
type: "DateTime",
|
|
1998
|
+
required: false
|
|
1999
|
+
}),
|
|
2000
|
+
ne: t.field({
|
|
2001
|
+
type: "DateTime",
|
|
2002
|
+
required: false
|
|
2003
|
+
}),
|
|
2004
|
+
gt: t.field({
|
|
2005
|
+
type: "DateTime",
|
|
2006
|
+
required: false
|
|
2007
|
+
}),
|
|
2008
|
+
gte: t.field({
|
|
2009
|
+
type: "DateTime",
|
|
2010
|
+
required: false
|
|
2011
|
+
}),
|
|
2012
|
+
lt: t.field({
|
|
2013
|
+
type: "DateTime",
|
|
2014
|
+
required: false
|
|
2015
|
+
}),
|
|
2016
|
+
lte: t.field({
|
|
2017
|
+
type: "DateTime",
|
|
2018
|
+
required: false
|
|
2019
|
+
}),
|
|
2020
|
+
in: t.field({
|
|
2021
|
+
type: ["DateTime"],
|
|
2022
|
+
required: false
|
|
2023
|
+
}),
|
|
2024
|
+
notIn: t.field({
|
|
2025
|
+
type: ["DateTime"],
|
|
2026
|
+
required: false
|
|
2027
|
+
}),
|
|
2028
|
+
isNull: t.boolean(),
|
|
2029
|
+
isNotNull: t.boolean(),
|
|
2030
|
+
arrayOverlaps: t.field({
|
|
2031
|
+
type: ["DateTime"],
|
|
2032
|
+
required: false
|
|
2033
|
+
}),
|
|
2034
|
+
arrayContained: t.field({
|
|
2035
|
+
type: ["DateTime"],
|
|
2036
|
+
required: false
|
|
2037
|
+
}),
|
|
2038
|
+
arrayContains: t.field({
|
|
2039
|
+
type: ["DateTime"],
|
|
2040
|
+
required: false
|
|
2041
|
+
}),
|
|
2042
|
+
AND: t.field({ type: [DateTimeWhereInputArgument] }),
|
|
2043
|
+
OR: t.field({ type: [DateTimeWhereInputArgument] }),
|
|
2044
|
+
NOT: t.field({ type: DateTimeWhereInputArgument })
|
|
2045
|
+
}) });
|
|
1754
2046
|
}
|
|
1755
2047
|
//#endregion
|
|
1756
2048
|
//#region lib/runtimeFiltersPlugin/filterTypes.ts
|
|
@@ -1761,9 +2053,11 @@ const applyFiltersKey = "applyFilters";
|
|
|
1761
2053
|
var RuntimeFiltersPlugin = class extends _pothos_core.BasePlugin {
|
|
1762
2054
|
tracer;
|
|
1763
2055
|
tracerEnabled;
|
|
2056
|
+
logger;
|
|
1764
2057
|
onTypeConfig(typeConfig) {
|
|
1765
2058
|
this.tracer = this.builder.options.otel?.tracer;
|
|
1766
2059
|
this.tracerEnabled = this.builder.options.otel?.enabled;
|
|
2060
|
+
this.logger = this.builder.options.logger?.enabled ? this.builder.options.logger.logger : void 0;
|
|
1767
2061
|
return typeConfig;
|
|
1768
2062
|
}
|
|
1769
2063
|
wrapResolve(resolver, fieldConfig) {
|
|
@@ -1792,7 +2086,8 @@ var RuntimeFiltersPlugin = class extends _pothos_core.BasePlugin {
|
|
|
1792
2086
|
}));
|
|
1793
2087
|
let resolved, prefetchedFilters;
|
|
1794
2088
|
if (this.tracer && this.tracerEnabled) {
|
|
1795
|
-
const o = await this.tracer.startActiveSpan(`
|
|
2089
|
+
const o = await this.tracer.startActiveSpan(`rumble.filter.resolve`, async (span) => {
|
|
2090
|
+
span.setAttribute("graphql.field.name", fieldConfig.name);
|
|
1796
2091
|
try {
|
|
1797
2092
|
return await Promise.all([resolver(parent, args, context, info), prefetchedFiltersPromises]);
|
|
1798
2093
|
} finally {
|
|
@@ -1814,17 +2109,37 @@ var RuntimeFiltersPlugin = class extends _pothos_core.BasePlugin {
|
|
|
1814
2109
|
return acc;
|
|
1815
2110
|
}, /* @__PURE__ */ new Set()));
|
|
1816
2111
|
span?.setAttribute("filters.allowed", allowed.length);
|
|
2112
|
+
this.logger?.debug({
|
|
2113
|
+
"graphql.field.name": fieldConfig.name,
|
|
2114
|
+
"filters.total": allFilters.length,
|
|
2115
|
+
"filters.allowed": allowed.length
|
|
2116
|
+
}, "runtime filters applied");
|
|
1817
2117
|
if (Array.isArray(resolved)) return allowed;
|
|
1818
2118
|
return allowed[0] ?? null;
|
|
1819
2119
|
};
|
|
1820
|
-
if (this.tracer && this.tracerEnabled) return this.tracer.startActiveSpan(`
|
|
2120
|
+
if (this.tracer && this.tracerEnabled) return this.tracer.startActiveSpan(`rumble.filter.apply`, async (span) => {
|
|
2121
|
+
span.setAttribute("graphql.field.name", fieldConfig.name);
|
|
1821
2122
|
try {
|
|
1822
2123
|
return await runFilters(span);
|
|
2124
|
+
} catch (error) {
|
|
2125
|
+
this.logger?.error({
|
|
2126
|
+
"graphql.field.name": fieldConfig.name,
|
|
2127
|
+
err: error
|
|
2128
|
+
}, "runtime filter threw");
|
|
2129
|
+
throw error;
|
|
1823
2130
|
} finally {
|
|
1824
2131
|
span.end();
|
|
1825
2132
|
}
|
|
1826
2133
|
});
|
|
1827
|
-
else
|
|
2134
|
+
else try {
|
|
2135
|
+
return await runFilters();
|
|
2136
|
+
} catch (error) {
|
|
2137
|
+
this.logger?.error({
|
|
2138
|
+
"graphql.field.name": fieldConfig.name,
|
|
2139
|
+
err: error
|
|
2140
|
+
}, "runtime filter threw");
|
|
2141
|
+
throw error;
|
|
2142
|
+
}
|
|
1828
2143
|
};
|
|
1829
2144
|
}
|
|
1830
2145
|
};
|
|
@@ -1837,7 +2152,7 @@ function registerRuntimeFiltersPlugin() {
|
|
|
1837
2152
|
}
|
|
1838
2153
|
//#endregion
|
|
1839
2154
|
//#region lib/schemaBuilder.ts
|
|
1840
|
-
const createSchemaBuilder = ({ db,
|
|
2155
|
+
const createSchemaBuilder = ({ db, pubsub, pothosConfig, otel, logger }) => {
|
|
1841
2156
|
const createSpan = otel?.enabled && otel.tracer ? (0, _pothos_tracing_opentelemetry.createOpenTelemetryWrapper)(otel.tracer, otel.options) : void 0;
|
|
1842
2157
|
registerRuntimeFiltersPlugin();
|
|
1843
2158
|
const schemaBuilder = new _pothos_core.default({
|
|
@@ -1865,18 +2180,40 @@ const createSchemaBuilder = ({ db, disableDefaultObjects, pubsub, pothosConfig,
|
|
|
1865
2180
|
}) },
|
|
1866
2181
|
defaultFieldNullability: false,
|
|
1867
2182
|
tracing: {
|
|
1868
|
-
default: otel?.enabled ? (config) => (0, _pothos_plugin_tracing.isRootField)(config) : () => false,
|
|
1869
|
-
wrap:
|
|
2183
|
+
default: otel?.enabled || logger?.enabled ? (config) => (0, _pothos_plugin_tracing.isRootField)(config) : () => false,
|
|
2184
|
+
wrap: (resolver, options, config) => {
|
|
2185
|
+
let r = createSpan ? createSpan(resolver, options) : resolver;
|
|
2186
|
+
if (logger?.enabled) {
|
|
2187
|
+
const log = logger.logger;
|
|
2188
|
+
r = (0, _pothos_plugin_tracing.wrapResolver)(r, (error, duration) => {
|
|
2189
|
+
if (error) log.error({
|
|
2190
|
+
"graphql.field.name": config.name,
|
|
2191
|
+
"graphql.parent.type": config.parentType,
|
|
2192
|
+
durationMs: duration,
|
|
2193
|
+
err: error
|
|
2194
|
+
}, "resolver failed");
|
|
2195
|
+
else log.debug({
|
|
2196
|
+
"graphql.field.name": config.name,
|
|
2197
|
+
"graphql.parent.type": config.parentType,
|
|
2198
|
+
durationMs: duration
|
|
2199
|
+
}, "resolver completed");
|
|
2200
|
+
});
|
|
2201
|
+
}
|
|
2202
|
+
return r;
|
|
2203
|
+
}
|
|
1870
2204
|
},
|
|
1871
|
-
otel
|
|
2205
|
+
otel,
|
|
2206
|
+
logger
|
|
1872
2207
|
});
|
|
1873
2208
|
schemaBuilder.addScalarType("JSON", graphql_scalars.JSONResolver);
|
|
1874
2209
|
schemaBuilder.addScalarType("Date", graphql_scalars.DateResolver);
|
|
1875
2210
|
schemaBuilder.addScalarType("DateTime", graphql_scalars.DateTimeISOResolver);
|
|
2211
|
+
schemaBuilder.addScalarType("BigInt", graphql_scalars.BigIntResolver);
|
|
2212
|
+
schemaBuilder.addScalarType("Bytes", graphql_scalars.ByteResolver);
|
|
1876
2213
|
implementDefaultWhereInputArgs(schemaBuilder);
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
2214
|
+
schemaBuilder.queryType({});
|
|
2215
|
+
schemaBuilder.subscriptionType({});
|
|
2216
|
+
schemaBuilder.mutationType({});
|
|
1880
2217
|
return { schemaBuilder };
|
|
1881
2218
|
};
|
|
1882
2219
|
//#endregion
|
|
@@ -1959,7 +2296,33 @@ export const r = rumble({
|
|
|
1959
2296
|
whereArgImplementer: whereArg,
|
|
1960
2297
|
makePubSubInstance
|
|
1961
2298
|
});
|
|
1962
|
-
const builtSchema = require_lazy.lazy(() =>
|
|
2299
|
+
const builtSchema = require_lazy.lazy(() => {
|
|
2300
|
+
let raw;
|
|
2301
|
+
try {
|
|
2302
|
+
raw = schemaBuilder.toSchema();
|
|
2303
|
+
} catch (e) {
|
|
2304
|
+
const unimplMatch = (e?.message ?? String(e)).match(/ObjectRef<(.+?)> has not been implemented/);
|
|
2305
|
+
if (unimplMatch) throw new RumbleError(`Could not build GraphQL schema: the table "${unimplMatch[1]}" is referenced in a relation but has not been registered. Call rumble.object({ table: "${unimplMatch[1]}" }) before building the schema.`);
|
|
2306
|
+
throw e;
|
|
2307
|
+
}
|
|
2308
|
+
const mutation = raw.getMutationType();
|
|
2309
|
+
const subscription = raw.getSubscriptionType();
|
|
2310
|
+
const emptyMutation = mutation && Object.keys(mutation.getFields()).length === 0;
|
|
2311
|
+
const emptySubscription = subscription && Object.keys(subscription.getFields()).length === 0;
|
|
2312
|
+
if (!emptyMutation && !emptySubscription) return raw;
|
|
2313
|
+
const config = raw.toConfig();
|
|
2314
|
+
const filteredTypes = config.types.filter((t) => {
|
|
2315
|
+
if (emptyMutation && t === mutation) return false;
|
|
2316
|
+
if (emptySubscription && t === subscription) return false;
|
|
2317
|
+
return true;
|
|
2318
|
+
});
|
|
2319
|
+
return new graphql.GraphQLSchema({
|
|
2320
|
+
...config,
|
|
2321
|
+
mutation: emptyMutation ? void 0 : config.mutation,
|
|
2322
|
+
subscription: emptySubscription ? void 0 : config.subscription,
|
|
2323
|
+
types: filteredTypes
|
|
2324
|
+
});
|
|
2325
|
+
});
|
|
1963
2326
|
const createYoga = (args) => {
|
|
1964
2327
|
const enableApiDocs = args?.enableApiDocs ?? process?.env?.NODE_ENV === "development";
|
|
1965
2328
|
return (0, graphql_yoga.createYoga)({
|
|
@@ -1970,26 +2333,60 @@ export const r = rumble({
|
|
|
1970
2333
|
plugins: [
|
|
1971
2334
|
...args?.plugins ?? [],
|
|
1972
2335
|
...enableApiDocs ? [] : [(0, _graphql_yoga_plugin_disable_introspection.useDisableIntrospection)(), (0, _escape_tech_graphql_armor.EnvelopArmorPlugin)(args?.armorConfig)],
|
|
1973
|
-
rumbleInput.otel?.enabled ? { onExecute: ({ setExecuteFn, executeFn }) => {
|
|
1974
|
-
setExecuteFn((options) =>
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
2336
|
+
rumbleInput.otel?.enabled || rumbleInput.logger?.enabled ? { onExecute: ({ setExecuteFn, executeFn }) => {
|
|
2337
|
+
setExecuteFn(async (options) => {
|
|
2338
|
+
const log = rumbleInput.logger?.enabled ? rumbleInput.logger.logger : void 0;
|
|
2339
|
+
const operationName = options.operationName ?? "anonymous";
|
|
2340
|
+
const start = Date.now();
|
|
2341
|
+
log?.info({ "graphql.operation.name": operationName }, "graphql execute start");
|
|
2342
|
+
const run = async () => {
|
|
1979
2343
|
const result = await executeFn(options);
|
|
1980
|
-
if (result && "errors" in result && result.errors?.length) {
|
|
1981
|
-
|
|
2344
|
+
if (result && "errors" in result && result.errors?.length) log?.error({
|
|
2345
|
+
"graphql.operation.name": operationName,
|
|
2346
|
+
durationMs: Date.now() - start,
|
|
2347
|
+
errors: result.errors.map((e) => e.message)
|
|
2348
|
+
}, "graphql execute completed with errors");
|
|
2349
|
+
else log?.info({
|
|
2350
|
+
"graphql.operation.name": operationName,
|
|
2351
|
+
durationMs: Date.now() - start
|
|
2352
|
+
}, "graphql execute completed");
|
|
2353
|
+
return result;
|
|
2354
|
+
};
|
|
2355
|
+
if (rumbleInput.otel?.enabled) return rumbleInput.otel.tracer.startActiveSpan(_pothos_tracing_opentelemetry.SpanNames.EXECUTE, { attributes: {
|
|
2356
|
+
[_pothos_tracing_opentelemetry.AttributeNames.OPERATION_NAME]: operationName,
|
|
2357
|
+
[_pothos_tracing_opentelemetry.AttributeNames.SOURCE]: options.document
|
|
2358
|
+
} }, async (span) => {
|
|
2359
|
+
try {
|
|
2360
|
+
const result = await run();
|
|
2361
|
+
if (result && "errors" in result && result.errors?.length) {
|
|
2362
|
+
for (const error of result.errors) span.recordException(error);
|
|
2363
|
+
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
2364
|
+
}
|
|
2365
|
+
return result;
|
|
2366
|
+
} catch (error) {
|
|
2367
|
+
if (error instanceof Error) span.recordException(error);
|
|
2368
|
+
log?.error({
|
|
2369
|
+
"graphql.operation.name": operationName,
|
|
2370
|
+
durationMs: Date.now() - start,
|
|
2371
|
+
err: error
|
|
2372
|
+
}, "graphql execute threw");
|
|
1982
2373
|
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
2374
|
+
throw error;
|
|
2375
|
+
} finally {
|
|
2376
|
+
span.end();
|
|
1983
2377
|
}
|
|
1984
|
-
|
|
2378
|
+
});
|
|
2379
|
+
try {
|
|
2380
|
+
return await run();
|
|
1985
2381
|
} catch (error) {
|
|
1986
|
-
|
|
1987
|
-
|
|
2382
|
+
log?.error({
|
|
2383
|
+
"graphql.operation.name": operationName,
|
|
2384
|
+
durationMs: Date.now() - start,
|
|
2385
|
+
err: error
|
|
2386
|
+
}, "graphql execute threw");
|
|
1988
2387
|
throw error;
|
|
1989
|
-
} finally {
|
|
1990
|
-
span.end();
|
|
1991
2388
|
}
|
|
1992
|
-
})
|
|
2389
|
+
});
|
|
1993
2390
|
} } : false
|
|
1994
2391
|
].filter(Boolean)
|
|
1995
2392
|
});
|