@adep/web-container 0.2.2 → 0.2.3
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/bundle-export.d.ts +75 -0
- package/dist/function-fetch-proxy.d.ts +8 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +229 -46
- package/dist/sim/runtime.d.ts +1 -0
- package/dist/vite-dev.d.ts +6 -0
- package/dist/web-container.esm.js +229 -46
- package/dist/web-container.iife.js +229 -46
- package/package.json +3 -3
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 产物导出(PV-006):把浏览器内 dev server 的 **blob 文档**序列化成一份可经真实 HTTP 源服务的
|
|
3
|
+
* 自包含产物(`{ html, modules, paths }`),交给平台预览子域。
|
|
4
|
+
*
|
|
5
|
+
* 背景(为什么需要它):Web IDE 的前端预览由 `@adep/web-container` 的 `vite` 命令在浏览器内编译,
|
|
6
|
+
* 产物是一个 **`blob:` 入口文档**——模块之间的 import 说明符在编译期被改写成彼此的 blob URL。
|
|
7
|
+
* blob URL 只在「创建它的那个标签页」里有效:复制出去在别的浏览器打开必然失效,且 blob 是不可
|
|
8
|
+
* 分层 scheme,`history.pushState('/about')` 这类单页路由调用直接抛错。于是「像 CodeSandbox 那样
|
|
9
|
+
* 复制预览链接」这件事在 blob 形态下不可能做到。
|
|
10
|
+
*
|
|
11
|
+
* 本模块的做法:**顺着 blob 引用抓一份完整模块图,改写成内容寻址的 HTTP 路径**——
|
|
12
|
+
* 1. 读入口文档 → 找出其中所有 `blob:` 引用(`<script src="blob:…">`);
|
|
13
|
+
* 2. 逐个读取这些 blob 的源码,再从中找出它们的 `blob:` 引用(编译期已改写为依赖的 URL)→ 递归,
|
|
14
|
+
* 直到闭包;
|
|
15
|
+
* 3. 每份源码按内容哈希命名(同内容必然同路径 → 跨版本复用、天然去重、变更必然换名),
|
|
16
|
+
* 路径形如 `/_adep/m/<hash>.js`(与服务端 `PREVIEW_MODULE_URL_RE` 白名单一致);
|
|
17
|
+
* 4. 把所有源码里的 blob URL 替换成对应路径,得到自包含产物。
|
|
18
|
+
*
|
|
19
|
+
* 增量发布(`known`):每次发布都把**完整闭包读一遍**(blob 就在页内存里,读取代价近乎为零),
|
|
20
|
+
* 但只把「服务端还没有的模块」放进产物体 `modules`——内容寻址下「同路径 ⇔ 同内容」,判据就是
|
|
21
|
+
* 「路径是否出现在上一次发布的映射值里」。产物另带 `paths`(本次入口所需的**全部**模块路径,
|
|
22
|
+
* 含未重复上传的那些),服务端据此写清单并清理幽灵模块:**清单必须完整**,漏报会把仍在用的
|
|
23
|
+
* 模块当幽灵删掉(页面白屏)。
|
|
24
|
+
*
|
|
25
|
+
* 为什么不干脆「已知模块连读都跳过」(更省一层):跳读会丢掉该模块的依赖边,除非调用方保证
|
|
26
|
+
* `known` 是**完整闭包**——把正确性押在调用方的记性上,一旦传了半张表就静默产出缺模块的产物,
|
|
27
|
+
* 白屏且极难诊断。读一遍是最便宜的保险;真正贵的是网络上传,而那部分已经省掉了。
|
|
28
|
+
*
|
|
29
|
+
* 产物随后由 `POST /api/v1/projects/:id/preview` 落到项目桶,由预览子域服务:真实 HTTP 源 →
|
|
30
|
+
* 跨浏览器可用、SPA 路由(history 模式)原生可用、`/api/*` 走平台网关执行项目函数。
|
|
31
|
+
*
|
|
32
|
+
* 边界:只搬运**文本模块**。图片等二进制资产若出现在 VFS 里,dev server 本就不会把它编译成模块
|
|
33
|
+
* (解析链只认 JS/TS/CSS/JSON/Vue),故不在此处理;`readText` 拿不到内容时响亮抛错,不静默产出残缺产物。
|
|
34
|
+
*/
|
|
35
|
+
/** 模块 URL 前缀(与 `server/domains/preview/store.ts` 的 `PREVIEW_MODULE_URL_PREFIX` 对齐)。 */
|
|
36
|
+
export declare const BUNDLE_MODULE_PATH_PREFIX = "/_adep/m/";
|
|
37
|
+
export interface BundleExportOptions {
|
|
38
|
+
/** 入口文档 URL(dev server 产出的 blob 文档)。 */
|
|
39
|
+
documentUrl: string;
|
|
40
|
+
/**
|
|
41
|
+
* 读取 URL 文本(默认 `fetch(url).then(r => r.text())`)。
|
|
42
|
+
* 注入缝:Node 单测用假实现,不依赖真实 blob 存储。
|
|
43
|
+
*/
|
|
44
|
+
readText?: (url: string) => Promise<string>;
|
|
45
|
+
/** 模块 URL 前缀(默认 `/_adep/m/`)。 */
|
|
46
|
+
modulePathPrefix?: string;
|
|
47
|
+
/** 内容 → 模块名(不含扩展名)。默认 sha256 前 16 位 hex;注入以便单测确定化。 */
|
|
48
|
+
moduleName?: (code: string) => string | Promise<string>;
|
|
49
|
+
/** 注入到入口文档 `</body>` 前的脚本(IDE 侧用于错误中继等;默认不注入)。 */
|
|
50
|
+
appendScript?: string;
|
|
51
|
+
/**
|
|
52
|
+
* 上一次发布的 blob URL → 模块路径映射(即上次 `ExportedBundle.urls`)。
|
|
53
|
+
* 用途只有一个:**跳过重复上传**(值集 = 服务端已有的路径集)。传空/不传 = 全量发布。
|
|
54
|
+
*/
|
|
55
|
+
known?: ReadonlyMap<string, string>;
|
|
56
|
+
}
|
|
57
|
+
export interface ExportedBundle {
|
|
58
|
+
/** 自包含入口文档(blob 引用已改写为模块路径)。 */
|
|
59
|
+
html: string;
|
|
60
|
+
/** **需要上传**的模块(路径 → 源码):已在上一次发布里存在的模块不再出现在这里。 */
|
|
61
|
+
modules: Record<string, string>;
|
|
62
|
+
/** 本次入口文档所需的**全部**模块路径(含本次未重复上传的;服务端据此写清单与清理幽灵)。 */
|
|
63
|
+
paths: string[];
|
|
64
|
+
/** 本次闭包的 blob URL → 路径映射;调用方保存,下次作为 `known` 传入。 */
|
|
65
|
+
urls: Map<string, string>;
|
|
66
|
+
}
|
|
67
|
+
/** 默认模块名:sha256 前 16 位 hex(浏览器均有 `crypto.subtle`)。 */
|
|
68
|
+
export declare function defaultModuleName(code: string): Promise<string>;
|
|
69
|
+
/** 找出文本里的全部 blob 引用(去重、保序)。 */
|
|
70
|
+
export declare function findBlobRefs(text: string): string[];
|
|
71
|
+
/**
|
|
72
|
+
* 导出产物:入口文档 + 递归闭包内全部模块(内容寻址命名 + 引用改写)。
|
|
73
|
+
* 读不到某个 blob(已被撤销 / 网络异常)→ 抛错(残缺产物比失败更糟:页面白屏且难以诊断)。
|
|
74
|
+
*/
|
|
75
|
+
export declare function exportBundle(options: BundleExportOptions): Promise<ExportedBundle>;
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* fetch 拦截器,把 `/api/*` 请求经 `window.parent.postMessage` 转发到 IDE 主线程;主线程
|
|
7
7
|
* 用 `OfflineRunner` 执行**当前项目的云函数草稿**(改函数即生效,因为每次都实时取草稿),
|
|
8
8
|
* 再把执行结果 postMessage 回 iframe。与 origin 无关:blob URL 与异源 Nodebox 隧道都能用。
|
|
9
|
+
* 新窗口打开预览(window.open)时 window.parent === window,回退到 window.opener.postMessage,
|
|
10
|
+
* 主线程经 event.source 单播回响应——新窗口与 iframe 共用同一套协议。
|
|
9
11
|
*
|
|
10
12
|
* 消息协议(两侧逐字对齐;改任一侧必须同步另一侧——见 web-project-template.ts 的
|
|
11
13
|
* web/vite.config.ts 模板里那份逐字复制的 FN_PROXY_SCRIPT):
|
|
@@ -37,6 +39,12 @@ export interface OfflineRunner {
|
|
|
37
39
|
* `new URL('/api/x', 'blob:…')` 抛 TypeError → 拦截器退化成原生 fetch 报
|
|
38
40
|
* "Failed to parse URL"。首次解析失败时用 `window.location.origin`(blob 继承创建者
|
|
39
41
|
* origin)兜底再解析一次。
|
|
42
|
+
*
|
|
43
|
+
* 新窗口预览(2026-09-12):`window.open` 打开的新标签页 `window.parent === window`,
|
|
44
|
+
* 回退到 `window.opener.postMessage` 转发 /api/* 请求(不能用 noopener,否则 opener 为 null)。
|
|
45
|
+
* blob URL SPA 路由:patch `history.pushState/replaceState`,对相对路径直接吞掉(blob 不可
|
|
46
|
+
* 分层,原生 pushState 解析相对路径抛 TypeError),`location.pathname` 覆盖为当前路由路径,
|
|
47
|
+
* sessionStorage 持久化,刷新后恢复。
|
|
40
48
|
*/
|
|
41
49
|
export declare const FN_FETCH_INTERCEPTOR_SCRIPT: string;
|
|
42
50
|
/** IDE 主线程侧的消息处理器类型:直接挂到 `window.addEventListener('message', handler)`。 */
|
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export type { ModuleCache, RequireOptions } from './module-loader';
|
|
|
31
31
|
export { createViteServer, scanEsmSpecifiers, ViteDevError } from './vite-dev';
|
|
32
32
|
export type { SfcCompiler, ViteServer, ViteServerOptions, ViteCommandPort, ViteCommandOutcome, EsmSpecifierHit, } from './vite-dev';
|
|
33
33
|
export * as posix from './path';
|
|
34
|
+
export { exportBundle, findBlobRefs, defaultModuleName, BUNDLE_MODULE_PATH_PREFIX, } from './bundle-export';
|
|
35
|
+
export type { BundleExportOptions, ExportedBundle } from './bundle-export';
|
|
34
36
|
export { snapshotVfs, restoreVfs, entriesToTree, createMemoryFsBackend, createIndexedDbFsBackend, pickDefaultFsBackend, createFsPersistence, createMemoryKvStorage, createCommandHistory, } from './persistence';
|
|
35
37
|
export type { FsPersistEntry, FsPersistenceBackend, FsPersistence, FsPersistenceOptions, CommandHistory, KvStorage, } from './persistence';
|
|
36
38
|
export { SHELL_COMMANDS, EXTRA_COMMANDS, COMPLETABLE_COMMANDS, parseCompletionContext, completeWithListing, completeShellInput, completeContainerInput, applyCompletion, candidateLabel, } from './completion';
|
package/dist/index.js
CHANGED
|
@@ -679,8 +679,8 @@ var init_strip_types = __esm({
|
|
|
679
679
|
return back1 === "," && this.tokenAt(3) === "import";
|
|
680
680
|
}
|
|
681
681
|
/**
|
|
682
|
-
* `(` 是否为参数列表(声明位):`function f(`、类体里的方法 `m(
|
|
683
|
-
*
|
|
682
|
+
* `(` 是否为参数列表(声明位):`function f(`、`async` 箭头 `async (`、类体里的方法 `m(`、
|
|
683
|
+
* 箭头 `=> (`,以及出现在 `(` `,` `=` `>` `:` `[` `{` `}` `;` 之后(如 `map((v: T) => v)` 的内层括号)。
|
|
684
684
|
* 调用位 `foo(` 与控制流 `if (` `for (` 都不算。
|
|
685
685
|
* 误判成声明位也不足以擦掉什么:`:` 还要过 `isAnnotationColon` 的「名字前驱必须是
|
|
686
686
|
* `(` `,` `{` `;` 修饰符」这一关,而三元表达式的中间段前面必然是 `?`。
|
|
@@ -689,6 +689,7 @@ var init_strip_types = __esm({
|
|
|
689
689
|
if (this.afterFunctionKeyword) return true;
|
|
690
690
|
if (this.tokenAt(2) === "function" && IDENT_START.test(this.tokenAt(1))) return true;
|
|
691
691
|
if (this.afterArrow) return true;
|
|
692
|
+
if (this.tokenAt(1) === "async") return true;
|
|
692
693
|
if (this.tokenAt(1) === "return") return true;
|
|
693
694
|
if (this.inClassBody() && IDENT_PART.test(this.prevSignificantChar(at - 1))) return true;
|
|
694
695
|
const prev = this.prevSignificantChar(at - 1);
|
|
@@ -1205,7 +1206,8 @@ var init_function_fetch_proxy = __esm({
|
|
|
1205
1206
|
' if (u.pathname.indexOf("/api/") !== 0 && u.pathname !== "/api") {',
|
|
1206
1207
|
" return ORIGINAL_FETCH(input, init);",
|
|
1207
1208
|
" }",
|
|
1208
|
-
"
|
|
1209
|
+
" var bridgeWindow = window.parent !== window ? window.parent : window.opener;",
|
|
1210
|
+
" if (!bridgeWindow) return ORIGINAL_FETCH(input, init);",
|
|
1209
1211
|
' method = (method || "GET").toUpperCase();',
|
|
1210
1212
|
" var target = u.pathname + u.search;",
|
|
1211
1213
|
" return readBody(input, init).then(function (textBody) {",
|
|
@@ -1216,10 +1218,75 @@ var init_function_fetch_proxy = __esm({
|
|
|
1216
1218
|
` resolve(new Response('{"error":{"code":"FN_PROXY_TIMEOUT","message":"\\u4e91\\u51fd\\u6570\\u6267\\u884c\\u8d85\\u65f6"}}', { status: 504, headers: { "content-type": "application/json" } }));`,
|
|
1217
1219
|
" }, TIMEOUT_MS);",
|
|
1218
1220
|
" pending[id] = { resolve: resolve, timer: timer };",
|
|
1219
|
-
'
|
|
1221
|
+
' bridgeWindow.postMessage({ __adepFnRequest: true, id: id, method: method, url: target, headers: headers, body: textBody }, "*");',
|
|
1220
1222
|
" });",
|
|
1221
1223
|
" });",
|
|
1222
1224
|
" };",
|
|
1225
|
+
"",
|
|
1226
|
+
" /* \u2014\u2014 blob URL SPA \u8DEF\u7531\u652F\u6301 \u2014\u2014",
|
|
1227
|
+
" blob: \u662F\u4E0D\u53EF\u5206\u5C42 scheme\uFF0Chistory.pushState('/about') \u5185\u90E8\u89E3\u6790\u76F8\u5BF9\u8DEF\u5F84\u4F1A\u629B",
|
|
1228
|
+
" TypeError \u2192 SPA history \u6A21\u5F0F\u8DEF\u7531\u5D29\u6E83\u3002patch pushState/replaceState\uFF0C\u5BF9\u76F8\u5BF9",
|
|
1229
|
+
" \u8DEF\u5F84\u76F4\u63A5\u541E\u6389\uFF08\u4E0D\u5BFC\u822A\u3001\u4E0D\u629B\u9519\uFF09\uFF0C\u8DEF\u7531\u5E93\u5185\u90E8\u72B6\u6001\u81EA\u884C\u66F4\u65B0\uFF1Blocation.pathname",
|
|
1230
|
+
" \u8986\u76D6\u4E3A\u5F53\u524D\u8DEF\u7531\u8DEF\u5F84\uFF0C\u4F9B\u8DEF\u7531\u5E93\u521D\u59CB\u5316\u65F6\u8BFB\u53D6\u3002sessionStorage \u6301\u4E45\u5316\uFF0C\u5237\u65B0\u540E\u6062\u590D\u3002 */",
|
|
1231
|
+
' if (window.location.protocol === "blob:") {',
|
|
1232
|
+
' var SPA_KEY = "__adep_spa_path";',
|
|
1233
|
+
' var spaPath = "/";',
|
|
1234
|
+
" try {",
|
|
1235
|
+
" var _saved = window.sessionStorage.getItem(SPA_KEY);",
|
|
1236
|
+
" if (_saved) spaPath = _saved;",
|
|
1237
|
+
" } catch (e) {}",
|
|
1238
|
+
" try {",
|
|
1239
|
+
' Object.defineProperty(window.location, "pathname", {',
|
|
1240
|
+
" get: function () { return spaPath; },",
|
|
1241
|
+
" configurable: true",
|
|
1242
|
+
" });",
|
|
1243
|
+
" } catch (e) {}",
|
|
1244
|
+
" var _origPush = window.history.pushState.bind(window.history);",
|
|
1245
|
+
" var _origReplace = window.history.replaceState.bind(window.history);",
|
|
1246
|
+
" function _isRelative(url) {",
|
|
1247
|
+
' if (!url || typeof url !== "string") return false;',
|
|
1248
|
+
' if (url.charAt(0) === "#") return false;',
|
|
1249
|
+
" if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url)) return false;",
|
|
1250
|
+
" return true;",
|
|
1251
|
+
" }",
|
|
1252
|
+
" function _resolvePath(url) {",
|
|
1253
|
+
' var qi = url.indexOf("?");',
|
|
1254
|
+
' var hi = url.indexOf("#");',
|
|
1255
|
+
" var end = url.length;",
|
|
1256
|
+
" if (qi !== -1) end = qi;",
|
|
1257
|
+
" if (hi !== -1 && hi < end) end = hi;",
|
|
1258
|
+
" var p = url.slice(0, end);",
|
|
1259
|
+
' if (p.charAt(0) !== "/") {',
|
|
1260
|
+
' var base = spaPath.slice(0, spaPath.lastIndexOf("/") + 1);',
|
|
1261
|
+
" p = base + p;",
|
|
1262
|
+
" }",
|
|
1263
|
+
' var parts = p.split("/");',
|
|
1264
|
+
" var out = [];",
|
|
1265
|
+
" for (var i = 0; i < parts.length; i++) {",
|
|
1266
|
+
" var seg = parts[i];",
|
|
1267
|
+
' if (seg === "" || seg === ".") continue;',
|
|
1268
|
+
' if (seg === "..") { out.pop(); continue; }',
|
|
1269
|
+
" out.push(seg);",
|
|
1270
|
+
" }",
|
|
1271
|
+
' return "/" + out.join("/");',
|
|
1272
|
+
" }",
|
|
1273
|
+
" window.history.pushState = function (state, title, url) {",
|
|
1274
|
+
" if (_isRelative(url)) {",
|
|
1275
|
+
" spaPath = _resolvePath(url);",
|
|
1276
|
+
" try { window.sessionStorage.setItem(SPA_KEY, spaPath); } catch (e) {}",
|
|
1277
|
+
" return;",
|
|
1278
|
+
" }",
|
|
1279
|
+
" return _origPush(state, title, url);",
|
|
1280
|
+
" };",
|
|
1281
|
+
" window.history.replaceState = function (state, title, url) {",
|
|
1282
|
+
" if (_isRelative(url)) {",
|
|
1283
|
+
" spaPath = _resolvePath(url);",
|
|
1284
|
+
" try { window.sessionStorage.setItem(SPA_KEY, spaPath); } catch (e) {}",
|
|
1285
|
+
" return;",
|
|
1286
|
+
" }",
|
|
1287
|
+
" return _origReplace(state, title, url);",
|
|
1288
|
+
" };",
|
|
1289
|
+
" }",
|
|
1223
1290
|
"})();"
|
|
1224
1291
|
].join("\n");
|
|
1225
1292
|
return src;
|
|
@@ -1574,7 +1641,10 @@ function createViteServer(vfs, options = {}) {
|
|
|
1574
1641
|
lastDoc = doc;
|
|
1575
1642
|
if (currentUrl !== "") revokeObjectURL(currentUrl);
|
|
1576
1643
|
currentUrl = createObjectURL(new Blob([doc], { type: "text/html" }));
|
|
1577
|
-
if (changed)
|
|
1644
|
+
if (changed) {
|
|
1645
|
+
broadcast(currentUrl);
|
|
1646
|
+
options.onDocumentChange?.(currentUrl);
|
|
1647
|
+
}
|
|
1578
1648
|
}
|
|
1579
1649
|
const server = {
|
|
1580
1650
|
get url() {
|
|
@@ -3611,7 +3681,11 @@ function bootstrap(options) {
|
|
|
3611
3681
|
const { createViteServer: createViteServer2 } = await Promise.resolve().then(() => (init_vite_dev(), vite_dev_exports));
|
|
3612
3682
|
const server = createViteServer2(vfs, {
|
|
3613
3683
|
root,
|
|
3614
|
-
...options.viteCompiler === void 0 ? {} : { sfcCompiler: options.viteCompiler }
|
|
3684
|
+
...options.viteCompiler === void 0 ? {} : { sfcCompiler: options.viteCompiler },
|
|
3685
|
+
// PV-006:内容重建 → 以 `serverready`(update: true)事件上报新文档 URL,宿主导出预览产物。
|
|
3686
|
+
onDocumentChange: (url) => {
|
|
3687
|
+
emit("serverready", { port: server.port, url, kind: "vite", root: server.root, update: true });
|
|
3688
|
+
}
|
|
3615
3689
|
});
|
|
3616
3690
|
await server.ready;
|
|
3617
3691
|
viteServer = server;
|
|
@@ -3776,6 +3850,92 @@ init_node_resolve();
|
|
|
3776
3850
|
init_vite_dev();
|
|
3777
3851
|
init_path();
|
|
3778
3852
|
|
|
3853
|
+
// packages/web-container/src/bundle-export.ts
|
|
3854
|
+
var BUNDLE_MODULE_PATH_PREFIX = "/_adep/m/";
|
|
3855
|
+
var BLOB_REF_RE = /blob:[^\s"'`<>()\\]+/g;
|
|
3856
|
+
function fnv1a(code) {
|
|
3857
|
+
let hash = 2166136261;
|
|
3858
|
+
for (let i = 0; i < code.length; i++) {
|
|
3859
|
+
hash ^= code.charCodeAt(i);
|
|
3860
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
3861
|
+
}
|
|
3862
|
+
return `${hash.toString(16).padStart(8, "0")}${code.length.toString(16)}`;
|
|
3863
|
+
}
|
|
3864
|
+
async function defaultModuleName(code) {
|
|
3865
|
+
const subtle = globalThis.crypto?.subtle;
|
|
3866
|
+
if (subtle === void 0) return fnv1a(code);
|
|
3867
|
+
const digest = await subtle.digest("SHA-256", new TextEncoder().encode(code));
|
|
3868
|
+
let hex = "";
|
|
3869
|
+
for (const byte of new Uint8Array(digest).slice(0, 8)) {
|
|
3870
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
3871
|
+
}
|
|
3872
|
+
return hex;
|
|
3873
|
+
}
|
|
3874
|
+
function findBlobRefs(text) {
|
|
3875
|
+
const found = text.match(BLOB_REF_RE);
|
|
3876
|
+
return found === null ? [] : [...new Set(found)];
|
|
3877
|
+
}
|
|
3878
|
+
function rewriteRefs(text, urls) {
|
|
3879
|
+
let out = text;
|
|
3880
|
+
for (const [blobUrl, path] of urls) {
|
|
3881
|
+
if (out.includes(blobUrl)) out = out.split(blobUrl).join(path);
|
|
3882
|
+
}
|
|
3883
|
+
return out;
|
|
3884
|
+
}
|
|
3885
|
+
function injectScript(html, script) {
|
|
3886
|
+
return /<\/body>/i.test(html) ? html.replace(/<\/body>/i, `${script}</body>`) : html + script;
|
|
3887
|
+
}
|
|
3888
|
+
async function exportBundle(options) {
|
|
3889
|
+
const readText = options.readText ?? ((url) => fetch(url).then((response) => response.text()));
|
|
3890
|
+
const nameOf = options.moduleName ?? defaultModuleName;
|
|
3891
|
+
const prefix = options.modulePathPrefix ?? BUNDLE_MODULE_PATH_PREFIX;
|
|
3892
|
+
const uploaded = new Set(options.known === void 0 ? [] : [...options.known.values()]);
|
|
3893
|
+
const urls = /* @__PURE__ */ new Map();
|
|
3894
|
+
const sources = [];
|
|
3895
|
+
const readOrThrow = async (url) => {
|
|
3896
|
+
try {
|
|
3897
|
+
return await readText(url);
|
|
3898
|
+
} catch (error) {
|
|
3899
|
+
throw new Error(
|
|
3900
|
+
`\u9884\u89C8\u4EA7\u7269\u5BFC\u51FA\u5931\u8D25\uFF1A\u8BFB\u4E0D\u5230\u6A21\u5757 ${url}\uFF08${error instanceof Error ? error.message : String(error)}\uFF09`,
|
|
3901
|
+
{ cause: error }
|
|
3902
|
+
);
|
|
3903
|
+
}
|
|
3904
|
+
};
|
|
3905
|
+
const html = await readOrThrow(options.documentUrl);
|
|
3906
|
+
const loadLayer = async (layer) => {
|
|
3907
|
+
const batch = [...new Set(layer)].filter((url) => !urls.has(url));
|
|
3908
|
+
if (batch.length === 0) return;
|
|
3909
|
+
const loaded = await Promise.all(
|
|
3910
|
+
batch.map(async (url) => {
|
|
3911
|
+
const code = await readOrThrow(url);
|
|
3912
|
+
return { url, code, path: `${prefix}${await nameOf(code)}.js` };
|
|
3913
|
+
})
|
|
3914
|
+
);
|
|
3915
|
+
const next = [];
|
|
3916
|
+
for (const item of loaded) {
|
|
3917
|
+
urls.set(item.url, item.path);
|
|
3918
|
+
sources.push({ url: item.url, code: item.code });
|
|
3919
|
+
next.push(...findBlobRefs(item.code));
|
|
3920
|
+
}
|
|
3921
|
+
await loadLayer(next);
|
|
3922
|
+
};
|
|
3923
|
+
await loadLayer(findBlobRefs(html));
|
|
3924
|
+
const modules = {};
|
|
3925
|
+
for (const { url, code } of sources) {
|
|
3926
|
+
const path = urls.get(url);
|
|
3927
|
+
if (uploaded.has(path)) continue;
|
|
3928
|
+
modules[path] = rewriteRefs(code, urls);
|
|
3929
|
+
}
|
|
3930
|
+
const rewrittenHtml = rewriteRefs(html, urls);
|
|
3931
|
+
return {
|
|
3932
|
+
html: options.appendScript === void 0 ? rewrittenHtml : injectScript(rewrittenHtml, options.appendScript),
|
|
3933
|
+
modules,
|
|
3934
|
+
paths: [...new Set(urls.values())].toSorted(),
|
|
3935
|
+
urls
|
|
3936
|
+
};
|
|
3937
|
+
}
|
|
3938
|
+
|
|
3779
3939
|
// packages/web-container/src/persistence.ts
|
|
3780
3940
|
function cloneEntries(entries) {
|
|
3781
3941
|
return entries.map(
|
|
@@ -5537,6 +5697,17 @@ var SimSqlEngine = class {
|
|
|
5537
5697
|
}
|
|
5538
5698
|
return { changes: 0 };
|
|
5539
5699
|
}
|
|
5700
|
+
if (/^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?/i.test(stmt)) {
|
|
5701
|
+
const idx = /^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))\s+ON\s+((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))/i.exec(
|
|
5702
|
+
stmt
|
|
5703
|
+
);
|
|
5704
|
+
if (idx === null) throw new SimDbError("DB_UNSAFE_OP", `\u65E0\u6CD5\u89E3\u6790 CREATE INDEX \u8BED\u53E5`);
|
|
5705
|
+
const tableName = ident(idx[3]);
|
|
5706
|
+
if (this.tables[tableName] === void 0) {
|
|
5707
|
+
throw new SimDbError("DB_UNSAFE_OP", `CREATE INDEX \u76EE\u6807\u8868\u4E0D\u5B58\u5728\uFF1A${tableName}`);
|
|
5708
|
+
}
|
|
5709
|
+
return { changes: 0 };
|
|
5710
|
+
}
|
|
5540
5711
|
if (/^INSERT\s+INTO/i.test(stmt)) return this.execInsert(stmt, params);
|
|
5541
5712
|
if (/^UPDATE\s+/i.test(stmt)) return this.execUpdate(stmt, params);
|
|
5542
5713
|
if (/^DELETE\s+FROM/i.test(stmt)) return this.execDelete(stmt, params);
|
|
@@ -5781,6 +5952,19 @@ function assertSafeStoragePath(path) {
|
|
|
5781
5952
|
}
|
|
5782
5953
|
return path;
|
|
5783
5954
|
}
|
|
5955
|
+
function assertStoragePrefix(prefix) {
|
|
5956
|
+
if (prefix !== void 0 && prefix !== "") {
|
|
5957
|
+
const segments = prefix.split("/");
|
|
5958
|
+
if (prefix.startsWith("/") || /[\\]/.test(prefix) || segments.some((segment) => segment === "..")) {
|
|
5959
|
+
throw new StorageError(
|
|
5960
|
+
400,
|
|
5961
|
+
STORAGE_CODES.invalidPath,
|
|
5962
|
+
`\u975E\u6CD5\u524D\u7F00 "${prefix}"\uFF08\u7981\u6B62 / \u5F00\u5934\u3001\u53CD\u659C\u6760\u6216 .. \u7A7F\u8D8A\uFF09`
|
|
5963
|
+
);
|
|
5964
|
+
}
|
|
5965
|
+
}
|
|
5966
|
+
return prefix ?? "";
|
|
5967
|
+
}
|
|
5784
5968
|
|
|
5785
5969
|
// packages/runtime/src/storage/hmac-sha256.ts
|
|
5786
5970
|
var K = new Uint32Array([
|
|
@@ -6100,16 +6284,7 @@ function createBrowserStorageDriver(options) {
|
|
|
6100
6284
|
await saveBucket(bucket);
|
|
6101
6285
|
},
|
|
6102
6286
|
async list(_projectId, prefix) {
|
|
6103
|
-
|
|
6104
|
-
const segments = prefix.split("/");
|
|
6105
|
-
if (prefix.startsWith("/") || /[\\]/.test(prefix) || segments.some((segment) => segment === "..")) {
|
|
6106
|
-
throw new StorageError(
|
|
6107
|
-
400,
|
|
6108
|
-
STORAGE_CODES.invalidPath,
|
|
6109
|
-
`\u975E\u6CD5\u524D\u7F00 "${prefix}"\uFF08\u7981\u6B62 / \u5F00\u5934\u3001\u53CD\u659C\u6760\u6216 .. \u7A7F\u8D8A\uFF09`
|
|
6110
|
-
);
|
|
6111
|
-
}
|
|
6112
|
-
}
|
|
6287
|
+
assertStoragePrefix(prefix);
|
|
6113
6288
|
const bucket = await loadBucket();
|
|
6114
6289
|
return Object.values(bucket).filter((object) => prefix === void 0 || object.path.startsWith(prefix)).map(toMeta).toSorted((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
6115
6290
|
},
|
|
@@ -6496,6 +6671,19 @@ function createCloudContainer() {
|
|
|
6496
6671
|
};
|
|
6497
6672
|
}
|
|
6498
6673
|
|
|
6674
|
+
// packages/runtime/src/functions/runtime/rpc-wire.ts
|
|
6675
|
+
function wireRpcResponses(port, pending) {
|
|
6676
|
+
port.on?.((message) => {
|
|
6677
|
+
const reply = message;
|
|
6678
|
+
if (reply?.type !== "rpc-response" || typeof reply.id !== "number") return;
|
|
6679
|
+
const entry = pending.get(reply.id);
|
|
6680
|
+
if (entry === void 0) return;
|
|
6681
|
+
pending.delete(reply.id);
|
|
6682
|
+
if (reply.ok === true) entry.resolve(reply.result);
|
|
6683
|
+
else entry.reject(new Error(reply.error ?? "RPC \u80FD\u529B\u8C03\u7528\u5931\u8D25"));
|
|
6684
|
+
});
|
|
6685
|
+
}
|
|
6686
|
+
|
|
6499
6687
|
// packages/web-container/src/sim/ctx.ts
|
|
6500
6688
|
function createLocalPort(handler) {
|
|
6501
6689
|
const listeners = [];
|
|
@@ -6525,17 +6713,6 @@ function createLocalPort(handler) {
|
|
|
6525
6713
|
}
|
|
6526
6714
|
};
|
|
6527
6715
|
}
|
|
6528
|
-
function wireRpcResponses(port, pending) {
|
|
6529
|
-
port.on((message) => {
|
|
6530
|
-
const reply = message;
|
|
6531
|
-
if (reply?.type !== "rpc-response" || typeof reply.id !== "number") return;
|
|
6532
|
-
const entry = pending.get(reply.id);
|
|
6533
|
-
if (entry === void 0) return;
|
|
6534
|
-
pending.delete(reply.id);
|
|
6535
|
-
if (reply.ok === true) entry.resolve(reply.result);
|
|
6536
|
-
else entry.reject(new Error(reply.error ?? "RPC \u80FD\u529B\u8C03\u7528\u5931\u8D25"));
|
|
6537
|
-
});
|
|
6538
|
-
}
|
|
6539
6716
|
function createFlatRpcClient(handler) {
|
|
6540
6717
|
return new Proxy(
|
|
6541
6718
|
{},
|
|
@@ -6749,6 +6926,27 @@ function createOfflineFunctionRunner(options = {}) {
|
|
|
6749
6926
|
return { run };
|
|
6750
6927
|
}
|
|
6751
6928
|
|
|
6929
|
+
// packages/runtime/src/sim/merge.ts
|
|
6930
|
+
function mergeBundles(bundles) {
|
|
6931
|
+
const capabilities = [];
|
|
6932
|
+
const byName = /* @__PURE__ */ new Map();
|
|
6933
|
+
for (const bundle of bundles) {
|
|
6934
|
+
for (const cap of bundle.capabilities) {
|
|
6935
|
+
const existing = byName.get(cap.name);
|
|
6936
|
+
if (existing !== void 0) capabilities.splice(capabilities.indexOf(existing), 1);
|
|
6937
|
+
byName.set(cap.name, cap);
|
|
6938
|
+
capabilities.push(cap);
|
|
6939
|
+
}
|
|
6940
|
+
}
|
|
6941
|
+
const rpcHandlers = {};
|
|
6942
|
+
for (const bundle of bundles) {
|
|
6943
|
+
for (const [name, handler] of Object.entries(bundle.rpcHandlers)) {
|
|
6944
|
+
rpcHandlers[name] = handler;
|
|
6945
|
+
}
|
|
6946
|
+
}
|
|
6947
|
+
return { capabilities, rpcHandlers };
|
|
6948
|
+
}
|
|
6949
|
+
|
|
6752
6950
|
// packages/runtime/src/database/sdk/kv.ts
|
|
6753
6951
|
var KV_TABLE = "_adep_kv";
|
|
6754
6952
|
var KV_RPC = {
|
|
@@ -6850,25 +7048,6 @@ function createKvCapability(driver) {
|
|
|
6850
7048
|
}
|
|
6851
7049
|
|
|
6852
7050
|
// packages/web-container/src/sim/runtime.ts
|
|
6853
|
-
function mergeBundles(bundles) {
|
|
6854
|
-
const capabilities = [];
|
|
6855
|
-
const byName = /* @__PURE__ */ new Map();
|
|
6856
|
-
for (const bundle of bundles) {
|
|
6857
|
-
for (const cap of bundle.capabilities) {
|
|
6858
|
-
const existing = byName.get(cap.name);
|
|
6859
|
-
if (existing !== void 0) capabilities.splice(capabilities.indexOf(existing), 1);
|
|
6860
|
-
byName.set(cap.name, cap);
|
|
6861
|
-
capabilities.push(cap);
|
|
6862
|
-
}
|
|
6863
|
-
}
|
|
6864
|
-
const rpcHandlers = {};
|
|
6865
|
-
for (const bundle of bundles) {
|
|
6866
|
-
for (const [name, handler] of Object.entries(bundle.rpcHandlers)) {
|
|
6867
|
-
rpcHandlers[name] = handler;
|
|
6868
|
-
}
|
|
6869
|
-
}
|
|
6870
|
-
return { capabilities, rpcHandlers };
|
|
6871
|
-
}
|
|
6872
7051
|
async function createBrowserSimRuntime(options) {
|
|
6873
7052
|
const kv = options.kv ?? pickDefaultSimKv();
|
|
6874
7053
|
const db = createBrowserSimDb({ projectId: options.projectId, kv });
|
|
@@ -6900,6 +7079,7 @@ async function createBrowserSimRuntime(options) {
|
|
|
6900
7079
|
// packages/web-container/src/index.ts
|
|
6901
7080
|
init_function_fetch_proxy();
|
|
6902
7081
|
export {
|
|
7082
|
+
BUNDLE_MODULE_PATH_PREFIX,
|
|
6903
7083
|
COMPLETABLE_COMMANDS,
|
|
6904
7084
|
DEFAULT_MAX_INSTALL_DEPTH,
|
|
6905
7085
|
EXTRA_COMMANDS,
|
|
@@ -6945,12 +7125,15 @@ export {
|
|
|
6945
7125
|
createViteServer,
|
|
6946
7126
|
createWorkerRuntime,
|
|
6947
7127
|
deepEqual,
|
|
7128
|
+
defaultModuleName,
|
|
6948
7129
|
displayPath,
|
|
6949
7130
|
entriesToTree,
|
|
6950
7131
|
evaluateModuleSource,
|
|
6951
7132
|
evaluateSource,
|
|
6952
7133
|
exampleTestSource,
|
|
6953
7134
|
expect as expectAssertion,
|
|
7135
|
+
exportBundle,
|
|
7136
|
+
findBlobRefs,
|
|
6954
7137
|
formatTestReport,
|
|
6955
7138
|
formatValue,
|
|
6956
7139
|
fullName,
|
package/dist/sim/runtime.d.ts
CHANGED
|
@@ -44,5 +44,6 @@ export interface BrowserSimRuntime {
|
|
|
44
44
|
/** 释放资源(当前无长持句柄;与 CLI createSimRuntime.dispose 对称保留)。 */
|
|
45
45
|
dispose(): Promise<void>;
|
|
46
46
|
}
|
|
47
|
+
/** 顺序合并多份 bundle:capabilities 去重(同名后者覆盖),rpcHandlers 浅合并。 */
|
|
47
48
|
/** 装配浏览器模拟运行时(async:db 引擎要先把 IndexedDB 里的快照灌回来)。 */
|
|
48
49
|
export declare function createBrowserSimRuntime(options: BrowserSimRuntimeOptions): Promise<BrowserSimRuntime>;
|
package/dist/vite-dev.d.ts
CHANGED
|
@@ -48,6 +48,12 @@ export interface ViteServerOptions {
|
|
|
48
48
|
/** blob URL 工厂(Node 单测注入假实现)。 */
|
|
49
49
|
createObjectURL?: (blob: Blob) => string;
|
|
50
50
|
revokeObjectURL?: (url: string) => void;
|
|
51
|
+
/**
|
|
52
|
+
* 文档重建且**内容有变化**时回调(PV-006):blob HMR 之外的第二条出口——
|
|
53
|
+
* IDE 侧据此把新产物导出并发布到预览子域(真实 HTTP 源)。
|
|
54
|
+
* 首建不回调(宿主由 `serverready` 事件拿首屏 URL,两处都回调会长重复发布)。
|
|
55
|
+
*/
|
|
56
|
+
onDocumentChange?: (url: string) => void;
|
|
51
57
|
}
|
|
52
58
|
/** dev server 句柄:url / root 对外只读,`touch` 驱动 HMR,`stop` 释放资源。 */
|
|
53
59
|
export interface ViteServer {
|
|
@@ -679,8 +679,8 @@ var init_strip_types = __esm({
|
|
|
679
679
|
return back1 === "," && this.tokenAt(3) === "import";
|
|
680
680
|
}
|
|
681
681
|
/**
|
|
682
|
-
* `(` 是否为参数列表(声明位):`function f(`、类体里的方法 `m(
|
|
683
|
-
*
|
|
682
|
+
* `(` 是否为参数列表(声明位):`function f(`、`async` 箭头 `async (`、类体里的方法 `m(`、
|
|
683
|
+
* 箭头 `=> (`,以及出现在 `(` `,` `=` `>` `:` `[` `{` `}` `;` 之后(如 `map((v: T) => v)` 的内层括号)。
|
|
684
684
|
* 调用位 `foo(` 与控制流 `if (` `for (` 都不算。
|
|
685
685
|
* 误判成声明位也不足以擦掉什么:`:` 还要过 `isAnnotationColon` 的「名字前驱必须是
|
|
686
686
|
* `(` `,` `{` `;` 修饰符」这一关,而三元表达式的中间段前面必然是 `?`。
|
|
@@ -689,6 +689,7 @@ var init_strip_types = __esm({
|
|
|
689
689
|
if (this.afterFunctionKeyword) return true;
|
|
690
690
|
if (this.tokenAt(2) === "function" && IDENT_START.test(this.tokenAt(1))) return true;
|
|
691
691
|
if (this.afterArrow) return true;
|
|
692
|
+
if (this.tokenAt(1) === "async") return true;
|
|
692
693
|
if (this.tokenAt(1) === "return") return true;
|
|
693
694
|
if (this.inClassBody() && IDENT_PART.test(this.prevSignificantChar(at - 1))) return true;
|
|
694
695
|
const prev = this.prevSignificantChar(at - 1);
|
|
@@ -1205,7 +1206,8 @@ var init_function_fetch_proxy = __esm({
|
|
|
1205
1206
|
' if (u.pathname.indexOf("/api/") !== 0 && u.pathname !== "/api") {',
|
|
1206
1207
|
" return ORIGINAL_FETCH(input, init);",
|
|
1207
1208
|
" }",
|
|
1208
|
-
"
|
|
1209
|
+
" var bridgeWindow = window.parent !== window ? window.parent : window.opener;",
|
|
1210
|
+
" if (!bridgeWindow) return ORIGINAL_FETCH(input, init);",
|
|
1209
1211
|
' method = (method || "GET").toUpperCase();',
|
|
1210
1212
|
" var target = u.pathname + u.search;",
|
|
1211
1213
|
" return readBody(input, init).then(function (textBody) {",
|
|
@@ -1216,10 +1218,75 @@ var init_function_fetch_proxy = __esm({
|
|
|
1216
1218
|
` resolve(new Response('{"error":{"code":"FN_PROXY_TIMEOUT","message":"\\u4e91\\u51fd\\u6570\\u6267\\u884c\\u8d85\\u65f6"}}', { status: 504, headers: { "content-type": "application/json" } }));`,
|
|
1217
1219
|
" }, TIMEOUT_MS);",
|
|
1218
1220
|
" pending[id] = { resolve: resolve, timer: timer };",
|
|
1219
|
-
'
|
|
1221
|
+
' bridgeWindow.postMessage({ __adepFnRequest: true, id: id, method: method, url: target, headers: headers, body: textBody }, "*");',
|
|
1220
1222
|
" });",
|
|
1221
1223
|
" });",
|
|
1222
1224
|
" };",
|
|
1225
|
+
"",
|
|
1226
|
+
" /* \u2014\u2014 blob URL SPA \u8DEF\u7531\u652F\u6301 \u2014\u2014",
|
|
1227
|
+
" blob: \u662F\u4E0D\u53EF\u5206\u5C42 scheme\uFF0Chistory.pushState('/about') \u5185\u90E8\u89E3\u6790\u76F8\u5BF9\u8DEF\u5F84\u4F1A\u629B",
|
|
1228
|
+
" TypeError \u2192 SPA history \u6A21\u5F0F\u8DEF\u7531\u5D29\u6E83\u3002patch pushState/replaceState\uFF0C\u5BF9\u76F8\u5BF9",
|
|
1229
|
+
" \u8DEF\u5F84\u76F4\u63A5\u541E\u6389\uFF08\u4E0D\u5BFC\u822A\u3001\u4E0D\u629B\u9519\uFF09\uFF0C\u8DEF\u7531\u5E93\u5185\u90E8\u72B6\u6001\u81EA\u884C\u66F4\u65B0\uFF1Blocation.pathname",
|
|
1230
|
+
" \u8986\u76D6\u4E3A\u5F53\u524D\u8DEF\u7531\u8DEF\u5F84\uFF0C\u4F9B\u8DEF\u7531\u5E93\u521D\u59CB\u5316\u65F6\u8BFB\u53D6\u3002sessionStorage \u6301\u4E45\u5316\uFF0C\u5237\u65B0\u540E\u6062\u590D\u3002 */",
|
|
1231
|
+
' if (window.location.protocol === "blob:") {',
|
|
1232
|
+
' var SPA_KEY = "__adep_spa_path";',
|
|
1233
|
+
' var spaPath = "/";',
|
|
1234
|
+
" try {",
|
|
1235
|
+
" var _saved = window.sessionStorage.getItem(SPA_KEY);",
|
|
1236
|
+
" if (_saved) spaPath = _saved;",
|
|
1237
|
+
" } catch (e) {}",
|
|
1238
|
+
" try {",
|
|
1239
|
+
' Object.defineProperty(window.location, "pathname", {',
|
|
1240
|
+
" get: function () { return spaPath; },",
|
|
1241
|
+
" configurable: true",
|
|
1242
|
+
" });",
|
|
1243
|
+
" } catch (e) {}",
|
|
1244
|
+
" var _origPush = window.history.pushState.bind(window.history);",
|
|
1245
|
+
" var _origReplace = window.history.replaceState.bind(window.history);",
|
|
1246
|
+
" function _isRelative(url) {",
|
|
1247
|
+
' if (!url || typeof url !== "string") return false;',
|
|
1248
|
+
' if (url.charAt(0) === "#") return false;',
|
|
1249
|
+
" if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url)) return false;",
|
|
1250
|
+
" return true;",
|
|
1251
|
+
" }",
|
|
1252
|
+
" function _resolvePath(url) {",
|
|
1253
|
+
' var qi = url.indexOf("?");',
|
|
1254
|
+
' var hi = url.indexOf("#");',
|
|
1255
|
+
" var end = url.length;",
|
|
1256
|
+
" if (qi !== -1) end = qi;",
|
|
1257
|
+
" if (hi !== -1 && hi < end) end = hi;",
|
|
1258
|
+
" var p = url.slice(0, end);",
|
|
1259
|
+
' if (p.charAt(0) !== "/") {',
|
|
1260
|
+
' var base = spaPath.slice(0, spaPath.lastIndexOf("/") + 1);',
|
|
1261
|
+
" p = base + p;",
|
|
1262
|
+
" }",
|
|
1263
|
+
' var parts = p.split("/");',
|
|
1264
|
+
" var out = [];",
|
|
1265
|
+
" for (var i = 0; i < parts.length; i++) {",
|
|
1266
|
+
" var seg = parts[i];",
|
|
1267
|
+
' if (seg === "" || seg === ".") continue;',
|
|
1268
|
+
' if (seg === "..") { out.pop(); continue; }',
|
|
1269
|
+
" out.push(seg);",
|
|
1270
|
+
" }",
|
|
1271
|
+
' return "/" + out.join("/");',
|
|
1272
|
+
" }",
|
|
1273
|
+
" window.history.pushState = function (state, title, url) {",
|
|
1274
|
+
" if (_isRelative(url)) {",
|
|
1275
|
+
" spaPath = _resolvePath(url);",
|
|
1276
|
+
" try { window.sessionStorage.setItem(SPA_KEY, spaPath); } catch (e) {}",
|
|
1277
|
+
" return;",
|
|
1278
|
+
" }",
|
|
1279
|
+
" return _origPush(state, title, url);",
|
|
1280
|
+
" };",
|
|
1281
|
+
" window.history.replaceState = function (state, title, url) {",
|
|
1282
|
+
" if (_isRelative(url)) {",
|
|
1283
|
+
" spaPath = _resolvePath(url);",
|
|
1284
|
+
" try { window.sessionStorage.setItem(SPA_KEY, spaPath); } catch (e) {}",
|
|
1285
|
+
" return;",
|
|
1286
|
+
" }",
|
|
1287
|
+
" return _origReplace(state, title, url);",
|
|
1288
|
+
" };",
|
|
1289
|
+
" }",
|
|
1223
1290
|
"})();"
|
|
1224
1291
|
].join("\n");
|
|
1225
1292
|
return src;
|
|
@@ -1574,7 +1641,10 @@ function createViteServer(vfs, options = {}) {
|
|
|
1574
1641
|
lastDoc = doc;
|
|
1575
1642
|
if (currentUrl !== "") revokeObjectURL(currentUrl);
|
|
1576
1643
|
currentUrl = createObjectURL(new Blob([doc], { type: "text/html" }));
|
|
1577
|
-
if (changed)
|
|
1644
|
+
if (changed) {
|
|
1645
|
+
broadcast(currentUrl);
|
|
1646
|
+
options.onDocumentChange?.(currentUrl);
|
|
1647
|
+
}
|
|
1578
1648
|
}
|
|
1579
1649
|
const server = {
|
|
1580
1650
|
get url() {
|
|
@@ -3611,7 +3681,11 @@ function bootstrap(options) {
|
|
|
3611
3681
|
const { createViteServer: createViteServer2 } = await Promise.resolve().then(() => (init_vite_dev(), vite_dev_exports));
|
|
3612
3682
|
const server = createViteServer2(vfs, {
|
|
3613
3683
|
root,
|
|
3614
|
-
...options.viteCompiler === void 0 ? {} : { sfcCompiler: options.viteCompiler }
|
|
3684
|
+
...options.viteCompiler === void 0 ? {} : { sfcCompiler: options.viteCompiler },
|
|
3685
|
+
// PV-006:内容重建 → 以 `serverready`(update: true)事件上报新文档 URL,宿主导出预览产物。
|
|
3686
|
+
onDocumentChange: (url) => {
|
|
3687
|
+
emit("serverready", { port: server.port, url, kind: "vite", root: server.root, update: true });
|
|
3688
|
+
}
|
|
3615
3689
|
});
|
|
3616
3690
|
await server.ready;
|
|
3617
3691
|
viteServer = server;
|
|
@@ -3776,6 +3850,92 @@ init_node_resolve();
|
|
|
3776
3850
|
init_vite_dev();
|
|
3777
3851
|
init_path();
|
|
3778
3852
|
|
|
3853
|
+
// src/bundle-export.ts
|
|
3854
|
+
var BUNDLE_MODULE_PATH_PREFIX = "/_adep/m/";
|
|
3855
|
+
var BLOB_REF_RE = /blob:[^\s"'`<>()\\]+/g;
|
|
3856
|
+
function fnv1a(code) {
|
|
3857
|
+
let hash = 2166136261;
|
|
3858
|
+
for (let i = 0; i < code.length; i++) {
|
|
3859
|
+
hash ^= code.charCodeAt(i);
|
|
3860
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
3861
|
+
}
|
|
3862
|
+
return `${hash.toString(16).padStart(8, "0")}${code.length.toString(16)}`;
|
|
3863
|
+
}
|
|
3864
|
+
async function defaultModuleName(code) {
|
|
3865
|
+
const subtle = globalThis.crypto?.subtle;
|
|
3866
|
+
if (subtle === void 0) return fnv1a(code);
|
|
3867
|
+
const digest = await subtle.digest("SHA-256", new TextEncoder().encode(code));
|
|
3868
|
+
let hex = "";
|
|
3869
|
+
for (const byte of new Uint8Array(digest).slice(0, 8)) {
|
|
3870
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
3871
|
+
}
|
|
3872
|
+
return hex;
|
|
3873
|
+
}
|
|
3874
|
+
function findBlobRefs(text) {
|
|
3875
|
+
const found = text.match(BLOB_REF_RE);
|
|
3876
|
+
return found === null ? [] : [...new Set(found)];
|
|
3877
|
+
}
|
|
3878
|
+
function rewriteRefs(text, urls) {
|
|
3879
|
+
let out = text;
|
|
3880
|
+
for (const [blobUrl, path] of urls) {
|
|
3881
|
+
if (out.includes(blobUrl)) out = out.split(blobUrl).join(path);
|
|
3882
|
+
}
|
|
3883
|
+
return out;
|
|
3884
|
+
}
|
|
3885
|
+
function injectScript(html, script) {
|
|
3886
|
+
return /<\/body>/i.test(html) ? html.replace(/<\/body>/i, `${script}</body>`) : html + script;
|
|
3887
|
+
}
|
|
3888
|
+
async function exportBundle(options) {
|
|
3889
|
+
const readText = options.readText ?? ((url) => fetch(url).then((response) => response.text()));
|
|
3890
|
+
const nameOf = options.moduleName ?? defaultModuleName;
|
|
3891
|
+
const prefix = options.modulePathPrefix ?? BUNDLE_MODULE_PATH_PREFIX;
|
|
3892
|
+
const uploaded = new Set(options.known === void 0 ? [] : [...options.known.values()]);
|
|
3893
|
+
const urls = /* @__PURE__ */ new Map();
|
|
3894
|
+
const sources = [];
|
|
3895
|
+
const readOrThrow = async (url) => {
|
|
3896
|
+
try {
|
|
3897
|
+
return await readText(url);
|
|
3898
|
+
} catch (error) {
|
|
3899
|
+
throw new Error(
|
|
3900
|
+
`\u9884\u89C8\u4EA7\u7269\u5BFC\u51FA\u5931\u8D25\uFF1A\u8BFB\u4E0D\u5230\u6A21\u5757 ${url}\uFF08${error instanceof Error ? error.message : String(error)}\uFF09`,
|
|
3901
|
+
{ cause: error }
|
|
3902
|
+
);
|
|
3903
|
+
}
|
|
3904
|
+
};
|
|
3905
|
+
const html = await readOrThrow(options.documentUrl);
|
|
3906
|
+
const loadLayer = async (layer) => {
|
|
3907
|
+
const batch = [...new Set(layer)].filter((url) => !urls.has(url));
|
|
3908
|
+
if (batch.length === 0) return;
|
|
3909
|
+
const loaded = await Promise.all(
|
|
3910
|
+
batch.map(async (url) => {
|
|
3911
|
+
const code = await readOrThrow(url);
|
|
3912
|
+
return { url, code, path: `${prefix}${await nameOf(code)}.js` };
|
|
3913
|
+
})
|
|
3914
|
+
);
|
|
3915
|
+
const next = [];
|
|
3916
|
+
for (const item of loaded) {
|
|
3917
|
+
urls.set(item.url, item.path);
|
|
3918
|
+
sources.push({ url: item.url, code: item.code });
|
|
3919
|
+
next.push(...findBlobRefs(item.code));
|
|
3920
|
+
}
|
|
3921
|
+
await loadLayer(next);
|
|
3922
|
+
};
|
|
3923
|
+
await loadLayer(findBlobRefs(html));
|
|
3924
|
+
const modules = {};
|
|
3925
|
+
for (const { url, code } of sources) {
|
|
3926
|
+
const path = urls.get(url);
|
|
3927
|
+
if (uploaded.has(path)) continue;
|
|
3928
|
+
modules[path] = rewriteRefs(code, urls);
|
|
3929
|
+
}
|
|
3930
|
+
const rewrittenHtml = rewriteRefs(html, urls);
|
|
3931
|
+
return {
|
|
3932
|
+
html: options.appendScript === void 0 ? rewrittenHtml : injectScript(rewrittenHtml, options.appendScript),
|
|
3933
|
+
modules,
|
|
3934
|
+
paths: [...new Set(urls.values())].toSorted(),
|
|
3935
|
+
urls
|
|
3936
|
+
};
|
|
3937
|
+
}
|
|
3938
|
+
|
|
3779
3939
|
// src/persistence.ts
|
|
3780
3940
|
function cloneEntries(entries) {
|
|
3781
3941
|
return entries.map(
|
|
@@ -5537,6 +5697,17 @@ var SimSqlEngine = class {
|
|
|
5537
5697
|
}
|
|
5538
5698
|
return { changes: 0 };
|
|
5539
5699
|
}
|
|
5700
|
+
if (/^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?/i.test(stmt)) {
|
|
5701
|
+
const idx = /^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))\s+ON\s+((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))/i.exec(
|
|
5702
|
+
stmt
|
|
5703
|
+
);
|
|
5704
|
+
if (idx === null) throw new SimDbError("DB_UNSAFE_OP", `\u65E0\u6CD5\u89E3\u6790 CREATE INDEX \u8BED\u53E5`);
|
|
5705
|
+
const tableName = ident(idx[3]);
|
|
5706
|
+
if (this.tables[tableName] === void 0) {
|
|
5707
|
+
throw new SimDbError("DB_UNSAFE_OP", `CREATE INDEX \u76EE\u6807\u8868\u4E0D\u5B58\u5728\uFF1A${tableName}`);
|
|
5708
|
+
}
|
|
5709
|
+
return { changes: 0 };
|
|
5710
|
+
}
|
|
5540
5711
|
if (/^INSERT\s+INTO/i.test(stmt)) return this.execInsert(stmt, params);
|
|
5541
5712
|
if (/^UPDATE\s+/i.test(stmt)) return this.execUpdate(stmt, params);
|
|
5542
5713
|
if (/^DELETE\s+FROM/i.test(stmt)) return this.execDelete(stmt, params);
|
|
@@ -5781,6 +5952,19 @@ function assertSafeStoragePath(path) {
|
|
|
5781
5952
|
}
|
|
5782
5953
|
return path;
|
|
5783
5954
|
}
|
|
5955
|
+
function assertStoragePrefix(prefix) {
|
|
5956
|
+
if (prefix !== void 0 && prefix !== "") {
|
|
5957
|
+
const segments = prefix.split("/");
|
|
5958
|
+
if (prefix.startsWith("/") || /[\\]/.test(prefix) || segments.some((segment) => segment === "..")) {
|
|
5959
|
+
throw new StorageError(
|
|
5960
|
+
400,
|
|
5961
|
+
STORAGE_CODES.invalidPath,
|
|
5962
|
+
`\u975E\u6CD5\u524D\u7F00 "${prefix}"\uFF08\u7981\u6B62 / \u5F00\u5934\u3001\u53CD\u659C\u6760\u6216 .. \u7A7F\u8D8A\uFF09`
|
|
5963
|
+
);
|
|
5964
|
+
}
|
|
5965
|
+
}
|
|
5966
|
+
return prefix ?? "";
|
|
5967
|
+
}
|
|
5784
5968
|
|
|
5785
5969
|
// ../runtime/src/storage/hmac-sha256.ts
|
|
5786
5970
|
var K = new Uint32Array([
|
|
@@ -6100,16 +6284,7 @@ function createBrowserStorageDriver(options) {
|
|
|
6100
6284
|
await saveBucket(bucket);
|
|
6101
6285
|
},
|
|
6102
6286
|
async list(_projectId, prefix) {
|
|
6103
|
-
|
|
6104
|
-
const segments = prefix.split("/");
|
|
6105
|
-
if (prefix.startsWith("/") || /[\\]/.test(prefix) || segments.some((segment) => segment === "..")) {
|
|
6106
|
-
throw new StorageError(
|
|
6107
|
-
400,
|
|
6108
|
-
STORAGE_CODES.invalidPath,
|
|
6109
|
-
`\u975E\u6CD5\u524D\u7F00 "${prefix}"\uFF08\u7981\u6B62 / \u5F00\u5934\u3001\u53CD\u659C\u6760\u6216 .. \u7A7F\u8D8A\uFF09`
|
|
6110
|
-
);
|
|
6111
|
-
}
|
|
6112
|
-
}
|
|
6287
|
+
assertStoragePrefix(prefix);
|
|
6113
6288
|
const bucket = await loadBucket();
|
|
6114
6289
|
return Object.values(bucket).filter((object) => prefix === void 0 || object.path.startsWith(prefix)).map(toMeta).toSorted((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
6115
6290
|
},
|
|
@@ -6496,6 +6671,19 @@ function createCloudContainer() {
|
|
|
6496
6671
|
};
|
|
6497
6672
|
}
|
|
6498
6673
|
|
|
6674
|
+
// ../runtime/src/functions/runtime/rpc-wire.ts
|
|
6675
|
+
function wireRpcResponses(port, pending) {
|
|
6676
|
+
port.on?.((message) => {
|
|
6677
|
+
const reply = message;
|
|
6678
|
+
if (reply?.type !== "rpc-response" || typeof reply.id !== "number") return;
|
|
6679
|
+
const entry = pending.get(reply.id);
|
|
6680
|
+
if (entry === void 0) return;
|
|
6681
|
+
pending.delete(reply.id);
|
|
6682
|
+
if (reply.ok === true) entry.resolve(reply.result);
|
|
6683
|
+
else entry.reject(new Error(reply.error ?? "RPC \u80FD\u529B\u8C03\u7528\u5931\u8D25"));
|
|
6684
|
+
});
|
|
6685
|
+
}
|
|
6686
|
+
|
|
6499
6687
|
// src/sim/ctx.ts
|
|
6500
6688
|
function createLocalPort(handler) {
|
|
6501
6689
|
const listeners = [];
|
|
@@ -6525,17 +6713,6 @@ function createLocalPort(handler) {
|
|
|
6525
6713
|
}
|
|
6526
6714
|
};
|
|
6527
6715
|
}
|
|
6528
|
-
function wireRpcResponses(port, pending) {
|
|
6529
|
-
port.on((message) => {
|
|
6530
|
-
const reply = message;
|
|
6531
|
-
if (reply?.type !== "rpc-response" || typeof reply.id !== "number") return;
|
|
6532
|
-
const entry = pending.get(reply.id);
|
|
6533
|
-
if (entry === void 0) return;
|
|
6534
|
-
pending.delete(reply.id);
|
|
6535
|
-
if (reply.ok === true) entry.resolve(reply.result);
|
|
6536
|
-
else entry.reject(new Error(reply.error ?? "RPC \u80FD\u529B\u8C03\u7528\u5931\u8D25"));
|
|
6537
|
-
});
|
|
6538
|
-
}
|
|
6539
6716
|
function createFlatRpcClient(handler) {
|
|
6540
6717
|
return new Proxy(
|
|
6541
6718
|
{},
|
|
@@ -6749,6 +6926,27 @@ function createOfflineFunctionRunner(options = {}) {
|
|
|
6749
6926
|
return { run };
|
|
6750
6927
|
}
|
|
6751
6928
|
|
|
6929
|
+
// ../runtime/src/sim/merge.ts
|
|
6930
|
+
function mergeBundles(bundles) {
|
|
6931
|
+
const capabilities = [];
|
|
6932
|
+
const byName = /* @__PURE__ */ new Map();
|
|
6933
|
+
for (const bundle of bundles) {
|
|
6934
|
+
for (const cap of bundle.capabilities) {
|
|
6935
|
+
const existing = byName.get(cap.name);
|
|
6936
|
+
if (existing !== void 0) capabilities.splice(capabilities.indexOf(existing), 1);
|
|
6937
|
+
byName.set(cap.name, cap);
|
|
6938
|
+
capabilities.push(cap);
|
|
6939
|
+
}
|
|
6940
|
+
}
|
|
6941
|
+
const rpcHandlers = {};
|
|
6942
|
+
for (const bundle of bundles) {
|
|
6943
|
+
for (const [name, handler] of Object.entries(bundle.rpcHandlers)) {
|
|
6944
|
+
rpcHandlers[name] = handler;
|
|
6945
|
+
}
|
|
6946
|
+
}
|
|
6947
|
+
return { capabilities, rpcHandlers };
|
|
6948
|
+
}
|
|
6949
|
+
|
|
6752
6950
|
// ../runtime/src/database/sdk/kv.ts
|
|
6753
6951
|
var KV_TABLE = "_adep_kv";
|
|
6754
6952
|
var KV_RPC = {
|
|
@@ -6850,25 +7048,6 @@ function createKvCapability(driver) {
|
|
|
6850
7048
|
}
|
|
6851
7049
|
|
|
6852
7050
|
// src/sim/runtime.ts
|
|
6853
|
-
function mergeBundles(bundles) {
|
|
6854
|
-
const capabilities = [];
|
|
6855
|
-
const byName = /* @__PURE__ */ new Map();
|
|
6856
|
-
for (const bundle of bundles) {
|
|
6857
|
-
for (const cap of bundle.capabilities) {
|
|
6858
|
-
const existing = byName.get(cap.name);
|
|
6859
|
-
if (existing !== void 0) capabilities.splice(capabilities.indexOf(existing), 1);
|
|
6860
|
-
byName.set(cap.name, cap);
|
|
6861
|
-
capabilities.push(cap);
|
|
6862
|
-
}
|
|
6863
|
-
}
|
|
6864
|
-
const rpcHandlers = {};
|
|
6865
|
-
for (const bundle of bundles) {
|
|
6866
|
-
for (const [name, handler] of Object.entries(bundle.rpcHandlers)) {
|
|
6867
|
-
rpcHandlers[name] = handler;
|
|
6868
|
-
}
|
|
6869
|
-
}
|
|
6870
|
-
return { capabilities, rpcHandlers };
|
|
6871
|
-
}
|
|
6872
7051
|
async function createBrowserSimRuntime(options) {
|
|
6873
7052
|
const kv = options.kv ?? pickDefaultSimKv();
|
|
6874
7053
|
const db = createBrowserSimDb({ projectId: options.projectId, kv });
|
|
@@ -6900,6 +7079,7 @@ async function createBrowserSimRuntime(options) {
|
|
|
6900
7079
|
// src/index.ts
|
|
6901
7080
|
init_function_fetch_proxy();
|
|
6902
7081
|
export {
|
|
7082
|
+
BUNDLE_MODULE_PATH_PREFIX,
|
|
6903
7083
|
COMPLETABLE_COMMANDS,
|
|
6904
7084
|
DEFAULT_MAX_INSTALL_DEPTH,
|
|
6905
7085
|
EXTRA_COMMANDS,
|
|
@@ -6945,12 +7125,15 @@ export {
|
|
|
6945
7125
|
createViteServer,
|
|
6946
7126
|
createWorkerRuntime,
|
|
6947
7127
|
deepEqual,
|
|
7128
|
+
defaultModuleName,
|
|
6948
7129
|
displayPath,
|
|
6949
7130
|
entriesToTree,
|
|
6950
7131
|
evaluateModuleSource,
|
|
6951
7132
|
evaluateSource,
|
|
6952
7133
|
exampleTestSource,
|
|
6953
7134
|
expect as expectAssertion,
|
|
7135
|
+
exportBundle,
|
|
7136
|
+
findBlobRefs,
|
|
6954
7137
|
formatTestReport,
|
|
6955
7138
|
formatValue,
|
|
6956
7139
|
fullName,
|
|
@@ -692,8 +692,8 @@ var AdepWebContainer = (() => {
|
|
|
692
692
|
return back1 === "," && this.tokenAt(3) === "import";
|
|
693
693
|
}
|
|
694
694
|
/**
|
|
695
|
-
* `(` 是否为参数列表(声明位):`function f(`、类体里的方法 `m(
|
|
696
|
-
*
|
|
695
|
+
* `(` 是否为参数列表(声明位):`function f(`、`async` 箭头 `async (`、类体里的方法 `m(`、
|
|
696
|
+
* 箭头 `=> (`,以及出现在 `(` `,` `=` `>` `:` `[` `{` `}` `;` 之后(如 `map((v: T) => v)` 的内层括号)。
|
|
697
697
|
* 调用位 `foo(` 与控制流 `if (` `for (` 都不算。
|
|
698
698
|
* 误判成声明位也不足以擦掉什么:`:` 还要过 `isAnnotationColon` 的「名字前驱必须是
|
|
699
699
|
* `(` `,` `{` `;` 修饰符」这一关,而三元表达式的中间段前面必然是 `?`。
|
|
@@ -702,6 +702,7 @@ var AdepWebContainer = (() => {
|
|
|
702
702
|
if (this.afterFunctionKeyword) return true;
|
|
703
703
|
if (this.tokenAt(2) === "function" && IDENT_START.test(this.tokenAt(1))) return true;
|
|
704
704
|
if (this.afterArrow) return true;
|
|
705
|
+
if (this.tokenAt(1) === "async") return true;
|
|
705
706
|
if (this.tokenAt(1) === "return") return true;
|
|
706
707
|
if (this.inClassBody() && IDENT_PART.test(this.prevSignificantChar(at - 1))) return true;
|
|
707
708
|
const prev = this.prevSignificantChar(at - 1);
|
|
@@ -1218,7 +1219,8 @@ var AdepWebContainer = (() => {
|
|
|
1218
1219
|
' if (u.pathname.indexOf("/api/") !== 0 && u.pathname !== "/api") {',
|
|
1219
1220
|
" return ORIGINAL_FETCH(input, init);",
|
|
1220
1221
|
" }",
|
|
1221
|
-
"
|
|
1222
|
+
" var bridgeWindow = window.parent !== window ? window.parent : window.opener;",
|
|
1223
|
+
" if (!bridgeWindow) return ORIGINAL_FETCH(input, init);",
|
|
1222
1224
|
' method = (method || "GET").toUpperCase();',
|
|
1223
1225
|
" var target = u.pathname + u.search;",
|
|
1224
1226
|
" return readBody(input, init).then(function (textBody) {",
|
|
@@ -1229,10 +1231,75 @@ var AdepWebContainer = (() => {
|
|
|
1229
1231
|
` resolve(new Response('{"error":{"code":"FN_PROXY_TIMEOUT","message":"\\u4e91\\u51fd\\u6570\\u6267\\u884c\\u8d85\\u65f6"}}', { status: 504, headers: { "content-type": "application/json" } }));`,
|
|
1230
1232
|
" }, TIMEOUT_MS);",
|
|
1231
1233
|
" pending[id] = { resolve: resolve, timer: timer };",
|
|
1232
|
-
'
|
|
1234
|
+
' bridgeWindow.postMessage({ __adepFnRequest: true, id: id, method: method, url: target, headers: headers, body: textBody }, "*");',
|
|
1233
1235
|
" });",
|
|
1234
1236
|
" });",
|
|
1235
1237
|
" };",
|
|
1238
|
+
"",
|
|
1239
|
+
" /* \u2014\u2014 blob URL SPA \u8DEF\u7531\u652F\u6301 \u2014\u2014",
|
|
1240
|
+
" blob: \u662F\u4E0D\u53EF\u5206\u5C42 scheme\uFF0Chistory.pushState('/about') \u5185\u90E8\u89E3\u6790\u76F8\u5BF9\u8DEF\u5F84\u4F1A\u629B",
|
|
1241
|
+
" TypeError \u2192 SPA history \u6A21\u5F0F\u8DEF\u7531\u5D29\u6E83\u3002patch pushState/replaceState\uFF0C\u5BF9\u76F8\u5BF9",
|
|
1242
|
+
" \u8DEF\u5F84\u76F4\u63A5\u541E\u6389\uFF08\u4E0D\u5BFC\u822A\u3001\u4E0D\u629B\u9519\uFF09\uFF0C\u8DEF\u7531\u5E93\u5185\u90E8\u72B6\u6001\u81EA\u884C\u66F4\u65B0\uFF1Blocation.pathname",
|
|
1243
|
+
" \u8986\u76D6\u4E3A\u5F53\u524D\u8DEF\u7531\u8DEF\u5F84\uFF0C\u4F9B\u8DEF\u7531\u5E93\u521D\u59CB\u5316\u65F6\u8BFB\u53D6\u3002sessionStorage \u6301\u4E45\u5316\uFF0C\u5237\u65B0\u540E\u6062\u590D\u3002 */",
|
|
1244
|
+
' if (window.location.protocol === "blob:") {',
|
|
1245
|
+
' var SPA_KEY = "__adep_spa_path";',
|
|
1246
|
+
' var spaPath = "/";',
|
|
1247
|
+
" try {",
|
|
1248
|
+
" var _saved = window.sessionStorage.getItem(SPA_KEY);",
|
|
1249
|
+
" if (_saved) spaPath = _saved;",
|
|
1250
|
+
" } catch (e) {}",
|
|
1251
|
+
" try {",
|
|
1252
|
+
' Object.defineProperty(window.location, "pathname", {',
|
|
1253
|
+
" get: function () { return spaPath; },",
|
|
1254
|
+
" configurable: true",
|
|
1255
|
+
" });",
|
|
1256
|
+
" } catch (e) {}",
|
|
1257
|
+
" var _origPush = window.history.pushState.bind(window.history);",
|
|
1258
|
+
" var _origReplace = window.history.replaceState.bind(window.history);",
|
|
1259
|
+
" function _isRelative(url) {",
|
|
1260
|
+
' if (!url || typeof url !== "string") return false;',
|
|
1261
|
+
' if (url.charAt(0) === "#") return false;',
|
|
1262
|
+
" if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(url)) return false;",
|
|
1263
|
+
" return true;",
|
|
1264
|
+
" }",
|
|
1265
|
+
" function _resolvePath(url) {",
|
|
1266
|
+
' var qi = url.indexOf("?");',
|
|
1267
|
+
' var hi = url.indexOf("#");',
|
|
1268
|
+
" var end = url.length;",
|
|
1269
|
+
" if (qi !== -1) end = qi;",
|
|
1270
|
+
" if (hi !== -1 && hi < end) end = hi;",
|
|
1271
|
+
" var p = url.slice(0, end);",
|
|
1272
|
+
' if (p.charAt(0) !== "/") {',
|
|
1273
|
+
' var base = spaPath.slice(0, spaPath.lastIndexOf("/") + 1);',
|
|
1274
|
+
" p = base + p;",
|
|
1275
|
+
" }",
|
|
1276
|
+
' var parts = p.split("/");',
|
|
1277
|
+
" var out = [];",
|
|
1278
|
+
" for (var i = 0; i < parts.length; i++) {",
|
|
1279
|
+
" var seg = parts[i];",
|
|
1280
|
+
' if (seg === "" || seg === ".") continue;',
|
|
1281
|
+
' if (seg === "..") { out.pop(); continue; }',
|
|
1282
|
+
" out.push(seg);",
|
|
1283
|
+
" }",
|
|
1284
|
+
' return "/" + out.join("/");',
|
|
1285
|
+
" }",
|
|
1286
|
+
" window.history.pushState = function (state, title, url) {",
|
|
1287
|
+
" if (_isRelative(url)) {",
|
|
1288
|
+
" spaPath = _resolvePath(url);",
|
|
1289
|
+
" try { window.sessionStorage.setItem(SPA_KEY, spaPath); } catch (e) {}",
|
|
1290
|
+
" return;",
|
|
1291
|
+
" }",
|
|
1292
|
+
" return _origPush(state, title, url);",
|
|
1293
|
+
" };",
|
|
1294
|
+
" window.history.replaceState = function (state, title, url) {",
|
|
1295
|
+
" if (_isRelative(url)) {",
|
|
1296
|
+
" spaPath = _resolvePath(url);",
|
|
1297
|
+
" try { window.sessionStorage.setItem(SPA_KEY, spaPath); } catch (e) {}",
|
|
1298
|
+
" return;",
|
|
1299
|
+
" }",
|
|
1300
|
+
" return _origReplace(state, title, url);",
|
|
1301
|
+
" };",
|
|
1302
|
+
" }",
|
|
1236
1303
|
"})();"
|
|
1237
1304
|
].join("\n");
|
|
1238
1305
|
return src;
|
|
@@ -1587,7 +1654,10 @@ var AdepWebContainer = (() => {
|
|
|
1587
1654
|
lastDoc = doc;
|
|
1588
1655
|
if (currentUrl !== "") revokeObjectURL(currentUrl);
|
|
1589
1656
|
currentUrl = createObjectURL(new Blob([doc], { type: "text/html" }));
|
|
1590
|
-
if (changed)
|
|
1657
|
+
if (changed) {
|
|
1658
|
+
broadcast(currentUrl);
|
|
1659
|
+
options.onDocumentChange?.(currentUrl);
|
|
1660
|
+
}
|
|
1591
1661
|
}
|
|
1592
1662
|
const server = {
|
|
1593
1663
|
get url() {
|
|
@@ -1689,6 +1759,7 @@ var AdepWebContainer = (() => {
|
|
|
1689
1759
|
// src/index.ts
|
|
1690
1760
|
var index_exports = {};
|
|
1691
1761
|
__export(index_exports, {
|
|
1762
|
+
BUNDLE_MODULE_PATH_PREFIX: () => BUNDLE_MODULE_PATH_PREFIX,
|
|
1692
1763
|
COMPLETABLE_COMMANDS: () => COMPLETABLE_COMMANDS,
|
|
1693
1764
|
DEFAULT_MAX_INSTALL_DEPTH: () => DEFAULT_MAX_INSTALL_DEPTH,
|
|
1694
1765
|
EXTRA_COMMANDS: () => EXTRA_COMMANDS,
|
|
@@ -1734,12 +1805,15 @@ var AdepWebContainer = (() => {
|
|
|
1734
1805
|
createViteServer: () => createViteServer,
|
|
1735
1806
|
createWorkerRuntime: () => createWorkerRuntime,
|
|
1736
1807
|
deepEqual: () => deepEqual,
|
|
1808
|
+
defaultModuleName: () => defaultModuleName,
|
|
1737
1809
|
displayPath: () => displayPath,
|
|
1738
1810
|
entriesToTree: () => entriesToTree,
|
|
1739
1811
|
evaluateModuleSource: () => evaluateModuleSource,
|
|
1740
1812
|
evaluateSource: () => evaluateSource,
|
|
1741
1813
|
exampleTestSource: () => exampleTestSource,
|
|
1742
1814
|
expectAssertion: () => expect,
|
|
1815
|
+
exportBundle: () => exportBundle,
|
|
1816
|
+
findBlobRefs: () => findBlobRefs,
|
|
1743
1817
|
formatTestReport: () => formatTestReport,
|
|
1744
1818
|
formatValue: () => formatValue,
|
|
1745
1819
|
fullName: () => fullName,
|
|
@@ -3711,7 +3785,11 @@ self.onmessage = async (event) => {
|
|
|
3711
3785
|
const { createViteServer: createViteServer2 } = await Promise.resolve().then(() => (init_vite_dev(), vite_dev_exports));
|
|
3712
3786
|
const server = createViteServer2(vfs, {
|
|
3713
3787
|
root,
|
|
3714
|
-
...options.viteCompiler === void 0 ? {} : { sfcCompiler: options.viteCompiler }
|
|
3788
|
+
...options.viteCompiler === void 0 ? {} : { sfcCompiler: options.viteCompiler },
|
|
3789
|
+
// PV-006:内容重建 → 以 `serverready`(update: true)事件上报新文档 URL,宿主导出预览产物。
|
|
3790
|
+
onDocumentChange: (url) => {
|
|
3791
|
+
emit("serverready", { port: server.port, url, kind: "vite", root: server.root, update: true });
|
|
3792
|
+
}
|
|
3715
3793
|
});
|
|
3716
3794
|
await server.ready;
|
|
3717
3795
|
viteServer = server;
|
|
@@ -3876,6 +3954,92 @@ self.onmessage = async (event) => {
|
|
|
3876
3954
|
init_vite_dev();
|
|
3877
3955
|
init_path();
|
|
3878
3956
|
|
|
3957
|
+
// src/bundle-export.ts
|
|
3958
|
+
var BUNDLE_MODULE_PATH_PREFIX = "/_adep/m/";
|
|
3959
|
+
var BLOB_REF_RE = /blob:[^\s"'`<>()\\]+/g;
|
|
3960
|
+
function fnv1a(code) {
|
|
3961
|
+
let hash = 2166136261;
|
|
3962
|
+
for (let i = 0; i < code.length; i++) {
|
|
3963
|
+
hash ^= code.charCodeAt(i);
|
|
3964
|
+
hash = Math.imul(hash, 16777619) >>> 0;
|
|
3965
|
+
}
|
|
3966
|
+
return `${hash.toString(16).padStart(8, "0")}${code.length.toString(16)}`;
|
|
3967
|
+
}
|
|
3968
|
+
async function defaultModuleName(code) {
|
|
3969
|
+
const subtle = globalThis.crypto?.subtle;
|
|
3970
|
+
if (subtle === void 0) return fnv1a(code);
|
|
3971
|
+
const digest = await subtle.digest("SHA-256", new TextEncoder().encode(code));
|
|
3972
|
+
let hex = "";
|
|
3973
|
+
for (const byte of new Uint8Array(digest).slice(0, 8)) {
|
|
3974
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
3975
|
+
}
|
|
3976
|
+
return hex;
|
|
3977
|
+
}
|
|
3978
|
+
function findBlobRefs(text) {
|
|
3979
|
+
const found = text.match(BLOB_REF_RE);
|
|
3980
|
+
return found === null ? [] : [...new Set(found)];
|
|
3981
|
+
}
|
|
3982
|
+
function rewriteRefs(text, urls) {
|
|
3983
|
+
let out = text;
|
|
3984
|
+
for (const [blobUrl, path] of urls) {
|
|
3985
|
+
if (out.includes(blobUrl)) out = out.split(blobUrl).join(path);
|
|
3986
|
+
}
|
|
3987
|
+
return out;
|
|
3988
|
+
}
|
|
3989
|
+
function injectScript(html, script) {
|
|
3990
|
+
return /<\/body>/i.test(html) ? html.replace(/<\/body>/i, `${script}</body>`) : html + script;
|
|
3991
|
+
}
|
|
3992
|
+
async function exportBundle(options) {
|
|
3993
|
+
const readText = options.readText ?? ((url) => fetch(url).then((response) => response.text()));
|
|
3994
|
+
const nameOf = options.moduleName ?? defaultModuleName;
|
|
3995
|
+
const prefix = options.modulePathPrefix ?? BUNDLE_MODULE_PATH_PREFIX;
|
|
3996
|
+
const uploaded = new Set(options.known === void 0 ? [] : [...options.known.values()]);
|
|
3997
|
+
const urls = /* @__PURE__ */ new Map();
|
|
3998
|
+
const sources = [];
|
|
3999
|
+
const readOrThrow = async (url) => {
|
|
4000
|
+
try {
|
|
4001
|
+
return await readText(url);
|
|
4002
|
+
} catch (error) {
|
|
4003
|
+
throw new Error(
|
|
4004
|
+
`\u9884\u89C8\u4EA7\u7269\u5BFC\u51FA\u5931\u8D25\uFF1A\u8BFB\u4E0D\u5230\u6A21\u5757 ${url}\uFF08${error instanceof Error ? error.message : String(error)}\uFF09`,
|
|
4005
|
+
{ cause: error }
|
|
4006
|
+
);
|
|
4007
|
+
}
|
|
4008
|
+
};
|
|
4009
|
+
const html = await readOrThrow(options.documentUrl);
|
|
4010
|
+
const loadLayer = async (layer) => {
|
|
4011
|
+
const batch = [...new Set(layer)].filter((url) => !urls.has(url));
|
|
4012
|
+
if (batch.length === 0) return;
|
|
4013
|
+
const loaded = await Promise.all(
|
|
4014
|
+
batch.map(async (url) => {
|
|
4015
|
+
const code = await readOrThrow(url);
|
|
4016
|
+
return { url, code, path: `${prefix}${await nameOf(code)}.js` };
|
|
4017
|
+
})
|
|
4018
|
+
);
|
|
4019
|
+
const next = [];
|
|
4020
|
+
for (const item of loaded) {
|
|
4021
|
+
urls.set(item.url, item.path);
|
|
4022
|
+
sources.push({ url: item.url, code: item.code });
|
|
4023
|
+
next.push(...findBlobRefs(item.code));
|
|
4024
|
+
}
|
|
4025
|
+
await loadLayer(next);
|
|
4026
|
+
};
|
|
4027
|
+
await loadLayer(findBlobRefs(html));
|
|
4028
|
+
const modules = {};
|
|
4029
|
+
for (const { url, code } of sources) {
|
|
4030
|
+
const path = urls.get(url);
|
|
4031
|
+
if (uploaded.has(path)) continue;
|
|
4032
|
+
modules[path] = rewriteRefs(code, urls);
|
|
4033
|
+
}
|
|
4034
|
+
const rewrittenHtml = rewriteRefs(html, urls);
|
|
4035
|
+
return {
|
|
4036
|
+
html: options.appendScript === void 0 ? rewrittenHtml : injectScript(rewrittenHtml, options.appendScript),
|
|
4037
|
+
modules,
|
|
4038
|
+
paths: [...new Set(urls.values())].toSorted(),
|
|
4039
|
+
urls
|
|
4040
|
+
};
|
|
4041
|
+
}
|
|
4042
|
+
|
|
3879
4043
|
// src/persistence.ts
|
|
3880
4044
|
function cloneEntries(entries) {
|
|
3881
4045
|
return entries.map(
|
|
@@ -5637,6 +5801,17 @@ self.onmessage = async (event) => {
|
|
|
5637
5801
|
}
|
|
5638
5802
|
return { changes: 0 };
|
|
5639
5803
|
}
|
|
5804
|
+
if (/^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?/i.test(stmt)) {
|
|
5805
|
+
const idx = /^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))\s+ON\s+((?:"[^"]+")|([A-Za-z_][A-Za-z0-9_]*))/i.exec(
|
|
5806
|
+
stmt
|
|
5807
|
+
);
|
|
5808
|
+
if (idx === null) throw new SimDbError("DB_UNSAFE_OP", `\u65E0\u6CD5\u89E3\u6790 CREATE INDEX \u8BED\u53E5`);
|
|
5809
|
+
const tableName = ident(idx[3]);
|
|
5810
|
+
if (this.tables[tableName] === void 0) {
|
|
5811
|
+
throw new SimDbError("DB_UNSAFE_OP", `CREATE INDEX \u76EE\u6807\u8868\u4E0D\u5B58\u5728\uFF1A${tableName}`);
|
|
5812
|
+
}
|
|
5813
|
+
return { changes: 0 };
|
|
5814
|
+
}
|
|
5640
5815
|
if (/^INSERT\s+INTO/i.test(stmt)) return this.execInsert(stmt, params);
|
|
5641
5816
|
if (/^UPDATE\s+/i.test(stmt)) return this.execUpdate(stmt, params);
|
|
5642
5817
|
if (/^DELETE\s+FROM/i.test(stmt)) return this.execDelete(stmt, params);
|
|
@@ -5881,6 +6056,19 @@ self.onmessage = async (event) => {
|
|
|
5881
6056
|
}
|
|
5882
6057
|
return path;
|
|
5883
6058
|
}
|
|
6059
|
+
function assertStoragePrefix(prefix) {
|
|
6060
|
+
if (prefix !== void 0 && prefix !== "") {
|
|
6061
|
+
const segments = prefix.split("/");
|
|
6062
|
+
if (prefix.startsWith("/") || /[\\]/.test(prefix) || segments.some((segment) => segment === "..")) {
|
|
6063
|
+
throw new StorageError(
|
|
6064
|
+
400,
|
|
6065
|
+
STORAGE_CODES.invalidPath,
|
|
6066
|
+
`\u975E\u6CD5\u524D\u7F00 "${prefix}"\uFF08\u7981\u6B62 / \u5F00\u5934\u3001\u53CD\u659C\u6760\u6216 .. \u7A7F\u8D8A\uFF09`
|
|
6067
|
+
);
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
return prefix ?? "";
|
|
6071
|
+
}
|
|
5884
6072
|
|
|
5885
6073
|
// ../runtime/src/storage/hmac-sha256.ts
|
|
5886
6074
|
var K = new Uint32Array([
|
|
@@ -6200,16 +6388,7 @@ self.onmessage = async (event) => {
|
|
|
6200
6388
|
await saveBucket(bucket);
|
|
6201
6389
|
},
|
|
6202
6390
|
async list(_projectId, prefix) {
|
|
6203
|
-
|
|
6204
|
-
const segments = prefix.split("/");
|
|
6205
|
-
if (prefix.startsWith("/") || /[\\]/.test(prefix) || segments.some((segment) => segment === "..")) {
|
|
6206
|
-
throw new StorageError(
|
|
6207
|
-
400,
|
|
6208
|
-
STORAGE_CODES.invalidPath,
|
|
6209
|
-
`\u975E\u6CD5\u524D\u7F00 "${prefix}"\uFF08\u7981\u6B62 / \u5F00\u5934\u3001\u53CD\u659C\u6760\u6216 .. \u7A7F\u8D8A\uFF09`
|
|
6210
|
-
);
|
|
6211
|
-
}
|
|
6212
|
-
}
|
|
6391
|
+
assertStoragePrefix(prefix);
|
|
6213
6392
|
const bucket = await loadBucket();
|
|
6214
6393
|
return Object.values(bucket).filter((object) => prefix === void 0 || object.path.startsWith(prefix)).map(toMeta).toSorted((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
6215
6394
|
},
|
|
@@ -6596,6 +6775,19 @@ self.onmessage = async (event) => {
|
|
|
6596
6775
|
};
|
|
6597
6776
|
}
|
|
6598
6777
|
|
|
6778
|
+
// ../runtime/src/functions/runtime/rpc-wire.ts
|
|
6779
|
+
function wireRpcResponses(port, pending) {
|
|
6780
|
+
port.on?.((message) => {
|
|
6781
|
+
const reply = message;
|
|
6782
|
+
if (reply?.type !== "rpc-response" || typeof reply.id !== "number") return;
|
|
6783
|
+
const entry = pending.get(reply.id);
|
|
6784
|
+
if (entry === void 0) return;
|
|
6785
|
+
pending.delete(reply.id);
|
|
6786
|
+
if (reply.ok === true) entry.resolve(reply.result);
|
|
6787
|
+
else entry.reject(new Error(reply.error ?? "RPC \u80FD\u529B\u8C03\u7528\u5931\u8D25"));
|
|
6788
|
+
});
|
|
6789
|
+
}
|
|
6790
|
+
|
|
6599
6791
|
// src/sim/ctx.ts
|
|
6600
6792
|
function createLocalPort(handler) {
|
|
6601
6793
|
const listeners = [];
|
|
@@ -6625,17 +6817,6 @@ self.onmessage = async (event) => {
|
|
|
6625
6817
|
}
|
|
6626
6818
|
};
|
|
6627
6819
|
}
|
|
6628
|
-
function wireRpcResponses(port, pending) {
|
|
6629
|
-
port.on((message) => {
|
|
6630
|
-
const reply = message;
|
|
6631
|
-
if (reply?.type !== "rpc-response" || typeof reply.id !== "number") return;
|
|
6632
|
-
const entry = pending.get(reply.id);
|
|
6633
|
-
if (entry === void 0) return;
|
|
6634
|
-
pending.delete(reply.id);
|
|
6635
|
-
if (reply.ok === true) entry.resolve(reply.result);
|
|
6636
|
-
else entry.reject(new Error(reply.error ?? "RPC \u80FD\u529B\u8C03\u7528\u5931\u8D25"));
|
|
6637
|
-
});
|
|
6638
|
-
}
|
|
6639
6820
|
function createFlatRpcClient(handler) {
|
|
6640
6821
|
return new Proxy(
|
|
6641
6822
|
{},
|
|
@@ -6849,6 +7030,27 @@ self.onmessage = async (event) => {
|
|
|
6849
7030
|
return { run };
|
|
6850
7031
|
}
|
|
6851
7032
|
|
|
7033
|
+
// ../runtime/src/sim/merge.ts
|
|
7034
|
+
function mergeBundles(bundles) {
|
|
7035
|
+
const capabilities = [];
|
|
7036
|
+
const byName = /* @__PURE__ */ new Map();
|
|
7037
|
+
for (const bundle of bundles) {
|
|
7038
|
+
for (const cap of bundle.capabilities) {
|
|
7039
|
+
const existing = byName.get(cap.name);
|
|
7040
|
+
if (existing !== void 0) capabilities.splice(capabilities.indexOf(existing), 1);
|
|
7041
|
+
byName.set(cap.name, cap);
|
|
7042
|
+
capabilities.push(cap);
|
|
7043
|
+
}
|
|
7044
|
+
}
|
|
7045
|
+
const rpcHandlers = {};
|
|
7046
|
+
for (const bundle of bundles) {
|
|
7047
|
+
for (const [name, handler] of Object.entries(bundle.rpcHandlers)) {
|
|
7048
|
+
rpcHandlers[name] = handler;
|
|
7049
|
+
}
|
|
7050
|
+
}
|
|
7051
|
+
return { capabilities, rpcHandlers };
|
|
7052
|
+
}
|
|
7053
|
+
|
|
6852
7054
|
// ../runtime/src/database/sdk/kv.ts
|
|
6853
7055
|
var KV_TABLE = "_adep_kv";
|
|
6854
7056
|
var KV_RPC = {
|
|
@@ -6950,25 +7152,6 @@ self.onmessage = async (event) => {
|
|
|
6950
7152
|
}
|
|
6951
7153
|
|
|
6952
7154
|
// src/sim/runtime.ts
|
|
6953
|
-
function mergeBundles(bundles) {
|
|
6954
|
-
const capabilities = [];
|
|
6955
|
-
const byName = /* @__PURE__ */ new Map();
|
|
6956
|
-
for (const bundle of bundles) {
|
|
6957
|
-
for (const cap of bundle.capabilities) {
|
|
6958
|
-
const existing = byName.get(cap.name);
|
|
6959
|
-
if (existing !== void 0) capabilities.splice(capabilities.indexOf(existing), 1);
|
|
6960
|
-
byName.set(cap.name, cap);
|
|
6961
|
-
capabilities.push(cap);
|
|
6962
|
-
}
|
|
6963
|
-
}
|
|
6964
|
-
const rpcHandlers = {};
|
|
6965
|
-
for (const bundle of bundles) {
|
|
6966
|
-
for (const [name, handler] of Object.entries(bundle.rpcHandlers)) {
|
|
6967
|
-
rpcHandlers[name] = handler;
|
|
6968
|
-
}
|
|
6969
|
-
}
|
|
6970
|
-
return { capabilities, rpcHandlers };
|
|
6971
|
-
}
|
|
6972
7155
|
async function createBrowserSimRuntime(options) {
|
|
6973
7156
|
const kv = options.kv ?? pickDefaultSimKv();
|
|
6974
7157
|
const db = createBrowserSimDb({ projectId: options.projectId, kv });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adep/web-container",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
13
|
"dependencies": {
|
|
14
|
-
"@adep/runtime": "0.2.
|
|
15
|
-
"@adep/types": "0.2.
|
|
14
|
+
"@adep/runtime": "0.2.3",
|
|
15
|
+
"@adep/types": "0.2.3"
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
18
|
"test": "vitest run",
|