prisma-generator-express 1.57.0 → 1.58.1

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 (42) hide show
  1. package/README.md +111 -29
  2. package/dist/copy/misc.js +25 -6
  3. package/dist/copy/misc.js.map +1 -1
  4. package/dist/generators/generateFastifyHandler.js +11 -0
  5. package/dist/generators/generateFastifyHandler.js.map +1 -1
  6. package/dist/generators/generateHonoHandler.js +14 -20
  7. package/dist/generators/generateHonoHandler.js.map +1 -1
  8. package/dist/generators/generateImportPrismaStatement.js +7 -1
  9. package/dist/generators/generateImportPrismaStatement.js.map +1 -1
  10. package/dist/generators/generateOperationCore.js +58 -17
  11. package/dist/generators/generateOperationCore.js.map +1 -1
  12. package/dist/generators/generateRouteConfigType.js +12 -7
  13. package/dist/generators/generateRouteConfigType.js.map +1 -1
  14. package/dist/generators/generateRouter.js +57 -32
  15. package/dist/generators/generateRouter.js.map +1 -1
  16. package/dist/generators/generateRouterFastify.js +235 -191
  17. package/dist/generators/generateRouterFastify.js.map +1 -1
  18. package/dist/generators/generateRouterHono.d.ts +2 -3
  19. package/dist/generators/generateRouterHono.js +131 -89
  20. package/dist/generators/generateRouterHono.js.map +1 -1
  21. package/dist/index.js +8 -6
  22. package/dist/index.js.map +1 -1
  23. package/package.json +1 -1
  24. package/src/copy/autoIncludeRuntime.ts +9 -5
  25. package/src/copy/buildModelOpenApi.ts +96 -0
  26. package/src/copy/docsRenderer.ts +577 -174
  27. package/src/copy/materializedRouter.ts +40 -1
  28. package/src/copy/misc.ts +23 -6
  29. package/src/copy/operationDefinitions.ts +10 -0
  30. package/src/copy/operationRuntime.ts +28 -9
  31. package/src/copy/routeConfig.express.ts +9 -9
  32. package/src/copy/routeConfig.hono.ts +63 -5
  33. package/src/copy/routeConfig.ts +44 -22
  34. package/src/generators/generateFastifyHandler.ts +12 -0
  35. package/src/generators/generateHonoHandler.ts +15 -20
  36. package/src/generators/generateImportPrismaStatement.ts +10 -1
  37. package/src/generators/generateOperationCore.ts +58 -17
  38. package/src/generators/generateRouteConfigType.ts +13 -8
  39. package/src/generators/generateRouter.ts +57 -32
  40. package/src/generators/generateRouterFastify.ts +235 -191
  41. package/src/generators/generateRouterHono.ts +131 -91
  42. package/src/index.ts +11 -7
