@m1212e/rumble 0.23.23 → 0.24.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/README.md +9 -5
- package/out/client/generate.d.cts +1 -12
- package/out/client/generate.d.cts.map +1 -1
- package/out/client/generate.d.mts +1 -12
- package/out/client/generate.d.mts.map +1 -1
- package/out/client.cjs +3 -3
- package/out/client.cjs.map +1 -1
- package/out/client.d.cts +17 -38
- package/out/client.d.cts.map +1 -1
- package/out/client.d.mts +17 -38
- package/out/client.d.mts.map +1 -1
- package/out/client.mjs +3 -3
- package/out/client.mjs.map +1 -1
- package/out/generate-CM3XjmlO.cjs.map +1 -1
- package/out/index.cjs +78 -53
- package/out/index.cjs.map +1 -1
- package/out/index.d.cts +233 -244
- package/out/index.d.cts.map +1 -1
- package/out/index.d.mts +234 -244
- package/out/index.d.mts.map +1 -1
- package/out/index.mjs +77 -52
- package/out/index.mjs.map +1 -1
- package/package.json +32 -32
package/out/index.mjs
CHANGED
|
@@ -62,7 +62,7 @@ return (
|
|
|
62
62
|
db.query.users
|
|
63
63
|
.findFirst({
|
|
64
64
|
...query,
|
|
65
|
-
where: ctx.abilities.users.filter("read").single.where,
|
|
65
|
+
where: (await ctx.abilities.users.filter("read")).single.where,
|
|
66
66
|
})
|
|
67
67
|
// note that we need to manually raise an error if the value is not found
|
|
68
68
|
.then(assertFindFirstExists)
|
|
@@ -104,7 +104,7 @@ name: args.newName,
|
|
|
104
104
|
.where(
|
|
105
105
|
and(
|
|
106
106
|
eq(schema.users.id, args.userId),
|
|
107
|
-
ctx.abilities.users.filter("update").single.where
|
|
107
|
+
(await ctx.abilities.users.filter("update")).single.where
|
|
108
108
|
)
|
|
109
109
|
)
|
|
110
110
|
.returning({ id: schema.users.id, name: schema.users.name })
|
|
@@ -167,7 +167,7 @@ pubsub.updated(user.id);
|
|
|
167
167
|
return db.query.user
|
|
168
168
|
.findFirst(
|
|
169
169
|
query(
|
|
170
|
-
ctx.abilities.user.filter('read').merge(
|
|
170
|
+
(await ctx.abilities.user.filter('read')).merge(
|
|
171
171
|
{
|
|
172
172
|
where: { id: user.id },
|
|
173
173
|
}
|
|
@@ -186,7 +186,7 @@ function mapNullFieldsToUndefined(obj) {
|
|
|
186
186
|
}
|
|
187
187
|
//#endregion
|
|
188
188
|
//#region package.json
|
|
189
|
-
var version = "0.
|
|
189
|
+
var version = "0.24.0";
|
|
190
190
|
//#endregion
|
|
191
191
|
//#region lib/helpers/errorLogging.ts
|
|
192
192
|
function hasMessage(error) {
|
|
@@ -618,7 +618,6 @@ function attributeValue(value) {
|
|
|
618
618
|
case "boolean": return value;
|
|
619
619
|
case "bigint": return value.toString();
|
|
620
620
|
case "undefined": return "null";
|
|
621
|
-
default: break;
|
|
622
621
|
}
|
|
623
622
|
if (value === null) return "null";
|
|
624
623
|
if (Array.isArray(value) && isHomogeneousPrimitiveArray(value)) return value;
|
|
@@ -656,11 +655,28 @@ function durationMs(start) {
|
|
|
656
655
|
//#endregion
|
|
657
656
|
//#region lib/abilityBuilder.ts
|
|
658
657
|
function isDynamicQueryFilter(filter) {
|
|
659
|
-
return typeof filter === "function"
|
|
658
|
+
return typeof filter === "function";
|
|
660
659
|
}
|
|
661
660
|
function isStaticQueryFilter(filter) {
|
|
662
661
|
return typeof filter !== "function";
|
|
663
662
|
}
|
|
663
|
+
/**
|
|
664
|
+
* filter() is async. Without type checking (plain JS, `any`) a forgotten await would
|
|
665
|
+
* hand the promise itself to drizzle
|
|
666
|
+
*/
|
|
667
|
+
function guardAgainstMissingAwait(promise) {
|
|
668
|
+
for (const key of [
|
|
669
|
+
"query",
|
|
670
|
+
"sql",
|
|
671
|
+
"merge"
|
|
672
|
+
]) Object.defineProperty(promise, key, {
|
|
673
|
+
enumerable: true,
|
|
674
|
+
get() {
|
|
675
|
+
throw new RumbleError(`Tried to access "${key}" on the result of abilities.<table>.filter(...) which is a Promise. filter() is async, did you forget to await it?`);
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
return promise;
|
|
679
|
+
}
|
|
664
680
|
const makeNothingRegisteredWarner = (config, logger) => debounce((model, action) => {
|
|
665
681
|
const msg = `No abilities registered for ${model}/${action} — blocking everything. Register the ability or ignore this warning if intentional.`;
|
|
666
682
|
if (logger) logger.warn({
|
|
@@ -716,8 +732,10 @@ const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerC
|
|
|
716
732
|
*/
|
|
717
733
|
when: (queryFilter) => {
|
|
718
734
|
for (const action of actions) {
|
|
719
|
-
if (queryFilters.get(action) === "unrestricted")
|
|
720
|
-
|
|
735
|
+
if (queryFilters.get(action) === "unrestricted") {
|
|
736
|
+
if (setUnrestrictedByThisCall.has(action)) queryFilters.set(action, []);
|
|
737
|
+
else continue;
|
|
738
|
+
}
|
|
721
739
|
queryFilters.get(action).push(queryFilter);
|
|
722
740
|
}
|
|
723
741
|
} };
|
|
@@ -822,7 +840,7 @@ by: (explicitFilter) => {
|
|
|
822
840
|
* @example
|
|
823
841
|
* ```ts
|
|
824
842
|
* author: t.relation("author", {
|
|
825
|
-
* query: (_args, ctx) => ctx.abilities.users.filter("read").query.single,
|
|
843
|
+
* query: async (_args, ctx) => (await ctx.abilities.users.filter("read")).query.single,
|
|
826
844
|
* }),
|
|
827
845
|
* ´´´
|
|
828
846
|
*/
|
|
@@ -860,7 +878,7 @@ by: (explicitFilter) => {
|
|
|
860
878
|
* .where(
|
|
861
879
|
* and(
|
|
862
880
|
* eq(schema.users.id, args.userId),
|
|
863
|
-
* ctx.abilities.users.filter("update").sql.where,
|
|
881
|
+
* (await ctx.abilities.users.filter("update")).sql.where,
|
|
864
882
|
* ),
|
|
865
883
|
* );
|
|
866
884
|
* ```
|
|
@@ -876,7 +894,7 @@ by: (explicitFilter) => {
|
|
|
876
894
|
* @example
|
|
877
895
|
* ```ts
|
|
878
896
|
* author: t.relation("author", {
|
|
879
|
-
* query: (_args, ctx) => ctx.abilities.users.filter("read").query.single,
|
|
897
|
+
* query: async (_args, ctx) => (await ctx.abilities.users.filter("read")).query.single,
|
|
880
898
|
* }),
|
|
881
899
|
* ´´´
|
|
882
900
|
*/
|
|
@@ -912,7 +930,7 @@ by: (explicitFilter) => {
|
|
|
912
930
|
* .where(
|
|
913
931
|
* and(
|
|
914
932
|
* eq(schema.users.id, args.userId),
|
|
915
|
-
* ctx.abilities.users.filter("update").sql.where,
|
|
933
|
+
* (await ctx.abilities.users.filter("update")).sql.where,
|
|
916
934
|
* ),
|
|
917
935
|
* );
|
|
918
936
|
* ```
|
|
@@ -935,8 +953,8 @@ by: (explicitFilter) => {
|
|
|
935
953
|
return ret;
|
|
936
954
|
}
|
|
937
955
|
return { withContext: (userContext) => {
|
|
938
|
-
|
|
939
|
-
const assembleAbilities = (attributes) => {
|
|
956
|
+
const prepare = (action) => {
|
|
957
|
+
const assembleAbilities = async (attributes) => {
|
|
940
958
|
const filters = queryFilters.get(action);
|
|
941
959
|
if (filters === "unrestricted") {
|
|
942
960
|
attributes[ATTR_ABILITIES_STATUS] = "unrestricted";
|
|
@@ -947,19 +965,12 @@ by: (explicitFilter) => {
|
|
|
947
965
|
nothingRegisteredWarningLogger(String(tableName), action);
|
|
948
966
|
return transformToResponse(blockEverythingFilter);
|
|
949
967
|
}
|
|
950
|
-
const
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
const result = func(userContext);
|
|
955
|
-
if (result === "allow") {
|
|
956
|
-
attributes[ATTR_ABILITIES_STATUS] = "unrestricted";
|
|
957
|
-
return transformToResponse();
|
|
958
|
-
}
|
|
959
|
-
if (result === void 0) continue;
|
|
960
|
-
dynamicResults[filtersReturned++] = result;
|
|
968
|
+
const rawResults = await Promise.all(dynamicQueryFilters[action].map((func) => func(userContext)));
|
|
969
|
+
if (rawResults.includes("allow")) {
|
|
970
|
+
attributes[ATTR_ABILITIES_STATUS] = "unrestricted";
|
|
971
|
+
return transformToResponse();
|
|
961
972
|
}
|
|
962
|
-
dynamicResults
|
|
973
|
+
const dynamicResults = rawResults.filter((r) => r !== void 0);
|
|
963
974
|
attributes[ATTR_ABILITIES_DYNAMIC] = dynamicResults.length;
|
|
964
975
|
attributes[ATTR_ABILITIES_STATIC] = simpleQueryFilters[action].length;
|
|
965
976
|
const allQueryFilters = [...simpleQueryFilters[action], ...dynamicResults];
|
|
@@ -974,13 +985,13 @@ by: (explicitFilter) => {
|
|
|
974
985
|
attributes[ATTR_ABILITIES_STATUS] = "applied";
|
|
975
986
|
return transformToResponse(mergedFilters);
|
|
976
987
|
};
|
|
977
|
-
const run = (span) => {
|
|
988
|
+
const run = async (span) => {
|
|
978
989
|
const attributes = {
|
|
979
990
|
[ATTR_TABLE]: String(tableName),
|
|
980
991
|
[ATTR_ACTION]: action
|
|
981
992
|
};
|
|
982
993
|
try {
|
|
983
|
-
const result = assembleAbilities(attributes);
|
|
994
|
+
const result = await assembleAbilities(attributes);
|
|
984
995
|
log?.debug({
|
|
985
996
|
...attributes,
|
|
986
997
|
...traceCorrelationFields(telemetryConfig, span)
|
|
@@ -998,14 +1009,23 @@ by: (explicitFilter) => {
|
|
|
998
1009
|
span?.setAttributes(attributes);
|
|
999
1010
|
}
|
|
1000
1011
|
};
|
|
1001
|
-
if (otel?.enabled && otel.tracer) return otel.tracer.startActiveSpan(SPAN_ABILITIES_PREPARE, (span) => {
|
|
1012
|
+
if (otel?.enabled && otel.tracer) return otel.tracer.startActiveSpan(SPAN_ABILITIES_PREPARE, async (span) => {
|
|
1002
1013
|
try {
|
|
1003
|
-
return run(span);
|
|
1014
|
+
return await run(span);
|
|
1004
1015
|
} finally {
|
|
1005
1016
|
span.end();
|
|
1006
1017
|
}
|
|
1007
1018
|
});
|
|
1008
1019
|
return run();
|
|
1020
|
+
};
|
|
1021
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1022
|
+
return { filter: (action) => {
|
|
1023
|
+
let prepared = cache.get(action);
|
|
1024
|
+
if (!prepared) {
|
|
1025
|
+
prepared = guardAgainstMissingAwait(prepare(action));
|
|
1026
|
+
cache.set(action, prepared);
|
|
1027
|
+
}
|
|
1028
|
+
return prepared;
|
|
1009
1029
|
} };
|
|
1010
1030
|
} };
|
|
1011
1031
|
};
|
|
@@ -1088,16 +1108,18 @@ const createEnumImplementer = ({ schema, schemaBuilder }) => {
|
|
|
1088
1108
|
if (enumObject) {
|
|
1089
1109
|
enumSchemaName = enumObject.enumName;
|
|
1090
1110
|
enumValues = enumObject.enumValues;
|
|
1091
|
-
} else if (enumColumn)
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1111
|
+
} else if (enumColumn) {
|
|
1112
|
+
if (is(enumColumn, MySqlEnumColumn) || is(enumColumn, MySqlEnumObjectColumn)) {
|
|
1113
|
+
enumValues = enumColumn.enumValues;
|
|
1114
|
+
const colTable = enumColumn.table;
|
|
1115
|
+
const tableName = colTable && isTable(colTable) ? getTableName(colTable) : void 0;
|
|
1116
|
+
enumSchemaName = tableName ? `${tableName}_${enumColumn.name}` : enumColumn.name;
|
|
1117
|
+
} else {
|
|
1118
|
+
const pgEnum = enumColumn.enum;
|
|
1119
|
+
if (!pgEnum) throw new RumbleError(`Could not find enum definition on column. Make sure the column is a pgEnum column.`);
|
|
1120
|
+
enumSchemaName = pgEnum.enumName;
|
|
1121
|
+
enumValues = enumColumn.enumValues;
|
|
1122
|
+
}
|
|
1101
1123
|
}
|
|
1102
1124
|
if (!enumSchemaName || !enumValues) throw new RumbleError("Could not determine enum structure!");
|
|
1103
1125
|
const graphqlImplementationName = refName ?? `${capitalize(toCamelCase(enumSchemaName))}Enum`;
|
|
@@ -1118,8 +1140,10 @@ function mapSQLTypeToGraphQLType({ sqlType, fieldName }) {
|
|
|
1118
1140
|
if (isIntLikeSQLTypeString(normalized)) ret = "Int";
|
|
1119
1141
|
if (isBigIntLikeSQLTypeString(normalized)) ret = "BigInt";
|
|
1120
1142
|
if (isFloatLikeSQLTypeString(normalized)) ret = "Float";
|
|
1121
|
-
if (isStringLikeSQLTypeString(normalized))
|
|
1122
|
-
|
|
1143
|
+
if (isStringLikeSQLTypeString(normalized)) {
|
|
1144
|
+
if (fieldName && (fieldName.toLowerCase().endsWith("_id") || fieldName.toLowerCase().endsWith("id"))) ret = "ID";
|
|
1145
|
+
else ret = "String";
|
|
1146
|
+
}
|
|
1123
1147
|
if (isIDLikeSQLTypeString(normalized)) ret = "ID";
|
|
1124
1148
|
if (isBooleanSQLTypeString(normalized)) ret = "Boolean";
|
|
1125
1149
|
if (isDateTimeLikeSQLTypeString(normalized)) ret = "DateTime";
|
|
@@ -1367,8 +1391,9 @@ const clientCreatorImplementer = ({ builtSchema }) => {
|
|
|
1367
1391
|
useExternalUrqlClient,
|
|
1368
1392
|
removeExisting
|
|
1369
1393
|
})}`);
|
|
1394
|
+
const schema = builtSchema();
|
|
1370
1395
|
await generateFromSchema({
|
|
1371
|
-
schema
|
|
1396
|
+
schema,
|
|
1372
1397
|
outputPath,
|
|
1373
1398
|
rumbleImportPath,
|
|
1374
1399
|
apiUrl,
|
|
@@ -1440,7 +1465,7 @@ const createCountQueryImplementer = ({ db, schemaBuilder, whereArgImplementer, m
|
|
|
1440
1465
|
resolve: async (_root, args, ctx, _info) => {
|
|
1441
1466
|
if (isAllowed && !await isAllowed(ctx)) throw new RumbleErrorSafe("Not allowed to perform this action");
|
|
1442
1467
|
deepSetProto(args);
|
|
1443
|
-
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);
|
|
1468
|
+
return db.select({ count: count() }).from(tableSchema.table).where((await ctx.abilities[table].filter(listAction)).merge(mapNullFieldsToUndefined(args)).sql.where).then(assertFirstEntryExists).then((r) => r.count);
|
|
1444
1469
|
}
|
|
1445
1470
|
}) };
|
|
1446
1471
|
});
|
|
@@ -2018,15 +2043,15 @@ const createObjectImplementer = ({ db, search, otel, logger: loggerConfig, schem
|
|
|
2018
2043
|
subscribe,
|
|
2019
2044
|
nullable,
|
|
2020
2045
|
description: `Get the ${pluralize.plural(relationSchema.tsName)} related to this ${pluralize.singular(tableSchema.tsName)}`,
|
|
2021
|
-
query: (args, ctx) => {
|
|
2046
|
+
query: async (args, ctx) => {
|
|
2022
2047
|
args = JSON.parse(JSON.stringify(args));
|
|
2023
2048
|
if (isMany) adjustQueryArgsForSearch({
|
|
2024
2049
|
search,
|
|
2025
2050
|
args,
|
|
2026
2051
|
tableSchema: relationSchema,
|
|
2027
|
-
abilities: ctx.abilities[relationSchema.tsName].filter(readAction)
|
|
2052
|
+
abilities: await ctx.abilities[relationSchema.tsName].filter(readAction)
|
|
2028
2053
|
});
|
|
2029
|
-
const filter = ctx.abilities[relationSchema.tsName].filter(readAction).merge({
|
|
2054
|
+
const filter = (await ctx.abilities[relationSchema.tsName].filter(readAction)).merge({
|
|
2030
2055
|
where: args.where,
|
|
2031
2056
|
limit: args.limit,
|
|
2032
2057
|
extras: args.extras
|
|
@@ -2186,16 +2211,16 @@ const createQueryImplementer = ({ db, schemaBuilder, search, otel, logger: logge
|
|
|
2186
2211
|
});
|
|
2187
2212
|
},
|
|
2188
2213
|
args: manyArgs,
|
|
2189
|
-
resolve: (query, _root, args, ctx, _info) => {
|
|
2214
|
+
resolve: async (query, _root, args, ctx, _info) => {
|
|
2190
2215
|
deepSetProto(args);
|
|
2191
2216
|
adjustQueryArgsForSearch({
|
|
2192
2217
|
search,
|
|
2193
2218
|
args,
|
|
2194
2219
|
tableSchema,
|
|
2195
|
-
abilities: ctx.abilities[table].filter(listAction)
|
|
2220
|
+
abilities: await ctx.abilities[table].filter(listAction)
|
|
2196
2221
|
});
|
|
2197
2222
|
const mappedArgs = mapNullFieldsToUndefined(args);
|
|
2198
|
-
const filter = ctx.abilities[table].filter(listAction).merge(mappedArgs).query.many;
|
|
2223
|
+
const filter = (await ctx.abilities[table].filter(listAction)).merge(mappedArgs).query.many;
|
|
2199
2224
|
if (mappedArgs.offset) filter.offset = mappedArgs.offset;
|
|
2200
2225
|
if (mappedArgs.orderBy) filter.orderBy = mappedArgs.orderBy;
|
|
2201
2226
|
const queryArgs = query(filter);
|
|
@@ -2223,9 +2248,9 @@ const createQueryImplementer = ({ db, schemaBuilder, search, otel, logger: logge
|
|
|
2223
2248
|
smartSubscription: true,
|
|
2224
2249
|
description: `Get a single ${singularName} by ID`,
|
|
2225
2250
|
args: { id: t.arg.id({ required: true }) },
|
|
2226
|
-
resolve: (query, _root, args, ctx, _info) => {
|
|
2251
|
+
resolve: async (query, _root, args, ctx, _info) => {
|
|
2227
2252
|
deepSetProto(args);
|
|
2228
|
-
const filter = ctx.abilities[table].filter(readAction).merge({ where: { [primaryKeyField.name]: args.id } }).query.single;
|
|
2253
|
+
const filter = (await ctx.abilities[table].filter(readAction)).merge({ where: { [primaryKeyField.name]: args.id } }).query.single;
|
|
2229
2254
|
const queryArgs = query(filter);
|
|
2230
2255
|
if (filter.columns) queryArgs.columns = filter.columns;
|
|
2231
2256
|
return findFirst ? findFirst({
|