@adep/web-container 0.2.1 → 0.2.2

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.
@@ -28,9 +28,15 @@ export interface OfflineRunner {
28
28
  /**
29
29
  * 浏览器端 fetch 拦截器脚本(IIFE,纯 JS——必须能直接内联进 `<script>`,不能有 TS / ESM)。
30
30
  *
31
- * **同步纪律**:本字符串与 `shared/sdk/web-project-template.ts` 里 web/vite.config.ts 模板的
32
- * `FN_PROXY_SCRIPT` 必须**逐字一致**(两处复制粘贴,无共享 import——预览 iframe 与浏览器内
33
- * vite-dev 是两个独立加载上下文,共享一份源码反而会引入打包耦合)。改这里务必同步另一侧。
31
+ * **同步纪律**:本字符串与 `packages/vite-plugin/src/ide-proxy.ts`
32
+ * `FN_FETCH_INTERCEPTOR_SCRIPT`(单一真相源)必须**逐字一致**(两处复制粘贴,无共享
33
+ * import——预览 iframe 与浏览器内 vite-dev 是两个独立加载上下文,共享一份源码反而会引入
34
+ * 打包耦合)。改这里务必同步另一侧。
35
+ *
36
+ * blob base 兼容(2026-09-11 修复):浏览器内 vite-dev 预览文档的 baseURI 是 blob: URL,
37
+ * `new URL('/api/x', 'blob:…')` 抛 TypeError → 拦截器退化成原生 fetch 报
38
+ * "Failed to parse URL"。首次解析失败时用 `window.location.origin`(blob 继承创建者
39
+ * origin)兜底再解析一次。
34
40
  */
35
41
  export declare const FN_FETCH_INTERCEPTOR_SCRIPT: string;
36
42
  /** IDE 主线程侧的消息处理器类型:直接挂到 `window.addEventListener('message', handler)`。 */
package/dist/index.js CHANGED
@@ -105,6 +105,27 @@ function withinPackage(pkgDirAbs, rel, context) {
105
105
  }
106
106
  return abs;
107
107
  }
