@maoyugames/phaser-framework 1.0.16 → 1.0.25

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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { b as HttpRequestOptions, U as Unsubscribe, H as Handler, p as LogLevel, A as AdResult, t as PurchaseResult, q as LoginResult, d as IPlatform, i as IPlatformLifecycle, S as SafeArea } from './types-WJYVtDfo.js';
2
- export { a as HttpMethod, c as HttpResponse, I as IDisposable, e as IPlatformAds, f as IPlatformAnalytics, g as IPlatformAuth, h as IPlatformDevice, j as IPlatformMission, k as IPlatformNet, l as IPlatformPayment, m as IPlatformShortcut, n as IPlatformSocket, o as IPlatformStorage, L as LaunchOptions, P as PlatformInfo, r as PlatformName, s as PlatformUnsupportedError, R as Result, u as SocketOptions, v as SystemInfo, V as Vec2, w as err, x as ok } from './types-WJYVtDfo.js';
1
+ import { b as HttpRequestOptions, U as Unsubscribe, H as Handler, p as LogLevel, A as AdResult, t as PurchaseResult, q as LoginResult, d as IPlatform, i as IPlatformLifecycle, S as SafeArea } from './types-CxYJ9uz0.js';
2
+ export { a as HttpMethod, c as HttpResponse, I as IDisposable, e as IPlatformAds, f as IPlatformAnalytics, g as IPlatformAuth, h as IPlatformDevice, j as IPlatformMission, k as IPlatformNet, l as IPlatformPayment, m as IPlatformShortcut, n as IPlatformSocket, o as IPlatformStorage, L as LaunchOptions, P as PlatformInfo, r as PlatformName, s as PlatformUnsupportedError, R as Result, u as SocketOptions, v as SystemInfo, V as Vec2, w as err, x as ok } from './types-CxYJ9uz0.js';
3
3
  import Phaser from 'phaser';
4
4
 
5
5
  /**
@@ -1805,6 +1805,35 @@ interface FacebookPlatformConfig {
1805
1805
  /** 广告位配置(覆盖 common.ads) */
1806
1806
  ads?: AdsConfig;
1807
1807
  }
1808
+ /**
1809
+ * YouTube Playables 平台配置。
1810
+ *
1811
+ * YouTube Playables SDK 由构建侧通过 `<script src="https://www.youtube.com/game_api/v1">`
1812
+ * 注入 <head>(必须在游戏 bundle 前),运行时挂载全局 `ytgame`。
1813
+ * 文档:https://developers.google.com/youtube/gaming/playables/reference/getting_started
1814
+ *
1815
+ * 注意:YouTube Playables **禁止任何内购**(off-platform 也不允许),故无 IAP 相关字段;
1816
+ * 广告(激励/插屏)走 ytgame.ads.*,广告位由 YouTube 后台 monetization 配置,游戏侧
1817
+ * rewardId 经 common.ads / ads 传入。
1818
+ */
1819
+ interface YouTubePlatformConfig {
1820
+ /** 页面标题(注入 index.html <title>) */
1821
+ title?: string;
1822
+ /**
1823
+ * YouTube Playables SDK 脚本地址(注入 index.html <head> 第一个 script)。
1824
+ * 不填用官方默认 https://www.youtube.com/game_api/v1。
1825
+ */
1826
+ sdkUrl?: string;
1827
+ /** 屏幕方向(用于 viewport / meta 提示) */
1828
+ orientation?: Orientation;
1829
+ /** 业务后端基址(覆盖 common.apiBaseURL) */
1830
+ apiBaseURL?: string;
1831
+ /**
1832
+ * 广告位配置(覆盖 common.ads)。YouTube 激励广告用 reward id(UUID 形态),
1833
+ * 由 YouTube 后台 monetization 生成,填到 ads.rewardedUnitId。
1834
+ */
1835
+ ads?: AdsConfig;
1836
+ }
1808
1837
  /** Capacitor(iOS + Android 原生 App)平台配置 */
