@faapi/faapi 6.2.0 → 6.4.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 +1397 -499
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +156 -3
- package/dist/index.js +1090 -259
- package/dist/index.js.map +1 -1
- package/dist/{routeTypes-Bm--uTbm.d.ts → routeTypes-_17rXzal.d.ts} +355 -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,503 @@ var init_generateAgentArtifacts = __esm({
|
|
|
3370
3372
|
}
|
|
3371
3373
|
});
|
|
3372
3374
|
|
|
3373
|
-
// src/
|
|
3375
|
+
// src/task/scanTasks.ts
|
|
3376
|
+
import fg4 from "fast-glob";
|
|
3374
3377
|
import path16 from "path";
|
|
3375
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
|
+
const timeoutMs = TIMEOUT_MS_RE.exec(source)?.[1];
|
|
3416
|
+
if (cron !== void 0) manifest.cron = cron;
|
|
3417
|
+
if (concurrency !== void 0) manifest.concurrency = Number(concurrency);
|
|
3418
|
+
if (retries !== void 0) manifest.retries = Number(retries);
|
|
3419
|
+
if (timeoutMs !== void 0) manifest.timeoutMs = Number(timeoutMs);
|
|
3420
|
+
tasks.push(manifest);
|
|
3421
|
+
}
|
|
3422
|
+
return tasks;
|
|
3423
|
+
}
|
|
3424
|
+
var TASK_PATTERNS, TASK_FILENAME, CRON_RE, CONCURRENCY_RE, RETRIES_RE, TIMEOUT_MS_RE;
|
|
3425
|
+
var init_scanTasks = __esm({
|
|
3426
|
+
"src/task/scanTasks.ts"() {
|
|
3427
|
+
"use strict";
|
|
3428
|
+
TASK_PATTERNS = ["src/tasks/**/task.ts"];
|
|
3429
|
+
TASK_FILENAME = "task.ts";
|
|
3430
|
+
CRON_RE = /(?:^|\n)\s*cron:\s*['"`]([^'"`\n]+)['"`]/;
|
|
3431
|
+
CONCURRENCY_RE = /(?:^|\n)\s*concurrency:\s*(\d+)/;
|
|
3432
|
+
RETRIES_RE = /(?:^|\n)\s*retries:\s*(\d+)/;
|
|
3433
|
+
TIMEOUT_MS_RE = /(?:^|\n)\s*timeoutMs:\s*(\d+)/;
|
|
3434
|
+
}
|
|
3435
|
+
});
|
|
3436
|
+
|
|
3437
|
+
// src/cli/generateTaskArtifacts.ts
|
|
3438
|
+
import path17 from "path";
|
|
3439
|
+
function serializeTasks(tasks, dist = "dist") {
|
|
3440
|
+
return tasks.map((t) => ({
|
|
3441
|
+
name: t.name,
|
|
3442
|
+
filePath: toProdFilePath(t.filePath, dist),
|
|
3443
|
+
...t.cron !== void 0 ? { cron: t.cron } : {},
|
|
3444
|
+
...t.concurrency !== void 0 ? { concurrency: t.concurrency } : {},
|
|
3445
|
+
...t.retries !== void 0 ? { retries: t.retries } : {},
|
|
3446
|
+
...t.timeoutMs !== void 0 ? { timeoutMs: t.timeoutMs } : {}
|
|
3447
|
+
}));
|
|
3448
|
+
}
|
|
3449
|
+
async function writeTasksModule(manifest, outputPath) {
|
|
3450
|
+
const content = `// \u81EA\u52A8\u751F\u6210,\u8BF7\u52FF\u624B\u52A8\u7F16\u8F91(faapi build/dev \u4EA7\u7269)
|
|
3451
|
+
export const tasks = ${JSON.stringify(manifest, null, 2)};
|
|
3452
|
+
`;
|
|
3453
|
+
await atomicWriteFile(outputPath, content);
|
|
3454
|
+
}
|
|
3455
|
+
function hydrateTasks(manifest) {
|
|
3456
|
+
return manifest.map((t) => ({
|
|
3457
|
+
name: t.name,
|
|
3458
|
+
filePath: t.filePath,
|
|
3459
|
+
cron: t.cron ?? void 0,
|
|
3460
|
+
concurrency: t.concurrency ?? void 0,
|
|
3461
|
+
retries: t.retries ?? void 0,
|
|
3462
|
+
timeoutMs: t.timeoutMs ?? void 0
|
|
3463
|
+
}));
|
|
3464
|
+
}
|
|
3465
|
+
function collectTaskSchemaSources(tasks, rootDir) {
|
|
3466
|
+
const tasksByFile = /* @__PURE__ */ new Map();
|
|
3467
|
+
for (const task of tasks) {
|
|
3468
|
+
if (!task.inputTypeName) continue;
|
|
3469
|
+
const absPath = path17.resolve(rootDir, task.filePath);
|
|
3470
|
+
let list = tasksByFile.get(absPath);
|
|
3471
|
+
if (!list) {
|
|
3472
|
+
list = [];
|
|
3473
|
+
tasksByFile.set(absPath, list);
|
|
3474
|
+
}
|
|
3475
|
+
list.push(task);
|
|
3476
|
+
}
|
|
3477
|
+
const programByFile = createPrograms([...tasksByFile.keys()]);
|
|
3478
|
+
const resolversByFile = /* @__PURE__ */ new Map();
|
|
3479
|
+
for (const filePath of tasksByFile.keys()) {
|
|
3480
|
+
resolversByFile.set(filePath, createLazyTypeResolver(programByFile.get(filePath), filePath));
|
|
3481
|
+
}
|
|
3482
|
+
const sources = [];
|
|
3483
|
+
for (const [filePath, fileTasks] of tasksByFile) {
|
|
3484
|
+
const program = programByFile.get(filePath);
|
|
3485
|
+
for (const task of fileTasks) {
|
|
3486
|
+
const typeInfo = extractTypeInfo(program, filePath, task.inputTypeName);
|
|
3487
|
+
sources.push({
|
|
3488
|
+
name: task.taskName,
|
|
3489
|
+
filePath,
|
|
3490
|
+
schemaName: task.inputTypeName,
|
|
3491
|
+
typeInfo
|
|
3492
|
+
});
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
return { sources, resolversByFile };
|
|
3496
|
+
}
|
|
3497
|
+
function generateTaskSchemaFileSource(sources, resolveType, helpersImportPath) {
|
|
3498
|
+
const lines = ["import { z } from 'zod';"];
|
|
3499
|
+
const schemaBlocks = [];
|
|
3500
|
+
for (const source of sources) {
|
|
3501
|
+
if (!source.typeInfo) continue;
|
|
3502
|
+
const block = [`// task ${source.name} \u2192 ${source.schemaName}`];
|
|
3503
|
+
const schemaCode = generateZodSchemaSource(
|
|
3504
|
+
source.typeInfo,
|
|
3505
|
+
resolveType,
|
|
3506
|
+
source.schemaName,
|
|
3507
|
+
false
|
|
3508
|
+
).replace(/^import \{ z \} from 'zod';\s*\n\s*\n/, "");
|
|
3509
|
+
block.push(schemaCode);
|
|
3510
|
+
block.push("");
|
|
3511
|
+
schemaBlocks.push(block.join("\n"));
|
|
3512
|
+
}
|
|
3513
|
+
const allSchemaCode = schemaBlocks.join("\n");
|
|
3514
|
+
if (helpersImportPath && usesCoerceHelpers(allSchemaCode)) {
|
|
3515
|
+
lines.push(
|
|
3516
|
+
`import { coerceNumber, coerceBoolean, coerceMap, coerceSet } from '${helpersImportPath}';`
|
|
3517
|
+
);
|
|
3518
|
+
}
|
|
3519
|
+
lines.push("");
|
|
3520
|
+
lines.push(...schemaBlocks);
|
|
3521
|
+
return lines.join("\n").replace(/\n+$/, "\n");
|
|
3522
|
+
}
|
|
3523
|
+
async function generateTaskArtifacts(manifests, rootDir, dist) {
|
|
3524
|
+
const metadata = [];
|
|
3525
|
+
if (manifests.length > 0) {
|
|
3526
|
+
const programByFile = createPrograms(manifests.map((m) => path17.resolve(rootDir, m.filePath)));
|
|
3527
|
+
for (const manifest of manifests) {
|
|
3528
|
+
const absPath = path17.resolve(rootDir, manifest.filePath);
|
|
3529
|
+
const result = extractToolMetadata(programByFile.get(absPath), absPath, "run", {
|
|
3530
|
+
name: manifest.name,
|
|
3531
|
+
filePath: manifest.filePath
|
|
3532
|
+
});
|
|
3533
|
+
if (result) {
|
|
3534
|
+
metadata.push({ ...result, taskName: manifest.name });
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
const serialized = serializeTasks(manifests, dist);
|
|
3539
|
+
const tasksPath = path17.resolve(rootDir, dist, TASKS_FILE);
|
|
3540
|
+
await writeTasksModule(serialized, tasksPath);
|
|
3541
|
+
if (metadata.length === 0) {
|
|
3542
|
+
return hydrateTasks(serialized);
|
|
3543
|
+
}
|
|
3544
|
+
const { sources, resolversByFile } = collectTaskSchemaSources(metadata, rootDir);
|
|
3545
|
+
if (sources.length === 0) {
|
|
3546
|
+
return hydrateTasks(serialized);
|
|
3547
|
+
}
|
|
3548
|
+
const sourcesByFile = /* @__PURE__ */ new Map();
|
|
3549
|
+
for (const source of sources) {
|
|
3550
|
+
let list = sourcesByFile.get(source.filePath);
|
|
3551
|
+
if (!list) {
|
|
3552
|
+
list = [];
|
|
3553
|
+
sourcesByFile.set(source.filePath, list);
|
|
3554
|
+
}
|
|
3555
|
+
list.push(source);
|
|
3556
|
+
}
|
|
3557
|
+
const fileEntries = [];
|
|
3558
|
+
for (const [filePath, fileSources] of sourcesByFile) {
|
|
3559
|
+
const relFile = path17.relative(rootDir, filePath).replace(/\\/g, "/");
|
|
3560
|
+
const outputPath = getSchemaOutputPath(relFile, dist, rootDir);
|
|
3561
|
+
const resolver = resolversByFile.get(filePath);
|
|
3562
|
+
let relForDir = relFile;
|
|
3563
|
+
if (relForDir.startsWith("src/")) {
|
|
3564
|
+
relForDir = relForDir.slice(4);
|
|
3565
|
+
}
|
|
3566
|
+
const dirIdx = relForDir.lastIndexOf("/");
|
|
3567
|
+
const zodRelDir = dirIdx >= 0 ? relForDir.slice(0, dirIdx) : "";
|
|
3568
|
+
const helpersImportPath = getHelpersImportPath(zodRelDir);
|
|
3569
|
+
const source = generateTaskSchemaFileSource(
|
|
3570
|
+
fileSources,
|
|
3571
|
+
(name) => resolver?.resolve(name)?.runtimeType,
|
|
3572
|
+
helpersImportPath
|
|
3573
|
+
);
|
|
3574
|
+
fileEntries.push({ outputPath, source });
|
|
3575
|
+
}
|
|
3576
|
+
const allSourceCode = fileEntries.map((e) => e.source).join("\n");
|
|
3577
|
+
if (usesCoerceHelpers(allSourceCode)) {
|
|
3578
|
+
const helpersPath = path17.resolve(rootDir, dist, HELPERS_FILENAME);
|
|
3579
|
+
const { existsSync: existsSync2 } = await import("fs");
|
|
3580
|
+
if (!existsSync2(helpersPath)) {
|
|
3581
|
+
await atomicWriteFile(helpersPath, generateHelpersFileSource());
|
|
3582
|
+
}
|
|
3583
|
+
}
|
|
3584
|
+
await Promise.all(
|
|
3585
|
+
fileEntries.map(({ outputPath, source }) => atomicWriteFile(outputPath, source))
|
|
3586
|
+
);
|
|
3587
|
+
return hydrateTasks(serialized);
|
|
3588
|
+
}
|
|
3589
|
+
var TASKS_FILE;
|
|
3590
|
+
var init_generateTaskArtifacts = __esm({
|
|
3591
|
+
"src/cli/generateTaskArtifacts.ts"() {
|
|
3592
|
+
"use strict";
|
|
3593
|
+
init_extractToolMetadata();
|
|
3594
|
+
init_createProgram();
|
|
3595
|
+
init_prodPaths();
|
|
3596
|
+
init_atomicWrite();
|
|
3597
|
+
init_generateSchemaFiles();
|
|
3598
|
+
init_extractHandlerTypes();
|
|
3599
|
+
init_generateZodSchema();
|
|
3600
|
+
TASKS_FILE = "faapi-tasks.js";
|
|
3601
|
+
}
|
|
3602
|
+
});
|
|
3603
|
+
|
|
3604
|
+
// src/cli/compileSourceFiles.ts
|
|
3605
|
+
import path18 from "path";
|
|
3606
|
+
import fs13 from "fs";
|
|
3607
|
+
import fg5 from "fast-glob";
|
|
3608
|
+
async function compileSourceFiles(options) {
|
|
3609
|
+
const { rootDir, dist, files, logLevel = "silent", production, atomicWrite } = options;
|
|
3610
|
+
const entryPoints = files ?? await fg5([`${APP_DIR}/**/*.ts`], {
|
|
3611
|
+
cwd: rootDir,
|
|
3612
|
+
onlyFiles: true,
|
|
3613
|
+
absolute: true,
|
|
3614
|
+
ignore: ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts"]
|
|
3615
|
+
});
|
|
3616
|
+
if (entryPoints.length === 0) {
|
|
3617
|
+
return { compiledFiles: [] };
|
|
3618
|
+
}
|
|
3619
|
+
const absDist = path18.resolve(rootDir, dist);
|
|
3620
|
+
await fs13.promises.mkdir(absDist, { recursive: true });
|
|
3621
|
+
const plugins = buildAliasPlugins(rootDir);
|
|
3622
|
+
const esbuild = await import("esbuild");
|
|
3623
|
+
const outbase = path18.resolve(rootDir, APP_DIR);
|
|
3624
|
+
const result = await esbuild.build({
|
|
3625
|
+
entryPoints,
|
|
3626
|
+
outdir: absDist,
|
|
3627
|
+
outbase,
|
|
3628
|
+
bundle: false,
|
|
3629
|
+
platform: "node",
|
|
3630
|
+
format: "esm",
|
|
3631
|
+
sourcemap: true,
|
|
3632
|
+
packages: "external",
|
|
3633
|
+
plugins,
|
|
3634
|
+
// build 语义:编译期 NODE_ENV 替换 + 死分支删除(见 AGENTS.md §5.3)
|
|
3635
|
+
...production ? { define: { "process.env.NODE_ENV": '"production"' }, minifySyntax: true } : {},
|
|
3636
|
+
logLevel,
|
|
3637
|
+
// dev 语义:esbuild 返回内存内容,由下方原子写落盘
|
|
3638
|
+
...atomicWrite ? { write: false } : {}
|
|
3639
|
+
});
|
|
3640
|
+
if (atomicWrite && result.outputFiles) {
|
|
3641
|
+
await Promise.all(
|
|
3642
|
+
result.outputFiles.map(async (file) => {
|
|
3643
|
+
await fs13.promises.mkdir(path18.dirname(file.path), { recursive: true });
|
|
3644
|
+
const tmp = `${file.path}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
|
|
3645
|
+
await fs13.promises.writeFile(tmp, file.contents);
|
|
3646
|
+
await fs13.promises.rename(tmp, file.path);
|
|
3647
|
+
})
|
|
3648
|
+
);
|
|
3649
|
+
}
|
|
3650
|
+
return { compiledFiles: entryPoints };
|
|
3651
|
+
}
|
|
3652
|
+
var init_compileSourceFiles = __esm({
|
|
3653
|
+
"src/cli/compileSourceFiles.ts"() {
|
|
3654
|
+
"use strict";
|
|
3655
|
+
init_aliasPlugin();
|
|
3656
|
+
init_prodPaths();
|
|
3657
|
+
}
|
|
3658
|
+
});
|
|
3659
|
+
|
|
3660
|
+
// src/cli/compileDevRoutes.ts
|
|
3661
|
+
async function compileDevRoutes(options) {
|
|
3662
|
+
return compileSourceFiles({ ...options, atomicWrite: true });
|
|
3663
|
+
}
|
|
3664
|
+
var init_compileDevRoutes = __esm({
|
|
3665
|
+
"src/cli/compileDevRoutes.ts"() {
|
|
3666
|
+
"use strict";
|
|
3667
|
+
init_compileSourceFiles();
|
|
3668
|
+
}
|
|
3669
|
+
});
|
|
3670
|
+
|
|
3671
|
+
// src/cli/compileOnDemand.ts
|
|
3672
|
+
import path19 from "path";
|
|
3673
|
+
import fs14 from "fs";
|
|
3674
|
+
function isProductFresh(sourceAbsPath, productAbsPath) {
|
|
3675
|
+
try {
|
|
3676
|
+
const srcStat = fs14.statSync(sourceAbsPath);
|
|
3677
|
+
const prodStat = fs14.statSync(productAbsPath);
|
|
3678
|
+
return prodStat.mtimeMs >= srcStat.mtimeMs;
|
|
3679
|
+
} catch {
|
|
3680
|
+
return false;
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
function createDevOnDemandState() {
|
|
3684
|
+
return {
|
|
3685
|
+
enabled: false,
|
|
3686
|
+
distDir: void 0,
|
|
3687
|
+
compiledFiles: /* @__PURE__ */ new Set(),
|
|
3688
|
+
generatedSchemas: /* @__PURE__ */ new Set(),
|
|
3689
|
+
inFlightCompilations: /* @__PURE__ */ new Map(),
|
|
3690
|
+
inFlightSchemaGenerations: /* @__PURE__ */ new Map()
|
|
3691
|
+
};
|
|
3692
|
+
}
|
|
3693
|
+
function clearCompiledFiles() {
|
|
3694
|
+
state.compiledFiles.clear();
|
|
3695
|
+
state.inFlightCompilations.clear();
|
|
3696
|
+
sourcePathCache.clear();
|
|
3697
|
+
}
|
|
3698
|
+
async function ensureCompiled(sourceAbsPath, rootDir, dist) {
|
|
3699
|
+
const inFlight2 = state.inFlightCompilations.get(sourceAbsPath);
|
|
3700
|
+
if (inFlight2) {
|
|
3701
|
+
await inFlight2.catch(() => {
|
|
3702
|
+
});
|
|
3703
|
+
return false;
|
|
3704
|
+
}
|
|
3705
|
+
if (state.compiledFiles.has(sourceAbsPath)) {
|
|
3706
|
+
return false;
|
|
3707
|
+
}
|
|
3708
|
+
if (!fs14.existsSync(sourceAbsPath)) {
|
|
3709
|
+
return false;
|
|
3710
|
+
}
|
|
3711
|
+
const productPath = prodSourcePathToProductPath(sourceAbsPath, rootDir, dist);
|
|
3712
|
+
if (productPath && isProductFresh(sourceAbsPath, productPath)) {
|
|
3713
|
+
state.compiledFiles.add(sourceAbsPath);
|
|
3714
|
+
return false;
|
|
3715
|
+
}
|
|
3716
|
+
const compilePromise = (async () => {
|
|
3717
|
+
const { insideFiles } = await collectRelativeImports([sourceAbsPath], rootDir);
|
|
3718
|
+
const files = [sourceAbsPath];
|
|
3719
|
+
for (const dep of insideFiles) {
|
|
3720
|
+
if (state.compiledFiles.has(dep)) continue;
|
|
3721
|
+
const depProduct = prodSourcePathToProductPath(dep, rootDir, dist);
|
|
3722
|
+
if (depProduct && isProductFresh(dep, depProduct)) continue;
|
|
3723
|
+
files.push(dep);
|
|
3724
|
+
}
|
|
3725
|
+
await compileDevRoutes({
|
|
3726
|
+
rootDir,
|
|
3727
|
+
dist,
|
|
3728
|
+
files,
|
|
3729
|
+
logLevel: "silent"
|
|
3730
|
+
});
|
|
3731
|
+
for (const file of files) {
|
|
3732
|
+
state.compiledFiles.add(file);
|
|
3733
|
+
}
|
|
3734
|
+
state.compiledFiles.add(sourceAbsPath);
|
|
3735
|
+
})();
|
|
3736
|
+
state.inFlightCompilations.set(sourceAbsPath, compilePromise);
|
|
3737
|
+
try {
|
|
3738
|
+
await compilePromise;
|
|
3739
|
+
return true;
|
|
3740
|
+
} finally {
|
|
3741
|
+
state.inFlightCompilations.delete(sourceAbsPath);
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
async function ensureMiddlewaresCompiled(middlewarePaths, rootDir) {
|
|
3745
|
+
if (!isDevOnDemandEnabled() || middlewarePaths.length === 0) return;
|
|
3746
|
+
const dist = getDevDist();
|
|
3747
|
+
if (!dist) return;
|
|
3748
|
+
for (const mwPath of middlewarePaths) {
|
|
3749
|
+
const sourcePath = prodPathToSourcePath(mwPath, rootDir, dist);
|
|
3750
|
+
try {
|
|
3751
|
+
await ensureCompiled(sourcePath, rootDir, dist);
|
|
3752
|
+
} catch (err) {
|
|
3753
|
+
console.error(`[faapi] Failed to compile middleware source ${sourcePath}:`, err);
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
}
|
|
3757
|
+
function prodSourcePathToProductPath(sourceAbsPath, rootDir, dist) {
|
|
3758
|
+
const rel = path19.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
|
|
3759
|
+
if (!rel.startsWith("src/")) return null;
|
|
3760
|
+
const relWithoutSrc = rel.slice(4);
|
|
3761
|
+
const jsRel = relWithoutSrc.replace(/\.ts$/, ".js");
|
|
3762
|
+
return path19.resolve(rootDir, dist, jsRel);
|
|
3763
|
+
}
|
|
3764
|
+
function clearGeneratedSchemas() {
|
|
3765
|
+
state.generatedSchemas.clear();
|
|
3766
|
+
state.inFlightSchemaGenerations.clear();
|
|
3767
|
+
}
|
|
3768
|
+
async function ensureSchemaGenerated(schemaPath, routeFilePath, routes, rootDir, dist) {
|
|
3769
|
+
const inFlight2 = state.inFlightSchemaGenerations.get(schemaPath);
|
|
3770
|
+
if (inFlight2) {
|
|
3771
|
+
await inFlight2.catch(() => {
|
|
3772
|
+
});
|
|
3773
|
+
return false;
|
|
3774
|
+
}
|
|
3775
|
+
if (state.generatedSchemas.has(schemaPath)) {
|
|
3776
|
+
return false;
|
|
3777
|
+
}
|
|
3778
|
+
const prodAbsPath = path19.resolve(rootDir, routeFilePath);
|
|
3779
|
+
const sourceAbsPath = prodPathToSourcePath(prodAbsPath, rootDir, dist);
|
|
3780
|
+
if (!fs14.existsSync(sourceAbsPath)) {
|
|
3781
|
+
return false;
|
|
3782
|
+
}
|
|
3783
|
+
if (isProductFresh(sourceAbsPath, schemaPath)) {
|
|
3784
|
+
state.generatedSchemas.add(schemaPath);
|
|
3785
|
+
return false;
|
|
3786
|
+
}
|
|
3787
|
+
const fileRoutes = routes.filter((r) => r.filePath === routeFilePath);
|
|
3788
|
+
if (fileRoutes.length === 0) {
|
|
3789
|
+
return false;
|
|
3790
|
+
}
|
|
3791
|
+
const sourceRelPath = path19.relative(rootDir, sourceAbsPath).replace(/\\/g, "/");
|
|
3792
|
+
const sourceRoutes = fileRoutes.map((r) => ({ ...r, filePath: sourceRelPath }));
|
|
3793
|
+
const generatePromise = (async () => {
|
|
3794
|
+
await generateSchemaFiles(sourceRoutes, rootDir, dist);
|
|
3795
|
+
state.generatedSchemas.add(schemaPath);
|
|
3796
|
+
})();
|
|
3797
|
+
state.inFlightSchemaGenerations.set(schemaPath, generatePromise);
|
|
3798
|
+
try {
|
|
3799
|
+
await generatePromise;
|
|
3800
|
+
return true;
|
|
3801
|
+
} finally {
|
|
3802
|
+
state.inFlightSchemaGenerations.delete(schemaPath);
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
async function deleteSchemaFiles(routes, rootDir, dist) {
|
|
3806
|
+
const deleted = /* @__PURE__ */ new Set();
|
|
3807
|
+
for (const route of routes) {
|
|
3808
|
+
const schemaPath = getRuntimeSchemaPath(route.filePath, dist, rootDir);
|
|
3809
|
+
if (deleted.has(schemaPath)) continue;
|
|
3810
|
+
deleted.add(schemaPath);
|
|
3811
|
+
try {
|
|
3812
|
+
await fs14.promises.unlink(schemaPath);
|
|
3813
|
+
} catch {
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
3817
|
+
function prodPathToSourcePath(prodAbsPath, rootDir, dist) {
|
|
3818
|
+
const cached = sourcePathCache.get(prodAbsPath);
|
|
3819
|
+
if (cached) return cached;
|
|
3820
|
+
const rel = path19.relative(rootDir, prodAbsPath).replace(/\\/g, "/");
|
|
3821
|
+
let relWithoutDist = rel;
|
|
3822
|
+
if (relWithoutDist.startsWith(`${dist}/`)) {
|
|
3823
|
+
relWithoutDist = relWithoutDist.slice(dist.length + 1);
|
|
3824
|
+
}
|
|
3825
|
+
const srcRel = `src/${relWithoutDist}`;
|
|
3826
|
+
const tsRel = srcRel.replace(/\.js$/, ".ts");
|
|
3827
|
+
const tsAbs = path19.resolve(rootDir, tsRel);
|
|
3828
|
+
let result;
|
|
3829
|
+
if (fs14.existsSync(tsAbs)) {
|
|
3830
|
+
result = tsAbs;
|
|
3831
|
+
} else {
|
|
3832
|
+
result = path19.resolve(rootDir, srcRel);
|
|
3833
|
+
}
|
|
3834
|
+
sourcePathCache.set(prodAbsPath, result);
|
|
3835
|
+
return result;
|
|
3836
|
+
}
|
|
3837
|
+
function setDevOnDemandEnabled(enabled) {
|
|
3838
|
+
state.enabled = enabled;
|
|
3839
|
+
}
|
|
3840
|
+
function isDevOnDemandEnabled() {
|
|
3841
|
+
return state.enabled;
|
|
3842
|
+
}
|
|
3843
|
+
function setDevDist(dist) {
|
|
3844
|
+
state.distDir = dist;
|
|
3845
|
+
}
|
|
3846
|
+
function getDevDist() {
|
|
3847
|
+
return state.distDir;
|
|
3848
|
+
}
|
|
3849
|
+
var state, sourcePathCache;
|
|
3850
|
+
var init_compileOnDemand = __esm({
|
|
3851
|
+
"src/cli/compileOnDemand.ts"() {
|
|
3852
|
+
"use strict";
|
|
3853
|
+
init_compileDevRoutes();
|
|
3854
|
+
init_generateSchemaFiles();
|
|
3855
|
+
init_generateSchemaFiles();
|
|
3856
|
+
init_collectImports();
|
|
3857
|
+
state = createDevOnDemandState();
|
|
3858
|
+
sourcePathCache = /* @__PURE__ */ new Map();
|
|
3859
|
+
}
|
|
3860
|
+
});
|
|
3861
|
+
|
|
3862
|
+
// src/config/loadConfig.ts
|
|
3863
|
+
import path20 from "path";
|
|
3864
|
+
import fs15 from "fs";
|
|
3376
3865
|
async function loadConfig(rootDir, dist) {
|
|
3377
|
-
const configProductPath =
|
|
3378
|
-
if (
|
|
3866
|
+
const configProductPath = path20.resolve(rootDir, dist, CONFIG_PRODUCT_FILE);
|
|
3867
|
+
if (fs15.existsSync(configProductPath)) {
|
|
3379
3868
|
const module = await importWithCacheBust(configProductPath);
|
|
3380
3869
|
return module.default ?? {};
|
|
3381
3870
|
}
|
|
3382
|
-
const hasSourceConfig =
|
|
3871
|
+
const hasSourceConfig = fs15.existsSync(path20.join(rootDir, "faapi.config.ts")) || fs15.existsSync(path20.join(rootDir, "faapi.config.js"));
|
|
3383
3872
|
if (hasSourceConfig) {
|
|
3384
3873
|
throw new Error(
|
|
3385
3874
|
`[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 +3886,8 @@ var init_loadConfig = __esm({
|
|
|
3397
3886
|
});
|
|
3398
3887
|
|
|
3399
3888
|
// src/cli/loadEnv.ts
|
|
3400
|
-
import
|
|
3401
|
-
import
|
|
3889
|
+
import fs16 from "fs";
|
|
3890
|
+
import path21 from "path";
|
|
3402
3891
|
function resolveEnv() {
|
|
3403
3892
|
return process.env.NODE_ENV || "development";
|
|
3404
3893
|
}
|
|
@@ -3465,9 +3954,9 @@ function loadEnv(rootDir) {
|
|
|
3465
3954
|
const files = getEnvFiles(env);
|
|
3466
3955
|
const merged = {};
|
|
3467
3956
|
for (const file of files) {
|
|
3468
|
-
const filePath =
|
|
3469
|
-
if (!
|
|
3470
|
-
const content =
|
|
3957
|
+
const filePath = path21.join(rootDir, file);
|
|
3958
|
+
if (!fs16.existsSync(filePath)) continue;
|
|
3959
|
+
const content = fs16.readFileSync(filePath, "utf-8");
|
|
3471
3960
|
const parsed = parseEnvFile(content, merged);
|
|
3472
3961
|
Object.assign(merged, parsed);
|
|
3473
3962
|
}
|
|
@@ -3573,7 +4062,7 @@ var init_esm = __esm({
|
|
|
3573
4062
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
3574
4063
|
const statMethod = opts.lstat ? lstat : stat;
|
|
3575
4064
|
if (wantBigintFsStats) {
|
|
3576
|
-
this._stat = (
|
|
4065
|
+
this._stat = (path31) => statMethod(path31, { bigint: true });
|
|
3577
4066
|
} else {
|
|
3578
4067
|
this._stat = statMethod;
|
|
3579
4068
|
}
|
|
@@ -3598,8 +4087,8 @@ var init_esm = __esm({
|
|
|
3598
4087
|
const par = this.parent;
|
|
3599
4088
|
const fil = par && par.files;
|
|
3600
4089
|
if (fil && fil.length > 0) {
|
|
3601
|
-
const { path:
|
|
3602
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
4090
|
+
const { path: path31, depth } = par;
|
|
4091
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path31));
|
|
3603
4092
|
const awaited = await Promise.all(slice);
|
|
3604
4093
|
for (const entry of awaited) {
|
|
3605
4094
|
if (!entry)
|
|
@@ -3639,20 +4128,20 @@ var init_esm = __esm({
|
|
|
3639
4128
|
this.reading = false;
|
|
3640
4129
|
}
|
|
3641
4130
|
}
|
|
3642
|
-
async _exploreDir(
|
|
4131
|
+
async _exploreDir(path31, depth) {
|
|
3643
4132
|
let files;
|
|
3644
4133
|
try {
|
|
3645
|
-
files = await readdir(
|
|
4134
|
+
files = await readdir(path31, this._rdOptions);
|
|
3646
4135
|
} catch (error) {
|
|
3647
4136
|
this._onError(error);
|
|
3648
4137
|
}
|
|
3649
|
-
return { files, depth, path:
|
|
4138
|
+
return { files, depth, path: path31 };
|
|
3650
4139
|
}
|
|
3651
|
-
async _formatEntry(dirent,
|
|
4140
|
+
async _formatEntry(dirent, path31) {
|
|
3652
4141
|
let entry;
|
|
3653
4142
|
const basename3 = this._isDirent ? dirent.name : dirent;
|
|
3654
4143
|
try {
|
|
3655
|
-
const fullPath = presolve(pjoin(
|
|
4144
|
+
const fullPath = presolve(pjoin(path31, basename3));
|
|
3656
4145
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
|
|
3657
4146
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
3658
4147
|
} catch (err) {
|
|
@@ -3713,16 +4202,16 @@ import { watchFile, unwatchFile, watch as fs_watch } from "fs";
|
|
|
3713
4202
|
import { open, stat as stat2, lstat as lstat2, realpath as fsrealpath } from "fs/promises";
|
|
3714
4203
|
import * as sysPath from "path";
|
|
3715
4204
|
import { type as osType } from "os";
|
|
3716
|
-
function createFsWatchInstance(
|
|
4205
|
+
function createFsWatchInstance(path31, options, listener, errHandler, emitRaw) {
|
|
3717
4206
|
const handleEvent = (rawEvent, evPath) => {
|
|
3718
|
-
listener(
|
|
3719
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
3720
|
-
if (evPath &&
|
|
3721
|
-
fsWatchBroadcast(sysPath.resolve(
|
|
4207
|
+
listener(path31);
|
|
4208
|
+
emitRaw(rawEvent, evPath, { watchedPath: path31 });
|
|
4209
|
+
if (evPath && path31 !== evPath) {
|
|
4210
|
+
fsWatchBroadcast(sysPath.resolve(path31, evPath), KEY_LISTENERS, sysPath.join(path31, evPath));
|
|
3722
4211
|
}
|
|
3723
4212
|
};
|
|
3724
4213
|
try {
|
|
3725
|
-
return fs_watch(
|
|
4214
|
+
return fs_watch(path31, {
|
|
3726
4215
|
persistent: options.persistent
|
|
3727
4216
|
}, handleEvent);
|
|
3728
4217
|
} catch (error) {
|
|
@@ -4067,12 +4556,12 @@ var init_handler = __esm({
|
|
|
4067
4556
|
listener(val1, val2, val3);
|
|
4068
4557
|
});
|
|
4069
4558
|
};
|
|
4070
|
-
setFsWatchListener = (
|
|
4559
|
+
setFsWatchListener = (path31, fullPath, options, handlers) => {
|
|
4071
4560
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
4072
4561
|
let cont = FsWatchInstances.get(fullPath);
|
|
4073
4562
|
let watcher;
|
|
4074
4563
|
if (!options.persistent) {
|
|
4075
|
-
watcher = createFsWatchInstance(
|
|
4564
|
+
watcher = createFsWatchInstance(path31, options, listener, errHandler, rawEmitter);
|
|
4076
4565
|
if (!watcher)
|
|
4077
4566
|
return;
|
|
4078
4567
|
return watcher.close.bind(watcher);
|
|
@@ -4083,7 +4572,7 @@ var init_handler = __esm({
|
|
|
4083
4572
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
4084
4573
|
} else {
|
|
4085
4574
|
watcher = createFsWatchInstance(
|
|
4086
|
-
|
|
4575
|
+
path31,
|
|
4087
4576
|
options,
|
|
4088
4577
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
4089
4578
|
errHandler,
|
|
@@ -4098,7 +4587,7 @@ var init_handler = __esm({
|
|
|
4098
4587
|
cont.watcherUnusable = true;
|
|
4099
4588
|
if (isWindows && error.code === "EPERM") {
|
|
4100
4589
|
try {
|
|
4101
|
-
const fd = await open(
|
|
4590
|
+
const fd = await open(path31, "r");
|
|
4102
4591
|
await fd.close();
|
|
4103
4592
|
broadcastErr(error);
|
|
4104
4593
|
} catch (err) {
|
|
@@ -4129,7 +4618,7 @@ var init_handler = __esm({
|
|
|
4129
4618
|
};
|
|
4130
4619
|
};
|
|
4131
4620
|
FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
4132
|
-
setFsWatchFileListener = (
|
|
4621
|
+
setFsWatchFileListener = (path31, fullPath, options, handlers) => {
|
|
4133
4622
|
const { listener, rawEmitter } = handlers;
|
|
4134
4623
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
4135
4624
|
const copts = cont && cont.options;
|
|
@@ -4151,7 +4640,7 @@ var init_handler = __esm({
|
|
|
4151
4640
|
});
|
|
4152
4641
|
const currmtime = curr.mtimeMs;
|
|
4153
4642
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
4154
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
4643
|
+
foreach(cont.listeners, (listener2) => listener2(path31, curr));
|
|
4155
4644
|
}
|
|
4156
4645
|
})
|
|
4157
4646
|
};
|
|
@@ -4179,13 +4668,13 @@ var init_handler = __esm({
|
|
|
4179
4668
|
* @param listener on fs change
|
|
4180
4669
|
* @returns closer for the watcher instance
|
|
4181
4670
|
*/
|
|
4182
|
-
_watchWithNodeFs(
|
|
4671
|
+
_watchWithNodeFs(path31, listener) {
|
|
4183
4672
|
const opts = this.fsw.options;
|
|
4184
|
-
const directory = sysPath.dirname(
|
|
4185
|
-
const basename3 = sysPath.basename(
|
|
4673
|
+
const directory = sysPath.dirname(path31);
|
|
4674
|
+
const basename3 = sysPath.basename(path31);
|
|
4186
4675
|
const parent = this.fsw._getWatchedDir(directory);
|
|
4187
4676
|
parent.add(basename3);
|
|
4188
|
-
const absolutePath = sysPath.resolve(
|
|
4677
|
+
const absolutePath = sysPath.resolve(path31);
|
|
4189
4678
|
const options = {
|
|
4190
4679
|
persistent: opts.persistent
|
|
4191
4680
|
};
|
|
@@ -4195,12 +4684,12 @@ var init_handler = __esm({
|
|
|
4195
4684
|
if (opts.usePolling) {
|
|
4196
4685
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
4197
4686
|
options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
|
|
4198
|
-
closer = setFsWatchFileListener(
|
|
4687
|
+
closer = setFsWatchFileListener(path31, absolutePath, options, {
|
|
4199
4688
|
listener,
|
|
4200
4689
|
rawEmitter: this.fsw._emitRaw
|
|
4201
4690
|
});
|
|
4202
4691
|
} else {
|
|
4203
|
-
closer = setFsWatchListener(
|
|
4692
|
+
closer = setFsWatchListener(path31, absolutePath, options, {
|
|
4204
4693
|
listener,
|
|
4205
4694
|
errHandler: this._boundHandleError,
|
|
4206
4695
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -4222,7 +4711,7 @@ var init_handler = __esm({
|
|
|
4222
4711
|
let prevStats = stats;
|
|
4223
4712
|
if (parent.has(basename3))
|
|
4224
4713
|
return;
|
|
4225
|
-
const listener = async (
|
|
4714
|
+
const listener = async (path31, newStats) => {
|
|
4226
4715
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
4227
4716
|
return;
|
|
4228
4717
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -4236,11 +4725,11 @@ var init_handler = __esm({
|
|
|
4236
4725
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
4237
4726
|
}
|
|
4238
4727
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
4239
|
-
this.fsw._closeFile(
|
|
4728
|
+
this.fsw._closeFile(path31);
|
|
4240
4729
|
prevStats = newStats2;
|
|
4241
4730
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
4242
4731
|
if (closer2)
|
|
4243
|
-
this.fsw._addPathCloser(
|
|
4732
|
+
this.fsw._addPathCloser(path31, closer2);
|
|
4244
4733
|
} else {
|
|
4245
4734
|
prevStats = newStats2;
|
|
4246
4735
|
}
|
|
@@ -4272,7 +4761,7 @@ var init_handler = __esm({
|
|
|
4272
4761
|
* @param item basename of this item
|
|
4273
4762
|
* @returns true if no more processing is needed for this entry.
|
|
4274
4763
|
*/
|
|
4275
|
-
async _handleSymlink(entry, directory,
|
|
4764
|
+
async _handleSymlink(entry, directory, path31, item) {
|
|
4276
4765
|
if (this.fsw.closed) {
|
|
4277
4766
|
return;
|
|
4278
4767
|
}
|
|
@@ -4282,7 +4771,7 @@ var init_handler = __esm({
|
|
|
4282
4771
|
this.fsw._incrReadyCount();
|
|
4283
4772
|
let linkPath;
|
|
4284
4773
|
try {
|
|
4285
|
-
linkPath = await fsrealpath(
|
|
4774
|
+
linkPath = await fsrealpath(path31);
|
|
4286
4775
|
} catch (e) {
|
|
4287
4776
|
this.fsw._emitReady();
|
|
4288
4777
|
return true;
|
|
@@ -4292,12 +4781,12 @@ var init_handler = __esm({
|
|
|
4292
4781
|
if (dir.has(item)) {
|
|
4293
4782
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
4294
4783
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
4295
|
-
this.fsw._emit(EV.CHANGE,
|
|
4784
|
+
this.fsw._emit(EV.CHANGE, path31, entry.stats);
|
|
4296
4785
|
}
|
|
4297
4786
|
} else {
|
|
4298
4787
|
dir.add(item);
|
|
4299
4788
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
4300
|
-
this.fsw._emit(EV.ADD,
|
|
4789
|
+
this.fsw._emit(EV.ADD, path31, entry.stats);
|
|
4301
4790
|
}
|
|
4302
4791
|
this.fsw._emitReady();
|
|
4303
4792
|
return true;
|
|
@@ -4326,9 +4815,9 @@ var init_handler = __esm({
|
|
|
4326
4815
|
return;
|
|
4327
4816
|
}
|
|
4328
4817
|
const item = entry.path;
|
|
4329
|
-
let
|
|
4818
|
+
let path31 = sysPath.join(directory, item);
|
|
4330
4819
|
current.add(item);
|
|
4331
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
4820
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path31, item)) {
|
|
4332
4821
|
return;
|
|
4333
4822
|
}
|
|
4334
4823
|
if (this.fsw.closed) {
|
|
@@ -4337,8 +4826,8 @@ var init_handler = __esm({
|
|
|
4337
4826
|
}
|
|
4338
4827
|
if (item === target || !target && !previous.has(item)) {
|
|
4339
4828
|
this.fsw._incrReadyCount();
|
|
4340
|
-
|
|
4341
|
-
this._addToNodeFs(
|
|
4829
|
+
path31 = sysPath.join(dir, sysPath.relative(dir, path31));
|
|
4830
|
+
this._addToNodeFs(path31, initialAdd, wh, depth + 1);
|
|
4342
4831
|
}
|
|
4343
4832
|
}).on(EV.ERROR, this._boundHandleError);
|
|
4344
4833
|
return new Promise((resolve3, reject) => {
|
|
@@ -4407,13 +4896,13 @@ var init_handler = __esm({
|
|
|
4407
4896
|
* @param depth Child path actually targeted for watch
|
|
4408
4897
|
* @param target Child path actually targeted for watch
|
|
4409
4898
|
*/
|
|
4410
|
-
async _addToNodeFs(
|
|
4899
|
+
async _addToNodeFs(path31, initialAdd, priorWh, depth, target) {
|
|
4411
4900
|
const ready = this.fsw._emitReady;
|
|
4412
|
-
if (this.fsw._isIgnored(
|
|
4901
|
+
if (this.fsw._isIgnored(path31) || this.fsw.closed) {
|
|
4413
4902
|
ready();
|
|
4414
4903
|
return false;
|
|
4415
4904
|
}
|
|
4416
|
-
const wh = this.fsw._getWatchHelpers(
|
|
4905
|
+
const wh = this.fsw._getWatchHelpers(path31);
|
|
4417
4906
|
if (priorWh) {
|
|
4418
4907
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
4419
4908
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -4429,8 +4918,8 @@ var init_handler = __esm({
|
|
|
4429
4918
|
const follow = this.fsw.options.followSymlinks;
|
|
4430
4919
|
let closer;
|
|
4431
4920
|
if (stats.isDirectory()) {
|
|
4432
|
-
const absPath = sysPath.resolve(
|
|
4433
|
-
const targetPath = follow ? await fsrealpath(
|
|
4921
|
+
const absPath = sysPath.resolve(path31);
|
|
4922
|
+
const targetPath = follow ? await fsrealpath(path31) : path31;
|
|
4434
4923
|
if (this.fsw.closed)
|
|
4435
4924
|
return;
|
|
4436
4925
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -4440,29 +4929,29 @@ var init_handler = __esm({
|
|
|
4440
4929
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
4441
4930
|
}
|
|
4442
4931
|
} else if (stats.isSymbolicLink()) {
|
|
4443
|
-
const targetPath = follow ? await fsrealpath(
|
|
4932
|
+
const targetPath = follow ? await fsrealpath(path31) : path31;
|
|
4444
4933
|
if (this.fsw.closed)
|
|
4445
4934
|
return;
|
|
4446
4935
|
const parent = sysPath.dirname(wh.watchPath);
|
|
4447
4936
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
4448
4937
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
4449
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
4938
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path31, wh, targetPath);
|
|
4450
4939
|
if (this.fsw.closed)
|
|
4451
4940
|
return;
|
|
4452
4941
|
if (targetPath !== void 0) {
|
|
4453
|
-
this.fsw._symlinkPaths.set(sysPath.resolve(
|
|
4942
|
+
this.fsw._symlinkPaths.set(sysPath.resolve(path31), targetPath);
|
|
4454
4943
|
}
|
|
4455
4944
|
} else {
|
|
4456
4945
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
4457
4946
|
}
|
|
4458
4947
|
ready();
|
|
4459
4948
|
if (closer)
|
|
4460
|
-
this.fsw._addPathCloser(
|
|
4949
|
+
this.fsw._addPathCloser(path31, closer);
|
|
4461
4950
|
return false;
|
|
4462
4951
|
} catch (error) {
|
|
4463
4952
|
if (this.fsw._handleError(error)) {
|
|
4464
4953
|
ready();
|
|
4465
|
-
return
|
|
4954
|
+
return path31;
|
|
4466
4955
|
}
|
|
4467
4956
|
}
|
|
4468
4957
|
}
|
|
@@ -4501,26 +4990,26 @@ function createPattern(matcher) {
|
|
|
4501
4990
|
}
|
|
4502
4991
|
return () => false;
|
|
4503
4992
|
}
|
|
4504
|
-
function normalizePath2(
|
|
4505
|
-
if (typeof
|
|
4993
|
+
function normalizePath2(path31) {
|
|
4994
|
+
if (typeof path31 !== "string")
|
|
4506
4995
|
throw new Error("string expected");
|
|
4507
|
-
|
|
4508
|
-
|
|
4996
|
+
path31 = sysPath2.normalize(path31);
|
|
4997
|
+
path31 = path31.replace(/\\/g, "/");
|
|
4509
4998
|
let prepend = false;
|
|
4510
|
-
if (
|
|
4999
|
+
if (path31.startsWith("//"))
|
|
4511
5000
|
prepend = true;
|
|
4512
5001
|
const DOUBLE_SLASH_RE2 = /\/\//;
|
|
4513
|
-
while (
|
|
4514
|
-
|
|
5002
|
+
while (path31.match(DOUBLE_SLASH_RE2))
|
|
5003
|
+
path31 = path31.replace(DOUBLE_SLASH_RE2, "/");
|
|
4515
5004
|
if (prepend)
|
|
4516
|
-
|
|
4517
|
-
return
|
|
5005
|
+
path31 = "/" + path31;
|
|
5006
|
+
return path31;
|
|
4518
5007
|
}
|
|
4519
5008
|
function matchPatterns(patterns, testString, stats) {
|
|
4520
|
-
const
|
|
5009
|
+
const path31 = normalizePath2(testString);
|
|
4521
5010
|
for (let index = 0; index < patterns.length; index++) {
|
|
4522
5011
|
const pattern = patterns[index];
|
|
4523
|
-
if (pattern(
|
|
5012
|
+
if (pattern(path31, stats)) {
|
|
4524
5013
|
return true;
|
|
4525
5014
|
}
|
|
4526
5015
|
}
|
|
@@ -4581,19 +5070,19 @@ var init_esm2 = __esm({
|
|
|
4581
5070
|
}
|
|
4582
5071
|
return str;
|
|
4583
5072
|
};
|
|
4584
|
-
normalizePathToUnix = (
|
|
4585
|
-
normalizeIgnored = (cwd = "") => (
|
|
4586
|
-
if (typeof
|
|
4587
|
-
return normalizePathToUnix(sysPath2.isAbsolute(
|
|
5073
|
+
normalizePathToUnix = (path31) => toUnix(sysPath2.normalize(toUnix(path31)));
|
|
5074
|
+
normalizeIgnored = (cwd = "") => (path31) => {
|
|
5075
|
+
if (typeof path31 === "string") {
|
|
5076
|
+
return normalizePathToUnix(sysPath2.isAbsolute(path31) ? path31 : sysPath2.join(cwd, path31));
|
|
4588
5077
|
} else {
|
|
4589
|
-
return
|
|
5078
|
+
return path31;
|
|
4590
5079
|
}
|
|
4591
5080
|
};
|
|
4592
|
-
getAbsolutePath = (
|
|
4593
|
-
if (sysPath2.isAbsolute(
|
|
4594
|
-
return
|
|
5081
|
+
getAbsolutePath = (path31, cwd) => {
|
|
5082
|
+
if (sysPath2.isAbsolute(path31)) {
|
|
5083
|
+
return path31;
|
|
4595
5084
|
}
|
|
4596
|
-
return sysPath2.join(cwd,
|
|
5085
|
+
return sysPath2.join(cwd, path31);
|
|
4597
5086
|
};
|
|
4598
5087
|
EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
4599
5088
|
DirEntry = class {
|
|
@@ -4648,10 +5137,10 @@ var init_esm2 = __esm({
|
|
|
4648
5137
|
STAT_METHOD_F = "stat";
|
|
4649
5138
|
STAT_METHOD_L = "lstat";
|
|
4650
5139
|
WatchHelper = class {
|
|
4651
|
-
constructor(
|
|
5140
|
+
constructor(path31, follow, fsw) {
|
|
4652
5141
|
this.fsw = fsw;
|
|
4653
|
-
const watchPath =
|
|
4654
|
-
this.path =
|
|
5142
|
+
const watchPath = path31;
|
|
5143
|
+
this.path = path31 = path31.replace(REPLACER_RE, "");
|
|
4655
5144
|
this.watchPath = watchPath;
|
|
4656
5145
|
this.fullWatchPath = sysPath2.resolve(watchPath);
|
|
4657
5146
|
this.dirParts = [];
|
|
@@ -4773,20 +5262,20 @@ var init_esm2 = __esm({
|
|
|
4773
5262
|
this._closePromise = void 0;
|
|
4774
5263
|
let paths = unifyPaths(paths_);
|
|
4775
5264
|
if (cwd) {
|
|
4776
|
-
paths = paths.map((
|
|
4777
|
-
const absPath = getAbsolutePath(
|
|
5265
|
+
paths = paths.map((path31) => {
|
|
5266
|
+
const absPath = getAbsolutePath(path31, cwd);
|
|
4778
5267
|
return absPath;
|
|
4779
5268
|
});
|
|
4780
5269
|
}
|
|
4781
|
-
paths.forEach((
|
|
4782
|
-
this._removeIgnoredPath(
|
|
5270
|
+
paths.forEach((path31) => {
|
|
5271
|
+
this._removeIgnoredPath(path31);
|
|
4783
5272
|
});
|
|
4784
5273
|
this._userIgnored = void 0;
|
|
4785
5274
|
if (!this._readyCount)
|
|
4786
5275
|
this._readyCount = 0;
|
|
4787
5276
|
this._readyCount += paths.length;
|
|
4788
|
-
Promise.all(paths.map(async (
|
|
4789
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
5277
|
+
Promise.all(paths.map(async (path31) => {
|
|
5278
|
+
const res = await this._nodeFsHandler._addToNodeFs(path31, !_internal, void 0, 0, _origAdd);
|
|
4790
5279
|
if (res)
|
|
4791
5280
|
this._emitReady();
|
|
4792
5281
|
return res;
|
|
@@ -4808,17 +5297,17 @@ var init_esm2 = __esm({
|
|
|
4808
5297
|
return this;
|
|
4809
5298
|
const paths = unifyPaths(paths_);
|
|
4810
5299
|
const { cwd } = this.options;
|
|
4811
|
-
paths.forEach((
|
|
4812
|
-
if (!sysPath2.isAbsolute(
|
|
5300
|
+
paths.forEach((path31) => {
|
|
5301
|
+
if (!sysPath2.isAbsolute(path31) && !this._closers.has(path31)) {
|
|
4813
5302
|
if (cwd)
|
|
4814
|
-
|
|
4815
|
-
|
|
5303
|
+
path31 = sysPath2.join(cwd, path31);
|
|
5304
|
+
path31 = sysPath2.resolve(path31);
|
|
4816
5305
|
}
|
|
4817
|
-
this._closePath(
|
|
4818
|
-
this._addIgnoredPath(
|
|
4819
|
-
if (this._watched.has(
|
|
5306
|
+
this._closePath(path31);
|
|
5307
|
+
this._addIgnoredPath(path31);
|
|
5308
|
+
if (this._watched.has(path31)) {
|
|
4820
5309
|
this._addIgnoredPath({
|
|
4821
|
-
path:
|
|
5310
|
+
path: path31,
|
|
4822
5311
|
recursive: true
|
|
4823
5312
|
});
|
|
4824
5313
|
}
|
|
@@ -4882,38 +5371,38 @@ var init_esm2 = __esm({
|
|
|
4882
5371
|
* @param stats arguments to be passed with event
|
|
4883
5372
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
4884
5373
|
*/
|
|
4885
|
-
async _emit(event,
|
|
5374
|
+
async _emit(event, path31, stats) {
|
|
4886
5375
|
if (this.closed)
|
|
4887
5376
|
return;
|
|
4888
5377
|
const opts = this.options;
|
|
4889
5378
|
if (isWindows)
|
|
4890
|
-
|
|
5379
|
+
path31 = sysPath2.normalize(path31);
|
|
4891
5380
|
if (opts.cwd)
|
|
4892
|
-
|
|
4893
|
-
const args = [
|
|
5381
|
+
path31 = sysPath2.relative(opts.cwd, path31);
|
|
5382
|
+
const args = [path31];
|
|
4894
5383
|
if (stats != null)
|
|
4895
5384
|
args.push(stats);
|
|
4896
5385
|
const awf = opts.awaitWriteFinish;
|
|
4897
5386
|
let pw;
|
|
4898
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
5387
|
+
if (awf && (pw = this._pendingWrites.get(path31))) {
|
|
4899
5388
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
4900
5389
|
return this;
|
|
4901
5390
|
}
|
|
4902
5391
|
if (opts.atomic) {
|
|
4903
5392
|
if (event === EVENTS.UNLINK) {
|
|
4904
|
-
this._pendingUnlinks.set(
|
|
5393
|
+
this._pendingUnlinks.set(path31, [event, ...args]);
|
|
4905
5394
|
setTimeout(() => {
|
|
4906
|
-
this._pendingUnlinks.forEach((entry,
|
|
5395
|
+
this._pendingUnlinks.forEach((entry, path32) => {
|
|
4907
5396
|
this.emit(...entry);
|
|
4908
5397
|
this.emit(EVENTS.ALL, ...entry);
|
|
4909
|
-
this._pendingUnlinks.delete(
|
|
5398
|
+
this._pendingUnlinks.delete(path32);
|
|
4910
5399
|
});
|
|
4911
5400
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
4912
5401
|
return this;
|
|
4913
5402
|
}
|
|
4914
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
5403
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path31)) {
|
|
4915
5404
|
event = EVENTS.CHANGE;
|
|
4916
|
-
this._pendingUnlinks.delete(
|
|
5405
|
+
this._pendingUnlinks.delete(path31);
|
|
4917
5406
|
}
|
|
4918
5407
|
}
|
|
4919
5408
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -4931,16 +5420,16 @@ var init_esm2 = __esm({
|
|
|
4931
5420
|
this.emitWithAll(event, args);
|
|
4932
5421
|
}
|
|
4933
5422
|
};
|
|
4934
|
-
this._awaitWriteFinish(
|
|
5423
|
+
this._awaitWriteFinish(path31, awf.stabilityThreshold, event, awfEmit);
|
|
4935
5424
|
return this;
|
|
4936
5425
|
}
|
|
4937
5426
|
if (event === EVENTS.CHANGE) {
|
|
4938
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
5427
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path31, 50);
|
|
4939
5428
|
if (isThrottled)
|
|
4940
5429
|
return this;
|
|
4941
5430
|
}
|
|
4942
5431
|
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,
|
|
5432
|
+
const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path31) : path31;
|
|
4944
5433
|
let stats2;
|
|
4945
5434
|
try {
|
|
4946
5435
|
stats2 = await stat3(fullPath);
|
|
@@ -4971,23 +5460,23 @@ var init_esm2 = __esm({
|
|
|
4971
5460
|
* @param timeout duration of time to suppress duplicate actions
|
|
4972
5461
|
* @returns tracking object or false if action should be suppressed
|
|
4973
5462
|
*/
|
|
4974
|
-
_throttle(actionType,
|
|
5463
|
+
_throttle(actionType, path31, timeout) {
|
|
4975
5464
|
if (!this._throttled.has(actionType)) {
|
|
4976
5465
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
4977
5466
|
}
|
|
4978
5467
|
const action = this._throttled.get(actionType);
|
|
4979
5468
|
if (!action)
|
|
4980
5469
|
throw new Error("invalid throttle");
|
|
4981
|
-
const actionPath = action.get(
|
|
5470
|
+
const actionPath = action.get(path31);
|
|
4982
5471
|
if (actionPath) {
|
|
4983
5472
|
actionPath.count++;
|
|
4984
5473
|
return false;
|
|
4985
5474
|
}
|
|
4986
5475
|
let timeoutObject;
|
|
4987
5476
|
const clear = () => {
|
|
4988
|
-
const item = action.get(
|
|
5477
|
+
const item = action.get(path31);
|
|
4989
5478
|
const count = item ? item.count : 0;
|
|
4990
|
-
action.delete(
|
|
5479
|
+
action.delete(path31);
|
|
4991
5480
|
clearTimeout(timeoutObject);
|
|
4992
5481
|
if (item)
|
|
4993
5482
|
clearTimeout(item.timeoutObject);
|
|
@@ -4995,7 +5484,7 @@ var init_esm2 = __esm({
|
|
|
4995
5484
|
};
|
|
4996
5485
|
timeoutObject = setTimeout(clear, timeout);
|
|
4997
5486
|
const thr = { timeoutObject, clear, count: 0 };
|
|
4998
|
-
action.set(
|
|
5487
|
+
action.set(path31, thr);
|
|
4999
5488
|
return thr;
|
|
5000
5489
|
}
|
|
5001
5490
|
_incrReadyCount() {
|
|
@@ -5009,44 +5498,44 @@ var init_esm2 = __esm({
|
|
|
5009
5498
|
* @param event
|
|
5010
5499
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
5011
5500
|
*/
|
|
5012
|
-
_awaitWriteFinish(
|
|
5501
|
+
_awaitWriteFinish(path31, threshold, event, awfEmit) {
|
|
5013
5502
|
const awf = this.options.awaitWriteFinish;
|
|
5014
5503
|
if (typeof awf !== "object")
|
|
5015
5504
|
return;
|
|
5016
5505
|
const pollInterval = awf.pollInterval;
|
|
5017
5506
|
let timeoutHandler;
|
|
5018
|
-
let fullPath =
|
|
5019
|
-
if (this.options.cwd && !sysPath2.isAbsolute(
|
|
5020
|
-
fullPath = sysPath2.join(this.options.cwd,
|
|
5507
|
+
let fullPath = path31;
|
|
5508
|
+
if (this.options.cwd && !sysPath2.isAbsolute(path31)) {
|
|
5509
|
+
fullPath = sysPath2.join(this.options.cwd, path31);
|
|
5021
5510
|
}
|
|
5022
5511
|
const now = /* @__PURE__ */ new Date();
|
|
5023
5512
|
const writes = this._pendingWrites;
|
|
5024
5513
|
function awaitWriteFinishFn(prevStat) {
|
|
5025
5514
|
statcb(fullPath, (err, curStat) => {
|
|
5026
|
-
if (err || !writes.has(
|
|
5515
|
+
if (err || !writes.has(path31)) {
|
|
5027
5516
|
if (err && err.code !== "ENOENT")
|
|
5028
5517
|
awfEmit(err);
|
|
5029
5518
|
return;
|
|
5030
5519
|
}
|
|
5031
5520
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
5032
5521
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
5033
|
-
writes.get(
|
|
5522
|
+
writes.get(path31).lastChange = now2;
|
|
5034
5523
|
}
|
|
5035
|
-
const pw = writes.get(
|
|
5524
|
+
const pw = writes.get(path31);
|
|
5036
5525
|
const df = now2 - pw.lastChange;
|
|
5037
5526
|
if (df >= threshold) {
|
|
5038
|
-
writes.delete(
|
|
5527
|
+
writes.delete(path31);
|
|
5039
5528
|
awfEmit(void 0, curStat);
|
|
5040
5529
|
} else {
|
|
5041
5530
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
5042
5531
|
}
|
|
5043
5532
|
});
|
|
5044
5533
|
}
|
|
5045
|
-
if (!writes.has(
|
|
5046
|
-
writes.set(
|
|
5534
|
+
if (!writes.has(path31)) {
|
|
5535
|
+
writes.set(path31, {
|
|
5047
5536
|
lastChange: now,
|
|
5048
5537
|
cancelWait: () => {
|
|
5049
|
-
writes.delete(
|
|
5538
|
+
writes.delete(path31);
|
|
5050
5539
|
clearTimeout(timeoutHandler);
|
|
5051
5540
|
return event;
|
|
5052
5541
|
}
|
|
@@ -5057,8 +5546,8 @@ var init_esm2 = __esm({
|
|
|
5057
5546
|
/**
|
|
5058
5547
|
* Determines whether user has asked to ignore this path.
|
|
5059
5548
|
*/
|
|
5060
|
-
_isIgnored(
|
|
5061
|
-
if (this.options.atomic && DOT_RE.test(
|
|
5549
|
+
_isIgnored(path31, stats) {
|
|
5550
|
+
if (this.options.atomic && DOT_RE.test(path31))
|
|
5062
5551
|
return true;
|
|
5063
5552
|
if (!this._userIgnored) {
|
|
5064
5553
|
const { cwd } = this.options;
|
|
@@ -5068,17 +5557,17 @@ var init_esm2 = __esm({
|
|
|
5068
5557
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
5069
5558
|
this._userIgnored = anymatch(list, void 0);
|
|
5070
5559
|
}
|
|
5071
|
-
return this._userIgnored(
|
|
5560
|
+
return this._userIgnored(path31, stats);
|
|
5072
5561
|
}
|
|
5073
|
-
_isntIgnored(
|
|
5074
|
-
return !this._isIgnored(
|
|
5562
|
+
_isntIgnored(path31, stat4) {
|
|
5563
|
+
return !this._isIgnored(path31, stat4);
|
|
5075
5564
|
}
|
|
5076
5565
|
/**
|
|
5077
5566
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
5078
5567
|
* @param path file or directory pattern being watched
|
|
5079
5568
|
*/
|
|
5080
|
-
_getWatchHelpers(
|
|
5081
|
-
return new WatchHelper(
|
|
5569
|
+
_getWatchHelpers(path31) {
|
|
5570
|
+
return new WatchHelper(path31, this.options.followSymlinks, this);
|
|
5082
5571
|
}
|
|
5083
5572
|
// Directory helpers
|
|
5084
5573
|
// -----------------
|
|
@@ -5110,63 +5599,63 @@ var init_esm2 = __esm({
|
|
|
5110
5599
|
* @param item base path of item/directory
|
|
5111
5600
|
*/
|
|
5112
5601
|
_remove(directory, item, isDirectory) {
|
|
5113
|
-
const
|
|
5114
|
-
const fullPath = sysPath2.resolve(
|
|
5115
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
5116
|
-
if (!this._throttle("remove",
|
|
5602
|
+
const path31 = sysPath2.join(directory, item);
|
|
5603
|
+
const fullPath = sysPath2.resolve(path31);
|
|
5604
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path31) || this._watched.has(fullPath);
|
|
5605
|
+
if (!this._throttle("remove", path31, 100))
|
|
5117
5606
|
return;
|
|
5118
5607
|
if (!isDirectory && this._watched.size === 1) {
|
|
5119
5608
|
this.add(directory, item, true);
|
|
5120
5609
|
}
|
|
5121
|
-
const wp = this._getWatchedDir(
|
|
5610
|
+
const wp = this._getWatchedDir(path31);
|
|
5122
5611
|
const nestedDirectoryChildren = wp.getChildren();
|
|
5123
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
5612
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path31, nested));
|
|
5124
5613
|
const parent = this._getWatchedDir(directory);
|
|
5125
5614
|
const wasTracked = parent.has(item);
|
|
5126
5615
|
parent.remove(item);
|
|
5127
5616
|
if (this._symlinkPaths.has(fullPath)) {
|
|
5128
5617
|
this._symlinkPaths.delete(fullPath);
|
|
5129
5618
|
}
|
|
5130
|
-
let relPath =
|
|
5619
|
+
let relPath = path31;
|
|
5131
5620
|
if (this.options.cwd)
|
|
5132
|
-
relPath = sysPath2.relative(this.options.cwd,
|
|
5621
|
+
relPath = sysPath2.relative(this.options.cwd, path31);
|
|
5133
5622
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
5134
5623
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
5135
5624
|
if (event === EVENTS.ADD)
|
|
5136
5625
|
return;
|
|
5137
5626
|
}
|
|
5138
|
-
this._watched.delete(
|
|
5627
|
+
this._watched.delete(path31);
|
|
5139
5628
|
this._watched.delete(fullPath);
|
|
5140
5629
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
5141
|
-
if (wasTracked && !this._isIgnored(
|
|
5142
|
-
this._emit(eventName,
|
|
5143
|
-
this._closePath(
|
|
5630
|
+
if (wasTracked && !this._isIgnored(path31))
|
|
5631
|
+
this._emit(eventName, path31);
|
|
5632
|
+
this._closePath(path31);
|
|
5144
5633
|
}
|
|
5145
5634
|
/**
|
|
5146
5635
|
* Closes all watchers for a path
|
|
5147
5636
|
*/
|
|
5148
|
-
_closePath(
|
|
5149
|
-
this._closeFile(
|
|
5150
|
-
const dir = sysPath2.dirname(
|
|
5151
|
-
this._getWatchedDir(dir).remove(sysPath2.basename(
|
|
5637
|
+
_closePath(path31) {
|
|
5638
|
+
this._closeFile(path31);
|
|
5639
|
+
const dir = sysPath2.dirname(path31);
|
|
5640
|
+
this._getWatchedDir(dir).remove(sysPath2.basename(path31));
|
|
5152
5641
|
}
|
|
5153
5642
|
/**
|
|
5154
5643
|
* Closes only file-specific watchers
|
|
5155
5644
|
*/
|
|
5156
|
-
_closeFile(
|
|
5157
|
-
const closers = this._closers.get(
|
|
5645
|
+
_closeFile(path31) {
|
|
5646
|
+
const closers = this._closers.get(path31);
|
|
5158
5647
|
if (!closers)
|
|
5159
5648
|
return;
|
|
5160
5649
|
closers.forEach((closer) => closer());
|
|
5161
|
-
this._closers.delete(
|
|
5650
|
+
this._closers.delete(path31);
|
|
5162
5651
|
}
|
|
5163
|
-
_addPathCloser(
|
|
5652
|
+
_addPathCloser(path31, closer) {
|
|
5164
5653
|
if (!closer)
|
|
5165
5654
|
return;
|
|
5166
|
-
let list = this._closers.get(
|
|
5655
|
+
let list = this._closers.get(path31);
|
|
5167
5656
|
if (!list) {
|
|
5168
5657
|
list = [];
|
|
5169
|
-
this._closers.set(
|
|
5658
|
+
this._closers.set(path31, list);
|
|
5170
5659
|
}
|
|
5171
5660
|
list.push(closer);
|
|
5172
5661
|
}
|
|
@@ -5192,73 +5681,6 @@ var init_esm2 = __esm({
|
|
|
5192
5681
|
}
|
|
5193
5682
|
});
|
|
5194
5683
|
|
|
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
5684
|
// src/cli/rebuildScheduler.ts
|
|
5263
5685
|
function createRebuildScheduler(options) {
|
|
5264
5686
|
const { rebuild, debounceMs = 100, onError } = options;
|
|
@@ -5321,7 +5743,7 @@ var init_rebuildScheduler = __esm({
|
|
|
5321
5743
|
});
|
|
5322
5744
|
|
|
5323
5745
|
// src/cli/watcher.ts
|
|
5324
|
-
import
|
|
5746
|
+
import path22 from "path";
|
|
5325
5747
|
function startWatcher(options) {
|
|
5326
5748
|
const { rootDir, app, devDist } = options;
|
|
5327
5749
|
async function rebuildRoutes(files) {
|
|
@@ -5336,6 +5758,7 @@ function startWatcher(options) {
|
|
|
5336
5758
|
await app.reloadRoutes();
|
|
5337
5759
|
await app.reloadTools();
|
|
5338
5760
|
await app.reloadAgents();
|
|
5761
|
+
await app.reloadTasks();
|
|
5339
5762
|
console.log(
|
|
5340
5763
|
`- Routes rebuilt${files.length > 0 ? `, ${files.length} file(s) recompiled` : ""}`
|
|
5341
5764
|
);
|
|
@@ -5356,9 +5779,9 @@ function startWatcher(options) {
|
|
|
5356
5779
|
}
|
|
5357
5780
|
});
|
|
5358
5781
|
const CONFIG_FILES = ["faapi.config.ts", "faapi.config.js"];
|
|
5359
|
-
const configAbsPaths = new Set(CONFIG_FILES.map((f) =>
|
|
5782
|
+
const configAbsPaths = new Set(CONFIG_FILES.map((f) => path22.resolve(rootDir, f)));
|
|
5360
5783
|
const watchPaths = ["src", "tsconfig.json", ...CONFIG_FILES];
|
|
5361
|
-
const scheduleOnly = /* @__PURE__ */ new Set([...configAbsPaths,
|
|
5784
|
+
const scheduleOnly = /* @__PURE__ */ new Set([...configAbsPaths, path22.resolve(rootDir, "tsconfig.json")]);
|
|
5362
5785
|
const watcher = esm_default.watch(watchPaths, {
|
|
5363
5786
|
cwd: rootDir,
|
|
5364
5787
|
ignoreInitial: true,
|
|
@@ -5375,7 +5798,7 @@ function startWatcher(options) {
|
|
|
5375
5798
|
watcher.on("add", (file) => handleFileEvent(file));
|
|
5376
5799
|
watcher.on("change", (file) => handleFileEvent(file));
|
|
5377
5800
|
function handleFileEvent(file) {
|
|
5378
|
-
const abs =
|
|
5801
|
+
const abs = path22.resolve(rootDir, file);
|
|
5379
5802
|
if (scheduleOnly.has(abs)) {
|
|
5380
5803
|
scheduler.schedule();
|
|
5381
5804
|
return;
|
|
@@ -5477,18 +5900,18 @@ function getWsIndex(routes) {
|
|
|
5477
5900
|
wsIndexCache.set(routes, index);
|
|
5478
5901
|
return index;
|
|
5479
5902
|
}
|
|
5480
|
-
function matchRoute(routes, method,
|
|
5903
|
+
function matchRoute(routes, method, path31) {
|
|
5481
5904
|
const index = getHttpIndex(routes);
|
|
5482
5905
|
const upper = method.toUpperCase();
|
|
5483
|
-
const hit = matchByMethod(index, upper,
|
|
5906
|
+
const hit = matchByMethod(index, upper, path31);
|
|
5484
5907
|
if (hit) return hit;
|
|
5485
5908
|
if (upper === "HEAD") {
|
|
5486
|
-
return matchByMethod(index, "GET",
|
|
5909
|
+
return matchByMethod(index, "GET", path31);
|
|
5487
5910
|
}
|
|
5488
5911
|
return null;
|
|
5489
5912
|
}
|
|
5490
|
-
function matchByMethod(index, method,
|
|
5491
|
-
const staticHit = index.static.get(`${method}|${
|
|
5913
|
+
function matchByMethod(index, method, path31) {
|
|
5914
|
+
const staticHit = index.static.get(`${method}|${path31}`);
|
|
5492
5915
|
if (staticHit) {
|
|
5493
5916
|
return { route: staticHit, params: {} };
|
|
5494
5917
|
}
|
|
@@ -5497,31 +5920,31 @@ function matchByMethod(index, method, path27) {
|
|
|
5497
5920
|
if (route.method !== method) {
|
|
5498
5921
|
continue;
|
|
5499
5922
|
}
|
|
5500
|
-
const params = matchSegments(entry.segments,
|
|
5923
|
+
const params = matchSegments(entry.segments, path31, route.paramNames, route.isCatchAll);
|
|
5501
5924
|
if (params !== null) {
|
|
5502
5925
|
return { route, params };
|
|
5503
5926
|
}
|
|
5504
5927
|
}
|
|
5505
5928
|
return null;
|
|
5506
5929
|
}
|
|
5507
|
-
function matchWsRoute(wsRoutes,
|
|
5930
|
+
function matchWsRoute(wsRoutes, path31) {
|
|
5508
5931
|
const index = getWsIndex(wsRoutes);
|
|
5509
|
-
const staticHit = index.static.get(
|
|
5932
|
+
const staticHit = index.static.get(path31);
|
|
5510
5933
|
if (staticHit) {
|
|
5511
5934
|
return { route: staticHit, params: {} };
|
|
5512
5935
|
}
|
|
5513
5936
|
for (const route of index.dynamics) {
|
|
5514
|
-
const params = matchDynamicPath(route.urlPath,
|
|
5937
|
+
const params = matchDynamicPath(route.urlPath, path31, route.paramNames, route.isCatchAll);
|
|
5515
5938
|
if (params !== null) {
|
|
5516
5939
|
return { route, params };
|
|
5517
5940
|
}
|
|
5518
5941
|
}
|
|
5519
5942
|
return null;
|
|
5520
5943
|
}
|
|
5521
|
-
function findAllowedMethods(routes,
|
|
5944
|
+
function findAllowedMethods(routes, path31) {
|
|
5522
5945
|
const index = getHttpIndex(routes);
|
|
5523
5946
|
const methods = /* @__PURE__ */ new Set();
|
|
5524
|
-
const staticMethods = index.methodsByStaticPath.get(
|
|
5947
|
+
const staticMethods = index.methodsByStaticPath.get(path31);
|
|
5525
5948
|
if (staticMethods) {
|
|
5526
5949
|
for (const method of staticMethods) {
|
|
5527
5950
|
methods.add(method);
|
|
@@ -5530,7 +5953,7 @@ function findAllowedMethods(routes, path27) {
|
|
|
5530
5953
|
for (const entry of index.dynamics) {
|
|
5531
5954
|
const params = matchSegments(
|
|
5532
5955
|
entry.segments,
|
|
5533
|
-
|
|
5956
|
+
path31,
|
|
5534
5957
|
entry.route.paramNames,
|
|
5535
5958
|
entry.route.isCatchAll
|
|
5536
5959
|
);
|
|
@@ -5543,11 +5966,11 @@ function findAllowedMethods(routes, path27) {
|
|
|
5543
5966
|
}
|
|
5544
5967
|
return Array.from(methods);
|
|
5545
5968
|
}
|
|
5546
|
-
function matchDynamicPath(pattern,
|
|
5547
|
-
return matchSegments(pattern.split("/").filter(Boolean),
|
|
5969
|
+
function matchDynamicPath(pattern, path31, paramNames, isCatchAll) {
|
|
5970
|
+
return matchSegments(pattern.split("/").filter(Boolean), path31, paramNames, isCatchAll);
|
|
5548
5971
|
}
|
|
5549
|
-
function matchSegments(patternSegments,
|
|
5550
|
-
const pathSegments =
|
|
5972
|
+
function matchSegments(patternSegments, path31, paramNames, isCatchAll) {
|
|
5973
|
+
const pathSegments = path31.split("/").filter(Boolean);
|
|
5551
5974
|
if (isCatchAll) {
|
|
5552
5975
|
const nonCatchAllCount = patternSegments.length - 1;
|
|
5553
5976
|
if (pathSegments.length <= nonCatchAllCount) {
|
|
@@ -5634,197 +6057,6 @@ var init_validateRouteModule = __esm({
|
|
|
5634
6057
|
}
|
|
5635
6058
|
});
|
|
5636
6059
|
|
|
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);
|
|
5769
|
-
}
|
|
5770
|
-
}
|
|
5771
|
-
async function deleteSchemaFiles(routes, rootDir, dist) {
|
|
5772
|
-
const deleted = /* @__PURE__ */ new Set();
|
|
5773
|
-
for (const route of routes) {
|
|
5774
|
-
const schemaPath = getRuntimeSchemaPath(route.filePath, dist, rootDir);
|
|
5775
|
-
if (deleted.has(schemaPath)) continue;
|
|
5776
|
-
deleted.add(schemaPath);
|
|
5777
|
-
try {
|
|
5778
|
-
await fs15.promises.unlink(schemaPath);
|
|
5779
|
-
} catch {
|
|
5780
|
-
}
|
|
5781
|
-
}
|
|
5782
|
-
}
|
|
5783
|
-
function prodPathToSourcePath(prodAbsPath, rootDir, dist) {
|
|
5784
|
-
const cached = sourcePathCache.get(prodAbsPath);
|
|
5785
|
-
if (cached) return cached;
|
|
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);
|
|
5790
|
-
}
|
|
5791
|
-
const srcRel = `src/${relWithoutDist}`;
|
|
5792
|
-
const tsRel = srcRel.replace(/\.js$/, ".ts");
|
|
5793
|
-
const tsAbs = path20.resolve(rootDir, tsRel);
|
|
5794
|
-
let result;
|
|
5795
|
-
if (fs15.existsSync(tsAbs)) {
|
|
5796
|
-
result = tsAbs;
|
|
5797
|
-
} else {
|
|
5798
|
-
result = path20.resolve(rootDir, srcRel);
|
|
5799
|
-
}
|
|
5800
|
-
sourcePathCache.set(prodAbsPath, result);
|
|
5801
|
-
return result;
|
|
5802
|
-
}
|
|
5803
|
-
function setDevOnDemandEnabled(enabled) {
|
|
5804
|
-
state.enabled = enabled;
|
|
5805
|
-
}
|
|
5806
|
-
function isDevOnDemandEnabled() {
|
|
5807
|
-
return state.enabled;
|
|
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"() {
|
|
5818
|
-
"use strict";
|
|
5819
|
-
init_compileDevRoutes();
|
|
5820
|
-
init_generateSchemaFiles();
|
|
5821
|
-
init_generateSchemaFiles();
|
|
5822
|
-
init_collectImports();
|
|
5823
|
-
state = createDevOnDemandState();
|
|
5824
|
-
sourcePathCache = /* @__PURE__ */ new Map();
|
|
5825
|
-
}
|
|
5826
|
-
});
|
|
5827
|
-
|
|
5828
6060
|
// src/loader/loadRouteModule.ts
|
|
5829
6061
|
async function loadRouteModule(filePath, method, rootDir) {
|
|
5830
6062
|
if (isDevOnDemandEnabled() && rootDir) {
|
|
@@ -6011,14 +6243,14 @@ var init_httpErrors = __esm({
|
|
|
6011
6243
|
issues;
|
|
6012
6244
|
};
|
|
6013
6245
|
RouteNotFoundError = class extends FaapiError {
|
|
6014
|
-
constructor(
|
|
6015
|
-
super("ROUTE_NOT_FOUND", `Route not found: ${
|
|
6246
|
+
constructor(path31) {
|
|
6247
|
+
super("ROUTE_NOT_FOUND", `Route not found: ${path31}`, 404);
|
|
6016
6248
|
this.name = "RouteNotFoundError";
|
|
6017
6249
|
}
|
|
6018
6250
|
};
|
|
6019
6251
|
MethodNotAllowedError = class extends FaapiError {
|
|
6020
|
-
constructor(method,
|
|
6021
|
-
super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${
|
|
6252
|
+
constructor(method, path31, allowedMethods) {
|
|
6253
|
+
super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path31}`, 405);
|
|
6022
6254
|
this.allowedMethods = allowedMethods;
|
|
6023
6255
|
this.name = "MethodNotAllowedError";
|
|
6024
6256
|
}
|
|
@@ -6262,6 +6494,7 @@ function createContextFromUrl(request, url, params, config = {}, ip = "", regist
|
|
|
6262
6494
|
};
|
|
6263
6495
|
if (registries) {
|
|
6264
6496
|
ctx.registries = registries;
|
|
6497
|
+
ctx.tasks = registries.taskHandle.get(ctx);
|
|
6265
6498
|
}
|
|
6266
6499
|
const extend = config?.extendContext;
|
|
6267
6500
|
if (typeof extend === "function") {
|
|
@@ -6551,11 +6784,39 @@ async function toResponse(value, meta) {
|
|
|
6551
6784
|
headers
|
|
6552
6785
|
});
|
|
6553
6786
|
}
|
|
6554
|
-
var init_toResponse = __esm({
|
|
6555
|
-
"src/response/toResponse.ts"() {
|
|
6787
|
+
var init_toResponse = __esm({
|
|
6788
|
+
"src/response/toResponse.ts"() {
|
|
6789
|
+
"use strict";
|
|
6790
|
+
init_isPlainObject();
|
|
6791
|
+
init_pendingMeta();
|
|
6792
|
+
}
|
|
6793
|
+
});
|
|
6794
|
+
|
|
6795
|
+
// src/task/taskRegistry.ts
|
|
6796
|
+
function createTaskRegistry() {
|
|
6797
|
+
let registry = /* @__PURE__ */ new Map();
|
|
6798
|
+
return {
|
|
6799
|
+
hydrate(tasks) {
|
|
6800
|
+
const next = /* @__PURE__ */ new Map();
|
|
6801
|
+
for (const task of tasks) {
|
|
6802
|
+
next.set(task.name, task);
|
|
6803
|
+
}
|
|
6804
|
+
registry = next;
|
|
6805
|
+
},
|
|
6806
|
+
get(name) {
|
|
6807
|
+
return registry.get(name);
|
|
6808
|
+
},
|
|
6809
|
+
list() {
|
|
6810
|
+
return Array.from(registry.values());
|
|
6811
|
+
},
|
|
6812
|
+
clear() {
|
|
6813
|
+
registry = /* @__PURE__ */ new Map();
|
|
6814
|
+
}
|
|
6815
|
+
};
|
|
6816
|
+
}
|
|
6817
|
+
var init_taskRegistry = __esm({
|
|
6818
|
+
"src/task/taskRegistry.ts"() {
|
|
6556
6819
|
"use strict";
|
|
6557
|
-
init_isPlainObject();
|
|
6558
|
-
init_pendingMeta();
|
|
6559
6820
|
}
|
|
6560
6821
|
});
|
|
6561
6822
|
|
|
@@ -6681,17 +6942,35 @@ function createAgentHandleStore() {
|
|
|
6681
6942
|
}
|
|
6682
6943
|
};
|
|
6683
6944
|
}
|
|
6945
|
+
function createTaskHandleStore() {
|
|
6946
|
+
let currentFactory = null;
|
|
6947
|
+
return {
|
|
6948
|
+
register(factory) {
|
|
6949
|
+
currentFactory = factory;
|
|
6950
|
+
},
|
|
6951
|
+
get(ctx) {
|
|
6952
|
+
if (currentFactory === null) return void 0;
|
|
6953
|
+
return currentFactory(ctx);
|
|
6954
|
+
},
|
|
6955
|
+
clear() {
|
|
6956
|
+
currentFactory = null;
|
|
6957
|
+
}
|
|
6958
|
+
};
|
|
6959
|
+
}
|
|
6684
6960
|
function createAppRegistries() {
|
|
6685
6961
|
const tool = createToolRegistry();
|
|
6686
6962
|
const agent = createAgentRegistry(tool);
|
|
6687
6963
|
const skill = createSkillRegistry();
|
|
6964
|
+
const task = createTaskRegistry();
|
|
6688
6965
|
const agentHandle = createAgentHandleStore();
|
|
6689
|
-
|
|
6966
|
+
const taskHandle = createTaskHandleStore();
|
|
6967
|
+
return { tool, agent, skill, task, agentHandle, taskHandle };
|
|
6690
6968
|
}
|
|
6691
6969
|
var defaultRegistries;
|
|
6692
6970
|
var init_registries = __esm({
|
|
6693
6971
|
"src/injection/registries.ts"() {
|
|
6694
6972
|
"use strict";
|
|
6973
|
+
init_taskRegistry();
|
|
6695
6974
|
defaultRegistries = createAppRegistries();
|
|
6696
6975
|
}
|
|
6697
6976
|
});
|
|
@@ -6758,6 +7037,9 @@ function getBuiltinInjectionValue(type, ctx, body) {
|
|
|
6758
7037
|
// Phase 3.5:调 @faapi/agent 插件注册的工厂获取 AgentHandle
|
|
6759
7038
|
case "agent":
|
|
6760
7039
|
return ctx.registries ? ctx.registries.agentHandle.get(ctx) : getAgentHandle(ctx);
|
|
7040
|
+
// 任务子系统:注入 TaskClient(入队/查询);未注册工厂(无 app 编排)时 undefined
|
|
7041
|
+
case "tasks":
|
|
7042
|
+
return ctx.registries ? ctx.registries.taskHandle.get(ctx) : void 0;
|
|
6761
7043
|
default:
|
|
6762
7044
|
return void 0;
|
|
6763
7045
|
}
|
|
@@ -6974,9 +7256,9 @@ async function validateInput(schemaPath, method, inputType, input) {
|
|
|
6974
7256
|
function mapZodIssues(error) {
|
|
6975
7257
|
return error.issues.map((issue) => {
|
|
6976
7258
|
const code = mapZodCode(issue);
|
|
6977
|
-
const
|
|
7259
|
+
const path31 = issue.path.map(String).join(".") || "";
|
|
6978
7260
|
return {
|
|
6979
|
-
path:
|
|
7261
|
+
path: path31,
|
|
6980
7262
|
code,
|
|
6981
7263
|
expected: issue.expected ?? mapExpectedFromMessage(issue.message),
|
|
6982
7264
|
received: issue.received ?? mapReceivedFromMessage(issue.message),
|
|
@@ -7474,9 +7756,9 @@ var init_wsHandler = __esm({
|
|
|
7474
7756
|
});
|
|
7475
7757
|
|
|
7476
7758
|
// src/server/handleWsUpgrade.ts
|
|
7477
|
-
import
|
|
7759
|
+
import fs17 from "fs";
|
|
7478
7760
|
import { WebSocketServer, WebSocket } from "ws";
|
|
7479
|
-
import
|
|
7761
|
+
import path23 from "path";
|
|
7480
7762
|
function getPathname(req) {
|
|
7481
7763
|
const url = req.url ?? "/";
|
|
7482
7764
|
const idx = url.indexOf("?");
|
|
@@ -7487,7 +7769,7 @@ async function loadWsHandler(filePath, ctx, rootDir) {
|
|
|
7487
7769
|
const dist = getDevDist();
|
|
7488
7770
|
if (dist) {
|
|
7489
7771
|
const sourcePath = prodPathToSourcePath(filePath, rootDir, dist);
|
|
7490
|
-
if (sourcePath &&
|
|
7772
|
+
if (sourcePath && fs17.existsSync(sourcePath)) {
|
|
7491
7773
|
await ensureCompiled(sourcePath, rootDir, dist);
|
|
7492
7774
|
}
|
|
7493
7775
|
}
|
|
@@ -7601,7 +7883,7 @@ function attachWebSocket(options) {
|
|
|
7601
7883
|
const finalHandler = async () => {
|
|
7602
7884
|
let handlers;
|
|
7603
7885
|
try {
|
|
7604
|
-
const absoluteFilePath =
|
|
7886
|
+
const absoluteFilePath = path23.resolve(rootDir, route.filePath);
|
|
7605
7887
|
handlers = await loadWsHandler(absoluteFilePath, ctx, rootDir);
|
|
7606
7888
|
} catch (err) {
|
|
7607
7889
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -7677,7 +7959,7 @@ import {
|
|
|
7677
7959
|
import { createSecureServer as createHttp2SecureServer } from "http2";
|
|
7678
7960
|
import { readFileSync } from "fs";
|
|
7679
7961
|
import { Readable as Readable3 } from "stream";
|
|
7680
|
-
import
|
|
7962
|
+
import path24 from "path";
|
|
7681
7963
|
function toWebRequest(req, bodyLimit = DEFAULT_BODY_LIMIT, requestSignal) {
|
|
7682
7964
|
const forwardedProto = req.headers["x-forwarded-proto"];
|
|
7683
7965
|
const protocol = Array.isArray(forwardedProto) ? forwardedProto[0]?.split(",")[0]?.trim() ?? "http" : forwardedProto?.split(",")[0]?.trim() ?? "http";
|
|
@@ -7873,7 +8155,7 @@ function getRoutePaths(route, rootDir, dist) {
|
|
|
7873
8155
|
let cached = routePathCache.get(route);
|
|
7874
8156
|
if (!cached) {
|
|
7875
8157
|
cached = {
|
|
7876
|
-
absFilePath:
|
|
8158
|
+
absFilePath: path24.resolve(rootDir, route.filePath),
|
|
7877
8159
|
schemaPath: getRuntimeSchemaPath(route.filePath, dist, rootDir)
|
|
7878
8160
|
};
|
|
7879
8161
|
routePathCache.set(route, cached);
|
|
@@ -8027,10 +8309,513 @@ var init_startServer = __esm({
|
|
|
8027
8309
|
}
|
|
8028
8310
|
});
|
|
8029
8311
|
|
|
8030
|
-
// src/
|
|
8031
|
-
import
|
|
8032
|
-
import fs17 from "fs";
|
|
8312
|
+
// src/task/taskWorker.ts
|
|
8313
|
+
import { Worker } from "worker_threads";
|
|
8033
8314
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
8315
|
+
function safeConfig(config) {
|
|
8316
|
+
if (config === void 0 || config === null) return config;
|
|
8317
|
+
try {
|
|
8318
|
+
structuredClone(config);
|
|
8319
|
+
return config;
|
|
8320
|
+
} catch {
|
|
8321
|
+
}
|
|
8322
|
+
try {
|
|
8323
|
+
return JSON.parse(JSON.stringify(config));
|
|
8324
|
+
} catch {
|
|
8325
|
+
return void 0;
|
|
8326
|
+
}
|
|
8327
|
+
}
|
|
8328
|
+
function buildWrapperSource(moduleUrl) {
|
|
8329
|
+
return `
|
|
8330
|
+
import { parentPort } from 'node:worker_threads';
|
|
8331
|
+
import * as mod from '${moduleUrl}';
|
|
8332
|
+
|
|
8333
|
+
const run = mod.run;
|
|
8334
|
+
if (typeof run !== 'function') {
|
|
8335
|
+
parentPort.postMessage({ type: 'error', message: 'Task module has no run export' });
|
|
8336
|
+
} else {
|
|
8337
|
+
let controller = null;
|
|
8338
|
+
parentPort.on('message', async (msg) => {
|
|
8339
|
+
if (msg?.type === 'abort') {
|
|
8340
|
+
controller?.abort(new Error(msg.reason));
|
|
8341
|
+
return;
|
|
8342
|
+
}
|
|
8343
|
+
if (msg?.type !== 'run') return;
|
|
8344
|
+
controller = new AbortController();
|
|
8345
|
+
const { payload, taskCtx } = msg;
|
|
8346
|
+
try {
|
|
8347
|
+
const result = await run(payload, { ...taskCtx, signal: controller.signal });
|
|
8348
|
+
parentPort.postMessage({ type: 'done', result });
|
|
8349
|
+
} catch (err) {
|
|
8350
|
+
parentPort.postMessage({
|
|
8351
|
+
type: 'error',
|
|
8352
|
+
message: err instanceof Error ? err.message : String(err),
|
|
8353
|
+
});
|
|
8354
|
+
}
|
|
8355
|
+
});
|
|
8356
|
+
}
|
|
8357
|
+
`;
|
|
8358
|
+
}
|
|
8359
|
+
async function runTaskInWorker(options) {
|
|
8360
|
+
const { taskModulePath, payload, taskCtx, timeoutMs, externalSignal } = options;
|
|
8361
|
+
const killGraceMs = options.killGraceMs ?? KILL_GRACE_MS;
|
|
8362
|
+
const moduleUrl = pathToFileURL2(taskModulePath).href;
|
|
8363
|
+
const wrapperUrl = new URL(
|
|
8364
|
+
`data:text/javascript,${encodeURIComponent(buildWrapperSource(moduleUrl))}`
|
|
8365
|
+
);
|
|
8366
|
+
return new Promise((resolve3, reject) => {
|
|
8367
|
+
let worker;
|
|
8368
|
+
try {
|
|
8369
|
+
worker = new Worker(wrapperUrl);
|
|
8370
|
+
} catch (err) {
|
|
8371
|
+
reject(err);
|
|
8372
|
+
return;
|
|
8373
|
+
}
|
|
8374
|
+
let phase = "running";
|
|
8375
|
+
let cancelReason = "";
|
|
8376
|
+
let graceTimer;
|
|
8377
|
+
const finish = (settle) => {
|
|
8378
|
+
if (phase === "settled") return;
|
|
8379
|
+
phase = "settled";
|
|
8380
|
+
clearTimeout(timeoutTimer);
|
|
8381
|
+
if (graceTimer) clearTimeout(graceTimer);
|
|
8382
|
+
externalSignal?.removeEventListener("abort", onExternalAbort);
|
|
8383
|
+
void worker.terminate();
|
|
8384
|
+
settle();
|
|
8385
|
+
};
|
|
8386
|
+
const startCancel = (reason) => {
|
|
8387
|
+
if (phase !== "running") return;
|
|
8388
|
+
phase = "grace";
|
|
8389
|
+
cancelReason = reason;
|
|
8390
|
+
worker.postMessage({ type: "abort", reason });
|
|
8391
|
+
graceTimer = setTimeout(() => {
|
|
8392
|
+
finish(
|
|
8393
|
+
() => reject(new TaskCancelledError(`Task "${taskCtx.job.name}" ${reason} and was terminated`))
|
|
8394
|
+
);
|
|
8395
|
+
}, killGraceMs);
|
|
8396
|
+
};
|
|
8397
|
+
const onExternalAbort = () => startCancel("cancelled (driver stop timeout)");
|
|
8398
|
+
if (externalSignal) {
|
|
8399
|
+
if (externalSignal.aborted) {
|
|
8400
|
+
onExternalAbort();
|
|
8401
|
+
} else {
|
|
8402
|
+
externalSignal.addEventListener("abort", onExternalAbort, { once: true });
|
|
8403
|
+
}
|
|
8404
|
+
}
|
|
8405
|
+
const timeoutTimer = setTimeout(
|
|
8406
|
+
() => startCancel(`timed out after ${timeoutMs}ms`),
|
|
8407
|
+
timeoutMs
|
|
8408
|
+
);
|
|
8409
|
+
worker.on("message", (msg) => {
|
|
8410
|
+
if (phase === "grace") {
|
|
8411
|
+
if (msg?.type === "done") {
|
|
8412
|
+
finish(
|
|
8413
|
+
() => reject(
|
|
8414
|
+
new TaskCancelledError(
|
|
8415
|
+
`Task "${taskCtx.job.name}" ${cancelReason} (task completed after the timeout)`
|
|
8416
|
+
)
|
|
8417
|
+
)
|
|
8418
|
+
);
|
|
8419
|
+
} else if (msg?.type === "error") {
|
|
8420
|
+
finish(() => reject(new TaskCancelledError(msg.message ?? cancelReason)));
|
|
8421
|
+
}
|
|
8422
|
+
return;
|
|
8423
|
+
}
|
|
8424
|
+
if (phase !== "running") return;
|
|
8425
|
+
if (msg?.type === "done") {
|
|
8426
|
+
finish(() => resolve3(msg.result));
|
|
8427
|
+
} else if (msg?.type === "error") {
|
|
8428
|
+
finish(() => reject(new Error(msg.message ?? "task worker error")));
|
|
8429
|
+
}
|
|
8430
|
+
});
|
|
8431
|
+
worker.on("error", (err) => {
|
|
8432
|
+
finish(() => reject(err));
|
|
8433
|
+
});
|
|
8434
|
+
worker.on("exit", (code) => {
|
|
8435
|
+
if (phase === "grace") {
|
|
8436
|
+
finish(() => reject(new TaskCancelledError(`Task "${taskCtx.job.name}" ${cancelReason}`)));
|
|
8437
|
+
} else if (phase === "running") {
|
|
8438
|
+
finish(() => reject(new Error(`Task worker exited unexpectedly (code ${code})`)));
|
|
8439
|
+
}
|
|
8440
|
+
});
|
|
8441
|
+
try {
|
|
8442
|
+
worker.postMessage({
|
|
8443
|
+
type: "run",
|
|
8444
|
+
payload,
|
|
8445
|
+
taskCtx: { config: safeConfig(taskCtx.config), job: taskCtx.job }
|
|
8446
|
+
});
|
|
8447
|
+
} catch (err) {
|
|
8448
|
+
finish(
|
|
8449
|
+
() => reject(
|
|
8450
|
+
new Error(`Task "${taskCtx.job.name}" payload/config is not cloneable: ${String(err)}`)
|
|
8451
|
+
)
|
|
8452
|
+
);
|
|
8453
|
+
}
|
|
8454
|
+
});
|
|
8455
|
+
}
|
|
8456
|
+
var KILL_GRACE_MS, TaskCancelledError;
|
|
8457
|
+
var init_taskWorker = __esm({
|
|
8458
|
+
"src/task/taskWorker.ts"() {
|
|
8459
|
+
"use strict";
|
|
8460
|
+
KILL_GRACE_MS = 5e3;
|
|
8461
|
+
TaskCancelledError = class extends Error {
|
|
8462
|
+
constructor(message) {
|
|
8463
|
+
super(message);
|
|
8464
|
+
this.name = "TaskCancelledError";
|
|
8465
|
+
}
|
|
8466
|
+
};
|
|
8467
|
+
}
|
|
8468
|
+
});
|
|
8469
|
+
|
|
8470
|
+
// src/task/taskQueue.ts
|
|
8471
|
+
import path25 from "path";
|
|
8472
|
+
function createTaskQueue(deps) {
|
|
8473
|
+
const { registry, rootDir, driver } = deps;
|
|
8474
|
+
const records = /* @__PURE__ */ new Map();
|
|
8475
|
+
const moduleCache2 = /* @__PURE__ */ new Map();
|
|
8476
|
+
const schemaCache = /* @__PURE__ */ new Map();
|
|
8477
|
+
let started = false;
|
|
8478
|
+
let stopped = false;
|
|
8479
|
+
const loadTaskModule = deps.loadTaskModule ?? (async (filePath) => await import(filePath));
|
|
8480
|
+
const loadPayloadSchema = deps.loadPayloadSchema ?? (async (filePath) => {
|
|
8481
|
+
const zodPath = path25.join(path25.dirname(filePath), "zod.js");
|
|
8482
|
+
try {
|
|
8483
|
+
const mod = await import(zodPath);
|
|
8484
|
+
const schemaKey = Object.keys(mod).find((k) => k.endsWith("Schema"));
|
|
8485
|
+
return schemaKey ? mod[schemaKey] : void 0;
|
|
8486
|
+
} catch {
|
|
8487
|
+
return void 0;
|
|
8488
|
+
}
|
|
8489
|
+
});
|
|
8490
|
+
function assertKnownTask(name) {
|
|
8491
|
+
const meta = registry.get(name);
|
|
8492
|
+
if (!meta) {
|
|
8493
|
+
const known = registry.list().map((t) => t.name).join(", ") || "(none)";
|
|
8494
|
+
throw new Error(`[faapi] Unknown task "${name}". Registered tasks: ${known}`);
|
|
8495
|
+
}
|
|
8496
|
+
}
|
|
8497
|
+
async function validatePayload(name, payload) {
|
|
8498
|
+
if (!schemaCache.has(name)) {
|
|
8499
|
+
const meta = registry.get(name);
|
|
8500
|
+
schemaCache.set(
|
|
8501
|
+
name,
|
|
8502
|
+
await loadPayloadSchema(path25.resolve(rootDir, meta.filePath)).catch(() => void 0)
|
|
8503
|
+
);
|
|
8504
|
+
}
|
|
8505
|
+
const schema = schemaCache.get(name);
|
|
8506
|
+
if (!schema || typeof schema.safeParse !== "function") {
|
|
8507
|
+
return payload;
|
|
8508
|
+
}
|
|
8509
|
+
const result = schema.safeParse(payload);
|
|
8510
|
+
if (!result.success) {
|
|
8511
|
+
throw new ValidationError(
|
|
8512
|
+
`Task payload validation failed for "${name}": ${JSON.stringify(result.error)}`,
|
|
8513
|
+
[]
|
|
8514
|
+
);
|
|
8515
|
+
}
|
|
8516
|
+
return result.data;
|
|
8517
|
+
}
|
|
8518
|
+
async function runJob(job) {
|
|
8519
|
+
const meta = registry.get(job.name);
|
|
8520
|
+
if (!meta) throw new Error(`[faapi] Unknown task "${job.name}" at worker dispatch`);
|
|
8521
|
+
const record = records.get(job.id) ?? {
|
|
8522
|
+
id: job.id,
|
|
8523
|
+
name: job.name,
|
|
8524
|
+
payload: job.payload,
|
|
8525
|
+
status: "pending",
|
|
8526
|
+
attempts: 0,
|
|
8527
|
+
createdAt: Date.now()
|
|
8528
|
+
};
|
|
8529
|
+
record.attempts = job.attempt;
|
|
8530
|
+
record.status = "running";
|
|
8531
|
+
record.error = void 0;
|
|
8532
|
+
records.set(job.id, record);
|
|
8533
|
+
try {
|
|
8534
|
+
let result;
|
|
8535
|
+
if (meta.timeoutMs !== void 0 && meta.timeoutMs > 0) {
|
|
8536
|
+
result = await (deps.runIsolated ?? runTaskInWorker)({
|
|
8537
|
+
taskModulePath: path25.resolve(rootDir, meta.filePath),
|
|
8538
|
+
payload: job.payload,
|
|
8539
|
+
taskCtx: {
|
|
8540
|
+
config: deps.config,
|
|
8541
|
+
job: { id: job.id, name: job.name, attempt: job.attempt }
|
|
8542
|
+
},
|
|
8543
|
+
timeoutMs: meta.timeoutMs,
|
|
8544
|
+
externalSignal: job.signal
|
|
8545
|
+
});
|
|
8546
|
+
} else {
|
|
8547
|
+
let mod = moduleCache2.get(job.name);
|
|
8548
|
+
if (!mod) {
|
|
8549
|
+
mod = await loadTaskModule(path25.resolve(rootDir, meta.filePath));
|
|
8550
|
+
moduleCache2.set(job.name, mod);
|
|
8551
|
+
}
|
|
8552
|
+
if (typeof mod.run !== "function") {
|
|
8553
|
+
throw new Error(`Task "${job.name}" module has no run export`);
|
|
8554
|
+
}
|
|
8555
|
+
const taskCtx = {
|
|
8556
|
+
signal: job.signal,
|
|
8557
|
+
config: deps.config,
|
|
8558
|
+
job: { id: job.id, name: job.name, attempt: job.attempt }
|
|
8559
|
+
};
|
|
8560
|
+
result = await mod.run(job.payload, taskCtx);
|
|
8561
|
+
}
|
|
8562
|
+
record.status = "done";
|
|
8563
|
+
record.result = result;
|
|
8564
|
+
return result;
|
|
8565
|
+
} catch (err) {
|
|
8566
|
+
const cancelled = err instanceof TaskCancelledError || job.signal.aborted;
|
|
8567
|
+
record.status = cancelled ? "cancelled" : "failed";
|
|
8568
|
+
record.error = err instanceof Error ? err.message : String(err);
|
|
8569
|
+
if (deps.onFailed) {
|
|
8570
|
+
void Promise.resolve().then(
|
|
8571
|
+
() => deps.onFailed({
|
|
8572
|
+
task: job.name,
|
|
8573
|
+
jobId: job.id,
|
|
8574
|
+
attempt: job.attempt,
|
|
8575
|
+
willRetry: job.attempt <= (meta.retries ?? 0),
|
|
8576
|
+
cancelled,
|
|
8577
|
+
error: record.error
|
|
8578
|
+
})
|
|
8579
|
+
).catch(() => {
|
|
8580
|
+
});
|
|
8581
|
+
}
|
|
8582
|
+
throw err;
|
|
8583
|
+
}
|
|
8584
|
+
}
|
|
8585
|
+
async function startWorkers() {
|
|
8586
|
+
for (const meta of registry.list()) {
|
|
8587
|
+
await driver.startWorker(meta.name, {
|
|
8588
|
+
concurrency: meta.concurrency ?? 1,
|
|
8589
|
+
process: runJob
|
|
8590
|
+
});
|
|
8591
|
+
}
|
|
8592
|
+
}
|
|
8593
|
+
const queue = {
|
|
8594
|
+
async enqueue(name, payload = {}, opts) {
|
|
8595
|
+
if (stopped) {
|
|
8596
|
+
throw new Error("[faapi] Task queue is stopped and no longer accepts jobs");
|
|
8597
|
+
}
|
|
8598
|
+
assertKnownTask(name);
|
|
8599
|
+
const data = await validatePayload(name, payload);
|
|
8600
|
+
const meta = registry.get(name);
|
|
8601
|
+
const id = await driver.enqueue(name, data, {
|
|
8602
|
+
delayMs: opts?.delayMs,
|
|
8603
|
+
retries: meta.retries ?? 0,
|
|
8604
|
+
dedupId: opts?.dedupId
|
|
8605
|
+
});
|
|
8606
|
+
if (!records.has(id)) {
|
|
8607
|
+
records.set(id, {
|
|
8608
|
+
id,
|
|
8609
|
+
name,
|
|
8610
|
+
payload: data,
|
|
8611
|
+
status: "pending",
|
|
8612
|
+
attempts: 0,
|
|
8613
|
+
createdAt: Date.now(),
|
|
8614
|
+
...opts?.delayMs ? { runAt: Date.now() + opts.delayMs } : {}
|
|
8615
|
+
});
|
|
8616
|
+
}
|
|
8617
|
+
return { id };
|
|
8618
|
+
},
|
|
8619
|
+
list(name) {
|
|
8620
|
+
const snapshot = [];
|
|
8621
|
+
for (const job of records.values()) {
|
|
8622
|
+
if (name !== void 0 && job.name !== name) continue;
|
|
8623
|
+
snapshot.push({ ...job });
|
|
8624
|
+
}
|
|
8625
|
+
return snapshot;
|
|
8626
|
+
},
|
|
8627
|
+
async listQueued(name) {
|
|
8628
|
+
if (!driver.list) {
|
|
8629
|
+
throw new Error(
|
|
8630
|
+
"[faapi] Task driver does not support listing queued tasks (TaskDriver.list is not implemented). Use the queue system's own management tools, or see driverTypes.md for per-driver capability."
|
|
8631
|
+
);
|
|
8632
|
+
}
|
|
8633
|
+
const queued = await driver.list({ name, limit: 50 });
|
|
8634
|
+
const merged = /* @__PURE__ */ new Map();
|
|
8635
|
+
for (const record of queued) {
|
|
8636
|
+
if (name !== void 0 && record.name !== name) continue;
|
|
8637
|
+
merged.set(record.id, {
|
|
8638
|
+
id: record.id,
|
|
8639
|
+
name: record.name,
|
|
8640
|
+
payload: record.payload,
|
|
8641
|
+
status: record.status,
|
|
8642
|
+
attempts: record.attempts,
|
|
8643
|
+
createdAt: record.createdAt,
|
|
8644
|
+
...record.result !== void 0 ? { result: record.result } : {},
|
|
8645
|
+
...record.error !== void 0 ? { error: record.error } : {},
|
|
8646
|
+
...record.runAt !== void 0 ? { runAt: record.runAt } : {}
|
|
8647
|
+
});
|
|
8648
|
+
}
|
|
8649
|
+
for (const local of records.values()) {
|
|
8650
|
+
if (name !== void 0 && local.name !== name) continue;
|
|
8651
|
+
merged.set(local.id, { ...local });
|
|
8652
|
+
}
|
|
8653
|
+
return [...merged.values()];
|
|
8654
|
+
},
|
|
8655
|
+
async cancel(name, id) {
|
|
8656
|
+
if (!driver.cancel) {
|
|
8657
|
+
throw new Error(
|
|
8658
|
+
"[faapi] Task driver does not support cancelling tasks (TaskDriver.cancel is not implemented)."
|
|
8659
|
+
);
|
|
8660
|
+
}
|
|
8661
|
+
await driver.cancel(name, id);
|
|
8662
|
+
const record = records.get(id);
|
|
8663
|
+
if (record) record.status = "cancelled";
|
|
8664
|
+
},
|
|
8665
|
+
async retry(name, id) {
|
|
8666
|
+
if (!driver.retry) {
|
|
8667
|
+
throw new Error(
|
|
8668
|
+
"[faapi] Task driver does not support retrying tasks (TaskDriver.retry is not implemented)."
|
|
8669
|
+
);
|
|
8670
|
+
}
|
|
8671
|
+
await driver.retry(name, id);
|
|
8672
|
+
const record = records.get(id);
|
|
8673
|
+
if (record) record.status = "pending";
|
|
8674
|
+
},
|
|
8675
|
+
async start() {
|
|
8676
|
+
if (stopped) {
|
|
8677
|
+
throw new Error("[faapi] Task queue is stopped and cannot be restarted");
|
|
8678
|
+
}
|
|
8679
|
+
if (started) return;
|
|
8680
|
+
started = true;
|
|
8681
|
+
await startWorkers();
|
|
8682
|
+
},
|
|
8683
|
+
async stop(timeoutMs = 1e4) {
|
|
8684
|
+
stopped = true;
|
|
8685
|
+
await driver.stop(timeoutMs);
|
|
8686
|
+
},
|
|
8687
|
+
async reload() {
|
|
8688
|
+
await driver.stopWorkers?.();
|
|
8689
|
+
await startWorkers();
|
|
8690
|
+
},
|
|
8691
|
+
invalidateModules() {
|
|
8692
|
+
moduleCache2.clear();
|
|
8693
|
+
},
|
|
8694
|
+
invalidateSchemas() {
|
|
8695
|
+
schemaCache.clear();
|
|
8696
|
+
}
|
|
8697
|
+
};
|
|
8698
|
+
return queue;
|
|
8699
|
+
}
|
|
8700
|
+
var init_taskQueue = __esm({
|
|
8701
|
+
"src/task/taskQueue.ts"() {
|
|
8702
|
+
"use strict";
|
|
8703
|
+
init_httpErrors();
|
|
8704
|
+
init_taskWorker();
|
|
8705
|
+
}
|
|
8706
|
+
});
|
|
8707
|
+
|
|
8708
|
+
// src/task/loadTaskDriver.ts
|
|
8709
|
+
async function loadTaskDriver(driver, driverOptions) {
|
|
8710
|
+
if (driver === void 0) {
|
|
8711
|
+
throw new Error(
|
|
8712
|
+
"[faapi] config.task.driver is required when tasks are defined. Install @faapi/task-pgboss or @faapi/task-bullmq and set `task.driver` to 'pgboss' or 'bullmq' (or pass a TaskDriver instance)."
|
|
8713
|
+
);
|
|
8714
|
+
}
|
|
8715
|
+
if (driver === "memory") {
|
|
8716
|
+
throw new Error(
|
|
8717
|
+
"[faapi] The built-in memory task driver has been removed. Migrate to a persistent driver: install @faapi/task-pgboss or @faapi/task-bullmq and set `task.driver` to 'pgboss' or 'bullmq'."
|
|
8718
|
+
);
|
|
8719
|
+
}
|
|
8720
|
+
if (typeof driver === "object") {
|
|
8721
|
+
return driver;
|
|
8722
|
+
}
|
|
8723
|
+
if (driver !== "pgboss" && driver !== "bullmq") {
|
|
8724
|
+
throw new Error(
|
|
8725
|
+
`[faapi] Unknown task driver "${driver}". Supported: 'pgboss', 'bullmq', or a TaskDriver instance.`
|
|
8726
|
+
);
|
|
8727
|
+
}
|
|
8728
|
+
const specifier = `@faapi/task-${driver}`;
|
|
8729
|
+
let mod;
|
|
8730
|
+
try {
|
|
8731
|
+
mod = await import(
|
|
8732
|
+
/* @vite-ignore */
|
|
8733
|
+
specifier
|
|
8734
|
+
);
|
|
8735
|
+
} catch {
|
|
8736
|
+
throw new Error(
|
|
8737
|
+
`[faapi] Task driver "${driver}" requires package "${specifier}" to be installed in your project.`
|
|
8738
|
+
);
|
|
8739
|
+
}
|
|
8740
|
+
const factoryName = driver === "pgboss" ? "createPgBossDriver" : "createBullMQDriver";
|
|
8741
|
+
const factory = mod[factoryName];
|
|
8742
|
+
if (typeof factory !== "function") {
|
|
8743
|
+
throw new Error(
|
|
8744
|
+
`[faapi] Package "${specifier}" does not export ${factoryName}() \u2014 check the package version.`
|
|
8745
|
+
);
|
|
8746
|
+
}
|
|
8747
|
+
return factory(driverOptions);
|
|
8748
|
+
}
|
|
8749
|
+
var init_loadTaskDriver = __esm({
|
|
8750
|
+
"src/task/loadTaskDriver.ts"() {
|
|
8751
|
+
"use strict";
|
|
8752
|
+
}
|
|
8753
|
+
});
|
|
8754
|
+
|
|
8755
|
+
// src/task/idleTaskDriver.ts
|
|
8756
|
+
function createIdleTaskDriver() {
|
|
8757
|
+
return {
|
|
8758
|
+
enqueue() {
|
|
8759
|
+
return Promise.reject(
|
|
8760
|
+
new Error(
|
|
8761
|
+
"[faapi] New task(s) were registered without a queue driver. Set config.task.driver to 'pgboss' or 'bullmq' (install the matching @faapi/task-* package) and restart the server."
|
|
8762
|
+
)
|
|
8763
|
+
);
|
|
8764
|
+
},
|
|
8765
|
+
startWorker() {
|
|
8766
|
+
},
|
|
8767
|
+
async stop() {
|
|
8768
|
+
},
|
|
8769
|
+
async stopWorkers() {
|
|
8770
|
+
}
|
|
8771
|
+
};
|
|
8772
|
+
}
|
|
8773
|
+
var init_idleTaskDriver = __esm({
|
|
8774
|
+
"src/task/idleTaskDriver.ts"() {
|
|
8775
|
+
"use strict";
|
|
8776
|
+
}
|
|
8777
|
+
});
|
|
8778
|
+
|
|
8779
|
+
// src/task/cronScheduler.ts
|
|
8780
|
+
import { Cron } from "croner";
|
|
8781
|
+
function createCronScheduler(registry, enqueue) {
|
|
8782
|
+
let schedules = [];
|
|
8783
|
+
return {
|
|
8784
|
+
start() {
|
|
8785
|
+
this.stop();
|
|
8786
|
+
for (const task of registry.list()) {
|
|
8787
|
+
if (!task.cron) continue;
|
|
8788
|
+
const schedule = new Cron(task.cron, () => {
|
|
8789
|
+
const runAt = schedule.currentRun();
|
|
8790
|
+
void Promise.resolve().then(
|
|
8791
|
+
() => enqueue(task.name, {
|
|
8792
|
+
dedupId: runAt ? `cron:${task.name}:${runAt.toISOString()}` : void 0
|
|
8793
|
+
})
|
|
8794
|
+
).catch((err) => {
|
|
8795
|
+
console.error(`[faapi] Cron enqueue failed for task "${task.name}":`, err);
|
|
8796
|
+
});
|
|
8797
|
+
});
|
|
8798
|
+
schedules.push(schedule);
|
|
8799
|
+
}
|
|
8800
|
+
},
|
|
8801
|
+
stop() {
|
|
8802
|
+
for (const schedule of schedules) {
|
|
8803
|
+
schedule.stop();
|
|
8804
|
+
}
|
|
8805
|
+
schedules = [];
|
|
8806
|
+
}
|
|
8807
|
+
};
|
|
8808
|
+
}
|
|
8809
|
+
var init_cronScheduler = __esm({
|
|
8810
|
+
"src/task/cronScheduler.ts"() {
|
|
8811
|
+
"use strict";
|
|
8812
|
+
}
|
|
8813
|
+
});
|
|
8814
|
+
|
|
8815
|
+
// src/cli/loadPlugins.ts
|
|
8816
|
+
import path26 from "path";
|
|
8817
|
+
import fs18 from "fs";
|
|
8818
|
+
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
8034
8819
|
async function loadPlugins(declarations, ctx, rootDir, dist) {
|
|
8035
8820
|
const handlerWrappers = [];
|
|
8036
8821
|
const upgradeWrappers = [];
|
|
@@ -8092,38 +8877,38 @@ async function loadPlugins(declarations, ctx, rootDir, dist) {
|
|
|
8092
8877
|
return { handlerWrappers, upgradeWrappers, failures };
|
|
8093
8878
|
}
|
|
8094
8879
|
function resolveLocalPluginSource(specifier, baseDir) {
|
|
8095
|
-
const base =
|
|
8096
|
-
if ((TS_EXTS.test(base) || JS_EXTS.test(base)) &&
|
|
8880
|
+
const base = path26.isAbsolute(specifier) ? specifier : path26.resolve(baseDir, specifier);
|
|
8881
|
+
if ((TS_EXTS.test(base) || JS_EXTS.test(base)) && fs18.existsSync(base)) return base;
|
|
8097
8882
|
for (const ext of [".ts", ".js"]) {
|
|
8098
|
-
if (
|
|
8883
|
+
if (fs18.existsSync(base + ext)) return base + ext;
|
|
8099
8884
|
}
|
|
8100
8885
|
for (const indexExt of ["/index.ts", "/index.js"]) {
|
|
8101
|
-
if (
|
|
8886
|
+
if (fs18.existsSync(base + indexExt)) return base + indexExt;
|
|
8102
8887
|
}
|
|
8103
8888
|
return null;
|
|
8104
8889
|
}
|
|
8105
8890
|
function mirrorProductPath(sourcePath, baseDir, distDir) {
|
|
8106
|
-
let rel =
|
|
8891
|
+
let rel = path26.relative(baseDir, sourcePath).replace(/\\/g, "/");
|
|
8107
8892
|
if (rel.startsWith("src/")) rel = rel.slice(4);
|
|
8108
|
-
return
|
|
8893
|
+
return path26.resolve(distDir, rel.replace(TS_EXTS, ".js"));
|
|
8109
8894
|
}
|
|
8110
8895
|
function isSourceNewer(sourcePath, productPath) {
|
|
8111
8896
|
try {
|
|
8112
|
-
return
|
|
8897
|
+
return fs18.statSync(sourcePath).mtimeMs > fs18.statSync(productPath).mtimeMs;
|
|
8113
8898
|
} catch {
|
|
8114
8899
|
return true;
|
|
8115
8900
|
}
|
|
8116
8901
|
}
|
|
8117
8902
|
async function importPluginModule(specifier, baseDir, dist) {
|
|
8118
8903
|
const isRelative = specifier.startsWith("./") || specifier.startsWith("../");
|
|
8119
|
-
if (!isRelative && !
|
|
8904
|
+
if (!isRelative && !path26.isAbsolute(specifier)) {
|
|
8120
8905
|
return import(specifier);
|
|
8121
8906
|
}
|
|
8122
|
-
const distDir = dist ?
|
|
8907
|
+
const distDir = dist ? path26.resolve(baseDir, dist) : null;
|
|
8123
8908
|
const sourcePath = resolveLocalPluginSource(specifier, baseDir);
|
|
8124
8909
|
const productPath = sourcePath && distDir ? mirrorProductPath(sourcePath, baseDir, distDir) : null;
|
|
8125
|
-
if (productPath !== null &&
|
|
8126
|
-
return import(
|
|
8910
|
+
if (productPath !== null && fs18.existsSync(productPath) && (sourcePath === null || !isSourceNewer(sourcePath, productPath))) {
|
|
8911
|
+
return import(pathToFileURL3(productPath).href);
|
|
8127
8912
|
}
|
|
8128
8913
|
if (sourcePath && TS_EXTS.test(sourcePath)) {
|
|
8129
8914
|
if (!isDevOnDemandEnabled() || !distDir) {
|
|
@@ -8131,25 +8916,25 @@ async function importPluginModule(specifier, baseDir, dist) {
|
|
|
8131
8916
|
`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
8917
|
);
|
|
8133
8918
|
}
|
|
8134
|
-
const relFromRoot =
|
|
8919
|
+
const relFromRoot = path26.relative(baseDir, sourcePath).replace(/\\/g, "/");
|
|
8135
8920
|
if (relFromRoot.startsWith("src/")) {
|
|
8136
8921
|
await ensureCompiled(sourcePath, baseDir, dist);
|
|
8137
8922
|
} else {
|
|
8138
8923
|
await compileProjectModules([sourcePath], baseDir, dist);
|
|
8139
8924
|
}
|
|
8140
|
-
if (productPath &&
|
|
8141
|
-
return import(
|
|
8925
|
+
if (productPath && fs18.existsSync(productPath)) {
|
|
8926
|
+
return import(pathToFileURL3(productPath).href);
|
|
8142
8927
|
}
|
|
8143
8928
|
}
|
|
8144
8929
|
if (sourcePath && JS_EXTS.test(sourcePath)) {
|
|
8145
|
-
return import(
|
|
8930
|
+
return import(pathToFileURL3(sourcePath).href);
|
|
8146
8931
|
}
|
|
8147
8932
|
const candidates = [
|
|
8148
8933
|
`${specifier}.ts`,
|
|
8149
8934
|
`${specifier}/index.ts`,
|
|
8150
8935
|
`${specifier}.js`,
|
|
8151
8936
|
`${specifier}/index.js`
|
|
8152
|
-
].map((c) =>
|
|
8937
|
+
].map((c) => path26.isAbsolute(c) ? c : path26.join(baseDir, c));
|
|
8153
8938
|
throw new Error(
|
|
8154
8939
|
`Cannot find local plugin "${specifier}" (resolved from ${baseDir}). Expected one of:
|
|
8155
8940
|
${candidates.join("\n ")}
|
|
@@ -8184,12 +8969,12 @@ var init_loadPlugins = __esm({
|
|
|
8184
8969
|
});
|
|
8185
8970
|
|
|
8186
8971
|
// src/cli/createAppCore.ts
|
|
8187
|
-
import
|
|
8188
|
-
import
|
|
8972
|
+
import fs19 from "fs";
|
|
8973
|
+
import path27 from "path";
|
|
8189
8974
|
import { PassThrough, Readable as Readable4 } from "stream";
|
|
8190
8975
|
async function loadAndHydrateTools(rootDir, dist, registries = defaultRegistries) {
|
|
8191
|
-
const toolsPath =
|
|
8192
|
-
if (!
|
|
8976
|
+
const toolsPath = path27.resolve(rootDir, dist, TOOLS_FILE2);
|
|
8977
|
+
if (!fs19.existsSync(toolsPath)) {
|
|
8193
8978
|
return [];
|
|
8194
8979
|
}
|
|
8195
8980
|
const serialized = await importWithCacheBust(toolsPath);
|
|
@@ -8198,8 +8983,8 @@ async function loadAndHydrateTools(rootDir, dist, registries = defaultRegistries
|
|
|
8198
8983
|
return hydrated;
|
|
8199
8984
|
}
|
|
8200
8985
|
async function loadAndHydrateAgents(rootDir, dist, registries = defaultRegistries) {
|
|
8201
|
-
const agentsPath =
|
|
8202
|
-
if (!
|
|
8986
|
+
const agentsPath = path27.resolve(rootDir, dist, AGENTS_FILE2);
|
|
8987
|
+
if (!fs19.existsSync(agentsPath)) {
|
|
8203
8988
|
return [];
|
|
8204
8989
|
}
|
|
8205
8990
|
const serialized = await importWithCacheBust(agentsPath);
|
|
@@ -8207,6 +8992,22 @@ async function loadAndHydrateAgents(rootDir, dist, registries = defaultRegistrie
|
|
|
8207
8992
|
registries.agent.hydrate(hydrated);
|
|
8208
8993
|
return hydrated;
|
|
8209
8994
|
}
|
|
8995
|
+
function getTaskDriverOptions(config) {
|
|
8996
|
+
if (!config?.task) return void 0;
|
|
8997
|
+
if (config.task.driver === "pgboss") return config.task.pgboss;
|
|
8998
|
+
if (config.task.driver === "bullmq") return config.task.bullmq;
|
|
8999
|
+
return void 0;
|
|
9000
|
+
}
|
|
9001
|
+
async function loadAndHydrateTasks(rootDir, dist, registries = defaultRegistries) {
|
|
9002
|
+
const tasksPath = path27.resolve(rootDir, dist, TASKS_FILE);
|
|
9003
|
+
if (!fs19.existsSync(tasksPath)) {
|
|
9004
|
+
return [];
|
|
9005
|
+
}
|
|
9006
|
+
const serialized = await importWithCacheBust(tasksPath);
|
|
9007
|
+
const hydrated = hydrateTasks(serialized.tasks ?? []);
|
|
9008
|
+
registries.task.hydrate(hydrated);
|
|
9009
|
+
return hydrated;
|
|
9010
|
+
}
|
|
8210
9011
|
function getCurrentApp() {
|
|
8211
9012
|
return globalThis[APP_INSTANCE_KEY] ?? null;
|
|
8212
9013
|
}
|
|
@@ -8239,8 +9040,8 @@ function isFaapiConfigKey(key) {
|
|
|
8239
9040
|
async function createAppBase(options) {
|
|
8240
9041
|
const rootDir = options?.rootDir ?? process.cwd();
|
|
8241
9042
|
const dist = options?.dist ?? process.env.FAAPI_DIST ?? DEFAULT_DIST;
|
|
8242
|
-
const routesPath =
|
|
8243
|
-
if (!
|
|
9043
|
+
const routesPath = path27.resolve(rootDir, dist, ROUTES_FILE);
|
|
9044
|
+
if (!fs19.existsSync(routesPath)) {
|
|
8244
9045
|
throw new Error(
|
|
8245
9046
|
`[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
9047
|
);
|
|
@@ -8262,6 +9063,29 @@ async function createAppBase(options) {
|
|
|
8262
9063
|
const registries = createAppRegistries();
|
|
8263
9064
|
const tools = await loadAndHydrateTools(rootDir, dist, registries);
|
|
8264
9065
|
const agents = await loadAndHydrateAgents(rootDir, dist, registries);
|
|
9066
|
+
const taskMetas = await loadAndHydrateTasks(rootDir, dist, registries);
|
|
9067
|
+
const taskDriver = taskMetas.length ? await loadTaskDriver(config?.task?.driver, getTaskDriverOptions(config)) : createIdleTaskDriver();
|
|
9068
|
+
const taskQueue = createTaskQueue({
|
|
9069
|
+
registry: registries.task,
|
|
9070
|
+
rootDir,
|
|
9071
|
+
config,
|
|
9072
|
+
driver: taskDriver,
|
|
9073
|
+
onFailed: config?.task?.onFailed
|
|
9074
|
+
});
|
|
9075
|
+
const cronScheduler = createCronScheduler(
|
|
9076
|
+
registries.task,
|
|
9077
|
+
(name, opts) => taskQueue.enqueue(name, void 0, opts)
|
|
9078
|
+
);
|
|
9079
|
+
const taskEnabled = process.env.FAAPI_TASKS_DISABLED === "1" ? false : config?.task?.enabled ?? true;
|
|
9080
|
+
const stopTaskRuntime = async () => {
|
|
9081
|
+
cronScheduler.stop();
|
|
9082
|
+
await taskQueue.stop(config?.task?.shutdownTimeoutMs ?? 1e4);
|
|
9083
|
+
};
|
|
9084
|
+
if (taskEnabled) {
|
|
9085
|
+
taskQueue.start();
|
|
9086
|
+
cronScheduler.start();
|
|
9087
|
+
}
|
|
9088
|
+
registries.taskHandle.register(() => taskQueue);
|
|
8265
9089
|
const pluginConfig = config ? Object.fromEntries(Object.entries(config).filter(([k]) => !isFaapiConfigKey(k))) : {};
|
|
8266
9090
|
const { server, routesRef } = createServer({
|
|
8267
9091
|
routes: sorted,
|
|
@@ -8303,14 +9127,22 @@ async function createAppBase(options) {
|
|
|
8303
9127
|
routes: sorted,
|
|
8304
9128
|
wsRoutes,
|
|
8305
9129
|
rootDir,
|
|
9130
|
+
tasks: taskQueue,
|
|
8306
9131
|
async listen(listenPort) {
|
|
8307
9132
|
const envPort = process.env.PORT ? Number(process.env.PORT) : void 0;
|
|
8308
9133
|
const actualPort = listenPort ?? options?.port ?? envPort ?? DEFAULT_PORT;
|
|
8309
9134
|
if (config?.lifecycle?.onBoot) {
|
|
8310
9135
|
try {
|
|
8311
|
-
await config.lifecycle.onBoot({
|
|
9136
|
+
await config.lifecycle.onBoot({
|
|
9137
|
+
rootDir,
|
|
9138
|
+
routes: sorted,
|
|
9139
|
+
server,
|
|
9140
|
+
registries,
|
|
9141
|
+
tasks: taskQueue
|
|
9142
|
+
});
|
|
8312
9143
|
console.log("- onBoot hook executed");
|
|
8313
9144
|
} catch (err) {
|
|
9145
|
+
await stopTaskRuntime();
|
|
8314
9146
|
const message = err instanceof Error ? err.message : String(err);
|
|
8315
9147
|
console.error(`[faapi] onBoot hook failed: ${message}`);
|
|
8316
9148
|
throw err;
|
|
@@ -8358,9 +9190,22 @@ async function createAppBase(options) {
|
|
|
8358
9190
|
console.log(` ${agent.name} [${exports}] ${agent.filePath}`);
|
|
8359
9191
|
}
|
|
8360
9192
|
}
|
|
9193
|
+
if (taskMetas.length > 0) {
|
|
9194
|
+
console.log(`- Loaded ${taskMetas.length} task(s):`);
|
|
9195
|
+
for (const taskMeta of taskMetas) {
|
|
9196
|
+
const schedule = taskMeta.cron ? ` cron=${taskMeta.cron}` : "";
|
|
9197
|
+
console.log(` ${taskMeta.name}${schedule} ${taskMeta.filePath}`);
|
|
9198
|
+
}
|
|
9199
|
+
}
|
|
8361
9200
|
registerDefaultShutdownHandlers();
|
|
8362
9201
|
if (config?.lifecycle?.onReady) {
|
|
8363
|
-
await config.lifecycle.onReady({
|
|
9202
|
+
await config.lifecycle.onReady({
|
|
9203
|
+
rootDir,
|
|
9204
|
+
routes: sorted,
|
|
9205
|
+
server,
|
|
9206
|
+
registries,
|
|
9207
|
+
tasks: taskQueue
|
|
9208
|
+
});
|
|
8364
9209
|
console.log("- onReady hook executed");
|
|
8365
9210
|
}
|
|
8366
9211
|
app.server = server;
|
|
@@ -8453,13 +9298,22 @@ async function createAppBase(options) {
|
|
|
8453
9298
|
closed = true;
|
|
8454
9299
|
const s = server;
|
|
8455
9300
|
s.closeIdleConnections?.();
|
|
9301
|
+
await stopTaskRuntime();
|
|
8456
9302
|
if (config?.lifecycle?.onClose) {
|
|
8457
|
-
await config.lifecycle.onClose({
|
|
9303
|
+
await config.lifecycle.onClose({
|
|
9304
|
+
rootDir,
|
|
9305
|
+
routes: sorted,
|
|
9306
|
+
server,
|
|
9307
|
+
registries,
|
|
9308
|
+
tasks: taskQueue
|
|
9309
|
+
});
|
|
8458
9310
|
}
|
|
8459
9311
|
registries.tool.clear();
|
|
8460
9312
|
registries.agent.clear();
|
|
8461
9313
|
registries.skill.clear();
|
|
9314
|
+
registries.task.clear();
|
|
8462
9315
|
registries.agentHandle.clear();
|
|
9316
|
+
registries.taskHandle.clear();
|
|
8463
9317
|
if (!server.listening) {
|
|
8464
9318
|
app.server = null;
|
|
8465
9319
|
if (getCurrentApp() === app) setCurrentApp(null);
|
|
@@ -8496,6 +9350,7 @@ async function createAppBase(options) {
|
|
|
8496
9350
|
registries,
|
|
8497
9351
|
dist,
|
|
8498
9352
|
patterns: ROUTE_PATTERNS,
|
|
9353
|
+
taskQueue,
|
|
8499
9354
|
server,
|
|
8500
9355
|
routesRef,
|
|
8501
9356
|
config,
|
|
@@ -8522,6 +9377,11 @@ var init_createAppCore = __esm({
|
|
|
8522
9377
|
init_generateRoutes();
|
|
8523
9378
|
init_generateToolArtifacts();
|
|
8524
9379
|
init_generateAgentArtifacts();
|
|
9380
|
+
init_generateTaskArtifacts();
|
|
9381
|
+
init_taskQueue();
|
|
9382
|
+
init_loadTaskDriver();
|
|
9383
|
+
init_idleTaskDriver();
|
|
9384
|
+
init_cronScheduler();
|
|
8525
9385
|
init_loadPlugins();
|
|
8526
9386
|
init_importWithCacheBust();
|
|
8527
9387
|
init_registries();
|
|
@@ -8547,12 +9407,14 @@ var init_createAppCore = __esm({
|
|
|
8547
9407
|
"logger",
|
|
8548
9408
|
"http2",
|
|
8549
9409
|
"trustedProxy",
|
|
8550
|
-
"response"
|
|
9410
|
+
"response",
|
|
9411
|
+
"task"
|
|
8551
9412
|
]);
|
|
8552
9413
|
}
|
|
8553
9414
|
});
|
|
8554
9415
|
|
|
8555
9416
|
// src/cli/createDevApp.ts
|
|
9417
|
+
import path28 from "path";
|
|
8556
9418
|
async function createDevApp(options) {
|
|
8557
9419
|
const { app, ctx } = await createAppBase(options);
|
|
8558
9420
|
const devApp = app;
|
|
@@ -8597,6 +9459,19 @@ async function createDevApp(options) {
|
|
|
8597
9459
|
await generateAgentArtifacts(agents, ctx.rootDir, ctx.dist);
|
|
8598
9460
|
await loadAndHydrateAgents(ctx.rootDir, ctx.dist, ctx.registries);
|
|
8599
9461
|
};
|
|
9462
|
+
devApp.reloadTasks = async () => {
|
|
9463
|
+
setLoadTimestamp(Date.now());
|
|
9464
|
+
invalidateProgramCache();
|
|
9465
|
+
const tasks = await scanTasks(ctx.rootDir, TASK_PATTERNS);
|
|
9466
|
+
for (const task of tasks) {
|
|
9467
|
+
await ensureCompiled(path28.resolve(ctx.rootDir, task.filePath), ctx.rootDir, ctx.dist);
|
|
9468
|
+
}
|
|
9469
|
+
await generateTaskArtifacts(tasks, ctx.rootDir, ctx.dist);
|
|
9470
|
+
ctx.taskQueue.invalidateModules();
|
|
9471
|
+
ctx.taskQueue.invalidateSchemas();
|
|
9472
|
+
await ctx.taskQueue.reload();
|
|
9473
|
+
await loadAndHydrateTasks(ctx.rootDir, ctx.dist, ctx.registries);
|
|
9474
|
+
};
|
|
8600
9475
|
return devApp;
|
|
8601
9476
|
}
|
|
8602
9477
|
var init_createDevApp = __esm({
|
|
@@ -8611,6 +9486,9 @@ var init_createDevApp = __esm({
|
|
|
8611
9486
|
init_scanAgents();
|
|
8612
9487
|
init_scanAgents();
|
|
8613
9488
|
init_generateAgentArtifacts();
|
|
9489
|
+
init_scanTasks();
|
|
9490
|
+
init_generateTaskArtifacts();
|
|
9491
|
+
init_compileOnDemand();
|
|
8614
9492
|
init_loadMiddlewares();
|
|
8615
9493
|
init_createProgram();
|
|
8616
9494
|
init_validateInput();
|
|
@@ -8625,9 +9503,10 @@ __export(devCommand_exports, {
|
|
|
8625
9503
|
devCommand: () => devCommand,
|
|
8626
9504
|
generateAgentArtifactsForDev: () => generateAgentArtifactsForDev,
|
|
8627
9505
|
generateRouteArtifacts: () => generateRouteArtifacts,
|
|
9506
|
+
generateTaskArtifactsForDev: () => generateTaskArtifactsForDev,
|
|
8628
9507
|
generateToolArtifactsForDev: () => generateToolArtifactsForDev
|
|
8629
9508
|
});
|
|
8630
|
-
import
|
|
9509
|
+
import path29 from "path";
|
|
8631
9510
|
async function devCommand(options) {
|
|
8632
9511
|
const rootDir = process.cwd();
|
|
8633
9512
|
if (!process.env.NODE_ENV) process.env.NODE_ENV = "development";
|
|
@@ -8646,6 +9525,8 @@ async function devCommand(options) {
|
|
|
8646
9525
|
await generateToolArtifactsForDev(rootDir, devDist);
|
|
8647
9526
|
console.log("- Generating agent manifest...");
|
|
8648
9527
|
await generateAgentArtifactsForDev(rootDir, devDist);
|
|
9528
|
+
console.log("- Generating task artifacts...");
|
|
9529
|
+
await generateTaskArtifactsForDev(rootDir, devDist);
|
|
8649
9530
|
console.log("- Starting dev app...");
|
|
8650
9531
|
const app = await createDevApp({ rootDir, port: options?.port });
|
|
8651
9532
|
await app.listen();
|
|
@@ -8654,7 +9535,7 @@ async function devCommand(options) {
|
|
|
8654
9535
|
async function generateRouteArtifacts(rootDir, patterns, dist) {
|
|
8655
9536
|
const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, dist);
|
|
8656
9537
|
const sorted = sortRoutes(routes);
|
|
8657
|
-
const routesPath =
|
|
9538
|
+
const routesPath = path29.resolve(rootDir, dist, ROUTES_FILE2);
|
|
8658
9539
|
const serialized = serializeRoutes(sorted, wsRoutes, rootDir, dist);
|
|
8659
9540
|
await writeRoutesModule(serialized, routesPath);
|
|
8660
9541
|
}
|
|
@@ -8666,6 +9547,13 @@ async function generateAgentArtifactsForDev(rootDir, dist) {
|
|
|
8666
9547
|
const agents = await scanAgents(rootDir, DEFAULT_AGENT_PATTERNS);
|
|
8667
9548
|
await generateAgentArtifacts(agents, rootDir, dist);
|
|
8668
9549
|
}
|
|
9550
|
+
async function generateTaskArtifactsForDev(rootDir, dist) {
|
|
9551
|
+
const tasks = await scanTasks(rootDir, TASK_PATTERNS);
|
|
9552
|
+
for (const task of tasks) {
|
|
9553
|
+
await ensureCompiled(path29.resolve(rootDir, task.filePath), rootDir, dist);
|
|
9554
|
+
}
|
|
9555
|
+
await generateTaskArtifacts(tasks, rootDir, dist);
|
|
9556
|
+
}
|
|
8669
9557
|
var DEV_DIST, ROUTES_FILE2;
|
|
8670
9558
|
var init_devCommand = __esm({
|
|
8671
9559
|
"src/cli/devCommand.ts"() {
|
|
@@ -8680,6 +9568,9 @@ var init_devCommand = __esm({
|
|
|
8680
9568
|
init_scanAgents();
|
|
8681
9569
|
init_scanAgents();
|
|
8682
9570
|
init_generateAgentArtifacts();
|
|
9571
|
+
init_scanTasks();
|
|
9572
|
+
init_generateTaskArtifacts();
|
|
9573
|
+
init_compileOnDemand();
|
|
8683
9574
|
init_loadConfig();
|
|
8684
9575
|
init_loadEnv();
|
|
8685
9576
|
init_watcher();
|
|
@@ -8707,15 +9598,15 @@ var buildCommand_exports = {};
|
|
|
8707
9598
|
__export(buildCommand_exports, {
|
|
8708
9599
|
buildCommand: () => buildCommand
|
|
8709
9600
|
});
|
|
8710
|
-
import
|
|
8711
|
-
import
|
|
9601
|
+
import path30 from "path";
|
|
9602
|
+
import fs20 from "fs";
|
|
8712
9603
|
async function buildCommand(options) {
|
|
8713
9604
|
const rootDir = options?.rootDir ?? process.cwd();
|
|
8714
9605
|
const outdir = options?.dist ?? DEFAULT_DIST2;
|
|
8715
|
-
const absOut =
|
|
8716
|
-
const realRoot = toRealPath(
|
|
9606
|
+
const absOut = path30.resolve(rootDir, outdir);
|
|
9607
|
+
const realRoot = toRealPath(path30.resolve(rootDir));
|
|
8717
9608
|
if (isInsideDir(toRealPath(absOut), realRoot)) {
|
|
8718
|
-
|
|
9609
|
+
fs20.rmSync(absOut, { recursive: true, force: true });
|
|
8719
9610
|
} else {
|
|
8720
9611
|
console.warn(
|
|
8721
9612
|
`! Output directory "${outdir}" is outside the project root, skipping clean (stale artifacts may remain)`
|
|
@@ -8723,10 +9614,10 @@ async function buildCommand(options) {
|
|
|
8723
9614
|
}
|
|
8724
9615
|
await compileConfig({ rootDir, dist: outdir });
|
|
8725
9616
|
const _config = await loadConfig(rootDir, outdir);
|
|
8726
|
-
const pkgPath =
|
|
8727
|
-
if (
|
|
9617
|
+
const pkgPath = path30.resolve(rootDir, "package.json");
|
|
9618
|
+
if (fs20.existsSync(pkgPath)) {
|
|
8728
9619
|
try {
|
|
8729
|
-
const pkg = JSON.parse(
|
|
9620
|
+
const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf-8"));
|
|
8730
9621
|
if (pkg.type !== "module") {
|
|
8731
9622
|
console.warn(
|
|
8732
9623
|
'! package.json is missing "type": "module" \u2014 build output is ESM and `node dist/main` will fail without it'
|
|
@@ -8754,7 +9645,7 @@ async function buildCommand(options) {
|
|
|
8754
9645
|
if (localPluginSources.length > 0) {
|
|
8755
9646
|
console.log("\n[2.5/8] Compiling local plugins...");
|
|
8756
9647
|
const outside = localPluginSources.filter((p) => {
|
|
8757
|
-
const rel =
|
|
9648
|
+
const rel = path30.relative(rootDir, p).replace(/\\/g, "/");
|
|
8758
9649
|
return !rel.startsWith("src/");
|
|
8759
9650
|
});
|
|
8760
9651
|
if (outside.length > 0) {
|
|
@@ -8778,9 +9669,9 @@ async function buildCommand(options) {
|
|
|
8778
9669
|
}
|
|
8779
9670
|
console.log("\n[4/8] Generating schema...");
|
|
8780
9671
|
await generateSchemaFiles(sorted, rootDir, outdir);
|
|
8781
|
-
console.log(` Schema: zod.js files under ${
|
|
9672
|
+
console.log(` Schema: zod.js files under ${path30.resolve(rootDir, outdir)}`);
|
|
8782
9673
|
console.log("\n[5/8] Generating routes manifest...");
|
|
8783
|
-
const routesPath =
|
|
9674
|
+
const routesPath = path30.resolve(rootDir, outdir, "faapi-routes.js");
|
|
8784
9675
|
const serialized = serializeRoutes(sorted, wsRoutes, rootDir, outdir);
|
|
8785
9676
|
await writeRoutesModule(serialized, routesPath);
|
|
8786
9677
|
console.log(` Written to ${routesPath}`);
|
|
@@ -8788,14 +9679,19 @@ async function buildCommand(options) {
|
|
|
8788
9679
|
const tools = await scanTools(rootDir, TOOL_PATTERNS);
|
|
8789
9680
|
const toolMeta = await generateToolArtifacts(tools, rootDir, outdir);
|
|
8790
9681
|
console.log(` Found ${toolMeta.length} tool(s)`);
|
|
8791
|
-
console.log(` Tool manifest: ${
|
|
9682
|
+
console.log(` Tool manifest: ${path30.resolve(rootDir, outdir, "faapi-tools.js")}`);
|
|
8792
9683
|
console.log("\n[7/8] Generating agent manifest...");
|
|
8793
9684
|
const agents = await scanAgents(rootDir, DEFAULT_AGENT_PATTERNS);
|
|
8794
9685
|
const agentMeta = await generateAgentArtifacts(agents, rootDir, outdir);
|
|
8795
9686
|
console.log(` Found ${agentMeta.length} agent(s)`);
|
|
8796
|
-
console.log(` Agent manifest: ${
|
|
9687
|
+
console.log(` Agent manifest: ${path30.resolve(rootDir, outdir, "faapi-agents.js")}`);
|
|
9688
|
+
console.log("\n[7.5/8] Generating task manifest and schema...");
|
|
9689
|
+
const tasks = await scanTasks(rootDir, TASK_PATTERNS);
|
|
9690
|
+
const taskMeta = await generateTaskArtifacts(tasks, rootDir, outdir);
|
|
9691
|
+
console.log(` Found ${taskMeta.length} task(s)`);
|
|
9692
|
+
console.log(` Task manifest: ${path30.resolve(rootDir, outdir, "faapi-tasks.js")}`);
|
|
8797
9693
|
console.log("\n[8/8] Generating entry file...");
|
|
8798
|
-
const mainPath =
|
|
9694
|
+
const mainPath = path30.resolve(rootDir, outdir, "main.js");
|
|
8799
9695
|
const createProdAppArgs = options?.dist && options.dist !== DEFAULT_DIST2 ? `{ dist: ${JSON.stringify(outdir)} }` : "";
|
|
8800
9696
|
const mainContent = `// \u7531 faapi build \u81EA\u52A8\u751F\u6210\uFF0C\u8BF7\u52FF\u624B\u52A8\u7F16\u8F91
|
|
8801
9697
|
import { createProdApp, loadEnv } from '@faapi/faapi';
|
|
@@ -8807,7 +9703,7 @@ loadEnv(process.cwd());
|
|
|
8807
9703
|
const app = await createProdApp(${createProdAppArgs});
|
|
8808
9704
|
await app.listen();
|
|
8809
9705
|
`;
|
|
8810
|
-
await
|
|
9706
|
+
await fs20.promises.writeFile(mainPath, mainContent, "utf-8");
|
|
8811
9707
|
console.log(` Written to ${mainPath}`);
|
|
8812
9708
|
console.log("\nfaapi build completed");
|
|
8813
9709
|
}
|
|
@@ -8821,7 +9717,7 @@ function extractLocalPluginSources(declarations, rootDir) {
|
|
|
8821
9717
|
else if (Array.isArray(decl)) specifier = decl[0];
|
|
8822
9718
|
else if ("path" in decl) specifier = decl.path;
|
|
8823
9719
|
if (!specifier) continue;
|
|
8824
|
-
if (!specifier.startsWith("./") && !specifier.startsWith("../") && !
|
|
9720
|
+
if (!specifier.startsWith("./") && !specifier.startsWith("../") && !path30.isAbsolute(specifier)) {
|
|
8825
9721
|
continue;
|
|
8826
9722
|
}
|
|
8827
9723
|
const source = resolveLocalPluginSource(specifier, rootDir);
|
|
@@ -8845,6 +9741,8 @@ var init_buildCommand = __esm({
|
|
|
8845
9741
|
init_scanAgents();
|
|
8846
9742
|
init_scanAgents();
|
|
8847
9743
|
init_generateAgentArtifacts();
|
|
9744
|
+
init_scanTasks();
|
|
9745
|
+
init_generateTaskArtifacts();
|
|
8848
9746
|
init_generateSchemaFiles();
|
|
8849
9747
|
init_generateRoutes();
|
|
8850
9748
|
init_compileBuildRoutes();
|