@rdlabo/workers-hono-kit 0.12.2 → 0.12.3-beta.pr51.sha3289ea609bb4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -17
- package/dist/firebase/social-auth.d.ts +90 -0
- package/dist/firebase/social-auth.js +108 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/docs/api-db.md +1 -1
- package/docs/data-layer.md +22 -25
- package/docs/http-auth.md +0 -5
- package/docs/quickstart.md +71 -0
- package/docs/realtime-offline.md +0 -5
- package/docs/testing-operations.md +0 -5
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,24 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Shared Hono building blocks for Cloudflare Workers APIs: weak ETags, NestJS-shaped validation and
|
|
4
4
|
error bodies, Firebase auth middleware, AWS helpers, AI Gateway wiring, Stripe, KV, queues,
|
|
5
|
-
realtime, and offline contracts.
|
|
6
|
-
application.
|
|
5
|
+
realtime, and offline contracts.
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
| Import | Responsibility |
|
|
11
|
-
| ---------------------------------------- | -------------------------------------------------------------------- |
|
|
12
|
-
| `@rdlabo/workers-hono-kit` | HTTP, auth, Firebase, AWS, AI, Stripe, KV, and queue primitives |
|
|
13
|
-
| `@rdlabo/workers-hono-kit/mysql` | Hono container adapter for `@rdlabo/workers-mysql` |
|
|
14
|
-
| `@rdlabo/workers-hono-kit/offline` | Offline replica wire, cursor, journal, and compatibility contracts |
|
|
15
|
-
| `@rdlabo/workers-hono-kit/realtime` | Durable Object WebSocket and retry helpers |
|
|
16
|
-
| `@rdlabo/workers-hono-kit/testing` | Auth helpers, fakes, Stripe fixtures, and compatibility test exports |
|
|
17
|
-
| `@rdlabo/workers-hono-kit/db` | Deprecated compatibility path for `@rdlabo/workers-mysql` |
|
|
18
|
-
| `@rdlabo/workers-hono-kit/business-time` | Deprecated compatibility path for `@rdlabo/workers-timezone` |
|
|
19
|
-
|
|
20
|
-
The root entry point does not load MySQL, Drizzle, or Node-only migration modules. MySQL consumers
|
|
21
|
-
install the standalone package, which owns `mysql2`; Hono-specific wiring stays in the `/mysql`
|
|
22
|
-
adapter.
|
|
7
|
+
[Try a Hono API locally](./docs/quickstart.md): send a health request, inspect its weak ETag, and see the missing-route JSON response. No Cloudflare account or open port is needed for the first exercise.
|
|
23
8
|
|
|
24
9
|
## Install
|
|
25
10
|
|
|
@@ -72,6 +57,22 @@ app.get('/health', (c) => c.json({ ok: true }));
|
|
|
72
57
|
export default app;
|
|
73
58
|
```
|
|
74
59
|
|
|
60
|
+
## Choose an entry point
|
|
61
|
+
|
|
62
|
+
| Import | Responsibility |
|
|
63
|
+
| ---------------------------------------- | -------------------------------------------------------------------- |
|
|
64
|
+
| `@rdlabo/workers-hono-kit` | HTTP, auth, Firebase, AWS, AI, Stripe, KV, and queue primitives |
|
|
65
|
+
| `@rdlabo/workers-hono-kit/mysql` | Hono container adapter for `@rdlabo/workers-mysql` |
|
|
66
|
+
| `@rdlabo/workers-hono-kit/offline` | Offline replica wire, cursor, journal, and compatibility contracts |
|
|
67
|
+
| `@rdlabo/workers-hono-kit/realtime` | Durable Object WebSocket and retry helpers |
|
|
68
|
+
| `@rdlabo/workers-hono-kit/testing` | Auth helpers, fakes, Stripe fixtures, and compatibility test exports |
|
|
69
|
+
| `@rdlabo/workers-hono-kit/db` | Deprecated compatibility path for `@rdlabo/workers-mysql` |
|
|
70
|
+
| `@rdlabo/workers-hono-kit/business-time` | Deprecated compatibility path for `@rdlabo/workers-timezone` |
|
|
71
|
+
|
|
72
|
+
The root entry point does not load MySQL, Drizzle, or Node-only migration modules. MySQL consumers
|
|
73
|
+
install the standalone package, which owns `mysql2`; Hono-specific wiring stays in the `/mysql`
|
|
74
|
+
adapter.
|
|
75
|
+
|
|
75
76
|
### Compatibility import deprecations
|
|
76
77
|
|
|
77
78
|
Kit `/db`, `/business-time`, and the DB-related `/testing` exports (`createTestDb`, pool/noop
|
|
@@ -81,6 +82,33 @@ compatibility aliases keep the same runtime identity and signatures; there is no
|
|
|
81
82
|
Kit-owned helpers such as `reopenGuardedPaymentFailedSet`, `/mysql` `createContainerRuntime`, and
|
|
82
83
|
Firebase/auth/KV/Stripe test helpers are not deprecated by this migration.
|
|
83
84
|
|
|
85
|
+
### Social auth (Google / Apple)
|
|
86
|
+
|
|
87
|
+
Root exports `verifyGoogleIdentityToken`, `verifyAppleIdentityToken`,
|
|
88
|
+
`hasFirebaseProviderIdentity`, and `createAppleClientSecret` cover reusable OIDC subject
|
|
89
|
+
verification, Firebase identity matching, and Apple `client_secret` signing. Applications keep
|
|
90
|
+
ownership of HTTP responses, persistence, and provider token exchange or revocation.
|
|
91
|
+
|
|
92
|
+
`hasFirebaseProviderIdentity` expects a **verified** Firebase ID-token payload. Pass
|
|
93
|
+
`requireSignInProvider: true` when an endpoint requires sign-in through that provider
|
|
94
|
+
(subject linked **and** `firebase.sign_in_provider` matches). Leave it `false` (default)
|
|
95
|
+
for link/unlink checks or combined login/link endpoints that also accept sessions established
|
|
96
|
+
through another provider. This policy belongs to the application.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
import { hasFirebaseProviderIdentity, verifyGoogleIdentityToken } from '@rdlabo/workers-hono-kit';
|
|
100
|
+
|
|
101
|
+
// firebaseVerifier is the application's configured FirebaseVerifier.
|
|
102
|
+
const verifiedFirebaseToken = await firebaseVerifier.verifyIdToken(firebaseIdToken);
|
|
103
|
+
const subject = await verifyGoogleIdentityToken(googleIdToken, GOOGLE_CLIENT_IDS);
|
|
104
|
+
// login: require active Google sign-in
|
|
105
|
+
if (!hasFirebaseProviderIdentity(verifiedFirebaseToken, 'google.com', subject, true)) {
|
|
106
|
+
return c.json({ error: 'Google identity mismatch' }, 401);
|
|
107
|
+
}
|
|
108
|
+
// link / unlink: subject present is enough
|
|
109
|
+
// hasFirebaseProviderIdentity(verifiedFirebaseToken, 'google.com', subject)
|
|
110
|
+
```
|
|
111
|
+
|
|
84
112
|
## Documentation
|
|
85
113
|
|
|
86
114
|
- [HTTP and Authentication](https://docs.rdlabo.dev/projects/workers-hono-kit/docs/http-auth)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { JWTVerifyGetKey } from 'jose';
|
|
2
|
+
import type { DecodedIdToken } from './firebase-verifier.js';
|
|
3
|
+
/** Apple Sign In OIDC issuer (`iss`) expected by {@link verifyAppleIdentityToken}. */
|
|
4
|
+
export declare const APPLE_IDENTITY_ISSUER = "https://appleid.apple.com";
|
|
5
|
+
/**
|
|
6
|
+
* Google OIDC issuers accepted by {@link verifyGoogleIdentityToken}.
|
|
7
|
+
*
|
|
8
|
+
* Google issues tokens with either the HTTPS form or the host-only form of
|
|
9
|
+
* `accounts.google.com`; both are listed so verification matches either claim.
|
|
10
|
+
*/
|
|
11
|
+
export declare const GOOGLE_IDENTITY_ISSUERS: readonly ["https://accounts.google.com", "accounts.google.com"];
|
|
12
|
+
/**
|
|
13
|
+
* Verify an Apple identity token and return its `sub` (Apple user id).
|
|
14
|
+
*
|
|
15
|
+
* Checks signature against Apple's JWKS (or an injected key resolver), enforces
|
|
16
|
+
* {@link APPLE_IDENTITY_ISSUER}, and requires `aud` to equal the configured Services ID /
|
|
17
|
+
* bundle id (`audience`).
|
|
18
|
+
*
|
|
19
|
+
* @param idToken - Raw Apple identity token JWT from Sign in with Apple.
|
|
20
|
+
* @param audience - Expected `aud` claim (Apple Services ID or native bundle id).
|
|
21
|
+
* @param getKey - Optional JWKS / key resolver; defaults to Apple's remote JWKS. Inject a
|
|
22
|
+
* static key in tests to avoid network I/O.
|
|
23
|
+
* @returns The token `sub` (Apple user identifier).
|
|
24
|
+
* @throws If signature, issuer, audience, or expiry fail verification, or `sub` is missing.
|
|
25
|
+
*/
|
|
26
|
+
export declare const verifyAppleIdentityToken: (idToken: string, audience: string, getKey?: JWTVerifyGetKey) => Promise<string>;
|
|
27
|
+
/**
|
|
28
|
+
* Verify a Google identity token and return its `sub` (Google user id).
|
|
29
|
+
*
|
|
30
|
+
* Checks signature against Google's OAuth2 certs (or an injected key resolver), accepts either
|
|
31
|
+
* issuer in {@link GOOGLE_IDENTITY_ISSUERS}, and requires `aud` to match the configured OAuth
|
|
32
|
+
* client id(s) (`audience`).
|
|
33
|
+
*
|
|
34
|
+
* @param idToken - Raw Google ID token JWT.
|
|
35
|
+
* @param audience - Expected `aud` claim: a single OAuth client id, or a list when native and
|
|
36
|
+
* web clients share one login endpoint.
|
|
37
|
+
* @param getKey - Optional JWKS / key resolver; defaults to Google's remote certs. Inject a
|
|
38
|
+
* static key in tests to avoid network I/O.
|
|
39
|
+
* @returns The token `sub` (Google user identifier).
|
|
40
|
+
* @throws If signature, issuer, audience, or expiry fail verification, or `sub` is missing.
|
|
41
|
+
*/
|
|
42
|
+
export declare const verifyGoogleIdentityToken: (idToken: string, audience: string | readonly string[], getKey?: JWTVerifyGetKey) => Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Return whether a **verified** Firebase ID token already carries the given provider subject.
|
|
45
|
+
*
|
|
46
|
+
* Reads `firebase.identities[providerId]` for `subject`. When `requireSignInProvider` is true,
|
|
47
|
+
* also requires `firebase.sign_in_provider === providerId` so the session was established with
|
|
48
|
+
* that provider (login), not merely that the identity is linked while signed in another way.
|
|
49
|
+
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* `token` must already be a verified Firebase ID-token payload (for example from
|
|
52
|
+
* {@link FirebaseVerifier.verifyIdToken} or auth middleware). This helper does not verify the
|
|
53
|
+
* Firebase JWT itself.
|
|
54
|
+
*
|
|
55
|
+
* Typical policy:
|
|
56
|
+
* - **Login** (`requireSignInProvider: true`): subject present and active sign-in provider matches.
|
|
57
|
+
* - **Link / unlink / linkage checks** (default `false`): subject present in identities only.
|
|
58
|
+
*
|
|
59
|
+
* @param token - Verified Firebase ID-token payload (`DecodedIdToken`).
|
|
60
|
+
* @param providerId - Firebase provider id (e.g. `'google.com'`, `'apple.com'`).
|
|
61
|
+
* @param subject - Provider subject previously returned by {@link verifyGoogleIdentityToken} or
|
|
62
|
+
* {@link verifyAppleIdentityToken}.
|
|
63
|
+
* @param requireSignInProvider - When `true`, also require `sign_in_provider === providerId`.
|
|
64
|
+
* @returns `true` when the identity (and optional active provider) matches.
|
|
65
|
+
*/
|
|
66
|
+
export declare const hasFirebaseProviderIdentity: (token: DecodedIdToken, providerId: string, subject: string, requireSignInProvider?: boolean) => boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Apple developer credentials used to mint a Sign in with Apple `client_secret` JWT.
|
|
69
|
+
*/
|
|
70
|
+
export interface AppleClientSecretConfig {
|
|
71
|
+
/** PEM-encoded PKCS#8 ES256 private key from the Apple developer key. */
|
|
72
|
+
privateKey: string;
|
|
73
|
+
/** Key id (`kid`) of the Apple developer key. */
|
|
74
|
+
keyId: string;
|
|
75
|
+
/** Apple Team ID used as the JWT `iss` claim. */
|
|
76
|
+
teamId: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Create a short-lived Sign in with Apple `client_secret` (ES256 JWT).
|
|
80
|
+
*
|
|
81
|
+
* The JWT is issued for `clientId` as `sub`, audience {@link APPLE_IDENTITY_ISSUER}, and expires
|
|
82
|
+
* 120 seconds after `now`. Used for Apple's token and revoke endpoints.
|
|
83
|
+
*
|
|
84
|
+
* @param config - Apple Team ID, key id, and PKCS#8 private key.
|
|
85
|
+
* @param clientId - Apple Services ID or native bundle id (`sub` claim).
|
|
86
|
+
* @param now - Unix time in seconds for `iat` / `exp`; injectable for deterministic tests.
|
|
87
|
+
* Defaults to the system clock.
|
|
88
|
+
* @returns A compact ES256 JWT suitable as Apple's `client_secret`.
|
|
89
|
+
*/
|
|
90
|
+
export declare const createAppleClientSecret: (config: AppleClientSecretConfig, clientId: string, now?: number) => Promise<string>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { SignJWT, createRemoteJWKSet, importPKCS8, jwtVerify } from 'jose';
|
|
2
|
+
/** Apple Sign In OIDC issuer (`iss`) expected by {@link verifyAppleIdentityToken}. */
|
|
3
|
+
export const APPLE_IDENTITY_ISSUER = 'https://appleid.apple.com';
|
|
4
|
+
/**
|
|
5
|
+
* Google OIDC issuers accepted by {@link verifyGoogleIdentityToken}.
|
|
6
|
+
*
|
|
7
|
+
* Google issues tokens with either the HTTPS form or the host-only form of
|
|
8
|
+
* `accounts.google.com`; both are listed so verification matches either claim.
|
|
9
|
+
*/
|
|
10
|
+
export const GOOGLE_IDENTITY_ISSUERS = ['https://accounts.google.com', 'accounts.google.com'];
|
|
11
|
+
const appleJwks = createRemoteJWKSet(new URL(`${APPLE_IDENTITY_ISSUER}/auth/keys`));
|
|
12
|
+
const googleJwks = createRemoteJWKSet(new URL('https://www.googleapis.com/oauth2/v3/certs'));
|
|
13
|
+
/**
|
|
14
|
+
* Verify an Apple identity token and return its `sub` (Apple user id).
|
|
15
|
+
*
|
|
16
|
+
* Checks signature against Apple's JWKS (or an injected key resolver), enforces
|
|
17
|
+
* {@link APPLE_IDENTITY_ISSUER}, and requires `aud` to equal the configured Services ID /
|
|
18
|
+
* bundle id (`audience`).
|
|
19
|
+
*
|
|
20
|
+
* @param idToken - Raw Apple identity token JWT from Sign in with Apple.
|
|
21
|
+
* @param audience - Expected `aud` claim (Apple Services ID or native bundle id).
|
|
22
|
+
* @param getKey - Optional JWKS / key resolver; defaults to Apple's remote JWKS. Inject a
|
|
23
|
+
* static key in tests to avoid network I/O.
|
|
24
|
+
* @returns The token `sub` (Apple user identifier).
|
|
25
|
+
* @throws If signature, issuer, audience, or expiry fail verification, or `sub` is missing.
|
|
26
|
+
*/
|
|
27
|
+
export const verifyAppleIdentityToken = async (idToken, audience, getKey = appleJwks) => {
|
|
28
|
+
const { payload } = await jwtVerify(idToken, getKey, { issuer: APPLE_IDENTITY_ISSUER, audience });
|
|
29
|
+
if (!payload.sub) {
|
|
30
|
+
throw new Error('Apple identity token has no subject');
|
|
31
|
+
}
|
|
32
|
+
return payload.sub;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Verify a Google identity token and return its `sub` (Google user id).
|
|
36
|
+
*
|
|
37
|
+
* Checks signature against Google's OAuth2 certs (or an injected key resolver), accepts either
|
|
38
|
+
* issuer in {@link GOOGLE_IDENTITY_ISSUERS}, and requires `aud` to match the configured OAuth
|
|
39
|
+
* client id(s) (`audience`).
|
|
40
|
+
*
|
|
41
|
+
* @param idToken - Raw Google ID token JWT.
|
|
42
|
+
* @param audience - Expected `aud` claim: a single OAuth client id, or a list when native and
|
|
43
|
+
* web clients share one login endpoint.
|
|
44
|
+
* @param getKey - Optional JWKS / key resolver; defaults to Google's remote certs. Inject a
|
|
45
|
+
* static key in tests to avoid network I/O.
|
|
46
|
+
* @returns The token `sub` (Google user identifier).
|
|
47
|
+
* @throws If signature, issuer, audience, or expiry fail verification, or `sub` is missing.
|
|
48
|
+
*/
|
|
49
|
+
export const verifyGoogleIdentityToken = async (idToken, audience, getKey = googleJwks) => {
|
|
50
|
+
const { payload } = await jwtVerify(idToken, getKey, {
|
|
51
|
+
issuer: [...GOOGLE_IDENTITY_ISSUERS],
|
|
52
|
+
audience: typeof audience === 'string' ? audience : [...audience],
|
|
53
|
+
});
|
|
54
|
+
if (!payload.sub) {
|
|
55
|
+
throw new Error('Google identity token has no subject');
|
|
56
|
+
}
|
|
57
|
+
return payload.sub;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Return whether a **verified** Firebase ID token already carries the given provider subject.
|
|
61
|
+
*
|
|
62
|
+
* Reads `firebase.identities[providerId]` for `subject`. When `requireSignInProvider` is true,
|
|
63
|
+
* also requires `firebase.sign_in_provider === providerId` so the session was established with
|
|
64
|
+
* that provider (login), not merely that the identity is linked while signed in another way.
|
|
65
|
+
*
|
|
66
|
+
* @remarks
|
|
67
|
+
* `token` must already be a verified Firebase ID-token payload (for example from
|
|
68
|
+
* {@link FirebaseVerifier.verifyIdToken} or auth middleware). This helper does not verify the
|
|
69
|
+
* Firebase JWT itself.
|
|
70
|
+
*
|
|
71
|
+
* Typical policy:
|
|
72
|
+
* - **Login** (`requireSignInProvider: true`): subject present and active sign-in provider matches.
|
|
73
|
+
* - **Link / unlink / linkage checks** (default `false`): subject present in identities only.
|
|
74
|
+
*
|
|
75
|
+
* @param token - Verified Firebase ID-token payload (`DecodedIdToken`).
|
|
76
|
+
* @param providerId - Firebase provider id (e.g. `'google.com'`, `'apple.com'`).
|
|
77
|
+
* @param subject - Provider subject previously returned by {@link verifyGoogleIdentityToken} or
|
|
78
|
+
* {@link verifyAppleIdentityToken}.
|
|
79
|
+
* @param requireSignInProvider - When `true`, also require `sign_in_provider === providerId`.
|
|
80
|
+
* @returns `true` when the identity (and optional active provider) matches.
|
|
81
|
+
*/
|
|
82
|
+
export const hasFirebaseProviderIdentity = (token, providerId, subject, requireSignInProvider = false) => {
|
|
83
|
+
const firebase = token;
|
|
84
|
+
const subjects = firebase.firebase?.identities?.[providerId];
|
|
85
|
+
return (Array.isArray(subjects) &&
|
|
86
|
+
subjects.includes(subject) &&
|
|
87
|
+
(!requireSignInProvider || firebase.firebase?.sign_in_provider === providerId));
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Create a short-lived Sign in with Apple `client_secret` (ES256 JWT).
|
|
91
|
+
*
|
|
92
|
+
* The JWT is issued for `clientId` as `sub`, audience {@link APPLE_IDENTITY_ISSUER}, and expires
|
|
93
|
+
* 120 seconds after `now`. Used for Apple's token and revoke endpoints.
|
|
94
|
+
*
|
|
95
|
+
* @param config - Apple Team ID, key id, and PKCS#8 private key.
|
|
96
|
+
* @param clientId - Apple Services ID or native bundle id (`sub` claim).
|
|
97
|
+
* @param now - Unix time in seconds for `iat` / `exp`; injectable for deterministic tests.
|
|
98
|
+
* Defaults to the system clock.
|
|
99
|
+
* @returns A compact ES256 JWT suitable as Apple's `client_secret`.
|
|
100
|
+
*/
|
|
101
|
+
export const createAppleClientSecret = async (config, clientId, now = Math.floor(Date.now() / 1000)) => new SignJWT({})
|
|
102
|
+
.setProtectedHeader({ alg: 'ES256', kid: config.keyId })
|
|
103
|
+
.setIssuer(config.teamId)
|
|
104
|
+
.setIssuedAt(now)
|
|
105
|
+
.setExpirationTime(now + 120)
|
|
106
|
+
.setAudience(APPLE_IDENTITY_ISSUER)
|
|
107
|
+
.setSubject(clientId)
|
|
108
|
+
.sign(await importPKCS8(config.privateKey, 'ES256'));
|
package/dist/index.d.ts
CHANGED
|
@@ -91,3 +91,5 @@ export { FirebaseIdTokenValidationError, JoseFirebaseVerifier, SECURETOKEN_JWK_U
|
|
|
91
91
|
export { IdentityToolkit } from './firebase/identity-toolkit.js';
|
|
92
92
|
export type { ServiceAccount } from './firebase/identity-toolkit.js';
|
|
93
93
|
export { createRemoteFirebaseVerifier, createServiceAccountVerifier } from './firebase/remote-verifier.js';
|
|
94
|
+
export { APPLE_IDENTITY_ISSUER, GOOGLE_IDENTITY_ISSUERS, createAppleClientSecret, hasFirebaseProviderIdentity, verifyAppleIdentityToken, verifyGoogleIdentityToken, } from './firebase/social-auth.js';
|
|
95
|
+
export type { AppleClientSecretConfig } from './firebase/social-auth.js';
|
package/dist/index.js
CHANGED
|
@@ -67,3 +67,4 @@ export { getTemporaryCredentials } from './aws/sts.js';
|
|
|
67
67
|
export { FirebaseIdTokenValidationError, JoseFirebaseVerifier, SECURETOKEN_JWK_URL, } from './firebase/jose-firebase-verifier.js';
|
|
68
68
|
export { IdentityToolkit } from './firebase/identity-toolkit.js';
|
|
69
69
|
export { createRemoteFirebaseVerifier, createServiceAccountVerifier } from './firebase/remote-verifier.js';
|
|
70
|
+
export { APPLE_IDENTITY_ISSUER, GOOGLE_IDENTITY_ISSUERS, createAppleClientSecret, hasFirebaseProviderIdentity, verifyAppleIdentityToken, verifyGoogleIdentityToken, } from './firebase/social-auth.js';
|
package/docs/api-db.md
CHANGED
|
@@ -27,7 +27,7 @@ compatibility_flags = ["nodejs_compat"]
|
|
|
27
27
|
| `Database` / `DisposableDatabase` / `HyperdriveDatabase` / `ReadTransaction` / `QueryRunner` / `TxOf` | The `read` / `query` / `readTransaction` / `write` / `transaction` API and its supporting types. |
|
|
28
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
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
|
|
30
|
+
| `retryWhenDeadlock(fn, retries?, delay?)` | Retry on MySQL `ER_LOCK_DEADLOCK` with a wait of `delay × attempt` between attempts. |
|
|
31
31
|
| `insertIdOf` / `affectedRowsOf` / `insertedIdsOf` / `DzWriteResult` | Extract `insertId` / `affectedRows` (and derive contiguous bulk-insert ids) from a mysql2 write result. |
|
|
32
32
|
| `toJstDate` / `jstTimestampParams` / `jstDatetimeParams` / `jstDateParams` | JST date/time normalization params (advanced use). |
|
|
33
33
|
| `MYSQL_TIMEZONE` | Default mysql2 connection `timezone` (`'+09:00'`) for the JST DB deployment. |
|
package/docs/data-layer.md
CHANGED
|
@@ -3,11 +3,6 @@
|
|
|
3
3
|
Standalone MySQL / Hyperdrive access for Workers, plus the thin Hono container adapter. Fixed
|
|
4
4
|
`+09:00` storage helpers are independent of IANA display timezones.
|
|
5
5
|
|
|
6
|
-
- [HTTP and Authentication](./http-auth.md)
|
|
7
|
-
- [Realtime and Offline](./realtime-offline.md)
|
|
8
|
-
- [Testing and Operations](./testing-operations.md)
|
|
9
|
-
- [API reference](./api.md)
|
|
10
|
-
|
|
11
6
|
Import database helpers from `@rdlabo/workers-mysql`. The package installs `mysql2` directly. Add
|
|
12
7
|
`drizzle-orm` only when using the `/drizzle` or `/testing` entry point. The old
|
|
13
8
|
`@rdlabo/workers-hono-kit/db` path is a deprecated compatibility re-export.
|
|
@@ -26,27 +21,11 @@ npm install -D @types/node@20
|
|
|
26
21
|
|
|
27
22
|
For candidate tarball installation, see [Development](./development.md).
|
|
28
23
|
|
|
29
|
-
## Migrating from workers-hono-kit
|
|
30
|
-
|
|
31
|
-
The package boundary is a breaking change in `0.12.0`. Update these imports before
|
|
32
|
-
upgrading:
|
|
33
|
-
|
|
34
|
-
| Current import | Replacement |
|
|
35
|
-
| ------------------------------------------------------- | ----------------------------------------------------- |
|
|
36
|
-
| `createContainerRuntime` from the kit root | `@rdlabo/workers-hono-kit/mysql` |
|
|
37
|
-
| `retryWhenDeadlock` from the kit root | `@rdlabo/workers-mysql` |
|
|
38
|
-
| DB helpers from `@rdlabo/workers-hono-kit/db` | `@rdlabo/workers-mysql`, `/drizzle`, or `/migrations` |
|
|
39
|
-
| DB test helpers from `@rdlabo/workers-hono-kit/testing` | `@rdlabo/workers-mysql/testing` |
|
|
40
|
-
|
|
41
|
-
The old `/db` and DB-related `/testing` exports remain available for backward compatibility.
|
|
42
|
-
Their individual functions and types carry `@deprecated` notices pointing to the standalone
|
|
43
|
-
package. No removal release is scheduled. The kit-owned `/mysql` adapter is not deprecated.
|
|
44
|
-
Because `/testing` statically re-exports DB helpers, all kit `/testing` consumers must install the
|
|
45
|
-
MySQL package and `drizzle-orm`, including consumers of non-DB helpers such as Firebase or KV fakes.
|
|
46
|
-
|
|
47
24
|
## Hyperdrive database
|
|
48
25
|
|
|
49
|
-
`createHyperdriveDatabase()` lazily opens primary and replica connections from Hyperdrive bindings.
|
|
26
|
+
`createHyperdriveDatabase()` lazily opens primary and replica connections from Hyperdrive bindings.
|
|
27
|
+
For read/write paths, retry boundaries, and invocation lifetime, see
|
|
28
|
+
[Workers MySQL Runtime](https://docs.rdlabo.dev/projects/workers-mysql/docs/runtime).
|
|
50
29
|
|
|
51
30
|
```ts
|
|
52
31
|
import { createHyperdriveDatabase } from '@rdlabo/workers-mysql';
|
|
@@ -84,7 +63,7 @@ import { createContainerRuntime } from '@rdlabo/workers-hono-kit/mysql';
|
|
|
84
63
|
|
|
85
64
|
## Writes and retries
|
|
86
65
|
|
|
87
|
-
- `retryWhenDeadlock()` retries `ER_LOCK_DEADLOCK` with
|
|
66
|
+
- `retryWhenDeadlock()` retries `ER_LOCK_DEADLOCK` with a wait of `delay × attempt` between attempts.
|
|
88
67
|
- `insertIdOf()`, `affectedRowsOf()`, and `insertedIdsOf()` normalize mysql2 write results.
|
|
89
68
|
- `withMysqlConnections()` opens primary and replica connections in parallel for a scoped operation.
|
|
90
69
|
|
|
@@ -110,6 +89,24 @@ addBusinessDays('2026-07-06', 3);
|
|
|
110
89
|
// '2026-07-09'
|
|
111
90
|
```
|
|
112
91
|
|
|
92
|
+
## Migrating from workers-hono-kit
|
|
93
|
+
|
|
94
|
+
The package boundary is a breaking change in `0.12.0`. Update these imports before
|
|
95
|
+
upgrading:
|
|
96
|
+
|
|
97
|
+
| Current import | Replacement |
|
|
98
|
+
| ------------------------------------------------------- | ----------------------------------------------------- |
|
|
99
|
+
| `createContainerRuntime` from the kit root | `@rdlabo/workers-hono-kit/mysql` |
|
|
100
|
+
| `retryWhenDeadlock` from the kit root | `@rdlabo/workers-mysql` |
|
|
101
|
+
| DB helpers from `@rdlabo/workers-hono-kit/db` | `@rdlabo/workers-mysql`, `/drizzle`, or `/migrations` |
|
|
102
|
+
| DB test helpers from `@rdlabo/workers-hono-kit/testing` | `@rdlabo/workers-mysql/testing` |
|
|
103
|
+
|
|
104
|
+
The old `/db` and DB-related `/testing` exports remain available for backward compatibility.
|
|
105
|
+
Their individual functions and types carry `@deprecated` notices pointing to the standalone
|
|
106
|
+
package. No removal release is scheduled. The kit-owned `/mysql` adapter is not deprecated.
|
|
107
|
+
Because `/testing` statically re-exports DB helpers, all kit `/testing` consumers must install the
|
|
108
|
+
MySQL package and `drizzle-orm`, including consumers of non-DB helpers such as Firebase or KV fakes.
|
|
109
|
+
|
|
113
110
|
## Next step
|
|
114
111
|
|
|
115
112
|
Continue to [Realtime and Offline](./realtime-offline.md), or see
|
package/docs/http-auth.md
CHANGED
|
@@ -3,11 +3,6 @@
|
|
|
3
3
|
Validation, Firebase authentication, shared NestJS-shaped error bodies, and response finalization
|
|
4
4
|
for Hono Workers APIs.
|
|
5
5
|
|
|
6
|
-
- [Data Layer](./data-layer.md)
|
|
7
|
-
- [Realtime and Offline](./realtime-offline.md)
|
|
8
|
-
- [Testing and Operations](./testing-operations.md)
|
|
9
|
-
- [API reference](./api.md)
|
|
10
|
-
|
|
11
6
|
## Validation
|
|
12
7
|
|
|
13
8
|
`validate(target, schema, options?)` adapts a Zod schema to Hono and returns a NestJS `ValidationPipe`-shaped `400` response. Use `createValidate({ sentry })` to bind optional reporting once.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Try a Hono API locally
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Give a Hono API consistent HTTP behavior: a health response with a weak ETag and a predictable JSON response for missing routes. You will see both without opening a port or creating a Cloudflare account.
|
|
6
|
+
|
|
7
|
+
## 1. Create a small project
|
|
8
|
+
|
|
9
|
+
Use Node.js 24 and npm for this exercise. The commands pin the documented kit release. npm installs its required peers; package managers configured to omit peers must follow the [installation requirements](../README.md).
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
mkdir hono-kit-demo
|
|
13
|
+
cd hono-kit-demo
|
|
14
|
+
npm init -y
|
|
15
|
+
npm pkg set type=module
|
|
16
|
+
npm install @rdlabo/workers-hono-kit@0.12.2 hono@4
|
|
17
|
+
npm install --save-dev tsx@4
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 2. Send two requests
|
|
21
|
+
|
|
22
|
+
Save this as `demo.ts`. `app.request()` exercises the Hono app in the current process.
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { Hono } from 'hono';
|
|
26
|
+
import { createAppErrorHandler, finalizeResponse, notFoundHandler } from '@rdlabo/workers-hono-kit';
|
|
27
|
+
|
|
28
|
+
const app = new Hono();
|
|
29
|
+
app.use('*', finalizeResponse());
|
|
30
|
+
app.onError(createAppErrorHandler());
|
|
31
|
+
app.notFound(notFoundHandler);
|
|
32
|
+
app.get('/health', (c) => c.json({ ok: true }));
|
|
33
|
+
|
|
34
|
+
const healthy = await app.request('/health');
|
|
35
|
+
console.log(healthy.status, await healthy.text());
|
|
36
|
+
console.log('weak etag:', healthy.headers.get('etag')?.startsWith('W/'));
|
|
37
|
+
|
|
38
|
+
const missing = await app.request('/missing');
|
|
39
|
+
console.log(missing.status, await missing.text());
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
npx tsx demo.ts
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Expected output (JSON property order does not matter):
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
200 {"ok":true}
|
|
50
|
+
weak etag: true
|
|
51
|
+
404 {"message":"Cannot GET /missing","error":"Not Found","statusCode":404}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
You have checked the HTTP behavior added by the kit. This exercise does not provision Workers bindings, authenticate users, or test a deployed service.
|
|
55
|
+
|
|
56
|
+
## 3. Move into your application
|
|
57
|
+
|
|
58
|
+
Keep the middleware and route registrations, remove the demonstration requests, and export `app` as your Worker handler. Continue with [HTTP and authentication](./http-auth.md). Add the [MySQL adapter](./data-layer.md) only when the application needs a database.
|
|
59
|
+
|
|
60
|
+
Existing kit users should check [the 0.12 import migration](./data-layer.md) before upgrading. The old `/db` and `/business-time` paths are compatibility exports; new integrations use the standalone packages.
|
|
61
|
+
|
|
62
|
+
## Next steps
|
|
63
|
+
|
|
64
|
+
| You need | Start with |
|
|
65
|
+
| ------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
66
|
+
| Shared Hono HTTP, auth, or queue behavior | `@rdlabo/workers-hono-kit` |
|
|
67
|
+
| MySQL access, with or without Hono | [Workers MySQL](https://docs.rdlabo.dev/projects/workers-mysql/docs/quickstart) |
|
|
68
|
+
| Timezone conversions and checks on new code | [Workers Timezone + ESLint](https://docs.rdlabo.dev/projects/workers-timezone/docs/quickstart) |
|
|
69
|
+
| Code conventions during development | [ESLint Plugin Rules](https://docs.rdlabo.dev/projects/eslint-plugin-rules/docs/quickstart) |
|
|
70
|
+
|
|
71
|
+
The kit supplies reusable infrastructure. Your routes, domain rules, credentials, and database schema remain in your application. Adopt one helper first; you do not need to adopt every entry point.
|
package/docs/realtime-offline.md
CHANGED
|
@@ -3,11 +3,6 @@
|
|
|
3
3
|
Durable Object WebSocket helpers and table-agnostic offline replica contracts. Product schemas,
|
|
4
4
|
Zod shapes, and domain policy stay in the application.
|
|
5
5
|
|
|
6
|
-
- [HTTP and Authentication](./http-auth.md)
|
|
7
|
-
- [Data Layer](./data-layer.md)
|
|
8
|
-
- [Testing and Operations](./testing-operations.md)
|
|
9
|
-
- [API reference](./api.md)
|
|
10
|
-
|
|
11
6
|
## Durable Object realtime
|
|
12
7
|
|
|
13
8
|
The root and `/realtime` entry points expose the same focused realtime primitives:
|
|
@@ -2,11 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Test helpers, queue batching, operational CLIs, and trust boundaries for Hono Workers applications.
|
|
4
4
|
|
|
5
|
-
- [HTTP and Authentication](./http-auth.md)
|
|
6
|
-
- [Data Layer](./data-layer.md)
|
|
7
|
-
- [Realtime and Offline](./realtime-offline.md)
|
|
8
|
-
- [API reference](./api.md)
|
|
9
|
-
|
|
10
5
|
## Testing entry point
|
|
11
6
|
|
|
12
7
|
`@rdlabo/workers-hono-kit/testing` is never loaded by production code. Its DB helpers are deprecated
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rdlabo/workers-hono-kit",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.3-beta.pr51.sha3289ea609bb4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -108,8 +108,8 @@
|
|
|
108
108
|
},
|
|
109
109
|
"peerDependencies": {
|
|
110
110
|
"@hono/zod-validator": "^0.8.0",
|
|
111
|
-
"@rdlabo/workers-mysql": "
|
|
112
|
-
"@rdlabo/workers-timezone": "
|
|
111
|
+
"@rdlabo/workers-mysql": "0.12.3-beta.pr51.sha3289ea609bb4",
|
|
112
|
+
"@rdlabo/workers-timezone": "0.12.3-beta.pr51.sha3289ea609bb4",
|
|
113
113
|
"ai": "^6.0.0",
|
|
114
114
|
"ai-gateway-provider": "^3.1.0",
|
|
115
115
|
"aws4fetch": "^1.0.20",
|