1809
1838
  interface CapacitorPlatformConfig {
1810
1839
  /** 应用包名/反向域名(写入 capacitor.config appId,如 com.example.app) */
@@ -1842,6 +1871,8 @@ interface PlatformConfig {
1842
1871
  facebook?: FacebookPlatformConfig;
1843
1872
  /** Capacitor 原生 App */
1844
1873
  capacitor?: CapacitorPlatformConfig;
1874
+ /** YouTube Playables */
1875
+ youtube?: YouTubePlatformConfig;
1845
1876
  }
1846
1877
 
1847
- export { AdResult, type AdsConfig, App, type AppConfig, BaseModule, BasePanel, BaseScene, Button, type ButtonConfig, type ButtonStyle, type CapacitorPlatformConfig, ConfigTable, ConfigTableManager, ErrorCode, type FacebookPlatformConfig, FrameworkError, Handler, HttpClient, type HttpClientConfig, HttpError, type HttpInterceptors, HttpRequestOptions, I18n, type I18nOptions, type IAccountService, type IAdsManager, type IAnalyticsManager, type IApp, type IAssetLoader, type IAudioManager, type IConfigService, type IEventBus, type IHttpClient, type IKcpTransport, type ILogger, type INetManager, type IPanelManager, type IPaymentManager, IPlatform, IPlatformLifecycle, type ISceneManager, type ISocketClient, type IStorageManager, IconButton, type IconButtonConfig, JsonCodec, KcpClient, type KcpClientConfig, type KcpTransportFactory, type LayerName, Layers, LogLevel, LoginResult, MessageChannel, type MessageChannelOptions, type MessageCodec, type MessageEnvelope, MessageError, ModuleRegistry, NetManager, type NetManagerOptions, type NetProtocol, ObjectPool, type Orientation, type PanelCtor, PanelManager, type PlatformConfig, PlatformContext, PurchaseResult, SafeArea, SaveManager, type SaveSlot, type SaveSlotOptions, type ScheduleHandle, Scheduler, type SocketClientConfig, type SocketState, type StartGameOptions, type Store, type TikTokPlatformConfig, TypedEventBus, Unsubscribe, type WeChatPlatformConfig, type WebPlatformConfig, WebSocketClient, createStore, createTypedEventBus, isFrameworkError, normalizeLocale, startGame };
1878
+ export { AdResult, type AdsConfig, App, type AppConfig, BaseModule, BasePanel, BaseScene, Button, type ButtonConfig, type ButtonStyle, type CapacitorPlatformConfig, ConfigTable, ConfigTableManager, ErrorCode, type FacebookPlatformConfig, FrameworkError, Handler, HttpClient, type HttpClientConfig, HttpError, type HttpInterceptors, HttpRequestOptions, I18n, type I18nOptions, type IAccountService, type IAdsManager, type IAnalyticsManager, type IApp, type IAssetLoader, type IAudioManager, type IConfigService, type IEventBus, type IHttpClient, type IKcpTransport, type ILogger, type INetManager, type IPanelManager, type IPaymentManager, IPlatform, IPlatformLifecycle, type ISceneManager, type ISocketClient, type IStorageManager, IconButton, type IconButtonConfig, JsonCodec, KcpClient, type KcpClientConfig, type KcpTransportFactory, type LayerName, Layers, LogLevel, LoginResult, MessageChannel, type MessageChannelOptions, type MessageCodec, type MessageEnvelope, MessageError, ModuleRegistry, NetManager, type NetManagerOptions, type NetProtocol, ObjectPool, type Orientation, type PanelCtor, PanelManager, type PlatformConfig, PlatformContext, PurchaseResult, SafeArea, SaveManager, type SaveSlot, type SaveSlotOptions, type ScheduleHandle, Scheduler, type SocketClientConfig, type SocketState, type StartGameOptions, type Store, type TikTokPlatformConfig, TypedEventBus, Unsubscribe, type WeChatPlatformConfig, type WebPlatformConfig, WebSocketClient, type YouTubePlatformConfig, createStore, createTypedEventBus, isFrameworkError, normalizeLocale, startGame };
@@ -1,5 +1,5 @@
1
- import { r as PlatformName, h as IPlatformDevice, i as IPlatformLifecycle } from '../types-WJYVtDfo.js';
2
- import { B as BasePlatform } from '../BasePlatform-CwVOo3_q.js';
1
+ import { r as PlatformName, h as IPlatformDevice, i as IPlatformLifecycle } from '../types-CxYJ9uz0.js';
2
+ import { B as BasePlatform } from '../BasePlatform-DqIfl-Oi.js';
3
3
 
4
4
  /**
5
5
  * Capacitor(原生 WebView 容器)平台适配器。
@@ -1,5 +1,5 @@
1
- import { BasePlatform, DomLifecycle } from '../chunk-PDESBWLX.js';
2
1
  import { PlatformUnsupportedError } from '../chunk-II3JM4R3.js';
2
+ import { BasePlatform, DomLifecycle } from '../chunk-NFEHUFN7.js';
3
3
  import { __publicField } from '../chunk-PKBMQBKP.js';
4
4
 
5
5
  // src/platform/impl/capacitor/CapacitorPlatform.ts
@@ -1,5 +1,5 @@
1
- import { r as PlatformName, o as IPlatformStorage, g as IPlatformAuth, e as IPlatformAds, l as IPlatformPayment, h as IPlatformDevice, i as IPlatformLifecycle } from '../types-WJYVtDfo.js';
2
- import { B as BasePlatform } from '../BasePlatform-CwVOo3_q.js';
1
+ import { r as PlatformName, o as IPlatformStorage, g as IPlatformAuth, e as IPlatformAds, l as IPlatformPayment, h as IPlatformDevice, m as IPlatformShortcut, i as IPlatformLifecycle } from '../types-CxYJ9uz0.js';
2
+ import { B as BasePlatform } from '../BasePlatform-DqIfl-Oi.js';
3
3
 
4
4
  /**
5
5
  * Facebook Instant Games 平台适配器。
@@ -24,8 +24,9 @@ declare class FacebookPlatform extends BasePlatform {
24
24
  readonly storage: IPlatformStorage;
25
25
  readonly auth: IPlatformAuth;
26
26
  readonly ads: IPlatformAds;
27
- readonly payment: IPlatformPayment;
27
+ payment?: IPlatformPayment;
28
28
  readonly device: IPlatformDevice;
29
+ readonly shortcut: IPlatformShortcut;
29
30
  readonly lifecycle: IPlatformLifecycle;
30
31
  init(): Promise<void>;
31
32
  }
@@ -1,5 +1,5 @@
1
- import { BasePlatform, DomLifecycle } from '../chunk-PDESBWLX.js';
2
1
  import { PlatformUnsupportedError } from '../chunk-II3JM4R3.js';
2
+ import { BasePlatform, DomLifecycle } from '../chunk-NFEHUFN7.js';
3
3
  import { __publicField } from '../chunk-PKBMQBKP.js';
4
4
 
5
5
  // src/platform/impl/facebook/FacebookPlatform.ts
@@ -86,12 +86,29 @@ var FacebookAuth = class {
86
86
  raw: info
87
87
  };
88
88
  }
89
+ // Zero Permissions 模型(SDK v8.0+,2026-06 核对 developers.facebook.com 官方文档):
90
+ // 游戏**无法直接 JS 读取** name/profile picture —— Meta 改在独立 iFrame「Overlay Views」内渲染
91
+ // 用户信息(游戏在 bundle 放 XML 布局,用 {{FBInstant.player.name}}/{{...photo}} 占位,
92
+ // FBInstant.views.createOverlayView 创建,游戏拿不到原始值)。
93
+ // 因此 getName/getPhoto 在 Zero Permissions 下返 undefined 或抛 → 这里 try-catch 返 null,
94
+ // 业务排行榜用 player ID / 游客显示。如需在游戏内显示真实 FB 昵称/头像,需另接 Overlay Views
95
+ // (XML 布局 + createOverlayView,本游戏未接;player 身份本身用 getSignedPlayerInfoAsync 已足够)。
89
96
  async getProfile() {
90
- requireFB("auth.getProfile");
91
- const name = FBInstant.player.getName();
92
- const photo = FBInstant.player.getPhoto();
93
- if (!name && !photo) return null;
94
- return { nickname: name ?? "", avatarUrl: photo ?? "" };
97
+ if (!hasFB()) return null;
98
+ try {
99
+ const name = FBInstant.player.getName?.();
100
+ const photo = FBInstant.player.getPhoto?.();
101
+ if (!name && !photo) return null;
102
+ return { nickname: name ?? "", avatarUrl: photo ?? "" };
103
+ } catch {
104
+ return null;
105
+ }
106
+ }
107
+ // 身份(login)用 getSignedPlayerInfoAsync(player ID + signature),Zero Permissions 与普通模式
108
+ // 都可用、且是 Zero Permissions 的**默认身份**。FB 无 TikTok 那种"显式授权拿 user.info"的弹窗,
109
+ // authorize 直接复用 login(业务「首次进排行榜引导授权」逻辑跨平台通用,FB 下等价于静默拿 player ID)。
110
+ authorize(_scope) {
111
+ return this.login();
95
112
  }
96
113
  };
97
114
  var FacebookAds = class {
@@ -144,6 +161,37 @@ var FacebookAds = class {
144
161
  }
145
162
  }
146
163
  };
164
+ var FacebookShortcut = class {
165
+ check() {
166
+ if (!hasFB() || typeof FBInstant.canCreateShortcutAsync !== "function") {
167
+ return Promise.resolve(false);
168
+ }
169
+ try {
170
+ return FBInstant.canCreateShortcutAsync().then(
171
+ (ok) => !!ok,
172
+ () => false
173
+ );
174
+ } catch {
175
+ return Promise.resolve(false);
176
+ }
177
+ }
178
+ add() {
179
+ if (!hasFB() || typeof FBInstant.createShortcutAsync !== "function") {
180
+ return Promise.resolve(false);
181
+ }
182
+ try {
183
+ return FBInstant.createShortcutAsync().then(
184
+ () => true,
185
+ () => false
186
+ );
187
+ } catch {
188
+ return Promise.resolve(false);
189
+ }
190
+ }
191
+ getMissionReward() {
192
+ return Promise.resolve({ canReceive: false });
193
+ }
194
+ };
147
195
  var FacebookPayment = class {
148
196
  async purchase(productId, extra) {
149
197
  requireFB("payment.purchase");
@@ -222,8 +270,12 @@ var FacebookPlatform = class extends BasePlatform {
222
270
  __publicField(this, "storage", this.fbStorage);
223
271
  __publicField(this, "auth", new FacebookAuth());
224
272
  __publicField(this, "ads", new FacebookAds());
273
+ // payment 在 iOS 宿主下由 init() 撤下(Apple 审核指南 4.7 禁止 iOS 展示任何支付/内购功能),故非 readonly。
225
274
  __publicField(this, "payment", new FacebookPayment());
226
275
  __publicField(this, "device", new FacebookDevice());
276
+ __publicField(this, "shortcut", new FacebookShortcut());
277
+ // mission 不实现(FB 无"侧边栏入口任务"概念,TikTok 特有);保持 undefined,
278
+ // 业务层 App.platform.mission?.startEntrance() 会安全跳过。
227
279
  __publicField(this, "lifecycle", new FacebookLifecycle(
228
280
  () => this.getLaunchOptions()
229
281
  ));
@@ -235,6 +287,12 @@ var FacebookPlatform = class extends BasePlatform {
235
287
  await FBInstant.initializeAsync();
236
288
  FBInstant.setLoadingProgress(100);
237
289
  await FBInstant.startGameAsync();
290
+ try {
291
+ if (FBInstant.getPlatform?.() === "IOS") {
292
+ this.payment = void 0;
293
+ }
294
+ } catch {
295
+ }
238
296
  try {
239
297
  this._system = { ...this._system, language: FBInstant.getLocale() };
240
298
  } catch {
@@ -1,5 +1,5 @@
1
- import { r as PlatformName, g as IPlatformAuth, e as IPlatformAds, l as IPlatformPayment, h as IPlatformDevice, f as IPlatformAnalytics, m as IPlatformShortcut, j as IPlatformMission, i as IPlatformLifecycle } from '../types-WJYVtDfo.js';
2
- import { B as BasePlatform } from '../BasePlatform-CwVOo3_q.js';
1
+ import { r as PlatformName, g as IPlatformAuth, e as IPlatformAds, l as IPlatformPayment, h as IPlatformDevice, f as IPlatformAnalytics, m as IPlatformShortcut, j as IPlatformMission, i as IPlatformLifecycle } from '../types-CxYJ9uz0.js';
2
+ import { B as BasePlatform } from '../BasePlatform-DqIfl-Oi.js';
3
3
 
4
4
  /**
5
5
  * TikTok Mini Games(Minis)平台适配器。
@@ -1,5 +1,5 @@
1
- import { BasePlatform } from '../chunk-PDESBWLX.js';
2
1
  import { PlatformUnsupportedError } from '../chunk-II3JM4R3.js';
2
+ import { BasePlatform } from '../chunk-NFEHUFN7.js';
3
3
  import { __publicField } from '../chunk-PKBMQBKP.js';
4
4
 
5
5
  // src/platform/impl/tiktok/TikTokPlatform.ts
@@ -57,29 +57,66 @@ var TikTokAuth = class {
57
57
  async getProfile() {
58
58
  return null;
59
59
  }
60
+ // 显式授权(game.authorize):弹 TikTok 授权页拿带 scope 的 code,后端换 token 后调 /v2/user/info/
61
+ // 取 nickname/avatar。callback 式,包成 Promise + 10s 超时(同 login,防永久 pending)。
62
+ authorize(scope = "user.info.basic") {
63
+ requireTT("auth.authorize");
64
+ return new Promise((resolve, reject) => {
65
+ let settled = false;
66
+ const timer = setTimeout(() => {
67
+ if (settled) return;
68
+ settled = true;
69
+ reject(new Error("TikTok authorize timeout"));
70
+ }, 1e4);
71
+ const finish = (fn) => {
72
+ if (settled) return;
73
+ settled = true;
74
+ clearTimeout(timer);
75
+ fn();
76
+ };
77
+ try {
78
+ TTMinis.game.authorize({
79
+ scope,
80
+ success: (res) => {
81
+ const code = res?.code ?? res?.authResponse?.code ?? "";
82
+ finish(() => code ? resolve({ code, raw: res }) : reject(new Error("authorize: no code")));
83
+ },
84
+ fail: (err) => finish(() => reject(new Error(`TikTok authorize fail: ${err?.errMsg ?? JSON.stringify(err)}`)))
85
+ });
86
+ } catch (e) {
87
+ finish(() => reject(e));
88
+ }
89
+ });
90
+ }
60
91
  };
61
92
  var TikTokAds = class {
62
- // TikTok H5 SDK 只有 createRewardedVideoAd(没有 preload/show/Interstitial 单独 API,
63
- // 实测 2026-05-28 反编译 connect.tiktok-minis.com/game/sdk.js 36 个 game.* 方法确认)。
64
- // 之前 framework 调 preloadRewardedVideo / showRewardedVideo / showInterstitialAd 全部
65
- // throw TypeError,被 try-catch 吞掉返 'failed',审核因此判"未集成有效营收功能"。
66
- //
67
- // 修复:统一走 createRewardedVideoAd(优先同步签名拿 ad 实例,失败试异步 callback 签名),
68
- // ad.onClose 等结果后 ad.show 触发展示;preloadRewarded 退化为 noop(SDK 自身管预加载)。
69
- async preloadRewarded(_adUnitId) {
93
+ constructor() {
94
+ // TikTok H5 SDK 激励视频:createRewardedVideoAd 返回**单例**广告对象,适配器缓存复用。
95
+ // 反编译 connect.tiktok-minis.com/game/sdk.js(2026-06-02,广告类 `Rn`)确认:ad 对象**确有**
96
+ // show/onClose/onError/offClose;**当前版本无** load/onLoad/destroy —— create 后广告自动拉取,
97
+ // show() 内部先 await SDK 自身加载 Promise 再展示。故本适配器对 load/onLoad 用**可选调用**
98
+ // (`ad.load?.()` / `ad.onLoad?.()`),缺失时 ensureLoaded 乐观置就绪并直接 show(由 show 内部等加载);
99
+ // 真机若为更高版本带 load/onLoad,则走预加载快路径。两种情况都正确,绝不调用不存在的方法。
100
+ //
101
+ // ⚠ 历史 bug(导致"广告请求多、曝光低"):旧实现每次 showRewarded 都 createRewardedVideoAd
102
+ // 新建实例并**立刻 show**(广告尚未加载完)。每次 create 触发一次广告请求,但 show 在未就绪时
103
+ // 失败 → 请求计数 +1、曝光计数 +0,且重复建实例放大请求量。
104
+ // 修复:单例缓存 + 一次性绑定回调 + 先 load 再 show(show 失败重试一次 load→show)。
105
+ /** 单例广告实例(按首个 adUnitId 创建,后续复用);创建失败时为 undefined 并允许下次重建 */
106
+ __publicField(this, "adPromise");
107
+ /** 当前 show 调用的结果回调;激励视频为模态,同一时刻只有一个 */
108
+ __publicField(this, "pendingShow");
109
+ /** 广告是否已加载就绪;show(消耗)或出错后置 false,需重新 load */
110
+ __publicField(this, "loaded", false);
70
111
  }
