zenstack-trpc 0.1.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/LICENSE +21 -0
- package/README.md +284 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/router-generator.d.ts +284 -0
- package/dist/router-generator.d.ts.map +1 -0
- package/dist/router-generator.js +93 -0
- package/dist/zod-schemas.d.ts +128 -0
- package/dist/zod-schemas.d.ts.map +1 -0
- package/dist/zod-schemas.js +362 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# zenstack-trpc
|
|
2
|
+
|
|
3
|
+
Auto-generate fully type-safe tRPC routers from [ZenStack V3](https://zenstack.dev) schemas.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Zero codegen** - Router generated at runtime from schema metadata
|
|
8
|
+
- **Full type inference** - Input AND output types from your ZenStack schema
|
|
9
|
+
- **Dynamic result typing** - `include`/`select` options reflected in return types
|
|
10
|
+
- **Zod validation** - Runtime input validation built-in
|
|
11
|
+
- **All CRUD operations** - findMany, findUnique, create, update, delete, and more
|
|
12
|
+
- **Standard tRPC** - Works with all tRPC adapters (HTTP, WebSocket, Next.js, etc.)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install zenstack-trpc @trpc/server @zenstackhq/orm zod
|
|
18
|
+
# or
|
|
19
|
+
pnpm add zenstack-trpc @trpc/server @zenstackhq/orm zod
|
|
20
|
+
# or
|
|
21
|
+
yarn add zenstack-trpc @trpc/server @zenstackhq/orm zod
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### 1. Define your ZenStack schema
|
|
27
|
+
|
|
28
|
+
```prisma
|
|
29
|
+
// schema.zmodel
|
|
30
|
+
model User {
|
|
31
|
+
id String @id @default(cuid())
|
|
32
|
+
email String @unique
|
|
33
|
+
name String?
|
|
34
|
+
posts Post[]
|
|
35
|
+
createdAt DateTime @default(now())
|
|
36
|
+
updatedAt DateTime @updatedAt
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
model Post {
|
|
40
|
+
id String @id @default(cuid())
|
|
41
|
+
title String
|
|
42
|
+
content String?
|
|
43
|
+
published Boolean @default(false)
|
|
44
|
+
author User @relation(fields: [authorId], references: [id])
|
|
45
|
+
authorId String
|
|
46
|
+
createdAt DateTime @default(now())
|
|
47
|
+
updatedAt DateTime @updatedAt
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 2. Generate ZenStack artifacts
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npx zenstack generate
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 3. Create the tRPC router
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { ZenStackClient } from "@zenstackhq/orm";
|
|
61
|
+
import { schema, SchemaType } from "./zenstack/schema.js";
|
|
62
|
+
import {
|
|
63
|
+
createTRPC,
|
|
64
|
+
createZenStackRouter,
|
|
65
|
+
type Context,
|
|
66
|
+
type TypedRouterCaller,
|
|
67
|
+
} from "zenstack-trpc";
|
|
68
|
+
|
|
69
|
+
// Create your database client
|
|
70
|
+
const db = new ZenStackClient(schema, {
|
|
71
|
+
dialect: yourDialect, // Kysely dialect (SQLite, PostgreSQL, MySQL, etc.)
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Create tRPC instance
|
|
75
|
+
const t = createTRPC<Context>();
|
|
76
|
+
|
|
77
|
+
// Generate the router from your schema
|
|
78
|
+
const appRouter = createZenStackRouter(schema, t);
|
|
79
|
+
|
|
80
|
+
// Export for client usage
|
|
81
|
+
export type AppRouter = typeof appRouter;
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 4. Use the router
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// Create a typed caller
|
|
88
|
+
const caller = appRouter.createCaller({ db }) as TypedRouterCaller<SchemaType>;
|
|
89
|
+
|
|
90
|
+
// All operations are fully typed!
|
|
91
|
+
const users = await caller.user.findMany();
|
|
92
|
+
// ^? { id: string, email: string, name: string | null, ... }[]
|
|
93
|
+
|
|
94
|
+
// Include relations - return type automatically includes them
|
|
95
|
+
const usersWithPosts = await caller.user.findMany({
|
|
96
|
+
include: { posts: true }
|
|
97
|
+
});
|
|
98
|
+
// ^? { id: string, ..., posts: Post[] }[]
|
|
99
|
+
|
|
100
|
+
// Select specific fields
|
|
101
|
+
const emails = await caller.user.findMany({
|
|
102
|
+
select: { id: true, email: true }
|
|
103
|
+
});
|
|
104
|
+
// ^? { id: string, email: string }[]
|
|
105
|
+
|
|
106
|
+
// Create with full validation
|
|
107
|
+
const user = await caller.user.create({
|
|
108
|
+
data: {
|
|
109
|
+
email: "alice@example.com",
|
|
110
|
+
name: "Alice",
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Update with type-safe where clause
|
|
115
|
+
await caller.user.update({
|
|
116
|
+
where: { id: user.id },
|
|
117
|
+
data: { name: "Alice Smith" },
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## API Reference
|
|
122
|
+
|
|
123
|
+
### `createTRPC<Context>()`
|
|
124
|
+
|
|
125
|
+
Creates a tRPC instance with the given context type.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { createTRPC, type Context } from "zenstack-trpc";
|
|
129
|
+
|
|
130
|
+
const t = createTRPC<Context>();
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### `createZenStackRouter(schema, t)`
|
|
134
|
+
|
|
135
|
+
Generates a tRPC router from a ZenStack schema.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
import { createZenStackRouter } from "zenstack-trpc";
|
|
139
|
+
|
|
140
|
+
const appRouter = createZenStackRouter(schema, t);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### `TypedRouterCaller<SchemaType>`
|
|
144
|
+
|
|
145
|
+
Type helper for fully typed caller with dynamic input/output inference.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import type { TypedRouterCaller } from "zenstack-trpc";
|
|
149
|
+
import type { SchemaType } from "./zenstack/schema.js";
|
|
150
|
+
|
|
151
|
+
const caller = appRouter.createCaller({ db }) as TypedRouterCaller<SchemaType>;
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### `TypedModelProcedures<Schema, Model>`
|
|
155
|
+
|
|
156
|
+
Type helper for a single model's procedures.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import type { TypedModelProcedures } from "zenstack-trpc";
|
|
160
|
+
|
|
161
|
+
type UserProcedures = TypedModelProcedures<SchemaType, "User">;
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Generated Router Structure
|
|
165
|
+
|
|
166
|
+
For each model in your schema, the following procedures are generated:
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
router
|
|
170
|
+
├── user
|
|
171
|
+
│ ├── findMany (query)
|
|
172
|
+
│ ├── findUnique (query)
|
|
173
|
+
│ ├── findFirst (query)
|
|
174
|
+
│ ├── create (mutation)
|
|
175
|
+
│ ├── createMany (mutation)
|
|
176
|
+
│ ├── update (mutation)
|
|
177
|
+
│ ├── updateMany (mutation)
|
|
178
|
+
│ ├── upsert (mutation)
|
|
179
|
+
│ ├── delete (mutation)
|
|
180
|
+
│ ├── deleteMany (mutation)
|
|
181
|
+
│ ├── count (query)
|
|
182
|
+
│ ├── aggregate (query)
|
|
183
|
+
│ └── groupBy (query)
|
|
184
|
+
├── post
|
|
185
|
+
│ └── ... (same operations)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Using with tRPC Adapters
|
|
189
|
+
|
|
190
|
+
### Standalone HTTP Server
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
import { createHTTPServer } from "@trpc/server/adapters/standalone";
|
|
194
|
+
|
|
195
|
+
const server = createHTTPServer({
|
|
196
|
+
router: appRouter,
|
|
197
|
+
createContext: () => ({ db }),
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
server.listen(3000);
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Next.js App Router
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// app/api/trpc/[trpc]/route.ts
|
|
207
|
+
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
|
|
208
|
+
|
|
209
|
+
const handler = (req: Request) =>
|
|
210
|
+
fetchRequestHandler({
|
|
211
|
+
endpoint: "/api/trpc",
|
|
212
|
+
req,
|
|
213
|
+
router: appRouter,
|
|
214
|
+
createContext: () => ({ db }),
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
export { handler as GET, handler as POST };
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Express
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
import express from "express";
|
|
224
|
+
import { createExpressMiddleware } from "@trpc/server/adapters/express";
|
|
225
|
+
|
|
226
|
+
const app = express();
|
|
227
|
+
|
|
228
|
+
app.use(
|
|
229
|
+
"/trpc",
|
|
230
|
+
createExpressMiddleware({
|
|
231
|
+
router: appRouter,
|
|
232
|
+
createContext: () => ({ db }),
|
|
233
|
+
})
|
|
234
|
+
);
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Advanced Usage
|
|
238
|
+
|
|
239
|
+
### Custom Context
|
|
240
|
+
|
|
241
|
+
Extend the context with authentication or other data:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
interface MyContext extends Context {
|
|
245
|
+
db: any;
|
|
246
|
+
userId?: string;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const t = createTRPC<MyContext>();
|
|
250
|
+
const appRouter = createZenStackRouter(schema, t);
|
|
251
|
+
|
|
252
|
+
// In your adapter
|
|
253
|
+
createContext: (opts) => ({
|
|
254
|
+
db: getEnhancedDb(opts.req), // ZenStack enhanced client with access control
|
|
255
|
+
userId: getUserFromRequest(opts.req),
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Zod Schema Access
|
|
260
|
+
|
|
261
|
+
Access the generated Zod schemas for custom validation:
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
import {
|
|
265
|
+
createModelSchemas,
|
|
266
|
+
createWhereSchema,
|
|
267
|
+
createCreateDataSchema,
|
|
268
|
+
} from "zenstack-trpc";
|
|
269
|
+
|
|
270
|
+
const userSchemas = createModelSchemas(schema, "User");
|
|
271
|
+
const whereSchema = createWhereSchema(schema, "User");
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Requirements
|
|
275
|
+
|
|
276
|
+
- Node.js >= 18
|
|
277
|
+
- TypeScript >= 5.0
|
|
278
|
+
- ZenStack V3 (`@zenstackhq/orm` >= 3.0.0)
|
|
279
|
+
- tRPC >= 11.0.0
|
|
280
|
+
- Zod >= 3.0.0
|
|
281
|
+
|
|
282
|
+
## License
|
|
283
|
+
|
|
284
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zenstack-trpc
|
|
3
|
+
*
|
|
4
|
+
* Auto-generate fully type-safe tRPC routers from ZenStack V3 schemas.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { ZenStackClient } from "@zenstackhq/orm";
|
|
9
|
+
* import { schema, SchemaType } from "./zenstack/schema.js";
|
|
10
|
+
* import {
|
|
11
|
+
* createTRPC,
|
|
12
|
+
* createZenStackRouter,
|
|
13
|
+
* type Context,
|
|
14
|
+
* type TypedRouterCaller,
|
|
15
|
+
* } from "zenstack-trpc";
|
|
16
|
+
*
|
|
17
|
+
* const db = new ZenStackClient(schema, { dialect: yourDialect });
|
|
18
|
+
* const t = createTRPC<Context>();
|
|
19
|
+
* const appRouter = createZenStackRouter(schema, t);
|
|
20
|
+
*
|
|
21
|
+
* // Create a typed caller with full type inference
|
|
22
|
+
* const caller = appRouter.createCaller({ db }) as TypedRouterCaller<SchemaType>;
|
|
23
|
+
*
|
|
24
|
+
* // All operations are fully typed!
|
|
25
|
+
* const users = await caller.user.findMany({ include: { posts: true } });
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @packageDocumentation
|
|
29
|
+
*/
|
|
30
|
+
export { createTRPC, createZenStackRouter, type Context, type ZenStackRouter, type TypedRouterCaller, type TypedModelProcedures, type TRPCInstance, } from "./router-generator.js";
|
|
31
|
+
export { createModelSchemas, createWhereSchema, createCreateDataSchema, createUpdateDataSchema, createUniqueWhereSchema, createSelectSchema, createIncludeSchema, createOrderBySchema, } from "./zod-schemas.js";
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,KAAK,OAAO,EACZ,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,YAAY,GAClB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zenstack-trpc
|
|
3
|
+
*
|
|
4
|
+
* Auto-generate fully type-safe tRPC routers from ZenStack V3 schemas.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { ZenStackClient } from "@zenstackhq/orm";
|
|
9
|
+
* import { schema, SchemaType } from "./zenstack/schema.js";
|
|
10
|
+
* import {
|
|
11
|
+
* createTRPC,
|
|
12
|
+
* createZenStackRouter,
|
|
13
|
+
* type Context,
|
|
14
|
+
* type TypedRouterCaller,
|
|
15
|
+
* } from "zenstack-trpc";
|
|
16
|
+
*
|
|
17
|
+
* const db = new ZenStackClient(schema, { dialect: yourDialect });
|
|
18
|
+
* const t = createTRPC<Context>();
|
|
19
|
+
* const appRouter = createZenStackRouter(schema, t);
|
|
20
|
+
*
|
|
21
|
+
* // Create a typed caller with full type inference
|
|
22
|
+
* const caller = appRouter.createCaller({ db }) as TypedRouterCaller<SchemaType>;
|
|
23
|
+
*
|
|
24
|
+
* // All operations are fully typed!
|
|
25
|
+
* const users = await caller.user.findMany({ include: { posts: true } });
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @packageDocumentation
|
|
29
|
+
*/
|
|
30
|
+
// Router generator and types
|
|
31
|
+
export { createTRPC, createZenStackRouter, } from "./router-generator.js";
|
|
32
|
+
// Zod schema generators (for advanced usage)
|
|
33
|
+
export { createModelSchemas, createWhereSchema, createCreateDataSchema, createUpdateDataSchema, createUniqueWhereSchema, createSelectSchema, createIncludeSchema, createOrderBySchema, } from "./zod-schemas.js";
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import type { SchemaDef, GetModels } from "@zenstackhq/orm/schema";
|
|
2
|
+
import type { FindManyArgs, FindUniqueArgs, FindFirstArgs, CreateArgs, CreateManyArgs, UpdateArgs, UpdateManyArgs, UpsertArgs, DeleteArgs, DeleteManyArgs, CountArgs, AggregateArgs, GroupByArgs, SimplifiedPlainResult } from "@zenstackhq/orm";
|
|
3
|
+
/**
|
|
4
|
+
* Context type for the tRPC router
|
|
5
|
+
* The db property accepts any ZenStack client instance
|
|
6
|
+
*/
|
|
7
|
+
export interface Context {
|
|
8
|
+
db: any;
|
|
9
|
+
}
|
|
10
|
+
declare const _initTRPCResult: import("@trpc/server").TRPCRootObject<Context, object, import("@trpc/server").TRPCRuntimeConfigOptions<Context, object>, {
|
|
11
|
+
ctx: Context;
|
|
12
|
+
meta: object;
|
|
13
|
+
errorShape: import("@trpc/server").TRPCDefaultErrorShape;
|
|
14
|
+
transformer: false;
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Type representing the result of initTRPC.context().create()
|
|
18
|
+
*/
|
|
19
|
+
export type TRPCInstance = typeof _initTRPCResult;
|
|
20
|
+
/**
|
|
21
|
+
* Creates a tRPC instance with the given context
|
|
22
|
+
* @returns A tRPC instance configured with your context type
|
|
23
|
+
*/
|
|
24
|
+
export declare function createTRPC<TContext extends Context>(): TRPCInstance;
|
|
25
|
+
/**
|
|
26
|
+
* Type helper to convert model names to lowercase
|
|
27
|
+
*/
|
|
28
|
+
type Uncapitalize<S extends string> = S extends `${infer First}${infer Rest}` ? `${Lowercase<First>}${Rest}` : S;
|
|
29
|
+
/**
|
|
30
|
+
* Type for a single model's procedures - provides FULL dynamic input AND output typing
|
|
31
|
+
*
|
|
32
|
+
* When you pass `include` or `select`, the return type automatically includes those fields!
|
|
33
|
+
*/
|
|
34
|
+
export interface TypedModelProcedures<Schema extends SchemaDef, Model extends GetModels<Schema>> {
|
|
35
|
+
/**
|
|
36
|
+
* Find multiple records
|
|
37
|
+
* @example
|
|
38
|
+
* // Returns User[] with posts included
|
|
39
|
+
* const users = await caller.user.findMany({ include: { posts: true } });
|
|
40
|
+
*/
|
|
41
|
+
findMany<T extends FindManyArgs<Schema, Model>>(input?: T): Promise<SimplifiedPlainResult<Schema, Model, T>[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Find a unique record by ID or unique field
|
|
44
|
+
*/
|
|
45
|
+
findUnique<T extends FindUniqueArgs<Schema, Model>>(input: T): Promise<SimplifiedPlainResult<Schema, Model, T> | null>;
|
|
46
|
+
/**
|
|
47
|
+
* Find the first matching record
|
|
48
|
+
*/
|
|
49
|
+
findFirst<T extends FindFirstArgs<Schema, Model>>(input?: T): Promise<SimplifiedPlainResult<Schema, Model, T> | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Create a new record
|
|
52
|
+
*/
|
|
53
|
+
create<T extends CreateArgs<Schema, Model>>(input: T): Promise<SimplifiedPlainResult<Schema, Model, T>>;
|
|
54
|
+
/**
|
|
55
|
+
* Create multiple records
|
|
56
|
+
*/
|
|
57
|
+
createMany(input: CreateManyArgs<Schema, Model>): Promise<{
|
|
58
|
+
count: number;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Update a record
|
|
62
|
+
*/
|
|
63
|
+
update<T extends UpdateArgs<Schema, Model>>(input: T): Promise<SimplifiedPlainResult<Schema, Model, T>>;
|
|
64
|
+
/**
|
|
65
|
+
* Update multiple records
|
|
66
|
+
*/
|
|
67
|
+
updateMany(input: UpdateManyArgs<Schema, Model>): Promise<{
|
|
68
|
+
count: number;
|
|
69
|
+
}>;
|
|
70
|
+
/**
|
|
71
|
+
* Create or update a record
|
|
72
|
+
*/
|
|
73
|
+
upsert<T extends UpsertArgs<Schema, Model>>(input: T): Promise<SimplifiedPlainResult<Schema, Model, T>>;
|
|
74
|
+
/**
|
|
75
|
+
* Delete a record
|
|
76
|
+
*/
|
|
77
|
+
delete<T extends DeleteArgs<Schema, Model>>(input: T): Promise<SimplifiedPlainResult<Schema, Model, T>>;
|
|
78
|
+
/**
|
|
79
|
+
* Delete multiple records
|
|
80
|
+
*/
|
|
81
|
+
deleteMany(input: DeleteManyArgs<Schema, Model>): Promise<{
|
|
82
|
+
count: number;
|
|
83
|
+
}>;
|
|
84
|
+
/**
|
|
85
|
+
* Count records
|
|
86
|
+
*/
|
|
87
|
+
count(input?: CountArgs<Schema, Model>): Promise<number>;
|
|
88
|
+
/**
|
|
89
|
+
* Aggregate records
|
|
90
|
+
*/
|
|
91
|
+
aggregate(input: AggregateArgs<Schema, Model>): Promise<any>;
|
|
92
|
+
/**
|
|
93
|
+
* Group records
|
|
94
|
+
*/
|
|
95
|
+
groupBy(input: GroupByArgs<Schema, Model>): Promise<any[]>;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Type for the generated router caller - maps model names to their typed procedures
|
|
99
|
+
*
|
|
100
|
+
* This provides FULL type inference including:
|
|
101
|
+
* - Input types (where, data, include, select, etc.)
|
|
102
|
+
* - Output types that reflect include/select options
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```ts
|
|
106
|
+
* const caller = appRouter.createCaller({ db }) as TypedRouterCaller<SchemaType>;
|
|
107
|
+
*
|
|
108
|
+
* // Basic query - returns User[]
|
|
109
|
+
* const users = await caller.user.findMany();
|
|
110
|
+
*
|
|
111
|
+
* // With include - returns (User & { posts: Post[] })[]
|
|
112
|
+
* const usersWithPosts = await caller.user.findMany({ include: { posts: true } });
|
|
113
|
+
*
|
|
114
|
+
* // With select - returns { id: string, email: string }[]
|
|
115
|
+
* const userEmails = await caller.user.findMany({ select: { id: true, email: true } });
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export type TypedRouterCaller<Schema extends SchemaDef> = {
|
|
119
|
+
[K in GetModels<Schema> as Uncapitalize<K>]: TypedModelProcedures<Schema, K>;
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Creates a tRPC router from a ZenStack schema.
|
|
123
|
+
*
|
|
124
|
+
* The router follows the pattern: router.modelName.operation
|
|
125
|
+
* Example: router.user.findMany(), router.post.create()
|
|
126
|
+
*
|
|
127
|
+
* For proper typing on the caller, use the TypedRouterCaller type:
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```ts
|
|
131
|
+
* import { createZenStackRouter, createTRPC, Context, TypedRouterCaller } from './trpc';
|
|
132
|
+
* import { schema, SchemaType } from '../zenstack/schema';
|
|
133
|
+
*
|
|
134
|
+
* const t = createTRPC<Context>();
|
|
135
|
+
* const appRouter = createZenStackRouter(schema, t);
|
|
136
|
+
*
|
|
137
|
+
* // Create a typed caller with FULL type inference
|
|
138
|
+
* const caller = appRouter.createCaller({ db }) as TypedRouterCaller<SchemaType>;
|
|
139
|
+
*
|
|
140
|
+
* // Types are fully inferred based on your query!
|
|
141
|
+
* const users = await caller.user.findMany();
|
|
142
|
+
* // ^? { id: string, email: string, name: string | null, ... }[]
|
|
143
|
+
*
|
|
144
|
+
* const usersWithPosts = await caller.user.findMany({ include: { posts: true } });
|
|
145
|
+
* // ^? { id: string, email: string, ..., posts: Post[] }[]
|
|
146
|
+
*
|
|
147
|
+
* export type AppRouter = typeof appRouter;
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export declare function createZenStackRouter<Schema extends SchemaDef>(schema: Schema, t: TRPCInstance): import("@trpc/server").TRPCBuiltRouter<{
|
|
151
|
+
ctx: Context;
|
|
152
|
+
meta: object;
|
|
153
|
+
errorShape: import("@trpc/server").TRPCDefaultErrorShape;
|
|
154
|
+
transformer: false;
|
|
155
|
+
}, import("@trpc/server").TRPCDecorateCreateRouterOptions<Record<string, import("@trpc/server").TRPCBuiltRouter<{
|
|
156
|
+
ctx: Context;
|
|
157
|
+
meta: object;
|
|
158
|
+
errorShape: import("@trpc/server").TRPCDefaultErrorShape;
|
|
159
|
+
transformer: false;
|
|
160
|
+
}, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
|
|
161
|
+
findMany: import("@trpc/server").TRPCQueryProcedure<{
|
|
162
|
+
input: unknown;
|
|
163
|
+
output: any;
|
|
164
|
+
meta: object;
|
|
165
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
166
|
+
input: unknown;
|
|
167
|
+
output: any;
|
|
168
|
+
meta: object;
|
|
169
|
+
}>;
|
|
170
|
+
findUnique: import("@trpc/server").TRPCQueryProcedure<{
|
|
171
|
+
input: unknown;
|
|
172
|
+
output: any;
|
|
173
|
+
meta: object;
|
|
174
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
175
|
+
input: unknown;
|
|
176
|
+
output: any;
|
|
177
|
+
meta: object;
|
|
178
|
+
}>;
|
|
179
|
+
findFirst: import("@trpc/server").TRPCQueryProcedure<{
|
|
180
|
+
input: unknown;
|
|
181
|
+
output: any;
|
|
182
|
+
meta: object;
|
|
183
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
184
|
+
input: unknown;
|
|
185
|
+
output: any;
|
|
186
|
+
meta: object;
|
|
187
|
+
}>;
|
|
188
|
+
count: import("@trpc/server").TRPCQueryProcedure<{
|
|
189
|
+
input: unknown;
|
|
190
|
+
output: any;
|
|
191
|
+
meta: object;
|
|
192
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
193
|
+
input: unknown;
|
|
194
|
+
output: any;
|
|
195
|
+
meta: object;
|
|
196
|
+
}>;
|
|
197
|
+
aggregate: import("@trpc/server").TRPCQueryProcedure<{
|
|
198
|
+
input: unknown;
|
|
199
|
+
output: any;
|
|
200
|
+
meta: object;
|
|
201
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
202
|
+
input: unknown;
|
|
203
|
+
output: any;
|
|
204
|
+
meta: object;
|
|
205
|
+
}>;
|
|
206
|
+
groupBy: import("@trpc/server").TRPCQueryProcedure<{
|
|
207
|
+
input: unknown;
|
|
208
|
+
output: any;
|
|
209
|
+
meta: object;
|
|
210
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
211
|
+
input: unknown;
|
|
212
|
+
output: any;
|
|
213
|
+
meta: object;
|
|
214
|
+
}>;
|
|
215
|
+
create: import("@trpc/server").TRPCQueryProcedure<{
|
|
216
|
+
input: unknown;
|
|
217
|
+
output: any;
|
|
218
|
+
meta: object;
|
|
219
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
220
|
+
input: unknown;
|
|
221
|
+
output: any;
|
|
222
|
+
meta: object;
|
|
223
|
+
}>;
|
|
224
|
+
createMany: import("@trpc/server").TRPCQueryProcedure<{
|
|
225
|
+
input: unknown;
|
|
226
|
+
output: any;
|
|
227
|
+
meta: object;
|
|
228
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
229
|
+
input: unknown;
|
|
230
|
+
output: any;
|
|
231
|
+
meta: object;
|
|
232
|
+
}>;
|
|
233
|
+
update: import("@trpc/server").TRPCQueryProcedure<{
|
|
234
|
+
input: unknown;
|
|
235
|
+
output: any;
|
|
236
|
+
meta: object;
|
|
237
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
238
|
+
input: unknown;
|
|
239
|
+
output: any;
|
|
240
|
+
meta: object;
|
|
241
|
+
}>;
|
|
242
|
+
updateMany: import("@trpc/server").TRPCQueryProcedure<{
|
|
243
|
+
input: unknown;
|
|
244
|
+
output: any;
|
|
245
|
+
meta: object;
|
|
246
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
247
|
+
input: unknown;
|
|
248
|
+
output: any;
|
|
249
|
+
meta: object;
|
|
250
|
+
}>;
|
|
251
|
+
upsert: import("@trpc/server").TRPCQueryProcedure<{
|
|
252
|
+
input: unknown;
|
|
253
|
+
output: any;
|
|
254
|
+
meta: object;
|
|
255
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
256
|
+
input: unknown;
|
|
257
|
+
output: any;
|
|
258
|
+
meta: object;
|
|
259
|
+
}>;
|
|
260
|
+
delete: import("@trpc/server").TRPCQueryProcedure<{
|
|
261
|
+
input: unknown;
|
|
262
|
+
output: any;
|
|
263
|
+
meta: object;
|
|
264
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
265
|
+
input: unknown;
|
|
266
|
+
output: any;
|
|
267
|
+
meta: object;
|
|
268
|
+
}>;
|
|
269
|
+
deleteMany: import("@trpc/server").TRPCQueryProcedure<{
|
|
270
|
+
input: unknown;
|
|
271
|
+
output: any;
|
|
272
|
+
meta: object;
|
|
273
|
+
}> | import("@trpc/server").TRPCMutationProcedure<{
|
|
274
|
+
input: unknown;
|
|
275
|
+
output: any;
|
|
276
|
+
meta: object;
|
|
277
|
+
}>;
|
|
278
|
+
}>>>>>;
|
|
279
|
+
/**
|
|
280
|
+
* Type helper to extract the router type for a given schema
|
|
281
|
+
*/
|
|
282
|
+
export type ZenStackRouter<Schema extends SchemaDef> = ReturnType<typeof createZenStackRouter<Schema>>;
|
|
283
|
+
export {};
|
|
284
|
+
//# sourceMappingURL=router-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router-generator.d.ts","sourceRoot":"","sources":["../src/router-generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,UAAU,EACV,cAAc,EACd,UAAU,EACV,cAAc,EACd,UAAU,EACV,UAAU,EACV,cAAc,EACd,SAAS,EACT,aAAa,EACb,WAAW,EACX,qBAAqB,EACtB,MAAM,iBAAiB,CAAC;AAIzB;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,GAAG,CAAC;CACT;AAGD,QAAA,MAAM,eAAe;;;;;EAAuC,CAAC;AAE7D;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,eAAe,CAAC;AAElD;;;GAGG;AACH,wBAAgB,UAAU,CAAC,QAAQ,SAAS,OAAO,KAAK,YAAY,CAEnE;AAED;;GAEG;AACH,KAAK,YAAY,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,GAAG,MAAM,KAAK,GAAG,MAAM,IAAI,EAAE,GACzE,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,GAC5B,CAAC,CAAC;AAEN;;;;GAIG;AACH,MAAM,WAAW,oBAAoB,CACnC,MAAM,SAAS,SAAS,EACxB,KAAK,SAAS,SAAS,CAAC,MAAM,CAAC;IAE/B;;;;;OAKG;IACH,QAAQ,CAAC,CAAC,SAAS,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EAC5C,KAAK,CAAC,EAAE,CAAC,GACR,OAAO,CAAC,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IAEtD;;OAEG;IACH,UAAU,CAAC,CAAC,SAAS,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,EAChD,KAAK,EAAE,CAAC,GACP,OAAO,CAAC,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAE3D;;OAEG;IACH,SAAS,CAAC,CAAC,SAAS,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,EAC9C,KAAK,CAAC,EAAE,CAAC,GACR,OAAO,CAAC,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAE3D;;OAEG;IACH,MAAM,CAAC,CAAC,SAAS,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,EACxC,KAAK,EAAE,CAAC,GACP,OAAO,CAAC,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAEpD;;OAEG;IACH,UAAU,CACR,KAAK,EAAE,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,GACnC,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE9B;;OAEG;IACH,MAAM,CAAC,CAAC,SAAS,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,EACxC,KAAK,EAAE,CAAC,GACP,OAAO,CAAC,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAEpD;;OAEG;IACH,UAAU,CACR,KAAK,EAAE,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,GACnC,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE9B;;OAEG;IACH,MAAM,CAAC,CAAC,SAAS,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,EACxC,KAAK,EAAE,CAAC,GACP,OAAO,CAAC,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAEpD;;OAEG;IACH,MAAM,CAAC,CAAC,SAAS,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,EACxC,KAAK,EAAE,CAAC,GACP,OAAO,CAAC,qBAAqB,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAEpD;;OAEG;IACH,UAAU,CACR,KAAK,EAAE,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,GACnC,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE9B;;OAEG;IACH,KAAK,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEzD;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAE7D;;OAEG;IACH,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;CAC5D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,iBAAiB,CAAC,MAAM,SAAS,SAAS,IAAI;KACvD,CAAC,IAAI,SAAS,CAAC,MAAM,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC;CAC7E,CAAC;AAqDF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,SAAS,SAAS,EAC3D,MAAM,EAAE,MAAM,EACd,CAAC,EAAE,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAahB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,CAAC,MAAM,SAAS,SAAS,IAAI,UAAU,CAC/D,OAAO,oBAAoB,CAAC,MAAM,CAAC,CACpC,CAAC"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { initTRPC, TRPCError } from "@trpc/server";
|
|
2
|
+
import { createModelSchemas } from "./zod-schemas.js";
|
|
3
|
+
// Internal type for initTRPC result
|
|
4
|
+
const _initTRPCResult = initTRPC.context().create();
|
|
5
|
+
/**
|
|
6
|
+
* Creates a tRPC instance with the given context
|
|
7
|
+
* @returns A tRPC instance configured with your context type
|
|
8
|
+
*/
|
|
9
|
+
export function createTRPC() {
|
|
10
|
+
return initTRPC.context().create();
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Creates procedures for a single model
|
|
14
|
+
*/
|
|
15
|
+
function createModelProcedures(schema, modelName, t) {
|
|
16
|
+
const schemas = createModelSchemas(schema, modelName);
|
|
17
|
+
const modelNameLower = modelName.charAt(0).toLowerCase() + modelName.slice(1);
|
|
18
|
+
const createHandler = (op, isQuery) => {
|
|
19
|
+
const inputSchema = schemas[op];
|
|
20
|
+
const handler = async ({ ctx, input }) => {
|
|
21
|
+
const db = ctx.db;
|
|
22
|
+
const model = db[modelNameLower];
|
|
23
|
+
if (!model || typeof model[op] !== "function") {
|
|
24
|
+
throw new TRPCError({
|
|
25
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
26
|
+
message: `Operation ${op} not found on model ${modelName}`,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return model[op](input);
|
|
30
|
+
};
|
|
31
|
+
if (isQuery) {
|
|
32
|
+
return t.procedure.input(inputSchema).query(handler);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
return t.procedure.input(inputSchema).mutation(handler);
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
const procedures = {
|
|
39
|
+
findMany: createHandler("findMany", true),
|
|
40
|
+
findUnique: createHandler("findUnique", true),
|
|
41
|
+
findFirst: createHandler("findFirst", true),
|
|
42
|
+
count: createHandler("count", true),
|
|
43
|
+
aggregate: createHandler("aggregate", true),
|
|
44
|
+
groupBy: createHandler("groupBy", true),
|
|
45
|
+
create: createHandler("create", false),
|
|
46
|
+
createMany: createHandler("createMany", false),
|
|
47
|
+
update: createHandler("update", false),
|
|
48
|
+
updateMany: createHandler("updateMany", false),
|
|
49
|
+
upsert: createHandler("upsert", false),
|
|
50
|
+
delete: createHandler("delete", false),
|
|
51
|
+
deleteMany: createHandler("deleteMany", false),
|
|
52
|
+
};
|
|
53
|
+
return t.router(procedures);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Creates a tRPC router from a ZenStack schema.
|
|
57
|
+
*
|
|
58
|
+
* The router follows the pattern: router.modelName.operation
|
|
59
|
+
* Example: router.user.findMany(), router.post.create()
|
|
60
|
+
*
|
|
61
|
+
* For proper typing on the caller, use the TypedRouterCaller type:
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* import { createZenStackRouter, createTRPC, Context, TypedRouterCaller } from './trpc';
|
|
66
|
+
* import { schema, SchemaType } from '../zenstack/schema';
|
|
67
|
+
*
|
|
68
|
+
* const t = createTRPC<Context>();
|
|
69
|
+
* const appRouter = createZenStackRouter(schema, t);
|
|
70
|
+
*
|
|
71
|
+
* // Create a typed caller with FULL type inference
|
|
72
|
+
* const caller = appRouter.createCaller({ db }) as TypedRouterCaller<SchemaType>;
|
|
73
|
+
*
|
|
74
|
+
* // Types are fully inferred based on your query!
|
|
75
|
+
* const users = await caller.user.findMany();
|
|
76
|
+
* // ^? { id: string, email: string, name: string | null, ... }[]
|
|
77
|
+
*
|
|
78
|
+
* const usersWithPosts = await caller.user.findMany({ include: { posts: true } });
|
|
79
|
+
* // ^? { id: string, email: string, ..., posts: Post[] }[]
|
|
80
|
+
*
|
|
81
|
+
* export type AppRouter = typeof appRouter;
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export function createZenStackRouter(schema, t) {
|
|
85
|
+
const modelRouters = {};
|
|
86
|
+
// Get all model names from the schema
|
|
87
|
+
const modelNames = Object.keys(schema.models);
|
|
88
|
+
for (const modelName of modelNames) {
|
|
89
|
+
const modelNameLower = modelName.charAt(0).toLowerCase() + modelName.slice(1);
|
|
90
|
+
modelRouters[modelNameLower] = createModelProcedures(schema, modelName, t);
|
|
91
|
+
}
|
|
92
|
+
return t.router(modelRouters);
|
|
93
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { SchemaDef } from "@zenstackhq/orm/schema";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a Zod schema for a model's where input
|
|
5
|
+
*/
|
|
6
|
+
export declare function createWhereSchema<Schema extends SchemaDef>(schema: Schema, modelName: keyof Schema["models"]): z.ZodTypeAny;
|
|
7
|
+
/**
|
|
8
|
+
* Creates a Zod schema for a model's create data input
|
|
9
|
+
*/
|
|
10
|
+
export declare function createCreateDataSchema<Schema extends SchemaDef>(schema: Schema, modelName: keyof Schema["models"]): z.ZodTypeAny;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a Zod schema for a model's update data input
|
|
13
|
+
*/
|
|
14
|
+
export declare function createUpdateDataSchema<Schema extends SchemaDef>(schema: Schema, modelName: keyof Schema["models"]): z.ZodTypeAny;
|
|
15
|
+
/**
|
|
16
|
+
* Creates a Zod schema for unique where input (id or unique fields)
|
|
17
|
+
*/
|
|
18
|
+
export declare function createUniqueWhereSchema<Schema extends SchemaDef>(schema: Schema, modelName: keyof Schema["models"]): z.ZodTypeAny;
|
|
19
|
+
/**
|
|
20
|
+
* Creates a Zod schema for select input
|
|
21
|
+
*/
|
|
22
|
+
export declare function createSelectSchema<Schema extends SchemaDef>(schema: Schema, modelName: keyof Schema["models"]): z.ZodTypeAny;
|
|
23
|
+
/**
|
|
24
|
+
* Creates a Zod schema for include input
|
|
25
|
+
*/
|
|
26
|
+
export declare function createIncludeSchema<Schema extends SchemaDef>(schema: Schema, modelName: keyof Schema["models"]): z.ZodTypeAny;
|
|
27
|
+
/**
|
|
28
|
+
* Creates a Zod schema for orderBy input
|
|
29
|
+
*/
|
|
30
|
+
export declare function createOrderBySchema<Schema extends SchemaDef>(schema: Schema, modelName: keyof Schema["models"]): z.ZodTypeAny;
|
|
31
|
+
/**
|
|
32
|
+
* Creates all Zod schemas for a model's CRUD operations
|
|
33
|
+
*/
|
|
34
|
+
export declare function createModelSchemas<Schema extends SchemaDef>(schema: Schema, modelName: keyof Schema["models"]): {
|
|
35
|
+
findMany: z.ZodOptional<z.ZodObject<{
|
|
36
|
+
where: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
37
|
+
select: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
38
|
+
include: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
39
|
+
orderBy: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
40
|
+
skip: z.ZodOptional<z.ZodNumber>;
|
|
41
|
+
take: z.ZodOptional<z.ZodNumber>;
|
|
42
|
+
cursor: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
43
|
+
distinct: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
44
|
+
}, z.core.$loose>>;
|
|
45
|
+
findUnique: z.ZodObject<{
|
|
46
|
+
where: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
47
|
+
select: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
48
|
+
include: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
49
|
+
}, z.core.$loose>;
|
|
50
|
+
findFirst: z.ZodOptional<z.ZodObject<{
|
|
51
|
+
where: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
52
|
+
select: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
53
|
+
include: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
54
|
+
orderBy: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
55
|
+
skip: z.ZodOptional<z.ZodNumber>;
|
|
56
|
+
cursor: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
57
|
+
}, z.core.$loose>>;
|
|
58
|
+
create: z.ZodObject<{
|
|
59
|
+
data: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
60
|
+
select: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
61
|
+
include: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
62
|
+
}, z.core.$loose>;
|
|
63
|
+
createMany: z.ZodOptional<z.ZodObject<{
|
|
64
|
+
data: z.ZodUnion<readonly [z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, z.ZodArray<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>]>;
|
|
65
|
+
skipDuplicates: z.ZodOptional<z.ZodBoolean>;
|
|
66
|
+
}, z.core.$loose>>;
|
|
67
|
+
update: z.ZodObject<{
|
|
68
|
+
where: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
69
|
+
data: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
70
|
+
select: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
71
|
+
include: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
72
|
+
}, z.core.$loose>;
|
|
73
|
+
updateMany: z.ZodObject<{
|
|
74
|
+
where: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
75
|
+
data: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
76
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
77
|
+
}, z.core.$loose>;
|
|
78
|
+
upsert: z.ZodObject<{
|
|
79
|
+
where: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
80
|
+
create: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
81
|
+
update: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
82
|
+
select: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
83
|
+
include: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
84
|
+
}, z.core.$loose>;
|
|
85
|
+
delete: z.ZodObject<{
|
|
86
|
+
where: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
87
|
+
select: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
88
|
+
include: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
89
|
+
}, z.core.$loose>;
|
|
90
|
+
deleteMany: z.ZodOptional<z.ZodObject<{
|
|
91
|
+
where: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
92
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
93
|
+
}, z.core.$loose>>;
|
|
94
|
+
count: z.ZodOptional<z.ZodObject<{
|
|
95
|
+
where: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
96
|
+
select: z.ZodOptional<z.ZodAny>;
|
|
97
|
+
cursor: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
98
|
+
skip: z.ZodOptional<z.ZodNumber>;
|
|
99
|
+
take: z.ZodOptional<z.ZodNumber>;
|
|
100
|
+
orderBy: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
101
|
+
}, z.core.$loose>>;
|
|
102
|
+
aggregate: z.ZodObject<{
|
|
103
|
+
where: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
104
|
+
cursor: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
105
|
+
skip: z.ZodOptional<z.ZodNumber>;
|
|
106
|
+
take: z.ZodOptional<z.ZodNumber>;
|
|
107
|
+
orderBy: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
108
|
+
_count: z.ZodOptional<z.ZodAny>;
|
|
109
|
+
_avg: z.ZodOptional<z.ZodAny>;
|
|
110
|
+
_sum: z.ZodOptional<z.ZodAny>;
|
|
111
|
+
_min: z.ZodOptional<z.ZodAny>;
|
|
112
|
+
_max: z.ZodOptional<z.ZodAny>;
|
|
113
|
+
}, z.core.$loose>;
|
|
114
|
+
groupBy: z.ZodObject<{
|
|
115
|
+
by: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>;
|
|
116
|
+
where: z.ZodOptional<z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
117
|
+
having: z.ZodOptional<z.ZodAny>;
|
|
118
|
+
orderBy: z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>;
|
|
119
|
+
skip: z.ZodOptional<z.ZodNumber>;
|
|
120
|
+
take: z.ZodOptional<z.ZodNumber>;
|
|
121
|
+
_count: z.ZodOptional<z.ZodAny>;
|
|
122
|
+
_avg: z.ZodOptional<z.ZodAny>;
|
|
123
|
+
_sum: z.ZodOptional<z.ZodAny>;
|
|
124
|
+
_min: z.ZodOptional<z.ZodAny>;
|
|
125
|
+
_max: z.ZodOptional<z.ZodAny>;
|
|
126
|
+
}, z.core.$loose>;
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=zod-schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zod-schemas.d.ts","sourceRoot":"","sources":["../src/zod-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAsDxD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,SAAS,SAAS,EACxD,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,MAAM,CAAC,QAAQ,CAAC,GAChC,CAAC,CAAC,UAAU,CAmDd;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,SAAS,SAAS,EAC7D,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,MAAM,CAAC,QAAQ,CAAC,GAChC,CAAC,CAAC,UAAU,CAyCd;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,SAAS,SAAS,EAC7D,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,MAAM,CAAC,QAAQ,CAAC,GAChC,CAAC,CAAC,UAAU,CAuCd;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,SAAS,SAAS,EAC9D,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,MAAM,CAAC,QAAQ,CAAC,GAChC,CAAC,CAAC,UAAU,CAed;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,SAAS,SAAS,EACzD,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,MAAM,CAAC,QAAQ,CAAC,GAChC,CAAC,CAAC,UAAU,CAcd;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,SAAS,SAAS,EAC1D,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,MAAM,CAAC,QAAQ,CAAC,GAChC,CAAC,CAAC,UAAU,CAiBd;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,SAAS,SAAS,EAC1D,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,MAAM,CAAC,QAAQ,CAAC,GAChC,CAAC,CAAC,UAAU,CAkBd;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,SAAS,SAAS,EACzD,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,MAAM,CAAC,QAAQ,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmIlC"}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Maps ZenStack field types to Zod schemas
|
|
4
|
+
*/
|
|
5
|
+
function getZodTypeForField(fieldType, isOptional, isArray) {
|
|
6
|
+
let baseSchema;
|
|
7
|
+
switch (fieldType) {
|
|
8
|
+
case "String":
|
|
9
|
+
baseSchema = z.string();
|
|
10
|
+
break;
|
|
11
|
+
case "Int":
|
|
12
|
+
case "Float":
|
|
13
|
+
baseSchema = z.number();
|
|
14
|
+
break;
|
|
15
|
+
case "BigInt":
|
|
16
|
+
baseSchema = z.bigint();
|
|
17
|
+
break;
|
|
18
|
+
case "Boolean":
|
|
19
|
+
baseSchema = z.boolean();
|
|
20
|
+
break;
|
|
21
|
+
case "DateTime":
|
|
22
|
+
baseSchema = z.union([z.date(), z.string().datetime()]);
|
|
23
|
+
break;
|
|
24
|
+
case "Json":
|
|
25
|
+
baseSchema = z.any();
|
|
26
|
+
break;
|
|
27
|
+
case "Bytes":
|
|
28
|
+
baseSchema = z.instanceof(Uint8Array);
|
|
29
|
+
break;
|
|
30
|
+
case "Decimal":
|
|
31
|
+
baseSchema = z.union([z.number(), z.string()]);
|
|
32
|
+
break;
|
|
33
|
+
default:
|
|
34
|
+
// For relation types or unknown types, use any
|
|
35
|
+
baseSchema = z.any();
|
|
36
|
+
}
|
|
37
|
+
if (isArray) {
|
|
38
|
+
baseSchema = z.array(baseSchema);
|
|
39
|
+
}
|
|
40
|
+
if (isOptional) {
|
|
41
|
+
baseSchema = baseSchema.optional().nullable();
|
|
42
|
+
}
|
|
43
|
+
return baseSchema;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Creates a Zod schema for a model's where input
|
|
47
|
+
*/
|
|
48
|
+
export function createWhereSchema(schema, modelName) {
|
|
49
|
+
const model = schema.models[modelName];
|
|
50
|
+
if (!model) {
|
|
51
|
+
return z.object({}).passthrough();
|
|
52
|
+
}
|
|
53
|
+
const fields = model.fields;
|
|
54
|
+
const shape = {};
|
|
55
|
+
for (const [fieldName, fieldDef] of Object.entries(fields)) {
|
|
56
|
+
const isRelation = fieldDef.relation !== undefined;
|
|
57
|
+
if (isRelation) {
|
|
58
|
+
// For relations, allow nested where clauses
|
|
59
|
+
shape[fieldName] = z.any().optional();
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
// For scalar fields, allow direct value or filter object
|
|
63
|
+
const fieldType = fieldDef.type;
|
|
64
|
+
const baseType = getZodTypeForField(fieldType, false, false);
|
|
65
|
+
shape[fieldName] = z
|
|
66
|
+
.union([
|
|
67
|
+
baseType,
|
|
68
|
+
z.object({
|
|
69
|
+
equals: baseType.optional(),
|
|
70
|
+
not: baseType.optional(),
|
|
71
|
+
in: z.array(baseType).optional(),
|
|
72
|
+
notIn: z.array(baseType).optional(),
|
|
73
|
+
lt: baseType.optional(),
|
|
74
|
+
lte: baseType.optional(),
|
|
75
|
+
gt: baseType.optional(),
|
|
76
|
+
gte: baseType.optional(),
|
|
77
|
+
contains: z.string().optional(),
|
|
78
|
+
startsWith: z.string().optional(),
|
|
79
|
+
endsWith: z.string().optional(),
|
|
80
|
+
}).passthrough(),
|
|
81
|
+
])
|
|
82
|
+
.optional();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Add logical operators
|
|
86
|
+
const baseSchema = z.object(shape).passthrough();
|
|
87
|
+
return z
|
|
88
|
+
.object({
|
|
89
|
+
AND: z.union([baseSchema, z.array(baseSchema)]).optional(),
|
|
90
|
+
OR: z.array(baseSchema).optional(),
|
|
91
|
+
NOT: z.union([baseSchema, z.array(baseSchema)]).optional(),
|
|
92
|
+
})
|
|
93
|
+
.merge(baseSchema)
|
|
94
|
+
.partial();
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Creates a Zod schema for a model's create data input
|
|
98
|
+
*/
|
|
99
|
+
export function createCreateDataSchema(schema, modelName) {
|
|
100
|
+
const model = schema.models[modelName];
|
|
101
|
+
if (!model) {
|
|
102
|
+
return z.object({}).passthrough();
|
|
103
|
+
}
|
|
104
|
+
const fields = model.fields;
|
|
105
|
+
const shape = {};
|
|
106
|
+
for (const [fieldName, fieldDef] of Object.entries(fields)) {
|
|
107
|
+
const isRelation = fieldDef.relation !== undefined;
|
|
108
|
+
const hasDefault = fieldDef.default !== undefined;
|
|
109
|
+
const isId = fieldDef.id === true;
|
|
110
|
+
const isUpdatedAt = fieldDef.updatedAt === true;
|
|
111
|
+
const isOptional = fieldDef.optional === true || hasDefault || isId || isUpdatedAt;
|
|
112
|
+
const isForeignKey = fieldDef.foreignKeyFor !== undefined;
|
|
113
|
+
if (isRelation) {
|
|
114
|
+
// For relations, allow create/connect/connectOrCreate
|
|
115
|
+
shape[fieldName] = z
|
|
116
|
+
.object({
|
|
117
|
+
create: z.any().optional(),
|
|
118
|
+
connect: z.any().optional(),
|
|
119
|
+
connectOrCreate: z.any().optional(),
|
|
120
|
+
})
|
|
121
|
+
.passthrough()
|
|
122
|
+
.optional();
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// Regular fields including foreign keys
|
|
126
|
+
const fieldType = fieldDef.type;
|
|
127
|
+
// Foreign keys are optional if a relation is used instead
|
|
128
|
+
const isFkOptional = isForeignKey ? true : isOptional;
|
|
129
|
+
shape[fieldName] = getZodTypeForField(fieldType, isFkOptional, fieldDef.array === true);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return z.object(shape).passthrough();
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Creates a Zod schema for a model's update data input
|
|
136
|
+
*/
|
|
137
|
+
export function createUpdateDataSchema(schema, modelName) {
|
|
138
|
+
const model = schema.models[modelName];
|
|
139
|
+
if (!model) {
|
|
140
|
+
return z.object({}).passthrough();
|
|
141
|
+
}
|
|
142
|
+
const fields = model.fields;
|
|
143
|
+
const shape = {};
|
|
144
|
+
for (const [fieldName, fieldDef] of Object.entries(fields)) {
|
|
145
|
+
const isRelation = fieldDef.relation !== undefined;
|
|
146
|
+
const isId = fieldDef.id === true;
|
|
147
|
+
if (isId)
|
|
148
|
+
continue; // Skip id fields for updates
|
|
149
|
+
if (isRelation) {
|
|
150
|
+
// For relations, allow full nested operations
|
|
151
|
+
shape[fieldName] = z
|
|
152
|
+
.object({
|
|
153
|
+
create: z.any().optional(),
|
|
154
|
+
connect: z.any().optional(),
|
|
155
|
+
connectOrCreate: z.any().optional(),
|
|
156
|
+
disconnect: z.any().optional(),
|
|
157
|
+
delete: z.any().optional(),
|
|
158
|
+
update: z.any().optional(),
|
|
159
|
+
updateMany: z.any().optional(),
|
|
160
|
+
deleteMany: z.any().optional(),
|
|
161
|
+
set: z.any().optional(),
|
|
162
|
+
upsert: z.any().optional(),
|
|
163
|
+
})
|
|
164
|
+
.passthrough()
|
|
165
|
+
.optional();
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
const fieldType = fieldDef.type;
|
|
169
|
+
shape[fieldName] = getZodTypeForField(fieldType, true, fieldDef.array === true);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return z.object(shape).passthrough();
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Creates a Zod schema for unique where input (id or unique fields)
|
|
176
|
+
*/
|
|
177
|
+
export function createUniqueWhereSchema(schema, modelName) {
|
|
178
|
+
const model = schema.models[modelName];
|
|
179
|
+
if (!model) {
|
|
180
|
+
return z.object({}).passthrough();
|
|
181
|
+
}
|
|
182
|
+
const uniqueFields = model.uniqueFields;
|
|
183
|
+
const shape = {};
|
|
184
|
+
for (const [fieldName, fieldDef] of Object.entries(uniqueFields)) {
|
|
185
|
+
const fieldType = fieldDef.type;
|
|
186
|
+
shape[fieldName] = getZodTypeForField(fieldType, true, false);
|
|
187
|
+
}
|
|
188
|
+
return z.object(shape).passthrough();
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Creates a Zod schema for select input
|
|
192
|
+
*/
|
|
193
|
+
export function createSelectSchema(schema, modelName) {
|
|
194
|
+
const model = schema.models[modelName];
|
|
195
|
+
if (!model) {
|
|
196
|
+
return z.object({}).passthrough();
|
|
197
|
+
}
|
|
198
|
+
const fields = model.fields;
|
|
199
|
+
const shape = {};
|
|
200
|
+
for (const fieldName of Object.keys(fields)) {
|
|
201
|
+
shape[fieldName] = z.union([z.boolean(), z.any()]).optional();
|
|
202
|
+
}
|
|
203
|
+
return z.object(shape).passthrough().optional();
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Creates a Zod schema for include input
|
|
207
|
+
*/
|
|
208
|
+
export function createIncludeSchema(schema, modelName) {
|
|
209
|
+
const model = schema.models[modelName];
|
|
210
|
+
if (!model) {
|
|
211
|
+
return z.object({}).passthrough();
|
|
212
|
+
}
|
|
213
|
+
const fields = model.fields;
|
|
214
|
+
const shape = {};
|
|
215
|
+
for (const [fieldName, fieldDef] of Object.entries(fields)) {
|
|
216
|
+
const isRelation = fieldDef.relation !== undefined;
|
|
217
|
+
if (isRelation) {
|
|
218
|
+
shape[fieldName] = z.union([z.boolean(), z.any()]).optional();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return z.object(shape).passthrough().optional();
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Creates a Zod schema for orderBy input
|
|
225
|
+
*/
|
|
226
|
+
export function createOrderBySchema(schema, modelName) {
|
|
227
|
+
const model = schema.models[modelName];
|
|
228
|
+
if (!model) {
|
|
229
|
+
return z.any();
|
|
230
|
+
}
|
|
231
|
+
const fields = model.fields;
|
|
232
|
+
const shape = {};
|
|
233
|
+
const sortOrder = z.enum(["asc", "desc"]);
|
|
234
|
+
for (const [fieldName, fieldDef] of Object.entries(fields)) {
|
|
235
|
+
const isRelation = fieldDef.relation !== undefined;
|
|
236
|
+
if (!isRelation) {
|
|
237
|
+
shape[fieldName] = sortOrder.optional();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return z.union([z.object(shape).passthrough(), z.array(z.object(shape).passthrough())]).optional();
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Creates all Zod schemas for a model's CRUD operations
|
|
244
|
+
*/
|
|
245
|
+
export function createModelSchemas(schema, modelName) {
|
|
246
|
+
const whereSchema = createWhereSchema(schema, modelName);
|
|
247
|
+
const uniqueWhereSchema = createUniqueWhereSchema(schema, modelName);
|
|
248
|
+
const createDataSchema = createCreateDataSchema(schema, modelName);
|
|
249
|
+
const updateDataSchema = createUpdateDataSchema(schema, modelName);
|
|
250
|
+
const selectSchema = createSelectSchema(schema, modelName);
|
|
251
|
+
const includeSchema = createIncludeSchema(schema, modelName);
|
|
252
|
+
const orderBySchema = createOrderBySchema(schema, modelName);
|
|
253
|
+
return {
|
|
254
|
+
findMany: z
|
|
255
|
+
.object({
|
|
256
|
+
where: whereSchema.optional(),
|
|
257
|
+
select: selectSchema,
|
|
258
|
+
include: includeSchema,
|
|
259
|
+
orderBy: orderBySchema,
|
|
260
|
+
skip: z.number().optional(),
|
|
261
|
+
take: z.number().optional(),
|
|
262
|
+
cursor: uniqueWhereSchema.optional(),
|
|
263
|
+
distinct: z.array(z.string()).optional(),
|
|
264
|
+
})
|
|
265
|
+
.passthrough()
|
|
266
|
+
.optional(),
|
|
267
|
+
findUnique: z.object({
|
|
268
|
+
where: uniqueWhereSchema,
|
|
269
|
+
select: selectSchema,
|
|
270
|
+
include: includeSchema,
|
|
271
|
+
}).passthrough(),
|
|
272
|
+
findFirst: z
|
|
273
|
+
.object({
|
|
274
|
+
where: whereSchema.optional(),
|
|
275
|
+
select: selectSchema,
|
|
276
|
+
include: includeSchema,
|
|
277
|
+
orderBy: orderBySchema,
|
|
278
|
+
skip: z.number().optional(),
|
|
279
|
+
cursor: uniqueWhereSchema.optional(),
|
|
280
|
+
})
|
|
281
|
+
.passthrough()
|
|
282
|
+
.optional(),
|
|
283
|
+
create: z.object({
|
|
284
|
+
data: createDataSchema,
|
|
285
|
+
select: selectSchema,
|
|
286
|
+
include: includeSchema,
|
|
287
|
+
}).passthrough(),
|
|
288
|
+
createMany: z
|
|
289
|
+
.object({
|
|
290
|
+
data: z.union([createDataSchema, z.array(createDataSchema)]),
|
|
291
|
+
skipDuplicates: z.boolean().optional(),
|
|
292
|
+
})
|
|
293
|
+
.passthrough()
|
|
294
|
+
.optional(),
|
|
295
|
+
update: z.object({
|
|
296
|
+
where: uniqueWhereSchema,
|
|
297
|
+
data: updateDataSchema,
|
|
298
|
+
select: selectSchema,
|
|
299
|
+
include: includeSchema,
|
|
300
|
+
}).passthrough(),
|
|
301
|
+
updateMany: z.object({
|
|
302
|
+
where: whereSchema.optional(),
|
|
303
|
+
data: updateDataSchema,
|
|
304
|
+
limit: z.number().optional(),
|
|
305
|
+
}).passthrough(),
|
|
306
|
+
upsert: z.object({
|
|
307
|
+
where: uniqueWhereSchema,
|
|
308
|
+
create: createDataSchema,
|
|
309
|
+
update: updateDataSchema,
|
|
310
|
+
select: selectSchema,
|
|
311
|
+
include: includeSchema,
|
|
312
|
+
}).passthrough(),
|
|
313
|
+
delete: z.object({
|
|
314
|
+
where: uniqueWhereSchema,
|
|
315
|
+
select: selectSchema,
|
|
316
|
+
include: includeSchema,
|
|
317
|
+
}).passthrough(),
|
|
318
|
+
deleteMany: z
|
|
319
|
+
.object({
|
|
320
|
+
where: whereSchema.optional(),
|
|
321
|
+
limit: z.number().optional(),
|
|
322
|
+
})
|
|
323
|
+
.passthrough()
|
|
324
|
+
.optional(),
|
|
325
|
+
count: z
|
|
326
|
+
.object({
|
|
327
|
+
where: whereSchema.optional(),
|
|
328
|
+
select: z.any().optional(),
|
|
329
|
+
cursor: uniqueWhereSchema.optional(),
|
|
330
|
+
skip: z.number().optional(),
|
|
331
|
+
take: z.number().optional(),
|
|
332
|
+
orderBy: orderBySchema,
|
|
333
|
+
})
|
|
334
|
+
.passthrough()
|
|
335
|
+
.optional(),
|
|
336
|
+
aggregate: z.object({
|
|
337
|
+
where: whereSchema.optional(),
|
|
338
|
+
cursor: uniqueWhereSchema.optional(),
|
|
339
|
+
skip: z.number().optional(),
|
|
340
|
+
take: z.number().optional(),
|
|
341
|
+
orderBy: orderBySchema,
|
|
342
|
+
_count: z.any().optional(),
|
|
343
|
+
_avg: z.any().optional(),
|
|
344
|
+
_sum: z.any().optional(),
|
|
345
|
+
_min: z.any().optional(),
|
|
346
|
+
_max: z.any().optional(),
|
|
347
|
+
}).passthrough(),
|
|
348
|
+
groupBy: z.object({
|
|
349
|
+
by: z.union([z.string(), z.array(z.string())]),
|
|
350
|
+
where: whereSchema.optional(),
|
|
351
|
+
having: z.any().optional(),
|
|
352
|
+
orderBy: orderBySchema,
|
|
353
|
+
skip: z.number().optional(),
|
|
354
|
+
take: z.number().optional(),
|
|
355
|
+
_count: z.any().optional(),
|
|
356
|
+
_avg: z.any().optional(),
|
|
357
|
+
_sum: z.any().optional(),
|
|
358
|
+
_min: z.any().optional(),
|
|
359
|
+
_max: z.any().optional(),
|
|
360
|
+
}).passthrough(),
|
|
361
|
+
};
|
|
362
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zenstack-trpc",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Auto-generate type-safe tRPC routers from ZenStack V3 schemas",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsc -p tsconfig.build.json",
|
|
22
|
+
"prepublishOnly": "pnpm run build",
|
|
23
|
+
"test": "vitest run",
|
|
24
|
+
"test:watch": "vitest",
|
|
25
|
+
"typecheck": "tsc --noEmit"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"zenstack",
|
|
29
|
+
"trpc",
|
|
30
|
+
"typescript",
|
|
31
|
+
"orm",
|
|
32
|
+
"prisma",
|
|
33
|
+
"typesafe",
|
|
34
|
+
"api",
|
|
35
|
+
"rpc",
|
|
36
|
+
"router",
|
|
37
|
+
"codegen"
|
|
38
|
+
],
|
|
39
|
+
"author": "",
|
|
40
|
+
"license": "MIT",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "https://github.com/olup/zenstack-trpc"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://github.com/olup/zenstack-trpc#readme",
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/olup/zenstack-trpc/issues"
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@trpc/server": ">=11.0.0",
|
|
54
|
+
"@zenstackhq/orm": ">=3.0.0",
|
|
55
|
+
"zod": ">=3.0.0 || >=4.0.0"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@trpc/server": "^11.8.1",
|
|
59
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
60
|
+
"@types/node": "^25.0.3",
|
|
61
|
+
"@zenstackhq/cli": "^3.1.0",
|
|
62
|
+
"@zenstackhq/orm": "^3.1.0",
|
|
63
|
+
"better-sqlite3": "^12.5.0",
|
|
64
|
+
"kysely": "^0.28.9",
|
|
65
|
+
"prisma": "^7.2.0",
|
|
66
|
+
"tsx": "^4.21.0",
|
|
67
|
+
"typescript": "^5.9.3",
|
|
68
|
+
"vitest": "^4.0.16",
|
|
69
|
+
"zod": "^4.3.5"
|
|
70
|
+
}
|
|
71
|
+
}
|