@rdlabo/workers-hono-kit 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +126 -11
  2. package/dist/ai/gateway.d.ts +54 -16
  3. package/dist/ai/gateway.js +37 -12
  4. package/dist/aws/cloudfront.d.ts +23 -5
  5. package/dist/aws/cloudfront.js +45 -6
  6. package/dist/aws/secrets-manager.d.ts +38 -4
  7. package/dist/aws/secrets-manager.js +48 -3
  8. package/dist/cache/kv-cache.d.ts +173 -10
  9. package/dist/cache/kv-cache.js +139 -7
  10. package/dist/db/connection.d.ts +56 -14
  11. package/dist/db/connection.js +39 -13
  12. package/dist/db/database.d.ts +160 -24
  13. package/dist/db/database.js +51 -7
  14. package/dist/db/index.d.ts +21 -10
  15. package/dist/db/index.js +17 -8
  16. package/dist/db/jst.d.ts +89 -6
  17. package/dist/db/jst.js +89 -23
  18. package/dist/db/orm-config.d.ts +61 -19
  19. package/dist/db/orm-config.js +43 -14
  20. package/dist/db/retry.d.ts +25 -3
  21. package/dist/db/retry.js +25 -3
  22. package/dist/db/write-result.d.ts +27 -4
  23. package/dist/db/write-result.js +22 -1
  24. package/dist/firebase/firebase-verifier.d.ts +53 -4
  25. package/dist/firebase/identity-toolkit.d.ts +54 -5
  26. package/dist/firebase/identity-toolkit.js +51 -0
  27. package/dist/firebase/jose-firebase-verifier.d.ts +81 -9
  28. package/dist/firebase/jose-firebase-verifier.js +68 -7
  29. package/dist/firebase/remote-verifier.d.ts +43 -5
  30. package/dist/firebase/remote-verifier.js +60 -11
  31. package/dist/http/app-env.d.ts +41 -8
  32. package/dist/http/app-env.js +38 -8
  33. package/dist/http/app-info.d.ts +25 -3
  34. package/dist/http/app-info.js +16 -2
  35. package/dist/http/http-status.d.ts +12 -3
  36. package/dist/http/http-status.js +12 -3
  37. package/dist/http/nest-error.d.ts +90 -29
  38. package/dist/http/nest-error.js +59 -18
  39. package/dist/http/user-protocol.d.ts +23 -3
  40. package/dist/http/user-protocol.js +14 -2
  41. package/dist/index.d.ts +41 -30
  42. package/dist/index.js +29 -21
  43. package/dist/middleware/auth.d.ts +75 -14
  44. package/dist/middleware/auth.js +31 -7
  45. package/dist/middleware/finalize-response.d.ts +30 -0
  46. package/dist/middleware/finalize-response.js +41 -12
  47. package/dist/middleware/validation.d.ts +83 -9
  48. package/dist/middleware/validation.js +52 -9
  49. package/dist/middleware/zod-coerce.d.ts +56 -2
  50. package/dist/middleware/zod-coerce.js +68 -9
  51. package/dist/stripe/client.d.ts +46 -9
  52. package/dist/stripe/client.js +41 -3
  53. package/dist/testing/auth.d.ts +60 -12
  54. package/dist/testing/auth.js +58 -10
  55. package/dist/testing/configurable-fake.d.ts +20 -9
  56. package/dist/testing/configurable-fake.js +23 -11
  57. package/dist/testing/db.d.ts +81 -12
  58. package/dist/testing/db.js +23 -1
  59. package/dist/testing/fakes.d.ts +79 -11
  60. package/dist/testing/fakes.js +70 -8
  61. package/dist/testing/index.d.ts +15 -8
  62. package/dist/testing/index.js +15 -10
  63. package/dist/testing/stripe-fixtures.d.ts +93 -3
  64. package/dist/testing/stripe-fixtures.js +93 -3
  65. package/package.json +19 -9
  66. package/src/ai/gateway.ts +66 -27
  67. package/src/aws/cloudfront.ts +46 -6
  68. package/src/aws/secrets-manager.ts +56 -7
  69. package/src/cache/kv-cache.ts +194 -12
  70. package/src/db/connection.ts +56 -14
  71. package/src/db/database.ts +163 -27
  72. package/src/db/index.ts +21 -12
  73. package/src/db/jst.ts +89 -23
  74. package/src/db/orm-config.ts +61 -19
  75. package/src/db/retry.ts +25 -3
  76. package/src/db/write-result.ts +27 -4
  77. package/src/firebase/firebase-verifier.ts +53 -4
  78. package/src/firebase/identity-toolkit.ts +57 -5
  79. package/src/firebase/jose-firebase-verifier.ts +81 -11
  80. package/src/firebase/remote-verifier.ts +61 -12
  81. package/src/http/app-env.ts +41 -8
  82. package/src/http/app-info.ts +25 -3
  83. package/src/http/http-status.ts +12 -3
  84. package/src/http/nest-error.ts +106 -37
  85. package/src/http/user-protocol.ts +23 -3
  86. package/src/index.ts +47 -33
  87. package/src/middleware/auth.ts +79 -17
  88. package/src/middleware/finalize-response.ts +41 -12
  89. package/src/middleware/validation.ts +89 -15
  90. package/src/middleware/zod-coerce.ts +68 -9
  91. package/src/stripe/client.ts +46 -9
  92. package/src/testing/auth.ts +60 -12
  93. package/src/testing/configurable-fake.ts +23 -11
  94. package/src/testing/db.ts +82 -13
  95. package/src/testing/fakes.ts +80 -12
  96. package/src/testing/index.ts +18 -13
  97. package/src/testing/stripe-fixtures.ts +93 -3
