shoplazza-cli 1.0.13 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/README.md +12 -12
  2. package/bin/shoplazza +5 -3
  3. package/lib/app/api/cli.js +225 -0
  4. package/lib/app/api/openapi.js +121 -0
  5. package/lib/app/api/partnerOpenapi.js +104 -0
  6. package/lib/app/bin/index.js +20 -0
  7. package/lib/app/bin/javy/javy-arm-linux-v5.0.1 +0 -0
  8. package/lib/app/bin/javy/javy-arm-macos-v5.0.1 +0 -0
  9. package/lib/app/bin/javy/javy-x86_64-linux-v5.0.1 +0 -0
  10. package/lib/app/bin/javy/javy-x86_64-macos-v5.0.1 +0 -0
  11. package/lib/app/bin/javy/javy-x86_64-windows-v5.0.1 +0 -0
  12. package/lib/app/commands/config/actions/link.js +189 -0
  13. package/lib/app/commands/config/actions/use.js +40 -0
  14. package/lib/app/commands/config/index.js +25 -0
  15. package/lib/app/commands/config/link.js +11 -0
  16. package/lib/app/commands/config/use.js +11 -0
  17. package/lib/app/commands/deploy/actions/deploy.js +196 -0
  18. package/lib/app/commands/deploy/index.js +11 -0
  19. package/lib/app/commands/dev/actions/dev.js +206 -0
  20. package/lib/app/commands/dev/index.js +11 -0
  21. package/lib/app/commands/generate/actions/extension.js +97 -0
  22. package/lib/app/commands/generate/actions/generateCheckout.js +58 -0
  23. package/lib/app/commands/generate/actions/generateFunction.js +56 -0
  24. package/lib/app/commands/generate/actions/generateTheme.js +128 -0
  25. package/lib/app/commands/generate/extension.js +11 -0
  26. package/lib/app/commands/generate/index.js +22 -0
  27. package/lib/app/commands/index.js +82 -0
  28. package/lib/app/commands/info/actions/info.js +168 -0
  29. package/lib/app/commands/info/index.js +11 -0
  30. package/lib/app/commands/init/actions/init.js +176 -0
  31. package/lib/app/commands/init/index.js +14 -0
  32. package/lib/app/commands/versions/actions/list.js +210 -0
  33. package/lib/app/commands/versions/index.js +22 -0
  34. package/lib/app/commands/versions/list.js +14 -0
  35. package/lib/app/constant/code.js +7 -0
  36. package/lib/app/constant/color.js +18 -0
  37. package/lib/app/constant/extension.js +16 -0
  38. package/lib/app/constant/host.js +23 -0
  39. package/lib/app/constant/sso.js +7 -0
  40. package/lib/app/index.js +4 -25
  41. package/lib/app/services/auth/config.js +33 -0
  42. package/lib/app/services/auth/index.js +9 -0
  43. package/lib/app/services/auth/oauth-server.js +70 -0
  44. package/lib/app/services/auth/partner-token.js +45 -0
  45. package/lib/app/services/auth/sso-token.js +69 -0
  46. package/lib/app/services/auth/store-token.js +100 -0
  47. package/lib/app/services/auth/url-builder.js +23 -0
  48. package/lib/app/services/config/index.js +41 -0
  49. package/lib/app/services/devServer/app.js +76 -0
  50. package/lib/app/services/devServer/index.js +103 -0
  51. package/lib/app/services/devServer/middleware/hmacValidatorMiddleWare.js +20 -0
  52. package/lib/app/services/devServer/middleware/index.js +5 -0
  53. package/lib/app/services/devServer/tunnel/index.js +43 -0
  54. package/lib/app/services/devServer/tunnel/providers/cloudflare.js +364 -0
  55. package/lib/app/services/devServer/tunnel/providers/ngrok.js +70 -0
  56. package/lib/app/services/devServer/utils/index.js +5 -0
  57. package/lib/app/services/devServer/utils/secureCompare.js +5 -0
  58. package/lib/app/services/devServer/views/app.ejs +133 -0
  59. package/lib/app/services/extension-build/buildCheckout.js +47 -0
  60. package/lib/app/services/extension-build/buildFunction.js +57 -0
  61. package/lib/app/services/extension-build/buildTheme.js +100 -0
  62. package/lib/app/services/extension-build/index.js +23 -0
  63. package/lib/app/services/extension-build/plugins/vite-plugin-add-extension-id.js +26 -0
  64. package/lib/app/services/extension-build/plugins/vite-plugin-transform-extension-html.js +207 -0
  65. package/lib/app/services/extension-diff/index.js +132 -0
  66. package/lib/app/services/extension-upsert/index.js +21 -0
  67. package/lib/app/services/extension-upsert/upsertCheckout.js +44 -0
  68. package/lib/app/services/extension-upsert/upsertFunction.js +52 -0
  69. package/lib/app/services/extension-upsert/upsertTheme.js +113 -0
  70. package/lib/app/services/oss/index.js +45 -0
  71. package/lib/app/services/partner/index.js +52 -0
  72. package/lib/app/store/base-store.js +37 -0
  73. package/lib/app/store/config-store.js +55 -0
  74. package/lib/app/store/config.js +21 -0
  75. package/lib/app/store/index.js +14 -0
  76. package/lib/app/store/install-store.js +41 -0
  77. package/lib/app/store/sso-store.js +55 -0
  78. package/lib/app/utils/asyncPool.js +42 -0
  79. package/lib/app/utils/debug/index.js +16 -0
  80. package/lib/app/utils/env.js +24 -0
  81. package/lib/app/utils/error.js +20 -0
  82. package/lib/app/utils/git.js +20 -0
  83. package/lib/app/utils/json.js +27 -0
  84. package/lib/app/utils/path.js +33 -0
  85. package/lib/app/utils/platform.js +37 -0
  86. package/lib/app/utils/request/cli.js +72 -0
  87. package/lib/app/utils/request/debug.js +13 -0
  88. package/lib/app/utils/request/openapi.js +67 -0
  89. package/lib/app/utils/request/partnerOpenapi.js +47 -0
  90. package/lib/app/utils/toml.js +56 -0
  91. package/lib/app/utils/views/message.js +68 -0
  92. package/lib/app/utils/views/select.js +36 -0
  93. package/lib/app/utils/withTempDir.js +55 -0
  94. package/lib/checkout/api.js +2 -0
  95. package/lib/function/bin/javy/javy-arm-macos-v5.0.1 +0 -0
  96. package/lib/oss.js +5 -2
  97. package/lib/theme-extension/index.js +29 -0
  98. package/lib/utils/config.js +1 -1
  99. package/package.json +12 -1
  100. package/examples/checkout-extension/README.md +0 -19
  101. package/examples/checkout-extension/extension.config.js +0 -4
  102. package/examples/checkout-extension/extensions/add-shipping-desc/extension.json +0 -10
  103. package/examples/checkout-extension/extensions/add-shipping-desc/src/index.js +0 -7
  104. package/examples/checkout-extension/extensions/ext-1/extension.json +0 -10
  105. package/examples/checkout-extension/extensions/ext-1/src/content.html +0 -3
  106. package/examples/checkout-extension/extensions/ext-1/src/index.html +0 -5
  107. package/examples/checkout-extension/extensions/ext-1/src/index.js +0 -11
  108. package/examples/checkout-extension/extensions/ext-1/src/script.html +0 -3
  109. package/examples/checkout-extension/extensions/ext-1/src/style.html +0 -3
  110. package/examples/checkout-extension/extensions/product-list/extension.json +0 -10
  111. package/examples/checkout-extension/extensions/product-list/src/index.js +0 -5
  112. package/examples/checkout-extension/extensions/rewrite-navigate/extension.json +0 -10
  113. package/examples/checkout-extension/extensions/rewrite-navigate/src/content.html +0 -38
  114. package/examples/checkout-extension/extensions/rewrite-navigate/src/index.html +0 -5
  115. package/examples/checkout-extension/extensions/rewrite-navigate/src/index.js +0 -12
  116. package/examples/checkout-extension/extensions/rewrite-navigate/src/script.html +0 -26
  117. package/examples/checkout-extension/extensions/rewrite-navigate/src/style.html +0 -23
  118. package/examples/checkout-extension/package-lock.json +0 -121
  119. package/examples/checkout-extension/package.json +0 -17
  120. /package/lib/{app → theme-extension}/api/index.js +0 -0
  121. /package/lib/{app → theme-extension}/commands/build.js +0 -0
  122. /package/lib/{app → theme-extension}/commands/connect.js +0 -0
  123. /package/lib/{app → theme-extension}/commands/create.js +0 -0
  124. /package/lib/{app → theme-extension}/commands/deploy.js +0 -0
  125. /package/lib/{app → theme-extension}/commands/list.js +0 -0
  126. /package/lib/{app → theme-extension}/commands/release.js +0 -0
  127. /package/lib/{app → theme-extension}/commands/serve.js +0 -0
  128. /package/lib/{app → theme-extension}/commands/versions.js +0 -0
  129. /package/lib/{app → theme-extension}/template/basic-app/README.md +0 -0
  130. /package/lib/{app → theme-extension}/template/basic-app/extension.config.json +0 -0
  131. /package/lib/{app → theme-extension}/template/basic-app/package.json +0 -0
  132. /package/lib/{app → theme-extension}/template/basic-app/theme-app/assets/index.css +0 -0
  133. /package/lib/{app → theme-extension}/template/basic-app/theme-app/assets-manifest.json +0 -0
  134. /package/lib/{app → theme-extension}/template/basic-app/theme-app/blocks/index.liquid +0 -0
  135. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/ar-SA.json +0 -0
  136. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/de-DE.json +0 -0
  137. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/en-US.json +0 -0
  138. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/es-ES.json +0 -0
  139. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/fr-FR.json +0 -0
  140. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/id-ID.json +0 -0
  141. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/it-IT.json +0 -0
  142. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/ja-JP.json +0 -0
  143. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/ko-KR.json +0 -0
  144. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/nl-NL.json +0 -0
  145. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/pl-PL.json +0 -0
  146. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/pt-PT.json +0 -0
  147. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/ru-RU.json +0 -0
  148. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/th-TH.json +0 -0
  149. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/zh-CN.json +0 -0
  150. /package/lib/{app → theme-extension}/template/basic-app/theme-app/locales/zh-TW.json +0 -0
  151. /package/lib/{app → theme-extension}/template/basic-app/theme-app/snippets/index.liquid +0 -0
  152. /package/lib/{app → theme-extension}/template/embed-app/README.md +0 -0
  153. /package/lib/{app → theme-extension}/template/embed-app/extension.config.json +0 -0
  154. /package/lib/{app → theme-extension}/template/embed-app/package.json +0 -0
  155. /package/lib/{app → theme-extension}/template/embed-app/theme-app/assets-manifest.json +0 -0
  156. /package/lib/{app → theme-extension}/template/embed-app/theme-app/blocks/index.liquid +0 -0
  157. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/ar-SA.json +0 -0
  158. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/de-DE.json +0 -0
  159. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/en-US.json +0 -0
  160. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/es-ES.json +0 -0
  161. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/fr-FR.json +0 -0
  162. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/id-ID.json +0 -0
  163. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/it-IT.json +0 -0
  164. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/ja-JP.json +0 -0
  165. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/ko-KR.json +0 -0
  166. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/nl-NL.json +0 -0
  167. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/pl-PL.json +0 -0
  168. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/pt-PT.json +0 -0
  169. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/ru-RU.json +0 -0
  170. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/th-TH.json +0 -0
  171. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/zh-CN.json +0 -0
  172. /package/lib/{app → theme-extension}/template/embed-app/theme-app/locales/zh-TW.json +0 -0
  173. /package/lib/{app → theme-extension}/template/embed-app/theme-app/snippets/index.liquid +0 -0
  174. /package/lib/{app → theme-extension}/template/embed-app/theme-app/snippets/index_css.liquid +0 -0
  175. /package/lib/{app → theme-extension}/utils/config.js +0 -0
  176. /package/lib/{app → theme-extension}/utils/index.js +0 -0
