@adep/cli 0.0.2 → 0.0.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 +4 -5
- package/dist/index.js +8 -10
- package/dist/worker-entry.js +692 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -391,15 +391,14 @@ export ADEP_HOME=~/.adep
|
|
|
391
391
|
|
|
392
392
|
### `adep.config.ts`
|
|
393
393
|
|
|
394
|
-
`adep init` 生成的项目标识文件,CLI 的 `dev` / `deploy`
|
|
394
|
+
`adep init` 生成的项目标识文件,CLI 的 `dev` / `deploy` 按此识别项目(CLI 直接 import 后读 `default` 的字段,无需任何辅助包):
|
|
395
395
|
|
|
396
396
|
```ts
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
export default defineConfig({
|
|
397
|
+
// adep 项目配置:CLI(dev / deploy)按 default export 读取(name / functionsDir)。
|
|
398
|
+
export default {
|
|
400
399
|
name: 'my-app', // 项目名(deploy 的默认 slug)
|
|
401
400
|
template: 'function', // 模板:empty | function
|
|
402
|
-
}
|
|
401
|
+
}
|
|
403
402
|
```
|
|
404
403
|
|
|
405
404
|
---
|
package/dist/index.js
CHANGED
|
@@ -5568,13 +5568,11 @@ var InitError = class extends Error {
|
|
|
5568
5568
|
this.name = "InitError";
|
|
5569
5569
|
}
|
|
5570
5570
|
};
|
|
5571
|
-
var ADEP_CONFIG = (name, template) =>
|
|
5572
|
-
|
|
5573
|
-
// adep \u9879\u76EE\u914D\u7F6E\uFF1ACLI\uFF08dev / deploy\uFF09\u4E0E\u5E73\u53F0\u6309\u6B64\u8BC6\u522B\u9879\u76EE\u3002
|
|
5574
|
-
export default defineConfig({
|
|
5571
|
+
var ADEP_CONFIG = (name, template) => `// adep \u9879\u76EE\u914D\u7F6E\uFF1ACLI\uFF08dev / deploy\uFF09\u6309 default export \u8BFB\u53D6\uFF08name / functionsDir\uFF09\u3002
|
|
5572
|
+
export default {
|
|
5575
5573
|
name: '${name}',
|
|
5576
5574
|
template: '${template}',
|
|
5577
|
-
}
|
|
5575
|
+
}
|
|
5578
5576
|
`;
|
|
5579
5577
|
var README = (name, template) => `# ${name}
|
|
5580
5578
|
|
|
@@ -5583,13 +5581,13 @@ AgentDeploy \u9879\u76EE\uFF08\u6A21\u677F\uFF1A${template}\uFF09\u3002
|
|
|
5583
5581
|
- \`adep dev\`\uFF1A\u672C\u5730\u8C03\u8BD5\uFF08CLI-002 \u4EA4\u4ED8\uFF09
|
|
5584
5582
|
- \`adep deploy\`\uFF1A\u53D1\u5E03\uFF08CLI-003 \u4EA4\u4ED8\uFF09
|
|
5585
5583
|
`;
|
|
5586
|
-
var HELLO_FUNCTION =
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
export default
|
|
5584
|
+
var HELLO_FUNCTION = `// \u793A\u4F8B\u51FD\u6570\uFF1Aadep deploy \u540E\u7ECF https://<project-slug>.<platform-domain>/hello \u89E6\u53D1\u3002
|
|
5585
|
+
// \u7528\u6237\u51FD\u6570\u5373 functions/ \u4E0B\u7684\u72EC\u7ACB\u6587\u4EF6\uFF0C\u9ED8\u8BA4\u5BFC\u51FA (ctx) => Response \u5F62\u72B6\u7684\u5904\u7406\u5668
|
|
5586
|
+
//\uFF08ctx \u5951\u7EA6\u89C1\u5E73\u53F0\u7684 FunctionContext\uFF1Amethod / path / query / headers / body / cloud.*\uFF09\u3002
|
|
5587
|
+
export default async (ctx) => {
|
|
5590
5588
|
const name = (ctx.query['name'] as string | undefined) ?? 'world'
|
|
5591
5589
|
return { message: \`hello \${name}\` }
|
|
5592
|
-
}
|
|
5590
|
+
}
|
|
5593
5591
|
`;
|
|
5594
5592
|
var FUNCTION_INDEX = `// \u51FD\u6570\u6A21\u5757\u5165\u53E3\uFF1A\u9879\u76EE\u91CC\u7684\u6BCF\u4E2A\u51FD\u6570\u662F functions/ \u4E0B\u7684\u72EC\u7ACB\u6587\u4EF6\uFF0C
|
|
5595
5593
|
// \u9ED8\u8BA4\u5BFC\u51FA (ctx) => Response \u5F62\u72B6\u7684\u5904\u7406\u5668\uFF08\u5951\u7EA6\u89C1 @adep/types \u7684 FunctionContext\uFF09\u3002
|
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
// packages/runtime/src/functions/runtime/worker-entry.ts
|
|
2
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
// packages/runtime/src/functions/runtime/cloud-container.ts
|
|
7
|
+
var KNOWN_CAPABILITY_HINTS = {
|
|
8
|
+
db: {
|
|
9
|
+
code: "DB_NOT_PROVISIONED",
|
|
10
|
+
message: "\u9879\u76EE\u6570\u636E\u5E93\u5C1A\u672A\u5F00\u542F\uFF1A\u8BF7\u5230\u9879\u76EE\u8BBE\u7F6E\u542F\u52A8\u6570\u636E\u5E93\uFF08DB_NOT_PROVISIONED\uFF09"
|
|
11
|
+
},
|
|
12
|
+
storage: {
|
|
13
|
+
code: "STORAGE_NOT_AVAILABLE",
|
|
14
|
+
message: "\u6587\u4EF6\u5B58\u50A8\u5C1A\u672A\u5F00\u542F\uFF1A\u8BF7\u5230\u9879\u76EE\u8BBE\u7F6E\u5F00\u542F\u6587\u4EF6\u5B58\u50A8\uFF08STORAGE_NOT_AVAILABLE\uFF09"
|
|
15
|
+
},
|
|
16
|
+
fetch: {
|
|
17
|
+
code: "FN_FETCH_DISABLED",
|
|
18
|
+
message: "\u6C99\u7BB1\u7F51\u7EDC\u672A\u5F00\u542F\uFF1A\u8BF7\u914D\u7F6E FUNCTIONS_FETCH_ALLOWLIST \u767D\u540D\u5355\u540E\u91CD\u542F\uFF08FN_FETCH_DISABLED\uFF09"
|
|
19
|
+
},
|
|
20
|
+
realtime: {
|
|
21
|
+
code: "REALTIME_NOT_AVAILABLE",
|
|
22
|
+
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"
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var CapabilityNotRegisteredError = class extends Error {
|
|
26
|
+
code;
|
|
27
|
+
constructor(code, message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.code = code;
|
|
30
|
+
this.name = "CapabilityNotRegisteredError";
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
function createCloud(registrations) {
|
|
34
|
+
const store = registrations;
|
|
35
|
+
return new Proxy(
|
|
36
|
+
{},
|
|
37
|
+
{
|
|
38
|
+
get(_target, prop) {
|
|
39
|
+
if (typeof prop !== "string") return void 0;
|
|
40
|
+
if (!store.has(prop)) {
|
|
41
|
+
const hint = KNOWN_CAPABILITY_HINTS[prop];
|
|
42
|
+
throw new CapabilityNotRegisteredError(
|
|
43
|
+
hint?.code ?? "CAPABILITY_NOT_REGISTERED",
|
|
44
|
+
hint?.message ?? `\u80FD\u529B "${prop}" \u672A\u6CE8\u518C\uFF1A\u8BF7\u5728\u51FD\u6570\u6240\u5728\u9879\u76EE\u5B89\u88C5\u5BF9\u5E94\u80FD\u529B`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return store.get(prop);
|
|
48
|
+
},
|
|
49
|
+
has: () => true
|
|
50
|
+
// 支持 'db' in ctx.cloud 形态
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
function createCloudContainer() {
|
|
55
|
+
const registry = /* @__PURE__ */ new Map();
|
|
56
|
+
const names = /* @__PURE__ */ new Set();
|
|
57
|
+
let cloud = null;
|
|
58
|
+
const rebuild = () => {
|
|
59
|
+
cloud = createCloud(registry);
|
|
60
|
+
};
|
|
61
|
+
rebuild();
|
|
62
|
+
return {
|
|
63
|
+
register(name, impl) {
|
|
64
|
+
registry.set(name, impl);
|
|
65
|
+
names.add(name);
|
|
66
|
+
rebuild();
|
|
67
|
+
},
|
|
68
|
+
unregister(name) {
|
|
69
|
+
registry.delete(name);
|
|
70
|
+
names.delete(name);
|
|
71
|
+
rebuild();
|
|
72
|
+
},
|
|
73
|
+
get names() {
|
|
74
|
+
return [...names];
|
|
75
|
+
},
|
|
76
|
+
get cloud() {
|
|
77
|
+
return cloud;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// packages/runtime/src/functions/runtime/sandbox.ts
|
|
83
|
+
import vm from "node:vm";
|
|
84
|
+
import * as nodeModule from "node:module";
|
|
85
|
+
|
|
86
|
+
// packages/runtime/src/functions/runtime/exports.ts
|
|
87
|
+
function transformModule(code) {
|
|
88
|
+
let out = code;
|
|
89
|
+
const named = [];
|
|
90
|
+
out = out.replace(/(^|\n)(\s*)export\s+default\s+/g, "$1$2module.default = ");
|
|
91
|
+
out = out.replace(
|
|
92
|
+
/import\s*\{([^}]*)\}\s*from\s*(['"][^'"]*['"])[ \t]*;?/g,
|
|
93
|
+
(_match, names, source) => `const {${names}} = require(${source})`
|
|
94
|
+
);
|
|
95
|
+
out = out.replace(
|
|
96
|
+
/import\s*\*\s*as\s*([A-Za-z_$][\w$]*)\s*from\s*(['"][^'"]*['"])[ \t]*;?/g,
|
|
97
|
+
(_match, name, source) => `const ${name} = require(${source})`
|
|
98
|
+
);
|
|
99
|
+
out = out.replace(
|
|
100
|
+
/import\s+([A-Za-z_$][\w$]*)\s+from\s*(['"][^'"]*['"])[ \t]*;?/g,
|
|
101
|
+
(_match, name, source) => `const ${name} = require(${source})`
|
|
102
|
+
);
|
|
103
|
+
out = out.replace(
|
|
104
|
+
/export\s+(const|let|var)\s+([A-Za-z_$][\w$]*)/g,
|
|
105
|
+
(_match, kw, name) => {
|
|
106
|
+
named.push(name);
|
|
107
|
+
return `${kw} ${name}`;
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
out = out.replace(
|
|
111
|
+
/export\s+(async\s+)?function\s*\*?\s*([A-Za-z_$][\w$]*)/g,
|
|
112
|
+
(_match, asyncKw, name) => {
|
|
113
|
+
named.push(name);
|
|
114
|
+
return `${asyncKw ?? ""}function ${name}`;
|
|
115
|
+
}
|
|
116
|
+
);
|
|
117
|
+
out = out.replace(/export\s+class\s+([A-Za-z_$][\w$]*)/g, (_match, name) => {
|
|
118
|
+
named.push(name);
|
|
119
|
+
return `class ${name}`;
|
|
120
|
+
});
|
|
121
|
+
if (named.length > 0) {
|
|
122
|
+
out += `
|
|
123
|
+
;Object.assign(exports, { ${named.join(", ")} })`;
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// packages/runtime/src/functions/runtime/sandbox.ts
|
|
129
|
+
var bunTranspiler;
|
|
130
|
+
function transpile(code) {
|
|
131
|
+
const bun = globalThis["Bun"];
|
|
132
|
+
if (bun?.Transpiler !== void 0) {
|
|
133
|
+
bunTranspiler ??= new bun.Transpiler({ loader: "ts" });
|
|
134
|
+
return bunTranspiler.transformSync(code);
|
|
135
|
+
}
|
|
136
|
+
const stripTypeScriptTypes2 = nodeModule.stripTypeScriptTypes;
|
|
137
|
+
if (typeof stripTypeScriptTypes2 === "function") {
|
|
138
|
+
return stripTypeScriptTypes2(code, { mode: "strip" });
|
|
139
|
+
}
|
|
140
|
+
return code;
|
|
141
|
+
}
|
|
142
|
+
function transformExports(code) {
|
|
143
|
+
return transformModule(code);
|
|
144
|
+
}
|
|
145
|
+
var SANDBOX_BUILTIN_REJECTED = "FN_SANDBOX_BUILTIN_BLOCKED";
|
|
146
|
+
function resolveModulePath(fromDir, request, files) {
|
|
147
|
+
if (!request.startsWith("./") && !request.startsWith("../")) return null;
|
|
148
|
+
const base = joinPosix(fromDir, request);
|
|
149
|
+
const candidates = [base, `${base}.ts`, `${base}.js`, `${base}/index.ts`, `${base}/index.js`];
|
|
150
|
+
for (const candidate of candidates) {
|
|
151
|
+
const normalized = normalizePosix(candidate);
|
|
152
|
+
if (files[normalized] !== void 0) return normalized;
|
|
153
|
+
}
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
function joinPosix(dir, request) {
|
|
157
|
+
const parts = [...dir.split("/"), ...request.split("/")];
|
|
158
|
+
const stack = [];
|
|
159
|
+
for (const part of parts) {
|
|
160
|
+
if (part === "" || part === ".") continue;
|
|
161
|
+
if (part === "..") stack.pop();
|
|
162
|
+
else stack.push(part);
|
|
163
|
+
}
|
|
164
|
+
return stack.join("/");
|
|
165
|
+
}
|
|
166
|
+
function normalizePosix(p) {
|
|
167
|
+
return joinPosix("", p);
|
|
168
|
+
}
|
|
169
|
+
function loadModule(path, files, sandboxGlobals, cache, resolveBare, bareCache) {
|
|
170
|
+
const cached = cache.get(path);
|
|
171
|
+
if (cached !== void 0) return cached;
|
|
172
|
+
const source = files[path];
|
|
173
|
+
if (source === void 0) {
|
|
174
|
+
throw new Error(`\u6A21\u5757\u4E0D\u5B58\u5728\uFF1A${path}`);
|
|
175
|
+
}
|
|
176
|
+
const exports = {};
|
|
177
|
+
cache.set(path, exports);
|
|
178
|
+
const dir = path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : "";
|
|
179
|
+
const module = { exports, default: void 0 };
|
|
180
|
+
const require2 = (request) => {
|
|
181
|
+
const resolved = resolveModulePath(dir, request, files);
|
|
182
|
+
if (resolved !== null) {
|
|
183
|
+
return loadModule(resolved, files, sandboxGlobals, cache, resolveBare, bareCache);
|
|
184
|
+
}
|
|
185
|
+
if (resolveBare !== void 0 && !request.startsWith("./") && !request.startsWith("../")) {
|
|
186
|
+
const cachedModule = bareCache?.get(request);
|
|
187
|
+
if (cachedModule !== void 0) return cachedModule;
|
|
188
|
+
const loaded = resolveBare(request);
|
|
189
|
+
bareCache?.set(request, loaded);
|
|
190
|
+
return loaded;
|
|
191
|
+
}
|
|
192
|
+
const error = new Error(
|
|
193
|
+
`\u6C99\u7BB1\u62D2\u7EDD\u8BE5\u5F15\u7528\uFF1A"${request}"\u3002\u51FD\u6570\u5185\u53EA\u80FD require \u9879\u76EE\u5185\u7684\u76F8\u5BF9\u8DEF\u5F84\u6587\u4EF6\uFF08FN_SANDBOX_BUILTIN_BLOCKED\uFF09`
|
|
194
|
+
);
|
|
195
|
+
error.code = SANDBOX_BUILTIN_REJECTED;
|
|
196
|
+
throw error;
|
|
197
|
+
};
|
|
198
|
+
let transformed;
|
|
199
|
+
try {
|
|
200
|
+
transformed = transformExports(transpile(source));
|
|
201
|
+
} catch (error) {
|
|
202
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
203
|
+
const stack = error.stack;
|
|
204
|
+
const line = typeof stack === "string" ? /(?:^|\n):(\d+)(?:\n|$)/.exec(stack)?.[1] : void 0;
|
|
205
|
+
const location = line === void 0 ? path : `${path}:${line}`;
|
|
206
|
+
const wrapped = new Error(`\u6E90\u7801\u8BED\u6CD5\u9519\u8BEF\uFF08${location}\uFF09\uFF1A${rawMessage}`, { cause: error });
|
|
207
|
+
const code = error.code;
|
|
208
|
+
if (typeof code === "string") wrapped.code = code;
|
|
209
|
+
throw wrapped;
|
|
210
|
+
}
|
|
211
|
+
const wrapper = vm.runInNewContext(
|
|
212
|
+
`(function (module, exports, require) { ${transformed}
|
|
213
|
+
})`,
|
|
214
|
+
vm.createContext({ ...sandboxGlobals }),
|
|
215
|
+
{ filename: path }
|
|
216
|
+
);
|
|
217
|
+
wrapper(module, exports, require2);
|
|
218
|
+
if (module.default !== void 0 && exports["default"] === void 0) {
|
|
219
|
+
exports["default"] = module.default;
|
|
220
|
+
}
|
|
221
|
+
return exports;
|
|
222
|
+
}
|
|
223
|
+
function loadFunctionModule(files, entry, sandboxGlobals, resolveBare) {
|
|
224
|
+
const cache = /* @__PURE__ */ new Map();
|
|
225
|
+
const bareCache = /* @__PURE__ */ new Map();
|
|
226
|
+
const entryExports = loadModule(entry, files, sandboxGlobals, cache, resolveBare, bareCache);
|
|
227
|
+
const handler = entryExports["default"];
|
|
228
|
+
if (typeof handler !== "function") {
|
|
229
|
+
throw new Error(`\u5165\u53E3\u6587\u4EF6 ${entry} \u7F3A\u5C11 default \u5BFC\u51FA\u7684\u51FD\u6570`);
|
|
230
|
+
}
|
|
231
|
+
return { handler };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// packages/runtime/src/functions/runtime/sandbox-fetch.ts
|
|
235
|
+
var SandboxFetchError = class extends Error {
|
|
236
|
+
code;
|
|
237
|
+
constructor(code, message) {
|
|
238
|
+
super(message);
|
|
239
|
+
this.name = "SandboxFetchError";
|
|
240
|
+
this.code = code;
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
var DEFAULT_MAX_RESPONSE_BYTES = 100 * 1024 * 1024;
|
|
244
|
+
var defaultFetch = (url, init) => (
|
|
245
|
+
// 真实 Response 与内部 ResponseLike 结构性一致;getReader 的多态签名差异属纯类型问题,运行语义等价。
|
|
246
|
+
globalThis.fetch(url, init)
|
|
247
|
+
);
|
|
248
|
+
function isHostAllowed(host, rules) {
|
|
249
|
+
const h = host.toLocaleLowerCase();
|
|
250
|
+
return rules.some((raw) => {
|
|
251
|
+
const rule = raw.trim().toLocaleLowerCase();
|
|
252
|
+
if (rule.startsWith("*.")) {
|
|
253
|
+
const base = rule.slice(2);
|
|
254
|
+
return h === base || h.endsWith(`.${base}`);
|
|
255
|
+
}
|
|
256
|
+
return h === rule;
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
var FORBIDDEN = (url) => new SandboxFetchError(
|
|
260
|
+
"FN_FETCH_FORBIDDEN",
|
|
261
|
+
`cloud.fetch \u62D2\u7EDD\u8BBF\u95EE "${url}"\uFF1A\u8BE5\u4E3B\u673A\u4E0D\u5728\u6C99\u7BB1\u7F51\u7EDC\u767D\u540D\u5355\u5185\uFF08FN_FETCH_FORBIDDEN\uFF09`
|
|
262
|
+
);
|
|
263
|
+
var TOO_LARGE = new SandboxFetchError(
|
|
264
|
+
"FN_FETCH_TOO_LARGE",
|
|
265
|
+
"cloud.fetch \u54CD\u5E94\u4F53\u8D85\u8FC7\u4E0A\u9650\uFF08FN_FETCH_TOO_LARGE\uFF09"
|
|
266
|
+
);
|
|
267
|
+
function headersToRecord(headers) {
|
|
268
|
+
const out = {};
|
|
269
|
+
if (headers === void 0 || headers === null) return out;
|
|
270
|
+
if (typeof headers.forEach === "function") {
|
|
271
|
+
;
|
|
272
|
+
headers.forEach((value, key) => {
|
|
273
|
+
out[String(key).toLocaleLowerCase()] = String(value);
|
|
274
|
+
});
|
|
275
|
+
return out;
|
|
276
|
+
}
|
|
277
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
278
|
+
out[key.toLocaleLowerCase()] = String(value);
|
|
279
|
+
}
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
function contentLength(res) {
|
|
283
|
+
const headers = res.headers;
|
|
284
|
+
if (headers === void 0 || headers === null) return null;
|
|
285
|
+
if (typeof headers.get === "function") {
|
|
286
|
+
const raw2 = headers.get?.("content-length");
|
|
287
|
+
if (raw2 === void 0 || raw2 === null || raw2.trim() === "") return null;
|
|
288
|
+
const n2 = Number(raw2);
|
|
289
|
+
return Number.isFinite(n2) && n2 >= 0 ? n2 : null;
|
|
290
|
+
}
|
|
291
|
+
const record = headers;
|
|
292
|
+
const raw = record["content-length"] ?? record["Content-Length"];
|
|
293
|
+
if (raw === void 0 || raw === null) return null;
|
|
294
|
+
const n = Number(raw);
|
|
295
|
+
return Number.isFinite(n) && n >= 0 ? n : null;
|
|
296
|
+
}
|
|
297
|
+
function concatChunks(chunks, total) {
|
|
298
|
+
const merged = new Uint8Array(total);
|
|
299
|
+
let offset = 0;
|
|
300
|
+
for (const chunk of chunks) {
|
|
301
|
+
merged.set(chunk, offset);
|
|
302
|
+
offset += chunk.byteLength;
|
|
303
|
+
}
|
|
304
|
+
return merged;
|
|
305
|
+
}
|
|
306
|
+
async function readBodyBytes(res, maxBytes) {
|
|
307
|
+
if (maxBytes > 0) {
|
|
308
|
+
const cl = contentLength(res);
|
|
309
|
+
if (cl !== null && cl > maxBytes) throw TOO_LARGE;
|
|
310
|
+
}
|
|
311
|
+
const body = res.body;
|
|
312
|
+
if (body !== void 0 && body !== null) {
|
|
313
|
+
const reader = body.getReader();
|
|
314
|
+
const chunks = [];
|
|
315
|
+
let total = 0;
|
|
316
|
+
for (; ; ) {
|
|
317
|
+
const { done, value } = await reader.read();
|
|
318
|
+
if (done) break;
|
|
319
|
+
if (value !== void 0) {
|
|
320
|
+
total += value.byteLength;
|
|
321
|
+
if (maxBytes > 0 && total > maxBytes) throw TOO_LARGE;
|
|
322
|
+
chunks.push(value);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return concatChunks(chunks, total);
|
|
326
|
+
}
|
|
327
|
+
if (typeof res.arrayBuffer === "function") {
|
|
328
|
+
const buffer = await res.arrayBuffer();
|
|
329
|
+
const bytes2 = new Uint8Array(buffer);
|
|
330
|
+
if (maxBytes > 0 && bytes2.byteLength > maxBytes) throw TOO_LARGE;
|
|
331
|
+
return bytes2;
|
|
332
|
+
}
|
|
333
|
+
const text = await res.text();
|
|
334
|
+
const bytes = encoder.encode(text);
|
|
335
|
+
if (maxBytes > 0 && bytes.byteLength > maxBytes) throw TOO_LARGE;
|
|
336
|
+
return bytes;
|
|
337
|
+
}
|
|
338
|
+
var encoder = new TextEncoder();
|
|
339
|
+
function errorMessageOf(error) {
|
|
340
|
+
if (error instanceof Error) return error.message;
|
|
341
|
+
return String(error);
|
|
342
|
+
}
|
|
343
|
+
function normalizeResponse(res, maxBytes) {
|
|
344
|
+
let cached = null;
|
|
345
|
+
let cachedPromise = null;
|
|
346
|
+
const readCached = () => {
|
|
347
|
+
if (cached !== null) return Promise.resolve(cached);
|
|
348
|
+
cachedPromise ??= readBodyBytes(res, maxBytes).then((bytes) => {
|
|
349
|
+
cached = bytes;
|
|
350
|
+
return bytes;
|
|
351
|
+
});
|
|
352
|
+
return cachedPromise;
|
|
353
|
+
};
|
|
354
|
+
const read = () => readCached();
|
|
355
|
+
return {
|
|
356
|
+
ok: res.ok,
|
|
357
|
+
status: res.status,
|
|
358
|
+
statusText: res.statusText,
|
|
359
|
+
headers: headersToRecord(res.headers),
|
|
360
|
+
text: async () => decoder.decode(await read()),
|
|
361
|
+
arrayBuffer: async () => bufferViewToArrayBuffer(await read()),
|
|
362
|
+
json: async () => JSON.parse(decoder.decode(await read()))
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
var decoder = new TextDecoder();
|
|
366
|
+
function bufferViewToArrayBuffer(bytes) {
|
|
367
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
368
|
+
}
|
|
369
|
+
function createSandboxFetch(options) {
|
|
370
|
+
const fetchImpl = options.fetchImpl ?? defaultFetch;
|
|
371
|
+
const { allowlist, timeoutMs } = options;
|
|
372
|
+
const maxBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
|
|
373
|
+
return async (url, init) => {
|
|
374
|
+
let parsed;
|
|
375
|
+
try {
|
|
376
|
+
parsed = new URL(url);
|
|
377
|
+
} catch {
|
|
378
|
+
throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 URL"${url}"`);
|
|
379
|
+
}
|
|
380
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw FORBIDDEN(url);
|
|
381
|
+
if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(url);
|
|
382
|
+
const controller = new AbortController();
|
|
383
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
384
|
+
const signals = [controller.signal];
|
|
385
|
+
if (init?.signal !== void 0) signals.push(init.signal);
|
|
386
|
+
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
387
|
+
try {
|
|
388
|
+
let res;
|
|
389
|
+
try {
|
|
390
|
+
res = await fetchImpl(url, { ...init, signal });
|
|
391
|
+
} catch (error) {
|
|
392
|
+
if (controller.signal.aborted) {
|
|
393
|
+
throw new SandboxFetchError(
|
|
394
|
+
"FN_FETCH_TIMEOUT",
|
|
395
|
+
`cloud.fetch \u8BF7\u6C42\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeoutMs}ms\uFF09`
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
if (init?.signal?.aborted === true) {
|
|
399
|
+
throw new SandboxFetchError("FN_FETCH_ABORTED", "cloud.fetch \u8BF7\u6C42\u88AB\u8C03\u7528\u65B9\u4E2D\u6B62");
|
|
400
|
+
}
|
|
401
|
+
throw new SandboxFetchError(
|
|
402
|
+
"FN_FETCH_FAILED",
|
|
403
|
+
`cloud.fetch \u8BF7\u6C42\u5931\u8D25\uFF1A${errorMessageOf(error)}`
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
return normalizeResponse(res, maxBytes);
|
|
407
|
+
} finally {
|
|
408
|
+
clearTimeout(timer);
|
|
409
|
+
}
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// packages/runtime/src/shared/capability-keys.ts
|
|
414
|
+
var RPC_CAPABILITY_KEY = "__adepRpc";
|
|
415
|
+
var CHAIN_CAPABILITY_KEY = "__adepChain";
|
|
416
|
+
var DB_RPC = {
|
|
417
|
+
/** 直通方法(如 query):`(method=query, args=[sql, params])`。 */
|
|
418
|
+
query: "query",
|
|
419
|
+
/** 读取 owned 表变更流(DB-006):`(method=changes, args=[table, query])`。 */
|
|
420
|
+
changes: "changes",
|
|
421
|
+
/** 开启事务:`begin → txId`。 */
|
|
422
|
+
begin: "begin",
|
|
423
|
+
/** 提交事务:`commit, args=[txId]`。 */
|
|
424
|
+
commit: "commit",
|
|
425
|
+
/** 回滚事务:`rollback, args=[txId]`。 */
|
|
426
|
+
rollback: "rollback",
|
|
427
|
+
/** 执行一条链:`chain, args=[ChainRequest]`。 */
|
|
428
|
+
chain: "chain"
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
// packages/runtime/src/functions/runtime/chain.ts
|
|
432
|
+
function isChainCapability(value) {
|
|
433
|
+
if (typeof value !== "object" || value === null) return false;
|
|
434
|
+
const spec = value[CHAIN_CAPABILITY_KEY];
|
|
435
|
+
return typeof spec === "object" && spec !== null && Array.isArray(spec.stepMethods) && Array.isArray(spec.terminalMethods);
|
|
436
|
+
}
|
|
437
|
+
function createChainClient(capability, port, pending, nextId, spec) {
|
|
438
|
+
const call = (method, args) => new Promise((resolve, reject) => {
|
|
439
|
+
const id = nextId();
|
|
440
|
+
pending.set(id, { resolve, reject });
|
|
441
|
+
port.postMessage({ type: "rpc", id, capability, method, args });
|
|
442
|
+
});
|
|
443
|
+
const makeChainable = (rootArgs, steps, txId) => new Proxy(
|
|
444
|
+
{},
|
|
445
|
+
{
|
|
446
|
+
get(_target, prop) {
|
|
447
|
+
if (typeof prop !== "string") return void 0;
|
|
448
|
+
if (spec.stepMethods.includes(prop)) {
|
|
449
|
+
return (...args) => makeChainable(rootArgs, [...steps, { method: prop, args }], txId);
|
|
450
|
+
}
|
|
451
|
+
if (spec.terminalMethods.includes(prop)) {
|
|
452
|
+
return (...args) => call(DB_RPC.chain, [
|
|
453
|
+
{
|
|
454
|
+
rootArgs,
|
|
455
|
+
steps,
|
|
456
|
+
terminal: prop,
|
|
457
|
+
terminalArgs: args,
|
|
458
|
+
...txId === void 0 ? {} : { txId }
|
|
459
|
+
}
|
|
460
|
+
]);
|
|
461
|
+
}
|
|
462
|
+
return void 0;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
const makeRoot = (txId) => new Proxy(
|
|
467
|
+
{},
|
|
468
|
+
{
|
|
469
|
+
get(_target, prop) {
|
|
470
|
+
if (typeof prop !== "string") return void 0;
|
|
471
|
+
if (prop === spec.rootMethod) {
|
|
472
|
+
return (...rootArgs) => makeChainable(rootArgs, [], txId);
|
|
473
|
+
}
|
|
474
|
+
if (spec.directMethods.includes(prop)) {
|
|
475
|
+
return (...args) => call(prop, args);
|
|
476
|
+
}
|
|
477
|
+
if (prop === spec.transactionMethod) {
|
|
478
|
+
return (fn) => orchestrateTransaction(fn, txId);
|
|
479
|
+
}
|
|
480
|
+
return void 0;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
);
|
|
484
|
+
const orchestrateTransaction = async (fn, _outerTxId) => {
|
|
485
|
+
const txId = await call(DB_RPC.begin, []);
|
|
486
|
+
const tx = makeRoot(txId);
|
|
487
|
+
const settle = async (commit) => {
|
|
488
|
+
await call(commit ? DB_RPC.commit : DB_RPC.rollback, [txId]).catch(() => void 0);
|
|
489
|
+
};
|
|
490
|
+
try {
|
|
491
|
+
const result = await fn(tx);
|
|
492
|
+
await settle(true);
|
|
493
|
+
return result;
|
|
494
|
+
} catch (error) {
|
|
495
|
+
await settle(false);
|
|
496
|
+
throw error;
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
return makeRoot(void 0);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// packages/runtime/src/functions/deps/manifest.ts
|
|
503
|
+
function splitBareSpecifier(request) {
|
|
504
|
+
if (request.startsWith("@")) {
|
|
505
|
+
const segments = request.split("/");
|
|
506
|
+
return segments.slice(0, 2).join("/");
|
|
507
|
+
}
|
|
508
|
+
return request.split("/")[0];
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// packages/runtime/src/functions/runtime/worker-entry.ts
|
|
512
|
+
function isRpcCapability(value) {
|
|
513
|
+
return typeof value === "object" && value !== null && value[RPC_CAPABILITY_KEY] === true;
|
|
514
|
+
}
|
|
515
|
+
function createRpcClient(capability, port, pending, nextId) {
|
|
516
|
+
return new Proxy(
|
|
517
|
+
{},
|
|
518
|
+
{
|
|
519
|
+
get(_target, method) {
|
|
520
|
+
if (typeof method !== "string") return void 0;
|
|
521
|
+
return (...args) => {
|
|
522
|
+
const id = nextId();
|
|
523
|
+
return new Promise((resolve, reject) => {
|
|
524
|
+
pending.set(id, { resolve, reject });
|
|
525
|
+
port.postMessage({ type: "rpc", id, capability, method, args });
|
|
526
|
+
});
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
);
|
|
531
|
+
}
|
|
532
|
+
function errorMessageOf2(error) {
|
|
533
|
+
if (error instanceof Error) return error.message;
|
|
534
|
+
if (typeof error === "object" && error !== null && "message" in error) {
|
|
535
|
+
const message = error.message;
|
|
536
|
+
if (typeof message === "string") return message;
|
|
537
|
+
}
|
|
538
|
+
return String(error);
|
|
539
|
+
}
|
|
540
|
+
function escapeRegExp(text) {
|
|
541
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
542
|
+
}
|
|
543
|
+
function firstUserFrame(stack, files) {
|
|
544
|
+
for (const key of Object.keys(files)) {
|
|
545
|
+
const match = new RegExp(
|
|
546
|
+
`(?<![\\w$/.])${escapeRegExp(key)}:(\\d+)(?::(\\d+))?(?::(\\d+))?`
|
|
547
|
+
).exec(stack);
|
|
548
|
+
if (match !== null) {
|
|
549
|
+
return match[2] === void 0 ? `${key}:${match[1]}` : `${key}:${match[1]}:${match[2]}`;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
return void 0;
|
|
553
|
+
}
|
|
554
|
+
function errorMessageWithLocation(error, files) {
|
|
555
|
+
const message = errorMessageOf2(error);
|
|
556
|
+
const stack = error.stack;
|
|
557
|
+
if (typeof stack !== "string") return message;
|
|
558
|
+
const frame = firstUserFrame(stack, files);
|
|
559
|
+
return frame === void 0 ? message : `${message}\uFF08${frame}\uFF09`;
|
|
560
|
+
}
|
|
561
|
+
function createBareResolver(deps) {
|
|
562
|
+
const platformRequire = createRequire(import.meta.url);
|
|
563
|
+
const depsRequire = deps.dir === null ? null : createRequire(join(deps.dir, "__deps__.js"));
|
|
564
|
+
return (request) => {
|
|
565
|
+
const root = splitBareSpecifier(request);
|
|
566
|
+
if (deps.builtin.includes(root)) return platformRequire(request);
|
|
567
|
+
if (deps.custom.includes(root)) {
|
|
568
|
+
if (depsRequire === null) {
|
|
569
|
+
throw new Error(`\u4F9D\u8D56 "${root}" \u672A\u88C5\u8F7D\uFF1A\u9879\u76EE\u4F9D\u8D56\u76EE\u5F55\u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u53D1\u5E03\u51FD\u6570\u89E6\u53D1\u4F9D\u8D56\u88C5\u8F7D`);
|
|
570
|
+
}
|
|
571
|
+
try {
|
|
572
|
+
return depsRequire(request);
|
|
573
|
+
} catch (error2) {
|
|
574
|
+
const message = errorMessageOf2(error2);
|
|
575
|
+
throw new Error(
|
|
576
|
+
`\u4F9D\u8D56 "${request}" \u88C5\u8F7D\u5931\u8D25\uFF08\u76EE\u5F55 ${deps.dir}\uFF09\uFF1A\u8BF7\u91CD\u65B0\u53D1\u5E03\u51FD\u6570\u4EE5\u89E6\u53D1\u4F9D\u8D56\u5B89\u88C5\u3002\u539F\u56E0\uFF1A${message}`,
|
|
577
|
+
{ cause: error2 }
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
const error = new Error(
|
|
582
|
+
`\u6C99\u7BB1\u62D2\u7EDD\u8BE5\u5F15\u7528\uFF1A"${request}"\u3002\u4EC5\u5141\u8BB8\u76F8\u5BF9\u8DEF\u5F84\u6587\u4EF6\u4E0E\u5DF2\u58F0\u660E\u7684\u4F9D\u8D56\uFF08\u5185\u7F6E\u767D\u540D\u5355\u6216 package.json\uFF09\uFF0C\u88F8\u6A21\u5757 "${root}" \u672A\u58F0\u660E\uFF08FN_SANDBOX_BUILTIN_BLOCKED\uFF09`
|
|
583
|
+
);
|
|
584
|
+
error.code = SANDBOX_BUILTIN_REJECTED;
|
|
585
|
+
throw error;
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
function runWorker(input, port) {
|
|
589
|
+
const emit = (level, args) => {
|
|
590
|
+
port.postMessage({ type: "log", level, message: args.map(String).join(" ") });
|
|
591
|
+
};
|
|
592
|
+
const forwardedConsole = {
|
|
593
|
+
log: (...args) => emit("log", args),
|
|
594
|
+
error: (...args) => emit("error", args),
|
|
595
|
+
warn: (...args) => emit("warn", args)
|
|
596
|
+
};
|
|
597
|
+
const container = createCloudContainer();
|
|
598
|
+
const pending = /* @__PURE__ */ new Map();
|
|
599
|
+
let rpcId = 0;
|
|
600
|
+
port.on?.((message) => {
|
|
601
|
+
const reply = message;
|
|
602
|
+
if (reply?.type !== "rpc-response" || typeof reply.id !== "number") return;
|
|
603
|
+
const entry = pending.get(reply.id);
|
|
604
|
+
if (entry === void 0) return;
|
|
605
|
+
pending.delete(reply.id);
|
|
606
|
+
if (reply.ok === true) entry.resolve(reply.result);
|
|
607
|
+
else entry.reject(new Error(reply.error ?? "RPC \u80FD\u529B\u8C03\u7528\u5931\u8D25"));
|
|
608
|
+
});
|
|
609
|
+
for (const capability of input.capabilities ?? []) {
|
|
610
|
+
container.register(
|
|
611
|
+
capability.name,
|
|
612
|
+
isChainCapability(capability.value) ? createChainClient(
|
|
613
|
+
capability.name,
|
|
614
|
+
port,
|
|
615
|
+
pending,
|
|
616
|
+
() => ++rpcId,
|
|
617
|
+
capability.value[CHAIN_CAPABILITY_KEY]
|
|
618
|
+
) : isRpcCapability(capability.value) ? createRpcClient(capability.name, port, pending, () => ++rpcId) : capability.value
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
const sandboxGlobals = {
|
|
622
|
+
console: forwardedConsole
|
|
623
|
+
};
|
|
624
|
+
if (input.env !== void 0) {
|
|
625
|
+
sandboxGlobals["process"] = { env: input.env };
|
|
626
|
+
}
|
|
627
|
+
if (input.fetch !== void 0) {
|
|
628
|
+
const sandboxFetch = createSandboxFetch({
|
|
629
|
+
allowlist: input.fetch.allowlist,
|
|
630
|
+
timeoutMs: input.fetch.timeoutMs,
|
|
631
|
+
...input.fetch.maxResponseBytes === void 0 ? {} : { maxResponseBytes: input.fetch.maxResponseBytes }
|
|
632
|
+
});
|
|
633
|
+
sandboxGlobals["fetch"] = sandboxFetch;
|
|
634
|
+
container.register("fetch", sandboxFetch);
|
|
635
|
+
}
|
|
636
|
+
const ctx = {
|
|
637
|
+
method: input.request.method,
|
|
638
|
+
path: input.request.path,
|
|
639
|
+
query: input.request.query,
|
|
640
|
+
headers: input.request.headers,
|
|
641
|
+
...input.request.body === void 0 ? {} : { body: input.request.body },
|
|
642
|
+
files: input.request.files ?? [],
|
|
643
|
+
cloud: container.cloud,
|
|
644
|
+
user: input.request.user ?? null
|
|
645
|
+
};
|
|
646
|
+
try {
|
|
647
|
+
const { handler } = loadFunctionModule(
|
|
648
|
+
input.files,
|
|
649
|
+
input.entry,
|
|
650
|
+
sandboxGlobals,
|
|
651
|
+
input.deps === void 0 ? void 0 : createBareResolver(input.deps)
|
|
652
|
+
);
|
|
653
|
+
const result = Promise.resolve(handler(ctx));
|
|
654
|
+
void result.then(
|
|
655
|
+
(value) => {
|
|
656
|
+
port.postMessage({ type: "result", body: value === void 0 ? null : value });
|
|
657
|
+
return void 0;
|
|
658
|
+
},
|
|
659
|
+
(error) => {
|
|
660
|
+
port.postMessage({
|
|
661
|
+
type: "error",
|
|
662
|
+
message: errorMessageWithLocation(error, input.files),
|
|
663
|
+
code: error.code,
|
|
664
|
+
// FN-011:函数抛错可携带 status(4xx/5xx),透传给执行器落 ExecutorError.status。
|
|
665
|
+
status: error.status
|
|
666
|
+
});
|
|
667
|
+
return void 0;
|
|
668
|
+
}
|
|
669
|
+
);
|
|
670
|
+
} catch (error) {
|
|
671
|
+
port.postMessage({
|
|
672
|
+
type: "error",
|
|
673
|
+
message: errorMessageWithLocation(error, input.files),
|
|
674
|
+
code: error.code,
|
|
675
|
+
status: error.status
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
var workerPort = parentPort;
|
|
680
|
+
if (workerPort !== null && workerData !== void 0) {
|
|
681
|
+
const port = {
|
|
682
|
+
// eslint-disable-next-line unicorn/require-post-message-target-origin -- node worker_threads 无 targetOrigin 语义
|
|
683
|
+
postMessage: (message) => workerPort.postMessage(message),
|
|
684
|
+
on: (listener) => {
|
|
685
|
+
workerPort.on("message", (value) => listener(value));
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
runWorker(workerData, port);
|
|
689
|
+
}
|
|
690
|
+
export {
|
|
691
|
+
runWorker
|
|
692
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adep/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "`adep` 命令行工具包位:登录 / 初始化 / 本地调试 / 部署 / 数据库维护 / 云存储 / 静态托管 / 微前端组件(widget),实现见任务单 CLI-001 ~ CLI-003 与 FE-002",
|
|
6
6
|
"bin": {
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"esbuild": "0.25.12"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@adep/runtime": "0.1.
|
|
18
|
-
"@adep/types": "0.1.
|
|
17
|
+
"@adep/runtime": "0.1.2",
|
|
18
|
+
"@adep/types": "0.1.3"
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"test": "vitest run",
|