package/README.md CHANGED
@@ -41,6 +41,7 @@ Supports **Express**, **Fastify**, and **Hono** targets via the `target` configu
41
41
  - [POST read endpoints](#post-read-endpoints)
42
42
  - [Materialized views router (Express)](#materialized-views-router-express)
43
43
  - [Progressive Endpoint Composition (Express SSE)](#progressive-endpoint-composition-express-sse)
44
+ - [updateEach (Express, Fastify, Hono, internal batch)](#updateeach-express-fastify-hono-internal-batch)
44
45
  - [Response shaping: select, include, omit](#response-shaping-select-include-omit)
45
46
  - [BigInt and Decimal handling](#bigint-and-decimal-handling)
46
47
  - [Pagination](#pagination)
@@ -349,15 +350,14 @@ PrismaClient is injected via `c.set('prisma', prismaInstance)` in middleware tha
349
350
 
350
351
  ### Hooks (Hono)
351
352
 
352
- Hono hooks are native Hono middleware functions:
353
+ Hono route hooks are generated pre/post handler hooks, not native Hono middleware chains. A hook continues by returning `void`. It short-circuits by returning a `Response`, and errors by throwing, including `HTTPException`.
353
354
 
354
355
  ```ts
355
- import type { HonoHookHandler } from './generated/routeConfig.target'
356
+ import type { HonoBeforeHook } from './generated/routeConfig.target'
356
357
 
357
- const auth: HonoHookHandler = async (c, next) => {
358
+ const auth: HonoBeforeHook = async (c) => {
358
359
  const token = c.req.header('authorization')
359
360
  if (!token) return c.json({ message: 'Unauthorized' }, 401)
360
- await next()
361
361
  }
362
362
 
363
363
  const userConfig = {
@@ -367,7 +367,7 @@ const userConfig = {
367
367
  }
368
368
  ```
369
369
 
370
- Call `await next()` to continue the chain. Return a `Response` to short-circuit subsequent hooks, the main handler, and the response middleware will not run.
370
+ `before` hooks run before the generated handler. `after` hooks run after the generated handler. Do not use `await next()` in generated Hono route hooks. Use normal `app.use()` middleware outside the generated router when you need native Hono downstream/after-`next()` behavior.
371
371
 
372
372
  ### HTTPException normalization
373
373
 
@@ -375,13 +375,13 @@ Throwing Hono's `HTTPException` from a hook short-circuits to a JSON error respo
375
375
 
376
376
  ```ts
377
377
  import { HTTPException } from 'hono/http-exception'
378
+ import type { HonoBeforeHook } from './generated/routeConfig.target'
378
379
 
379
- const auth: HonoHookHandler = async (c, next) => {
380
+ const auth: HonoBeforeHook = async (c) => {
380
381
  const token = c.req.header('authorization')
381
382
  if (!token) {
382
383
  throw new HTTPException(401, { message: 'Unauthorized' })
383
384
  }
384
- await next()
385
385
  }
386
386
  ```
387
387
 
@@ -423,7 +423,7 @@ Both `Bindings` (what the runtime injects) and `Variables` (what your middleware
423
423
 
424
424
  ### Query Builder
425
425
 
426
- The Query Builder playground is Node-only and **not auto-started** by the Hono target. The generated `?ui=playground` route can render the playground iframe, but the Hono router does not start the Query Builder server. Start `prisma-query-builder-ui` manually in a separate process and point the config to that server when needed.
426
+ The Query Builder playground is Node-only and **not auto-started** by the Hono target. The generated `?ui=playground` route can render the playground iframe, but the Hono router never starts the Query Builder server, even when query builder config is present. Start `prisma-query-builder-ui` manually in a separate process and point the config to that server when needed.
427
427
 
428
428
  ### Query string differences
429
429
 
@@ -437,8 +437,8 @@ The `encodeQueryParams` client utility does not emit duplicate keys, so this onl
437
437
  | ------ | ------- | ------- | ---- |
438
438
  | Generated function | `ModelRouter(config)` returns `express.Router` | `ModelRoutes(fastify, config)` registers on instance | `ModelRouter(config)` returns `Hono` instance |
439
439
  | Mounting | `app.use('/', ModelRouter(config))` | `fastify.register(async (i) => { await ModelRoutes(i, config) })` | `app.route('/', ModelRouter(config))` |
440
- | Hook types | `RequestHandler[]` | `FastifyHookHandler[]` | `HonoHookHandler[]` (native middleware) |
441
- | Hook signature | `(req, res, next)` | `(request, reply)` | `(c, next)` |
440
+ | Hook types | `RequestHandler[]` | `FastifyHookHandler[]` | `HonoBeforeHook[]` / `HonoAfterHook[]` |
441
+ | Hook signature | `(req, res, next)` | `(request, reply)` | `(c) => Response \| void` |
442
442
  | Guard resolveVariant | `express.Request` | `FastifyRequest` | Hono `Context` |
443
443
  | PrismaClient injection | `req.prisma = prisma` | `request.prisma = prisma` | `c.set('prisma', prisma)` |
444
444
  | Error handling | Express error middleware | `setErrorHandler` | `app.onError` |
@@ -487,10 +487,13 @@ Fastify hooks receive `(request: FastifyRequest, reply: FastifyReply)`. If a hoo
487
487
  ```ts
488
488
  const userConfig = {
489
489
  findMany: {
490
- before: [async (c, next) => { /* auth check */ await next() }],
490
+ before: [async (c) => {
491
+ const token = c.req.header('authorization')
492
+ if (!token) return c.json({ message: 'Unauthorized' }, 401)
493
+ }],
491
494
  },
492
495
  create: {
493
- before: [async (c, next) => { /* auth + validation */ await next() }],
496
+ before: [async (c) => { /* auth + validation */ }],
494
497
  },
495
498
  findUnique: {},
496
499
  }
@@ -498,7 +501,7 @@ const userConfig = {
498
501
  app.route('/', UserRouter(userConfig))
499
502
  ```
500
503
 
501
- Hono hooks are native middleware functions. Call `await next()` to continue the chain. Return a `Response` (e.g. `c.json({...}, 403)`) or throw `HTTPException` to short-circuit — subsequent hooks and the handler will not run.
504
+ Hono route hooks return `void` to continue. Return a `Response` (e.g. `c.json({...}, 403)`) or throw `HTTPException` to short-circuit — subsequent hooks and the handler will not run.
502
505
 
503
506
  Only operations listed in the config (or all when `enableAll: true`) are registered. Operations not listed produce no routes.
504
507
 
@@ -1300,7 +1303,7 @@ All write operations accept the full Prisma args object as the JSON request body
1300
1303
 
1301
1304
  Write operations that return records (`create`, `update`, `delete`, `upsert`, `createManyAndReturn`, `updateManyAndReturn`) support `select`, `include`, and `omit` in the request body to control the response shape. When `writeStrategy = "forceReturn"`, the generated `createMany` and `updateMany` endpoints are rewritten to returning methods and also support `select`, `include`, and `omit`.
1302
1305
 
1303
- For Express, mount `express.json()` before the router so request bodies are parsed. For Hono, malformed JSON bodies are rejected with 400 (`{ "message": "Invalid JSON in request body" }`) before reaching the handler.
1306
+ For Express, mount `express.json()` before the router so request bodies are parsed. For Hono, malformed JSON bodies and non-object bodies are rejected with 400 (`{ "message": "Request body must be a JSON object" }`) before reaching the handler.
1304
1307
 
1305
1308
  ### Bulk operations
1306
1309
 
@@ -1472,7 +1475,8 @@ Supported view options:
1472
1475
  | `schema` | `string` | Optional schema name |
1473
1476
  | `defaultLimit` | `number` | Default page size for this view |
1474
1477
  | `maxLimit` | `number` | Maximum page size for this view |
1475
- | `orderBy` | `string \| object` | Deterministic sort column |
1478
+ | `orderBy` | `string \| object` | Deterministic default sort column |
1479
+ | `allowedOrderBy` | `string[]` | Optional allowlist for client-provided `orderBy` query fields |
1476
1480
  | `authorize` | `function` | Optional per-view authorization hook |
1477
1481
 
1478
1482
  `orderBy` can be a string:
@@ -1562,7 +1566,9 @@ GET /materialized/jobAdStats?take=100&skip=200
1562
1566
 
1563
1567
  `take` is clamped to the configured max limit. `skip` is clamped to zero or greater.
1564
1568
 
1565
- When `skip > 0`, the view must define `orderBy`. This prevents unstable offset pagination.
1569
+ Clients may provide an `orderBy` query parameter. If `allowedOrderBy` is set, client-provided `orderBy` must be one of those fields. If `allowedOrderBy` is omitted, any valid SQL identifier is accepted and quoted.
1570
+
1571
+ When `skip > 0`, the view must define `orderBy` or receive a valid client `orderBy`. This prevents unstable offset pagination.
1566
1572
 
1567
1573
  ```ts
1568
1574
  views: {
@@ -1578,7 +1584,7 @@ views: {
1578
1584
 
1579
1585
  ### Identifier safety
1580
1586
 
1581
- Only registered view names can be queried. Database identifiers such as `schema`, `relation`, and `orderBy.field` must match this pattern:
1587
+ Only registered view names can be queried. Database identifiers such as `schema`, `relation`, `orderBy.field`, and `allowedOrderBy` entries must match this pattern:
1582
1588
 
1583
1589
  ```txt
1584
1590
  ^[A-Za-z_][A-Za-z0-9_]*$
@@ -2141,6 +2147,8 @@ Example deep event sequence:
2141
2147
 
2142
2148
  Auto-include is designed for supported Prisma `include` and relation `select` trees on reads.
2143
2149
 
2150
+ When `findManyPaginatedMode = "transaction"`, the root `findMany` and the total count run inside one interactive transaction, so `data` and `total` are mutually consistent. Relation stages, however, load **after** the transaction commits and are not part of it — relation batches can reflect writes committed between the root transaction and the stage queries.
2151
+
2144
2152
  Supported root operations:
2145
2153
 
2146
2154
  - `findUnique`
@@ -2330,6 +2338,67 @@ The server sends keepalive comments periodically:
2330
2338
 
2331
2339
  If compression middleware is used, configure it to skip `text/event-stream`, or ensure `res.flush()` is available so events are flushed promptly.
2332
2340
 
2341
+ ## updateEach (Express, Fastify, Hono, internal batch)
2342
+
2343
+ `updateEach` applies many independent per-row updates in one request. Each item is passed to Prisma `update`.
2344
+
2345
+ ```http
2346
+ POST /{modelname}/each
2347
+ ```
2348
+
2349
+ Request body is a JSON array of Prisma `update` args. Each item should contain `{ where, data }`:
2350
+
2351
+ ```json
2352
+ [
2353
+ { "where": { "id": "a" }, "data": { "status": "done" } },
2354
+ { "where": { "id": "b" }, "data": { "status": "failed" } }
2355
+ ]
2356
+ ```
2357
+
2358
+ By default each row runs independently and the response is a per-row result array:
2359
+
2360
+ ```json
2361
+ [
2362
+ { "status": "ok", "data": { "id": "a", "status": "done" } },
2363
+ { "status": "error", "error": "Record not found" }
2364
+ ]
2365
+ ```
2366
+
2367
+ Send header `x-batch-atomic: true` to run all rows inside a single interactive transaction instead. In atomic mode any failing row rolls back the whole batch and the request errors; the endpoint requires a Prisma client with transaction support.
2368
+
2369
+ Batch size is capped to protect the database connection pool:
2370
+
2371
+ | Mode | Maximum items | Execution |
2372
+ | ---- | ------------- | --------- |
2373
+ | Non-atomic | 1000 | Bounded worker pool |
2374
+ | Atomic | 100 | Sequential updates inside one transaction |
2375
+
2376
+ Requests above the limit return 400.
2377
+
2378
+ ### Enabling
2379
+
2380
+ `updateEach` is **opt-in only** on Express, Fastify, and Hono. It is **not** enabled by `enableAll: true`. Add it explicitly:
2381
+
2382
+ ```ts
2383
+ app.use('/', UserRouter({
2384
+ updateEach: {
2385
+ before: [requireInternalAuth],
2386
+ },
2387
+ }))
2388
+ ```
2389
+
2390
+ Only `before` and `after` hooks are configurable. It has no `shape`, `pagination`, or progressive config. In development, enabling `updateEach` without a `before` hook may print a warning because this route bypasses guard shapes and should be protected explicitly.
2391
+
2392
+ When enabled, `updateEach` is included in generated OpenAPI output as `POST /{modelname}/each`. It remains excluded from `enableAll: true`.
2393
+
2394
+ ### Guard and security
2395
+
2396
+ `updateEach` does **not** apply prisma-guard shapes on any target, by design. It is intended as a trusted internal batch path, for example worker-to-backend bulk updates, not a public endpoint. Because it bypasses guard, it can write any field the underlying `update` allows.
2397
+
2398
+ Caller resolution still runs before hooks on all three targets, so a `before` hook can read the resolved caller for its own authorization logic. This is separate from guard shapes and does not enable guard enforcement for `updateEach`.
2399
+
2400
+ Protect it yourself with route middleware (`before`) enforcing authentication or network-level restrictions. A guard `variantHeader` such as `x-api-variant` is **not** a security boundary — it only selects a caller value for hooks to read and is trivially spoofable. Do not expose `/each` to untrusted callers.
2401
+
2333
2402
  ## Response shaping: select, include, omit
2334
2403
 
2335
2404
  Read and single-record write operations support three response shaping parameters:
@@ -2354,7 +2423,7 @@ On the client side, `encodeQueryParams` handles BigInt serialization automatical
2354
2423
 
2355
2424
  `findManyPaginated` returns `{ data, total, hasMore }`. Execution is controlled by the schema-wide `findManyPaginatedMode` generator option. The default is `"promiseAll"`, which runs `findMany` and `count` concurrently with `Promise.all`. This is faster but not atomic under concurrent writes. `"transaction"` runs both queries inside an interactive transaction and returns `500` if transaction support is missing.
2356
2425
 
2357
- The `hasMore` field is reliable for forward offset pagination (`skip` + `take`) only. When using cursor-based pagination or negative `take` (backward pagination), `hasMore` may be inaccurate.
2426
+ The `hasMore` field is reliable for forward offset pagination (`skip` + positive `take`) only. When `take` is `0`, `hasMore` is `false`. When using cursor-based pagination or negative `take` (backward pagination), `hasMore` may be inaccurate.
2358
2427
 
2359
2428
  When `distinct` is used with `findManyPaginated`, the total count is determined by executing a distinct query up to the configured limit (default: 100,000 rows). If the number of distinct values exceeds this limit, the total falls back to an approximate non-distinct count. When a guard shape is configured together with `distinct`, the total falls back to a guarded non-distinct count so the internal count query does not need to reuse the public read projection.
2360
2429
 
@@ -2371,7 +2440,7 @@ UserRouter({
2371
2440
  })
2372
2441
  ```
2373
2442
 
2374
- `pagination.defaultLimit` is applied when the client omits `take`. `pagination.maxLimit` caps `take` by absolute value. `pagination.distinctCountLimit` overrides the default 100,000 row threshold for distinct count estimation. All settings apply to `findMany` and `findManyPaginated`.
2443
+ `pagination.defaultLimit` is applied when the client omits `take`. It is not applied when a guard shape controls pagination. `pagination.maxLimit` caps `take` by absolute value even when a guard shape is present. `pagination.distinctCountLimit` overrides the default 100,000 row threshold for distinct count estimation. All settings apply to `findMany` and `findManyPaginated`.
2375
2444
 
2376
2445
  ### Materialized count source
2377
2446
 
@@ -2416,7 +2485,7 @@ UserRouter({
2416
2485
 
2417
2486
  The materialized-view count source is used only when the request has no dynamic `where`, no `distinct`, and no guard shape. If any of those are present, the handler falls back to the normal delegate count so `total` stays consistent with the filtered data.
2418
2487
 
2419
- The materialized count query uses PostgreSQL-style `$N` placeholders and `LIMIT 1`, so it is intended for PostgreSQL and CockroachDB-style clients. The optional `countSource.where` supports flat equality and `null` only. Operators and nested objects are not supported.
2488
+ The materialized count query uses PostgreSQL-style `$N` placeholders and `LIMIT 1`, so it is intended for PostgreSQL and CockroachDB-style clients. The optional `countSource.where` supports flat equality and `null` only. Operators, arrays, and nested objects are rejected at router construction.
2420
2489
 
2421
2490
  Example with a static filter on the count view:
2422
2491
 
@@ -2443,7 +2512,7 @@ All errors are returned as JSON with a `message` field:
2443
2512
  { "message": "Unique constraint violation" }
2444
2513
  ```
2445
2514
 
2446
- Each generated router installs error handling (Express middleware, Fastify `setErrorHandler`, or Hono `app.onError`) that normalizes errors. Prisma error codes are mapped to appropriate HTTP status codes. Guard errors are mapped as follows: `ShapeError` and `CallerError` → 400, `PolicyError` → 403.
2515
+ Each generated router installs error handling (Express middleware, Fastify `setErrorHandler`, or Hono `app.onError`) that normalizes errors. Prisma error codes are mapped to appropriate HTTP status codes. Guard errors are mapped as follows: `ShapeError` and `CallerError` → 400, `PolicyError` → 403. In production, unmapped/internal 500-level errors return a generic `Internal server error` message. Client-error details such as validation or conflict messages may still be included.
2447
2516
 
2448
2517
  For the Hono target, thrown `HTTPException` instances are caught by `app.onError` and converted to `{ "message": err.message }` with the exception's status code. Custom response bodies attached to `HTTPException` are not preserved — see [HTTPException normalization](#httpexception-normalization).
2449
2518
 
@@ -2581,7 +2650,7 @@ type Env = {
2581
2650
  const prisma = new PrismaClient()
2582
2651
 
2583
2652
  const userConfig = {
2584
- findMany: { before: [async (c, next) => { /* auth */ await next() }] },
2653
+ findMany: { before: [async (c) => { /* auth */ }] },
2585
2654
  create: {},
2586
2655
  findUnique: {},
2587
2656
  }
@@ -2623,7 +2692,7 @@ app.get('/docs', generateCombinedDocs({
2623
2692
  | `/docs/{modelname}?ui=yaml` | Raw YAML |
2624
2693
  | `/docs/{modelname}?ui=playground` | Query playground |
2625
2694
 
2626
- The `?ui=playground` endpoint requires `prisma-query-builder-ui`. For Express and Fastify, the builder is auto-started in development. For Hono, the builder must be started manually in a separate process (see [Query Builder](#query-builder)).
2695
+ The `?ui=playground` endpoint requires `prisma-query-builder-ui`. For Express and Fastify, the builder is auto-started in development. For Hono, the router never starts the builder; start it manually in a separate process (see [Query Builder](#query-builder)).
2627
2696
 
2628
2697
  Disable in production via `NODE_ENV=production` or `DISABLE_OPENAPI=true`. Override with `disableOpenApi: false` in config to force-enable.
2629
2698
 
@@ -2745,7 +2814,7 @@ For the Express target, GET read endpoints can also stream SSE events when the r
2745
2814
 
2746
2815
  ## Skipping models
2747
2816
 
2748
- Add `/// generator off` to a model's documentation to skip generation:
2817
+ Add `/// generator off` to a model's documentation to skip generation. The marker must be on its own documentation line:
2749
2818
 
2750
2819
  ```prisma
2751
2820
  /// generator off
@@ -2766,6 +2835,7 @@ generator express {
2766
2835
  target = "express"
2767
2836
  writeStrategy = "regular"
2768
2837
  findManyPaginatedMode = "promiseAll"
2838
+ dropGuard = false
2769
2839
  }
2770
2840
  ```
2771
2841
 
@@ -2774,6 +2844,9 @@ generator express {
2774
2844
  | `target` | `"express"`, `"fastify"`, `"hono"` | `"express"` | Selects the generated router target. |
2775
2845
  | `writeStrategy` | `"regular"`, `"throwOnNonReturning"`, `"forceReturn"` | `"regular"` | Controls only `createMany` and `updateMany`. See [Write strategy](#write-strategy). |
2776
2846
  | `findManyPaginatedMode` | `"promiseAll"`, `"transaction"` | `"promiseAll"` | Controls whether generated `findManyPaginated` handlers run data and count with `Promise.all` or inside an interactive transaction. See [findManyPaginated execution mode](#findmanypaginated-execution-mode). |
2847
+ | `dropGuard` | `true`, `false` | `false` | When `true`, generated routers never pass guard shapes to Prisma. Runtime `E2E=true` also disables guard in emitted routers, even when generator `dropGuard` is `false`. Route-level and operation-level `dropGuard` config fields do not exist. |
2848
+
2849
+ > Route-level and operation-level `dropGuard` config fields were removed because they were never read at runtime. Use generator `dropGuard = true` or `E2E === 'true'`.
2777
2850
 
2778
2851
  ### Express
2779
2852
 
@@ -2843,6 +2916,7 @@ interface RouteConfig<TCtx = unknown> {
2843
2916
  upsert?: OperationConfig
2844
2917
  delete?: OperationConfig
2845
2918
  deleteMany?: OperationConfig
2919
+ updateEach?: UpdateEachConfig
2846
2920
  }
2847
2921
 
2848
2922
  interface OperationConfig {
@@ -2857,6 +2931,11 @@ interface ReadOperationConfig<TCtx = unknown> extends OperationConfig {
2857
2931
  progressiveStages?: Record<string, ProgressiveStage<TCtx>>
2858
2932
  }
2859
2933
 
2934
+ interface UpdateEachConfig {
2935
+ before?: RequestHandler[]
2936
+ after?: RequestHandler[]
2937
+ }
2938
+
2860
2939
  type ManualProgressiveVariantConfig = {
2861
2940
  enabled?: boolean
2862
2941
  mode?: 'manual'
@@ -2938,19 +3017,22 @@ The Hono config is identical except for hook and resolver types:
2938
3017
 
2939
3018
  ```ts
2940
3019
  interface OperationConfig {
2941
- before?: HonoHookHandler[]
2942
- after?: HonoHookHandler[]
3020
+ before?: HonoBeforeHook[]
3021
+ after?: HonoAfterHook[]
2943
3022
  shape?: Record<string, any>
2944
3023
  pagination?: Partial<PaginationConfig>
2945
3024
  }
2946
3025
 
2947
- type HonoHookHandler<Env extends { Variables: any } = any> = (
3026
+ type HonoBeforeHook<Env extends { Variables: any } = any> = (
3027
+ c: Context<Env>,
3028
+ ) => Promise<Response | void> | Response | void
3029
+
3030
+ type HonoAfterHook<Env extends { Variables: any } = any> = (
2948
3031
  c: Context<Env>,
2949
- next: Next,
2950
3032
  ) => Promise<Response | void> | Response | void
2951
3033
  ```
2952
3034
 
2953
- The `guard.resolveVariant` callback receives Hono's `Context`. Hooks are native Hono middleware call `await next()` to continue the chain, return a `Response` (or throw `HTTPException`) to short-circuit.
3035
+ The `guard.resolveVariant` callback receives Hono's `Context`. Hono route hooks return `void` to continue, return a `Response` to short-circuit, or throw `HTTPException` to error. They do not receive `next`. Use normal Hono `app.use()` middleware outside the generated router when native middleware `next()` semantics are required.
2954
3036
 
2955
3037
  The Hono router does not auto-start the Query Builder. Set `queryBuilder: false` to make the playground route return 404, or run `prisma-query-builder-ui` manually for development.
2956
3038
 
package/dist/copy/misc.js CHANGED
@@ -25,16 +25,35 @@ function isSafeKey(key) {
25
25
  }
26
26
  function sanitizeKeys(value) {
27
27
  if (Array.isArray(value)) {
28
- return value.map(sanitizeKeys);
28
+ let mutated = false;
29
+ const out = new Array(value.length);
30
+ for (let i = 0; i < value.length; i++) {
31
+ const sanitized = sanitizeKeys(value[i]);
32
+ if (sanitized !== value[i])
33
+ mutated = true;
34
+ out[i] = sanitized;
35
+ }
36
+ return (mutated ? out : value);
29
37
  }
30
38
  if (isPlainObject(value)) {
31
- const result = {};
32
- for (const key of Object.keys(value)) {
33
- if (!isSafeKey(key))
39
+ const keys = Object.keys(value);
40
+ let hasUnsafe = false;
41
+ let childrenMutated = false;
42
+ const sanitizedChildren = {};
43
+ for (const key of keys) {
44
+ if (!isSafeKey(key)) {
45
+ hasUnsafe = true;
34
46
  continue;
35
- result[key] = sanitizeKeys(value[key]);
47
+ }
48
+ const original = value[key];
49
+ const sanitized = sanitizeKeys(original);
50
+ if (sanitized !== original)
51
+ childrenMutated = true;
52
+ sanitizedChildren[key] = sanitized;
36
53
  }
37
- return result;
54
+ if (!hasUnsafe && !childrenMutated)
55
+ return value;
56
+ return sanitizedChildren;
38
57
  }
39
58
  return value;
40
59
  }
@@ -1 +1 @@
1
- {"version":3,"file":"misc.js","sourceRoot":"","sources":["../../src/copy/misc.ts"],"names":[],"mappings":";;;AAIA,sCAKC;AAID,8BAEC;AAED,oCAaC;AAED,0CAOC;AAED,kDAGC;AAED,wBAIC;AAlDM,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE;IAC3E,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;AAC7E,CAAC,CAAA;AAFY,QAAA,QAAQ,YAEpB;AAED,SAAgB,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC7D,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACtC,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAA;IAC1C,OAAO,KAAK,KAAK,MAAM,CAAC,SAAS,IAAI,KAAK,KAAK,IAAI,CAAA;AACrD,CAAC;AAED,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,aAAa,EAAE,WAAW,CAAC,CAAC,CAAA;AAEtE,SAAgB,SAAS,CAAC,GAAW;IACnC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;AAC9B,CAAC;AAED,SAAgB,YAAY,CAAI,KAAQ;IACtC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAM,CAAA;IACrC,CAAC;IACD,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,MAAM,GAA4B,EAAE,CAAA;QAC1C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACrC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;gBAAE,SAAQ;YAC7B,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAE,KAAiC,CAAC,GAAG,CAAC,CAAC,CAAA;QACrE,CAAC;QACD,OAAO,MAAW,CAAA;IACpB,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,SAAgB,eAAe,CAAC,CAAS;IACvC,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAA;IACjB,IAAI,MAAM,GAAG,CAAC,CAAA;IACd,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,MAAM,GAAG,GAAG,GAAG,MAAM,CAAA;IAClD,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;IAC9E,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,EAAE,CAAA;IAC7B,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAgB,mBAAmB,CAAC,IAAY;IAC9C,IAAI,IAAI,KAAK,GAAG;QAAE,OAAO,EAAE,CAAA;IAC3B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;AACtD,CAAC;AAED,SAAgB,MAAM;IACpB,OAAO,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG;QAClD,CAAC,CAAC,OAAO,CAAC,GAAG;QACb,CAAC,CAAE,EAAyC,CAAA;AAChD,CAAC"}
1
+ {"version":3,"file":"misc.js","sourceRoot":"","sources":["../../src/copy/misc.ts"],"names":[],"mappings":"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"}
@@ -63,6 +63,16 @@ export async function ${exportName}(
63
63
  ext.resultStatus = ${statusCode}
64
64
  }`;
65
65
  }).join('\n');
66
+ const updateEachExportName = `${modelName}UpdateEach`;
67
+ const updateEachHandler = `
68
+ export async function ${updateEachExportName}(
69
+ request: FastifyRequest,
70
+ _reply: FastifyReply,
71
+ ): Promise<void> {
72
+ const atomic = request.headers['x-batch-atomic'] === 'true'
73
+ const data = await core.updateEach(buildContext(request), atomic)
74
+ ;(request as FastifyExtended).resultData = data
75
+ }`;
66
76
  return `import type { FastifyRequest, FastifyReply } from 'fastify'
67
77
  import * as core from './${modelName}Core${ext}'
68
78
  import type { OperationContext, FindManyPaginatedMode } from '../operationRuntime${ext}'
@@ -96,6 +106,7 @@ function buildContext(request: FastifyRequest): OperationContext {
96
106
  }
97
107
  ${readHandlers}
98
108
  ${writeHandlers}
109
+ ${updateEachHandler}
99
110
  `;
100
111
  }
101
112
  //# sourceMappingURL=generateFastifyHandler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"generateFastifyHandler.js","sourceRoot":"","sources":["../../src/generators/generateFastifyHandler.ts"],"names":[],"mappings":";;AA0CA,wDAqEC;AA7GD,kDAA8C;AAE9C,MAAM,aAAa,GAA2B;IAC5C,MAAM,EAAE,cAAc;CACvB,CAAA;AAED,SAAS,UAAU,CAAC,EAAU;IAC5B,OAAO,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,CAAA;AAChC,CAAC;AAED,MAAM,QAAQ,GAAG;IACf,UAAU;IACV,WAAW;IACX,kBAAkB;IAClB,YAAY;IACZ,mBAAmB;IACnB,mBAAmB;IACnB,WAAW;IACX,OAAO;IACP,SAAS;CACV,CAAA;AAED,MAAM,SAAS,GAAG;IAChB,QAAQ;IACR,YAAY;IACZ,qBAAqB;IACrB,QAAQ;IACR,YAAY;IACZ,qBAAqB;IACrB,QAAQ;IACR,QAAQ;IACR,YAAY;CACb,CAAA;AAED,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,QAAQ;IACR,YAAY;IACZ,qBAAqB;CACtB,CAAC,CAAA;AAEF,SAAgB,sBAAsB,CAAC,OAGtC;IACC,MAAM,GAAG,GAAG,IAAA,qBAAS,EAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAA;IAEpC,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACvC,MAAM,UAAU,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5E,OAAO;wBACa,UAAU;;;;4BAIN,UAAU,CAAC,EAAE,CAAC;;EAExC,CAAA;IACA,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACzC,MAAM,UAAU,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5E,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;QAElD,OAAO;wBACa,UAAU;;;;4BAIN,UAAU,CAAC,EAAE,CAAC;;;uBAGnB,UAAU;EAC/B,CAAA;IACA,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,OAAO;2BACkB,SAAS,OAAO,GAAG;mFACqC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6BpF,YAAY;EACZ,aAAa;CACd,CAAA;AACD,CAAC"}
1
+ {"version":3,"file":"generateFastifyHandler.js","sourceRoot":"","sources":["../../src/generators/generateFastifyHandler.ts"],"names":[],"mappings":";;AA0CA,wDAiFC;AAzHD,kDAA8C;AAE9C,MAAM,aAAa,GAA2B;IAC5C,MAAM,EAAE,cAAc;CACvB,CAAA;AAED,SAAS,UAAU,CAAC,EAAU;IAC5B,OAAO,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,CAAA;AAChC,CAAC;AAED,MAAM,QAAQ,GAAG;IACf,UAAU;IACV,WAAW;IACX,kBAAkB;IAClB,YAAY;IACZ,mBAAmB;IACnB,mBAAmB;IACnB,WAAW;IACX,OAAO;IACP,SAAS;CACV,CAAA;AAED,MAAM,SAAS,GAAG;IAChB,QAAQ;IACR,YAAY;IACZ,qBAAqB;IACrB,QAAQ;IACR,YAAY;IACZ,qBAAqB;IACrB,QAAQ;IACR,QAAQ;IACR,YAAY;CACb,CAAA;AAED,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,QAAQ;IACR,YAAY;IACZ,qBAAqB;CACtB,CAAC,CAAA;AAEF,SAAgB,sBAAsB,CAAC,OAGtC;IACC,MAAM,GAAG,GAAG,IAAA,qBAAS,EAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAA;IAEpC,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACvC,MAAM,UAAU,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5E,OAAO;wBACa,UAAU;;;;4BAIN,UAAU,CAAC,EAAE,CAAC;;EAExC,CAAA;IACA,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACzC,MAAM,UAAU,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5E,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;QAElD,OAAO;wBACa,UAAU;;;;4BAIN,UAAU,CAAC,EAAE,CAAC;;;uBAGnB,UAAU;EAC/B,CAAA;IACA,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,oBAAoB,GAAG,GAAG,SAAS,YAAY,CAAA;IACrD,MAAM,iBAAiB,GAAG;wBACJ,oBAAoB;;;;;;;EAO1C,CAAA;IAEA,OAAO;2BACkB,SAAS,OAAO,GAAG;mFACqC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6BpF,YAAY;EACZ,aAAa;EACb,iBAAiB;CAClB,CAAA;AACD,CAAC"}
@@ -41,7 +41,7 @@ function generateHonoHandler(options) {
41
41
  const readHandlers = READ_OPS.map((op) => {
42
42
  const exportName = `${modelName}${op.charAt(0).toUpperCase() + op.slice(1)}`;
43
43
  return `
44
- export async function ${exportName}(c: Context<HonoEnv>): Promise<void> {
44
+ export async function ${exportName}(c: HandlerContext): Promise<void> {
45
45
  const data = await core.${coreFnName(op)}(buildContext(c))
46
46
  c.set('resultData', data)
47
47
  }`;
@@ -50,33 +50,27 @@ export async function ${exportName}(c: Context<HonoEnv>): Promise<void> {
50
50
  const exportName = `${modelName}${op.charAt(0).toUpperCase() + op.slice(1)}`;
51
51
  const statusCode = CREATED_OPS.has(op) ? 201 : 200;
52
52
  return `
53
- export async function ${exportName}(c: Context<HonoEnv>): Promise<void> {
53
+ export async function ${exportName}(c: HandlerContext): Promise<void> {
54
54
  const data = await core.${coreFnName(op)}(buildContext(c))
55
55
  c.set('resultData', data)
56
56
  c.set('resultStatus', ${statusCode})
57
57
  }`;
58
58
  }).join('\n');
59
+ const updateEachExportName = `${modelName}UpdateEach`;
60
+ const updateEachHandler = `
61
+ export async function ${updateEachExportName}(c: HandlerContext): Promise<void> {
62
+ const atomic = c.req.header('x-batch-atomic') === 'true'
63
+ const data = await core.updateEach(buildContext(c), atomic)
64
+ c.set('resultData', data)
65
+ }`;
59
66
  return `import type { Context } from 'hono'
60
67
  import * as core from './${modelName}Core${ext}'
61
- import type { OperationContext, FindManyPaginatedMode } from '../operationRuntime${ext}'
62
-
63
- type HonoVariables = {
64
- prisma: unknown
65
- postgres?: unknown
66
- sqlite?: unknown
67
- parsedQuery?: Record<string, unknown>
68
- body?: unknown
69
- routeConfig?: { pagination?: OperationContext['paginationConfig'] }
70
- guardShape?: Record<string, unknown>
71
- guardCaller?: string
72
- findManyPaginatedMode?: FindManyPaginatedMode
73
- resultData?: unknown
74
- resultStatus?: number
75
- }
68
+ import type { OperationContext } from '../operationRuntime${ext}'
69
+ import type { HonoInternalVariables } from '../routeConfig.target${ext}'
76
70
 
77
- type HonoEnv = { Variables: HonoVariables }
71
+ type HandlerContext = Context<{ Variables: HonoInternalVariables }>
78
72
 
79
- function buildContext(c: Context<HonoEnv>): OperationContext {
73
+ function buildContext(c: HandlerContext): OperationContext {
80
74
  return {
81
75
  prisma: c.get('prisma'),
82
76
  postgres: c.get('postgres'),
@@ -86,11 +80,11 @@ function buildContext(c: Context<HonoEnv>): OperationContext {
86
80
  guardShape: c.get('guardShape'),
87
81
  guardCaller: c.get('guardCaller'),
88
82
  paginationConfig: c.get('routeConfig')?.pagination,
89
- findManyPaginatedMode: c.get('findManyPaginatedMode'),
90
83
  }
91
84
  }
92
85
  ${readHandlers}
93
86
  ${writeHandlers}
87
+ ${updateEachHandler}
94
88
  `;
95
89
  }
96
90
  //# sourceMappingURL=generateHonoHandler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"generateHonoHandler.js","sourceRoot":"","sources":["../../src/generators/generateHonoHandler.ts"],"names":[],"mappings":";;AA0CA,kDAgEC;AAxGD,kDAA8C;AAE9C,MAAM,aAAa,GAA2B;IAC5C,MAAM,EAAE,cAAc;CACvB,CAAA;AAED,SAAS,UAAU,CAAC,EAAU;IAC5B,OAAO,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,CAAA;AAChC,CAAC;AAED,MAAM,QAAQ,GAAG;IACf,UAAU;IACV,WAAW;IACX,kBAAkB;IAClB,YAAY;IACZ,mBAAmB;IACnB,mBAAmB;IACnB,WAAW;IACX,OAAO;IACP,SAAS;CACV,CAAA;AAED,MAAM,SAAS,GAAG;IAChB,QAAQ;IACR,YAAY;IACZ,qBAAqB;IACrB,QAAQ;IACR,YAAY;IACZ,qBAAqB;IACrB,QAAQ;IACR,QAAQ;IACR,YAAY;CACb,CAAA;AAED,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,QAAQ;IACR,YAAY;IACZ,qBAAqB;CACtB,CAAC,CAAA;AAEF,SAAgB,mBAAmB,CAAC,OAGnC;IACC,MAAM,GAAG,GAAG,IAAA,qBAAS,EAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAA;IAEpC,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACvC,MAAM,UAAU,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5E,OAAO;wBACa,UAAU;4BACN,UAAU,CAAC,EAAE,CAAC;;EAExC,CAAA;IACA,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACzC,MAAM,UAAU,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5E,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;QAElD,OAAO;wBACa,UAAU;4BACN,UAAU,CAAC,EAAE,CAAC;;0BAEhB,UAAU;EAClC,CAAA;IACA,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,OAAO;2BACkB,SAAS,OAAO,GAAG;mFACqC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+BpF,YAAY;EACZ,aAAa;CACd,CAAA;AACD,CAAC"}
1
+ {"version":3,"file":"generateHonoHandler.js","sourceRoot":"","sources":["../../src/generators/generateHonoHandler.ts"],"names":[],"mappings":";;AA0CA,kDA2DC;AAnGD,kDAA8C;AAE9C,MAAM,aAAa,GAA2B;IAC5C,MAAM,EAAE,cAAc;CACvB,CAAA;AAED,SAAS,UAAU,CAAC,EAAU;IAC5B,OAAO,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,CAAA;AAChC,CAAC;AAED,MAAM,QAAQ,GAAG;IACf,UAAU;IACV,WAAW;IACX,kBAAkB;IAClB,YAAY;IACZ,mBAAmB;IACnB,mBAAmB;IACnB,WAAW;IACX,OAAO;IACP,SAAS;CACV,CAAA;AAED,MAAM,SAAS,GAAG;IAChB,QAAQ;IACR,YAAY;IACZ,qBAAqB;IACrB,QAAQ;IACR,YAAY;IACZ,qBAAqB;IACrB,QAAQ;IACR,QAAQ;IACR,YAAY;CACb,CAAA;AAED,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC;IAC1B,QAAQ;IACR,YAAY;IACZ,qBAAqB;CACtB,CAAC,CAAA;AAEF,SAAgB,mBAAmB,CAAC,OAGnC;IACC,MAAM,GAAG,GAAG,IAAA,qBAAS,EAAC,OAAO,CAAC,WAAW,CAAC,CAAA;IAC1C,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAA;IAEpC,MAAM,YAAY,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACvC,MAAM,UAAU,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5E,OAAO;wBACa,UAAU;4BACN,UAAU,CAAC,EAAE,CAAC;;EAExC,CAAA;IACA,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;QACzC,MAAM,UAAU,GAAG,GAAG,SAAS,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5E,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;QAElD,OAAO;wBACa,UAAU;4BACN,UAAU,CAAC,EAAE,CAAC;;0BAEhB,UAAU;EAClC,CAAA;IACA,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,MAAM,oBAAoB,GAAG,GAAG,SAAS,YAAY,CAAA;IACrD,MAAM,iBAAiB,GAAG;wBACJ,oBAAoB;;;;EAI1C,CAAA;IAEA,OAAO;2BACkB,SAAS,OAAO,GAAG;4DACc,GAAG;mEACI,GAAG;;;;;;;;;;;;;;;;EAgBpE,YAAY;EACZ,aAAa;EACb,iBAAiB;CAClB,CAAA;AACD,CAAC"}
@@ -89,8 +89,14 @@ function getGuardShapesImport(options, modelName) {
89
89
  if (!outputValue)
90
90
  return null;
91
91
  const shapesFilePath = path_1.default.join(guard.output.value, 'shapes.ts');
92
- if (!fs.existsSync(shapesFilePath))
92
+ if (!fs.existsSync(shapesFilePath)) {
93
+ console.warn('[prisma-generator-express] prisma-guard generator detected but "' +
94
+ shapesFilePath +
95
+ '" was not found. Guard shapes will not be imported for model "' +
96
+ modelName +
97
+ '". Declare the "guard" generator before "express" in schema.prisma and re-run prisma generate.');
93
98
  return null;
99
+ }
94
100
  const fromDir = path_1.default.join(outputValue, modelName);
95
101
  const shapesPath = path_1.default.join(guard.output.value, 'shapes');
96
102
  return getRelativeImportPath(fromDir, shapesPath);
@@ -1 +1 @@
1
- {"version":3,"file":"generateImportPrismaStatement.js","sourceRoot":"","sources":["../../src/generators/generateImportPrismaStatement.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generateImportPrismaStatement.js","sourceRoot":"","sources":["../../src/generators/generateImportPrismaStatement.ts"],"names":[],"mappings":"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"}
@@ -158,7 +158,7 @@ ${paginatedBody}
158
158
  const skip = (typeof query.skip === 'number' ? query.skip : 0)
159
159
  const takeRaw = (typeof query.take === 'number' ? query.take : items.length)
160
160
  const absTake = Math.abs(takeRaw)
161
- const hasMore = items.length >= absTake && skip + items.length < total
161
+ const hasMore = absTake > 0 && items.length >= absTake && skip + items.length < total
162
162
 
163
163
  return { data: items, total, hasMore }
164
164
  }
@@ -167,35 +167,76 @@ export async function updateEach(
167
167
  ctx: OperationContext,
168
168
  atomic: boolean,
169
169
  ): Promise<unknown> {
170
- const body = ctx.body
171
- if (!Array.isArray(body)) {
170
+ const rawBody = ctx.body
171
+ if (!Array.isArray(rawBody)) {
172
172
  throw new HttpError(400, 'updateEach body must be an array of { where, data } items')
173
173
  }
174
- const items = body as Record<string, unknown>[]
175
- const client = ctx.prisma as PrismaClientLike
174
+
175
+ const MAX_ITEMS_NON_ATOMIC = 1000
176
+ const MAX_ITEMS_ATOMIC = 100
177
+
178
+ if (atomic && rawBody.length > MAX_ITEMS_ATOMIC) {
179
+ throw new HttpError(
180
+ 400,
181
+ 'atomic updateEach body exceeds max size of ' + MAX_ITEMS_ATOMIC + ' items',
182
+ )
183
+ }
184
+ if (!atomic && rawBody.length > MAX_ITEMS_NON_ATOMIC) {
185
+ throw new HttpError(
186
+ 400,
187
+ 'updateEach body exceeds max size of ' + MAX_ITEMS_NON_ATOMIC + ' items',
188
+ )
189
+ }
190
+
191
+ const items = rawBody.map((item, index) => {
192
+ const sanitized = validateBody(item)
193
+ if (!('where' in sanitized) || sanitized.where === undefined) {
194
+ throw new HttpError(400, 'updateEach item at index ' + index + ' is missing "where"')
195
+ }
196
+ if (!('data' in sanitized) || sanitized.data === undefined) {
197
+ throw new HttpError(400, 'updateEach item at index ' + index + ' is missing "data"')
198
+ }
199
+ return sanitized
200
+ })
201
+ const extended = await getExtendedClient(ctx)
176
202
 
177
203
  if (atomic) {
178
- if (typeof client.$transaction !== 'function') {
204
+ const txClient = extended as PrismaClientLike
205
+ if (typeof txClient.$transaction !== 'function') {
179
206
  throw new HttpError(500, 'Atomic updateEach requires transaction support on the Prisma client')
180
207
  }
181
- const runInteractive = client.$transaction as unknown as <T>(
208
+ const runInteractive = txClient.$transaction as unknown as <T>(
182
209
  fn: (tx: unknown) => Promise<T>,
183
210
  ) => Promise<T>
184
211
  return runInteractive(async (tx) => {
185
212
  const txDelegate = getDelegate(tx, '${modelNameLower}')
186
- return Promise.all(items.map((item) => txDelegate.update(item)))
213
+ const out: unknown[] = new Array(items.length)
214
+ for (let i = 0; i < items.length; i++) {
215
+ out[i] = await txDelegate.update(items[i])
216
+ }
217
+ return out
187
218
  })
188
219
  }
189
220
 
190
- const delegate = getDelegate(client, '${modelNameLower}')
191
- const settled = await Promise.allSettled(
192
- items.map((item) => delegate.update(item)),
193
- )
194
- return settled.map((result) =>
195
- result.status === 'fulfilled'
196
- ? { status: 'ok', data: result.value }
197
- : { status: 'error', error: mapError(result.reason).message },
198
- )
221
+ const delegate = getDelegate(extended, '${modelNameLower}')
222
+ const CONCURRENCY = 8
223
+ const results: Array<{ status: 'ok'; data: unknown } | { status: 'error'; error: string }> =
224
+ new Array(items.length)
225
+ let cursor = 0
226
+ const workerCount = Math.min(CONCURRENCY, items.length)
227
+ const workers = Array.from({ length: workerCount }, async () => {
228
+ for (;;) {
229
+ const i = cursor++
230
+ if (i >= items.length) return
231
+ try {
232
+ results[i] = { status: 'ok', data: await delegate.update(items[i]) }
233
+ } catch (err) {
234
+ results[i] = { status: 'error', error: mapError(err).message }
235
+ }
236
+ }
237
+ })
238
+ await Promise.all(workers)
239
+ return results
199
240
  }
200
241
  `;
201
242
  }