@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/README.md +40 -7
- package/dist/index.js +20798 -20336
- package/dist/vite.js +168 -42
- package/dist/worker-entry.js +136 -39
- package/package.json +7 -7
- package/templates/app-mobile/plugins/adep-bridge/Package.swift +48 -0
- package/templates/app-mobile/plugins/adep-bridge/ios/AdepBridge.podspec +6 -3
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.
|
|
3881
|
+
wrapper = vm.runInContext(
|
|
3796
3882
|
`(function (module, exports, require) { ${transformed}
|
|
3797
3883
|
})`,
|
|
3798
|
-
|
|
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
|
|
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
|
-
|
|
4021
|
-
|
|
4022
|
-
const
|
|
4023
|
-
|
|
4024
|
-
}
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
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
|
-
|
|
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(
|
|
4205
|
+
parsed = new URL(raw);
|
|
4111
4206
|
} catch {
|
|
4112
|
-
throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 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(
|
|
4115
|
-
if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(
|
|
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
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
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
|
-
|
|
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))
|
|
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((
|
|
4296
|
-
headers[key] =
|
|
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
|
-
|
|
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;
|
package/dist/worker-entry.js
CHANGED
|
@@ -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.
|
|
348
|
+
wrapper = vm.runInContext(
|
|
325
349
|
`(function (module, exports, require) { ${transformed}
|
|
326
350
|
})`,
|
|
327
|
-
|
|
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
|
|
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
|
-
|
|
403
|
-
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
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
|
-
|
|
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(
|
|
525
|
+
parsed = new URL(raw);
|
|
493
526
|
} catch {
|
|
494
|
-
throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 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(
|
|
497
|
-
if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(
|
|
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
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
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
|
-
|
|
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))
|
|
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((
|
|
707
|
-
headers[key] =
|
|
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.
|
|
8
|
+
"homepage": "https://adep.top",
|
|
9
9
|
"bugs": "https://gitcode.com/yunqiang_wu/ai-yunke/issues",
|
|
10
|
-
"version": "0.4.
|
|
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/
|
|
34
|
-
"@adep/
|
|
35
|
-
"@adep/
|
|
36
|
-
"@adep/vite-plugin": "0.1.
|
|
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.
|
|
39
|
+
"@adep/types": "0.3.2"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"test": "vitest run",
|