zuburi-game-platform 0.1.0

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 (98) hide show
  1. package/README.md +214 -0
  2. package/dist/achievements/listAchievements.d.ts +5 -0
  3. package/dist/achievements/listAchievements.d.ts.map +1 -0
  4. package/dist/achievements/listAchievements.js +8 -0
  5. package/dist/achievements/listAchievements.js.map +1 -0
  6. package/dist/client/createGamePlatformClient.d.ts +4 -0
  7. package/dist/client/createGamePlatformClient.d.ts.map +1 -0
  8. package/dist/client/createGamePlatformClient.js +37 -0
  9. package/dist/client/createGamePlatformClient.js.map +1 -0
  10. package/dist/client/types.d.ts +35 -0
  11. package/dist/client/types.d.ts.map +1 -0
  12. package/dist/client/types.js +2 -0
  13. package/dist/client/types.js.map +1 -0
  14. package/dist/errors/GamePlatformError.d.ts +11 -0
  15. package/dist/errors/GamePlatformError.d.ts.map +1 -0
  16. package/dist/errors/GamePlatformError.js +13 -0
  17. package/dist/errors/GamePlatformError.js.map +1 -0
  18. package/dist/errors/normalizeError.d.ts +3 -0
  19. package/dist/errors/normalizeError.d.ts.map +1 -0
  20. package/dist/errors/normalizeError.js +69 -0
  21. package/dist/errors/normalizeError.js.map +1 -0
  22. package/dist/events/emitEvent.d.ts +5 -0
  23. package/dist/events/emitEvent.d.ts.map +1 -0
  24. package/dist/events/emitEvent.js +25 -0
  25. package/dist/events/emitEvent.js.map +1 -0
  26. package/dist/events/idempotency.d.ts +2 -0
  27. package/dist/events/idempotency.d.ts.map +1 -0
  28. package/dist/events/idempotency.js +14 -0
  29. package/dist/events/idempotency.js.map +1 -0
  30. package/dist/index.d.ts +11 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +3 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/internal/callRpc.d.ts +20 -0
  35. package/dist/internal/callRpc.d.ts.map +1 -0
  36. package/dist/internal/callRpc.js +40 -0
  37. package/dist/internal/callRpc.js.map +1 -0
  38. package/dist/internal/mappers.d.ts +24 -0
  39. package/dist/internal/mappers.d.ts.map +1 -0
  40. package/dist/internal/mappers.js +184 -0
  41. package/dist/internal/mappers.js.map +1 -0
  42. package/dist/internal/validateApiVersion.d.ts +4 -0
  43. package/dist/internal/validateApiVersion.d.ts.map +1 -0
  44. package/dist/internal/validateApiVersion.js +12 -0
  45. package/dist/internal/validateApiVersion.js.map +1 -0
  46. package/dist/internal/validators.d.ts +14 -0
  47. package/dist/internal/validators.d.ts.map +1 -0
  48. package/dist/internal/validators.js +53 -0
  49. package/dist/internal/validators.js.map +1 -0
  50. package/dist/login-bonus/claimAvailable.d.ts +5 -0
  51. package/dist/login-bonus/claimAvailable.d.ts.map +1 -0
  52. package/dist/login-bonus/claimAvailable.js +10 -0
  53. package/dist/login-bonus/claimAvailable.js.map +1 -0
  54. package/dist/login-bonus/claimProgram.d.ts +5 -0
  55. package/dist/login-bonus/claimProgram.d.ts.map +1 -0
  56. package/dist/login-bonus/claimProgram.js +12 -0
  57. package/dist/login-bonus/claimProgram.js.map +1 -0
  58. package/dist/login-bonus/getStatus.d.ts +5 -0
  59. package/dist/login-bonus/getStatus.d.ts.map +1 -0
  60. package/dist/login-bonus/getStatus.js +8 -0
  61. package/dist/login-bonus/getStatus.js.map +1 -0
  62. package/dist/points/getAccount.d.ts +5 -0
  63. package/dist/points/getAccount.d.ts.map +1 -0
  64. package/dist/points/getAccount.js +8 -0
  65. package/dist/points/getAccount.js.map +1 -0
  66. package/dist/points/listTransactions.d.ts +5 -0
  67. package/dist/points/listTransactions.d.ts.map +1 -0
  68. package/dist/points/listTransactions.js +17 -0
  69. package/dist/points/listTransactions.js.map +1 -0
  70. package/dist/state/getState.d.ts +5 -0
  71. package/dist/state/getState.d.ts.map +1 -0
  72. package/dist/state/getState.js +7 -0
  73. package/dist/state/getState.js.map +1 -0
  74. package/dist/types/achievement.d.ts +24 -0
  75. package/dist/types/achievement.d.ts.map +1 -0
  76. package/dist/types/achievement.js +2 -0
  77. package/dist/types/achievement.js.map +1 -0
  78. package/dist/types/api.d.ts +11 -0
  79. package/dist/types/api.d.ts.map +1 -0
  80. package/dist/types/api.js +2 -0
  81. package/dist/types/api.js.map +1 -0
  82. package/dist/types/event.d.ts +46 -0
  83. package/dist/types/event.d.ts.map +1 -0
  84. package/dist/types/event.js +2 -0
  85. package/dist/types/event.js.map +1 -0
  86. package/dist/types/loginBonus.d.ts +45 -0
  87. package/dist/types/loginBonus.d.ts.map +1 -0
  88. package/dist/types/loginBonus.js +2 -0
  89. package/dist/types/loginBonus.js.map +1 -0
  90. package/dist/types/point.d.ts +26 -0
  91. package/dist/types/point.d.ts.map +1 -0
  92. package/dist/types/point.js +2 -0
  93. package/dist/types/point.js.map +1 -0
  94. package/dist/types/state.d.ts +14 -0
  95. package/dist/types/state.d.ts.map +1 -0
  96. package/dist/types/state.js +2 -0
  97. package/dist/types/state.js.map +1 -0
  98. package/package.json +48 -0
