@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.
@@ -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
- * 以及出现在 `(` `,` `=` `>` `:` `[` `{` `}` `;` 之后(如 `map((v: T) => v)` 的内层括号)。
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
- " if (window.parent === window) return ORIGINAL_FETCH(input, init);",
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
- ' window.parent.postMessage({ __adepFnRequest: true, id: id, method: method, url: target, headers: headers, body: textBody }, "*");',
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) broadcast(currentUrl);
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
- if (prefix !== void 0 && prefix !== "") {
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,
@@ -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>;
@@ -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
- * 以及出现在 `(` `,` `=` `>` `:` `[` `{` `}` `;` 之后(如 `map((v: T) => v)` 的内层括号)。
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
- " if (window.parent === window) return ORIGINAL_FETCH(input, init);",
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
- ' window.parent.postMessage({ __adepFnRequest: true, id: id, method: method, url: target, headers: headers, body: textBody }, "*");',
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) broadcast(currentUrl);
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
- if (prefix !== void 0 && prefix !== "") {
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
- * 以及出现在 `(` `,` `=` `>` `:` `[` `{` `}` `;` 之后(如 `map((v: T) => v)` 的内层括号)。
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
- " if (window.parent === window) return ORIGINAL_FETCH(input, init);",
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
- ' window.parent.postMessage({ __adepFnRequest: true, id: id, method: method, url: target, headers: headers, body: textBody }, "*");',
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) broadcast(currentUrl);
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
- if (prefix !== void 0 && prefix !== "") {
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.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.2",
15
- "@adep/types": "0.2.2"
14
+ "@adep/runtime": "0.2.3",
15
+ "@adep/types": "0.2.3"
16
16
  },
17
17
  "scripts": {
18
18
  "test": "vitest run",