@zhimakechuang/game-sdk 1.0.0 → 1.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.
- package/README.md +176 -176
- package/dist/index.d.mts +25 -5
- package/dist/index.d.ts +25 -5
- package/dist/index.global.js +109 -3
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +103 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +110 -3
- package/dist/index.mjs.map +1 -1
- package/package.json +43 -55
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/platform.ts","../src/index.ts"],"sourcesContent":["/**\r\n * 平台适配层\r\n *\r\n * 自动检测运行环境(微信小游戏 / 抖音小游戏 / H5),\r\n * 封装 HTTP 请求和本地存储,对上层提供统一接口。\r\n */\r\n\r\n// 小游戏运行时的全局对象(微信/抖音),H5 下不存在\r\ndeclare const wx: any;\r\ndeclare const tt: any;\r\n\r\n// ─── 环境检测 ──────────────────────────────────────────\r\n\r\ntype Runtime = 'wx' | 'tt' | 'h5';\r\n\r\nfunction detectRuntime(): Runtime {\r\n if (typeof wx !== 'undefined' && typeof (wx as any).request === 'function') return 'wx';\r\n if (typeof tt !== 'undefined' && typeof (tt as any).request === 'function') return 'tt';\r\n return 'h5';\r\n}\r\n\r\nconst runtime = detectRuntime();\r\n\r\n// ─── HTTP 请求 ─────────────────────────────────────────\r\n\r\nexport interface HttpRequest {\r\n url: string;\r\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\r\n headers?: Record<string, string>;\r\n body?: any;\r\n}\r\n\r\nexport interface HttpResponse<T = any> {\r\n status: number;\r\n data: T;\r\n}\r\n\r\nexport async function request<T = any>(req: HttpRequest): Promise<HttpResponse<T>> {\r\n if (runtime === 'wx' || runtime === 'tt') {\r\n return miniProgramRequest<T>(req);\r\n }\r\n return fetchRequest<T>(req);\r\n}\r\n\r\n/** 微信/抖音小游戏请求(wx.request / tt.request) */\r\nfunction miniProgramRequest<T>(req: HttpRequest): Promise<HttpResponse<T>> {\r\n const api = (runtime === 'wx' ? wx : tt) as any;\r\n return new Promise((resolve, reject) => {\r\n api.request({\r\n url: req.url,\r\n method: req.method,\r\n header: req.headers || {},\r\n data: req.body,\r\n success: (res: any) => {\r\n resolve({\r\n status: res.statusCode,\r\n data: res.data as T,\r\n });\r\n },\r\n fail: (err: any) => {\r\n reject(new Error(err?.errMsg || 'request failed'));\r\n },\r\n });\r\n });\r\n}\r\n\r\n/** H5 fetch 请求 */\r\nasync function fetchRequest<T>(req: HttpRequest): Promise<HttpResponse<T>> {\r\n const res = await fetch(req.url, {\r\n method: req.method,\r\n headers: { 'Content-Type': 'application/json', ...req.headers },\r\n body: req.body !== undefined ? JSON.stringify(req.body) : undefined,\r\n });\r\n const data = await res.json().catch(() => ({}));\r\n return { status: res.status, data: data as T };\r\n}\r\n\r\n// ─── 本地存储(token 持久化)────────────────────────────\r\n\r\nexport function storageGet(key: string): string | null {\r\n if (runtime === 'wx' || runtime === 'tt') {\r\n const api = (runtime === 'wx' ? wx : tt) as any;\r\n try {\r\n const val = api.getStorageSync(key);\r\n return val || null;\r\n } catch {\r\n return null;\r\n }\r\n }\r\n try {\r\n return localStorage.getItem(key);\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport function storageSet(key: string, value: string): void {\r\n if (runtime === 'wx' || runtime === 'tt') {\r\n const api = (runtime === 'wx' ? wx : tt) as any;\r\n try {\r\n api.setStorageSync(key, value);\r\n } catch {\r\n /* ignore */\r\n }\r\n return;\r\n }\r\n try {\r\n localStorage.setItem(key, value);\r\n } catch {\r\n /* ignore */\r\n }\r\n}\r\n\r\nexport function storageRemove(key: string): void {\r\n if (runtime === 'wx' || runtime === 'tt') {\r\n const api = (runtime === 'wx' ? wx : tt) as any;\r\n try {\r\n api.removeStorageSync(key);\r\n } catch {\r\n /* ignore */\r\n }\r\n return;\r\n }\r\n try {\r\n localStorage.removeItem(key);\r\n } catch {\r\n /* ignore */\r\n }\r\n}\r\n","/**\r\n * @game-factory/game-sdk\r\n *\r\n * 通用游戏 SDK,封装 game-server-shared 所有 HTTP 接口。\r\n *\r\n * 用法(工程化):\r\n * import { createGameSdk } from '@game-factory/game-sdk';\r\n * const sdk = createGameSdk({ gameId: 'merge-game', baseUrl: 'https://api.example.com' });\r\n * await sdk.auth.login({ code: 'xxx', platform: 'wx' });\r\n * await sdk.score.submit({ score: 1000 });\r\n *\r\n * 用法(CDN):\r\n * <script src=\"https://cdn.example.com/game-sdk/index.umd.js\"></script>\r\n * const sdk = GameSDK.createGameSdk({ gameId: 'merge-game', baseUrl: '...' });\r\n *\r\n * 用法(模拟模式,不连服务端):\r\n * const sdk = createGameSdk({ gameId: 'demo', baseUrl: '', mock: true });\r\n */\r\n\r\nimport {\r\n GameSdkConfig, LoginOptions, LoginResult, PlayerProfile,\r\n SubmitScoreOptions, SubmitScoreResult,\r\n LeaderboardEntry, MyRank,\r\n} from './types';\r\nimport { request, storageGet, storageSet, storageRemove } from './platform';\r\n\r\n// re-export 所有类型,供外部使用\r\nexport * from './types';\r\n\r\n// ─── 错误 ──────────────────────────────────────────────\r\n\r\nexport class GameSdkError extends Error {\r\n constructor(\r\n message: string,\r\n public status: number,\r\n public data?: any,\r\n ) {\r\n super(message);\r\n this.name = 'GameSdkError';\r\n }\r\n}\r\n\r\n// ─── SDK 接口定义 ───────────────────────────────────────\r\n\r\nexport interface GameSdk {\r\n /** 身份与登录 */\r\n auth: {\r\n /** 登录(微信/抖音/访客),成功后自动持久化 token */\r\n login(opts: LoginOptions): Promise<LoginResult>;\r\n /** 获取当前登录玩家信息(需先 login) */\r\n me(): Promise<PlayerProfile>;\r\n /** 登出,清除本地 token */\r\n logout(): void;\r\n /** 获取当前 token(用于调试) */\r\n getToken(): string | null;\r\n };\r\n /** 分数 */\r\n score: {\r\n /** 提交分数(需登录) */\r\n submit(opts: SubmitScoreOptions): Promise<SubmitScoreResult>;\r\n };\r\n /** 排行榜 */\r\n leaderboard: {\r\n /** 获取 Top N(匿名可访问,limit 默认 100) */\r\n top(limit?: number): Promise<LeaderboardEntry[]>;\r\n /** 获取当前玩家排名(需登录) */\r\n myRank(): Promise<MyRank>;\r\n /** 获取附近排名(需登录,range 默认 ±5) */\r\n nearby(range?: number): Promise<LeaderboardEntry[]>;\r\n };\r\n /** 玩家档案 */\r\n player: {\r\n /** 获取当前玩家档案(需登录) */\r\n getProfile(): Promise<PlayerProfile>;\r\n /** 更新档案(昵称/头像/扩展字段) */\r\n updateProfile(patch: {\r\n nickname?: string;\r\n avatarUrl?: string;\r\n extra?: Record<string, any>;\r\n }): Promise<PlayerProfile>;\r\n };\r\n /** 云存档(KV) */\r\n save: {\r\n /** 保存数据到指定 key */\r\n set(key: string, value: Record<string, any>): Promise<{ success: boolean; key: string; value: Record<string, any> }>;\r\n /** 读取指定 key 的数据 */\r\n get(key: string): Promise<Record<string, any> | null>;\r\n /** 读取所有存档 */\r\n getAll(): Promise<Record<string, any>>;\r\n /** 删除指定 key */\r\n remove(key: string): Promise<{ success: boolean; key: string }>;\r\n /** 清空所有存档 */\r\n clear(): Promise<{ success: boolean; deleted: number }>;\r\n };\r\n}\r\n\r\n// ─── 创建 SDK ───────────────────────────────────────────\r\n\r\nexport function createGameSdk(config: GameSdkConfig): GameSdk {\r\n const apiUrl = `${config.baseUrl}/game-api/${config.gameId}`;\r\n const tokenKey = `gf_token_${config.gameId}`;\r\n\r\n // token 管理(从本地存储恢复)\r\n let token: string | null = storageGet(tokenKey);\r\n\r\n function getToken(): string | null {\r\n return token;\r\n }\r\n\r\n function setToken(t: string | null): void {\r\n token = t;\r\n if (t) storageSet(tokenKey, t);\r\n else storageRemove(tokenKey);\r\n }\r\n\r\n // ── 内部 HTTP 封装 ──────────────────────────────────\r\n async function call<T = any>(\r\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\r\n path: string,\r\n body?: any,\r\n ): Promise<T> {\r\n if (config.mock) {\r\n return mockResponse<T>(method, path, body);\r\n }\r\n\r\n const headers: Record<string, string> = {};\r\n if (token) headers['Authorization'] = `Bearer ${token}`;\r\n\r\n const res = await request<T>({\r\n url: `${apiUrl}${path}`,\r\n method,\r\n headers,\r\n body,\r\n });\r\n\r\n if (res.status >= 400) {\r\n const msg = (res.data as any)?.message || `HTTP ${res.status}`;\r\n throw new GameSdkError(msg, res.status, res.data);\r\n }\r\n return res.data;\r\n }\r\n\r\n // ── 方法面 ──────────────────────────────────────────\r\n const sdk: GameSdk = {\r\n auth: {\r\n async login(opts: LoginOptions): Promise<LoginResult> {\r\n const result = await call<LoginResult>('POST', '/auth/login', opts);\r\n setToken(result.token);\r\n return result;\r\n },\r\n me: () => call<PlayerProfile>('GET', '/auth/me'),\r\n logout: () => setToken(null),\r\n getToken,\r\n },\r\n\r\n score: {\r\n submit: (opts: SubmitScoreOptions) => call<SubmitScoreResult>('POST', '/score/submit', opts),\r\n },\r\n\r\n leaderboard: {\r\n top: (limit?: number) =>\r\n call<LeaderboardEntry[]>('GET', `/leaderboard?limit=${limit || 100}`),\r\n myRank: () => call<MyRank>('GET', '/leaderboard/me'),\r\n nearby: (range?: number) =>\r\n call<LeaderboardEntry[]>('GET', `/leaderboard/nearby?range=${range || 5}`),\r\n },\r\n\r\n player: {\r\n getProfile: () => call<PlayerProfile>('GET', '/player/profile'),\r\n updateProfile: (patch) => call<PlayerProfile>('PATCH', '/player/profile', patch),\r\n },\r\n\r\n save: {\r\n set: (key, value) => call('POST', `/storage/${key}`, { value }),\r\n get: (key) => call('GET', `/storage/${key}`),\r\n getAll: () => call('GET', '/storage'),\r\n remove: (key) => call('DELETE', `/storage/${key}`),\r\n clear: () => call('DELETE', '/storage'),\r\n },\r\n };\r\n\r\n return sdk;\r\n}\r\n\r\n// ─── Mock 模式(模板开发不连服务端)──────────────────────\r\n\r\nlet mockScore = 0;\r\nlet mockSaves: Record<string, Record<string, any>> = {};\r\nlet mockToken: string | null = null;\r\n\r\nasync function mockResponse<T>(method: string, path: string, body: any): Promise<T> {\r\n await new Promise((r) => setTimeout(r, 50)); // 模拟网络延迟\r\n\r\n // auth\r\n if (path === '/auth/login') {\r\n mockToken = `mock_token_${Date.now()}`;\r\n const profile: PlayerProfile = {\r\n openId: 'mock_openid',\r\n platform: body?.platform || 'guest',\r\n nickname: body?.nickname || '测试玩家',\r\n bestScore: mockScore,\r\n playCount: 0,\r\n createdAt: new Date().toISOString(),\r\n lastSeenAt: new Date().toISOString(),\r\n };\r\n return { token: mockToken, openId: 'mock_openid', isNew: true, profile } as T;\r\n }\r\n if (path === '/auth/me') {\r\n return {\r\n openId: 'mock_openid',\r\n platform: 'guest',\r\n nickname: '测试玩家',\r\n bestScore: mockScore,\r\n playCount: 1,\r\n createdAt: new Date().toISOString(),\r\n lastSeenAt: new Date().toISOString(),\r\n } as T;\r\n }\r\n\r\n // score\r\n if (path === '/score/submit') {\r\n const prev = mockScore;\r\n mockScore = Math.max(mockScore, body?.score || 0);\r\n return {\r\n submitted: body?.score || 0,\r\n isHighScore: (body?.score || 0) > prev,\r\n previousBest: prev,\r\n bestScore: mockScore,\r\n rank: 1,\r\n } as T;\r\n }\r\n\r\n // leaderboard\r\n if (path.startsWith('/leaderboard?')) {\r\n return [{\r\n rank: 1,\r\n openId: 'mock***oid',\r\n nickname: '测试玩家',\r\n score: mockScore,\r\n isSelf: true,\r\n }] as T;\r\n }\r\n if (path === '/leaderboard/me') {\r\n return { rank: 1, score: mockScore, totalPlayers: 1, playCount: 1 } as T;\r\n }\r\n if (path.startsWith('/leaderboard/nearby')) {\r\n return [{\r\n rank: 1,\r\n openId: 'mock***oid',\r\n nickname: '测试玩家',\r\n score: mockScore,\r\n isSelf: true,\r\n }] as T;\r\n }\r\n\r\n // player\r\n if (path === '/player/profile' && method === 'GET') {\r\n return {\r\n openId: 'mock_openid',\r\n platform: 'guest',\r\n nickname: '测试玩家',\r\n bestScore: mockScore,\r\n playCount: 1,\r\n createdAt: new Date().toISOString(),\r\n lastSeenAt: new Date().toISOString(),\r\n } as T;\r\n }\r\n\r\n // storage\r\n if (path.startsWith('/storage/') && method === 'POST') {\r\n const key = path.split('/storage/')[1];\r\n mockSaves[key] = body?.value || {};\r\n return { success: true, key, value: mockSaves[key] } as T;\r\n }\r\n if (path.startsWith('/storage/') && method === 'GET') {\r\n const key = path.split('/storage/')[1];\r\n return (mockSaves[key] || null) as T;\r\n }\r\n if (path === '/storage' && method === 'GET') {\r\n return mockSaves as T;\r\n }\r\n if (path.startsWith('/storage/') && method === 'DELETE') {\r\n const key = path.split('/storage/')[1];\r\n delete mockSaves[key];\r\n return { success: true, key } as T;\r\n }\r\n if (path === '/storage' && method === 'DELETE') {\r\n const count = Object.keys(mockSaves).length;\r\n mockSaves = {};\r\n return { success: true, deleted: count } as T;\r\n }\r\n\r\n return {} as T;\r\n}\r\n"],"mappings":";AAeA,SAAS,gBAAyB;AAChC,MAAI,OAAO,OAAO,eAAe,OAAQ,GAAW,YAAY,WAAY,QAAO;AACnF,MAAI,OAAO,OAAO,eAAe,OAAQ,GAAW,YAAY,WAAY,QAAO;AACnF,SAAO;AACT;AAEA,IAAM,UAAU,cAAc;AAgB9B,eAAsB,QAAiB,KAA4C;AACjF,MAAI,YAAY,QAAQ,YAAY,MAAM;AACxC,WAAO,mBAAsB,GAAG;AAAA,EAClC;AACA,SAAO,aAAgB,GAAG;AAC5B;AAGA,SAAS,mBAAsB,KAA4C;AACzE,QAAM,MAAO,YAAY,OAAO,KAAK;AACrC,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,QAAQ;AAAA,MACV,KAAK,IAAI;AAAA,MACT,QAAQ,IAAI;AAAA,MACZ,QAAQ,IAAI,WAAW,CAAC;AAAA,MACxB,MAAM,IAAI;AAAA,MACV,SAAS,CAAC,QAAa;AACrB,gBAAQ;AAAA,UACN,QAAQ,IAAI;AAAA,UACZ,MAAM,IAAI;AAAA,QACZ,CAAC;AAAA,MACH;AAAA,MACA,MAAM,CAAC,QAAa;AAClB,eAAO,IAAI,MAAM,KAAK,UAAU,gBAAgB,CAAC;AAAA,MACnD;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAGA,eAAe,aAAgB,KAA4C;AACzE,QAAM,MAAM,MAAM,MAAM,IAAI,KAAK;AAAA,IAC/B,QAAQ,IAAI;AAAA,IACZ,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,IAAI,QAAQ;AAAA,IAC9D,MAAM,IAAI,SAAS,SAAY,KAAK,UAAU,IAAI,IAAI,IAAI;AAAA,EAC5D,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,SAAO,EAAE,QAAQ,IAAI,QAAQ,KAAgB;AAC/C;AAIO,SAAS,WAAW,KAA4B;AACrD,MAAI,YAAY,QAAQ,YAAY,MAAM;AACxC,UAAM,MAAO,YAAY,OAAO,KAAK;AACrC,QAAI;AACF,YAAM,MAAM,IAAI,eAAe,GAAG;AAClC,aAAO,OAAO;AAAA,IAChB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,MAAI;AACF,WAAO,aAAa,QAAQ,GAAG;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,WAAW,KAAa,OAAqB;AAC3D,MAAI,YAAY,QAAQ,YAAY,MAAM;AACxC,UAAM,MAAO,YAAY,OAAO,KAAK;AACrC,QAAI;AACF,UAAI,eAAe,KAAK,KAAK;AAAA,IAC/B,QAAQ;AAAA,IAER;AACA;AAAA,EACF;AACA,MAAI;AACF,iBAAa,QAAQ,KAAK,KAAK;AAAA,EACjC,QAAQ;AAAA,EAER;AACF;AAEO,SAAS,cAAc,KAAmB;AAC/C,MAAI,YAAY,QAAQ,YAAY,MAAM;AACxC,UAAM,MAAO,YAAY,OAAO,KAAK;AACrC,QAAI;AACF,UAAI,kBAAkB,GAAG;AAAA,IAC3B,QAAQ;AAAA,IAER;AACA;AAAA,EACF;AACA,MAAI;AACF,iBAAa,WAAW,GAAG;AAAA,EAC7B,QAAQ;AAAA,EAER;AACF;;;ACjGO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC,YACE,SACO,QACA,MACP;AACA,UAAM,OAAO;AAHN;AACA;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AA0DO,SAAS,cAAc,QAAgC;AAC5D,QAAM,SAAS,GAAG,OAAO,OAAO,aAAa,OAAO,MAAM;AAC1D,QAAM,WAAW,YAAY,OAAO,MAAM;AAG1C,MAAI,QAAuB,WAAW,QAAQ;AAE9C,WAAS,WAA0B;AACjC,WAAO;AAAA,EACT;AAEA,WAAS,SAAS,GAAwB;AACxC,YAAQ;AACR,QAAI,EAAG,YAAW,UAAU,CAAC;AAAA,QACxB,eAAc,QAAQ;AAAA,EAC7B;AAGA,iBAAe,KACb,QACA,MACA,MACY;AACZ,QAAI,OAAO,MAAM;AACf,aAAO,aAAgB,QAAQ,MAAM,IAAI;AAAA,IAC3C;AAEA,UAAM,UAAkC,CAAC;AACzC,QAAI,MAAO,SAAQ,eAAe,IAAI,UAAU,KAAK;AAErD,UAAM,MAAM,MAAM,QAAW;AAAA,MAC3B,KAAK,GAAG,MAAM,GAAG,IAAI;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,QAAI,IAAI,UAAU,KAAK;AACrB,YAAM,MAAO,IAAI,MAAc,WAAW,QAAQ,IAAI,MAAM;AAC5D,YAAM,IAAI,aAAa,KAAK,IAAI,QAAQ,IAAI,IAAI;AAAA,IAClD;AACA,WAAO,IAAI;AAAA,EACb;AAGA,QAAM,MAAe;AAAA,IACnB,MAAM;AAAA,MACJ,MAAM,MAAM,MAA0C;AACpD,cAAM,SAAS,MAAM,KAAkB,QAAQ,eAAe,IAAI;AAClE,iBAAS,OAAO,KAAK;AACrB,eAAO;AAAA,MACT;AAAA,MACA,IAAI,MAAM,KAAoB,OAAO,UAAU;AAAA,MAC/C,QAAQ,MAAM,SAAS,IAAI;AAAA,MAC3B;AAAA,IACF;AAAA,IAEA,OAAO;AAAA,MACL,QAAQ,CAAC,SAA6B,KAAwB,QAAQ,iBAAiB,IAAI;AAAA,IAC7F;AAAA,IAEA,aAAa;AAAA,MACX,KAAK,CAAC,UACJ,KAAyB,OAAO,sBAAsB,SAAS,GAAG,EAAE;AAAA,MACtE,QAAQ,MAAM,KAAa,OAAO,iBAAiB;AAAA,MACnD,QAAQ,CAAC,UACP,KAAyB,OAAO,6BAA6B,SAAS,CAAC,EAAE;AAAA,IAC7E;AAAA,IAEA,QAAQ;AAAA,MACN,YAAY,MAAM,KAAoB,OAAO,iBAAiB;AAAA,MAC9D,eAAe,CAAC,UAAU,KAAoB,SAAS,mBAAmB,KAAK;AAAA,IACjF;AAAA,IAEA,MAAM;AAAA,MACJ,KAAK,CAAC,KAAK,UAAU,KAAK,QAAQ,YAAY,GAAG,IAAI,EAAE,MAAM,CAAC;AAAA,MAC9D,KAAK,CAAC,QAAQ,KAAK,OAAO,YAAY,GAAG,EAAE;AAAA,MAC3C,QAAQ,MAAM,KAAK,OAAO,UAAU;AAAA,MACpC,QAAQ,CAAC,QAAQ,KAAK,UAAU,YAAY,GAAG,EAAE;AAAA,MACjD,OAAO,MAAM,KAAK,UAAU,UAAU;AAAA,IACxC;AAAA,EACF;AAEA,SAAO;AACT;AAIA,IAAI,YAAY;AAChB,IAAI,YAAiD,CAAC;AACtD,IAAI,YAA2B;AAE/B,eAAe,aAAgB,QAAgB,MAAc,MAAuB;AAClF,QAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAG1C,MAAI,SAAS,eAAe;AAC1B,gBAAY,cAAc,KAAK,IAAI,CAAC;AACpC,UAAM,UAAyB;AAAA,MAC7B,QAAQ;AAAA,MACR,UAAU,MAAM,YAAY;AAAA,MAC5B,UAAU,MAAM,YAAY;AAAA,MAC5B,WAAW;AAAA,MACX,WAAW;AAAA,MACX,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACrC;AACA,WAAO,EAAE,OAAO,WAAW,QAAQ,eAAe,OAAO,MAAM,QAAQ;AAAA,EACzE;AACA,MAAI,SAAS,YAAY;AACvB,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,UAAU;AAAA,MACV,WAAW;AAAA,MACX,WAAW;AAAA,MACX,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACrC;AAAA,EACF;AAGA,MAAI,SAAS,iBAAiB;AAC5B,UAAM,OAAO;AACb,gBAAY,KAAK,IAAI,WAAW,MAAM,SAAS,CAAC;AAChD,WAAO;AAAA,MACL,WAAW,MAAM,SAAS;AAAA,MAC1B,cAAc,MAAM,SAAS,KAAK;AAAA,MAClC,cAAc;AAAA,MACd,WAAW;AAAA,MACX,MAAM;AAAA,IACR;AAAA,EACF;AAGA,MAAI,KAAK,WAAW,eAAe,GAAG;AACpC,WAAO,CAAC;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,OAAO;AAAA,MACP,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AACA,MAAI,SAAS,mBAAmB;AAC9B,WAAO,EAAE,MAAM,GAAG,OAAO,WAAW,cAAc,GAAG,WAAW,EAAE;AAAA,EACpE;AACA,MAAI,KAAK,WAAW,qBAAqB,GAAG;AAC1C,WAAO,CAAC;AAAA,MACN,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,OAAO;AAAA,MACP,QAAQ;AAAA,IACV,CAAC;AAAA,EACH;AAGA,MAAI,SAAS,qBAAqB,WAAW,OAAO;AAClD,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,UAAU;AAAA,MACV,UAAU;AAAA,MACV,WAAW;AAAA,MACX,WAAW;AAAA,MACX,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MAClC,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACrC;AAAA,EACF;AAGA,MAAI,KAAK,WAAW,WAAW,KAAK,WAAW,QAAQ;AACrD,UAAM,MAAM,KAAK,MAAM,WAAW,EAAE,CAAC;AACrC,cAAU,GAAG,IAAI,MAAM,SAAS,CAAC;AACjC,WAAO,EAAE,SAAS,MAAM,KAAK,OAAO,UAAU,GAAG,EAAE;AAAA,EACrD;AACA,MAAI,KAAK,WAAW,WAAW,KAAK,WAAW,OAAO;AACpD,UAAM,MAAM,KAAK,MAAM,WAAW,EAAE,CAAC;AACrC,WAAQ,UAAU,GAAG,KAAK;AAAA,EAC5B;AACA,MAAI,SAAS,cAAc,WAAW,OAAO;AAC3C,WAAO;AAAA,EACT;AACA,MAAI,KAAK,WAAW,WAAW,KAAK,WAAW,UAAU;AACvD,UAAM,MAAM,KAAK,MAAM,WAAW,EAAE,CAAC;AACrC,WAAO,UAAU,GAAG;AACpB,WAAO,EAAE,SAAS,MAAM,IAAI;AAAA,EAC9B;AACA,MAAI,SAAS,cAAc,WAAW,UAAU;AAC9C,UAAM,QAAQ,OAAO,KAAK,SAAS,EAAE;AACrC,gBAAY,CAAC;AACb,WAAO,EAAE,SAAS,MAAM,SAAS,MAAM;AAAA,EACzC;AAEA,SAAO,CAAC;AACV;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/platform.ts","../src/sign.ts","../src/index.ts"],"sourcesContent":["/**\n * 平台适配层\n *\n * 自动检测运行环境(微信小游戏 / 抖音小游戏 / H5),\n * 封装 HTTP 请求和本地存储,对上层提供统一接口。\n */\n\n// 小游戏运行时的全局对象(微信/抖音),H5 下不存在\ndeclare const wx: any;\ndeclare const tt: any;\n\n// ─── 环境检测 ──────────────────────────────────────────\n\ntype Runtime = 'wx' | 'tt' | 'h5';\n\nfunction detectRuntime(): Runtime {\n if (typeof wx !== 'undefined' && typeof (wx as any).request === 'function') return 'wx';\n if (typeof tt !== 'undefined' && typeof (tt as any).request === 'function') return 'tt';\n return 'h5';\n}\n\nconst runtime = detectRuntime();\n\n// ─── HTTP 请求 ─────────────────────────────────────────\n\nexport interface HttpRequest {\n url: string;\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n headers?: Record<string, string>;\n body?: any;\n}\n\nexport interface HttpResponse<T = any> {\n status: number;\n data: T;\n}\n\nexport async function request<T = any>(req: HttpRequest): Promise<HttpResponse<T>> {\n if (runtime === 'wx' || runtime === 'tt') {\n return miniProgramRequest<T>(req);\n }\n return fetchRequest<T>(req);\n}\n\n/** 微信/抖音小游戏请求(wx.request / tt.request) */\nfunction miniProgramRequest<T>(req: HttpRequest): Promise<HttpResponse<T>> {\n const api = (runtime === 'wx' ? wx : tt) as any;\n return new Promise((resolve, reject) => {\n api.request({\n url: req.url,\n method: req.method,\n header: req.headers || {},\n data: req.body,\n success: (res: any) => {\n resolve({\n status: res.statusCode,\n data: res.data as T,\n });\n },\n fail: (err: any) => {\n reject(new Error(err?.errMsg || 'request failed'));\n },\n });\n });\n}\n\n/** H5 fetch 请求 */\nasync function fetchRequest<T>(req: HttpRequest): Promise<HttpResponse<T>> {\n const res = await fetch(req.url, {\n method: req.method,\n headers: { 'Content-Type': 'application/json', ...req.headers },\n body: req.body !== undefined ? JSON.stringify(req.body) : undefined,\n });\n const data = await res.json().catch(() => ({}));\n return { status: res.status, data: data as T };\n}\n\n// ─── 本地存储(token 持久化)────────────────────────────\n\nexport function storageGet(key: string): string | null {\n if (runtime === 'wx' || runtime === 'tt') {\n const api = (runtime === 'wx' ? wx : tt) as any;\n try {\n const val = api.getStorageSync(key);\n return val || null;\n } catch {\n return null;\n }\n }\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nexport function storageSet(key: string, value: string): void {\n if (runtime === 'wx' || runtime === 'tt') {\n const api = (runtime === 'wx' ? wx : tt) as any;\n try {\n api.setStorageSync(key, value);\n } catch {\n /* ignore */\n }\n return;\n }\n try {\n localStorage.setItem(key, value);\n } catch {\n /* ignore */\n }\n}\n\nexport function storageRemove(key: string): void {\n if (runtime === 'wx' || runtime === 'tt') {\n const api = (runtime === 'wx' ? wx : tt) as any;\n try {\n api.removeStorageSync(key);\n } catch {\n /* ignore */\n }\n return;\n }\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n}\n","/**\n * 请求签名工具\n *\n * 参考 AWS S3 / 微信支付 / 高德地图的大厂 SDK 鉴权设计:\n * - 每个请求携带 AccessKey(公开标识)+ 签名(HMAC-SHA256)\n * - 签名内容包含:HTTP方法、路径、时间戳、请求体哈希、AccessKey\n * - SecretKey 仅在本地参与签名计算,不会出现在网络请求中\n *\n * 安全特性:\n * - HMAC 签名 → 请求内容未被篡改\n * - 时间戳 → 请求超过 5 分钟无效(防重放)\n * - Nonce 随机串 → 同一时间窗口内不可重复(防重放)\n */\n\n/** 签名计算所需的凭证 */\nexport interface SignCredential {\n accessKey: string;\n secretKey: string;\n}\n\n/**\n * 计算 body 的 SHA256 哈希(hex)。\n * 小游戏环境无原生 crypto.subtle 同步 API,这里用简化实现兼容多端。\n */\nfunction sha256Hex(input: string): string {\n // 优先使用微信/抖音小游戏的 crypto(同步)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const g = globalThis as any;\n if (g.crypto?.createHash) {\n return g.crypto.createHash('sha256').update(input).digest('hex');\n }\n // Node 环境\n if (typeof require === 'function') {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const nodeCrypto = require('crypto');\n return nodeCrypto.createHash('sha256').update(input).digest('hex');\n } catch {\n /* fall through */\n }\n }\n // 兜底:简单哈希(非加密安全,仅用于无法获取 crypto 的环境)\n let h1 = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h1 ^= input.charCodeAt(i);\n h1 = Math.imul(h1, 0x01000193);\n }\n return (h1 >>> 0).toString(16).padStart(8, '0').repeat(8);\n}\n\n/**\n * HMAC-SHA256 签名。\n * 优先使用原生 crypto,兜底用纯 JS 实现。\n */\nfunction hmacSha256(key: string, message: string): string {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const g = globalThis as any;\n if (g.crypto?.createHmac) {\n return g.crypto.createHmac('sha256', key).update(message).digest('hex');\n }\n if (typeof require === 'function') {\n try {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const nodeCrypto = require('crypto');\n return nodeCrypto.createHmac('sha256', key).update(message).digest('hex');\n } catch {\n /* fall through */\n }\n }\n // 兜底:简化 HMAC(非加密安全)\n const blockLen = 64;\n const k = key.length > blockLen ? sha256Hex(key) : key.padEnd(blockLen, '\\0');\n const ipad = Array.from(k).map((c) => String.fromCharCode(c.charCodeAt(0) ^ 0x36)).join('');\n const opad = Array.from(k).map((c) => String.fromCharCode(c.charCodeAt(0) ^ 0x5c)).join('');\n return sha256Hex(opad + sha256Hex(ipad + message));\n}\n\n/** 生成随机 Nonce(16 位) */\nexport function generateNonce(): string {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const g = globalThis as any;\n if (g.crypto?.randomBytes) {\n return g.crypto.randomBytes(8).toString('hex');\n }\n let s = '';\n const chars = '0123456789abcdef';\n for (let i = 0; i < 16; i++) s += chars[Math.floor(Math.random() * 16)];\n return s;\n}\n\n/**\n * 构造请求签名头。\n *\n * 签名算法(HMAC-SHA256):\n * signStr = method + \"\\n\" + path + \"\\n\" + timestamp + \"\\n\" + sha256(body) + \"\\n\" + accessKey\n * signature = HMAC_SHA256(secretKey, signStr)\n *\n * @param method HTTP 方法\n * @param path 请求路径(不含 baseUrl,如 /game-api/xxx/score/submit)\n * @param body 请求体(对象或 undefined)\n * @param credential AK/SK 凭证\n * @returns 签名头对象,可直接合并到请求 headers\n */\nexport function buildSignHeaders(\n method: string,\n path: string,\n body: unknown,\n credential: SignCredential,\n): Record<string, string> {\n const timestamp = Math.floor(Date.now() / 1000);\n const nonce = generateNonce();\n const bodyStr = body !== undefined && body !== null ? JSON.stringify(body) : '';\n const bodyHash = sha256Hex(bodyStr);\n\n const signStr = [method, path, String(timestamp), bodyHash, credential.accessKey].join('\\n');\n const signature = hmacSha256(credential.secretKey, signStr);\n\n return {\n 'X-Access-Key': credential.accessKey,\n 'X-Timestamp': String(timestamp),\n 'X-Nonce': nonce,\n 'X-Signature': signature,\n };\n}\n","/**\n * @game-factory/game-sdk\n *\n * 通用游戏 SDK,封装 game-server-shared 所有 HTTP 接口。\n *\n * 用法(工程化):\n * import { createGameSdk } from '@game-factory/game-sdk';\n * const sdk = createGameSdk({ baseUrl: 'https://api.example.com' });\n * await sdk.auth.login({ code: 'xxx', platform: 'wx' });\n * await sdk.score.submit({ score: 1000 });\n *\n * 用法(CDN):\n * <script src=\"https://cdn.example.com/game-sdk/index.umd.js\"></script>\n * const sdk = GameSDK.createGameSdk({ baseUrl: '...' });\n *\n * 用法(模拟模式,不连服务端):\n * const sdk = createGameSdk({ baseUrl: '', mock: true });\n *\n * 凭证说明:\n * gameId / accessKey / secretKey 由平台在构建时注入到 __GAME_CONFIG__,\n * SDK 自动读取,开发者无需手动传入。\n * 模板开发阶段使用 mock 模式,不需要凭证。\n */\n\nimport {\n GameSdkConfig, LoginOptions, LoginResult, PlayerProfile,\n SubmitScoreOptions, SubmitScoreResult,\n LeaderboardEntry, MyRank,\n} from './types';\nimport { request, storageGet, storageSet, storageRemove } from './platform';\nimport { buildSignHeaders } from './sign';\n\n// 运行时由平台构建时注入的配置(esbuild define)\ndeclare const __GAME_CONFIG__: {\n gameId?: string;\n accessKey?: string;\n secretKey?: string;\n baseUrl?: string;\n} | undefined;\n\n// re-export 所有类型,供外部使用\nexport * from './types';\n\n// ─── 错误 ──────────────────────────────────────────────\n\nexport class GameSdkError extends Error {\n constructor(\n message: string,\n public status: number,\n public data?: any,\n ) {\n super(message);\n this.name = 'GameSdkError';\n }\n}\n\n// ─── SDK 接口定义 ───────────────────────────────────────\n\nexport interface GameSdk {\n /** 身份与登录 */\n auth: {\n /** 登录(微信/抖音/访客),成功后自动持久化 token */\n login(opts: LoginOptions): Promise<LoginResult>;\n /** 获取当前登录玩家信息(需先 login) */\n me(): Promise<PlayerProfile>;\n /** 登出,清除本地 token */\n logout(): void;\n /** 获取当前 token(用于调试) */\n getToken(): string | null;\n };\n /** 分数 */\n score: {\n /** 提交分数(需登录) */\n submit(opts: SubmitScoreOptions): Promise<SubmitScoreResult>;\n };\n /** 排行榜 */\n leaderboard: {\n /** 获取 Top N(匿名可访问,limit 默认 100) */\n top(limit?: number): Promise<LeaderboardEntry[]>;\n /** 获取当前玩家排名(需登录) */\n myRank(): Promise<MyRank>;\n /** 获取附近排名(需登录,range 默认 ±5) */\n nearby(range?: number): Promise<LeaderboardEntry[]>;\n };\n /** 玩家档案 */\n player: {\n /** 获取当前玩家档案(需登录) */\n getProfile(): Promise<PlayerProfile>;\n /** 更新档案(昵称/头像/扩展字段) */\n updateProfile(patch: {\n nickname?: string;\n avatarUrl?: string;\n extra?: Record<string, any>;\n }): Promise<PlayerProfile>;\n };\n /** 云存档(KV) */\n save: {\n /** 保存数据到指定 key */\n set(key: string, value: Record<string, any>): Promise<{ success: boolean; key: string; value: Record<string, any> }>;\n /** 读取指定 key 的数据 */\n get(key: string): Promise<Record<string, any> | null>;\n /** 读取所有存档 */\n getAll(): Promise<Record<string, any>>;\n /** 删除指定 key */\n remove(key: string): Promise<{ success: boolean; key: string }>;\n /** 清空所有存档 */\n clear(): Promise<{ success: boolean; deleted: number }>;\n };\n}\n\n// ─── 创建 SDK ───────────────────────────────────────────\n\nexport function createGameSdk(config: GameSdkConfig): GameSdk {\n // 合并 __GAME_CONFIG__ 注入的凭证(构建时注入,运行时自动读取)\n // 优先级:显式传入 > __GAME_CONFIG__ 注入\n const runtimeConfig = resolveRuntimeConfig(config);\n\n // 非 mock 模式必须有 gameId(游戏运行阶段由平台注入)\n if (!runtimeConfig.mock && !runtimeConfig.gameId) {\n throw new GameSdkError(\n '缺少 gameId:请确认游戏已通过平台构建,或使用 mock 模式进行模板开发',\n 400,\n );\n }\n\n // 非 mock 模式必须有凭证(游戏运行阶段由平台注入)\n if (!runtimeConfig.mock && (!runtimeConfig.accessKey || !runtimeConfig.secretKey)) {\n throw new GameSdkError(\n '缺少 accessKey/secretKey:请确认游戏已通过平台构建并颁发了模板级凭证',\n 400,\n );\n }\n\n const apiUrl = runtimeConfig.baseUrl\n ? `${runtimeConfig.baseUrl}/game-api/${runtimeConfig.gameId}`\n : '';\n const tokenKey = `gf_token_${runtimeConfig.gameId ?? 'mock'}`;\n\n // token 管理(从本地存储恢复)\n let token: string | null = storageGet(tokenKey);\n\n function getToken(): string | null {\n return token;\n }\n\n function setToken(t: string | null): void {\n token = t;\n if (t) storageSet(tokenKey, t);\n else storageRemove(tokenKey);\n }\n\n // ── 内部 HTTP 封装 ──────────────────────────────────\n async function call<T = any>(\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n path: string,\n body?: any,\n ): Promise<T> {\n if (runtimeConfig.mock) {\n return mockResponse<T>(method, path, body);\n }\n\n const headers: Record<string, string> = {};\n\n // 附加请求签名(HMAC-SHA256,防篡改 + 防重放)\n if (runtimeConfig.accessKey && runtimeConfig.secretKey) {\n const signHeaders = buildSignHeaders(\n method,\n path,\n body,\n { accessKey: runtimeConfig.accessKey, secretKey: runtimeConfig.secretKey },\n );\n Object.assign(headers, signHeaders);\n }\n\n // 附加玩家 token\n if (token) headers['Authorization'] = `Bearer ${token}`;\n\n const res = await request<T>({\n url: `${apiUrl}${path}`,\n method,\n headers,\n body,\n });\n\n if (res.status >= 400) {\n const msg = (res.data as any)?.message || `HTTP ${res.status}`;\n throw new GameSdkError(msg, res.status, res.data);\n }\n return res.data;\n }\n\n // ── 方法面 ──────────────────────────────────────────\n const sdk: GameSdk = {\n auth: {\n async login(opts: LoginOptions): Promise<LoginResult> {\n const result = await call<LoginResult>('POST', '/auth/login', opts);\n setToken(result.token);\n return result;\n },\n me: () => call<PlayerProfile>('GET', '/auth/me'),\n logout: () => setToken(null),\n getToken,\n },\n\n score: {\n submit: (opts: SubmitScoreOptions) => call<SubmitScoreResult>('POST', '/score/submit', opts),\n },\n\n leaderboard: {\n top: (limit?: number) =>\n call<LeaderboardEntry[]>('GET', `/leaderboard?limit=${limit || 100}`),\n myRank: () => call<MyRank>('GET', '/leaderboard/me'),\n nearby: (range?: number) =>\n call<LeaderboardEntry[]>('GET', `/leaderboard/nearby?range=${range || 5}`),\n },\n\n player: {\n getProfile: () => call<PlayerProfile>('GET', '/player/profile'),\n updateProfile: (patch) => call<PlayerProfile>('PATCH', '/player/profile', patch),\n },\n\n save: {\n set: (key, value) => call('POST', `/storage/${key}`, { value }),\n get: (key) => call('GET', `/storage/${key}`),\n getAll: () => call('GET', '/storage'),\n remove: (key) => call('DELETE', `/storage/${key}`),\n clear: () => call('DELETE', '/storage'),\n },\n };\n\n return sdk;\n}\n\n// ─── 运行时配置解析 ─────────────────────────────────────\n\n/**\n * 合并用户显式传入的配置与 __GAME_CONFIG__ 注入的配置。\n * 优先级:显式传入 > __GAME_CONFIG__ 注入\n *\n * __GAME_CONFIG__ 由平台构建时通过 esbuild define 注入:\n * define: { '__GAME_CONFIG__': JSON.stringify({ gameId, accessKey, secretKey, baseUrl }) }\n */\nfunction resolveRuntimeConfig(config: GameSdkConfig): GameSdkConfig {\n let injected: Partial<GameSdkConfig> = {};\n try {\n // __GAME_CONFIG__ 可能不存在(模板开发阶段 / CDN 直连)\n // eslint-disable-next-line no-undef\n injected = (typeof __GAME_CONFIG__ !== 'undefined' ? __GAME_CONFIG__ : {}) as any;\n } catch {\n // __GAME_CONFIG__ 未定义时 ReferenceError,忽略\n injected = {};\n }\n\n return {\n gameId: config.gameId ?? injected.gameId,\n baseUrl: config.baseUrl ?? injected.baseUrl ?? '',\n accessKey: config.accessKey ?? injected.accessKey,\n secretKey: config.secretKey ?? injected.secretKey,\n mock: config.mock,\n };\n}\n\n// ─── Mock 模式(模板开发不连服务端)──────────────────────\n\nlet mockScore = 0;\nlet mockSaves: Record<string, Record<string, any>> = {};\nlet mockToken: string | null = null;\n\nasync function mockResponse<T>(method: string, path: string, body: any): Promise<T> {\n await new Promise((r) => setTimeout(r, 50)); // 模拟网络延迟\n\n // auth\n if (path === '/auth/login') {\n mockToken = `mock_token_${Date.now()}`;\n const profile: PlayerProfile = {\n openId: 'mock_openid',\n platform: body?.platform || 'guest',\n nickname: body?.nickname || '测试玩家',\n bestScore: mockScore,\n playCount: 0,\n createdAt: new Date().toISOString(),\n lastSeenAt: new Date().toISOString(),\n };\n return { token: mockToken, openId: 'mock_openid', isNew: true, profile } as T;\n }\n if (path === '/auth/me') {\n return {\n openId: 'mock_openid',\n platform: 'guest',\n nickname: '测试玩家',\n bestScore: mockScore,\n playCount: 1,\n createdAt: new Date().toISOString(),\n lastSeenAt: new Date().toISOString(),\n } as T;\n }\n\n // score\n if (path === '/score/submit') {\n const prev = mockScore;\n mockScore = Math.max(mockScore, body?.score || 0);\n return {\n submitted: body?.score || 0,\n isHighScore: (body?.score || 0) > prev,\n previousBest: prev,\n bestScore: mockScore,\n rank: 1,\n } as T;\n }\n\n // leaderboard\n if (path.startsWith('/leaderboard?')) {\n return [{\n rank: 1,\n openId: 'mock***oid',\n nickname: '测试玩家',\n score: mockScore,\n isSelf: true,\n }] as T;\n }\n if (path === '/leaderboard/me') {\n return { rank: 1, score: mockScore, totalPlayers: 1, playCount: 1 } as T;\n }\n if (path.startsWith('/leaderboard/nearby')) {\n return [{\n rank: 1,\n openId: 'mock***oid',\n nickname: '测试玩家',\n score: mockScore,\n isSelf: true,\n }] as T;\n }\n\n // player\n if (path === '/player/profile' && method === 'GET') {\n return {\n openId: 'mock_openid',\n platform: 'guest',\n nickname: '测试玩家',\n bestScore: mockScore,\n playCount: 1,\n createdAt: new Date().toISOString(),\n lastSeenAt: new Date().toISOString(),\n } as T;\n }\n\n // storage\n if (path.startsWith('/storage/') && method === 'POST') {\n const key = path.split('/storage/')[1];\n mockSaves[key] = body?.value || {};\n return { success: true, key, value: mockSaves[key] } as T;\n }\n if (path.startsWith('/storage/') && method === 'GET') {\n const key = path.split('/storage/')[1];\n return (mockSaves[key] || null) as T;\n }\n if (path === '/storage' && method === 'GET') {\n return mockSaves as T;\n }\n if (path.startsWith('/storage/') && method === 'DELETE') {\n const key = path.split('/storage/')[1];\n delete mockSaves[key];\n return { success: true, key } as T;\n }\n if (path === '/storage' && method === 'DELETE') {\n const count = Object.keys(mockSaves).length;\n mockSaves = {};\n return { success: true, deleted: count } as T;\n }\n\n return {} as T;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,62 +1,50 @@
|
|
|
1
|
-
{
|
|
1
|
+
{
|
|
2
2
|
"name": "@zhimakechuang/game-sdk",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "通用游戏 SDK
|
|
5
|
-
"homepage": "https://github.com/Chuang0516/game-factory/tree/main/packages/game-sdk",
|
|
6
|
-
"repository": {
|
|
7
|
-
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/Chuang0516/game-factory.git",
|
|
9
|
-
"directory": "packages/game-sdk"
|
|
10
|
-
},
|
|
11
|
-
"bugs": {
|
|
12
|
-
"url": "https://github.com/Chuang0516/game-factory/issues"
|
|
13
|
-
},
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "通用游戏 SDK — 封装 game-server-shared 所有 HTTP 接口,支持微信/抖音/H5",
|
|
14
5
|
"main": "./dist/index.js",
|
|
15
6
|
"module": "./dist/index.mjs",
|
|
16
7
|
"unpkg": "./dist/index.global.js",
|
|
17
|
-
"sideEffects": false,
|
|
18
8
|
"types": "./dist/index.d.ts",
|
|
19
|
-
"exports": {
|
|
20
|
-
".": {
|
|
21
|
-
"types": "./dist/index.d.ts",
|
|
22
|
-
"import": "./dist/index.mjs",
|
|
23
|
-
"require": "./dist/index.js"
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
"files": [
|
|
27
|
-
"dist",
|
|
28
|
-
"README.md"
|
|
29
|
-
],
|
|
30
|
-
"scripts": {
|
|
31
|
-
"build": "tsup",
|
|
32
|
-
"dev": "tsup --watch",
|
|
33
|
-
"clean": "rimraf dist",
|
|
34
|
-
"prepublishOnly": "tsup",
|
|
35
|
-
"publish:patch": "npm version patch && npm publish",
|
|
36
|
-
"publish:minor": "npm version minor && npm publish",
|
|
37
|
-
"publish:major": "npm version major && npm publish"
|
|
38
|
-
},
|
|
39
|
-
"keywords": [
|
|
40
|
-
"game",
|
|
41
|
-
"sdk",
|
|
42
|
-
"leaderboard",
|
|
43
|
-
"score",
|
|
44
|
-
"miniprogram",
|
|
45
|
-
"wechat",
|
|
46
|
-
"douyin",
|
|
47
|
-
"nestjs"
|
|
48
|
-
],
|
|
49
|
-
"license": "MIT",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsup",
|
|
22
|
+
"dev": "tsup --watch",
|
|
23
|
+
"clean": "rimraf dist",
|
|
24
|
+
"prepublishOnly": "tsup",
|
|
25
|
+
"publish:patch": "npm version patch && npm publish",
|
|
26
|
+
"publish:minor": "npm version minor && npm publish",
|
|
27
|
+
"publish:major": "npm version major && npm publish"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"game",
|
|
31
|
+
"sdk",
|
|
32
|
+
"leaderboard",
|
|
33
|
+
"score",
|
|
34
|
+
"miniprogram",
|
|
35
|
+
"wechat",
|
|
36
|
+
"douyin",
|
|
37
|
+
"nestjs"
|
|
38
|
+
],
|
|
39
|
+
"license": "MIT",
|
|
50
40
|
"publishConfig": {
|
|
51
|
-
"registry": "https://registry.npmjs.org/"
|
|
52
|
-
|
|
41
|
+
"registry": "https://registry.npmjs.org/"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=16.0.0"
|
|
53
45
|
},
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"typescript": "^5.4.0"
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"tsup": "^8.0.0",
|
|
48
|
+
"typescript": "^5.4.0"
|
|
49
|
+
}
|
|
50
|
+
}
|