package/README.md ADDED
@@ -0,0 +1,214 @@
1
+ # zuburi-game-platform
2
+
3
+ ZUBURI のゲームから KZTK Game Platform を利用するための、React 非依存 TypeScript SDK です。
4
+
5
+ このパッケージは **npmjs.org の public package** として配布します。リポジトリ全体ではなく、`packages/game-platform` の内容だけが publish 対象です。
6
+
7
+ ## Package
8
+
9
+ ```text
10
+ Package: zuburi-game-platform
11
+ Registry: https://registry.npmjs.org/
12
+ ```
13
+
14
+ ## Install
15
+
16
+ GitHub Packages 用の `.npmrc` や GitHub 認証は不要です。
17
+
18
+ ```bash
19
+ pnpm add zuburi-game-platform @supabase/supabase-js
20
+ ```
21
+
22
+ npm / yarn など、npm registry を利用できる別の package manager からも通常どおり install できます。
23
+
24
+ ## Basic usage
25
+
26
+ SDK は Supabase Client を生成しません。ゲームが既に利用している、ログイン Session 付きの Client を注入します。
27
+
28
+ ```ts
29
+ import { createGamePlatformClient } from 'zuburi-game-platform'
30
+ import { supabase } from '../lib/supabase'
31
+
32
+ export const platform = createGamePlatformClient({
33
+ supabase,
34
+ gameId: 'coffee_roaster',
35
+ })
36
+
37
+ const state = await platform.getState()
38
+ const account = await platform.points.getAccount()
39
+ const achievements = await platform.achievements.list()
40
+ ```
41
+
42
+ `service_role` / Supabase Secret key / `userId` は SDK に渡しません。ユーザーは Backend が `auth.uid()` から判定します。
43
+
44
+ ## Client events
45
+
46
+ Admin で `client` または `both` に設定された Event のみ送信できます。
47
+
48
+ ```ts
49
+ await platform.events.emit({
50
+ type: 'tutorial_completed',
51
+ sourceId: 'tutorial:v1',
52
+ })
53
+ ```
54
+
55
+ `sourceId` がある場合、標準の idempotency key は次です。
56
+
57
+ ```text
58
+ {gameId}:{eventType}:{sourceId}
59
+ ```
60
+
61
+ 明示的な `idempotencyKey` がある場合はそちらを優先します。
62
+
63
+ ## Typed game-local events
64
+
65
+ ```ts
66
+ type CoffeeRoasterEvents = {
67
+ tutorial_completed: {
68
+ value?: never
69
+ metadata?: never
70
+ }
71
+
72
+ roast_completed: {
73
+ value: number
74
+ metadata?: {
75
+ difficulty?: 'normal' | 'hard'
76
+ }
77
+ }
78
+ }
79
+
80
+ const platform = createGamePlatformClient<CoffeeRoasterEvents>({
81
+ supabase,
82
+ gameId: 'coffee_roaster',
83
+ })
84
+ ```
85
+
86
+ Event Definition はゲーム側で型を持ち、共通 SDK 自体へゲーム固有 Event をハードコードしません。
87
+
88
+ ## Points
89
+
90
+ ```ts
91
+ const account = await platform.points.getAccount()
92
+
93
+ const page = await platform.points.listTransactions({
94
+ limit: 30,
95
+ cursor: null,
96
+ })
97
+ ```
98
+
99
+ ポイント額は Backend / Admin ルールで決定します。SDK に `points.add()` や `points.set()` はありません。
100
+
101
+ ## Achievements
102
+
103
+ ```ts
104
+ const achievements = await platform.achievements.list()
105
+ ```
106
+
107
+ 現在のゲーム固有実績と Global 実績を取得します。進捗・解除判定・報酬付与は Backend が行います。
108
+
109
+ ## Login bonus
110
+
111
+ ```ts
112
+ const status = await platform.loginBonus.getStatus()
113
+ const result = await platform.loginBonus.claimAvailable()
114
+ ```
115
+
116
+ 特定 Campaign だけ受け取る場合:
117
+
118
+ ```ts
119
+ await platform.loginBonus.claimProgram('summer_campaign_2026')
120
+ ```
121
+
122
+ `claimAvailable()` は Backend が現在有効な Standard と Campaign を選択します。ゲーム固有 Standard がある場合は Global Standard より優先されます。
123
+
124
+ ## Development write guard
125
+
126
+ ```ts
127
+ const platform = createGamePlatformClient({
128
+ supabase,
129
+ gameId: 'coffee_roaster',
130
+ writesEnabled: import.meta.env.VITE_DEVELOPMENT_MODE !== 'true',
131
+ })
132
+ ```
133
+
134
+ `writesEnabled=false` の場合、Event送信とLogin Bonus claimは `WRITE_DISABLED` になります。これは開発事故防止であり、セキュリティ境界ではありません。
135
+
136
+ ## Security boundary
137
+
138
+ SDKは意図的に以下を提供しません。
139
+
140
+ ```text
141
+ points.add()
142
+ points.set()
143
+ achievements.unlock()
144
+ achievements.setProgress()
145
+ events.emitTrusted()
146
+ ```
147
+
148
+ スコア、勝敗、ゲーム終了など改ざん耐性が必要な処理は、ゲーム固有 RPC で検証して Trusted Event を発火してください。
149
+
150
+ ## Repository development
151
+
152
+ この monorepo は pnpm workspace を使用します。repository root で実行してください。
153
+
154
+ ```bash
155
+ pnpm install
156
+ pnpm check
157
+ ```
158
+
159
+ workspace 定義は `pnpm-workspace.yaml`、使用する pnpm version は root `package.json` の `packageManager` で固定しています。
160
+
161
+ ## Publishing
162
+
163
+ GitHub Actions の利用時間を抑えるため、release用のbuildは**ローカルマシンで作成してrepositoryへcommit**します。
164
+
165
+ repository rootで:
166
+
167
+ ```bash
168
+ pnpm release:prepare
169
+ ```
170
+
171
+ これによりローカルでtypecheck / test / buildを行い、次を生成します。
172
+
173
+ ```text
174
+ packages/game-platform/dist/
175
+ packages/game-platform/release-build.json
176
+ ```
177
+
178
+ `release-build.json` にはsourceとdistのSHA-256が記録されるため、build後にsourceだけ変更した状態ではRelease Actionが失敗します。
179
+
180
+ 生成物をcommitしてmainへpushします。
181
+
182
+ ```bash
183
+ git add pnpm-lock.yaml packages/game-platform/dist packages/game-platform/release-build.json packages/game-platform/package.json packages/game-platform/README.md
184
+ git commit -m "Prepare game platform SDK release"
185
+ git push origin main
186
+ ```
187
+
188
+ その後:
189
+
190
+ ```text
191
+ Actions
192
+ → Release Game Platform SDK
193
+ → Run workflow
194
+ → Branch: main
195
+ → Run workflow
196
+ ```
197
+
198
+ Release Action側では **pnpm install / typecheck / test / buildを実行しません**。commit済みartifactのhash検証、`npm pack --dry-run`、tag作成、npmjs.orgへのpublish、GitHub Release作成だけを行います。
199
+
200
+ 実際の registry publish に `npm publish` を使うのは、npm Trusted Publishing / OIDC 認証を npm CLI が処理するためです。workspace の package manager は pnpm のままです。
201
+
202
+ 初回 publish だけは npm にパッケージがまだ存在しないため、GitHub Actions secret `NPM_TOKEN` を一時的に使用できます。初回 publish 後は npmjs.com の package settings で `ZUBURI/kztk-game-platform` の `publish-sdk.yml` を Trusted Publisher に登録し、`npm publish` を許可してください。その後は `NPM_TOKEN` を削除し、OIDC で publish できます。
203
+
204
+ ローカルでprebuilt artifactだけ再確認する場合:
205
+
206
+ ```bash
207
+ pnpm release:verify
208
+ ```
209
+
210
+ 詳細は [`../../docs/PUBLISHING.md`](../../docs/PUBLISHING.md) を参照してください。
211
+
212
+ ## Supported backend
213
+
214
+ KZTK Game Platform Backend API v1 を対象とします。
@@ -0,0 +1,5 @@
1
+ import { type SupabaseRpcClient } from "../internal/callRpc.js";
2
+ import type { GamePlatformLogger } from "../types/api.js";
3
+ import type { AchievementState } from "../types/achievement.js";
4
+ export declare function listAchievements(supabase: SupabaseRpcClient, gameId: string, logger?: GamePlatformLogger): Promise<AchievementState[]>;
5
+ //# sourceMappingURL=listAchievements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listAchievements.d.ts","sourceRoot":"","sources":["../../src/achievements/listAchievements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAGxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE/D,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,iBAAiB,EAC3B,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAG7B"}
@@ -0,0 +1,8 @@
1
+ import { callRpc } from "../internal/callRpc.js";
2
+ import { mapAchievementState } from "../internal/mappers.js";
3
+ import { array, record } from "../internal/validators.js";
4
+ export async function listAchievements(supabase, gameId, logger) {
5
+ const raw = await callRpc(supabase, "game_platform_v1_list_achievements", { p_game_id: gameId }, { logger });
6
+ return array(record(raw, "achievement response").items, "achievement response.items").map(mapAchievementState);
7
+ }
8
+ //# sourceMappingURL=listAchievements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listAchievements.js","sourceRoot":"","sources":["../../src/achievements/listAchievements.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAA0B,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAC5D,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,2BAA2B,CAAA;AAIzD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,QAA2B,EAC3B,MAAc,EACd,MAA2B;IAE3B,MAAM,GAAG,GAAG,MAAM,OAAO,CAAU,QAAQ,EAAE,oCAAoC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;IACrH,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC,KAAK,EAAE,4BAA4B,CAAC,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAChH,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { EventMap, DefaultEventMap } from "../types/event.js";
2
+ import type { CreateGamePlatformClientOptions, GamePlatformClient } from "./types.js";
3
+ export declare function createGamePlatformClient<TEvents extends EventMap = DefaultEventMap>(options: CreateGamePlatformClientOptions): GamePlatformClient<TEvents>;
4
+ //# sourceMappingURL=createGamePlatformClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createGamePlatformClient.d.ts","sourceRoot":"","sources":["../../src/client/createGamePlatformClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAqB,MAAM,mBAAmB,CAAA;AAWrF,OAAO,KAAK,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAGrF,wBAAgB,wBAAwB,CACtC,OAAO,SAAS,QAAQ,GAAG,eAAe,EAC1C,OAAO,EAAE,+BAA+B,GAAG,kBAAkB,CAAC,OAAO,CAAC,CA4BvE"}
@@ -0,0 +1,37 @@
1
+ import { GamePlatformError } from "../errors/GamePlatformError.js";
2
+ import { listAchievements } from "../achievements/listAchievements.js";
3
+ import { emitEvent } from "../events/emitEvent.js";
4
+ import { getLoginBonusStatus } from "../login-bonus/getStatus.js";
5
+ import { claimAvailableLoginBonuses } from "../login-bonus/claimAvailable.js";
6
+ import { claimLoginBonusProgram } from "../login-bonus/claimProgram.js";
7
+ import { getPointAccount } from "../points/getAccount.js";
8
+ import { listTransactions } from "../points/listTransactions.js";
9
+ import { getState } from "../state/getState.js";
10
+ export function createGamePlatformClient(options) {
11
+ const gameId = options.gameId.trim();
12
+ if (!gameId)
13
+ throw new GamePlatformError("GAME_NOT_FOUND", "gameId is required");
14
+ const supabase = options.supabase;
15
+ const writesEnabled = options.writesEnabled ?? true;
16
+ const logger = options.logger;
17
+ return Object.freeze({
18
+ gameId,
19
+ getState: () => getState(supabase, gameId, logger),
20
+ events: Object.freeze({
21
+ emit: (input) => emitEvent(supabase, gameId, writesEnabled, input, logger),
22
+ }),
23
+ points: Object.freeze({
24
+ getAccount: () => getPointAccount(supabase, logger),
25
+ listTransactions: (input) => listTransactions(supabase, input, logger),
26
+ }),
27
+ achievements: Object.freeze({
28
+ list: () => listAchievements(supabase, gameId, logger),
29
+ }),
30
+ loginBonus: Object.freeze({
31
+ getStatus: () => getLoginBonusStatus(supabase, gameId, logger),
32
+ claimAvailable: () => claimAvailableLoginBonuses(supabase, gameId, writesEnabled, logger),
33
+ claimProgram: (programId) => claimLoginBonusProgram(supabase, gameId, programId, writesEnabled, logger),
34
+ }),
35
+ });
36
+ }
37
+ //# sourceMappingURL=createGamePlatformClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createGamePlatformClient.js","sourceRoot":"","sources":["../../src/client/createGamePlatformClient.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAA;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAA;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AAK/C,MAAM,UAAU,wBAAwB,CAEtC,OAAwC;IACxC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;IACpC,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,iBAAiB,CAAC,gBAAgB,EAAE,oBAAoB,CAAC,CAAA;IAEhF,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAwC,CAAA;IACjE,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,IAAI,CAAA;IACnD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAA;IAE7B,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,MAAM;QACN,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;QAClD,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;YACpB,IAAI,EAAE,CAAuC,KAAwC,EAAE,EAAE,CACvF,SAAS,CAAiB,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,CAAC;SAC5E,CAAC;QACF,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;YACpB,UAAU,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,MAAM,CAAC;YACnD,gBAAgB,EAAE,CAAC,KAA+B,EAAE,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC;SACjG,CAAC;QACF,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC;YAC1B,IAAI,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SACvD,CAAC;QACF,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC;YACxB,SAAS,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;YAC9D,cAAc,EAAE,GAAG,EAAE,CAAC,0BAA0B,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC;YACzF,YAAY,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,CAAC;SAChH,CAAC;KACH,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,35 @@
1
+ import type { GamePlatformLogger } from "../types/api.js";
2
+ import type { AchievementState } from "../types/achievement.js";
3
+ import type { DefaultEventMap, EmitEventInputFor, EventMap, EventResult } from "../types/event.js";
4
+ import type { LoginBonusClaimResult, LoginBonusSingleClaimResult, LoginBonusStatus } from "../types/loginBonus.js";
5
+ import type { ListTransactionsOptions, PointAccount, PointTransactionPage } from "../types/point.js";
6
+ import type { GamePlatformState } from "../types/state.js";
7
+ export type SupabaseClientLike = {
8
+ rpc: (...args: any[]) => any;
9
+ };
10
+ export type CreateGamePlatformClientOptions = {
11
+ supabase: SupabaseClientLike;
12
+ gameId: string;
13
+ writesEnabled?: boolean;
14
+ logger?: GamePlatformLogger;
15
+ };
16
+ export type GamePlatformClient<TEvents extends EventMap = DefaultEventMap> = {
17
+ readonly gameId: string;
18
+ getState: () => Promise<GamePlatformState>;
19
+ events: {
20
+ emit: <TType extends keyof TEvents & string>(input: EmitEventInputFor<TEvents, TType>) => Promise<EventResult>;
21
+ };
22
+ points: {
23
+ getAccount: () => Promise<PointAccount>;
24
+ listTransactions: (options?: ListTransactionsOptions) => Promise<PointTransactionPage>;
25
+ };
26
+ achievements: {
27
+ list: () => Promise<AchievementState[]>;
28
+ };
29
+ loginBonus: {
30
+ getStatus: () => Promise<LoginBonusStatus>;
31
+ claimAvailable: () => Promise<LoginBonusClaimResult>;
32
+ claimProgram: (programId: string) => Promise<LoginBonusSingleClaimResult>;
33
+ };
34
+ };
35
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAClG,OAAO,KAAK,EAAE,qBAAqB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAClH,OAAO,KAAK,EAAE,uBAAuB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAA;AACpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AAE1D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAA;CAC7B,CAAA;AAED,MAAM,MAAM,+BAA+B,GAAG;IAC5C,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAC5B,CAAA;AAED,MAAM,MAAM,kBAAkB,CAAC,OAAO,SAAS,QAAQ,GAAG,eAAe,IAAI;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAC1C,MAAM,EAAE;QACN,IAAI,EAAE,CAAC,KAAK,SAAS,MAAM,OAAO,GAAG,MAAM,EACzC,KAAK,EAAE,iBAAiB,CAAC,OAAO,EAAE,KAAK,CAAC,KACrC,OAAO,CAAC,WAAW,CAAC,CAAA;KAC1B,CAAA;IACD,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAA;QACvC,gBAAgB,EAAE,CAAC,OAAO,CAAC,EAAE,uBAAuB,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAAA;KACvF,CAAA;IACD,YAAY,EAAE;QACZ,IAAI,EAAE,MAAM,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAA;KACxC,CAAA;IACD,UAAU,EAAE;QACV,SAAS,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAA;QAC1C,cAAc,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,CAAA;QACpD,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,2BAA2B,CAAC,CAAA;KAC1E,CAAA;CACF,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/client/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ export type GamePlatformErrorCode = "AUTH_REQUIRED" | "PROFILE_REQUIRED" | "GAME_NOT_FOUND" | "GAME_INACTIVE" | "EVENT_NOT_FOUND" | "EVENT_NOT_ALLOWED" | "SOURCE_ID_REQUIRED" | "INVALID_EVENT_VALUE" | "INVALID_METADATA" | "IDEMPOTENCY_CONFLICT" | "LOGIN_PROGRAM_NOT_FOUND" | "LOGIN_PROGRAM_NOT_ELIGIBLE" | "NETWORK_ERROR" | "RPC_ERROR" | "INVALID_RESPONSE" | "INVALID_CURSOR" | "INVALID_ARGUMENT" | "API_VERSION_MISMATCH" | "WRITE_DISABLED";
2
+ export declare class GamePlatformError extends Error {
3
+ readonly code: GamePlatformErrorCode;
4
+ readonly retryable: boolean;
5
+ readonly cause: unknown;
6
+ constructor(code: GamePlatformErrorCode, message: string, options?: {
7
+ retryable?: boolean;
8
+ cause?: unknown;
9
+ });
10
+ }
11
+ //# sourceMappingURL=GamePlatformError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GamePlatformError.d.ts","sourceRoot":"","sources":["../../src/errors/GamePlatformError.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAC7B,eAAe,GACf,kBAAkB,GAClB,gBAAgB,GAChB,eAAe,GACf,iBAAiB,GACjB,mBAAmB,GACnB,oBAAoB,GACpB,qBAAqB,GACrB,kBAAkB,GAClB,sBAAsB,GACtB,yBAAyB,GACzB,4BAA4B,GAC5B,eAAe,GACf,WAAW,GACX,kBAAkB,GAClB,gBAAgB,GAChB,kBAAkB,GAClB,sBAAsB,GACtB,gBAAgB,CAAA;AAEpB,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAA;IACpC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,SAAkB,KAAK,EAAE,OAAO,CAAA;gBAG9B,IAAI,EAAE,qBAAqB,EAC3B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAQzD"}
@@ -0,0 +1,13 @@
1
+ export class GamePlatformError extends Error {
2
+ code;
3
+ retryable;
4
+ cause;
5
+ constructor(code, message, options = {}) {
6
+ super(message);
7
+ this.name = "GamePlatformError";
8
+ this.code = code;
9
+ this.retryable = options.retryable ?? false;
10
+ this.cause = options.cause;
11
+ }
12
+ }
13
+ //# sourceMappingURL=GamePlatformError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GamePlatformError.js","sourceRoot":"","sources":["../../src/errors/GamePlatformError.ts"],"names":[],"mappings":"AAqBA,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IACjC,IAAI,CAAuB;IAC3B,SAAS,CAAS;IACT,KAAK,CAAS;IAEhC,YACE,IAA2B,EAC3B,OAAe,EACf,UAAoD,EAAE;QAEtD,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAA;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAA;QAC3C,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAA;IAC5B,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import { GamePlatformError } from "./GamePlatformError.js";
2
+ export declare function normalizeError(error: unknown): GamePlatformError;
3
+ //# sourceMappingURL=normalizeError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizeError.d.ts","sourceRoot":"","sources":["../../src/errors/normalizeError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA8B,MAAM,wBAAwB,CAAA;AAsDtF,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAwBhE"}
@@ -0,0 +1,69 @@
1
+ import { GamePlatformError } from "./GamePlatformError.js";
2
+ const BUSINESS_CODES = new Set([
3
+ "AUTH_REQUIRED",
4
+ "PROFILE_REQUIRED",
5
+ "GAME_NOT_FOUND",
6
+ "GAME_INACTIVE",
7
+ "EVENT_NOT_FOUND",
8
+ "EVENT_NOT_ALLOWED",
9
+ "SOURCE_ID_REQUIRED",
10
+ "INVALID_EVENT_VALUE",
11
+ "INVALID_METADATA",
12
+ "IDEMPOTENCY_CONFLICT",
13
+ "LOGIN_PROGRAM_NOT_FOUND",
14
+ "LOGIN_PROGRAM_NOT_ELIGIBLE",
15
+ ]);
16
+ function asMessage(error) {
17
+ if (error instanceof Error)
18
+ return error.message;
19
+ if (typeof error === "string")
20
+ return error;
21
+ if (error && typeof error === "object" && "message" in error) {
22
+ const message = error.message;
23
+ if (typeof message === "string")
24
+ return message;
25
+ }
26
+ return "Unknown RPC error";
27
+ }
28
+ function extractBusinessCode(message) {
29
+ for (const code of BUSINESS_CODES) {
30
+ if (message === code || message.startsWith(`${code}:`) || message.includes(` ${code}`)) {
31
+ return code;
32
+ }
33
+ }
34
+ return null;
35
+ }
36
+ function looksNetworkRelated(error, message) {
37
+ if (error instanceof TypeError && /fetch|network|connection|socket/i.test(message))
38
+ return true;
39
+ return /failed to fetch|networkerror|network request failed|econnreset|etimedout|fetch failed/i.test(message);
40
+ }
41
+ function looksTransientRpc(error) {
42
+ if (!error || typeof error !== "object")
43
+ return false;
44
+ const code = error.code;
45
+ return typeof code === "string" && ["40001", "40P01", "53300", "57P01", "57P03"].includes(code);
46
+ }
47
+ export function normalizeError(error) {
48
+ if (error instanceof GamePlatformError)
49
+ return error;
50
+ const message = asMessage(error);
51
+ const businessCode = extractBusinessCode(message);
52
+ if (businessCode) {
53
+ return new GamePlatformError(businessCode, message, { cause: error });
54
+ }
55
+ if (looksNetworkRelated(error, message)) {
56
+ return new GamePlatformError("NETWORK_ERROR", message, {
57
+ retryable: true,
58
+ cause: error,
59
+ });
60
+ }
61
+ if (looksTransientRpc(error)) {
62
+ return new GamePlatformError("RPC_ERROR", message, {
63
+ retryable: true,
64
+ cause: error,
65
+ });
66
+ }
67
+ return new GamePlatformError("RPC_ERROR", message, { cause: error });
68
+ }
69
+ //# sourceMappingURL=normalizeError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizeError.js","sourceRoot":"","sources":["../../src/errors/normalizeError.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { type SupabaseRpcClient } from "../internal/callRpc.js";
2
+ import type { GamePlatformLogger } from "../types/api.js";
3
+ import type { EmitEventInputFor, EventMap, EventResult } from "../types/event.js";
4
+ export declare function emitEvent<TEvents extends EventMap, TType extends keyof TEvents & string>(supabase: SupabaseRpcClient, gameId: string, writesEnabled: boolean, input: EmitEventInputFor<TEvents, TType>, logger?: GamePlatformLogger): Promise<EventResult>;
5
+ //# sourceMappingURL=emitEvent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitEvent.d.ts","sourceRoot":"","sources":["../../src/events/emitEvent.ts"],"names":[],"mappings":"AACA,OAAO,EAAW,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAExE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAGjF,wBAAsB,SAAS,CAC7B,OAAO,SAAS,QAAQ,EACxB,KAAK,SAAS,MAAM,OAAO,GAAG,MAAM,EAEpC,QAAQ,EAAE,iBAAiB,EAC3B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,OAAO,EACtB,KAAK,EAAE,iBAAiB,CAAC,OAAO,EAAE,KAAK,CAAC,EACxC,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,WAAW,CAAC,CAgCtB"}
@@ -0,0 +1,25 @@
1
+ import { GamePlatformError } from "../errors/GamePlatformError.js";
2
+ import { callRpc } from "../internal/callRpc.js";
3
+ import { mapEventResult } from "../internal/mappers.js";
4
+ import { createIdempotencyKey } from "./idempotency.js";
5
+ export async function emitEvent(supabase, gameId, writesEnabled, input, logger) {
6
+ if (!writesEnabled) {
7
+ throw new GamePlatformError("WRITE_DISABLED", "Game Platform writes are disabled");
8
+ }
9
+ const idempotencyKey = createIdempotencyKey(gameId, input.type, "sourceId" in input ? input.sourceId : undefined, "idempotencyKey" in input ? input.idempotencyKey : undefined);
10
+ const raw = await callRpc(supabase, "game_platform_v1_emit_event", {
11
+ p_game_id: gameId,
12
+ p_event_type: input.type,
13
+ p_value: "value" in input ? input.value ?? null : null,
14
+ p_source_id: "sourceId" in input ? input.sourceId ?? null : null,
15
+ p_idempotency_key: idempotencyKey,
16
+ p_metadata: "metadata" in input ? input.metadata ?? {} : {},
17
+ }, { logger });
18
+ const result = mapEventResult(raw);
19
+ logger?.debug?.("Game Platform event processed", {
20
+ eventType: input.type,
21
+ duplicate: result.duplicate,
22
+ });
23
+ return result;
24
+ }
25
+ //# sourceMappingURL=emitEvent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitEvent.js","sourceRoot":"","sources":["../../src/events/emitEvent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,OAAO,EAA0B,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAGvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAEvD,MAAM,CAAC,KAAK,UAAU,SAAS,CAI7B,QAA2B,EAC3B,MAAc,EACd,aAAsB,EACtB,KAAwC,EACxC,MAA2B;IAE3B,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,iBAAiB,CAAC,gBAAgB,EAAE,mCAAmC,CAAC,CAAA;IACpF,CAAC;IAED,MAAM,cAAc,GAAG,oBAAoB,CACzC,MAAM,EACN,KAAK,CAAC,IAAI,EACV,UAAU,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,EAChD,gBAAgB,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAC7D,CAAA;IAED,MAAM,GAAG,GAAG,MAAM,OAAO,CACvB,QAAQ,EACR,6BAA6B,EAC7B;QACE,SAAS,EAAE,MAAM;QACjB,YAAY,EAAE,KAAK,CAAC,IAAI;QACxB,OAAO,EAAE,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI;QACtD,WAAW,EAAE,UAAU,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI;QAChE,iBAAiB,EAAE,cAAc;QACjC,UAAU,EAAE,UAAU,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;KAC5D,EACD,EAAE,MAAM,EAAE,CACX,CAAA;IAED,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,CAAA;IAClC,MAAM,EAAE,KAAK,EAAE,CAAC,+BAA+B,EAAE;QAC/C,SAAS,EAAE,KAAK,CAAC,IAAI;QACrB,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAC,CAAA;IACF,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function createIdempotencyKey(gameId: string, eventType: string, sourceId?: string, explicitKey?: string): string;
2
+ //# sourceMappingURL=idempotency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../src/events/idempotency.ts"],"names":[],"mappings":"AAAA,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,CAaR"}
@@ -0,0 +1,14 @@
1
+ export function createIdempotencyKey(gameId, eventType, sourceId, explicitKey) {
2
+ const explicit = explicitKey?.trim();
3
+ if (explicit)
4
+ return explicit;
5
+ const source = sourceId?.trim();
6
+ if (source)
7
+ return `${gameId}:${eventType}:${source}`;
8
+ const cryptoObject = globalThis.crypto;
9
+ if (cryptoObject && typeof cryptoObject.randomUUID === "function") {
10
+ return cryptoObject.randomUUID();
11
+ }
12
+ return `gp:${Date.now()}:${Math.random().toString(36).slice(2)}:${Math.random().toString(36).slice(2)}`;
13
+ }
14
+ //# sourceMappingURL=idempotency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../../src/events/idempotency.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,oBAAoB,CAClC,MAAc,EACd,SAAiB,EACjB,QAAiB,EACjB,WAAoB;IAEpB,MAAM,QAAQ,GAAG,WAAW,EAAE,IAAI,EAAE,CAAA;IACpC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAA;IAE7B,MAAM,MAAM,GAAG,QAAQ,EAAE,IAAI,EAAE,CAAA;IAC/B,IAAI,MAAM;QAAE,OAAO,GAAG,MAAM,IAAI,SAAS,IAAI,MAAM,EAAE,CAAA;IAErD,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,CAAA;IACtC,IAAI,YAAY,IAAI,OAAO,YAAY,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;QAClE,OAAO,YAAY,CAAC,UAAU,EAAE,CAAA;IAClC,CAAC;IAED,OAAO,MAAM,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAA;AACzG,CAAC"}
@@ -0,0 +1,11 @@
1
+ export { createGamePlatformClient } from "./client/createGamePlatformClient.js";
2
+ export type { CreateGamePlatformClientOptions, GamePlatformClient, SupabaseClientLike } from "./client/types.js";
3
+ export { GamePlatformError } from "./errors/GamePlatformError.js";
4
+ export type { GamePlatformErrorCode } from "./errors/GamePlatformError.js";
5
+ export type { GamePlatformLogger, Json, JsonObject } from "./types/api.js";
6
+ export type { AchievementProgress, AchievementState, AchievementUnlocked } from "./types/achievement.js";
7
+ export type { DefaultEventMap, EmitEventInputFor, EventMap, EventResult, EventSpec } from "./types/event.js";
8
+ export type { LoginBonusClaim, LoginBonusClaimResult, LoginBonusProgramState, LoginBonusSingleClaimResult, LoginBonusStatus } from "./types/loginBonus.js";
9
+ export type { ListTransactionsOptions, PointAccount, PointTransaction, PointTransactionPage } from "./types/point.js";
10
+ export type { GamePlatformState } from "./types/state.js";
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAA;AAC/E,YAAY,EAAE,+BAA+B,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAEhH,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AACjE,YAAY,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AAE1E,YAAY,EAAE,kBAAkB,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC1E,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AACxG,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5G,YAAY,EAAE,eAAe,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC1J,YAAY,EAAE,uBAAuB,EAAE,YAAY,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACrH,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createGamePlatformClient } from "./client/createGamePlatformClient.js";
2
+ export { GamePlatformError } from "./errors/GamePlatformError.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAA;AAG/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA"}
@@ -0,0 +1,20 @@
1
+ import type { GamePlatformLogger } from "../types/api.js";
2
+ type RpcErrorLike = {
3
+ code?: string;
4
+ message?: string;
5
+ details?: string;
6
+ hint?: string;
7
+ };
8
+ export type SupabaseRpcClient = {
9
+ rpc: (fn: string, args?: Record<string, unknown>) => PromiseLike<{
10
+ data: unknown;
11
+ error: RpcErrorLike | null;
12
+ }>;
13
+ };
14
+ type CallRpcOptions = {
15
+ logger?: GamePlatformLogger | undefined;
16
+ retries?: number;
17
+ };
18
+ export declare function callRpc<T>(client: SupabaseRpcClient, name: string, params: Record<string, unknown> | undefined, options?: CallRpcOptions): Promise<T>;
19
+ export {};
20
+ //# sourceMappingURL=callRpc.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callRpc.d.ts","sourceRoot":"","sources":["../../src/internal/callRpc.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAKzD,KAAK,YAAY,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAExF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,CACH,EAAE,EAAE,MAAM,EACV,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC3B,WAAW,CAAC;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAAA;KAAE,CAAC,CAAA;CAChE,CAAA;AAED,KAAK,cAAc,GAAG;IACpB,MAAM,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAA;IACvC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB,CAAA;AAID,wBAAsB,OAAO,CAAC,CAAC,EAC7B,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC3C,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,CAAC,CAAC,CAkCZ"}
@@ -0,0 +1,40 @@
1
+ import { normalizeError } from "../errors/normalizeError.js";
2
+ import { GamePlatformError } from "../errors/GamePlatformError.js";
3
+ import { validateApiVersion } from "./validateApiVersion.js";
4
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
5
+ export async function callRpc(client, name, params, options = {}) {
6
+ const maxRetries = options.retries ?? 1;
7
+ let attempt = 0;
8
+ while (true) {
9
+ const startedAt = Date.now();
10
+ try {
11
+ const { data, error } = await client.rpc(name, params);
12
+ if (error)
13
+ throw normalizeError(error);
14
+ if (data === null || data === undefined) {
15
+ throw new GamePlatformError("INVALID_RESPONSE", `${name} returned no data`);
16
+ }
17
+ validateApiVersion(data);
18
+ options.logger?.debug?.("Game Platform RPC completed", {
19
+ rpc: name,
20
+ durationMs: Date.now() - startedAt,
21
+ attempt,
22
+ });
23
+ return data;
24
+ }
25
+ catch (error) {
26
+ const normalized = normalizeError(error);
27
+ options.logger?.warn?.("Game Platform RPC failed", {
28
+ rpc: name,
29
+ code: normalized.code,
30
+ retryable: normalized.retryable,
31
+ attempt,
32
+ });
33
+ if (!normalized.retryable || attempt >= maxRetries)
34
+ throw normalized;
35
+ attempt += 1;
36
+ await sleep(100 * attempt);
37
+ }
38
+ }
39
+ }
40
+ //# sourceMappingURL=callRpc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"callRpc.js","sourceRoot":"","sources":["../../src/internal/callRpc.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAgB5D,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;AAErF,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,MAAyB,EACzB,IAAY,EACZ,MAA2C,EAC3C,UAA0B,EAAE;IAE5B,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,CAAA;IACvC,IAAI,OAAO,GAAG,CAAC,CAAA;IAEf,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QAC5B,IAAI,CAAC;YACH,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;YACtD,IAAI,KAAK;gBAAE,MAAM,cAAc,CAAC,KAAK,CAAC,CAAA;YACtC,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACxC,MAAM,IAAI,iBAAiB,CAAC,kBAAkB,EAAE,GAAG,IAAI,mBAAmB,CAAC,CAAA;YAC7E,CAAC;YAED,kBAAkB,CAAC,IAAI,CAAC,CAAA;YACxB,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,6BAA6B,EAAE;gBACrD,GAAG,EAAE,IAAI;gBACT,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS;gBAClC,OAAO;aACR,CAAC,CAAA;YACF,OAAO,IAAS,CAAA;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,CAAA;YACxC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,0BAA0B,EAAE;gBACjD,GAAG,EAAE,IAAI;gBACT,IAAI,EAAE,UAAU,CAAC,IAAI;gBACrB,SAAS,EAAE,UAAU,CAAC,SAAS;gBAC/B,OAAO;aACR,CAAC,CAAA;YAEF,IAAI,CAAC,UAAU,CAAC,SAAS,IAAI,OAAO,IAAI,UAAU;gBAAE,MAAM,UAAU,CAAA;YACpE,OAAO,IAAI,CAAC,CAAA;YACZ,MAAM,KAAK,CAAC,GAAG,GAAG,OAAO,CAAC,CAAA;QAC5B,CAAC;IACH,CAAC;AACH,CAAC"}