@@ -0,0 +1,364 @@
1
+ const { exec, spawn } = require('child_process');
2
+ const os = require('os');
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const https = require('https');
6
+
7
+ // Cloudflare版本配置
8
+ const CLOUDFLARE_VERSION = '2024.8.2';
9
+ const CLOUDFLARE_BASE_URL = `https://github.com/cloudflare/cloudflared/releases/download/${CLOUDFLARE_VERSION}`;
10
+
11
+ // 不同平台和架构的二进制文件映射配置
12
+ const PLATFORM_CONFIGS = {
13
+ linux: {
14
+ x64: 'cloudflared-linux-amd64',
15
+ arm64: 'cloudflared-linux-arm64',
16
+ arm: 'cloudflared-linux-arm',
17
+ ia32: 'cloudflared-linux-386'
18
+ },
19
+ darwin: {
20
+ x64: 'cloudflared-darwin-amd64.tgz',
21
+ arm64: 'cloudflared-darwin-arm64.tgz'
22
+ },
23
+ win32: {
24
+ x64: 'cloudflared-windows-amd64.exe',
25
+ ia32: 'cloudflared-windows-386.exe',
26
+ arm64: 'cloudflared-windows-amd64.exe'
27
+ }
28
+ };
29
+
30
+ // 根据当前系统平台和架构获取cloudflared二进制文件的下载URL
31
+ function getDownloadUrl() {
32
+ const platform = os.platform();
33
+ const arch = os.arch();
34
+
35
+ const platformConfig = PLATFORM_CONFIGS[platform];
36
+ if (!platformConfig) {
37
+ throw new Error(`Unsupported platform: ${platform}`);
38
+ }
39
+
40
+ const filename = platformConfig[arch];
41
+ if (!filename) {
42
+ throw new Error(`Unsupported architecture: ${platform}-${arch}`);
43
+ }
44
+
45
+ return {
46
+ url: `${CLOUDFLARE_BASE_URL}/${filename}`,
47
+ filename,
48
+ isCompressed: filename.endsWith('.tgz')
49
+ };
50
+ }
51
+
52
+ // 获取cloudflared二进制文件的本地存储路径
53
+ function getBinaryPath() {
54
+ const platform = os.platform();
55
+ const extension = platform === 'win32' ? '.exe' : '';
56
+ const cloudflaredDir = path.join(process.env.APP_PROJECT_DIR, 'bin', 'cloudflared');
57
+
58
+ // 检查路径状态并确保目录存在
59
+ try {
60
+ const stat = fs.statSync(cloudflaredDir);
61
+ if (!stat.isDirectory()) {
62
+ console.log('⚠️ Removing conflicting file at cloudflared directory path...');
63
+ fs.unlinkSync(cloudflaredDir);
64
+ fs.mkdirSync(cloudflaredDir, { recursive: true });
65
+ }
66
+ // 如果已经是目录,无需处理
67
+ } catch (error) {
68
+ // 路径不存在,直接创建目录
69
+ fs.mkdirSync(cloudflaredDir, { recursive: true });
70
+ }
71
+
72
+ return path.join(cloudflaredDir, `cloudflared${extension}`);
73
+ }
74
+
75
+ // 比较两个版本号,返回值:>0表示versionA更新,<0表示versionB更新,=0表示相同
76
+ function compareVersions(versionA, versionB) {
77
+ const parseVersion = (v) => v.split('.').map(Number);
78
+ const [majorA, minorA, patchA] = parseVersion(versionA);
79
+ const [majorB, minorB, patchB] = parseVersion(versionB);
80
+
81
+ if (majorA !== majorB) return majorA - majorB;
82
+ if (minorA !== minorB) return minorA - minorB;
83
+ return patchA - patchB;
84
+ }
85
+
86
+ // 检查本地cloudflared版本是否满足要求
87
+ async function checkVersion(binaryPath) {
88
+ return new Promise((resolve) => {
89
+ // 如果二进制文件不存在,返回false
90
+ if (!fs.existsSync(binaryPath)) return resolve(false);
91
+
92
+ // 执行version命令获取当前版本
93
+ exec(`"${binaryPath}" --version`, (error, stdout) => {
94
+ if (error) return resolve(false);
95
+ const versionMatch = stdout.match(/cloudflared version (\d+\.\d+\.\d+)/);
96
+ if (versionMatch) {
97
+ const currentVersion = versionMatch[1];
98
+ const needsUpdate = compareVersions(CLOUDFLARE_VERSION, currentVersion) > 0;
99
+ console.log(`Current version: ${currentVersion}, Target version: ${CLOUDFLARE_VERSION}`);
100
+ resolve(!needsUpdate);
101
+ } else {
102
+ resolve(false);
103
+ }
104
+ });
105
+ });
106
+ }
107
+
108
+ // 下载文件,支持总体10秒下载时长限制和HTTP重定向处理
109
+ async function downloadFile(url, destination) {
110
+ return new Promise(async (resolve, reject) => {
111
+ console.log(`📥 Downloading: ${path.basename(url)}`);
112
+
113
+ const file = fs.createWriteStream(destination);
114
+ let isFinished = false;
115
+
116
+ // 设置总体下载时长限制:10秒,不管下载速度如何
117
+ const downloadTimeoutId = setTimeout(() => {
118
+ if (!isFinished) {
119
+ isFinished = true;
120
+ request.destroy();
121
+ file.close();
122
+ if (fs.existsSync(destination)) fs.unlinkSync(destination);
123
+ reject(new Error('Download took too long'));
124
+ }
125
+ }, 10000);
126
+
127
+ const request = https.get(url, (response) => {
128
+ // 处理HTTP重定向
129
+ if (response.statusCode === 301 || response.statusCode === 302) {
130
+ const redirectUrl = response.headers.location;
131
+ if (redirectUrl) {
132
+ clearTimeout(downloadTimeoutId);
133
+ file.close();
134
+ if (fs.existsSync(destination)) fs.unlinkSync(destination);
135
+ // 修复异步错误处理
136
+ downloadFile(redirectUrl, destination)
137
+ .then(() => resolve())
138
+ .catch((error) => reject(error));
139
+ return;
140
+ }
141
+ }
142
+
143
+ // 检查HTTP状态码
144
+ if (response.statusCode !== 200) {
145
+ clearTimeout(downloadTimeoutId);
146
+ file.close();
147
+ if (fs.existsSync(destination)) fs.unlinkSync(destination);
148
+ return reject(new Error(`Download failed: HTTP ${response.statusCode}`));
149
+ }
150
+
151
+ // 将响应数据写入文件
152
+ response.pipe(file);
153
+
154
+ file.on('finish', () => {
155
+ if (!isFinished) {
156
+ isFinished = true;
157
+ clearTimeout(downloadTimeoutId);
158
+ file.close();
159
+ console.log('✅ Download completed');
160
+ resolve();
161
+ }
162
+ });
163
+
164
+ file.on('error', (err) => {
165
+ if (!isFinished) {
166
+ isFinished = true;
167
+ clearTimeout(downloadTimeoutId);
168
+ file.close();
169
+ if (fs.existsSync(destination)) fs.unlinkSync(destination);
170
+ reject(err);
171
+ }
172
+ });
173
+ });
174
+
175
+ // 处理请求错误
176
+ request.on('error', (err) => {
177
+ if (!isFinished) {
178
+ isFinished = true;
179
+ clearTimeout(downloadTimeoutId);
180
+ file.close();
181
+ if (fs.existsSync(destination)) fs.unlinkSync(destination);
182
+ reject(err);
183
+ }
184
+ });
185
+
186
+ // 设置请求超时(服务器无响应):5秒
187
+ request.setTimeout(5000, () => {
188
+ if (!isFinished) {
189
+ request.destroy();
190
+ }
191
+ });
192
+ });
193
+ }
194
+
195
+ // 解压tgz文件(仅macOS平台的cloudflared需要解压)
196
+ async function extractTgz(tgzPath, extractDir) {
197
+ return new Promise((resolve, reject) => {
198
+ console.log('📦 Extracting file...');
199
+ const filename = path.basename(tgzPath);
200
+ exec(`tar -xzf "${filename}"`, { cwd: extractDir }, (error) => {
201
+ if (error) return reject(new Error(`Extraction failed: ${error.message}`));
202
+ console.log('✅ Extraction completed');
203
+ resolve();
204
+ });
205
+ });
206
+ }
207
+
208
+ // 安装cloudflared(如果不存在或版本过低)
209
+ async function installCloudflared() {
210
+ console.log('🔍 Checking cloudflared...');
211
+ const binaryPath = getBinaryPath();
212
+
213
+ // 检查现有版本是否满足要求
214
+ if (await checkVersion(binaryPath)) {
215
+ console.log('✅ cloudflared is already at target version or higher');
216
+ return; // 成功时不需要返回值
217
+ }
218
+
219
+ console.log('🔄 Installing cloudflared...');
220
+ try {
221
+ const { url, filename, isCompressed } = getDownloadUrl();
222
+ const platform = os.platform();
223
+
224
+ // macOS平台需要解压tgz文件
225
+ if (isCompressed && platform === 'darwin') {
226
+ const tgzPath = path.join(path.dirname(binaryPath), filename);
227
+ await downloadFile(url, tgzPath);
228
+ await extractTgz(tgzPath, path.dirname(binaryPath));
229
+ const extractedPath = path.join(path.dirname(binaryPath), 'cloudflared');
230
+ if (fs.existsSync(extractedPath)) fs.renameSync(extractedPath, binaryPath);
231
+ if (fs.existsSync(tgzPath)) fs.unlinkSync(tgzPath);
232
+ } else {
233
+ // Linux直接下载二进制文件
234
+ await downloadFile(url, binaryPath);
235
+ }
236
+
237
+ // 为非Windows平台设置可执行权限
238
+ if (platform !== 'win32') {
239
+ fs.chmodSync(binaryPath, '755');
240
+ }
241
+
242
+ // 验证安装是否成功
243
+ const isWorking = await new Promise((resolve) => {
244
+ exec(`"${binaryPath}" --version`, (error) => resolve(!error));
245
+ });
246
+
247
+ if (isWorking) {
248
+ console.log('✅ cloudflared installed successfully!');
249
+ return; // 成功时不需要返回值
250
+ }
251
+ throw new Error('Installation verification failed');
252
+ } catch (error) {
253
+ console.error('❌ Installation failed:', error.message);
254
+ // 抛出具体错误而不是返回false,这样上层能获得具体的失败原因
255
+ throw new Error(`cloudflared installation failed: ${error.message}`);
256
+ }
257
+ }
258
+
259
+ // 启动Cloudflare Quick Tunnel,返回包含url和close方法的对象
260
+ async function startQuickTunnel(port) {
261
+ console.log('🚀 Starting Cloudflare Tunnel...');
262
+
263
+ const binaryPath = getBinaryPath();
264
+ // 启动cloudflared进程,创建快速隧道
265
+ const tunnel = spawn(binaryPath, ['tunnel', '--url', `http://localhost:${port}`, '--no-autoupdate'], {
266
+ stdio: 'pipe'
267
+ });
268
+
269
+ let tunnelUrl = null;
270
+
271
+ // 设置10秒超时
272
+ const timeout = setTimeout(() => {
273
+ if (!tunnelUrl) {
274
+ console.error('❌ Tunnel startup timeout');
275
+ try {
276
+ tunnel.kill();
277
+ } catch {}
278
+ }
279
+ }, 10000);
280
+
281
+ // 从cloudflared输出中解析隧道URL
282
+ const tryParse = (buf) => {
283
+ const output = String(buf);
284
+ const match = output.match(/https:\/\/[a-zA-Z0-9-]+\.trycloudflare\.com/);
285
+ if (match && !tunnelUrl) {
286
+ tunnelUrl = match[0];
287
+ }
288
+ };
289
+
290
+ // 监听标准输出和错误输出
291
+ tunnel.stdout.on('data', tryParse);
292
+ tunnel.stderr.on('data', tryParse);
293
+
294
+ return await new Promise((resolve, reject) => {
295
+ const finishIfReady = () => {
296
+ if (tunnelUrl) {
297
+ clearTimeout(timeout);
298
+ resolve({
299
+ url: tunnelUrl,
300
+ close: () => {
301
+ try {
302
+ tunnel.kill('SIGTERM');
303
+ } catch {}
304
+ }
305
+ });
306
+ }
307
+ };
308
+
309
+ // 监听隧道进程事件
310
+ tunnel.on('error', (err) => {
311
+ clearTimeout(timeout);
312
+ reject(new Error(`Tunnel process error: ${err.message}`));
313
+ });
314
+ tunnel.on('close', (code) => {
315
+ clearTimeout(timeout);
316
+ if (!tunnelUrl) return reject(new Error(`Tunnel exited with code=${code}`));
317
+ });
318
+
319
+ // 每100ms检查一次隧道URL是否获取到
320
+ const iv = setInterval(() => {
321
+ if (tunnelUrl) {
322
+ clearInterval(iv);
323
+ finishIfReady();
324
+ }
325
+ }, 100);
326
+
327
+ // 兜底:31秒后停止轮询
328
+ setTimeout(() => {
329
+ clearInterval(iv);
330
+ if (!tunnelUrl) {
331
+ reject(new Error('Tunnel not ready'));
332
+ }
333
+ }, 31000);
334
+ });
335
+ }
336
+
337
+ // Cloudflare隧道管理类
338
+ class CloudflareTunnel {
339
+ constructor(port) {
340
+ this.port = port;
341
+ this._closer = null;
342
+ }
343
+
344
+ // 安装cloudflared(如果需要)并启动Quick Tunnel
345
+ async start() {
346
+ // installCloudflared现在会直接抛出错误,不需要检查返回值
347
+ await installCloudflared();
348
+ const { url, close } = await startQuickTunnel(this.port);
349
+ this._closer = close;
350
+ return { url, close };
351
+ }
352
+
353
+ // 关闭隧道
354
+ async close() {
355
+ if (this._closer) {
356
+ try {
357
+ await this._closer();
358
+ } catch {}
359
+ this._closer = null;
360
+ }
361
+ }
362
+ }
363
+
364
+ module.exports = CloudflareTunnel;
@@ -0,0 +1,70 @@
1
+ const ngrok = require('@ngrok/ngrok');
2
+ const chalk = require('chalk');
3
+ const inquirer = require('inquirer');
4
+ const path = require('path');
5
+ const { writeEnvWithDotenv } = require('../../../../utils/env');
6
+
7
+ module.exports = class NgrokTunnel {
8
+ constructor(port, token, domain) {
9
+ this.port = port;
10
+ this.token = token;
11
+ this.domain = domain;
12
+ }
13
+
14
+ async start() {
15
+ if (!this.token) {
16
+ console.log(
17
+ `\nGet your ngrok token from ${chalk.underline.green('https://dashboard.ngrok.com/get-started/your-authtoken')}`
18
+ );
19
+
20
+ const token = await inquirer
21
+ .prompt({
22
+ type: 'input',
23
+ name: 'token',
24
+ message: 'Please input your ngrok token:',
25
+ validate: (value) => {
26
+ return value.trim() !== '' ? true : "NGROK_AUTHTOKEN can't be empty";
27
+ }
28
+ })
29
+ .then((r) => r.token);
30
+
31
+ this.token = token;
32
+ }
33
+
34
+ if (!this.domain) {
35
+ console.log(
36
+ `\nGet your ngrok domain from ${chalk.underline.green('https://dashboard.ngrok.com/domains')}`
37
+ );
38
+
39
+ const domain = await inquirer
40
+ .prompt({
41
+ type: 'input',
42
+ name: 'domain',
43
+ message: 'Please input your ngrok domain:'
44
+ })
45
+ .then((r) => r.domain);
46
+ this.domain = domain;
47
+ }
48
+
49
+ // 存储 token(创建或更新 .env,而不覆盖其它键)
50
+ await writeEnvWithDotenv(path.join(process.cwd(), '.env'), {
51
+ NGROK_AUTHTOKEN: this.token,
52
+ NGROK_DOMAIN: this.domain
53
+ });
54
+
55
+ const listener = await ngrok
56
+ .forward({
57
+ addr: this.port,
58
+ authtoken: this.token,
59
+ domain: this.domain
60
+ })
61
+ .catch((err) => {
62
+ throw err;
63
+ });
64
+
65
+ return {
66
+ url: listener.url(),
67
+ close: () => listener.close()
68
+ };
69
+ }
70
+ };
@@ -0,0 +1,5 @@
1
+ const secureCompare = require('./secureCompare');
2
+
3
+ module.exports = {
4
+ secureCompare
5
+ };
@@ -0,0 +1,5 @@
1
+ const crypto = require('crypto');
2
+
3
+ module.exports = function secureCompare(a, b) {
4
+ return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b));
5
+ };
@@ -0,0 +1,133 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Shoplazza Dev Server</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
16
+ background: linear-gradient(135deg, #f5f5f5 0%, #e8e8e8 100%);
17
+ min-height: 100vh;
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ color: #333;
22
+ line-height: 1.6;
23
+ }
24
+
25
+ .container {
26
+ background: rgba(255, 255, 255, 0.95);
27
+ backdrop-filter: blur(20px);
28
+ border-radius: 20px;
29
+ padding: 3rem 2.5rem;
30
+ text-align: center;
31
+ max-width: 500px;
32
+ width: 90%;
33
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
34
+ border: 1px solid rgba(255, 255, 255, 0.2);
35
+ }
36
+
37
+ .logo {
38
+ width: 80px;
39
+ height: 80px;
40
+ background: linear-gradient(135deg, #e53e3e, #c53030);
41
+ border-radius: 20px;
42
+ margin: 0 auto 2rem;
43
+ display: flex;
44
+ align-items: center;
45
+ justify-content: center;
46
+ font-size: 2rem;
47
+ font-weight: bold;
48
+ color: white;
49
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
50
+ }
51
+
52
+ h1 {
53
+ font-size: 2.2rem;
54
+ margin-bottom: 1rem;
55
+ background: linear-gradient(135deg, #e53e3e, #c53030);
56
+ -webkit-background-clip: text;
57
+ -webkit-text-fill-color: transparent;
58
+ background-clip: text;
59
+ font-weight: 700;
60
+ letter-spacing: -0.5px;
61
+ }
62
+
63
+ p {
64
+ font-size: 1.1rem;
65
+ color: #666;
66
+ margin-bottom: 2rem;
67
+ opacity: 0.8;
68
+ }
69
+
70
+ .status {
71
+ display: inline-flex;
72
+ align-items: center;
73
+ gap: 0.5rem;
74
+ background: #e8f5e8;
75
+ color: #2e7d2e;
76
+ padding: 0.6rem 1.2rem;
77
+ border-radius: 50px;
78
+ font-size: 0.9rem;
79
+ font-weight: 500;
80
+ border: 1px solid #c3e6c3;
81
+ }
82
+
83
+ .status-dot {
84
+ width: 8px;
85
+ height: 8px;
86
+ background: #4ade80;
87
+ border-radius: 50%;
88
+ animation: pulse 2s infinite;
89
+ }
90
+
91
+ @keyframes pulse {
92
+ 0%, 100% { opacity: 1; }
93
+ 50% { opacity: 0.5; }
94
+ }
95
+
96
+ .footer {
97
+ margin-top: 2rem;
98
+ font-size: 0.85rem;
99
+ color: #999;
100
+ opacity: 0.7;
101
+ }
102
+
103
+ @media (max-width: 480px) {
104
+ .container {
105
+ padding: 2rem 1.5rem;
106
+ margin: 1rem;
107
+ }
108
+
109
+ h1 {
110
+ font-size: 1.8rem;
111
+ }
112
+
113
+ p {
114
+ font-size: 1rem;
115
+ }
116
+ }
117
+ </style>
118
+ </head>
119
+ <body>
120
+ <div class="container">
121
+ <div class="logo">S</div>
122
+ <h1>Welcome to Shoplazza</h1>
123
+ <p>Development server is running smoothly</p>
124
+ <div class="status">
125
+ <div class="status-dot"></div>
126
+ Server Active
127
+ </div>
128
+ <div class="footer">
129
+ Ready to build amazing apps
130
+ </div>
131
+ </div>
132
+ </body>
133
+ </html>
@@ -0,0 +1,47 @@
1
+ const path = require('path');
2
+ const { build } = require('vite');
3
+ const { vitePluginAddExtensionId } = require('./plugins/vite-plugin-add-extension-id');
4
+ const { vitePluginTransformExtensionHtml } = require('./plugins/vite-plugin-transform-extension-html');
5
+
6
+ async function buildCheckout(extension) {
7
+ // build checkout extension
8
+ const extensionDir = path.dirname(extension.configPath);
9
+
10
+ // entry file
11
+ const entryFile = path.join(extensionDir, 'src', 'index.js');
12
+
13
+ // outputDir
14
+ const outputDir = path.join(extensionDir, '..', '..', 'app-deploy');
15
+
16
+ const pageEntry = { [extension.extension_name]: entryFile };
17
+
18
+ const res = await build({
19
+ plugins: [vitePluginTransformExtensionHtml(), vitePluginAddExtensionId(extension.extension_name)],
20
+ root: path.dirname(entryFile),
21
+ build: {
22
+ minify: false,
23
+ emptyOutDir: false,
24
+ copyPublicDir: false,
25
+ rollupOptions: {
26
+ input: pageEntry,
27
+ output: {
28
+ entryFileNames: '[name].[hash].js',
29
+ chunkFileNames: '[name].[hash].js',
30
+ assetFileNames: '[name].[hash].[ext]',
31
+ compact: true,
32
+ inlineDynamicImports: false,
33
+ dir: outputDir
34
+ }
35
+ }
36
+ }
37
+ });
38
+
39
+ // 返回打包后的文件路径
40
+ const outputFile = path.join(outputDir, res.output[0].fileName);
41
+
42
+ return outputFile;
43
+ }
44
+
45
+ module.exports = {
46
+ buildCheckout
47
+ };
@@ -0,0 +1,57 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+ const crypto = require('crypto');
4
+ const { getLibPath } = require('../../bin');
5
+ const { exec } = require('child_process');
6
+ const { promisify } = require('util');
7
+
8
+ const execPromise = promisify(exec);
9
+
10
+ function hashFileStream(filePath, algorithm = 'md5') {
11
+ return new Promise((resolve, reject) => {
12
+ const hash = crypto.createHash(algorithm);
13
+ const stream = fs.createReadStream(filePath);
14
+
15
+ stream.on('data', (chunk) => hash.update(chunk));
16
+ stream.on('end', () => resolve(hash.digest('hex')));
17
+ stream.on('error', reject);
18
+ });
19
+ }
20
+
21
+ async function buildFunction(extension) {
22
+ // build function extension
23
+ const extensionDir = path.dirname(extension.configPath);
24
+
25
+ // entry file
26
+ const entryFile = path.join(extensionDir, 'src', 'index.js');
27
+
28
+ // entry file hash
29
+ const entryFileHash = await hashFileStream(entryFile);
30
+
31
+ // output file
32
+ const outputFile = path.join(
33
+ extensionDir,
34
+ '..',
35
+ '..',
36
+ 'app-deploy',
37
+ `${extension.extension_name}.${entryFileHash}.wasm`
38
+ );
39
+
40
+ // javy path
41
+ const javyPath = getLibPath('javy');
42
+
43
+ try {
44
+ const { stderr } = await execPromise(`"${javyPath}" build "${entryFile}" -o "${outputFile}"`);
45
+ if (stderr) {
46
+ throw new Error(stderr);
47
+ }
48
+ } catch (err) {
49
+ throw err;
50
+ }
51
+
52
+ return outputFile;
53
+ }
54
+
55
+ module.exports = {
56
+ buildFunction
57
+ };