@m1212e/rumble 0.12.12 → 0.12.14

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 CHANGED
@@ -129,10 +129,10 @@ rumble({
129
129
  ## Helpers
130
130
  Rumble offers various helpers to make it easy and fast to implement your api. Ofcourse you can write your api by hand using the provided `schemaBuilder` from the rumble initiator, but since this might get repetitive, the provided helpers automate a lot of this work for you while also automatically applying the concepts of rumble directly into your api.
131
131
 
132
- ### arg
133
- `arg` is a helper to implement query arguments for filtering the results of a query for certain results. In many cases you would implement arguments for a query with something as `matchUsername: t.arg.string()` which is supposed to restrict the query to users which have that username. The arg helper implements such a filter tailored to the specific entity which you then can directly pass on to the database query.
132
+ ### whereArg
133
+ `whereArg` is a helper to implement query arguments for filtering the results of a query for certain results. In many cases you would implement arguments for a query with something as `matchUsername: t.arg.string()` which is supposed to restrict the query to users which have that username. The whereArg helper implements such a filter tailored to the specific entity which you then can directly pass on to the database query.
134
134
  ```ts
135
- const WhereArgs = arg({
135
+ const WhereArgs = whereArg({
136
136
  table: "posts",
137
137
  });
138
138
 
@@ -210,6 +210,9 @@ const enumRef = enum_({
210
210
  ```
211
211
  > The enum parameter allows other fields to be used to reference an enum. This is largely due to how this is used internally. Because of the way how drizzle handles enums, we are not able to provide type safety with enums. In case you actually need to use it, the above way is the recommended approach.
212
212
 
213
+ ### and more...
214
+ See [the example file](./example/src/main.ts) or clone it and let intellisense guide you. Rumble offers various other helpers which might become handy!
215
+
213
216
  ## Running the server
214
217
  In case you directly want to run a server from your rumble instance, you can do so by using the `createYoga` function. It returns a graphql `yoga` instance which can be used to provide a graphql api in [a multitude of ways](https://the-guild.dev/graphql/yoga-server/docs/integrations/z-other-environments).
215
218
  ```ts
@@ -242,7 +245,9 @@ await clientCreator({
242
245
  // to point to your custom client
243
246
  });
244
247
  ```
245
- > The client uses [urql](https://nearform.com/open-source/urql/docs/basics/core/) under the hood. If you would like more info on how the internals work, please see the docs.
248
+ > The client uses [urql](https://nearform.com/open-source/urql/docs/basics/core/) under the hood. If you would like more info on how the internals work, please see their docs.
249
+
250
+ This way of generating code is especially helpful in monorepos, where it is convenient to output client code when running the server during development. If you do not use a monorepo and want to decouple the generation process, see below.
246
251
 
247
252
  An example usage might look like this:
248
253
  ```ts
@@ -272,7 +277,7 @@ users.subscribe((s) => s?.at(0));
272
277
  // you can directly access the values of an awaited result
273
278
  console.log(users.firstName)
274
279
  ```
275
- ### Alternative
280
+ ### Alternative decoupled client generation
276
281
  As an alternative to use the client generator with a fully instanciated rumble instance, you can also import the `generateFromSchema` function from rumble and pass it a standard `GraphQLSchema` object to generate the client:
277
282
  ```ts
278
283
  import { generateFromSchema } from "@m1212e/rumble";
package/out/index.cjs CHANGED
@@ -22,35 +22,21 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
 
23
23
  //#endregion
24
24
  let node_fs_promises = require("node:fs/promises");
25
- node_fs_promises = __toESM(node_fs_promises);
26
25
  let node_path = require("node:path");
27
- node_path = __toESM(node_path);
28
26
  let graphql = require("graphql");
29
- graphql = __toESM(graphql);
30
27
  let es_toolkit = require("es-toolkit");
31
- es_toolkit = __toESM(es_toolkit);
32
28
  let wonka = require("wonka");
33
- wonka = __toESM(wonka);
34
29
  let __escape_tech_graphql_armor = require("@escape.tech/graphql-armor");
35
- __escape_tech_graphql_armor = __toESM(__escape_tech_graphql_armor);
36
30
  let __graphql_yoga_plugin_disable_introspection = require("@graphql-yoga/plugin-disable-introspection");
37
- __graphql_yoga_plugin_disable_introspection = __toESM(__graphql_yoga_plugin_disable_introspection);
38
31
  let graphql_yoga = require("graphql-yoga");
39
- graphql_yoga = __toESM(graphql_yoga);
40
32
  let sofa_api = require("sofa-api");
41
- sofa_api = __toESM(sofa_api);
42
33
  let drizzle_orm = require("drizzle-orm");
43
- drizzle_orm = __toESM(drizzle_orm);
44
34
  let drizzle_orm_casing = require("drizzle-orm/casing");
45
- drizzle_orm_casing = __toESM(drizzle_orm_casing);
46
35
  let drizzle_orm_pg_core = require("drizzle-orm/pg-core");
47
- drizzle_orm_pg_core = __toESM(drizzle_orm_pg_core);
48
36
  let pluralize = require("pluralize");
49
37
  pluralize = __toESM(pluralize);
50
38
  let drizzle_orm_mysql_core = require("drizzle-orm/mysql-core");
51
- drizzle_orm_mysql_core = __toESM(drizzle_orm_mysql_core);
52
39
  let drizzle_orm_sqlite_core = require("drizzle-orm/sqlite-core");
53
- drizzle_orm_sqlite_core = __toESM(drizzle_orm_sqlite_core);
54
40
  let __pothos_core = require("@pothos/core");
55
41
  __pothos_core = __toESM(__pothos_core);
56
42
  let __pothos_plugin_drizzle = require("@pothos/plugin-drizzle");
@@ -58,7 +44,6 @@ __pothos_plugin_drizzle = __toESM(__pothos_plugin_drizzle);
58
44
  let __pothos_plugin_smart_subscriptions = require("@pothos/plugin-smart-subscriptions");
59
45
  __pothos_plugin_smart_subscriptions = __toESM(__pothos_plugin_smart_subscriptions);
60
46
  let graphql_scalars = require("graphql-scalars");
61
- graphql_scalars = __toESM(graphql_scalars);
62
47
 
63
48
  //#region lib/client/generate/client.ts
64
49
  function generateClient({ apiUrl, rumbleImportPath, useExternalUrqlClient, availableSubscriptions }) {
@@ -259,25 +244,21 @@ function makeGraphQLQuery({ queryName, input, client, enableSubscription = false
259
244
  const argsString = stringifyArgumentObjectToGraphqlList(input[argsKey] ?? {});
260
245
  const operationString = (operationVerb) => `${operationVerb} ${otwQueryName} { ${queryName}${argsString} { ${stringifySelection(input)} }}`;
261
246
  let awaitedReturnValueReference;
262
- const response = (0, wonka.pipe)((0, wonka.merge)([client.query(operationString("query"), {}), enableSubscription ? client.subscription(operationString("subscription"), {}) : wonka.empty]), (0, wonka.map)((v) => {
247
+ const observable = (0, wonka.pipe)((0, wonka.merge)([client.query(operationString("query"), {}), enableSubscription ? client.subscription(operationString("subscription"), {}) : wonka.empty]), (0, wonka.map)((v) => {
263
248
  const data = v.data?.[queryName];
264
249
  if (!data && v.error) throw v.error;
265
250
  return data;
266
251
  }), (0, wonka.onPush)((data) => {
267
252
  if (awaitedReturnValueReference) Object.assign(awaitedReturnValueReference, data);
268
- }));
253
+ }), wonka.toObservable);
269
254
  const promise = new Promise((resolve) => {
270
- const unsub = (0, wonka.toObservable)(response).subscribe((v) => {
255
+ const unsub = observable.subscribe((v) => {
271
256
  unsub();
272
- const newObservableWithAwaitedValue = (0, wonka.pipe)((0, wonka.merge)([response, (0, wonka.fromValue)(v)]), wonka.toObservable);
273
- awaitedReturnValueReference = {
274
- ...v,
275
- ...newObservableWithAwaitedValue
276
- };
257
+ awaitedReturnValueReference = Object.assign(v, observable);
277
258
  resolve(awaitedReturnValueReference);
278
259
  }).unsubscribe;
279
260
  });
280
- Object.assign(promise, (0, wonka.toObservable)(response));
261
+ Object.assign(promise, observable);
281
262
  return promise;
282
263
  }
283
264
  function makeGraphQLMutation({ mutationName, input, client }) {
@@ -716,6 +697,8 @@ function tableHelper({ db, table }) {
716
697
  if (!foundRelation) throw new RumbleError(`Could not find schema for ${JSON.stringify(table)}`);
717
698
  const foundSchema = Object.values(db._.schema).find((schema) => schema.dbName === foundRelation.table[drizzleOriginalNameSymbol]);
718
699
  if (!foundSchema) throw new RumbleError(`Could not find schema for ${JSON.stringify(table)}`);
700
+ const fullSchema = db._.fullSchema?.[foundSchema.tsName];
701
+ if (!fullSchema) throw new RumbleError(`Could not find full schema for ${JSON.stringify(table)}`);
719
702
  return {
720
703
  columns: foundSchema.columns,
721
704
  primaryKey: foundSchema.primaryKey,
@@ -723,7 +706,8 @@ function tableHelper({ db, table }) {
723
706
  dbName: foundSchema.dbName,
724
707
  tsName: foundSchema.tsName,
725
708
  foundSchema,
726
- foundRelation
709
+ foundRelation,
710
+ fullSchema
727
711
  };
728
712
  }
729
713
 
@@ -1078,6 +1062,7 @@ const createWhereArgImplementer = ({ db, schemaBuilder, enumImplementer }) => {
1078
1062
  const clientCreatorImplementer = ({ builtSchema }) => {
1079
1063
  if (process.env.NODE_ENV !== "development") console.warn("Running rumble client generation in non development mode. Are you sure this is correct?");
1080
1064
  const clientCreator = async ({ apiUrl, outputPath, rumbleImportPath, useExternalUrqlClient }) => {
1065
+ if (process.env.NODE_ENV !== "development") console.warn("Running rumble client generation in non development mode. Are you sure this is correct?");
1081
1066
  await generateFromSchema({
1082
1067
  schema: builtSchema(),
1083
1068
  outputPath,
@@ -1102,6 +1087,56 @@ const createContextFunction = ({ context: makeUserContext, abilityBuilder }) =>
1102
1087
  };
1103
1088
  };
1104
1089
 
1090
+ //#endregion
1091
+ //#region lib/helpers/protoMapper.ts
1092
+ function deepSetProto(obj, proto = Object.prototype, seen = /* @__PURE__ */ new WeakSet()) {
1093
+ if (obj === null || typeof obj !== "object") return;
1094
+ if (seen.has(obj)) return;
1095
+ seen.add(obj);
1096
+ Object.setPrototypeOf(obj, proto);
1097
+ for (const key of Object.keys(obj)) deepSetProto(obj[key], proto, seen);
1098
+ }
1099
+
1100
+ //#endregion
1101
+ //#region lib/countQuery.ts
1102
+ const createCountQueryImplementer = ({ db, schemaBuilder, whereArgImplementer, makePubSubInstance }) => {
1103
+ return ({ table, listAction = "read", isAllowed }) => {
1104
+ const WhereArg = whereArgImplementer({ table });
1105
+ const { registerOnInstance } = makePubSubInstance({ table });
1106
+ const tableSchema = tableHelper({
1107
+ db,
1108
+ table
1109
+ });
1110
+ return schemaBuilder.queryFields((t) => {
1111
+ return { [`${pluralize.default.plural(table.toString())}Count`]: t.field({
1112
+ type: "Int",
1113
+ nullable: false,
1114
+ smartSubscription: true,
1115
+ description: `Count all ${pluralize.default.plural(table.toString())}`,
1116
+ subscribe: (subscriptions, _root, _args, _ctx, _info) => {
1117
+ registerOnInstance({
1118
+ instance: subscriptions,
1119
+ action: "created"
1120
+ });
1121
+ registerOnInstance({
1122
+ instance: subscriptions,
1123
+ action: "removed"
1124
+ });
1125
+ },
1126
+ args: { where: t.arg({
1127
+ type: WhereArg,
1128
+ required: false
1129
+ }) },
1130
+ resolve: async (root, args, ctx, info) => {
1131
+ if (isAllowed && !await isAllowed(ctx)) throw new RumbleErrorSafe("Not allowed to perform this action");
1132
+ deepSetProto(args);
1133
+ return db.select({ count: (0, drizzle_orm.count)() }).from(tableSchema.fullSchema).where(ctx.abilities[table].filter(listAction).merge(mapNullFieldsToUndefined(args)).sql.where).then(assertFirstEntryExists).then((r) => r.count);
1134
+ }
1135
+ }) };
1136
+ });
1137
+ };
1138
+ };
1139
+
1105
1140
  //#endregion
1106
1141
  //#region lib/helpers/sofaOpenAPIWebhookDocs.ts
1107
1142
  const sofaOpenAPIWebhookDocs = {
@@ -1550,7 +1585,7 @@ const createQueryImplementer = ({ db, schemaBuilder, search, whereArgImplementer
1550
1585
  },
1551
1586
  args: manyArgs,
1552
1587
  resolve: (query, _root, args, ctx, _info) => {
1553
- Object.setPrototypeOf(args, Object.prototype);
1588
+ deepSetProto(args);
1554
1589
  adjustQueryArgsForSearch({
1555
1590
  search,
1556
1591
  args,
@@ -1573,7 +1608,7 @@ const createQueryImplementer = ({ db, schemaBuilder, search, whereArgImplementer
1573
1608
  description: `Get a single ${pluralize.default.singular(table.toString())} by ID`,
1574
1609
  args: { id: t.arg.id({ required: true }) },
1575
1610
  resolve: (query, _root, args, ctx, _info) => {
1576
- Object.setPrototypeOf(args, Object.prototype);
1611
+ deepSetProto(args);
1577
1612
  const filter = ctx.abilities[table].filter(readAction).merge({ where: { [primaryKeyField.name]: args.id } }).query.single;
1578
1613
  const q = query(filter);
1579
1614
  if (filter.columns) q.columns = filter.columns;
@@ -1849,6 +1884,12 @@ export const db = drizzle(
1849
1884
  orderArgImplementer: orderArg,
1850
1885
  makePubSubInstance
1851
1886
  });
1887
+ const countQuery = createCountQueryImplementer({
1888
+ ...rumbleInput,
1889
+ schemaBuilder,
1890
+ whereArgImplementer: whereArg,
1891
+ makePubSubInstance
1892
+ });
1852
1893
  const builtSchema = lazy(() => schemaBuilder.toSchema());
1853
1894
  const createYoga = (args) => {
1854
1895
  const enableApiDocs = args?.enableApiDocs ?? process?.env?.NODE_ENV === "development";
@@ -1882,7 +1923,8 @@ export const db = drizzle(
1882
1923
  clientCreator: clientCreatorImplementer({
1883
1924
  ...rumbleInput,
1884
1925
  builtSchema
1885
- })
1926
+ }),
1927
+ countQuery
1886
1928
  };
1887
1929
  };
1888
1930