@ai0x0/utils 0.0.47 → 0.0.49

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.
Files changed (74) hide show
  1. package/.agents/skills/ai0x0-utils/backend/SKILL.md +40 -38
  2. package/SKILL.md +39 -37
  3. package/es/__shims__/next-rest-framework.d.ts +2 -0
  4. package/es/backend/route-operation/delete-operation.d.ts +7 -24
  5. package/es/backend/route-operation/delete-operation.js +9 -127
  6. package/es/backend/route-operation/get-list-operation.d.ts +5 -31
  7. package/es/backend/route-operation/get-list-operation.js +10 -142
  8. package/es/backend/route-operation/get-operation.d.ts +5 -25
  9. package/es/backend/route-operation/get-operation.js +10 -139
  10. package/es/backend/route-operation/get-table-name.js +0 -2
  11. package/es/backend/route-operation/index.d.ts +1 -0
  12. package/es/backend/route-operation/index.js +2 -1
  13. package/es/backend/route-operation/open-api-operation.d.ts +6 -0
  14. package/es/backend/route-operation/open-api-operation.js +13 -0
  15. package/es/backend/route-operation/operation-common.d.ts +38 -0
  16. package/es/backend/route-operation/operation-common.js +148 -0
  17. package/es/backend/route-operation/operation-core.d.ts +3 -0
  18. package/es/backend/route-operation/operation-core.js +3 -0
  19. package/es/backend/route-operation/post-operation.d.ts +5 -25
  20. package/es/backend/route-operation/post-operation.js +13 -189
  21. package/es/backend/route-operation/put-operation.d.ts +4 -23
  22. package/es/backend/route-operation/put-operation.js +10 -155
  23. package/es/backend/route-operation/read-operation-core.d.ts +55 -0
  24. package/es/backend/route-operation/read-operation-core.js +224 -0
  25. package/es/backend/route-operation/write-operation-core.d.ts +94 -0
  26. package/es/backend/route-operation/write-operation-core.js +390 -0
  27. package/es/backend/sqlite/route-operation/delete-operation.d.ts +9 -26
  28. package/es/backend/sqlite/route-operation/delete-operation.js +9 -127
  29. package/es/backend/sqlite/route-operation/get-list-operation.d.ts +6 -32
  30. package/es/backend/sqlite/route-operation/get-list-operation.js +10 -142
  31. package/es/backend/sqlite/route-operation/get-operation.d.ts +6 -26
  32. package/es/backend/sqlite/route-operation/get-operation.js +10 -139
  33. package/es/backend/sqlite/route-operation/index.d.ts +1 -0
  34. package/es/backend/sqlite/route-operation/index.js +2 -1
  35. package/es/backend/sqlite/route-operation/post-operation.d.ts +6 -26
  36. package/es/backend/sqlite/route-operation/post-operation.js +13 -189
  37. package/es/backend/sqlite/route-operation/put-operation.d.ts +6 -25
  38. package/es/backend/sqlite/route-operation/put-operation.js +10 -155
  39. package/lib/__shims__/next-rest-framework.d.ts +2 -0
  40. package/lib/backend/route-operation/delete-operation.d.ts +7 -24
  41. package/lib/backend/route-operation/delete-operation.js +6 -56
  42. package/lib/backend/route-operation/get-list-operation.d.ts +5 -31
  43. package/lib/backend/route-operation/get-list-operation.js +7 -72
  44. package/lib/backend/route-operation/get-operation.d.ts +5 -25
  45. package/lib/backend/route-operation/get-operation.js +7 -71
  46. package/lib/backend/route-operation/index.d.ts +1 -0
  47. package/lib/backend/route-operation/index.js +3 -1
  48. package/lib/backend/route-operation/open-api-operation.d.ts +6 -0
  49. package/lib/backend/route-operation/open-api-operation.js +37 -0
  50. package/lib/backend/route-operation/operation-common.d.ts +38 -0
  51. package/lib/backend/route-operation/operation-common.js +90 -0
  52. package/lib/backend/route-operation/operation-core.d.ts +3 -0
  53. package/lib/backend/route-operation/operation-core.js +27 -0
  54. package/lib/backend/route-operation/post-operation.d.ts +5 -25
  55. package/lib/backend/route-operation/post-operation.js +7 -71
  56. package/lib/backend/route-operation/put-operation.d.ts +4 -23
  57. package/lib/backend/route-operation/put-operation.js +7 -68
  58. package/lib/backend/route-operation/read-operation-core.d.ts +55 -0
  59. package/lib/backend/route-operation/read-operation-core.js +151 -0
  60. package/lib/backend/route-operation/write-operation-core.d.ts +94 -0
  61. package/lib/backend/route-operation/write-operation-core.js +181 -0
  62. package/lib/backend/sqlite/route-operation/delete-operation.d.ts +9 -26
  63. package/lib/backend/sqlite/route-operation/delete-operation.js +6 -56
  64. package/lib/backend/sqlite/route-operation/get-list-operation.d.ts +6 -32
  65. package/lib/backend/sqlite/route-operation/get-list-operation.js +7 -75
  66. package/lib/backend/sqlite/route-operation/get-operation.d.ts +6 -26
  67. package/lib/backend/sqlite/route-operation/get-operation.js +7 -71
  68. package/lib/backend/sqlite/route-operation/index.d.ts +1 -0
  69. package/lib/backend/sqlite/route-operation/index.js +3 -1
  70. package/lib/backend/sqlite/route-operation/post-operation.d.ts +6 -26
  71. package/lib/backend/sqlite/route-operation/post-operation.js +7 -71
  72. package/lib/backend/sqlite/route-operation/put-operation.d.ts +6 -25
  73. package/lib/backend/sqlite/route-operation/put-operation.js +7 -68
  74. 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 `onError` or the factory default error handling.
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 | Notable optional options |
91
- | ------------------- | -------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
92
- | `createTableSchema` | pg `Table` + 5 zod schemas | `name`, `columns` | `refineSchema`, `extraConfig` |
93
- | `getOperation` | GET one by filters | `bodySchema`, `querySchema` | `table`, `setParams`, `relations`, `jsonArrayFields`, `byCreator`, `onSuccess`, `onError`, `summary`, `description`, `tags` |
94
- | `getListOperation` | GET paginated list | `bodySchema`, `querySchema` | `table`, `setParams`, `relations`, `jsonArrayFields`, `onSuccess`, `onError`, `summary`, `description`, `tags` |
95
- | `postOperation` | POST create | `bodySchema` | `table`, `contentType`, `parseBody`, `outputBodySchema`, `setBody`, `onSuccess`, `onError`, `summary`, `description`, `tags` |
96
- | `putOperation` | PUT update | `bodySchema` | `table`, `outputBodySchema`, `setBody`, `byCreator`, `onSuccess`, `onError`, `summary`, `description`, `tags` |
97
- | `deleteOperation` | DELETE | — | `table`, `byCreator`, `onSuccess`, `onError`, `summary`, `description`, `tags` |
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
- `tags` can be passed manually to override the default OpenAPI tag inferred from `table`.
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`, `onSuccess({ params })` is typed from `bodySchema` plus the injected `creatorId` / `editorId`, so custom action helpers can usually consume `params` directly without reparsing the body schema.
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 `onSuccess`, 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.
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); `bodySchema` in `getListOperation` stays as the full select schema (`queryListSelectAgentSchema`).
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
- querySchema: queryAgentSchema,
197
- bodySchema: selectAgentSchema,
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
- bodySchema: insertAgentSchema,
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
- bodySchema: updateAgentSchema,
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 + `onSuccess` tree building
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
- querySchema: queryListChannelSchema,
243
- bodySchema: queryListSelectChannelSchema,
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
- onSuccess: async (result) => {
266
+ handler: async ({ data }) => {
265
267
  // 内部字段过滤 / 映射 / 树化都写在这里
266
- return result;
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 `onSuccess` or `setParams`.
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
- - `bodySchema` on list/get is the **response item** schema, not the query shape. Query shape goes in `querySchema`.
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 `onError` for any operation exposed to untrusted clients.
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 `bodySchema`.
357
- - Post-processing output — `onSuccess({ params, data, req }) => Promise<data>`; POST/PUT params are inferred from `bodySchema`, and on GET_LIST `data` is `{ data, total }`.
358
- - Error rewriting — `onError(err) => Promise<Response | undefined>`. Return `undefined` to rethrow.
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 | Notable optional options |
70
- | ------------------- | -------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
71
- | `createTableSchema` | pg `Table` + 5 zod schemas | `name`, `columns` | `refineSchema`, `extraConfig` |
72
- | `getOperation` | GET one by filters | `bodySchema`, `querySchema` | `table`, `setParams`, `relations`, `jsonArrayFields`, `byCreator`, `onSuccess`, `onError`, `summary`, `description`, `tags` |
73
- | `getListOperation` | GET paginated list | `bodySchema`, `querySchema` | `table`, `setParams`, `relations`, `jsonArrayFields`, `onSuccess`, `onError`, `summary`, `description`, `tags` |
74
- | `postOperation` | POST create | `bodySchema` | `table`, `contentType`, `parseBody`, `outputBodySchema`, `setBody`, `onSuccess`, `onError`, `summary`, `description`, `tags` |
75
- | `putOperation` | PUT update | `bodySchema` | `table`, `outputBodySchema`, `setBody`, `byCreator`, `onSuccess`, `onError`, `summary`, `description`, `tags` |
76
- | `deleteOperation` | DELETE | — | `table`, `byCreator`, `onSuccess`, `onError`, `summary`, `description`, `tags` |
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
- `tags` can be passed manually to override the default OpenAPI tag inferred from `table`.
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`, `onSuccess({ params })` is typed from `bodySchema` plus the injected `creatorId` / `editorId`, so custom action helpers can usually consume `params` directly without reparsing the body schema.
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 `onSuccess`, 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.
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); `bodySchema` in `getListOperation` stays as the full select schema (`queryListSelectAgentSchema`).
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
- querySchema: queryAgentSchema,
176
- bodySchema: selectAgentSchema,
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
- bodySchema: insertAgentSchema,
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
- bodySchema: updateAgentSchema,
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 + `onSuccess` tree building
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
- querySchema: queryListChannelSchema,
222
- bodySchema: queryListSelectChannelSchema,
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
- onSuccess: async (result) => {
245
+ handler: async ({ data }) => {
244
246
  // 内部字段过滤 / 映射 / 树化都写在这里
245
- return result;
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 `onSuccess` or `setParams`.
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
- - `bodySchema` on list/get is the **response item** schema, not the query shape. Query shape goes in `querySchema`.
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 `onError` for any operation exposed to untrusted clients.
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 `bodySchema`.
336
- - Post-processing output — `onSuccess({ params, data, req }) => Promise<data>`; POST/PUT params are inferred from `bodySchema`, and on GET_LIST `data` is `{ data, total }`.
337
- - Error rewriting — `onError(err) => Promise<Response | undefined>`. Return `undefined` to rethrow.
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
- type DeleteOperationParams<B extends z.ZodSchema> = z.infer<B> & Record<string, unknown> & {
7
- creatorId?: string;
8
- id: string;
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
- }) => <TTable extends BaseTable, B extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>> = z.ZodObject<{
29
- id: z.ZodString;
30
- }, z.core.$strip>>({ table, bodySchema, description, summary, tags, onSuccess, onError, byCreator, }: DeleteOperationOptions<TTable, B>) => import("next-rest-framework").RouteOperationDefinition<"DELETE">;
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
- function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
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 { createDeleteAction } from "../actions/index.js";
9
- import { HttpError } from "../errors.js";
10
- var defaultDeleteBodySchema = z.object({
11
- id: z.string()
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 function (_ref2) {
17
- var table = _ref2.table,
18
- _ref2$bodySchema = _ref2.bodySchema,
19
- bodySchema = _ref2$bodySchema === void 0 ? defaultDeleteBodySchema : _ref2$bodySchema,
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
- type GetListOperationParams<Q extends ZodSchema> = z.infer<Q> & Record<string, unknown> & {
7
- creatorId?: string;
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 z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, Q extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, TTable extends BaseTable>({ querySchema, bodySchema, table, description, summary, tags, relations, jsonArrayFields, setParams, onSuccess, onError, byCreator, }: GetListOperationOptions<T, Q, TTable>) => import("next-rest-framework").RouteOperationDefinition<"GET">;
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">;