@adep/cli 0.1.1 → 0.1.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.
- package/dist/index.js +2405 -164
- package/dist/vite.js +1378 -21
- package/dist/worker-entry.js +33 -2
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -250,6 +250,51 @@ function asStringRecord(value) {
|
|
|
250
250
|
}
|
|
251
251
|
return rec;
|
|
252
252
|
}
|
|
253
|
+
function asBindingArray(value, label, errors, optionalKey) {
|
|
254
|
+
if (!Array.isArray(value)) {
|
|
255
|
+
errors.push(`${label} \u5FC5\u987B\u662F\u6570\u7EC4\uFF0C\u6536\u5230 ${typeNameOf(value)}`);
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
const out = [];
|
|
259
|
+
value.forEach((item, i) => {
|
|
260
|
+
const idx = `${label}[${i}]`;
|
|
261
|
+
if (item === null || typeof item !== "object" || Array.isArray(item)) {
|
|
262
|
+
errors.push(`${idx} \u5FC5\u987B\u662F\u5BF9\u8C61\uFF0C\u6536\u5230 ${typeNameOf(item)}`);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const rec = item;
|
|
266
|
+
if (typeof rec.name !== "string" || rec.name.length === 0) {
|
|
267
|
+
errors.push(`${idx}.name \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\uFF0C\u6536\u5230 ${typeNameOf(rec.name)}`);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const entry = { name: rec.name };
|
|
271
|
+
const optional = rec[optionalKey];
|
|
272
|
+
if (optional !== void 0) {
|
|
273
|
+
if (typeof optional !== "string") {
|
|
274
|
+
errors.push(`${idx}.${optionalKey} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32\uFF0C\u6536\u5230 ${typeNameOf(optional)}`);
|
|
275
|
+
} else {
|
|
276
|
+
entry[optionalKey] = optional;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
out.push(entry);
|
|
280
|
+
});
|
|
281
|
+
return out;
|
|
282
|
+
}
|
|
283
|
+
function asCfBindingsConfig(value, errors) {
|
|
284
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
285
|
+
errors.push(`cfBindings \u5FC5\u987B\u662F\u5BF9\u8C61\uFF0C\u6536\u5230 ${typeNameOf(value)}`);
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
const rec = value;
|
|
289
|
+
const bindings = {};
|
|
290
|
+
const kv = rec.kv === void 0 ? void 0 : asBindingArray(rec.kv, "cfBindings.kv", errors, "namespace");
|
|
291
|
+
if (kv !== null && kv !== void 0) bindings.kv = kv;
|
|
292
|
+
const d1 = rec.d1 === void 0 ? void 0 : asBindingArray(rec.d1, "cfBindings.d1", errors, "database");
|
|
293
|
+
if (d1 !== null && d1 !== void 0) bindings.d1 = d1;
|
|
294
|
+
const r2 = rec.r2 === void 0 ? void 0 : asBindingArray(rec.r2, "cfBindings.r2", errors, "bucket");
|
|
295
|
+
if (r2 !== null && r2 !== void 0) bindings.r2 = r2;
|
|
296
|
+
return bindings;
|
|
297
|
+
}
|
|
253
298
|
function parseAdepConfig(raw) {
|
|
254
299
|
if (raw === null || raw === void 0) {
|
|
255
300
|
return { ok: true, data: { ...DEFAULT_ADEP_CONFIG } };
|
|
@@ -310,6 +355,10 @@ function parseAdepConfig(raw) {
|
|
|
310
355
|
data.runtime = rec;
|
|
311
356
|
}
|
|
312
357
|
}
|
|
358
|
+
if (input.cfBindings !== void 0) {
|
|
359
|
+
const bindings = asCfBindingsConfig(input.cfBindings, errors);
|
|
360
|
+
if (bindings !== null) data.cfBindings = bindings;
|
|
361
|
+
}
|
|
313
362
|
if (errors.length > 0) return { ok: false, errors };
|
|
314
363
|
return { ok: true, data };
|
|
315
364
|
}
|
|
@@ -345,6 +394,9 @@ function renderAdepConfig(config, options = {}) {
|
|
|
345
394
|
if (config.runtime !== void 0) {
|
|
346
395
|
lines.push(` runtime: ${renderTsValue(config.runtime)},`);
|
|
347
396
|
}
|
|
397
|
+
if (config.cfBindings !== void 0) {
|
|
398
|
+
lines.push(` cfBindings: ${renderTsValue(config.cfBindings)},`);
|
|
399
|
+
}
|
|
348
400
|
if (options.extraFields !== void 0) {
|
|
349
401
|
for (const [key, value] of Object.entries(options.extraFields)) {
|
|
350
402
|
lines.push(` ${key}: ${renderTsValue(value)},`);
|
|
@@ -417,6 +469,8 @@ var init_capability_keys = __esm({
|
|
|
417
469
|
DB_RPC = {
|
|
418
470
|
/** 直通方法(如 query):`(method=query, args=[sql, params])`。 */
|
|
419
471
|
query: "query",
|
|
472
|
+
/** 写路径(CF-013):`(method=writeQuery, args=[sql, params])` → `{ changes }`。 */
|
|
473
|
+
writeQuery: "writeQuery",
|
|
420
474
|
/** 读取 owned 表变更流(DB-006):`(method=changes, args=[table, query])`。 */
|
|
421
475
|
changes: "changes",
|
|
422
476
|
/** 开启事务:`begin → txId`。 */
|
|
@@ -479,6 +533,30 @@ var init_executor = __esm({
|
|
|
479
533
|
}
|
|
480
534
|
});
|
|
481
535
|
|
|
536
|
+
// packages/runtime/src/shared/http-envelope.ts
|
|
537
|
+
function isAdepHttpEnvelope(value) {
|
|
538
|
+
if (typeof value !== "object" || value === null) return false;
|
|
539
|
+
const envelope = value.__adepHttp;
|
|
540
|
+
if (typeof envelope !== "object" || envelope === null) return false;
|
|
541
|
+
const status = envelope.status;
|
|
542
|
+
return typeof status === "number" && Number.isInteger(status) && status >= 100 && status <= 599;
|
|
543
|
+
}
|
|
544
|
+
function writeEnvelopeResponse(res, envelope) {
|
|
545
|
+
const { status, headers, body } = envelope.__adepHttp;
|
|
546
|
+
const noBody = status >= 100 && status < 200 || status === 204 || status === 205;
|
|
547
|
+
res.writeHead(status, { "content-type": "application/json", ...headers });
|
|
548
|
+
if (noBody) {
|
|
549
|
+
res.end();
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
res.end(JSON.stringify(body === void 0 ? null : body));
|
|
553
|
+
}
|
|
554
|
+
var init_http_envelope = __esm({
|
|
555
|
+
"packages/runtime/src/shared/http-envelope.ts"() {
|
|
556
|
+
"use strict";
|
|
557
|
+
}
|
|
558
|
+
});
|
|
559
|
+
|
|
482
560
|
// packages/runtime/src/functions/domain.ts
|
|
483
561
|
var MAX_TOTAL_SOURCE_BYTES, FnError;
|
|
484
562
|
var init_domain = __esm({
|
|
@@ -573,6 +651,13 @@ function classifyDependencies(dependencies, builtin) {
|
|
|
573
651
|
}
|
|
574
652
|
return { builtinDeps, customDeps };
|
|
575
653
|
}
|
|
654
|
+
function splitBareSpecifier(request) {
|
|
655
|
+
if (request.startsWith("@")) {
|
|
656
|
+
const segments = request.split("/");
|
|
657
|
+
return segments.slice(0, 2).join("/");
|
|
658
|
+
}
|
|
659
|
+
return request.split("/")[0];
|
|
660
|
+
}
|
|
576
661
|
function depsKeyOf(dependencies) {
|
|
577
662
|
const canonical = Object.entries(dependencies).map(([name, version]) => `${name}@${version}`).toSorted().join("\n");
|
|
578
663
|
return createHash("sha256").update(canonical).digest("hex").slice(0, 16);
|
|
@@ -589,14 +674,14 @@ var init_manifest = __esm({
|
|
|
589
674
|
});
|
|
590
675
|
|
|
591
676
|
// packages/runtime/src/functions/deps/resolve.ts
|
|
592
|
-
import { resolve as
|
|
677
|
+
import { resolve as resolve3 } from "node:path";
|
|
593
678
|
function resolveExecutionDeps(config, projectId, manifestContent) {
|
|
594
679
|
const manifest = tryParseManifestDependencies(manifestContent);
|
|
595
680
|
const { customDeps } = classifyDependencies(manifest, config.builtin);
|
|
596
681
|
const custom = Object.keys(customDeps);
|
|
597
682
|
return {
|
|
598
683
|
// 绝对化(相对进程 cwd,与 installer 侧 resolve(rootDir) 同基准):worker 的 createRequire 需要确定路径
|
|
599
|
-
dir: custom.length === 0 ? null :
|
|
684
|
+
dir: custom.length === 0 ? null : resolve3(config.rootDir, projectId, depsKeyOf(customDeps)),
|
|
600
685
|
builtin: [...config.builtin],
|
|
601
686
|
custom
|
|
602
687
|
};
|
|
@@ -611,22 +696,22 @@ var init_resolve = __esm({
|
|
|
611
696
|
// packages/runtime/src/functions/runtime/worker-executor.ts
|
|
612
697
|
import { readFileSync } from "node:fs";
|
|
613
698
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
614
|
-
import { dirname as
|
|
699
|
+
import { dirname as dirname4, join as join3 } from "node:path";
|
|
615
700
|
import { Worker } from "node:worker_threads";
|
|
616
701
|
function resolveWorkerEntry(baseDir = fileURLToPath(new URL(".", import.meta.url))) {
|
|
617
702
|
const candidates = [
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
703
|
+
join3(baseDir, "worker-entry.js"),
|
|
704
|
+
join3(baseDir, "worker-entry.ts"),
|
|
705
|
+
join3(baseDir, "domains/functions/runtime/worker-entry.js"),
|
|
706
|
+
join3(baseDir, "domains/functions/runtime/worker-entry.ts")
|
|
622
707
|
];
|
|
623
708
|
let current = baseDir;
|
|
624
709
|
for (let depth = 0; depth < 8; depth += 1) {
|
|
625
710
|
candidates.push(
|
|
626
|
-
|
|
627
|
-
|
|
711
|
+
join3(current, "packages/runtime/src/functions/runtime/worker-entry.js"),
|
|
712
|
+
join3(current, "packages/runtime/src/functions/runtime/worker-entry.ts")
|
|
628
713
|
);
|
|
629
|
-
const parent =
|
|
714
|
+
const parent = dirname4(current);
|
|
630
715
|
if (parent === current) break;
|
|
631
716
|
current = parent;
|
|
632
717
|
}
|
|
@@ -668,7 +753,7 @@ var init_worker_executor = __esm({
|
|
|
668
753
|
const entry = input.entry ?? "index.ts";
|
|
669
754
|
const deps = this.depsConfig === void 0 ? void 0 : resolveExecutionDeps(this.depsConfig, input.project.id, input.files["package.json"]);
|
|
670
755
|
const logs = [];
|
|
671
|
-
return new Promise((
|
|
756
|
+
return new Promise((resolve13, reject) => {
|
|
672
757
|
let worker;
|
|
673
758
|
try {
|
|
674
759
|
worker = new Worker(resolveWorkerEntry(), {
|
|
@@ -730,7 +815,7 @@ var init_worker_executor = __esm({
|
|
|
730
815
|
if (message.type === "result") {
|
|
731
816
|
clearTimeout(timer);
|
|
732
817
|
void worker.terminate();
|
|
733
|
-
|
|
818
|
+
resolve13({ body: message.body, logs });
|
|
734
819
|
return;
|
|
735
820
|
}
|
|
736
821
|
if (message.type === "error") {
|
|
@@ -827,21 +912,35 @@ function unsafeOperation(message) {
|
|
|
827
912
|
function stripLiterals(sql) {
|
|
828
913
|
return sql.replace(/'[^']*(?:''[^']*)*'/g, "").replace(/"[^"]*(?:""[^"]*)*"/g, "").replace(/--[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
829
914
|
}
|
|
830
|
-
function
|
|
915
|
+
function sqlHead(sql) {
|
|
916
|
+
return sql.trim().replace(/^\(+/, "").trim().toUpperCase();
|
|
917
|
+
}
|
|
918
|
+
function assertSafeStatement(sql) {
|
|
831
919
|
const stripped = stripLiterals(sql);
|
|
832
920
|
if (stripped.includes(";")) {
|
|
833
921
|
throw unsafeOperation("\u4EC5\u5141\u8BB8\u5355\u6761\u8BED\u53E5\uFF0C\u7981\u6B62\u591A\u8BED\u53E5\u5806\u53E0");
|
|
834
922
|
}
|
|
835
|
-
const head = stripped.trim().replace(/^\(+/, "").trim().toUpperCase();
|
|
836
|
-
if (!head.startsWith("SELECT") && !head.startsWith("WITH")) {
|
|
837
|
-
throw unsafeOperation("cloud.db.query \u4EC5\u5141\u8BB8\u53EA\u8BFB\u67E5\u8BE2\uFF08SELECT / WITH\uFF09");
|
|
838
|
-
}
|
|
839
923
|
if (/ATTACH|DETACH/.test(stripped.toUpperCase())) {
|
|
840
924
|
throw unsafeOperation("\u7981\u6B62\u8DE8\u5E93\u64CD\u4F5C\uFF08ATTACH / DETACH\uFF09");
|
|
841
925
|
}
|
|
842
926
|
if (/SQLITE_\w+/i.test(stripped)) {
|
|
843
927
|
throw unsafeOperation("\u7981\u6B62\u8BBF\u95EE\u7CFB\u7EDF\u8868\uFF08sqlite_*\uFF09");
|
|
844
928
|
}
|
|
929
|
+
return sqlHead(stripped);
|
|
930
|
+
}
|
|
931
|
+
function assertReadOnlyQuery(sql) {
|
|
932
|
+
const head = assertSafeStatement(sql);
|
|
933
|
+
if (!head.startsWith("SELECT") && !head.startsWith("WITH")) {
|
|
934
|
+
throw unsafeOperation("cloud.db.query \u4EC5\u5141\u8BB8\u53EA\u8BFB\u67E5\u8BE2\uFF08SELECT / WITH\uFF09");
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
function assertWriteQuery(sql) {
|
|
938
|
+
const head = assertSafeStatement(sql);
|
|
939
|
+
if (!head.startsWith("INSERT") && !head.startsWith("UPDATE") && !head.startsWith("DELETE")) {
|
|
940
|
+
throw unsafeOperation(
|
|
941
|
+
"cloud.db.writeQuery \u4EC5\u5141\u8BB8 DML\uFF08INSERT / UPDATE / DELETE\uFF09\uFF0CDDL \u8BF7\u8D70\u63A7\u5236\u53F0"
|
|
942
|
+
);
|
|
943
|
+
}
|
|
845
944
|
}
|
|
846
945
|
var init_guards = __esm({
|
|
847
946
|
"packages/runtime/src/database/builder/guards.ts"() {
|
|
@@ -1417,6 +1516,14 @@ function createCloudDb(driver, options = {}) {
|
|
|
1417
1516
|
assertReadOnlyQuery(sql);
|
|
1418
1517
|
return driver.all(sql, params);
|
|
1419
1518
|
},
|
|
1519
|
+
async writeQuery(sql, params) {
|
|
1520
|
+
if (!Array.isArray(params)) {
|
|
1521
|
+
throw unsafeOperation("writeQuery \u7684 params \u5FC5\u987B\u63D0\u4F9B\uFF08\u65E0\u53C2\u4F20 []\uFF09");
|
|
1522
|
+
}
|
|
1523
|
+
guardWrite();
|
|
1524
|
+
assertWriteQuery(sql);
|
|
1525
|
+
return driver.run(sql, params);
|
|
1526
|
+
},
|
|
1420
1527
|
async changes(table, query = {}) {
|
|
1421
1528
|
return readChanges(driver, table, query);
|
|
1422
1529
|
}
|
|
@@ -1489,6 +1596,10 @@ function createDbCapability(driver, options = {}) {
|
|
|
1489
1596
|
const [sql, params] = args;
|
|
1490
1597
|
return await db.query(sql, params);
|
|
1491
1598
|
}
|
|
1599
|
+
case DB_RPC.writeQuery: {
|
|
1600
|
+
const [sql, params] = args;
|
|
1601
|
+
return await db.writeQuery(sql, params);
|
|
1602
|
+
}
|
|
1492
1603
|
case DB_RPC.changes: {
|
|
1493
1604
|
const [table, query] = args;
|
|
1494
1605
|
return await db.changes(table, query);
|
|
@@ -1543,7 +1654,8 @@ var init_cloud = __esm({
|
|
|
1543
1654
|
rootMethod: "table",
|
|
1544
1655
|
stepMethods: ["select", "where", "orderBy", "limit", "offset"],
|
|
1545
1656
|
terminalMethods: ["get", "first", "count", "insert", "insertMany", "update", "delete"],
|
|
1546
|
-
|
|
1657
|
+
// CF-013:writeQuery 是 D1 shim 的写路径(INSERT/UPDATE/DELETE),与 query 并列直通方法。
|
|
1658
|
+
directMethods: ["query", "writeQuery", "changes"],
|
|
1547
1659
|
transactionMethod: "transaction"
|
|
1548
1660
|
};
|
|
1549
1661
|
WRITE_TERMINALS = /* @__PURE__ */ new Set(["insert", "insertMany", "update", "delete"]);
|
|
@@ -2001,6 +2113,113 @@ var init_db = __esm({
|
|
|
2001
2113
|
}
|
|
2002
2114
|
});
|
|
2003
2115
|
|
|
2116
|
+
// packages/runtime/src/database/sdk/kv.ts
|
|
2117
|
+
function nowSeconds() {
|
|
2118
|
+
return Math.floor(Date.now() / 1e3);
|
|
2119
|
+
}
|
|
2120
|
+
function errorWithCode2(error) {
|
|
2121
|
+
const code = typeof error === "object" && error !== null && typeof error.code === "string" ? error.code : void 0;
|
|
2122
|
+
if (code !== void 0 && error instanceof Error) {
|
|
2123
|
+
return new Error(`[${code}] ${error.message}`);
|
|
2124
|
+
}
|
|
2125
|
+
return error;
|
|
2126
|
+
}
|
|
2127
|
+
function createKvCapability(driver) {
|
|
2128
|
+
let ensurePromise = null;
|
|
2129
|
+
const ensureTable = () => {
|
|
2130
|
+
if (ensurePromise === null) {
|
|
2131
|
+
ensurePromise = driver.run(
|
|
2132
|
+
`CREATE TABLE IF NOT EXISTS ${KV_TABLE} (key TEXT PRIMARY KEY, value TEXT, expires_at INTEGER)`
|
|
2133
|
+
).then(() => void 0);
|
|
2134
|
+
}
|
|
2135
|
+
return ensurePromise;
|
|
2136
|
+
};
|
|
2137
|
+
const handler = async (method, args) => {
|
|
2138
|
+
try {
|
|
2139
|
+
await ensureTable();
|
|
2140
|
+
switch (method) {
|
|
2141
|
+
case KV_RPC.get: {
|
|
2142
|
+
const [key] = args;
|
|
2143
|
+
const row = await driver.get(`SELECT value, expires_at FROM ${KV_TABLE} WHERE key = ?`, [
|
|
2144
|
+
key
|
|
2145
|
+
]);
|
|
2146
|
+
if (row === null || row === void 0) return null;
|
|
2147
|
+
const expiresAt = row.expires_at;
|
|
2148
|
+
if (expiresAt !== null && expiresAt !== void 0 && Number(expiresAt) <= nowSeconds()) {
|
|
2149
|
+
return null;
|
|
2150
|
+
}
|
|
2151
|
+
return row.value;
|
|
2152
|
+
}
|
|
2153
|
+
case KV_RPC.put: {
|
|
2154
|
+
const [key, value, options] = args;
|
|
2155
|
+
const ttl = options?.expirationTtlSeconds;
|
|
2156
|
+
const expiresAt = typeof ttl === "number" && ttl > 0 ? nowSeconds() + Math.floor(ttl) : null;
|
|
2157
|
+
await driver.run(`DELETE FROM ${KV_TABLE} WHERE key = ?`, [key]);
|
|
2158
|
+
await driver.run(`INSERT INTO ${KV_TABLE} (key, value, expires_at) VALUES (?, ?, ?)`, [
|
|
2159
|
+
key,
|
|
2160
|
+
value,
|
|
2161
|
+
expiresAt
|
|
2162
|
+
]);
|
|
2163
|
+
return void 0;
|
|
2164
|
+
}
|
|
2165
|
+
case KV_RPC.delete: {
|
|
2166
|
+
const [key] = args;
|
|
2167
|
+
await driver.run(`DELETE FROM ${KV_TABLE} WHERE key = ?`, [key]);
|
|
2168
|
+
return void 0;
|
|
2169
|
+
}
|
|
2170
|
+
case KV_RPC.list: {
|
|
2171
|
+
const [options] = args;
|
|
2172
|
+
const prefix = options?.prefix ?? "";
|
|
2173
|
+
const limit = options?.limit ?? KV_LIST_DEFAULT_LIMIT;
|
|
2174
|
+
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 ?`, [
|
|
2175
|
+
`${prefix}%`
|
|
2176
|
+
]);
|
|
2177
|
+
const now = nowSeconds();
|
|
2178
|
+
const entries = [];
|
|
2179
|
+
for (const row of rows) {
|
|
2180
|
+
const expiresAt = row.expires_at;
|
|
2181
|
+
if (expiresAt !== null && expiresAt !== void 0 && Number(expiresAt) <= now) continue;
|
|
2182
|
+
entries.push({
|
|
2183
|
+
name: String(row.key),
|
|
2184
|
+
expiration: expiresAt === null || expiresAt === void 0 ? null : Number(expiresAt)
|
|
2185
|
+
});
|
|
2186
|
+
if (entries.length >= limit) break;
|
|
2187
|
+
}
|
|
2188
|
+
return entries;
|
|
2189
|
+
}
|
|
2190
|
+
default:
|
|
2191
|
+
throw new Error(
|
|
2192
|
+
`\u672A\u77E5\u7684 cloud.kv \u65B9\u6CD5 "${String(method)}"\uFF08${KV_CAPABILITY_CODES.invalidMethod}\uFF09`
|
|
2193
|
+
);
|
|
2194
|
+
}
|
|
2195
|
+
} catch (error) {
|
|
2196
|
+
throw errorWithCode2(error);
|
|
2197
|
+
}
|
|
2198
|
+
};
|
|
2199
|
+
return {
|
|
2200
|
+
capabilities: [{ name: "kv", value: { [RPC_CAPABILITY_KEY]: true } }],
|
|
2201
|
+
rpcHandlers: { kv: handler }
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
var KV_TABLE, KV_RPC, KV_CAPABILITY_CODES, KV_LIST_DEFAULT_LIMIT;
|
|
2205
|
+
var init_kv = __esm({
|
|
2206
|
+
"packages/runtime/src/database/sdk/kv.ts"() {
|
|
2207
|
+
"use strict";
|
|
2208
|
+
init_capability_keys();
|
|
2209
|
+
KV_TABLE = "_adep_kv";
|
|
2210
|
+
KV_RPC = {
|
|
2211
|
+
get: "get",
|
|
2212
|
+
put: "put",
|
|
2213
|
+
delete: "delete",
|
|
2214
|
+
list: "list"
|
|
2215
|
+
};
|
|
2216
|
+
KV_CAPABILITY_CODES = {
|
|
2217
|
+
invalidMethod: "KV_INVALID_METHOD"
|
|
2218
|
+
};
|
|
2219
|
+
KV_LIST_DEFAULT_LIMIT = 100;
|
|
2220
|
+
}
|
|
2221
|
+
});
|
|
2222
|
+
|
|
2004
2223
|
// packages/runtime/src/storage/driver.ts
|
|
2005
2224
|
function assertSafeStoragePath(path) {
|
|
2006
2225
|
if (typeof path !== "string" || path.length === 0) {
|
|
@@ -2259,9 +2478,12 @@ function createStorageCapability(driver, signer, projectId) {
|
|
|
2259
2478
|
const handler = async (method, args) => {
|
|
2260
2479
|
switch (method) {
|
|
2261
2480
|
case "upload": {
|
|
2262
|
-
const [path, data] = args;
|
|
2481
|
+
const [path, data, options] = args;
|
|
2263
2482
|
return toStoredFile(
|
|
2264
|
-
await driver.put(projectId, path, asBytes(data), {
|
|
2483
|
+
await driver.put(projectId, path, asBytes(data), {
|
|
2484
|
+
visibility: DEFAULT_VISIBILITY,
|
|
2485
|
+
...options?.contentType === void 0 ? {} : { contentType: options.contentType }
|
|
2486
|
+
})
|
|
2265
2487
|
);
|
|
2266
2488
|
}
|
|
2267
2489
|
case "get": {
|
|
@@ -2306,15 +2528,15 @@ var init_cloud2 = __esm({
|
|
|
2306
2528
|
});
|
|
2307
2529
|
|
|
2308
2530
|
// packages/runtime/src/storage/driver/local.ts
|
|
2309
|
-
import { mkdir as
|
|
2310
|
-
import { dirname as
|
|
2531
|
+
import { mkdir as mkdir4, readFile as readFile3, readdir as readdir2, rm as rm2, stat as stat4, unlink, writeFile as writeFile3 } from "node:fs/promises";
|
|
2532
|
+
import { dirname as dirname5, join as join4 } from "node:path";
|
|
2311
2533
|
function createLocalStorageDriver(options) {
|
|
2312
2534
|
const quotaBytes = options.quotaBytes ?? PROJECT_QUOTA_BYTES;
|
|
2313
|
-
const bucketDir = (projectId) =>
|
|
2314
|
-
const objectPath = (projectId, path) =>
|
|
2315
|
-
const metaPath = (projectId, path) =>
|
|
2535
|
+
const bucketDir = (projectId) => join4(options.dir, projectId);
|
|
2536
|
+
const objectPath = (projectId, path) => join4(bucketDir(projectId), path);
|
|
2537
|
+
const metaPath = (projectId, path) => join4(dirname5(objectPath(projectId, path)), `${path.split("/").pop() ?? ""}${META_SUFFIX}`);
|
|
2316
2538
|
const readStoredMeta = async (projectId, path) => {
|
|
2317
|
-
const raw = await
|
|
2539
|
+
const raw = await readFile3(metaPath(projectId, path), "utf8");
|
|
2318
2540
|
return JSON.parse(raw);
|
|
2319
2541
|
};
|
|
2320
2542
|
const writeStoredMeta = async (projectId, path, meta) => {
|
|
@@ -2324,7 +2546,7 @@ function createLocalStorageDriver(options) {
|
|
|
2324
2546
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2325
2547
|
...meta.contentType === void 0 ? {} : { contentType: meta.contentType }
|
|
2326
2548
|
};
|
|
2327
|
-
await
|
|
2549
|
+
await writeFile3(metaPath(projectId, path), JSON.stringify(payload), "utf8");
|
|
2328
2550
|
};
|
|
2329
2551
|
const totalSize = async (projectId) => {
|
|
2330
2552
|
const root = bucketDir(projectId);
|
|
@@ -2332,20 +2554,20 @@ function createLocalStorageDriver(options) {
|
|
|
2332
2554
|
const walk = async (dir) => {
|
|
2333
2555
|
let entries;
|
|
2334
2556
|
try {
|
|
2335
|
-
entries = await
|
|
2557
|
+
entries = await readdir2(dir, { withFileTypes: true });
|
|
2336
2558
|
} catch {
|
|
2337
2559
|
return;
|
|
2338
2560
|
}
|
|
2339
2561
|
const tasks = [];
|
|
2340
2562
|
for (const entry of entries) {
|
|
2341
|
-
const full =
|
|
2563
|
+
const full = join4(dir, entry.name);
|
|
2342
2564
|
if (entry.isDirectory()) {
|
|
2343
2565
|
tasks.push(walk(full));
|
|
2344
2566
|
} else if (entry.isFile() && !entry.name.endsWith(META_SUFFIX)) {
|
|
2345
2567
|
tasks.push(
|
|
2346
2568
|
(async () => {
|
|
2347
2569
|
try {
|
|
2348
|
-
const info = await
|
|
2570
|
+
const info = await stat4(full);
|
|
2349
2571
|
total += info.size;
|
|
2350
2572
|
} catch {
|
|
2351
2573
|
}
|
|
@@ -2367,7 +2589,7 @@ function createLocalStorageDriver(options) {
|
|
|
2367
2589
|
const used = await totalSize(projectId);
|
|
2368
2590
|
let existingSize = 0;
|
|
2369
2591
|
try {
|
|
2370
|
-
existingSize = (await
|
|
2592
|
+
existingSize = (await stat4(objectPath(projectId, path))).size;
|
|
2371
2593
|
} catch {
|
|
2372
2594
|
existingSize = 0;
|
|
2373
2595
|
}
|
|
@@ -2382,8 +2604,8 @@ function createLocalStorageDriver(options) {
|
|
|
2382
2604
|
);
|
|
2383
2605
|
}
|
|
2384
2606
|
const dest = objectPath(projectId, path);
|
|
2385
|
-
await
|
|
2386
|
-
await
|
|
2607
|
+
await mkdir4(dirname5(dest), { recursive: true });
|
|
2608
|
+
await writeFile3(dest, data);
|
|
2387
2609
|
await writeStoredMeta(projectId, path, {
|
|
2388
2610
|
visibility: putOptions.visibility,
|
|
2389
2611
|
size: data.byteLength,
|
|
@@ -2406,7 +2628,7 @@ function createLocalStorageDriver(options) {
|
|
|
2406
2628
|
} catch {
|
|
2407
2629
|
throw new StorageError(404, STORAGE_CODES.notFound, `\u6587\u4EF6 "${path}" \u4E0D\u5B58\u5728`);
|
|
2408
2630
|
}
|
|
2409
|
-
const data = await
|
|
2631
|
+
const data = await readFile3(objectPath(projectId, path));
|
|
2410
2632
|
const result = {
|
|
2411
2633
|
path,
|
|
2412
2634
|
size: meta.size,
|
|
@@ -2437,7 +2659,7 @@ function createLocalStorageDriver(options) {
|
|
|
2437
2659
|
assertSafeStoragePath(path);
|
|
2438
2660
|
let exists = true;
|
|
2439
2661
|
try {
|
|
2440
|
-
await
|
|
2662
|
+
await stat4(objectPath(projectId, path));
|
|
2441
2663
|
} catch {
|
|
2442
2664
|
exists = false;
|
|
2443
2665
|
}
|
|
@@ -2460,25 +2682,25 @@ function createLocalStorageDriver(options) {
|
|
|
2460
2682
|
}
|
|
2461
2683
|
const root = bucketDir(projectId);
|
|
2462
2684
|
const metas = [];
|
|
2463
|
-
const walk = async (dir,
|
|
2685
|
+
const walk = async (dir, relative4) => {
|
|
2464
2686
|
let entries;
|
|
2465
2687
|
try {
|
|
2466
|
-
entries = await
|
|
2688
|
+
entries = await readdir2(dir, { withFileTypes: true });
|
|
2467
2689
|
} catch {
|
|
2468
2690
|
return;
|
|
2469
2691
|
}
|
|
2470
2692
|
const tasks = [];
|
|
2471
2693
|
for (const entry of entries) {
|
|
2472
2694
|
if (entry.name.endsWith(META_SUFFIX)) continue;
|
|
2473
|
-
const full =
|
|
2474
|
-
const rel =
|
|
2695
|
+
const full = join4(dir, entry.name);
|
|
2696
|
+
const rel = relative4 === "" ? entry.name : `${relative4}/${entry.name}`;
|
|
2475
2697
|
if (entry.isFile()) {
|
|
2476
2698
|
if (prefix !== void 0 && !rel.startsWith(prefix)) continue;
|
|
2477
2699
|
tasks.push(
|
|
2478
2700
|
(async () => {
|
|
2479
2701
|
try {
|
|
2480
2702
|
const payload = JSON.parse(
|
|
2481
|
-
await
|
|
2703
|
+
await readFile3(full + META_SUFFIX, "utf8")
|
|
2482
2704
|
);
|
|
2483
2705
|
metas.push({
|
|
2484
2706
|
path: rel,
|
|
@@ -2520,9 +2742,9 @@ var init_local = __esm({
|
|
|
2520
2742
|
});
|
|
2521
2743
|
|
|
2522
2744
|
// packages/cli/src/sim/storage.ts
|
|
2523
|
-
import { join as
|
|
2745
|
+
import { join as join5 } from "node:path";
|
|
2524
2746
|
function createSimStorageCapability(options) {
|
|
2525
|
-
const dir =
|
|
2747
|
+
const dir = join5(options.cwd, ".adep", "sim", "storage");
|
|
2526
2748
|
const driver = createLocalStorageDriver({ dir });
|
|
2527
2749
|
const signer = {
|
|
2528
2750
|
secret: options.secret ?? "adep-sim-secret",
|
|
@@ -2669,10 +2891,10 @@ var init_realtime = __esm({
|
|
|
2669
2891
|
});
|
|
2670
2892
|
|
|
2671
2893
|
// packages/cli/src/sim/runtime.ts
|
|
2672
|
-
import { mkdir as
|
|
2673
|
-
import { dirname as
|
|
2894
|
+
import { mkdir as mkdir5, readFile as readFile4, writeFile as writeFile4 } from "node:fs/promises";
|
|
2895
|
+
import { dirname as dirname6, join as join6 } from "node:path";
|
|
2674
2896
|
function simDir(cwd) {
|
|
2675
|
-
return
|
|
2897
|
+
return join6(cwd, ".adep", "sim");
|
|
2676
2898
|
}
|
|
2677
2899
|
function mergeBundles(bundles) {
|
|
2678
2900
|
const capabilities = [];
|
|
@@ -2694,19 +2916,19 @@ function mergeBundles(bundles) {
|
|
|
2694
2916
|
return { capabilities, rpcHandlers };
|
|
2695
2917
|
}
|
|
2696
2918
|
function jsonStorage(cwd) {
|
|
2697
|
-
const file =
|
|
2919
|
+
const file = join6(simDir(cwd), "db.json");
|
|
2698
2920
|
return {
|
|
2699
2921
|
async load() {
|
|
2700
2922
|
try {
|
|
2701
|
-
const raw = await
|
|
2923
|
+
const raw = await readFile4(file, "utf8");
|
|
2702
2924
|
return JSON.parse(raw);
|
|
2703
2925
|
} catch {
|
|
2704
2926
|
return null;
|
|
2705
2927
|
}
|
|
2706
2928
|
},
|
|
2707
2929
|
async save(tables) {
|
|
2708
|
-
await
|
|
2709
|
-
await
|
|
2930
|
+
await mkdir5(dirname6(file), { recursive: true });
|
|
2931
|
+
await writeFile4(file, JSON.stringify(tables), "utf8");
|
|
2710
2932
|
}
|
|
2711
2933
|
};
|
|
2712
2934
|
}
|
|
@@ -2721,7 +2943,8 @@ async function createSimRuntime(options) {
|
|
|
2721
2943
|
projectId: options.projectId ?? "local"
|
|
2722
2944
|
});
|
|
2723
2945
|
const simRealtime = createSimRealtimeCapability();
|
|
2724
|
-
const
|
|
2946
|
+
const kvBundle = createKvCapability(db.driver);
|
|
2947
|
+
const bundle = mergeBundles([db.bundle, storage.bundle, kvBundle, simRealtime.bundle]);
|
|
2725
2948
|
await db.engine.load();
|
|
2726
2949
|
return {
|
|
2727
2950
|
bundle,
|
|
@@ -2736,14 +2959,15 @@ var init_runtime = __esm({
|
|
|
2736
2959
|
"packages/cli/src/sim/runtime.ts"() {
|
|
2737
2960
|
"use strict";
|
|
2738
2961
|
init_db();
|
|
2962
|
+
init_kv();
|
|
2739
2963
|
init_storage();
|
|
2740
2964
|
init_realtime();
|
|
2741
2965
|
}
|
|
2742
2966
|
});
|
|
2743
2967
|
|
|
2744
2968
|
// packages/cli/src/sim/env.ts
|
|
2745
|
-
import { readFile as
|
|
2746
|
-
import { join as
|
|
2969
|
+
import { readFile as readFile5 } from "node:fs/promises";
|
|
2970
|
+
import { join as join7 } from "node:path";
|
|
2747
2971
|
function parseEnv(content) {
|
|
2748
2972
|
const env = {};
|
|
2749
2973
|
for (const raw of content.split(/\r?\n/)) {
|
|
@@ -2758,8 +2982,8 @@ function parseEnv(content) {
|
|
|
2758
2982
|
return env;
|
|
2759
2983
|
}
|
|
2760
2984
|
async function loadSimEnv(cwd, config = {}) {
|
|
2761
|
-
const envRoot = await
|
|
2762
|
-
const envLocal = await
|
|
2985
|
+
const envRoot = await readFile5(join7(cwd, ".env"), "utf8").catch(() => "");
|
|
2986
|
+
const envLocal = await readFile5(join7(cwd, ".env.local"), "utf8").catch(() => "");
|
|
2763
2987
|
const merged = { ...parseEnv(envRoot), ...parseEnv(envLocal) };
|
|
2764
2988
|
for (const secret of config.secrets ?? []) {
|
|
2765
2989
|
if (merged[secret] !== void 0) continue;
|
|
@@ -2837,6 +3061,19 @@ var init_invoke = __esm({
|
|
|
2837
3061
|
}
|
|
2838
3062
|
});
|
|
2839
3063
|
|
|
3064
|
+
// packages/cli/src/builtin-deps.ts
|
|
3065
|
+
var LOCAL_STATE_OBJECTS_SPECIFIER, LOCAL_BUILTIN_DEPS;
|
|
3066
|
+
var init_builtin_deps = __esm({
|
|
3067
|
+
"packages/cli/src/builtin-deps.ts"() {
|
|
3068
|
+
"use strict";
|
|
3069
|
+
LOCAL_STATE_OBJECTS_SPECIFIER = "@adep/runtime/state-objects";
|
|
3070
|
+
LOCAL_BUILTIN_DEPS = [
|
|
3071
|
+
"@adep/cf-compat",
|
|
3072
|
+
LOCAL_STATE_OBJECTS_SPECIFIER
|
|
3073
|
+
];
|
|
3074
|
+
}
|
|
3075
|
+
});
|
|
3076
|
+
|
|
2840
3077
|
// packages/cli/src/sim/boundary.ts
|
|
2841
3078
|
var SIM_BOUNDARIES;
|
|
2842
3079
|
var init_boundary = __esm({
|
|
@@ -2873,12 +3110,12 @@ var init_boundary = __esm({
|
|
|
2873
3110
|
});
|
|
2874
3111
|
|
|
2875
3112
|
// packages/cli/src/ts-config.ts
|
|
2876
|
-
import { stat as
|
|
2877
|
-
import { join as
|
|
3113
|
+
import { stat as stat5, readFile as readFile6 } from "node:fs/promises";
|
|
3114
|
+
import { join as join8 } from "node:path";
|
|
2878
3115
|
async function loadAdepConfigModule(cwd) {
|
|
2879
|
-
const configPath =
|
|
3116
|
+
const configPath = join8(cwd, "adep.config.ts");
|
|
2880
3117
|
try {
|
|
2881
|
-
await
|
|
3118
|
+
await stat5(configPath);
|
|
2882
3119
|
} catch {
|
|
2883
3120
|
return null;
|
|
2884
3121
|
}
|
|
@@ -2890,7 +3127,7 @@ async function loadAdepConfigModule(cwd) {
|
|
|
2890
3127
|
rawDefault = mod.default;
|
|
2891
3128
|
} else {
|
|
2892
3129
|
const { transform } = await import("esbuild");
|
|
2893
|
-
const source = await
|
|
3130
|
+
const source = await readFile6(configPath, "utf8");
|
|
2894
3131
|
const { code } = await transform(source, {
|
|
2895
3132
|
loader: "ts",
|
|
2896
3133
|
format: "esm",
|
|
@@ -2920,7 +3157,1120 @@ ${result.errors.map((e) => ` - ${e}`).join("\n")}`;
|
|
|
2920
3157
|
var init_ts_config = __esm({
|
|
2921
3158
|
"packages/cli/src/ts-config.ts"() {
|
|
2922
3159
|
"use strict";
|
|
2923
|
-
init_adep_config();
|
|
3160
|
+
init_adep_config();
|
|
3161
|
+
}
|
|
3162
|
+
});
|
|
3163
|
+
|
|
3164
|
+
// packages/runtime/src/state-objects/runtime.ts
|
|
3165
|
+
var StateObject, StateObjectError, StateObjectRuntime;
|
|
3166
|
+
var init_runtime2 = __esm({
|
|
3167
|
+
"packages/runtime/src/state-objects/runtime.ts"() {
|
|
3168
|
+
"use strict";
|
|
3169
|
+
StateObject = class {
|
|
3170
|
+
id;
|
|
3171
|
+
/** 对象内存状态(单例驻留);load 时从后端恢复。 */
|
|
3172
|
+
state;
|
|
3173
|
+
alarmAt = null;
|
|
3174
|
+
/** 运行时注入的持久化句柄(内部;protected,子类 flush() 使用)。 */
|
|
3175
|
+
runtime;
|
|
3176
|
+
constructor(id) {
|
|
3177
|
+
this.id = id;
|
|
3178
|
+
this.state = this.initialState();
|
|
3179
|
+
}
|
|
3180
|
+
/** 供运行时读取/序列化。 */
|
|
3181
|
+
getState() {
|
|
3182
|
+
return { ...this.state };
|
|
3183
|
+
}
|
|
3184
|
+
/** 通用快照方法(任何对象可经 invoke(type, id, 'snapshot') 读取)。 */
|
|
3185
|
+
async snapshot() {
|
|
3186
|
+
return this.getState();
|
|
3187
|
+
}
|
|
3188
|
+
/** 设置未来唤醒(alarm):到期时运行时调 onAlarm(调度 CF-037 落地)。 */
|
|
3189
|
+
setAlarm(at) {
|
|
3190
|
+
this.alarmAt = at;
|
|
3191
|
+
}
|
|
3192
|
+
clearAlarm() {
|
|
3193
|
+
this.alarmAt = null;
|
|
3194
|
+
}
|
|
3195
|
+
/** alarm 到期回调(子类覆写)。 */
|
|
3196
|
+
async onAlarm() {
|
|
3197
|
+
}
|
|
3198
|
+
/** 显式持久化当前状态(方法内任何位置可调)。 */
|
|
3199
|
+
async flush() {
|
|
3200
|
+
await this.runtime.flush(this);
|
|
3201
|
+
}
|
|
3202
|
+
};
|
|
3203
|
+
StateObjectError = class extends Error {
|
|
3204
|
+
code;
|
|
3205
|
+
constructor(code, message) {
|
|
3206
|
+
super(message);
|
|
3207
|
+
this.name = "StateObjectError";
|
|
3208
|
+
this.code = code;
|
|
3209
|
+
}
|
|
3210
|
+
};
|
|
3211
|
+
StateObjectRuntime = class {
|
|
3212
|
+
registry = /* @__PURE__ */ new Map();
|
|
3213
|
+
instances = /* @__PURE__ */ new Map();
|
|
3214
|
+
backend;
|
|
3215
|
+
constructor(backend) {
|
|
3216
|
+
this.backend = backend;
|
|
3217
|
+
}
|
|
3218
|
+
define(cls) {
|
|
3219
|
+
this.registry.set(cls.typeName, cls);
|
|
3220
|
+
}
|
|
3221
|
+
/** 已注册类型清单(控制台/审计用)。 */
|
|
3222
|
+
types() {
|
|
3223
|
+
return [...this.registry.keys()];
|
|
3224
|
+
}
|
|
3225
|
+
/** 内存驻留实例数(诊断用)。 */
|
|
3226
|
+
residentCount() {
|
|
3227
|
+
return this.instances.size;
|
|
3228
|
+
}
|
|
3229
|
+
key(type, id) {
|
|
3230
|
+
return `${type}:${id}`;
|
|
3231
|
+
}
|
|
3232
|
+
/** 取对象实例(单例驻留):内存有 → 复用;无 → 后端恢复或 new 初始态。 */
|
|
3233
|
+
async get(type, id) {
|
|
3234
|
+
const cls = this.registry.get(type);
|
|
3235
|
+
if (cls === void 0) {
|
|
3236
|
+
throw new StateObjectError(
|
|
3237
|
+
"STATE_TYPE_NOT_REGISTERED",
|
|
3238
|
+
`\u72B6\u6001\u5BF9\u8C61\u7C7B\u578B "${type}" \u672A\u6CE8\u518C\uFF08\u5DF2\u6CE8\u518C\uFF1A${this.types().join(", ") || "\u65E0"}\uFF09`
|
|
3239
|
+
);
|
|
3240
|
+
}
|
|
3241
|
+
const k = this.key(type, id);
|
|
3242
|
+
const hit = this.instances.get(k);
|
|
3243
|
+
if (hit !== void 0) return hit;
|
|
3244
|
+
const snapshot = await this.backend.load(type, id);
|
|
3245
|
+
const raw = new cls(id);
|
|
3246
|
+
const created = raw;
|
|
3247
|
+
created.state = snapshot !== null ? snapshot.state : created.state;
|
|
3248
|
+
created.alarmAt = snapshot !== null ? snapshot.alarmAt : null;
|
|
3249
|
+
created.runtime = this;
|
|
3250
|
+
this.instances.set(k, created);
|
|
3251
|
+
return created;
|
|
3252
|
+
}
|
|
3253
|
+
/** 方法路由:单例对象上串行执行方法。 */
|
|
3254
|
+
async invoke(type, id, method, args = []) {
|
|
3255
|
+
const inst = await this.get(type, id);
|
|
3256
|
+
const fn = inst[method];
|
|
3257
|
+
if (typeof fn !== "function") {
|
|
3258
|
+
throw new StateObjectError(
|
|
3259
|
+
"STATE_METHOD_NOT_FOUND",
|
|
3260
|
+
`\u72B6\u6001\u5BF9\u8C61 "${type}:${id}" \u65E0\u65B9\u6CD5 "${method}"`
|
|
3261
|
+
);
|
|
3262
|
+
}
|
|
3263
|
+
try {
|
|
3264
|
+
return await fn.call(inst, ...args);
|
|
3265
|
+
} catch (error) {
|
|
3266
|
+
if (error instanceof StateObjectError) throw error;
|
|
3267
|
+
throw new StateObjectError(
|
|
3268
|
+
"STATE_INVOKE_ERROR",
|
|
3269
|
+
`\u72B6\u6001\u5BF9\u8C61 "${type}:${id}" \u65B9\u6CD5 "${method}" \u6267\u884C\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`
|
|
3270
|
+
);
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
/** 持久化快照(对象方法内 flush() 触发,或运行时统一写)。 */
|
|
3274
|
+
async flush(inst) {
|
|
3275
|
+
const raw = inst;
|
|
3276
|
+
const type = inst.constructor.typeName;
|
|
3277
|
+
if (type === void 0) {
|
|
3278
|
+
throw new StateObjectError("STATE_TYPE_NOT_REGISTERED", "\u72B6\u6001\u5BF9\u8C61\u7C7B\u5FC5\u987B\u58F0\u660E static typeName");
|
|
3279
|
+
}
|
|
3280
|
+
await this.backend.save(type, inst.id, inst.getState(), raw.alarmAt);
|
|
3281
|
+
}
|
|
3282
|
+
/**
|
|
3283
|
+
* alarm 轮询:把到期对象唤醒并调 onAlarm(CF-037)。
|
|
3284
|
+
* - onAlarm 后统一持久化(flush):onAlarm 内改的 state 与 setAlarm 重调度 / clearAlarm
|
|
3285
|
+
* 的新 alarmAt 都落库;
|
|
3286
|
+
* - 单对象 onAlarm 抛错**不阻塞**同 tick 其余对象:失败对象不落库、alarmAt 保留
|
|
3287
|
+
* → 下轮 dueAlarms 仍命中重试;错误折叠进 failed(不泄漏内部栈)。
|
|
3288
|
+
*/
|
|
3289
|
+
async pump(now) {
|
|
3290
|
+
const due = await this.backend.dueAlarms(now);
|
|
3291
|
+
const fired = [];
|
|
3292
|
+
const failed = [];
|
|
3293
|
+
for (const d of due) {
|
|
3294
|
+
try {
|
|
3295
|
+
const inst = await this.get(d.type, d.id);
|
|
3296
|
+
await inst.onAlarm();
|
|
3297
|
+
await this.flush(inst);
|
|
3298
|
+
fired.push({ type: d.type, id: d.id });
|
|
3299
|
+
} catch (error) {
|
|
3300
|
+
failed.push({
|
|
3301
|
+
type: d.type,
|
|
3302
|
+
id: d.id,
|
|
3303
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3304
|
+
});
|
|
3305
|
+
}
|
|
3306
|
+
}
|
|
3307
|
+
return { fired, failed };
|
|
3308
|
+
}
|
|
3309
|
+
/** 释放内存驻留(进程重启 / 测试隔离用);不删后端数据。 */
|
|
3310
|
+
evictAll() {
|
|
3311
|
+
this.instances.clear();
|
|
3312
|
+
}
|
|
3313
|
+
};
|
|
3314
|
+
}
|
|
3315
|
+
});
|
|
3316
|
+
|
|
3317
|
+
// packages/runtime/src/state-objects/backend.ts
|
|
3318
|
+
function createProjectDriverStateBackend(driver) {
|
|
3319
|
+
let ensured = false;
|
|
3320
|
+
const ensureTable = async () => {
|
|
3321
|
+
if (ensured) return;
|
|
3322
|
+
await driver.run(
|
|
3323
|
+
`CREATE TABLE IF NOT EXISTS ${STATE_OBJECTS_TABLE} ("type" TEXT NOT NULL, "id" TEXT NOT NULL, "state" TEXT NOT NULL, "alarm_at" INTEGER, "updated_at" INTEGER NOT NULL, PRIMARY KEY ("type", "id"))`
|
|
3324
|
+
);
|
|
3325
|
+
ensured = true;
|
|
3326
|
+
};
|
|
3327
|
+
return {
|
|
3328
|
+
async load(type, id) {
|
|
3329
|
+
await ensureTable();
|
|
3330
|
+
const rows = await driver.all(
|
|
3331
|
+
`SELECT "state", "alarm_at" FROM ${STATE_OBJECTS_TABLE} WHERE "type" = ? AND "id" = ?`,
|
|
3332
|
+
[type, id]
|
|
3333
|
+
);
|
|
3334
|
+
if (rows.length === 0) return null;
|
|
3335
|
+
return {
|
|
3336
|
+
state: JSON.parse(String(rows[0].state)),
|
|
3337
|
+
alarmAt: rows[0].alarm_at
|
|
3338
|
+
};
|
|
3339
|
+
},
|
|
3340
|
+
async save(type, id, state, alarmAt) {
|
|
3341
|
+
await ensureTable();
|
|
3342
|
+
const existing = await driver.all(
|
|
3343
|
+
`SELECT 1 FROM ${STATE_OBJECTS_TABLE} WHERE "type" = ? AND "id" = ?`,
|
|
3344
|
+
[type, id]
|
|
3345
|
+
);
|
|
3346
|
+
const stateJson = JSON.stringify(state);
|
|
3347
|
+
if (existing.length === 0) {
|
|
3348
|
+
await driver.run(
|
|
3349
|
+
`INSERT INTO ${STATE_OBJECTS_TABLE} ("type", "id", "state", "alarm_at", "updated_at") VALUES (?, ?, ?, ?, ?)`,
|
|
3350
|
+
[type, id, stateJson, alarmAt, Date.now()]
|
|
3351
|
+
);
|
|
3352
|
+
} else {
|
|
3353
|
+
await driver.run(
|
|
3354
|
+
`UPDATE ${STATE_OBJECTS_TABLE} SET "state" = ?, "alarm_at" = ?, "updated_at" = ? WHERE "type" = ? AND "id" = ?`,
|
|
3355
|
+
[stateJson, alarmAt, Date.now(), type, id]
|
|
3356
|
+
);
|
|
3357
|
+
}
|
|
3358
|
+
},
|
|
3359
|
+
async dueAlarms(now) {
|
|
3360
|
+
await ensureTable();
|
|
3361
|
+
const rows = await driver.all(
|
|
3362
|
+
`SELECT "type", "id", "alarm_at" FROM ${STATE_OBJECTS_TABLE} WHERE "alarm_at" IS NOT NULL AND "alarm_at" <= ?`,
|
|
3363
|
+
[now]
|
|
3364
|
+
);
|
|
3365
|
+
return rows.map((r) => ({
|
|
3366
|
+
type: r.type,
|
|
3367
|
+
id: r.id,
|
|
3368
|
+
alarmAt: r.alarm_at
|
|
3369
|
+
}));
|
|
3370
|
+
},
|
|
3371
|
+
async remove(type, id) {
|
|
3372
|
+
await ensureTable();
|
|
3373
|
+
await driver.run(`DELETE FROM ${STATE_OBJECTS_TABLE} WHERE "type" = ? AND "id" = ?`, [
|
|
3374
|
+
type,
|
|
3375
|
+
id
|
|
3376
|
+
]);
|
|
3377
|
+
}
|
|
3378
|
+
};
|
|
3379
|
+
}
|
|
3380
|
+
var STATE_OBJECTS_TABLE;
|
|
3381
|
+
var init_backend = __esm({
|
|
3382
|
+
"packages/runtime/src/state-objects/backend.ts"() {
|
|
3383
|
+
"use strict";
|
|
3384
|
+
STATE_OBJECTS_TABLE = "_adep_state_objects";
|
|
3385
|
+
}
|
|
3386
|
+
});
|
|
3387
|
+
|
|
3388
|
+
// packages/runtime/src/state-objects/http.ts
|
|
3389
|
+
function parseStatePath(rest) {
|
|
3390
|
+
const segments = rest.split("/").filter((s) => s.length > 0);
|
|
3391
|
+
if (segments[0] !== STATE_PATH || segments.length < 4) return null;
|
|
3392
|
+
return {
|
|
3393
|
+
type: decodeURIComponent(segments[1]),
|
|
3394
|
+
id: decodeURIComponent(segments[2]),
|
|
3395
|
+
method: decodeURIComponent(segments.slice(3).join("/"))
|
|
3396
|
+
};
|
|
3397
|
+
}
|
|
3398
|
+
function parseStateBody(body) {
|
|
3399
|
+
if (body === null || body.length === 0) return { args: [] };
|
|
3400
|
+
try {
|
|
3401
|
+
const parsed = JSON.parse(body);
|
|
3402
|
+
if (!Array.isArray(parsed)) return { error: "body \u5FC5\u987B\u662F\u53C2\u6570\u6570\u7EC4\uFF08JSON\uFF09" };
|
|
3403
|
+
return { args: parsed };
|
|
3404
|
+
} catch {
|
|
3405
|
+
return { error: "body \u5FC5\u987B\u662F\u5408\u6CD5 JSON" };
|
|
3406
|
+
}
|
|
3407
|
+
}
|
|
3408
|
+
var STATE_PATH;
|
|
3409
|
+
var init_http = __esm({
|
|
3410
|
+
"packages/runtime/src/state-objects/http.ts"() {
|
|
3411
|
+
"use strict";
|
|
3412
|
+
STATE_PATH = "state";
|
|
3413
|
+
}
|
|
3414
|
+
});
|
|
3415
|
+
|
|
3416
|
+
// packages/runtime/src/state-objects/demo.ts
|
|
3417
|
+
var DemoCounter, DemoChatRoom;
|
|
3418
|
+
var init_demo = __esm({
|
|
3419
|
+
"packages/runtime/src/state-objects/demo.ts"() {
|
|
3420
|
+
"use strict";
|
|
3421
|
+
init_runtime2();
|
|
3422
|
+
DemoCounter = class extends StateObject {
|
|
3423
|
+
static typeName = "DemoCounter";
|
|
3424
|
+
initialState() {
|
|
3425
|
+
return { count: 0 };
|
|
3426
|
+
}
|
|
3427
|
+
async increment(by = 1) {
|
|
3428
|
+
this.state.count += by;
|
|
3429
|
+
await this.flush();
|
|
3430
|
+
return { count: this.state.count };
|
|
3431
|
+
}
|
|
3432
|
+
async get() {
|
|
3433
|
+
return { count: this.state.count };
|
|
3434
|
+
}
|
|
3435
|
+
};
|
|
3436
|
+
DemoChatRoom = class extends StateObject {
|
|
3437
|
+
static typeName = "DemoChatRoom";
|
|
3438
|
+
initialState() {
|
|
3439
|
+
return { members: [], messages: [], cursor: 0 };
|
|
3440
|
+
}
|
|
3441
|
+
async join(user) {
|
|
3442
|
+
if (!this.state.members.includes(user)) this.state.members.push(user);
|
|
3443
|
+
this.state.cursor += 1;
|
|
3444
|
+
await this.flush();
|
|
3445
|
+
return { members: this.state.members, cursor: this.state.cursor };
|
|
3446
|
+
}
|
|
3447
|
+
async send(user, text) {
|
|
3448
|
+
const seq = this.state.messages.length + 1;
|
|
3449
|
+
this.state.messages.push({ user, text, at: Date.now() });
|
|
3450
|
+
await this.flush();
|
|
3451
|
+
return { seq };
|
|
3452
|
+
}
|
|
3453
|
+
};
|
|
3454
|
+
}
|
|
3455
|
+
});
|
|
3456
|
+
|
|
3457
|
+
// packages/runtime/src/functions/runtime/exports.ts
|
|
3458
|
+
function transformModule(code) {
|
|
3459
|
+
let out = code;
|
|
3460
|
+
const named = [];
|
|
3461
|
+
out = out.replace(/(^|\n)(\s*)export\s+default\s+/g, "$1$2module.default = ");
|
|
3462
|
+
out = out.replace(
|
|
3463
|
+
/import\s*\{([^}]*)\}\s*from\s*(['"][^'"]*['"])[ \t]*;?/g,
|
|
3464
|
+
(_match, names, source) => `const {${names}} = require(${source})`
|
|
3465
|
+
);
|
|
3466
|
+
out = out.replace(
|
|
3467
|
+
/import\s*\*\s*as\s*([A-Za-z_$][\w$]*)\s*from\s*(['"][^'"]*['"])[ \t]*;?/g,
|
|
3468
|
+
(_match, name, source) => `const ${name} = require(${source})`
|
|
3469
|
+
);
|
|
3470
|
+
out = out.replace(
|
|
3471
|
+
/import\s+([A-Za-z_$][\w$]*)\s+from\s*(['"][^'"]*['"])[ \t]*;?/g,
|
|
3472
|
+
(_match, name, source) => `const ${name} = require(${source})`
|
|
3473
|
+
);
|
|
3474
|
+
out = out.replace(
|
|
3475
|
+
/export\s+(const|let|var)\s+([A-Za-z_$][\w$]*)/g,
|
|
3476
|
+
(_match, kw, name) => {
|
|
3477
|
+
named.push(name);
|
|
3478
|
+
return `${kw} ${name}`;
|
|
3479
|
+
}
|
|
3480
|
+
);
|
|
3481
|
+
out = out.replace(
|
|
3482
|
+
/export\s+(async\s+)?function\s*\*?\s*([A-Za-z_$][\w$]*)/g,
|
|
3483
|
+
(_match, asyncKw, name) => {
|
|
3484
|
+
named.push(name);
|
|
3485
|
+
return `${asyncKw ?? ""}function ${name}`;
|
|
3486
|
+
}
|
|
3487
|
+
);
|
|
3488
|
+
out = out.replace(/export\s+class\s+([A-Za-z_$][\w$]*)/g, (_match, name) => {
|
|
3489
|
+
named.push(name);
|
|
3490
|
+
return `class ${name}`;
|
|
3491
|
+
});
|
|
3492
|
+
if (named.length > 0) {
|
|
3493
|
+
out += `
|
|
3494
|
+
;Object.assign(exports, { ${named.join(", ")} })`;
|
|
3495
|
+
}
|
|
3496
|
+
return out;
|
|
3497
|
+
}
|
|
3498
|
+
var init_exports = __esm({
|
|
3499
|
+
"packages/runtime/src/functions/runtime/exports.ts"() {
|
|
3500
|
+
"use strict";
|
|
3501
|
+
}
|
|
3502
|
+
});
|
|
3503
|
+
|
|
3504
|
+
// packages/runtime/src/functions/runtime/sandbox.ts
|
|
3505
|
+
import vm from "node:vm";
|
|
3506
|
+
import * as nodeModule from "node:module";
|
|
3507
|
+
function transpile(code) {
|
|
3508
|
+
const bun = globalThis["Bun"];
|
|
3509
|
+
if (bun?.Transpiler !== void 0) {
|
|
3510
|
+
bunTranspiler ??= new bun.Transpiler({ loader: "ts" });
|
|
3511
|
+
return bunTranspiler.transformSync(code);
|
|
3512
|
+
}
|
|
3513
|
+
const stripTypeScriptTypes2 = nodeModule.stripTypeScriptTypes;
|
|
3514
|
+
if (typeof stripTypeScriptTypes2 === "function") {
|
|
3515
|
+
return stripTypeScriptTypes2(code, { mode: "strip" });
|
|
3516
|
+
}
|
|
3517
|
+
return code;
|
|
3518
|
+
}
|
|
3519
|
+
function transformExports(code) {
|
|
3520
|
+
return transformModule(code);
|
|
3521
|
+
}
|
|
3522
|
+
function resolveModulePath(fromDir, request, files) {
|
|
3523
|
+
if (!request.startsWith("./") && !request.startsWith("../")) return null;
|
|
3524
|
+
const base = joinPosix(fromDir, request);
|
|
3525
|
+
const candidates = [base, `${base}.ts`, `${base}.js`, `${base}/index.ts`, `${base}/index.js`];
|
|
3526
|
+
for (const candidate of candidates) {
|
|
3527
|
+
const normalized = normalizePosix(candidate);
|
|
3528
|
+
if (files[normalized] !== void 0) return normalized;
|
|
3529
|
+
}
|
|
3530
|
+
return null;
|
|
3531
|
+
}
|
|
3532
|
+
function joinPosix(dir, request) {
|
|
3533
|
+
const parts = [...dir.split("/"), ...request.split("/")];
|
|
3534
|
+
const stack = [];
|
|
3535
|
+
for (const part of parts) {
|
|
3536
|
+
if (part === "" || part === ".") continue;
|
|
3537
|
+
if (part === "..") stack.pop();
|
|
3538
|
+
else stack.push(part);
|
|
3539
|
+
}
|
|
3540
|
+
return stack.join("/");
|
|
3541
|
+
}
|
|
3542
|
+
function normalizePosix(p) {
|
|
3543
|
+
return joinPosix("", p);
|
|
3544
|
+
}
|
|
3545
|
+
function loadModule(path, files, sandboxGlobals, cache, resolveBare, bareCache) {
|
|
3546
|
+
const cached = cache.get(path);
|
|
3547
|
+
if (cached !== void 0) return cached;
|
|
3548
|
+
const source = files[path];
|
|
3549
|
+
if (source === void 0) {
|
|
3550
|
+
throw new Error(`\u6A21\u5757\u4E0D\u5B58\u5728\uFF1A${path}`);
|
|
3551
|
+
}
|
|
3552
|
+
const exports = {};
|
|
3553
|
+
cache.set(path, exports);
|
|
3554
|
+
const dir = path.includes("/") ? path.slice(0, path.lastIndexOf("/")) : "";
|
|
3555
|
+
const module = { exports, default: void 0 };
|
|
3556
|
+
const require2 = (request) => {
|
|
3557
|
+
const resolved = resolveModulePath(dir, request, files);
|
|
3558
|
+
if (resolved !== null) {
|
|
3559
|
+
return loadModule(resolved, files, sandboxGlobals, cache, resolveBare, bareCache);
|
|
3560
|
+
}
|
|
3561
|
+
if (resolveBare !== void 0 && !request.startsWith("./") && !request.startsWith("../")) {
|
|
3562
|
+
const cachedModule = bareCache?.get(request);
|
|
3563
|
+
if (cachedModule !== void 0) return cachedModule;
|
|
3564
|
+
const loaded = resolveBare(request);
|
|
3565
|
+
bareCache?.set(request, loaded);
|
|
3566
|
+
return loaded;
|
|
3567
|
+
}
|
|
3568
|
+
const error = new Error(
|
|
3569
|
+
`\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`
|
|
3570
|
+
);
|
|
3571
|
+
error.code = SANDBOX_BUILTIN_REJECTED;
|
|
3572
|
+
throw error;
|
|
3573
|
+
};
|
|
3574
|
+
let transformed;
|
|
3575
|
+
try {
|
|
3576
|
+
transformed = transformExports(transpile(source));
|
|
3577
|
+
} catch (error) {
|
|
3578
|
+
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
3579
|
+
const stack = error.stack;
|
|
3580
|
+
const line = typeof stack === "string" ? /(?:^|\n):(\d+)(?:\n|$)/.exec(stack)?.[1] : void 0;
|
|
3581
|
+
const location = line === void 0 ? path : `${path}:${line}`;
|
|
3582
|
+
const wrapped = new Error(`\u6E90\u7801\u8BED\u6CD5\u9519\u8BEF\uFF08${location}\uFF09\uFF1A${rawMessage}`, { cause: error });
|
|
3583
|
+
const code = error.code;
|
|
3584
|
+
if (typeof code === "string") wrapped.code = code;
|
|
3585
|
+
throw wrapped;
|
|
3586
|
+
}
|
|
3587
|
+
const wrapper = vm.runInNewContext(
|
|
3588
|
+
`(function (module, exports, require) { ${transformed}
|
|
3589
|
+
})`,
|
|
3590
|
+
vm.createContext({ ...sandboxGlobals }),
|
|
3591
|
+
{ filename: path }
|
|
3592
|
+
);
|
|
3593
|
+
wrapper(module, exports, require2);
|
|
3594
|
+
if (module.default !== void 0 && exports["default"] === void 0) {
|
|
3595
|
+
exports["default"] = module.default;
|
|
3596
|
+
}
|
|
3597
|
+
return exports;
|
|
3598
|
+
}
|
|
3599
|
+
function loadFunctionModule(files, entry, sandboxGlobals, resolveBare) {
|
|
3600
|
+
const cache = /* @__PURE__ */ new Map();
|
|
3601
|
+
const bareCache = /* @__PURE__ */ new Map();
|
|
3602
|
+
const entryExports = loadModule(entry, files, sandboxGlobals, cache, resolveBare, bareCache);
|
|
3603
|
+
const handler = entryExports["default"];
|
|
3604
|
+
if (typeof handler !== "function") {
|
|
3605
|
+
throw new Error(`\u5165\u53E3\u6587\u4EF6 ${entry} \u7F3A\u5C11 default \u5BFC\u51FA\u7684\u51FD\u6570`);
|
|
3606
|
+
}
|
|
3607
|
+
return { handler };
|
|
3608
|
+
}
|
|
3609
|
+
var bunTranspiler, SANDBOX_BUILTIN_REJECTED;
|
|
3610
|
+
var init_sandbox = __esm({
|
|
3611
|
+
"packages/runtime/src/functions/runtime/sandbox.ts"() {
|
|
3612
|
+
"use strict";
|
|
3613
|
+
init_exports();
|
|
3614
|
+
SANDBOX_BUILTIN_REJECTED = "FN_SANDBOX_BUILTIN_BLOCKED";
|
|
3615
|
+
}
|
|
3616
|
+
});
|
|
3617
|
+
|
|
3618
|
+
// packages/runtime/src/state-objects/loader.ts
|
|
3619
|
+
function resolveStateObjectSpecifier(request) {
|
|
3620
|
+
if (request === STATE_OBJECTS_SPECIFIER) return { StateObject };
|
|
3621
|
+
return void 0;
|
|
3622
|
+
}
|
|
3623
|
+
function defaultStateSandboxGlobals() {
|
|
3624
|
+
return {
|
|
3625
|
+
console,
|
|
3626
|
+
URL,
|
|
3627
|
+
URLSearchParams,
|
|
3628
|
+
Request,
|
|
3629
|
+
Response,
|
|
3630
|
+
Headers,
|
|
3631
|
+
TextEncoder,
|
|
3632
|
+
TextDecoder,
|
|
3633
|
+
AbortController,
|
|
3634
|
+
AbortSignal,
|
|
3635
|
+
ReadableStream,
|
|
3636
|
+
WritableStream,
|
|
3637
|
+
TransformStream,
|
|
3638
|
+
Blob,
|
|
3639
|
+
FormData,
|
|
3640
|
+
crypto,
|
|
3641
|
+
performance,
|
|
3642
|
+
atob,
|
|
3643
|
+
btoa,
|
|
3644
|
+
structuredClone
|
|
3645
|
+
};
|
|
3646
|
+
}
|
|
3647
|
+
function collectStateObjectClasses(exports_) {
|
|
3648
|
+
const classes = [];
|
|
3649
|
+
for (const value of Object.values(exports_)) {
|
|
3650
|
+
if (typeof value !== "function") continue;
|
|
3651
|
+
const proto = value.prototype;
|
|
3652
|
+
if (typeof proto !== "object" || proto === null) continue;
|
|
3653
|
+
if (!(proto instanceof StateObject)) continue;
|
|
3654
|
+
if (!Object.hasOwn(value, "typeName")) {
|
|
3655
|
+
throw new Error(
|
|
3656
|
+
`\u72B6\u6001\u5BF9\u8C61\u7C7B\u5FC5\u987B\u58F0\u660E static typeName\uFF08${value.name ?? "anonymous"}\uFF09`
|
|
3657
|
+
);
|
|
3658
|
+
}
|
|
3659
|
+
const typeName = value.typeName;
|
|
3660
|
+
if (typeof typeName !== "string" || typeName.length === 0) {
|
|
3661
|
+
throw new Error(
|
|
3662
|
+
`\u72B6\u6001\u5BF9\u8C61\u7C7B\u5FC5\u987B\u58F0\u660E static typeName\uFF08${value.name ?? "anonymous"}\uFF09`
|
|
3663
|
+
);
|
|
3664
|
+
}
|
|
3665
|
+
classes.push(value);
|
|
3666
|
+
}
|
|
3667
|
+
return classes;
|
|
3668
|
+
}
|
|
3669
|
+
function loadStateObjectClasses(files, entry = STATE_OBJECTS_ENTRY, options = {}) {
|
|
3670
|
+
if (files[entry] === void 0) {
|
|
3671
|
+
throw new Error(`\u72B6\u6001\u5BF9\u8C61\u58F0\u660E\u6587\u4EF6\u7F3A\u5931\uFF1A${entry}\uFF08\u7EA6\u5B9A ${STATE_OBJECTS_ENTRY}\uFF09`);
|
|
3672
|
+
}
|
|
3673
|
+
const globals = options.sandboxGlobals ?? defaultStateSandboxGlobals();
|
|
3674
|
+
const exportsObject = loadModule(entry, files, globals, /* @__PURE__ */ new Map(), options.resolveBare, /* @__PURE__ */ new Map());
|
|
3675
|
+
return collectStateObjectClasses(exportsObject);
|
|
3676
|
+
}
|
|
3677
|
+
var STATE_OBJECTS_ENTRY, STATE_OBJECTS_SPECIFIER;
|
|
3678
|
+
var init_loader = __esm({
|
|
3679
|
+
"packages/runtime/src/state-objects/loader.ts"() {
|
|
3680
|
+
"use strict";
|
|
3681
|
+
init_runtime2();
|
|
3682
|
+
init_sandbox();
|
|
3683
|
+
STATE_OBJECTS_ENTRY = "state-objects.ts";
|
|
3684
|
+
STATE_OBJECTS_SPECIFIER = "@adep/runtime/state-objects";
|
|
3685
|
+
}
|
|
3686
|
+
});
|
|
3687
|
+
|
|
3688
|
+
// packages/runtime/src/state-objects/index.ts
|
|
3689
|
+
var init_state_objects = __esm({
|
|
3690
|
+
"packages/runtime/src/state-objects/index.ts"() {
|
|
3691
|
+
"use strict";
|
|
3692
|
+
init_runtime2();
|
|
3693
|
+
init_backend();
|
|
3694
|
+
init_http();
|
|
3695
|
+
init_demo();
|
|
3696
|
+
init_loader();
|
|
3697
|
+
}
|
|
3698
|
+
});
|
|
3699
|
+
|
|
3700
|
+
// packages/runtime/src/functions/runtime/cloud-container.ts
|
|
3701
|
+
function createCloud(registrations) {
|
|
3702
|
+
const store = registrations;
|
|
3703
|
+
return new Proxy(
|
|
3704
|
+
{},
|
|
3705
|
+
{
|
|
3706
|
+
get(_target, prop) {
|
|
3707
|
+
if (typeof prop !== "string") return void 0;
|
|
3708
|
+
if (!store.has(prop)) {
|
|
3709
|
+
const hint = KNOWN_CAPABILITY_HINTS[prop];
|
|
3710
|
+
throw new CapabilityNotRegisteredError(
|
|
3711
|
+
hint?.code ?? "CAPABILITY_NOT_REGISTERED",
|
|
3712
|
+
hint?.message ?? `\u80FD\u529B "${prop}" \u672A\u6CE8\u518C\uFF1A\u8BF7\u5728\u51FD\u6570\u6240\u5728\u9879\u76EE\u5B89\u88C5\u5BF9\u5E94\u80FD\u529B`
|
|
3713
|
+
);
|
|
3714
|
+
}
|
|
3715
|
+
return store.get(prop);
|
|
3716
|
+
},
|
|
3717
|
+
has: () => true
|
|
3718
|
+
// 支持 'db' in ctx.cloud 形态
|
|
3719
|
+
}
|
|
3720
|
+
);
|
|
3721
|
+
}
|
|
3722
|
+
function createCloudContainer() {
|
|
3723
|
+
const registry = /* @__PURE__ */ new Map();
|
|
3724
|
+
const names = /* @__PURE__ */ new Set();
|
|
3725
|
+
let cloud = null;
|
|
3726
|
+
const rebuild = () => {
|
|
3727
|
+
cloud = createCloud(registry);
|
|
3728
|
+
};
|
|
3729
|
+
rebuild();
|
|
3730
|
+
return {
|
|
3731
|
+
register(name, impl) {
|
|
3732
|
+
registry.set(name, impl);
|
|
3733
|
+
names.add(name);
|
|
3734
|
+
rebuild();
|
|
3735
|
+
},
|
|
3736
|
+
unregister(name) {
|
|
3737
|
+
registry.delete(name);
|
|
3738
|
+
names.delete(name);
|
|
3739
|
+
rebuild();
|
|
3740
|
+
},
|
|
3741
|
+
get names() {
|
|
3742
|
+
return [...names];
|
|
3743
|
+
},
|
|
3744
|
+
get cloud() {
|
|
3745
|
+
return cloud;
|
|
3746
|
+
}
|
|
3747
|
+
};
|
|
3748
|
+
}
|
|
3749
|
+
var KNOWN_CAPABILITY_HINTS, CapabilityNotRegisteredError;
|
|
3750
|
+
var init_cloud_container = __esm({
|
|
3751
|
+
"packages/runtime/src/functions/runtime/cloud-container.ts"() {
|
|
3752
|
+
"use strict";
|
|
3753
|
+
KNOWN_CAPABILITY_HINTS = {
|
|
3754
|
+
db: {
|
|
3755
|
+
code: "DB_NOT_PROVISIONED",
|
|
3756
|
+
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"
|
|
3757
|
+
},
|
|
3758
|
+
storage: {
|
|
3759
|
+
code: "STORAGE_NOT_AVAILABLE",
|
|
3760
|
+
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"
|
|
3761
|
+
},
|
|
3762
|
+
fetch: {
|
|
3763
|
+
code: "FN_FETCH_DISABLED",
|
|
3764
|
+
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"
|
|
3765
|
+
},
|
|
3766
|
+
realtime: {
|
|
3767
|
+
code: "REALTIME_NOT_AVAILABLE",
|
|
3768
|
+
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"
|
|
3769
|
+
},
|
|
3770
|
+
kv: {
|
|
3771
|
+
code: "KV_NOT_PROVISIONED",
|
|
3772
|
+
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"
|
|
3773
|
+
}
|
|
3774
|
+
};
|
|
3775
|
+
CapabilityNotRegisteredError = class extends Error {
|
|
3776
|
+
code;
|
|
3777
|
+
constructor(code, message) {
|
|
3778
|
+
super(message);
|
|
3779
|
+
this.code = code;
|
|
3780
|
+
this.name = "CapabilityNotRegisteredError";
|
|
3781
|
+
}
|
|
3782
|
+
};
|
|
3783
|
+
}
|
|
3784
|
+
});
|
|
3785
|
+
|
|
3786
|
+
// packages/runtime/src/functions/runtime/sandbox-fetch.ts
|
|
3787
|
+
function isHostAllowed(host, rules) {
|
|
3788
|
+
const h = host.toLocaleLowerCase();
|
|
3789
|
+
return rules.some((raw) => {
|
|
3790
|
+
const rule = raw.trim().toLocaleLowerCase();
|
|
3791
|
+
if (rule.startsWith("*.")) {
|
|
3792
|
+
const base = rule.slice(2);
|
|
3793
|
+
return h === base || h.endsWith(`.${base}`);
|
|
3794
|
+
}
|
|
3795
|
+
return h === rule;
|
|
3796
|
+
});
|
|
3797
|
+
}
|
|
3798
|
+
function headersToRecord(headers) {
|
|
3799
|
+
const out = {};
|
|
3800
|
+
if (headers === void 0 || headers === null) return out;
|
|
3801
|
+
if (typeof headers.forEach === "function") {
|
|
3802
|
+
;
|
|
3803
|
+
headers.forEach((value, key) => {
|
|
3804
|
+
out[String(key).toLocaleLowerCase()] = String(value);
|
|
3805
|
+
});
|
|
3806
|
+
return out;
|
|
3807
|
+
}
|
|
3808
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
3809
|
+
out[key.toLocaleLowerCase()] = String(value);
|
|
3810
|
+
}
|
|
3811
|
+
return out;
|
|
3812
|
+
}
|
|
3813
|
+
function contentLength(res) {
|
|
3814
|
+
const headers = res.headers;
|
|
3815
|
+
if (headers === void 0 || headers === null) return null;
|
|
3816
|
+
if (typeof headers.get === "function") {
|
|
3817
|
+
const raw2 = headers.get?.("content-length");
|
|
3818
|
+
if (raw2 === void 0 || raw2 === null || raw2.trim() === "") return null;
|
|
3819
|
+
const n2 = Number(raw2);
|
|
3820
|
+
return Number.isFinite(n2) && n2 >= 0 ? n2 : null;
|
|
3821
|
+
}
|
|
3822
|
+
const record = headers;
|
|
3823
|
+
const raw = record["content-length"] ?? record["Content-Length"];
|
|
3824
|
+
if (raw === void 0 || raw === null) return null;
|
|
3825
|
+
const n = Number(raw);
|
|
3826
|
+
return Number.isFinite(n) && n >= 0 ? n : null;
|
|
3827
|
+
}
|
|
3828
|
+
function concatChunks(chunks, total) {
|
|
3829
|
+
const merged = new Uint8Array(total);
|
|
3830
|
+
let offset = 0;
|
|
3831
|
+
for (const chunk of chunks) {
|
|
3832
|
+
merged.set(chunk, offset);
|
|
3833
|
+
offset += chunk.byteLength;
|
|
3834
|
+
}
|
|
3835
|
+
return merged;
|
|
3836
|
+
}
|
|
3837
|
+
async function readBodyBytes(res, maxBytes) {
|
|
3838
|
+
if (maxBytes > 0) {
|
|
3839
|
+
const cl = contentLength(res);
|
|
3840
|
+
if (cl !== null && cl > maxBytes) throw TOO_LARGE;
|
|
3841
|
+
}
|
|
3842
|
+
const body = res.body;
|
|
3843
|
+
if (body !== void 0 && body !== null) {
|
|
3844
|
+
const reader = body.getReader();
|
|
3845
|
+
const chunks = [];
|
|
3846
|
+
let total = 0;
|
|
3847
|
+
for (; ; ) {
|
|
3848
|
+
const { done, value } = await reader.read();
|
|
3849
|
+
if (done) break;
|
|
3850
|
+
if (value !== void 0) {
|
|
3851
|
+
total += value.byteLength;
|
|
3852
|
+
if (maxBytes > 0 && total > maxBytes) throw TOO_LARGE;
|
|
3853
|
+
chunks.push(value);
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
return concatChunks(chunks, total);
|
|
3857
|
+
}
|
|
3858
|
+
if (typeof res.arrayBuffer === "function") {
|
|
3859
|
+
const buffer = await res.arrayBuffer();
|
|
3860
|
+
const bytes2 = new Uint8Array(buffer);
|
|
3861
|
+
if (maxBytes > 0 && bytes2.byteLength > maxBytes) throw TOO_LARGE;
|
|
3862
|
+
return bytes2;
|
|
3863
|
+
}
|
|
3864
|
+
const text = await res.text();
|
|
3865
|
+
const bytes = encoder.encode(text);
|
|
3866
|
+
if (maxBytes > 0 && bytes.byteLength > maxBytes) throw TOO_LARGE;
|
|
3867
|
+
return bytes;
|
|
3868
|
+
}
|
|
3869
|
+
function errorMessageOf(error) {
|
|
3870
|
+
if (error instanceof Error) return error.message;
|
|
3871
|
+
return String(error);
|
|
3872
|
+
}
|
|
3873
|
+
function normalizeResponse(res, maxBytes) {
|
|
3874
|
+
let cached = null;
|
|
3875
|
+
let cachedPromise = null;
|
|
3876
|
+
const readCached = () => {
|
|
3877
|
+
if (cached !== null) return Promise.resolve(cached);
|
|
3878
|
+
cachedPromise ??= readBodyBytes(res, maxBytes).then((bytes) => {
|
|
3879
|
+
cached = bytes;
|
|
3880
|
+
return bytes;
|
|
3881
|
+
});
|
|
3882
|
+
return cachedPromise;
|
|
3883
|
+
};
|
|
3884
|
+
const read = () => readCached();
|
|
3885
|
+
return {
|
|
3886
|
+
ok: res.ok,
|
|
3887
|
+
status: res.status,
|
|
3888
|
+
statusText: res.statusText,
|
|
3889
|
+
headers: headersToRecord(res.headers),
|
|
3890
|
+
text: async () => decoder.decode(await read()),
|
|
3891
|
+
arrayBuffer: async () => bufferViewToArrayBuffer(await read()),
|
|
3892
|
+
json: async () => JSON.parse(decoder.decode(await read()))
|
|
3893
|
+
};
|
|
3894
|
+
}
|
|
3895
|
+
function bufferViewToArrayBuffer(bytes) {
|
|
3896
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
3897
|
+
}
|
|
3898
|
+
function createSandboxFetch(options) {
|
|
3899
|
+
const fetchImpl = options.fetchImpl ?? defaultFetch;
|
|
3900
|
+
const { allowlist, timeoutMs } = options;
|
|
3901
|
+
const maxBytes = options.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
|
|
3902
|
+
return async (url, init) => {
|
|
3903
|
+
let parsed;
|
|
3904
|
+
try {
|
|
3905
|
+
parsed = new URL(url);
|
|
3906
|
+
} catch {
|
|
3907
|
+
throw new SandboxFetchError("FN_FETCH_INVALID_URL", `cloud.fetch \u6536\u5230\u975E\u6CD5 URL"${url}"`);
|
|
3908
|
+
}
|
|
3909
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw FORBIDDEN(url);
|
|
3910
|
+
if (!isHostAllowed(parsed.hostname, allowlist)) throw FORBIDDEN(url);
|
|
3911
|
+
const controller = new AbortController();
|
|
3912
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
3913
|
+
const signals = [controller.signal];
|
|
3914
|
+
if (init?.signal !== void 0) signals.push(init.signal);
|
|
3915
|
+
const signal = signals.length === 1 ? signals[0] : AbortSignal.any(signals);
|
|
3916
|
+
try {
|
|
3917
|
+
let res;
|
|
3918
|
+
try {
|
|
3919
|
+
res = await fetchImpl(url, { ...init, signal });
|
|
3920
|
+
} catch (error) {
|
|
3921
|
+
if (controller.signal.aborted) {
|
|
3922
|
+
throw new SandboxFetchError(
|
|
3923
|
+
"FN_FETCH_TIMEOUT",
|
|
3924
|
+
`cloud.fetch \u8BF7\u6C42\u8D85\u65F6\uFF08\u8D85\u8FC7 ${timeoutMs}ms\uFF09`
|
|
3925
|
+
);
|
|
3926
|
+
}
|
|
3927
|
+
if (init?.signal?.aborted === true) {
|
|
3928
|
+
throw new SandboxFetchError("FN_FETCH_ABORTED", "cloud.fetch \u8BF7\u6C42\u88AB\u8C03\u7528\u65B9\u4E2D\u6B62");
|
|
3929
|
+
}
|
|
3930
|
+
throw new SandboxFetchError(
|
|
3931
|
+
"FN_FETCH_FAILED",
|
|
3932
|
+
`cloud.fetch \u8BF7\u6C42\u5931\u8D25\uFF1A${errorMessageOf(error)}`
|
|
3933
|
+
);
|
|
3934
|
+
}
|
|
3935
|
+
return normalizeResponse(res, maxBytes);
|
|
3936
|
+
} finally {
|
|
3937
|
+
clearTimeout(timer);
|
|
3938
|
+
}
|
|
3939
|
+
};
|
|
3940
|
+
}
|
|
3941
|
+
var SandboxFetchError, DEFAULT_MAX_RESPONSE_BYTES, defaultFetch, FORBIDDEN, TOO_LARGE, encoder, decoder;
|
|
3942
|
+
var init_sandbox_fetch = __esm({
|
|
3943
|
+
"packages/runtime/src/functions/runtime/sandbox-fetch.ts"() {
|
|
3944
|
+
"use strict";
|
|
3945
|
+
SandboxFetchError = class extends Error {
|
|
3946
|
+
code;
|
|
3947
|
+
constructor(code, message) {
|
|
3948
|
+
super(message);
|
|
3949
|
+
this.name = "SandboxFetchError";
|
|
3950
|
+
this.code = code;
|
|
3951
|
+
}
|
|
3952
|
+
};
|
|
3953
|
+
DEFAULT_MAX_RESPONSE_BYTES = 100 * 1024 * 1024;
|
|
3954
|
+
defaultFetch = (url, init) => (
|
|
3955
|
+
// 真实 Response 与内部 ResponseLike 结构性一致;getReader 的多态签名差异属纯类型问题,运行语义等价。
|
|
3956
|
+
globalThis.fetch(url, init)
|
|
3957
|
+
);
|
|
3958
|
+
FORBIDDEN = (url) => new SandboxFetchError(
|
|
3959
|
+
"FN_FETCH_FORBIDDEN",
|
|
3960
|
+
`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`
|
|
3961
|
+
);
|
|
3962
|
+
TOO_LARGE = new SandboxFetchError(
|
|
3963
|
+
"FN_FETCH_TOO_LARGE",
|
|
3964
|
+
"cloud.fetch \u54CD\u5E94\u4F53\u8D85\u8FC7\u4E0A\u9650\uFF08FN_FETCH_TOO_LARGE\uFF09"
|
|
3965
|
+
);
|
|
3966
|
+
encoder = new TextEncoder();
|
|
3967
|
+
decoder = new TextDecoder();
|
|
3968
|
+
}
|
|
3969
|
+
});
|
|
3970
|
+
|
|
3971
|
+
// packages/runtime/src/functions/runtime/rpc-key.ts
|
|
3972
|
+
var init_rpc_key = __esm({
|
|
3973
|
+
"packages/runtime/src/functions/runtime/rpc-key.ts"() {
|
|
3974
|
+
"use strict";
|
|
3975
|
+
init_capability_keys();
|
|
3976
|
+
}
|
|
3977
|
+
});
|
|
3978
|
+
|
|
3979
|
+
// packages/runtime/src/functions/runtime/chain.ts
|
|
3980
|
+
function isChainCapability(value) {
|
|
3981
|
+
if (typeof value !== "object" || value === null) return false;
|
|
3982
|
+
const spec = value[CHAIN_CAPABILITY_KEY];
|
|
3983
|
+
return typeof spec === "object" && spec !== null && Array.isArray(spec.stepMethods) && Array.isArray(spec.terminalMethods);
|
|
3984
|
+
}
|
|
3985
|
+
function createChainClient(capability, port, pending, nextId, spec) {
|
|
3986
|
+
const call = (method, args) => new Promise((resolve13, reject) => {
|
|
3987
|
+
const id = nextId();
|
|
3988
|
+
pending.set(id, { resolve: resolve13, reject });
|
|
3989
|
+
port.postMessage({ type: "rpc", id, capability, method, args });
|
|
3990
|
+
});
|
|
3991
|
+
const makeChainable = (rootArgs, steps, txId) => new Proxy(
|
|
3992
|
+
{},
|
|
3993
|
+
{
|
|
3994
|
+
get(_target, prop) {
|
|
3995
|
+
if (typeof prop !== "string") return void 0;
|
|
3996
|
+
if (spec.stepMethods.includes(prop)) {
|
|
3997
|
+
return (...args) => makeChainable(rootArgs, [...steps, { method: prop, args }], txId);
|
|
3998
|
+
}
|
|
3999
|
+
if (spec.terminalMethods.includes(prop)) {
|
|
4000
|
+
return (...args) => call(DB_RPC.chain, [
|
|
4001
|
+
{
|
|
4002
|
+
rootArgs,
|
|
4003
|
+
steps,
|
|
4004
|
+
terminal: prop,
|
|
4005
|
+
terminalArgs: args,
|
|
4006
|
+
...txId === void 0 ? {} : { txId }
|
|
4007
|
+
}
|
|
4008
|
+
]);
|
|
4009
|
+
}
|
|
4010
|
+
return void 0;
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
);
|
|
4014
|
+
const makeRoot = (txId) => new Proxy(
|
|
4015
|
+
{},
|
|
4016
|
+
{
|
|
4017
|
+
get(_target, prop) {
|
|
4018
|
+
if (typeof prop !== "string") return void 0;
|
|
4019
|
+
if (prop === spec.rootMethod) {
|
|
4020
|
+
return (...rootArgs) => makeChainable(rootArgs, [], txId);
|
|
4021
|
+
}
|
|
4022
|
+
if (spec.directMethods.includes(prop)) {
|
|
4023
|
+
return (...args) => call(prop, args);
|
|
4024
|
+
}
|
|
4025
|
+
if (prop === spec.transactionMethod) {
|
|
4026
|
+
return (fn) => orchestrateTransaction(fn, txId);
|
|
4027
|
+
}
|
|
4028
|
+
return void 0;
|
|
4029
|
+
}
|
|
4030
|
+
}
|
|
4031
|
+
);
|
|
4032
|
+
const orchestrateTransaction = async (fn, _outerTxId) => {
|
|
4033
|
+
const txId = await call(DB_RPC.begin, []);
|
|
4034
|
+
const tx = makeRoot(txId);
|
|
4035
|
+
const settle = async (commit) => {
|
|
4036
|
+
await call(commit ? DB_RPC.commit : DB_RPC.rollback, [txId]).catch(() => void 0);
|
|
4037
|
+
};
|
|
4038
|
+
try {
|
|
4039
|
+
const result = await fn(tx);
|
|
4040
|
+
await settle(true);
|
|
4041
|
+
return result;
|
|
4042
|
+
} catch (error) {
|
|
4043
|
+
await settle(false);
|
|
4044
|
+
throw error;
|
|
4045
|
+
}
|
|
4046
|
+
};
|
|
4047
|
+
return makeRoot(void 0);
|
|
4048
|
+
}
|
|
4049
|
+
var init_chain = __esm({
|
|
4050
|
+
"packages/runtime/src/functions/runtime/chain.ts"() {
|
|
4051
|
+
"use strict";
|
|
4052
|
+
init_capability_keys();
|
|
4053
|
+
init_capability_keys();
|
|
4054
|
+
}
|
|
4055
|
+
});
|
|
4056
|
+
|
|
4057
|
+
// packages/runtime/src/functions/runtime/worker-entry.ts
|
|
4058
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
4059
|
+
import { createRequire as createRequire2 } from "node:module";
|
|
4060
|
+
import { join as join9 } from "node:path";
|
|
4061
|
+
function isRpcCapability(value) {
|
|
4062
|
+
return typeof value === "object" && value !== null && value[RPC_CAPABILITY_KEY] === true;
|
|
4063
|
+
}
|
|
4064
|
+
function createRpcClient(capability, port, pending, nextId) {
|
|
4065
|
+
return new Proxy(
|
|
4066
|
+
{},
|
|
4067
|
+
{
|
|
4068
|
+
get(_target, method) {
|
|
4069
|
+
if (typeof method !== "string") return void 0;
|
|
4070
|
+
return (...args) => {
|
|
4071
|
+
const id = nextId();
|
|
4072
|
+
return new Promise((resolve13, reject) => {
|
|
4073
|
+
pending.set(id, { resolve: resolve13, reject });
|
|
4074
|
+
port.postMessage({ type: "rpc", id, capability, method, args });
|
|
4075
|
+
});
|
|
4076
|
+
};
|
|
4077
|
+
}
|
|
4078
|
+
}
|
|
4079
|
+
);
|
|
4080
|
+
}
|
|
4081
|
+
function errorMessageOf2(error) {
|
|
4082
|
+
if (error instanceof Error) return error.message;
|
|
4083
|
+
if (typeof error === "object" && error !== null && "message" in error) {
|
|
4084
|
+
const message = error.message;
|
|
4085
|
+
if (typeof message === "string") return message;
|
|
4086
|
+
}
|
|
4087
|
+
return String(error);
|
|
4088
|
+
}
|
|
4089
|
+
function escapeRegExp2(text) {
|
|
4090
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4091
|
+
}
|
|
4092
|
+
function firstUserFrame(stack, files) {
|
|
4093
|
+
for (const key of Object.keys(files)) {
|
|
4094
|
+
const match = new RegExp(
|
|
4095
|
+
`(?<![\\w$/.])${escapeRegExp2(key)}:(\\d+)(?::(\\d+))?(?::(\\d+))?`
|
|
4096
|
+
).exec(stack);
|
|
4097
|
+
if (match !== null) {
|
|
4098
|
+
return match[2] === void 0 ? `${key}:${match[1]}` : `${key}:${match[1]}:${match[2]}`;
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
return void 0;
|
|
4102
|
+
}
|
|
4103
|
+
function errorMessageWithLocation(error, files) {
|
|
4104
|
+
const message = errorMessageOf2(error);
|
|
4105
|
+
const stack = error.stack;
|
|
4106
|
+
if (typeof stack !== "string") return message;
|
|
4107
|
+
const frame = firstUserFrame(stack, files);
|
|
4108
|
+
return frame === void 0 ? message : `${message}\uFF08${frame}\uFF09`;
|
|
4109
|
+
}
|
|
4110
|
+
function createBareResolver(deps) {
|
|
4111
|
+
const platformRequire = createRequire2(import.meta.url);
|
|
4112
|
+
const depsRequire = deps.dir === null ? null : createRequire2(join9(deps.dir, "__deps__.js"));
|
|
4113
|
+
return (request) => {
|
|
4114
|
+
if (deps.builtin.includes(request)) return platformRequire(request);
|
|
4115
|
+
const root = splitBareSpecifier(request);
|
|
4116
|
+
if (deps.builtin.includes(root)) return platformRequire(request);
|
|
4117
|
+
if (deps.custom.includes(root)) {
|
|
4118
|
+
if (depsRequire === null) {
|
|
4119
|
+
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`);
|
|
4120
|
+
}
|
|
4121
|
+
try {
|
|
4122
|
+
return depsRequire(request);
|
|
4123
|
+
} catch (error2) {
|
|
4124
|
+
const message = errorMessageOf2(error2);
|
|
4125
|
+
throw new Error(
|
|
4126
|
+
`\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}`,
|
|
4127
|
+
{ cause: error2 }
|
|
4128
|
+
);
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
4131
|
+
const error = new Error(
|
|
4132
|
+
`\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`
|
|
4133
|
+
);
|
|
4134
|
+
error.code = SANDBOX_BUILTIN_REJECTED;
|
|
4135
|
+
throw error;
|
|
4136
|
+
};
|
|
4137
|
+
}
|
|
4138
|
+
function runWorker(input, port) {
|
|
4139
|
+
const emit = (level, args) => {
|
|
4140
|
+
port.postMessage({ type: "log", level, message: args.map(String).join(" ") });
|
|
4141
|
+
};
|
|
4142
|
+
const forwardedConsole = {
|
|
4143
|
+
log: (...args) => emit("log", args),
|
|
4144
|
+
error: (...args) => emit("error", args),
|
|
4145
|
+
warn: (...args) => emit("warn", args)
|
|
4146
|
+
};
|
|
4147
|
+
const container = createCloudContainer();
|
|
4148
|
+
const pending = /* @__PURE__ */ new Map();
|
|
4149
|
+
let rpcId = 0;
|
|
4150
|
+
port.on?.((message) => {
|
|
4151
|
+
const reply = message;
|
|
4152
|
+
if (reply?.type !== "rpc-response" || typeof reply.id !== "number") return;
|
|
4153
|
+
const entry = pending.get(reply.id);
|
|
4154
|
+
if (entry === void 0) return;
|
|
4155
|
+
pending.delete(reply.id);
|
|
4156
|
+
if (reply.ok === true) entry.resolve(reply.result);
|
|
4157
|
+
else entry.reject(new Error(reply.error ?? "RPC \u80FD\u529B\u8C03\u7528\u5931\u8D25"));
|
|
4158
|
+
});
|
|
4159
|
+
for (const capability of input.capabilities ?? []) {
|
|
4160
|
+
container.register(
|
|
4161
|
+
capability.name,
|
|
4162
|
+
isChainCapability(capability.value) ? createChainClient(
|
|
4163
|
+
capability.name,
|
|
4164
|
+
port,
|
|
4165
|
+
pending,
|
|
4166
|
+
() => ++rpcId,
|
|
4167
|
+
capability.value[CHAIN_CAPABILITY_KEY]
|
|
4168
|
+
) : isRpcCapability(capability.value) ? createRpcClient(capability.name, port, pending, () => ++rpcId) : capability.value
|
|
4169
|
+
);
|
|
4170
|
+
}
|
|
4171
|
+
const sandboxGlobals = {
|
|
4172
|
+
console: forwardedConsole,
|
|
4173
|
+
// CF-015:Cloudflare Workers 代码重度依赖 Web 标准 API(URL / Request / Response /
|
|
4174
|
+
// Headers / 流 / 编码 / crypto 等)。Node 18+ 全局内置,直接注入 vm 上下文;
|
|
4175
|
+
// 与 @adep/cf-compat 的 Workers 适配(ctxToRequest / responseToEnvelopeBody)配套。
|
|
4176
|
+
URL,
|
|
4177
|
+
URLSearchParams,
|
|
4178
|
+
Request,
|
|
4179
|
+
Response,
|
|
4180
|
+
Headers,
|
|
4181
|
+
TextEncoder,
|
|
4182
|
+
TextDecoder,
|
|
4183
|
+
AbortController,
|
|
4184
|
+
AbortSignal,
|
|
4185
|
+
ReadableStream,
|
|
4186
|
+
WritableStream,
|
|
4187
|
+
TransformStream,
|
|
4188
|
+
Blob,
|
|
4189
|
+
FormData,
|
|
4190
|
+
crypto,
|
|
4191
|
+
performance,
|
|
4192
|
+
atob,
|
|
4193
|
+
btoa,
|
|
4194
|
+
structuredClone
|
|
4195
|
+
};
|
|
4196
|
+
if (input.env !== void 0) {
|
|
4197
|
+
sandboxGlobals["process"] = { env: input.env };
|
|
4198
|
+
}
|
|
4199
|
+
if (input.fetch !== void 0) {
|
|
4200
|
+
const sandboxFetch = createSandboxFetch({
|
|
4201
|
+
allowlist: input.fetch.allowlist,
|
|
4202
|
+
timeoutMs: input.fetch.timeoutMs,
|
|
4203
|
+
...input.fetch.maxResponseBytes === void 0 ? {} : { maxResponseBytes: input.fetch.maxResponseBytes }
|
|
4204
|
+
});
|
|
4205
|
+
sandboxGlobals["fetch"] = sandboxFetch;
|
|
4206
|
+
container.register("fetch", sandboxFetch);
|
|
4207
|
+
}
|
|
4208
|
+
const ctx = {
|
|
4209
|
+
method: input.request.method,
|
|
4210
|
+
path: input.request.path,
|
|
4211
|
+
query: input.request.query,
|
|
4212
|
+
headers: input.request.headers,
|
|
4213
|
+
...input.request.body === void 0 ? {} : { body: input.request.body },
|
|
4214
|
+
files: input.request.files ?? [],
|
|
4215
|
+
cloud: container.cloud,
|
|
4216
|
+
user: input.request.user ?? null,
|
|
4217
|
+
...input.env === void 0 ? {} : { env: input.env }
|
|
4218
|
+
};
|
|
4219
|
+
try {
|
|
4220
|
+
const { handler } = loadFunctionModule(
|
|
4221
|
+
input.files,
|
|
4222
|
+
input.entry,
|
|
4223
|
+
sandboxGlobals,
|
|
4224
|
+
input.deps === void 0 ? void 0 : createBareResolver(input.deps)
|
|
4225
|
+
);
|
|
4226
|
+
const result = Promise.resolve(handler(ctx));
|
|
4227
|
+
void result.then(
|
|
4228
|
+
(value) => {
|
|
4229
|
+
port.postMessage({ type: "result", body: value === void 0 ? null : value });
|
|
4230
|
+
return void 0;
|
|
4231
|
+
},
|
|
4232
|
+
(error) => {
|
|
4233
|
+
port.postMessage({
|
|
4234
|
+
type: "error",
|
|
4235
|
+
message: errorMessageWithLocation(error, input.files),
|
|
4236
|
+
code: error.code,
|
|
4237
|
+
// FN-011:函数抛错可携带 status(4xx/5xx),透传给执行器落 ExecutorError.status。
|
|
4238
|
+
status: error.status
|
|
4239
|
+
});
|
|
4240
|
+
return void 0;
|
|
4241
|
+
}
|
|
4242
|
+
);
|
|
4243
|
+
} catch (error) {
|
|
4244
|
+
port.postMessage({
|
|
4245
|
+
type: "error",
|
|
4246
|
+
message: errorMessageWithLocation(error, input.files),
|
|
4247
|
+
code: error.code,
|
|
4248
|
+
status: error.status
|
|
4249
|
+
});
|
|
4250
|
+
}
|
|
4251
|
+
}
|
|
4252
|
+
var workerPort;
|
|
4253
|
+
var init_worker_entry = __esm({
|
|
4254
|
+
"packages/runtime/src/functions/runtime/worker-entry.ts"() {
|
|
4255
|
+
"use strict";
|
|
4256
|
+
init_cloud_container();
|
|
4257
|
+
init_sandbox();
|
|
4258
|
+
init_sandbox_fetch();
|
|
4259
|
+
init_rpc_key();
|
|
4260
|
+
init_chain();
|
|
4261
|
+
init_sandbox();
|
|
4262
|
+
init_manifest();
|
|
4263
|
+
workerPort = parentPort;
|
|
4264
|
+
if (workerPort !== null && workerData !== void 0) {
|
|
4265
|
+
const port = {
|
|
4266
|
+
// eslint-disable-next-line unicorn/require-post-message-target-origin -- node worker_threads 无 targetOrigin 语义
|
|
4267
|
+
postMessage: (message) => workerPort.postMessage(message),
|
|
4268
|
+
on: (listener) => {
|
|
4269
|
+
workerPort.on("message", (value) => listener(value));
|
|
4270
|
+
}
|
|
4271
|
+
};
|
|
4272
|
+
runWorker(workerData, port);
|
|
4273
|
+
}
|
|
2924
4274
|
}
|
|
2925
4275
|
});
|
|
2926
4276
|
|
|
@@ -2934,15 +4284,16 @@ __export(dev_exports, {
|
|
|
2934
4284
|
});
|
|
2935
4285
|
import { createServer } from "node:http";
|
|
2936
4286
|
import { watch } from "node:fs";
|
|
2937
|
-
import { mkdir as
|
|
2938
|
-
import { basename, join as
|
|
4287
|
+
import { mkdir as mkdir6, readdir as readdir3, readFile as readFile7, writeFile as writeFile5 } from "node:fs/promises";
|
|
4288
|
+
import { basename as basename2, join as join10, resolve as resolve4 } from "node:path";
|
|
2939
4289
|
async function loadConfig(cwd) {
|
|
2940
|
-
const name =
|
|
4290
|
+
const name = basename2(resolve4(cwd));
|
|
2941
4291
|
const config = await loadAdepConfigModule(cwd);
|
|
2942
4292
|
return {
|
|
2943
4293
|
name: config?.name ?? name,
|
|
2944
4294
|
functionsDir: config?.functionsDir ?? "functions",
|
|
2945
|
-
functionsPrefix: config?.functions_prefix ?? ""
|
|
4295
|
+
functionsPrefix: config?.functions_prefix ?? "",
|
|
4296
|
+
...config?.environment === void 0 ? {} : { environment: config.environment }
|
|
2946
4297
|
};
|
|
2947
4298
|
}
|
|
2948
4299
|
function parseEnvFile(content) {
|
|
@@ -2963,17 +4314,17 @@ async function collectFunctions(dir) {
|
|
|
2963
4314
|
const walk = async (sub, prefix) => {
|
|
2964
4315
|
let entries;
|
|
2965
4316
|
try {
|
|
2966
|
-
entries = await
|
|
4317
|
+
entries = await readdir3(sub, { withFileTypes: true });
|
|
2967
4318
|
} catch {
|
|
2968
4319
|
return;
|
|
2969
4320
|
}
|
|
2970
4321
|
for (const entry of entries) {
|
|
2971
4322
|
const rel = prefix.length === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
2972
|
-
const full =
|
|
4323
|
+
const full = join10(sub, entry.name);
|
|
2973
4324
|
if (entry.isDirectory()) {
|
|
2974
4325
|
await walk(full, rel);
|
|
2975
4326
|
} else if (entry.isFile() && entry.name.endsWith(".ts")) {
|
|
2976
|
-
files[rel] = await
|
|
4327
|
+
files[rel] = await readFile7(full, "utf8");
|
|
2977
4328
|
}
|
|
2978
4329
|
}
|
|
2979
4330
|
};
|
|
@@ -3036,25 +4387,27 @@ function printBoundaries(log) {
|
|
|
3036
4387
|
}
|
|
3037
4388
|
}
|
|
3038
4389
|
async function ensureGitignore(cwd) {
|
|
3039
|
-
const gitignorePath =
|
|
3040
|
-
const existing = await
|
|
4390
|
+
const gitignorePath = join10(cwd, ".gitignore");
|
|
4391
|
+
const existing = await readFile7(gitignorePath, "utf8").catch(() => "");
|
|
3041
4392
|
if (existing.split(/\r?\n/).includes(".adep/")) return;
|
|
3042
|
-
await
|
|
4393
|
+
await writeFile5(gitignorePath, `${existing.replace(/\n+$/, "")}
|
|
3043
4394
|
.adep/
|
|
3044
4395
|
`, "utf8");
|
|
3045
4396
|
}
|
|
3046
4397
|
async function startDevServer(options) {
|
|
3047
|
-
const cwd =
|
|
4398
|
+
const cwd = resolve4(options.cwd);
|
|
3048
4399
|
const log = options.log ?? ((line) => process.stdout.write(`${line}
|
|
3049
4400
|
`));
|
|
3050
4401
|
const config = await loadConfig(cwd);
|
|
3051
4402
|
if (options.prefix !== void 0) {
|
|
3052
4403
|
config.functionsPrefix = options.prefix.replace(/^\/+|\/+$/g, "");
|
|
3053
4404
|
}
|
|
3054
|
-
const functionsDir =
|
|
4405
|
+
const functionsDir = join10(cwd, config.functionsDir);
|
|
3055
4406
|
const coldStartAt = Date.now();
|
|
3056
|
-
const executor = new WorkerFunctionExecutor(
|
|
3057
|
-
|
|
4407
|
+
const executor = new WorkerFunctionExecutor({
|
|
4408
|
+
deps: { rootDir: cwd, builtin: LOCAL_BUILTIN_DEPS }
|
|
4409
|
+
});
|
|
4410
|
+
await mkdir6(functionsDir, { recursive: true });
|
|
3058
4411
|
const runtime = await createSimRuntime({
|
|
3059
4412
|
cwd,
|
|
3060
4413
|
projectId: "local",
|
|
@@ -3063,19 +4416,91 @@ async function startDevServer(options) {
|
|
|
3063
4416
|
});
|
|
3064
4417
|
await ensureGitignore(cwd);
|
|
3065
4418
|
let files = await collectFunctions(functionsDir);
|
|
3066
|
-
let env =
|
|
4419
|
+
let env = {
|
|
4420
|
+
...config.environment,
|
|
4421
|
+
...await loadSimEnv(cwd).catch(() => ({}))
|
|
4422
|
+
};
|
|
3067
4423
|
const dbBundle = runtime.bundle;
|
|
3068
4424
|
const invokeHandler = createSimInvokeHandler({ executor, files });
|
|
4425
|
+
let stateRuntime = null;
|
|
4426
|
+
let stateFingerprint = "";
|
|
4427
|
+
const bareResolver = createBareResolver({
|
|
4428
|
+
dir: null,
|
|
4429
|
+
builtin: [...LOCAL_BUILTIN_DEPS],
|
|
4430
|
+
custom: []
|
|
4431
|
+
});
|
|
4432
|
+
const collectStateDeclarations = () => {
|
|
4433
|
+
const declared = [];
|
|
4434
|
+
const dirs = /* @__PURE__ */ new Set();
|
|
4435
|
+
for (const path of Object.keys(files)) {
|
|
4436
|
+
if (path === STATE_OBJECTS_ENTRY || path.endsWith(`/${STATE_OBJECTS_ENTRY}`)) {
|
|
4437
|
+
dirs.add(path === STATE_OBJECTS_ENTRY ? "" : path.slice(0, -STATE_OBJECTS_ENTRY.length - 1));
|
|
4438
|
+
}
|
|
4439
|
+
}
|
|
4440
|
+
for (const dir of dirs) {
|
|
4441
|
+
const prefix = dir.length === 0 ? "" : `${dir}/`;
|
|
4442
|
+
const fileMap = {};
|
|
4443
|
+
for (const [path, content] of Object.entries(files)) {
|
|
4444
|
+
if (path.startsWith(prefix)) fileMap[path] = content;
|
|
4445
|
+
}
|
|
4446
|
+
declared.push({ dir, files: fileMap });
|
|
4447
|
+
}
|
|
4448
|
+
return declared;
|
|
4449
|
+
};
|
|
4450
|
+
const stateObjectBareResolver = (request) => {
|
|
4451
|
+
const state = resolveStateObjectSpecifier(request);
|
|
4452
|
+
if (state !== void 0) return state;
|
|
4453
|
+
return bareResolver(request);
|
|
4454
|
+
};
|
|
4455
|
+
const refreshStateRuntime = () => {
|
|
4456
|
+
const declarations = collectStateDeclarations();
|
|
4457
|
+
const fingerprint = declarations.map(
|
|
4458
|
+
(d) => `${d.dir}\0${d.files[`${d.dir === "" ? "" : `${d.dir}/`}${STATE_OBJECTS_ENTRY}`] ?? ""}`
|
|
4459
|
+
).join("");
|
|
4460
|
+
if (stateRuntime !== null && stateFingerprint === fingerprint) return stateRuntime;
|
|
4461
|
+
stateRuntime = new StateObjectRuntime(createProjectDriverStateBackend(runtime.db.driver));
|
|
4462
|
+
stateRuntime.define(DemoCounter);
|
|
4463
|
+
stateRuntime.define(DemoChatRoom);
|
|
4464
|
+
for (const d of declarations) {
|
|
4465
|
+
const loaded = loadStateObjectClasses(
|
|
4466
|
+
d.files,
|
|
4467
|
+
`${d.dir === "" ? "" : `${d.dir}/`}${STATE_OBJECTS_ENTRY}`,
|
|
4468
|
+
{
|
|
4469
|
+
resolveBare: stateObjectBareResolver
|
|
4470
|
+
}
|
|
4471
|
+
);
|
|
4472
|
+
for (const cls of loaded) stateRuntime.define(cls);
|
|
4473
|
+
}
|
|
4474
|
+
stateFingerprint = fingerprint;
|
|
4475
|
+
return stateRuntime;
|
|
4476
|
+
};
|
|
4477
|
+
let stateAlarmTimer;
|
|
4478
|
+
const startStateAlarmScheduler = () => {
|
|
4479
|
+
if (stateAlarmTimer !== void 0) return;
|
|
4480
|
+
stateAlarmTimer = setInterval(() => {
|
|
4481
|
+
void refreshStateRuntime().pump(Date.now()).catch((error) => {
|
|
4482
|
+
log(
|
|
4483
|
+
`[adep] \u72B6\u6001\u5BF9\u8C61 alarm tick \u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`
|
|
4484
|
+
);
|
|
4485
|
+
});
|
|
4486
|
+
}, options.stateAlarmTickMs ?? 1e3);
|
|
4487
|
+
stateAlarmTimer.unref?.();
|
|
4488
|
+
};
|
|
4489
|
+
const stopStateAlarmScheduler = () => {
|
|
4490
|
+
if (stateAlarmTimer === void 0) return;
|
|
4491
|
+
clearInterval(stateAlarmTimer);
|
|
4492
|
+
stateAlarmTimer = void 0;
|
|
4493
|
+
};
|
|
3069
4494
|
const reload = async () => {
|
|
3070
4495
|
const [nextFiles, envText2] = await Promise.all([
|
|
3071
4496
|
collectFunctions(functionsDir),
|
|
3072
|
-
|
|
4497
|
+
readFile7(join10(cwd, ".env.local"), "utf8").catch(() => "")
|
|
3073
4498
|
]);
|
|
3074
4499
|
files = nextFiles;
|
|
3075
|
-
env = parseEnvFile(envText2);
|
|
4500
|
+
env = { ...config.environment, ...parseEnvFile(envText2) };
|
|
3076
4501
|
};
|
|
3077
|
-
const envText = await
|
|
3078
|
-
env = parseEnvFile(envText);
|
|
4502
|
+
const envText = await readFile7(join10(cwd, ".env.local"), "utf8").catch(() => "");
|
|
4503
|
+
env = { ...config.environment, ...parseEnvFile(envText) };
|
|
3079
4504
|
let debounceTimer;
|
|
3080
4505
|
let watcher;
|
|
3081
4506
|
try {
|
|
@@ -3135,6 +4560,43 @@ async function startDevServer(options) {
|
|
|
3135
4560
|
});
|
|
3136
4561
|
return;
|
|
3137
4562
|
}
|
|
4563
|
+
if (fnName === "state") {
|
|
4564
|
+
const normalizedPrefix = config.functionsPrefix.replace(/^\/+|\/+$/g, "");
|
|
4565
|
+
const rest = normalizedPrefix.length === 0 ? url.pathname : url.pathname.replace(new RegExp(`^/${normalizedPrefix}`), "");
|
|
4566
|
+
const parsed = parseStatePath(rest);
|
|
4567
|
+
if (parsed === null) {
|
|
4568
|
+
writeJson(res, 400, {
|
|
4569
|
+
error: { code: "STATE_BAD_PATH", message: "\u72B6\u6001\u5BF9\u8C61\u8DEF\u5F84\uFF1A/api/state/:type/:id/:method" }
|
|
4570
|
+
});
|
|
4571
|
+
return;
|
|
4572
|
+
}
|
|
4573
|
+
const rawBody = await bodyOf(req);
|
|
4574
|
+
const parsedBody = parseStateBody(
|
|
4575
|
+
req.method === "GET" || req.method === "HEAD" ? null : rawBody === void 0 ? null : typeof rawBody === "string" ? rawBody : JSON.stringify(rawBody)
|
|
4576
|
+
);
|
|
4577
|
+
if ("error" in parsedBody) {
|
|
4578
|
+
writeJson(res, 400, { error: { code: "STATE_BAD_ARGS", message: parsedBody.error } });
|
|
4579
|
+
return;
|
|
4580
|
+
}
|
|
4581
|
+
try {
|
|
4582
|
+
const result = await refreshStateRuntime().invoke(
|
|
4583
|
+
parsed.type,
|
|
4584
|
+
parsed.id,
|
|
4585
|
+
parsed.method,
|
|
4586
|
+
parsedBody.args
|
|
4587
|
+
);
|
|
4588
|
+
writeJson(res, 200, { ok: true, data: result });
|
|
4589
|
+
} catch (error) {
|
|
4590
|
+
if (error instanceof StateObjectError) {
|
|
4591
|
+
writeJson(res, error.code === "STATE_TYPE_NOT_REGISTERED" ? 404 : 400, {
|
|
4592
|
+
error: { code: error.code, message: error.message }
|
|
4593
|
+
});
|
|
4594
|
+
return;
|
|
4595
|
+
}
|
|
4596
|
+
writeJson(res, 500, { error: { code: "STATE_INVOKE_ERROR", message: String(error) } });
|
|
4597
|
+
}
|
|
4598
|
+
return;
|
|
4599
|
+
}
|
|
3138
4600
|
const entry = resolveFunctionEntry(files, fnName);
|
|
3139
4601
|
if (entry === void 0) {
|
|
3140
4602
|
log(`[adep] ${req.method ?? "GET"} /${fnName} -> 404\uFF08\u51FD\u6570\u4E0D\u5B58\u5728\uFF09`);
|
|
@@ -3146,8 +4608,15 @@ async function startDevServer(options) {
|
|
|
3146
4608
|
try {
|
|
3147
4609
|
const result = await executor.execute(input);
|
|
3148
4610
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
3149
|
-
log(`[adep] ${req.method ?? "GET"} /${fnName} -> 200 ${durationMs}ms`);
|
|
3150
4611
|
for (const line of result.logs) log(`[adep] ${line}`);
|
|
4612
|
+
if (isAdepHttpEnvelope(result.body)) {
|
|
4613
|
+
log(
|
|
4614
|
+
`[adep] ${req.method ?? "GET"} /${fnName} -> ${result.body.__adepHttp.status} ${durationMs}ms`
|
|
4615
|
+
);
|
|
4616
|
+
writeEnvelopeResponse(res, result.body);
|
|
4617
|
+
return;
|
|
4618
|
+
}
|
|
4619
|
+
log(`[adep] ${req.method ?? "GET"} /${fnName} -> 200 ${durationMs}ms`);
|
|
3151
4620
|
writeJson(res, 200, result.body === void 0 ? null : result.body);
|
|
3152
4621
|
} catch (error) {
|
|
3153
4622
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
@@ -3205,10 +4674,12 @@ async function startDevServer(options) {
|
|
|
3205
4674
|
log(`[adep] curl \u793A\u4F8B\uFF1Acurl ${baseUrl}${curlPath}`);
|
|
3206
4675
|
printBoundaries(log);
|
|
3207
4676
|
log(`[adep] \u6A21\u62DF\u6570\u636E\u76EE\u5F55\uFF1A${simDir(cwd)}`);
|
|
4677
|
+
startStateAlarmScheduler();
|
|
3208
4678
|
return {
|
|
3209
4679
|
port: actualPort,
|
|
3210
4680
|
baseUrl,
|
|
3211
4681
|
close: async () => {
|
|
4682
|
+
stopStateAlarmScheduler();
|
|
3212
4683
|
if (debounceTimer !== void 0) clearTimeout(debounceTimer);
|
|
3213
4684
|
watcher?.close();
|
|
3214
4685
|
await new Promise((resolveClose) => server.close(() => resolveClose()));
|
|
@@ -3221,12 +4692,16 @@ var init_dev = __esm({
|
|
|
3221
4692
|
"packages/cli/src/dev.ts"() {
|
|
3222
4693
|
"use strict";
|
|
3223
4694
|
init_executor();
|
|
4695
|
+
init_http_envelope();
|
|
3224
4696
|
init_worker_executor();
|
|
3225
4697
|
init_runtime();
|
|
3226
4698
|
init_env();
|
|
3227
4699
|
init_invoke();
|
|
4700
|
+
init_builtin_deps();
|
|
3228
4701
|
init_boundary();
|
|
3229
4702
|
init_ts_config();
|
|
4703
|
+
init_state_objects();
|
|
4704
|
+
init_worker_entry();
|
|
3230
4705
|
}
|
|
3231
4706
|
});
|
|
3232
4707
|
|
|
@@ -3302,8 +4777,8 @@ __export(server_exports, {
|
|
|
3302
4777
|
});
|
|
3303
4778
|
import { createServer as createServer2 } from "node:http";
|
|
3304
4779
|
import { watch as watch2 } from "node:fs";
|
|
3305
|
-
import { mkdir as
|
|
3306
|
-
import { join as
|
|
4780
|
+
import { mkdir as mkdir7, readdir as readdir4, readFile as readFile8, stat as stat6 } from "node:fs/promises";
|
|
4781
|
+
import { join as join11, relative as relative2, resolve as resolve5 } from "node:path";
|
|
3307
4782
|
function envNumber(name) {
|
|
3308
4783
|
const raw = process.env[name];
|
|
3309
4784
|
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
@@ -3319,17 +4794,17 @@ async function collectFunctions2(dir) {
|
|
|
3319
4794
|
const walk = async (sub, prefix) => {
|
|
3320
4795
|
let entries;
|
|
3321
4796
|
try {
|
|
3322
|
-
entries = await
|
|
4797
|
+
entries = await readdir4(sub, { withFileTypes: true });
|
|
3323
4798
|
} catch {
|
|
3324
4799
|
return;
|
|
3325
4800
|
}
|
|
3326
4801
|
for (const entry of entries) {
|
|
3327
4802
|
const rel = prefix.length === 0 ? entry.name : `${prefix}/${entry.name}`;
|
|
3328
|
-
const full =
|
|
4803
|
+
const full = join11(sub, entry.name);
|
|
3329
4804
|
if (entry.isDirectory()) {
|
|
3330
4805
|
await walk(full, rel);
|
|
3331
4806
|
} else if (entry.isFile() && entry.name.endsWith(".ts")) {
|
|
3332
|
-
files[rel] = await
|
|
4807
|
+
files[rel] = await readFile8(full, "utf8");
|
|
3333
4808
|
}
|
|
3334
4809
|
}
|
|
3335
4810
|
};
|
|
@@ -3364,27 +4839,27 @@ function contentTypeFor(path) {
|
|
|
3364
4839
|
return MIME_TYPES[ext] ?? "application/octet-stream";
|
|
3365
4840
|
}
|
|
3366
4841
|
async function startServeServer(options) {
|
|
3367
|
-
const cwd =
|
|
4842
|
+
const cwd = resolve5(options.cwd);
|
|
3368
4843
|
const log = options.log ?? ((line) => process.stdout.write(`${line}
|
|
3369
4844
|
`));
|
|
3370
4845
|
const host = options.host ?? envString("HOST") ?? "127.0.0.1";
|
|
3371
4846
|
const port = options.port ?? envNumber("PORT") ?? 8787;
|
|
3372
|
-
const staticDir =
|
|
4847
|
+
const staticDir = resolve5(
|
|
3373
4848
|
cwd,
|
|
3374
4849
|
options.staticDir ?? envString("ADEP_SERVE_STATIC_DIR") ?? "public"
|
|
3375
4850
|
);
|
|
3376
4851
|
const config = await loadConfig(cwd);
|
|
3377
|
-
const functionsDir =
|
|
4852
|
+
const functionsDir = join11(cwd, config.functionsDir);
|
|
3378
4853
|
let hasStatic = false;
|
|
3379
4854
|
try {
|
|
3380
|
-
const publicStat = await
|
|
4855
|
+
const publicStat = await stat6(staticDir);
|
|
3381
4856
|
hasStatic = publicStat.isDirectory();
|
|
3382
4857
|
} catch {
|
|
3383
4858
|
hasStatic = false;
|
|
3384
4859
|
}
|
|
3385
4860
|
let files = await collectFunctions2(functionsDir);
|
|
3386
4861
|
let functionNames = listFunctionNames(files);
|
|
3387
|
-
const envText = await
|
|
4862
|
+
const envText = await readFile8(join11(cwd, ".env.local"), "utf8").catch(() => "");
|
|
3388
4863
|
let env = parseEnvFile(envText);
|
|
3389
4864
|
let simEnv = await loadSimEnv(cwd).catch(() => ({}));
|
|
3390
4865
|
const runtime = await createSimRuntime({
|
|
@@ -3393,7 +4868,9 @@ async function startServeServer(options) {
|
|
|
3393
4868
|
baseUrl: `http://${host}:${port}`,
|
|
3394
4869
|
log
|
|
3395
4870
|
});
|
|
3396
|
-
const executor = new WorkerFunctionExecutor(
|
|
4871
|
+
const executor = new WorkerFunctionExecutor({
|
|
4872
|
+
deps: { rootDir: cwd, builtin: LOCAL_BUILTIN_DEPS }
|
|
4873
|
+
});
|
|
3397
4874
|
const dbBundle = runtime.bundle;
|
|
3398
4875
|
let invokeHandler = createSimInvokeHandler({ executor, files });
|
|
3399
4876
|
let stopWatch;
|
|
@@ -3401,7 +4878,7 @@ async function startServeServer(options) {
|
|
|
3401
4878
|
const reload = async () => {
|
|
3402
4879
|
const [nextFiles, envLocalText] = await Promise.all([
|
|
3403
4880
|
collectFunctions2(functionsDir),
|
|
3404
|
-
|
|
4881
|
+
readFile8(join11(cwd, ".env.local"), "utf8").catch(() => "")
|
|
3405
4882
|
]);
|
|
3406
4883
|
files = nextFiles;
|
|
3407
4884
|
env = parseEnvFile(envLocalText);
|
|
@@ -3418,7 +4895,7 @@ async function startServeServer(options) {
|
|
|
3418
4895
|
void reload();
|
|
3419
4896
|
}, 120);
|
|
3420
4897
|
};
|
|
3421
|
-
await
|
|
4898
|
+
await mkdir7(functionsDir, { recursive: true }).catch(() => void 0);
|
|
3422
4899
|
try {
|
|
3423
4900
|
watchers.push(watch2(functionsDir, { recursive: true }, scheduleReload));
|
|
3424
4901
|
} catch {
|
|
@@ -3444,8 +4921,8 @@ async function startServeServer(options) {
|
|
|
3444
4921
|
if (typeof value === "string") headers[key] = value;
|
|
3445
4922
|
}
|
|
3446
4923
|
const body = req.method === "GET" || req.method === "HEAD" ? void 0 : await bodyOf2(req);
|
|
3447
|
-
const hasEnv = Object.keys(env).length > 0 || Object.keys(simEnv).length > 0;
|
|
3448
|
-
const mergedEnv = { ...simEnv, ...env };
|
|
4924
|
+
const hasEnv = Object.keys(env).length > 0 || Object.keys(simEnv).length > 0 || Object.keys(config.environment ?? {}).length > 0;
|
|
4925
|
+
const mergedEnv = { ...config.environment, ...simEnv, ...env };
|
|
3449
4926
|
return {
|
|
3450
4927
|
project: { id: "local", slug: config.name },
|
|
3451
4928
|
fn: { id: fnName, name: fnName },
|
|
@@ -3470,16 +4947,16 @@ async function startServeServer(options) {
|
|
|
3470
4947
|
const serveStatic = async (pathname, res) => {
|
|
3471
4948
|
if (!hasStatic) return false;
|
|
3472
4949
|
const safePath = pathname.replace(/\.\.\//g, "").replace(/^\//, "");
|
|
3473
|
-
const filePath =
|
|
3474
|
-
const resolvedPath = safePath === "" || safePath === "/" ?
|
|
4950
|
+
const filePath = join11(staticDir, safePath);
|
|
4951
|
+
const resolvedPath = safePath === "" || safePath === "/" ? join11(filePath, "index.html") : filePath;
|
|
3475
4952
|
try {
|
|
3476
|
-
const fileStat = await
|
|
4953
|
+
const fileStat = await stat6(resolvedPath);
|
|
3477
4954
|
if (fileStat.isDirectory()) {
|
|
3478
|
-
const indexPath =
|
|
4955
|
+
const indexPath = join11(resolvedPath, "index.html");
|
|
3479
4956
|
try {
|
|
3480
|
-
const indexStat = await
|
|
4957
|
+
const indexStat = await stat6(indexPath);
|
|
3481
4958
|
if (indexStat.isFile()) {
|
|
3482
|
-
const content2 = await
|
|
4959
|
+
const content2 = await readFile8(indexPath);
|
|
3483
4960
|
res.writeHead(200, { "content-type": contentTypeFor(indexPath) });
|
|
3484
4961
|
res.end(content2);
|
|
3485
4962
|
return true;
|
|
@@ -3488,7 +4965,7 @@ async function startServeServer(options) {
|
|
|
3488
4965
|
}
|
|
3489
4966
|
return false;
|
|
3490
4967
|
}
|
|
3491
|
-
const content = await
|
|
4968
|
+
const content = await readFile8(resolvedPath);
|
|
3492
4969
|
res.writeHead(200, { "content-type": contentTypeFor(resolvedPath) });
|
|
3493
4970
|
res.end(content);
|
|
3494
4971
|
return true;
|
|
@@ -3523,10 +5000,17 @@ async function startServeServer(options) {
|
|
|
3523
5000
|
try {
|
|
3524
5001
|
const result = await executor.execute(input);
|
|
3525
5002
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
3526
|
-
log(`[serve] ${req.method ?? "GET"} ${pathname} -> 200 ${durationMs}ms`);
|
|
3527
5003
|
if (options.watch === true) {
|
|
3528
5004
|
for (const line of result.logs) log(`[serve] ${line}`);
|
|
3529
5005
|
}
|
|
5006
|
+
if (isAdepHttpEnvelope(result.body)) {
|
|
5007
|
+
log(
|
|
5008
|
+
`[serve] ${req.method ?? "GET"} ${pathname} -> ${result.body.__adepHttp.status} ${durationMs}ms`
|
|
5009
|
+
);
|
|
5010
|
+
writeEnvelopeResponse(res, result.body);
|
|
5011
|
+
return;
|
|
5012
|
+
}
|
|
5013
|
+
log(`[serve] ${req.method ?? "GET"} ${pathname} -> 200 ${durationMs}ms`);
|
|
3530
5014
|
writeJson2(res, 200, result.body === void 0 ? null : result.body);
|
|
3531
5015
|
} catch (error) {
|
|
3532
5016
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
@@ -3602,7 +5086,7 @@ async function startServeServer(options) {
|
|
|
3602
5086
|
functions: functionNames,
|
|
3603
5087
|
functionsPrefix: config.functionsPrefix,
|
|
3604
5088
|
hasStatic,
|
|
3605
|
-
staticDir:
|
|
5089
|
+
staticDir: relative2(cwd, staticDir).length > 0 ? relative2(cwd, staticDir) : staticDir
|
|
3606
5090
|
});
|
|
3607
5091
|
log(banner);
|
|
3608
5092
|
if (options.watch === true) {
|
|
@@ -3625,7 +5109,9 @@ var init_server = __esm({
|
|
|
3625
5109
|
"packages/cli/src/serve/server.ts"() {
|
|
3626
5110
|
"use strict";
|
|
3627
5111
|
init_executor();
|
|
5112
|
+
init_http_envelope();
|
|
3628
5113
|
init_worker_executor();
|
|
5114
|
+
init_builtin_deps();
|
|
3629
5115
|
init_runtime();
|
|
3630
5116
|
init_env();
|
|
3631
5117
|
init_invoke();
|
|
@@ -3763,8 +5249,8 @@ __export(deploy_exports, {
|
|
|
3763
5249
|
deploy: () => deploy
|
|
3764
5250
|
});
|
|
3765
5251
|
import { createHash as createHash2 } from "node:crypto";
|
|
3766
|
-
import { readdir as
|
|
3767
|
-
import { join as
|
|
5252
|
+
import { readdir as readdir5, readFile as readFile9 } from "node:fs/promises";
|
|
5253
|
+
import { join as join12, resolve as resolve6, basename as basename3 } from "node:path";
|
|
3768
5254
|
function functionUrlBase(prefix) {
|
|
3769
5255
|
const raw = (prefix ?? "/api").trim();
|
|
3770
5256
|
const normalized = raw === "/" ? "/" : raw.replace(/\/+$/, "");
|
|
@@ -3774,13 +5260,13 @@ async function collectEntries(functionsDir) {
|
|
|
3774
5260
|
const entries = {};
|
|
3775
5261
|
let names;
|
|
3776
5262
|
try {
|
|
3777
|
-
const dirents = await
|
|
3778
|
-
names = dirents.filter((entry) => entry.isFile() && entry.name.endsWith(".ts")).map((entry) =>
|
|
5263
|
+
const dirents = await readdir5(functionsDir, { withFileTypes: true });
|
|
5264
|
+
names = dirents.filter((entry) => entry.isFile() && entry.name.endsWith(".ts")).map((entry) => basename3(entry.name, ".ts")).toSorted();
|
|
3779
5265
|
} catch {
|
|
3780
5266
|
return entries;
|
|
3781
5267
|
}
|
|
3782
5268
|
for (const name of names) {
|
|
3783
|
-
entries[name] = await
|
|
5269
|
+
entries[name] = await readFile9(join12(functionsDir, `${name}.ts`), "utf8");
|
|
3784
5270
|
}
|
|
3785
5271
|
return entries;
|
|
3786
5272
|
}
|
|
@@ -3792,10 +5278,10 @@ async function deploy(paths, options) {
|
|
|
3792
5278
|
const log = options.silent === true ? () => void 0 : options.log ?? ((line) => process.stdout.write(`${line}
|
|
3793
5279
|
`));
|
|
3794
5280
|
const client = await createClient(paths);
|
|
3795
|
-
const cwd =
|
|
5281
|
+
const cwd = resolve6(options.cwd);
|
|
3796
5282
|
const config = await loadConfig(cwd);
|
|
3797
5283
|
const slug = options.slug ?? config.name;
|
|
3798
|
-
const functionsDir = options.functionsDir === void 0 ?
|
|
5284
|
+
const functionsDir = options.functionsDir === void 0 ? join12(cwd, config.functionsDir) : resolve6(cwd, options.functionsDir);
|
|
3799
5285
|
const local = await collectEntries(functionsDir);
|
|
3800
5286
|
if (Object.keys(local).length === 0) {
|
|
3801
5287
|
throw new CliError("NO_FUNCTIONS", `${functionsDir} \u4E0B\u6CA1\u6709\u51FD\u6570\u6587\u4EF6`);
|
|
@@ -3878,7 +5364,7 @@ var client_exports2 = {};
|
|
|
3878
5364
|
__export(client_exports2, {
|
|
3879
5365
|
createExportApiClient: () => createExportApiClient
|
|
3880
5366
|
});
|
|
3881
|
-
import { writeFile as
|
|
5367
|
+
import { writeFile as writeFile6 } from "node:fs/promises";
|
|
3882
5368
|
function toExportJob(view) {
|
|
3883
5369
|
return {
|
|
3884
5370
|
id: view.id,
|
|
@@ -3929,7 +5415,7 @@ async function createExportApiClient(paths) {
|
|
|
3929
5415
|
}
|
|
3930
5416
|
const response = await client.download(download.downloadUrl, "EXPORT_DOWNLOAD_FAILED");
|
|
3931
5417
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
3932
|
-
await
|
|
5418
|
+
await writeFile6(outputPath, bytes);
|
|
3933
5419
|
return { path: outputPath, size: bytes.length };
|
|
3934
5420
|
}
|
|
3935
5421
|
};
|
|
@@ -3949,7 +5435,7 @@ function zipRead(buf) {
|
|
|
3949
5435
|
if (eocd < 0) throw new ZipFormatError("\u627E\u4E0D\u5230 EOCD \u7B7E\u540D");
|
|
3950
5436
|
const total = r.u16(eocd + 10);
|
|
3951
5437
|
const centralOffset = r.u32(eocd + 16);
|
|
3952
|
-
const
|
|
5438
|
+
const decoder2 = new TextDecoder();
|
|
3953
5439
|
const out = /* @__PURE__ */ new Map();
|
|
3954
5440
|
let cursor = centralOffset;
|
|
3955
5441
|
for (let i = 0; i < total; i++) {
|
|
@@ -3963,7 +5449,7 @@ function zipRead(buf) {
|
|
|
3963
5449
|
const commentLen = r.u16(cursor + 32);
|
|
3964
5450
|
const localHeaderOffset = r.u32(cursor + 42);
|
|
3965
5451
|
if (localHeaderOffset + 30 > buf.length) throw new ZipFormatError("\u672C\u5730\u5934\u504F\u79FB\u8D8A\u754C");
|
|
3966
|
-
const name =
|
|
5452
|
+
const name = decoder2.decode(r.slice(cursor + 46, nameLen));
|
|
3967
5453
|
const localNameLen = r.u16(localHeaderOffset + 26);
|
|
3968
5454
|
const localExtraLen = r.u16(localHeaderOffset + 28);
|
|
3969
5455
|
const dataStart = localHeaderOffset + 30 + localNameLen + localExtraLen;
|
|
@@ -4026,33 +5512,33 @@ var fs_exports = {};
|
|
|
4026
5512
|
__export(fs_exports, {
|
|
4027
5513
|
createExportFileSystem: () => createExportFileSystem
|
|
4028
5514
|
});
|
|
4029
|
-
import { mkdir as
|
|
4030
|
-
import { dirname as
|
|
5515
|
+
import { mkdir as mkdir8, readFile as readFile10, rm as rm3, stat as stat7, writeFile as writeFile7 } from "node:fs/promises";
|
|
5516
|
+
import { dirname as dirname7, join as join13 } from "node:path";
|
|
4031
5517
|
function createExportFileSystem() {
|
|
4032
5518
|
return {
|
|
4033
|
-
readFile: (path) =>
|
|
4034
|
-
writeFile: (path, content) =>
|
|
5519
|
+
readFile: (path) => readFile10(path),
|
|
5520
|
+
writeFile: (path, content) => writeFile7(path, content),
|
|
4035
5521
|
// 同时用于清理解压临时目录(${zip}.tmp 是个目录)——递归强制删除,缺失不报错。
|
|
4036
5522
|
deleteFile: async (path) => {
|
|
4037
5523
|
await rm3(path, { recursive: true, force: true });
|
|
4038
5524
|
},
|
|
4039
5525
|
fileExists: async (path) => {
|
|
4040
5526
|
try {
|
|
4041
|
-
await
|
|
5527
|
+
await stat7(path);
|
|
4042
5528
|
return true;
|
|
4043
5529
|
} catch {
|
|
4044
5530
|
return false;
|
|
4045
5531
|
}
|
|
4046
5532
|
},
|
|
4047
5533
|
mkdir: async (path) => {
|
|
4048
|
-
await
|
|
5534
|
+
await mkdir8(path, { recursive: true });
|
|
4049
5535
|
},
|
|
4050
5536
|
unzip: async (zipPath, targetDir) => {
|
|
4051
|
-
const entries = zipRead(await
|
|
5537
|
+
const entries = zipRead(await readFile10(zipPath));
|
|
4052
5538
|
for (const [name, data] of entries) {
|
|
4053
|
-
const dest =
|
|
4054
|
-
await
|
|
4055
|
-
await
|
|
5539
|
+
const dest = join13(targetDir, name);
|
|
5540
|
+
await mkdir8(dirname7(dest), { recursive: true });
|
|
5541
|
+
await writeFile7(dest, data);
|
|
4056
5542
|
}
|
|
4057
5543
|
}
|
|
4058
5544
|
};
|
|
@@ -4553,7 +6039,7 @@ ${detail}`);
|
|
|
4553
6039
|
* @param ms 毫秒数
|
|
4554
6040
|
*/
|
|
4555
6041
|
sleep(ms) {
|
|
4556
|
-
return new Promise((
|
|
6042
|
+
return new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
4557
6043
|
}
|
|
4558
6044
|
};
|
|
4559
6045
|
}
|
|
@@ -4571,10 +6057,10 @@ __export(db_exports, {
|
|
|
4571
6057
|
dbStatus: () => dbStatus,
|
|
4572
6058
|
dbStop: () => dbStop
|
|
4573
6059
|
});
|
|
4574
|
-
import { resolve as
|
|
6060
|
+
import { resolve as resolve8 } from "node:path";
|
|
4575
6061
|
async function open2(paths, options) {
|
|
4576
6062
|
const client = await createClient(paths);
|
|
4577
|
-
const project2 = await resolveSlug(
|
|
6063
|
+
const project2 = await resolveSlug(resolve8(options.cwd), options.slug);
|
|
4578
6064
|
return { client, project: project2 };
|
|
4579
6065
|
}
|
|
4580
6066
|
async function dbStart(paths, options) {
|
|
@@ -4648,11 +6134,11 @@ __export(storage_exports, {
|
|
|
4648
6134
|
storageRemove: () => storageRemove,
|
|
4649
6135
|
storageUpload: () => storageUpload
|
|
4650
6136
|
});
|
|
4651
|
-
import { mkdir as
|
|
4652
|
-
import { basename as
|
|
6137
|
+
import { mkdir as mkdir9, readFile as readFile11, stat as stat8, writeFile as writeFile8 } from "node:fs/promises";
|
|
6138
|
+
import { basename as basename4, dirname as dirname8, extname, join as join14, resolve as resolve9 } from "node:path";
|
|
4653
6139
|
async function open3(paths, options) {
|
|
4654
6140
|
const client = await createClient(paths);
|
|
4655
|
-
const project2 = await resolveSlug(
|
|
6141
|
+
const project2 = await resolveSlug(resolve9(options.cwd), options.slug);
|
|
4656
6142
|
return { client, project: project2 };
|
|
4657
6143
|
}
|
|
4658
6144
|
function contentTypeOf(path) {
|
|
@@ -4685,20 +6171,20 @@ function contentTypeOf(path) {
|
|
|
4685
6171
|
}
|
|
4686
6172
|
async function storageUpload(paths, options) {
|
|
4687
6173
|
const { client, project: project2 } = await open3(paths, options);
|
|
4688
|
-
const local =
|
|
4689
|
-
const info = await
|
|
6174
|
+
const local = resolve9(options.file);
|
|
6175
|
+
const info = await stat8(local).catch(() => null);
|
|
4690
6176
|
if (info === null || !info.isFile()) {
|
|
4691
6177
|
throw new CliError("FILE_NOT_FOUND", `\u672C\u5730\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${local}`);
|
|
4692
6178
|
}
|
|
4693
6179
|
const visibility = options.visibility ?? "private";
|
|
4694
|
-
const bytes = await
|
|
6180
|
+
const bytes = await readFile11(local);
|
|
4695
6181
|
const form = new FormData();
|
|
4696
6182
|
form.set("path", options.path);
|
|
4697
6183
|
form.set("visibility", visibility);
|
|
4698
6184
|
form.set(
|
|
4699
6185
|
"file",
|
|
4700
6186
|
new Blob([bytes], { type: contentTypeOf(options.path) }),
|
|
4701
|
-
|
|
6187
|
+
basename4(local)
|
|
4702
6188
|
);
|
|
4703
6189
|
const response = await client.upload(
|
|
4704
6190
|
`/api/v1/projects/${project2}/files`,
|
|
@@ -4732,9 +6218,9 @@ async function storageDownload(paths, options) {
|
|
|
4732
6218
|
const url = await resolveDownloadUrl(client, project2, options.path);
|
|
4733
6219
|
const response = await client.download(url, "STORAGE_DOWNLOAD_FAILED");
|
|
4734
6220
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
4735
|
-
const output =
|
|
4736
|
-
await
|
|
4737
|
-
await
|
|
6221
|
+
const output = resolve9(options.output ?? join14(resolve9(options.cwd), basename4(options.path)));
|
|
6222
|
+
await mkdir9(dirname8(output), { recursive: true });
|
|
6223
|
+
await writeFile8(output, bytes);
|
|
4738
6224
|
return { path: options.path, output, size: bytes.length };
|
|
4739
6225
|
}
|
|
4740
6226
|
async function storageRemove(paths, options) {
|
|
@@ -4758,7 +6244,7 @@ __export(doctor_exports, {
|
|
|
4758
6244
|
formatDoctorReport: () => formatDoctorReport,
|
|
4759
6245
|
runDoctor: () => runDoctor
|
|
4760
6246
|
});
|
|
4761
|
-
import { stat as
|
|
6247
|
+
import { stat as stat9 } from "node:fs/promises";
|
|
4762
6248
|
async function probeServer(server) {
|
|
4763
6249
|
const controller = new AbortController();
|
|
4764
6250
|
const timer = setTimeout(() => controller.abort(), SERVER_PROBE_TIMEOUT_MS);
|
|
@@ -4776,7 +6262,7 @@ async function probeServer(server) {
|
|
|
4776
6262
|
}
|
|
4777
6263
|
async function credentialMode(file) {
|
|
4778
6264
|
try {
|
|
4779
|
-
const info = await
|
|
6265
|
+
const info = await stat9(file);
|
|
4780
6266
|
return (info.mode & 511).toString(8).padStart(3, "0");
|
|
4781
6267
|
} catch {
|
|
4782
6268
|
return void 0;
|
|
@@ -4865,7 +6351,7 @@ __export(mcp_exports, {
|
|
|
4865
6351
|
mcpPublish: () => mcpPublish,
|
|
4866
6352
|
mcpUnpublish: () => mcpUnpublish
|
|
4867
6353
|
});
|
|
4868
|
-
import { resolve as
|
|
6354
|
+
import { resolve as resolve10 } from "node:path";
|
|
4869
6355
|
async function mcpPublish(paths, options) {
|
|
4870
6356
|
const fn = requireFunctionName(options.fn);
|
|
4871
6357
|
const client = await createClient(paths);
|
|
@@ -4946,7 +6432,7 @@ function requireFunctionName(fn) {
|
|
|
4946
6432
|
return fn.trim();
|
|
4947
6433
|
}
|
|
4948
6434
|
async function resolveProject(client, options) {
|
|
4949
|
-
const slug = await resolveSlug(
|
|
6435
|
+
const slug = await resolveSlug(resolve10(options.cwd), options.slug);
|
|
4950
6436
|
const listed = await client.request("/api/v1/projects", {}, "PROJECT_LIST_FAILED");
|
|
4951
6437
|
const match = listed.projects.find((project2) => project2.slug === slug);
|
|
4952
6438
|
if (match === void 0) {
|
|
@@ -5047,13 +6533,13 @@ __export(functions_exports, {
|
|
|
5047
6533
|
functionsList: () => functionsList,
|
|
5048
6534
|
functionsLogs: () => functionsLogs
|
|
5049
6535
|
});
|
|
5050
|
-
import { resolve as
|
|
6536
|
+
import { resolve as resolve11 } from "node:path";
|
|
5051
6537
|
async function functionsDeploy(paths, options) {
|
|
5052
6538
|
const startedAt = Date.now();
|
|
5053
6539
|
const result = await deploy(paths, {
|
|
5054
6540
|
cwd: options.cwd,
|
|
5055
6541
|
...options.slug === void 0 ? {} : { slug: options.slug },
|
|
5056
|
-
...options.dir === void 0 ? {} : { functionsDir:
|
|
6542
|
+
...options.dir === void 0 ? {} : { functionsDir: resolve11(options.cwd, options.dir) },
|
|
5057
6543
|
silent: true
|
|
5058
6544
|
});
|
|
5059
6545
|
return { ...result, elapsedSeconds: (Date.now() - startedAt) / 1e3 };
|
|
@@ -5099,7 +6585,7 @@ async function functionsLogs(paths, options) {
|
|
|
5099
6585
|
return { functionId: target.id, name: target.name, logs: result.logs };
|
|
5100
6586
|
}
|
|
5101
6587
|
async function resolveProject2(client, options) {
|
|
5102
|
-
const slug = await resolveSlug(
|
|
6588
|
+
const slug = await resolveSlug(resolve11(options.cwd), options.slug);
|
|
5103
6589
|
const projects = await projectsListFrom(client);
|
|
5104
6590
|
const match = projects.find((project2) => project2.slug === slug);
|
|
5105
6591
|
if (match === void 0) {
|
|
@@ -5131,11 +6617,11 @@ __export(hosting_exports, {
|
|
|
5131
6617
|
hostingInfo: () => hostingInfo,
|
|
5132
6618
|
hostingPull: () => hostingPull
|
|
5133
6619
|
});
|
|
5134
|
-
import { readdir as
|
|
5135
|
-
import { dirname as
|
|
6620
|
+
import { readdir as readdir6, readFile as readFile12, stat as stat10 } from "node:fs/promises";
|
|
6621
|
+
import { dirname as dirname9, join as join15, relative as relative3, resolve as resolve12, sep } from "node:path";
|
|
5136
6622
|
async function open4(paths, options) {
|
|
5137
6623
|
const client = await createClient(paths);
|
|
5138
|
-
const project2 = await resolveSlug(
|
|
6624
|
+
const project2 = await resolveSlug(resolve12(options.cwd), options.slug);
|
|
5139
6625
|
return { client, project: project2 };
|
|
5140
6626
|
}
|
|
5141
6627
|
async function hostingInfo(paths, options) {
|
|
@@ -5148,21 +6634,21 @@ async function hostingInfo(paths, options) {
|
|
|
5148
6634
|
};
|
|
5149
6635
|
}
|
|
5150
6636
|
async function collectSiteFiles(dir) {
|
|
5151
|
-
const root =
|
|
6637
|
+
const root = resolve12(dir);
|
|
5152
6638
|
const files = /* @__PURE__ */ new Map();
|
|
5153
6639
|
const walk = async (sub) => {
|
|
5154
6640
|
let entries;
|
|
5155
6641
|
try {
|
|
5156
|
-
entries = await
|
|
6642
|
+
entries = await readdir6(sub, { withFileTypes: true });
|
|
5157
6643
|
} catch {
|
|
5158
6644
|
return;
|
|
5159
6645
|
}
|
|
5160
6646
|
for (const entry of entries) {
|
|
5161
|
-
const full =
|
|
6647
|
+
const full = join15(sub, entry.name);
|
|
5162
6648
|
if (entry.isDirectory()) {
|
|
5163
6649
|
await walk(full);
|
|
5164
6650
|
} else if (entry.isFile()) {
|
|
5165
|
-
const rel =
|
|
6651
|
+
const rel = relative3(root, full).split(sep).join("/");
|
|
5166
6652
|
files.set(rel, full);
|
|
5167
6653
|
}
|
|
5168
6654
|
}
|
|
@@ -5176,13 +6662,13 @@ async function hostingDeploy(paths, options) {
|
|
|
5176
6662
|
`));
|
|
5177
6663
|
const files = await collectSiteFiles(options.dir);
|
|
5178
6664
|
if (files.size === 0) {
|
|
5179
|
-
throw new CliError("NO_SITE_FILES", `\u7AD9\u70B9\u76EE\u5F55\u4E3A\u7A7A\uFF1A${
|
|
6665
|
+
throw new CliError("NO_SITE_FILES", `\u7AD9\u70B9\u76EE\u5F55\u4E3A\u7A7A\uFF1A${resolve12(options.dir)}`);
|
|
5180
6666
|
}
|
|
5181
6667
|
const uploaded = [];
|
|
5182
6668
|
for (const [rel, full] of files) {
|
|
5183
6669
|
log(`[adep] \u4E0A\u4F20 site/${rel}`);
|
|
5184
|
-
const info2 = await
|
|
5185
|
-
const bytes = await
|
|
6670
|
+
const info2 = await stat10(full);
|
|
6671
|
+
const bytes = await readFile12(full);
|
|
5186
6672
|
const form = new FormData();
|
|
5187
6673
|
form.set("path", `site/${rel}`);
|
|
5188
6674
|
form.set("visibility", "public");
|
|
@@ -5209,8 +6695,8 @@ async function hostingPull(paths, options) {
|
|
|
5209
6695
|
const log = options.log ?? ((line) => process.stdout.write(`${line}
|
|
5210
6696
|
`));
|
|
5211
6697
|
const info = await hostingInfo(paths, options);
|
|
5212
|
-
const outputDir =
|
|
5213
|
-
const { mkdir:
|
|
6698
|
+
const outputDir = resolve12(options.output ?? resolve12(options.cwd));
|
|
6699
|
+
const { mkdir: mkdir10, writeFile: writeFile9 } = await import("node:fs/promises");
|
|
5214
6700
|
const files = [];
|
|
5215
6701
|
for (const entry of info.files) {
|
|
5216
6702
|
if (entry.visibility !== "public" || entry.path === HOSTING_CONFIG_PATH) continue;
|
|
@@ -5220,9 +6706,9 @@ async function hostingPull(paths, options) {
|
|
|
5220
6706
|
const response = await client.download(url, "HOSTING_DOWNLOAD_FAILED");
|
|
5221
6707
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
5222
6708
|
const rel = entry.path.replace(/^site\//, "");
|
|
5223
|
-
const target =
|
|
5224
|
-
await
|
|
5225
|
-
await
|
|
6709
|
+
const target = join15(outputDir, ...rel.split("/"));
|
|
6710
|
+
await mkdir10(dirname9(target), { recursive: true });
|
|
6711
|
+
await writeFile9(target, bytes);
|
|
5226
6712
|
files.push({ path: entry.path, size: bytes.length });
|
|
5227
6713
|
}
|
|
5228
6714
|
return { siteUrl: info.siteUrl, outputDir, files };
|
|
@@ -5252,7 +6738,7 @@ var init_hosting = __esm({
|
|
|
5252
6738
|
// packages/cli/src/cli.ts
|
|
5253
6739
|
init_config();
|
|
5254
6740
|
import { Command } from "commander";
|
|
5255
|
-
import { createRequire as
|
|
6741
|
+
import { createRequire as createRequire3 } from "node:module";
|
|
5256
6742
|
|
|
5257
6743
|
// packages/cli/src/output.ts
|
|
5258
6744
|
init_auth();
|
|
@@ -6163,6 +7649,760 @@ function registerInit(program2, ctx) {
|
|
|
6163
7649
|
);
|
|
6164
7650
|
}
|
|
6165
7651
|
|
|
7652
|
+
// packages/cli/src/import-cf.ts
|
|
7653
|
+
init_adep_config();
|
|
7654
|
+
import { copyFile, mkdir as mkdir3, readFile as readFile2, readdir, stat as stat3, writeFile as writeFile2 } from "node:fs/promises";
|
|
7655
|
+
import { basename, dirname as dirname3, join as join2, relative, resolve as resolve2 } from "node:path";
|
|
7656
|
+
var TomlSyntaxError = class extends Error {
|
|
7657
|
+
constructor(message) {
|
|
7658
|
+
super(message);
|
|
7659
|
+
this.name = "TomlSyntaxError";
|
|
7660
|
+
}
|
|
7661
|
+
};
|
|
7662
|
+
function parseTomlValue(raw) {
|
|
7663
|
+
const text = raw.trim();
|
|
7664
|
+
if (text === "") throw new TomlSyntaxError("\u7A7A\u503C");
|
|
7665
|
+
if (text.startsWith("[") && text.endsWith("]")) {
|
|
7666
|
+
const inner = text.slice(1, -1).trim();
|
|
7667
|
+
if (inner === "") return [];
|
|
7668
|
+
return splitTopLevel(inner).map((part) => parseTomlValue(part));
|
|
7669
|
+
}
|
|
7670
|
+
if (text.startsWith("{") && text.endsWith("}")) {
|
|
7671
|
+
const inner = text.slice(1, -1).trim();
|
|
7672
|
+
const table = {};
|
|
7673
|
+
if (inner !== "") {
|
|
7674
|
+
for (const pair of splitTopLevel(inner)) {
|
|
7675
|
+
const eq = pair.indexOf("=");
|
|
7676
|
+
if (eq <= 0) throw new TomlSyntaxError(`\u5185\u8054\u8868\u6761\u76EE\u975E\u6CD5\uFF1A${pair}`);
|
|
7677
|
+
const key = pair.slice(0, eq).trim().replace(/^"|"$/g, "");
|
|
7678
|
+
table[key] = parseTomlValue(pair.slice(eq + 1));
|
|
7679
|
+
}
|
|
7680
|
+
}
|
|
7681
|
+
return table;
|
|
7682
|
+
}
|
|
7683
|
+
if (text.startsWith('"') || text.startsWith("'")) {
|
|
7684
|
+
return unquoteToml(text);
|
|
7685
|
+
}
|
|
7686
|
+
if (text === "true") return true;
|
|
7687
|
+
if (text === "false") return false;
|
|
7688
|
+
const number = Number(text);
|
|
7689
|
+
if (text !== "" && !Number.isNaN(number) && /^-?\d+(\.\d+)?$/.test(text)) return number;
|
|
7690
|
+
return text;
|
|
7691
|
+
}
|
|
7692
|
+
function splitTopLevel(text) {
|
|
7693
|
+
const parts = [];
|
|
7694
|
+
let depth = 0;
|
|
7695
|
+
let quote = null;
|
|
7696
|
+
let current = "";
|
|
7697
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
7698
|
+
const ch = text[i];
|
|
7699
|
+
if (quote !== null) {
|
|
7700
|
+
current += ch;
|
|
7701
|
+
if (ch === quote && text[i - 1] !== "\\") quote = null;
|
|
7702
|
+
continue;
|
|
7703
|
+
}
|
|
7704
|
+
if (ch === '"' || ch === "'") {
|
|
7705
|
+
quote = ch;
|
|
7706
|
+
current += ch;
|
|
7707
|
+
continue;
|
|
7708
|
+
}
|
|
7709
|
+
if (ch === "{" || ch === "[") depth += 1;
|
|
7710
|
+
if (ch === "}" || ch === "]") depth -= 1;
|
|
7711
|
+
if (ch === "," && depth === 0) {
|
|
7712
|
+
parts.push(current);
|
|
7713
|
+
current = "";
|
|
7714
|
+
continue;
|
|
7715
|
+
}
|
|
7716
|
+
current += ch;
|
|
7717
|
+
}
|
|
7718
|
+
if (quote !== null) throw new TomlSyntaxError("\u5B57\u7B26\u4E32\u672A\u95ED\u5408");
|
|
7719
|
+
parts.push(current);
|
|
7720
|
+
return parts;
|
|
7721
|
+
}
|
|
7722
|
+
function unquoteToml(text) {
|
|
7723
|
+
const quote = text[0];
|
|
7724
|
+
const body = text.slice(1, -1);
|
|
7725
|
+
if (quote === "'") return body;
|
|
7726
|
+
return body.replace(/\\n/g, "\n").replace(/\\t/g, " ").replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
7727
|
+
}
|
|
7728
|
+
function parseWranglerToml(text) {
|
|
7729
|
+
const root = {};
|
|
7730
|
+
let current = { table: root };
|
|
7731
|
+
const tableAt = (path) => {
|
|
7732
|
+
let node = root;
|
|
7733
|
+
for (const seg of path) {
|
|
7734
|
+
const next = node[seg];
|
|
7735
|
+
if (next === void 0) {
|
|
7736
|
+
const created = {};
|
|
7737
|
+
node[seg] = created;
|
|
7738
|
+
node = created;
|
|
7739
|
+
} else if (typeof next === "object" && next !== null && !Array.isArray(next)) {
|
|
7740
|
+
node = next;
|
|
7741
|
+
} else {
|
|
7742
|
+
throw new TomlSyntaxError(`\u8DEF\u5F84\u51B2\u7A81\uFF1A${path.join(".")}\uFF08${seg} \u4E0D\u662F\u8868\uFF09`);
|
|
7743
|
+
}
|
|
7744
|
+
}
|
|
7745
|
+
return node;
|
|
7746
|
+
};
|
|
7747
|
+
const lines = text.split(/\r?\n/);
|
|
7748
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
7749
|
+
const rawLine = lines[i] ?? "";
|
|
7750
|
+
const line = stripTomlComment(rawLine).trim();
|
|
7751
|
+
if (line === "") continue;
|
|
7752
|
+
const arrayHeader = /^\[\[([^\]]+)\]\]\s*$/.exec(line);
|
|
7753
|
+
if (arrayHeader !== null) {
|
|
7754
|
+
const path = (arrayHeader[1] ?? "").trim().split(".").filter(Boolean);
|
|
7755
|
+
if (path.length === 0) throw new TomlSyntaxError(`\u7A7A\u6570\u7EC4\u8868\uFF1A${line}`);
|
|
7756
|
+
const parent = path.length === 1 ? root : tableAt(path.slice(0, -1));
|
|
7757
|
+
const key2 = path[path.length - 1] ?? "";
|
|
7758
|
+
let arr = parent[key2];
|
|
7759
|
+
if (arr === void 0) {
|
|
7760
|
+
arr = [];
|
|
7761
|
+
parent[key2] = arr;
|
|
7762
|
+
}
|
|
7763
|
+
if (!Array.isArray(arr)) throw new TomlSyntaxError(`\u8DEF\u5F84\u51B2\u7A81\uFF1A${key2} \u4E0D\u662F\u6570\u7EC4`);
|
|
7764
|
+
const table = {};
|
|
7765
|
+
arr.push(table);
|
|
7766
|
+
current = { table };
|
|
7767
|
+
continue;
|
|
7768
|
+
}
|
|
7769
|
+
const header = /^\[([^\]]+)\]\s*$/.exec(line);
|
|
7770
|
+
if (header !== null) {
|
|
7771
|
+
const path = (header[1] ?? "").trim().split(".").filter(Boolean);
|
|
7772
|
+
if (path.length !== 1) throw new TomlSyntaxError(`\u4EC5\u652F\u6301\u5355\u6BB5\u8868\uFF1A${line}`);
|
|
7773
|
+
current = { table: tableAt(path) };
|
|
7774
|
+
continue;
|
|
7775
|
+
}
|
|
7776
|
+
const eq = line.indexOf("=");
|
|
7777
|
+
if (eq <= 0) throw new TomlSyntaxError(`\u7B2C ${i + 1} \u884C\u65E0\u6CD5\u89E3\u6790\uFF1A${rawLine}`);
|
|
7778
|
+
const key = line.slice(0, eq).trim().replace(/^"|"$/g, "");
|
|
7779
|
+
let valueRaw = line.slice(eq + 1);
|
|
7780
|
+
let depth = bracketDepth(valueRaw);
|
|
7781
|
+
while (depth > 0) {
|
|
7782
|
+
if (i + 1 >= lines.length) {
|
|
7783
|
+
throw new TomlSyntaxError(`\u7B2C ${i + 1} \u884C\u503C\u672A\u95ED\u5408\uFF1A${key}`);
|
|
7784
|
+
}
|
|
7785
|
+
i += 1;
|
|
7786
|
+
const next = stripTomlComment(lines[i] ?? "").trim();
|
|
7787
|
+
valueRaw += ` ${next}`;
|
|
7788
|
+
depth = bracketDepth(valueRaw);
|
|
7789
|
+
}
|
|
7790
|
+
const value = parseTomlValue(valueRaw);
|
|
7791
|
+
current.table[key] = value;
|
|
7792
|
+
}
|
|
7793
|
+
return normalizeWranglerToml(root);
|
|
7794
|
+
}
|
|
7795
|
+
function stripTomlComment(line) {
|
|
7796
|
+
const hash = line.indexOf("#");
|
|
7797
|
+
if (hash === -1) return line;
|
|
7798
|
+
return line.slice(0, hash);
|
|
7799
|
+
}
|
|
7800
|
+
function bracketDepth(text) {
|
|
7801
|
+
let depth = 0;
|
|
7802
|
+
let quote = null;
|
|
7803
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
7804
|
+
const ch = text[i];
|
|
7805
|
+
if (quote !== null) {
|
|
7806
|
+
if (ch === quote && text[i - 1] !== "\\") quote = null;
|
|
7807
|
+
continue;
|
|
7808
|
+
}
|
|
7809
|
+
if (ch === '"' || ch === "'") {
|
|
7810
|
+
quote = ch;
|
|
7811
|
+
continue;
|
|
7812
|
+
}
|
|
7813
|
+
if (ch === "[" || ch === "{") depth += 1;
|
|
7814
|
+
if (ch === "]" || ch === "}") depth -= 1;
|
|
7815
|
+
}
|
|
7816
|
+
return depth;
|
|
7817
|
+
}
|
|
7818
|
+
function tomlString(v) {
|
|
7819
|
+
return typeof v === "string" ? v : void 0;
|
|
7820
|
+
}
|
|
7821
|
+
function tomlStringRecord(v) {
|
|
7822
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return void 0;
|
|
7823
|
+
const out = {};
|
|
7824
|
+
for (const [k, value] of Object.entries(v)) {
|
|
7825
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
7826
|
+
out[k] = String(value);
|
|
7827
|
+
}
|
|
7828
|
+
}
|
|
7829
|
+
return out;
|
|
7830
|
+
}
|
|
7831
|
+
function tomlBindingArray(v) {
|
|
7832
|
+
if (!Array.isArray(v)) return void 0;
|
|
7833
|
+
const out = [];
|
|
7834
|
+
for (const item of v) {
|
|
7835
|
+
if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
7836
|
+
const rec = {};
|
|
7837
|
+
for (const [k, value] of Object.entries(item)) {
|
|
7838
|
+
if (typeof value === "string" || typeof value === "number") rec[k] = String(value);
|
|
7839
|
+
}
|
|
7840
|
+
out.push(rec);
|
|
7841
|
+
}
|
|
7842
|
+
}
|
|
7843
|
+
return out;
|
|
7844
|
+
}
|
|
7845
|
+
function normalizeWranglerToml(root) {
|
|
7846
|
+
const name = tomlString(root.name);
|
|
7847
|
+
const main = tomlString(root.main);
|
|
7848
|
+
const vars = tomlStringRecord(root.vars);
|
|
7849
|
+
const kv = tomlBindingArray(root.kv_namespaces);
|
|
7850
|
+
const d1 = tomlBindingArray(root.d1_databases);
|
|
7851
|
+
const r2 = tomlBindingArray(root.r2_buckets);
|
|
7852
|
+
const triggersRaw = typeof root.triggers === "object" && root.triggers !== null ? root.triggers : void 0;
|
|
7853
|
+
const crons = triggersRaw !== void 0 && Array.isArray(triggersRaw.crons) ? triggersRaw.crons.map(String) : void 0;
|
|
7854
|
+
const assetsRaw = typeof root.assets === "object" && root.assets !== null ? root.assets : void 0;
|
|
7855
|
+
const assetsDir = assetsRaw === void 0 ? void 0 : tomlString(assetsRaw.directory);
|
|
7856
|
+
const siteRaw = typeof root.site === "object" && root.site !== null ? root.site : void 0;
|
|
7857
|
+
const siteBucket = siteRaw === void 0 ? void 0 : tomlString(siteRaw.bucket);
|
|
7858
|
+
const out = { raw: root };
|
|
7859
|
+
if (name !== void 0) out.name = name;
|
|
7860
|
+
if (main !== void 0) out.main = main;
|
|
7861
|
+
if (vars !== void 0) out.vars = vars;
|
|
7862
|
+
if (kv !== void 0) out.kv_namespaces = kv;
|
|
7863
|
+
if (d1 !== void 0) out.d1_databases = d1;
|
|
7864
|
+
if (r2 !== void 0) out.r2_buckets = r2;
|
|
7865
|
+
if (crons !== void 0) out.triggers = { crons };
|
|
7866
|
+
if (assetsDir !== void 0) out.assets = { directory: assetsDir };
|
|
7867
|
+
if (siteBucket !== void 0) out.site = { bucket: siteBucket };
|
|
7868
|
+
const doRaw = typeof root.durable_objects === "object" && root.durable_objects !== null ? root.durable_objects : void 0;
|
|
7869
|
+
const doBindings = doRaw === void 0 ? void 0 : tomlBindingArray(doRaw.bindings);
|
|
7870
|
+
if (doBindings !== void 0 && doBindings.length > 0) {
|
|
7871
|
+
out.durable_objects = {
|
|
7872
|
+
bindings: doBindings
|
|
7873
|
+
};
|
|
7874
|
+
}
|
|
7875
|
+
return out;
|
|
7876
|
+
}
|
|
7877
|
+
var ImportCfError = class extends Error {
|
|
7878
|
+
code;
|
|
7879
|
+
constructor(code, message) {
|
|
7880
|
+
super(message);
|
|
7881
|
+
this.code = code;
|
|
7882
|
+
this.name = "ImportCfError";
|
|
7883
|
+
}
|
|
7884
|
+
};
|
|
7885
|
+
function slugify(name, fallback) {
|
|
7886
|
+
const slug = name.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
|
|
7887
|
+
return slug.length > 0 ? slug : fallback;
|
|
7888
|
+
}
|
|
7889
|
+
var SKIP_COPY_NAMES = /* @__PURE__ */ new Set([
|
|
7890
|
+
"wrangler.toml",
|
|
7891
|
+
"wrangler.json",
|
|
7892
|
+
"wrangler.jsonc",
|
|
7893
|
+
".dev.vars",
|
|
7894
|
+
"package.json",
|
|
7895
|
+
"package-lock.json",
|
|
7896
|
+
"pnpm-lock.yaml",
|
|
7897
|
+
"yarn.lock",
|
|
7898
|
+
"bun.lockb",
|
|
7899
|
+
"node_modules",
|
|
7900
|
+
".git",
|
|
7901
|
+
"dist",
|
|
7902
|
+
".wrangler",
|
|
7903
|
+
"test",
|
|
7904
|
+
"tests"
|
|
7905
|
+
]);
|
|
7906
|
+
async function copyWorkerDir(sourceDir, targetDir, mainFile) {
|
|
7907
|
+
const copied = [];
|
|
7908
|
+
const renamedIndex = [];
|
|
7909
|
+
const entries = await readdir(sourceDir, { withFileTypes: true });
|
|
7910
|
+
for (const entry of entries) {
|
|
7911
|
+
const from = join2(sourceDir, entry.name);
|
|
7912
|
+
const to = join2(targetDir, entry.name);
|
|
7913
|
+
if (SKIP_COPY_NAMES.has(entry.name)) continue;
|
|
7914
|
+
if (entry.isDirectory()) {
|
|
7915
|
+
await mkdir3(to, { recursive: true });
|
|
7916
|
+
const sub = await copyWorkerDir(from, to, "");
|
|
7917
|
+
copied.push(...sub.copied);
|
|
7918
|
+
renamedIndex.push(...sub.renamedIndex);
|
|
7919
|
+
continue;
|
|
7920
|
+
}
|
|
7921
|
+
if (from === mainFile) continue;
|
|
7922
|
+
if (entry.name === "index.ts") {
|
|
7923
|
+
await copyFile(from, join2(targetDir, "_index.ts"));
|
|
7924
|
+
renamedIndex.push("_index.ts");
|
|
7925
|
+
continue;
|
|
7926
|
+
}
|
|
7927
|
+
await copyFile(from, to);
|
|
7928
|
+
copied.push(relative(targetDir, to));
|
|
7929
|
+
}
|
|
7930
|
+
return { copied, renamedIndex };
|
|
7931
|
+
}
|
|
7932
|
+
async function readDevVars(sourceDir) {
|
|
7933
|
+
try {
|
|
7934
|
+
const text = await readFile2(join2(sourceDir, ".dev.vars"), "utf8");
|
|
7935
|
+
const vars = {};
|
|
7936
|
+
for (const line of text.split(/\r?\n/)) {
|
|
7937
|
+
const trimmed = line.trim();
|
|
7938
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
7939
|
+
const eq = trimmed.indexOf("=");
|
|
7940
|
+
if (eq <= 0) continue;
|
|
7941
|
+
const key = trimmed.slice(0, eq).trim();
|
|
7942
|
+
vars[key] = trimmed.slice(eq + 1).trim().replace(/^"|"$/g, "");
|
|
7943
|
+
}
|
|
7944
|
+
return vars;
|
|
7945
|
+
} catch {
|
|
7946
|
+
return {};
|
|
7947
|
+
}
|
|
7948
|
+
}
|
|
7949
|
+
async function copyAssetsToWeb(sourceDir, assetsDir, outDir) {
|
|
7950
|
+
const files = [];
|
|
7951
|
+
const from = resolve2(sourceDir, assetsDir);
|
|
7952
|
+
const target = join2(outDir, "web");
|
|
7953
|
+
await mkdir3(target, { recursive: true });
|
|
7954
|
+
const entries = await readdir(from, { withFileTypes: true });
|
|
7955
|
+
for (const entry of entries) {
|
|
7956
|
+
const src = join2(from, entry.name);
|
|
7957
|
+
const dst = join2(target, entry.name);
|
|
7958
|
+
if (entry.isDirectory()) {
|
|
7959
|
+
await mkdir3(dst, { recursive: true });
|
|
7960
|
+
const sub = await copyAssetsToWeb(src, ".", outDir);
|
|
7961
|
+
files.push(...sub);
|
|
7962
|
+
continue;
|
|
7963
|
+
}
|
|
7964
|
+
await copyFile(src, dst);
|
|
7965
|
+
files.push(relative(outDir, dst));
|
|
7966
|
+
}
|
|
7967
|
+
return files;
|
|
7968
|
+
}
|
|
7969
|
+
function bindingsFromWrangler(config) {
|
|
7970
|
+
const cfBindings = {};
|
|
7971
|
+
const kv = config.kv_namespaces?.map((b) => ({
|
|
7972
|
+
name: b.binding ?? "",
|
|
7973
|
+
...b.id === void 0 ? {} : { namespace: b.id }
|
|
7974
|
+
}));
|
|
7975
|
+
if (kv !== void 0 && kv.length > 0) cfBindings.kv = kv;
|
|
7976
|
+
const d1 = config.d1_databases?.map((b) => ({
|
|
7977
|
+
name: b.binding ?? "",
|
|
7978
|
+
...b.database_name === void 0 ? b.database_id === void 0 ? {} : { database: b.database_id } : { database: b.database_name }
|
|
7979
|
+
}));
|
|
7980
|
+
if (d1 !== void 0 && d1.length > 0) cfBindings.d1 = d1;
|
|
7981
|
+
const r2 = config.r2_buckets?.map((b) => ({
|
|
7982
|
+
name: b.binding ?? "",
|
|
7983
|
+
...b.bucket_name === void 0 ? {} : { bucket: b.bucket_name }
|
|
7984
|
+
}));
|
|
7985
|
+
if (r2 !== void 0 && r2.length > 0) cfBindings.r2 = r2;
|
|
7986
|
+
const isEmpty = cfBindings.kv === void 0 && cfBindings.d1 === void 0 && cfBindings.r2 === void 0;
|
|
7987
|
+
return {
|
|
7988
|
+
cfBindings: isEmpty ? void 0 : cfBindings,
|
|
7989
|
+
environment: config.vars
|
|
7990
|
+
};
|
|
7991
|
+
}
|
|
7992
|
+
function collectDurableObjectBindings(config) {
|
|
7993
|
+
return (config.durable_objects?.bindings ?? []).filter((b) => b.class_name !== void 0).map((b) => ({ name: b.name, className: b.class_name }));
|
|
7994
|
+
}
|
|
7995
|
+
function escapeRegExp(text) {
|
|
7996
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7997
|
+
}
|
|
7998
|
+
function findClosingBrace(text, openIdx) {
|
|
7999
|
+
let depth = 0;
|
|
8000
|
+
let i = openIdx;
|
|
8001
|
+
let quote = null;
|
|
8002
|
+
while (i < text.length) {
|
|
8003
|
+
const ch = text[i];
|
|
8004
|
+
if (quote !== null) {
|
|
8005
|
+
if (ch === "\\") {
|
|
8006
|
+
i += 2;
|
|
8007
|
+
continue;
|
|
8008
|
+
}
|
|
8009
|
+
if (ch === quote) quote = null;
|
|
8010
|
+
i += 1;
|
|
8011
|
+
continue;
|
|
8012
|
+
}
|
|
8013
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
8014
|
+
quote = ch;
|
|
8015
|
+
i += 1;
|
|
8016
|
+
continue;
|
|
8017
|
+
}
|
|
8018
|
+
if (ch === "/" && text[i + 1] === "/") {
|
|
8019
|
+
const nl = text.indexOf("\n", i);
|
|
8020
|
+
i = nl === -1 ? text.length : nl + 1;
|
|
8021
|
+
continue;
|
|
8022
|
+
}
|
|
8023
|
+
if (ch === "/" && text[i + 1] === "*") {
|
|
8024
|
+
const end = text.indexOf("*/", i + 2);
|
|
8025
|
+
i = end === -1 ? text.length : end + 2;
|
|
8026
|
+
continue;
|
|
8027
|
+
}
|
|
8028
|
+
if (ch === "{") depth += 1;
|
|
8029
|
+
else if (ch === "}") {
|
|
8030
|
+
depth -= 1;
|
|
8031
|
+
if (depth === 0) return i;
|
|
8032
|
+
}
|
|
8033
|
+
i += 1;
|
|
8034
|
+
}
|
|
8035
|
+
return -1;
|
|
8036
|
+
}
|
|
8037
|
+
function stripDurableObjectClasses(workerText, classNames) {
|
|
8038
|
+
let text = workerText;
|
|
8039
|
+
const removed = [];
|
|
8040
|
+
for (const className of classNames) {
|
|
8041
|
+
const pattern = new RegExp(
|
|
8042
|
+
`export\\s+class\\s+${escapeRegExp(className)}\\s+extends\\s+DurableObject`
|
|
8043
|
+
);
|
|
8044
|
+
const match = pattern.exec(text);
|
|
8045
|
+
if (match === null) continue;
|
|
8046
|
+
const startLine = text.lastIndexOf("\n", match.index) + 1;
|
|
8047
|
+
const braceIdx = text.indexOf("{", match.index);
|
|
8048
|
+
if (braceIdx === -1) continue;
|
|
8049
|
+
const endIdx = findClosingBrace(text, braceIdx);
|
|
8050
|
+
if (endIdx === -1) continue;
|
|
8051
|
+
let removeEnd = endIdx + 1;
|
|
8052
|
+
if (text[removeEnd] === "\n") removeEnd += 1;
|
|
8053
|
+
removed.push({ className, source: text.slice(startLine, endIdx + 1).trim() });
|
|
8054
|
+
text = text.slice(0, startLine) + text.slice(removeEnd);
|
|
8055
|
+
}
|
|
8056
|
+
text = text.replace(/^[ \t]*import[ \t][^\n]*from[ \t]['"]cloudflare:workers['"][^\n]*\n/gm, "");
|
|
8057
|
+
return { text, removed };
|
|
8058
|
+
}
|
|
8059
|
+
function extractMethodNames(source) {
|
|
8060
|
+
const names = [];
|
|
8061
|
+
const re = /(?:^|\n)[ \t]*(?:async[ \t]+)?([A-Za-z_$][\w$]*)[ \t]*\(/g;
|
|
8062
|
+
const CONTROL = /* @__PURE__ */ new Set(["if", "for", "while", "switch", "catch", "function", "with"]);
|
|
8063
|
+
let m;
|
|
8064
|
+
while ((m = re.exec(source)) !== null) {
|
|
8065
|
+
const name = m[1];
|
|
8066
|
+
if (CONTROL.has(name) || name === "constructor" || name === "super" || names.includes(name)) {
|
|
8067
|
+
continue;
|
|
8068
|
+
}
|
|
8069
|
+
names.push(name);
|
|
8070
|
+
}
|
|
8071
|
+
return names;
|
|
8072
|
+
}
|
|
8073
|
+
function renderStateObjectDeclaration(bindings, removed) {
|
|
8074
|
+
const lines = [
|
|
8075
|
+
"/**",
|
|
8076
|
+
" * \u7531 `adep import cf` \u751F\u6210\uFF08CF-039\uFF09\uFF1Awrangler [[durable_objects.bindings]] \u2192 \u72B6\u6001\u5BF9\u8C61\u58F0\u660E\u3002",
|
|
8077
|
+
" * \u539F DurableObject \u7C7B\u6E90\u7801\u4FDD\u7559\u5728 durable-objects/<ClassName>.ts\uFF08AI \u8FC1\u79FB\u53C2\u8003\uFF09\u3002",
|
|
8078
|
+
" * \u8C03\u7528\u9762\uFF1AHTTP POST /api/state/<Type>/<id>/<method> \xB7 MCP state.invoke \xB7 WS state:<Type>:<id>",
|
|
8079
|
+
" */",
|
|
8080
|
+
"import { StateObject } from '@adep/runtime/state-objects'",
|
|
8081
|
+
""
|
|
8082
|
+
];
|
|
8083
|
+
for (const { className } of bindings) {
|
|
8084
|
+
const removedSrc = removed.find((r) => r.className === className);
|
|
8085
|
+
const methods = extractMethodNames(removedSrc?.source ?? "");
|
|
8086
|
+
lines.push("/**", ` * \u8FC1\u79FB\u81EA DurableObject \u7C7B \`${className}\`\u3002`);
|
|
8087
|
+
if (methods.length > 0) {
|
|
8088
|
+
lines.push(
|
|
8089
|
+
` * \u539F\u65B9\u6CD5\uFF08AI \u8FC1\u79FB\uFF1A\u628A fetch \u5165\u53E3\u8DEF\u7531\u62C6\u6210 StateObject \u65B9\u6CD5\uFF09\uFF1A${methods.join(", ")}`
|
|
8090
|
+
);
|
|
8091
|
+
} else {
|
|
8092
|
+
lines.push(` * \u539F\u7C7B\u6E90\u7801\u89C1 durable-objects/${className}.ts\u3002`);
|
|
8093
|
+
}
|
|
8094
|
+
lines.push(" */");
|
|
8095
|
+
lines.push(`export class ${className} extends StateObject<Record<string, unknown>> {`);
|
|
8096
|
+
lines.push(` static typeName = '${className}'`);
|
|
8097
|
+
lines.push("");
|
|
8098
|
+
lines.push(" protected initialState(): Record<string, unknown> {");
|
|
8099
|
+
lines.push(
|
|
8100
|
+
" // TODO\uFF1A\u4ECE\u539F DurableObject \u7684 this.state \u8FC1\u79FB\u521D\u59CB\u72B6\u6001\uFF08\u89C1 durable-objects/\uFF09\u3002"
|
|
8101
|
+
);
|
|
8102
|
+
lines.push(" return {}");
|
|
8103
|
+
lines.push(" }");
|
|
8104
|
+
lines.push("}");
|
|
8105
|
+
lines.push("");
|
|
8106
|
+
}
|
|
8107
|
+
return lines.join("\n");
|
|
8108
|
+
}
|
|
8109
|
+
function unsupportedFromWrangler(config) {
|
|
8110
|
+
const out = [];
|
|
8111
|
+
const raw = config.raw;
|
|
8112
|
+
if (raw.queues !== void 0) {
|
|
8113
|
+
out.push("queues\uFF08\u961F\u5217\uFF09\u2192 \u66FF\u4EE3\uFF1Acron \u89E6\u53D1\u5668 + \u6570\u636E\u5E93\u4EFB\u52A1\u8868\u8F6E\u8BE2");
|
|
8114
|
+
}
|
|
8115
|
+
if (raw.email_bindings !== void 0) {
|
|
8116
|
+
out.push("email_bindings \u2192 \u66FF\u4EE3\uFF1A\u7B2C\u4E09\u65B9\u90AE\u4EF6 API\uFF08\u5982 Resend/Mailgun\uFF09");
|
|
8117
|
+
}
|
|
8118
|
+
if (raw.services !== void 0) {
|
|
8119
|
+
out.push("services\uFF08service bindings\uFF09\u2192 \u66FF\u4EE3\uFF1A\u51FD\u6570\u95F4\u7ECF HTTP \u6216\u4E91\u6570\u636E\u5E93\u5171\u4EAB\u8C03\u7528");
|
|
8120
|
+
}
|
|
8121
|
+
if (raw.assets === void 0 && raw.site === void 0 && raw["_ssr"] !== void 0) {
|
|
8122
|
+
out.push("SSR\uFF08\u524D\u7AEF\u6E32\u67D3\uFF09\u2192 adep web/ \u4E3A\u9759\u6001\u6258\u7BA1\uFF0CSSR \u9700\u81EA\u884C\u5728\u51FD\u6570\u5185\u5B9E\u73B0");
|
|
8123
|
+
}
|
|
8124
|
+
return out;
|
|
8125
|
+
}
|
|
8126
|
+
function renderFunctionEntry(workerImport, bindings) {
|
|
8127
|
+
const bindingsLine = bindings === void 0 ? "" : `, { bindings: ${JSON.stringify(bindings, null, 2).replace(/\n/g, "\n ")} }`;
|
|
8128
|
+
return `/**
|
|
8129
|
+
* \u7531 \`adep import cf\` \u751F\u6210\uFF08CF-015\uFF09\uFF1A\u628A Cloudflare Workers \u6A21\u5757\u683C\u5F0F\u5305\u88C5\u4E3A adep \u4E91\u51FD\u6570\u3002
|
|
8130
|
+
* \u7ED1\u5B9A\u58F0\u660E\u6765\u81EA wrangler.toml\uFF1B\u672A\u652F\u6301\u9879\u4E0E\u66FF\u4EE3\u6A21\u5F0F\u89C1 MIGRATION.md\u3002
|
|
8131
|
+
*
|
|
8132
|
+
* \u6CE8\u610F\uFF1Aadep \u6C99\u7BB1\u628A \`import x from './x'\` \u8F6C\u8BD1\u4E3A \`require('./x')\`\uFF08\u8FD4\u56DE\u6A21\u5757\u5BF9\u8C61\uFF09\uFF0C
|
|
8133
|
+
* \u56E0\u6B64\u8FD9\u91CC\u5BF9 default \u4E0E\u5177\u540D\u4E24\u79CD worker \u5BFC\u51FA\u5F62\u6001\u90FD\u505A\u517C\u5BB9\u3002
|
|
8134
|
+
*/
|
|
8135
|
+
import { createAdepFunction } from '@adep/cf-compat'
|
|
8136
|
+
import workerModule from './${workerImport}'
|
|
8137
|
+
|
|
8138
|
+
const worker = (workerModule as { default?: unknown }).default ?? workerModule
|
|
8139
|
+
|
|
8140
|
+
export default createAdepFunction(worker as never${bindingsLine})
|
|
8141
|
+
`;
|
|
8142
|
+
}
|
|
8143
|
+
function renderMigrationReport(config, result) {
|
|
8144
|
+
const lines = [
|
|
8145
|
+
`# \u8FC1\u79FB\u62A5\u544A\uFF1A${config.name ?? "worker"}\uFF08\u7531 adep import cf \u751F\u6210\uFF09`,
|
|
8146
|
+
"",
|
|
8147
|
+
"## \u6620\u5C04\u6E05\u5355",
|
|
8148
|
+
"",
|
|
8149
|
+
`- worker\uFF1A\`${config.main ?? "(\u65E0 main)"}\` \u2192 \`functions/${result.workerName}/\``,
|
|
8150
|
+
`- vars\uFF1A${result.mapping.vars} \u4E2A \u2192 adep.config.ts environment`,
|
|
8151
|
+
result.mapping.secretsFromDevVars ? "- secrets\uFF08.dev.vars\uFF09\u2192 environment\uFF08\u5EFA\u8BAE\u4E0A\u7EBF\u524D\u8FC1\u5230\u5E73\u53F0 env \u7BA1\u7406\uFF09" : "",
|
|
8152
|
+
`- KV \u7ED1\u5B9A\uFF1A${result.mapping.kv} \u4E2A`,
|
|
8153
|
+
`- D1 \u7ED1\u5B9A\uFF1A${result.mapping.d1} \u4E2A`,
|
|
8154
|
+
`- R2 \u7ED1\u5B9A\uFF1A${result.mapping.r2} \u4E2A`,
|
|
8155
|
+
result.mapping.crons.length > 0 ? `- cron \u89E6\u53D1\u5668\uFF1A${result.mapping.crons.join(", ")}\uFF08adep \u4FA7\u5728\u63A7\u5236\u53F0/MCP \u914D\u7F6E\u89E6\u53D1\u5668\uFF1BcreateAdepFunction \u5DF2\u652F\u6301 scheduled \u6D3E\u53D1\uFF09` : "",
|
|
8156
|
+
result.mapping.assets ? "- \u9759\u6001\u8D44\u6E90 assets \u2192 web/\uFF08adep \u524D\u7AEF\u6258\u7BA1\uFF09" : "",
|
|
8157
|
+
result.mapping.dos > 0 ? `- Durable Objects \u7ED1\u5B9A\uFF1A${result.mapping.dos} \u4E2A\uFF08class_name \u2192 functions/${result.workerName}/state-objects.ts \u58F0\u660E\u9AA8\u67B6\uFF1B\u539F\u7C7B\u4FDD\u7559 durable-objects/\uFF09` : "",
|
|
8158
|
+
"",
|
|
8159
|
+
"## \u672A\u652F\u6301\u9879\u4E0E\u66FF\u4EE3\u6A21\u5F0F",
|
|
8160
|
+
"",
|
|
8161
|
+
...result.unsupported.length > 0 ? result.unsupported.map((item) => `- ${item}`) : ["- \uFF08\u65E0\uFF09"],
|
|
8162
|
+
"",
|
|
8163
|
+
"## AI \u8FC1\u79FB\u63D0\u793A",
|
|
8164
|
+
"",
|
|
8165
|
+
"- `ctx`\uFF08adep\uFF09\u2194 Workers `Request`\uFF1A`ctx.req` \u5373 Request \u5F62\u72B6\uFF0C`ctx.body`/`ctx.query`/`ctx.headers` \u5DF2\u63D0\u4F9B",
|
|
8166
|
+
"- `env.XXX` \u7ED1\u5B9A\u5728\u51FD\u6570\u5185\u7ECF cfEnv \u7EC4\u88C5\uFF08vars=process.env\u3001KV/D1/R2 \u4E3A shim \u5B9E\u4F8B\uFF09",
|
|
8167
|
+
"- Response \u8FD4\u56DE\u7ECF `__adepHttp` \u4FE1\u5C01\uFF0Cstatus/headers/body \u5168\u4FDD\u7559",
|
|
8168
|
+
"- \u591A\u6587\u4EF6 worker\uFF1A\u4F9D\u8D56\u6A21\u5757\u5DF2\u968F\u76EE\u5F55\u590D\u5236\u8FDB\u51FD\u6570\u76EE\u5F55\uFF0C\u4FDD\u6301\u76F8\u5BF9 import \u5373\u53EF",
|
|
8169
|
+
"- \u4E91\u51FD\u6570\u9ED8\u8BA4\u540C\u6B65\u6267\u884C\u6A21\u578B\uFF1A`ctx.waitUntil` \u4E3A\u7A7A\u5B9E\u73B0\uFF08v1\uFF09",
|
|
8170
|
+
"",
|
|
8171
|
+
"## Durable Objects \u8FC1\u79FB\u8981\u70B9\uFF08CF-039\uFF09",
|
|
8172
|
+
"",
|
|
8173
|
+
"- `this.state`\uFF08DO storage\uFF09\u2192 StateObject \u76F4\u63A5\u8BFB\u5199 `this.state` \u5B57\u6BB5 + `await this.flush()` \u6301\u4E45\u5316",
|
|
8174
|
+
"- `this.ctx.storage.setAlarm(t)` \u2192 `this.setAlarm(t)`\uFF1B`this.ctx.storage.getAlarm()` \u2192 alarm \u67E5\u8BE2\u9762\uFF08CF-037\uFF09",
|
|
8175
|
+
"- `this.ctx.waitUntil(p)` \u2192 adep \u540C\u6B65\u6267\u884C\u6A21\u578B\uFF0C\u7A7A\u5B9E\u73B0\uFF08v1\uFF09",
|
|
8176
|
+
"- `stub.fetch(req)` \u8C03\u7528\u65B9 \u2192 `fetch('/api/state/<ClassName>/<id>/<path>')` \u6216 WS `state:<ClassName>:<id>` \u6216 MCP `state.invoke`",
|
|
8177
|
+
"- `env.<BINDING>.idFromName(name)` \u2192 \u5BF9\u8C61 id \u5373\u8C03\u7528\u8DEF\u5F84\u7684 `<id>` \u6BB5",
|
|
8178
|
+
"- `[[migrations]]` \u4E3A CF \u7AEF DO \u7248\u672C\u7BA1\u7406\uFF0Cadep \u65E0\u5BF9\u5E94\u6982\u5FF5\uFF08\u5FFD\u7565\uFF09"
|
|
8179
|
+
];
|
|
8180
|
+
return `${lines.filter((line) => line !== "").join("\n")}
|
|
8181
|
+
`;
|
|
8182
|
+
}
|
|
8183
|
+
function renderImportPackageJson(name) {
|
|
8184
|
+
return `${JSON.stringify(
|
|
8185
|
+
{
|
|
8186
|
+
name,
|
|
8187
|
+
private: true,
|
|
8188
|
+
scripts: {
|
|
8189
|
+
dev: "adep dev",
|
|
8190
|
+
serve: "adep serve",
|
|
8191
|
+
deploy: "adep deploy",
|
|
8192
|
+
doctor: "adep doctor"
|
|
8193
|
+
},
|
|
8194
|
+
dependencies: {
|
|
8195
|
+
"@adep/cli": "latest",
|
|
8196
|
+
"@adep/cf-compat": "latest"
|
|
8197
|
+
}
|
|
8198
|
+
},
|
|
8199
|
+
null,
|
|
8200
|
+
2
|
|
8201
|
+
)}
|
|
8202
|
+
`;
|
|
8203
|
+
}
|
|
8204
|
+
async function importCfProject(cwd, sourceDir, options = {}) {
|
|
8205
|
+
let tomlText;
|
|
8206
|
+
try {
|
|
8207
|
+
tomlText = await readFile2(join2(sourceDir, "wrangler.toml"), "utf8");
|
|
8208
|
+
} catch {
|
|
8209
|
+
throw new ImportCfError(
|
|
8210
|
+
"NO_WRANGLER",
|
|
8211
|
+
`\u627E\u4E0D\u5230 ${join2(sourceDir, "wrangler.toml")}\uFF1A\u9700\u5728 Cloudflare \u9879\u76EE\u6839\u6267\u884C\u5BFC\u5165`
|
|
8212
|
+
);
|
|
8213
|
+
}
|
|
8214
|
+
let config;
|
|
8215
|
+
try {
|
|
8216
|
+
config = parseWranglerToml(tomlText);
|
|
8217
|
+
} catch (error) {
|
|
8218
|
+
throw new ImportCfError(
|
|
8219
|
+
"TOML_PARSE",
|
|
8220
|
+
`wrangler.toml \u89E3\u6790\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`
|
|
8221
|
+
);
|
|
8222
|
+
}
|
|
8223
|
+
const workerName = slugify(config.name ?? basename(sourceDir), "worker");
|
|
8224
|
+
const outDir = resolve2(cwd, options.out ?? `imported-${workerName}`);
|
|
8225
|
+
try {
|
|
8226
|
+
await stat3(outDir);
|
|
8227
|
+
throw new ImportCfError("DIR_EXISTS", `\u8F93\u51FA\u76EE\u5F55 ${outDir} \u5DF2\u5B58\u5728\uFF1A\u8BF7\u6362 --out \u6216\u5148\u5220\u9664`);
|
|
8228
|
+
} catch (error) {
|
|
8229
|
+
if (error instanceof ImportCfError) throw error;
|
|
8230
|
+
}
|
|
8231
|
+
const { cfBindings, environment } = bindingsFromWrangler(config);
|
|
8232
|
+
const devVars = await readDevVars(sourceDir);
|
|
8233
|
+
const mergedEnv = environment === void 0 && Object.keys(devVars).length === 0 ? void 0 : { ...environment, ...devVars };
|
|
8234
|
+
const files = [];
|
|
8235
|
+
const warnings = [];
|
|
8236
|
+
const adepConfig = {
|
|
8237
|
+
name: workerName,
|
|
8238
|
+
functionsDir: "functions",
|
|
8239
|
+
functions_prefix: "/api",
|
|
8240
|
+
...mergedEnv === void 0 ? {} : { environment: mergedEnv },
|
|
8241
|
+
...cfBindings === void 0 ? {} : { cfBindings }
|
|
8242
|
+
};
|
|
8243
|
+
const configText = renderAdepConfig(adepConfig, {
|
|
8244
|
+
header: "// \u7531 adep import cf \u751F\u6210\uFF08CF-015\uFF09\uFF1A\u7ED1\u5B9A\u58F0\u660E\u4E0E wrangler.toml \u5BF9\u5E94"
|
|
8245
|
+
});
|
|
8246
|
+
await mkdir3(outDir, { recursive: true });
|
|
8247
|
+
await writeFile2(join2(outDir, "adep.config.ts"), configText, "utf8");
|
|
8248
|
+
files.push("adep.config.ts");
|
|
8249
|
+
await writeFile2(join2(outDir, "package.json"), renderImportPackageJson(workerName), "utf8");
|
|
8250
|
+
files.push("package.json");
|
|
8251
|
+
let mainCopied = false;
|
|
8252
|
+
if (config.main !== void 0) {
|
|
8253
|
+
const mainAbs = resolve2(sourceDir, config.main);
|
|
8254
|
+
let mainStat;
|
|
8255
|
+
try {
|
|
8256
|
+
mainStat = await stat3(mainAbs);
|
|
8257
|
+
} catch {
|
|
8258
|
+
throw new ImportCfError("MAIN_MISSING", `main \u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${config.main}`);
|
|
8259
|
+
}
|
|
8260
|
+
if (!mainStat.isFile()) {
|
|
8261
|
+
throw new ImportCfError("MAIN_NOT_FILE", `main \u4E0D\u662F\u6587\u4EF6\uFF1A${config.main}`);
|
|
8262
|
+
}
|
|
8263
|
+
const fnDir = join2(outDir, "functions", workerName);
|
|
8264
|
+
await mkdir3(fnDir, { recursive: true });
|
|
8265
|
+
await copyFile(mainAbs, join2(fnDir, "worker.ts"));
|
|
8266
|
+
files.push(`functions/${workerName}/worker.ts`);
|
|
8267
|
+
mainCopied = true;
|
|
8268
|
+
const mainDir = dirname3(mainAbs);
|
|
8269
|
+
const { copied, renamedIndex } = await copyWorkerDir(mainDir, fnDir, mainAbs);
|
|
8270
|
+
if (renamedIndex.length > 0) {
|
|
8271
|
+
warnings.push(
|
|
8272
|
+
`\u51FD\u6570\u76EE\u5F55\u539F\u6709 index.ts \u5DF2\u6539\u540D\u4E3A _index.ts\uFF08\u5165\u53E3\u88AB\u5305\u88C5\u5360\u7528\uFF09\uFF1A${renamedIndex.join(", ")}`
|
|
8273
|
+
);
|
|
8274
|
+
}
|
|
8275
|
+
files.push(...copied.map((f) => `functions/${workerName}/${f}`));
|
|
8276
|
+
if (copied.length > 0) {
|
|
8277
|
+
warnings.push(`\u5DF2\u590D\u5236\u540C\u76EE\u5F55\u4F9D\u8D56\u6A21\u5757 ${copied.length} \u4E2A\uFF08\u4FDD\u6301\u76F8\u5BF9 import\uFF09`);
|
|
8278
|
+
}
|
|
8279
|
+
await writeFile2(join2(fnDir, "index.ts"), renderFunctionEntry("worker", cfBindings), "utf8");
|
|
8280
|
+
files.push(`functions/${workerName}/index.ts`);
|
|
8281
|
+
}
|
|
8282
|
+
let dosMigrated = 0;
|
|
8283
|
+
if (mainCopied) {
|
|
8284
|
+
const doBindings = collectDurableObjectBindings(config);
|
|
8285
|
+
const noClassName = (config.durable_objects?.bindings ?? []).filter(
|
|
8286
|
+
(b) => b.class_name === void 0
|
|
8287
|
+
);
|
|
8288
|
+
if (noClassName.length > 0) {
|
|
8289
|
+
warnings.push(
|
|
8290
|
+
`durable_objects \u7ED1\u5B9A\u7F3A\u5C11 class_name\uFF08\u65E0\u6CD5\u751F\u6210\u58F0\u660E\uFF09\uFF1A${noClassName.map((b) => b.name).join(", ")}`
|
|
8291
|
+
);
|
|
8292
|
+
}
|
|
8293
|
+
if (doBindings.length > 0) {
|
|
8294
|
+
const fnDir = join2(outDir, "functions", workerName);
|
|
8295
|
+
const workerPath = join2(fnDir, "worker.ts");
|
|
8296
|
+
const workerText = await readFile2(workerPath, "utf8");
|
|
8297
|
+
const { text: stripped, removed } = stripDurableObjectClasses(
|
|
8298
|
+
workerText,
|
|
8299
|
+
doBindings.map((b) => b.className)
|
|
8300
|
+
);
|
|
8301
|
+
await writeFile2(workerPath, stripped, "utf8");
|
|
8302
|
+
if (removed.length > 0) {
|
|
8303
|
+
await mkdir3(join2(fnDir, "durable-objects"), { recursive: true });
|
|
8304
|
+
await Promise.all(
|
|
8305
|
+
removed.map(async (r) => {
|
|
8306
|
+
await writeFile2(
|
|
8307
|
+
join2(fnDir, "durable-objects", `${r.className}.ts`),
|
|
8308
|
+
`${r.source}
|
|
8309
|
+
`,
|
|
8310
|
+
"utf8"
|
|
8311
|
+
);
|
|
8312
|
+
files.push(`functions/${workerName}/durable-objects/${r.className}.ts`);
|
|
8313
|
+
})
|
|
8314
|
+
);
|
|
8315
|
+
}
|
|
8316
|
+
if (removed.length > 0) {
|
|
8317
|
+
await writeFile2(
|
|
8318
|
+
join2(fnDir, "state-objects.ts"),
|
|
8319
|
+
renderStateObjectDeclaration(doBindings, removed),
|
|
8320
|
+
"utf8"
|
|
8321
|
+
);
|
|
8322
|
+
files.push(`functions/${workerName}/state-objects.ts`);
|
|
8323
|
+
dosMigrated = removed.length;
|
|
8324
|
+
}
|
|
8325
|
+
if (removed.length < doBindings.length) {
|
|
8326
|
+
warnings.push(
|
|
8327
|
+
`\u4EE5\u4E0B DO \u7C7B\u672A\u5728 main \u6587\u4EF6\u4E2D\u627E\u5230\uFF08\u53EF\u80FD\u5728\u5176\u4ED6\u6A21\u5757\uFF0C\u9700\u624B\u52A8\u8FC1\u79FB\uFF09\uFF1A${doBindings.filter((b) => !removed.some((r) => r.className === b.className)).map((b) => b.className).join(", ")}`
|
|
8328
|
+
);
|
|
8329
|
+
}
|
|
8330
|
+
}
|
|
8331
|
+
}
|
|
8332
|
+
let assetsCopied = false;
|
|
8333
|
+
const assetsDir = config.assets?.directory ?? config.site?.bucket;
|
|
8334
|
+
if (assetsDir !== void 0) {
|
|
8335
|
+
try {
|
|
8336
|
+
const copied = await copyAssetsToWeb(sourceDir, assetsDir, outDir);
|
|
8337
|
+
assetsCopied = true;
|
|
8338
|
+
files.push(...copied);
|
|
8339
|
+
} catch (error) {
|
|
8340
|
+
warnings.push(
|
|
8341
|
+
`\u9759\u6001\u8D44\u6E90\u76EE\u5F55 ${assetsDir} \u590D\u5236\u5931\u8D25\uFF08${error instanceof Error ? error.message : String(error)}\uFF09\uFF0C\u8DF3\u8FC7`
|
|
8342
|
+
);
|
|
8343
|
+
}
|
|
8344
|
+
}
|
|
8345
|
+
const mapping = {
|
|
8346
|
+
vars: Object.keys(mergedEnv ?? {}).length,
|
|
8347
|
+
kv: config.kv_namespaces?.length ?? 0,
|
|
8348
|
+
d1: config.d1_databases?.length ?? 0,
|
|
8349
|
+
r2: config.r2_buckets?.length ?? 0,
|
|
8350
|
+
dos: dosMigrated,
|
|
8351
|
+
crons: config.triggers?.crons ?? [],
|
|
8352
|
+
assets: assetsCopied,
|
|
8353
|
+
secretsFromDevVars: Object.keys(devVars).length > 0
|
|
8354
|
+
};
|
|
8355
|
+
const unsupported = unsupportedFromWrangler(config);
|
|
8356
|
+
const migration = renderMigrationReport(config, {
|
|
8357
|
+
outDir,
|
|
8358
|
+
workerName,
|
|
8359
|
+
mapping,
|
|
8360
|
+
unsupported,
|
|
8361
|
+
warnings
|
|
8362
|
+
});
|
|
8363
|
+
await writeFile2(join2(outDir, "MIGRATION.md"), migration, "utf8");
|
|
8364
|
+
files.push("MIGRATION.md");
|
|
8365
|
+
if (!mainCopied) {
|
|
8366
|
+
warnings.push("wrangler.toml \u672A\u58F0\u660E main\uFF1A\u672A\u751F\u6210\u51FD\u6570\uFF0C\u4EC5\u751F\u6210\u914D\u7F6E\u4E0E\u62A5\u544A");
|
|
8367
|
+
}
|
|
8368
|
+
return { outDir, workerName, mapping, unsupported, files, warnings };
|
|
8369
|
+
}
|
|
8370
|
+
|
|
8371
|
+
// packages/cli/src/commands/import.ts
|
|
8372
|
+
function registerImport(program2, ctx) {
|
|
8373
|
+
const importCmd = program2.command("import").description("\u8FC1\u79FB\u5DE5\u5177\uFF1A\u4ECE Cloudflare / \u5176\u4ED6\u5E73\u53F0\u5BFC\u5165\u9879\u76EE\uFF08\u5F53\u524D\u652F\u6301 cf\uFF09");
|
|
8374
|
+
const cfCmd = importCmd.command("cf").description("\u4ECE Cloudflare Workers \u9879\u76EE\u751F\u6210 adep \u9AA8\u67B6\uFF08wrangler.toml \u2192 \u51FD\u6570 + \u7ED1\u5B9A\u58F0\u660E\uFF09");
|
|
8375
|
+
cfCmd.argument("<sourceDir>", "Cloudflare \u9879\u76EE\u6839\uFF08\u542B wrangler.toml\uFF09").option("-o, --out <dir>", "\u8F93\u51FA\u76EE\u5F55\uFF08\u7F3A\u7701 cwd/imported-<workerName>\uFF09").action(async (sourceDir, flags) => {
|
|
8376
|
+
const command = ctx.io("import cf");
|
|
8377
|
+
await command.run(async () => {
|
|
8378
|
+
const options = {};
|
|
8379
|
+
if (flags.out !== void 0) options.out = flags.out;
|
|
8380
|
+
const result = await importCfProject(ctx.cwd, sourceDir, options);
|
|
8381
|
+
command.ok(
|
|
8382
|
+
{
|
|
8383
|
+
outDir: result.outDir,
|
|
8384
|
+
workerName: result.workerName,
|
|
8385
|
+
mapping: result.mapping,
|
|
8386
|
+
unsupported: result.unsupported,
|
|
8387
|
+
warnings: result.warnings,
|
|
8388
|
+
files: result.files
|
|
8389
|
+
},
|
|
8390
|
+
(data) => {
|
|
8391
|
+
const d = data;
|
|
8392
|
+
const lines = [
|
|
8393
|
+
`\u5DF2\u5BFC\u5165 ${d.workerName} \u2192 ${d.outDir}`,
|
|
8394
|
+
`\u6620\u5C04\uFF1Avars ${d.mapping.vars} \xB7 KV ${d.mapping.kv} \xB7 D1 ${d.mapping.d1} \xB7 R2 ${d.mapping.r2}` + (d.mapping.crons.length > 0 ? ` \xB7 cron ${d.mapping.crons.join(",")}` : "") + (d.mapping.assets ? " \xB7 assets\u2192web/" : ""),
|
|
8395
|
+
...d.unsupported.length > 0 ? [`\u672A\u652F\u6301\uFF1A${d.unsupported.length} \u9879\uFF08\u8BE6\u89C1 MIGRATION.md\uFF09`, ...d.unsupported] : [],
|
|
8396
|
+
...d.warnings.map((w) => `\u63D0\u793A\uFF1A${w}`),
|
|
8397
|
+
`\u8FC1\u79FB\u62A5\u544A\uFF1A${d.outDir}/MIGRATION.md`
|
|
8398
|
+
];
|
|
8399
|
+
return lines.join("\n");
|
|
8400
|
+
}
|
|
8401
|
+
);
|
|
8402
|
+
});
|
|
8403
|
+
});
|
|
8404
|
+
}
|
|
8405
|
+
|
|
6166
8406
|
// packages/cli/src/commands/dev.ts
|
|
6167
8407
|
function registerDevServers(program2, ctx) {
|
|
6168
8408
|
program2.command("dev").description("\u672C\u5730\u8C03\u8BD5\uFF1A\u76D1\u542C functions/ \u70ED\u91CD\u8F7D\uFF0Chttp://localhost:<port>/<fnName>").option("-p, --port <port>", "\u76D1\u542C\u7AEF\u53E3\uFF08\u7F3A\u7701 8787\uFF09", "8787").action(async (flags) => {
|
|
@@ -6205,9 +8445,9 @@ function registerDevServers(program2, ctx) {
|
|
|
6205
8445
|
init_auth();
|
|
6206
8446
|
init_client();
|
|
6207
8447
|
init_deploy();
|
|
6208
|
-
import { resolve as
|
|
8448
|
+
import { resolve as resolve7 } from "node:path";
|
|
6209
8449
|
async function resolveProjectId(client, cwd, slug) {
|
|
6210
|
-
const resolvedSlug = await resolveSlug(
|
|
8450
|
+
const resolvedSlug = await resolveSlug(resolve7(cwd), slug);
|
|
6211
8451
|
const listed = await client.request("/api/v1/projects", {}, "PROJECT_LIST_FAILED");
|
|
6212
8452
|
const match = listed.projects.find((project2) => project2.slug === resolvedSlug);
|
|
6213
8453
|
if (match === void 0) {
|
|
@@ -6858,7 +9098,7 @@ function registerHosting(program2, ctx) {
|
|
|
6858
9098
|
}
|
|
6859
9099
|
|
|
6860
9100
|
// packages/cli/src/cli.ts
|
|
6861
|
-
var requireJson2 =
|
|
9101
|
+
var requireJson2 = createRequire3(import.meta.url);
|
|
6862
9102
|
var APP_VERSION = requireJson2("../package.json").version;
|
|
6863
9103
|
function buildProgram(options = {}) {
|
|
6864
9104
|
const output = options.output ?? ((line) => process.stdout.write(`${line}
|
|
@@ -6871,6 +9111,7 @@ function buildProgram(options = {}) {
|
|
|
6871
9111
|
const jsonMode = () => program2.opts()["json"] === true;
|
|
6872
9112
|
const ctx = createCliContext({ output, paths, cwd, json: jsonMode });
|
|
6873
9113
|
registerInit(program2, ctx);
|
|
9114
|
+
registerImport(program2, ctx);
|
|
6874
9115
|
registerDevServers(program2, ctx);
|
|
6875
9116
|
registerPublish(program2, ctx);
|
|
6876
9117
|
registerExport(program2, ctx);
|