@playcraft/build 0.0.36 → 0.0.37

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.
@@ -1029,8 +1029,19 @@ export class PlayableScriptsAdapter {
1029
1029
  }, options.timeout);
1030
1030
  // shell: true 时 Node.js 把 cmd + args 拼接成 shell 字符串,
1031
1031
  // 但不会转义 args 中的特殊字符(如 URL 里的 &)。
1032
- // 用单引号包裹每个 arg 防止 shell 解析(单引号内只有 ' 本身需要转义)。
1033
- const shellSafeArgs = args.map(a => `'${a.replace(/'/g, "'\\''")}'`);
1032
+ // 需要按平台分别处理:
1033
+ // - Windows (cmd.exe) 不认识单引号,用双引号;cmd 的特殊字符用 ^ 转义
1034
+ // - POSIX (bash/sh) 使用单引号强引用
1035
+ const isWindows = process.platform === 'win32';
1036
+ const shellSafeArgs = args.map((a) => {
1037
+ if (isWindows) {
1038
+ // cmd.exe:用双引号包裹,内部的 " 转义为 "",其它 shell 元字符用 ^ 转义
1039
+ const escaped = a.replace(/"/g, '""').replace(/([&|<>^%])/g, '^$1');
1040
+ return `"${escaped}"`;
1041
+ }
1042
+ // POSIX:单引号包裹,单引号本身通过 '\'' 断开转义
1043
+ return `'${a.replace(/'/g, "'\\''")}'`;
1044
+ });
1034
1045
  const child = spawn(cmd, shellSafeArgs, {
1035
1046
  cwd: options.cwd,
1036
1047
  env: options.env,
@@ -70,14 +70,14 @@ export declare abstract class PlatformAdapter {
70
70
  */
71
71
  protected getTrackingAdapterScriptAsync(): Promise<string>;
72
72
  /**
73
- * 从 @playcraft/adsdk 包中提取 tracking 实现代码
74
- * 读取 IIFE 版本中 `// src/tracking.ts` 到 `var tracking = {...};` 的代码段
75
- * 包装为独立 IIFE 并暴露 window.PlayCraftTracking
73
+ * 从 @playcraft/ads-tracking 包中提取 tracking 实现代码
74
+ * 读取 IIFE 版本,剥掉最外层的自执行箭头函数包装,
75
+ * 重新包裹为独立 IIFE 并通过 window.PlayCraftTracking 暴露。
76
76
  */
77
77
  protected getAdsdkTrackingScriptAsync(): Promise<string>;
78
78
  /**
79
79
  * 在 HTML 中注入 Tracking 适配器(异步版本)
80
- * 注入顺序:1.Config → 2.adsdk tracking 实现 → 3.Adapter 桥接脚本
80
+ * 注入顺序:1.Config → 2.ads-tracking 实现 → 3.Adapter 桥接脚本
81
81
  */
82
82
  protected injectTrackingAdapterAsync(html: string): Promise<string>;
83
83
  }
@@ -6,8 +6,8 @@ import { minifyPatchCode } from '../utils/minify.js';
6
6
  let cachedMinifiedCTAAdapter = null;
7
7
  // 缓存压缩后的 Tracking 适配器代码
8
8
  let cachedMinifiedTrackingAdapter = null;
9
- // 缓存从 @playcraft/adsdk 提取的 tracking 实现代码
10
- let cachedAdsdkTrackingScript = null;
9
+ // 缓存从 @playcraft/ads-tracking 提取的 tracking 实现代码
10
+ let cachedAdsTrackingScript = null;
11
11
  // 缓存压缩后的平台脚本
12
12
  const platformScriptCache = new Map();
13
13
  export class PlatformAdapter {
@@ -151,54 +151,59 @@ export class PlatformAdapter {
151
151
  return `<script>${minified}</script>`;
152
152
  }
153
153
  /**
154
- * 从 @playcraft/adsdk 包中提取 tracking 实现代码
155
- * 读取 IIFE 版本中 `// src/tracking.ts` 到 `var tracking = {...};` 的代码段
156
- * 包装为独立 IIFE 并暴露 window.PlayCraftTracking
154
+ * 从 @playcraft/ads-tracking 包中提取 tracking 实现代码
155
+ * 读取 IIFE 版本,剥掉最外层的自执行箭头函数包装,
156
+ * 重新包裹为独立 IIFE 并通过 window.PlayCraftTracking 暴露。
157
157
  */
158
158
  async getAdsdkTrackingScriptAsync() {
159
- if (cachedAdsdkTrackingScript) {
160
- return `<script>${cachedAdsdkTrackingScript}</script>`;
159
+ if (cachedAdsTrackingScript) {
160
+ return `<script>${cachedAdsTrackingScript}</script>`;
161
161
  }
162
162
  try {
163
- // 从 @playcraft/adsdk 的 IIFE 构建中读取
163
+ // 从 @playcraft/ads-tracking 的 IIFE 构建中读取
164
164
  const { createRequire } = await import('module');
165
165
  const require = createRequire(import.meta.url);
166
- const adsdkPath = require.resolve('@playcraft/adsdk/dist/iife/index.js');
167
- const iifeCode = await fs.readFile(adsdkPath, 'utf-8');
168
- // 提取 tracking 部分:从 "// src/tracking.ts" "trackChallengeFailed" 对象结尾
166
+ const adsTrackingPath = require.resolve('@playcraft/ads-tracking/dist/iife/index.js');
167
+ const iifeCode = await fs.readFile(adsTrackingPath, 'utf-8');
168
+ // 提取 IIFE 内部实现:ads-tracking IIFE 形如
169
+ // "use strict";\n(() => {\n // src/tracking.ts\n ...\n var tracking = {...};\n})();
170
+ // 需要剥掉最外层 (() => { ... })(); 保留内部代码,
171
+ // 再在末尾挂到 window.PlayCraftTracking 上。
169
172
  const startMarker = '// src/tracking.ts';
170
- const endMarker = 'trackChallengeFailed: _trackChallengeFailed';
171
173
  const startIdx = iifeCode.indexOf(startMarker);
172
- const endIdx = iifeCode.indexOf(endMarker);
173
- if (startIdx === -1 || endIdx === -1) {
174
- console.warn('[Tracking] 无法从 @playcraft/adsdk 中提取 tracking 代码,使用内置 adapter');
174
+ if (startIdx === -1) {
175
+ console.warn('[Tracking] 无法从 @playcraft/ads-tracking 中提取 tracking 代码,使用内置 adapter');
175
176
  return '';
176
177
  }
177
- // 找到 tracking 对象定义结束的 `};`
178
- const afterEnd = iifeCode.indexOf('};', endIdx);
179
- const trackingCode = iifeCode.substring(startIdx, afterEnd + 2);
178
+ // 找到最后一个 "})();" 作为 IIFE 结束
179
+ const endIdx = iifeCode.lastIndexOf('})();');
180
+ if (endIdx === -1 || endIdx <= startIdx) {
181
+ console.warn('[Tracking] 无法定位 @playcraft/ads-tracking IIFE 结尾,使用内置 adapter');
182
+ return '';
183
+ }
184
+ const innerCode = iifeCode.substring(startIdx, endIdx).trimEnd();
180
185
  // 包装为独立 IIFE,暴露 window.PlayCraftTracking
181
- const wrappedCode = `(function(){"use strict";${trackingCode}\nwindow.PlayCraftTracking=tracking;})();`;
182
- const minified = await minifyPatchCode(wrappedCode, 'adsdk-tracking-extract');
183
- cachedAdsdkTrackingScript = minified;
186
+ const wrappedCode = `(function(){"use strict";${innerCode}\nwindow.PlayCraftTracking=tracking;})();`;
187
+ const minified = await minifyPatchCode(wrappedCode, 'ads-tracking-extract');
188
+ cachedAdsTrackingScript = minified;
184
189
  return `<script>${minified}</script>`;
185
190
  }
186
191
  catch (e) {
187
- console.warn('[Tracking] 无法加载 @playcraft/adsdk:', e.message);
192
+ console.warn('[Tracking] 无法加载 @playcraft/ads-tracking:', e.message);
188
193
  return '';
189
194
  }
190
195
  }
191
196
  /**
192
197
  * 在 HTML 中注入 Tracking 适配器(异步版本)
193
- * 注入顺序:1.Config → 2.adsdk tracking 实现 → 3.Adapter 桥接脚本
198
+ * 注入顺序:1.Config → 2.ads-tracking 实现 → 3.Adapter 桥接脚本
194
199
  */
195
200
  async injectTrackingAdapterAsync(html) {
196
201
  if (this.options.tracking?.enabled === false)
197
202
  return html;
198
203
  const configScript = this.getTrackingConfigScript();
199
- const adsdkScript = await this.getAdsdkTrackingScriptAsync();
204
+ const adsTrackingScript = await this.getAdsdkTrackingScriptAsync();
200
205
  const trackingScript = await this.getTrackingAdapterScriptAsync();
201
- const combined = configScript + adsdkScript + trackingScript;
206
+ const combined = configScript + adsTrackingScript + trackingScript;
202
207
  if (html.includes('</head>')) {
203
208
  return html.replace('</head>', `${combined}\n</head>`);
204
209
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcraft/build",
3
- "version": "0.0.36",
3
+ "version": "0.0.37",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,6 +16,11 @@
16
16
  "require": "./dist/types.js",
17
17
  "types": "./dist/types.d.ts"
18
18
  },
19
+ "./scene-asset-collector": {
20
+ "import": "./dist/analyzers/scene-asset-collector.js",
21
+ "require": "./dist/analyzers/scene-asset-collector.js",
22
+ "types": "./dist/analyzers/scene-asset-collector.d.ts"
23
+ },
19
24
  "./dist/*": "./dist/*"
20
25
  },
21
26
  "files": [
@@ -30,7 +35,7 @@
30
35
  "release": "node scripts/release.js"
31
36
  },
32
37
  "dependencies": {
33
- "@playcraft/adsdk": "^1.0.13",
38
+ "@playcraft/ads-tracking": "^1.0.0",
34
39
  "@gltf-transform/core": "^4.3.0",
35
40
  "@gltf-transform/extensions": "^4.3.0",
36
41
  "@gltf-transform/functions": "^4.3.0",
@@ -50,7 +55,7 @@
50
55
  "vite-plugin-singlefile": "^2.3.2"
51
56
  },
52
57
  "devDependencies": {
53
- "@playcraft/adsdk": "^1.0.13",
58
+ "@playcraft/ads-tracking": "^1.0.0",
54
59
  "@types/archiver": "^6.0.4",
55
60
  "@types/mime-types": "^2.1.4",
56
61
  "@types/node": "^22.19.8",
@@ -2,8 +2,20 @@
2
2
  * PlayCraft Tracking Adapter (Bridge Layer)
3
3
  *
4
4
  * 职责:在正确的生命周期节点调用 window.PlayCraftTracking 的方法。
5
- * 渠道埋点逻辑由 @playcraft/adsdk 的 tracking 模块提供(构建时自动提取注入)。
6
- * 如果 adsdk tracking 未注入,adapter 的调用会静默忽略。
5
+ * 渠道埋点逻辑由 @playcraft/ads-tracking 的 tracking 模块提供(构建时自动提取注入)。
6
+ * 如果 ads-tracking 未注入,adapter 的调用会静默忽略。
7
+ *
8
+ * 新 API 对照(@playcraft/ads-tracking v1.0.0):
9
+ * onPlaycraftTrackingInit - SDK 初始化开始
10
+ * onPlaycraftTrackingLoading - 游戏资源加载中
11
+ * onPlaycraftTrackingLoaded - 资源加载完成、主场景就绪
12
+ * onPlaycraftChallengeStart - 游戏挑战开始(首次用户交互)
13
+ * onPlaycraftChallengeProgress - 进度上报 (percent 0-100),内部自动触发里程碑
14
+ * onPlaycraftChallengeSuccess - 游戏挑战成功
15
+ * onPlaycraftChallengeFailed - 游戏挑战失败
16
+ * onPlaycraftChallengeFinish - 游戏结束
17
+ * onPlaycraftInstall - 点击安装
18
+ * onPlaycraftRetry - 重试游戏
7
19
  */
8
20
  (function() {
9
21
  'use strict';
@@ -23,28 +35,44 @@
23
35
  }
24
36
  }
25
37
 
38
+ // ===== 进度上报(兼容旧的 setTotalInteractions + checkInteractionProgress)=====
39
+ // 新包只提供 onPlaycraftChallengeProgress(percent 0-100),里程碑由 SDK 内部处理。
40
+ // 这里维护 total 状态,将 (current/total*100) 转换为百分比上报。
41
+ var _total = totalInteractions > 0 ? totalInteractions : 0;
42
+ function setTotal(n) {
43
+ if (typeof n === 'number' && n > 0) _total = n;
44
+ }
45
+ function reportProgress(current) {
46
+ if (typeof current !== 'number') return;
47
+ var percent;
48
+ if (_total > 0) {
49
+ percent = Math.min(100, Math.max(0, (current / _total) * 100));
50
+ } else {
51
+ // 未设置 total 时,把传入值当作已经是百分比
52
+ percent = Math.min(100, Math.max(0, current));
53
+ }
54
+ call('onPlaycraftChallengeProgress', [percent]);
55
+ }
56
+
26
57
  // ===== 自动埋点 =====
27
58
 
28
- // 1. setTotalInteractions + onInit(立即)
29
- if (totalInteractions > 0) {
30
- call('setTotalInteractions', [totalInteractions]);
31
- }
32
- call('onInit', [adNetwork]);
59
+ // 1. onPlaycraftTrackingInit(立即)
60
+ call('onPlaycraftTrackingInit', [adNetwork]);
33
61
 
34
- // 2. onStart(页面加载完成)
62
+ // 2. onPlaycraftTrackingLoading(页面加载完成)
35
63
  if (document.readyState === 'complete') {
36
- setTimeout(function() { call('onStart', [adNetwork]); }, 0);
64
+ setTimeout(function() { call('onPlaycraftTrackingLoading', [adNetwork]); }, 0);
37
65
  } else {
38
- window.addEventListener('load', function() { call('onStart', [adNetwork]); });
66
+ window.addEventListener('load', function() { call('onPlaycraftTrackingLoading', [adNetwork]); });
39
67
  }
40
68
 
41
- // 2.5 onInteractive(游戏完全就绪,可交互)
69
+ // 2.5 onPlaycraftTrackingLoaded(游戏完全就绪,可交互)
42
70
  // 优先级: playcraft:interactive 事件 > PlayCanvas app.on('start') fallback
43
71
  var _interactive = false;
44
72
  function triggerInteractive() {
45
73
  if (_interactive) return;
46
74
  _interactive = true;
47
- call('onInteractive', [adNetwork]);
75
+ call('onPlaycraftTrackingLoaded', [adNetwork]);
48
76
  }
49
77
  // 优先监听游戏主动派发的 playcraft:interactive
50
78
  document.addEventListener('playcraft:interactive', triggerInteractive);
@@ -66,12 +94,12 @@
66
94
  }
67
95
  setTimeout(pollPlayCanvasApp, 0);
68
96
 
69
- // 3. onInteraction(首次用户交互,只触发一次)
97
+ // 3. onPlaycraftChallengeStart(首次用户交互,只触发一次)
70
98
  var _interacted = false;
71
99
  function onFirstInteraction() {
72
100
  if (_interacted) return;
73
101
  _interacted = true;
74
- call('onInteraction', [adNetwork]);
102
+ call('onPlaycraftChallengeStart', [adNetwork]);
75
103
  document.removeEventListener('touchstart', onFirstInteraction, true);
76
104
  document.removeEventListener('mousedown', onFirstInteraction, true);
77
105
  document.removeEventListener('pointerdown', onFirstInteraction, true);
@@ -80,13 +108,13 @@
80
108
  document.addEventListener('mousedown', onFirstInteraction, true);
81
109
  document.addEventListener('pointerdown', onFirstInteraction, true);
82
110
 
83
- // 4. onInstall(hook CTA adapter)
111
+ // 4. onPlaycraftInstall(hook CTA adapter)
84
112
  var _ctaAttempts = 0;
85
113
  function hookCTA() {
86
114
  if (window.PlayCraftCTA && window.PlayCraftCTA.jump) {
87
115
  var _orig = window.PlayCraftCTA.jump;
88
116
  window.PlayCraftCTA.jump = function(opts) {
89
- call('onInstall', [adNetwork]);
117
+ call('onPlaycraftInstall', [adNetwork]);
90
118
  _orig.call(window.PlayCraftCTA, opts);
91
119
  };
92
120
  if (window.jump2AppStore) window.jump2AppStore = window.PlayCraftCTA.jump;
@@ -110,21 +138,30 @@
110
138
  });
111
139
  }
112
140
 
113
- listenOnce('playcraft:finish', 'onFinish');
114
- listen('playcraft:success', 'trackChallengeSuccess');
115
- listen('playcraft:failed', 'trackChallengeFailed');
141
+ listenOnce('playcraft:finish', 'onPlaycraftChallengeFinish');
142
+ listen('playcraft:success', 'onPlaycraftChallengeSuccess');
143
+ listen('playcraft:failed', 'onPlaycraftChallengeFailed');
116
144
  document.addEventListener('playcraft:retry', function() {
117
145
  once['playcraft:finish'] = false;
118
- call('onRetry', [adNetwork]);
146
+ call('onPlaycraftRetry', [adNetwork]);
119
147
  });
120
148
  document.addEventListener('playcraft:setTotal', function(e) {
121
149
  var d = e.detail;
122
150
  var n = typeof d === 'number' ? d : (d && d.total);
123
- if (typeof n === 'number') call('setTotalInteractions', [n]);
151
+ if (typeof n === 'number') setTotal(n);
124
152
  });
125
153
  document.addEventListener('playcraft:progress', function(e) {
126
154
  var d = e.detail;
127
- var n = typeof d === 'number' ? d : (d && d.current);
128
- if (typeof n === 'number') call('checkInteractionProgress', [n]);
155
+ // 兼容两种传参:number(当前值)或 { current, total, percent }
156
+ if (typeof d === 'number') {
157
+ reportProgress(d);
158
+ } else if (d && typeof d === 'object') {
159
+ if (typeof d.total === 'number') setTotal(d.total);
160
+ if (typeof d.percent === 'number') {
161
+ call('onPlaycraftChallengeProgress', [Math.min(100, Math.max(0, d.percent))]);
162
+ } else if (typeof d.current === 'number') {
163
+ reportProgress(d.current);
164
+ }
165
+ }
129
166
  });
130
167
  })();