diff --git a/apps/cjs-test/CHANGELOG.md b/apps/cjs-test/CHANGELOG.md index 960e57cf7..88f8d13a3 100644 --- a/apps/cjs-test/CHANGELOG.md +++ b/apps/cjs-test/CHANGELOG.md @@ -1,5 +1,13 @@ # cjs-test +## 0.2.5 + +### Patch Changes + +- CRUD schemas +- Updated dependencies + - @stackframe/stack@2.4.5 + ## 0.2.4 ### Patch Changes diff --git a/apps/cjs-test/package.json b/apps/cjs-test/package.json index 05cb23a9e..1edd6654d 100644 --- a/apps/cjs-test/package.json +++ b/apps/cjs-test/package.json @@ -1,6 +1,6 @@ { "name": "@stackframe/example-cjs-test", - "version": "0.2.4", + "version": "0.2.5", "private": true, "scripts": { "dev": "next dev --port 8106", diff --git a/apps/demo/CHANGELOG.md b/apps/demo/CHANGELOG.md index 0ae51d1cf..c75464152 100644 --- a/apps/demo/CHANGELOG.md +++ b/apps/demo/CHANGELOG.md @@ -1,5 +1,14 @@ # demo-app +## 1.1.5 + +### Patch Changes + +- CRUD schemas +- Updated dependencies + - @stackframe/stack-shared@2.4.3 + - @stackframe/stack@2.4.5 + ## 1.1.4 ### Patch Changes diff --git a/apps/demo/package.json b/apps/demo/package.json index c484eb4a8..aee6cac3b 100644 --- a/apps/demo/package.json +++ b/apps/demo/package.json @@ -1,6 +1,6 @@ { "name": "@stackframe/example-demo-app", - "version": "1.1.4", + "version": "1.1.5", "description": "", "private": true, "scripts": { diff --git a/apps/e2e/CHANGELOG.md b/apps/e2e/CHANGELOG.md index 5fef07525..8bd2db8ec 100644 --- a/apps/e2e/CHANGELOG.md +++ b/apps/e2e/CHANGELOG.md @@ -1,5 +1,11 @@ # e2e-tests +## 0.1.2 + +### Patch Changes + +- CRUD schemas + ## 0.1.1 ### Patch Changes diff --git a/apps/e2e/package.json b/apps/e2e/package.json index 742350be9..2a8b1926f 100644 --- a/apps/e2e/package.json +++ b/apps/e2e/package.json @@ -1,6 +1,6 @@ { "name": "@stackframe/e2e-tests", - "version": "0.1.1", + "version": "0.1.2", "private": true, "type": "module", "scripts": { diff --git a/apps/middleware/CHANGELOG.md b/apps/middleware/CHANGELOG.md index c35b0395d..9446a79b8 100644 --- a/apps/middleware/CHANGELOG.md +++ b/apps/middleware/CHANGELOG.md @@ -1,5 +1,12 @@ # middleware-demo +## 0.2.5 + +### Patch Changes + +- Updated dependencies + - @stackframe/stack@2.4.5 + ## 0.2.4 ### Patch Changes diff --git a/apps/middleware/package.json b/apps/middleware/package.json index 6b005337a..dda130608 100644 --- a/apps/middleware/package.json +++ b/apps/middleware/package.json @@ -1,6 +1,6 @@ { "name": "@stackframe/example-middleware-demo", - "version": "0.2.4", + "version": "0.2.5", "private": true, "scripts": { "dev": "next dev --port 8107", diff --git a/apps/partial-prerendering/CHANGELOG.md b/apps/partial-prerendering/CHANGELOG.md index 99f6438d4..7f3b85010 100644 --- a/apps/partial-prerendering/CHANGELOG.md +++ b/apps/partial-prerendering/CHANGELOG.md @@ -1,5 +1,13 @@ # partial-prerendering +## 0.2.5 + +### Patch Changes + +- CRUD schemas +- Updated dependencies + - @stackframe/stack@2.4.5 + ## 0.2.4 ### Patch Changes diff --git a/apps/partial-prerendering/package.json b/apps/partial-prerendering/package.json index 4689d0fc0..1364e206c 100644 --- a/apps/partial-prerendering/package.json +++ b/apps/partial-prerendering/package.json @@ -1,6 +1,6 @@ { "name": "@stackframe/example-partial-prerendering", - "version": "0.2.4", + "version": "0.2.5", "private": true, "scripts": { "dev": "next dev --port 8105", diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index c92f457bf..1fb3d612f 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,11 @@ # stack-docs +## 0.1.4 + +### Patch Changes + +- CRUD schemas + ## 0.1.3 ### Patch Changes diff --git a/docs/package.json b/docs/package.json index 85faa9195..48f5ea1ea 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,6 +1,6 @@ { "name": "@stackframe/stack-docs", - "version": "0.1.3", + "version": "0.1.4", "private": true, "scripts": { "docusaurus": "dotenv -c -- docusaurus", diff --git a/packages/stack-sc/CHANGELOG.md b/packages/stack-sc/CHANGELOG.md index 11e4aef8e..dd701eb56 100644 --- a/packages/stack-sc/CHANGELOG.md +++ b/packages/stack-sc/CHANGELOG.md @@ -1,5 +1,11 @@ # @stackframe/stack-sc +## 1.5.2 + +### Patch Changes + +- CRUD schemas + ## 1.5.1 ### Patch Changes diff --git a/packages/stack-sc/package.json b/packages/stack-sc/package.json index d3956c95c..77e2e2162 100644 --- a/packages/stack-sc/package.json +++ b/packages/stack-sc/package.json @@ -1,6 +1,6 @@ { "name": "@stackframe/stack-sc", - "version": "1.5.1", + "version": "1.5.2", "exports": { "./force-server": { "types": "./dist/index.server.d.ts", diff --git a/packages/stack-server/CHANGELOG.md b/packages/stack-server/CHANGELOG.md index 50a5b835d..c91d9214e 100644 --- a/packages/stack-server/CHANGELOG.md +++ b/packages/stack-server/CHANGELOG.md @@ -1,5 +1,14 @@ # @stackframe/stack-server +## 0.4.5 + +### Patch Changes + +- CRUD schemas +- Updated dependencies + - @stackframe/stack-shared@2.4.3 + - @stackframe/stack@2.4.5 + ## 0.4.4 ### Patch Changes diff --git a/packages/stack-server/package.json b/packages/stack-server/package.json index 48eae4abd..3412bfe14 100644 --- a/packages/stack-server/package.json +++ b/packages/stack-server/package.json @@ -1,6 +1,6 @@ { "name": "@stackframe/stack-server", - "version": "0.4.4", + "version": "0.4.5", "private": true, "scripts": { "clean": "rimraf .next && rimraf node_modules", diff --git a/packages/stack-server/src/route-handlers/prisma-handler.tsx b/packages/stack-server/src/route-handlers/prisma-handler.tsx new file mode 100644 index 000000000..5a9c2b8b1 --- /dev/null +++ b/packages/stack-server/src/route-handlers/prisma-handler.tsx @@ -0,0 +1,162 @@ +import { CrudSchema, CrudTypeOf } from "@stackframe/stack-shared/dist/crud"; +import { CrudHandlers, createCrudHandlers } from "./crud-handler"; +import { SmartRequestAuth } from "./smart-request"; +import { Prisma } from "@prisma/client"; +import { GetResult } from "@prisma/client/runtime/library"; +import { StatusError, throwErr } from "@stackframe/stack-shared/dist/utils/errors"; +import { prismaClient } from "@/prisma-client"; + +type AllPrismaModelNames = Prisma.TypeMap["meta"]["modelProps"]; +type WhereUnique = Prisma.TypeMap["model"][Capitalize]["operations"]["findUniqueOrThrow"]["args"]["where"]; +type WhereMany = Prisma.TypeMap["model"][Capitalize]["operations"]["findMany"]["args"]["where"]; +type Where = WhereUnique & WhereMany; +type Include = Prisma.TypeMap["model"][Capitalize]["operations"]["findMany"]["args"]["include"]; +type BaseFields = Where & Partial>; +type PRead, I extends Include> = [T, W, I];//GetResult]["payload"], { where: W, include: I }, "findUniqueOrThrow">; +type PUpdate = Prisma.TypeMap["model"][Capitalize]["operations"]["update"]["args"]["data"]; +type PCreate = Prisma.TypeMap["model"][Capitalize]["operations"]["create"]["args"]["data"]; + +type A = PRead<"projectUser", {}, {}>; + +type Context = { + params: Record, + auth: SmartRequestAuth | null, +}; + +type AllowedCrudSchema = CrudSchema & { admin: { readSchema: {} } }; +type AllowedCrudType = CrudTypeOf & { Admin: { Read: {} } }; + +type CRead = T extends { Admin: { Read: infer R } } ? R : never; +type CCreate = T extends { Admin: { Create: infer R } } ? R : never; +type CUpdate = T extends { Admin: { Update: infer R } } ? R : never; +type CEitherWrite = CCreate | CUpdate; + +type Options< + T extends AllowedCrudType, + PrismaModelName extends AllPrismaModelNames, + ParamName extends string, + W extends Where, + I extends Include, + B extends BaseFields, +> = + & { + paramNames: ParamName[], + baseFields: (context: Context) => Promise, + where?: (context: Context) => Promise, + whereUnique?: (context: Context) => Promise>, + include: (context: Context) => Promise, + crudToPrisma?: ((crud: CEitherWrite, context: Context) => Promise | Omit, keyof B>>), + prismaToCrud?: (prisma: PRead, context: Context) => Promise>, + fieldMapping?: any, + createNotFoundError?: (context: Context) => Error, + } + & ( + | { + crudToPrisma: {}, + prismaToCrud: {}, + fieldMapping?: void, + } + | { + crudToPrisma: void, + prismaToCrud: void, + fieldMapping: {}, + } + ); + +type CrudHandlersFromCrudType> = CrudHandlers< + | ("Create" extends keyof T["Admin"] ? "Create" : never) + | ("Read" extends keyof T["Admin"] ? "Read" : never) + | ("Read" extends keyof T["Admin"] ? "List" : never) + | ("Update" extends keyof T["Admin"] ? "Update" : never) + | ("Delete" extends keyof T["Admin"] ? "Delete" : never) +>; + +export function createPrismaCrudHandlers< + S extends AllowedCrudSchema, + PrismaModelName extends AllPrismaModelNames, + ParamName extends string, + O extends Options & AllowedCrudType, PrismaModelName, ParamName, any, any, any> +>( + crudSchema: S, + prismaModelName: PrismaModelName, + options: O, +): CrudHandlersFromCrudType> { + const wrapper = (func: (data: any, context: Context, queryBase: any) => Promise): (opts: { params: Record, data?: unknown, auth: SmartRequestAuth | null }) => Promise => { + return async (req) => { + const context: Context = { + params: req.params, + auth: req.auth, + }; + const whereBase = await options.where?.(context); + const includeBase = await options.include(context); + try { + return await func(req.data, context, { where: whereBase, include: includeBase }); + } catch (e) { + if ((e as any)?.code === 'P2025') { + throw (options.createNotFoundError ?? (() => new StatusError(StatusError.NotFound)))(context); + } + throw e; + } + }; + }; + + const prismaToCrud = options.prismaToCrud ?? throwErr("missing prismaToCrud is not yet implemented"); + const crudToPrisma = options.crudToPrisma ?? throwErr("missing crudToPrisma is not yet implemented"); + + return createCrudHandlers(crudSchema, { + paramNames: [], + onRead: wrapper(async (data, context) => { + const prisma = await (prismaClient[prismaModelName].findUniqueOrThrow as any)({ + include: await options.include(context), + where: { + ...await options.baseFields(context), + ...await options.where?.(context), + ...await options.whereUnique?.(context), + }, + }); + return await prismaToCrud(prisma, context); + }), + onList: wrapper(async (data, context) => { + const prisma: any[] = await (prismaClient[prismaModelName].findMany as any)({ + include: await options.include(context), + where: { + ...await options.baseFields(context), + ...await options.where?.(context), + }, + }); + return await Promise.all(prisma.map((p) => prismaToCrud(p, context))); + }), + onCreate: wrapper(async (data, context) => { + const prisma = await (prismaClient[prismaModelName].create as any)({ + include: await options.include(context), + data: { + ...await options.baseFields(context), + ...await crudToPrisma(data, context), + }, + }); + return await prismaToCrud(prisma, context); + }), + onUpdate: wrapper(async (data, context) => { + const prisma = await (prismaClient[prismaModelName].update as any)({ + include: await options.include(context), + where: { + ...await options.baseFields(context), + ...await options.where?.(context), + ...await options.whereUnique?.(context), + }, + data: await crudToPrisma(data, context), + }); + return await prismaToCrud(prisma, context); + }), + onDelete: wrapper(async (data, context) => { + await (prismaClient[prismaModelName].delete as any)({ + include: await options.include(context), + where: { + ...await options.baseFields(context), + ...await options.where?.(context), + ...await options.whereUnique?.(context), + }, + }); + }), + }); +} diff --git a/packages/stack-shared/CHANGELOG.md b/packages/stack-shared/CHANGELOG.md index c8a991c81..5614a9edc 100644 --- a/packages/stack-shared/CHANGELOG.md +++ b/packages/stack-shared/CHANGELOG.md @@ -1,5 +1,13 @@ # @stackframe/stack-shared +## 2.4.3 + +### Patch Changes + +- CRUD schemas +- Updated dependencies + - @stackframe/stack-sc@1.5.2 + ## 2.4.2 ### Patch Changes diff --git a/packages/stack-shared/package.json b/packages/stack-shared/package.json index 7ea2e74ee..ec0845a1c 100644 --- a/packages/stack-shared/package.json +++ b/packages/stack-shared/package.json @@ -1,6 +1,6 @@ { "name": "@stackframe/stack-shared", - "version": "2.4.2", + "version": "2.4.3", "main": "./dist/index.js", "types": "./dist/index.d.ts", "scripts": { diff --git a/packages/stack/CHANGELOG.md b/packages/stack/CHANGELOG.md index 3f72b3adf..ee0309d19 100644 --- a/packages/stack/CHANGELOG.md +++ b/packages/stack/CHANGELOG.md @@ -1,5 +1,14 @@ # @stackframe/stack +## 2.4.5 + +### Patch Changes + +- CRUD schemas +- Updated dependencies + - @stackframe/stack-shared@2.4.3 + - @stackframe/stack-sc@1.5.2 + ## 2.4.4 ### Patch Changes diff --git a/packages/stack/package.json b/packages/stack/package.json index c9d161857..96cd3d25f 100644 --- a/packages/stack/package.json +++ b/packages/stack/package.json @@ -1,6 +1,6 @@ { "name": "@stackframe/stack", - "version": "2.4.4", + "version": "2.4.5", "sideEffects": false, "exports": { ".": {