71
- showRewarded(adUnitId) {
72
- requireTT("ads.showRewarded");
73
- return new Promise((resolve) => {
74
- const onResult = (isEnded) => resolve(isEnded ? "completed" : "skipped");
75
- const onError = (msg) => {
76
- console.warn("[ads.showRewarded] error:", msg);
77
- resolve("failed");
78
- };
112
+ /** 取/建单例广告实例,并**一次性**绑定 onLoad/onClose/onError(复用不重复绑定) */
113
+ getAd(adUnitId) {
114
+ if (this.adPromise) return this.adPromise;
115
+ this.adPromise = new Promise((resolve) => {
79
116
  try {
80
117
  const adSync = TTMinis.game.createRewardedVideoAd({ adUnitId });
81
118
  if (adSync && typeof adSync.show === "function") {
82
- this.wireAd(adSync, onResult, onError);
119
+ resolve(this.wireOnce(adSync));
83
120
  return;
84
121
  }
85
122
  } catch (e) {
@@ -87,43 +124,94 @@ var TikTokAds = class {
87
124
  }
88
125
  try {
89
126
  TTMinis.game.createRewardedVideoAd((res) => {
90
- if (res?.is_success && res.rewardedVideoAd) {
91
- this.wireAd(res.rewardedVideoAd, onResult, onError);
92
- } else {
93
- onError(JSON.stringify(res?.error ?? "create failed"));
94
- }
127
+ if (res?.is_success && res.rewardedVideoAd) resolve(this.wireOnce(res.rewardedVideoAd));
128
+ else resolve(void 0);
95
129
  }, { adUnitId });
96
130
  } catch (e) {
97
- onError(e?.message ?? "createRewardedVideoAd both signatures failed");
131
+ console.warn("[ads] async createRewardedVideoAd failed:", e?.message);
132
+ resolve(void 0);
98
133
  }
99
134
  });
135
+ return this.adPromise;
100
136
  }
101
- wireAd(ad, onResult, onError) {
102
- let settled = false;
137
+ /** 一次性绑定生命周期回调:onLoad 标记就绪;onClose/onError 结算当前 pendingShow */
138
+ wireOnce(ad) {
139
+ try {
140
+ ad.onLoad?.(() => {
141
+ this.loaded = true;
142
+ });
143
+ } catch {
144
+ }
103
145
  try {
104
146
  ad.onClose((res) => {
105
- if (settled) return;
106
- settled = true;
107
- onResult(!!res?.isEnded);
147
+ this.loaded = false;
148
+ this.settle(res?.isEnded ? "completed" : "skipped");
108
149
  });
109
150
  } catch (e) {
110
- onError(`onClose unsupported: ${e?.message}`);
151
+ console.warn("[ads] onClose unsupported:", e?.message);
152
+ }
153
+ try {
154
+ ad.onError?.((res) => {
155
+ this.loaded = false;
156
+ console.warn("[ads.showRewarded] error:", res?.error?.error_msg);
157
+ this.settle("failed");
158
+ });
159
+ } catch {
160
+ }
161
+ return ad;
162
+ }
163
+ /** 结算当前 show 的结果(只结算挂起的那一次) */
164
+ settle(r) {
165
+ const resolve = this.pendingShow;
166
+ this.pendingShow = void 0;
167
+ resolve?.(r);
168
+ }
169
+ /** 确保广告已加载:已就绪直接返回,否则 load() 等待(老 SDK 无 load 则乐观置就绪) */
170
+ async ensureLoaded(ad) {
171
+ if (this.loaded) return;
172
+ if (typeof ad.load === "function") await ad.load();
173
+ this.loaded = true;
174
+ }
175
+ /** 预加载:创建单例 + 拉一条广告,使后续 show 即时展示(降低请求/曝光错配) */
176
+ async preloadRewarded(adUnitId) {
177
+ if (!hasTT()) return;
178
+ const ad = await this.getAd(adUnitId);
179
+ if (!ad) {
180
+ this.adPromise = void 0;
111
181
  return;
112
182
  }
113
- if (typeof ad.onError === "function") {
114
- try {
115
- ad.onError((res) => {
116
- if (settled) return;
117
- settled = true;
118
- onError(res?.error?.error_msg ?? "ad error");
119
- });
120
- } catch {
121
- }
183
+ try {
184
+ await this.ensureLoaded(ad);
185
+ } catch (e) {
186
+ console.warn("[ads.preloadRewarded] load failed:", e?.message);
122
187
  }
123
- void ad.show().catch((e) => {
124
- if (settled) return;
125
- settled = true;
126
- onError(e?.message ?? "show rejected");
188
+ }
189
+ showRewarded(adUnitId) {
190
+ requireTT("ads.showRewarded");
191
+ return new Promise((resolve) => {
192
+ this.pendingShow = resolve;
193
+ void (async () => {
194
+ const ad = await this.getAd(adUnitId);
195
+ if (!ad) {
196
+ this.adPromise = void 0;
197
+ this.settle("failed");
198
+ return;
199
+ }
200
+ try {
201
+ await this.ensureLoaded(ad);
202
+ await ad.show();
203
+ } catch (e) {
204
+ console.warn("[ads.showRewarded] show failed, retry after load:", e?.message);
205
+ this.loaded = false;
206
+ try {
207
+ await this.ensureLoaded(ad);
208
+ await ad.show();
209
+ } catch (e2) {
210
+ console.warn("[ads.showRewarded] show failed after reload:", e2?.message);
211
+ this.settle("failed");
212
+ }
213
+ }
214
+ })();
127
215
  });
128
216
  }
129
217
  // 插页广告:官方文档 createInterstitialAd 自 SDK 0.3.0;当前反编译版 sdk.js 未见此 API,
@@ -276,7 +364,12 @@ var TikTokPayment = class {
276
364
  fail: (err) => {
277
365
  if (settled) return;
278
366
  settled = true;
279
- reject(new Error(`TikTok pay failed: ${err?.errMsg ?? "unknown"}`));
367
+ const e = err;
368
+ const code = e?.error?.error_code;
369
+ const msg = e?.error?.error_msg ?? e?.errMsg ?? "unknown";
370
+ const wrapped = new Error(`TikTok pay failed${code !== void 0 ? ` (code=${code})` : ""}: ${msg}`);
371
+ wrapped.cause = err;
372
+ reject(wrapped);
280
373
  }
281
374
  });
282
375
  } catch (e) {
@@ -311,15 +404,14 @@ var TikTokDevice = class {
311
404
  } catch {
312
405
  }
313
406
  }
314
- async setClipboard(text) {
315
- requireTT("device.setClipboard");
316
- await TTMinis.game.setClipboardData({ data: text });
317
- }
318
- async getClipboard() {
319
- requireTT("device.getClipboard");
320
- const res = await TTMinis.game.getClipboardData();
321
- return res?.data ?? "";
322
- }
407
+ // 剪贴板:TikTok Minis 下**无任何可用实现**,故 setClipboard/getClipboard 不提供(IPlatformDevice
408
+ // 两者均为可选,业务侧 device.setClipboard?.() 会安全跳过)。依据(反编译 connect.tiktok-minis.com/game/sdk.js
409
+ // 2026-06-02):
410
+ // 1. TTMinis.game 上**不存在** setClipboardData/getClipboardData(这是臆造的方法名;SDK 的
411
+ // copyToClipboard/fetchClipBoard 属直播宿主 JSB,不在 minigame game API 暴露,也无 game 方法);
412
+ // 2. navigator.clipboard.read/readText/write/writeText 被 SDK 主动禁用(替换为 console.error 桩),
413
+ // 故浏览器退路也走不通。
414
+ // 结论:TikTok 真无剪贴板能力 → 如实不实现,而非调用不存在的方法在运行时抛 TypeError。
323
415
  // shareAppMessage 是 callback 式(2026-05-30 反编译核对),不是 Promise;这里包成 Promise。
324
416
  share(payload) {
325
417
  requireTT("device.share");
@@ -1,5 +1,5 @@
1
- import { r as PlatformName, h as IPlatformDevice } from '../types-WJYVtDfo.js';
2
- import { B as BasePlatform } from '../BasePlatform-CwVOo3_q.js';
1
+ import { r as PlatformName, h as IPlatformDevice } from '../types-CxYJ9uz0.js';
2
+ import { B as BasePlatform } from '../BasePlatform-DqIfl-Oi.js';
3
3
 
4
4
  /**
5
5
  * Web(普通浏览器 / H5)平台适配器。
@@ -1,4 +1,4 @@
1
- import { BasePlatform } from '../chunk-PDESBWLX.js';
1
+ import { BasePlatform } from '../chunk-NFEHUFN7.js';
2
2
  import { __publicField } from '../chunk-PKBMQBKP.js';
3
3
 
4
4
  // src/platform/impl/web/WebPlatform.ts
@@ -1,4 +1,4 @@
1
- import { d as IPlatform, k as IPlatformNet, o as IPlatformStorage, i as IPlatformLifecycle, g as IPlatformAuth, e as IPlatformAds, l as IPlatformPayment, h as IPlatformDevice, P as PlatformInfo, L as LaunchOptions } from '../types-WJYVtDfo.js';
1
+ import { d as IPlatform, k as IPlatformNet, o as IPlatformStorage, i as IPlatformLifecycle, g as IPlatformAuth, e as IPlatformAds, l as IPlatformPayment, h as IPlatformDevice, P as PlatformInfo, L as LaunchOptions } from '../types-CxYJ9uz0.js';
2
2
 
3
3
  /**
4
4
  * 微信小游戏平台适配器。
@@ -0,0 +1,71 @@
1
+ import { r as PlatformName, o as IPlatformStorage, g as IPlatformAuth, e as IPlatformAds, h as IPlatformDevice, i as IPlatformLifecycle } from '../types-CxYJ9uz0.js';
2
+ import { B as BasePlatform } from '../BasePlatform-DqIfl-Oi.js';
3
+
4
+ /**
5
+ * YouTube Playables 平台适配器。
6
+ *
7
+ * 运行环境:YouTube Playables(YouTube 内嵌的 HTML5 小游戏沙箱),全局对象 `ytgame.*`。
8
+ * SDK 由外壳 index.html 的 <script src="https://www.youtube.com/game_api/v1"> 注入(页面第一个脚本)。
9
+ *
10
+ * API 来源(2026-06-01 拉取核对,代码内各处标注):
11
+ * - 官方 SDK reference:https://developers.google.com/youtube/gaming/playables/reference/sdk
12
+ * - 官方 getting_started:https://developers.google.com/youtube/gaming/playables/reference/getting_started
13
+ * - Phaser 官方模板:https://github.com/phaserjs/template-youtube-playables(src/YouTubePlayables.js)
14
+ *
15
+ * 能力实现:
16
+ * - hasDOM=true(Playables 跑在 webview),isMiniGame=true
17
+ * - net:fetch + 标准 WebSocket(继承 BasePlatform)
18
+ * - storage:ytgame.game.saveData/loadData 是「整份序列化 string」存档(非 per-key KV),
19
+ * IPlatformStorage 是同步 KV → 采用「内存缓存 + 异步整份回写」:init 时 loadData 预拉
20
+ * 解析成 Map,getItem 读缓存,setItem 写缓存并去抖异步 saveData 整份。
21
+ * - auth:YouTube **不暴露用户身份/openId**,也无可换 token 的 OAuth code。存档由 YouTube 内部
22
+ * 按已登录用户隔离。login 返回一个**本地持久化的匿名 id**(存进 storage),并把它当 code/openId,
23
+ * 供业务排行榜「本机身份」使用;getProfile 返 null(拿不到昵称/头像);authorize 不实现(undefined)。
24
+ * - ads:ytgame.ads.requestRewardedAd(rewardId) → 'completed'/'skipped';requestInterstitialAd() → 'closed'/'failed'。
25
+ * - payment:**不实现**(undefined)。YouTube Playables 禁止任何内购(含 off-platform),
26
+ * 业务层 App.payment 为 undefined 自动降级。
27
+ * - device.share:YouTube Playables SDK 无分享 API → share 不实现(undefined);震动/剪贴板退回浏览器。
28
+ * - lifecycle:ytgame.system.onPause/onResume;非 Playables 环境退回 DOM visibilitychange。
29
+ * - shortcut / mission:YouTube 无对应能力 → 不实现(undefined)。
30
+ * - 排行榜:ytgame.engagement.sendScore({value}) 作为平台特有能力暴露 sendScore(score)(见类末尾)。
31
+ *
32
+ * init 时序(官方:SDK ready → firstFrameReady → ... → gameReady):
33
+ * 读 DOM 系统信息 → firstFrameReady() → getLanguage 覆盖语言 → 预拉存档 → 同步音频状态 → gameReady()。
34
+ * setLoadingProgress(p):YouTube **无** setLoadingProgress API,映射为「首次 >0 触发 firstFrameReady,
35
+ * 到 1 触发 gameReady」,保持与其它平台一致的对外接口。
36
+ *
37
+ * 所有 ytgame 访问均 typeof 检测 + try-catch:非 YouTube 环境(typeof ytgame === 'undefined')安全降级,不崩。
38
+ */
39
+
40
+ declare class YouTubePlatform extends BasePlatform {
41
+ readonly platformName: PlatformName;
42
+ readonly isMiniGame = true;
43
+ private readonly ytStorage;
44
+ /** firstFrameReady / gameReady 仅可调用一次的幂等守卫 */
45
+ private _firstFrameReady;
46
+ private _gameReady;
47
+ readonly storage: IPlatformStorage;
48
+ readonly auth: IPlatformAuth;
49
+ readonly ads: IPlatformAds;
50
+ readonly device: IPlatformDevice;
51
+ readonly lifecycle: IPlatformLifecycle;
52
+ init(): Promise<void>;
53
+ /**
54
+ * 加载进度上报。YouTube **无** setLoadingProgress API:
55
+ * 这里把对外接口映射为「首次 progress>0 触发 firstFrameReady,progress≥1 触发 gameReady」,
56
+ * 让业务加载场景的进度回调在 YouTube 上也能正确驱动 firstFrameReady/gameReady 时序。
57
+ * @param progress 0..1 的小数
58
+ */
59
+ setLoadingProgress(progress: number): void;
60
+ /**
61
+ * 上报排行榜分数(YouTube 平台特有能力,framework 无通用 score 接口)。
62
+ * 业务可经 (App.platform as YouTubePlatform).sendScore(n) 调用。
63
+ * ytgame.engagement.sendScore({value}):value 须为整数且 ≤ Number.MAX_SAFE_INTEGER,否则被拒。
64
+ * 文档:https://developers.google.com/youtube/gaming/playables/reference/sdk#sendscore
65
+ */
66
+ sendScore(value: number): Promise<boolean>;
67
+ private callFirstFrameReady;
68
+ private callGameReady;
69
+ }
70
+
71
+ export { YouTubePlatform };