@adep/web-container 0.2.1 → 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.
@@ -118,6 +118,27 @@ var AdepWebContainer = (() => {
118
118
  }
119
119
  return abs;
120
120
  }
121
+ function pickConditionTarget(conditions, order, resolveRel, context) {
122
+ for (const key of order) {
123
+ const value = conditions[key];
124
+ if (value === void 0 || value === null) continue;
125
+ if (typeof value === "string") return resolveRel(value);
126
+ if (typeof value === "object" && !Array.isArray(value)) {
127
+ const nested = pickConditionTarget(
128
+ value,
129
+ order,
130
+ resolveRel,
131
+ context
132
+ );
133
+ if (nested !== null) return nested;
134
+ continue;
135
+ }
136
+ throw new ResolveError(
137
+ `${context}\uFF1Aexports["."]["${key}"] \u5F62\u6001\u4E0D\u652F\u6301\uFF08\u4EC5\u63A5\u53D7\u5B57\u7B26\u4E32 / null / \u6761\u4EF6\u5BF9\u8C61\uFF09`
138
+ );
139
+ }
140
+ return null;
141
+ }
121
142
  function resolvePackageEntry(meta, pkgDirAbs, condition) {
122
143
  const context = `\u5305 ${meta.name ?? meta.version ?? pkgDirAbs} \u7684 exports/main \u58F0\u660E`;
123
144
  const exp = meta.exports;
@@ -138,20 +159,11 @@ var AdepWebContainer = (() => {
138
159
  if (typeof dot !== "object" || Array.isArray(dot)) {
139
160
  throw new ResolveError(`${context}\uFF1Aexports["."] \u5F62\u6001\u4E0D\u652F\u6301\uFF08\u4EC5\u63A5\u53D7\u5B57\u7B26\u4E32\u6216\u6761\u4EF6\u5BF9\u8C61\uFF09`);
140
161
  }
141
- const conditions = dot;
142
- const order = condition === "import" ? ["import", "node", "default"] : ["require", "node", "default"];
143
- for (const key of order) {
144
- const value = conditions[key];
145
- if (typeof value === "string") return withinPackage(pkgDirAbs, value, context);
146
- if (value !== void 0 && (typeof value !== "object" || value === null)) {
147
- throw new ResolveError(
148
- `${context}\uFF1Aexports["."]["${key}"] \u5F62\u6001\u4E0D\u652F\u6301\uFF08\u4EC5\u63A5\u53D7\u5B57\u7B26\u4E32\u6216 null\uFF09`
149
- );
150
- }
151
- }
152
- throw new ResolveError(
153
- `${context}\uFF1Aexports["."] \u6761\u4EF6\u5BF9\u8C61\u91CC\u6CA1\u6709 ${condition}/node/default \u5B57\u7B26\u4E32\u5165\u53E3\uFF08\u5B50\u8DEF\u5F84/\u5D4C\u5957\u6761\u4EF6\u4E0D\u652F\u6301\uFF09`
154
- );
162
+ const order = condition === "import" ? IMPORT_CONDITION_ORDER : REQUIRE_CONDITION_ORDER;
163
+ const resolveRel = (rel) => withinPackage(pkgDirAbs, rel, context);
164
+ const hit = pickConditionTarget(dot, order, resolveRel, context);
165
+ if (hit !== null) return hit;
166
+ throw new ResolveError(`${context}\uFF1Aexports["."] \u6761\u4EF6\u5BF9\u8C61\u91CC\u6CA1\u6709 ${order.join("/")} \u5165\u53E3`);
155
167
  }
156
168
  if (meta.main !== void 0) {
157
169
  if (typeof meta.main !== "string") {
@@ -249,11 +261,13 @@ var AdepWebContainer = (() => {
249
261
  `\u627E\u4E0D\u5230\u5305 "${specifier}"\uFF1A${dirname(fromFileAbs)} \u53CA\u5176\u5404\u7EA7\u7236\u76EE\u5F55\u4E0B\u90FD\u6CA1\u6709 node_modules/${name}\uFF08\u5148\u5728\u7EC8\u7AEF npm install ${name}\uFF09`
250
262
  );
251
263
  }
252
- var RESOLVE_EXTENSIONS, ResolveError;
264
+ var IMPORT_CONDITION_ORDER, REQUIRE_CONDITION_ORDER, RESOLVE_EXTENSIONS, ResolveError;
253
265
  var init_node_resolve = __esm({
254
266
  "src/node-resolve.ts"() {
255
267
  "use strict";
256
268
  init_path();
269
+ IMPORT_CONDITION_ORDER = ["browser", "import", "module", "default"];
270
+ REQUIRE_CONDITION_ORDER = ["require", "node", "default"];
257
271
  RESOLVE_EXTENSIONS = [".js", ".mjs", ".cjs", ".json"];
258
272
  ResolveError = class extends Error {
259
273
  constructor(message) {
@@ -528,6 +542,7 @@ var AdepWebContainer = (() => {
528
542
  return;
529
543
  case "as":
530
544
  case "satisfies": {
545
+ if (this.frames[this.frames.length - 1]?.specifierList === true) return;
531
546
  if (this.tokenAt(2) === "*" || this.tokenAt(2) === "type") return;
532
547
  const nextChar = this.skipWhitespaceFrom(this.i);
533
548
  if (nextChar === null || "=,:]})".includes(this.src[nextChar])) return;
@@ -580,7 +595,8 @@ var AdepWebContainer = (() => {
580
595
  const frame = {
581
596
  kind: c === "(" ? "paren" : c === "{" ? "brace" : "bracket",
582
597
  declare: false,
583
- classBody: false
598
+ classBody: false,
599
+ specifierList: c === "{" && this.isImportSpecifierBrace()
584
600
  };
585
601
  if (c === "{" && this.pendingClassBody) {
586
602
  frame.classBody = true;
@@ -667,8 +683,17 @@ var AdepWebContainer = (() => {
667
683
  return true;
668
684
  }
669
685
  /**
670
- * `(` 是否为参数列表(声明位):`function f(`、类体里的方法 `m(`、箭头 `=> (`,
671
- * 以及出现在 `(` `,` `=` `>` `:` `[` `{` `}` `;` 之后(如 `map((v: T) => v)` 的内层括号)。
686
+ * `{` import / export 的说明符列表吗——`{ a as b }` 覆盖 `import { … }`、
687
+ * `export { }` `import def, { }` 三种写法(后者说明符花括号的前驱 token `,`)。
688
+ */
689
+ isImportSpecifierBrace() {
690
+ const back1 = this.tokenAt(1);
691
+ if (back1 === "import" || back1 === "export") return true;
692
+ return back1 === "," && this.tokenAt(3) === "import";
693
+ }
694
+ /**
695
+ * `(` 是否为参数列表(声明位):`function f(`、`async` 箭头 `async (`、类体里的方法 `m(`、
696
+ * 箭头 `=> (`,以及出现在 `(` `,` `=` `>` `:` `[` `{` `}` `;` 之后(如 `map((v: T) => v)` 的内层括号)。
672
697
  * 调用位 `foo(` 与控制流 `if (` `for (` 都不算。
673
698
  * 误判成声明位也不足以擦掉什么:`:` 还要过 `isAnnotationColon` 的「名字前驱必须是
674
699
  * `(` `,` `{` `;` 修饰符」这一关,而三元表达式的中间段前面必然是 `?`。
@@ -677,6 +702,8 @@ var AdepWebContainer = (() => {
677
702
  if (this.afterFunctionKeyword) return true;
678
703
  if (this.tokenAt(2) === "function" && IDENT_START.test(this.tokenAt(1))) return true;
679
704
  if (this.afterArrow) return true;
705
+ if (this.tokenAt(1) === "async") return true;
706
+ if (this.tokenAt(1) === "return") return true;
680
707
  if (this.inClassBody() && IDENT_PART.test(this.prevSignificantChar(at - 1))) return true;
681
708
  const prev = this.prevSignificantChar(at - 1);
682
709
  return prev === "" || "(,=>:[{};".includes(prev);
@@ -1185,11 +1212,15 @@ var AdepWebContainer = (() => {
1185
1212
  " }",
1186
1213
  " var u;",
1187
1214
  " try { u = new URL(urlStr, document.baseURI); }",
1188
- " catch (e) { return ORIGINAL_FETCH(input, init); }",
1215
+ " catch (e0) {",
1216
+ " try { u = new URL(urlStr, window.location.origin); }",
1217
+ " catch (e1) { return ORIGINAL_FETCH(input, init); }",
1218
+ " }",
1189
1219
  ' if (u.pathname.indexOf("/api/") !== 0 && u.pathname !== "/api") {',
1190
1220
  " return ORIGINAL_FETCH(input, init);",
1191
1221
  " }",
1192
- " 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);",
1193
1224
  ' method = (method || "GET").toUpperCase();',
1194
1225
  " var target = u.pathname + u.search;",
1195
1226
  " return readBody(input, init).then(function (textBody) {",
@@ -1200,10 +1231,75 @@ var AdepWebContainer = (() => {
1200
1231
  ` resolve(new Response('{"error":{"code":"FN_PROXY_TIMEOUT","message":"\\u4e91\\u51fd\\u6570\\u6267\\u884c\\u8d85\\u65f6"}}', { status: 504, headers: { "content-type": "application/json" } }));`,
1201
1232
  " }, TIMEOUT_MS);",
1202
1233
  " pending[id] = { resolve: resolve, timer: timer };",
1203
- ' 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 }, "*");',
1204
1235
  " });",
1205
1236
  " });",
1206
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
+ " }",
1207
1303
  "})();"
1208
1304
  ].join("\n");
1209
1305
  return src;
@@ -1324,6 +1420,18 @@ var AdepWebContainer = (() => {
1324
1420
  "export default css;"
1325
1421
  ].join("\n");
1326
1422
  }
1423
+ function inlineStyleBlock(css, key) {
1424
+ return [
1425
+ "{",
1426
+ `const css = ${JSON.stringify(css)};`,
1427
+ 'const el = document.createElement("style");',
1428
+ 'el.setAttribute("data-adep-vite", "");',
1429
+ `el.setAttribute("data-adep-style", ${JSON.stringify(key)});`,
1430
+ "el.textContent = css;",
1431
+ "document.head.appendChild(el);",
1432
+ "}"
1433
+ ].join("\n");
1434
+ }
1327
1435
  function jsonModule(source, abs) {
1328
1436
  try {
1329
1437
  JSON.parse(source);
@@ -1374,6 +1482,17 @@ var AdepWebContainer = (() => {
1374
1482
  } catch {
1375
1483
  }
1376
1484
  };
1485
+ const DEFINES = [
1486
+ [/\bprocess\s*\.\s*env\s*\.\s*NODE_ENV\b/g, JSON.stringify("development")],
1487
+ [/\b__VUE_OPTIONS_API__\b/g, "true"],
1488
+ [/\b__VUE_PROD_DEVTOOLS__\b/g, "false"],
1489
+ [/\b__VUE_PROD_HYDRATION_MISMATCH_DETAILS__\b/g, "false"]
1490
+ ];
1491
+ function applyDefines(code) {
1492
+ let out = code;
1493
+ for (const [pattern, value] of DEFINES) out = out.replace(pattern, value);
1494
+ return out;
1495
+ }
1377
1496
  function transformModule2(abs, source) {
1378
1497
  if (abs.endsWith(".css")) return cssModule(source);
1379
1498
  if (abs.endsWith(".json")) return jsonModule(source, abs);
@@ -1384,21 +1503,27 @@ var AdepWebContainer = (() => {
1384
1503
  `${abs} \u662F Vue SFC\uFF0C\u4F46\u5F53\u524D\u73AF\u5883\u672A\u88C5\u914D SFC \u7F16\u8BD1\u5668\uFF08IDE \u4FA7\u9700\u6CE8\u5165 vue/compiler-sfc\uFF09`
1385
1504
  );
1386
1505
  const compiled = sfcCompiler(source, abs);
1387
- return [...compiled.styles.map((css) => cssModule(css)), compiled.script].join("\n");
1506
+ return [
1507
+ ...compiled.styles.map((css, index) => inlineStyleBlock(css, `${abs}#${index}`)),
1508
+ applyDefines(stripTypes(compiled.script))
1509
+ ].join("\n");
1388
1510
  }
1389
1511
  if (abs.endsWith(".tsx") || abs.endsWith(".jsx"))
1390
1512
  throw new ViteDevError(
1391
1513
  "JSX_UNSUPPORTED",
1392
1514
  `${abs} \u4F7F\u7528 JSX\uFF1A\u6D4F\u89C8\u5668\u5185 vite \u53EA\u64E6\u9664 TS \u7C7B\u578B\u6807\u6CE8\uFF0C\u4E0D\u8F6C\u8BD1 JSX\uFF08\u8BF7\u7528 h() \u6E32\u67D3\u51FD\u6570\u6216 .vue SFC\uFF09`
1393
1515
  );
1394
- if (abs.endsWith(".ts") || abs.endsWith(".mts")) return stripTypes(source);
1395
- return source;
1516
+ if (abs.endsWith(".ts") || abs.endsWith(".mts")) return applyDefines(stripTypes(source));
1517
+ return applyDefines(source);
1518
+ }
1519
+ function absoluteFromRoot(spec) {
1520
+ return join(root, spec.replace(/^\/+/, ""));
1396
1521
  }
1397
1522
  async function resolveSpecifier(spec, importerAbs) {
1398
1523
  if (EXTERNAL_SPECIFIER.test(spec) || DATA_OR_BLOB.test(spec)) return spec;
1399
1524
  let target;
1400
1525
  if (spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/")) {
1401
- const base = spec.startsWith("/") ? spec : join(dirname(importerAbs), spec);
1526
+ const base = spec.startsWith("/") ? absoluteFromRoot(spec) : join(dirname(importerAbs), spec);
1402
1527
  target = resolveWithSuffixes(vfs, base);
1403
1528
  if (!vfs.exists(target) || vfs.isDirectory(target))
1404
1529
  throw new ViteDevError(
@@ -1487,7 +1612,7 @@ var AdepWebContainer = (() => {
1487
1612
  html = html.replace(
1488
1613
  MODULE_SCRIPT_SRC_RE,
1489
1614
  (_whole, pre, _q1, mid, _q2, src, post) => {
1490
- const base = src.startsWith("/") ? normalize(src) : join(dirname(entryAbs), src);
1615
+ const base = src.startsWith("/") ? absoluteFromRoot(src) : join(dirname(entryAbs), src);
1491
1616
  const abs = resolveWithSuffixes(vfs, base);
1492
1617
  if (!vfs.exists(abs) || vfs.isDirectory(abs))
1493
1618
  throw new ViteDevError(
@@ -1495,7 +1620,7 @@ var AdepWebContainer = (() => {
1495
1620
  `\u5165\u53E3 module script \u627E\u4E0D\u5230 "${src}"\uFF08root: ${root}\uFF1B\u8BD5\u8FC7\u6269\u5C55\u540D ${VITE_RESOLVE_SUFFIXES.join("/")} \u4E0E\u76EE\u5F55 index\uFF09`
1496
1621
  );
1497
1622
  entryModuleUrls.push(loadModuleUrl(abs));
1498
- return `<script${pre}${mid}src="__adep_vite_entry_${entryModuleUrls.length - 1}__"${post}><\/script>`;
1623
+ return `<script${pre}${mid}src="__adep_vite_entry_${entryModuleUrls.length - 1}__"${post} type="module"><\/script>`;
1499
1624
  }
1500
1625
  );
1501
1626
  html = html.replace(
@@ -1505,7 +1630,7 @@ var AdepWebContainer = (() => {
1505
1630
  const abs = `${INLINE_PREFIX}${inlineSeq++}.mjs`;
1506
1631
  inlineSources.set(abs, body);
1507
1632
  entryModuleUrls.push(loadModuleUrl(abs));
1508
- return `<script${pre}${post}src="__adep_vite_entry_${entryModuleUrls.length - 1}__"><\/script>`;
1633
+ return `<script${pre}${post}src="__adep_vite_entry_${entryModuleUrls.length - 1}__" type="module"><\/script>`;
1509
1634
  }
1510
1635
  );
1511
1636
  const urls = await Promise.all(entryModuleUrls);
@@ -1529,7 +1654,10 @@ var AdepWebContainer = (() => {
1529
1654
  lastDoc = doc;
1530
1655
  if (currentUrl !== "") revokeObjectURL(currentUrl);
1531
1656
  currentUrl = createObjectURL(new Blob([doc], { type: "text/html" }));
1532
- if (changed) broadcast(currentUrl);
1657
+ if (changed) {
1658
+ broadcast(currentUrl);
1659
+ options.onDocumentChange?.(currentUrl);
1660
+ }
1533
1661
  }
1534
1662
  const server = {
1535
1663
  get url() {
@@ -1631,6 +1759,7 @@ var AdepWebContainer = (() => {
1631
1759
  // src/index.ts
1632
1760
  var index_exports = {};
1633
1761
  __export(index_exports, {
1762
+ BUNDLE_MODULE_PATH_PREFIX: () => BUNDLE_MODULE_PATH_PREFIX,
1634
1763
  COMPLETABLE_COMMANDS: () => COMPLETABLE_COMMANDS,
1635
1764
  DEFAULT_MAX_INSTALL_DEPTH: () => DEFAULT_MAX_INSTALL_DEPTH,
1636
1765
  EXTRA_COMMANDS: () => EXTRA_COMMANDS,
@@ -1676,12 +1805,15 @@ var AdepWebContainer = (() => {
1676
1805
  createViteServer: () => createViteServer,
1677
1806
  createWorkerRuntime: () => createWorkerRuntime,
1678
1807
  deepEqual: () => deepEqual,
1808
+ defaultModuleName: () => defaultModuleName,
1679
1809
  displayPath: () => displayPath,
1680
1810
  entriesToTree: () => entriesToTree,
1681
1811
  evaluateModuleSource: () => evaluateModuleSource,
1682
1812
  evaluateSource: () => evaluateSource,
1683
1813
  exampleTestSource: () => exampleTestSource,
1684
1814
  expectAssertion: () => expect,
1815
+ exportBundle: () => exportBundle,
1816
+ findBlobRefs: () => findBlobRefs,
1685
1817
  formatTestReport: () => formatTestReport,
1686
1818
  formatValue: () => formatValue,
1687
1819
  fullName: () => fullName,
@@ -3653,7 +3785,11 @@ self.onmessage = async (event) => {
3653
3785
  const { createViteServer: createViteServer2 } = await Promise.resolve().then(() => (init_vite_dev(), vite_dev_exports));
3654
3786
  const server = createViteServer2(vfs, {
3655
3787
  root,
3656
- ...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
+ }
3657
3793
  });
3658
3794
  await server.ready;
3659
3795
  viteServer = server;
@@ -3818,6 +3954,92 @@ self.onmessage = async (event) => {
3818
3954
  init_vite_dev();
3819
3955
  init_path();
3820
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
+
3821
4043
  // src/persistence.ts
3822
4044
  function cloneEntries(entries) {
3823
4045
  return entries.map(
@@ -4559,6 +4781,8 @@ self.onmessage = async (event) => {
4559
4781
  var DB_RPC = {
4560
4782
  /** 直通方法(如 query):`(method=query, args=[sql, params])`。 */
4561
4783
  query: "query",
4784
+ /** 写路径(CF-013):`(method=writeQuery, args=[sql, params])` → `{ changes }`。 */
4785
+ writeQuery: "writeQuery",
4562
4786
  /** 读取 owned 表变更流(DB-006):`(method=changes, args=[table, query])`。 */
4563
4787
  changes: "changes",
4564
4788
  /** 开启事务:`begin → txId`。 */
@@ -4635,21 +4859,35 @@ self.onmessage = async (event) => {
4635
4859
  function stripLiterals(sql) {
4636
4860
  return sql.replace(/'[^']*(?:''[^']*)*'/g, "").replace(/"[^"]*(?:""[^"]*)*"/g, "").replace(/--[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
4637
4861
  }
4638
- function assertReadOnlyQuery(sql) {
4862
+ function sqlHead(sql) {
4863
+ return sql.trim().replace(/^\(+/, "").trim().toUpperCase();
4864
+ }
4865
+ function assertSafeStatement(sql) {
4639
4866
  const stripped = stripLiterals(sql);
4640
4867
  if (stripped.includes(";")) {
4641
4868
  throw unsafeOperation("\u4EC5\u5141\u8BB8\u5355\u6761\u8BED\u53E5\uFF0C\u7981\u6B62\u591A\u8BED\u53E5\u5806\u53E0");
4642
4869
  }
4643
- const head = stripped.trim().replace(/^\(+/, "").trim().toUpperCase();
4644
- if (!head.startsWith("SELECT") && !head.startsWith("WITH")) {
4645
- throw unsafeOperation("cloud.db.query \u4EC5\u5141\u8BB8\u53EA\u8BFB\u67E5\u8BE2\uFF08SELECT / WITH\uFF09");
4646
- }
4647
4870
  if (/ATTACH|DETACH/.test(stripped.toUpperCase())) {
4648
4871
  throw unsafeOperation("\u7981\u6B62\u8DE8\u5E93\u64CD\u4F5C\uFF08ATTACH / DETACH\uFF09");
4649
4872
  }
4650
4873
  if (/SQLITE_\w+/i.test(stripped)) {
4651
4874
  throw unsafeOperation("\u7981\u6B62\u8BBF\u95EE\u7CFB\u7EDF\u8868\uFF08sqlite_*\uFF09");
4652
4875
  }
4876
+ return sqlHead(stripped);
4877
+ }
4878
+ function assertReadOnlyQuery(sql) {
4879
+ const head = assertSafeStatement(sql);
4880
+ if (!head.startsWith("SELECT") && !head.startsWith("WITH")) {
4881
+ throw unsafeOperation("cloud.db.query \u4EC5\u5141\u8BB8\u53EA\u8BFB\u67E5\u8BE2\uFF08SELECT / WITH\uFF09");
4882
+ }
4883
+ }
4884
+ function assertWriteQuery(sql) {
4885
+ const head = assertSafeStatement(sql);
4886
+ if (!head.startsWith("INSERT") && !head.startsWith("UPDATE") && !head.startsWith("DELETE")) {
4887
+ throw unsafeOperation(
4888
+ "cloud.db.writeQuery \u4EC5\u5141\u8BB8 DML\uFF08INSERT / UPDATE / DELETE\uFF09\uFF0CDDL \u8BF7\u8D70\u63A7\u5236\u53F0"
4889
+ );
4890
+ }
4653
4891
  }
4654
4892
 
4655
4893
  // ../runtime/src/database/builder/owned.ts
@@ -5195,6 +5433,14 @@ self.onmessage = async (event) => {
5195
5433
  assertReadOnlyQuery(sql);
5196
5434
  return driver.all(sql, params);
5197
5435
  },
5436
+ async writeQuery(sql, params) {
5437
+ if (!Array.isArray(params)) {
5438
+ throw unsafeOperation("writeQuery \u7684 params \u5FC5\u987B\u63D0\u4F9B\uFF08\u65E0\u53C2\u4F20 []\uFF09");
5439
+ }
5440
+ guardWrite();
5441
+ assertWriteQuery(sql);
5442
+ return driver.run(sql, params);
5443
+ },
5198
5444
  async changes(table, query = {}) {
5199
5445
  return readChanges(driver, table, query);
5200
5446
  }
@@ -5210,7 +5456,8 @@ self.onmessage = async (event) => {
5210
5456
  rootMethod: "table",
5211
5457
  stepMethods: ["select", "where", "orderBy", "limit", "offset"],
5212
5458
  terminalMethods: ["get", "first", "count", "insert", "insertMany", "update", "delete"],
5213
- directMethods: ["query", "changes"],
5459
+ // CF-013:writeQuery 是 D1 shim 的写路径(INSERT/UPDATE/DELETE),与 query 并列直通方法。
5460
+ directMethods: ["query", "writeQuery", "changes"],
5214
5461
  transactionMethod: "transaction"
5215
5462
  };
5216
5463
  var WRITE_TERMINALS = /* @__PURE__ */ new Set(["insert", "insertMany", "update", "delete"]);
@@ -5265,6 +5512,10 @@ self.onmessage = async (event) => {
5265
5512
  const [sql, params] = args;
5266
5513
  return await db.query(sql, params);
5267
5514
  }
5515
+ case DB_RPC.writeQuery: {
5516
+ const [sql, params] = args;
5517
+ return await db.writeQuery(sql, params);
5518
+ }
5268
5519
  case DB_RPC.changes: {
5269
5520
  const [table, query] = args;
5270
5521
  return await db.changes(table, query);
@@ -5550,6 +5801,17 @@ self.onmessage = async (event) => {
5550
5801
  }
5551
5802
  return { changes: 0 };
5552
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
+ }
5553
5815
  if (/^INSERT\s+INTO/i.test(stmt)) return this.execInsert(stmt, params);
5554
5816
  if (/^UPDATE\s+/i.test(stmt)) return this.execUpdate(stmt, params);
5555
5817
  if (/^DELETE\s+FROM/i.test(stmt)) return this.execDelete(stmt, params);
@@ -5794,6 +6056,19 @@ self.onmessage = async (event) => {
5794
6056
  }
5795
6057
  return path;
5796
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
+ }
5797
6072
 
5798
6073
  // ../runtime/src/storage/hmac-sha256.ts
5799
6074
  var K = new Uint32Array([
@@ -5994,9 +6269,12 @@ self.onmessage = async (event) => {
5994
6269
  const handler = async (method, args) => {
5995
6270
  switch (method) {
5996
6271
  case "upload": {
5997
- const [path, data] = args;
6272
+ const [path, data, options] = args;
5998
6273
  return toStoredFile(
5999
- await driver.put(projectId, path, asBytes(data), { visibility: DEFAULT_VISIBILITY })
6274
+ await driver.put(projectId, path, asBytes(data), {
6275
+ visibility: DEFAULT_VISIBILITY,
6276
+ ...options?.contentType === void 0 ? {} : { contentType: options.contentType }
6277
+ })
6000
6278
  );
6001
6279
  }
6002
6280
  case "get": {
@@ -6110,16 +6388,7 @@ self.onmessage = async (event) => {
6110
6388
  await saveBucket(bucket);
6111
6389
  },
6112
6390
  async list(_projectId, prefix) {
6113
- if (prefix !== void 0 && prefix !== "") {
6114
- const segments = prefix.split("/");
6115
- if (prefix.startsWith("/") || /[\\]/.test(prefix) || segments.some((segment) => segment === "..")) {
6116
- throw new StorageError(
6117
- 400,
6118
- STORAGE_CODES.invalidPath,
6119
- `\u975E\u6CD5\u524D\u7F00 "${prefix}"\uFF08\u7981\u6B62 / \u5F00\u5934\u3001\u53CD\u659C\u6760\u6216 .. \u7A7F\u8D8A\uFF09`
6120
- );
6121
- }
6122
- }
6391
+ assertStoragePrefix(prefix);
6123
6392
  const bucket = await loadBucket();
6124
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);
6125
6394
  },
@@ -6443,6 +6712,10 @@ self.onmessage = async (event) => {
6443
6712
  realtime: {
6444
6713
  code: "REALTIME_NOT_AVAILABLE",
6445
6714
  message: "\u5B9E\u65F6\u901A\u9053\u672A\u88C5\u914D\uFF1Arealtime \u57DF\u672A\u88C5\u8F7D\uFF08\u68C0\u67E5\u90E8\u7F72\u5F62\u6001\u4E0E Redis \u914D\u7F6E\uFF09\uFF08REALTIME_NOT_AVAILABLE\uFF09"
6715
+ },
6716
+ kv: {
6717
+ code: "KV_NOT_PROVISIONED",
6718
+ message: "\u9879\u76EE KV \u5B58\u50A8\u672A\u53EF\u7528\uFF1Akv \u968F\u9879\u76EE\u6570\u636E\u5E93\u63D0\u4F9B\uFF0C\u8BF7\u5148\u542F\u52A8\u6570\u636E\u5E93\uFF08KV_NOT_PROVISIONED\uFF09"
6446
6719
  }
6447
6720
  };
6448
6721
  var CapabilityNotRegisteredError = class extends Error {
@@ -6502,6 +6775,19 @@ self.onmessage = async (event) => {
6502
6775
  };
6503
6776
  }
6504
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
+
6505
6791
  // src/sim/ctx.ts
6506
6792
  function createLocalPort(handler) {
6507
6793
  const listeners = [];
@@ -6531,17 +6817,6 @@ self.onmessage = async (event) => {
6531
6817
  }
6532
6818
  };
6533
6819
  }
6534
- function wireRpcResponses(port, pending) {
6535
- port.on((message) => {
6536
- const reply = message;
6537
- if (reply?.type !== "rpc-response" || typeof reply.id !== "number") return;
6538
- const entry = pending.get(reply.id);
6539
- if (entry === void 0) return;
6540
- pending.delete(reply.id);
6541
- if (reply.ok === true) entry.resolve(reply.result);
6542
- else entry.reject(new Error(reply.error ?? "RPC \u80FD\u529B\u8C03\u7528\u5931\u8D25"));
6543
- });
6544
- }
6545
6820
  function createFlatRpcClient(handler) {
6546
6821
  return new Proxy(
6547
6822
  {},
@@ -6755,7 +7030,7 @@ self.onmessage = async (event) => {
6755
7030
  return { run };
6756
7031
  }
6757
7032
 
6758
- // src/sim/runtime.ts
7033
+ // ../runtime/src/sim/merge.ts
6759
7034
  function mergeBundles(bundles) {
6760
7035
  const capabilities = [];
6761
7036
  const byName = /* @__PURE__ */ new Map();
@@ -6775,6 +7050,108 @@ self.onmessage = async (event) => {
6775
7050
  }
6776
7051
  return { capabilities, rpcHandlers };
6777
7052
  }
7053
+
7054
+ // ../runtime/src/database/sdk/kv.ts
7055
+ var KV_TABLE = "_adep_kv";
7056
+ var KV_RPC = {
7057
+ get: "get",
7058
+ put: "put",
7059
+ delete: "delete",
7060
+ list: "list"
7061
+ };
7062
+ var KV_CAPABILITY_CODES = {
7063
+ invalidMethod: "KV_INVALID_METHOD"
7064
+ };
7065
+ var KV_LIST_DEFAULT_LIMIT = 100;
7066
+ function nowSeconds() {
7067
+ return Math.floor(Date.now() / 1e3);
7068
+ }
7069
+ function errorWithCode2(error) {
7070
+ const code = typeof error === "object" && error !== null && typeof error.code === "string" ? error.code : void 0;
7071
+ if (code !== void 0 && error instanceof Error) {
7072
+ return new Error(`[${code}] ${error.message}`);
7073
+ }
7074
+ return error;
7075
+ }
7076
+ function createKvCapability(driver) {
7077
+ let ensurePromise = null;
7078
+ const ensureTable = () => {
7079
+ if (ensurePromise === null) {
7080
+ ensurePromise = driver.run(
7081
+ `CREATE TABLE IF NOT EXISTS ${KV_TABLE} (key TEXT PRIMARY KEY, value TEXT, expires_at INTEGER)`
7082
+ ).then(() => void 0);
7083
+ }
7084
+ return ensurePromise;
7085
+ };
7086
+ const handler = async (method, args) => {
7087
+ try {
7088
+ await ensureTable();
7089
+ switch (method) {
7090
+ case KV_RPC.get: {
7091
+ const [key] = args;
7092
+ const row = await driver.get(`SELECT value, expires_at FROM ${KV_TABLE} WHERE key = ?`, [
7093
+ key
7094
+ ]);
7095
+ if (row === null || row === void 0) return null;
7096
+ const expiresAt = row.expires_at;
7097
+ if (expiresAt !== null && expiresAt !== void 0 && Number(expiresAt) <= nowSeconds()) {
7098
+ return null;
7099
+ }
7100
+ return row.value;
7101
+ }
7102
+ case KV_RPC.put: {
7103
+ const [key, value, options] = args;
7104
+ const ttl = options?.expirationTtlSeconds;
7105
+ const expiresAt = typeof ttl === "number" && ttl > 0 ? nowSeconds() + Math.floor(ttl) : null;
7106
+ await driver.run(`DELETE FROM ${KV_TABLE} WHERE key = ?`, [key]);
7107
+ await driver.run(`INSERT INTO ${KV_TABLE} (key, value, expires_at) VALUES (?, ?, ?)`, [
7108
+ key,
7109
+ value,
7110
+ expiresAt
7111
+ ]);
7112
+ return void 0;
7113
+ }
7114
+ case KV_RPC.delete: {
7115
+ const [key] = args;
7116
+ await driver.run(`DELETE FROM ${KV_TABLE} WHERE key = ?`, [key]);
7117
+ return void 0;
7118
+ }
7119
+ case KV_RPC.list: {
7120
+ const [options] = args;
7121
+ const prefix = options?.prefix ?? "";
7122
+ const limit = options?.limit ?? KV_LIST_DEFAULT_LIMIT;
7123
+ const rows = prefix === "" ? await driver.all(`SELECT key, expires_at FROM ${KV_TABLE}`) : await driver.all(`SELECT key, expires_at FROM ${KV_TABLE} WHERE key LIKE ?`, [
7124
+ `${prefix}%`
7125
+ ]);
7126
+ const now = nowSeconds();
7127
+ const entries = [];
7128
+ for (const row of rows) {
7129
+ const expiresAt = row.expires_at;
7130
+ if (expiresAt !== null && expiresAt !== void 0 && Number(expiresAt) <= now) continue;
7131
+ entries.push({
7132
+ name: String(row.key),
7133
+ expiration: expiresAt === null || expiresAt === void 0 ? null : Number(expiresAt)
7134
+ });
7135
+ if (entries.length >= limit) break;
7136
+ }
7137
+ return entries;
7138
+ }
7139
+ default:
7140
+ throw new Error(
7141
+ `\u672A\u77E5\u7684 cloud.kv \u65B9\u6CD5 "${String(method)}"\uFF08${KV_CAPABILITY_CODES.invalidMethod}\uFF09`
7142
+ );
7143
+ }
7144
+ } catch (error) {
7145
+ throw errorWithCode2(error);
7146
+ }
7147
+ };
7148
+ return {
7149
+ capabilities: [{ name: "kv", value: { [RPC_CAPABILITY_KEY]: true } }],
7150
+ rpcHandlers: { kv: handler }
7151
+ };
7152
+ }
7153
+
7154
+ // src/sim/runtime.ts
6778
7155
  async function createBrowserSimRuntime(options) {
6779
7156
  const kv = options.kv ?? pickDefaultSimKv();
6780
7157
  const db = createBrowserSimDb({ projectId: options.projectId, kv });
@@ -6789,7 +7166,8 @@ self.onmessage = async (event) => {
6789
7166
  projectId: options.projectId,
6790
7167
  ...options.realtime === void 0 ? {} : options.realtime
6791
7168
  });
6792
- const bundle = mergeBundles([db.bundle, storage.bundle, realtime.bundle]);
7169
+ const kvBundle = createKvCapability(db.driver);
7170
+ const bundle = mergeBundles([db.bundle, storage.bundle, kvBundle, realtime.bundle]);
6793
7171
  await db.engine.load();
6794
7172
  const runner = createOfflineFunctionRunner(options.runner ?? {});
6795
7173
  return {