@@ -1,14 +1,37 @@
1
- import { databaseFrom } from '../db/database';
1
+ import { databaseFrom } from '../db/database.js';
2
2
  /**
3
- * オフライン route テスト用の in-memory FirebaseVerifier(4 repo 同一実装を集約)。
4
- * `register(token, { uid })` で偽 ID を仕込む。
3
+ * In-memory {@link FirebaseVerifier} implementation for offline route tests.
4
+ *
5
+ * Seed fake identities with {@link FakeFirebaseVerifier.register | register(token, { uid })}, then
6
+ * verification resolves the registered decoded token instead of calling a real Firebase backend.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * const firebase = new FakeFirebaseVerifier();
11
+ * firebase.register('tok-1', { uid: 'uid-1' });
12
+ * const decoded = await firebase.verifyIdToken('tok-1'); // { uid: 'uid-1' }
13
+ * ```
5
14
  */
6
15
  export class FakeFirebaseVerifier {
7
16
  tokens = new Map();
17
+ /** UIDs passed to {@link FakeFirebaseVerifier.deleteUser}, in call order, for assertions. */
8
18
  deleted = [];
19
+ /**
20
+ * Register a fake decoded token so that {@link FakeFirebaseVerifier.verifyIdToken} resolves it.
21
+ *
22
+ * @param token - Token string clients will present.
23
+ * @param record - Decoded token returned on verification (must include `uid`).
24
+ */
9
25
  register(token, record) {
10
26
  this.tokens.set(token, record);
11
27
  }
28
+ /**
29
+ * Resolve the decoded token previously registered for `idToken`.
30
+ *
31
+ * @param idToken - Token string to verify.
32
+ * @returns The registered decoded token.
33
+ * @throws Error if the token was never registered.
34
+ */
12
35
  async verifyIdToken(idToken) {
13
36
  const record = this.tokens.get(idToken);
14
37
  if (!record) {
@@ -16,16 +39,42 @@ export class FakeFirebaseVerifier {
16
39
  }
17
40
  return record;
18
41
  }
42
+ /**
43
+ * Return a minimal user record echoing the requested UID.
44
+ *
45
+ * @param uid - UID to look up.
46
+ * @returns An object containing the `uid` (never `null` in this fake).
47
+ */
19
48
  async getUser(uid) {
20
49
  return { uid };
21
50
  }
51
+ /**
52
+ * Record a user deletion by appending the UID to {@link FakeFirebaseVerifier.deleted}.
53
+ *
54
+ * @param uid - UID being deleted.
55
+ */
22
56
  async deleteUser(uid) {
23
57
  this.deleted.push(uid);
24
58
  }
25
59
  }
26
60
  /**
27
- * テスト用にプール 1 本を primary/replica 兼用にした Database(foodlabel PoolDatabase 相当)。
28
- * `dispose()` はプールを閉じる。orm は消費側が自分の drizzle-orm で作って渡す(型同一性の分離)。
61
+ * Create a `Database` backed by a single pool used as both primary and replica, suitable for tests.
62
+ *
63
+ * @remarks
64
+ * `dispose()` ends the pool. The `orm` is provided by the caller (rather than constructed here) so
65
+ * the returned database uses the consumer's own `drizzle-orm` types, avoiding type-identity clashes
66
+ * across duplicated `drizzle-orm` installs.
67
+ *
68
+ * @typeParam TDrizzle - The Drizzle instance type provided by the consumer.
69
+ * @param options - Pool and Drizzle instance. See {@link CreatePoolDatabaseOptions}.
70
+ * @returns A {@link DisposableDatabase} whose `dispose()` closes the pool.
71
+ * @example
72
+ * ```ts
73
+ * const pool = testDb.createTestPool();
74
+ * const db = createPoolDatabase({ pool, orm: drizzle(pool, { schema }) });
75
+ * // ... run tests ...
76
+ * await db.dispose();
77
+ * ```
29
78
  */
30
79
  export function createPoolDatabase(options) {
31
80
  const { pool, orm } = options;
@@ -38,9 +87,22 @@ export function createPoolDatabase(options) {
38
87
  };
39
88
  }
40
89
  /**
41
- * DB に触れない route(GET / 等)用の Database スタブ。write/transaction は誤用検知のため throw。
42
- * dispose は no-op(Hyperdrive/Pool 背面の DisposableDatabase を期待する repo でもそのまま使える)。
43
- * orm 型は呼び出し側が指定(既定 unknown)。
90
+ * Create a no-op `Database` stub for routes that never touch the database (e.g. plain GET handlers).
91
+ *
92
+ * @remarks
93
+ * `read` resolves to an empty array, while `write` and `transaction` throw so that any unexpected
94
+ * database access is caught as a misuse. `dispose` is a no-op, so this can stand in for a
95
+ * {@link DisposableDatabase} backing (e.g. a Hyperdrive- or pool-based one) without changes.
96
+ *
97
+ * @typeParam TDrizzle - The Drizzle instance type the consumer expects (defaults to `unknown`).
98
+ * @returns A {@link DisposableDatabase} that reads empty and throws on writes/transactions.
99
+ * @throws Error from `write`/`transaction` if they are accessed.
100
+ * @example
101
+ * ```ts
102
+ * const db = createNoopDatabase();
103
+ * await db.read('SELECT 1'); // []
104
+ * await db.write(async (dz) => dz); // throws: noopDatabase.write accessed unexpectedly
105
+ * ```
44
106
  */
45
107
  export function createNoopDatabase() {
46
108
  return {
@@ -1,8 +1,15 @@
1
- export { createTestDb } from './db';
2
- export type { TestDb, CreateTestDbOptions, TestDbConnection } from './db';
3
- export { FakeFirebaseVerifier, createPoolDatabase, createNoopDatabase } from './fakes';
4
- export type { CreatePoolDatabaseOptions } from './fakes';
5
- export type { Database, DisposableDatabase, QueryRunner, TxOf } from '../db/database';
6
- export { authHeaders, registerFirebaseToken, provisionUser } from './auth';
7
- export { configurableFake } from './configurable-fake';
8
- export { fakeApiList, fakePaymentIntent, fakeStripeEvent, fakeCheckoutSession, fakeCustomer, fakePrice, fakeSubscription, } from './stripe-fixtures';
1
+ /**
2
+ * Shared test infrastructure for Hono on Cloudflare Workers projects (depends on `mysql2`/`drizzle-orm`).
3
+ *
4
+ * Test-only helpers that are never loaded at runtime. This subpath consolidates the duplicated
5
+ * test boilerplate (test DB setup, in-memory fakes, auth header builders, Stripe fixtures) that
6
+ * tends to be copy-pasted across projects into a single, importable surface.
7
+ */
8
+ export { createTestDb } from './db.js';
9
+ export type { TestDb, CreateTestDbOptions, TestDbConnection } from './db.js';
10
+ export { FakeFirebaseVerifier, createPoolDatabase, createNoopDatabase } from './fakes.js';
11
+ export type { CreatePoolDatabaseOptions } from './fakes.js';
12
+ export type { Database, DisposableDatabase, QueryRunner, TxOf } from '../db/database.js';
13
+ export { authHeaders, registerFirebaseToken, provisionUser } from './auth.js';
14
+ export { configurableFake } from './configurable-fake.js';
15
+ export { fakeApiList, fakePaymentIntent, fakeStripeEvent, fakeCheckoutSession, fakeCustomer, fakePrice, fakeSubscription, } from './stripe-fixtures.js';
@@ -1,10 +1,15 @@
1
- // @rdlabo/workers-hono-kit/testing — フリート共通のテスト基盤(mysql2/drizzle 依存)。
2
- // 実行時には読み込まれないテスト専用ヘルパ。各 repo testing/db.ts・fakes.ts を集約。
3
- export { createTestDb } from './db';
4
- export { FakeFirebaseVerifier, createPoolDatabase, createNoopDatabase } from './fakes';
5
- // 認証テストヘルパ(route spec のヘッダ生成・ユーザ provision を集約)。
6
- export { authHeaders, registerFirebaseToken, provisionUser } from './auth';
7
- // test double ヘルパ(未設定メソッドで明示 throw する部分実装 fake)。
8
- export { configurableFake } from './configurable-fake';
9
- // Stripe オブジェクトの test fixture factory。
10
- export { fakeApiList, fakePaymentIntent, fakeStripeEvent, fakeCheckoutSession, fakeCustomer, fakePrice, fakeSubscription, } from './stripe-fixtures';
1
+ /**
2
+ * Shared test infrastructure for Hono on Cloudflare Workers projects (depends on `mysql2`/`drizzle-orm`).
3
+ *
4
+ * Test-only helpers that are never loaded at runtime. This subpath consolidates the duplicated
5
+ * test boilerplate (test DB setup, in-memory fakes, auth header builders, Stripe fixtures) that
6
+ * tends to be copy-pasted across projects into a single, importable surface.
7
+ */
8
+ export { createTestDb } from './db.js';
9
+ export { FakeFirebaseVerifier, createPoolDatabase, createNoopDatabase } from './fakes.js';
10
+ // Authentication test helpers (route-spec header builders and user provisioning).
11
+ export { authHeaders, registerFirebaseToken, provisionUser } from './auth.js';
12
+ // Test double helper (partial-implementation fake that throws explicitly on unconfigured members).
13
+ export { configurableFake } from './configurable-fake.js';
14
+ // Test fixture factories for Stripe objects.
15
+ export { fakeApiList, fakePaymentIntent, fakeStripeEvent, fakeCheckoutSession, fakeCustomer, fakePrice, fakeSubscription, } from './stripe-fixtures.js';
@@ -1,13 +1,103 @@
1
1
  import type Stripe from 'stripe';
2
2
  /**
3
- * Stripe オブジェクトの test fixture factory。実 SDK 型は巨大なので、テストが参照する範囲だけを
4
- * 妥当な既定値で組み、`over` で上書きする(最後に 1 度だけ Stripe 型へキャスト)。fleet 各 repo の
5
- * 課金テストで同じダミー PaymentIntent/Event/... を手組みしていた重複を集約する。
3
+ * Test fixture factories for Stripe objects.
4
+ *
5
+ * The real SDK types are enormous, so each factory builds only the fields tests typically read,
6
+ * fills them with reasonable defaults, lets you override via `over`, and casts to the Stripe type
7
+ * once at the end. This consolidates the duplicated hand-built dummy `PaymentIntent`/`Event`/... that
8
+ * billing tests tend to reconstruct in every project.
9
+ */
10
+ /**
11
+ * Build a fake Stripe `ApiList` wrapping the given data.
12
+ *
13
+ * @remarks Defaults: `object: 'list'`, `has_more: false`, `url: '/v1/_test'`.
14
+ * @typeParam T - Element type of the list.
15
+ * @param data - Items to place in `data`.
16
+ * @param over - Field overrides merged last.
17
+ * @returns A `Stripe.ApiList<T>` fixture.
18
+ * @example
19
+ * ```ts
20
+ * const list = fakeApiList([fakePaymentIntent()]);
21
+ * ```
6
22
  */
7
23
  export declare function fakeApiList<T>(data: T[], over?: Partial<Stripe.ApiList<T>>): Stripe.ApiList<T>;
24
+ /**
25
+ * Build a fake Stripe `PaymentIntent`.
26
+ *
27
+ * @remarks Defaults: `id: 'pi_test_1'`, `object: 'payment_intent'`, `amount: 1000`, `currency: 'jpy'`,
28
+ * `status: 'succeeded'`, `created: 1_700_000_000`.
29
+ * @param over - Field overrides merged last.
30
+ * @returns A `Stripe.PaymentIntent` fixture.
31
+ * @example
32
+ * ```ts
33
+ * const pi = fakePaymentIntent({ status: 'requires_payment_method' });
34
+ * ```
35
+ */
8
36
  export declare function fakePaymentIntent(over?: Partial<Stripe.PaymentIntent>): Stripe.PaymentIntent;
37
+ /**
38
+ * Build a fake Stripe webhook `Event` wrapping the given payload.
39
+ *
40
+ * @remarks Defaults: `id: 'evt_test_1'`, `object: 'event'`, `api_version: '2024-06-20'`,
41
+ * `created: 1_700_000_000`, `livemode: false`. The payload is placed at `data.object`.
42
+ * @param type - Event type (e.g. `'payment_intent.succeeded'`), assigned to `type`.
43
+ * @param dataObject - The object placed at `data.object`.
44
+ * @param over - Field overrides merged last.
45
+ * @returns A `Stripe.Event` fixture.
46
+ * @example
47
+ * ```ts
48
+ * const event = fakeStripeEvent('payment_intent.succeeded', fakePaymentIntent());
49
+ * ```
50
+ */
9
51
  export declare function fakeStripeEvent(type: string, dataObject: unknown, over?: Partial<Stripe.Event>): Stripe.Event;
52
+ /**
53
+ * Build a fake Stripe `Checkout.Session`.
54
+ *
55
+ * @remarks Defaults: `id: 'cs_test_1'`, `object: 'checkout.session'`,
56
+ * `url: 'https://checkout.stripe.test/cs_test_1'`, `mode: 'subscription'`, `status: 'open'`.
57
+ * @param over - Field overrides merged last.
58
+ * @returns A `Stripe.Checkout.Session` fixture.
59
+ * @example
60
+ * ```ts
61
+ * const session = fakeCheckoutSession({ status: 'complete' });
62
+ * ```
63
+ */
10
64
  export declare function fakeCheckoutSession(over?: Partial<Stripe.Checkout.Session>): Stripe.Checkout.Session;
65
+ /**
66
+ * Build a fake Stripe `Customer`.
67
+ *
68
+ * @remarks Defaults: `id: 'cus_test_1'`, `object: 'customer'`, `created: 1_700_000_000`,
69
+ * `livemode: false`.
70
+ * @param over - Field overrides merged last.
71
+ * @returns A `Stripe.Customer` fixture.
72
+ * @example
73
+ * ```ts
74
+ * const customer = fakeCustomer({ email: 'a@example.com' });
75
+ * ```
76
+ */
11
77
  export declare function fakeCustomer(over?: Partial<Stripe.Customer>): Stripe.Customer;
78
+ /**
79
+ * Build a fake Stripe `Price`.
80
+ *
81
+ * @remarks Defaults: `id: 'price_test_1'`, `object: 'price'`, `active: true`, `currency: 'jpy'`,
82
+ * `unit_amount: 1000`.
83
+ * @param over - Field overrides merged last.
84
+ * @returns A `Stripe.Price` fixture.
85
+ * @example
86
+ * ```ts
87
+ * const price = fakePrice({ unit_amount: 2000 });
88
+ * ```
89
+ */
12
90
  export declare function fakePrice(over?: Partial<Stripe.Price>): Stripe.Price;
91
+ /**
92
+ * Build a fake Stripe `Subscription`.
93
+ *
94
+ * @remarks Defaults: `id: 'sub_test_1'`, `object: 'subscription'`, `status: 'active'`,
95
+ * `customer: 'cus_test_1'`, `created: 1_700_000_000`.
96
+ * @param over - Field overrides merged last.
97
+ * @returns A `Stripe.Subscription` fixture.
98
+ * @example
99
+ * ```ts
100
+ * const sub = fakeSubscription({ status: 'canceled' });
101
+ * ```
102
+ */
13
103
  export declare function fakeSubscription(over?: Partial<Stripe.Subscription>): Stripe.Subscription;
@@ -1,7 +1,23 @@
1
1
  /**
2
- * Stripe オブジェクトの test fixture factory。実 SDK 型は巨大なので、テストが参照する範囲だけを
3
- * 妥当な既定値で組み、`over` で上書きする(最後に 1 度だけ Stripe 型へキャスト)。fleet 各 repo の
4
- * 課金テストで同じダミー PaymentIntent/Event/... を手組みしていた重複を集約する。
2
+ * Test fixture factories for Stripe objects.
3
+ *
4
+ * The real SDK types are enormous, so each factory builds only the fields tests typically read,
5
+ * fills them with reasonable defaults, lets you override via `over`, and casts to the Stripe type
6
+ * once at the end. This consolidates the duplicated hand-built dummy `PaymentIntent`/`Event`/... that
7
+ * billing tests tend to reconstruct in every project.
8
+ */
9
+ /**
10
+ * Build a fake Stripe `ApiList` wrapping the given data.
11
+ *
12
+ * @remarks Defaults: `object: 'list'`, `has_more: false`, `url: '/v1/_test'`.
13
+ * @typeParam T - Element type of the list.
14
+ * @param data - Items to place in `data`.
15
+ * @param over - Field overrides merged last.
16
+ * @returns A `Stripe.ApiList<T>` fixture.
17
+ * @example
18
+ * ```ts
19
+ * const list = fakeApiList([fakePaymentIntent()]);
20
+ * ```
5
21
  */
6
22
  export function fakeApiList(data, over = {}) {
7
23
  return {
@@ -12,6 +28,18 @@ export function fakeApiList(data, over = {}) {
12
28
  ...over,
13
29
  };
14
30
  }
31
+ /**
32
+ * Build a fake Stripe `PaymentIntent`.
33
+ *
34
+ * @remarks Defaults: `id: 'pi_test_1'`, `object: 'payment_intent'`, `amount: 1000`, `currency: 'jpy'`,
35
+ * `status: 'succeeded'`, `created: 1_700_000_000`.
36
+ * @param over - Field overrides merged last.
37
+ * @returns A `Stripe.PaymentIntent` fixture.
38
+ * @example
39
+ * ```ts
40
+ * const pi = fakePaymentIntent({ status: 'requires_payment_method' });
41
+ * ```
42
+ */
15
43
  export function fakePaymentIntent(over = {}) {
16
44
  return {
17
45
  id: 'pi_test_1',
@@ -23,6 +51,20 @@ export function fakePaymentIntent(over = {}) {
23
51
  ...over,
24
52
  };
25
53
  }
54
+ /**
55
+ * Build a fake Stripe webhook `Event` wrapping the given payload.
56
+ *
57
+ * @remarks Defaults: `id: 'evt_test_1'`, `object: 'event'`, `api_version: '2024-06-20'`,
58
+ * `created: 1_700_000_000`, `livemode: false`. The payload is placed at `data.object`.
59
+ * @param type - Event type (e.g. `'payment_intent.succeeded'`), assigned to `type`.
60
+ * @param dataObject - The object placed at `data.object`.
61
+ * @param over - Field overrides merged last.
62
+ * @returns A `Stripe.Event` fixture.
63
+ * @example
64
+ * ```ts
65
+ * const event = fakeStripeEvent('payment_intent.succeeded', fakePaymentIntent());
66
+ * ```
67
+ */
26
68
  export function fakeStripeEvent(type, dataObject, over = {}) {
27
69
  return {
28
70
  id: 'evt_test_1',
@@ -35,6 +77,18 @@ export function fakeStripeEvent(type, dataObject, over = {}) {
35
77
  ...over,
36
78
  };
37
79
  }
80
+ /**
81
+ * Build a fake Stripe `Checkout.Session`.
82
+ *
83
+ * @remarks Defaults: `id: 'cs_test_1'`, `object: 'checkout.session'`,
84
+ * `url: 'https://checkout.stripe.test/cs_test_1'`, `mode: 'subscription'`, `status: 'open'`.
85
+ * @param over - Field overrides merged last.
86
+ * @returns A `Stripe.Checkout.Session` fixture.
87
+ * @example
88
+ * ```ts
89
+ * const session = fakeCheckoutSession({ status: 'complete' });
90
+ * ```
91
+ */
38
92
  export function fakeCheckoutSession(over = {}) {
39
93
  return {
40
94
  id: 'cs_test_1',
@@ -45,6 +99,18 @@ export function fakeCheckoutSession(over = {}) {
45
99
  ...over,
46
100
  };
47
101
  }
102
+ /**
103
+ * Build a fake Stripe `Customer`.
104
+ *
105
+ * @remarks Defaults: `id: 'cus_test_1'`, `object: 'customer'`, `created: 1_700_000_000`,
106
+ * `livemode: false`.
107
+ * @param over - Field overrides merged last.
108
+ * @returns A `Stripe.Customer` fixture.
109
+ * @example
110
+ * ```ts
111
+ * const customer = fakeCustomer({ email: 'a@example.com' });
112
+ * ```
113
+ */
48
114
  export function fakeCustomer(over = {}) {
49
115
  return {
50
116
  id: 'cus_test_1',
@@ -54,6 +120,18 @@ export function fakeCustomer(over = {}) {
54
120
  ...over,
55
121
  };
56
122
  }
123
+ /**
124
+ * Build a fake Stripe `Price`.
125
+ *
126
+ * @remarks Defaults: `id: 'price_test_1'`, `object: 'price'`, `active: true`, `currency: 'jpy'`,
127
+ * `unit_amount: 1000`.
128
+ * @param over - Field overrides merged last.
129
+ * @returns A `Stripe.Price` fixture.
130
+ * @example
131
+ * ```ts
132
+ * const price = fakePrice({ unit_amount: 2000 });
133
+ * ```
134
+ */
57
135
  export function fakePrice(over = {}) {
58
136
  return {
59
137
  id: 'price_test_1',
@@ -64,6 +142,18 @@ export function fakePrice(over = {}) {
64
142
  ...over,
65
143
  };
66
144
  }
145
+ /**
146
+ * Build a fake Stripe `Subscription`.
147
+ *
148
+ * @remarks Defaults: `id: 'sub_test_1'`, `object: 'subscription'`, `status: 'active'`,
149
+ * `customer: 'cus_test_1'`, `created: 1_700_000_000`.
150
+ * @param over - Field overrides merged last.
151
+ * @returns A `Stripe.Subscription` fixture.
152
+ * @example
153
+ * ```ts
154
+ * const sub = fakeSubscription({ status: 'canceled' });
155
+ * ```
156
+ */
67
157
  export function fakeSubscription(over = {}) {
68
158
  return {
69
159
  id: 'sub_test_1',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rdlabo/workers-hono-kit",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -68,7 +68,11 @@
68
68
  "lint": "eslint \"src/**/*.ts\"",
69
69
  "lint:fix": "eslint \"src/**/*.ts\" --fix",
70
70
  "format": "prettier --write \"src/**/*.ts\"",
71
- "format:check": "prettier --check \"src/**/*.ts\""
71
+ "format:check": "prettier --check \"src/**/*.ts\"",
72
+ "release": "np --no-tests --no-publish"
73
+ },
74
+ "dependencies": {
75
+ "stripe": "^20.4.1"
72
76
  },
73
77
  "peerDependencies": {
74
78
  "@hono/zod-validator": "^0.8.0",
@@ -79,15 +83,21 @@
79
83
  "hono": "^4.6.0",
80
84
  "jose": "^6.2.0",
81
85
  "mysql2": "^3.14.0",
82
- "stripe": ">=15",
83
86
  "zod": "^4.4.0"
84
87
  },
85
88
  "peerDependenciesMeta": {
86
- "ai": { "optional": true },
87
- "ai-gateway-provider": { "optional": true },
88
- "stripe": { "optional": true },
89
- "drizzle-orm": { "optional": true },
90
- "mysql2": { "optional": true }
89
+ "ai": {
90
+ "optional": true
91
+ },
92
+ "ai-gateway-provider": {
93
+ "optional": true
94
+ },
95
+ "drizzle-orm": {
96
+ "optional": true
97
+ },
98
+ "mysql2": {
99
+ "optional": true
100
+ }
91
101
  },
92
102
  "devDependencies": {
93
103
  "@ai-sdk/anthropic": "^3.0.84",
@@ -103,8 +113,8 @@
103
113
  "hono": "^4.6.0",
104
114
  "jose": "^6.2.3",
105
115
  "mysql2": "3.22.5",
116
+ "np": "^8.0.4",
106
117
  "prettier": "^3.8.4",
107
- "stripe": "^20.4.1",
108
118
  "typescript": "~5.6.2",
109
119
  "typescript-eslint": "^8.61.1",
110
120
  "vitest": "^2.1.0",
package/src/ai/gateway.ts CHANGED
@@ -1,61 +1,100 @@
1
1
  /**
2
- * Cloudflare AI Gateway のプロバイダ生成(`ai` SDK + `ai-gateway-provider`)。
3
- * フリート共通 = foodlabel / winecode / receptray hono の AI 呼び出しを必ず Gateway 経由にする。
2
+ * Cloudflare AI Gateway provider factory built on the Vercel AI SDK and `ai-gateway-provider`.
4
3
  *
5
- * `createAiGateway` が返す wrapper `@ai-sdk/*` のモデルを包むと、SDK が組み立てた
6
- * プロバイダ宛リクエスト(api.openai.com / api.anthropic.com / *-aiplatform.googleapis.com 等)を
7
- * AI Gateway Universal Endpoint 経由に差し替える。OpenAI / Anthropic / Google Vertex(SA) の
8
- * いずれも同じ `aigateway(model)` で透過的にルーティングされる(Vertex も対応)。
4
+ * Routes OpenAI, Anthropic, and Google Vertex `@ai-sdk/*` models through the AI Gateway Universal
5
+ * Endpoint. The wrapper returned by `createAiGateway` intercepts the provider-bound requests the SDK
6
+ * assembles (`api.openai.com`, `api.anthropic.com`, `*-aiplatform.googleapis.com`, etc.) and redirects
7
+ * them through the Gateway. Every provider is routed transparently via the same `aigateway(model)` call.
9
8
  *
10
- * ここはインフラ層(Gateway 識別子と認証トークンの注入だけ)。プロバイダの API キーや
11
- * Vertex SA 認証情報は各 repo 側でモデル生成時に渡す(pass-through)。
9
+ * @remarks
10
+ * This module is purely the infrastructure layer: it injects only the Gateway identifier and (optionally)
11
+ * the Gateway authentication token. Provider API keys and Vertex service-account credentials are supplied
12
+ * by the caller at model-construction time and passed through untouched.
12
13
  */
13
14
  import { createAiGateway } from 'ai-gateway-provider';
14
15
  import type { AiGateway, AiGatewayBindingSettings, AiGatewayOptions } from 'ai-gateway-provider';
15
16
 
16
17
  export type { AiGateway, AiGatewayOptions } from 'ai-gateway-provider';
17
18
 
18
- /** Workers の AI binding(`env.AI.gateway(name)`)の最小形。Cloudflare の `AiGateway` が構造的に適合する。 */
19
+ /**
20
+ * Minimal shape of a Workers AI binding (`env.AI.gateway(name)`).
21
+ *
22
+ * @remarks
23
+ * Cloudflare's runtime `AiGateway` type is structurally compatible with this binding shape.
24
+ */
19
25
  export type AiGatewayBinding = AiGatewayBindingSettings['binding'];
20
26
 
21
27
  /**
22
- * AI Gateway 設定。2 系統:
23
- * - binding 形: Workers ランタイム(本番 / `wrangler dev`)。`env.AI.gateway(name)` を渡す。
24
- * binding 経由は同一アカウント内で事前認証されるため Gateway トークン不要。
25
- * - REST 形: Workers 外(Node eval ハーネス等、binding 不可)。accountId + gateway + token で REST。
28
+ * Configuration for the AI Gateway provider. This is a union with two mutually exclusive forms.
29
+ *
30
+ * @remarks
31
+ * - **Binding form** for the Workers runtime (production and `wrangler dev`). Pass the
32
+ * `env.AI.gateway(name)` binding. Requests through a binding are pre-authenticated within the same
33
+ * Cloudflare account, so no Gateway token is required.
34
+ * - **REST form** — for non-Workers contexts where a binding is unavailable (e.g. a Node evaluation
35
+ * harness). Supply `accountId`, `gateway`, and (for authenticated Gateways) `token` to reach the
36
+ * Gateway over REST.
26
37
  */
27
38
  export type AiGatewayConfig =
28
39
  | {
29
- /** `env.AI.gateway(name)` 等の AI Gateway binding。 */
40
+ /** The AI Gateway binding, typically obtained via `env.AI.gateway(name)`. */
30
41
  binding: AiGatewayBinding;
31
- /** キャッシュ / リトライ / メタデータ等の Gateway オプション(任意)。 */
42
+ /** Optional Gateway options such as caching, retries, and request metadata. */
32
43
  options?: AiGatewayOptions;
33
44
  }
34
45
  | {
35
- /** Cloudflare アカウント ID */
46
+ /** Cloudflare account ID that owns the Gateway. */
36
47
  accountId: string;
37
- /** AI Gateway 名。 */
48
+ /** AI Gateway name. */
38
49
  gateway: string;
39
50
  /**
40
- * `cf-aig-authorization` に載せる Gateway 認証トークン。Authenticated Gateway のときだけ必要。
41
- * unauthenticated Gateway では省略可(プロバイダの API キーではなく Gateway 自体への認証)。
51
+ * Gateway authentication token sent in the `cf-aig-authorization` header. Required only for an
52
+ * Authenticated Gateway; omit it for an unauthenticated Gateway. This authenticates the request to
53
+ * the Gateway itself and is distinct from any provider API key.
42
54
  */
43
55
  token?: string;
44
- /** キャッシュ / リトライ / メタデータ等の Gateway オプション(任意)。 */
56
+ /** Optional Gateway options such as caching, retries, and request metadata. */
45
57
  options?: AiGatewayOptions;
46
58
  };
47
59
 
60
+ /** Provider object exposing the AI Gateway model wrapper. */
48
61
  export interface AiGatewayProvider {
49
62
  /**
50
- * `@ai-sdk/*` のモデルを包んで AI Gateway 経由にする。
51
- * 例: `aigateway(createAnthropic({ apiKey }).('claude-...'))`。
52
- * 配列を渡すとフォールバック(先頭から順に試行)になる。
63
+ * Wraps an `@ai-sdk/*` model so its requests are routed through the AI Gateway.
64
+ *
65
+ * @remarks
66
+ * Example invocation: `aigateway(createAnthropic({ apiKey })('claude-...'))`. Passing an array of
67
+ * models enables fallback behavior — each model is attempted in order from the start of the array.
53
68
  */
54
69
  aigateway: AiGateway;
55
70
  }
56
71
 
57
72
  /**
58
- * AI Gateway 用のプロバイダを生成する。binding / REST 形のどちらでも可(欠落時は fail-fast)。
73
+ * Create an AI Gateway provider from either the binding form or the REST form of the configuration.
74
+ *
75
+ * @param config - The Gateway configuration; either the binding form or the REST form.
76
+ * @returns A provider whose `aigateway` wrapper routes models through the AI Gateway.
77
+ * @throws Error When the REST form is used and `accountId` or `gateway` is missing (fail-fast).
78
+ * @example
79
+ * ```ts
80
+ * // Binding form (Workers runtime: production / wrangler dev)
81
+ * import { createAnthropic } from '@ai-sdk/anthropic';
82
+ *
83
+ * const { aigateway } = createAiGatewayProvider({ binding: env.AI.gateway('my-gateway') });
84
+ * const model = aigateway(createAnthropic({ apiKey: env.ANTHROPIC_API_KEY })('claude-3-5-sonnet-latest'));
85
+ * ```
86
+ * @example
87
+ * ```ts
88
+ * // REST form (non-Workers context, e.g. a Node evaluation harness)
89
+ * import { createOpenAI } from '@ai-sdk/openai';
90
+ *
91
+ * const { aigateway } = createAiGatewayProvider({
92
+ * accountId: process.env.CF_ACCOUNT_ID!,
93
+ * gateway: 'my-gateway',
94
+ * token: process.env.CF_AIG_TOKEN, // only for an Authenticated Gateway
95
+ * });
96
+ * const model = aigateway(createOpenAI({ apiKey: process.env.OPENAI_API_KEY })('gpt-4o'));
97
+ * ```
59
98
  */
60
99
  export function createAiGatewayProvider(config: AiGatewayConfig): AiGatewayProvider {
61
100
  if ('binding' in config) {
@@ -63,13 +102,13 @@ export function createAiGatewayProvider(config: AiGatewayConfig): AiGatewayProvi
63
102
  }
64
103
 
65
104
  if (!config.accountId) {
66
- throw new Error('AI Gateway: accountId が未設定です');
105
+ throw new Error('AI Gateway: accountId is not set');
67
106
  }
68
107
  if (!config.gateway) {
69
- throw new Error('AI Gateway: gateway 名が未設定です');
108
+ throw new Error('AI Gateway: gateway name is not set');
70
109
  }
71
110
 
72
- // token Authenticated Gateway のときだけ apiKey として送る。unauthenticated では undefined で可。
111
+ // The token is sent as apiKey only for an Authenticated Gateway; undefined is fine when unauthenticated.
73
112
  return {
74
113
  aigateway: createAiGateway({
75
114
  accountId: config.accountId,