@adep/cli 0.4.0 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vite.js CHANGED
@@ -15,6 +15,8 @@ var DB_RPC = {
15
15
  query: "query",
16
16
  /** 写路径(CF-013):`(method=writeQuery, args=[sql, params])` → `{ changes }`。 */
17
17
  writeQuery: "writeQuery",
18
+ /** 通用执行(BUG-4,2026-10-14):`(method=execute, args=[sql, params?])` → `{ changes }`。 */
19
+ execute: "execute",
18
20
  /** 读取 owned 表变更流(DB-006):`(method=changes, args=[table, query])`。 */
19
21
  changes: "changes",
20
22
  /** 开启事务:`begin → txId`。 */
@@ -62,6 +64,30 @@ function normalizeHttpStatus(status) {
62
64
  }
63
65
 
64
66
  // packages/runtime/src/shared/http-envelope.ts
67
+ function coerceAdepHttpEnvelope(value) {
68
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
69
+ if (isAdepHttpEnvelope(value)) return value;
70
+ const keys = Object.keys(value);
71
+ if (keys.length === 0) return null;
72
+ if (!keys.every((key) => key === "status" || key === "body" || key === "headers")) return null;
73
+ const candidate = value;
74
+ const status = candidate.status;
75
+ if (typeof status !== "number" || !Number.isInteger(status) || status < 100 || status > 599) {
76
+ return null;
77
+ }
78
+ if ("headers" in candidate && candidate.headers !== void 0) {
79
+ const headers = candidate.headers;
80
+ if (typeof headers !== "object" || headers === null || Array.isArray(headers)) return null;
81
+ if (!Object.values(headers).every((headerValue2) => typeof headerValue2 === "string")) return null;
82
+ }
83
+ return {
84
+ __adepHttp: {
85
+ status,
86
+ ...candidate.headers === void 0 ? {} : { headers: candidate.headers },
87
+ body: candidate.body
88
+ }
89
+ };
90
+ }
65
91
  function isAdepHttpEnvelope(value) {
66
92
  if (typeof value !== "object" || value === null) return false;
67
93
  const envelope = value.__adepHttp;
@@ -454,6 +480,14 @@ function assertWriteQuery(sql) {
454
480
  );
455
481
  }
456
482
  }
483
+ function classifyStatement(sql) {
484
+ const head = sqlHead(stripLiterals(sql));
485
+ if (head.startsWith("SELECT") || head.startsWith("WITH")) return "read";
486
+ if (head.startsWith("INSERT") || head.startsWith("UPDATE") || head.startsWith("DELETE")) {
487
+ return "write";
488
+ }
489
+ return "ddl";
490
+ }
457
491
 
458
492
  // packages/runtime/src/database/builder/owned.ts
459
493
  var OWNED_PK = "id";
@@ -1006,6 +1040,21 @@ function createCloudDb(driver, options = {}) {
1006
1040
  assertWriteQuery(sql);
1007
1041
  return driver.run(sql, params);
1008
1042
  },
1043
+ async execute(sql, params = []) {
1044
+ if (sql.trim() === "") {
1045
+ throw unsafeOperation("execute \u7684 sql \u4E0D\u80FD\u4E3A\u7A7A");
1046
+ }
1047
+ if (!Array.isArray(params)) {
1048
+ throw unsafeOperation("execute \u7684 params \u5FC5\u987B\u662F\u6570\u7EC4\uFF08\u65E0\u53C2\u53EF\u4E0D\u4F20\uFF09");
1049
+ }
1050
+ guardWrite();
1051
+ const kind = classifyStatement(sql);
1052
+ if (kind === "read") {
1053
+ await driver.all(sql, params);
1054
+ return { changes: 0 };
1055
+ }
1056
+ return driver.run(sql, params);
1057
+ },
1009
1058
  async changes(table, query = {}) {
1010
1059
  return readChanges(driver, table, query);
1011
1060
  }
