@m1212e/rumble 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +15 -0
- package/index.cjs +2 -0
- package/index.cjs.map +1 -0
- package/index.d.cts +176 -0
- package/index.d.ts +176 -0
- package/index.js +2 -0
- package/index.js.map +1 -0
- package/package.json +24 -0
package/README.md
ADDED
package/index.cjs
ADDED
@@ -0,0 +1,2 @@
|
|
1
|
+
'use strict';var P=require('@pothos/core'),O=require('@pothos/plugin-drizzle'),graphqlYoga=require('graphql-yoga'),drizzleOrm=require('drizzle-orm');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var P__default=/*#__PURE__*/_interopDefault(P);var O__default=/*#__PURE__*/_interopDefault(O);function h(e){return typeof e!="function"}function A(e){return typeof e=="function"&&e.constructor.name!=="AsyncFunction"}var C=({db:e,actions:s=["create","read","update","delete"]})=>{let u={},c={},p=n=>({allow:r=>{let i=c[n];i||(i={},c[n]=i);let a=Array.isArray(r)?r:[r];for(let d of a){let o=i[d];o||(o=[],i[d]=o);}return {when:d=>{for(let o of a)i[o].push(d);}}}});for(let n of Object.keys(e.query))u[n]=p(n);return {...u,registeredConditions:c,buildWithUserContext:n=>{let r={},i=a=>({filter:d=>{let o=c[a];if(!o)throw "TODO (No allowed entry found for this condition) #1";let f=o[d];if(!f)throw "TODO (No allowed entry found for this condition) #2";let b=f.filter(h),B=f.filter(A).map(t=>t(n)),D=[...b,...B],m;for(let t of D)t.limit&&(m===void 0||t.limit>m)&&(m=t.limit);let l;for(let t of D)t.columns&&(l===void 0?l=t.columns:l={...l,...t.columns});let x=D.filter(t=>t.where).map(t=>t.where);return {where:x.length>0?drizzleOrm.or(...x):void 0,columns:l,limit:m}}});for(let a of Object.keys(e.query))r[a]=i(a);return r}}};var U=async({db:e,nativeServerOptions:s,context:u})=>{let c=C({db:e}),p=async r=>{let i=u?await u(r):{};return {...i,abilities:c.buildWithUserContext(i)}},n=new P__default.default({plugins:[O__default.default],drizzle:{client:e},defaultFieldNullability:!1,defaultInputFieldRequiredness:!0});return n.queryType({}),n.mutationType({}),{abilityBuilder:c,schemaBuilder:n,yoga:()=>graphqlYoga.createYoga({...s,schema:n.toSchema(),context:p})}};var y=class extends Error{constructor(s){super(s),this.name="RumbleError";}};var g=e=>{if(!e)throw new y("Value not found but required (findFirst)");return e},z=e=>{let s=e.at(0);if(!s)throw new y("Value not found but required (firstEntry)");return s};exports.RumbleError=y;exports.assertFindFirstExists=g;exports.assertFirstEntryExists=z;exports.rumble=U;//# sourceMappingURL=index.cjs.map
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
package/index.cjs.map
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../lib/abilities/builder.ts","../lib/gql/builder.ts","../lib/helpers/rumbleError.ts","../lib/helpers/helper.ts"],"names":["isSimpleCondition","condition","isSyncFunctionCondition","createAbilityBuilder","db","actions","builder","registeredConditions","createEntityObject","entityKey","action","conditionsPerEntity","conditionsPerEntityAndAction","userContext","simpleConditions","syncFunctionConditions","allConditionObjects","highestLimit","conditionObject","combinedAllowedColumns","accumulatedWhereConditions","o","or","rumble","nativeServerOptions","makeUserContext","abilityBuilder","makeContext","req","nativeBuilder","SchemaBuilder","DrizzlePlugin","createYoga","RumbleError","message","assertFindFirstExists","value","assertFirstEntryExists","v"],"mappings":"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","file":"index.cjs","sourcesContent":["import { or } from \"drizzle-orm\";\nimport type {\n\tGenericDrizzleDbTypeConstraints,\n\tQueryConditionObject,\n} from \"../types/genericDrizzleDbType\";\n\nexport type AbilityBuilder = ReturnType<typeof createAbilityBuilder>;\n\ntype Condition<DBParameters, UserContext> =\n\t| SimpleCondition<DBParameters>\n\t| SyncFunctionCondition<DBParameters, UserContext>;\n// | AsyncFunctionCondition<DBParameters, UserContext>;\n\ntype SimpleCondition<DBParameters> = DBParameters;\ntype SyncFunctionCondition<DBParameters, UserContext> = (\n\tcontext: UserContext,\n) => DBParameters;\n// type AsyncFunctionCondition<DBParameters, UserContext> = (\n// \tcontext: UserContext,\n// ) => Promise<DBParameters>;\n\n// type guards for the condition types\nfunction isSimpleCondition<DBParameters, UserContext>(\n\tcondition: Condition<DBParameters, UserContext>,\n): condition is SimpleCondition<DBParameters> {\n\treturn typeof condition !== \"function\";\n}\n\nfunction isSyncFunctionCondition<DBParameters, UserContext>(\n\tcondition: Condition<DBParameters, UserContext>,\n): condition is SyncFunctionCondition<DBParameters, UserContext> {\n\treturn (\n\t\ttypeof condition === \"function\" &&\n\t\tcondition.constructor.name !== \"AsyncFunction\"\n\t);\n}\n\n// function isAsyncFunctionCondition<DBParameters, UserContext>(\n// \tcondition: Condition<DBParameters, UserContext>,\n// ): condition is AsyncFunctionCondition<DBParameters, UserContext> {\n// \treturn (\n// \t\ttypeof condition === \"function\" &&\n// \t\tcondition.constructor.name === \"AsyncFunction\"\n// \t);\n// }\n\nexport const createAbilityBuilder = <\n\tUserContext extends Record<string, any>,\n\tDB extends GenericDrizzleDbTypeConstraints,\n\tAction extends string = \"create\" | \"read\" | \"update\" | \"delete\",\n>({\n\tdb,\n\tactions = [\"create\", \"read\", \"update\", \"delete\"] as Action[],\n}: {\n\tdb: DB;\n\tactions?: Action[];\n}) => {\n\ttype DBEntityKey = keyof DB[\"query\"];\n\n\tconst builder: {\n\t\t[key in DBEntityKey]: ReturnType<typeof createEntityObject>;\n\t} = {} as any;\n\n\tconst registeredConditions: {\n\t\t[key in DBEntityKey]: {\n\t\t\t[key in Action[number]]: (\n\t\t\t\t| QueryConditionObject\n\t\t\t\t| ((context: UserContext) => QueryConditionObject)\n\t\t\t)[];\n\t\t\t// | ((context: UserContext) => Promise<QueryConditionObject>)\n\t\t};\n\t} = {} as any;\n\n\tconst createEntityObject = (entityKey: DBEntityKey) => ({\n\t\tallow: (action: Action | Action[]) => {\n\t\t\ttype DBParameters = Parameters<DB[\"query\"][DBEntityKey][\"findMany\"]>[0];\n\n\t\t\tlet conditionsPerEntity = registeredConditions[entityKey];\n\t\t\tif (!conditionsPerEntity) {\n\t\t\t\tconditionsPerEntity = {} as any;\n\t\t\t\tregisteredConditions[entityKey] = conditionsPerEntity;\n\t\t\t}\n\n\t\t\tconst actions = Array.isArray(action) ? action : [action];\n\t\t\tfor (const action of actions) {\n\t\t\t\tlet conditionsPerEntityAndAction = conditionsPerEntity[action];\n\t\t\t\tif (!conditionsPerEntityAndAction) {\n\t\t\t\t\tconditionsPerEntityAndAction = [];\n\t\t\t\t\tconditionsPerEntity[action] = conditionsPerEntityAndAction;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\twhen: (condition: Condition<DBParameters, UserContext>) => {\n\t\t\t\t\tfor (const action of actions) {\n\t\t\t\t\t\tconst conditionsPerEntityAndAction = conditionsPerEntity[action];\n\t\t\t\t\t\tconditionsPerEntityAndAction.push(condition);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t});\n\n\tfor (const entityKey of Object.keys(db.query) as DBEntityKey[]) {\n\t\tbuilder[entityKey] = createEntityObject(entityKey);\n\t}\n\treturn {\n\t\t...builder,\n\t\tregisteredConditions,\n\t\tbuildWithUserContext: (userContext: UserContext) => {\n\t\t\tconst builder: {\n\t\t\t\t[key in DBEntityKey]: ReturnType<typeof createEntityObject>;\n\t\t\t} = {} as any;\n\n\t\t\tconst createEntityObject = (entityKey: DBEntityKey) => ({\n\t\t\t\tfilter: (action: Action) => {\n\t\t\t\t\tconst conditionsPerEntity = registeredConditions[entityKey];\n\t\t\t\t\tif (!conditionsPerEntity) {\n\t\t\t\t\t\tthrow \"TODO (No allowed entry found for this condition) #1\";\n\t\t\t\t\t}\n\n\t\t\t\t\tconst conditionsPerEntityAndAction = conditionsPerEntity[action];\n\t\t\t\t\tif (!conditionsPerEntityAndAction) {\n\t\t\t\t\t\tthrow \"TODO (No allowed entry found for this condition) #2\";\n\t\t\t\t\t}\n\n\t\t\t\t\tconst simpleConditions =\n\t\t\t\t\t\tconditionsPerEntityAndAction.filter(isSimpleCondition);\n\n\t\t\t\t\tconst syncFunctionConditions = conditionsPerEntityAndAction\n\t\t\t\t\t\t.filter(isSyncFunctionCondition)\n\t\t\t\t\t\t.map((condition) => condition(userContext));\n\n\t\t\t\t\t// const asyncFunctionConditions = await Promise.all(\n\t\t\t\t\t// \tconditionsPerEntityAndAction\n\t\t\t\t\t// \t\t.filter(isAsyncFunctionCondition)\n\t\t\t\t\t// \t\t.map((condition) => condition(userContext)),\n\t\t\t\t\t// );\n\n\t\t\t\t\tconst allConditionObjects = [\n\t\t\t\t\t\t...simpleConditions,\n\t\t\t\t\t\t...syncFunctionConditions,\n\t\t\t\t\t\t// ...asyncFunctionConditions,\n\t\t\t\t\t];\n\n\t\t\t\t\tlet highestLimit = undefined;\n\t\t\t\t\tfor (const conditionObject of allConditionObjects) {\n\t\t\t\t\t\tif (conditionObject.limit) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\thighestLimit === undefined ||\n\t\t\t\t\t\t\t\tconditionObject.limit > highestLimit\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\thighestLimit = conditionObject.limit;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tlet combinedAllowedColumns: Record<string, any> | undefined =\n\t\t\t\t\t\tundefined;\n\t\t\t\t\tfor (const conditionObject of allConditionObjects) {\n\t\t\t\t\t\tif (conditionObject.columns) {\n\t\t\t\t\t\t\tif (combinedAllowedColumns === undefined) {\n\t\t\t\t\t\t\t\tcombinedAllowedColumns = conditionObject.columns;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcombinedAllowedColumns = {\n\t\t\t\t\t\t\t\t\t...combinedAllowedColumns,\n\t\t\t\t\t\t\t\t\t...conditionObject.columns,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst accumulatedWhereConditions = allConditionObjects\n\t\t\t\t\t\t.filter((o) => o.where)\n\t\t\t\t\t\t.map((o) => o.where);\n\n\t\t\t\t\tconst combinedWhere =\n\t\t\t\t\t\taccumulatedWhereConditions.length > 0\n\t\t\t\t\t\t\t? or(...accumulatedWhereConditions)\n\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\twhere: combinedWhere,\n\t\t\t\t\t\tcolumns: combinedAllowedColumns,\n\t\t\t\t\t\tlimit: highestLimit,\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tfor (const entityKey of Object.keys(db.query) as DBEntityKey[]) {\n\t\t\t\tbuilder[entityKey] = createEntityObject(entityKey);\n\t\t\t}\n\n\t\t\treturn builder;\n\t\t},\n\t};\n};\n","import SchemaBuilder from \"@pothos/core\";\nimport DrizzlePlugin from \"@pothos/plugin-drizzle\";\nimport { type YogaServerOptions, createYoga } from \"graphql-yoga\";\nimport { createAbilityBuilder } from \"../abilities/builder\";\nimport type { GenericDrizzleDbTypeConstraints } from \"../types/genericDrizzleDbType\";\n\nexport const rumble = async <\n\tUserContext extends Record<string, any>,\n\tDB extends GenericDrizzleDbTypeConstraints,\n\tRequestEvent extends Record<string, any>,\n>({\n\tdb,\n\tnativeServerOptions,\n\tcontext: makeUserContext,\n}: {\n\t/**\n\t * Your drizzle database instance\n\t */\n\tdb: DB;\n\t/**\n\t * Optional options for the native GraphQL Yoga server\n\t */\n\tnativeServerOptions?:\n\t\t| Omit<YogaServerOptions<RequestEvent, any>, \"schema\" | \"context\">\n\t\t| undefined;\n\t/**\n\t * A function for providing context for each request based on the incoming HTTP Request.\n\t * The type of the parameter equals the HTTPRequest type of your chosen server.\n\t */\n\tcontext?:\n\t\t| ((event: RequestEvent) => Promise<UserContext> | UserContext)\n\t\t| undefined;\n}) => {\n\tconst abilityBuilder = createAbilityBuilder<UserContext, DB>({\n\t\tdb,\n\t});\n\n\tconst makeContext = async (req: RequestEvent) => {\n\t\tconst userContext = makeUserContext\n\t\t\t? await makeUserContext(req)\n\t\t\t: ({} as UserContext);\n\t\treturn {\n\t\t\t...userContext,\n\t\t\tabilities: abilityBuilder.buildWithUserContext(userContext),\n\t\t};\n\t};\n\n\tconst nativeBuilder = new SchemaBuilder<{\n\t\tContext: Awaited<ReturnType<typeof makeContext>>;\n\t\t// Scalars: Scalars<Prisma.Decimal, Prisma.InputJsonValue | null, Prisma.InputJsonValue> & {\n\t\t// \tFile: {\n\t\t// \t\tInput: File;\n\t\t// \t\tOutput: never;\n\t\t// \t};\n\t\t// \tJSONObject: {\n\t\t// \t\tInput: any;\n\t\t// \t\tOutput: any;\n\t\t// \t};\n\t\t// };\n\t\tDrizzleSchema: DB[\"_\"][\"fullSchema\"];\n\t\tDefaultFieldNullability: false;\n\t\tDefaultArgumentNullability: false;\n\t\tDefaultInputFieldRequiredness: true;\n\t}>({\n\t\tplugins: [DrizzlePlugin],\n\t\tdrizzle: {\n\t\t\tclient: db,\n\t\t},\n\t\tdefaultFieldNullability: false,\n\t\tdefaultInputFieldRequiredness: true,\n\t});\n\n\tnativeBuilder.queryType({});\n\tnativeBuilder.mutationType({});\n\n\treturn {\n\t\t/**\n * The ability builder. Use it to declare whats allowed for each entity in your DB.\n * \n * @example\n * \n * ```ts\n * // users can edit themselves\n abilityBuilder.users\n .allow([\"read\", \"update\", \"delete\"])\n .when(({ userId }) => ({ where: eq(schema.users.id, userId) }));\n \n // everyone can read posts\n abilityBuilder.posts.allow(\"read\");\n * \n * ```\n */\n\t\tabilityBuilder,\n\t\t/**\n\t\t * The pothos schema builder. See https://pothos-graphql.dev/docs/plugins/drizzle\n\t\t */\n\t\tschemaBuilder: nativeBuilder,\n\t\t/**\n * The native yoga instance. Can be used to run an actual HTTP server.\n * \n * @example\n * \n * ```ts\n import { createServer } from \"node:http\";\n * const server = createServer(yoga());\n server.listen(3000, () => {\n console.log(\"Visit http://localhost:3000/graphql\");\n });\n * ```\n */\n\t\tyoga: () =>\n\t\t\tcreateYoga<RequestEvent>({\n\t\t\t\t...nativeServerOptions,\n\t\t\t\tschema: nativeBuilder.toSchema(),\n\t\t\t\tcontext: makeContext,\n\t\t\t}),\n\t};\n};\n","export class RumbleError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RumbleError\";\n\t}\n}\n","import { RumbleError } from \"./rumbleError\";\n\n/**\n * \n * Helper function to map a drizzle findFirst query result,\n * which may be optional, to a correct drizzle type.\n * \n * @throws RumbleError\n * \n * @example\n * \n * ```ts\n * schemaBuilder.queryFields((t) => {\n return {\n findFirstUser: t.drizzleField({\n type: UserRef,\n resolve: (query, root, args, ctx, info) => {\n return (\n db.query.users\n .findFirst({\n ...query,\n where: ctx.abilities.users.filter(\"read\").where,\n })\n // note that we need to manually raise an error if the value is not found\n .then(assertFindFirstExists)\n );\n },\n }),\n };\n });\n * ```\n */\nexport const assertFindFirstExists = <T>(value: T | undefined): T => {\n\tif (!value) throw new RumbleError(\"Value not found but required (findFirst)\");\n\treturn value;\n};\n\n/**\n * \n * Helper function to map a drizzle findFirst query result,\n * which may be optional, to a correct drizzle type.\n * \n * @throws RumbleError\n * \n * @example\n * \n * ```ts\n schemaBuilder.mutationFields((t) => {\n return {\n updateUsername: t.drizzleField({\n type: UserRef,\n args: {\n userId: t.arg.int({ required: true }),\n newName: t.arg.string({ required: true }),\n },\n resolve: (query, root, args, ctx, info) => {\n return db\n .update(schema.users)\n .set({\n name: args.newName,\n })\n .where(\n and(\n eq(schema.users.id, args.userId),\n ctx.abilities.users.filter(\"update\").where\n )\n )\n .returning({ id: schema.users.id, name: schema.users.name })\n // note that we need to manually raise an error if the value is not found\n .then(assertFirstEntryExists);\n },\n }),\n };\n });\n * ```\n */\nexport const assertFirstEntryExists = <T>(value: T[]): T => {\n\tconst v = value.at(0);\n\tif (!v) throw new RumbleError(\"Value not found but required (firstEntry)\");\n\treturn v;\n};\n"]}
|
package/index.d.cts
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
import * as graphql_yoga from 'graphql-yoga';
|
2
|
+
import { YogaServerOptions } from 'graphql-yoga';
|
3
|
+
import * as drizzle_orm from 'drizzle-orm';
|
4
|
+
import { DrizzleClient } from '@pothos/plugin-drizzle';
|
5
|
+
|
6
|
+
type QueryConditionObject = {
|
7
|
+
where: any;
|
8
|
+
columns: any;
|
9
|
+
limit: any;
|
10
|
+
};
|
11
|
+
type GenericDrizzleDbTypeConstraints = {
|
12
|
+
query: {
|
13
|
+
[key: string]: {
|
14
|
+
findMany: (P: QueryConditionObject) => any;
|
15
|
+
};
|
16
|
+
};
|
17
|
+
} & DrizzleClient;
|
18
|
+
|
19
|
+
declare const rumble: <UserContext extends Record<string, any>, DB extends GenericDrizzleDbTypeConstraints, RequestEvent extends Record<string, any>>({ db, nativeServerOptions, context: makeUserContext, }: {
|
20
|
+
/**
|
21
|
+
* Your drizzle database instance
|
22
|
+
*/
|
23
|
+
db: DB;
|
24
|
+
/**
|
25
|
+
* Optional options for the native GraphQL Yoga server
|
26
|
+
*/
|
27
|
+
nativeServerOptions?: Omit<YogaServerOptions<RequestEvent, any>, "schema" | "context"> | undefined;
|
28
|
+
/**
|
29
|
+
* A function for providing context for each request based on the incoming HTTP Request.
|
30
|
+
* The type of the parameter equals the HTTPRequest type of your chosen server.
|
31
|
+
*/
|
32
|
+
context?: ((event: RequestEvent) => Promise<UserContext> | UserContext) | undefined;
|
33
|
+
}) => Promise<{
|
34
|
+
/**
|
35
|
+
* The ability builder. Use it to declare whats allowed for each entity in your DB.
|
36
|
+
*
|
37
|
+
* @example
|
38
|
+
*
|
39
|
+
* ```ts
|
40
|
+
* // users can edit themselves
|
41
|
+
abilityBuilder.users
|
42
|
+
.allow(["read", "update", "delete"])
|
43
|
+
.when(({ userId }) => ({ where: eq(schema.users.id, userId) }));
|
44
|
+
|
45
|
+
// everyone can read posts
|
46
|
+
abilityBuilder.posts.allow("read");
|
47
|
+
*
|
48
|
+
* ```
|
49
|
+
*/
|
50
|
+
abilityBuilder: (keyof DB["query"] extends infer T extends keyof DB["query"] ? { [key in T]: {
|
51
|
+
allow: (action: "create" | "read" | "update" | "delete" | ("create" | "read" | "update" | "delete")[]) => {
|
52
|
+
when: (condition: Parameters<DB["query"][keyof DB["query"]]["findMany"]>[0] | ((context: UserContext) => Parameters<DB["query"][keyof DB["query"]]["findMany"]>[0])) => void;
|
53
|
+
};
|
54
|
+
}; } : never) & {
|
55
|
+
registeredConditions: keyof DB["query"] extends infer T_1 extends keyof DB["query"] ? { [key_1 in T_1]: {
|
56
|
+
[x: string]: (QueryConditionObject | ((context: UserContext) => QueryConditionObject))[];
|
57
|
+
}; } : never;
|
58
|
+
buildWithUserContext: (userContext: UserContext) => keyof DB["query"] extends infer T_2 extends keyof DB["query"] ? { [key_2 in T_2]: {
|
59
|
+
filter: (action: "create" | "read" | "update" | "delete") => {
|
60
|
+
where: drizzle_orm.SQL<unknown> | undefined;
|
61
|
+
columns: Record<string, any> | undefined;
|
62
|
+
limit: any;
|
63
|
+
};
|
64
|
+
}; } : never;
|
65
|
+
};
|
66
|
+
/**
|
67
|
+
* The pothos schema builder. See https://pothos-graphql.dev/docs/plugins/drizzle
|
68
|
+
*/
|
69
|
+
schemaBuilder: PothosSchemaTypes.SchemaBuilder<PothosSchemaTypes.ExtendDefaultTypes<{
|
70
|
+
Context: Awaited<ReturnType<(req: RequestEvent) => Promise<UserContext & {
|
71
|
+
abilities: keyof DB["query"] extends infer T_2 extends keyof DB["query"] ? { [key_2 in T_2]: {
|
72
|
+
filter: (action: "create" | "read" | "update" | "delete") => {
|
73
|
+
where: drizzle_orm.SQL<unknown> | undefined;
|
74
|
+
columns: Record<string, any> | undefined;
|
75
|
+
limit: any;
|
76
|
+
};
|
77
|
+
}; } : never;
|
78
|
+
}>>>;
|
79
|
+
DrizzleSchema: DB["_"]["fullSchema"];
|
80
|
+
DefaultFieldNullability: false;
|
81
|
+
DefaultArgumentNullability: false;
|
82
|
+
DefaultInputFieldRequiredness: true;
|
83
|
+
}>>;
|
84
|
+
/**
|
85
|
+
* The native yoga instance. Can be used to run an actual HTTP server.
|
86
|
+
*
|
87
|
+
* @example
|
88
|
+
*
|
89
|
+
* ```ts
|
90
|
+
import { createServer } from "node:http";
|
91
|
+
* const server = createServer(yoga());
|
92
|
+
server.listen(3000, () => {
|
93
|
+
console.log("Visit http://localhost:3000/graphql");
|
94
|
+
});
|
95
|
+
* ```
|
96
|
+
*/
|
97
|
+
yoga: () => graphql_yoga.YogaServerInstance<RequestEvent, {}>;
|
98
|
+
}>;
|
99
|
+
|
100
|
+
/**
|
101
|
+
*
|
102
|
+
* Helper function to map a drizzle findFirst query result,
|
103
|
+
* which may be optional, to a correct drizzle type.
|
104
|
+
*
|
105
|
+
* @throws RumbleError
|
106
|
+
*
|
107
|
+
* @example
|
108
|
+
*
|
109
|
+
* ```ts
|
110
|
+
* schemaBuilder.queryFields((t) => {
|
111
|
+
return {
|
112
|
+
findFirstUser: t.drizzleField({
|
113
|
+
type: UserRef,
|
114
|
+
resolve: (query, root, args, ctx, info) => {
|
115
|
+
return (
|
116
|
+
db.query.users
|
117
|
+
.findFirst({
|
118
|
+
...query,
|
119
|
+
where: ctx.abilities.users.filter("read").where,
|
120
|
+
})
|
121
|
+
// note that we need to manually raise an error if the value is not found
|
122
|
+
.then(assertFindFirstExists)
|
123
|
+
);
|
124
|
+
},
|
125
|
+
}),
|
126
|
+
};
|
127
|
+
});
|
128
|
+
* ```
|
129
|
+
*/
|
130
|
+
declare const assertFindFirstExists: <T>(value: T | undefined) => T;
|
131
|
+
/**
|
132
|
+
*
|
133
|
+
* Helper function to map a drizzle findFirst query result,
|
134
|
+
* which may be optional, to a correct drizzle type.
|
135
|
+
*
|
136
|
+
* @throws RumbleError
|
137
|
+
*
|
138
|
+
* @example
|
139
|
+
*
|
140
|
+
* ```ts
|
141
|
+
schemaBuilder.mutationFields((t) => {
|
142
|
+
return {
|
143
|
+
updateUsername: t.drizzleField({
|
144
|
+
type: UserRef,
|
145
|
+
args: {
|
146
|
+
userId: t.arg.int({ required: true }),
|
147
|
+
newName: t.arg.string({ required: true }),
|
148
|
+
},
|
149
|
+
resolve: (query, root, args, ctx, info) => {
|
150
|
+
return db
|
151
|
+
.update(schema.users)
|
152
|
+
.set({
|
153
|
+
name: args.newName,
|
154
|
+
})
|
155
|
+
.where(
|
156
|
+
and(
|
157
|
+
eq(schema.users.id, args.userId),
|
158
|
+
ctx.abilities.users.filter("update").where
|
159
|
+
)
|
160
|
+
)
|
161
|
+
.returning({ id: schema.users.id, name: schema.users.name })
|
162
|
+
// note that we need to manually raise an error if the value is not found
|
163
|
+
.then(assertFirstEntryExists);
|
164
|
+
},
|
165
|
+
}),
|
166
|
+
};
|
167
|
+
});
|
168
|
+
* ```
|
169
|
+
*/
|
170
|
+
declare const assertFirstEntryExists: <T>(value: T[]) => T;
|
171
|
+
|
172
|
+
declare class RumbleError extends Error {
|
173
|
+
constructor(message: string);
|
174
|
+
}
|
175
|
+
|
176
|
+
export { RumbleError, assertFindFirstExists, assertFirstEntryExists, rumble };
|
package/index.d.ts
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
import * as graphql_yoga from 'graphql-yoga';
|
2
|
+
import { YogaServerOptions } from 'graphql-yoga';
|
3
|
+
import * as drizzle_orm from 'drizzle-orm';
|
4
|
+
import { DrizzleClient } from '@pothos/plugin-drizzle';
|
5
|
+
|
6
|
+
type QueryConditionObject = {
|
7
|
+
where: any;
|
8
|
+
columns: any;
|
9
|
+
limit: any;
|
10
|
+
};
|
11
|
+
type GenericDrizzleDbTypeConstraints = {
|
12
|
+
query: {
|
13
|
+
[key: string]: {
|
14
|
+
findMany: (P: QueryConditionObject) => any;
|
15
|
+
};
|
16
|
+
};
|
17
|
+
} & DrizzleClient;
|
18
|
+
|
19
|
+
declare const rumble: <UserContext extends Record<string, any>, DB extends GenericDrizzleDbTypeConstraints, RequestEvent extends Record<string, any>>({ db, nativeServerOptions, context: makeUserContext, }: {
|
20
|
+
/**
|
21
|
+
* Your drizzle database instance
|
22
|
+
*/
|
23
|
+
db: DB;
|
24
|
+
/**
|
25
|
+
* Optional options for the native GraphQL Yoga server
|
26
|
+
*/
|
27
|
+
nativeServerOptions?: Omit<YogaServerOptions<RequestEvent, any>, "schema" | "context"> | undefined;
|
28
|
+
/**
|
29
|
+
* A function for providing context for each request based on the incoming HTTP Request.
|
30
|
+
* The type of the parameter equals the HTTPRequest type of your chosen server.
|
31
|
+
*/
|
32
|
+
context?: ((event: RequestEvent) => Promise<UserContext> | UserContext) | undefined;
|
33
|
+
}) => Promise<{
|
34
|
+
/**
|
35
|
+
* The ability builder. Use it to declare whats allowed for each entity in your DB.
|
36
|
+
*
|
37
|
+
* @example
|
38
|
+
*
|
39
|
+
* ```ts
|
40
|
+
* // users can edit themselves
|
41
|
+
abilityBuilder.users
|
42
|
+
.allow(["read", "update", "delete"])
|
43
|
+
.when(({ userId }) => ({ where: eq(schema.users.id, userId) }));
|
44
|
+
|
45
|
+
// everyone can read posts
|
46
|
+
abilityBuilder.posts.allow("read");
|
47
|
+
*
|
48
|
+
* ```
|
49
|
+
*/
|
50
|
+
abilityBuilder: (keyof DB["query"] extends infer T extends keyof DB["query"] ? { [key in T]: {
|
51
|
+
allow: (action: "create" | "read" | "update" | "delete" | ("create" | "read" | "update" | "delete")[]) => {
|
52
|
+
when: (condition: Parameters<DB["query"][keyof DB["query"]]["findMany"]>[0] | ((context: UserContext) => Parameters<DB["query"][keyof DB["query"]]["findMany"]>[0])) => void;
|
53
|
+
};
|
54
|
+
}; } : never) & {
|
55
|
+
registeredConditions: keyof DB["query"] extends infer T_1 extends keyof DB["query"] ? { [key_1 in T_1]: {
|
56
|
+
[x: string]: (QueryConditionObject | ((context: UserContext) => QueryConditionObject))[];
|
57
|
+
}; } : never;
|
58
|
+
buildWithUserContext: (userContext: UserContext) => keyof DB["query"] extends infer T_2 extends keyof DB["query"] ? { [key_2 in T_2]: {
|
59
|
+
filter: (action: "create" | "read" | "update" | "delete") => {
|
60
|
+
where: drizzle_orm.SQL<unknown> | undefined;
|
61
|
+
columns: Record<string, any> | undefined;
|
62
|
+
limit: any;
|
63
|
+
};
|
64
|
+
}; } : never;
|
65
|
+
};
|
66
|
+
/**
|
67
|
+
* The pothos schema builder. See https://pothos-graphql.dev/docs/plugins/drizzle
|
68
|
+
*/
|
69
|
+
schemaBuilder: PothosSchemaTypes.SchemaBuilder<PothosSchemaTypes.ExtendDefaultTypes<{
|
70
|
+
Context: Awaited<ReturnType<(req: RequestEvent) => Promise<UserContext & {
|
71
|
+
abilities: keyof DB["query"] extends infer T_2 extends keyof DB["query"] ? { [key_2 in T_2]: {
|
72
|
+
filter: (action: "create" | "read" | "update" | "delete") => {
|
73
|
+
where: drizzle_orm.SQL<unknown> | undefined;
|
74
|
+
columns: Record<string, any> | undefined;
|
75
|
+
limit: any;
|
76
|
+
};
|
77
|
+
}; } : never;
|
78
|
+
}>>>;
|
79
|
+
DrizzleSchema: DB["_"]["fullSchema"];
|
80
|
+
DefaultFieldNullability: false;
|
81
|
+
DefaultArgumentNullability: false;
|
82
|
+
DefaultInputFieldRequiredness: true;
|
83
|
+
}>>;
|
84
|
+
/**
|
85
|
+
* The native yoga instance. Can be used to run an actual HTTP server.
|
86
|
+
*
|
87
|
+
* @example
|
88
|
+
*
|
89
|
+
* ```ts
|
90
|
+
import { createServer } from "node:http";
|
91
|
+
* const server = createServer(yoga());
|
92
|
+
server.listen(3000, () => {
|
93
|
+
console.log("Visit http://localhost:3000/graphql");
|
94
|
+
});
|
95
|
+
* ```
|
96
|
+
*/
|
97
|
+
yoga: () => graphql_yoga.YogaServerInstance<RequestEvent, {}>;
|
98
|
+
}>;
|
99
|
+
|
100
|
+
/**
|
101
|
+
*
|
102
|
+
* Helper function to map a drizzle findFirst query result,
|
103
|
+
* which may be optional, to a correct drizzle type.
|
104
|
+
*
|
105
|
+
* @throws RumbleError
|
106
|
+
*
|
107
|
+
* @example
|
108
|
+
*
|
109
|
+
* ```ts
|
110
|
+
* schemaBuilder.queryFields((t) => {
|
111
|
+
return {
|
112
|
+
findFirstUser: t.drizzleField({
|
113
|
+
type: UserRef,
|
114
|
+
resolve: (query, root, args, ctx, info) => {
|
115
|
+
return (
|
116
|
+
db.query.users
|
117
|
+
.findFirst({
|
118
|
+
...query,
|
119
|
+
where: ctx.abilities.users.filter("read").where,
|
120
|
+
})
|
121
|
+
// note that we need to manually raise an error if the value is not found
|
122
|
+
.then(assertFindFirstExists)
|
123
|
+
);
|
124
|
+
},
|
125
|
+
}),
|
126
|
+
};
|
127
|
+
});
|
128
|
+
* ```
|
129
|
+
*/
|
130
|
+
declare const assertFindFirstExists: <T>(value: T | undefined) => T;
|
131
|
+
/**
|
132
|
+
*
|
133
|
+
* Helper function to map a drizzle findFirst query result,
|
134
|
+
* which may be optional, to a correct drizzle type.
|
135
|
+
*
|
136
|
+
* @throws RumbleError
|
137
|
+
*
|
138
|
+
* @example
|
139
|
+
*
|
140
|
+
* ```ts
|
141
|
+
schemaBuilder.mutationFields((t) => {
|
142
|
+
return {
|
143
|
+
updateUsername: t.drizzleField({
|
144
|
+
type: UserRef,
|
145
|
+
args: {
|
146
|
+
userId: t.arg.int({ required: true }),
|
147
|
+
newName: t.arg.string({ required: true }),
|
148
|
+
},
|
149
|
+
resolve: (query, root, args, ctx, info) => {
|
150
|
+
return db
|
151
|
+
.update(schema.users)
|
152
|
+
.set({
|
153
|
+
name: args.newName,
|
154
|
+
})
|
155
|
+
.where(
|
156
|
+
and(
|
157
|
+
eq(schema.users.id, args.userId),
|
158
|
+
ctx.abilities.users.filter("update").where
|
159
|
+
)
|
160
|
+
)
|
161
|
+
.returning({ id: schema.users.id, name: schema.users.name })
|
162
|
+
// note that we need to manually raise an error if the value is not found
|
163
|
+
.then(assertFirstEntryExists);
|
164
|
+
},
|
165
|
+
}),
|
166
|
+
};
|
167
|
+
});
|
168
|
+
* ```
|
169
|
+
*/
|
170
|
+
declare const assertFirstEntryExists: <T>(value: T[]) => T;
|
171
|
+
|
172
|
+
declare class RumbleError extends Error {
|
173
|
+
constructor(message: string);
|
174
|
+
}
|
175
|
+
|
176
|
+
export { RumbleError, assertFindFirstExists, assertFirstEntryExists, rumble };
|
package/index.js
ADDED
@@ -0,0 +1,2 @@
|
|
1
|
+
import P from'@pothos/core';import O from'@pothos/plugin-drizzle';import {createYoga}from'graphql-yoga';import {or}from'drizzle-orm';function h(e){return typeof e!="function"}function A(e){return typeof e=="function"&&e.constructor.name!=="AsyncFunction"}var C=({db:e,actions:s=["create","read","update","delete"]})=>{let u={},c={},p=n=>({allow:r=>{let i=c[n];i||(i={},c[n]=i);let a=Array.isArray(r)?r:[r];for(let d of a){let o=i[d];o||(o=[],i[d]=o);}return {when:d=>{for(let o of a)i[o].push(d);}}}});for(let n of Object.keys(e.query))u[n]=p(n);return {...u,registeredConditions:c,buildWithUserContext:n=>{let r={},i=a=>({filter:d=>{let o=c[a];if(!o)throw "TODO (No allowed entry found for this condition) #1";let f=o[d];if(!f)throw "TODO (No allowed entry found for this condition) #2";let b=f.filter(h),B=f.filter(A).map(t=>t(n)),D=[...b,...B],m;for(let t of D)t.limit&&(m===void 0||t.limit>m)&&(m=t.limit);let l;for(let t of D)t.columns&&(l===void 0?l=t.columns:l={...l,...t.columns});let x=D.filter(t=>t.where).map(t=>t.where);return {where:x.length>0?or(...x):void 0,columns:l,limit:m}}});for(let a of Object.keys(e.query))r[a]=i(a);return r}}};var U=async({db:e,nativeServerOptions:s,context:u})=>{let c=C({db:e}),p=async r=>{let i=u?await u(r):{};return {...i,abilities:c.buildWithUserContext(i)}},n=new P({plugins:[O],drizzle:{client:e},defaultFieldNullability:!1,defaultInputFieldRequiredness:!0});return n.queryType({}),n.mutationType({}),{abilityBuilder:c,schemaBuilder:n,yoga:()=>createYoga({...s,schema:n.toSchema(),context:p})}};var y=class extends Error{constructor(s){super(s),this.name="RumbleError";}};var g=e=>{if(!e)throw new y("Value not found but required (findFirst)");return e},z=e=>{let s=e.at(0);if(!s)throw new y("Value not found but required (firstEntry)");return s};export{y as RumbleError,g as assertFindFirstExists,z as assertFirstEntryExists,U as rumble};//# sourceMappingURL=index.js.map
|
2
|
+
//# sourceMappingURL=index.js.map
|
package/index.js.map
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../lib/abilities/builder.ts","../lib/gql/builder.ts","../lib/helpers/rumbleError.ts","../lib/helpers/helper.ts"],"names":["isSimpleCondition","condition","isSyncFunctionCondition","createAbilityBuilder","db","actions","builder","registeredConditions","createEntityObject","entityKey","action","conditionsPerEntity","conditionsPerEntityAndAction","userContext","simpleConditions","syncFunctionConditions","allConditionObjects","highestLimit","conditionObject","combinedAllowedColumns","accumulatedWhereConditions","o","or","rumble","nativeServerOptions","makeUserContext","abilityBuilder","makeContext","req","nativeBuilder","SchemaBuilder","DrizzlePlugin","createYoga","RumbleError","message","assertFindFirstExists","value","assertFirstEntryExists","v"],"mappings":"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","file":"index.js","sourcesContent":["import { or } from \"drizzle-orm\";\nimport type {\n\tGenericDrizzleDbTypeConstraints,\n\tQueryConditionObject,\n} from \"../types/genericDrizzleDbType\";\n\nexport type AbilityBuilder = ReturnType<typeof createAbilityBuilder>;\n\ntype Condition<DBParameters, UserContext> =\n\t| SimpleCondition<DBParameters>\n\t| SyncFunctionCondition<DBParameters, UserContext>;\n// | AsyncFunctionCondition<DBParameters, UserContext>;\n\ntype SimpleCondition<DBParameters> = DBParameters;\ntype SyncFunctionCondition<DBParameters, UserContext> = (\n\tcontext: UserContext,\n) => DBParameters;\n// type AsyncFunctionCondition<DBParameters, UserContext> = (\n// \tcontext: UserContext,\n// ) => Promise<DBParameters>;\n\n// type guards for the condition types\nfunction isSimpleCondition<DBParameters, UserContext>(\n\tcondition: Condition<DBParameters, UserContext>,\n): condition is SimpleCondition<DBParameters> {\n\treturn typeof condition !== \"function\";\n}\n\nfunction isSyncFunctionCondition<DBParameters, UserContext>(\n\tcondition: Condition<DBParameters, UserContext>,\n): condition is SyncFunctionCondition<DBParameters, UserContext> {\n\treturn (\n\t\ttypeof condition === \"function\" &&\n\t\tcondition.constructor.name !== \"AsyncFunction\"\n\t);\n}\n\n// function isAsyncFunctionCondition<DBParameters, UserContext>(\n// \tcondition: Condition<DBParameters, UserContext>,\n// ): condition is AsyncFunctionCondition<DBParameters, UserContext> {\n// \treturn (\n// \t\ttypeof condition === \"function\" &&\n// \t\tcondition.constructor.name === \"AsyncFunction\"\n// \t);\n// }\n\nexport const createAbilityBuilder = <\n\tUserContext extends Record<string, any>,\n\tDB extends GenericDrizzleDbTypeConstraints,\n\tAction extends string = \"create\" | \"read\" | \"update\" | \"delete\",\n>({\n\tdb,\n\tactions = [\"create\", \"read\", \"update\", \"delete\"] as Action[],\n}: {\n\tdb: DB;\n\tactions?: Action[];\n}) => {\n\ttype DBEntityKey = keyof DB[\"query\"];\n\n\tconst builder: {\n\t\t[key in DBEntityKey]: ReturnType<typeof createEntityObject>;\n\t} = {} as any;\n\n\tconst registeredConditions: {\n\t\t[key in DBEntityKey]: {\n\t\t\t[key in Action[number]]: (\n\t\t\t\t| QueryConditionObject\n\t\t\t\t| ((context: UserContext) => QueryConditionObject)\n\t\t\t)[];\n\t\t\t// | ((context: UserContext) => Promise<QueryConditionObject>)\n\t\t};\n\t} = {} as any;\n\n\tconst createEntityObject = (entityKey: DBEntityKey) => ({\n\t\tallow: (action: Action | Action[]) => {\n\t\t\ttype DBParameters = Parameters<DB[\"query\"][DBEntityKey][\"findMany\"]>[0];\n\n\t\t\tlet conditionsPerEntity = registeredConditions[entityKey];\n\t\t\tif (!conditionsPerEntity) {\n\t\t\t\tconditionsPerEntity = {} as any;\n\t\t\t\tregisteredConditions[entityKey] = conditionsPerEntity;\n\t\t\t}\n\n\t\t\tconst actions = Array.isArray(action) ? action : [action];\n\t\t\tfor (const action of actions) {\n\t\t\t\tlet conditionsPerEntityAndAction = conditionsPerEntity[action];\n\t\t\t\tif (!conditionsPerEntityAndAction) {\n\t\t\t\t\tconditionsPerEntityAndAction = [];\n\t\t\t\t\tconditionsPerEntity[action] = conditionsPerEntityAndAction;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\twhen: (condition: Condition<DBParameters, UserContext>) => {\n\t\t\t\t\tfor (const action of actions) {\n\t\t\t\t\t\tconst conditionsPerEntityAndAction = conditionsPerEntity[action];\n\t\t\t\t\t\tconditionsPerEntityAndAction.push(condition);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t});\n\n\tfor (const entityKey of Object.keys(db.query) as DBEntityKey[]) {\n\t\tbuilder[entityKey] = createEntityObject(entityKey);\n\t}\n\treturn {\n\t\t...builder,\n\t\tregisteredConditions,\n\t\tbuildWithUserContext: (userContext: UserContext) => {\n\t\t\tconst builder: {\n\t\t\t\t[key in DBEntityKey]: ReturnType<typeof createEntityObject>;\n\t\t\t} = {} as any;\n\n\t\t\tconst createEntityObject = (entityKey: DBEntityKey) => ({\n\t\t\t\tfilter: (action: Action) => {\n\t\t\t\t\tconst conditionsPerEntity = registeredConditions[entityKey];\n\t\t\t\t\tif (!conditionsPerEntity) {\n\t\t\t\t\t\tthrow \"TODO (No allowed entry found for this condition) #1\";\n\t\t\t\t\t}\n\n\t\t\t\t\tconst conditionsPerEntityAndAction = conditionsPerEntity[action];\n\t\t\t\t\tif (!conditionsPerEntityAndAction) {\n\t\t\t\t\t\tthrow \"TODO (No allowed entry found for this condition) #2\";\n\t\t\t\t\t}\n\n\t\t\t\t\tconst simpleConditions =\n\t\t\t\t\t\tconditionsPerEntityAndAction.filter(isSimpleCondition);\n\n\t\t\t\t\tconst syncFunctionConditions = conditionsPerEntityAndAction\n\t\t\t\t\t\t.filter(isSyncFunctionCondition)\n\t\t\t\t\t\t.map((condition) => condition(userContext));\n\n\t\t\t\t\t// const asyncFunctionConditions = await Promise.all(\n\t\t\t\t\t// \tconditionsPerEntityAndAction\n\t\t\t\t\t// \t\t.filter(isAsyncFunctionCondition)\n\t\t\t\t\t// \t\t.map((condition) => condition(userContext)),\n\t\t\t\t\t// );\n\n\t\t\t\t\tconst allConditionObjects = [\n\t\t\t\t\t\t...simpleConditions,\n\t\t\t\t\t\t...syncFunctionConditions,\n\t\t\t\t\t\t// ...asyncFunctionConditions,\n\t\t\t\t\t];\n\n\t\t\t\t\tlet highestLimit = undefined;\n\t\t\t\t\tfor (const conditionObject of allConditionObjects) {\n\t\t\t\t\t\tif (conditionObject.limit) {\n\t\t\t\t\t\t\tif (\n\t\t\t\t\t\t\t\thighestLimit === undefined ||\n\t\t\t\t\t\t\t\tconditionObject.limit > highestLimit\n\t\t\t\t\t\t\t) {\n\t\t\t\t\t\t\t\thighestLimit = conditionObject.limit;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tlet combinedAllowedColumns: Record<string, any> | undefined =\n\t\t\t\t\t\tundefined;\n\t\t\t\t\tfor (const conditionObject of allConditionObjects) {\n\t\t\t\t\t\tif (conditionObject.columns) {\n\t\t\t\t\t\t\tif (combinedAllowedColumns === undefined) {\n\t\t\t\t\t\t\t\tcombinedAllowedColumns = conditionObject.columns;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tcombinedAllowedColumns = {\n\t\t\t\t\t\t\t\t\t...combinedAllowedColumns,\n\t\t\t\t\t\t\t\t\t...conditionObject.columns,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst accumulatedWhereConditions = allConditionObjects\n\t\t\t\t\t\t.filter((o) => o.where)\n\t\t\t\t\t\t.map((o) => o.where);\n\n\t\t\t\t\tconst combinedWhere =\n\t\t\t\t\t\taccumulatedWhereConditions.length > 0\n\t\t\t\t\t\t\t? or(...accumulatedWhereConditions)\n\t\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\twhere: combinedWhere,\n\t\t\t\t\t\tcolumns: combinedAllowedColumns,\n\t\t\t\t\t\tlimit: highestLimit,\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t});\n\n\t\t\tfor (const entityKey of Object.keys(db.query) as DBEntityKey[]) {\n\t\t\t\tbuilder[entityKey] = createEntityObject(entityKey);\n\t\t\t}\n\n\t\t\treturn builder;\n\t\t},\n\t};\n};\n","import SchemaBuilder from \"@pothos/core\";\nimport DrizzlePlugin from \"@pothos/plugin-drizzle\";\nimport { type YogaServerOptions, createYoga } from \"graphql-yoga\";\nimport { createAbilityBuilder } from \"../abilities/builder\";\nimport type { GenericDrizzleDbTypeConstraints } from \"../types/genericDrizzleDbType\";\n\nexport const rumble = async <\n\tUserContext extends Record<string, any>,\n\tDB extends GenericDrizzleDbTypeConstraints,\n\tRequestEvent extends Record<string, any>,\n>({\n\tdb,\n\tnativeServerOptions,\n\tcontext: makeUserContext,\n}: {\n\t/**\n\t * Your drizzle database instance\n\t */\n\tdb: DB;\n\t/**\n\t * Optional options for the native GraphQL Yoga server\n\t */\n\tnativeServerOptions?:\n\t\t| Omit<YogaServerOptions<RequestEvent, any>, \"schema\" | \"context\">\n\t\t| undefined;\n\t/**\n\t * A function for providing context for each request based on the incoming HTTP Request.\n\t * The type of the parameter equals the HTTPRequest type of your chosen server.\n\t */\n\tcontext?:\n\t\t| ((event: RequestEvent) => Promise<UserContext> | UserContext)\n\t\t| undefined;\n}) => {\n\tconst abilityBuilder = createAbilityBuilder<UserContext, DB>({\n\t\tdb,\n\t});\n\n\tconst makeContext = async (req: RequestEvent) => {\n\t\tconst userContext = makeUserContext\n\t\t\t? await makeUserContext(req)\n\t\t\t: ({} as UserContext);\n\t\treturn {\n\t\t\t...userContext,\n\t\t\tabilities: abilityBuilder.buildWithUserContext(userContext),\n\t\t};\n\t};\n\n\tconst nativeBuilder = new SchemaBuilder<{\n\t\tContext: Awaited<ReturnType<typeof makeContext>>;\n\t\t// Scalars: Scalars<Prisma.Decimal, Prisma.InputJsonValue | null, Prisma.InputJsonValue> & {\n\t\t// \tFile: {\n\t\t// \t\tInput: File;\n\t\t// \t\tOutput: never;\n\t\t// \t};\n\t\t// \tJSONObject: {\n\t\t// \t\tInput: any;\n\t\t// \t\tOutput: any;\n\t\t// \t};\n\t\t// };\n\t\tDrizzleSchema: DB[\"_\"][\"fullSchema\"];\n\t\tDefaultFieldNullability: false;\n\t\tDefaultArgumentNullability: false;\n\t\tDefaultInputFieldRequiredness: true;\n\t}>({\n\t\tplugins: [DrizzlePlugin],\n\t\tdrizzle: {\n\t\t\tclient: db,\n\t\t},\n\t\tdefaultFieldNullability: false,\n\t\tdefaultInputFieldRequiredness: true,\n\t});\n\n\tnativeBuilder.queryType({});\n\tnativeBuilder.mutationType({});\n\n\treturn {\n\t\t/**\n * The ability builder. Use it to declare whats allowed for each entity in your DB.\n * \n * @example\n * \n * ```ts\n * // users can edit themselves\n abilityBuilder.users\n .allow([\"read\", \"update\", \"delete\"])\n .when(({ userId }) => ({ where: eq(schema.users.id, userId) }));\n \n // everyone can read posts\n abilityBuilder.posts.allow(\"read\");\n * \n * ```\n */\n\t\tabilityBuilder,\n\t\t/**\n\t\t * The pothos schema builder. See https://pothos-graphql.dev/docs/plugins/drizzle\n\t\t */\n\t\tschemaBuilder: nativeBuilder,\n\t\t/**\n * The native yoga instance. Can be used to run an actual HTTP server.\n * \n * @example\n * \n * ```ts\n import { createServer } from \"node:http\";\n * const server = createServer(yoga());\n server.listen(3000, () => {\n console.log(\"Visit http://localhost:3000/graphql\");\n });\n * ```\n */\n\t\tyoga: () =>\n\t\t\tcreateYoga<RequestEvent>({\n\t\t\t\t...nativeServerOptions,\n\t\t\t\tschema: nativeBuilder.toSchema(),\n\t\t\t\tcontext: makeContext,\n\t\t\t}),\n\t};\n};\n","export class RumbleError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"RumbleError\";\n\t}\n}\n","import { RumbleError } from \"./rumbleError\";\n\n/**\n * \n * Helper function to map a drizzle findFirst query result,\n * which may be optional, to a correct drizzle type.\n * \n * @throws RumbleError\n * \n * @example\n * \n * ```ts\n * schemaBuilder.queryFields((t) => {\n return {\n findFirstUser: t.drizzleField({\n type: UserRef,\n resolve: (query, root, args, ctx, info) => {\n return (\n db.query.users\n .findFirst({\n ...query,\n where: ctx.abilities.users.filter(\"read\").where,\n })\n // note that we need to manually raise an error if the value is not found\n .then(assertFindFirstExists)\n );\n },\n }),\n };\n });\n * ```\n */\nexport const assertFindFirstExists = <T>(value: T | undefined): T => {\n\tif (!value) throw new RumbleError(\"Value not found but required (findFirst)\");\n\treturn value;\n};\n\n/**\n * \n * Helper function to map a drizzle findFirst query result,\n * which may be optional, to a correct drizzle type.\n * \n * @throws RumbleError\n * \n * @example\n * \n * ```ts\n schemaBuilder.mutationFields((t) => {\n return {\n updateUsername: t.drizzleField({\n type: UserRef,\n args: {\n userId: t.arg.int({ required: true }),\n newName: t.arg.string({ required: true }),\n },\n resolve: (query, root, args, ctx, info) => {\n return db\n .update(schema.users)\n .set({\n name: args.newName,\n })\n .where(\n and(\n eq(schema.users.id, args.userId),\n ctx.abilities.users.filter(\"update\").where\n )\n )\n .returning({ id: schema.users.id, name: schema.users.name })\n // note that we need to manually raise an error if the value is not found\n .then(assertFirstEntryExists);\n },\n }),\n };\n });\n * ```\n */\nexport const assertFirstEntryExists = <T>(value: T[]): T => {\n\tconst v = value.at(0);\n\tif (!v) throw new RumbleError(\"Value not found but required (firstEntry)\");\n\treturn v;\n};\n"]}
|
package/package.json
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
{
|
2
|
+
"name": "@m1212e/rumble",
|
3
|
+
"module": "index.ts",
|
4
|
+
"type": "module",
|
5
|
+
"peerDependencies": {
|
6
|
+
"typescript": "^5.7.2",
|
7
|
+
"drizzle-orm": "^0.38.3"
|
8
|
+
},
|
9
|
+
"dependencies": {
|
10
|
+
"@pothos/plugin-drizzle": "^0.5.3",
|
11
|
+
"graphql-yoga": "^5.10.8",
|
12
|
+
"@pothos/core": "^4.3.0"
|
13
|
+
},
|
14
|
+
"version": "0.0.1",
|
15
|
+
"exports": {
|
16
|
+
"./package.json": "./package.json",
|
17
|
+
".": {
|
18
|
+
"require": "./index.cjs",
|
19
|
+
"import": "./index.js",
|
20
|
+
"node": "./index.cjs",
|
21
|
+
"default": "./index.cjs"
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|