@ai0x0/utils 0.0.47 → 0.0.48
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/.agents/skills/ai0x0-utils/backend/SKILL.md +40 -38
- package/SKILL.md +39 -37
- package/es/__shims__/next-rest-framework.d.ts +2 -0
- package/es/backend/route-operation/delete-operation.d.ts +7 -24
- package/es/backend/route-operation/delete-operation.js +9 -127
- package/es/backend/route-operation/get-list-operation.d.ts +5 -31
- package/es/backend/route-operation/get-list-operation.js +10 -142
- package/es/backend/route-operation/get-operation.d.ts +5 -25
- package/es/backend/route-operation/get-operation.js +10 -139
- package/es/backend/route-operation/get-table-name.js +0 -2
- package/es/backend/route-operation/index.d.ts +1 -0
- package/es/backend/route-operation/index.js +2 -1
- package/es/backend/route-operation/open-api-operation.d.ts +6 -0
- package/es/backend/route-operation/open-api-operation.js +13 -0
- package/es/backend/route-operation/operation-common.d.ts +38 -0
- package/es/backend/route-operation/operation-common.js +148 -0
- package/es/backend/route-operation/operation-core.d.ts +3 -0
- package/es/backend/route-operation/operation-core.js +3 -0
- package/es/backend/route-operation/post-operation.d.ts +5 -25
- package/es/backend/route-operation/post-operation.js +13 -189
- package/es/backend/route-operation/put-operation.d.ts +4 -23
- package/es/backend/route-operation/put-operation.js +10 -155
- package/es/backend/route-operation/read-operation-core.d.ts +55 -0
- package/es/backend/route-operation/read-operation-core.js +224 -0
- package/es/backend/route-operation/write-operation-core.d.ts +94 -0
- package/es/backend/route-operation/write-operation-core.js +390 -0
- package/es/backend/sqlite/route-operation/delete-operation.d.ts +9 -26
- package/es/backend/sqlite/route-operation/delete-operation.js +9 -127
- package/es/backend/sqlite/route-operation/get-list-operation.d.ts +6 -32
- package/es/backend/sqlite/route-operation/get-list-operation.js +10 -142
- package/es/backend/sqlite/route-operation/get-operation.d.ts +6 -26
- package/es/backend/sqlite/route-operation/get-operation.js +10 -139
- package/es/backend/sqlite/route-operation/index.d.ts +1 -0
- package/es/backend/sqlite/route-operation/index.js +2 -1
- package/es/backend/sqlite/route-operation/post-operation.d.ts +6 -26
- package/es/backend/sqlite/route-operation/post-operation.js +13 -189
- package/es/backend/sqlite/route-operation/put-operation.d.ts +6 -25
- package/es/backend/sqlite/route-operation/put-operation.js +10 -155
- package/lib/__shims__/next-rest-framework.d.ts +2 -0
- package/lib/backend/route-operation/delete-operation.d.ts +7 -24
- package/lib/backend/route-operation/delete-operation.js +6 -56
- package/lib/backend/route-operation/get-list-operation.d.ts +5 -31
- package/lib/backend/route-operation/get-list-operation.js +7 -72
- package/lib/backend/route-operation/get-operation.d.ts +5 -25
- package/lib/backend/route-operation/get-operation.js +7 -71
- package/lib/backend/route-operation/index.d.ts +1 -0
- package/lib/backend/route-operation/index.js +3 -1
- package/lib/backend/route-operation/open-api-operation.d.ts +6 -0
- package/lib/backend/route-operation/open-api-operation.js +37 -0
- package/lib/backend/route-operation/operation-common.d.ts +38 -0
- package/lib/backend/route-operation/operation-common.js +90 -0
- package/lib/backend/route-operation/operation-core.d.ts +3 -0
- package/lib/backend/route-operation/operation-core.js +27 -0
- package/lib/backend/route-operation/post-operation.d.ts +5 -25
- package/lib/backend/route-operation/post-operation.js +7 -71
- package/lib/backend/route-operation/put-operation.d.ts +4 -23
- package/lib/backend/route-operation/put-operation.js +7 -68
- package/lib/backend/route-operation/read-operation-core.d.ts +55 -0
- package/lib/backend/route-operation/read-operation-core.js +151 -0
- package/lib/backend/route-operation/write-operation-core.d.ts +94 -0
- package/lib/backend/route-operation/write-operation-core.js +181 -0
- package/lib/backend/sqlite/route-operation/delete-operation.d.ts +9 -26
- package/lib/backend/sqlite/route-operation/delete-operation.js +6 -56
- package/lib/backend/sqlite/route-operation/get-list-operation.d.ts +6 -32
- package/lib/backend/sqlite/route-operation/get-list-operation.js +7 -75
- package/lib/backend/sqlite/route-operation/get-operation.d.ts +6 -26
- package/lib/backend/sqlite/route-operation/get-operation.js +7 -71
- package/lib/backend/sqlite/route-operation/index.d.ts +1 -0
- package/lib/backend/sqlite/route-operation/index.js +3 -1
- package/lib/backend/sqlite/route-operation/post-operation.d.ts +6 -26
- package/lib/backend/sqlite/route-operation/post-operation.js +7 -71
- package/lib/backend/sqlite/route-operation/put-operation.d.ts +6 -25
- package/lib/backend/sqlite/route-operation/put-operation.js +7 -68
- package/package.json +1 -1
|
@@ -36,7 +36,7 @@ The bundled ESLint preset treats every custom rule as an error. Backend examples
|
|
|
36
36
|
- Use braces for every control-flow block because `curly` is enabled as `error`.
|
|
37
37
|
- Keep route files thin. Put reusable logic in `utils/actions/<resource>.ts` instead of exporting helpers from `route.ts`.
|
|
38
38
|
- For files with 150+ lines, add the three-line `===` section divider blocks described in the `nextjs` skill.
|
|
39
|
-
- Avoid `try/catch` in route handlers; use `
|
|
39
|
+
- Avoid `try/catch` in route handlers; use `catch` or the factory default error handling.
|
|
40
40
|
|
|
41
41
|
## Project Setup
|
|
42
42
|
|
|
@@ -87,24 +87,22 @@ export const getListOperation = createGetListOperation({ db, getSession });
|
|
|
87
87
|
|
|
88
88
|
## Factory Catalog
|
|
89
89
|
|
|
90
|
-
| Export | Purpose | Required options
|
|
91
|
-
| ------------------- | -------------------------- |
|
|
92
|
-
| `createTableSchema` | pg `Table` + 5 zod schemas | `name`, `columns`
|
|
93
|
-
| `getOperation` | GET one by filters | `
|
|
94
|
-
| `getListOperation` | GET paginated list | `
|
|
95
|
-
| `postOperation` | POST create | `
|
|
96
|
-
| `putOperation` | PUT update | `
|
|
97
|
-
| `deleteOperation` | DELETE | —
|
|
90
|
+
| Export | Purpose | Required options | Notable optional options |
|
|
91
|
+
| ------------------- | -------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
92
|
+
| `createTableSchema` | pg `Table` + 5 zod schemas | `name`, `columns` | `refineSchema`, `extraConfig` |
|
|
93
|
+
| `getOperation` | GET one by filters | `schemas.query`, `schemas.response` | `table`, `setParams`, `relations`, `jsonArrayFields`, `access.byCreator`, `handler`, `catch`, `openApiOperation` |
|
|
94
|
+
| `getListOperation` | GET paginated list | `schemas.query`, `schemas.response` | `table`, `setParams`, `relations`, `jsonArrayFields`, `handler`, `catch`, `openApiOperation` |
|
|
95
|
+
| `postOperation` | POST create | `schemas.body` | `table`, `contentType`, `parseBody`, `schemas.response`, `setBody`, `handler`, `catch`, `openApiOperation` |
|
|
96
|
+
| `putOperation` | PUT update | `schemas.body` | `table`, `schemas.response`, `setBody`, `access.byCreator`, `handler`, `catch`, `openApiOperation` |
|
|
97
|
+
| `deleteOperation` | DELETE | — | `table`, `schemas.body`, `access.byCreator`, `handler`, `catch`, `openApiOperation` |
|
|
98
98
|
|
|
99
|
-
`
|
|
100
|
-
|
|
101
|
-
`summary` and `description` are forwarded into the OpenAPI operation object.
|
|
99
|
+
`openApiOperation` is spread directly into the `next-rest-framework` operation metadata. Use it for `summary`, `description`, `tags`, `deprecated`, `security`, `externalDocs`, and future OpenAPI operation fields. When omitted, `tags` defaults to the table name when `table` is supplied.
|
|
102
100
|
|
|
103
101
|
`postOperation` defaults to `contentType: "application/json"`. Pass `contentType: "multipart/form-data"` to read `await req.formData()` as body, or pass both a custom `contentType` and `parseBody(req)` for custom payload parsing.
|
|
104
102
|
|
|
105
|
-
In `postOperation` and `putOperation`, `
|
|
103
|
+
In `postOperation` and `putOperation`, `handler({ params })` is typed from `schemas.body` plus the injected `creatorId` / `editorId`, so custom action helpers can usually consume `params` directly without reparsing the body schema.
|
|
106
104
|
|
|
107
|
-
`table` and factory-level `db` are optional for route operations. When `table` is omitted or `db` is not provided, the operation still parses input, applies `setParams` / `setBody`, session-derived fields, and `
|
|
105
|
+
`table` and factory-level `db` are optional for route operations. When `table` is omitted or `db` is not provided, the operation still parses input, applies `setParams` / `setBody`, session-derived fields, and `handler`, but skips the built-in table action (`select` / `insert` / `update` / `delete`). Use this for custom endpoints that want the same OpenAPI and response wiring without a direct table operation.
|
|
108
106
|
|
|
109
107
|
## Canonical Examples
|
|
110
108
|
|
|
@@ -168,7 +166,7 @@ export const queryListAgentSchema = queryListSchema(
|
|
|
168
166
|
export const queryListSelectAgentSchema = selectAgentSchema;
|
|
169
167
|
```
|
|
170
168
|
|
|
171
|
-
Note the two-layer shape: `queryListSchema(...)` input is the **partial of the select schema** (optionally merged with extra querystring-only fields); `
|
|
169
|
+
Note the two-layer shape: `queryListSchema(...)` input is the **partial of the select schema** (optionally merged with extra querystring-only fields); `schemas.response` in `getListOperation` stays as the full select schema (`queryListSelectAgentSchema`).
|
|
172
170
|
|
|
173
171
|
### 2. Detail CRUD route
|
|
174
172
|
|
|
@@ -193,10 +191,12 @@ import { seedAgentData } from "@backend/utils/actions";
|
|
|
193
191
|
|
|
194
192
|
export const { POST, GET, DELETE, PUT } = route({
|
|
195
193
|
getAgent: getOperation({
|
|
196
|
-
|
|
197
|
-
|
|
194
|
+
schemas: {
|
|
195
|
+
query: queryAgentSchema,
|
|
196
|
+
response: selectAgentSchema,
|
|
197
|
+
},
|
|
198
198
|
table: agents,
|
|
199
|
-
summary: "助手详情",
|
|
199
|
+
openApiOperation: { summary: "助手详情" },
|
|
200
200
|
setParams: async (req) => {
|
|
201
201
|
const { userId } = (await getSession(req)) || {};
|
|
202
202
|
await seedAgentData(userId as string);
|
|
@@ -204,24 +204,24 @@ export const { POST, GET, DELETE, PUT } = route({
|
|
|
204
204
|
},
|
|
205
205
|
}),
|
|
206
206
|
postAgent: postOperation({
|
|
207
|
-
|
|
207
|
+
schemas: { body: insertAgentSchema },
|
|
208
208
|
table: agents,
|
|
209
|
-
summary: "添加助手",
|
|
209
|
+
openApiOperation: { summary: "添加助手" },
|
|
210
210
|
setBody: async () => ({ tags: ["my"] }),
|
|
211
211
|
}),
|
|
212
212
|
putAgent: putOperation({
|
|
213
|
-
|
|
213
|
+
schemas: { body: updateAgentSchema },
|
|
214
214
|
table: agents,
|
|
215
|
-
summary: "编辑助手",
|
|
215
|
+
openApiOperation: { summary: "编辑助手" },
|
|
216
216
|
}),
|
|
217
217
|
deleteAgent: deleteOperation({
|
|
218
218
|
table: agents,
|
|
219
|
-
summary: "删除助手",
|
|
219
|
+
openApiOperation: { summary: "删除助手" },
|
|
220
220
|
}),
|
|
221
221
|
});
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
-
### 3. List route with relations + `
|
|
224
|
+
### 3. List route with relations + `handler` tree building
|
|
225
225
|
|
|
226
226
|
```ts
|
|
227
227
|
// app/(backend)/api/channel/list/route.ts
|
|
@@ -239,10 +239,12 @@ import { seedChannelData } from "@backend/utils/actions/channel";
|
|
|
239
239
|
|
|
240
240
|
export const { GET } = route({
|
|
241
241
|
getChannelList: getListOperation({
|
|
242
|
-
|
|
243
|
-
|
|
242
|
+
schemas: {
|
|
243
|
+
query: queryListChannelSchema,
|
|
244
|
+
response: queryListSelectChannelSchema,
|
|
245
|
+
},
|
|
244
246
|
table: channels,
|
|
245
|
-
summary: "获取渠道列表",
|
|
247
|
+
openApiOperation: { summary: "获取渠道列表" },
|
|
246
248
|
relations: [
|
|
247
249
|
{
|
|
248
250
|
table: models,
|
|
@@ -261,9 +263,9 @@ export const { GET } = route({
|
|
|
261
263
|
await seedChannelData(userId as string);
|
|
262
264
|
return {};
|
|
263
265
|
},
|
|
264
|
-
|
|
266
|
+
handler: async ({ data }) => {
|
|
265
267
|
// 内部字段过滤 / 映射 / 树化都写在这里
|
|
266
|
-
return
|
|
268
|
+
return data;
|
|
267
269
|
},
|
|
268
270
|
}),
|
|
269
271
|
});
|
|
@@ -328,8 +330,8 @@ export const { GET } = route({
|
|
|
328
330
|
## Built-in Conventions
|
|
329
331
|
|
|
330
332
|
- **Base fields** auto-added by `createTableSchema`: `id` (uuid, pk, default random) / `creatorId` / `editorId` / `accessedAt` / `createdAt` / `updatedAt`.
|
|
331
|
-
- **Ownership check**: with `byCreator: true` (default on PUT/DELETE/GET), PUT/DELETE pre-fetch the row filtered by `creatorId = session.userId` and throw `"未找到…对象,或没有权限"` if missing. Set `byCreator: false` to skip (e.g. admin endpoints).
|
|
332
|
-
- **Auto injection**: POST writes `creatorId = session.userId`; PUT writes `editorId = session.userId`; GET/GET_LIST append `creatorId = session.userId` to the filter when `byCreator` is on.
|
|
333
|
+
- **Ownership check**: with `access: { byCreator: true }` (default on PUT/DELETE/GET), PUT/DELETE pre-fetch the row filtered by `creatorId = session.userId` and throw `"未找到…对象,或没有权限"` if missing. Set `access: { byCreator: false }` to skip (e.g. admin endpoints).
|
|
334
|
+
- **Auto injection**: POST writes `creatorId = session.userId`; PUT writes `editorId = session.userId`; GET/GET_LIST append `creatorId = session.userId` to the filter when `access.byCreator` is on.
|
|
333
335
|
- **Date coercion**: any body field ending in `At` is passed through `dayjs(value).toDate()` before write — accept ISO strings from clients.
|
|
334
336
|
- **List filtering** (all querystring values are strings):
|
|
335
337
|
- `key=a,b,c` → `IN (...)` when key ends with `Id`; otherwise `OR ILIKE %a% OR ILIKE %b%`
|
|
@@ -342,20 +344,20 @@ export const { GET } = route({
|
|
|
342
344
|
## Guardrails
|
|
343
345
|
|
|
344
346
|
- Always pass `insertXxxSchema` to POST, `updateXxxSchema` (has `.required({ id: true })`) to PUT, `selectXxxSchema` to GET body, `queryListSchema(selectXxxSchema.partial())` to GET_LIST query.
|
|
345
|
-
- Query-string schemas stay `z.string()` — numbers / arrays from querystrings must stay strings and be parsed inside `
|
|
347
|
+
- Query-string schemas stay `z.string()` — numbers / arrays from querystrings must stay strings and be parsed inside `handler` or `setParams`.
|
|
346
348
|
- Do not reuse `insertSchema` for PUT — missing `id` silently dispatches an update without a where clause.
|
|
347
|
-
- `
|
|
349
|
+
- `schemas.response` on list/get is the **response item** schema, not the query shape. Query shape goes in `schemas.query`.
|
|
348
350
|
- Do not inline `{ db, getSession }` into individual route files — always import the pre-bound `postOperation` / `getListOperation` / … from `@backend/utils/route-operation` so session/DB swaps are one-file changes.
|
|
349
351
|
- `jsonb` columns declared with `jsonb(...)` carry no zod info — extend the matching insert/select schema with `.merge(z.object({ field: z.array(z.string()).optional() }))` to keep typing accurate.
|
|
350
|
-
- Swallowing errors: an unhandled throw in a handler propagates to next-rest-framework as a 500. Provide `
|
|
352
|
+
- Swallowing errors: an unhandled throw in a handler propagates to next-rest-framework as a 500. Provide `catch` for any operation exposed to untrusted clients.
|
|
351
353
|
- For non-CRUD behavior (middleware chains, streaming, custom `TypedNextResponse` unions, RPC) stop using these factories and switch to the `create-next-rest-framework-api` skill.
|
|
352
354
|
|
|
353
355
|
## Escape Hatches
|
|
354
356
|
|
|
355
357
|
- Extra server-side filters — `setParams(req) => Promise<Record<string, unknown>>` on GET/GET_LIST; returned keys are merged into the filter.
|
|
356
|
-
- Extra columns on write — `setBody(req) => Promise<Partial<infer<IB>>>` on POST/PUT; returned fields are merged into the body before validation runs against `
|
|
357
|
-
- Post-processing output — `
|
|
358
|
-
- Error rewriting — `
|
|
358
|
+
- Extra columns on write — `setBody(req) => Promise<Partial<infer<IB>>>` on POST/PUT; returned fields are merged into the body before validation runs against `schemas.body`.
|
|
359
|
+
- Post-processing output — `handler({ params, data, req }) => Promise<data>`; POST/PUT params are inferred from `schemas.body`, and on GET_LIST `data` is `{ data, total }`.
|
|
360
|
+
- Error rewriting — `catch(err) => Promise<Response | undefined>`. Return `undefined` to rethrow.
|
|
359
361
|
|
|
360
362
|
## Imports
|
|
361
363
|
|
|
@@ -373,6 +375,6 @@ ESM consumers can substitute `/es/backend/...` instead of `/lib/backend/...` whe
|
|
|
373
375
|
- Route files contain zero hand-written `{ db, getSession }` wiring; all operations come from `@backend/utils/route-operation`.
|
|
374
376
|
- Each domain has a `table.ts` (columns + `createTableSchema`) and an `index.ts` (five zod schemas + re-exports).
|
|
375
377
|
- Every list endpoint accepts `current / pageSize / orderBy / orderDir / *AtFrom / *AtTo` and — where applicable — `jsonArrayFields` is declared.
|
|
376
|
-
- `byCreator` is explicitly set when the endpoint is admin-facing or otherwise crosses ownership boundaries.
|
|
378
|
+
- `access.byCreator` is explicitly set when the endpoint is admin-facing or otherwise crosses ownership boundaries.
|
|
377
379
|
- Non-CRUD endpoints are implemented via raw `routeOperation` + action helpers, not by forcing a CRUD factory.
|
|
378
380
|
- `next-rest-framework validate` / `generate` (from the partner skill) still pass — the factory values are plain `RouteOperationDefinition` and participate in OpenAPI generation.
|
package/SKILL.md
CHANGED
|
@@ -66,24 +66,22 @@ export const getListOperation = createGetListOperation({ db, getSession });
|
|
|
66
66
|
|
|
67
67
|
## Factory Catalog
|
|
68
68
|
|
|
69
|
-
| Export | Purpose | Required options
|
|
70
|
-
| ------------------- | -------------------------- |
|
|
71
|
-
| `createTableSchema` | pg `Table` + 5 zod schemas | `name`, `columns`
|
|
72
|
-
| `getOperation` | GET one by filters | `
|
|
73
|
-
| `getListOperation` | GET paginated list | `
|
|
74
|
-
| `postOperation` | POST create | `
|
|
75
|
-
| `putOperation` | PUT update | `
|
|
76
|
-
| `deleteOperation` | DELETE | —
|
|
69
|
+
| Export | Purpose | Required options | Notable optional options |
|
|
70
|
+
| ------------------- | -------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
71
|
+
| `createTableSchema` | pg `Table` + 5 zod schemas | `name`, `columns` | `refineSchema`, `extraConfig` |
|
|
72
|
+
| `getOperation` | GET one by filters | `schemas.query`, `schemas.response` | `table`, `setParams`, `relations`, `jsonArrayFields`, `access.byCreator`, `handler`, `catch`, `openApiOperation` |
|
|
73
|
+
| `getListOperation` | GET paginated list | `schemas.query`, `schemas.response` | `table`, `setParams`, `relations`, `jsonArrayFields`, `handler`, `catch`, `openApiOperation` |
|
|
74
|
+
| `postOperation` | POST create | `schemas.body` | `table`, `contentType`, `parseBody`, `schemas.response`, `setBody`, `handler`, `catch`, `openApiOperation` |
|
|
75
|
+
| `putOperation` | PUT update | `schemas.body` | `table`, `schemas.response`, `setBody`, `access.byCreator`, `handler`, `catch`, `openApiOperation` |
|
|
76
|
+
| `deleteOperation` | DELETE | — | `table`, `schemas.body`, `access.byCreator`, `handler`, `catch`, `openApiOperation` |
|
|
77
77
|
|
|
78
|
-
`
|
|
79
|
-
|
|
80
|
-
`summary` and `description` are forwarded into the OpenAPI operation object.
|
|
78
|
+
`openApiOperation` is spread directly into the `next-rest-framework` operation metadata. Use it for `summary`, `description`, `tags`, `deprecated`, `security`, `externalDocs`, and future OpenAPI operation fields. When omitted, `tags` defaults to the table name when `table` is supplied.
|
|
81
79
|
|
|
82
80
|
`postOperation` defaults to `contentType: "application/json"`. Pass `contentType: "multipart/form-data"` to read `await req.formData()` as body, or pass both a custom `contentType` and `parseBody(req)` for custom payload parsing.
|
|
83
81
|
|
|
84
|
-
In `postOperation` and `putOperation`, `
|
|
82
|
+
In `postOperation` and `putOperation`, `handler({ params })` is typed from `schemas.body` plus the injected `creatorId` / `editorId`, so custom action helpers can usually consume `params` directly without reparsing the body schema.
|
|
85
83
|
|
|
86
|
-
`table` and factory-level `db` are optional for route operations. When `table` is omitted or `db` is not provided, the operation still parses input, applies `setParams` / `setBody`, session-derived fields, and `
|
|
84
|
+
`table` and factory-level `db` are optional for route operations. When `table` is omitted or `db` is not provided, the operation still parses input, applies `setParams` / `setBody`, session-derived fields, and `handler`, but skips the built-in table action (`select` / `insert` / `update` / `delete`). Use this for custom endpoints that want the same OpenAPI and response wiring without a direct table operation.
|
|
87
85
|
|
|
88
86
|
## Canonical Examples
|
|
89
87
|
|
|
@@ -147,7 +145,7 @@ export const queryListAgentSchema = queryListSchema(
|
|
|
147
145
|
export const queryListSelectAgentSchema = selectAgentSchema;
|
|
148
146
|
```
|
|
149
147
|
|
|
150
|
-
Note the two-layer shape: `queryListSchema(...)` input is the **partial of the select schema** (optionally merged with extra querystring-only fields); `
|
|
148
|
+
Note the two-layer shape: `queryListSchema(...)` input is the **partial of the select schema** (optionally merged with extra querystring-only fields); `schemas.response` in `getListOperation` stays as the full select schema (`queryListSelectAgentSchema`).
|
|
151
149
|
|
|
152
150
|
### 2. Detail CRUD route
|
|
153
151
|
|
|
@@ -172,10 +170,12 @@ import { seedAgentData } from "@backend/utils/actions";
|
|
|
172
170
|
|
|
173
171
|
export const { POST, GET, DELETE, PUT } = route({
|
|
174
172
|
getAgent: getOperation({
|
|
175
|
-
|
|
176
|
-
|
|
173
|
+
schemas: {
|
|
174
|
+
query: queryAgentSchema,
|
|
175
|
+
response: selectAgentSchema,
|
|
176
|
+
},
|
|
177
177
|
table: agents,
|
|
178
|
-
summary: "助手详情",
|
|
178
|
+
openApiOperation: { summary: "助手详情" },
|
|
179
179
|
setParams: async (req) => {
|
|
180
180
|
const { userId } = (await getSession(req)) || {};
|
|
181
181
|
await seedAgentData(userId as string);
|
|
@@ -183,24 +183,24 @@ export const { POST, GET, DELETE, PUT } = route({
|
|
|
183
183
|
},
|
|
184
184
|
}),
|
|
185
185
|
postAgent: postOperation({
|
|
186
|
-
|
|
186
|
+
schemas: { body: insertAgentSchema },
|
|
187
187
|
table: agents,
|
|
188
|
-
summary: "添加助手",
|
|
188
|
+
openApiOperation: { summary: "添加助手" },
|
|
189
189
|
setBody: async () => ({ tags: ["my"] }),
|
|
190
190
|
}),
|
|
191
191
|
putAgent: putOperation({
|
|
192
|
-
|
|
192
|
+
schemas: { body: updateAgentSchema },
|
|
193
193
|
table: agents,
|
|
194
|
-
summary: "编辑助手",
|
|
194
|
+
openApiOperation: { summary: "编辑助手" },
|
|
195
195
|
}),
|
|
196
196
|
deleteAgent: deleteOperation({
|
|
197
197
|
table: agents,
|
|
198
|
-
summary: "删除助手",
|
|
198
|
+
openApiOperation: { summary: "删除助手" },
|
|
199
199
|
}),
|
|
200
200
|
});
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
-
### 3. List route with relations + `
|
|
203
|
+
### 3. List route with relations + `handler` tree building
|
|
204
204
|
|
|
205
205
|
```ts
|
|
206
206
|
// app/(backend)/api/channel/list/route.ts
|
|
@@ -218,10 +218,12 @@ import { seedChannelData } from "@backend/utils/actions/channel";
|
|
|
218
218
|
|
|
219
219
|
export const { GET } = route({
|
|
220
220
|
getChannelList: getListOperation({
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
schemas: {
|
|
222
|
+
query: queryListChannelSchema,
|
|
223
|
+
response: queryListSelectChannelSchema,
|
|
224
|
+
},
|
|
223
225
|
table: channels,
|
|
224
|
-
summary: "获取渠道列表",
|
|
226
|
+
openApiOperation: { summary: "获取渠道列表" },
|
|
225
227
|
relations: [
|
|
226
228
|
{
|
|
227
229
|
table: models,
|
|
@@ -240,9 +242,9 @@ export const { GET } = route({
|
|
|
240
242
|
await seedChannelData(userId as string);
|
|
241
243
|
return {};
|
|
242
244
|
},
|
|
243
|
-
|
|
245
|
+
handler: async ({ data }) => {
|
|
244
246
|
// 内部字段过滤 / 映射 / 树化都写在这里
|
|
245
|
-
return
|
|
247
|
+
return data;
|
|
246
248
|
},
|
|
247
249
|
}),
|
|
248
250
|
});
|
|
@@ -307,8 +309,8 @@ export const { GET } = route({
|
|
|
307
309
|
## Built-in Conventions
|
|
308
310
|
|
|
309
311
|
- **Base fields** auto-added by `createTableSchema`: `id` (uuid, pk, default random) / `creatorId` / `editorId` / `accessedAt` / `createdAt` / `updatedAt`.
|
|
310
|
-
- **Ownership check**: with `byCreator: true` (default on PUT/DELETE/GET), PUT/DELETE pre-fetch the row filtered by `creatorId = session.userId` and throw `"未找到…对象,或没有权限"` if missing. Set `byCreator: false` to skip (e.g. admin endpoints).
|
|
311
|
-
- **Auto injection**: POST writes `creatorId = session.userId`; PUT writes `editorId = session.userId`; GET/GET_LIST append `creatorId = session.userId` to the filter when `byCreator` is on.
|
|
312
|
+
- **Ownership check**: with `access: { byCreator: true }` (default on PUT/DELETE/GET), PUT/DELETE pre-fetch the row filtered by `creatorId = session.userId` and throw `"未找到…对象,或没有权限"` if missing. Set `access: { byCreator: false }` to skip (e.g. admin endpoints).
|
|
313
|
+
- **Auto injection**: POST writes `creatorId = session.userId`; PUT writes `editorId = session.userId`; GET/GET_LIST append `creatorId = session.userId` to the filter when `access.byCreator` is on.
|
|
312
314
|
- **Date coercion**: any body field ending in `At` is passed through `dayjs(value).toDate()` before write — accept ISO strings from clients.
|
|
313
315
|
- **List filtering** (all querystring values are strings):
|
|
314
316
|
- `key=a,b,c` → `IN (...)` when key ends with `Id`; otherwise `OR ILIKE %a% OR ILIKE %b%`
|
|
@@ -321,20 +323,20 @@ export const { GET } = route({
|
|
|
321
323
|
## Guardrails
|
|
322
324
|
|
|
323
325
|
- Always pass `insertXxxSchema` to POST, `updateXxxSchema` (has `.required({ id: true })`) to PUT, `selectXxxSchema` to GET body, `queryListSchema(selectXxxSchema.partial())` to GET_LIST query.
|
|
324
|
-
- Query-string schemas stay `z.string()` — numbers / arrays from querystrings must stay strings and be parsed inside `
|
|
326
|
+
- Query-string schemas stay `z.string()` — numbers / arrays from querystrings must stay strings and be parsed inside `handler` or `setParams`.
|
|
325
327
|
- Do not reuse `insertSchema` for PUT — missing `id` silently dispatches an update without a where clause.
|
|
326
|
-
- `
|
|
328
|
+
- `schemas.response` on list/get is the **response item** schema, not the query shape. Query shape goes in `schemas.query`.
|
|
327
329
|
- Do not inline `{ db, getSession }` into individual route files — always import the pre-bound `postOperation` / `getListOperation` / … from `@backend/utils/route-operation` so session/DB swaps are one-file changes.
|
|
328
330
|
- `jsonb` columns declared with `jsonb(...)` carry no zod info — extend the matching insert/select schema with `.merge(z.object({ field: z.array(z.string()).optional() }))` to keep typing accurate.
|
|
329
|
-
- Swallowing errors: an unhandled throw in a handler propagates to next-rest-framework as a 500. Provide `
|
|
331
|
+
- Swallowing errors: an unhandled throw in a handler propagates to next-rest-framework as a 500. Provide `catch` for any operation exposed to untrusted clients.
|
|
330
332
|
- For non-CRUD behavior (middleware chains, streaming, custom `TypedNextResponse` unions, RPC) stop using these factories and switch to the `create-next-rest-framework-api` skill.
|
|
331
333
|
|
|
332
334
|
## Escape Hatches
|
|
333
335
|
|
|
334
336
|
- Extra server-side filters — `setParams(req) => Promise<Record<string, unknown>>` on GET/GET_LIST; returned keys are merged into the filter.
|
|
335
|
-
- Extra columns on write — `setBody(req) => Promise<Partial<infer<IB>>>` on POST/PUT; returned fields are merged into the body before validation runs against `
|
|
336
|
-
- Post-processing output — `
|
|
337
|
-
- Error rewriting — `
|
|
337
|
+
- Extra columns on write — `setBody(req) => Promise<Partial<infer<IB>>>` on POST/PUT; returned fields are merged into the body before validation runs against `schemas.body`.
|
|
338
|
+
- Post-processing output — `handler({ params, data, req }) => Promise<data>`; POST/PUT params are inferred from `schemas.body`, and on GET_LIST `data` is `{ data, total }`.
|
|
339
|
+
- Error rewriting — `catch(err) => Promise<Response | undefined>`. Return `undefined` to rethrow.
|
|
338
340
|
|
|
339
341
|
## Imports
|
|
340
342
|
|
|
@@ -352,6 +354,6 @@ ESM consumers can substitute `/es/backend/...` instead of `/lib/backend/...` whe
|
|
|
352
354
|
- Route files contain zero hand-written `{ db, getSession }` wiring; all operations come from `@backend/utils/route-operation`.
|
|
353
355
|
- Each domain has a `table.ts` (columns + `createTableSchema`) and an `index.ts` (five zod schemas + re-exports).
|
|
354
356
|
- Every list endpoint accepts `current / pageSize / orderBy / orderDir / *AtFrom / *AtTo` and — where applicable — `jsonArrayFields` is declared.
|
|
355
|
-
- `byCreator` is explicitly set when the endpoint is admin-facing or otherwise crosses ownership boundaries.
|
|
357
|
+
- `access.byCreator` is explicitly set when the endpoint is admin-facing or otherwise crosses ownership boundaries.
|
|
356
358
|
- Non-CRUD endpoints are implemented via raw `routeOperation` + action helpers, not by forcing a CRUD factory.
|
|
357
359
|
- `next-rest-framework validate` / `generate` (from the partner skill) still pass — the factory values are plain `RouteOperationDefinition` and participate in OpenAPI generation.
|
|
@@ -23,7 +23,9 @@ declare module "next-rest-framework" {
|
|
|
23
23
|
export const routeOperation: <M extends string>(opts: {
|
|
24
24
|
method: M;
|
|
25
25
|
openApiOperation?: {
|
|
26
|
+
deprecated?: boolean;
|
|
26
27
|
description?: string;
|
|
28
|
+
security?: unknown[];
|
|
27
29
|
summary?: string;
|
|
28
30
|
tags?: string[];
|
|
29
31
|
};
|
|
@@ -1,31 +1,14 @@
|
|
|
1
|
-
import { TypedNextResponse } from "next-rest-framework";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
import { BaseTable } from "../types.js";
|
|
4
1
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
5
2
|
import { NextRequest } from "next/server";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export interface DeleteOperationOptions<TTable extends BaseTable, B extends z.ZodSchema> {
|
|
11
|
-
table?: TTable;
|
|
12
|
-
bodySchema?: B;
|
|
13
|
-
description?: string;
|
|
14
|
-
summary?: string;
|
|
15
|
-
tags?: string[];
|
|
16
|
-
onSuccess?: (payload: {
|
|
17
|
-
params: DeleteOperationParams<B>;
|
|
18
|
-
data: unknown;
|
|
19
|
-
}) => Promise<void>;
|
|
20
|
-
onError?(error: Error): Promise<ReturnType<(typeof TypedNextResponse)["json"]> | undefined>;
|
|
21
|
-
byCreator?: boolean;
|
|
22
|
-
}
|
|
3
|
+
import { ZodSchema } from "zod";
|
|
4
|
+
import { BaseTable } from "../types.js";
|
|
5
|
+
import { type DeleteOperationOptions as CoreDeleteOperationOptions } from "./operation-core.js";
|
|
6
|
+
export type DeleteOperationOptions<TTable extends BaseTable, B extends ZodSchema> = CoreDeleteOperationOptions<TTable, B>;
|
|
23
7
|
export declare const createDeleteOperation: ({ db, getSession, }: {
|
|
24
8
|
db?: NodePgDatabase<Record<string, unknown>> | undefined;
|
|
25
9
|
getSession(req: NextRequest): Promise<{
|
|
26
10
|
userId?: string;
|
|
27
11
|
} | undefined>;
|
|
28
|
-
}) => <
|
|
29
|
-
id:
|
|
30
|
-
},
|
|
31
|
-
export {};
|
|
12
|
+
}) => <B extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>> = import("zod").ZodObject<{
|
|
13
|
+
id: import("zod").ZodString;
|
|
14
|
+
}, import("zod/v4/core").$strip>>({ table, schemas, access, openApiOperation, handler, catch: catchHandler, }: CoreDeleteOperationOptions<BaseTable, B>) => import("next-rest-framework").RouteOperationDefinition<"DELETE">;
|
|
@@ -1,133 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw new Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw new Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw new Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; }
|
|
3
|
-
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
4
|
-
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
5
|
-
import { routeOperation, TypedNextResponse } from "next-rest-framework";
|
|
6
|
-
import { z } from "zod";
|
|
1
|
+
import { createDeleteAction } from "../actions/create-delete-action.js";
|
|
7
2
|
import getTableName from "./get-table-name.js";
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
3
|
+
import { createDeleteOperationFactory } from "./operation-core.js";
|
|
4
|
+
var createOperation = createDeleteOperationFactory({
|
|
5
|
+
createAction: createDeleteAction,
|
|
6
|
+
getTableName: getTableName
|
|
12
7
|
});
|
|
13
8
|
export var createDeleteOperation = function createDeleteOperation(_ref) {
|
|
14
9
|
var db = _ref.db,
|
|
15
10
|
getSession = _ref.getSession;
|
|
16
|
-
return
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
description = _ref2.description,
|
|
21
|
-
summary = _ref2.summary,
|
|
22
|
-
tags = _ref2.tags,
|
|
23
|
-
onSuccess = _ref2.onSuccess,
|
|
24
|
-
onError = _ref2.onError,
|
|
25
|
-
_ref2$byCreator = _ref2.byCreator,
|
|
26
|
-
byCreator = _ref2$byCreator === void 0 ? true : _ref2$byCreator;
|
|
27
|
-
return routeOperation({
|
|
28
|
-
method: "DELETE",
|
|
29
|
-
openApiOperation: {
|
|
30
|
-
description: description,
|
|
31
|
-
summary: summary,
|
|
32
|
-
tags: tags !== null && tags !== void 0 ? tags : table ? [getTableName(table)] : []
|
|
33
|
-
}
|
|
34
|
-
}).input({
|
|
35
|
-
body: bodySchema,
|
|
36
|
-
contentType: "application/json"
|
|
37
|
-
}).handler( /*#__PURE__*/function () {
|
|
38
|
-
var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(req) {
|
|
39
|
-
var body, _ref4, userId, tableParams, data, response;
|
|
40
|
-
return _regeneratorRuntime().wrap(function _callee$(_context) {
|
|
41
|
-
while (1) switch (_context.prev = _context.next) {
|
|
42
|
-
case 0:
|
|
43
|
-
_context.prev = 0;
|
|
44
|
-
_context.t0 = bodySchema;
|
|
45
|
-
_context.next = 4;
|
|
46
|
-
return req.json();
|
|
47
|
-
case 4:
|
|
48
|
-
_context.t1 = _context.sent;
|
|
49
|
-
body = _context.t0.parse.call(_context.t0, _context.t1);
|
|
50
|
-
if (!byCreator) {
|
|
51
|
-
_context.next = 15;
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
_context.next = 9;
|
|
55
|
-
return getSession(req);
|
|
56
|
-
case 9:
|
|
57
|
-
_context.t2 = _context.sent;
|
|
58
|
-
if (_context.t2) {
|
|
59
|
-
_context.next = 12;
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
_context.t2 = {};
|
|
63
|
-
case 12:
|
|
64
|
-
_ref4 = _context.t2;
|
|
65
|
-
userId = _ref4.userId;
|
|
66
|
-
body.creatorId = userId;
|
|
67
|
-
case 15:
|
|
68
|
-
tableParams = body;
|
|
69
|
-
if (!(table && db)) {
|
|
70
|
-
_context.next = 22;
|
|
71
|
-
break;
|
|
72
|
-
}
|
|
73
|
-
_context.next = 19;
|
|
74
|
-
return createDeleteAction({
|
|
75
|
-
table: table,
|
|
76
|
-
db: db
|
|
77
|
-
})(tableParams);
|
|
78
|
-
case 19:
|
|
79
|
-
_context.t3 = _context.sent;
|
|
80
|
-
_context.next = 23;
|
|
81
|
-
break;
|
|
82
|
-
case 22:
|
|
83
|
-
_context.t3 = body;
|
|
84
|
-
case 23:
|
|
85
|
-
data = _context.t3;
|
|
86
|
-
_context.next = 26;
|
|
87
|
-
return onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess({
|
|
88
|
-
data: data,
|
|
89
|
-
params: body
|
|
90
|
-
});
|
|
91
|
-
case 26:
|
|
92
|
-
return _context.abrupt("return", TypedNextResponse.json(data, {
|
|
93
|
-
status: 200
|
|
94
|
-
}));
|
|
95
|
-
case 29:
|
|
96
|
-
_context.prev = 29;
|
|
97
|
-
_context.t4 = _context["catch"](0);
|
|
98
|
-
if (!(_context.t4 instanceof HttpError)) {
|
|
99
|
-
console.error(_context.t4);
|
|
100
|
-
}
|
|
101
|
-
_context.next = 34;
|
|
102
|
-
return onError === null || onError === void 0 ? void 0 : onError(_context.t4);
|
|
103
|
-
case 34:
|
|
104
|
-
response = _context.sent;
|
|
105
|
-
if (!response) {
|
|
106
|
-
_context.next = 37;
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
return _context.abrupt("return", response);
|
|
110
|
-
case 37:
|
|
111
|
-
if (!(_context.t4 instanceof HttpError)) {
|
|
112
|
-
_context.next = 39;
|
|
113
|
-
break;
|
|
114
|
-
}
|
|
115
|
-
return _context.abrupt("return", TypedNextResponse.json({
|
|
116
|
-
message: _context.t4.message
|
|
117
|
-
}, {
|
|
118
|
-
status: _context.t4.status
|
|
119
|
-
}));
|
|
120
|
-
case 39:
|
|
121
|
-
throw _context.t4;
|
|
122
|
-
case 40:
|
|
123
|
-
case "end":
|
|
124
|
-
return _context.stop();
|
|
125
|
-
}
|
|
126
|
-
}, _callee, null, [[0, 29]]);
|
|
127
|
-
}));
|
|
128
|
-
return function (_x) {
|
|
129
|
-
return _ref3.apply(this, arguments);
|
|
130
|
-
};
|
|
131
|
-
}());
|
|
132
|
-
};
|
|
11
|
+
return createOperation({
|
|
12
|
+
db: db,
|
|
13
|
+
getSession: getSession
|
|
14
|
+
});
|
|
133
15
|
};
|
|
@@ -1,38 +1,12 @@
|
|
|
1
|
-
import { z, ZodSchema } from "zod";
|
|
2
|
-
import { NextRequest } from "next/server";
|
|
3
|
-
import { TypedNextRequest, TypedNextResponse } from "next-rest-framework";
|
|
4
1
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
2
|
+
import { NextRequest } from "next/server";
|
|
3
|
+
import { ZodSchema } from "zod";
|
|
5
4
|
import { BaseTable, GetListRelations } from "../types.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
};
|
|
9
|
-
export interface GetListOperationOptions<T extends ZodSchema, Q extends ZodSchema, TTable extends BaseTable> {
|
|
10
|
-
bodySchema: T;
|
|
11
|
-
jsonArrayFields?: string[];
|
|
12
|
-
querySchema: Q;
|
|
13
|
-
relations?: GetListRelations;
|
|
14
|
-
setParams?(req: TypedNextRequest<"GET", "application/json", unknown, z.infer<Q>>): Promise<Record<string, unknown>>;
|
|
15
|
-
byCreator?: boolean;
|
|
16
|
-
description?: string;
|
|
17
|
-
summary?: string;
|
|
18
|
-
tags?: string[];
|
|
19
|
-
table?: TTable;
|
|
20
|
-
onSuccess?<D extends T>(payload: {
|
|
21
|
-
params: GetListOperationParams<Q>;
|
|
22
|
-
data: {
|
|
23
|
-
data: z.infer<D>[];
|
|
24
|
-
total: number;
|
|
25
|
-
};
|
|
26
|
-
}): Promise<{
|
|
27
|
-
data: z.infer<T>[];
|
|
28
|
-
total: number;
|
|
29
|
-
}>;
|
|
30
|
-
onError?(error: Error): Promise<ReturnType<(typeof TypedNextResponse)["json"]> | undefined>;
|
|
31
|
-
}
|
|
5
|
+
import { type GetListOperationOptions as CoreGetListOperationOptions } from "./operation-core.js";
|
|
6
|
+
export type GetListOperationOptions<T extends ZodSchema, Q extends ZodSchema, TTable extends BaseTable> = CoreGetListOperationOptions<T, Q, TTable, GetListRelations>;
|
|
32
7
|
export declare const createGetListOperation: ({ db, getSession, }: {
|
|
33
8
|
db?: NodePgDatabase<Record<string, unknown>> | undefined;
|
|
34
9
|
getSession(req: NextRequest): Promise<{
|
|
35
10
|
userId?: string;
|
|
36
11
|
} | undefined>;
|
|
37
|
-
}) => <T extends
|
|
38
|
-
export {};
|
|
12
|
+
}) => <T extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>, Q extends ZodSchema<unknown, unknown, import("zod/v4/core").$ZodTypeInternals<unknown, unknown>>>({ schemas, access, table, openApiOperation, relations, jsonArrayFields, setParams, handler, catch: catchHandler, }: CoreGetListOperationOptions<T, Q, BaseTable, GetListRelations>) => import("next-rest-framework").RouteOperationDefinition<"GET">;
|