@m1212e/rumble 0.20.0 → 0.21.3
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/client.cjs +2 -3
- package/out/client.cjs.map +1 -1
- package/out/client.d.cts.map +1 -1
- package/out/client.d.mts.map +1 -1
- package/out/client.mjs +2 -3
- package/out/client.mjs.map +1 -1
- package/out/generate-B4w-hX0K.mjs.map +1 -1
- package/out/generate-Ch5LEiBr.cjs.map +1 -1
- package/out/index.cjs +130 -51
- package/out/index.cjs.map +1 -1
- package/out/index.d.cts +21 -9
- package/out/index.d.cts.map +1 -1
- package/out/index.d.mts +21 -9
- package/out/index.d.mts.map +1 -1
- package/out/index.mjs +132 -53
- package/out/index.mjs.map +1 -1
- package/package.json +7 -4
package/out/index.mjs
CHANGED
|
@@ -7,9 +7,9 @@ import { SpanStatusCode, trace } from "@opentelemetry/api";
|
|
|
7
7
|
import { AttributeNames, SpanNames, createOpenTelemetryWrapper } from "@pothos/tracing-opentelemetry";
|
|
8
8
|
import { capitalize, cloneDeep, debounce, merge, toMerged } from "es-toolkit";
|
|
9
9
|
import { createPubSub, createYoga } from "graphql-yoga";
|
|
10
|
-
import { One, count, relationsFilterToSQL, sql } from "drizzle-orm";
|
|
10
|
+
import { One, count, getTableColumns, getTableName, is, isTable, relationsFilterToSQL, sql } from "drizzle-orm";
|
|
11
11
|
import { toCamelCase } from "drizzle-orm/casing";
|
|
12
|
-
import { PgEnumColumn, PgTable } from "drizzle-orm/pg-core";
|
|
12
|
+
import { PgColumn, PgEnumColumn, PgEnumObjectColumn, PgTable, isPgEnum } from "drizzle-orm/pg-core";
|
|
13
13
|
import pluralize from "pluralize";
|
|
14
14
|
import { MySqlTable } from "drizzle-orm/mysql-core";
|
|
15
15
|
import { SQLiteTable } from "drizzle-orm/sqlite-core";
|
|
@@ -180,7 +180,7 @@ function mapNullFieldsToUndefined(obj) {
|
|
|
180
180
|
}
|
|
181
181
|
//#endregion
|
|
182
182
|
//#region package.json
|
|
183
|
-
var version = "0.
|
|
183
|
+
var version = "0.21.3";
|
|
184
184
|
//#endregion
|
|
185
185
|
//#region lib/helpers/mergeFilters.ts
|
|
186
186
|
function mergeFilters(filterA, filterB, mode = "AND") {
|
|
@@ -300,31 +300,70 @@ function createDistinctValuesFromSQLType(sqlType) {
|
|
|
300
300
|
}
|
|
301
301
|
//#endregion
|
|
302
302
|
//#region lib/helpers/tableHelpers.ts
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
const
|
|
306
|
-
const
|
|
303
|
+
const indexCache = /* @__PURE__ */ new WeakMap();
|
|
304
|
+
function buildIndex(db) {
|
|
305
|
+
const byTsName = /* @__PURE__ */ new Map();
|
|
306
|
+
const byDbName = /* @__PURE__ */ new Map();
|
|
307
|
+
const byTableRef = /* @__PURE__ */ new Map();
|
|
308
|
+
for (const [tsName, rawEntry] of Object.entries(db._.relations ?? {})) {
|
|
309
|
+
const entry = rawEntry;
|
|
310
|
+
const tableObj = entry.table;
|
|
311
|
+
if (!tableObj) continue;
|
|
312
|
+
const columns = isTable(tableObj) ? getTableColumns(tableObj) : {};
|
|
313
|
+
const primaryKey = Object.fromEntries(Object.entries(columns).filter(([, col]) => col.primary));
|
|
314
|
+
const dbName = isTable(tableObj) ? getTableName(tableObj) : entry.name ?? tsName;
|
|
315
|
+
const resolved = {
|
|
316
|
+
name: entry.name ?? tsName,
|
|
317
|
+
dbName,
|
|
318
|
+
table: tableObj,
|
|
319
|
+
columns,
|
|
320
|
+
primaryKey,
|
|
321
|
+
relations: entry.relations ?? {},
|
|
322
|
+
foundRelation: entry
|
|
323
|
+
};
|
|
324
|
+
byTsName.set(resolved.name, resolved);
|
|
325
|
+
byDbName.set(resolved.dbName, resolved);
|
|
326
|
+
byTableRef.set(tableObj, resolved);
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
byTsName,
|
|
330
|
+
byDbName,
|
|
331
|
+
byTableRef
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function getIndex(db) {
|
|
335
|
+
let idx = indexCache.get(db);
|
|
336
|
+
if (!idx) {
|
|
337
|
+
idx = buildIndex(db);
|
|
338
|
+
indexCache.set(db, idx);
|
|
339
|
+
}
|
|
340
|
+
return idx;
|
|
341
|
+
}
|
|
307
342
|
function tableHelper({ db, table }) {
|
|
308
|
-
|
|
309
|
-
|
|
343
|
+
const idx = getIndex(db);
|
|
344
|
+
let entry;
|
|
345
|
+
if (typeof table === "string") entry = idx.byTsName.get(table) ?? idx.byDbName.get(table);
|
|
346
|
+
else if (table && typeof table === "object") {
|
|
310
347
|
const t = table;
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
348
|
+
if (t.table) {
|
|
349
|
+
entry = idx.byTableRef.get(t.table);
|
|
350
|
+
if (!entry && t.name) entry = idx.byTsName.get(t.name) ?? idx.byDbName.get(t.name);
|
|
351
|
+
}
|
|
352
|
+
if (!entry) entry = idx.byTableRef.get(t);
|
|
353
|
+
if (!entry && isTable(t)) {
|
|
354
|
+
const dbName = getTableName(t);
|
|
355
|
+
entry = idx.byDbName.get(dbName);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (!entry) throw new RumbleError(`Could not find schema for ${JSON.stringify(typeof table === "string" ? table : "<table-object>")}`);
|
|
320
359
|
return {
|
|
321
|
-
columns,
|
|
322
|
-
primaryKey,
|
|
323
|
-
relations:
|
|
324
|
-
dbName,
|
|
325
|
-
tsName,
|
|
326
|
-
table:
|
|
327
|
-
foundRelation
|
|
360
|
+
columns: entry.columns,
|
|
361
|
+
primaryKey: entry.primaryKey,
|
|
362
|
+
relations: entry.relations,
|
|
363
|
+
dbName: entry.dbName,
|
|
364
|
+
tsName: entry.name,
|
|
365
|
+
table: entry.table,
|
|
366
|
+
foundRelation: entry.foundRelation
|
|
328
367
|
};
|
|
329
368
|
}
|
|
330
369
|
//#endregion
|
|
@@ -678,9 +717,10 @@ const createOrderArgImplementer = ({ db, schemaBuilder }) => {
|
|
|
678
717
|
return acc;
|
|
679
718
|
}, {});
|
|
680
719
|
const relations = Object.entries(tableSchema.relations ?? {}).reduce((acc, [key, value]) => {
|
|
720
|
+
const targetTsName = value.targetTableName;
|
|
681
721
|
const referenceModel = orderArgImplementer({ dbName: tableHelper({
|
|
682
722
|
db,
|
|
683
|
-
table:
|
|
723
|
+
table: targetTsName
|
|
684
724
|
}).dbName });
|
|
685
725
|
acc[key] = t.field({
|
|
686
726
|
type: referenceModel,
|
|
@@ -703,21 +743,36 @@ const createOrderArgImplementer = ({ db, schemaBuilder }) => {
|
|
|
703
743
|
//#endregion
|
|
704
744
|
//#region lib/enum.ts
|
|
705
745
|
/**
|
|
706
|
-
* Checks if a column is a
|
|
746
|
+
* Checks if a column is a Postgres enum column
|
|
707
747
|
*/
|
|
708
748
|
function isEnumSchema(schemaType) {
|
|
709
|
-
return schemaType
|
|
749
|
+
return is(schemaType, PgEnumColumn) || is(schemaType, PgEnumObjectColumn);
|
|
710
750
|
}
|
|
711
|
-
const createEnumImplementer = ({ schemaBuilder }) => {
|
|
751
|
+
const createEnumImplementer = ({ schema, schemaBuilder }) => {
|
|
712
752
|
const referenceStorage = /* @__PURE__ */ new Map();
|
|
713
753
|
/**
|
|
714
754
|
* Registers a Postgres enum as a GraphQL enum type.
|
|
715
755
|
*
|
|
716
|
-
*
|
|
717
|
-
*
|
|
756
|
+
* You can identify the enum in three ways:
|
|
757
|
+
* - `tsName`: the exported TypeScript identifier from your schema module. This is the
|
|
758
|
+
* most typesafe form, only valid enum keys autocomplete, typos are caught at compile time.
|
|
759
|
+
* - `enum`: pass the pgEnum object directly.
|
|
760
|
+
* - `enumColumn`: pass a column that uses the enum.
|
|
761
|
+
*
|
|
718
762
|
* Use `refName` to override the auto-generated GraphQL type name.
|
|
719
763
|
*/
|
|
720
|
-
const enumImplementer = (
|
|
764
|
+
const enumImplementer = (args) => {
|
|
765
|
+
const { refName } = args;
|
|
766
|
+
const tsName = args.tsName;
|
|
767
|
+
const enumObjectArg = args.enum;
|
|
768
|
+
const enumColumn = args.enumColumn;
|
|
769
|
+
let enumObject = enumObjectArg;
|
|
770
|
+
if (tsName) {
|
|
771
|
+
const candidate = schema[tsName];
|
|
772
|
+
if (!candidate || !isPgEnum(candidate)) throw new RumbleError(`Could not find a pgEnum exported as ${JSON.stringify(tsName)} in the provided schema. Make sure the identifier exists and refers to a value created with \`pgEnum(...)\`.`);
|
|
773
|
+
enumObject = candidate;
|
|
774
|
+
}
|
|
775
|
+
if (!enumObject && !enumColumn) throw new RumbleError("Could not determine enum structure! Pass one of 'tsName' (requires `schema` on rumble()), 'enum' (the pgEnum object), or 'enumColumn' (a column that uses the enum).");
|
|
721
776
|
let enumSchemaName;
|
|
722
777
|
let enumValues;
|
|
723
778
|
if (enumObject) {
|
|
@@ -731,10 +786,9 @@ const createEnumImplementer = ({ schemaBuilder }) => {
|
|
|
731
786
|
}
|
|
732
787
|
if (!enumSchemaName || !enumValues) throw new RumbleError("Could not determine enum structure!");
|
|
733
788
|
const graphqlImplementationName = refName ?? `${capitalize(toCamelCase(enumSchemaName))}Enum`;
|
|
734
|
-
|
|
735
|
-
if (
|
|
736
|
-
const
|
|
737
|
-
ret = implement();
|
|
789
|
+
const cached = referenceStorage.get(graphqlImplementationName);
|
|
790
|
+
if (cached) return cached;
|
|
791
|
+
const ret = schemaBuilder.enumType(graphqlImplementationName, { values: enumValues });
|
|
738
792
|
referenceStorage.set(graphqlImplementationName, ret);
|
|
739
793
|
return ret;
|
|
740
794
|
};
|
|
@@ -802,9 +856,10 @@ const createWhereArgImplementer = ({ db, schemaBuilder, enumImplementer }) => {
|
|
|
802
856
|
return acc;
|
|
803
857
|
}, {});
|
|
804
858
|
const relations = Object.entries(tableSchema.relations ?? {}).reduce((acc, [key, value]) => {
|
|
859
|
+
const targetTsName = value.targetTableName;
|
|
805
860
|
const referenceModel = whereArgImplementer({ dbName: tableHelper({
|
|
806
861
|
db,
|
|
807
|
-
table:
|
|
862
|
+
table: targetTsName
|
|
808
863
|
}).dbName });
|
|
809
864
|
acc[key] = t.field({
|
|
810
865
|
type: referenceModel,
|
|
@@ -899,7 +954,7 @@ const createCountQueryImplementer = ({ db, schemaBuilder, whereArgImplementer, m
|
|
|
899
954
|
type: WhereArg,
|
|
900
955
|
required: false
|
|
901
956
|
}) },
|
|
902
|
-
resolve: async (
|
|
957
|
+
resolve: async (_root, args, ctx, _info) => {
|
|
903
958
|
if (isAllowed && !await isAllowed(ctx)) throw new RumbleErrorSafe("Not allowed to perform this action");
|
|
904
959
|
deepSetProto(args);
|
|
905
960
|
return db.select({ count: count() }).from(tableSchema.table).where(ctx.abilities[table].filter(listAction).merge(mapNullFieldsToUndefined(args)).sql.where).then(assertFirstEntryExists).then((r) => r.count);
|
|
@@ -1026,16 +1081,23 @@ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, n
|
|
|
1026
1081
|
}
|
|
1027
1082
|
//#endregion
|
|
1028
1083
|
//#region lib/helpers/determineDialectFromSchema.ts
|
|
1084
|
+
const dialectCache = /* @__PURE__ */ new WeakMap();
|
|
1029
1085
|
function determineDBDialectFromSchema(schema) {
|
|
1086
|
+
const cached = dialectCache.get(schema);
|
|
1087
|
+
if (cached) return cached;
|
|
1030
1088
|
const found = /* @__PURE__ */ new Set();
|
|
1031
1089
|
for (const table of Object.values(schema).map((t) => t.table)) {
|
|
1032
|
-
if (typeof table !== "object") continue;
|
|
1033
|
-
if (table
|
|
1034
|
-
else if (table
|
|
1035
|
-
else if (table
|
|
1090
|
+
if (!table || typeof table !== "object") continue;
|
|
1091
|
+
if (is(table, PgTable)) found.add("postgres");
|
|
1092
|
+
else if (is(table, MySqlTable)) found.add("mysql");
|
|
1093
|
+
else if (is(table, SQLiteTable)) found.add("sqlite");
|
|
1036
1094
|
}
|
|
1037
1095
|
const dialects = Array.from(found);
|
|
1038
|
-
if (dialects.length === 1)
|
|
1096
|
+
if (dialects.length === 1) {
|
|
1097
|
+
const only = dialects[0];
|
|
1098
|
+
dialectCache.set(schema, only);
|
|
1099
|
+
return only;
|
|
1100
|
+
}
|
|
1039
1101
|
if (dialects.length === 0) throw new Error("No tables found in schema, could not determine dialect");
|
|
1040
1102
|
throw new Error(`Multiple dialects found in schema: ${dialects.join(", ")}`);
|
|
1041
1103
|
}
|
|
@@ -1067,7 +1129,7 @@ async function initSearchIfApplicable(input) {
|
|
|
1067
1129
|
*/
|
|
1068
1130
|
function adjustQueryArgsForSearch({ search, args, tableSchema, abilities }) {
|
|
1069
1131
|
if (search?.enabled && args.search && args.search.length > 0) {
|
|
1070
|
-
const columnsToSearch = (abilities.query.many.columns ? Object.entries(tableSchema.columns).filter(([key]) => abilities.query.many.columns[key]) : Object.entries(tableSchema.columns)).filter(([
|
|
1132
|
+
const columnsToSearch = (abilities.query.many.columns ? Object.entries(tableSchema.columns).filter(([key]) => abilities.query.many.columns[key]) : Object.entries(tableSchema.columns)).filter(([_key, col]) => isStringLikeSQLTypeString(col.getSQLType()) || isIDLikeSQLTypeString(col.getSQLType()));
|
|
1071
1133
|
const searchParam = sql`${args.search}`;
|
|
1072
1134
|
args.extras = { search_distance: (table) => sql`${sql.join(columnsToSearch.map(([key]) => {
|
|
1073
1135
|
return sql`COALESCE((${table[key]}::TEXT <-> ${searchParam}), 1)`;
|
|
@@ -1133,7 +1195,7 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
|
|
|
1133
1195
|
fields: (t) => {
|
|
1134
1196
|
const columns = tableSchema.columns;
|
|
1135
1197
|
const configMap = /* @__PURE__ */ new Map();
|
|
1136
|
-
const userAdjustments = adjust?.(new Proxy(t, { get: (target, prop,
|
|
1198
|
+
const userAdjustments = adjust?.(new Proxy(t, { get: (target, prop, _receiver) => {
|
|
1137
1199
|
if (typeof target[prop] !== "function" || prop === "arg" || prop === "builder" || prop === "graphqlKind" || prop === "kind" || prop === "listRef" || prop === "table" || prop === "typename" || prop === "variant" || prop.toString().startsWith("boolean") || prop.toString().startsWith("float") || prop.toString().startsWith("id") || prop.toString().startsWith("int") || prop.toString().startsWith("string") || prop.toString().startsWith("expose")) return target[prop];
|
|
1138
1200
|
return (...params) => {
|
|
1139
1201
|
const ref = target[prop](...params);
|
|
@@ -1155,9 +1217,12 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
|
|
|
1155
1217
|
return acc;
|
|
1156
1218
|
}
|
|
1157
1219
|
let isArray = false;
|
|
1158
|
-
if (column
|
|
1159
|
-
|
|
1160
|
-
|
|
1220
|
+
if (is(column, PgColumn)) {
|
|
1221
|
+
const dimensions = column.dimensions;
|
|
1222
|
+
if (dimensions && dimensions > 0) {
|
|
1223
|
+
if (dimensions !== 1) throw new RumbleError("Only one-dimensional arrays are supported for default object implementation");
|
|
1224
|
+
isArray = true;
|
|
1225
|
+
}
|
|
1161
1226
|
}
|
|
1162
1227
|
if (isEnumSchema(column)) {
|
|
1163
1228
|
const enumImpl = enumImplementer({ enumColumn: column });
|
|
@@ -1176,9 +1241,10 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
|
|
|
1176
1241
|
return acc;
|
|
1177
1242
|
}, {});
|
|
1178
1243
|
const relations = Object.entries(tableSchema.relations ?? {}).reduce((acc, [key, value]) => {
|
|
1244
|
+
const targetTsName = value.targetTableName;
|
|
1179
1245
|
const relationSchema = tableHelper({
|
|
1180
1246
|
db,
|
|
1181
|
-
table:
|
|
1247
|
+
table: targetTsName
|
|
1182
1248
|
});
|
|
1183
1249
|
const WhereArg = whereArgImplementer({ dbName: relationSchema.dbName });
|
|
1184
1250
|
const OrderArg = orderArgImplementer({ dbName: relationSchema.dbName });
|
|
@@ -1186,7 +1252,7 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
|
|
|
1186
1252
|
let nullable = false;
|
|
1187
1253
|
let isMany = true;
|
|
1188
1254
|
let filterSpecifier = "many";
|
|
1189
|
-
if (value
|
|
1255
|
+
if (is(value, One)) {
|
|
1190
1256
|
isMany = false;
|
|
1191
1257
|
nullable = value.optional || !value.sourceColumns.every((column) => column.notNull);
|
|
1192
1258
|
filterSpecifier = "single";
|
|
@@ -1254,7 +1320,7 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
|
|
|
1254
1320
|
fields.search_distance = t.float({
|
|
1255
1321
|
description: "The search distance of the object. If a search is provided, this field will be populated with the search distance.",
|
|
1256
1322
|
nullable: true,
|
|
1257
|
-
resolve: (parent,
|
|
1323
|
+
resolve: (parent, _args, _ctx, _info) => parent.search_distance
|
|
1258
1324
|
});
|
|
1259
1325
|
}
|
|
1260
1326
|
return {
|
|
@@ -1619,9 +1685,10 @@ const createSchemaBuilder = ({ db, disableDefaultObjects, pubsub, pothosConfig,
|
|
|
1619
1685
|
client: db,
|
|
1620
1686
|
relations: db._.relations,
|
|
1621
1687
|
getTableConfig(table) {
|
|
1688
|
+
const columns = isTable(table) ? Object.values(getTableColumns(table)) : [];
|
|
1622
1689
|
return {
|
|
1623
|
-
columns
|
|
1624
|
-
primaryKeys:
|
|
1690
|
+
columns,
|
|
1691
|
+
primaryKeys: columns.filter((v) => v.primary)
|
|
1625
1692
|
};
|
|
1626
1693
|
}
|
|
1627
1694
|
},
|
|
@@ -1658,6 +1725,18 @@ export const db = drizzle(
|
|
|
1658
1725
|
},
|
|
1659
1726
|
);
|
|
1660
1727
|
|
|
1728
|
+
`);
|
|
1729
|
+
if (!rumbleInput.schema || typeof rumbleInput.schema !== "object" || Object.keys(rumbleInput.schema).length === 0) throw new RumbleError(`
|
|
1730
|
+
rumble requires the drizzle schema object to be passed when initializing.
|
|
1731
|
+
Import your schema module and pass it alongside \`db\`:
|
|
1732
|
+
|
|
1733
|
+
import * as schema from "./db/schema";
|
|
1734
|
+
|
|
1735
|
+
export const r = rumble({
|
|
1736
|
+
db,
|
|
1737
|
+
schema, // <--- add this line
|
|
1738
|
+
});
|
|
1739
|
+
|
|
1661
1740
|
`);
|
|
1662
1741
|
if (!rumbleInput.actions) rumbleInput.actions = [
|
|
1663
1742
|
"read",
|