@rdlabo/workers-hono-kit 0.11.2 → 0.12.0-beta.pr48.sha371f5792ce8a

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 (56) hide show
  1. package/README.md +73 -37
  2. package/dist/aws/sts.d.ts +2 -2
  3. package/dist/aws/sts.js +4 -4
  4. package/dist/business-time/index.d.ts +68 -122
  5. package/dist/business-time/index.js +55 -222
  6. package/dist/container/middleware.d.ts +1 -2
  7. package/dist/container/middleware.js +1 -1
  8. package/dist/db/index.d.ts +95 -20
  9. package/dist/db/index.js +56 -15
  10. package/dist/db/payment-failed.d.ts +1 -1
  11. package/dist/db/payment-failed.js +1 -1
  12. package/dist/index.d.ts +0 -3
  13. package/dist/index.js +0 -3
  14. package/dist/mysql/index.d.ts +9 -0
  15. package/dist/mysql/index.js +8 -0
  16. package/dist/testing/auth.d.ts +1 -1
  17. package/dist/testing/db.d.ts +19 -106
  18. package/dist/testing/db.js +3 -95
  19. package/dist/testing/fakes.d.ts +15 -97
  20. package/dist/testing/fakes.js +12 -104
  21. package/dist/testing/index.d.ts +8 -3
  22. package/dist/testing/index.js +8 -2
  23. package/docs/api-business-time.md +47 -30
  24. package/docs/api-db.md +48 -29
  25. package/docs/api-offline.md +11 -15
  26. package/docs/api-root.md +64 -58
  27. package/docs/api-testing.md +41 -13
  28. package/docs/api.md +35 -12
  29. package/docs/cli.md +11 -7
  30. package/docs/data-layer.md +68 -5
  31. package/docs/development.md +125 -5
  32. package/docs/http-auth.md +15 -0
  33. package/docs/realtime-offline.md +15 -0
  34. package/docs/role-policies.md +4 -4
  35. package/docs/testing-operations.md +25 -2
  36. package/package.json +24 -18
  37. package/scripts/db-baseline.mjs +3 -68
  38. package/scripts/workspace-package-smoke.mjs +206 -0
  39. package/dist/business-time/types.d.ts +0 -9
  40. package/dist/business-time/types.js +0 -5
  41. package/dist/db/columns.d.ts +0 -38
  42. package/dist/db/columns.js +0 -38
  43. package/dist/db/connection.d.ts +0 -74
  44. package/dist/db/connection.js +0 -67
  45. package/dist/db/database.d.ts +0 -249
  46. package/dist/db/database.js +0 -196
  47. package/dist/db/jst.d.ts +0 -45
  48. package/dist/db/jst.js +0 -47
  49. package/dist/db/migrate.d.ts +0 -51
  50. package/dist/db/migrate.js +0 -109
  51. package/dist/db/orm-config.d.ts +0 -127
  52. package/dist/db/orm-config.js +0 -122
  53. package/dist/db/retry.d.ts +0 -28
  54. package/dist/db/retry.js +0 -56
  55. package/dist/db/write-result.d.ts +0 -39
  56. package/dist/db/write-result.js +0 -34
package/docs/api-db.md CHANGED
@@ -1,32 +1,50 @@
1
- # API: `@rdlabo/workers-hono-kit/db`
2
-
3
- Requires the `drizzle-orm` and `mysql2` peers. Reads use raw SQL against the replica by default, with an explicit primary read path for freshness; writes/transactions run against the primary through the Drizzle ORM with deadlock retry. The kit deliberately does not depend on the ORM's type identity — you pass the Drizzle instance in.
4
-
5
- | Export | Description |
6
- | --- | --- |
7
- | `createHyperdriveDatabase(options)` | `HyperdriveDatabase` that lazily opens primary/replica connections from Hyperdrive bindings per request. `read()` targets the replica, `query()` targets the primary, and `readTransaction()` pins raw and Drizzle reads to one primary consistent snapshot. Read transactions are serialized on a separately cached connection and must not be recursively nested. A SELECT or complete read-only transaction is repeated at most once on a fresh connection after a fatal mysql2 connection error. Writes and write transactions are not repeated. Workers cleans connections up at invocation end; the legacy `dispose()` is a no-op. |
8
- | `createMysqlDatabase(options)` | Assemble a `Database` from an already-connected Drizzle ORM + replica `QueryRunner`. |
9
- | `databaseFrom(orm, replica)` | Build a `Database` from an existing Drizzle instance + replica handle. |
10
- | `Database` / `DisposableDatabase` / `HyperdriveDatabase` / `ReadTransaction` / `QueryRunner` / `TxOf` | The `read` / `query` / `readTransaction` / `write` / `transaction` API and its supporting types. |
11
- | `hyperdriveConnectionOptions(hyperdrive, overrides?)` / `HyperdriveLike` / `ExecutionContextLike` | Build mysql2 `createConnection` options from a Hyperdrive binding (`disableEval`, `decimalNumbers`, `timezone '+09:00'` by default). `timezone` controls mysql2's JavaScript `Date` conversion; it does not change the MySQL session timezone. |
12
- | `withMysqlConnections(...)` | Open primary/replica connections in parallel and run a function. Workers cleans them up at invocation end. |
13
- | `retryWhenDeadlock(fn, retries?, delay?)` | Same deadlock-retry helper as the root export. |
14
- | `insertIdOf` / `affectedRowsOf` / `insertedIdsOf` / `DzWriteResult` | Extract `insertId` / `affectedRows` (and derive contiguous bulk-insert ids) from a mysql2 write result. |
15
- | `toJstDate` / `jstTimestampParams` / `jstDatetimeParams` / `jstDateParams` | JST date/time normalization params (advanced use). |
16
- | `MYSQL_TIMEZONE` | Default mysql2 connection `timezone` (`'+09:00'`) for the JST DB deployment. |
17
- | `jstTimestamp` / `jstDatetime` / `jstDate` | Drizzle column helpers (no repo-side wrapper needed). |
18
- | `jstOnUpdateNow` | SQL expression for `ON UPDATE CURRENT_TIMESTAMP`. The `jstTimestamp` customType (and friends) do not support `.onUpdateNow()`, so pair it with `.$onUpdateFn(() => jstOnUpdateNow(fsp))`. |
19
- | `DRIZZLE_ORM_OPTIONS` / `honoDrizzleConfig(options)` / `HonoDrizzleConfigOptions` | Shared Drizzle casing (`snake_case`) for both the runtime `drizzle()` call and `drizzle.config.ts`, keeping config ↔ runtime in sync. |
20
- | `resolveDbSecret()` / `ResolvedDbSecret` | Resolve DB connection info from the `DB_SECRET` env var (an AWS RDS managed-secret JSON string) for CI migrate / local tooling. Returns `undefined` when `DB_SECRET` is unset; throws on invalid JSON or a missing required key. |
21
- | `baselineMigrations(options)` / `readBaselineEntry(migrationsFolder)` / `BaselineMigrationsOptions` / `BaselineResult` / `BaselineEntry` | Brownfield first-deploy helper: mark an existing `0000_*` migration as applied without re-running DDL. |
1
+ # API: `@rdlabo/workers-mysql`
2
+
3
+ `mysql2` is a direct dependency. The package root contains Workers-safe MySQL and Hyperdrive code;
4
+ Drizzle and Node-only migrations are isolated in `/drizzle` and `/migrations`. The deprecated
5
+ `@rdlabo/workers-hono-kit/db` subpath re-exports all three surfaces during migration.
6
+
7
+ Workers using this package must enable the Node.js compatibility APIs required by `mysql2`:
8
+
9
+ ```toml
10
+ # wrangler.toml
11
+ compatibility_flags = ["nodejs_compat"]
12
+ ```
13
+
14
+ | Import | Scope |
15
+ | ---------------------------------- | ----------------------------------------------------------------------- |
16
+ | `@rdlabo/workers-mysql` | Runtime connection, database, retry, result, and fixed-JST wire helpers |
17
+ | `@rdlabo/workers-mysql/drizzle` | Drizzle configuration and column helpers |
18
+ | `@rdlabo/workers-mysql/migrations` | Node-only baseline helpers |
19
+ | `@rdlabo/workers-mysql/testing` | Local MySQL/Drizzle test database and fakes |
20
+ | `@rdlabo/workers-hono-kit/mysql` | Hono request-container adapter |
21
+
22
+ | Export | Description |
23
+ | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
24
+ | `createHyperdriveDatabase(options)` | `HyperdriveDatabase` that lazily opens primary/replica connections from Hyperdrive bindings per request. `read()` targets the replica, `query()` targets the primary, and `readTransaction()` pins raw and Drizzle reads to one primary consistent snapshot. Read transactions are serialized on a separately cached connection and must not be recursively nested. A SELECT or complete read-only transaction is repeated at most once on a fresh connection after a fatal mysql2 connection error. Writes and write transactions are not repeated. Workers cleans connections up at invocation end; the legacy `dispose()` is a no-op. |
25
+ | `createMysqlDatabase(options)` | Assemble a `Database` from an already-connected Drizzle ORM + replica `QueryRunner`. |
26
+ | `databaseFrom(orm, replica)` | Build a `Database` from an existing Drizzle instance + replica handle. |
27
+ | `Database` / `DisposableDatabase` / `HyperdriveDatabase` / `ReadTransaction` / `QueryRunner` / `TxOf` | The `read` / `query` / `readTransaction` / `write` / `transaction` API and its supporting types. |
28
+ | `hyperdriveConnectionOptions(hyperdrive, overrides?)` / `HyperdriveLike` / `ExecutionContextLike` | Build mysql2 `createConnection` options from a Hyperdrive binding (`disableEval`, `decimalNumbers`, `timezone '+09:00'` by default). `timezone` controls mysql2's JavaScript `Date` conversion; it does not change the MySQL session timezone. |
29
+ | `withMysqlConnections(...)` | Open primary/replica connections in parallel and run a function. Workers cleans them up at invocation end. |
30
+ | `retryWhenDeadlock(fn, retries?, delay?)` | Retry on MySQL `ER_LOCK_DEADLOCK` with exponential backoff. |
31
+ | `insertIdOf` / `affectedRowsOf` / `insertedIdsOf` / `DzWriteResult` | Extract `insertId` / `affectedRows` (and derive contiguous bulk-insert ids) from a mysql2 write result. |
32
+ | `toJstDate` / `jstTimestampParams` / `jstDatetimeParams` / `jstDateParams` | JST date/time normalization params (advanced use). |
33
+ | `MYSQL_TIMEZONE` | Default mysql2 connection `timezone` (`'+09:00'`) for the JST DB deployment. |
34
+ | `jstTimestamp` / `jstDatetime` / `jstDate` | Drizzle column helpers (no repo-side wrapper needed). |
35
+ | `jstOnUpdateNow` | SQL expression for `ON UPDATE CURRENT_TIMESTAMP`. The `jstTimestamp` customType (and friends) do not support `.onUpdateNow()`, so pair it with `.$onUpdateFn(() => jstOnUpdateNow(fsp))`. |
36
+ | `DRIZZLE_ORM_OPTIONS` / `workersDrizzleConfig(options)` / `WorkersDrizzleConfigOptions` | `/drizzle`: shared casing (`snake_case`) for runtime and `drizzle.config.ts`. The old `hono*` names remain deprecated aliases. |
37
+ | `resolveDbSecret()` / `ResolvedDbSecret` | Resolve DB connection info from the `DB_SECRET` env var (an AWS RDS managed-secret JSON string) for CI migrate / local tooling. Returns `undefined` when `DB_SECRET` is unset; throws on invalid JSON or a missing required key. |
38
+ | `baselineMigrations(options)` / `readBaselineEntry(migrationsFolder)` / `BaselineMigrationsOptions` / `BaselineResult` / `BaselineEntry` | Brownfield first-deploy helper: mark an existing `0000_*` migration as applied without re-running DDL. |
39
+ | `createContainerRuntime(options)` | Hono adapter that opens primary/replica connections per invocation and injects the application container. Imported from `@rdlabo/workers-hono-kit/mysql`. |
22
40
 
