@faapi/faapi 6.2.0 → 6.3.0
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/cli/index.js +1269 -517
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +116 -3
- package/dist/index.js +943 -252
- package/dist/index.js.map +1 -1
- package/dist/{routeTypes-Bm--uTbm.d.ts → routeTypes-CCveqSnY.d.ts} +214 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +48 -2
- package/dist/testing.js.map +1 -1
- package/package.json +2 -1
package/dist/cli/index.js
CHANGED
|
@@ -649,9 +649,9 @@ var init_constants = __esm({
|
|
|
649
649
|
});
|
|
650
650
|
|
|
651
651
|
// src/utils/normalizePath.ts
|
|
652
|
-
function normalizePath(
|
|
653
|
-
if (!
|
|
654
|
-
let result =
|
|
652
|
+
function normalizePath(path31) {
|
|
653
|
+
if (!path31) return "";
|
|
654
|
+
let result = path31.replace(/\\/g, "/");
|
|
655
655
|
result = result.replace(/\/+/g, "/");
|
|
656
656
|
result = result.replace(/\/+$/, "");
|
|
657
657
|
if (result && !result.startsWith("/")) {
|
|
@@ -2258,8 +2258,10 @@ var init_resolveInjection = __esm({
|
|
|
2258
2258
|
fields: "fields",
|
|
2259
2259
|
agent: "agent",
|
|
2260
2260
|
// Phase 2.3
|
|
2261
|
-
agents: "agents"
|
|
2261
|
+
agents: "agents",
|
|
2262
2262
|
// Phase 2.3
|
|
2263
|
+
tasks: "tasks"
|
|
2264
|
+
// 任务子系统:TaskClient(入队/查询)
|
|
2263
2265
|
};
|
|
2264
2266
|
injectionCache = /* @__PURE__ */ new WeakMap();
|
|
2265
2267
|
}
|
|
@@ -3370,16 +3372,498 @@ var init_generateAgentArtifacts = __esm({
|
|
|
3370
3372
|
}
|
|
3371
3373
|
});
|
|
3372
3374
|
|
|
3375
|
+
// src/task/scanTasks.ts
|
|
3376
|
+
import fg4 from "fast-glob";
|
|
3377
|
+
import path16 from "path";
|
|
3378
|
+
import fs12 from "fs";
|
|
3379
|
+
function filePathToTaskName(filePath) {
|
|
3380
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
3381
|
+
const match = normalized.match(/(?:^|\/)tasks\/(.+)\/task\.ts$/);
|
|
3382
|
+
if (!match) {
|
|
3383
|
+
throw new Error(`[faapi] Invalid task file path: ${filePath}`);
|
|
3384
|
+
}
|
|
3385
|
+
return match[1].split("/").join(".");
|
|
3386
|
+
}
|
|
3387
|
+
async function scanTasks(rootDir, patterns) {
|
|
3388
|
+
const files = await fg4(patterns, {
|
|
3389
|
+
cwd: rootDir,
|
|
3390
|
+
onlyFiles: true,
|
|
3391
|
+
absolute: false
|
|
3392
|
+
});
|
|
3393
|
+
const tasks = [];
|
|
3394
|
+
const seen = /* @__PURE__ */ new Map();
|
|
3395
|
+
for (const file of files) {
|
|
3396
|
+
const normalizedFile = file.replace(/\\/g, "/");
|
|
3397
|
+
const fileName = path16.posix.basename(normalizedFile);
|
|
3398
|
+
if (fileName !== TASK_FILENAME) {
|
|
3399
|
+
continue;
|
|
3400
|
+
}
|
|
3401
|
+
const absPath = path16.resolve(rootDir, normalizedFile);
|
|
3402
|
+
const source = await fs12.promises.readFile(absPath, "utf8").catch(() => "");
|
|
3403
|
+
const name = filePathToTaskName(normalizedFile);
|
|
3404
|
+
const prevFile = seen.get(name);
|
|
3405
|
+
if (prevFile) {
|
|
3406
|
+
throw new Error(
|
|
3407
|
+
`Task conflict: "${name}" declared in both ${prevFile} and ${normalizedFile}`
|
|
3408
|
+
);
|
|
3409
|
+
}
|
|
3410
|
+
seen.set(name, normalizedFile);
|
|
3411
|
+
const manifest = { name, filePath: normalizedFile };
|
|
3412
|
+
const cron = CRON_RE.exec(source)?.[1];
|
|
3413
|
+
const concurrency = CONCURRENCY_RE.exec(source)?.[1];
|
|
3414
|
+
const retries = RETRIES_RE.exec(source)?.[1];
|
|
3415
|
+
if (cron !== void 0) manifest.cron = cron;
|
|
3416
|
+
if (concurrency !== void 0) manifest.concurrency = Number(concurrency);
|
|
3417
|
+
if (retries !== void 0) manifest.retries = Number(retries);
|
|
3418
|
+
tasks.push(manifest);
|
|
3419
|
+
}
|
|
3420
|
+
return tasks;
|
|
3421
|
+
}
|
|
3422
|
+
var TASK_PATTERNS, TASK_FILENAME, CRON_RE, CONCURRENCY_RE, RETRIES_RE;
|
|
3423
|
+
var init_scanTasks = __esm({
|
|
3424
|
+
"src/task/scanTasks.ts"() {
|
|
3425
|
+
"use strict";
|
|
3426
|
+
TASK_PATTERNS = ["src/tasks/**/task.ts"];
|
|
3427
|
+
TASK_FILENAME = "task.ts";
|
|
3428
|
+
CRON_RE = /(?:^|\n)\s*cron:\s*['"`]([^'"`\n]+)['"`]/;
|
|
3429
|
+
CONCURRENCY_RE = /(?:^|\n)\s*concurrency:\s*(\d+)/;
|
|
3430
|
+
RETRIES_RE = /(?:^|\n)\s*retries:\s*(\d+)/;
|
|
3431
|
+
}
|
|
3432
|
+
});
|
|
3433
|
+
|
|
3434
|
+
// src/cli/generateTaskArtifacts.ts
|
|
3435
|
+
import path17 from "path";
|
|
3436
|
+
function serializeTasks(tasks, dist = "dist") {
|
|
3437
|
+
return tasks.map((t) => ({
|
|
3438
|
+
name: t.name,
|
|
3439
|
+
filePath: toProdFilePath(t.filePath, dist),
|
|
3440
|
+
...t.cron !== void 0 ? { cron: t.cron } : {},
|
|
3441
|
+
...t.concurrency !== void 0 ? { concurrency: t.concurrency } : {},
|
|
3442
|
+
...t.retries !== void 0 ? { retries: t.retries } : {}
|
|
3443
|
+
}));
|
|
3444
|
+
}
|
|
3445
|
+
async function writeTasksModule(manifest, outputPath) {
|
|
3446
|
+
const content = `// \u81EA\u52A8\u751F\u6210,\u8BF7\u52FF\u624B\u52A8\u7F16\u8F91(faapi build/dev \u4EA7\u7269)
|
|
3447
|
+
export const tasks = ${JSON.stringify(manifest, null, 2)};
|
|
3448
|
+
`;
|
|
3449
|
+
await atomicWriteFile(outputPath, content);
|
|
3450
|
+
}
|
|
3451
|
+
function hydrateTasks(manifest) {
|
|
3452
|
+
return manifest.map((t) => ({
|
|
3453
|
+
name: t.name,
|
|
3454
|
+
filePath: t.filePath,
|
|
3455
|
+
cron: t.cron ?? void 0,
|
|
3456
|
+
concurrency: t.concurrency ?? void 0,
|
|
3457
|
+
retries: t.retries ?? void 0
|
|
3458
|
+
}));
|
|
3459
|
+
}
|
|
3460
|
+
function collectTaskSchemaSources(tasks, rootDir) {
|
|
3461
|
+
const tasksByFile = /* @__PURE__ */ new Map();
|
|
3462
|
+
for (const task of tasks) {
|
|
3463
|
+
if (!task.inputTypeName) continue;
|
|
3464
|
+
const absPath = path17.resolve(rootDir, task.filePath);
|
|
3465
|
+
let list = tasksByFile.get(absPath);
|
|
3466
|
+
if (!list) {
|
|
3467
|
+
list = [];
|
|
3468
|
+
tasksByFile.set(absPath, list);
|
|
3469
|
+
}
|
|
3470
|
+
list.push(task);
|
|
3471
|
+
}
|
|
3472
|
+
const programByFile = createPrograms([...tasksByFile.keys()]);
|
|
3473
|
+
const resolversByFile = /* @__PURE__ */ new Map();
|
|
3474
|
+
for (const filePath of tasksByFile.keys()) {
|
|
3475
|
+
resolversByFile.set(filePath, createLazyTypeResolver(programByFile.get(filePath), filePath));
|
|
3476
|
+
}
|
|
3477
|
+
const sources = [];
|
|
3478
|
+
for (const [filePath, fileTasks] of tasksByFile) {
|
|
3479
|
+
const program = programByFile.get(filePath);
|
|
3480
|
+
for (const task of fileTasks) {
|
|
3481
|
+
const typeInfo = extractTypeInfo(program, filePath, task.inputTypeName);
|
|
3482
|
+
sources.push({
|
|
3483
|
+
name: task.taskName,
|
|
3484
|
+
filePath,
|
|
3485
|
+
schemaName: task.inputTypeName,
|
|
3486
|
+
typeInfo
|
|
3487
|
+
});
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
return { sources, resolversByFile };
|
|
3491
|
+
}
|
|
3492
|
+
function generateTaskSchemaFileSource(sources, resolveType, helpersImportPath) {
|
|
3493
|
+
const lines = ["import { z } from 'zod';"];
|
|
3494
|
+
const schemaBlocks = [];
|
|
3495
|
+
for (const source of sources) {
|
|
3496
|
+
if (!source.typeInfo) continue;
|
|
3497
|
+
const block = [`// task ${source.name} \u2192 ${source.schemaName}`];
|
|
3498
|
+
const schemaCode = generateZodSchemaSource(
|
|
3499
|
+
source.typeInfo,
|
|
3500
|
+
resolveType,
|
|
3501
|
+
source.schemaName,
|
|
3502
|
+
false
|
|
3503
|
+
).replace(/^import \{ z \} from 'zod';\s*\n\s*\n/, "");
|
|
3504
|
+
block.push(schemaCode);
|
|
3505
|
+
block.push("");
|
|
3506
|
+
schemaBlocks.push(block.join("\n"));
|
|
3507
|
+
}
|
|
3508
|
+
const allSchemaCode = schemaBlocks.join("\n");
|
|
3509
|
+
if (helpersImportPath && usesCoerceHelpers(allSchemaCode)) {
|
|
3510
|
+
lines.push(
|
|
3511
|
+
`import { coerceNumber, coerceBoolean, coerceMap, coerceSet } from '${helpersImportPath}';`
|
|
3512
|
+
);
|
|
3513
|
+
}
|
|
3514
|
+
lines.push("");
|
|
3515
|
+
lines.push(...schemaBlocks);
|
|
3516
|
+
return lines.join("\n").replace(/\n+$/, "\n");
|
|
3517
|
+
}
|
|
3518
|
+
async function generateTaskArtifacts(manifests, rootDir, dist) {
|
|
3519
|
+
const metadata = [];
|
|
3520
|
+
if (manifests.length > 0) {
|
|
3521
|
+
const programByFile = createPrograms(manifests.map((m) => path17.resolve(rootDir, m.filePath)));
|
|
3522
|
+
for (const manifest of manifests) {
|
|
3523
|
+
const absPath = path17.resolve(rootDir, manifest.filePath);
|
|
3524
|
+
const result = extractToolMetadata(programByFile.get(absPath), absPath, "run", {
|
|
3525
|
+
name: manifest.name,
|
|
3526
|
+
filePath: manifest.filePath
|
|
3527
|
+
});
|
|
3528
|
+
if (result) {
|
|
3529
|
+
metadata.push({ ...result, taskName: manifest.name });
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
const serialized = serializeTasks(manifests, dist);
|
|
3534
|
+
const tasksPath = path17.resolve(rootDir, dist, TASKS_FILE);
|
|
3535
|
+
await writeTasksModule(serialized, tasksPath);
|
|
3536
|
+
if (metadata.length === 0) {
|
|
3537
|
+
return hydrateTasks(serialized);
|
|
3538
|
+
}
|
|
3539
|
+
const { sources, resolversByFile } = collectTaskSchemaSources(metadata, rootDir);
|
|
3540
|
+
if (sources.length === 0) {
|
|
3541
|
+
return hydrateTasks(serialized);
|
|
3542
|
+
}
|
|
3543
|
+
const sourcesByFile = /* @__PURE__ */ new Map();
|
|
3544
|
+
for (const source of sources) {
|
|
3545
|
+
let list = sourcesByFile.get(source.filePath);
|
|
3546
|
+
if (!list) {
|
|
3547
|
+
list = [];
|
|
3548
|
+
sourcesByFile.set(source.filePath, list);
|
|
3549
|
+
}
|
|
3550
|
+
list.push(source);
|
|
3551
|
+
}
|
|
3552
|
+
const fileEntries = [];
|
|
3553
|
+
for (const [filePath, fileSources] of sourcesByFile) {
|
|
3554
|
+
const relFile = path17.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
3555
|
+
const outputPath = getSchemaOutputPath(relFile, dist, rootDir);
|
|
3556
|
+
const resolver = resolversByFile.get(filePath);
|
|
3557
|
+
let relForDir = relFile;
|
|
3558
|
+
if (relForDir.startsWith("src/")) {
|
|
3559
|
+
relForDir = relForDir.slice(4);
|
|
3560
|
+
}
|
|
3561
|
+
const dirIdx = relForDir.lastIndexOf("/");
|
|
3562
|
+
const zodRelDir = dirIdx >= 0 ? relForDir.slice(0, dirIdx) : "";
|
|
3563
|
+
const helpersImportPath = getHelpersImportPath(zodRelDir);
|
|
3564
|
+
const source = generateTaskSchemaFileSource(
|
|
3565
|
+
fileSources,
|
|
3566
|
+
(name) => resolver?.resolve(name)?.runtimeType,
|
|
3567
|
+
helpersImportPath
|
|
3568
|
+
);
|
|
3569
|
+
fileEntries.push({ outputPath, source });
|
|
3570
|
+
}
|
|
3571
|
+
const allSourceCode = fileEntries.map((e) => e.source).join("\n");
|
|
3572
|
+
if (usesCoerceHelpers(allSourceCode)) {
|
|
3573
|
+
const helpersPath = path17.resolve(rootDir, dist, HELPERS_FILENAME);
|
|
3574
|
+
const { existsSync: existsSync2 } = await import("fs");
|
|
3575
|
+
if (!existsSync2(helpersPath)) {
|
|
3576
|
+
await atomicWriteFile(helpersPath, generateHelpersFileSource());
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
await Promise.all(
|
|
3580
|
+
fileEntries.map(({ outputPath, source }) => atomicWriteFile(outputPath, source))
|
|
3581
|
+
);
|
|
3582
|
+
return hydrateTasks(serialized);
|
|
3583
|
+
}
|
|
3584
|
+
var TASKS_FILE;
|
|
3585
|
+
var init_generateTaskArtifacts = __esm({
|
|
3586
|
+
"src/cli/generateTaskArtifacts.ts"() {
|
|
3587
|
+
"use strict";
|
|
3588
|
+
init_extractToolMetadata();
|
|
3589
|
+
init_createProgram();
|
|
3590
|
+
init_prodPaths();
|
|
3591
|
+
init_atomicWrite();
|
|
3592
|
+
init_generateSchemaFiles();
|
|
3593
|
+
init_extractHandlerTypes();
|
|
3594
|
+
init_generateZodSchema();
|
|
3595
|
+
TASKS_FILE = "faapi-tasks.js";
|
|
3596
|
+
}
|
|
3597
|
+
});
|
|
3598
|
+
|
|
3599
|
+
// src/cli/compileSourceFiles.ts
|
|
3600
|
+
import path18 from "path";
|
|
3601
|
+
import fs13 from "fs";
|
|
3602
|
+
import fg5 from "fast-glob";
|
|
3603
|
+
async function compileSourceFiles(options) {
|
|
3604
|
+
const { rootDir, dist, files, logLevel = "silent", production, atomicWrite } = options;
|
|
3605
|
+
const entryPoints = files ?? await fg5([`${APP_DIR}/**/*.ts`], {
|
|
3606
|
+
cwd: rootDir,
|
|
3607
|
+
onlyFiles: true,
|
|
3608
|
+
absolute: true,
|
|
3609
|
+
ignore: ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts"]
|
|
3610
|
+
});
|
|
3611
|
+
if (entryPoints.length === 0) {
|
|
3612
|
+
return { compiledFiles: [] };
|
|
3613
|
+
}
|
|
3614
|
+
const absDist = path18.resolve(rootDir, dist);
|
|
3615
|
+
await fs13.promises.mkdir(absDist, { recursive: true });
|
|
3616
|
+
const plugins = buildAliasPlugins(rootDir);
|
|
3617
|
+
const esbuild = await import("esbuild");
|
|
3618
|
+
const outbase = path18.resolve(rootDir, APP_DIR);
|
|
3619
|
+
const result = await esbuild.build({
|
|
3620
|
+
entryPoints,
|
|
3621
|
+
outdir: absDist,
|
|
3622
|
+
outbase,
|
|
3623
|
+
bundle: false,
|
|
3624
|
+
platform: "node",
|
|
3625
|
+
format: "esm",
|
|
3626
|
+
sourcemap: true,
|
|
3627
|
+
packages: "external",
|
|
3628
|
+
plugins,
|
|
3629
|
+
// build 语义:编译期 NODE_ENV 替换 + 死分支删除(见 AGENTS.md §5.3)
|
|
3630
|
+
...production ? { define: { "process.env.NODE_ENV": '"production"' }, minifySyntax: true } : {},
|
|
3631
|
+
logLevel,
|
|
3632
|
+
// dev 语义:esbuild 返回内存内容,由下方原子写落盘
|
|
3633
|
+
...atomicWrite ? { write: false } : {}
|
|
3634
|
+
});
|
|
3635
|
+
if (atomicWrite && result.outputFiles) {
|
|
3636
|
+
await Promise.all(
|
|
3637
|
+
result.outputFiles.map(async (file) => {
|
|
3638
|
+
await fs13.promises.mkdir(path18.dirname(file.path), { recursive: true });
|
|
3639
|
+
const tmp = `${file.path}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
3640
|
+
await fs13.promises.writeFile(tmp, file.contents);
|
|
3641
|
+
await fs13.promises.rename(tmp, file.path);
|
|
3642
|
+
})
|
|
3643
|
+
);
|
|
3644
|
+
}
|
|
3645
|
+
return { compiledFiles: entryPoints };
|
|
3646
|
+
}
|
|
3647
|
+
var init_compileSourceFiles = __esm({
|
|
3648
|
+
"src/cli/compileSourceFiles.ts"() {
|
|
3649
|
+
"use strict";
|
|
3650
|
+
init_aliasPlugin();
|
|
3651
|
+
init_prodPaths();
|
|
3652
|
+
}
|
|
3653
|
+
});
|
|
3654
|
+
|
|
3655
|
+
// src/cli/compileDevRoutes.ts
|
|
3656
|
+
async function compileDevRoutes(options) {
|
|
3657
|
+
return compileSourceFiles({ ...options, atomicWrite: true });
|
|
3658
|
+
}
|
|
3659
|
+
var init_compileDevRoutes = __esm({
|
|
3660
|
+
"src/cli/compileDevRoutes.ts"() {
|
|
3661
|
+
"use strict";
|
|
3662
|
+
init_compileSourceFiles();
|
|
3663
|
+
}
|
|
3664
|
+
});
|
|
3665
|
+
|
|
3666
|
+
// src/cli/compileOnDemand.ts
|
|
3667
|
+
import path19 from "path";
|
|
3668
|
+
import fs14 from "fs";
|
|
3669
|
+
function isProductFresh(sourceAbsPath, productAbsPath) {
|
|
3670
|
+
try {
|
|
3671
|
+
const srcStat = fs14.statSync(sourceAbsPath);
|
|
3672
|
+
const prodStat = fs14.statSync(productAbsPath);
|
|
3673
|
+
return prodStat.mtimeMs >= srcStat.mtimeMs;
|
|
3674
|
+
} catch {
|
|
3675
|
+
return false;
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
function createDevOnDemandState() {
|
|
3679
|
+
return {
|
|
3680
|
+
enabled: false,
|
|
3681
|
+
distDir: void 0,
|
|
3682
|
+
compiledFiles: /* @__PURE__ */ new Set(),
|
|
3683
|
+
generatedSchemas: /* @__PURE__ */ new Set(),
|
|
3684
|
+
inFlightCompilations: /* @__PURE__ */ new Map(),
|
|
3685
|
+
inFlightSchemaGenerations: /* @__PURE__ */ new Map()
|
|
3686
|
+
};
|
|
3687
|
+
}
|
|
3688
|
+
function clearCompiledFiles() {
|
|
3689
|
+
state.compiledFiles.clear();
|
|
3690
|
+
state.inFlightCompilations.clear();
|
|
3691
|
+
sourcePathCache.clear();
|
|
3692
|
+
}
|
|
3693
|
+
async function ensureCompiled(sourceAbsPath, rootDir, dist) {
|
|
3694
|
+
const inFlight2 = state.inFlightCompilations.get(sourceAbsPath);
|
|
3695
|
+
if (inFlight2) {
|
|
3696
|
+
await inFlight2.catch(() => {
|
|
3697
|
+
});
|
|
3698
|
+
return false;
|
|
3699
|
+
}
|
|
3700
|
+
if (state.compiledFiles.has(sourceAbsPath)) {
|
|
3701
|
+
return false;
|
|
3702
|
+
}
|
|
3703
|
+
if (!fs14.existsSync(sourceAbsPath)) {
|
|
3704
|
+
return false;
|
|
3705
|
+
}
|
|
3706
|
+
const productPath = prodSourcePathToProductPath(sourceAbsPath, rootDir, dist);
|
|
3707
|
+
if (productPath && isProductFresh(sourceAbsPath, productPath)) {
|
|
3708
|
+
state.compiledFiles.add(sourceAbsPath);
|
|
3709
|
+
return false;
|
|
3710
|
+
}
|
|
3711
|
+
const compilePromise = (async () => {
|
|
3712
|
+
const { insideFiles } = await collectRelativeImports([sourceAbsPath], rootDir);
|
|
3713
|
+
const files = [sourceAbsPath];
|
|
3714
|
+
for (const dep of insideFiles) {
|
|
3715
|
+
if (state.compiledFiles.has(dep)) continue;
|
|
3716
|
+
const depProduct = prodSourcePathToProductPath(dep, rootDir, dist);
|
|
3717
|
+
if (depProduct && isProductFresh(dep, depProduct)) continue;
|
|
3718
|
+
files.push(dep);
|
|
3719
|
+
}
|
|
3720
|
+
await compileDevRoutes({
|
|
3721
|
+
rootDir,
|
|
3722
|
+
dist,
|
|
3723
|
+
files,
|
|
3724
|
+
logLevel: "silent"
|
|
3725
|
+
});
|
|
3726
|
+
for (const file of files) {
|
|
3727
|
+
state.compiledFiles.add(file);
|
|
3728
|
+
}
|
|
3729
|
+
state.compiledFiles.add(sourceAbsPath);
|
|
3730
|
+
})();
|
|
3731
|
+
state.inFlightCompilations.set(sourceAbsPath, compilePromise);
|
|
3732
|
+
try {
|
|
3733
|
+
await compilePromise;
|
|
3734
|
+
return true;
|
|
3735
|
+
} finally {
|
|
3736
|
+
state.inFlightCompilations.delete(sourceAbsPath);
|
|
3737
|
+
}
|
|
3738
|
+
}
|
|
3739
|
+
async function ensureMiddlewaresCompiled(middlewarePaths, rootDir) {
|
|
3740
|
+
if (!isDevOnDemandEnabled() || middlewarePaths.length === 0) return;
|
|
3741
|
+
const dist = getDevDist();
|
|
3742
|
+
if (!dist) return;
|
|
3743
|
+
for (const mwPath of middlewarePaths) {
|
|
3744
|
+
const sourcePath = prodPathToSourcePath(mwPath, rootDir, dist);
|
|
3745
|
+
try {
|
|
3746
|
+
await ensureCompiled(sourcePath, rootDir, dist);
|
|
3747
|
+
} catch (err) {
|
|
3748
|
+
console.error(`[faapi] Failed to compile middleware source ${sourcePath}:`, err);
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
function prodSourcePathToProductPath(sourceAbsPath, rootDir, dist) {
|
|
3753
|
+
const rel = path19.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
|
|
3754
|
+
if (!rel.startsWith("src/")) return null;
|
|
3755
|
+
const relWithoutSrc = rel.slice(4);
|
|
3756
|
+
const jsRel = relWithoutSrc.replace(/\.ts$/, ".js");
|
|
3757
|
+
return path19.resolve(rootDir, dist, jsRel);
|
|
3758
|
+
}
|
|
3759
|
+
function clearGeneratedSchemas() {
|
|
3760
|
+
state.generatedSchemas.clear();
|
|
3761
|
+
state.inFlightSchemaGenerations.clear();
|
|
3762
|
+
}
|
|
3763
|
+
async function ensureSchemaGenerated(schemaPath, routeFilePath, routes, rootDir, dist) {
|
|
3764
|
+
const inFlight2 = state.inFlightSchemaGenerations.get(schemaPath);
|
|
3765
|
+
if (inFlight2) {
|
|
3766
|
+
await inFlight2.catch(() => {
|
|
3767
|
+
});
|
|
3768
|
+
return false;
|
|
3769
|
+
}
|
|
3770
|
+
if (state.generatedSchemas.has(schemaPath)) {
|
|
3771
|
+
return false;
|
|
3772
|
+
}
|
|
3773
|
+
const prodAbsPath = path19.resolve(rootDir, routeFilePath);
|
|
3774
|
+
const sourceAbsPath = prodPathToSourcePath(prodAbsPath, rootDir, dist);
|
|
3775
|
+
if (!fs14.existsSync(sourceAbsPath)) {
|
|
3776
|
+
return false;
|
|
3777
|
+
}
|
|
3778
|
+
if (isProductFresh(sourceAbsPath, schemaPath)) {
|
|
3779
|
+
state.generatedSchemas.add(schemaPath);
|
|
3780
|
+
return false;
|
|
3781
|
+
}
|
|
3782
|
+
const fileRoutes = routes.filter((r) => r.filePath === routeFilePath);
|
|
3783
|
+
if (fileRoutes.length === 0) {
|
|
3784
|
+
return false;
|
|
3785
|
+
}
|
|
3786
|
+
const sourceRelPath = path19.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
|
|
3787
|
+
const sourceRoutes = fileRoutes.map((r) => ({ ...r, filePath: sourceRelPath }));
|
|
3788
|
+
const generatePromise = (async () => {
|
|
3789
|
+
await generateSchemaFiles(sourceRoutes, rootDir, dist);
|
|
3790
|
+
state.generatedSchemas.add(schemaPath);
|
|
3791
|
+
})();
|
|
3792
|
+
state.inFlightSchemaGenerations.set(schemaPath, generatePromise);
|
|
3793
|
+
try {
|
|
3794
|
+
await generatePromise;
|
|
3795
|
+
return true;
|
|
3796
|
+
} finally {
|
|
3797
|
+
state.inFlightSchemaGenerations.delete(schemaPath);
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
async function deleteSchemaFiles(routes, rootDir, dist) {
|
|
3801
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
3802
|
+
for (const route of routes) {
|
|
3803
|
+
const schemaPath = getRuntimeSchemaPath(route.filePath, dist, rootDir);
|
|
3804
|
+
if (deleted.has(schemaPath)) continue;
|
|
3805
|
+
deleted.add(schemaPath);
|
|
3806
|
+
try {
|
|
3807
|
+
await fs14.promises.unlink(schemaPath);
|
|
3808
|
+
} catch {
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
function prodPathToSourcePath(prodAbsPath, rootDir, dist) {
|
|
3813
|
+
const cached = sourcePathCache.get(prodAbsPath);
|
|
3814
|
+
if (cached) return cached;
|
|
3815
|
+
const rel = path19.relative(rootDir, prodAbsPath).replace(/\\/g, "/");
|
|
3816
|
+
let relWithoutDist = rel;
|
|
3817
|
+
if (relWithoutDist.startsWith(`${dist}/`)) {
|
|
3818
|
+
relWithoutDist = relWithoutDist.slice(dist.length + 1);
|
|
3819
|
+
}
|
|
3820
|
+
const srcRel = `src/${relWithoutDist}`;
|
|
3821
|
+
const tsRel = srcRel.replace(/\.js$/, ".ts");
|
|
3822
|
+
const tsAbs = path19.resolve(rootDir, tsRel);
|
|
3823
|
+
let result;
|
|
3824
|
+
if (fs14.existsSync(tsAbs)) {
|
|
3825
|
+
result = tsAbs;
|
|
3826
|
+
} else {
|
|
3827
|
+
result = path19.resolve(rootDir, srcRel);
|
|
3828
|
+
}
|
|
3829
|
+
sourcePathCache.set(prodAbsPath, result);
|
|
3830
|
+
return result;
|
|
3831
|
+
}
|
|
3832
|
+
function setDevOnDemandEnabled(enabled) {
|
|
3833
|
+
state.enabled = enabled;
|
|
3834
|
+
}
|
|
3835
|
+
function isDevOnDemandEnabled() {
|
|
3836
|
+
return state.enabled;
|
|
3837
|
+
}
|
|
3838
|
+
function setDevDist(dist) {
|
|
3839
|
+
state.distDir = dist;
|
|
3840
|
+
}
|
|
3841
|
+
function getDevDist() {
|
|
3842
|
+
return state.distDir;
|
|
3843
|
+
}
|
|
3844
|
+
var state, sourcePathCache;
|
|
3845
|
+
var init_compileOnDemand = __esm({
|
|
3846
|
+
"src/cli/compileOnDemand.ts"() {
|
|
3847
|
+
"use strict";
|
|
3848
|
+
init_compileDevRoutes();
|
|
3849
|
+
init_generateSchemaFiles();
|
|
3850
|
+
init_generateSchemaFiles();
|
|
3851
|
+
init_collectImports();
|
|
3852
|
+
state = createDevOnDemandState();
|
|
3853
|
+
sourcePathCache = /* @__PURE__ */ new Map();
|
|
3854
|
+
}
|
|
3855
|
+
});
|
|
3856
|
+
|
|
3373
3857
|
// src/config/loadConfig.ts
|
|
3374
|
-
import
|
|
3375
|
-
import
|
|
3858
|
+
import path20 from "path";
|
|
3859
|
+
import fs15 from "fs";
|
|
3376
3860
|
async function loadConfig(rootDir, dist) {
|
|
3377
|
-
const configProductPath =
|
|
3378
|
-
if (
|
|
3861
|
+
const configProductPath = path20.resolve(rootDir, dist, CONFIG_PRODUCT_FILE);
|
|
3862
|
+
if (fs15.existsSync(configProductPath)) {
|
|
3379
3863
|
const module = await importWithCacheBust(configProductPath);
|
|
3380
3864
|
return module.default ?? {};
|
|
3381
3865
|
}
|
|
3382
|
-
const hasSourceConfig =
|
|
3866
|
+
const hasSourceConfig = fs15.existsSync(path20.join(rootDir, "faapi.config.ts")) || fs15.existsSync(path20.join(rootDir, "faapi.config.js"));
|
|
3383
3867
|
if (hasSourceConfig) {
|
|
3384
3868
|
throw new Error(
|
|
3385
3869
|
`[faapi] ${dist}/${CONFIG_PRODUCT_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
|
|
@@ -3397,8 +3881,8 @@ var init_loadConfig = __esm({
|
|
|
3397
3881
|
});
|
|
3398
3882
|
|
|
3399
3883
|
// src/cli/loadEnv.ts
|
|
3400
|
-
import
|
|
3401
|
-
import
|
|
3884
|
+
import fs16 from "fs";
|
|
3885
|
+
import path21 from "path";
|
|
3402
3886
|
function resolveEnv() {
|
|
3403
3887
|
return process.env.NODE_ENV || "development";
|
|
3404
3888
|
}
|
|
@@ -3465,9 +3949,9 @@ function loadEnv(rootDir) {
|
|
|
3465
3949
|
const files = getEnvFiles(env);
|
|
3466
3950
|
const merged = {};
|
|
3467
3951
|
for (const file of files) {
|
|
3468
|
-
const filePath =
|
|
3469
|
-
if (!
|
|
3470
|
-
const content =
|
|
3952
|
+
const filePath = path21.join(rootDir, file);
|
|
3953
|
+
if (!fs16.existsSync(filePath)) continue;
|
|
3954
|
+
const content = fs16.readFileSync(filePath, "utf-8");
|
|
3471
3955
|
const parsed = parseEnvFile(content, merged);
|
|
3472
3956
|
Object.assign(merged, parsed);
|
|
3473
3957
|
}
|
|
@@ -3573,7 +4057,7 @@ var init_esm = __esm({
|
|
|
3573
4057
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
3574
4058
|
const statMethod = opts.lstat ? lstat : stat;
|
|
3575
4059
|
if (wantBigintFsStats) {
|
|
3576
|
-
this._stat = (
|
|
4060
|
+
this._stat = (path31) => statMethod(path31, { bigint: true });
|
|
3577
4061
|
} else {
|
|
3578
4062
|
this._stat = statMethod;
|
|
3579
4063
|
}
|
|
@@ -3598,8 +4082,8 @@ var init_esm = __esm({
|
|
|
3598
4082
|
const par = this.parent;
|
|
3599
4083
|
const fil = par && par.files;
|
|
3600
4084
|
if (fil && fil.length > 0) {
|
|
3601
|
-
const { path:
|
|
3602
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
4085
|
+
const { path: path31, depth } = par;
|
|
4086
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path31));
|
|
3603
4087
|
const awaited = await Promise.all(slice);
|
|
3604
4088
|
for (const entry of awaited) {
|
|
3605
4089
|
if (!entry)
|
|
@@ -3639,20 +4123,20 @@ var init_esm = __esm({
|
|
|
3639
4123
|
this.reading = false;
|
|
3640
4124
|
}
|
|
3641
4125
|
}
|
|
3642
|
-
async _exploreDir(
|
|
4126
|
+
async _exploreDir(path31, depth) {
|
|
3643
4127
|
let files;
|
|
3644
4128
|
try {
|
|
3645
|
-
files = await readdir(
|
|
4129
|
+
files = await readdir(path31, this._rdOptions);
|
|
3646
4130
|
} catch (error) {
|
|
3647
4131
|
this._onError(error);
|
|
3648
4132
|
}
|
|
3649
|
-
return { files, depth, path:
|
|
4133
|
+
return { files, depth, path: path31 };
|
|
3650
4134
|
}
|
|
3651
|
-
async _formatEntry(dirent,
|
|
4135
|
+
async _formatEntry(dirent, path31) {
|
|
3652
4136
|
let entry;
|
|
3653
4137
|
const basename3 = this._isDirent ? dirent.name : dirent;
|
|
3654
4138
|
try {
|
|
3655
|
-
const fullPath = presolve(pjoin(
|
|
4139
|
+
const fullPath = presolve(pjoin(path31, basename3));
|
|
3656
4140
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
|
|
3657
4141
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
3658
4142
|
} catch (err) {
|
|
@@ -3713,16 +4197,16 @@ import { watchFile, unwatchFile, watch as fs_watch } from "fs";
|
|
|
3713
4197
|
import { open, stat as stat2, lstat as lstat2, realpath as fsrealpath } from "fs/promises";
|
|
3714
4198
|
import * as sysPath from "path";
|
|
3715
4199
|
import { type as osType } from "os";
|
|
3716
|
-
function createFsWatchInstance(
|
|
4200
|
+
function createFsWatchInstance(path31, options, listener, errHandler, emitRaw) {
|
|
3717
4201
|
const handleEvent = (rawEvent, evPath) => {
|
|
3718
|
-
listener(
|
|
3719
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
3720
|
-
if (evPath &&
|
|
3721
|
-
fsWatchBroadcast(sysPath.resolve(
|
|
4202
|
+
listener(path31);
|
|
4203
|
+
emitRaw(rawEvent, evPath, { watchedPath: path31 });
|
|
4204
|
+
if (evPath && path31 !== evPath) {
|
|
4205
|
+
fsWatchBroadcast(sysPath.resolve(path31, evPath), KEY_LISTENERS, sysPath.join(path31, evPath));
|
|
3722
4206
|
}
|
|
3723
4207
|
};
|
|
3724
4208
|
try {
|
|
3725
|
-
return fs_watch(
|
|
4209
|
+
return fs_watch(path31, {
|
|
3726
4210
|
persistent: options.persistent
|
|
3727
4211
|
}, handleEvent);
|
|
3728
4212
|
} catch (error) {
|
|
@@ -4067,12 +4551,12 @@ var init_handler = __esm({
|
|
|
4067
4551
|
listener(val1, val2, val3);
|
|
4068
4552
|
});
|
|
4069
4553
|
};
|
|
4070
|
-
setFsWatchListener = (
|
|
4554
|
+
setFsWatchListener = (path31, fullPath, options, handlers) => {
|
|
4071
4555
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
4072
4556
|
let cont = FsWatchInstances.get(fullPath);
|
|
4073
4557
|
let watcher;
|
|
4074
4558
|
if (!options.persistent) {
|
|
4075
|
-
watcher = createFsWatchInstance(
|
|
4559
|
+
watcher = createFsWatchInstance(path31, options, listener, errHandler, rawEmitter);
|
|
4076
4560
|
if (!watcher)
|
|
4077
4561
|
return;
|
|
4078
4562
|
return watcher.close.bind(watcher);
|
|
@@ -4083,7 +4567,7 @@ var init_handler = __esm({
|
|
|
4083
4567
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
4084
4568
|
} else {
|
|
4085
4569
|
watcher = createFsWatchInstance(
|
|
4086
|
-
|
|
4570
|
+
path31,
|
|
4087
4571
|
options,
|
|
4088
4572
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
4089
4573
|
errHandler,
|
|
@@ -4098,7 +4582,7 @@ var init_handler = __esm({
|
|
|
4098
4582
|
cont.watcherUnusable = true;
|
|
4099
4583
|
if (isWindows && error.code === "EPERM") {
|
|
4100
4584
|
try {
|
|
4101
|
-
const fd = await open(
|
|
4585
|
+
const fd = await open(path31, "r");
|
|
4102
4586
|
await fd.close();
|
|
4103
4587
|
broadcastErr(error);
|
|
4104
4588
|
} catch (err) {
|
|
@@ -4129,7 +4613,7 @@ var init_handler = __esm({
|
|
|
4129
4613
|
};
|
|
4130
4614
|
};
|
|
4131
4615
|
FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
4132
|
-
setFsWatchFileListener = (
|
|
4616
|
+
setFsWatchFileListener = (path31, fullPath, options, handlers) => {
|
|
4133
4617
|
const { listener, rawEmitter } = handlers;
|
|
4134
4618
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
4135
4619
|
const copts = cont && cont.options;
|
|
@@ -4151,7 +4635,7 @@ var init_handler = __esm({
|
|
|
4151
4635
|
});
|
|
4152
4636
|
const currmtime = curr.mtimeMs;
|
|
4153
4637
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
4154
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
4638
|
+
foreach(cont.listeners, (listener2) => listener2(path31, curr));
|
|
4155
4639
|
}
|
|
4156
4640
|
})
|
|
4157
4641
|
};
|
|
@@ -4179,13 +4663,13 @@ var init_handler = __esm({
|
|
|
4179
4663
|
* @param listener on fs change
|
|
4180
4664
|
* @returns closer for the watcher instance
|
|
4181
4665
|
*/
|
|
4182
|
-
_watchWithNodeFs(
|
|
4666
|
+
_watchWithNodeFs(path31, listener) {
|
|
4183
4667
|
const opts = this.fsw.options;
|
|
4184
|
-
const directory = sysPath.dirname(
|
|
4185
|
-
const basename3 = sysPath.basename(
|
|
4668
|
+
const directory = sysPath.dirname(path31);
|
|
4669
|
+
const basename3 = sysPath.basename(path31);
|
|
4186
4670
|
const parent = this.fsw._getWatchedDir(directory);
|
|
4187
4671
|
parent.add(basename3);
|
|
4188
|
-
const absolutePath = sysPath.resolve(
|
|
4672
|
+
const absolutePath = sysPath.resolve(path31);
|
|
4189
4673
|
const options = {
|
|
4190
4674
|
persistent: opts.persistent
|
|
4191
4675
|
};
|
|
@@ -4195,12 +4679,12 @@ var init_handler = __esm({
|
|
|
4195
4679
|
if (opts.usePolling) {
|
|
4196
4680
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
4197
4681
|
options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
|
|
4198
|
-
closer = setFsWatchFileListener(
|
|
4682
|
+
closer = setFsWatchFileListener(path31, absolutePath, options, {
|
|
4199
4683
|
listener,
|
|
4200
4684
|
rawEmitter: this.fsw._emitRaw
|
|
4201
4685
|
});
|
|
4202
4686
|
} else {
|
|
4203
|
-
closer = setFsWatchListener(
|
|
4687
|
+
closer = setFsWatchListener(path31, absolutePath, options, {
|
|
4204
4688
|
listener,
|
|
4205
4689
|
errHandler: this._boundHandleError,
|
|
4206
4690
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -4222,7 +4706,7 @@ var init_handler = __esm({
|
|
|
4222
4706
|
let prevStats = stats;
|
|
4223
4707
|
if (parent.has(basename3))
|
|
4224
4708
|
return;
|
|
4225
|
-
const listener = async (
|
|
4709
|
+
const listener = async (path31, newStats) => {
|
|
4226
4710
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
4227
4711
|
return;
|
|
4228
4712
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -4236,11 +4720,11 @@ var init_handler = __esm({
|
|
|
4236
4720
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
4237
4721
|
}
|
|
4238
4722
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
4239
|
-
this.fsw._closeFile(
|
|
4723
|
+
this.fsw._closeFile(path31);
|
|
4240
4724
|
prevStats = newStats2;
|
|
4241
4725
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
4242
4726
|
if (closer2)
|
|
4243
|
-
this.fsw._addPathCloser(
|
|
4727
|
+
this.fsw._addPathCloser(path31, closer2);
|
|
4244
4728
|
} else {
|
|
4245
4729
|
prevStats = newStats2;
|
|
4246
4730
|
}
|
|
@@ -4272,7 +4756,7 @@ var init_handler = __esm({
|
|
|
4272
4756
|
* @param item basename of this item
|
|
4273
4757
|
* @returns true if no more processing is needed for this entry.
|
|
4274
4758
|
*/
|
|
4275
|
-
async _handleSymlink(entry, directory,
|
|
4759
|
+
async _handleSymlink(entry, directory, path31, item) {
|
|
4276
4760
|
if (this.fsw.closed) {
|
|
4277
4761
|
return;
|
|
4278
4762
|
}
|
|
@@ -4282,7 +4766,7 @@ var init_handler = __esm({
|
|
|
4282
4766
|
this.fsw._incrReadyCount();
|
|
4283
4767
|
let linkPath;
|
|
4284
4768
|
try {
|
|
4285
|
-
linkPath = await fsrealpath(
|
|
4769
|
+
linkPath = await fsrealpath(path31);
|
|
4286
4770
|
} catch (e) {
|
|
4287
4771
|
this.fsw._emitReady();
|
|
4288
4772
|
return true;
|
|
@@ -4292,12 +4776,12 @@ var init_handler = __esm({
|
|
|
4292
4776
|
if (dir.has(item)) {
|
|
4293
4777
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
4294
4778
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
4295
|
-
this.fsw._emit(EV.CHANGE,
|
|
4779
|
+
this.fsw._emit(EV.CHANGE, path31, entry.stats);
|
|
4296
4780
|
}
|
|
4297
4781
|
} else {
|
|
4298
4782
|
dir.add(item);
|
|
4299
4783
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
4300
|
-
this.fsw._emit(EV.ADD,
|
|
4784
|
+
this.fsw._emit(EV.ADD, path31, entry.stats);
|
|
4301
4785
|
}
|
|
4302
4786
|
this.fsw._emitReady();
|
|
4303
4787
|
return true;
|
|
@@ -4326,9 +4810,9 @@ var init_handler = __esm({
|
|
|
4326
4810
|
return;
|
|
4327
4811
|
}
|
|
4328
4812
|
const item = entry.path;
|
|
4329
|
-
let
|
|
4813
|
+
let path31 = sysPath.join(directory, item);
|
|
4330
4814
|
current.add(item);
|
|
4331
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
4815
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path31, item)) {
|
|
4332
4816
|
return;
|
|
4333
4817
|
}
|
|
4334
4818
|
if (this.fsw.closed) {
|
|
@@ -4337,8 +4821,8 @@ var init_handler = __esm({
|
|
|
4337
4821
|
}
|
|
4338
4822
|
if (item === target || !target && !previous.has(item)) {
|
|
4339
4823
|
this.fsw._incrReadyCount();
|
|
4340
|
-
|
|
4341
|
-
this._addToNodeFs(
|
|
4824
|
+
path31 = sysPath.join(dir, sysPath.relative(dir, path31));
|
|
4825
|
+
this._addToNodeFs(path31, initialAdd, wh, depth + 1);
|
|
4342
4826
|
}
|
|
4343
4827
|
}).on(EV.ERROR, this._boundHandleError);
|
|
4344
4828
|
return new Promise((resolve3, reject) => {
|
|
@@ -4407,13 +4891,13 @@ var init_handler = __esm({
|
|
|
4407
4891
|
* @param depth Child path actually targeted for watch
|
|
4408
4892
|
* @param target Child path actually targeted for watch
|
|
4409
4893
|
*/
|
|
4410
|
-
async _addToNodeFs(
|
|
4894
|
+
async _addToNodeFs(path31, initialAdd, priorWh, depth, target) {
|
|
4411
4895
|
const ready = this.fsw._emitReady;
|
|
4412
|
-
if (this.fsw._isIgnored(
|
|
4896
|
+
if (this.fsw._isIgnored(path31) || this.fsw.closed) {
|
|
4413
4897
|
ready();
|
|
4414
4898
|
return false;
|
|
4415
4899
|
}
|
|
4416
|
-
const wh = this.fsw._getWatchHelpers(
|
|
4900
|
+
const wh = this.fsw._getWatchHelpers(path31);
|
|
4417
4901
|
if (priorWh) {
|
|
4418
4902
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
4419
4903
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -4429,8 +4913,8 @@ var init_handler = __esm({
|
|
|
4429
4913
|
const follow = this.fsw.options.followSymlinks;
|
|
4430
4914
|
let closer;
|
|
4431
4915
|
if (stats.isDirectory()) {
|
|
4432
|
-
const absPath = sysPath.resolve(
|
|
4433
|
-
const targetPath = follow ? await fsrealpath(
|
|
4916
|
+
const absPath = sysPath.resolve(path31);
|
|
4917
|
+
const targetPath = follow ? await fsrealpath(path31) : path31;
|
|
4434
4918
|
if (this.fsw.closed)
|
|
4435
4919
|
return;
|
|
4436
4920
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -4440,29 +4924,29 @@ var init_handler = __esm({
|
|
|
4440
4924
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
4441
4925
|
}
|
|
4442
4926
|
} else if (stats.isSymbolicLink()) {
|
|
4443
|
-
const targetPath = follow ? await fsrealpath(
|
|
4927
|
+
const targetPath = follow ? await fsrealpath(path31) : path31;
|
|
4444
4928
|
if (this.fsw.closed)
|
|
4445
4929
|
return;
|
|
4446
4930
|
const parent = sysPath.dirname(wh.watchPath);
|
|
4447
4931
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
4448
4932
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
4449
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
4933
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path31, wh, targetPath);
|
|
4450
4934
|
if (this.fsw.closed)
|
|
4451
4935
|
return;
|
|
4452
4936
|
if (targetPath !== void 0) {
|
|
4453
|
-
this.fsw._symlinkPaths.set(sysPath.resolve(
|
|
4937
|
+
this.fsw._symlinkPaths.set(sysPath.resolve(path31), targetPath);
|
|
4454
4938
|
}
|
|
4455
4939
|
} else {
|
|
4456
4940
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
4457
4941
|
}
|
|
4458
4942
|
ready();
|
|
4459
4943
|
if (closer)
|
|
4460
|
-
this.fsw._addPathCloser(
|
|
4944
|
+
this.fsw._addPathCloser(path31, closer);
|
|
4461
4945
|
return false;
|
|
4462
4946
|
} catch (error) {
|
|
4463
4947
|
if (this.fsw._handleError(error)) {
|
|
4464
4948
|
ready();
|
|
4465
|
-
return
|
|
4949
|
+
return path31;
|
|
4466
4950
|
}
|
|
4467
4951
|
}
|
|
4468
4952
|
}
|
|
@@ -4501,26 +4985,26 @@ function createPattern(matcher) {
|
|
|
4501
4985
|
}
|
|
4502
4986
|
return () => false;
|
|
4503
4987
|
}
|
|
4504
|
-
function normalizePath2(
|
|
4505
|
-
if (typeof
|
|
4988
|
+
function normalizePath2(path31) {
|
|
4989
|
+
if (typeof path31 !== "string")
|
|
4506
4990
|
throw new Error("string expected");
|
|
4507
|
-
|
|
4508
|
-
|
|
4991
|
+
path31 = sysPath2.normalize(path31);
|
|
4992
|
+
path31 = path31.replace(/\\/g, "/");
|
|
4509
4993
|
let prepend = false;
|
|
4510
|
-
if (
|
|
4994
|
+
if (path31.startsWith("//"))
|
|
4511
4995
|
prepend = true;
|
|
4512
4996
|
const DOUBLE_SLASH_RE2 = /\/\//;
|
|
4513
|
-
while (
|
|
4514
|
-
|
|
4997
|
+
while (path31.match(DOUBLE_SLASH_RE2))
|
|
4998
|
+
path31 = path31.replace(DOUBLE_SLASH_RE2, "/");
|
|
4515
4999
|
if (prepend)
|
|
4516
|
-
|
|
4517
|
-
return
|
|
5000
|
+
path31 = "/" + path31;
|
|
5001
|
+
return path31;
|
|
4518
5002
|
}
|
|
4519
5003
|
function matchPatterns(patterns, testString, stats) {
|
|
4520
|
-
const
|
|
5004
|
+
const path31 = normalizePath2(testString);
|
|
4521
5005
|
for (let index = 0; index < patterns.length; index++) {
|
|
4522
5006
|
const pattern = patterns[index];
|
|
4523
|
-
if (pattern(
|
|
5007
|
+
if (pattern(path31, stats)) {
|
|
4524
5008
|
return true;
|
|
4525
5009
|
}
|
|
4526
5010
|
}
|
|
@@ -4581,19 +5065,19 @@ var init_esm2 = __esm({
|
|
|
4581
5065
|
}
|
|
4582
5066
|
return str;
|
|
4583
5067
|
};
|
|
4584
|
-
normalizePathToUnix = (
|
|
4585
|
-
normalizeIgnored = (cwd = "") => (
|
|
4586
|
-
if (typeof
|
|
4587
|
-
return normalizePathToUnix(sysPath2.isAbsolute(
|
|
5068
|
+
normalizePathToUnix = (path31) => toUnix(sysPath2.normalize(toUnix(path31)));
|
|
5069
|
+
normalizeIgnored = (cwd = "") => (path31) => {
|
|
5070
|
+
if (typeof path31 === "string") {
|
|
5071
|
+
return normalizePathToUnix(sysPath2.isAbsolute(path31) ? path31 : sysPath2.join(cwd, path31));
|
|
4588
5072
|
} else {
|
|
4589
|
-
return
|
|
5073
|
+
return path31;
|
|
4590
5074
|
}
|
|
4591
5075
|
};
|
|
4592
|
-
getAbsolutePath = (
|
|
4593
|
-
if (sysPath2.isAbsolute(
|
|
4594
|
-
return
|
|
5076
|
+
getAbsolutePath = (path31, cwd) => {
|
|
5077
|
+
if (sysPath2.isAbsolute(path31)) {
|
|
5078
|
+
return path31;
|
|
4595
5079
|
}
|
|
4596
|
-
return sysPath2.join(cwd,
|
|
5080
|
+
return sysPath2.join(cwd, path31);
|
|
4597
5081
|
};
|
|
4598
5082
|
EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
4599
5083
|
DirEntry = class {
|
|
@@ -4648,10 +5132,10 @@ var init_esm2 = __esm({
|
|
|
4648
5132
|
STAT_METHOD_F = "stat";
|
|
4649
5133
|
STAT_METHOD_L = "lstat";
|
|
4650
5134
|
WatchHelper = class {
|
|
4651
|
-
constructor(
|
|
5135
|
+
constructor(path31, follow, fsw) {
|
|
4652
5136
|
this.fsw = fsw;
|
|
4653
|
-
const watchPath =
|
|
4654
|
-
this.path =
|
|
5137
|
+
const watchPath = path31;
|
|
5138
|
+
this.path = path31 = path31.replace(REPLACER_RE, "");
|
|
4655
5139
|
this.watchPath = watchPath;
|
|
4656
5140
|
this.fullWatchPath = sysPath2.resolve(watchPath);
|
|
4657
5141
|
this.dirParts = [];
|
|
@@ -4773,20 +5257,20 @@ var init_esm2 = __esm({
|
|
|
4773
5257
|
this._closePromise = void 0;
|
|
4774
5258
|
let paths = unifyPaths(paths_);
|
|
4775
5259
|
if (cwd) {
|
|
4776
|
-
paths = paths.map((
|
|
4777
|
-
const absPath = getAbsolutePath(
|
|
5260
|
+
paths = paths.map((path31) => {
|
|
5261
|
+
const absPath = getAbsolutePath(path31, cwd);
|
|
4778
5262
|
return absPath;
|
|
4779
5263
|
});
|
|
4780
5264
|
}
|
|
4781
|
-
paths.forEach((
|
|
4782
|
-
this._removeIgnoredPath(
|
|
5265
|
+
paths.forEach((path31) => {
|
|
5266
|
+
this._removeIgnoredPath(path31);
|
|
4783
5267
|
});
|
|
4784
5268
|
this._userIgnored = void 0;
|
|
4785
5269
|
if (!this._readyCount)
|
|
4786
5270
|
this._readyCount = 0;
|
|
4787
5271
|
this._readyCount += paths.length;
|
|
4788
|
-
Promise.all(paths.map(async (
|
|
4789
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
5272
|
+
Promise.all(paths.map(async (path31) => {
|
|
5273
|
+
const res = await this._nodeFsHandler._addToNodeFs(path31, !_internal, void 0, 0, _origAdd);
|
|
4790
5274
|
if (res)
|
|
4791
5275
|
this._emitReady();
|
|
4792
5276
|
return res;
|
|
@@ -4808,17 +5292,17 @@ var init_esm2 = __esm({
|
|
|
4808
5292
|
return this;
|
|
4809
5293
|
const paths = unifyPaths(paths_);
|
|
4810
5294
|
const { cwd } = this.options;
|
|
4811
|
-
paths.forEach((
|
|
4812
|
-
if (!sysPath2.isAbsolute(
|
|
5295
|
+
paths.forEach((path31) => {
|
|
5296
|
+
if (!sysPath2.isAbsolute(path31) && !this._closers.has(path31)) {
|
|
4813
5297
|
if (cwd)
|
|
4814
|
-
|
|
4815
|
-
|
|
5298
|
+
path31 = sysPath2.join(cwd, path31);
|
|
5299
|
+
path31 = sysPath2.resolve(path31);
|
|
4816
5300
|
}
|
|
4817
|
-
this._closePath(
|
|
4818
|
-
this._addIgnoredPath(
|
|
4819
|
-
if (this._watched.has(
|
|
5301
|
+
this._closePath(path31);
|
|
5302
|
+
this._addIgnoredPath(path31);
|
|
5303
|
+
if (this._watched.has(path31)) {
|
|
4820
5304
|
this._addIgnoredPath({
|
|
4821
|
-
path:
|
|
5305
|
+
path: path31,
|
|
4822
5306
|
recursive: true
|
|
4823
5307
|
});
|
|
4824
5308
|
}
|
|
@@ -4882,38 +5366,38 @@ var init_esm2 = __esm({
|
|
|
4882
5366
|
* @param stats arguments to be passed with event
|
|
4883
5367
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
4884
5368
|
*/
|
|
4885
|
-
async _emit(event,
|
|
5369
|
+
async _emit(event, path31, stats) {
|
|
4886
5370
|
if (this.closed)
|
|
4887
5371
|
return;
|
|
4888
5372
|
const opts = this.options;
|
|
4889
5373
|
if (isWindows)
|
|
4890
|
-
|
|
5374
|
+
path31 = sysPath2.normalize(path31);
|
|
4891
5375
|
if (opts.cwd)
|
|
4892
|
-
|
|
4893
|
-
const args = [
|
|
5376
|
+
path31 = sysPath2.relative(opts.cwd, path31);
|
|
5377
|
+
const args = [path31];
|
|
4894
5378
|
if (stats != null)
|
|
4895
5379
|
args.push(stats);
|
|
4896
5380
|
const awf = opts.awaitWriteFinish;
|
|
4897
5381
|
let pw;
|
|
4898
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
5382
|
+
if (awf && (pw = this._pendingWrites.get(path31))) {
|
|
4899
5383
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
4900
5384
|
return this;
|
|
4901
5385
|
}
|
|
4902
5386
|
if (opts.atomic) {
|
|
4903
5387
|
if (event === EVENTS.UNLINK) {
|
|
4904
|
-
this._pendingUnlinks.set(
|
|
5388
|
+
this._pendingUnlinks.set(path31, [event, ...args]);
|
|
4905
5389
|
setTimeout(() => {
|
|
4906
|
-
this._pendingUnlinks.forEach((entry,
|
|
5390
|
+
this._pendingUnlinks.forEach((entry, path32) => {
|
|
4907
5391
|
this.emit(...entry);
|
|
4908
5392
|
this.emit(EVENTS.ALL, ...entry);
|
|
4909
|
-
this._pendingUnlinks.delete(
|
|
5393
|
+
this._pendingUnlinks.delete(path32);
|
|
4910
5394
|
});
|
|
4911
5395
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
4912
5396
|
return this;
|
|
4913
5397
|
}
|
|
4914
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
5398
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path31)) {
|
|
4915
5399
|
event = EVENTS.CHANGE;
|
|
4916
|
-
this._pendingUnlinks.delete(
|
|
5400
|
+
this._pendingUnlinks.delete(path31);
|
|
4917
5401
|
}
|
|
4918
5402
|
}
|
|
4919
5403
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -4931,16 +5415,16 @@ var init_esm2 = __esm({
|
|
|
4931
5415
|
this.emitWithAll(event, args);
|
|
4932
5416
|
}
|
|
4933
5417
|
};
|
|
4934
|
-
this._awaitWriteFinish(
|
|
5418
|
+
this._awaitWriteFinish(path31, awf.stabilityThreshold, event, awfEmit);
|
|
4935
5419
|
return this;
|
|
4936
5420
|
}
|
|
4937
5421
|
if (event === EVENTS.CHANGE) {
|
|
4938
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
5422
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path31, 50);
|
|
4939
5423
|
if (isThrottled)
|
|
4940
5424
|
return this;
|
|
4941
5425
|
}
|
|
4942
5426
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
4943
|
-
const fullPath = opts.cwd ? sysPath2.join(opts.cwd,
|
|
5427
|
+
const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path31) : path31;
|
|
4944
5428
|
let stats2;
|
|
4945
5429
|
try {
|
|
4946
5430
|
stats2 = await stat3(fullPath);
|
|
@@ -4971,23 +5455,23 @@ var init_esm2 = __esm({
|
|
|
4971
5455
|
* @param timeout duration of time to suppress duplicate actions
|
|
4972
5456
|
* @returns tracking object or false if action should be suppressed
|
|
4973
5457
|
*/
|
|
4974
|
-
_throttle(actionType,
|
|
5458
|
+
_throttle(actionType, path31, timeout) {
|
|
4975
5459
|
if (!this._throttled.has(actionType)) {
|
|
4976
5460
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
4977
5461
|
}
|
|
4978
5462
|
const action = this._throttled.get(actionType);
|
|
4979
5463
|
if (!action)
|
|
4980
5464
|
throw new Error("invalid throttle");
|
|
4981
|
-
const actionPath = action.get(
|
|
5465
|
+
const actionPath = action.get(path31);
|
|
4982
5466
|
if (actionPath) {
|
|
4983
5467
|
actionPath.count++;
|
|
4984
5468
|
return false;
|
|
4985
5469
|
}
|
|
4986
5470
|
let timeoutObject;
|
|
4987
5471
|
const clear = () => {
|
|
4988
|
-
const item = action.get(
|
|
5472
|
+
const item = action.get(path31);
|
|
4989
5473
|
const count = item ? item.count : 0;
|
|
4990
|
-
action.delete(
|
|
5474
|
+
action.delete(path31);
|
|
4991
5475
|
clearTimeout(timeoutObject);
|
|
4992
5476
|
if (item)
|
|
4993
5477
|
clearTimeout(item.timeoutObject);
|
|
@@ -4995,7 +5479,7 @@ var init_esm2 = __esm({
|
|
|
4995
5479
|
};
|
|
4996
5480
|
timeoutObject = setTimeout(clear, timeout);
|
|
4997
5481
|
const thr = { timeoutObject, clear, count: 0 };
|
|
4998
|
-
action.set(
|
|
5482
|
+
action.set(path31, thr);
|
|
4999
5483
|
return thr;
|
|
5000
5484
|
}
|
|
5001
5485
|
_incrReadyCount() {
|
|
@@ -5009,44 +5493,44 @@ var init_esm2 = __esm({
|
|
|
5009
5493
|
* @param event
|
|
5010
5494
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
5011
5495
|
*/
|
|
5012
|
-
_awaitWriteFinish(
|
|
5496
|
+
_awaitWriteFinish(path31, threshold, event, awfEmit) {
|
|
5013
5497
|
const awf = this.options.awaitWriteFinish;
|
|
5014
5498
|
if (typeof awf !== "object")
|
|
5015
5499
|
return;
|
|
5016
5500
|
const pollInterval = awf.pollInterval;
|
|
5017
5501
|
let timeoutHandler;
|
|
5018
|
-
let fullPath =
|
|
5019
|
-
if (this.options.cwd && !sysPath2.isAbsolute(
|
|
5020
|
-
fullPath = sysPath2.join(this.options.cwd,
|
|
5502
|
+
let fullPath = path31;
|
|
5503
|
+
if (this.options.cwd && !sysPath2.isAbsolute(path31)) {
|
|
5504
|
+
fullPath = sysPath2.join(this.options.cwd, path31);
|
|
5021
5505
|
}
|
|
5022
5506
|
const now = /* @__PURE__ */ new Date();
|
|
5023
5507
|
const writes = this._pendingWrites;
|
|
5024
5508
|
function awaitWriteFinishFn(prevStat) {
|
|
5025
5509
|
statcb(fullPath, (err, curStat) => {
|
|
5026
|
-
if (err || !writes.has(
|
|
5510
|
+
if (err || !writes.has(path31)) {
|
|
5027
5511
|
if (err && err.code !== "ENOENT")
|
|
5028
5512
|
awfEmit(err);
|
|
5029
5513
|
return;
|
|
5030
5514
|
}
|
|
5031
5515
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
5032
5516
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
5033
|
-
writes.get(
|
|
5517
|
+
writes.get(path31).lastChange = now2;
|
|
5034
5518
|
}
|
|
5035
|
-
const pw = writes.get(
|
|
5519
|
+
const pw = writes.get(path31);
|
|
5036
5520
|
const df = now2 - pw.lastChange;
|
|
5037
5521
|
if (df >= threshold) {
|
|
5038
|
-
writes.delete(
|
|
5522
|
+
writes.delete(path31);
|
|
5039
5523
|
awfEmit(void 0, curStat);
|
|
5040
5524
|
} else {
|
|
5041
5525
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
5042
5526
|
}
|
|
5043
5527
|
});
|
|
5044
5528
|
}
|
|
5045
|
-
if (!writes.has(
|
|
5046
|
-
writes.set(
|
|
5529
|
+
if (!writes.has(path31)) {
|
|
5530
|
+
writes.set(path31, {
|
|
5047
5531
|
lastChange: now,
|
|
5048
5532
|
cancelWait: () => {
|
|
5049
|
-
writes.delete(
|
|
5533
|
+
writes.delete(path31);
|
|
5050
5534
|
clearTimeout(timeoutHandler);
|
|
5051
5535
|
return event;
|
|
5052
5536
|
}
|
|
@@ -5057,8 +5541,8 @@ var init_esm2 = __esm({
|
|
|
5057
5541
|
/**
|
|
5058
5542
|
* Determines whether user has asked to ignore this path.
|
|
5059
5543
|
*/
|
|
5060
|
-
_isIgnored(
|
|
5061
|
-
if (this.options.atomic && DOT_RE.test(
|
|
5544
|
+
_isIgnored(path31, stats) {
|
|
5545
|
+
if (this.options.atomic && DOT_RE.test(path31))
|
|
5062
5546
|
return true;
|
|
5063
5547
|
if (!this._userIgnored) {
|
|
5064
5548
|
const { cwd } = this.options;
|
|
@@ -5068,17 +5552,17 @@ var init_esm2 = __esm({
|
|
|
5068
5552
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
5069
5553
|
this._userIgnored = anymatch(list, void 0);
|
|
5070
5554
|
}
|
|
5071
|
-
return this._userIgnored(
|
|
5555
|
+
return this._userIgnored(path31, stats);
|
|
5072
5556
|
}
|
|
5073
|
-
_isntIgnored(
|
|
5074
|
-
return !this._isIgnored(
|
|
5557
|
+
_isntIgnored(path31, stat4) {
|
|
5558
|
+
return !this._isIgnored(path31, stat4);
|
|
5075
5559
|
}
|
|
5076
5560
|
/**
|
|
5077
5561
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
5078
5562
|
* @param path file or directory pattern being watched
|
|
5079
5563
|
*/
|
|
5080
|
-
_getWatchHelpers(
|
|
5081
|
-
return new WatchHelper(
|
|
5564
|
+
_getWatchHelpers(path31) {
|
|
5565
|
+
return new WatchHelper(path31, this.options.followSymlinks, this);
|
|
5082
5566
|
}
|
|
5083
5567
|
// Directory helpers
|
|
5084
5568
|
// -----------------
|
|
@@ -5110,63 +5594,63 @@ var init_esm2 = __esm({
|
|
|
5110
5594
|
* @param item base path of item/directory
|
|
5111
5595
|
*/
|
|
5112
5596
|
_remove(directory, item, isDirectory) {
|
|
5113
|
-
const
|
|
5114
|
-
const fullPath = sysPath2.resolve(
|
|
5115
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
5116
|
-
if (!this._throttle("remove",
|
|
5597
|
+
const path31 = sysPath2.join(directory, item);
|
|
5598
|
+
const fullPath = sysPath2.resolve(path31);
|
|
5599
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path31) || this._watched.has(fullPath);
|
|
5600
|
+
if (!this._throttle("remove", path31, 100))
|
|
5117
5601
|
return;
|
|
5118
5602
|
if (!isDirectory && this._watched.size === 1) {
|
|
5119
5603
|
this.add(directory, item, true);
|
|
5120
5604
|
}
|
|
5121
|
-
const wp = this._getWatchedDir(
|
|
5605
|
+
const wp = this._getWatchedDir(path31);
|
|
5122
5606
|
const nestedDirectoryChildren = wp.getChildren();
|
|
5123
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
5607
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path31, nested));
|
|
5124
5608
|
const parent = this._getWatchedDir(directory);
|
|
5125
5609
|
const wasTracked = parent.has(item);
|
|
5126
5610
|
parent.remove(item);
|
|
5127
5611
|
if (this._symlinkPaths.has(fullPath)) {
|
|
5128
5612
|
this._symlinkPaths.delete(fullPath);
|
|
5129
5613
|
}
|
|
5130
|
-
let relPath =
|
|
5614
|
+
let relPath = path31;
|
|
5131
5615
|
if (this.options.cwd)
|
|
5132
|
-
relPath = sysPath2.relative(this.options.cwd,
|
|
5616
|
+
relPath = sysPath2.relative(this.options.cwd, path31);
|
|
5133
5617
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
5134
5618
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
5135
5619
|
if (event === EVENTS.ADD)
|
|
5136
5620
|
return;
|
|
5137
5621
|
}
|
|
5138
|
-
this._watched.delete(
|
|
5622
|
+
this._watched.delete(path31);
|
|
5139
5623
|
this._watched.delete(fullPath);
|
|
5140
5624
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
5141
|
-
if (wasTracked && !this._isIgnored(
|
|
5142
|
-
this._emit(eventName,
|
|
5143
|
-
this._closePath(
|
|
5625
|
+
if (wasTracked && !this._isIgnored(path31))
|
|
5626
|
+
this._emit(eventName, path31);
|
|
5627
|
+
this._closePath(path31);
|
|
5144
5628
|
}
|
|
5145
5629
|
/**
|
|
5146
5630
|
* Closes all watchers for a path
|
|
5147
5631
|
*/
|
|
5148
|
-
_closePath(
|
|
5149
|
-
this._closeFile(
|
|
5150
|
-
const dir = sysPath2.dirname(
|
|
5151
|
-
this._getWatchedDir(dir).remove(sysPath2.basename(
|
|
5632
|
+
_closePath(path31) {
|
|
5633
|
+
this._closeFile(path31);
|
|
5634
|
+
const dir = sysPath2.dirname(path31);
|
|
5635
|
+
this._getWatchedDir(dir).remove(sysPath2.basename(path31));
|
|
5152
5636
|
}
|
|
5153
5637
|
/**
|
|
5154
5638
|
* Closes only file-specific watchers
|
|
5155
5639
|
*/
|
|
5156
|
-
_closeFile(
|
|
5157
|
-
const closers = this._closers.get(
|
|
5640
|
+
_closeFile(path31) {
|
|
5641
|
+
const closers = this._closers.get(path31);
|
|
5158
5642
|
if (!closers)
|
|
5159
5643
|
return;
|
|
5160
5644
|
closers.forEach((closer) => closer());
|
|
5161
|
-
this._closers.delete(
|
|
5645
|
+
this._closers.delete(path31);
|
|
5162
5646
|
}
|
|
5163
|
-
_addPathCloser(
|
|
5647
|
+
_addPathCloser(path31, closer) {
|
|
5164
5648
|
if (!closer)
|
|
5165
5649
|
return;
|
|
5166
|
-
let list = this._closers.get(
|
|
5650
|
+
let list = this._closers.get(path31);
|
|
5167
5651
|
if (!list) {
|
|
5168
5652
|
list = [];
|
|
5169
|
-
this._closers.set(
|
|
5653
|
+
this._closers.set(path31, list);
|
|
5170
5654
|
}
|
|
5171
5655
|
list.push(closer);
|
|
5172
5656
|
}
|
|
@@ -5192,73 +5676,6 @@ var init_esm2 = __esm({
|
|
|
5192
5676
|
}
|
|
5193
5677
|
});
|
|
5194
5678
|
|
|
5195
|
-
// src/cli/compileSourceFiles.ts
|
|
5196
|
-
import path18 from "path";
|
|
5197
|
-
import fs14 from "fs";
|
|
5198
|
-
import fg4 from "fast-glob";
|
|
5199
|
-
async function compileSourceFiles(options) {
|
|
5200
|
-
const { rootDir, dist, files, logLevel = "silent", production, atomicWrite } = options;
|
|
5201
|
-
const entryPoints = files ?? await fg4([`${APP_DIR}/**/*.ts`], {
|
|
5202
|
-
cwd: rootDir,
|
|
5203
|
-
onlyFiles: true,
|
|
5204
|
-
absolute: true,
|
|
5205
|
-
ignore: ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts"]
|
|
5206
|
-
});
|
|
5207
|
-
if (entryPoints.length === 0) {
|
|
5208
|
-
return { compiledFiles: [] };
|
|
5209
|
-
}
|
|
5210
|
-
const absDist = path18.resolve(rootDir, dist);
|
|
5211
|
-
await fs14.promises.mkdir(absDist, { recursive: true });
|
|
5212
|
-
const plugins = buildAliasPlugins(rootDir);
|
|
5213
|
-
const esbuild = await import("esbuild");
|
|
5214
|
-
const outbase = path18.resolve(rootDir, APP_DIR);
|
|
5215
|
-
const result = await esbuild.build({
|
|
5216
|
-
entryPoints,
|
|
5217
|
-
outdir: absDist,
|
|
5218
|
-
outbase,
|
|
5219
|
-
bundle: false,
|
|
5220
|
-
platform: "node",
|
|
5221
|
-
format: "esm",
|
|
5222
|
-
sourcemap: true,
|
|
5223
|
-
packages: "external",
|
|
5224
|
-
plugins,
|
|
5225
|
-
// build 语义:编译期 NODE_ENV 替换 + 死分支删除(见 AGENTS.md §5.3)
|
|
5226
|
-
...production ? { define: { "process.env.NODE_ENV": '"production"' }, minifySyntax: true } : {},
|
|
5227
|
-
logLevel,
|
|
5228
|
-
// dev 语义:esbuild 返回内存内容,由下方原子写落盘
|
|
5229
|
-
...atomicWrite ? { write: false } : {}
|
|
5230
|
-
});
|
|
5231
|
-
if (atomicWrite && result.outputFiles) {
|
|
5232
|
-
await Promise.all(
|
|
5233
|
-
result.outputFiles.map(async (file) => {
|
|
5234
|
-
await fs14.promises.mkdir(path18.dirname(file.path), { recursive: true });
|
|
5235
|
-
const tmp = `${file.path}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
5236
|
-
await fs14.promises.writeFile(tmp, file.contents);
|
|
5237
|
-
await fs14.promises.rename(tmp, file.path);
|
|
5238
|
-
})
|
|
5239
|
-
);
|
|
5240
|
-
}
|
|
5241
|
-
return { compiledFiles: entryPoints };
|
|
5242
|
-
}
|
|
5243
|
-
var init_compileSourceFiles = __esm({
|
|
5244
|
-
"src/cli/compileSourceFiles.ts"() {
|
|
5245
|
-
"use strict";
|
|
5246
|
-
init_aliasPlugin();
|
|
5247
|
-
init_prodPaths();
|
|
5248
|
-
}
|
|
5249
|
-
});
|
|
5250
|
-
|
|
5251
|
-
// src/cli/compileDevRoutes.ts
|
|
5252
|
-
async function compileDevRoutes(options) {
|
|
5253
|
-
return compileSourceFiles({ ...options, atomicWrite: true });
|
|
5254
|
-
}
|
|
5255
|
-
var init_compileDevRoutes = __esm({
|
|
5256
|
-
"src/cli/compileDevRoutes.ts"() {
|
|
5257
|
-
"use strict";
|
|
5258
|
-
init_compileSourceFiles();
|
|
5259
|
-
}
|
|
5260
|
-
});
|
|
5261
|
-
|
|
5262
5679
|
// src/cli/rebuildScheduler.ts
|
|
5263
5680
|
function createRebuildScheduler(options) {
|
|
5264
5681
|
const { rebuild, debounceMs = 100, onError } = options;
|
|
@@ -5321,7 +5738,7 @@ var init_rebuildScheduler = __esm({
|
|
|
5321
5738
|
});
|
|
5322
5739
|
|
|
5323
5740
|
// src/cli/watcher.ts
|
|
5324
|
-
import
|
|
5741
|
+
import path22 from "path";
|
|
5325
5742
|
function startWatcher(options) {
|
|
5326
5743
|
const { rootDir, app, devDist } = options;
|
|
5327
5744
|
async function rebuildRoutes(files) {
|
|
@@ -5336,6 +5753,7 @@ function startWatcher(options) {
|
|
|
5336
5753
|
await app.reloadRoutes();
|
|
5337
5754
|
await app.reloadTools();
|
|
5338
5755
|
await app.reloadAgents();
|
|
5756
|
+
await app.reloadTasks();
|
|
5339
5757
|
console.log(
|
|
5340
5758
|
`- Routes rebuilt${files.length > 0 ? `, ${files.length} file(s) recompiled` : ""}`
|
|
5341
5759
|
);
|
|
@@ -5356,9 +5774,9 @@ function startWatcher(options) {
|
|
|
5356
5774
|
}
|
|
5357
5775
|
});
|
|
5358
5776
|
const CONFIG_FILES = ["faapi.config.ts", "faapi.config.js"];
|
|
5359
|
-
const configAbsPaths = new Set(CONFIG_FILES.map((f) =>
|
|
5777
|
+
const configAbsPaths = new Set(CONFIG_FILES.map((f) => path22.resolve(rootDir, f)));
|
|
5360
5778
|
const watchPaths = ["src", "tsconfig.json", ...CONFIG_FILES];
|
|
5361
|
-
const scheduleOnly = /* @__PURE__ */ new Set([...configAbsPaths,
|
|
5779
|
+
const scheduleOnly = /* @__PURE__ */ new Set([...configAbsPaths, path22.resolve(rootDir, "tsconfig.json")]);
|
|
5362
5780
|
const watcher = esm_default.watch(watchPaths, {
|
|
5363
5781
|
cwd: rootDir,
|
|
5364
5782
|
ignoreInitial: true,
|
|
@@ -5375,7 +5793,7 @@ function startWatcher(options) {
|
|
|
5375
5793
|
watcher.on("add", (file) => handleFileEvent(file));
|
|
5376
5794
|
watcher.on("change", (file) => handleFileEvent(file));
|
|
5377
5795
|
function handleFileEvent(file) {
|
|
5378
|
-
const abs =
|
|
5796
|
+
const abs = path22.resolve(rootDir, file);
|
|
5379
5797
|
if (scheduleOnly.has(abs)) {
|
|
5380
5798
|
scheduler.schedule();
|
|
5381
5799
|
return;
|
|
@@ -5477,18 +5895,18 @@ function getWsIndex(routes) {
|
|
|
5477
5895
|
wsIndexCache.set(routes, index);
|
|
5478
5896
|
return index;
|
|
5479
5897
|
}
|
|
5480
|
-
function matchRoute(routes, method,
|
|
5898
|
+
function matchRoute(routes, method, path31) {
|
|
5481
5899
|
const index = getHttpIndex(routes);
|
|
5482
5900
|
const upper = method.toUpperCase();
|
|
5483
|
-
const hit = matchByMethod(index, upper,
|
|
5901
|
+
const hit = matchByMethod(index, upper, path31);
|
|
5484
5902
|
if (hit) return hit;
|
|
5485
5903
|
if (upper === "HEAD") {
|
|
5486
|
-
return matchByMethod(index, "GET",
|
|
5904
|
+
return matchByMethod(index, "GET", path31);
|
|
5487
5905
|
}
|
|
5488
5906
|
return null;
|
|
5489
5907
|
}
|
|
5490
|
-
function matchByMethod(index, method,
|
|
5491
|
-
const staticHit = index.static.get(`${method}|${
|
|
5908
|
+
function matchByMethod(index, method, path31) {
|
|
5909
|
+
const staticHit = index.static.get(`${method}|${path31}`);
|
|
5492
5910
|
if (staticHit) {
|
|
5493
5911
|
return { route: staticHit, params: {} };
|
|
5494
5912
|
}
|
|
@@ -5497,31 +5915,31 @@ function matchByMethod(index, method, path27) {
|
|
|
5497
5915
|
if (route.method !== method) {
|
|
5498
5916
|
continue;
|
|
5499
5917
|
}
|
|
5500
|
-
const params = matchSegments(entry.segments,
|
|
5918
|
+
const params = matchSegments(entry.segments, path31, route.paramNames, route.isCatchAll);
|
|
5501
5919
|
if (params !== null) {
|
|
5502
5920
|
return { route, params };
|
|
5503
5921
|
}
|
|
5504
5922
|
}
|
|
5505
5923
|
return null;
|
|
5506
5924
|
}
|
|
5507
|
-
function matchWsRoute(wsRoutes,
|
|
5925
|
+
function matchWsRoute(wsRoutes, path31) {
|
|
5508
5926
|
const index = getWsIndex(wsRoutes);
|
|
5509
|
-
const staticHit = index.static.get(
|
|
5927
|
+
const staticHit = index.static.get(path31);
|
|
5510
5928
|
if (staticHit) {
|
|
5511
5929
|
return { route: staticHit, params: {} };
|
|
5512
5930
|
}
|
|
5513
5931
|
for (const route of index.dynamics) {
|
|
5514
|
-
const params = matchDynamicPath(route.urlPath,
|
|
5932
|
+
const params = matchDynamicPath(route.urlPath, path31, route.paramNames, route.isCatchAll);
|
|
5515
5933
|
if (params !== null) {
|
|
5516
5934
|
return { route, params };
|
|
5517
5935
|
}
|
|
5518
5936
|
}
|
|
5519
5937
|
return null;
|
|
5520
5938
|
}
|
|
5521
|
-
function findAllowedMethods(routes,
|
|
5939
|
+
function findAllowedMethods(routes, path31) {
|
|
5522
5940
|
const index = getHttpIndex(routes);
|
|
5523
5941
|
const methods = /* @__PURE__ */ new Set();
|
|
5524
|
-
const staticMethods = index.methodsByStaticPath.get(
|
|
5942
|
+
const staticMethods = index.methodsByStaticPath.get(path31);
|
|
5525
5943
|
if (staticMethods) {
|
|
5526
5944
|
for (const method of staticMethods) {
|
|
5527
5945
|
methods.add(method);
|
|
@@ -5530,7 +5948,7 @@ function findAllowedMethods(routes, path27) {
|
|
|
5530
5948
|
for (const entry of index.dynamics) {
|
|
5531
5949
|
const params = matchSegments(
|
|
5532
5950
|
entry.segments,
|
|
5533
|
-
|
|
5951
|
+
path31,
|
|
5534
5952
|
entry.route.paramNames,
|
|
5535
5953
|
entry.route.isCatchAll
|
|
5536
5954
|
);
|
|
@@ -5543,11 +5961,11 @@ function findAllowedMethods(routes, path27) {
|
|
|
5543
5961
|
}
|
|
5544
5962
|
return Array.from(methods);
|
|
5545
5963
|
}
|
|
5546
|
-
function matchDynamicPath(pattern,
|
|
5547
|
-
return matchSegments(pattern.split("/").filter(Boolean),
|
|
5964
|
+
function matchDynamicPath(pattern, path31, paramNames, isCatchAll) {
|
|
5965
|
+
return matchSegments(pattern.split("/").filter(Boolean), path31, paramNames, isCatchAll);
|
|
5548
5966
|
}
|
|
5549
|
-
function matchSegments(patternSegments,
|
|
5550
|
-
const pathSegments =
|
|
5967
|
+
function matchSegments(patternSegments, path31, paramNames, isCatchAll) {
|
|
5968
|
+
const pathSegments = path31.split("/").filter(Boolean);
|
|
5551
5969
|
if (isCatchAll) {
|
|
5552
5970
|
const nonCatchAllCount = patternSegments.length - 1;
|
|
5553
5971
|
if (pathSegments.length <= nonCatchAllCount) {
|
|
@@ -5598,230 +6016,39 @@ var init_matchRoute = __esm({
|
|
|
5598
6016
|
httpIndexCache = /* @__PURE__ */ new WeakMap();
|
|
5599
6017
|
wsIndexCache = /* @__PURE__ */ new WeakMap();
|
|
5600
6018
|
}
|
|
5601
|
-
});
|
|
5602
|
-
|
|
5603
|
-
// src/loader/resolveExports.ts
|
|
5604
|
-
function resolveExport(module, exportName) {
|
|
5605
|
-
if (exportName in module && typeof module[exportName] !== "undefined") {
|
|
5606
|
-
return module[exportName];
|
|
5607
|
-
}
|
|
5608
|
-
const defaultExport = module.default;
|
|
5609
|
-
if (defaultExport !== null && typeof defaultExport === "object") {
|
|
5610
|
-
const value = defaultExport[exportName];
|
|
5611
|
-
if (value !== void 0) {
|
|
5612
|
-
return value;
|
|
5613
|
-
}
|
|
5614
|
-
}
|
|
5615
|
-
return void 0;
|
|
5616
|
-
}
|
|
5617
|
-
var init_resolveExports = __esm({
|
|
5618
|
-
"src/loader/resolveExports.ts"() {
|
|
5619
|
-
"use strict";
|
|
5620
|
-
}
|
|
5621
|
-
});
|
|
5622
|
-
|
|
5623
|
-
// src/loader/validateRouteModule.ts
|
|
5624
|
-
function validateRouteModule(value, method, filePath) {
|
|
5625
|
-
if (typeof value !== "function") {
|
|
5626
|
-
throw new Error(
|
|
5627
|
-
`Route module "${filePath}" does not export a valid handler for method "${method}". Expected a function, got ${typeof value}.`
|
|
5628
|
-
);
|
|
5629
|
-
}
|
|
5630
|
-
}
|
|
5631
|
-
var init_validateRouteModule = __esm({
|
|
5632
|
-
"src/loader/validateRouteModule.ts"() {
|
|
5633
|
-
"use strict";
|
|
5634
|
-
}
|
|
5635
|
-
});
|
|
5636
|
-
|
|
5637
|
-
// src/cli/compileOnDemand.ts
|
|
5638
|
-
import path20 from "path";
|
|
5639
|
-
import fs15 from "fs";
|
|
5640
|
-
function isProductFresh(sourceAbsPath, productAbsPath) {
|
|
5641
|
-
try {
|
|
5642
|
-
const srcStat = fs15.statSync(sourceAbsPath);
|
|
5643
|
-
const prodStat = fs15.statSync(productAbsPath);
|
|
5644
|
-
return prodStat.mtimeMs >= srcStat.mtimeMs;
|
|
5645
|
-
} catch {
|
|
5646
|
-
return false;
|
|
5647
|
-
}
|
|
5648
|
-
}
|
|
5649
|
-
function createDevOnDemandState() {
|
|
5650
|
-
return {
|
|
5651
|
-
enabled: false,
|
|
5652
|
-
distDir: void 0,
|
|
5653
|
-
compiledFiles: /* @__PURE__ */ new Set(),
|
|
5654
|
-
generatedSchemas: /* @__PURE__ */ new Set(),
|
|
5655
|
-
inFlightCompilations: /* @__PURE__ */ new Map(),
|
|
5656
|
-
inFlightSchemaGenerations: /* @__PURE__ */ new Map()
|
|
5657
|
-
};
|
|
5658
|
-
}
|
|
5659
|
-
function clearCompiledFiles() {
|
|
5660
|
-
state.compiledFiles.clear();
|
|
5661
|
-
state.inFlightCompilations.clear();
|
|
5662
|
-
sourcePathCache.clear();
|
|
5663
|
-
}
|
|
5664
|
-
async function ensureCompiled(sourceAbsPath, rootDir, dist) {
|
|
5665
|
-
const inFlight2 = state.inFlightCompilations.get(sourceAbsPath);
|
|
5666
|
-
if (inFlight2) {
|
|
5667
|
-
await inFlight2.catch(() => {
|
|
5668
|
-
});
|
|
5669
|
-
return false;
|
|
5670
|
-
}
|
|
5671
|
-
if (state.compiledFiles.has(sourceAbsPath)) {
|
|
5672
|
-
return false;
|
|
5673
|
-
}
|
|
5674
|
-
if (!fs15.existsSync(sourceAbsPath)) {
|
|
5675
|
-
return false;
|
|
5676
|
-
}
|
|
5677
|
-
const productPath = prodSourcePathToProductPath(sourceAbsPath, rootDir, dist);
|
|
5678
|
-
if (productPath && isProductFresh(sourceAbsPath, productPath)) {
|
|
5679
|
-
state.compiledFiles.add(sourceAbsPath);
|
|
5680
|
-
return false;
|
|
5681
|
-
}
|
|
5682
|
-
const compilePromise = (async () => {
|
|
5683
|
-
const { insideFiles } = await collectRelativeImports([sourceAbsPath], rootDir);
|
|
5684
|
-
const files = [sourceAbsPath];
|
|
5685
|
-
for (const dep of insideFiles) {
|
|
5686
|
-
if (state.compiledFiles.has(dep)) continue;
|
|
5687
|
-
const depProduct = prodSourcePathToProductPath(dep, rootDir, dist);
|
|
5688
|
-
if (depProduct && isProductFresh(dep, depProduct)) continue;
|
|
5689
|
-
files.push(dep);
|
|
5690
|
-
}
|
|
5691
|
-
await compileDevRoutes({
|
|
5692
|
-
rootDir,
|
|
5693
|
-
dist,
|
|
5694
|
-
files,
|
|
5695
|
-
logLevel: "silent"
|
|
5696
|
-
});
|
|
5697
|
-
for (const file of files) {
|
|
5698
|
-
state.compiledFiles.add(file);
|
|
5699
|
-
}
|
|
5700
|
-
state.compiledFiles.add(sourceAbsPath);
|
|
5701
|
-
})();
|
|
5702
|
-
state.inFlightCompilations.set(sourceAbsPath, compilePromise);
|
|
5703
|
-
try {
|
|
5704
|
-
await compilePromise;
|
|
5705
|
-
return true;
|
|
5706
|
-
} finally {
|
|
5707
|
-
state.inFlightCompilations.delete(sourceAbsPath);
|
|
5708
|
-
}
|
|
5709
|
-
}
|
|
5710
|
-
async function ensureMiddlewaresCompiled(middlewarePaths, rootDir) {
|
|
5711
|
-
if (!isDevOnDemandEnabled() || middlewarePaths.length === 0) return;
|
|
5712
|
-
const dist = getDevDist();
|
|
5713
|
-
if (!dist) return;
|
|
5714
|
-
for (const mwPath of middlewarePaths) {
|
|
5715
|
-
const sourcePath = prodPathToSourcePath(mwPath, rootDir, dist);
|
|
5716
|
-
try {
|
|
5717
|
-
await ensureCompiled(sourcePath, rootDir, dist);
|
|
5718
|
-
} catch (err) {
|
|
5719
|
-
console.error(`[faapi] Failed to compile middleware source ${sourcePath}:`, err);
|
|
5720
|
-
}
|
|
5721
|
-
}
|
|
5722
|
-
}
|
|
5723
|
-
function prodSourcePathToProductPath(sourceAbsPath, rootDir, dist) {
|
|
5724
|
-
const rel = path20.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
|
|
5725
|
-
if (!rel.startsWith("src/")) return null;
|
|
5726
|
-
const relWithoutSrc = rel.slice(4);
|
|
5727
|
-
const jsRel = relWithoutSrc.replace(/\.ts$/, ".js");
|
|
5728
|
-
return path20.resolve(rootDir, dist, jsRel);
|
|
5729
|
-
}
|
|
5730
|
-
function clearGeneratedSchemas() {
|
|
5731
|
-
state.generatedSchemas.clear();
|
|
5732
|
-
state.inFlightSchemaGenerations.clear();
|
|
5733
|
-
}
|
|
5734
|
-
async function ensureSchemaGenerated(schemaPath, routeFilePath, routes, rootDir, dist) {
|
|
5735
|
-
const inFlight2 = state.inFlightSchemaGenerations.get(schemaPath);
|
|
5736
|
-
if (inFlight2) {
|
|
5737
|
-
await inFlight2.catch(() => {
|
|
5738
|
-
});
|
|
5739
|
-
return false;
|
|
5740
|
-
}
|
|
5741
|
-
if (state.generatedSchemas.has(schemaPath)) {
|
|
5742
|
-
return false;
|
|
5743
|
-
}
|
|
5744
|
-
const prodAbsPath = path20.resolve(rootDir, routeFilePath);
|
|
5745
|
-
const sourceAbsPath = prodPathToSourcePath(prodAbsPath, rootDir, dist);
|
|
5746
|
-
if (!fs15.existsSync(sourceAbsPath)) {
|
|
5747
|
-
return false;
|
|
5748
|
-
}
|
|
5749
|
-
if (isProductFresh(sourceAbsPath, schemaPath)) {
|
|
5750
|
-
state.generatedSchemas.add(schemaPath);
|
|
5751
|
-
return false;
|
|
5752
|
-
}
|
|
5753
|
-
const fileRoutes = routes.filter((r) => r.filePath === routeFilePath);
|
|
5754
|
-
if (fileRoutes.length === 0) {
|
|
5755
|
-
return false;
|
|
5756
|
-
}
|
|
5757
|
-
const sourceRelPath = path20.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
|
|
5758
|
-
const sourceRoutes = fileRoutes.map((r) => ({ ...r, filePath: sourceRelPath }));
|
|
5759
|
-
const generatePromise = (async () => {
|
|
5760
|
-
await generateSchemaFiles(sourceRoutes, rootDir, dist);
|
|
5761
|
-
state.generatedSchemas.add(schemaPath);
|
|
5762
|
-
})();
|
|
5763
|
-
state.inFlightSchemaGenerations.set(schemaPath, generatePromise);
|
|
5764
|
-
try {
|
|
5765
|
-
await generatePromise;
|
|
5766
|
-
return true;
|
|
5767
|
-
} finally {
|
|
5768
|
-
state.inFlightSchemaGenerations.delete(schemaPath);
|
|
6019
|
+
});
|
|
6020
|
+
|
|
6021
|
+
// src/loader/resolveExports.ts
|
|
6022
|
+
function resolveExport(module, exportName) {
|
|
6023
|
+
if (exportName in module && typeof module[exportName] !== "undefined") {
|
|
6024
|
+
return module[exportName];
|
|
5769
6025
|
}
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
if (deleted.has(schemaPath)) continue;
|
|
5776
|
-
deleted.add(schemaPath);
|
|
5777
|
-
try {
|
|
5778
|
-
await fs15.promises.unlink(schemaPath);
|
|
5779
|
-
} catch {
|
|
6026
|
+
const defaultExport = module.default;
|
|
6027
|
+
if (defaultExport !== null && typeof defaultExport === "object") {
|
|
6028
|
+
const value = defaultExport[exportName];
|
|
6029
|
+
if (value !== void 0) {
|
|
6030
|
+
return value;
|
|
5780
6031
|
}
|
|
5781
6032
|
}
|
|
6033
|
+
return void 0;
|
|
5782
6034
|
}
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
const rel = path20.relative(rootDir, prodAbsPath).replace(/\\/g, "/");
|
|
5787
|
-
let relWithoutDist = rel;
|
|
5788
|
-
if (relWithoutDist.startsWith(`${dist}/`)) {
|
|
5789
|
-
relWithoutDist = relWithoutDist.slice(dist.length + 1);
|
|
6035
|
+
var init_resolveExports = __esm({
|
|
6036
|
+
"src/loader/resolveExports.ts"() {
|
|
6037
|
+
"use strict";
|
|
5790
6038
|
}
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
if (
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
6039
|
+
});
|
|
6040
|
+
|
|
6041
|
+
// src/loader/validateRouteModule.ts
|
|
6042
|
+
function validateRouteModule(value, method, filePath) {
|
|
6043
|
+
if (typeof value !== "function") {
|
|
6044
|
+
throw new Error(
|
|
6045
|
+
`Route module "${filePath}" does not export a valid handler for method "${method}". Expected a function, got ${typeof value}.`
|
|
6046
|
+
);
|
|
5799
6047
|
}
|
|
5800
|
-
sourcePathCache.set(prodAbsPath, result);
|
|
5801
|
-
return result;
|
|
5802
|
-
}
|
|
5803
|
-
function setDevOnDemandEnabled(enabled) {
|
|
5804
|
-
state.enabled = enabled;
|
|
5805
6048
|
}
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
}
|
|
5809
|
-
function setDevDist(dist) {
|
|
5810
|
-
state.distDir = dist;
|
|
5811
|
-
}
|
|
5812
|
-
function getDevDist() {
|
|
5813
|
-
return state.distDir;
|
|
5814
|
-
}
|
|
5815
|
-
var state, sourcePathCache;
|
|
5816
|
-
var init_compileOnDemand = __esm({
|
|
5817
|
-
"src/cli/compileOnDemand.ts"() {
|
|
6049
|
+
var init_validateRouteModule = __esm({
|
|
6050
|
+
"src/loader/validateRouteModule.ts"() {
|
|
5818
6051
|
"use strict";
|
|
5819
|
-
init_compileDevRoutes();
|
|
5820
|
-
init_generateSchemaFiles();
|
|
5821
|
-
init_generateSchemaFiles();
|
|
5822
|
-
init_collectImports();
|
|
5823
|
-
state = createDevOnDemandState();
|
|
5824
|
-
sourcePathCache = /* @__PURE__ */ new Map();
|
|
5825
6052
|
}
|
|
5826
6053
|
});
|
|
5827
6054
|
|
|
@@ -6011,14 +6238,14 @@ var init_httpErrors = __esm({
|
|
|
6011
6238
|
issues;
|
|
6012
6239
|
};
|
|
6013
6240
|
RouteNotFoundError = class extends FaapiError {
|
|
6014
|
-
constructor(
|
|
6015
|
-
super("ROUTE_NOT_FOUND", `Route not found: ${
|
|
6241
|
+
constructor(path31) {
|
|
6242
|
+
super("ROUTE_NOT_FOUND", `Route not found: ${path31}`, 404);
|
|
6016
6243
|
this.name = "RouteNotFoundError";
|
|
6017
6244
|
}
|
|
6018
6245
|
};
|
|
6019
6246
|
MethodNotAllowedError = class extends FaapiError {
|
|
6020
|
-
constructor(method,
|
|
6021
|
-
super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${
|
|
6247
|
+
constructor(method, path31, allowedMethods) {
|
|
6248
|
+
super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path31}`, 405);
|
|
6022
6249
|
this.allowedMethods = allowedMethods;
|
|
6023
6250
|
this.name = "MethodNotAllowedError";
|
|
6024
6251
|
}
|
|
@@ -6262,6 +6489,7 @@ function createContextFromUrl(request, url, params, config = {}, ip = "", regist
|
|
|
6262
6489
|
};
|
|
6263
6490
|
if (registries) {
|
|
6264
6491
|
ctx.registries = registries;
|
|
6492
|
+
ctx.tasks = registries.taskHandle.get(ctx);
|
|
6265
6493
|
}
|
|
6266
6494
|
const extend = config?.extendContext;
|
|
6267
6495
|
if (typeof extend === "function") {
|
|
@@ -6559,6 +6787,34 @@ var init_toResponse = __esm({
|
|
|
6559
6787
|
}
|
|
6560
6788
|
});
|
|
6561
6789
|
|
|
6790
|
+
// src/task/taskRegistry.ts
|
|
6791
|
+
function createTaskRegistry() {
|
|
6792
|
+
let registry = /* @__PURE__ */ new Map();
|
|
6793
|
+
return {
|
|
6794
|
+
hydrate(tasks) {
|
|
6795
|
+
const next = /* @__PURE__ */ new Map();
|
|
6796
|
+
for (const task of tasks) {
|
|
6797
|
+
next.set(task.name, task);
|
|
6798
|
+
}
|
|
6799
|
+
registry = next;
|
|
6800
|
+
},
|
|
6801
|
+
get(name) {
|
|
6802
|
+
return registry.get(name);
|
|
6803
|
+
},
|
|
6804
|
+
list() {
|
|
6805
|
+
return Array.from(registry.values());
|
|
6806
|
+
},
|
|
6807
|
+
clear() {
|
|
6808
|
+
registry = /* @__PURE__ */ new Map();
|
|
6809
|
+
}
|
|
6810
|
+
};
|
|
6811
|
+
}
|
|
6812
|
+
var init_taskRegistry = __esm({
|
|
6813
|
+
"src/task/taskRegistry.ts"() {
|
|
6814
|
+
"use strict";
|
|
6815
|
+
}
|
|
6816
|
+
});
|
|
6817
|
+
|
|
6562
6818
|
// src/injection/registries.ts
|
|
6563
6819
|
function createToolRegistry() {
|
|
6564
6820
|
let registry = /* @__PURE__ */ new Map();
|
|
@@ -6681,17 +6937,35 @@ function createAgentHandleStore() {
|
|
|
6681
6937
|
}
|
|
6682
6938
|
};
|
|
6683
6939
|
}
|
|
6940
|
+
function createTaskHandleStore() {
|
|
6941
|
+
let currentFactory = null;
|
|
6942
|
+
return {
|
|
6943
|
+
register(factory) {
|
|
6944
|
+
currentFactory = factory;
|
|
6945
|
+
},
|
|
6946
|
+
get(ctx) {
|
|
6947
|
+
if (currentFactory === null) return void 0;
|
|
6948
|
+
return currentFactory(ctx);
|
|
6949
|
+
},
|
|
6950
|
+
clear() {
|
|
6951
|
+
currentFactory = null;
|
|
6952
|
+
}
|
|
6953
|
+
};
|
|
6954
|
+
}
|
|
6684
6955
|
function createAppRegistries() {
|
|
6685
6956
|
const tool = createToolRegistry();
|
|
6686
6957
|
const agent = createAgentRegistry(tool);
|
|
6687
6958
|
const skill = createSkillRegistry();
|
|
6959
|
+
const task = createTaskRegistry();
|
|
6688
6960
|
const agentHandle = createAgentHandleStore();
|
|
6689
|
-
|
|
6961
|
+
const taskHandle = createTaskHandleStore();
|
|
6962
|
+
return { tool, agent, skill, task, agentHandle, taskHandle };
|
|
6690
6963
|
}
|
|
6691
6964
|
var defaultRegistries;
|
|
6692
6965
|
var init_registries = __esm({
|
|
6693
6966
|
"src/injection/registries.ts"() {
|
|
6694
6967
|
"use strict";
|
|
6968
|
+
init_taskRegistry();
|
|
6695
6969
|
defaultRegistries = createAppRegistries();
|
|
6696
6970
|
}
|
|
6697
6971
|
});
|
|
@@ -6758,6 +7032,9 @@ function getBuiltinInjectionValue(type, ctx, body) {
|
|
|
6758
7032
|
// Phase 3.5:调 @faapi/agent 插件注册的工厂获取 AgentHandle
|
|
6759
7033
|
case "agent":
|
|
6760
7034
|
return ctx.registries ? ctx.registries.agentHandle.get(ctx) : getAgentHandle(ctx);
|
|
7035
|
+
// 任务子系统:注入 TaskClient(入队/查询);未注册工厂(无 app 编排)时 undefined
|
|
7036
|
+
case "tasks":
|
|
7037
|
+
return ctx.registries ? ctx.registries.taskHandle.get(ctx) : void 0;
|
|
6761
7038
|
default:
|
|
6762
7039
|
return void 0;
|
|
6763
7040
|
}
|
|
@@ -6974,9 +7251,9 @@ async function validateInput(schemaPath, method, inputType, input) {
|
|
|
6974
7251
|
function mapZodIssues(error) {
|
|
6975
7252
|
return error.issues.map((issue) => {
|
|
6976
7253
|
const code = mapZodCode(issue);
|
|
6977
|
-
const
|
|
7254
|
+
const path31 = issue.path.map(String).join(".") || "";
|
|
6978
7255
|
return {
|
|
6979
|
-
path:
|
|
7256
|
+
path: path31,
|
|
6980
7257
|
code,
|
|
6981
7258
|
expected: issue.expected ?? mapExpectedFromMessage(issue.message),
|
|
6982
7259
|
received: issue.received ?? mapReceivedFromMessage(issue.message),
|
|
@@ -7474,9 +7751,9 @@ var init_wsHandler = __esm({
|
|
|
7474
7751
|
});
|
|
7475
7752
|
|
|
7476
7753
|
// src/server/handleWsUpgrade.ts
|
|
7477
|
-
import
|
|
7754
|
+
import fs17 from "fs";
|
|
7478
7755
|
import { WebSocketServer, WebSocket } from "ws";
|
|
7479
|
-
import
|
|
7756
|
+
import path23 from "path";
|
|
7480
7757
|
function getPathname(req) {
|
|
7481
7758
|
const url = req.url ?? "/";
|
|
7482
7759
|
const idx = url.indexOf("?");
|
|
@@ -7487,7 +7764,7 @@ async function loadWsHandler(filePath, ctx, rootDir) {
|
|
|
7487
7764
|
const dist = getDevDist();
|
|
7488
7765
|
if (dist) {
|
|
7489
7766
|
const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
|
|
7490
|
-
if (sourcePath &&
|
|
7767
|
+
if (sourcePath && fs17.existsSync(sourcePath)) {
|
|
7491
7768
|
await ensureCompiled(sourcePath, rootDir, dist);
|
|
7492
7769
|
}
|
|
7493
7770
|
}
|
|
@@ -7601,7 +7878,7 @@ function attachWebSocket(options) {
|
|
|
7601
7878
|
const finalHandler = async () => {
|
|
7602
7879
|
let handlers;
|
|
7603
7880
|
try {
|
|
7604
|
-
const absoluteFilePath =
|
|
7881
|
+
const absoluteFilePath = path23.resolve(rootDir, route.filePath);
|
|
7605
7882
|
handlers = await loadWsHandler(absoluteFilePath, ctx, rootDir);
|
|
7606
7883
|
} catch (err) {
|
|
7607
7884
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -7677,7 +7954,7 @@ import {
|
|
|
7677
7954
|
import { createSecureServer as createHttp2SecureServer } from "http2";
|
|
7678
7955
|
import { readFileSync } from "fs";
|
|
7679
7956
|
import { Readable as Readable3 } from "stream";
|
|
7680
|
-
import
|
|
7957
|
+
import path24 from "path";
|
|
7681
7958
|
function toWebRequest(req, bodyLimit = DEFAULT_BODY_LIMIT, requestSignal) {
|
|
7682
7959
|
const forwardedProto = req.headers["x-forwarded-proto"];
|
|
7683
7960
|
const protocol = Array.isArray(forwardedProto) ? forwardedProto[0]?.split(",")[0]?.trim() ?? "http" : forwardedProto?.split(",")[0]?.trim() ?? "http";
|
|
@@ -7873,7 +8150,7 @@ function getRoutePaths(route, rootDir, dist) {
|
|
|
7873
8150
|
let cached = routePathCache.get(route);
|
|
7874
8151
|
if (!cached) {
|
|
7875
8152
|
cached = {
|
|
7876
|
-
absFilePath:
|
|
8153
|
+
absFilePath: path24.resolve(rootDir, route.filePath),
|
|
7877
8154
|
schemaPath: getRuntimeSchemaPath(route.filePath, dist, rootDir)
|
|
7878
8155
|
};
|
|
7879
8156
|
routePathCache.set(route, cached);
|
|
@@ -8027,9 +8304,371 @@ var init_startServer = __esm({
|
|
|
8027
8304
|
}
|
|
8028
8305
|
});
|
|
8029
8306
|
|
|
8307
|
+
// src/task/memoryDriver.ts
|
|
8308
|
+
import { randomUUID } from "crypto";
|
|
8309
|
+
function createMemoryDriver() {
|
|
8310
|
+
const jobs = /* @__PURE__ */ new Map();
|
|
8311
|
+
const pendingByTask = /* @__PURE__ */ new Map();
|
|
8312
|
+
const runningCount = /* @__PURE__ */ new Map();
|
|
8313
|
+
const workers = /* @__PURE__ */ new Map();
|
|
8314
|
+
let stopped = false;
|
|
8315
|
+
function pump() {
|
|
8316
|
+
if (stopped) return;
|
|
8317
|
+
for (const [name, worker] of workers) {
|
|
8318
|
+
const queue = pendingByTask.get(name);
|
|
8319
|
+
if (!queue || queue.length === 0) continue;
|
|
8320
|
+
let running = runningCount.get(name) ?? 0;
|
|
8321
|
+
while (running < worker.concurrency && queue.length > 0) {
|
|
8322
|
+
const head = jobs.get(queue[0]);
|
|
8323
|
+
if (!head) {
|
|
8324
|
+
queue.shift();
|
|
8325
|
+
continue;
|
|
8326
|
+
}
|
|
8327
|
+
if (head.runAt > Date.now()) break;
|
|
8328
|
+
queue.shift();
|
|
8329
|
+
running += 1;
|
|
8330
|
+
runningCount.set(name, running);
|
|
8331
|
+
void dispatch(name, worker, head);
|
|
8332
|
+
}
|
|
8333
|
+
if (running === 0) runningCount.delete(name);
|
|
8334
|
+
else runningCount.set(name, running);
|
|
8335
|
+
}
|
|
8336
|
+
}
|
|
8337
|
+
async function dispatch(name, worker, job) {
|
|
8338
|
+
job.attempts += 1;
|
|
8339
|
+
const driverJob = {
|
|
8340
|
+
id: job.id,
|
|
8341
|
+
name,
|
|
8342
|
+
payload: job.payload,
|
|
8343
|
+
attempt: job.attempts,
|
|
8344
|
+
signal: job.controller.signal
|
|
8345
|
+
};
|
|
8346
|
+
try {
|
|
8347
|
+
await worker.process(driverJob);
|
|
8348
|
+
} catch (err) {
|
|
8349
|
+
if (job.attempts <= job.retries) {
|
|
8350
|
+
const delay = Math.min(RETRY_BASE_DELAY_MS * 2 ** (job.attempts - 1), RETRY_MAX_DELAY_MS);
|
|
8351
|
+
job.runAt = Date.now() + delay;
|
|
8352
|
+
job.timer = setTimeout(() => {
|
|
8353
|
+
job.timer = void 0;
|
|
8354
|
+
pendingByTask.get(name)?.push(job.id);
|
|
8355
|
+
pump();
|
|
8356
|
+
}, delay);
|
|
8357
|
+
}
|
|
8358
|
+
void err;
|
|
8359
|
+
} finally {
|
|
8360
|
+
const running = (runningCount.get(name) ?? 1) - 1;
|
|
8361
|
+
if (running <= 0) runningCount.delete(name);
|
|
8362
|
+
else runningCount.set(name, running);
|
|
8363
|
+
pump();
|
|
8364
|
+
}
|
|
8365
|
+
}
|
|
8366
|
+
return {
|
|
8367
|
+
async enqueue(name, payload, opts) {
|
|
8368
|
+
if (stopped) {
|
|
8369
|
+
throw new Error("[faapi] Task queue is stopped and no longer accepts jobs");
|
|
8370
|
+
}
|
|
8371
|
+
const id = randomUUID();
|
|
8372
|
+
const job = {
|
|
8373
|
+
id,
|
|
8374
|
+
payload,
|
|
8375
|
+
retries: opts?.retries ?? 0,
|
|
8376
|
+
runAt: opts?.delayMs ? Date.now() + opts.delayMs : Date.now(),
|
|
8377
|
+
attempts: 0,
|
|
8378
|
+
controller: new AbortController()
|
|
8379
|
+
};
|
|
8380
|
+
jobs.set(id, job);
|
|
8381
|
+
if (job.runAt > Date.now()) {
|
|
8382
|
+
job.timer = setTimeout(() => {
|
|
8383
|
+
job.timer = void 0;
|
|
8384
|
+
pendingByTask.get(name)?.push(id);
|
|
8385
|
+
pump();
|
|
8386
|
+
}, job.runAt - Date.now());
|
|
8387
|
+
} else {
|
|
8388
|
+
let q = pendingByTask.get(name);
|
|
8389
|
+
if (!q) {
|
|
8390
|
+
q = [];
|
|
8391
|
+
pendingByTask.set(name, q);
|
|
8392
|
+
}
|
|
8393
|
+
q.push(id);
|
|
8394
|
+
pump();
|
|
8395
|
+
}
|
|
8396
|
+
return id;
|
|
8397
|
+
},
|
|
8398
|
+
startWorker(name, opts) {
|
|
8399
|
+
workers.set(name, opts);
|
|
8400
|
+
pump();
|
|
8401
|
+
},
|
|
8402
|
+
async stop(timeoutMs = 1e4) {
|
|
8403
|
+
stopped = true;
|
|
8404
|
+
for (const job of jobs.values()) {
|
|
8405
|
+
if (job.timer) {
|
|
8406
|
+
clearTimeout(job.timer);
|
|
8407
|
+
job.timer = void 0;
|
|
8408
|
+
}
|
|
8409
|
+
}
|
|
8410
|
+
const start = Date.now();
|
|
8411
|
+
while (Date.now() - start < timeoutMs) {
|
|
8412
|
+
const total = Array.from(runningCount.values()).reduce((sum, n) => sum + n, 0);
|
|
8413
|
+
if (total === 0) return;
|
|
8414
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
8415
|
+
}
|
|
8416
|
+
for (const job of jobs.values()) {
|
|
8417
|
+
job.controller.abort();
|
|
8418
|
+
}
|
|
8419
|
+
},
|
|
8420
|
+
async stopWorkers() {
|
|
8421
|
+
workers.clear();
|
|
8422
|
+
}
|
|
8423
|
+
};
|
|
8424
|
+
}
|
|
8425
|
+
var RETRY_BASE_DELAY_MS, RETRY_MAX_DELAY_MS;
|
|
8426
|
+
var init_memoryDriver = __esm({
|
|
8427
|
+
"src/task/memoryDriver.ts"() {
|
|
8428
|
+
"use strict";
|
|
8429
|
+
RETRY_BASE_DELAY_MS = 500;
|
|
8430
|
+
RETRY_MAX_DELAY_MS = 3e4;
|
|
8431
|
+
}
|
|
8432
|
+
});
|
|
8433
|
+
|
|
8434
|
+
// src/task/taskQueue.ts
|
|
8435
|
+
import path25 from "path";
|
|
8436
|
+
function createTaskQueue(deps) {
|
|
8437
|
+
const { registry, rootDir } = deps;
|
|
8438
|
+
const driver = deps.driver ?? createMemoryDriver();
|
|
8439
|
+
const records = /* @__PURE__ */ new Map();
|
|
8440
|
+
const moduleCache2 = /* @__PURE__ */ new Map();
|
|
8441
|
+
const schemaCache = /* @__PURE__ */ new Map();
|
|
8442
|
+
let started = false;
|
|
8443
|
+
let stopped = false;
|
|
8444
|
+
const loadTaskModule = deps.loadTaskModule ?? (async (filePath) => await import(filePath));
|
|
8445
|
+
const loadPayloadSchema = deps.loadPayloadSchema ?? (async (filePath) => {
|
|
8446
|
+
const zodPath = path25.join(path25.dirname(filePath), "zod.js");
|
|
8447
|
+
try {
|
|
8448
|
+
const mod = await import(zodPath);
|
|
8449
|
+
const schemaKey = Object.keys(mod).find((k) => k.endsWith("Schema"));
|
|
8450
|
+
return schemaKey ? mod[schemaKey] : void 0;
|
|
8451
|
+
} catch {
|
|
8452
|
+
return void 0;
|
|
8453
|
+
}
|
|
8454
|
+
});
|
|
8455
|
+
function assertKnownTask(name) {
|
|
8456
|
+
const meta = registry.get(name);
|
|
8457
|
+
if (!meta) {
|
|
8458
|
+
const known = registry.list().map((t) => t.name).join(", ") || "(none)";
|
|
8459
|
+
throw new Error(`[faapi] Unknown task "${name}". Registered tasks: ${known}`);
|
|
8460
|
+
}
|
|
8461
|
+
}
|
|
8462
|
+
async function validatePayload(name, payload) {
|
|
8463
|
+
if (!schemaCache.has(name)) {
|
|
8464
|
+
const meta = registry.get(name);
|
|
8465
|
+
schemaCache.set(
|
|
8466
|
+
name,
|
|
8467
|
+
await loadPayloadSchema(path25.resolve(rootDir, meta.filePath)).catch(() => void 0)
|
|
8468
|
+
);
|
|
8469
|
+
}
|
|
8470
|
+
const schema = schemaCache.get(name);
|
|
8471
|
+
if (!schema || typeof schema.safeParse !== "function") {
|
|
8472
|
+
return payload;
|
|
8473
|
+
}
|
|
8474
|
+
const result = schema.safeParse(payload);
|
|
8475
|
+
if (!result.success) {
|
|
8476
|
+
throw new ValidationError(
|
|
8477
|
+
`Task payload validation failed for "${name}": ${JSON.stringify(result.error)}`,
|
|
8478
|
+
[]
|
|
8479
|
+
);
|
|
8480
|
+
}
|
|
8481
|
+
return result.data;
|
|
8482
|
+
}
|
|
8483
|
+
async function runJob(job) {
|
|
8484
|
+
const meta = registry.get(job.name);
|
|
8485
|
+
if (!meta) throw new Error(`[faapi] Unknown task "${job.name}" at worker dispatch`);
|
|
8486
|
+
const record = records.get(job.id) ?? {
|
|
8487
|
+
id: job.id,
|
|
8488
|
+
name: job.name,
|
|
8489
|
+
payload: job.payload,
|
|
8490
|
+
status: "pending",
|
|
8491
|
+
attempts: 0,
|
|
8492
|
+
createdAt: Date.now()
|
|
8493
|
+
};
|
|
8494
|
+
record.attempts = job.attempt;
|
|
8495
|
+
record.status = "running";
|
|
8496
|
+
record.error = void 0;
|
|
8497
|
+
records.set(job.id, record);
|
|
8498
|
+
try {
|
|
8499
|
+
let mod = moduleCache2.get(job.name);
|
|
8500
|
+
if (!mod) {
|
|
8501
|
+
mod = await loadTaskModule(path25.resolve(rootDir, meta.filePath));
|
|
8502
|
+
moduleCache2.set(job.name, mod);
|
|
8503
|
+
}
|
|
8504
|
+
if (typeof mod.run !== "function") {
|
|
8505
|
+
throw new Error(`Task "${job.name}" module has no run export`);
|
|
8506
|
+
}
|
|
8507
|
+
const taskCtx = {
|
|
8508
|
+
signal: job.signal,
|
|
8509
|
+
config: deps.config,
|
|
8510
|
+
job: { id: job.id, name: job.name, attempt: job.attempt }
|
|
8511
|
+
};
|
|
8512
|
+
const result = await mod.run(job.payload, taskCtx);
|
|
8513
|
+
record.status = "done";
|
|
8514
|
+
record.result = result;
|
|
8515
|
+
return result;
|
|
8516
|
+
} catch (err) {
|
|
8517
|
+
record.status = "failed";
|
|
8518
|
+
record.error = err instanceof Error ? err.message : String(err);
|
|
8519
|
+
throw err;
|
|
8520
|
+
}
|
|
8521
|
+
}
|
|
8522
|
+
async function startWorkers() {
|
|
8523
|
+
for (const meta of registry.list()) {
|
|
8524
|
+
await driver.startWorker(meta.name, {
|
|
8525
|
+
concurrency: meta.concurrency ?? 1,
|
|
8526
|
+
process: runJob
|
|
8527
|
+
});
|
|
8528
|
+
}
|
|
8529
|
+
}
|
|
8530
|
+
const queue = {
|
|
8531
|
+
async enqueue(name, payload = {}, opts) {
|
|
8532
|
+
if (stopped) {
|
|
8533
|
+
throw new Error("[faapi] Task queue is stopped and no longer accepts jobs");
|
|
8534
|
+
}
|
|
8535
|
+
assertKnownTask(name);
|
|
8536
|
+
const data = await validatePayload(name, payload);
|
|
8537
|
+
const meta = registry.get(name);
|
|
8538
|
+
const id = await driver.enqueue(name, data, {
|
|
8539
|
+
delayMs: opts?.delayMs,
|
|
8540
|
+
retries: meta.retries ?? 0
|
|
8541
|
+
});
|
|
8542
|
+
if (!records.has(id)) {
|
|
8543
|
+
records.set(id, {
|
|
8544
|
+
id,
|
|
8545
|
+
name,
|
|
8546
|
+
payload: data,
|
|
8547
|
+
status: "pending",
|
|
8548
|
+
attempts: 0,
|
|
8549
|
+
createdAt: Date.now(),
|
|
8550
|
+
...opts?.delayMs ? { runAt: Date.now() + opts.delayMs } : {}
|
|
8551
|
+
});
|
|
8552
|
+
}
|
|
8553
|
+
return { id };
|
|
8554
|
+
},
|
|
8555
|
+
list(name) {
|
|
8556
|
+
const snapshot = [];
|
|
8557
|
+
for (const job of records.values()) {
|
|
8558
|
+
if (name !== void 0 && job.name !== name) continue;
|
|
8559
|
+
snapshot.push({ ...job });
|
|
8560
|
+
}
|
|
8561
|
+
return snapshot;
|
|
8562
|
+
},
|
|
8563
|
+
async start() {
|
|
8564
|
+
if (stopped) {
|
|
8565
|
+
throw new Error("[faapi] Task queue is stopped and cannot be restarted");
|
|
8566
|
+
}
|
|
8567
|
+
if (started) return;
|
|
8568
|
+
started = true;
|
|
8569
|
+
await startWorkers();
|
|
8570
|
+
},
|
|
8571
|
+
async stop(timeoutMs = 1e4) {
|
|
8572
|
+
stopped = true;
|
|
8573
|
+
await driver.stop(timeoutMs);
|
|
8574
|
+
},
|
|
8575
|
+
async reload() {
|
|
8576
|
+
await driver.stopWorkers?.();
|
|
8577
|
+
await startWorkers();
|
|
8578
|
+
},
|
|
8579
|
+
invalidateModules() {
|
|
8580
|
+
moduleCache2.clear();
|
|
8581
|
+
},
|
|
8582
|
+
invalidateSchemas() {
|
|
8583
|
+
schemaCache.clear();
|
|
8584
|
+
}
|
|
8585
|
+
};
|
|
8586
|
+
return queue;
|
|
8587
|
+
}
|
|
8588
|
+
var init_taskQueue = __esm({
|
|
8589
|
+
"src/task/taskQueue.ts"() {
|
|
8590
|
+
"use strict";
|
|
8591
|
+
init_httpErrors();
|
|
8592
|
+
init_memoryDriver();
|
|
8593
|
+
}
|
|
8594
|
+
});
|
|
8595
|
+
|
|
8596
|
+
// src/task/loadTaskDriver.ts
|
|
8597
|
+
async function loadTaskDriver(driver, driverOptions) {
|
|
8598
|
+
if (driver === void 0 || driver === "memory") {
|
|
8599
|
+
return createMemoryDriver();
|
|
8600
|
+
}
|
|
8601
|
+
if (typeof driver === "object") {
|
|
8602
|
+
return driver;
|
|
8603
|
+
}
|
|
8604
|
+
if (driver !== "pgboss" && driver !== "bullmq") {
|
|
8605
|
+
throw new Error(
|
|
8606
|
+
`[faapi] Unknown task driver "${driver}". Supported: 'memory' (default), 'pgboss', 'bullmq', or a TaskDriver instance.`
|
|
8607
|
+
);
|
|
8608
|
+
}
|
|
8609
|
+
const specifier = `@faapi/task-${driver}`;
|
|
8610
|
+
let mod;
|
|
8611
|
+
try {
|
|
8612
|
+
mod = await import(
|
|
8613
|
+
/* @vite-ignore */
|
|
8614
|
+
specifier
|
|
8615
|
+
);
|
|
8616
|
+
} catch {
|
|
8617
|
+
throw new Error(
|
|
8618
|
+
`[faapi] Task driver "${driver}" requires package "${specifier}" to be installed in your project.`
|
|
8619
|
+
);
|
|
8620
|
+
}
|
|
8621
|
+
const factoryName = driver === "pgboss" ? "createPgBossDriver" : "createBullMQDriver";
|
|
8622
|
+
const factory = mod[factoryName];
|
|
8623
|
+
if (typeof factory !== "function") {
|
|
8624
|
+
throw new Error(
|
|
8625
|
+
`[faapi] Package "${specifier}" does not export ${factoryName}() \u2014 check the package version.`
|
|
8626
|
+
);
|
|
8627
|
+
}
|
|
8628
|
+
return factory(driverOptions);
|
|
8629
|
+
}
|
|
8630
|
+
var init_loadTaskDriver = __esm({
|
|
8631
|
+
"src/task/loadTaskDriver.ts"() {
|
|
8632
|
+
"use strict";
|
|
8633
|
+
init_memoryDriver();
|
|
8634
|
+
}
|
|
8635
|
+
});
|
|
8636
|
+
|
|
8637
|
+
// src/task/cronScheduler.ts
|
|
8638
|
+
import { Cron } from "croner";
|
|
8639
|
+
function createCronScheduler(registry, enqueue) {
|
|
8640
|
+
let schedules = [];
|
|
8641
|
+
return {
|
|
8642
|
+
start() {
|
|
8643
|
+
this.stop();
|
|
8644
|
+
for (const task of registry.list()) {
|
|
8645
|
+
if (!task.cron) continue;
|
|
8646
|
+
schedules.push(
|
|
8647
|
+
new Cron(task.cron, () => {
|
|
8648
|
+
void Promise.resolve().then(() => enqueue(task.name)).catch((err) => {
|
|
8649
|
+
console.error(`[faapi] Cron enqueue failed for task "${task.name}":`, err);
|
|
8650
|
+
});
|
|
8651
|
+
})
|
|
8652
|
+
);
|
|
8653
|
+
}
|
|
8654
|
+
},
|
|
8655
|
+
stop() {
|
|
8656
|
+
for (const schedule of schedules) {
|
|
8657
|
+
schedule.stop();
|
|
8658
|
+
}
|
|
8659
|
+
schedules = [];
|
|
8660
|
+
}
|
|
8661
|
+
};
|
|
8662
|
+
}
|
|
8663
|
+
var init_cronScheduler = __esm({
|
|
8664
|
+
"src/task/cronScheduler.ts"() {
|
|
8665
|
+
"use strict";
|
|
8666
|
+
}
|
|
8667
|
+
});
|
|
8668
|
+
|
|
8030
8669
|
// src/cli/loadPlugins.ts
|
|
8031
|
-
import
|
|
8032
|
-
import
|
|
8670
|
+
import path26 from "path";
|
|
8671
|
+
import fs18 from "fs";
|
|
8033
8672
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
8034
8673
|
async function loadPlugins(declarations, ctx, rootDir, dist) {
|
|
8035
8674
|
const handlerWrappers = [];
|
|
@@ -8092,37 +8731,37 @@ async function loadPlugins(declarations, ctx, rootDir, dist) {
|
|
|
8092
8731
|
return { handlerWrappers, upgradeWrappers, failures };
|
|
8093
8732
|
}
|
|
8094
8733
|
function resolveLocalPluginSource(specifier, baseDir) {
|
|
8095
|
-
const base =
|
|
8096
|
-
if ((TS_EXTS.test(base) || JS_EXTS.test(base)) &&
|
|
8734
|
+
const base = path26.isAbsolute(specifier) ? specifier : path26.resolve(baseDir, specifier);
|
|
8735
|
+
if ((TS_EXTS.test(base) || JS_EXTS.test(base)) && fs18.existsSync(base)) return base;
|
|
8097
8736
|
for (const ext of [".ts", ".js"]) {
|
|
8098
|
-
if (
|
|
8737
|
+
if (fs18.existsSync(base + ext)) return base + ext;
|
|
8099
8738
|
}
|
|
8100
8739
|
for (const indexExt of ["/index.ts", "/index.js"]) {
|
|
8101
|
-
if (
|
|
8740
|
+
if (fs18.existsSync(base + indexExt)) return base + indexExt;
|
|
8102
8741
|
}
|
|
8103
8742
|
return null;
|
|
8104
8743
|
}
|
|
8105
8744
|
function mirrorProductPath(sourcePath, baseDir, distDir) {
|
|
8106
|
-
let rel =
|
|
8745
|
+
let rel = path26.relative(baseDir, sourcePath).replace(/\\/g, "/");
|
|
8107
8746
|
if (rel.startsWith("src/")) rel = rel.slice(4);
|
|
8108
|
-
return
|
|
8747
|
+
return path26.resolve(distDir, rel.replace(TS_EXTS, ".js"));
|
|
8109
8748
|
}
|
|
8110
8749
|
function isSourceNewer(sourcePath, productPath) {
|
|
8111
8750
|
try {
|
|
8112
|
-
return
|
|
8751
|
+
return fs18.statSync(sourcePath).mtimeMs > fs18.statSync(productPath).mtimeMs;
|
|
8113
8752
|
} catch {
|
|
8114
8753
|
return true;
|
|
8115
8754
|
}
|
|
8116
8755
|
}
|
|
8117
8756
|
async function importPluginModule(specifier, baseDir, dist) {
|
|
8118
8757
|
const isRelative = specifier.startsWith("./") || specifier.startsWith("../");
|
|
8119
|
-
if (!isRelative && !
|
|
8758
|
+
if (!isRelative && !path26.isAbsolute(specifier)) {
|
|
8120
8759
|
return import(specifier);
|
|
8121
8760
|
}
|
|
8122
|
-
const distDir = dist ?
|
|
8761
|
+
const distDir = dist ? path26.resolve(baseDir, dist) : null;
|
|
8123
8762
|
const sourcePath = resolveLocalPluginSource(specifier, baseDir);
|
|
8124
8763
|
const productPath = sourcePath && distDir ? mirrorProductPath(sourcePath, baseDir, distDir) : null;
|
|
8125
|
-
if (productPath !== null &&
|
|
8764
|
+
if (productPath !== null && fs18.existsSync(productPath) && (sourcePath === null || !isSourceNewer(sourcePath, productPath))) {
|
|
8126
8765
|
return import(pathToFileURL2(productPath).href);
|
|
8127
8766
|
}
|
|
8128
8767
|
if (sourcePath && TS_EXTS.test(sourcePath)) {
|
|
@@ -8131,13 +8770,13 @@ async function importPluginModule(specifier, baseDir, dist) {
|
|
|
8131
8770
|
`Local plugin "${specifier}" has no up-to-date build artifact (dist is stale or missing). Run "faapi build" first, or use "faapi dev" for on-demand compilation.`
|
|
8132
8771
|
);
|
|
8133
8772
|
}
|
|
8134
|
-
const relFromRoot =
|
|
8773
|
+
const relFromRoot = path26.relative(baseDir, sourcePath).replace(/\\/g, "/");
|
|
8135
8774
|
if (relFromRoot.startsWith("src/")) {
|
|
8136
8775
|
await ensureCompiled(sourcePath, baseDir, dist);
|
|
8137
8776
|
} else {
|
|
8138
8777
|
await compileProjectModules([sourcePath], baseDir, dist);
|
|
8139
8778
|
}
|
|
8140
|
-
if (productPath &&
|
|
8779
|
+
if (productPath && fs18.existsSync(productPath)) {
|
|
8141
8780
|
return import(pathToFileURL2(productPath).href);
|
|
8142
8781
|
}
|
|
8143
8782
|
}
|
|
@@ -8149,7 +8788,7 @@ async function importPluginModule(specifier, baseDir, dist) {
|
|
|
8149
8788
|
`${specifier}/index.ts`,
|
|
8150
8789
|
`${specifier}.js`,
|
|
8151
8790
|
`${specifier}/index.js`
|
|
8152
|
-
].map((c) =>
|
|
8791
|
+
].map((c) => path26.isAbsolute(c) ? c : path26.join(baseDir, c));
|
|
8153
8792
|
throw new Error(
|
|
8154
8793
|
`Cannot find local plugin "${specifier}" (resolved from ${baseDir}). Expected one of:
|
|
8155
8794
|
${candidates.join("\n ")}
|
|
@@ -8184,12 +8823,12 @@ var init_loadPlugins = __esm({
|
|
|
8184
8823
|
});
|
|
8185
8824
|
|
|
8186
8825
|
// src/cli/createAppCore.ts
|
|
8187
|
-
import
|
|
8188
|
-
import
|
|
8826
|
+
import fs19 from "fs";
|
|
8827
|
+
import path27 from "path";
|
|
8189
8828
|
import { PassThrough, Readable as Readable4 } from "stream";
|
|
8190
8829
|
async function loadAndHydrateTools(rootDir, dist, registries = defaultRegistries) {
|
|
8191
|
-
const toolsPath =
|
|
8192
|
-
if (!
|
|
8830
|
+
const toolsPath = path27.resolve(rootDir, dist, TOOLS_FILE2);
|
|
8831
|
+
if (!fs19.existsSync(toolsPath)) {
|
|
8193
8832
|
return [];
|
|
8194
8833
|
}
|
|
8195
8834
|
const serialized = await importWithCacheBust(toolsPath);
|
|
@@ -8198,8 +8837,8 @@ async function loadAndHydrateTools(rootDir, dist, registries = defaultRegistries
|
|
|
8198
8837
|
return hydrated;
|
|
8199
8838
|
}
|
|
8200
8839
|
async function loadAndHydrateAgents(rootDir, dist, registries = defaultRegistries) {
|
|
8201
|
-
const agentsPath =
|
|
8202
|
-
if (!
|
|
8840
|
+
const agentsPath = path27.resolve(rootDir, dist, AGENTS_FILE2);
|
|
8841
|
+
if (!fs19.existsSync(agentsPath)) {
|
|
8203
8842
|
return [];
|
|
8204
8843
|
}
|
|
8205
8844
|
const serialized = await importWithCacheBust(agentsPath);
|
|
@@ -8207,6 +8846,24 @@ async function loadAndHydrateAgents(rootDir, dist, registries = defaultRegistrie
|
|
|
8207
8846
|
registries.agent.hydrate(hydrated);
|
|
8208
8847
|
return hydrated;
|
|
8209
8848
|
}
|
|
8849
|
+
function getTaskDriverOptions(config) {
|
|
8850
|
+
if (!config?.task) return void 0;
|
|
8851
|
+
const taskConfig = config.task;
|
|
8852
|
+
const driver = taskConfig.driver;
|
|
8853
|
+
if (driver === "pgboss") return taskConfig.pgboss;
|
|
8854
|
+
if (driver === "bullmq") return taskConfig.bullmq;
|
|
8855
|
+
return void 0;
|
|
8856
|
+
}
|
|
8857
|
+
async function loadAndHydrateTasks(rootDir, dist, registries = defaultRegistries) {
|
|
8858
|
+
const tasksPath = path27.resolve(rootDir, dist, TASKS_FILE);
|
|
8859
|
+
if (!fs19.existsSync(tasksPath)) {
|
|
8860
|
+
return [];
|
|
8861
|
+
}
|
|
8862
|
+
const serialized = await importWithCacheBust(tasksPath);
|
|
8863
|
+
const hydrated = hydrateTasks(serialized.tasks ?? []);
|
|
8864
|
+
registries.task.hydrate(hydrated);
|
|
8865
|
+
return hydrated;
|
|
8866
|
+
}
|
|
8210
8867
|
function getCurrentApp() {
|
|
8211
8868
|
return globalThis[APP_INSTANCE_KEY] ?? null;
|
|
8212
8869
|
}
|
|
@@ -8239,8 +8896,8 @@ function isFaapiConfigKey(key) {
|
|
|
8239
8896
|
async function createAppBase(options) {
|
|
8240
8897
|
const rootDir = options?.rootDir ?? process.cwd();
|
|
8241
8898
|
const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
|
|
8242
|
-
const routesPath =
|
|
8243
|
-
if (!
|
|
8899
|
+
const routesPath = path27.resolve(rootDir, dist, ROUTES_FILE);
|
|
8900
|
+
if (!fs19.existsSync(routesPath)) {
|
|
8244
8901
|
throw new Error(
|
|
8245
8902
|
`[faapi] ${dist}/${ROUTES_FILE} \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C \`faapi build\`\uFF08\u6216 \`faapi dev\`\uFF09\u751F\u6210\u4EA7\u7269\u3002`
|
|
8246
8903
|
);
|
|
@@ -8262,6 +8919,28 @@ async function createAppBase(options) {
|
|
|
8262
8919
|
const registries = createAppRegistries();
|
|
8263
8920
|
const tools = await loadAndHydrateTools(rootDir, dist, registries);
|
|
8264
8921
|
const agents = await loadAndHydrateAgents(rootDir, dist, registries);
|
|
8922
|
+
const taskMetas = await loadAndHydrateTasks(rootDir, dist, registries);
|
|
8923
|
+
const taskDriver = await loadTaskDriver(config?.task?.driver, getTaskDriverOptions(config));
|
|
8924
|
+
const taskQueue = createTaskQueue({
|
|
8925
|
+
registry: registries.task,
|
|
8926
|
+
rootDir,
|
|
8927
|
+
config,
|
|
8928
|
+
driver: taskDriver
|
|
8929
|
+
});
|
|
8930
|
+
const cronScheduler = createCronScheduler(
|
|
8931
|
+
registries.task,
|
|
8932
|
+
(name) => taskQueue.enqueue(name)
|
|
8933
|
+
);
|
|
8934
|
+
const taskEnabled = process.env.FAAPI_TASKS_DISABLED === "1" ? false : config?.task?.enabled ?? true;
|
|
8935
|
+
const stopTaskRuntime = async () => {
|
|
8936
|
+
cronScheduler.stop();
|
|
8937
|
+
await taskQueue.stop(config?.task?.shutdownTimeoutMs ?? 1e4);
|
|
8938
|
+
};
|
|
8939
|
+
if (taskEnabled) {
|
|
8940
|
+
taskQueue.start();
|
|
8941
|
+
cronScheduler.start();
|
|
8942
|
+
}
|
|
8943
|
+
registries.taskHandle.register(() => taskQueue);
|
|
8265
8944
|
const pluginConfig = config ? Object.fromEntries(Object.entries(config).filter(([k]) => !isFaapiConfigKey(k))) : {};
|
|
8266
8945
|
const { server, routesRef } = createServer({
|
|
8267
8946
|
routes: sorted,
|
|
@@ -8303,14 +8982,22 @@ async function createAppBase(options) {
|
|
|
8303
8982
|
routes: sorted,
|
|
8304
8983
|
wsRoutes,
|
|
8305
8984
|
rootDir,
|
|
8985
|
+
tasks: taskQueue,
|
|
8306
8986
|
async listen(listenPort) {
|
|
8307
8987
|
const envPort = process.env.PORT ? Number(process.env.PORT) : void 0;
|
|
8308
8988
|
const actualPort = listenPort ?? options?.port ?? envPort ?? DEFAULT_PORT;
|
|
8309
8989
|
if (config?.lifecycle?.onBoot) {
|
|
8310
8990
|
try {
|
|
8311
|
-
await config.lifecycle.onBoot({
|
|
8991
|
+
await config.lifecycle.onBoot({
|
|
8992
|
+
rootDir,
|
|
8993
|
+
routes: sorted,
|
|
8994
|
+
server,
|
|
8995
|
+
registries,
|
|
8996
|
+
tasks: taskQueue
|
|
8997
|
+
});
|
|
8312
8998
|
console.log("- onBoot hook executed");
|
|
8313
8999
|
} catch (err) {
|
|
9000
|
+
await stopTaskRuntime();
|
|
8314
9001
|
const message = err instanceof Error ? err.message : String(err);
|
|
8315
9002
|
console.error(`[faapi] onBoot hook failed: ${message}`);
|
|
8316
9003
|
throw err;
|
|
@@ -8358,9 +9045,22 @@ async function createAppBase(options) {
|
|
|
8358
9045
|
console.log(` ${agent.name} [${exports}] ${agent.filePath}`);
|
|
8359
9046
|
}
|
|
8360
9047
|
}
|
|
9048
|
+
if (taskMetas.length > 0) {
|
|
9049
|
+
console.log(`- Loaded ${taskMetas.length} task(s):`);
|
|
9050
|
+
for (const taskMeta of taskMetas) {
|
|
9051
|
+
const schedule = taskMeta.cron ? ` cron=${taskMeta.cron}` : "";
|
|
9052
|
+
console.log(` ${taskMeta.name}${schedule} ${taskMeta.filePath}`);
|
|
9053
|
+
}
|
|
9054
|
+
}
|
|
8361
9055
|
registerDefaultShutdownHandlers();
|
|
8362
9056
|
if (config?.lifecycle?.onReady) {
|
|
8363
|
-
await config.lifecycle.onReady({
|
|
9057
|
+
await config.lifecycle.onReady({
|
|
9058
|
+
rootDir,
|
|
9059
|
+
routes: sorted,
|
|
9060
|
+
server,
|
|
9061
|
+
registries,
|
|
9062
|
+
tasks: taskQueue
|
|
9063
|
+
});
|
|
8364
9064
|
console.log("- onReady hook executed");
|
|
8365
9065
|
}
|
|
8366
9066
|
app.server = server;
|
|
@@ -8453,13 +9153,22 @@ async function createAppBase(options) {
|
|
|
8453
9153
|
closed = true;
|
|
8454
9154
|
const s = server;
|
|
8455
9155
|
s.closeIdleConnections?.();
|
|
9156
|
+
await stopTaskRuntime();
|
|
8456
9157
|
if (config?.lifecycle?.onClose) {
|
|
8457
|
-
await config.lifecycle.onClose({
|
|
9158
|
+
await config.lifecycle.onClose({
|
|
9159
|
+
rootDir,
|
|
9160
|
+
routes: sorted,
|
|
9161
|
+
server,
|
|
9162
|
+
registries,
|
|
9163
|
+
tasks: taskQueue
|
|
9164
|
+
});
|
|
8458
9165
|
}
|
|
8459
9166
|
registries.tool.clear();
|
|
8460
9167
|
registries.agent.clear();
|
|
8461
9168
|
registries.skill.clear();
|
|
9169
|
+
registries.task.clear();
|
|
8462
9170
|
registries.agentHandle.clear();
|
|
9171
|
+
registries.taskHandle.clear();
|
|
8463
9172
|
if (!server.listening) {
|
|
8464
9173
|
app.server = null;
|
|
8465
9174
|
if (getCurrentApp() === app) setCurrentApp(null);
|
|
@@ -8496,6 +9205,7 @@ async function createAppBase(options) {
|
|
|
8496
9205
|
registries,
|
|
8497
9206
|
dist,
|
|
8498
9207
|
patterns: ROUTE_PATTERNS,
|
|
9208
|
+
taskQueue,
|
|
8499
9209
|
server,
|
|
8500
9210
|
routesRef,
|
|
8501
9211
|
config,
|
|
@@ -8522,6 +9232,10 @@ var init_createAppCore = __esm({
|
|
|
8522
9232
|
init_generateRoutes();
|
|
8523
9233
|
init_generateToolArtifacts();
|
|
8524
9234
|
init_generateAgentArtifacts();
|
|
9235
|
+
init_generateTaskArtifacts();
|
|
9236
|
+
init_taskQueue();
|
|
9237
|
+
init_loadTaskDriver();
|
|
9238
|
+
init_cronScheduler();
|
|
8525
9239
|
init_loadPlugins();
|
|
8526
9240
|
init_importWithCacheBust();
|
|
8527
9241
|
init_registries();
|
|
@@ -8547,12 +9261,14 @@ var init_createAppCore = __esm({
|
|
|
8547
9261
|
"logger",
|
|
8548
9262
|
"http2",
|
|
8549
9263
|
"trustedProxy",
|
|
8550
|
-
"response"
|
|
9264
|
+
"response",
|
|
9265
|
+
"task"
|
|
8551
9266
|
]);
|
|
8552
9267
|
}
|
|
8553
9268
|
});
|
|
8554
9269
|
|
|
8555
9270
|
// src/cli/createDevApp.ts
|
|
9271
|
+
import path28 from "path";
|
|
8556
9272
|
async function createDevApp(options) {
|
|
8557
9273
|
const { app, ctx } = await createAppBase(options);
|
|
8558
9274
|
const devApp = app;
|
|
@@ -8597,6 +9313,19 @@ async function createDevApp(options) {
|
|
|
8597
9313
|
await generateAgentArtifacts(agents, ctx.rootDir, ctx.dist);
|
|
8598
9314
|
await loadAndHydrateAgents(ctx.rootDir, ctx.dist, ctx.registries);
|
|
8599
9315
|
};
|
|
9316
|
+
devApp.reloadTasks = async () => {
|
|
9317
|
+
setLoadTimestamp(Date.now());
|
|
9318
|
+
invalidateProgramCache();
|
|
9319
|
+
const tasks = await scanTasks(ctx.rootDir, TASK_PATTERNS);
|
|
9320
|
+
for (const task of tasks) {
|
|
9321
|
+
await ensureCompiled(path28.resolve(ctx.rootDir, task.filePath), ctx.rootDir, ctx.dist);
|
|
9322
|
+
}
|
|
9323
|
+
await generateTaskArtifacts(tasks, ctx.rootDir, ctx.dist);
|
|
9324
|
+
ctx.taskQueue.invalidateModules();
|
|
9325
|
+
ctx.taskQueue.invalidateSchemas();
|
|
9326
|
+
await ctx.taskQueue.reload();
|
|
9327
|
+
await loadAndHydrateTasks(ctx.rootDir, ctx.dist, ctx.registries);
|
|
9328
|
+
};
|
|
8600
9329
|
return devApp;
|
|
8601
9330
|
}
|
|
8602
9331
|
var init_createDevApp = __esm({
|
|
@@ -8611,6 +9340,9 @@ var init_createDevApp = __esm({
|
|
|
8611
9340
|
init_scanAgents();
|
|
8612
9341
|
init_scanAgents();
|
|
8613
9342
|
init_generateAgentArtifacts();
|
|
9343
|
+
init_scanTasks();
|
|
9344
|
+
init_generateTaskArtifacts();
|
|
9345
|
+
init_compileOnDemand();
|
|
8614
9346
|
init_loadMiddlewares();
|
|
8615
9347
|
init_createProgram();
|
|
8616
9348
|
init_validateInput();
|
|
@@ -8625,9 +9357,10 @@ __export(devCommand_exports, {
|
|
|
8625
9357
|
devCommand: () => devCommand,
|
|
8626
9358
|
generateAgentArtifactsForDev: () => generateAgentArtifactsForDev,
|
|
8627
9359
|
generateRouteArtifacts: () => generateRouteArtifacts,
|
|
9360
|
+
generateTaskArtifactsForDev: () => generateTaskArtifactsForDev,
|
|
8628
9361
|
generateToolArtifactsForDev: () => generateToolArtifactsForDev
|
|
8629
9362
|
});
|
|
8630
|
-
import
|
|
9363
|
+
import path29 from "path";
|
|
8631
9364
|
async function devCommand(options) {
|
|
8632
9365
|
const rootDir = process.cwd();
|
|
8633
9366
|
if (!process.env.NODE_ENV) process.env.NODE_ENV = "development";
|
|
@@ -8646,6 +9379,8 @@ async function devCommand(options) {
|
|
|
8646
9379
|
await generateToolArtifactsForDev(rootDir, devDist);
|
|
8647
9380
|
console.log("- Generating agent manifest...");
|
|
8648
9381
|
await generateAgentArtifactsForDev(rootDir, devDist);
|
|
9382
|
+
console.log("- Generating task artifacts...");
|
|
9383
|
+
await generateTaskArtifactsForDev(rootDir, devDist);
|
|
8649
9384
|
console.log("- Starting dev app...");
|
|
8650
9385
|
const app = await createDevApp({ rootDir, port: options?.port });
|
|
8651
9386
|
await app.listen();
|
|
@@ -8654,7 +9389,7 @@ async function devCommand(options) {
|
|
|
8654
9389
|
async function generateRouteArtifacts(rootDir, patterns, dist) {
|
|
8655
9390
|
const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, dist);
|
|
8656
9391
|
const sorted = sortRoutes(routes);
|
|
8657
|
-
const routesPath =
|
|
9392
|
+
const routesPath = path29.resolve(rootDir, dist, ROUTES_FILE2);
|
|
8658
9393
|
const serialized = serializeRoutes(sorted, wsRoutes, rootDir, dist);
|
|
8659
9394
|
await writeRoutesModule(serialized, routesPath);
|
|
8660
9395
|
}
|
|
@@ -8666,6 +9401,13 @@ async function generateAgentArtifactsForDev(rootDir, dist) {
|
|
|
8666
9401
|
const agents = await scanAgents(rootDir, DEFAULT_AGENT_PATTERNS);
|
|
8667
9402
|
await generateAgentArtifacts(agents, rootDir, dist);
|
|
8668
9403
|
}
|
|
9404
|
+
async function generateTaskArtifactsForDev(rootDir, dist) {
|
|
9405
|
+
const tasks = await scanTasks(rootDir, TASK_PATTERNS);
|
|
9406
|
+
for (const task of tasks) {
|
|
9407
|
+
await ensureCompiled(path29.resolve(rootDir, task.filePath), rootDir, dist);
|
|
9408
|
+
}
|
|
9409
|
+
await generateTaskArtifacts(tasks, rootDir, dist);
|
|
9410
|
+
}
|
|
8669
9411
|
var DEV_DIST, ROUTES_FILE2;
|
|
8670
9412
|
var init_devCommand = __esm({
|
|
8671
9413
|
"src/cli/devCommand.ts"() {
|
|
@@ -8680,6 +9422,9 @@ var init_devCommand = __esm({
|
|
|
8680
9422
|
init_scanAgents();
|
|
8681
9423
|
init_scanAgents();
|
|
8682
9424
|
init_generateAgentArtifacts();
|
|
9425
|
+
init_scanTasks();
|
|
9426
|
+
init_generateTaskArtifacts();
|
|
9427
|
+
init_compileOnDemand();
|
|
8683
9428
|
init_loadConfig();
|
|
8684
9429
|
init_loadEnv();
|
|
8685
9430
|
init_watcher();
|
|
@@ -8707,15 +9452,15 @@ var buildCommand_exports = {};
|
|
|
8707
9452
|
__export(buildCommand_exports, {
|
|
8708
9453
|
buildCommand: () => buildCommand
|
|
8709
9454
|
});
|
|
8710
|
-
import
|
|
8711
|
-
import
|
|
9455
|
+
import path30 from "path";
|
|
9456
|
+
import fs20 from "fs";
|
|
8712
9457
|
async function buildCommand(options) {
|
|
8713
9458
|
const rootDir = options?.rootDir ?? process.cwd();
|
|
8714
9459
|
const outdir = options?.dist ?? DEFAULT_DIST2;
|
|
8715
|
-
const absOut =
|
|
8716
|
-
const realRoot = toRealPath(
|
|
9460
|
+
const absOut = path30.resolve(rootDir, outdir);
|
|
9461
|
+
const realRoot = toRealPath(path30.resolve(rootDir));
|
|
8717
9462
|
if (isInsideDir(toRealPath(absOut), realRoot)) {
|
|
8718
|
-
|
|
9463
|
+
fs20.rmSync(absOut, { recursive: true, force: true });
|
|
8719
9464
|
} else {
|
|
8720
9465
|
console.warn(
|
|
8721
9466
|
`! Output directory "${outdir}" is outside the project root, skipping clean (stale artifacts may remain)`
|
|
@@ -8723,10 +9468,10 @@ async function buildCommand(options) {
|
|
|
8723
9468
|
}
|
|
8724
9469
|
await compileConfig({ rootDir, dist: outdir });
|
|
8725
9470
|
const _config = await loadConfig(rootDir, outdir);
|
|
8726
|
-
const pkgPath =
|
|
8727
|
-
if (
|
|
9471
|
+
const pkgPath = path30.resolve(rootDir, "package.json");
|
|
9472
|
+
if (fs20.existsSync(pkgPath)) {
|
|
8728
9473
|
try {
|
|
8729
|
-
const pkg = JSON.parse(
|
|
9474
|
+
const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf-8"));
|
|
8730
9475
|
if (pkg.type !== "module") {
|
|
8731
9476
|
console.warn(
|
|
8732
9477
|
'! package.json is missing "type": "module" \u2014 build output is ESM and `node dist/main` will fail without it'
|
|
@@ -8754,7 +9499,7 @@ async function buildCommand(options) {
|
|
|
8754
9499
|
if (localPluginSources.length > 0) {
|
|
8755
9500
|
console.log("\n[2.5/8] Compiling local plugins...");
|
|
8756
9501
|
const outside = localPluginSources.filter((p) => {
|
|
8757
|
-
const rel =
|
|
9502
|
+
const rel = path30.relative(rootDir, p).replace(/\\/g, "/");
|
|
8758
9503
|
return !rel.startsWith("src/");
|
|
8759
9504
|
});
|
|
8760
9505
|
if (outside.length > 0) {
|
|
@@ -8778,9 +9523,9 @@ async function buildCommand(options) {
|
|
|
8778
9523
|
}
|
|
8779
9524
|
console.log("\n[4/8] Generating schema...");
|
|
8780
9525
|
await generateSchemaFiles(sorted, rootDir, outdir);
|
|
8781
|
-
console.log(` Schema: zod.js files under ${
|
|
9526
|
+
console.log(` Schema: zod.js files under ${path30.resolve(rootDir, outdir)}`);
|
|
8782
9527
|
console.log("\n[5/8] Generating routes manifest...");
|
|
8783
|
-
const routesPath =
|
|
9528
|
+
const routesPath = path30.resolve(rootDir, outdir, "faapi-routes.js");
|
|
8784
9529
|
const serialized = serializeRoutes(sorted, wsRoutes, rootDir, outdir);
|
|
8785
9530
|
await writeRoutesModule(serialized, routesPath);
|
|
8786
9531
|
console.log(` Written to ${routesPath}`);
|
|
@@ -8788,14 +9533,19 @@ async function buildCommand(options) {
|
|
|
8788
9533
|
const tools = await scanTools(rootDir, TOOL_PATTERNS);
|
|
8789
9534
|
const toolMeta = await generateToolArtifacts(tools, rootDir, outdir);
|
|
8790
9535
|
console.log(` Found ${toolMeta.length} tool(s)`);
|
|
8791
|
-
console.log(` Tool manifest: ${
|
|
9536
|
+
console.log(` Tool manifest: ${path30.resolve(rootDir, outdir, "faapi-tools.js")}`);
|
|
8792
9537
|
console.log("\n[7/8] Generating agent manifest...");
|
|
8793
9538
|
const agents = await scanAgents(rootDir, DEFAULT_AGENT_PATTERNS);
|
|
8794
9539
|
const agentMeta = await generateAgentArtifacts(agents, rootDir, outdir);
|
|
8795
9540
|
console.log(` Found ${agentMeta.length} agent(s)`);
|
|
8796
|
-
console.log(` Agent manifest: ${
|
|
9541
|
+
console.log(` Agent manifest: ${path30.resolve(rootDir, outdir, "faapi-agents.js")}`);
|
|
9542
|
+
console.log("\n[7.5/8] Generating task manifest and schema...");
|
|
9543
|
+
const tasks = await scanTasks(rootDir, TASK_PATTERNS);
|
|
9544
|
+
const taskMeta = await generateTaskArtifacts(tasks, rootDir, outdir);
|
|
9545
|
+
console.log(` Found ${taskMeta.length} task(s)`);
|
|
9546
|
+
console.log(` Task manifest: ${path30.resolve(rootDir, outdir, "faapi-tasks.js")}`);
|
|
8797
9547
|
console.log("\n[8/8] Generating entry file...");
|
|
8798
|
-
const mainPath =
|
|
9548
|
+
const mainPath = path30.resolve(rootDir, outdir, "main.js");
|
|
8799
9549
|
const createProdAppArgs = options?.dist && options.dist !== DEFAULT_DIST2 ? `{ dist: ${JSON.stringify(outdir)} }` : "";
|
|
8800
9550
|
const mainContent = `// \u7531 faapi build \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u7F16\u8F91
|
|
8801
9551
|
import { createProdApp, loadEnv } from '@faapi/faapi';
|
|
@@ -8807,7 +9557,7 @@ loadEnv(process.cwd());
|
|
|
8807
9557
|
const app = await createProdApp(${createProdAppArgs});
|
|
8808
9558
|
await app.listen();
|
|
8809
9559
|
`;
|
|
8810
|
-
await
|
|
9560
|
+
await fs20.promises.writeFile(mainPath, mainContent, "utf-8");
|
|
8811
9561
|
console.log(` Written to ${mainPath}`);
|
|
8812
9562
|
console.log("\nfaapi build completed");
|
|
8813
9563
|
}
|
|
@@ -8821,7 +9571,7 @@ function extractLocalPluginSources(declarations, rootDir) {
|
|
|
8821
9571
|
else if (Array.isArray(decl)) specifier = decl[0];
|
|
8822
9572
|
else if ("path" in decl) specifier = decl.path;
|
|
8823
9573
|
if (!specifier) continue;
|
|
8824
|
-
if (!specifier.startsWith("./") && !specifier.startsWith("../") && !
|
|
9574
|
+
if (!specifier.startsWith("./") && !specifier.startsWith("../") && !path30.isAbsolute(specifier)) {
|
|
8825
9575
|
continue;
|
|
8826
9576
|
}
|
|
8827
9577
|
const source = resolveLocalPluginSource(specifier, rootDir);
|
|
@@ -8845,6 +9595,8 @@ var init_buildCommand = __esm({
|
|
|
8845
9595
|
init_scanAgents();
|
|
8846
9596
|
init_scanAgents();
|
|
8847
9597
|
init_generateAgentArtifacts();
|
|
9598
|
+
init_scanTasks();
|
|
9599
|
+
init_generateTaskArtifacts();
|
|
8848
9600
|
init_generateSchemaFiles();
|
|
8849
9601
|
init_generateRoutes();
|
|
8850
9602
|
init_compileBuildRoutes();
|