@@ -1081,6 +1130,10 @@ function createDbCapability(driver, options = {}) {
1081
1130
  const [sql, params] = args;
1082
1131
  return await db.writeQuery(sql, params);
1083
1132
  }
1133
+ case DB_RPC.execute: {
1134
+ const [sql, params] = args;
1135
+ return await db.execute(sql, params ?? []);
1136
+ }
1084
1137
  case DB_RPC.changes: {
1085
1138
  const [table, query] = args;
1086
1139
  return await db.changes(table, query);
@@ -3211,6 +3264,15 @@ function parseAdepConfig(raw) {
3211
3264
  data.environment = rec;
3212
3265
  }
3213
3266
  }
3267
+ if (input.db_environment !== void 0) {
3268
+ if (input.db_environment !== "dev" && input.db_environment !== "prod") {
3269
+ errors.push(
3270
+ `db_environment \u53EA\u63A5\u53D7 'dev' \u6216 'prod'\uFF0C\u6536\u5230 ${typeNameOf(input.db_environment)}\uFF08\u6CE8\u610F\u522B\u4E0E environment \u6DF7\u7528\uFF1A\u540E\u8005\u662F\u6CE8\u5165\u51FD\u6570\u8FD0\u884C\u65F6\u7684\u53D8\u91CF\u8868\uFF09`
3271
+ );
3272
+ } else {
3273
+ data.db_environment = input.db_environment;
3274
+ }
3275
+ }
3214
3276
  if (input.runtime !== void 0) {
3215
3277
  const rec = asRecord(input.runtime);
3216
3278
  if (rec === null) {
@@ -3717,6 +3779,29 @@ function transformExports(code) {
3717
3779
  return transformModule(code);
3718
3780
  }
3719
3781
  var SANDBOX_BUILTIN_REJECTED = "FN_SANDBOX_BUILTIN_BLOCKED";
3782
+ var REALM_VALUE_KEY = "__adepRealmValue";
3783
+ function realmValue(value) {
3784
+ return { [REALM_VALUE_KEY]: value };
3785
+ }
3786
+ function isRealmValue(value) {
3787
+ return typeof value === "object" && value !== null && REALM_VALUE_KEY in value;
3788
+ }
3789
+ function createSandboxContext(sandboxGlobals) {
3790
+ const direct = {};
3791
+ const realmEntries = {};
3792
+ for (const [key, value] of Object.entries(sandboxGlobals)) {
3793
+ if (isRealmValue(value)) realmEntries[key] = value[REALM_VALUE_KEY];
3794
+ else direct[key] = value;
3795
+ }
3796
+ const context = vm.createContext(direct, { codeGeneration: { strings: false, wasm: false } });
3797
+ if (Object.keys(realmEntries).length > 0) {
3798
+ const literal = JSON.stringify(JSON.stringify(realmEntries));
3799
+ vm.runInContext(`Object.assign(globalThis, JSON.parse(${literal}))`, context, {
3800
+ filename: "<sandbox-realm-globals>"
3801
+ });
3802
+ }
3803
+ return context;
3804
+ }
3720
3805
  function resolveModulePath(fromDir, request, files) {
3721
3806
  if (!request.startsWith("./") && !request.startsWith("../")) return null;
3722
3807
  const base = joinPosix(fromDir, request);
@@ -3790,12 +3875,13 @@ function loadModule(path, files, sandboxGlobals, cache, resolveBare, bareCache)
3790
3875
  } catch (error) {
3791
3876
  throw syntaxErrorWithLocation(error, path);
3792
3877
  }
3878
+ const context = createSandboxContext(sandboxGlobals);
3793
3879
  let wrapper;
3794
3880
  try {
3795
- wrapper = vm.runInNewContext(
3881
+ wrapper = vm.runInContext(
3796
3882
  `(function (module, exports, require) { ${transformed}
3797
3883
  })`,
3798
- vm.createContext({ ...sandboxGlobals }),
3884
+ context,
3799
3885
  { filename: path }
3800
3886
  );
3801
3887
  } catch (error) {
@@ -3975,6 +4061,8 @@ var SandboxFetchError = class extends Error {
3975
4061
  }
3976
4062
  };
3977
4063
  var DEFAULT_MAX_RESPONSE_BYTES = 100 * 1024 * 1024;
4064
+ var DEFAULT_MAX_REDIRECTS = 5;
4065
+ var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
3978
4066
  var defaultFetch = (url, init) => (
3979
4067
  // 真实 Response 与内部 ResponseLike 结构性一致;getReader 的多态签名差异属纯类型问题,运行语义等价。
3980
4068
  globalThis.fetch(url, init)
@@ -4013,18 +4101,24 @@ function headersToRecord(headers) {
4013
4101
  }
4014
4102
  return out;
4015
4103
  }
4016
- function contentLength(res) {
4017
- const headers = res.headers;
4104
+ function headerValue(headers, name) {
4018
4105
  if (headers === void 0 || headers === null) return null;
4106
+ let raw;
4019
4107
  if (typeof headers.get === "function") {
4020
- const raw2 = headers.get?.("content-length");
4021
- if (raw2 === void 0 || raw2 === null || raw2.trim() === "") return null;
4022
- const n2 = Number(raw2);
4023
- return Number.isFinite(n2) && n2 >= 0 ? n2 : null;
4024
- }
4025
- const record = headers;
4026
- const raw = record["content-length"] ?? record["Content-Length"];
4027
- if (raw === void 0 || raw === null) return null;
4108
+ raw = headers.get?.(name);
4109
+ } else {
4110
+ const record = headers;
4111
+ raw = record[name.toLocaleLowerCase()] ?? record[name];
4112
+ }
4113
+ if (raw === void 0 || raw === null || raw.trim() === "") return null;
4114
+ return raw;
4115
+ }
4116
+ function contentLength(res) {
4117
+ const raw = headerValue(
4118
+ res.headers,
4119
+ "content-length"
4120
+ );
4121
+ if (raw === null) return null;
4028
4122
  const n = Number(raw);
4029
4123
  return Number.isFinite(n) && n >= 0 ? n : null;
4030
4124
  }
@@ -4104,44 +4198,72 @@ function createSandboxFetch(options) {
4104
4198
  const fetchImpl = options.fetchImpl ?? defaultFetch;
4105
4199
  const { allowlist, timeoutMs } = options;
4106
4200
  const maxBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
4107
- return async (url, init) => {
4201
+ const maxRedirects = Math.max(0, options.maxRedirects ?? DEFAULT_MAX_REDIRECTS);
4202
+ const guard = (raw) => {
4108
4203
  let parsed;
4109
4204
  try {
4110
- parsed = new URL(url);
4205
+ parsed = new URL(raw);
4111
4206
  } catch {
4112
- throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 URL"${url}"`);
4207
+ throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 URL"${raw}"`);
4113
4208
  }
4114
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw FORBIDDEN(url);
4115
- if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(url);
4209
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw FORBIDDEN(raw);
4210
+ if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(raw);
4211
+ return parsed;
4212
+ };
4213
+ const fetchOnce = async (url, init) => {
4116
4214
  const controller = new AbortController();
4117
4215
  const timer = setTimeout(() => controller.abort(), timeoutMs);
4118
4216
  const signals = [controller.signal];
4119
4217
  if (init?.signal !== void 0) signals.push(init.signal);
4120
4218
  const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
4219
+ const request = {
4220
+ ...init,
4221
+ signal,
4222
+ redirect: "manual"
4223
+ };
4121
4224
  try {
4122
- let res;
4123
- try {
4124
- res = await fetchImpl(url, { ...init, signal });
4125
- } catch (error) {
4126
- if (controller.signal.aborted) {
4127
- throw new SandboxFetchError(
4128
- "FN_FETCH_TIMEOUT",
4129
- `cloud.fetch \u8BF7\u6C42\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeoutMs}ms\uFF09`
4130
- );
4131
- }
4132
- if (init?.signal?.aborted === true) {
4133
- throw new SandboxFetchError("FN_FETCH_ABORTED", "cloud.fetch \u8BF7\u6C42\u88AB\u8C03\u7528\u65B9\u4E2D\u6B62");
4134
- }
4135
- throw new SandboxFetchError(
4136
- "FN_FETCH_FAILED",
4137
- `cloud.fetch \u8BF7\u6C42\u5931\u8D25\uFF1A${errorMessageOf(error)}`
4138
- );
4225
+ return await fetchImpl(url, request);
4226
+ } catch (error) {
4227
+ if (controller.signal.aborted) {
4228
+ throw new SandboxFetchError("FN_FETCH_TIMEOUT", `cloud.fetch \u8BF7\u6C42\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeoutMs}ms\uFF09`);
4139
4229
  }
4140
- return normalizeResponse(res, maxBytes);
4230
+ if (init?.signal?.aborted === true) {
4231
+ throw new SandboxFetchError("FN_FETCH_ABORTED", "cloud.fetch \u8BF7\u6C42\u88AB\u8C03\u7528\u65B9\u4E2D\u6B62");
4232
+ }
4233
+ throw new SandboxFetchError(
4234
+ "FN_FETCH_FAILED",
4235
+ `cloud.fetch \u8BF7\u6C42\u5931\u8D25\uFF1A${errorMessageOf(error)}`
4236
+ );
4141
4237
  } finally {
4142
4238
  clearTimeout(timer);
4143
4239
  }
4144
4240
  };
4241
+ return async (url, init) => {
4242
+ let target = guard(url);
4243
+ let currentInit = init === void 0 ? void 0 : { ...init };
4244
+ let lastRes = null;
4245
+ for (let hop = 0; hop <= maxRedirects; hop++) {
4246
+ const res = await fetchOnce(target.href, currentInit);
4247
+ const location = REDIRECT_STATUSES.has(res.status) ? headerValue(res.headers, "location") : null;
4248
+ if (location === null || hop === maxRedirects) {
4249
+ lastRes = res;
4250
+ break;
4251
+ }
4252
+ let next;
4253
+ try {
4254
+ next = new URL(location, target.href);
4255
+ } catch {
4256
+ lastRes = res;
4257
+ break;
4258
+ }
4259
+ if (res.status !== 307 && res.status !== 308 && currentInit !== void 0) {
4260
+ currentInit.method = "GET";
4261
+ delete currentInit.body;
4262
+ }
4263
+ target = guard(next.href);
4264
+ }
4265
+ return normalizeResponse(lastRes, maxBytes);
4266
+ };
4145
4267
  }
4146
4268
 
4147
4269
  // packages/runtime/src/functions/runtime/flat-rpc-proxy.ts
@@ -4280,7 +4402,9 @@ function errorMessageOf2(error) {
4280
4402
  return String(error);
4281
4403
  }
4282
4404
  async function normalizeResultBody(value) {
4283
- if (!(value instanceof Response)) return value;
4405
+ if (!(value instanceof Response)) {
4406
+ return coerceAdepHttpEnvelope(value) ?? value;
4407
+ }
4284
4408
  const contentType = value.headers.get("content-type") ?? "";
4285
4409
  const text = await value.text();
4286
4410
  let payload = text;
@@ -4292,8 +4416,8 @@ async function normalizeResultBody(value) {
4292
4416
  }
4293
4417
  }
4294
4418
  const headers = {};
4295
- value.headers.forEach((headerValue, key) => {
4296
- headers[key] = headerValue;
4419
+ value.headers.forEach((headerValue2, key) => {
4420
+ headers[key] = headerValue2;
4297
4421
  });
4298
4422
  return { __adepHttp: { status: value.status, body: payload, headers } };
4299
4423
  }
@@ -4400,7 +4524,7 @@ function runWorker(input, port) {
4400
4524
  structuredClone
4401
4525
  };
4402
4526
  if (input.env !== void 0) {
4403
- sandboxGlobals["process"] = { env: input.env };
4527
+ sandboxGlobals["process"] = realmValue({ env: input.env });
4404
4528
  }
4405
4529
  if (input.fetch !== void 0) {
4406
4530
  const sandboxFetch = createSandboxFetch({
@@ -4528,7 +4652,7 @@ function createBearerUserResolver(issuer) {
4528
4652
  function isRuntimeSource(name) {
4529
4653
  return (name.endsWith(".ts") || name.endsWith(".js")) && !name.endsWith(".d.ts") && !name.endsWith(".test.ts");
4530
4654
  }
4531
- async function collectFunctions(dir) {
4655
+ async function collectFunctions(dir, readImpl = (path) => readFile6(path, "utf8")) {
4532
4656
  const files = {};
4533
4657
  const walk = async (sub, prefix) => {
4534
4658
  let entries;
@@ -4543,7 +4667,8 @@ async function collectFunctions(dir) {
4543
4667
  if (entry.isDirectory()) {
4544
4668
  await walk(full, rel);
4545
4669
  } else if (entry.isFile() && isRuntimeSource(entry.name)) {
4546
- files[rel] = await readFile6(full, "utf8");
4670
+ const content = await readImpl(full).catch(() => null);
4671
+ if (content !== null) files[rel] = content;
4547
4672
  }
4548
4673
  }
4549
4674
  };
@@ -4619,7 +4744,8 @@ async function loadConfig(cwd) {
4619
4744
  name: config?.name ?? name,
4620
4745
  functionsDir: config?.functionsDir ?? "functions",
4621
4746
  functionsPrefix: config?.functions_prefix ?? "",
4622
- ...config?.environment === void 0 ? {} : { environment: config.environment }
4747
+ ...config?.environment === void 0 ? {} : { environment: config.environment },
4748
+ ...config?.db_environment === void 0 ? {} : { dbEnvironment: config.db_environment }
4623
4749
  };
4624
4750
  }
4625
4751
  var parseEnvFile = parseEnv;
@@ -246,6 +246,29 @@ function transformExports(code) {
246
246
  return transformModule(code);
247
247
  }
248
248
  var SANDBOX_BUILTIN_REJECTED = "FN_SANDBOX_BUILTIN_BLOCKED";
249
+ var REALM_VALUE_KEY = "__adepRealmValue";
250
+ function realmValue(value) {
251
+ return { [REALM_VALUE_KEY]: value };
252
+ }
253
+ function isRealmValue(value) {
254
+ return typeof value === "object" && value !== null && REALM_VALUE_KEY in value;
255
+ }
256
+ function createSandboxContext(sandboxGlobals) {
257
+ const direct = {};
258
+ const realmEntries = {};
259
+ for (const [key, value] of Object.entries(sandboxGlobals)) {
260
+ if (isRealmValue(value)) realmEntries[key] = value[REALM_VALUE_KEY];
261
+ else direct[key] = value;
262
+ }
263
+ const context = vm.createContext(direct, { codeGeneration: { strings: false, wasm: false } });
264
+ if (Object.keys(realmEntries).length > 0) {
265
+ const literal = JSON.stringify(JSON.stringify(realmEntries));
266
+ vm.runInContext(`Object.assign(globalThis, JSON.parse(${literal}))`, context, {
267
+ filename: "<sandbox-realm-globals>"
268
+ });
269
+ }
270
+ return context;
271
+ }
249
272
  function resolveModulePath(fromDir, request, files) {
250
273
  if (!request.startsWith("./") && !request.startsWith("../")) return null;
251
274
  const base = joinPosix(fromDir, request);
@@ -319,12 +342,13 @@ function loadModule(path, files, sandboxGlobals, cache, resolveBare, bareCache)
319
342
  } catch (error) {
320
343
  throw syntaxErrorWithLocation(error, path);
321
344
  }
345
+ const context = createSandboxContext(sandboxGlobals);
322
346
  let wrapper;
323
347
  try {
324
- wrapper = vm.runInNewContext(
348
+ wrapper = vm.runInContext(
325
349
  `(function (module, exports, require) { ${transformed}
326
350
  })`,
327
- vm.createContext({ ...sandboxGlobals }),
351
+ context,
328
352
  { filename: path }
329
353
  );
330
354
  } catch (error) {
@@ -357,6 +381,8 @@ var SandboxFetchError = class extends Error {
357
381
  }
358
382
  };
359
383
  var DEFAULT_MAX_RESPONSE_BYTES = 100 * 1024 * 1024;
384
+ var DEFAULT_MAX_REDIRECTS = 5;
385
+ var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
360
386
  var defaultFetch = (url, init) => (
361
387
  // 真实 Response 与内部 ResponseLike 结构性一致;getReader 的多态签名差异属纯类型问题,运行语义等价。
362
388
  globalThis.fetch(url, init)
@@ -395,18 +421,24 @@ function headersToRecord(headers) {
395
421
  }
396
422
  return out;
397
423
  }
398
- function contentLength(res) {
399
- const headers = res.headers;
424
+ function headerValue(headers, name) {
400
425
  if (headers === void 0 || headers === null) return null;
426
+ let raw;
401
427
  if (typeof headers.get === "function") {
402
- const raw2 = headers.get?.("content-length");
403
- if (raw2 === void 0 || raw2 === null || raw2.trim() === "") return null;
404
- const n2 = Number(raw2);
405
- return Number.isFinite(n2) && n2 >= 0 ? n2 : null;
406
- }
407
- const record = headers;
408
- const raw = record["content-length"] ?? record["Content-Length"];
409
- if (raw === void 0 || raw === null) return null;
428
+ raw = headers.get?.(name);
429
+ } else {
430
+ const record = headers;
431
+ raw = record[name.toLocaleLowerCase()] ?? record[name];
432
+ }
433
+ if (raw === void 0 || raw === null || raw.trim() === "") return null;
434
+ return raw;
435
+ }
436
+ function contentLength(res) {
437
+ const raw = headerValue(
438
+ res.headers,
439
+ "content-length"
440
+ );
441
+ if (raw === null) return null;
410
442
  const n = Number(raw);
411
443
  return Number.isFinite(n) && n >= 0 ? n : null;
412
444
  }
@@ -486,44 +518,72 @@ function createSandboxFetch(options) {
486
518
  const fetchImpl = options.fetchImpl ?? defaultFetch;
487
519
  const { allowlist, timeoutMs } = options;
488
520
  const maxBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
489
- return async (url, init) => {
521
+ const maxRedirects = Math.max(0, options.maxRedirects ?? DEFAULT_MAX_REDIRECTS);
522
+ const guard = (raw) => {
490
523
  let parsed;
491
524
  try {
492
- parsed = new URL(url);
525
+ parsed = new URL(raw);
493
526
  } catch {
494
- throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 URL"${url}"`);
527
+ throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 URL"${raw}"`);
495
528
  }
496
- if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw FORBIDDEN(url);
497
- if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(url);
529
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw FORBIDDEN(raw);
530
+ if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(raw);
531
+ return parsed;
532
+ };
533
+ const fetchOnce = async (url, init) => {
498
534
  const controller = new AbortController();
499
535
  const timer = setTimeout(() => controller.abort(), timeoutMs);
500
536
  const signals = [controller.signal];
501
537
  if (init?.signal !== void 0) signals.push(init.signal);
502
538
  const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
539
+ const request = {
540
+ ...init,
541
+ signal,
542
+ redirect: "manual"
543
+ };
503
544
  try {
504
- let res;
505
- try {
506
- res = await fetchImpl(url, { ...init, signal });
507
- } catch (error) {
508
- if (controller.signal.aborted) {
509
- throw new SandboxFetchError(
510
- "FN_FETCH_TIMEOUT",
511
- `cloud.fetch \u8BF7\u6C42\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeoutMs}ms\uFF09`
512
- );
513
- }
514
- if (init?.signal?.aborted === true) {
515
- throw new SandboxFetchError("FN_FETCH_ABORTED", "cloud.fetch \u8BF7\u6C42\u88AB\u8C03\u7528\u65B9\u4E2D\u6B62");
516
- }
517
- throw new SandboxFetchError(
518
- "FN_FETCH_FAILED",
519
- `cloud.fetch \u8BF7\u6C42\u5931\u8D25\uFF1A${errorMessageOf(error)}`
520
- );
545
+ return await fetchImpl(url, request);
546
+ } catch (error) {
547
+ if (controller.signal.aborted) {
548
+ throw new SandboxFetchError("FN_FETCH_TIMEOUT", `cloud.fetch \u8BF7\u6C42\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeoutMs}ms\uFF09`);
521
549
  }
522
- return normalizeResponse(res, maxBytes);
550
+ if (init?.signal?.aborted === true) {
551
+ throw new SandboxFetchError("FN_FETCH_ABORTED", "cloud.fetch \u8BF7\u6C42\u88AB\u8C03\u7528\u65B9\u4E2D\u6B62");
552
+ }
553
+ throw new SandboxFetchError(
554
+ "FN_FETCH_FAILED",
555
+ `cloud.fetch \u8BF7\u6C42\u5931\u8D25\uFF1A${errorMessageOf(error)}`
556
+ );
523
557
  } finally {
524
558
  clearTimeout(timer);
525
559
  }
526
560
  };
561
+ return async (url, init) => {
562
+ let target = guard(url);
563
+ let currentInit = init === void 0 ? void 0 : { ...init };
564
+ let lastRes = null;
565
+ for (let hop = 0; hop <= maxRedirects; hop++) {
566
+ const res = await fetchOnce(target.href, currentInit);
567
+ const location = REDIRECT_STATUSES.has(res.status) ? headerValue(res.headers, "location") : null;
568
+ if (location === null || hop === maxRedirects) {
569
+ lastRes = res;
570
+ break;
571
+ }
572
+ let next;
573
+ try {
574
+ next = new URL(location, target.href);
575
+ } catch {
576
+ lastRes = res;
577
+ break;
578
+ }
579
+ if (res.status !== 307 && res.status !== 308 && currentInit !== void 0) {
580
+ currentInit.method = "GET";
581
+ delete currentInit.body;
582
+ }
583
+ target = guard(next.href);
584
+ }
585
+ return normalizeResponse(lastRes, maxBytes);
586
+ };
527
587
  }
528
588
 
529
589
  // packages/runtime/src/shared/capability-keys.ts
@@ -534,6 +594,8 @@ var DB_RPC = {
534
594
  query: "query",
535
595
  /** 写路径(CF-013):`(method=writeQuery, args=[sql, params])` → `{ changes }`。 */
536
596
  writeQuery: "writeQuery",
597
+ /** 通用执行(BUG-4,2026-10-14):`(method=execute, args=[sql, params?])` → `{ changes }`。 */
598
+ execute: "execute",
537
599
  /** 读取 owned 表变更流(DB-006):`(method=changes, args=[table, query])`。 */
538
600
  changes: "changes",
539
601
  /** 开启事务:`begin → txId`。 */
@@ -672,6 +734,39 @@ function createChainClient(capability, port, pending, nextId, spec) {
672
734
  return makeRoot(void 0);
673
735
  }
674
736
 
737
+ // packages/runtime/src/shared/http-envelope.ts
738
+ function coerceAdepHttpEnvelope(value) {
739
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
740
+ if (isAdepHttpEnvelope(value)) return value;
741
+ const keys = Object.keys(value);
742
+ if (keys.length === 0) return null;
743
+ if (!keys.every((key) => key === "status" || key === "body" || key === "headers")) return null;
744
+ const candidate = value;
745
+ const status = candidate.status;
746
+ if (typeof status !== "number" || !Number.isInteger(status) || status < 100 || status > 599) {
747
+ return null;
748
+ }
749
+ if ("headers" in candidate && candidate.headers !== void 0) {
750
+ const headers = candidate.headers;
751
+ if (typeof headers !== "object" || headers === null || Array.isArray(headers)) return null;
752
+ if (!Object.values(headers).every((headerValue2) => typeof headerValue2 === "string")) return null;
753
+ }
754
+ return {
755
+ __adepHttp: {
756
+ status,
757
+ ...candidate.headers === void 0 ? {} : { headers: candidate.headers },
758
+ body: candidate.body
759
+ }
760
+ };
761
+ }
762
+ function isAdepHttpEnvelope(value) {
763
+ if (typeof value !== "object" || value === null) return false;
764
+ const envelope = value.__adepHttp;
765
+ if (typeof envelope !== "object" || envelope === null) return false;
766
+ const status = envelope.status;
767
+ return typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599;
768
+ }
769
+
675
770
  // packages/runtime/src/functions/deps/manifest.ts
676
771
  function splitBareSpecifier(request) {
677
772
  if (request.startsWith("@")) {
@@ -691,7 +786,9 @@ function errorMessageOf2(error) {
691
786
  return String(error);
692
787
  }
693
788
  async function normalizeResultBody(value) {
694
- if (!(value instanceof Response)) return value;
789
+ if (!(value instanceof Response)) {
790
+ return coerceAdepHttpEnvelope(value) ?? value;
791
+ }
695
792
  const contentType = value.headers.get("content-type") ?? "";
696
793
  const text = await value.text();
697
794
  let payload = text;
@@ -703,8 +800,8 @@ async function normalizeResultBody(value) {
703
800
  }
704
801
  }
705
802
  const headers = {};
706
- value.headers.forEach((headerValue, key) => {
707
- headers[key] = headerValue;
803
+ value.headers.forEach((headerValue2, key) => {
804
+ headers[key] = headerValue2;
708
805
  });
709
806
  return { __adepHttp: { status: value.status, body: payload, headers } };
710
807
  }
@@ -811,7 +908,7 @@ function runWorker(input, port) {
811
908
  structuredClone
812
909
  };
813
910
  if (input.env !== void 0) {
814
- sandboxGlobals["process"] = { env: input.env };
911
+ sandboxGlobals["process"] = realmValue({ env: input.env });
815
912
  }
816
913
  if (input.fetch !== void 0) {
817
914
  const sandboxFetch = createSandboxFetch({
package/package.json CHANGED
@@ -5,9 +5,9 @@
5
5
  "type": "git",
6
6
  "url": "git+https://gitcode.com/yunqiang_wu/ai-yunke.git"
7
7
  },
8
- "homepage": "https://adep.jajabjbj.top",
8
+ "homepage": "https://adep.top",
9
9
  "bugs": "https://gitcode.com/yunqiang_wu/ai-yunke/issues",
10
- "version": "0.4.0",
10
+ "version": "0.4.4",
11
11
  "type": "module",
12
12
  "description": "`adep` 命令行工具包位:登录 / 初始化 / 本地调试 / 部署 / 数据库维护 / 云存储 / 静态托管 / 微前端组件(widget),实现见任务单 CLI-001 ~ CLI-003 与 FE-002;子路径 `@adep/cli/vite` 提供云函数 vite 插件(CLI-014)",
13
13
  "bin": {
@@ -30,13 +30,13 @@
30
30
  "esbuild": "0.25.12",
31
31
  "yaml": "2.9.1",
32
32
  "zod": "^4.4.3",
33
- "@adep/crypto": "0.1.0",
34
- "@adep/cf-compat": "0.1.1",
35
- "@adep/runtime": "0.3.2",
36
- "@adep/vite-plugin": "0.1.8"
33
+ "@adep/cf-compat": "0.1.3",
34
+ "@adep/runtime": "0.3.4",
35
+ "@adep/crypto": "0.1.1",
36
+ "@adep/vite-plugin": "0.1.9"
37
37
  },
38
38
  "devDependencies": {
39
- "@adep/types": "0.3.0"
39
+ "@adep/types": "0.3.2"
40
40
  },
41
41
  "scripts": {
42
42
  "test": "vitest run",