23
41
  ## Drizzle column helpers (`jstTimestamp`, etc.)
24
42
 
25
- - `drizzle-orm` is a **peer** only. The kit does not include `drizzle-orm` as a dependency (even after publishing, it uses the consumer's single copy).
43
+ - `drizzle-orm` is an optional **peer** used by `@rdlabo/workers-mysql/drizzle` and `/testing`.
26
44
  - The consumer just keeps `drizzle-orm` in its `dependencies` as usual. **No `overrides` in `package.json` are needed.**
27
- - The npm-published artifact contains no `devDependencies`, so installing it does not add a kit-specific `drizzle-orm` (there is only the one peer copy).
45
+ - The npm-published artifact contains no `devDependencies`, so installing it does not add a package-specific `drizzle-orm`.
28
46
  - The column helpers `import` the consumer's `drizzle-orm` at runtime, and the types are the `customType` inference as-is (`MySqlCustomColumnBuilder<…>`). No `any` is used, so the column's semantic type propagates to the consumer table's `$inferSelect`.
29
- - **Precondition: resolve drizzle to a single copy.** Drizzle's `SQL` is a **nominal** type carrying a private field `shouldInlineParams`, so if the kit and the consumer resolve different copies, `jstTimestamp(…).default(sql\`…\`)` fails the whole schema with `TS2345 separate declarations of a private property 'shouldInlineParams'`. Under `file:`-link development, `drizzle-orm` nests under the kit and becomes a second copy, so **pin `drizzle-orm` to the consumer's own single copy in `tsconfig.json`**:
47
+ - **Precondition: resolve drizzle to a single copy.** Drizzle's `SQL` is a **nominal** type carrying a private field `shouldInlineParams`, so if the package and consumer resolve different copies, ``jstTimestamp(…).default(sql`…`)`` fails the whole schema with `TS2345 separate declarations of a private property 'shouldInlineParams'`. Under `file:`-link development, `drizzle-orm` can nest under the linked package, so **pin `drizzle-orm` to the consumer's own single copy in `tsconfig.json`**:
30
48
 
31
49
  ```jsonc
32
50
  // tsconfig.json compilerOptions (merge with existing paths if any)
@@ -37,13 +55,14 @@ Requires the `drizzle-orm` and `mysql2` peers. Reads use raw SQL against the rep
37
55
  ```
38
56
 
39
57
  With `moduleResolution: "Bundler"`, `baseUrl` is not required (if `baseUrl` is already set, drop the leading `./`). On the published package (a single copy) these `paths` are harmless. **No `overrides` needed.**
40
- - When developing against the kit via a direct `file:` link, run `npm install` in the kit repo itself to satisfy its peers (do not add `overrides` on the consumer side).
58
+
59
+ - When developing via a direct `file:` link, run `npm install` in this repository to satisfy workspace peers.
41
60
 
42
61
  ## `CURRENT_TIMESTAMP` vs the connection `timezone:'+09:00'`
43
62
 
44
- | Path | Who decides the time | Relationship to JST |
45
- | --- | --- | --- |
46
- | The app binds a `Date` (INSERT/UPDATE) | mysql2 + connection `timezone:'+09:00'` | Treated as JST on the wire (`datetime-wire` test) |
47
- | `DEFAULT CURRENT_TIMESTAMP` / `ON UPDATE CURRENT_TIMESTAMP` | The MySQL server (session `time_zone`) | A **separate path** from the connection option. JST if the RDS `time_zone` is `+09:00`, UTC if UTC |
63
+ | Path | Who decides the time | Relationship to JST |
64
+ | ----------------------------------------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------- |
65
+ | The app binds a `Date` (INSERT/UPDATE) | mysql2 + connection `timezone:'+09:00'` | Treated as JST on the wire (`datetime-wire` test) |
66
+ | `DEFAULT CURRENT_TIMESTAMP` / `ON UPDATE CURRENT_TIMESTAMP` | The MySQL server (session `time_zone`) | A **separate path** from the connection option. JST if the RDS `time_zone` is `+09:00`, UTC if UTC |
48
67
 
49
68
  `jstTimestamp` / `jstDatetime` only handle read/write pass-through and DATE normalization; they do not change the timezone of server-side defaults. For columns that need `ON UPDATE`, keep the DDL intent with `.$onUpdateFn(() => jstOnUpdateNow(6))`.
@@ -4,23 +4,19 @@ Table-agnostic building blocks for product-owned REST ↔ DB method converters a
4
4
 
5
5
  This is an additive subpath: existing root and subpath exports are unchanged. Consumers can migrate converter internals independently without changing REST payloads, schema hashes, or persisted SQLite rows. For an `AUTO_INCREMENT` table, omit `id` from a create method's table scheme; keep the client-generated UUID in `local_id` and keep `server_id` null until the server confirms its id.
6
6
 
7
- | Export | Description |
8
- | --- | --- |
9
- | `defineRestDbMethodConverter(converter)` | Type a product-owned, pure `MethodScheme ↔ TableScheme` converter without hiding HTTP or persistence side effects. |
10
- | `RestDbMethodConverter` | Product-owned converter contract. Select and insert bundles may differ; every represented table and column remains required. |
11
- | `CompleteRestDbTableScheme` | Compile-time lock requiring every represented table key and row column. |
12
- | `toReplicaIsoDatetime(value)` | `Date` / datetime string → canonical UTC ISO-8601 wire value. |
13
- | `toReplicaDateOnly(value)` | `Date` / date string / `null` → canonical `YYYY-MM-DD` / `null`. |
14
- | `replicaTimestampMs(value)` | Replica datetime → epoch milliseconds for legacy DTOs. |
15
- | `toTinyIntFlag(value)` / `fromTinyIntFlag(value)` | Boolean-like value ↔ numeric tinyint flag. |
16
- | `replicaNowIso(clock?)` | Injectable wall clock → canonical UTC ISO-8601 wire value. |
7
+ | Export | Description |
8
+ | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
9
+ | `defineRestDbMethodConverter(converter)` | Type a product-owned, pure `MethodScheme ↔ TableScheme` converter without hiding HTTP or persistence side effects. |
10
+ | `RestDbMethodConverter` | Product-owned converter contract. Select and insert bundles may differ; every represented table and column remains required. |
11
+ | `CompleteRestDbTableScheme` | Compile-time lock requiring every represented table key and row column. |
12
+ | `toReplicaIsoDatetime(value)` | `Date` / datetime string → canonical UTC ISO-8601 wire value. |
13
+ | `toReplicaDateOnly(value)` | `Date` / date string / `null` → canonical `YYYY-MM-DD` / `null`. |
14
+ | `replicaTimestampMs(value)` | Replica datetime → epoch milliseconds for legacy DTOs. |
15
+ | `toTinyIntFlag(value)` / `fromTinyIntFlag(value)` | Boolean-like value ↔ numeric tinyint flag. |
16
+ | `replicaNowIso(clock?)` | Injectable wall clock → canonical UTC ISO-8601 wire value. |
17
17
 
18
18
  ```ts
19
- import {
20
- defineRestDbMethodConverter,
21
- replicaNowIso,
22
- toReplicaIsoDatetime,
23
- } from '@rdlabo/workers-hono-kit/offline';
19
+ import { defineRestDbMethodConverter, replicaNowIso, toReplicaIsoDatetime } from '@rdlabo/workers-hono-kit/offline';
24
20
 
25
21
  type Tables = {
26
22
  foods: FoodRow[];
package/docs/api-root.md CHANGED
@@ -1,64 +1,70 @@
1
1
  # API: `@rdlabo/workers-hono-kit`
2
2
 
3
- The root export is web-standard only: it runs on `workerd` and never depends on Node.js APIs or `mysql2`. The table below lists the helpers exported from the root entry point.
3
+ The root export runs on `workerd` without loading MySQL or Drizzle. It requires
4
+ `ai-gateway-provider` because it statically exports `createAiGatewayProvider`. Install it with
5
+ `npm install @rdlabo/workers-hono-kit ai-gateway-provider`. MySQL infrastructure lives in
6
+ `@rdlabo/workers-mysql`; its Hono adapter is `@rdlabo/workers-hono-kit/mysql`.
4
7
 
5
- | Export | Description |
6
- | --- | --- |
7
- | `finalizeResponse()` | Middleware that adds a weak `ETag` (delegates to `hono/etag`; also handles `If-None-Match` → `304`). |
8
- | `validate(target, schema, options?)` | Zod validator → NestJS `ValidationPipe`-shaped `400` (`{ statusCode, message[], error }`). `options.onValidationError(err, c)` to report (e.g. Sentry). |
9
- | `createValidate({ sentry? })` | Bound `validate` factory. Pass `sentry` on Sentry apps; omit for console-only (review, cbs-ai). |
10
- | `createSentryValidate(sentry)` | **Deprecated** — use `createValidate({ sentry })`. |
11
- | `zNum` / `zNumWithDefault` / `zNumOptional` / `zNumNullable` | Number-coercion zod schemas (mirror class-transformer `@Transform`). |
12
- | `getAuthenticationSecret<T>(options, secretId)` / `AwsSecretsOptions` | Fetch a secret from AWS Secrets Manager (SigV4 `fetch`, per-isolate cache). |
13
- | `getTemporaryCredentials(options)` / `GetTemporaryCredentialsOptions` / `StsCredentials` | STS `AssumeRole` via SigV4 `fetch` (global `sts.amazonaws.com`); returns temporary credentials for browser S3 uploads. |
14
- | `getCloudFrontSignedUrl(url, privateKeyPem, keyPairId, dateLessThan)` | CloudFront signed URL (canned policy, RSA-SHA1, URL-safe base64) — Web Crypto reimpl of `@aws-sdk/cloudfront-signer`, byte-identical query order. |
15
- | `JoseFirebaseVerifier` / `FirebaseVerifier` / `DecodedIdToken` | Firebase ID-token verification (`verifyIdToken`, `getUser`, `deleteUser`). |
16
- | `createRemoteFirebaseVerifier(projectId)` | Convenience factory: production verifier with a cached remote JWKS (verification only). |
17
- | `createServiceAccountVerifier(serviceAccountJson)` | Cached verifier built from a service-account JSON, **with `IdentityToolkit`** (getUser/deleteUser). One per isolate, re-created only when the SA JSON changes. |
18
- | `IdentityToolkit` / `ServiceAccount` / `SECURETOKEN_JWK_URL` | Identity Toolkit REST client + constants for `getUser` / `deleteUser`. |
19
- | `retryWhenDeadlock(fn, retries?, delay?)` | Retry on MySQL `ER_LOCK_DEADLOCK` with exponential backoff. |
20
- | `getUserProtocol(c)` / `IUserProtocol` | Read client IP / UA (`CF-Connecting-IP` → `X-Forwarded-For`). |
21
- | `getAppInfo(c)` / `AppInfo` | Read `x-amz-meta-version` / `x-amz-meta-uuid`. |
22
- | `resolveAppEnv(env)` / `isProductionEnv(env)` / `AppEnv` | Resolve `'development'` / `'production'` from `env.APP_ENV` (defaults to `'production'` for safety). |
23
- | `HttpStatus` | Standard HTTP status code enum (IANA registry). |
24
- | `createHttpErrorHandler(options?)` / `HttpErrorHandlerOptions` | `app.onError()` handler that maps a thrown `HTTPException` to `{ statusCode, message, error? }` (`401` omits `error`). Optional custom error predicate and unhandled-error report hook. Unhandled errors log via `console.error` (mysql2 errors include `sqlMessage` / `errno` when detectable). |
25
- | `createAppErrorHandler(options?)` / `CreateAppErrorHandlerOptions` | Standard `app.onError`: {@link createQueryFailedErrorHandler} + default {@link classifyGenericMysqlDriverError} + optional `sentry` (Sentry apps), `getReportError` / `reportError` (tests / container), or neither (no external reporting). |
26
- | `createQueryFailedErrorHandler(options)` / `QueryFailedClassifier` / `ClassifiedDbError` | Lower-level compose when you need full control over `classify` + `onUnhandledError` without defaults. |
27
- | `classifyGenericMysqlDriverError(err)` | Default classifier: any mysql2 driver error → `{ statusCode: 500, message: 'Internal server error' }`; non-DB errors → `null`. |
28
- | `findMysqlDriverError(err)` / `logMysqlDriverError(err, statusCode)` | Low-level mysql2 driver-error detection (follows `err.cause`) and structured logging. For custom classifiers (e.g. odss). |
29
- | `notFoundHandler(c)` | `app.notFound()` handler with `{ message: 'Cannot METHOD path', error, statusCode }` 404 body. |
30
- | `normalizeTrailingSlash(request)` | Strip trailing slash(es) from the request URL before routing (Express/Nest parity). Does **not** 301-redirect — preserves POST/PUT/DELETE bodies. |
31
- | `HTTP_ERROR_PHRASES` | `{ 400, 401, 403, 404 }` → standard `error` field phrases. |
32
- | `createAuthMiddleware(options)` / `AuthMiddlewareOptions` | Factory for a Firebase-token auth middleware: reads the token header, verifies, resolves the DB user id, and stashes the result on the context. Omit `resolveUserId` for a token-only (login) guard. |
33
- | `createIdentityAuthFailureBody()` / `createLegacyIdentityAuthFailureBody()` / `createAuthFailureBody(scope, code, message)` / `AuthFailureScope` | Stable wire contract for distinguishing a lost global identity (`identity`) from recent-login (`reauthentication`) and feature credential (`credential`) failures. The legacy helper tags products whose installed clients still require auth failure as `403`. |
34
- | `perfLog(options?)` / `PerfLogOptions` / `AnalyticsEngineDatasetLike` | Middleware that records one per-request latency data point (`t_app`, colo, cold/warm, route, status) and emits it to **Workers Logs** (`console.log`) and/or **Workers Analytics Engine** (`writeDataPoint`). Lets you measure low-traffic Workers without a live `wrangler tail`. |
35
- | `createMaintenanceMiddleware(options)` / `createMaintenanceWaitHandler(options)` / `isMaintenanceEnabled(env)` / `MAINTENANCE_CODE` / `MAINTENANCE_WAIT_PATH` | Fleet maintenance short-circuit: when enabled (`MAINTENANCE=1`), every non-allowlisted request returns `503` + `{ statusCode, message, code: 'MAINTENANCE' }` **before** container/DB. Pair with `GET /public/maintenance/wait` SSE (`event: ping` / `event: ended`) so clients can auto-dismiss a lock UI. Mount after `cors`, before `containerMiddleware`. |
36
- | `ErrorReporter` / `ErrorReportContext` | Types for a `reportError`-style unhandled-error reporter (e.g. wired to Sentry), paired with `createHttpErrorHandler`'s `onUnhandledError`. |
37
- | `createSentryErrorReporter(sentry)` / `SentryExceptionReporterLike` | Build an `ErrorReporter` that forwards to Sentry with an optional `request_id` tag (no hard `@sentry/cloudflare` dependency). |
38
- | `DeferExecutor` / `defaultDefer` / `createWaitUntilDefer(ctx)` | Fire-and-forget executor for Workers: both variants log background rejections without propagating them; `createWaitUntilDefer` also registers work via `ctx.waitUntil`. |
39
- | `configureHibernationAutoResponse` / `upgradeHibernationWebSocket` / `broadcastHibernationWebSockets` | Hibernation WebSocket room primitives: runtime ping/pong without waking JavaScript, attachment-before-accept upgrade, and broadcast through sockets restored by `getWebSockets()`. |
40
- | `acknowledgeHibernationWebSocketClose` / `closeHibernationWebSocket` | Safe close helpers, including normalization of reserved received-only close codes. |
41
- | `retryDurableObjectOperation(operation, options?)` / `isRetryableDurableObjectError(error)` | Retry idempotent DO work only for `retryable && !overloaded`, with jittered exponential backoff. `operation` runs per attempt so callers create a fresh stub after an exception. |
42
- | `createIdempotencyInput(...)` / `runIdempotentMutation(...)` | Canonical payload hashing and a transaction-bound mutation state machine. Missing keys preserve legacy behavior; replay/conflict/in-flight semantics are shared while each app owns its schema and ORM adapter. |
43
- | `withIdempotencyHttpErrors(run)` | Maps only standard idempotency failures to 400/409/503 and rethrows unrelated failures. |
44
- | `createAiGatewayProvider(config)` / `AiGatewayConfig` / `AiGatewayProvider` | Route `@ai-sdk` models through the Cloudflare AI Gateway, via either a Workers `AI` binding or REST credentials (`accountId` / `gateway` / `token`). |
45
- | `KVCache` / `KVNamespace` / `KVCacheOptions` / `KVCacheErrorContext` / `KVCacheOperation` | Workers-KV cache-aside helper (key `appName+version+table_type_column`, sha256 for string ids, TTL clamped ≥60s). Set `appName` / `version` per application; optional `onError(error, context)` observes fail-soft read/parse/serialize/write/delete failures. Context contains only the operation and logical table, not cache types, keys, ids, or values. |
46
- | `createStripeClient(secret, opts?)` / `verifyStripeWebhook(...)` / `CreateStripeClientOptions` | Workers-native Stripe client (fetch transport) + async webhook verification (SubtleCrypto). `apiVersion` optional (pin to a fixed Stripe API version). |
47
- | `extractStripeFailureReason(source)` / `StripeFailureReason` | Duck-type a Stripe `PaymentIntent` / `Invoice` / `{ paymentIntent?, invoice? }` / thrown error into a normalized `{ code, declineCode, message, paymentIntentId, invoiceId, subscriptionId }` (SDK-free), or `null`. |
48
- | `stripeFailureMessageJa(reason)` | Render a `StripeFailureReason` (or `null`) as a single user-facing Japanese sentence (`decline_code` > `code`; fraud codes masked; unknown → generic). |
49
- | `PaymentDeclinedError` / `toPaymentDeclinedError(error, status?)` / `PaymentDeclinedBody` | `HTTPException` carrying a verbatim `{ statusCode, message, code?, declineCode? }` body for a synchronous card decline (defaults to `400`). `toPaymentDeclinedError` returns `null` for non-declines (re-throw → 500). |
50
- | `classifyStripeReconcile(subscription)` / `StripeReconcileAction` | Classify an expanded Stripe subscription into `trial` / `clear` / `canceled` / `failed` / `action_required` / `none` (termination evaluated before `succeeded`). Consumer does the DB write. |
51
- | `serializePaymentFailure(record)` / `parsePaymentFailure(receipt)` / `PaymentFailureRecord` / `PaymentFailureReason` / `PaymentFailureSource` | (De)serialize the `payment_failed.receipt` JSON. `parsePaymentFailure` restores both a full Stripe record and a bare IAP reason. |
52
- | `serializeIapFailureReason(reason)` / `IapFailureReason` | Serialize an IAP reason (`billing_retry` / `auto_renew_off` / `subscription_canceled` / `subscription_gone` + provider codes) directly, without the source/timestamp wrapper. |
53
- | `paymentFailureMessageJa(input)` / `PaymentFailureStatus` / `PaymentFailureType` / `UNRESOLVED_PAYMENT_STATUSES` | Provider-agnostic Japanese message for a `payment_failed` row (`canceled` re-subscribe prompt, IAP `failed` App Store/Google Play prompt, else Stripe wording). `UNRESOLVED_PAYMENT_STATUSES` = everything except `resolved` for read/resolve `WHERE`. |
54
- | `iapFailureKey(input)` | Provider-native `payment_failed.recursions_id`: iOS `${original_transaction_id}:${expires_date_ms}`, Android `${orderId}` (provider is in the `type` column). |
55
- | `verifyAppleReceipt(receipt, opts)` / `classifyAppleRenewal(verify, now)` / `AppleRenewalClassification` / `AppleRenewalState` / `AppleVerifyReceiptResponse` / `ApplePendingRenewalInfo` / `AppleLatestReceiptInfo` | Verify an App Store receipt (production → sandbox fallback; inject `password` / `fetchImpl`) and classify it into `billing_retry` / `lapsed` / `active` / `unknown` plus the raw fields used (`statusCode` / `billingRetryStatus` / `autoRenewStatus`, latest `original_transaction_id` / `expires_date_ms`). |
56
- | `googleAccessToken(creds, fetch?)` / `getGoogleSubscription(opts)` / `classifyGoogleSubscription(purchase, now)` / `GoogleSubscriptionClassification` / `GoogleSubscriptionState` / `GoogleSubscriptionPurchase` / `GoogleOAuthCredentials` | Exchange a refresh token for an Android Publisher access token (throws on `invalid_grant`), fetch a subscription purchase, and classify it into `canceled` / `gone` / `active` / `unknown` plus raw `statusCode` / `cancelReason`. |
57
- | `sendInChunks(queue, messages, options?)` / `QueueLike` / `QueueSendMessage` | Send queue messages in bounded chunks to stay under the Workers subrequest cap per invocation. `options.chunkSize` sets the per-batch size (defaults to and is capped at 100). |
58
- | `processBatch(batch, handler, options?)` / `isNonRetryableQueueError(error)` / `NonRetryableQueueErrorLike` / `MessageBatchLike` / `QueueMessageLike` / `ProcessBatchOptions` / `ProcessBatchResult` | Process a queue batch with bounded concurrency. Errors explicitly tagged with `queueDisposition: 'discard'` are reported and acked as permanent failures; all other errors are retried. |
59
- | `createQueueErrorHandler(options)` / `CreateQueueErrorHandlerOptions` | Factory for `processBatch`'s `onError`: logs every failure; optional Sentry capture with queue/message context; optional `maxRetries` gate (report only on final attempt, except permanent failures which are reported immediately). |
60
- | `assertStripeCustomerUpdated(options)` | Preserve the shared Stripe UPDATE→existence-check algorithm. `createNotFoundError(customerId)` can supply a domain-specific error without forking the algorithm. |
61
- | `ExecutionContextLike` | Minimal `waitUntil`-only Workers execution context shape used by lifecycle-compatible APIs and deferred work helpers. |
8
+ Version `0.12.0` moves the former root exports `createContainerRuntime` and
9
+ `retryWhenDeadlock` to those MySQL-specific entry points. This is intentionally not a root
10
+ re-export: retaining it would make MySQL runtime and declaration resolution mandatory again.
11
+
12
+ | Export | Description |
13
+ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
14
+ | `finalizeResponse()` | Middleware that adds a weak `ETag` (delegates to `hono/etag`; also handles `If-None-Match` → `304`). |
15
+ | `validate(target, schema, options?)` | Zod validator → NestJS `ValidationPipe`-shaped `400` (`{ statusCode, message[], error }`). `options.onValidationError(err, c)` to report (e.g. Sentry). |
16
+ | `createValidate({ sentry? })` | Bound `validate` factory. Pass `sentry` on Sentry apps; omit for console-only (review, cbs-ai). |
17
+ | `createSentryValidate(sentry)` | **Deprecated** — use `createValidate({ sentry })`. |
18
+ | `zNum` / `zNumWithDefault` / `zNumOptional` / `zNumNullable` | Number-coercion zod schemas (mirror class-transformer `@Transform`). |
19
+ | `getAuthenticationSecret<T>(options, secretId)` / `AwsSecretsOptions` | Fetch a secret from AWS Secrets Manager (SigV4 `fetch`, per-isolate cache). |
20
+ | `getTemporaryCredentials(options)` / `GetTemporaryCredentialsOptions` / `StsCredentials` | STS `AssumeRole` via SigV4 `fetch` (global `sts.amazonaws.com`); returns temporary credentials for browser S3 uploads. |
21
+ | `getCloudFrontSignedUrl(url, privateKeyPem, keyPairId, dateLessThan)` | CloudFront signed URL (canned policy, RSA-SHA1, URL-safe base64) — Web Crypto reimpl of `@aws-sdk/cloudfront-signer`, byte-identical query order. |
22
+ | `JoseFirebaseVerifier` / `FirebaseVerifier` / `DecodedIdToken` | Firebase ID-token verification (`verifyIdToken`, `getUser`, `deleteUser`). |
23
+ | `createRemoteFirebaseVerifier(projectId)` | Convenience factory: production verifier with a cached remote JWKS (verification only). |
24
+ | `createServiceAccountVerifier(serviceAccountJson)` | Cached verifier built from a service-account JSON, **with `IdentityToolkit`** (getUser/deleteUser). One per isolate, re-created only when the SA JSON changes. |
25
+ | `IdentityToolkit` / `ServiceAccount` / `SECURETOKEN_JWK_URL` | Identity Toolkit REST client + constants for `getUser` / `deleteUser`. |
26
+ | `getUserProtocol(c)` / `IUserProtocol` | Read client IP / UA (`CF-Connecting-IP` → `X-Forwarded-For`). |
27
+ | `getAppInfo(c)` / `AppInfo` | Read `x-amz-meta-version` / `x-amz-meta-uuid`. |
28
+ | `resolveAppEnv(env)` / `isProductionEnv(env)` / `AppEnv` | Resolve `'development'` / `'production'` from `env.APP_ENV` (defaults to `'production'` for safety). |
29
+ | `HttpStatus` | Standard HTTP status code enum (IANA registry). |
30
+ | `createHttpErrorHandler(options?)` / `HttpErrorHandlerOptions` | `app.onError()` handler that maps a thrown `HTTPException` to `{ statusCode, message, error? }` (`401` omits `error`). Optional custom error predicate and unhandled-error report hook. Unhandled errors log via `console.error` (mysql2 errors include `sqlMessage` / `errno` when detectable). |
31
+ | `createAppErrorHandler(options?)` / `CreateAppErrorHandlerOptions` | Standard `app.onError`: {@link createQueryFailedErrorHandler} + default {@link classifyGenericMysqlDriverError} + optional `sentry` (Sentry apps), `getReportError` / `reportError` (tests / container), or neither (no external reporting). |
32
+ | `createQueryFailedErrorHandler(options)` / `QueryFailedClassifier` / `ClassifiedDbError` | Lower-level compose when you need full control over `classify` + `onUnhandledError` without defaults. |
33
+ | `classifyGenericMysqlDriverError(err)` | Default classifier: any mysql2 driver error → `{ statusCode: 500, message: 'Internal server error' }`; non-DB errors → `null`. |
34
+ | `findMysqlDriverError(err)` / `logMysqlDriverError(err, statusCode)` | Low-level mysql2 driver-error detection (follows `err.cause`) and structured logging. For custom classifiers (e.g. odss). |
35
+ | `notFoundHandler(c)` | `app.notFound()` handler with `{ message: 'Cannot METHOD path', error, statusCode }` 404 body. |
36
+ | `normalizeTrailingSlash(request)` | Strip trailing slash(es) from the request URL before routing (Express/Nest parity). Does **not** 301-redirect — preserves POST/PUT/DELETE bodies. |
37
+ | `HTTP_ERROR_PHRASES` | `{ 400, 401, 403, 404 }` → standard `error` field phrases. |
38
+ | `createAuthMiddleware(options)` / `AuthMiddlewareOptions` | Factory for a Firebase-token auth middleware: reads the token header, verifies, resolves the DB user id, and stashes the result on the context. Omit `resolveUserId` for a token-only (login) guard. |
39
+ | `createIdentityAuthFailureBody()` / `createLegacyIdentityAuthFailureBody()` / `createAuthFailureBody(scope, code, message)` / `AuthFailureScope` | Stable wire contract for distinguishing a lost global identity (`identity`) from recent-login (`reauthentication`) and feature credential (`credential`) failures. The legacy helper tags products whose installed clients still require auth failure as `403`. |
40
+ | `perfLog(options?)` / `PerfLogOptions` / `AnalyticsEngineDatasetLike` | Middleware that records one per-request latency data point (`t_app`, colo, cold/warm, route, status) and emits it to **Workers Logs** (`console.log`) and/or **Workers Analytics Engine** (`writeDataPoint`). Lets you measure low-traffic Workers without a live `wrangler tail`. |
41
+ | `createMaintenanceMiddleware(options)` / `createMaintenanceWaitHandler(options)` / `isMaintenanceEnabled(env)` / `MAINTENANCE_CODE` / `MAINTENANCE_WAIT_PATH` | Fleet maintenance short-circuit: when enabled (`MAINTENANCE=1`), every non-allowlisted request returns `503` + `{ statusCode, message, code: 'MAINTENANCE' }` **before** container/DB. Pair with `GET /public/maintenance/wait` SSE (`event: ping` / `event: ended`) so clients can auto-dismiss a lock UI. Mount after `cors`, before `containerMiddleware`. |
42
+ | `ErrorReporter` / `ErrorReportContext` | Types for a `reportError`-style unhandled-error reporter (e.g. wired to Sentry), paired with `createHttpErrorHandler`'s `onUnhandledError`. |
43
+ | `createSentryErrorReporter(sentry)` / `SentryExceptionReporterLike` | Build an `ErrorReporter` that forwards to Sentry with an optional `request_id` tag (no hard `@sentry/cloudflare` dependency). |
44
+ | `DeferExecutor` / `defaultDefer` / `createWaitUntilDefer(ctx)` | Fire-and-forget executor for Workers: both variants log background rejections without propagating them; `createWaitUntilDefer` also registers work via `ctx.waitUntil`. |
45
+ | `configureHibernationAutoResponse` / `upgradeHibernationWebSocket` / `broadcastHibernationWebSockets` | Hibernation WebSocket room primitives: runtime ping/pong without waking JavaScript, attachment-before-accept upgrade, and broadcast through sockets restored by `getWebSockets()`. |
46
+ | `acknowledgeHibernationWebSocketClose` / `closeHibernationWebSocket` | Safe close helpers, including normalization of reserved received-only close codes. |
47
+ | `retryDurableObjectOperation(operation, options?)` / `isRetryableDurableObjectError(error)` | Retry idempotent DO work only for `retryable && !overloaded`, with jittered exponential backoff. `operation` runs per attempt so callers create a fresh stub after an exception. |
48
+ | `createIdempotencyInput(...)` / `runIdempotentMutation(...)` | Canonical payload hashing and a transaction-bound mutation state machine. Missing keys preserve legacy behavior; replay/conflict/in-flight semantics are shared while each app owns its schema and ORM adapter. |
49
+ | `withIdempotencyHttpErrors(run)` | Maps only standard idempotency failures to 400/409/503 and rethrows unrelated failures. |
50
+ | `createAiGatewayProvider(config)` / `AiGatewayConfig` / `AiGatewayProvider` | Route `@ai-sdk` models through the Cloudflare AI Gateway, via either a Workers `AI` binding or REST credentials (`accountId` / `gateway` / `token`). |
51
+ | `KVCache` / `KVNamespace` / `KVCacheOptions` / `KVCacheErrorContext` / `KVCacheOperation` | Workers-KV cache-aside helper (key `appName+version+table_type_column`, sha256 for string ids, TTL clamped ≥60s). Set `appName` / `version` per application; optional `onError(error, context)` observes fail-soft read/parse/serialize/write/delete failures. Context contains only the operation and logical table, not cache types, keys, ids, or values. |
52
+ | `createStripeClient(secret, opts?)` / `verifyStripeWebhook(...)` / `CreateStripeClientOptions` | Workers-native Stripe client (fetch transport) + async webhook verification (SubtleCrypto). `apiVersion` optional (pin to a fixed Stripe API version). |
53
+ | `extractStripeFailureReason(source)` / `StripeFailureReason` | Duck-type a Stripe `PaymentIntent` / `Invoice` / `{ paymentIntent?, invoice? }` / thrown error into a normalized `{ code, declineCode, message, paymentIntentId, invoiceId, subscriptionId }` (SDK-free), or `null`. |
54
+ | `stripeFailureMessageJa(reason)` | Render a `StripeFailureReason` (or `null`) as a single user-facing Japanese sentence (`decline_code` > `code`; fraud codes masked; unknown → generic). |
55
+ | `PaymentDeclinedError` / `toPaymentDeclinedError(error, status?)` / `PaymentDeclinedBody` | `HTTPException` carrying a verbatim `{ statusCode, message, code?, declineCode? }` body for a synchronous card decline (defaults to `400`). `toPaymentDeclinedError` returns `null` for non-declines (re-throw → 500). |
56
+ | `classifyStripeReconcile(subscription)` / `StripeReconcileAction` | Classify an expanded Stripe subscription into `trial` / `clear` / `canceled` / `failed` / `action_required` / `none` (termination evaluated before `succeeded`). Consumer does the DB write. |
57
+ | `serializePaymentFailure(record)` / `parsePaymentFailure(receipt)` / `PaymentFailureRecord` / `PaymentFailureReason` / `PaymentFailureSource` | (De)serialize the `payment_failed.receipt` JSON. `parsePaymentFailure` restores both a full Stripe record and a bare IAP reason. |
58
+ | `serializeIapFailureReason(reason)` / `IapFailureReason` | Serialize an IAP reason (`billing_retry` / `auto_renew_off` / `subscription_canceled` / `subscription_gone` + provider codes) directly, without the source/timestamp wrapper. |
59
+ | `paymentFailureMessageJa(input)` / `PaymentFailureStatus` / `PaymentFailureType` / `UNRESOLVED_PAYMENT_STATUSES` | Provider-agnostic Japanese message for a `payment_failed` row (`canceled` re-subscribe prompt, IAP `failed` App Store/Google Play prompt, else Stripe wording). `UNRESOLVED_PAYMENT_STATUSES` = everything except `resolved` for read/resolve `WHERE`. |
60
+ | `iapFailureKey(input)` | Provider-native `payment_failed.recursions_id`: iOS `${original_transaction_id}:${expires_date_ms}`, Android `${orderId}` (provider is in the `type` column). |
61
+ | `verifyAppleReceipt(receipt, opts)` / `classifyAppleRenewal(verify, now)` / `AppleRenewalClassification` / `AppleRenewalState` / `AppleVerifyReceiptResponse` / `ApplePendingRenewalInfo` / `AppleLatestReceiptInfo` | Verify an App Store receipt (production → sandbox fallback; inject `password` / `fetchImpl`) and classify it into `billing_retry` / `lapsed` / `active` / `unknown` plus the raw fields used (`statusCode` / `billingRetryStatus` / `autoRenewStatus`, latest `original_transaction_id` / `expires_date_ms`). |
62
+ | `googleAccessToken(creds, fetch?)` / `getGoogleSubscription(opts)` / `classifyGoogleSubscription(purchase, now)` / `GoogleSubscriptionClassification` / `GoogleSubscriptionState` / `GoogleSubscriptionPurchase` / `GoogleOAuthCredentials` | Exchange a refresh token for an Android Publisher access token (throws on `invalid_grant`), fetch a subscription purchase, and classify it into `canceled` / `gone` / `active` / `unknown` plus raw `statusCode` / `cancelReason`. |
63
+ | `sendInChunks(queue, messages, options?)` / `QueueLike` / `QueueSendMessage` | Send queue messages in bounded chunks to stay under the Workers subrequest cap per invocation. `options.chunkSize` sets the per-batch size (defaults to and is capped at 100). |
64
+ | `processBatch(batch, handler, options?)` / `isNonRetryableQueueError(error)` / `NonRetryableQueueErrorLike` / `MessageBatchLike` / `QueueMessageLike` / `ProcessBatchOptions` / `ProcessBatchResult` | Process a queue batch with bounded concurrency. Errors explicitly tagged with `queueDisposition: 'discard'` are reported and acked as permanent failures; all other errors are retried. |
65
+ | `createQueueErrorHandler(options)` / `CreateQueueErrorHandlerOptions` | Factory for `processBatch`'s `onError`: logs every failure; optional Sentry capture with queue/message context; optional `maxRetries` gate (report only on final attempt, except permanent failures which are reported immediately). |
66
+ | `assertStripeCustomerUpdated(options)` | Preserve the shared Stripe UPDATE→existence-check algorithm. `createNotFoundError(customerId)` can supply a domain-specific error without forking the algorithm. |
67
+ | `ExecutionContextLike` | Minimal `waitUntil`-only Workers execution context shape used by lifecycle-compatible APIs and deferred work helpers. |
62
68
 
63
69
  Permanent Queue failures must opt in with the Queue-specific marker; unrelated `retryable` fields are ignored:
64
70
 
@@ -1,16 +1,44 @@
1
- # API: `@rdlabo/workers-hono-kit/testing`
1
+ # Testing APIs
2
2
 
3
- Requires the `drizzle-orm` and `mysql2` peers. Consolidates duplicated test boilerplate.
3
+ From kit `0.12.0`, DB test helpers are owned by `@rdlabo/workers-mysql`. Install that package and
4
+ `drizzle-orm` when using either package's `/testing` entry point.
4
5
 
5
- | Export | Description |
6
- | --- | --- |
6
+ ## `@rdlabo/workers-mysql/testing`
7
+
8
+ This is the canonical home of the local MySQL/Drizzle test database and database fakes. Install
9
+ `drizzle-orm` when using this entry point; `mysql2` is included by the MySQL package.
10
+
11
+ | Export | Description |
12
+ | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
7
13
  | `createTestDb(options)` / `TestDb` / `CreateTestDbOptions` / `TestDbConnection` | Test database built from committed Drizzle migrations as the single source of truth: `resetSchema` / `createTestPool` / `truncateAll` / `seed` / `mysqlReachable`. |
8
- | `FakeFirebaseVerifier` | In-memory `FirebaseVerifier` for offline route tests (`register` / `verifyIdToken` / `getUser` / `deleteUser`). |
9
- | `createPoolDatabase(options)` / `CreatePoolDatabaseOptions` | A `Database` backed by a single pool used as both primary and replica. |
10
- | `createNoopDatabase()` | A `Database` stub that throws on `write` / `transaction` to catch accidental DB use in DB-less routes. |
11
- | `authHeaders(token, opts?)` | Build interceptor-compatible auth headers for requests. |
12
- | `registerFirebaseToken(firebase, uid, record?, token?)` | Register a token in a `FakeFirebaseVerifier` (no DB). |
13
- | `provisionUser(pool, firebase, opts)` | Register a token and provision a conventional `users(id, firebase_uid, agree)` row; returns the user id (idempotent). |
14
- | `configurableFake(impl, name?)` | Build a test double from a partial implementation; un-stubbed members throw `"${name}.${method} not configured"`. |
15
- | `fakeApiList` / `fakePaymentIntent` / `fakeStripeEvent` / `fakeCheckoutSession` / `fakeCustomer` / `fakePrice` / `fakeSubscription` | Stripe object fixtures with sensible defaults, overridable per test. |
16
- | `fakeKv()` / `fakeQueue()` / `FakeQueue` | In-memory Workers KV / Queues producer doubles (`sent` + `batchCount` on queues for subrequest-bound assertions). |
14
+ | `createPoolDatabase(options)` / `CreatePoolDatabaseOptions` | A `Database` backed by a single pool used as both primary and replica. |
15
+ | `createNoopDatabase()` | A `Database` stub that throws on `write` / `transaction` to catch accidental DB use in DB-less routes. |
16
+
17
+ ## `@rdlabo/workers-hono-kit/testing`
18
+
19
+ Hono, Firebase, Stripe, KV, and Queue test helpers remain owned by the kit. Its DB exports are
20
+ deprecated compatibility re-exports of `@rdlabo/workers-mysql/testing`.
21
+
22
+ Only the promoted DB functions and types carry `@deprecated` notices, with the replacement import
23
+ path shown in editor documentation. Kit-owned helpers such as `FakeFirebaseVerifier`, `fakeKv`,
24
+ and `fakeQueue` are not deprecated. Existing imports remain supported.
25
+
26
+ These compatibility exports are loaded statically. Importing **any** helper from this entry point
27
+ requires `@rdlabo/workers-mysql` and `drizzle-orm`, even when the test does not use a database.
28
+ For candidate tarball installation, see [Development](./development.md).
29
+
30
+ | Export | Description |
31
+ | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
32
+ | `FakeFirebaseVerifier` | In-memory `FirebaseVerifier` for offline route tests (`register` / `verifyIdToken` / `getUser` / `deleteUser`). |
33
+ | `authHeaders(token, opts?)` | Build interceptor-compatible auth headers for requests. |
34
+ | `registerFirebaseToken(firebase, uid, record?, token?)` | Register a token in a `FakeFirebaseVerifier` (no DB). |
35
+ | `provisionUser(pool, firebase, opts)` | Register a token and provision a conventional `users(id, firebase_uid, agree)` row; returns the user id (idempotent). |
36
+ | `configurableFake(impl, name?)` | Build a test double from a partial implementation; un-stubbed members throw `"${name}.${method} not configured"`. |
37
+ | `fakeApiList` / `fakePaymentIntent` / `fakeStripeEvent` / `fakeCheckoutSession` / `fakeCustomer` / `fakePrice` / `fakeSubscription` | Stripe object fixtures with sensible defaults, overridable per test. |
38
+ | `fakeKv()` / `fakeQueue()` / `FakeQueue` | In-memory Workers KV / Queues producer doubles (`sent` + `batchCount` on queues for subrequest-bound assertions). |
39
+
40
+ ## Next step
41
+
42
+ Return to [Testing and Operations](./testing-operations.md), or see
43
+ [`@rdlabo/workers-mysql/testing`](https://docs.rdlabo.dev/projects/workers-mysql/docs/tooling) for
44
+ the canonical DB test helpers.
package/docs/api.md CHANGED
@@ -1,12 +1,35 @@
1
- # API
2
-
3
- `@rdlabo/workers-hono-kit` exposes these entry points. This page maps each entry point to its dedicated reference page. For feature-level examples, see [HTTP and Authentication](./http-auth.md), [Data Layer](./data-layer.md), [Realtime and Offline](./realtime-offline.md), and [Testing and Operations](./testing-operations.md).
4
-
5
- | Entry point | Description | Reference |
6
- | --- | --- | --- |
7
- | `@rdlabo/workers-hono-kit` | Web-standard helpers (middleware, HTTP, Firebase, AWS, AI, Stripe, KV, queues, idempotency). | [Root](./api-root.md) |
8
- | `@rdlabo/workers-hono-kit/db` | MySQL data layer (mysql2 + Drizzle), JST column helpers, baseline migrations. | [DB](./api-db.md) |
9
- | `@rdlabo/workers-hono-kit/business-time` | JST business calendar and date-time conversions. | [Business time](./api-business-time.md) |
10
- | `@rdlabo/workers-hono-kit/offline` | Table-agnostic REST/DB method converters and replica wire helpers. | [Offline](./api-offline.md) |
11
- | `@rdlabo/workers-hono-kit/realtime` | Durable Object WebSocket and retry helpers. | [Realtime and Offline](./realtime-offline.md) |
12
- | `@rdlabo/workers-hono-kit/testing` | Drizzle-backed test DB, fakes, fixtures, and binding doubles. | [Testing](./api-testing.md) |
1
+ # Packages and API entry points
2
+
3
+ Entry-point map for the Hono kit and the standalone MySQL and timezone packages.
4
+
5
+ - [HTTP and Authentication](./http-auth.md)
6
+ - [Data Layer](./data-layer.md)
7
+ - [Realtime and Offline](./realtime-offline.md)
8
+ - [Testing and Operations](./testing-operations.md)
9
+
10
+ ## Standalone packages
11
+
12
+ | Entry point | Description | Reference |
13
+ | ---------------------------------- | -------------------------------------------------------------------------------- | --------------------------------------- |
14
+ | `@rdlabo/workers-hono-kit` | Workers-compatible Hono and infrastructure helpers; no MySQL runtime dependency. | [Root](./api-root.md) |
15
+ | `@rdlabo/workers-mysql` | Canonical Workers MySQL and Hyperdrive data layer. | [DB](./api-db.md) |
16
+ | `@rdlabo/workers-mysql/drizzle` | Optional Drizzle configuration and JST columns. | [DB](./api-db.md) |
17
+ | `@rdlabo/workers-mysql/migrations` | Node.js migration and brownfield baseline helpers. | [DB](./api-db.md) |
18
+ | `@rdlabo/workers-mysql/testing` | Local MySQL/Drizzle test database and fakes. | [Testing](./api-testing.md) |
19
+ | `@rdlabo/workers-timezone` | Canonical IANA calendar and date-time conversions. | [Business time](./api-business-time.md) |
20
+
21
+ ## Hono kit subpaths
22
+
23
+ | Entry point | Description | Reference |
24
+ | ---------------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------- |
25
+ | `@rdlabo/workers-hono-kit/mysql` | Hono container adapter for the MySQL package. | [DB](./api-db.md) |
26
+ | `@rdlabo/workers-hono-kit/db` | Deprecated compatibility re-export of the MySQL package. | [DB](./api-db.md) |
27
+ | `@rdlabo/workers-hono-kit/business-time` | Deprecated compatibility re-export; requires `@rdlabo/workers-timezone`. | [Business time](./api-business-time.md) |
28
+ | `@rdlabo/workers-hono-kit/offline` | Table-agnostic REST/DB method converters and replica wire helpers. | [Offline](./api-offline.md) |
29
+ | `@rdlabo/workers-hono-kit/realtime` | Durable Object WebSocket and retry helpers. | [Realtime and Offline](./realtime-offline.md) |
30
+ | `@rdlabo/workers-hono-kit/testing` | Drizzle-backed test DB, fakes, fixtures, and binding doubles. | [Testing](./api-testing.md) |
31
+
32
+ ## Next step
33
+
34
+ Open the reference linked above for each entry point, or start from
35
+ [HTTP and Authentication](./http-auth.md) for feature-level examples.
package/docs/cli.md CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
  The package ships `bin` commands that can be run with `npx` or wired into npm scripts in the consuming app. They are operational helpers: AWS credential sync, subrequest fan-out gating, and brownfield database baselining.
4
4
 
5
- | Command | Use |
6
- | --- | --- |
7
- | `workers-hono-kit-sync-dev-aws <wrangler-args…>` | Launch `wrangler` with AWS credentials injected as `--var`, resolved from the active AWS profile (honors `AWS_PROFILE`, supports short-lived SSO/temporary creds). Nothing is written to disk — replaces `.dev.vars`. Wire it as the `dev` script, e.g. `AWS_PROFILE=<p> workers-hono-kit-sync-dev-aws dev --var APP_ENV:development`. |
8
- | `workers-hono-kit-check-subrequest-fanout [dir…]` | CI gate that greps for per-item external-call fan-outs (`runWithConcurrency(` / `PromisePool` / `.withConcurrency(`) that would eventually exceed the Workers subrequest cap. Annotate a genuinely-safe site with `subrequest-ok`. Scans `src` by default; exits 1 on an un-annotated marker. |
9
- | `workers-hono-kit-db-baseline [--migrations ./drizzle]` | Brownfield first-deploy helper: record the baseline `0000` migration as *already applied* on an existing MySQL DB without running its DDL (the CLI wrapper around `baselineMigrations` / `readBaselineEntry`). Reads DB credentials from `DB_SECRET` (AWS RDS managed secret) or the individual `DB_*` env vars. |
5
+ | Command | Use |
6
+ | ------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
7
+ | `workers-hono-kit-sync-dev-aws <wrangler-args…>` | Launch `wrangler` with AWS credentials injected as `--var`, resolved from the active AWS profile (honors `AWS_PROFILE`, supports short-lived SSO/temporary creds). Nothing is written to disk — replaces `.dev.vars`. Wire it as the `dev` script, e.g. `AWS_PROFILE=<p> workers-hono-kit-sync-dev-aws dev --var APP_ENV:development`. |
8
+ | `workers-hono-kit-check-subrequest-fanout [dir…]` | CI gate that greps for per-item external-call fan-outs (`runWithConcurrency(` / `PromisePool` / `.withConcurrency(`) that would eventually exceed the Workers subrequest cap. Annotate a genuinely-safe site with `subrequest-ok`. Scans `src` by default; exits 1 on an un-annotated marker. |
9
+ | `workers-mysql-db-baseline [--migrations ./drizzle]` | Brownfield first-deploy helper from `@rdlabo/workers-mysql`: record the baseline `0000` migration as _already applied_ on an existing MySQL DB without running its DDL (the CLI wrapper around `baselineMigrations` / `readBaselineEntry`). Reads DB credentials from `DB_SECRET` (AWS RDS managed secret) or the individual `DB_*` env vars. |
10
+ | `workers-hono-kit-db-baseline [--migrations ./drizzle]` | Deprecated compatibility alias for `workers-mysql-db-baseline`. |
10
11
 
11
12
  ## `workers-hono-kit-sync-dev-aws`
12
13
 
@@ -24,10 +25,13 @@ Run this in CI to catch per-item external call patterns (for example `runWithCon
24
25
  workers-hono-kit-check-subrequest-fanout src
25
26
  ```
26
27
 
27
- ## `workers-hono-kit-db-baseline`
28
+ ## `workers-mysql-db-baseline`
28
29
 
29
30
  Use this for a brownfield first deploy against a MySQL database that already matches the schema in your first migration (`0000_*`). It records that migration as already applied without running its DDL, so later migrations can apply normally. Database credentials are read from `DB_SECRET` (an AWS RDS managed-secret JSON string) or from individual `DB_*` environment variables.
30
31
 
31
32
  ```bash
32
- workers-hono-kit-db-baseline --migrations ./drizzle
33
+ workers-mysql-db-baseline --migrations ./drizzle
33
34
  ```
35
+
36
+ Existing scripts may continue to call `workers-hono-kit-db-baseline` during migration, but new
37
+ integrations should use the command owned by `@rdlabo/workers-mysql`.