108
+ function pickConditionTarget(conditions, order, resolveRel, context) {
109
+ for (const key of order) {
110
+ const value = conditions[key];
111
+ if (value === void 0 || value === null) continue;
112
+ if (typeof value === "string") return resolveRel(value);
113
+ if (typeof value === "object" && !Array.isArray(value)) {
114
+ const nested = pickConditionTarget(
115
+ value,
116
+ order,
117
+ resolveRel,
118
+ context
119
+ );
120
+ if (nested !== null) return nested;
121
+ continue;
122
+ }
123
+ throw new ResolveError(
124
+ `${context}\uFF1Aexports["."]["${key}"] \u5F62\u6001\u4E0D\u652F\u6301\uFF08\u4EC5\u63A5\u53D7\u5B57\u7B26\u4E32 / null / \u6761\u4EF6\u5BF9\u8C61\uFF09`
125
+ );
126
+ }
127
+ return null;
128
+ }
108
129
  function resolvePackageEntry(meta, pkgDirAbs, condition) {
109
130
  const context = `\u5305 ${meta.name ?? meta.version ?? pkgDirAbs} \u7684 exports/main \u58F0\u660E`;
110
131
  const exp = meta.exports;
@@ -125,20 +146,11 @@ function resolvePackageEntry(meta, pkgDirAbs, condition) {
125
146
  if (typeof dot !== "object" || Array.isArray(dot)) {
126
147
  throw new ResolveError(`${context}\uFF1Aexports["."] \u5F62\u6001\u4E0D\u652F\u6301\uFF08\u4EC5\u63A5\u53D7\u5B57\u7B26\u4E32\u6216\u6761\u4EF6\u5BF9\u8C61\uFF09`);
127
148
  }
128
- const conditions = dot;
129
- const order = condition === "import" ? ["import", "node", "default"] : ["require", "node", "default"];
130
- for (const key of order) {
131
- const value = conditions[key];
132
- if (typeof value === "string") return withinPackage(pkgDirAbs, value, context);
133
- if (value !== void 0 && (typeof value !== "object" || value === null)) {
134
- throw new ResolveError(
135
- `${context}\uFF1Aexports["."]["${key}"] \u5F62\u6001\u4E0D\u652F\u6301\uFF08\u4EC5\u63A5\u53D7\u5B57\u7B26\u4E32\u6216 null\uFF09`
136
- );
137
- }
138
- }
139
- throw new ResolveError(
140
- `${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`
141
- );
149
+ const order = condition === "import" ? IMPORT_CONDITION_ORDER : REQUIRE_CONDITION_ORDER;
150
+ const resolveRel = (rel) => withinPackage(pkgDirAbs, rel, context);
151
+ const hit = pickConditionTarget(dot, order, resolveRel, context);
152
+ if (hit !== null) return hit;
153
+ throw new ResolveError(`${context}\uFF1Aexports["."] \u6761\u4EF6\u5BF9\u8C61\u91CC\u6CA1\u6709 ${order.join("/")} \u5165\u53E3`);
142
154
  }
143
155
  if (meta.main !== void 0) {
144
156
  if (typeof meta.main !== "string") {
@@ -236,11 +248,13 @@ function resolveImport(fs, specifier, fromFileAbs, condition = "require") {
236
248
  `\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`
237
249
  );
238
250
  }
239
- var RESOLVE_EXTENSIONS, ResolveError;
251
+ var IMPORT_CONDITION_ORDER, REQUIRE_CONDITION_ORDER, RESOLVE_EXTENSIONS, ResolveError;
240
252
  var init_node_resolve = __esm({
241
253
  "packages/web-container/src/node-resolve.ts"() {
242
254
  "use strict";
243
255
  init_path();
256
+ IMPORT_CONDITION_ORDER = ["browser", "import", "module", "default"];
257
+ REQUIRE_CONDITION_ORDER = ["require", "node", "default"];
244
258
  RESOLVE_EXTENSIONS = [".js", ".mjs", ".cjs", ".json"];
245
259
  ResolveError = class extends Error {
246
260
  constructor(message) {
@@ -515,6 +529,7 @@ var init_strip_types = __esm({
515
529
  return;
516
530
  case "as":
517
531
  case "satisfies": {
532
+ if (this.frames[this.frames.length - 1]?.specifierList === true) return;
518
533
  if (this.tokenAt(2) === "*" || this.tokenAt(2) === "type") return;
519
534
  const nextChar = this.skipWhitespaceFrom(this.i);
520
535
  if (nextChar === null || "=,:]})".includes(this.src[nextChar])) return;
@@ -567,7 +582,8 @@ var init_strip_types = __esm({
567
582
  const frame = {
568
583
  kind: c === "(" ? "paren" : c === "{" ? "brace" : "bracket",
569
584
  declare: false,
570
- classBody: false
585
+ classBody: false,
586
+ specifierList: c === "{" && this.isImportSpecifierBrace()
571
587
  };
572
588
  if (c === "{" && this.pendingClassBody) {
573
589
  frame.classBody = true;
@@ -653,6 +669,15 @@ var init_strip_types = __esm({
653
669
  this.i = end;
654
670
  return true;
655
671
  }
672
+ /**
673
+ * `{` 是 import / export 的说明符列表吗——`{ a as b }` 覆盖 `import { … }`、
674
+ * `export { … }` 与 `import def, { … }` 三种写法(后者说明符花括号的前驱 token 是 `,`)。
675
+ */
676
+ isImportSpecifierBrace() {
677
+ const back1 = this.tokenAt(1);
678
+ if (back1 === "import" || back1 === "export") return true;
679
+ return back1 === "," && this.tokenAt(3) === "import";
680
+ }
656
681
  /**
657
682
  * `(` 是否为参数列表(声明位):`function f(`、类体里的方法 `m(`、箭头 `=> (`,
658
683
  * 以及出现在 `(` `,` `=` `>` `:` `[` `{` `}` `;` 之后(如 `map((v: T) => v)` 的内层括号)。
@@ -664,6 +689,7 @@ var init_strip_types = __esm({
664
689
  if (this.afterFunctionKeyword) return true;
665
690
  if (this.tokenAt(2) === "function" && IDENT_START.test(this.tokenAt(1))) return true;
666
691
  if (this.afterArrow) return true;
692
+ if (this.tokenAt(1) === "return") return true;
667
693
  if (this.inClassBody() && IDENT_PART.test(this.prevSignificantChar(at - 1))) return true;
668
694
  const prev = this.prevSignificantChar(at - 1);
669
695
  return prev === "" || "(,=>:[{};".includes(prev);
@@ -1172,7 +1198,10 @@ var init_function_fetch_proxy = __esm({
1172
1198
  " }",
1173
1199
  " var u;",
1174
1200
  " try { u = new URL(urlStr, document.baseURI); }",
1175
- " catch (e) { return ORIGINAL_FETCH(input, init); }",
1201
+ " catch (e0) {",
1202
+ " try { u = new URL(urlStr, window.location.origin); }",
1203
+ " catch (e1) { return ORIGINAL_FETCH(input, init); }",
1204
+ " }",
1176
1205
  ' if (u.pathname.indexOf("/api/") !== 0 && u.pathname !== "/api") {',
1177
1206
  " return ORIGINAL_FETCH(input, init);",
1178
1207
  " }",
@@ -1311,6 +1340,18 @@ function cssModule(css) {
1311
1340
  "export default css;"
1312
1341
  ].join("\n");
1313
1342
  }
1343
+ function inlineStyleBlock(css, key) {
1344
+ return [
1345
+ "{",
1346
+ `const css = ${JSON.stringify(css)};`,
1347
+ 'const el = document.createElement("style");',
1348
+ 'el.setAttribute("data-adep-vite", "");',
1349
+ `el.setAttribute("data-adep-style", ${JSON.stringify(key)});`,
1350
+ "el.textContent = css;",
1351
+ "document.head.appendChild(el);",
1352
+ "}"
1353
+ ].join("\n");
1354
+ }
1314
1355
  function jsonModule(source, abs) {
1315
1356
  try {
1316
1357
  JSON.parse(source);
@@ -1361,6 +1402,17 @@ function createViteServer(vfs, options = {}) {
1361
1402
  } catch {
1362
1403
  }
1363
1404
  };
1405
+ const DEFINES = [
1406
+ [/\bprocess\s*\.\s*env\s*\.\s*NODE_ENV\b/g, JSON.stringify("development")],
1407
+ [/\b__VUE_OPTIONS_API__\b/g, "true"],
1408
+ [/\b__VUE_PROD_DEVTOOLS__\b/g, "false"],
1409
+ [/\b__VUE_PROD_HYDRATION_MISMATCH_DETAILS__\b/g, "false"]
1410
+ ];
1411
+ function applyDefines(code) {
1412
+ let out = code;
1413
+ for (const [pattern, value] of DEFINES) out = out.replace(pattern, value);
1414
+ return out;
1415
+ }
1364
1416
  function transformModule2(abs, source) {
1365
1417
  if (abs.endsWith(".css")) return cssModule(source);
1366
1418
  if (abs.endsWith(".json")) return jsonModule(source, abs);
@@ -1371,21 +1423,27 @@ function createViteServer(vfs, options = {}) {
1371
1423
  `${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`
1372
1424
  );
1373
1425
  const compiled = sfcCompiler(source, abs);
1374
- return [...compiled.styles.map((css) => cssModule(css)), compiled.script].join("\n");
1426
+ return [
1427
+ ...compiled.styles.map((css, index) => inlineStyleBlock(css, `${abs}#${index}`)),
1428
+ applyDefines(stripTypes(compiled.script))
1429
+ ].join("\n");
1375
1430
  }
1376
1431
  if (abs.endsWith(".tsx") || abs.endsWith(".jsx"))
1377
1432
  throw new ViteDevError(
1378
1433
  "JSX_UNSUPPORTED",
1379
1434
  `${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`
1380
1435
  );
1381
- if (abs.endsWith(".ts") || abs.endsWith(".mts")) return stripTypes(source);
1382
- return source;
1436
+ if (abs.endsWith(".ts") || abs.endsWith(".mts")) return applyDefines(stripTypes(source));
1437
+ return applyDefines(source);
1438
+ }
1439
+ function absoluteFromRoot(spec) {
1440
+ return join(root, spec.replace(/^\/+/, ""));
1383
1441
  }
1384
1442
  async function resolveSpecifier(spec, importerAbs) {
1385
1443
  if (EXTERNAL_SPECIFIER.test(spec) || DATA_OR_BLOB.test(spec)) return spec;
1386
1444
  let target;
1387
1445
  if (spec.startsWith("./") || spec.startsWith("../") || spec.startsWith("/")) {
1388
- const base = spec.startsWith("/") ? spec : join(dirname(importerAbs), spec);
1446
+ const base = spec.startsWith("/") ? absoluteFromRoot(spec) : join(dirname(importerAbs), spec);
1389
1447
  target = resolveWithSuffixes(vfs, base);
1390
1448
  if (!vfs.exists(target) || vfs.isDirectory(target))
1391
1449
  throw new ViteDevError(
@@ -1474,7 +1532,7 @@ function createViteServer(vfs, options = {}) {
1474
1532
  html = html.replace(
1475
1533
  MODULE_SCRIPT_SRC_RE,
1476
1534
  (_whole, pre, _q1, mid, _q2, src, post) => {
1477
- const base = src.startsWith("/") ? normalize(src) : join(dirname(entryAbs), src);
1535
+ const base = src.startsWith("/") ? absoluteFromRoot(src) : join(dirname(entryAbs), src);
1478
1536
  const abs = resolveWithSuffixes(vfs, base);
1479
1537
  if (!vfs.exists(abs) || vfs.isDirectory(abs))
1480
1538
  throw new ViteDevError(
@@ -1482,7 +1540,7 @@ function createViteServer(vfs, options = {}) {
1482
1540
  `\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`
1483
1541
  );
1484
1542
  entryModuleUrls.push(loadModuleUrl(abs));
1485
- return `<script${pre}${mid}src="__adep_vite_entry_${entryModuleUrls.length - 1}__"${post}></script>`;
1543
+ return `<script${pre}${mid}src="__adep_vite_entry_${entryModuleUrls.length - 1}__"${post} type="module"></script>`;
1486
1544
  }
1487
1545
  );
1488
1546
  html = html.replace(
@@ -1492,7 +1550,7 @@ function createViteServer(vfs, options = {}) {
1492
1550
  const abs = `${INLINE_PREFIX}${inlineSeq++}.mjs`;
1493
1551
  inlineSources.set(abs, body);
1494
1552
  entryModuleUrls.push(loadModuleUrl(abs));
1495
- return `<script${pre}${post}src="__adep_vite_entry_${entryModuleUrls.length - 1}__"></script>`;
1553
+ return `<script${pre}${post}src="__adep_vite_entry_${entryModuleUrls.length - 1}__" type="module"></script>`;
1496
1554
  }
1497
1555
  );
1498
1556
  const urls = await Promise.all(entryModuleUrls);
@@ -4459,6 +4517,8 @@ var CHAIN_CAPABILITY_KEY = "__adepChain";
4459
4517
  var DB_RPC = {
4460
4518
  /** 直通方法(如 query):`(method=query, args=[sql, params])`。 */
4461
4519
  query: "query",
4520
+ /** 写路径(CF-013):`(method=writeQuery, args=[sql, params])` → `{ changes }`。 */
4521
+ writeQuery: "writeQuery",
4462
4522
  /** 读取 owned 表变更流(DB-006):`(method=changes, args=[table, query])`。 */
4463
4523
  changes: "changes",
4464
4524
  /** 开启事务:`begin → txId`。 */
@@ -4535,21 +4595,35 @@ function unsafeOperation(message) {
4535
4595
  function stripLiterals(sql) {
4536
4596
  return sql.replace(/'[^']*(?:''[^']*)*'/g, "").replace(/"[^"]*(?:""[^"]*)*"/g, "").replace(/--[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
4537
4597
  }
4538
- function assertReadOnlyQuery(sql) {
4598
+ function sqlHead(sql) {
4599
+ return sql.trim().replace(/^\(+/, "").trim().toUpperCase();
4600
+ }
4601
+ function assertSafeStatement(sql) {
4539
4602
  const stripped = stripLiterals(sql);
4540
4603
  if (stripped.includes(";")) {
4541
4604
  throw unsafeOperation("\u4EC5\u5141\u8BB8\u5355\u6761\u8BED\u53E5\uFF0C\u7981\u6B62\u591A\u8BED\u53E5\u5806\u53E0");
4542
4605
  }
4543
- const head = stripped.trim().replace(/^\(+/, "").trim().toUpperCase();
4544
- if (!head.startsWith("SELECT") && !head.startsWith("WITH")) {
4545
- throw unsafeOperation("cloud.db.query \u4EC5\u5141\u8BB8\u53EA\u8BFB\u67E5\u8BE2\uFF08SELECT / WITH\uFF09");
4546
- }
4547
4606
  if (/ATTACH|DETACH/.test(stripped.toUpperCase())) {
4548
4607
  throw unsafeOperation("\u7981\u6B62\u8DE8\u5E93\u64CD\u4F5C\uFF08ATTACH / DETACH\uFF09");
4549
4608
  }
4550
4609
  if (/SQLITE_\w+/i.test(stripped)) {
4551
4610
  throw unsafeOperation("\u7981\u6B62\u8BBF\u95EE\u7CFB\u7EDF\u8868\uFF08sqlite_*\uFF09");
4552
4611
  }
4612
+ return sqlHead(stripped);
4613
+ }
4614
+ function assertReadOnlyQuery(sql) {
4615
+ const head = assertSafeStatement(sql);
4616
+ if (!head.startsWith("SELECT") && !head.startsWith("WITH")) {
4617
+ throw unsafeOperation("cloud.db.query \u4EC5\u5141\u8BB8\u53EA\u8BFB\u67E5\u8BE2\uFF08SELECT / WITH\uFF09");
4618
+ }
4619
+ }
4620
+ function assertWriteQuery(sql) {
4621
+ const head = assertSafeStatement(sql);
4622
+ if (!head.startsWith("INSERT") && !head.startsWith("UPDATE") && !head.startsWith("DELETE")) {
4623
+ throw unsafeOperation(
4624
+ "cloud.db.writeQuery \u4EC5\u5141\u8BB8 DML\uFF08INSERT / UPDATE / DELETE\uFF09\uFF0CDDL \u8BF7\u8D70\u63A7\u5236\u53F0"
4625
+ );
4626
+ }
4553
4627
  }
4554
4628
 
4555
4629
  // packages/runtime/src/database/builder/owned.ts
@@ -5095,6 +5169,14 @@ function createCloudDb(driver, options = {}) {
5095
5169
  assertReadOnlyQuery(sql);
5096
5170
  return driver.all(sql, params);
5097
5171
  },
5172
+ async writeQuery(sql, params) {
5173
+ if (!Array.isArray(params)) {
5174
+ throw unsafeOperation("writeQuery \u7684 params \u5FC5\u987B\u63D0\u4F9B\uFF08\u65E0\u53C2\u4F20 []\uFF09");
5175
+ }
5176
+ guardWrite();
5177
+ assertWriteQuery(sql);
5178
+ return driver.run(sql, params);
5179
+ },
5098
5180
  async changes(table, query = {}) {
5099
5181
  return readChanges(driver, table, query);
5100
5182
  }
@@ -5110,7 +5192,8 @@ var CLOUD_DB_SPEC = {
5110
5192
  rootMethod: "table",
5111
5193
  stepMethods: ["select", "where", "orderBy", "limit", "offset"],
5112
5194
  terminalMethods: ["get", "first", "count", "insert", "insertMany", "update", "delete"],
5113
- directMethods: ["query", "changes"],
5195
+ // CF-013:writeQuery 是 D1 shim 的写路径(INSERT/UPDATE/DELETE),与 query 并列直通方法。
5196
+ directMethods: ["query", "writeQuery", "changes"],
5114
5197
  transactionMethod: "transaction"
5115
5198
  };
5116
5199
  var WRITE_TERMINALS = /* @__PURE__ */ new Set(["insert", "insertMany", "update", "delete"]);
@@ -5165,6 +5248,10 @@ function createDbCapability(driver, options = {}) {
5165
5248
  const [sql, params] = args;
5166
5249
  return await db.query(sql, params);
5167
5250
  }
5251
+ case DB_RPC.writeQuery: {
5252
+ const [sql, params] = args;
5253
+ return await db.writeQuery(sql, params);
5254
+ }
5168
5255
  case DB_RPC.changes: {
5169
5256
  const [table, query] = args;
5170
5257
  return await db.changes(table, query);
@@ -5894,9 +5981,12 @@ function createStorageCapability(driver, signer, projectId) {
5894
5981
  const handler = async (method, args) => {
5895
5982
  switch (method) {
5896
5983
  case "upload": {
5897
- const [path, data] = args;
5984
+ const [path, data, options] = args;
5898
5985
  return toStoredFile(
5899
- await driver.put(projectId, path, asBytes(data), { visibility: DEFAULT_VISIBILITY })
5986
+ await driver.put(projectId, path, asBytes(data), {
5987
+ visibility: DEFAULT_VISIBILITY,
5988
+ ...options?.contentType === void 0 ? {} : { contentType: options.contentType }
5989
+ })
5900
5990
  );
5901
5991
  }
5902
5992
  case "get": {
@@ -6343,6 +6433,10 @@ var KNOWN_CAPABILITY_HINTS = {
6343
6433
  realtime: {
6344
6434
  code: "REALTIME_NOT_AVAILABLE",
6345
6435
  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"
6436
+ },
6437
+ kv: {
6438
+ code: "KV_NOT_PROVISIONED",
6439
+ 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"
6346
6440
  }
6347
6441
  };
6348
6442
  var CapabilityNotRegisteredError = class extends Error {
@@ -6655,6 +6749,106 @@ function createOfflineFunctionRunner(options = {}) {
6655
6749
  return { run };
6656
6750
  }
6657
6751
 
6752
+ // packages/runtime/src/database/sdk/kv.ts
6753
+ var KV_TABLE = "_adep_kv";
6754
+ var KV_RPC = {
6755
+ get: "get",
6756
+ put: "put",
6757
+ delete: "delete",
6758
+ list: "list"
6759
+ };
6760
+ var KV_CAPABILITY_CODES = {
6761
+ invalidMethod: "KV_INVALID_METHOD"
6762
+ };
6763
+ var KV_LIST_DEFAULT_LIMIT = 100;
6764
+ function nowSeconds() {
6765
+ return Math.floor(Date.now() / 1e3);
6766
+ }
6767
+ function errorWithCode2(error) {
6768
+ const code = typeof error === "object" && error !== null && typeof error.code === "string" ? error.code : void 0;
6769
+ if (code !== void 0 && error instanceof Error) {
6770
+ return new Error(`[${code}] ${error.message}`);
6771
+ }
6772
+ return error;
6773
+ }
6774
+ function createKvCapability(driver) {
6775
+ let ensurePromise = null;
6776
+ const ensureTable = () => {
6777
+ if (ensurePromise === null) {
6778
+ ensurePromise = driver.run(
6779
+ `CREATE TABLE IF NOT EXISTS ${KV_TABLE} (key TEXT PRIMARY KEY, value TEXT, expires_at INTEGER)`
6780
+ ).then(() => void 0);
6781
+ }
6782
+ return ensurePromise;
6783
+ };
6784
+ const handler = async (method, args) => {
6785
+ try {
6786
+ await ensureTable();
6787
+ switch (method) {
6788
+ case KV_RPC.get: {
6789
+ const [key] = args;
6790
+ const row = await driver.get(`SELECT value, expires_at FROM ${KV_TABLE} WHERE key = ?`, [
6791
+ key
6792
+ ]);
6793
+ if (row === null || row === void 0) return null;
6794
+ const expiresAt = row.expires_at;
6795
+ if (expiresAt !== null && expiresAt !== void 0 && Number(expiresAt) <= nowSeconds()) {
6796
+ return null;
6797
+ }
6798
+ return row.value;
6799
+ }
6800
+ case KV_RPC.put: {
6801
+ const [key, value, options] = args;
6802
+ const ttl = options?.expirationTtlSeconds;
6803
+ const expiresAt = typeof ttl === "number" && ttl > 0 ? nowSeconds() + Math.floor(ttl) : null;
6804
+ await driver.run(`DELETE FROM ${KV_TABLE} WHERE key = ?`, [key]);
6805
+ await driver.run(`INSERT INTO ${KV_TABLE} (key, value, expires_at) VALUES (?, ?, ?)`, [
6806
+ key,
6807
+ value,
6808
+ expiresAt
6809
+ ]);
6810
+ return void 0;
6811
+ }
6812
+ case KV_RPC.delete: {
6813
+ const [key] = args;
6814
+ await driver.run(`DELETE FROM ${KV_TABLE} WHERE key = ?`, [key]);
6815
+ return void 0;
6816
+ }
6817
+ case KV_RPC.list: {
6818
+ const [options] = args;
6819
+ const prefix = options?.prefix ?? "";
6820
+ const limit = options?.limit ?? KV_LIST_DEFAULT_LIMIT;
6821
+ 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 ?`, [
6822
+ `${prefix}%`
6823
+ ]);
6824
+ const now = nowSeconds();
6825
+ const entries = [];
6826
+ for (const row of rows) {
6827
+ const expiresAt = row.expires_at;
6828
+ if (expiresAt !== null && expiresAt !== void 0 && Number(expiresAt) <= now) continue;
6829
+ entries.push({
6830
+ name: String(row.key),
6831
+ expiration: expiresAt === null || expiresAt === void 0 ? null : Number(expiresAt)
6832
+ });
6833
+ if (entries.length >= limit) break;
6834
+ }
6835
+ return entries;
6836
+ }
6837
+ default:
6838
+ throw new Error(
6839
+ `\u672A\u77E5\u7684 cloud.kv \u65B9\u6CD5 "${String(method)}"\uFF08${KV_CAPABILITY_CODES.invalidMethod}\uFF09`
6840
+ );
6841
+ }
6842
+ } catch (error) {
6843
+ throw errorWithCode2(error);
6844
+ }
6845
+ };
6846
+ return {
6847
+ capabilities: [{ name: "kv", value: { [RPC_CAPABILITY_KEY]: true } }],
6848
+ rpcHandlers: { kv: handler }
6849
+ };
6850
+ }
6851
+
6658
6852
  // packages/web-container/src/sim/runtime.ts
6659
6853
  function mergeBundles(bundles) {
6660
6854
  const capabilities = [];
@@ -6689,7 +6883,8 @@ async function createBrowserSimRuntime(options) {
6689
6883
  projectId: options.projectId,
6690
6884
  ...options.realtime === void 0 ? {} : options.realtime
6691
6885
  });
6692
- const bundle = mergeBundles([db.bundle, storage.bundle, realtime.bundle]);
6886
+ const kvBundle = createKvCapability(db.driver);
6887
+ const bundle = mergeBundles([db.bundle, storage.bundle, kvBundle, realtime.bundle]);
6693
6888
  await db.engine.load();
6694
6889
  const runner = createOfflineFunctionRunner(options.runner ?? {});
6695
6890
  return {
@@ -11,6 +11,8 @@
11
11
  * (真 live binding、真模块隔离),不是 CJS 打平;
12
12
  * - **转换**:`.ts` 复用 stripTypes 擦类型;`.css` 变成「注入 `<style>` 的副作用模块」;
13
13
  * `.json` 变成默认导出对象;`.vue` 经注入的 SFC 编译器(IDE 侧装 `vue/compiler-sfc`);
14
+ * JS 系产物统一过 define 替换(`process.env.NODE_ENV` / `__VUE_*__`),让 esm-bundler
15
+ * 构建(vue 等)在无打包器的浏览器里也能跑;
14
16
  * - **依赖**:裸说明符经 node-resolve 命中 VFS `node_modules/`(终端 `npm install` 的产物),
15
17
  * 与 CLI / 线上同一套解析规则;相对说明符支持 Vite 风格省略扩展名(.ts/.js/.vue/…);
16
18
  * - **HMR**:订阅 VFS 变更(`vfs.onMutate`,IDE 保存 / shell 写入 / npm 解包皆触发)→