hono-crud 0.13.12 → 0.13.14

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 (102) hide show
  1. package/CHANGELOG.md +145 -0
  2. package/README.md +11 -10
  3. package/dist/api-version/index.d.ts +11 -67
  4. package/dist/api-version/index.js +1 -1
  5. package/dist/audit/index.d.ts +32 -7
  6. package/dist/audit/index.js +1 -1
  7. package/dist/auth/index.d.ts +573 -5
  8. package/dist/auth/index.js +1 -1
  9. package/dist/builder/index.d.ts +406 -0
  10. package/dist/builder/index.js +1 -0
  11. package/dist/chunk-57MTTJMU.js +1 -0
  12. package/dist/chunk-5P4RVSHT.js +1 -0
  13. package/dist/chunk-6GZBIUE2.js +1 -0
  14. package/dist/chunk-CTOFQ5RC.js +1 -0
  15. package/dist/chunk-CWQSQUV4.js +1 -0
  16. package/dist/chunk-E3MK476S.js +1 -0
  17. package/dist/chunk-H3VBYIDA.js +1 -0
  18. package/dist/chunk-HYXDMJ4K.js +1 -0
  19. package/dist/chunk-JBKQLYEU.js +1 -0
  20. package/dist/chunk-L5CVVJQH.js +1 -0
  21. package/dist/chunk-O62WFEW2.js +1 -0
  22. package/dist/chunk-O6D3QREM.js +11 -0
  23. package/dist/chunk-P7HU2KIE.js +1 -0
  24. package/dist/chunk-SDNXN7M5.js +1 -0
  25. package/dist/chunk-TLI3TRUA.js +1 -0
  26. package/dist/chunk-V7ABUFW5.js +1 -0
  27. package/dist/chunk-WBHWKOTP.js +1 -0
  28. package/dist/chunk-XR6JRDGX.js +1 -0
  29. package/dist/chunk-YB6AVUPQ.js +1 -0
  30. package/dist/cloudflare/index.d.ts +78 -0
  31. package/dist/config/index.d.ts +12 -6
  32. package/dist/config/index.js +1 -1
  33. package/dist/context-keys-9Q1takVn.d.ts +2313 -0
  34. package/dist/context-m0qIRK5d.d.ts +21 -0
  35. package/dist/contracts-C3YJpWrM.d.ts +224 -0
  36. package/dist/{emitter-B6dLhiMF.d.ts → emitter-B8EL76d3.d.ts} +8 -61
  37. package/dist/encryption/index.d.ts +2 -2
  38. package/dist/encryption/index.js +1 -1
  39. package/dist/events/index.d.ts +12 -6
  40. package/dist/events/index.js +1 -1
  41. package/dist/functional/index.d.ts +36 -18
  42. package/dist/functional/index.js +1 -1
  43. package/dist/health/index.d.ts +101 -0
  44. package/dist/health/index.js +1 -0
  45. package/dist/index-s1OVgHp-.d.ts +2072 -0
  46. package/dist/index.d.ts +675 -24
  47. package/dist/index.js +2 -1
  48. package/dist/internal.d.ts +24 -49
  49. package/dist/internal.js +1 -1
  50. package/dist/jwt--gbnOqR-.d.ts +48 -0
  51. package/dist/logging/index.d.ts +34 -16
  52. package/dist/logging/index.js +1 -1
  53. package/dist/memory-B50rK9-O.d.ts +132 -0
  54. package/dist/memory-ttl-store-D2Bbo-do.d.ts +116 -0
  55. package/dist/middleware-CrCo6EgQ.d.ts +104 -0
  56. package/dist/multi-tenant/index.d.ts +42 -22
  57. package/dist/multi-tenant/index.js +1 -1
  58. package/dist/path-match-DGLu6wld.d.ts +22 -0
  59. package/dist/{registry-3p4qTDGZ.d.ts → registry-DYXgzGt0.d.ts} +5 -33
  60. package/dist/request-info-OQ40JnWp.d.ts +24 -0
  61. package/dist/{route-BHXjgMro.d.ts → route-CrJCBJMO.d.ts} +1 -1
  62. package/dist/serialization/index.js +1 -1
  63. package/dist/storage/index.d.ts +143 -68
  64. package/dist/storage/index.js +1 -1
  65. package/dist/{middleware-DBIpdsJ1.d.ts → types-B3XBv6XB.d.ts} +11 -98
  66. package/dist/types-ByPjCs4O.d.ts +103 -0
  67. package/dist/{types-DKPLu-9p.d.ts → types-C2iJttg9.d.ts} +79 -48
  68. package/dist/{types-O7tEXxjA.d.ts → types-C9boymY0.d.ts} +2 -2
  69. package/dist/types-COJwpQfh.d.ts +67 -0
  70. package/dist/types-ChLYHg52.d.ts +55 -0
  71. package/dist/{types-Drjma4gp.d.ts → types-lAPVBoYa.d.ts} +1 -1
  72. package/dist/versioning/index.d.ts +28 -7
  73. package/dist/versioning/index.js +1 -1
  74. package/dist/wait-until-ClcuhBgw.d.ts +12 -0
  75. package/package.json +12 -4
  76. package/dist/chunk-2T3DGA4L.js +0 -1
  77. package/dist/chunk-4KMRJFCY.js +0 -1
  78. package/dist/chunk-5F2VPG4B.js +0 -11
  79. package/dist/chunk-6GO5LUOZ.js +0 -1
  80. package/dist/chunk-6MS7YXSZ.js +0 -1
  81. package/dist/chunk-6XVCICWS.js +0 -1
  82. package/dist/chunk-A7ZLZUMJ.js +0 -1
  83. package/dist/chunk-DALXO46J.js +0 -1
  84. package/dist/chunk-EX4S3Q4M.js +0 -1
  85. package/dist/chunk-H5D4D6Y7.js +0 -1
  86. package/dist/chunk-JEOLCWK3.js +0 -1
  87. package/dist/chunk-JNRSTMFA.js +0 -1
  88. package/dist/chunk-KTDHELSH.js +0 -1
  89. package/dist/chunk-MCXQ77DB.js +0 -1
  90. package/dist/chunk-OCJC5XWY.js +0 -1
  91. package/dist/chunk-PDHKGPGZ.js +0 -1
  92. package/dist/chunk-STAO4FWC.js +0 -1
  93. package/dist/chunk-VESRPXGC.js +0 -1
  94. package/dist/chunk-VJRDAVID.js +0 -1
  95. package/dist/chunk-WKCG5D5U.js +0 -1
  96. package/dist/cloudflare-kv-types-ByUEHhBc.d.ts +0 -30
  97. package/dist/helpers--TefHzCj.d.ts +0 -515
  98. package/dist/index-B1PgVRqD.d.ts +0 -895
  99. package/dist/index-B3hpRLJg.d.ts +0 -681
  100. package/dist/index-Cjm4QRuE.d.ts +0 -4386
  101. package/dist/types/cloudflare.d.ts +0 -64
  102. /package/dist/{types/cloudflare.js → cloudflare/index.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,150 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.13.14
4
+
5
+ ### Patch Changes
6
+
7
+ - 376d8d8: Adapter correctness batch:
8
+
9
+ - **Prisma soft-delete**: `read` and `update` now exclude soft-deleted rows (previously deleted data stayed readable and updatable on Prisma only), and `PrismaUpdateEndpoint` implements `findExisting` — restoring write policies, ETag If-Match, versioning, and audit prior-state capture that silently no-oped.
10
+ - **Prisma wiring parity**: new `getPrismaClient` resolver (`_tx` → class field → `CONTEXT_KEYS.prismaClient` context slot) and `createPrismaCrud(prisma, meta)` factory mirroring the Drizzle adapter; the `prisma` class field is now optional.
11
+ - **Upsert match-and-restore**: upsert/import/batchUpsert now match soft-deleted rows and clear the soft-delete field on update, identically across all three adapters (previously single upsert skipped soft-deleted rows — unique-constraint errors on SQL — while batch upsert overwrote them without restoring). Drizzle native ON CONFLICT paths document their divergence.
12
+ - **BatchDelete** responses go through the full finalize pipeline (computed fields, serialization profile, transform) instead of serializer-only.
13
+ - **like/ilike contract**: one cross-adapter definition — literal substring needle (`%` stripped, `_` inert), `like` follows database collation case behavior, `ilike` always case-insensitive. Drizzle no longer passes live user wildcards into SQL LIKE and no longer emits PostgreSQL-only `ILIKE` on sqlite/mysql.
14
+ - **Workers waitUntil**: logging flush, cache invalidation (`withCacheInvalidation`), api-key `updateLastUsed`, and error-handler hooks now register through `waitUntil` so they survive the response on Cloudflare Workers. `emitAsync` removed (zero call sites). One exported `WaitUntil` type (`WaitUntilFn` removed).
15
+ - **Prisma LIKE wildcard leak**: Prisma's `contains` compiles to SQL LIKE without escaping, so user-supplied `_` acted as a live single-char wildcard in like/ilike filters and search (verified against Postgres). Needles are now escaped via `escapeLikeWildcards`.
16
+ - **Prisma `useTransaction` honored**: Create/Update/Delete now wrap `handle()` in `$transaction` when `useTransaction = true` (mirroring Drizzle) — previously the flag existed but did nothing on single-record verbs, so hooks saw no transaction and an after-hook throw did not roll back the write.
17
+
18
+ - a50d497: Core structure consolidation (internal — public import surface unchanged except three dead exports):
19
+
20
+ - One canonical CRUD route table (`CRUD_ROUTES`, exported via `hono-crud/internal`): all 22 endpoint slots as ordered `[name, verb, subPath]` rows with the registration-order invariants documented in one place. `registerCrud`'s 125-line if-chain is now a loop over it; the OpenAPI paths emitter's private duplicate table is gone; `CrudEndpointName` is derived from the table so it can never drift.
21
+ - Health is now a core subpath: `hono-crud/health` replaces the retired `@hono-crud/health` package (same API; zero deps and zero core coupling made a separate package pure overhead).
22
+ - New `hono-crud/cloudflare` module home (merges the former `types/` and `shared/` single-file directories).
23
+ - "Phase E" finished: auth context accessors live in `auth/context.ts` (also exported from `hono-crud/auth`), the back-compat shim `core/context-helpers.ts` is gone, and context reads use `CONTEXT_KEYS` constants instead of string literals.
24
+ - One canonical helper each: `getClientIp` (the `trustProxy` knob is now honored, library-wide default `true` — edge-first; logging middleware previously discarded `trustProxy: false`), one `PathPattern` (auth/logging/rate-limit re-export it), logging's pure delegation shims deleted.
25
+ - Removed dead exports: `createNullableRegistry`, `createRegistryWithDefault`, `PerTenantOpenApiConfig` (use `OpenAPIConfig`).
26
+
27
+ - e121270: Docs truth sweep — every shipped code sample now typechecks against the real API, plus the type-level fixes that pass surfaced:
28
+
29
+ - `withCache` / `withCacheInvalidation` / `withAuth` now accept abstract base classes and return an extendable constructor type. The documented `class X extends withCache(MemoryReadEndpoint)` pattern previously failed to compile for consumers (adapter endpoint classes are abstract, and the old `TBase & Constructor<...>` return type could not be extended — TS2510). Behavior unchanged; types only (`AbstractConstructor` is exported from `hono-crud/internal`).
30
+ - `PendingActionSchema` is now exported from `hono-crud/auth` — its JSDoc already directed storage-adapter authors to validate rows with it, but it was never re-exported.
31
+ - `PrismaClient` (the structural client constraint) is now exported from `@hono-crud/prisma` so consumers can name the type that `prisma = ...` and `createPrismaCrud(...)` accept.
32
+ - Corrected shipped JSDoc: the `withCache` and `AuthenticatedEndpoint` examples no longer show a `handle(ctx)` override (the registrar injects context; `handle()` is parameterless), and the lifecycle-hook docs no longer claim `fire-and-forget` is the default `afterHookMode` (the default is `sequential`).
33
+
34
+ - 55577a9: BREAKING: export-surface rewrite — the root barrel owns only the CRUD core; every feature family is importable only from its subpath.
35
+
36
+ The rule: each `hono-crud/<feature>` subpath barrel is the complete canonical surface of its feature family; the root `'hono-crud'` barrel owns only the CRUD core (model/meta, router/registrar, endpoint classes + result types, exceptions/error-handler, generic context helpers, core infra utils, OpenAPI utilities) and never exports renamed aliases; `hono-crud/internal` is now an explicit curated list (no `export *`).
37
+
38
+ Evicted from the root barrel (import from the subpath instead):
39
+
40
+ - `hono-crud/auth` — all middleware/guards/storage/validators/JWT-claims plus the auth context accessors (`getUser`, `getUserId`, `hasRole`, …) and types (`AuthEnv`, `JWTClaims`, `ApprovalStorage`, …).
41
+ - `hono-crud/logging` — `createLoggingMiddleware`, storage setters/getters, redact/extract/truncate utilities, `MemoryLoggingStorage`, `getRequestStartTime`, logging types. (`getRequestId`/`generateRequestId` remain on the root as generic context helpers.)
42
+ - `hono-crud/storage` — `createStorageMiddleware` + per-feature storage middlewares, `getStorage(ctx, key)` and resolvers, `StorageRegistry`, `StorageEnv`, storage contracts.
43
+ - `hono-crud/events` — `CrudEventEmitter`, emitter setters/getters, `registerWebhooks`, `CRUD_EVENT_TYPES`, event/webhook types.
44
+ - `hono-crud/serialization` — `applyProfile` family, `SerializationProfile`/`SerializationConfig`.
45
+ - `hono-crud/encryption` — encrypt/decrypt helpers, `StaticKeyProvider`, `encryptedValueSchema`, encryption types.
46
+ - `hono-crud/api-version` — `apiVersion`, `getApiVersion`, `getApiVersionConfig`, `apiVersionedResponse`, versioning-strategy types.
47
+ - `hono-crud/audit` — `AuditLogger`, `MemoryAuditLogStorage`, `createAuditLogger`, audit storage setters/getters, and (newly on the barrel) `getAuditConfig` + `calculateChanges`.
48
+ - `hono-crud/versioning` — `VersionManager`, `MemoryVersioningStorage`, `createVersionManager`, versioning storage setters/getters, and (newly on the barrel) `getVersioningConfig`.
49
+ - `hono-crud/multi-tenant` — `multiTenant`, `TenantEnv`, `MultiTenantMiddlewareConfig`, and (newly on the barrel) `getMultiTenantConfig` + `extractTenantId`.
50
+ - `hono-crud/functional` — `createCreate`/`createList`/`createRead`/`createUpdate`/`createDelete` and their config types.
51
+ - `hono-crud/builder` (new subpath) — `crud` and the `*Builder` classes.
52
+ - `hono-crud/config` — the endpoint-config types (`CreateEndpointConfig`, …, `EndpointsConfig`, `AdapterBundle`, `GeneratedEndpoints`). `defineEndpoints` (the function) stays on the root.
53
+
54
+ Deleted outright (no new home):
55
+
56
+ - The rename aliases `getContextRequestId`, `matchLoggingPath`, `extractLoggingUserId`, `LoggingPathPattern` — use `getRequestId` (root), `matchPath`, `extractUserId`, `PathPattern` (all on `hono-crud/logging`).
57
+ - The 17 `Config*Endpoint` type aliases (`ConfigCreateEndpoint`, …) — use the original `*EndpointConfig` names from `hono-crud/config`.
58
+ - `getHandlerForApp` is no longer on the public barrel (it remains module-level in core).
59
+
60
+ `@hono-crud/memory`: the `getStorage` export alias is removed — use `getStore`.
61
+
62
+ `@hono-crud/mcp`: now imports exclusively from `hono-crud/internal` (no behavior change).
63
+
64
+ The model-meta contract types (`AuditConfig`, `VersioningConfig`, `MultiTenantConfig`, `SoftDeleteConfig`, `AuditLogEntry`, `VersionHistoryEntry`, …) stay on the root barrel — they are fields of the model meta, distinct from the feature runtime.
65
+
66
+ (Published as a patch per this repo's pre-1.0 versioning policy.)
67
+
68
+ - fd3895f: Cursor pagination is now real on all three adapters. Previously core advertised cursor query params and `next_cursor`/`prev_cursor` response fields, but only the memory adapter implemented them — Drizzle and Prisma silently fell back to offset pagination.
69
+
70
+ - **Drizzle**: keyset via `WHERE cursorField > decoded ORDER BY cursorField LIMIT n+1`; **Prisma**: native `cursor` + `skip: 1` + `take: n+1`. All three adapters build the cursor-mode `result_info` envelope through one shared core helper, so the shape is byte-identical.
71
+ - **`prev_cursor` removed** (breaking): cursor walks are next-only (Stripe-style) — SQL keyset "previous" requires a reversed query and was only ever implemented in memory.
72
+ - **`order_by` is forced to the cursor field during cursor walks** (documented on the query param) — previously the three adapters could diverge on sort semantics mid-walk.
73
+ - **No silent degradation**: cursor query params and `next_cursor` only appear in the OpenAPI schema when the endpoint enables cursor pagination AND the adapter supports it; enabling it on an unsupporting adapter throws `ConfigurationException` instead of quietly serving offset pages.
74
+ - List-query logic deduplicated per adapter (`executeDrizzleListQuery` / memory store query helper, mirroring Prisma's existing `executePrismaQuery`) and batch OpenAPI scaffolding shared by the three id-keyed batch verbs.
75
+
76
+ - 08e7e95: Naming & config-shape unification (breaking renames, no aliases).
77
+
78
+ **Model feature enablement idiom.** `Model.audit` and `Model.versioning` are now `boolean | Config` like their `softDelete`/`multiTenant` siblings; the required `enabled` field is removed from `AuditConfig` and `VersioningConfig`. Write `audit: true` / `versioning: true` (or a config object — presence enables); `getAuditConfig`/`getVersioningConfig` and the `AuditLogger`/`VersionManager`/`createAuditLogger`/`createVersionManager` config params accept the union. `NormalizedAuditConfig`/`NormalizedVersioningConfig` still carry `enabled`. `fieldEncryption` stays presence-enabled (it has required members).
79
+
80
+ **Path-filter and message vocabulary.** `excludePaths` is the one name for "paths this middleware bypasses": `AuthConfig.skipPaths` → `excludePaths`, `RateLimitConfig.skipPaths` → `excludePaths`, and mcp `AutoOptions.include`/`exclude` → `includePaths`/`excludePaths` (they match `registerCrud` mount paths with the same shared matcher). `AuthConfig.unauthorizedMessage` → `errorMessage`, matching rate-limit and multiTenant.
81
+
82
+ **Duration unit suffixes (renames only — no field changed its unit).**
83
+
84
+ | Old | New | Unit |
85
+ | ------------------------------------------------------------------------ | -------------------------------- | ------- |
86
+ | `CacheConfig.ttl` | `ttlSeconds` | seconds |
87
+ | `IdempotencyConfig.ttl` | `ttlSeconds` | seconds |
88
+ | `IdempotencyConfig.lockTimeout` | `lockTimeoutSeconds` | seconds |
89
+ | `JWTConfig.clockTolerance` / `JWTClaimsValidationOptions.clockTolerance` | `clockToleranceSeconds` | seconds |
90
+ | `SubscribeEndpointConfig.heartbeatInterval` | `heartbeatIntervalMs` | ms |
91
+ | `SubscribeEndpointConfig.connectionTimeout` | `connectionTimeoutMs` | ms |
92
+ | `HealthCheck.timeout` | `timeoutMs` | ms |
93
+ | `HealthConfig.defaultTimeout` | `defaultTimeoutMs` | ms |
94
+ | `WebhookEndpoint.timeout` | `timeoutMs` | ms |
95
+ | `MemoryTtlStoreOptions.cleanupInterval` | `cleanupIntervalMs` | ms |
96
+ | `MemoryLoggingStorageOptions.maxAge` / `.cleanupInterval` | `maxAgeMs` / `cleanupIntervalMs` | ms |
97
+ | `MemoryIdempotencyStorageOptions.cleanupInterval` | `cleanupIntervalMs` | ms |
98
+ | `MemoryRateLimitStorageOptions.cleanupInterval` | `cleanupIntervalMs` | ms |
99
+
100
+ Documented exception: `RateLimitResult.retryAfter` keeps its name (mirrors the HTTP Retry-After header, seconds by RFC).
101
+
102
+ **Env types.** `TenantEnv` now types its tenant variable optional (`string | undefined` until the middleware runs), matching every other `*Env`. `HonoCrudEnv` now folds in `TenantEnv & ApiVersionEnv`, making its "all core context variables" claim true.
103
+
104
+ **Rate-limit extractor nullability.** `extractUserId` and `extractAPIKey` return `string | undefined` (was `string | null`); `KeyExtractor` is `(ctx) => string | undefined` — custom extractors returning `null` must return `undefined`. `extractIP` keeps its `'unknown'` fail-closed sentinel: a falsy key would skip rate limiting entirely, and limits must not fail open when no IP is derivable.
105
+
106
+ - 90ef0da: Storage & middleware-family unification:
107
+
108
+ - **RedisIdempotencyStorage** (`@hono-crud/idempotency`): production idempotency backend whose lock acquisition is ONE atomic `SET key value NX PX ttl` round-trip; compatible with `@upstash/redis` (edge-safe) out of the box. Deliberately no Cloudflare KV backend — KV lacks compare-and-swap, so a KV lock would be advisory only (documented in the package README and the `IdempotencyStorage.lock` contract).
109
+ - **Breaking — idempotency error shape**: the middleware now throws `IdempotencyKeyRequiredException` (400 `IDEMPOTENCY_KEY_REQUIRED`) and `IdempotencyConflictException` (409 `IDEMPOTENCY_CONFLICT`) instead of hand-returning `ctx.json` envelopes, so idempotency errors flow through `createErrorHandler` (ErrorMappers / ErrorHooks / custom `responseEnvelope` / requestId injection) like every sibling middleware. Bodies are unchanged under the default envelope on bare Hono apps; with `createErrorHandler` they now honor your envelope and gain `error.requestId`.
110
+ - **Breaking — Prisma model-mapping registry removed**: `registerPrismaModelMapping` / `registerPrismaModelMappings` / `clearPrismaModelMappings` (module-global mutable state, per-isolate on Workers) are deleted. Set the delegate name statically instead: `defineModel({ tableName: 'people', table: 'person', ... })` or `RelationConfig.table` for relations; the camelCase+singularize derivation remains the fallback.
111
+ - **Missing-storage posture unified**: cache mixins and the idempotency middleware now log a once-per-isolate warning when no storage resolves (rate-limit's existing warning gained the same once-guard); idempotency with `required: true` and no storage throws `ConfigurationException` instead of silently voiding replay protection.
112
+ - **Cache default removed**: `getCacheStorage()` no longer lazily installs a global `MemoryCacheStorage` on read (`lazyDefaultOnGet` retired for cache) — it returns honest `null` until storage is configured. Docs no longer claim a memory default exists.
113
+ - **Approval storage joins the unified injection system**: `CONTEXT_KEYS.approvalStorage` slot, `createStorageMiddleware({ approvalStorage })` / `createApprovalStorageMiddleware()`, and the `setApprovalStorage` / `getApprovalStorage` / `getApprovalStorageRequired` / `resolveApprovalStorage` quartet on `hono-crud/auth`. `requireApproval` resolves storage per request (explicit > context > global > warned in-memory default). **Breaking**: `ApprovalConfig.approvalStorage` renamed to `storage` (matching every sibling config).
114
+ - **Quartet uniformity**: every storage feature now exports its full set/get/getRequired/resolve quartet plus registry from its package/subpath barrel (`resolveRateLimitStorage`, `resolveCacheStorage`, `cacheStorageRegistry`, `rateLimitStorageRegistry`, `idempotencyStorageRegistry`, `eventEmitterRegistry`, `getAPIKeyStorageRequired`, `resolveAPIKeyStorage`, logging's Required/resolve pair on `hono-crud/logging`, …).
115
+ - **ConfigurationException sweep**: request-time misconfiguration (audit/versioning manager without storage, `getDrizzleDb` / `getPrismaClient` resolution, `resetRateLimit`, Prisma `$transaction` capability check, `StorageRegistry.getRequired` / `resolveRequired`) now throws `ConfigurationException` (500 `CONFIGURATION_ERROR`) instead of plain `Error`.
116
+ - `MemoryCacheStorageOptions` and `MemoryIdempotencyStorageOptions` are exported; docs now lead every storage-backed feature with `createStorageMiddleware` (the in-code recommended path) and present `set*Storage` as the long-lived-server option.
117
+
118
+ - 7a7808d: Verb & sugar-surface parity batch:
119
+
120
+ - **Config API verb parity**: `defineEndpoints` gains the 5 missing verbs — `bulkPatch`, `versionHistory`, `versionRead`, `versionCompare`, `versionRollback` — completing all 22 `registerCrud` slots. `AdapterBundle` gains the matching optional slots, and all three first-party bundles (Memory/Drizzle/Prisma) now fill every slot. `GeneratedEndpoints` is derived as a `Pick` over `CrudEndpoints`, so the two types can never drift again.
121
+ - **Loud config failure (BREAKING)**: configuring a verb whose adapter bundle lacks the matching base class now throws a plain `Error` at definition time instead of silently skipping the route. Correct configurations are unaffected.
122
+ - **BulkPatch on every adapter**: new `DrizzleBulkPatchEndpoint` (single `UPDATE ... WHERE` + RETURNING) and `PrismaBulkPatchEndpoint` (`updateMany`; count-only, `returnRecords` unsupported). `MemoryBulkPatchEndpoint` now respects soft-delete visibility (soft-deleted rows are never patched) and bumps managed `updatedAt` — previously it patched deleted rows and skipped the timestamp bump. Core `BulkPatchEndpoint` ships a default `getUpdateSchema()` (model schema minus managed fields, partial) so it works from the config API without subclassing. New conformance cell pins the contract on all three adapters.
123
+ - **OpenAPI schema overrides now actually merge (fix)**: user-supplied `responses`/`request`/`security`/`operationId` previously type-checked but were clobbered by the generated blocks on every surface. A shared `mergeRouteSchema` seam (exported from `hono-crud`) now merges user blocks over the generated schema in every endpoint `getSchema()`. Config `openapi` widened to `Partial<OpenAPIRouteSchema>` and the builder gains `.openapi(schema)`.
124
+ - **`searchFieldName` → `searchParamName` (BREAKING)**: the list-endpoint inline-search query-param knob is renamed everywhere (`ListEndpoint`, `ListFilterParseOptions`, functional `ListConfig`, `NormalizedEndpointConfig`); it names a query parameter, not a model field. Builder `.searchParam()` and config `search.paramName` spellings are unchanged. The default divergence is deliberate and now documented: inline list search defaults to `'search'`, the dedicated `/search` route to `'q'`.
125
+ - **Builder alias removal (BREAKING)**: pre-1.0 back-compat aliases `orderBy()` and `defaultOrder()` are deleted — `.sortable()` / `.defaultSort()` are the only spellings. The builder also no longer hardcodes its own copy of factory defaults (20/100/'search'/'id'/'sequential'/false); unset knobs pass `undefined` through so `generateEndpointClass` is the single source of defaults (behavior identical).
126
+ - **`bodySchema` parity**: functional `CreateConfig`/`UpdateConfig` gain `bodySchema` and the Create/Update builders gain `.bodySchema(schema)`, matching the class and config APIs. Fixed a factory bug where the generated `getBodySchema` override crashed body-schema-less verbs.
127
+ - **Honest hook typing**: the core create/update/delete trio across functional/builder/config now types the hook context as the exported `HookContext` (previously the actively-wrong `tx?: unknown`) and reuses the exported `AfterUpdateHook`/`AfterDeleteHook` aliases. Extended-verb config hook bags are retyped to what is actually passed (upsert gets `isCreate: boolean`, batch-create gets per-item `(item, index)`, batch-update gets `(id, data)`, import gets `(row, rowNumber, mode, tx?)`, clone's `before` gets the prepared clone payload; phantom `tx` params that were never passed are dropped). Previously-dead config hooks now fire: `search.hooks.after` is wired to `afterSearch`, `batchUpsert.hooks.before/after` to `beforeBatch`/`afterBatch`, and `AggregateEndpoint` gains an `after(result)` lifecycle hook so `aggregate.hooks.after` works.
128
+ - **Env-generic config middlewares**: `EndpointsConfig<M, E>` threads `E` so per-endpoint `middlewares` are `MiddlewareHandler<E>[]`, matching the functional and builder surfaces.
129
+
130
+ ## 0.13.13
131
+
132
+ ### Patch Changes
133
+
134
+ - 66f789c: Naming + docs sweep (breaking, patch): one name per role across the whole library, and the docs now document the code that exists.
135
+
136
+ - Middleware factories: `idempotency()` → `createIdempotencyMiddleware()`. (`multiTenant()`/`apiVersion()` keep their feature-named forms — each anchors an accessor family sharing its prefix; the doctrine in CLAUDE.md documents the rule and the exceptions.)
137
+ - Surface packages are Hono-idiomatic factories you mount yourself: swagger's `setupSwaggerUI`/`setupReDoc`/`setupDocsIndex` (app mutators) become `swaggerUI()`/`redocUI()`/`docsIndex()` returning `MiddlewareHandler` with `SwaggerUIConfig`/`RedocUIConfig`/`DocsIndexConfig` (adopting scalar's `specUrl`/`pageTitle` vocabulary; `UIOptions` deleted); scalar's `setupScalar` is deleted (`app.get(path, scalarUI(config))`); health's `createHealthEndpoints`/`createHealthHandler` collapse into `createHealthRoutes(config): Hono` mounted via `app.route()`.
138
+ - The word "Versioning" now means record history only: HTTP negotiation renames to `ApiVersioningConfig` (was `VersioningMiddlewareConfig`), `ApiVersionStrategy`, `ApiVersionTransformer`, `apiVersionedResponse()`.
139
+ - Multi-tenant has one source-of-truth union: new exported `TenantIdSource` owned by the model-level `MultiTenantConfig`; the middleware config derives from it and renames to `MultiTenantMiddlewareConfig` (was `*Options`). Runtime defaults unchanged.
140
+ - `CrudMcpOptions` → `CrudMcpConfig` per the now-documented Config-vs-Options rule.
141
+ - Rate-limit key prefix unified: one exported `DEFAULT_RATE_LIMIT_KEY_PREFIX = 'rl'`; the KV and Redis storage prefixes no longer add their own divergent defaults ('rl:'/'ratelimit:'), so all backends build identical keys. In-flight rate-limit windows under old prefixes expire naturally.
142
+ - Vestigial `RouterOptions.base`/`docs_url`/`redoc_url` deleted (UI paths live in the swagger/scalar packages; `openapi_url` stays).
143
+ - One API-key hasher: the canonical `hashAPIKey` moves to `auth/hash.ts` and `defaultHashAPIKey` is now an alias of it — hashing is byte-identical, stored keys keep matching.
144
+ - Sorting aliases removed from the functional config: `orderByFields`/`defaultOrderBy`/`defaultOrderDirection` and `SortingConfig.defaultDirection` are gone — canonical `sortFields`/`defaultSort`/`defaultOrder` only.
145
+ - Drizzle's stale 5-verb `DrizzleAdapters` bundle in factory.ts is deleted; the 17-entry bundle in the package barrel is the only one, mirroring prisma and memory.
146
+ - Docs: CLAUDE.md/AGENTS.md gain the naming doctrine and a corrected Drizzle Adapter Pattern section using the real type names (`DrizzleDatabaseConstraint`, `Database<Row>`, `QueryBuilder<Row>`, `cast<Row>()`); all docs/READMEs/examples migrated to the new names and call patterns.
147
+
3
148
  ## 0.13.12
4
149
 
5
150
  ### Patch Changes
package/README.md CHANGED
@@ -29,20 +29,21 @@ You will also want a storage adapter, e.g. `@hono-crud/memory`, `@hono-crud/driz
29
29
  ## Usage
30
30
 
31
31
  ```ts
32
+ import { Hono } from 'hono';
33
+ import { defineMeta, defineModel, fromHono, registerCrud } from 'hono-crud';
34
+ import { MemoryCreateEndpoint, MemoryListEndpoint, MemoryReadEndpoint } from '@hono-crud/memory';
32
35
  import { z } from 'zod';
33
- import { fromHono, registerCrud, defineModel, defineMeta } from 'hono-crud';
34
- import { MemoryCreateEndpoint, MemoryReadEndpoint, MemoryListEndpoint } from '@hono-crud/memory';
35
36
 
36
- const UserSchema = z.object({ id: z.string(), name: z.string() });
37
- const model = defineModel({ schema: UserSchema, primaryKey: 'id' });
38
- const meta = defineMeta({ tableName: 'users' });
37
+ const UserSchema = z.object({ id: z.uuid(), name: z.string() });
38
+ const UserModel = defineModel({ tableName: 'users', schema: UserSchema, primaryKeys: ['id'] });
39
+ const userMeta = defineMeta({ model: UserModel });
40
+
41
+ class UserCreate extends MemoryCreateEndpoint { _meta = userMeta; }
42
+ class UserRead extends MemoryReadEndpoint { _meta = userMeta; }
43
+ class UserList extends MemoryListEndpoint { _meta = userMeta; }
39
44
 
40
45
  const app = fromHono(new Hono());
41
- registerCrud(app, '/users', {
42
- model,
43
- meta,
44
- endpoints: { create: MemoryCreateEndpoint, read: MemoryReadEndpoint, list: MemoryListEndpoint },
45
- });
46
+ registerCrud(app, '/users', { create: UserCreate, read: UserRead, list: UserList });
46
47
  ```
47
48
 
48
49
  See the [repository README](https://github.com/kshdotdev/hono-crud) for the full guide.
@@ -1,64 +1,6 @@
1
- import { MiddlewareHandler, Env, Context } from 'hono';
2
-
3
- /**
4
- * Strategy for extracting the API version from requests.
5
- */
6
- type VersionStrategy = 'url' | 'header' | 'query';
7
- /**
8
- * A version transformer function that converts request/response data
9
- * between API versions.
10
- */
11
- type VersionTransformer = (data: Record<string, unknown>) => Record<string, unknown>;
12
- /**
13
- * Configuration for a single API version.
14
- */
15
- interface ApiVersionConfig {
16
- /** Version identifier (e.g. '1', '2', '2024-01-15') */
17
- version: string;
18
- /** Optional middleware to apply for this version */
19
- middleware?: MiddlewareHandler[];
20
- /** Transform incoming request body from this version to latest */
21
- requestTransformer?: VersionTransformer;
22
- /** Transform outgoing response data from latest to this version */
23
- responseTransformer?: VersionTransformer;
24
- /** ISO date string when this version was deprecated */
25
- deprecated?: string;
26
- /** ISO date string when this version will be removed */
27
- sunset?: string;
28
- }
29
- /**
30
- * Configuration for the API versioning middleware.
31
- */
32
- interface VersioningMiddlewareConfig {
33
- /** Available API versions. First is treated as default if no defaultVersion specified. */
34
- versions: ApiVersionConfig[];
35
- /** Default version when none is specified by client */
36
- defaultVersion?: string;
37
- /** Version extraction strategy. @default 'header' */
38
- strategy?: VersionStrategy;
39
- /** Header name for header strategy. @default 'Accept-Version' */
40
- headerName?: string;
41
- /** Query parameter name for query strategy. @default 'version' */
42
- queryParam?: string;
43
- /** URL prefix pattern for URL strategy (e.g. '/v{version}'). @default '/v{version}' */
44
- urlPattern?: string;
45
- /** Custom version extractor (overrides strategy) */
46
- extractVersion?: (ctx: Context) => string | undefined;
47
- /** Whether to add version headers to responses. @default true */
48
- addHeaders?: boolean;
49
- }
50
- /**
51
- * Environment type additions for API versioning.
52
- *
53
- * Variables are typed optional because they are only set after the api-version
54
- * middleware has run. Use `extends ApiVersionEnv` on your app's `Env` to opt in.
55
- */
56
- interface ApiVersionEnv extends Env {
57
- Variables: {
58
- apiVersion?: string;
59
- apiVersionConfig?: ApiVersionConfig;
60
- };
61
- }
1
+ import { MiddlewareHandler, Context } from 'hono';
2
+ import { a as ApiVersioningConfig, b as ApiVersionConfig } from '../types-COJwpQfh.js';
3
+ export { A as ApiVersionEnv, c as ApiVersionStrategy, d as ApiVersionTransformer } from '../types-COJwpQfh.js';
62
4
 
63
5
  /**
64
6
  * Create API versioning middleware.
@@ -66,7 +8,7 @@ interface ApiVersionEnv extends Env {
66
8
  * @example
67
9
  * ```ts
68
10
  * import { Hono } from 'hono';
69
- * import { apiVersion } from 'hono-crud';
11
+ * import { apiVersion } from 'hono-crud/api-version';
70
12
  *
71
13
  * const app = new Hono();
72
14
  *
@@ -89,7 +31,7 @@ interface ApiVersionEnv extends Env {
89
31
  * }));
90
32
  * ```
91
33
  */
92
- declare function apiVersion(config: VersioningMiddlewareConfig): MiddlewareHandler;
34
+ declare function apiVersion(config: ApiVersioningConfig): MiddlewareHandler;
93
35
  /**
94
36
  * Get the current API version from context.
95
37
  */
@@ -100,15 +42,17 @@ declare function getApiVersion(ctx: Context): string | undefined;
100
42
  declare function getApiVersionConfig(ctx: Context): ApiVersionConfig | undefined;
101
43
  /**
102
44
  * Middleware that transforms response JSON using the active version's responseTransformer.
103
- * Apply AFTER your route handlers.
45
+ * Register it BEFORE your route handlers (and after apiVersion()) — like all Hono
46
+ * middleware it wraps the handler via `await next()` and rewrites the response on the
47
+ * way out. Middleware registered after a response-producing handler never runs.
104
48
  *
105
49
  * @example
106
50
  * ```ts
107
51
  * app.use('*', apiVersion({ ... }));
52
+ * app.use('*', apiVersionedResponse());
108
53
  * app.get('/users/:id', handler);
109
- * app.use('*', versionedResponse());
110
54
  * ```
111
55
  */
112
- declare function versionedResponse(): MiddlewareHandler;
56
+ declare function apiVersionedResponse(): MiddlewareHandler;
113
57
 
114
- export { type ApiVersionConfig, type ApiVersionEnv, type VersionStrategy, type VersionTransformer, type VersioningMiddlewareConfig, apiVersion, getApiVersion, getApiVersionConfig, versionedResponse };
58
+ export { ApiVersionConfig, ApiVersioningConfig, apiVersion, apiVersionedResponse, getApiVersion, getApiVersionConfig };
@@ -1 +1 @@
1
- export{a as apiVersion,b as getApiVersion,c as getApiVersionConfig,d as versionedResponse}from'../chunk-A7ZLZUMJ.js';import'../chunk-6XVCICWS.js';import'../chunk-6GO5LUOZ.js';
1
+ import {b,a}from'../chunk-XR6JRDGX.js';function C(e,r){return e.req.header(r)??void 0}function h(e,r){return e.req.query(r)??void 0}function l(e,r){let t=e.req.path,a=r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace("\\{version\\}","([^/]+)"),p=new RegExp(`^${a}`);return t.match(p)?.[1]}function A(e){let{versions:r,strategy:t="header",headerName:d="Accept-Version",queryParam:a$1="version",urlPattern:p="/v{version}",extractVersion:f,addHeaders:c=true}=e,V=e.defaultVersion??r[0]?.version,u=new Map;for(let n of r)u.set(n.version,n);return async(n,m)=>{let o=f?f(n):{header:()=>C(n,d),query:()=>h(n,a$1),url:()=>l(n,p)}[t]();if(o=o??V,!o)throw new b("API version is required",400,"VERSION_REQUIRED");let i=u.get(o);if(!i)throw new b(`Unsupported API version: ${o}`,400,"UNSUPPORTED_VERSION");if(n.set(a.apiVersion,o),n.set(a.apiVersionConfig,i),c&&(n.header("X-API-Version",o),i.deprecated&&n.header("Deprecation",i.deprecated),i.sunset&&n.header("Sunset",i.sunset)),i.middleware&&i.middleware.length>0)for(let y of i.middleware)await y(n,async()=>{});await m();}}function w(e){return e.get(a.apiVersion)}function v(e){return e.get(a.apiVersionConfig)}function E(){return async(e,r)=>{await r();let t=e.get(a.apiVersionConfig);if(!(!t?.responseTransformer||!e.res.headers.get("content-type")?.includes("application/json")))try{let a=await e.res.json(),p=t.responseTransformer(a);e.res=new Response(JSON.stringify(p),{status:e.res.status,headers:e.res.headers});}catch{}}}export{A as apiVersion,E as apiVersionedResponse,w as getApiVersion,v as getApiVersionConfig};
@@ -1,11 +1,27 @@
1
- import { Context } from 'hono';
2
- import { A as AuditConfig, f as AuditLogEntry, g as AuditAction } from '../types-DKPLu-9p.js';
3
- import { S as StorageRegistry } from '../registry-3p4qTDGZ.js';
1
+ import { S as StorageRegistry } from '../registry-DYXgzGt0.js';
2
+ import { Context, Env } from 'hono';
3
+ import { m as AuditFieldChange, l as AuditConfig, Z as NormalizedAuditConfig, n as AuditLogEntry, k as AuditAction } from '../types-C2iJttg9.js';
4
4
  import 'zod';
5
+ import '../path-match-DGLu6wld.js';
5
6
  import '../types-B5wq2iKZ.js';
6
- import '../types-Drjma4gp.js';
7
+ import '../types-lAPVBoYa.js';
7
8
  import '@hono/zod-openapi';
8
9
 
10
+ /**
11
+ * Audit configuration normalization + field-change diffing.
12
+ */
13
+
14
+ /**
15
+ * Get normalized audit configuration with defaults.
16
+ * Accepts the model-side union: `true` enables with defaults, a config
17
+ * object enables with overrides, `false`/`undefined` disables.
18
+ */
19
+ declare function getAuditConfig(config?: boolean | AuditConfig): NormalizedAuditConfig;
20
+ /**
21
+ * Calculate field changes between two records.
22
+ */
23
+ declare function calculateChanges(oldRecord: Record<string, unknown> | undefined, newRecord: Record<string, unknown> | undefined, excludeFields?: string[]): AuditFieldChange[];
24
+
9
25
  /**
10
26
  * Interface for audit log storage adapters.
11
27
  * Implement this to store audit logs in your preferred storage.
@@ -82,13 +98,22 @@ declare const getAuditStorage: () => AuditLogStorage | null;
82
98
  * default when none was configured.
83
99
  */
84
100
  declare const getAuditStorageRequired: () => AuditLogStorage;
101
+ /**
102
+ * Resolves audit storage with priority: explicit param > context > global.
103
+ * Never creates a default — returns null when nothing is configured.
104
+ *
105
+ * @param ctx - Optional Hono context
106
+ * @param explicitStorage - Optional explicit storage instance
107
+ * @returns The resolved storage, or null when no storage was configured
108
+ */
109
+ declare function resolveAuditStorage<E extends Env>(ctx?: Context<E>, explicitStorage?: AuditLogStorage): AuditLogStorage | null;
85
110
  /**
86
111
  * Audit logger class for creating audit log entries.
87
112
  */
88
113
  declare class AuditLogger {
89
114
  private config;
90
115
  private storage;
91
- constructor(config: AuditConfig | undefined, storage?: AuditLogStorage, ctx?: Context);
116
+ constructor(config: boolean | AuditConfig | undefined, storage?: AuditLogStorage, ctx?: Context);
92
117
  private getStorage;
93
118
  /**
94
119
  * Check if audit logging is enabled for an action.
@@ -151,6 +176,6 @@ declare class AuditLogger {
151
176
  * const logger = createAuditLogger(config, myStorage);
152
177
  * ```
153
178
  */
154
- declare function createAuditLogger(config: AuditConfig | undefined, storage?: AuditLogStorage, ctx?: Context): AuditLogger;
179
+ declare function createAuditLogger(config: boolean | AuditConfig | undefined, storage?: AuditLogStorage, ctx?: Context): AuditLogger;
155
180
 
156
- export { type AuditLogStorage, AuditLogger, MemoryAuditLogStorage, auditStorageRegistry, createAuditLogger, getAuditStorage, getAuditStorageRequired, setAuditStorage };
181
+ export { type AuditLogStorage, AuditLogger, MemoryAuditLogStorage, auditStorageRegistry, calculateChanges, createAuditLogger, getAuditConfig, getAuditStorage, getAuditStorageRequired, resolveAuditStorage, setAuditStorage };
@@ -1 +1 @@
1
- export{f as AuditLogger,a as MemoryAuditLogStorage,b as auditStorageRegistry,g as createAuditLogger,d as getAuditStorage,e as getAuditStorageRequired,c as setAuditStorage}from'../chunk-MCXQ77DB.js';import'../chunk-EX4S3Q4M.js';import'../chunk-OCJC5XWY.js';import'../chunk-VJRDAVID.js';import'../chunk-6GO5LUOZ.js';
1
+ export{g as AuditLogger,a as MemoryAuditLogStorage,b as auditStorageRegistry,h as createAuditLogger,d as getAuditStorage,e as getAuditStorageRequired,f as resolveAuditStorage,c as setAuditStorage}from'../chunk-WBHWKOTP.js';export{b as calculateChanges,a as getAuditConfig}from'../chunk-L5CVVJQH.js';import'../chunk-5P4RVSHT.js';import'../chunk-TLI3TRUA.js';import'../chunk-XR6JRDGX.js';