@m1212e/rumble 0.20.0 → 0.21.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +127 -47
- package/out/index.cjs.map +1 -1
- package/out/index.d.cts +16 -8
- package/out/index.d.cts.map +1 -1
- package/out/index.d.mts +16 -8
- package/out/index.d.mts.map +1 -1
- package/out/index.mjs +129 -49
- package/out/index.mjs.map +1 -1
- package/package.json +7 -3
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.2";
|
|
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) {
|
|
@@ -802,9 +857,10 @@ const createWhereArgImplementer = ({ db, schemaBuilder, enumImplementer }) => {
|
|
|
802
857
|
return acc;
|
|
803
858
|
}, {});
|
|
804
859
|
const relations = Object.entries(tableSchema.relations ?? {}).reduce((acc, [key, value]) => {
|
|
860
|
+
const targetTsName = value.targetTableName;
|
|
805
861
|
const referenceModel = whereArgImplementer({ dbName: tableHelper({
|
|
806
862
|
db,
|
|
807
|
-
table:
|
|
863
|
+
table: targetTsName
|
|
808
864
|
}).dbName });
|
|
809
865
|
acc[key] = t.field({
|
|
810
866
|
type: referenceModel,
|
|
@@ -899,7 +955,7 @@ const createCountQueryImplementer = ({ db, schemaBuilder, whereArgImplementer, m
|
|
|
899
955
|
type: WhereArg,
|
|
900
956
|
required: false
|
|
901
957
|
}) },
|
|
902
|
-
resolve: async (
|
|
958
|
+
resolve: async (_root, args, ctx, _info) => {
|
|
903
959
|
if (isAllowed && !await isAllowed(ctx)) throw new RumbleErrorSafe("Not allowed to perform this action");
|
|
904
960
|
deepSetProto(args);
|
|
905
961
|
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 +1082,23 @@ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, n
|
|
|
1026
1082
|
}
|
|
1027
1083
|
//#endregion
|
|
1028
1084
|
//#region lib/helpers/determineDialectFromSchema.ts
|
|
1085
|
+
const dialectCache = /* @__PURE__ */ new WeakMap();
|
|
1029
1086
|
function determineDBDialectFromSchema(schema) {
|
|
1087
|
+
const cached = dialectCache.get(schema);
|
|
1088
|
+
if (cached) return cached;
|
|
1030
1089
|
const found = /* @__PURE__ */ new Set();
|
|
1031
1090
|
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
|
|
1091
|
+
if (!table || typeof table !== "object") continue;
|
|
1092
|
+
if (is(table, PgTable)) found.add("postgres");
|
|
1093
|
+
else if (is(table, MySqlTable)) found.add("mysql");
|
|
1094
|
+
else if (is(table, SQLiteTable)) found.add("sqlite");
|
|
1036
1095
|
}
|
|
1037
1096
|
const dialects = Array.from(found);
|
|
1038
|
-
if (dialects.length === 1)
|
|
1097
|
+
if (dialects.length === 1) {
|
|
1098
|
+
const only = dialects[0];
|
|
1099
|
+
dialectCache.set(schema, only);
|
|
1100
|
+
return only;
|
|
1101
|
+
}
|
|
1039
1102
|
if (dialects.length === 0) throw new Error("No tables found in schema, could not determine dialect");
|
|
1040
1103
|
throw new Error(`Multiple dialects found in schema: ${dialects.join(", ")}`);
|
|
1041
1104
|
}
|
|
@@ -1067,7 +1130,7 @@ async function initSearchIfApplicable(input) {
|
|
|
1067
1130
|
*/
|
|
1068
1131
|
function adjustQueryArgsForSearch({ search, args, tableSchema, abilities }) {
|
|
1069
1132
|
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(([
|
|
1133
|
+
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
1134
|
const searchParam = sql`${args.search}`;
|
|
1072
1135
|
args.extras = { search_distance: (table) => sql`${sql.join(columnsToSearch.map(([key]) => {
|
|
1073
1136
|
return sql`COALESCE((${table[key]}::TEXT <-> ${searchParam}), 1)`;
|
|
@@ -1133,7 +1196,7 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
|
|
|
1133
1196
|
fields: (t) => {
|
|
1134
1197
|
const columns = tableSchema.columns;
|
|
1135
1198
|
const configMap = /* @__PURE__ */ new Map();
|
|
1136
|
-
const userAdjustments = adjust?.(new Proxy(t, { get: (target, prop,
|
|
1199
|
+
const userAdjustments = adjust?.(new Proxy(t, { get: (target, prop, _receiver) => {
|
|
1137
1200
|
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
1201
|
return (...params) => {
|
|
1139
1202
|
const ref = target[prop](...params);
|
|
@@ -1155,9 +1218,12 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
|
|
|
1155
1218
|
return acc;
|
|
1156
1219
|
}
|
|
1157
1220
|
let isArray = false;
|
|
1158
|
-
if (column
|
|
1159
|
-
|
|
1160
|
-
|
|
1221
|
+
if (is(column, PgColumn)) {
|
|
1222
|
+
const dimensions = column.dimensions;
|
|
1223
|
+
if (dimensions && dimensions > 0) {
|
|
1224
|
+
if (dimensions !== 1) throw new RumbleError("Only one-dimensional arrays are supported for default object implementation");
|
|
1225
|
+
isArray = true;
|
|
1226
|
+
}
|
|
1161
1227
|
}
|
|
1162
1228
|
if (isEnumSchema(column)) {
|
|
1163
1229
|
const enumImpl = enumImplementer({ enumColumn: column });
|
|
@@ -1176,9 +1242,10 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
|
|
|
1176
1242
|
return acc;
|
|
1177
1243
|
}, {});
|
|
1178
1244
|
const relations = Object.entries(tableSchema.relations ?? {}).reduce((acc, [key, value]) => {
|
|
1245
|
+
const targetTsName = value.targetTableName;
|
|
1179
1246
|
const relationSchema = tableHelper({
|
|
1180
1247
|
db,
|
|
1181
|
-
table:
|
|
1248
|
+
table: targetTsName
|
|
1182
1249
|
});
|
|
1183
1250
|
const WhereArg = whereArgImplementer({ dbName: relationSchema.dbName });
|
|
1184
1251
|
const OrderArg = orderArgImplementer({ dbName: relationSchema.dbName });
|
|
@@ -1186,7 +1253,7 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
|
|
|
1186
1253
|
let nullable = false;
|
|
1187
1254
|
let isMany = true;
|
|
1188
1255
|
let filterSpecifier = "many";
|
|
1189
|
-
if (value
|
|
1256
|
+
if (is(value, One)) {
|
|
1190
1257
|
isMany = false;
|
|
1191
1258
|
nullable = value.optional || !value.sourceColumns.every((column) => column.notNull);
|
|
1192
1259
|
filterSpecifier = "single";
|
|
@@ -1254,7 +1321,7 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
|
|
|
1254
1321
|
fields.search_distance = t.float({
|
|
1255
1322
|
description: "The search distance of the object. If a search is provided, this field will be populated with the search distance.",
|
|
1256
1323
|
nullable: true,
|
|
1257
|
-
resolve: (parent,
|
|
1324
|
+
resolve: (parent, _args, _ctx, _info) => parent.search_distance
|
|
1258
1325
|
});
|
|
1259
1326
|
}
|
|
1260
1327
|
return {
|
|
@@ -1619,9 +1686,10 @@ const createSchemaBuilder = ({ db, disableDefaultObjects, pubsub, pothosConfig,
|
|
|
1619
1686
|
client: db,
|
|
1620
1687
|
relations: db._.relations,
|
|
1621
1688
|
getTableConfig(table) {
|
|
1689
|
+
const columns = isTable(table) ? Object.values(getTableColumns(table)) : [];
|
|
1622
1690
|
return {
|
|
1623
|
-
columns
|
|
1624
|
-
primaryKeys:
|
|
1691
|
+
columns,
|
|
1692
|
+
primaryKeys: columns.filter((v) => v.primary)
|
|
1625
1693
|
};
|
|
1626
1694
|
}
|
|
1627
1695
|
},
|
|
@@ -1658,6 +1726,18 @@ export const db = drizzle(
|
|
|
1658
1726
|
},
|
|
1659
1727
|
);
|
|
1660
1728
|
|
|
1729
|
+
`);
|
|
1730
|
+
if (!rumbleInput.schema || typeof rumbleInput.schema !== "object" || Object.keys(rumbleInput.schema).length === 0) throw new RumbleError(`
|
|
1731
|
+
rumble requires the drizzle schema object to be passed when initializing.
|
|
1732
|
+
Import your schema module and pass it alongside \`db\`:
|
|
1733
|
+
|
|
1734
|
+
import * as schema from "./db/schema";
|
|
1735
|
+
|
|
1736
|
+
export const r = rumble({
|
|
1737
|
+
db,
|
|
1738
|
+
schema, // <--- add this line
|
|
1739
|
+
});
|
|
1740
|
+
|
|
1661
1741
|
`);
|
|
1662
1742
|
if (!rumbleInput.actions) rumbleInput.actions = [
|
|
1663
1743
|
"read",
|