@faapi/faapi 0.0.0-canary.c6dc0f7 → 0.0.0-canary.e2ee43b
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 +630 -435
- package/dist/cli/index.js.map +1 -1
- package/package.json +2 -2
package/dist/cli/index.js
CHANGED
|
@@ -23,9 +23,9 @@ var init_constants = __esm({
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
// src/utils/normalizePath.ts
|
|
26
|
-
function normalizePath(
|
|
27
|
-
if (!
|
|
28
|
-
let result =
|
|
26
|
+
function normalizePath(path15) {
|
|
27
|
+
if (!path15) return "";
|
|
28
|
+
let result = path15.replace(/\\/g, "/");
|
|
29
29
|
result = result.replace(/\/+/g, "/");
|
|
30
30
|
result = result.replace(/\/+$/, "");
|
|
31
31
|
if (result && !result.startsWith("/")) {
|
|
@@ -89,9 +89,9 @@ function getLoadTimestamp() {
|
|
|
89
89
|
}
|
|
90
90
|
async function importWithCacheBust(filePath) {
|
|
91
91
|
let url = pathToFileURL(filePath).href;
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
-
url += `?t=${
|
|
92
|
+
const ts7 = getLoadTimestamp();
|
|
93
|
+
if (ts7 !== void 0) {
|
|
94
|
+
url += `?t=${ts7}`;
|
|
95
95
|
}
|
|
96
96
|
return await import(url);
|
|
97
97
|
}
|
|
@@ -157,18 +157,32 @@ var init_loadMiddlewares = __esm({
|
|
|
157
157
|
import fg from "fast-glob";
|
|
158
158
|
import path from "path";
|
|
159
159
|
import fs from "fs";
|
|
160
|
-
|
|
160
|
+
function toProdAbsPath(sourceAbsPath, rootDir, prodDir) {
|
|
161
|
+
const rel = path.relative(rootDir, sourceAbsPath);
|
|
162
|
+
const prodRel = `${prodDir}/${rel.replace(/\.ts$/, ".js")}`;
|
|
163
|
+
return path.resolve(rootDir, prodRel);
|
|
164
|
+
}
|
|
165
|
+
async function findMergedMiddlewares(routeFilePath, rootDir, prodDir) {
|
|
161
166
|
const routeDir = path.dirname(routeFilePath);
|
|
162
167
|
const resolvedRoot = path.resolve(rootDir);
|
|
163
168
|
const mwPaths = [];
|
|
164
169
|
let currentDir = path.resolve(rootDir, routeDir);
|
|
165
170
|
while (true) {
|
|
166
|
-
|
|
167
|
-
const mwPath = path.join(currentDir,
|
|
171
|
+
if (prodDir) {
|
|
172
|
+
const mwPath = path.join(currentDir, "middlewares.js");
|
|
168
173
|
const absMwPath = path.resolve(rootDir, mwPath);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
174
|
+
const prodAbsMwPath = toProdAbsPath(absMwPath, rootDir, prodDir);
|
|
175
|
+
if (fs.existsSync(prodAbsMwPath)) {
|
|
176
|
+
mwPaths.push(prodAbsMwPath);
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
for (const ext of [".ts", ".js"]) {
|
|
180
|
+
const mwPath = path.join(currentDir, `middlewares${ext}`);
|
|
181
|
+
const absMwPath = path.resolve(rootDir, mwPath);
|
|
182
|
+
if (fs.existsSync(absMwPath)) {
|
|
183
|
+
mwPaths.push(absMwPath);
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
172
186
|
}
|
|
173
187
|
}
|
|
174
188
|
if (currentDir === resolvedRoot) break;
|
|
@@ -222,7 +236,7 @@ async function hasWsExport(absPath) {
|
|
|
222
236
|
return false;
|
|
223
237
|
}
|
|
224
238
|
}
|
|
225
|
-
async function scanRoutes(rootDir, patterns, appDir) {
|
|
239
|
+
async function scanRoutes(rootDir, patterns, appDir, prodDir) {
|
|
226
240
|
const dir = appDir ?? ".";
|
|
227
241
|
const files = await fg(patterns, {
|
|
228
242
|
cwd: rootDir,
|
|
@@ -236,12 +250,13 @@ async function scanRoutes(rootDir, patterns, appDir) {
|
|
|
236
250
|
const fileName = normalizedFile.split("/").pop();
|
|
237
251
|
if (fileName === "handler.ts" || fileName === "handler.js") {
|
|
238
252
|
const absPath = path.resolve(rootDir, normalizedFile);
|
|
253
|
+
const importPath = prodDir ? toProdAbsPath(absPath, rootDir, prodDir) : absPath;
|
|
239
254
|
const urlPath = filePathToUrlPath(normalizedFile, dir);
|
|
240
255
|
const paramNames = extractParamNames(urlPath);
|
|
241
256
|
const isDynamic = paramNames.length > 0;
|
|
242
257
|
const isCatchAll = normalizedFile.split("/").some(isCatchAllSegment);
|
|
243
|
-
const middlewareBundle = await findMergedMiddlewares(normalizedFile, rootDir);
|
|
244
|
-
const methods = await extractMethodsFromHandler(
|
|
258
|
+
const middlewareBundle = await findMergedMiddlewares(normalizedFile, rootDir, prodDir);
|
|
259
|
+
const methods = await extractMethodsFromHandler(importPath);
|
|
245
260
|
for (const method of methods) {
|
|
246
261
|
routes.push({
|
|
247
262
|
method,
|
|
@@ -254,7 +269,7 @@ async function scanRoutes(rootDir, patterns, appDir) {
|
|
|
254
269
|
injectors: middlewareBundle?.injectors
|
|
255
270
|
});
|
|
256
271
|
}
|
|
257
|
-
const hasWs = await hasWsExport(
|
|
272
|
+
const hasWs = await hasWsExport(importPath);
|
|
258
273
|
if (hasWs) {
|
|
259
274
|
wsRoutes.push({
|
|
260
275
|
urlPath,
|
|
@@ -1122,35 +1137,35 @@ function generateObjectValidation(type, varName, issuesVar, ctx, pathExpr) {
|
|
|
1122
1137
|
return lines.join("\n");
|
|
1123
1138
|
}
|
|
1124
1139
|
function generateValueValidation(type, varName, issuesVar, ctx, pathExpr) {
|
|
1125
|
-
const
|
|
1140
|
+
const path15 = pathExpr ?? "''";
|
|
1126
1141
|
switch (type.kind) {
|
|
1127
1142
|
case "any":
|
|
1128
1143
|
case "unknown":
|
|
1129
1144
|
return "";
|
|
1130
1145
|
// 不校验
|
|
1131
1146
|
case "string":
|
|
1132
|
-
return `if (typeof ${varName} !== 'string') ${issuesVar}.push({ path: ${
|
|
1147
|
+
return `if (typeof ${varName} !== 'string') ${issuesVar}.push({ path: ${path15}, code: 'TYPE_MISMATCH', expected: 'string', received: typeof ${varName}, message: '\u671F\u671B string\uFF0C\u5B9E\u9645 ' + typeof ${varName} });`;
|
|
1133
1148
|
case "number":
|
|
1134
|
-
return `if (typeof ${varName} !== 'number' || Number.isNaN(${varName})) ${issuesVar}.push({ path: ${
|
|
1149
|
+
return `if (typeof ${varName} !== 'number' || Number.isNaN(${varName})) ${issuesVar}.push({ path: ${path15}, code: 'TYPE_MISMATCH', expected: 'number', received: typeof ${varName}, message: '\u671F\u671B number\uFF0C\u5B9E\u9645 ' + typeof ${varName} });`;
|
|
1135
1150
|
case "boolean":
|
|
1136
|
-
return `if (typeof ${varName} !== 'boolean') ${issuesVar}.push({ path: ${
|
|
1151
|
+
return `if (typeof ${varName} !== 'boolean') ${issuesVar}.push({ path: ${path15}, code: 'TYPE_MISMATCH', expected: 'boolean', received: typeof ${varName}, message: '\u671F\u671B boolean\uFF0C\u5B9E\u9645 ' + typeof ${varName} });`;
|
|
1137
1152
|
case "bigint":
|
|
1138
|
-
return `if (typeof ${varName} !== 'bigint') ${issuesVar}.push({ path: ${
|
|
1153
|
+
return `if (typeof ${varName} !== 'bigint') ${issuesVar}.push({ path: ${path15}, code: 'TYPE_MISMATCH', expected: 'bigint', received: typeof ${varName}, message: '\u671F\u671B bigint\uFF0C\u5B9E\u9645 ' + typeof ${varName} });`;
|
|
1139
1154
|
case "null":
|
|
1140
|
-
return `if (${varName} !== null) ${issuesVar}.push({ path: ${
|
|
1155
|
+
return `if (${varName} !== null) ${issuesVar}.push({ path: ${path15}, code: 'TYPE_MISMATCH', expected: 'null', received: typeof ${varName}, message: '\u671F\u671B null\uFF0C\u5B9E\u9645 ' + typeof ${varName} });`;
|
|
1141
1156
|
case "undefined":
|
|
1142
|
-
return `if (${varName} !== undefined) ${issuesVar}.push({ path: ${
|
|
1157
|
+
return `if (${varName} !== undefined) ${issuesVar}.push({ path: ${path15}, code: 'TYPE_MISMATCH', expected: 'undefined', received: typeof ${varName}, message: '\u671F\u671B undefined' });`;
|
|
1143
1158
|
case "literal":
|
|
1144
|
-
return `if (${varName} !== ${JSON.stringify(type.value)}) ${issuesVar}.push({ path: ${
|
|
1159
|
+
return `if (${varName} !== ${JSON.stringify(type.value)}) ${issuesVar}.push({ path: ${path15}, code: 'INVALID_VALUE', expected: ${JSON.stringify(JSON.stringify(type.value))}, received: JSON.stringify(${varName}), message: '\u671F\u671B\u5B57\u9762\u91CF ${JSON.stringify(type.value)}\uFF0C\u5B9E\u9645 ' + JSON.stringify(${varName}) });`;
|
|
1145
1160
|
case "date":
|
|
1146
1161
|
return `if (${varName} instanceof Date) { /* Date \u5B9E\u4F8B,\u901A\u8FC7 */ }
|
|
1147
|
-
else if (typeof ${varName} !== 'string') ${issuesVar}.push({ path: ${
|
|
1148
|
-
else if (!/^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,3})?(Z|[+-]\\d{2}:?\\d{2})?)?$/.test(${varName}) || isNaN(new Date(${varName}).getTime())) ${issuesVar}.push({ path: ${
|
|
1162
|
+
else if (typeof ${varName} !== 'string') ${issuesVar}.push({ path: ${path15}, code: 'TYPE_MISMATCH', expected: 'Date | ISO 8601 string', received: typeof ${varName}, message: '\u671F\u671B Date \u6216 ISO 8601 \u5B57\u7B26\u4E32\uFF0C\u5B9E\u9645 ' + typeof ${varName} });
|
|
1163
|
+
else if (!/^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,3})?(Z|[+-]\\d{2}:?\\d{2})?)?$/.test(${varName}) || isNaN(new Date(${varName}).getTime())) ${issuesVar}.push({ path: ${path15}, code: 'INVALID_FORMAT', expected: 'ISO 8601', received: String(${varName}), message: '\u4E0D\u662F\u5408\u6CD5\u7684 ISO 8601 \u65E5\u671F\u5B57\u7B26\u4E32: ' + ${varName} });`;
|
|
1149
1164
|
case "array": {
|
|
1150
1165
|
const id = ctx.nextVarId();
|
|
1151
1166
|
const itemVar = `item${id}`;
|
|
1152
1167
|
const indexVar = `i${id}`;
|
|
1153
|
-
const elemPath = `${
|
|
1168
|
+
const elemPath = `${path15} + '[' + ${indexVar} + ']'`;
|
|
1154
1169
|
const elemValidation = generateValueValidation(
|
|
1155
1170
|
type.element,
|
|
1156
1171
|
itemVar,
|
|
@@ -1158,7 +1173,7 @@ else if (!/^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,3})?(Z|[+-]\\d{2
|
|
|
1158
1173
|
ctx,
|
|
1159
1174
|
elemPath
|
|
1160
1175
|
);
|
|
1161
|
-
return `if (!Array.isArray(${varName})) ${issuesVar}.push({ path: ${
|
|
1176
|
+
return `if (!Array.isArray(${varName})) ${issuesVar}.push({ path: ${path15}, code: 'TYPE_MISMATCH', expected: 'array', received: typeof ${varName}, message: '\u671F\u671B\u6570\u7EC4\uFF0C\u5B9E\u9645 ' + typeof ${varName} });
|
|
1162
1177
|
else for (let ${indexVar} = 0; ${indexVar} < ${varName}.length; ${indexVar}++) { const ${itemVar} = ${varName}[${indexVar}]; ${elemValidation} }`;
|
|
1163
1178
|
}
|
|
1164
1179
|
case "tuple": {
|
|
@@ -1170,21 +1185,21 @@ else for (let ${indexVar} = 0; ${indexVar} < ${varName}.length; ${indexVar}++) {
|
|
|
1170
1185
|
const indexVar = `ti${id}`;
|
|
1171
1186
|
const lines = [];
|
|
1172
1187
|
lines.push(
|
|
1173
|
-
`if (!Array.isArray(${varName})) ${issuesVar}.push({ path: ${
|
|
1188
|
+
`if (!Array.isArray(${varName})) ${issuesVar}.push({ path: ${path15}, code: 'TYPE_MISMATCH', expected: 'tuple', received: typeof ${varName}, message: '\u671F\u671B\u5143\u7EC4\uFF08\u6570\u7EC4\uFF09\uFF0C\u5B9E\u9645 ' + typeof ${varName} });`
|
|
1174
1189
|
);
|
|
1175
1190
|
lines.push(`else {`);
|
|
1176
1191
|
lines.push(
|
|
1177
|
-
`if (${varName}.length < ${minRequired}) ${issuesVar}.push({ path: ${
|
|
1192
|
+
`if (${varName}.length < ${minRequired}) ${issuesVar}.push({ path: ${path15}, code: 'MISSING_FIELD', expected: 'tuple length >= ${minRequired}', received: 'length ' + ${varName}.length, message: '\u5143\u7EC4\u957F\u5EA6\u4E0D\u8DB3\uFF0C\u671F\u671B\u81F3\u5C11 ${minRequired}\uFF0C\u5B9E\u9645 ' + ${varName}.length });`
|
|
1178
1193
|
);
|
|
1179
1194
|
if (!restElement) {
|
|
1180
1195
|
lines.push(
|
|
1181
|
-
`if (${varName}.length > ${fixedCount}) ${issuesVar}.push({ path: ${
|
|
1196
|
+
`if (${varName}.length > ${fixedCount}) ${issuesVar}.push({ path: ${path15}, code: 'INVALID_VALUE', expected: 'tuple length = ${fixedCount}', received: 'length ' + ${varName}.length, message: '\u5143\u7EC4\u957F\u5EA6\u8D85\u51FA\uFF0C\u671F\u671B ${fixedCount}\uFF0C\u5B9E\u9645 ' + ${varName}.length });`
|
|
1182
1197
|
);
|
|
1183
1198
|
}
|
|
1184
1199
|
for (let i = 0; i < type.elements.length; i++) {
|
|
1185
1200
|
const elem = type.elements[i];
|
|
1186
1201
|
if (elem.rest) continue;
|
|
1187
|
-
const elemPath = `${
|
|
1202
|
+
const elemPath = `${path15} + '[' + ${i} + ']'`;
|
|
1188
1203
|
const elemAccess = `${varName}[${i}]`;
|
|
1189
1204
|
if (elem.optional) {
|
|
1190
1205
|
lines.push(`if (${i} < ${varName}.length && ${elemAccess} !== undefined) {`);
|
|
@@ -1195,7 +1210,7 @@ else for (let ${indexVar} = 0; ${indexVar} < ${varName}.length; ${indexVar}++) {
|
|
|
1195
1210
|
lines.push(`}`);
|
|
1196
1211
|
}
|
|
1197
1212
|
if (restElement) {
|
|
1198
|
-
const restPath = `${
|
|
1213
|
+
const restPath = `${path15} + '[' + ${indexVar} + ']'`;
|
|
1199
1214
|
const restValidation = generateValueValidation(
|
|
1200
1215
|
restElement.type,
|
|
1201
1216
|
itemVar,
|
|
@@ -1213,23 +1228,23 @@ else for (let ${indexVar} = 0; ${indexVar} < ${varName}.length; ${indexVar}++) {
|
|
|
1213
1228
|
case "union": {
|
|
1214
1229
|
const tempVar = `tempIssues_${Math.random().toString(36).slice(2, 8)}`;
|
|
1215
1230
|
const memberChecks = type.members.map((member) => {
|
|
1216
|
-
const check = generateValueValidation(member, varName, tempVar, ctx,
|
|
1231
|
+
const check = generateValueValidation(member, varName, tempVar, ctx, path15);
|
|
1217
1232
|
return `(() => { const ${tempVar} = []; ${check}; return ${tempVar}.length === 0; })()`;
|
|
1218
1233
|
});
|
|
1219
1234
|
const expected = runtimeTypeToExpected(type);
|
|
1220
|
-
return `if (!(${memberChecks.join(" || ")})) ${issuesVar}.push({ path: ${
|
|
1235
|
+
return `if (!(${memberChecks.join(" || ")})) ${issuesVar}.push({ path: ${path15}, code: 'TYPE_MISMATCH', expected: ${JSON.stringify(expected)}, received: typeof ${varName}, message: '\u503C ' + JSON.stringify(${varName}) + ' \u4E0D\u5339\u914D\u8054\u5408\u7C7B\u578B ${expected.replace(/'/g, "\\'")}' });`;
|
|
1221
1236
|
}
|
|
1222
1237
|
case "record": {
|
|
1223
|
-
const valuePath = `${
|
|
1238
|
+
const valuePath = `${path15} + '.' + key`;
|
|
1224
1239
|
const valueValidation = generateValueValidation(type.value, "val", issuesVar, ctx, valuePath);
|
|
1225
|
-
return `if (typeof ${varName} !== 'object' || ${varName} === null || Array.isArray(${varName})) ${issuesVar}.push({ path: ${
|
|
1240
|
+
return `if (typeof ${varName} !== 'object' || ${varName} === null || Array.isArray(${varName})) ${issuesVar}.push({ path: ${path15}, code: 'TYPE_MISMATCH', expected: 'object', received: typeof ${varName}, message: '\u671F\u671B\u5BF9\u8C61\uFF0C\u5B9E\u9645 ' + typeof ${varName} });
|
|
1226
1241
|
else for (const [key, val] of Object.entries(${varName})) { ${valueValidation} }`;
|
|
1227
1242
|
}
|
|
1228
1243
|
case "object": {
|
|
1229
|
-
return generateInlineObjectValidation(type, varName, issuesVar, ctx,
|
|
1244
|
+
return generateInlineObjectValidation(type, varName, issuesVar, ctx, path15);
|
|
1230
1245
|
}
|
|
1231
1246
|
case "ref": {
|
|
1232
|
-
return `validate_${type.name}(${varName}, ${
|
|
1247
|
+
return `validate_${type.name}(${varName}, ${path15}, ${issuesVar});`;
|
|
1233
1248
|
}
|
|
1234
1249
|
}
|
|
1235
1250
|
}
|
|
@@ -1289,6 +1304,75 @@ var init_generateValidatorCode = __esm({
|
|
|
1289
1304
|
}
|
|
1290
1305
|
});
|
|
1291
1306
|
|
|
1307
|
+
// src/validator/schemaRegistry.ts
|
|
1308
|
+
var SchemaRegistry, schemaRegistry;
|
|
1309
|
+
var init_schemaRegistry = __esm({
|
|
1310
|
+
"src/validator/schemaRegistry.ts"() {
|
|
1311
|
+
"use strict";
|
|
1312
|
+
SchemaRegistry = class {
|
|
1313
|
+
manifest = /* @__PURE__ */ new Map();
|
|
1314
|
+
/**
|
|
1315
|
+
* 批量加载 manifest
|
|
1316
|
+
* 覆盖已有数据
|
|
1317
|
+
*/
|
|
1318
|
+
loadManifest(manifest) {
|
|
1319
|
+
this.manifest.clear();
|
|
1320
|
+
for (const [filePath, fileSchemas] of manifest) {
|
|
1321
|
+
const copy = /* @__PURE__ */ new Map();
|
|
1322
|
+
fileSchemas.forEach((value, key) => copy.set(key, value));
|
|
1323
|
+
this.manifest.set(filePath, copy);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* 查询单条 schema
|
|
1328
|
+
* @returns SchemaEntry | null | undefined
|
|
1329
|
+
* - SchemaEntry:有类型声明
|
|
1330
|
+
* - null:无类型声明(跳过校验)
|
|
1331
|
+
* - undefined:manifest 不完整(抛错)
|
|
1332
|
+
*/
|
|
1333
|
+
get(filePath, schemaName) {
|
|
1334
|
+
const fileSchemas = this.manifest.get(filePath);
|
|
1335
|
+
if (!fileSchemas) return void 0;
|
|
1336
|
+
return fileSchemas.get(schemaName);
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1339
|
+
* 设置单个文件的所有 schema
|
|
1340
|
+
* 覆盖该文件的已有数据
|
|
1341
|
+
*/
|
|
1342
|
+
set(filePath, schemas) {
|
|
1343
|
+
const copy = /* @__PURE__ */ new Map();
|
|
1344
|
+
schemas.forEach((value, key) => copy.set(key, value));
|
|
1345
|
+
this.manifest.set(filePath, copy);
|
|
1346
|
+
}
|
|
1347
|
+
/**
|
|
1348
|
+
* 删除单个文件(文件被删除时)
|
|
1349
|
+
*/
|
|
1350
|
+
delete(filePath) {
|
|
1351
|
+
this.manifest.delete(filePath);
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* 判断文件是否已注册
|
|
1355
|
+
*/
|
|
1356
|
+
hasFile(filePath) {
|
|
1357
|
+
return this.manifest.has(filePath);
|
|
1358
|
+
}
|
|
1359
|
+
/**
|
|
1360
|
+
* 清空(测试用 / watch 全量重建前)
|
|
1361
|
+
*/
|
|
1362
|
+
clear() {
|
|
1363
|
+
this.manifest.clear();
|
|
1364
|
+
}
|
|
1365
|
+
/**
|
|
1366
|
+
* 已注册的文件数量
|
|
1367
|
+
*/
|
|
1368
|
+
get size() {
|
|
1369
|
+
return this.manifest.size;
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
schemaRegistry = new SchemaRegistry();
|
|
1373
|
+
}
|
|
1374
|
+
});
|
|
1375
|
+
|
|
1292
1376
|
// src/ast/createProgram.ts
|
|
1293
1377
|
import ts4 from "typescript";
|
|
1294
1378
|
function invalidateProgramCache() {
|
|
@@ -1521,9 +1605,17 @@ async function writeSchemaModule(entries, allTypesMap, outputPath) {
|
|
|
1521
1605
|
await fs3.mkdir(path4.dirname(outputPath), { recursive: true });
|
|
1522
1606
|
await fs3.writeFile(outputPath, source, "utf-8");
|
|
1523
1607
|
}
|
|
1608
|
+
async function generateSchemaFile(routes, rootDir, outputPath) {
|
|
1609
|
+
const { sources, allTypesByFile } = collectRouteSchemaSources(routes, rootDir);
|
|
1610
|
+
const entries = sources.map(({ filePath, schemaName, typeInfo }) => ({
|
|
1611
|
+
filePath,
|
|
1612
|
+
schemaName,
|
|
1613
|
+
typeInfo
|
|
1614
|
+
}));
|
|
1615
|
+
await writeSchemaModule(entries, allTypesByFile, outputPath);
|
|
1616
|
+
}
|
|
1524
1617
|
async function readManifestFile(inputPath) {
|
|
1525
|
-
const
|
|
1526
|
-
const mod = await import(fileUrl);
|
|
1618
|
+
const mod = await importWithCacheBust(inputPath);
|
|
1527
1619
|
const validators = mod.validators;
|
|
1528
1620
|
const properties = mod.properties ?? {};
|
|
1529
1621
|
const manifest = /* @__PURE__ */ new Map();
|
|
@@ -1546,27 +1638,51 @@ async function readManifestFile(inputPath) {
|
|
|
1546
1638
|
}
|
|
1547
1639
|
return manifest;
|
|
1548
1640
|
}
|
|
1641
|
+
function remapManifestKeys(manifest, rootDir, prodDir) {
|
|
1642
|
+
const remapped = /* @__PURE__ */ new Map();
|
|
1643
|
+
const rootPrefix = rootDir + path4.sep;
|
|
1644
|
+
for (const [filePath, fileSchemas] of manifest) {
|
|
1645
|
+
let rel = filePath;
|
|
1646
|
+
if (filePath.startsWith(rootPrefix)) {
|
|
1647
|
+
rel = filePath.slice(rootPrefix.length);
|
|
1648
|
+
} else if (filePath.startsWith(rootDir)) {
|
|
1649
|
+
rel = filePath.slice(rootDir.length).replace(/^[/\\]/, "");
|
|
1650
|
+
}
|
|
1651
|
+
const prodRel = `${prodDir}/${rel.replace(/\.ts$/, ".js")}`;
|
|
1652
|
+
const prodAbs = path4.resolve(rootDir, prodRel);
|
|
1653
|
+
remapped.set(prodAbs, fileSchemas);
|
|
1654
|
+
}
|
|
1655
|
+
return remapped;
|
|
1656
|
+
}
|
|
1657
|
+
async function loadSchemaToRegistry(schemaPath, rootDir, prodDir, remap = true) {
|
|
1658
|
+
const manifest = await readManifestFile(schemaPath);
|
|
1659
|
+
const finalManifest = remap ? remapManifestKeys(manifest, rootDir, prodDir) : manifest;
|
|
1660
|
+
schemaRegistry.loadManifest(finalManifest);
|
|
1661
|
+
return finalManifest;
|
|
1662
|
+
}
|
|
1549
1663
|
var init_generateSchema = __esm({
|
|
1550
1664
|
"src/cli/generateSchema.ts"() {
|
|
1551
1665
|
"use strict";
|
|
1552
1666
|
init_generateValidatorCode();
|
|
1667
|
+
init_schemaRegistry();
|
|
1553
1668
|
init_collectRouteSchemaSources();
|
|
1669
|
+
init_importWithCacheBust();
|
|
1554
1670
|
}
|
|
1555
1671
|
});
|
|
1556
1672
|
|
|
1557
1673
|
// src/cli/generateRoutes.ts
|
|
1558
1674
|
import fs4 from "fs";
|
|
1559
1675
|
import path5 from "path";
|
|
1560
|
-
function toProdFilePath(filePath) {
|
|
1676
|
+
function toProdFilePath(filePath, prodDir) {
|
|
1561
1677
|
const jsPath = filePath.replace(/\.ts$/, ".js");
|
|
1562
|
-
return jsPath.startsWith(
|
|
1678
|
+
return jsPath.startsWith(`${prodDir}/`) ? jsPath : `${prodDir}/${jsPath}`;
|
|
1563
1679
|
}
|
|
1564
|
-
function serializeRoutes(routes, wsRoutes, rootDir) {
|
|
1680
|
+
function serializeRoutes(routes, wsRoutes, rootDir, prodDir = "dist") {
|
|
1565
1681
|
const serialize = (route) => {
|
|
1566
|
-
const middlewarePaths = extractMiddlewarePaths(route.filePath, rootDir);
|
|
1682
|
+
const middlewarePaths = extractMiddlewarePaths(route.filePath, rootDir, prodDir);
|
|
1567
1683
|
const serialized = {
|
|
1568
1684
|
urlPath: route.urlPath,
|
|
1569
|
-
filePath: toProdFilePath(route.filePath),
|
|
1685
|
+
filePath: toProdFilePath(route.filePath, prodDir),
|
|
1570
1686
|
paramNames: route.paramNames,
|
|
1571
1687
|
isDynamic: route.isDynamic,
|
|
1572
1688
|
isCatchAll: route.isCatchAll,
|
|
@@ -1582,21 +1698,21 @@ function serializeRoutes(routes, wsRoutes, rootDir) {
|
|
|
1582
1698
|
wsRoutes: wsRoutes.map(serialize)
|
|
1583
1699
|
};
|
|
1584
1700
|
}
|
|
1585
|
-
function extractMiddlewarePaths(routeFilePath, rootDir) {
|
|
1701
|
+
function extractMiddlewarePaths(routeFilePath, rootDir, prodDir) {
|
|
1586
1702
|
const routeDir = path5.dirname(routeFilePath);
|
|
1587
1703
|
const resolvedRoot = path5.resolve(rootDir);
|
|
1588
1704
|
const paths = [];
|
|
1589
1705
|
let currentDir = path5.resolve(rootDir, routeDir);
|
|
1590
1706
|
while (true) {
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1707
|
+
const mwTsPath = path5.join(currentDir, "middlewares.ts");
|
|
1708
|
+
const mwJsPath = path5.join(currentDir, "middlewares.js");
|
|
1709
|
+
const absTsPath = path5.resolve(rootDir, mwTsPath);
|
|
1710
|
+
const absJsPath = path5.resolve(rootDir, mwJsPath);
|
|
1711
|
+
const absMwPath = fs4.existsSync(absTsPath) ? absTsPath : fs4.existsSync(absJsPath) ? absJsPath : null;
|
|
1712
|
+
if (absMwPath) {
|
|
1713
|
+
const relMwPath = path5.relative(rootDir, absMwPath);
|
|
1714
|
+
const prodAbsPath = path5.resolve(rootDir, toProdFilePath(relMwPath, prodDir));
|
|
1715
|
+
paths.push(prodAbsPath);
|
|
1600
1716
|
}
|
|
1601
1717
|
if (currentDir === resolvedRoot) break;
|
|
1602
1718
|
const parentDir = path5.dirname(currentDir);
|
|
@@ -1669,23 +1785,184 @@ var init_generateRoutes = __esm({
|
|
|
1669
1785
|
}
|
|
1670
1786
|
});
|
|
1671
1787
|
|
|
1788
|
+
// src/utils/readTsconfig.ts
|
|
1789
|
+
import ts6 from "typescript";
|
|
1790
|
+
import path6 from "path";
|
|
1791
|
+
import fs5 from "fs";
|
|
1792
|
+
function readTsconfig(rootDir) {
|
|
1793
|
+
const tsconfigPath = path6.resolve(rootDir, "tsconfig.json");
|
|
1794
|
+
if (!fs5.existsSync(tsconfigPath)) return null;
|
|
1795
|
+
const configFile = ts6.readConfigFile(tsconfigPath, ts6.sys.readFile);
|
|
1796
|
+
if (configFile.error || !configFile.config) return null;
|
|
1797
|
+
const parsed = ts6.parseJsonConfigFileContent(configFile.config, ts6.sys, rootDir);
|
|
1798
|
+
const baseUrl = parsed.options.baseUrl ?? rootDir;
|
|
1799
|
+
const rawPaths = parsed.options.paths;
|
|
1800
|
+
if (!rawPaths) return null;
|
|
1801
|
+
const paths = {};
|
|
1802
|
+
for (const [pattern, targets] of Object.entries(rawPaths)) {
|
|
1803
|
+
paths[pattern] = targets.map((t) => path6.resolve(baseUrl, t));
|
|
1804
|
+
}
|
|
1805
|
+
return { baseUrl, paths };
|
|
1806
|
+
}
|
|
1807
|
+
var init_readTsconfig = __esm({
|
|
1808
|
+
"src/utils/readTsconfig.ts"() {
|
|
1809
|
+
"use strict";
|
|
1810
|
+
}
|
|
1811
|
+
});
|
|
1812
|
+
|
|
1813
|
+
// src/utils/resolveAlias.ts
|
|
1814
|
+
function resolveAlias(specifier, config) {
|
|
1815
|
+
const candidates = [];
|
|
1816
|
+
for (const [pattern, targets] of Object.entries(config.paths)) {
|
|
1817
|
+
const wildcardIndex = pattern.indexOf("*");
|
|
1818
|
+
if (wildcardIndex === -1) {
|
|
1819
|
+
if (specifier === pattern) {
|
|
1820
|
+
candidates.push(...targets);
|
|
1821
|
+
}
|
|
1822
|
+
continue;
|
|
1823
|
+
}
|
|
1824
|
+
const prefix = pattern.slice(0, wildcardIndex);
|
|
1825
|
+
const suffix = pattern.slice(wildcardIndex + 1);
|
|
1826
|
+
if (specifier.startsWith(prefix) && specifier.endsWith(suffix) && specifier.length >= prefix.length + suffix.length) {
|
|
1827
|
+
const captured = specifier.slice(prefix.length, specifier.length - suffix.length);
|
|
1828
|
+
for (const target of targets) {
|
|
1829
|
+
candidates.push(target.replace("*", captured));
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
return candidates;
|
|
1834
|
+
}
|
|
1835
|
+
var init_resolveAlias = __esm({
|
|
1836
|
+
"src/utils/resolveAlias.ts"() {
|
|
1837
|
+
"use strict";
|
|
1838
|
+
}
|
|
1839
|
+
});
|
|
1840
|
+
|
|
1841
|
+
// src/cli/compileRoutes.ts
|
|
1842
|
+
import path7 from "path";
|
|
1843
|
+
import fs6 from "fs";
|
|
1844
|
+
import fg2 from "fast-glob";
|
|
1845
|
+
function toProdExtension(filePath) {
|
|
1846
|
+
if (filePath.endsWith(".ts")) return filePath.slice(0, -3) + ".js";
|
|
1847
|
+
if (filePath.endsWith(".tsx")) return filePath.slice(0, -4) + ".js";
|
|
1848
|
+
if (filePath.endsWith(".jsx")) return filePath.slice(0, -4) + ".js";
|
|
1849
|
+
return filePath;
|
|
1850
|
+
}
|
|
1851
|
+
function toProdImportPath(sourceFile, importer) {
|
|
1852
|
+
const importerDir = path7.dirname(importer);
|
|
1853
|
+
let rel = path7.relative(importerDir, sourceFile);
|
|
1854
|
+
rel = rel.split(path7.sep).join("/");
|
|
1855
|
+
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
1856
|
+
return toProdExtension(rel);
|
|
1857
|
+
}
|
|
1858
|
+
function createAliasPlugin(config) {
|
|
1859
|
+
const EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
1860
|
+
const INDEX_EXTS = [
|
|
1861
|
+
"/index.ts",
|
|
1862
|
+
"/index.tsx",
|
|
1863
|
+
"/index.js",
|
|
1864
|
+
"/index.jsx",
|
|
1865
|
+
"/index.mjs",
|
|
1866
|
+
"/index.cjs"
|
|
1867
|
+
];
|
|
1868
|
+
const SPEC_RE = /(\bfrom\s*|import\s*\(\s*)(['"])([^'"]+)\2/g;
|
|
1869
|
+
return {
|
|
1870
|
+
name: "faapi-alias",
|
|
1871
|
+
setup(build) {
|
|
1872
|
+
build.onLoad({ filter: /\.(ts|tsx|js|jsx|mjs|cjs)$/ }, (args) => {
|
|
1873
|
+
let source;
|
|
1874
|
+
try {
|
|
1875
|
+
source = fs6.readFileSync(args.path, "utf8");
|
|
1876
|
+
} catch {
|
|
1877
|
+
return void 0;
|
|
1878
|
+
}
|
|
1879
|
+
const importer = args.path;
|
|
1880
|
+
let modified = false;
|
|
1881
|
+
const newSource = source.replace(SPEC_RE, (full, prefix, quote, specifier) => {
|
|
1882
|
+
if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("file:") || specifier.startsWith("node:")) {
|
|
1883
|
+
return full;
|
|
1884
|
+
}
|
|
1885
|
+
const candidates = resolveAlias(specifier, config);
|
|
1886
|
+
for (const candidate of candidates) {
|
|
1887
|
+
for (const ext of EXTS) {
|
|
1888
|
+
const file = candidate + ext;
|
|
1889
|
+
if (fs6.existsSync(file)) {
|
|
1890
|
+
modified = true;
|
|
1891
|
+
return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
for (const indexExt of INDEX_EXTS) {
|
|
1895
|
+
const file = candidate + indexExt;
|
|
1896
|
+
if (fs6.existsSync(file)) {
|
|
1897
|
+
modified = true;
|
|
1898
|
+
return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
return full;
|
|
1903
|
+
});
|
|
1904
|
+
if (!modified) return void 0;
|
|
1905
|
+
return { contents: newSource, loader: "default" };
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
async function compileRoutes(options) {
|
|
1911
|
+
const { rootDir, appDir, outDir, files, logLevel = "silent" } = options;
|
|
1912
|
+
const allFiles = files ?? await fg2([`${appDir}/**/*.ts`], {
|
|
1913
|
+
cwd: rootDir,
|
|
1914
|
+
onlyFiles: true,
|
|
1915
|
+
absolute: true,
|
|
1916
|
+
ignore: ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts"]
|
|
1917
|
+
});
|
|
1918
|
+
if (allFiles.length === 0) {
|
|
1919
|
+
return { compiledFiles: [] };
|
|
1920
|
+
}
|
|
1921
|
+
const absOutDir = path7.resolve(rootDir, outDir);
|
|
1922
|
+
await fs6.promises.mkdir(absOutDir, { recursive: true });
|
|
1923
|
+
const tsconfig = readTsconfig(rootDir);
|
|
1924
|
+
const plugins = tsconfig ? [createAliasPlugin(tsconfig)] : [];
|
|
1925
|
+
const esbuild = await import("esbuild");
|
|
1926
|
+
await esbuild.build({
|
|
1927
|
+
entryPoints: allFiles,
|
|
1928
|
+
outdir: absOutDir,
|
|
1929
|
+
outbase: rootDir,
|
|
1930
|
+
bundle: false,
|
|
1931
|
+
platform: "node",
|
|
1932
|
+
format: "esm",
|
|
1933
|
+
sourcemap: true,
|
|
1934
|
+
packages: "external",
|
|
1935
|
+
plugins,
|
|
1936
|
+
logLevel
|
|
1937
|
+
});
|
|
1938
|
+
return { compiledFiles: allFiles };
|
|
1939
|
+
}
|
|
1940
|
+
var init_compileRoutes = __esm({
|
|
1941
|
+
"src/cli/compileRoutes.ts"() {
|
|
1942
|
+
"use strict";
|
|
1943
|
+
init_readTsconfig();
|
|
1944
|
+
init_resolveAlias();
|
|
1945
|
+
}
|
|
1946
|
+
});
|
|
1947
|
+
|
|
1672
1948
|
// src/cli/buildCommand.ts
|
|
1673
1949
|
var buildCommand_exports = {};
|
|
1674
1950
|
__export(buildCommand_exports, {
|
|
1675
1951
|
buildCommand: () => buildCommand,
|
|
1676
1952
|
parseBuildArgs: () => parseBuildArgs
|
|
1677
1953
|
});
|
|
1678
|
-
import
|
|
1679
|
-
import fs5 from "fs";
|
|
1680
|
-
import fg2 from "fast-glob";
|
|
1954
|
+
import path8 from "path";
|
|
1681
1955
|
async function buildCommand(options) {
|
|
1682
1956
|
const { rootDir, patterns, appDir, outdir, types } = options;
|
|
1683
1957
|
console.log("faapi build started");
|
|
1684
1958
|
console.log(`- Root: ${rootDir}`);
|
|
1685
1959
|
console.log(`- Patterns: ${patterns.join(", ")}`);
|
|
1686
1960
|
console.log(`- Output: ${outdir}`);
|
|
1687
|
-
console.log("\n[1/5]
|
|
1688
|
-
const
|
|
1961
|
+
console.log("\n[1/5] Compiling TypeScript...");
|
|
1962
|
+
const result = await compileRoutes({ rootDir, appDir, outDir: outdir, logLevel: "silent" });
|
|
1963
|
+
console.log(` Compiled ${result.compiledFiles.length} file(s)`);
|
|
1964
|
+
console.log("\n[2/5] Scanning routes...");
|
|
1965
|
+
const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, appDir, outdir);
|
|
1689
1966
|
const sorted = sortRoutes(routes);
|
|
1690
1967
|
console.log(` Found ${sorted.length} routes, ${wsRoutes.length} WS routes`);
|
|
1691
1968
|
const conflicts = detectRouteConflicts(sorted);
|
|
@@ -1698,62 +1975,21 @@ async function buildCommand(options) {
|
|
|
1698
1975
|
}
|
|
1699
1976
|
}
|
|
1700
1977
|
}
|
|
1701
|
-
console.log("\n[
|
|
1702
|
-
const typesPath = types ?
|
|
1978
|
+
console.log("\n[3/5] Generating types...");
|
|
1979
|
+
const typesPath = types ? path8.resolve(rootDir, types) : path8.resolve(rootDir, "faapi-types.ts");
|
|
1703
1980
|
await generateTypes(sorted, rootDir, typesPath);
|
|
1704
1981
|
console.log(` Written to ${typesPath}`);
|
|
1705
|
-
console.log("\n[
|
|
1706
|
-
const schemaPath =
|
|
1707
|
-
|
|
1708
|
-
await writeSchemaModule(entries, allTypesByFile, schemaPath);
|
|
1982
|
+
console.log("\n[4/5] Generating schema module...");
|
|
1983
|
+
const schemaPath = path8.resolve(rootDir, outdir, "faapi-schema.js");
|
|
1984
|
+
await generateSchemaFile(sorted, rootDir, schemaPath);
|
|
1709
1985
|
console.log(` Written to ${schemaPath}`);
|
|
1710
|
-
console.log("\n[
|
|
1711
|
-
const routesPath =
|
|
1712
|
-
const serialized = serializeRoutes(sorted, wsRoutes, rootDir);
|
|
1986
|
+
console.log("\n[5/5] Generating routes manifest...");
|
|
1987
|
+
const routesPath = path8.resolve(rootDir, outdir, "faapi-routes.js");
|
|
1988
|
+
const serialized = serializeRoutes(sorted, wsRoutes, rootDir, outdir);
|
|
1713
1989
|
await writeRoutesModule(serialized, routesPath);
|
|
1714
1990
|
console.log(` Written to ${routesPath}`);
|
|
1715
|
-
console.log("\n[5/5] Compiling TypeScript...");
|
|
1716
|
-
await compileTypeScript(rootDir, patterns, appDir, outdir);
|
|
1717
|
-
console.log(" Done");
|
|
1718
1991
|
console.log("\nfaapi build completed");
|
|
1719
1992
|
}
|
|
1720
|
-
async function compileTypeScript(rootDir, patterns, appDir, outdir) {
|
|
1721
|
-
const esbuild = await import("esbuild");
|
|
1722
|
-
const allPatterns = [...patterns, `${appDir}/**/middlewares.ts`];
|
|
1723
|
-
const files = await fg2(allPatterns, {
|
|
1724
|
-
cwd: rootDir,
|
|
1725
|
-
onlyFiles: true,
|
|
1726
|
-
absolute: true
|
|
1727
|
-
});
|
|
1728
|
-
if (files.length === 0) {
|
|
1729
|
-
console.log(" No TypeScript files to compile");
|
|
1730
|
-
return;
|
|
1731
|
-
}
|
|
1732
|
-
const absOutdir = path6.resolve(rootDir, outdir);
|
|
1733
|
-
await fs5.promises.mkdir(absOutdir, { recursive: true });
|
|
1734
|
-
await esbuild.build({
|
|
1735
|
-
entryPoints: files,
|
|
1736
|
-
outdir: absOutdir,
|
|
1737
|
-
outbase: rootDir,
|
|
1738
|
-
bundle: false,
|
|
1739
|
-
// 不 bundle,逐文件编译
|
|
1740
|
-
platform: "node",
|
|
1741
|
-
format: "esm",
|
|
1742
|
-
sourcemap: true,
|
|
1743
|
-
packages: "external",
|
|
1744
|
-
// 依赖保持外部引用
|
|
1745
|
-
logLevel: "info"
|
|
1746
|
-
});
|
|
1747
|
-
}
|
|
1748
|
-
function extractSchemaEntries(routes, rootDir) {
|
|
1749
|
-
const { sources, allTypesByFile } = collectRouteSchemaSources(routes, rootDir);
|
|
1750
|
-
const entries = sources.map(({ filePath, schemaName, typeInfo }) => ({
|
|
1751
|
-
filePath,
|
|
1752
|
-
schemaName,
|
|
1753
|
-
typeInfo
|
|
1754
|
-
}));
|
|
1755
|
-
return { entries, allTypesByFile };
|
|
1756
|
-
}
|
|
1757
1993
|
function parseBuildArgs(argv) {
|
|
1758
1994
|
const rootDir = process.cwd();
|
|
1759
1995
|
let outdir = "dist";
|
|
@@ -1790,7 +2026,7 @@ var init_buildCommand = __esm({
|
|
|
1790
2026
|
init_generateTypes();
|
|
1791
2027
|
init_generateSchema();
|
|
1792
2028
|
init_generateRoutes();
|
|
1793
|
-
|
|
2029
|
+
init_compileRoutes();
|
|
1794
2030
|
}
|
|
1795
2031
|
});
|
|
1796
2032
|
|
|
@@ -2441,42 +2677,42 @@ var init_parseArgs = __esm({
|
|
|
2441
2677
|
});
|
|
2442
2678
|
|
|
2443
2679
|
// src/router/matchRoute.ts
|
|
2444
|
-
function matchRoute(routes, method,
|
|
2680
|
+
function matchRoute(routes, method, path15) {
|
|
2445
2681
|
for (const route of routes) {
|
|
2446
2682
|
if (route.method !== method) {
|
|
2447
2683
|
continue;
|
|
2448
2684
|
}
|
|
2449
2685
|
if (!route.isDynamic) {
|
|
2450
|
-
if (route.urlPath ===
|
|
2686
|
+
if (route.urlPath === path15) {
|
|
2451
2687
|
return { route, params: {} };
|
|
2452
2688
|
}
|
|
2453
2689
|
continue;
|
|
2454
2690
|
}
|
|
2455
|
-
const params = matchDynamicPath(route.urlPath,
|
|
2691
|
+
const params = matchDynamicPath(route.urlPath, path15, route.paramNames, route.isCatchAll);
|
|
2456
2692
|
if (params !== null) {
|
|
2457
2693
|
return { route, params };
|
|
2458
2694
|
}
|
|
2459
2695
|
}
|
|
2460
2696
|
return null;
|
|
2461
2697
|
}
|
|
2462
|
-
function matchWsRoute(wsRoutes,
|
|
2698
|
+
function matchWsRoute(wsRoutes, path15) {
|
|
2463
2699
|
for (const route of wsRoutes) {
|
|
2464
2700
|
if (!route.isDynamic) {
|
|
2465
|
-
if (route.urlPath ===
|
|
2701
|
+
if (route.urlPath === path15) {
|
|
2466
2702
|
return { route, params: {} };
|
|
2467
2703
|
}
|
|
2468
2704
|
continue;
|
|
2469
2705
|
}
|
|
2470
|
-
const params = matchDynamicPath(route.urlPath,
|
|
2706
|
+
const params = matchDynamicPath(route.urlPath, path15, route.paramNames, route.isCatchAll);
|
|
2471
2707
|
if (params !== null) {
|
|
2472
2708
|
return { route, params };
|
|
2473
2709
|
}
|
|
2474
2710
|
}
|
|
2475
2711
|
return null;
|
|
2476
2712
|
}
|
|
2477
|
-
function matchDynamicPath(pattern,
|
|
2713
|
+
function matchDynamicPath(pattern, path15, paramNames, isCatchAll) {
|
|
2478
2714
|
const patternSegments = pattern.split("/").filter(Boolean);
|
|
2479
|
-
const pathSegments =
|
|
2715
|
+
const pathSegments = path15.split("/").filter(Boolean);
|
|
2480
2716
|
if (isCatchAll) {
|
|
2481
2717
|
const nonCatchAllCount = patternSegments.length - 1;
|
|
2482
2718
|
if (pathSegments.length <= nonCatchAllCount) {
|
|
@@ -2895,14 +3131,14 @@ var init_httpErrors = __esm({
|
|
|
2895
3131
|
issues;
|
|
2896
3132
|
};
|
|
2897
3133
|
RouteNotFoundError = class extends FaapiError {
|
|
2898
|
-
constructor(
|
|
2899
|
-
super("ROUTE_NOT_FOUND", `Route not found: ${
|
|
3134
|
+
constructor(path15) {
|
|
3135
|
+
super("ROUTE_NOT_FOUND", `Route not found: ${path15}`, 404);
|
|
2900
3136
|
this.name = "RouteNotFoundError";
|
|
2901
3137
|
}
|
|
2902
3138
|
};
|
|
2903
3139
|
MethodNotAllowedError = class extends FaapiError {
|
|
2904
|
-
constructor(method,
|
|
2905
|
-
super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${
|
|
3140
|
+
constructor(method, path15, allowedMethods) {
|
|
3141
|
+
super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path15}`, 405);
|
|
2906
3142
|
this.allowedMethods = allowedMethods;
|
|
2907
3143
|
this.name = "MethodNotAllowedError";
|
|
2908
3144
|
}
|
|
@@ -3261,75 +3497,6 @@ var init_sendNodeResponse = __esm({
|
|
|
3261
3497
|
}
|
|
3262
3498
|
});
|
|
3263
3499
|
|
|
3264
|
-
// src/validator/schemaRegistry.ts
|
|
3265
|
-
var SchemaRegistry, schemaRegistry;
|
|
3266
|
-
var init_schemaRegistry = __esm({
|
|
3267
|
-
"src/validator/schemaRegistry.ts"() {
|
|
3268
|
-
"use strict";
|
|
3269
|
-
SchemaRegistry = class {
|
|
3270
|
-
manifest = /* @__PURE__ */ new Map();
|
|
3271
|
-
/**
|
|
3272
|
-
* 批量加载 manifest
|
|
3273
|
-
* 覆盖已有数据
|
|
3274
|
-
*/
|
|
3275
|
-
loadManifest(manifest) {
|
|
3276
|
-
this.manifest.clear();
|
|
3277
|
-
for (const [filePath, fileSchemas] of manifest) {
|
|
3278
|
-
const copy = /* @__PURE__ */ new Map();
|
|
3279
|
-
fileSchemas.forEach((value, key) => copy.set(key, value));
|
|
3280
|
-
this.manifest.set(filePath, copy);
|
|
3281
|
-
}
|
|
3282
|
-
}
|
|
3283
|
-
/**
|
|
3284
|
-
* 查询单条 schema
|
|
3285
|
-
* @returns SchemaEntry | null | undefined
|
|
3286
|
-
* - SchemaEntry:有类型声明
|
|
3287
|
-
* - null:无类型声明(跳过校验)
|
|
3288
|
-
* - undefined:manifest 不完整(抛错)
|
|
3289
|
-
*/
|
|
3290
|
-
get(filePath, schemaName) {
|
|
3291
|
-
const fileSchemas = this.manifest.get(filePath);
|
|
3292
|
-
if (!fileSchemas) return void 0;
|
|
3293
|
-
return fileSchemas.get(schemaName);
|
|
3294
|
-
}
|
|
3295
|
-
/**
|
|
3296
|
-
* 设置单个文件的所有 schema
|
|
3297
|
-
* 覆盖该文件的已有数据
|
|
3298
|
-
*/
|
|
3299
|
-
set(filePath, schemas) {
|
|
3300
|
-
const copy = /* @__PURE__ */ new Map();
|
|
3301
|
-
schemas.forEach((value, key) => copy.set(key, value));
|
|
3302
|
-
this.manifest.set(filePath, copy);
|
|
3303
|
-
}
|
|
3304
|
-
/**
|
|
3305
|
-
* 删除单个文件(文件被删除时)
|
|
3306
|
-
*/
|
|
3307
|
-
delete(filePath) {
|
|
3308
|
-
this.manifest.delete(filePath);
|
|
3309
|
-
}
|
|
3310
|
-
/**
|
|
3311
|
-
* 判断文件是否已注册
|
|
3312
|
-
*/
|
|
3313
|
-
hasFile(filePath) {
|
|
3314
|
-
return this.manifest.has(filePath);
|
|
3315
|
-
}
|
|
3316
|
-
/**
|
|
3317
|
-
* 清空(测试用 / watch 全量重建前)
|
|
3318
|
-
*/
|
|
3319
|
-
clear() {
|
|
3320
|
-
this.manifest.clear();
|
|
3321
|
-
}
|
|
3322
|
-
/**
|
|
3323
|
-
* 已注册的文件数量
|
|
3324
|
-
*/
|
|
3325
|
-
get size() {
|
|
3326
|
-
return this.manifest.size;
|
|
3327
|
-
}
|
|
3328
|
-
};
|
|
3329
|
-
schemaRegistry = new SchemaRegistry();
|
|
3330
|
-
}
|
|
3331
|
-
});
|
|
3332
|
-
|
|
3333
3500
|
// src/validator/coerceInput.ts
|
|
3334
3501
|
function coerceInput(input, properties) {
|
|
3335
3502
|
const issues = [];
|
|
@@ -3342,16 +3509,16 @@ function coerceInput(input, properties) {
|
|
|
3342
3509
|
}
|
|
3343
3510
|
return { data, issues };
|
|
3344
3511
|
}
|
|
3345
|
-
function coerceValue(value, type,
|
|
3512
|
+
function coerceValue(value, type, path15, issues) {
|
|
3346
3513
|
switch (type.kind) {
|
|
3347
3514
|
case "number":
|
|
3348
3515
|
if (typeof value === "string") {
|
|
3349
|
-
return coerceStringToNumber(value,
|
|
3516
|
+
return coerceStringToNumber(value, path15, issues);
|
|
3350
3517
|
}
|
|
3351
3518
|
return value;
|
|
3352
3519
|
case "boolean":
|
|
3353
3520
|
if (typeof value === "string") {
|
|
3354
|
-
return coerceStringToBoolean(value,
|
|
3521
|
+
return coerceStringToBoolean(value, path15, issues);
|
|
3355
3522
|
}
|
|
3356
3523
|
return value;
|
|
3357
3524
|
case "array":
|
|
@@ -3359,7 +3526,7 @@ function coerceValue(value, type, path12, issues) {
|
|
|
3359
3526
|
return value;
|
|
3360
3527
|
}
|
|
3361
3528
|
if (Array.isArray(value)) {
|
|
3362
|
-
return value.map((item, i) => coerceValue(item, type.element, `${
|
|
3529
|
+
return value.map((item, i) => coerceValue(item, type.element, `${path15}[${i}]`, issues));
|
|
3363
3530
|
}
|
|
3364
3531
|
return value;
|
|
3365
3532
|
case "tuple": {
|
|
@@ -3368,10 +3535,10 @@ function coerceValue(value, type, path12, issues) {
|
|
|
3368
3535
|
return value.map((item, i) => {
|
|
3369
3536
|
const elem = type.elements[i];
|
|
3370
3537
|
if (elem && !elem.rest) {
|
|
3371
|
-
return coerceValue(item, elem.type, `${
|
|
3538
|
+
return coerceValue(item, elem.type, `${path15}[${i}]`, issues);
|
|
3372
3539
|
}
|
|
3373
3540
|
if (restElement) {
|
|
3374
|
-
return coerceValue(item, restElement.type, `${
|
|
3541
|
+
return coerceValue(item, restElement.type, `${path15}[${i}]`, issues);
|
|
3375
3542
|
}
|
|
3376
3543
|
return item;
|
|
3377
3544
|
});
|
|
@@ -3379,7 +3546,7 @@ function coerceValue(value, type, path12, issues) {
|
|
|
3379
3546
|
case "union":
|
|
3380
3547
|
for (const member of type.members) {
|
|
3381
3548
|
const tempIssues = [];
|
|
3382
|
-
const coerced = coerceValue(value, member,
|
|
3549
|
+
const coerced = coerceValue(value, member, path15, tempIssues);
|
|
3383
3550
|
if (tempIssues.length === 0) {
|
|
3384
3551
|
return coerced;
|
|
3385
3552
|
}
|
|
@@ -3400,7 +3567,7 @@ function coerceValue(value, type, path12, issues) {
|
|
|
3400
3567
|
result[prop.name] = coerceValue(
|
|
3401
3568
|
obj[prop.name],
|
|
3402
3569
|
prop.type,
|
|
3403
|
-
`${
|
|
3570
|
+
`${path15}.${prop.name}`,
|
|
3404
3571
|
issues
|
|
3405
3572
|
);
|
|
3406
3573
|
}
|
|
@@ -3412,31 +3579,31 @@ function coerceValue(value, type, path12, issues) {
|
|
|
3412
3579
|
return value;
|
|
3413
3580
|
}
|
|
3414
3581
|
}
|
|
3415
|
-
function coerceStringToNumber(value,
|
|
3582
|
+
function coerceStringToNumber(value, path15, issues) {
|
|
3416
3583
|
if (value.trim() === "") {
|
|
3417
3584
|
issues.push({
|
|
3418
|
-
path:
|
|
3585
|
+
path: path15,
|
|
3419
3586
|
code: "COERCE_FAILED",
|
|
3420
3587
|
expected: "number",
|
|
3421
3588
|
received: "string",
|
|
3422
|
-
message: `\u5B57\u6BB5 "${
|
|
3589
|
+
message: `\u5B57\u6BB5 "${path15}" \u7C7B\u578B\u8F6C\u6362\u5931\u8D25\uFF1A\u65E0\u6CD5\u5C06 "${value}" \u8F6C\u4E3A number`
|
|
3423
3590
|
});
|
|
3424
3591
|
return value;
|
|
3425
3592
|
}
|
|
3426
3593
|
const num = Number(value);
|
|
3427
3594
|
if (Number.isNaN(num)) {
|
|
3428
3595
|
issues.push({
|
|
3429
|
-
path:
|
|
3596
|
+
path: path15,
|
|
3430
3597
|
code: "COERCE_FAILED",
|
|
3431
3598
|
expected: "number",
|
|
3432
3599
|
received: "string",
|
|
3433
|
-
message: `\u5B57\u6BB5 "${
|
|
3600
|
+
message: `\u5B57\u6BB5 "${path15}" \u7C7B\u578B\u8F6C\u6362\u5931\u8D25\uFF1A\u65E0\u6CD5\u5C06 "${value}" \u8F6C\u4E3A number`
|
|
3434
3601
|
});
|
|
3435
3602
|
return value;
|
|
3436
3603
|
}
|
|
3437
3604
|
return num;
|
|
3438
3605
|
}
|
|
3439
|
-
function coerceStringToBoolean(value,
|
|
3606
|
+
function coerceStringToBoolean(value, path15, issues) {
|
|
3440
3607
|
if (value === "true" || value === "1") {
|
|
3441
3608
|
return true;
|
|
3442
3609
|
}
|
|
@@ -3444,11 +3611,11 @@ function coerceStringToBoolean(value, path12, issues) {
|
|
|
3444
3611
|
return false;
|
|
3445
3612
|
}
|
|
3446
3613
|
issues.push({
|
|
3447
|
-
path:
|
|
3614
|
+
path: path15,
|
|
3448
3615
|
code: "COERCE_FAILED",
|
|
3449
3616
|
expected: "boolean",
|
|
3450
3617
|
received: "string",
|
|
3451
|
-
message: `\u5B57\u6BB5 "${
|
|
3618
|
+
message: `\u5B57\u6BB5 "${path15}" \u7C7B\u578B\u8F6C\u6362\u5931\u8D25\uFF1A\u65E0\u6CD5\u5C06 "${value}" \u8F6C\u4E3A boolean`
|
|
3452
3619
|
});
|
|
3453
3620
|
return value;
|
|
3454
3621
|
}
|
|
@@ -3588,26 +3755,26 @@ var init_cors = __esm({
|
|
|
3588
3755
|
});
|
|
3589
3756
|
|
|
3590
3757
|
// src/server/serveStatic.ts
|
|
3591
|
-
import
|
|
3592
|
-
import
|
|
3758
|
+
import fs7 from "fs";
|
|
3759
|
+
import path9 from "path";
|
|
3593
3760
|
import { createReadStream } from "fs";
|
|
3594
3761
|
import { Readable as Readable2 } from "stream";
|
|
3595
3762
|
async function serveStatic(urlPath, staticDir) {
|
|
3596
|
-
const resolved =
|
|
3597
|
-
const relative3 =
|
|
3598
|
-
if (relative3.startsWith("..") ||
|
|
3763
|
+
const resolved = path9.resolve(staticDir, "." + urlPath);
|
|
3764
|
+
const relative3 = path9.relative(staticDir, resolved);
|
|
3765
|
+
if (relative3.startsWith("..") || path9.isAbsolute(relative3)) {
|
|
3599
3766
|
return null;
|
|
3600
3767
|
}
|
|
3601
3768
|
let stat4;
|
|
3602
3769
|
try {
|
|
3603
|
-
stat4 = await
|
|
3770
|
+
stat4 = await fs7.promises.stat(resolved);
|
|
3604
3771
|
} catch {
|
|
3605
3772
|
return null;
|
|
3606
3773
|
}
|
|
3607
3774
|
if (stat4.isDirectory()) {
|
|
3608
|
-
const indexPath =
|
|
3775
|
+
const indexPath = path9.join(resolved, "index.html");
|
|
3609
3776
|
try {
|
|
3610
|
-
const indexStat = await
|
|
3777
|
+
const indexStat = await fs7.promises.stat(indexPath);
|
|
3611
3778
|
if (indexStat.isFile()) {
|
|
3612
3779
|
return serveFile(indexPath, indexStat.size);
|
|
3613
3780
|
}
|
|
@@ -3621,7 +3788,7 @@ async function serveStatic(urlPath, staticDir) {
|
|
|
3621
3788
|
return serveFile(resolved, stat4.size);
|
|
3622
3789
|
}
|
|
3623
3790
|
function serveFile(filePath, size) {
|
|
3624
|
-
const ext =
|
|
3791
|
+
const ext = path9.extname(filePath).toLowerCase();
|
|
3625
3792
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
3626
3793
|
const stream = createReadStream(filePath);
|
|
3627
3794
|
const webStream = Readable2.toWeb(stream);
|
|
@@ -3821,7 +3988,7 @@ var init_wsHandler = __esm({
|
|
|
3821
3988
|
|
|
3822
3989
|
// src/server/handleWsUpgrade.ts
|
|
3823
3990
|
import { WebSocketServer, WebSocket } from "ws";
|
|
3824
|
-
import
|
|
3991
|
+
import path10 from "path";
|
|
3825
3992
|
function getPathname(req) {
|
|
3826
3993
|
const url = req.url ?? "/";
|
|
3827
3994
|
const idx = url.indexOf("?");
|
|
@@ -3904,7 +4071,7 @@ function attachWebSocket(options) {
|
|
|
3904
4071
|
const finalHandler = async () => {
|
|
3905
4072
|
let handlers;
|
|
3906
4073
|
try {
|
|
3907
|
-
const absoluteFilePath =
|
|
4074
|
+
const absoluteFilePath = path10.resolve(rootDir, route.filePath);
|
|
3908
4075
|
handlers = await loadWsHandler(absoluteFilePath, ctx);
|
|
3909
4076
|
} catch (err) {
|
|
3910
4077
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -3966,7 +4133,7 @@ import {
|
|
|
3966
4133
|
createServer as createHttpServer
|
|
3967
4134
|
} from "http";
|
|
3968
4135
|
import { Readable as Readable3 } from "stream";
|
|
3969
|
-
import
|
|
4136
|
+
import path11 from "path";
|
|
3970
4137
|
function toWebRequest(req) {
|
|
3971
4138
|
const forwardedProto = req.headers["x-forwarded-proto"];
|
|
3972
4139
|
const protocol = Array.isArray(forwardedProto) ? forwardedProto[0]?.split(",")[0]?.trim() ?? "http" : forwardedProto?.split(",")[0]?.trim() ?? "http";
|
|
@@ -4010,15 +4177,15 @@ function limitStreamSize(stream, maxSize) {
|
|
|
4010
4177
|
}
|
|
4011
4178
|
});
|
|
4012
4179
|
}
|
|
4013
|
-
function findAllowedMethods(routes,
|
|
4180
|
+
function findAllowedMethods(routes, path15) {
|
|
4014
4181
|
const methods = /* @__PURE__ */ new Set();
|
|
4015
4182
|
for (const route of routes) {
|
|
4016
|
-
if (route.urlPath ===
|
|
4183
|
+
if (route.urlPath === path15) {
|
|
4017
4184
|
methods.add(route.method);
|
|
4018
4185
|
continue;
|
|
4019
4186
|
}
|
|
4020
4187
|
if (route.isDynamic) {
|
|
4021
|
-
const params = matchDynamicPath(route.urlPath,
|
|
4188
|
+
const params = matchDynamicPath(route.urlPath, path15, route.paramNames, route.isCatchAll);
|
|
4022
4189
|
if (params !== null) {
|
|
4023
4190
|
methods.add(route.method);
|
|
4024
4191
|
}
|
|
@@ -4085,7 +4252,7 @@ async function handleRequest(routes, rootDir, req, res, corsMiddleware, staticDi
|
|
|
4085
4252
|
const match = matchRoute(routes, method, urlPath);
|
|
4086
4253
|
if (!match) {
|
|
4087
4254
|
if (staticDir) {
|
|
4088
|
-
const absStaticDir =
|
|
4255
|
+
const absStaticDir = path11.resolve(rootDir, staticDir);
|
|
4089
4256
|
const staticResponse = await serveStatic(urlPath, absStaticDir);
|
|
4090
4257
|
if (staticResponse) {
|
|
4091
4258
|
return mergeMeta(staticResponse, meta);
|
|
@@ -4099,7 +4266,7 @@ async function handleRequest(routes, rootDir, req, res, corsMiddleware, staticDi
|
|
|
4099
4266
|
}
|
|
4100
4267
|
ctx.params = match.params;
|
|
4101
4268
|
const { route } = match;
|
|
4102
|
-
const absoluteFilePath =
|
|
4269
|
+
const absoluteFilePath = path11.resolve(rootDir, route.filePath);
|
|
4103
4270
|
const routeModule = await loadRouteModule(absoluteFilePath, route.method);
|
|
4104
4271
|
const input = await resolveInput(route.method, request);
|
|
4105
4272
|
const inputType = getInputTypeForMethod(route.method);
|
|
@@ -4354,7 +4521,7 @@ var init_esm = __esm({
|
|
|
4354
4521
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
4355
4522
|
const statMethod = opts.lstat ? lstat : stat;
|
|
4356
4523
|
if (wantBigintFsStats) {
|
|
4357
|
-
this._stat = (
|
|
4524
|
+
this._stat = (path15) => statMethod(path15, { bigint: true });
|
|
4358
4525
|
} else {
|
|
4359
4526
|
this._stat = statMethod;
|
|
4360
4527
|
}
|
|
@@ -4379,8 +4546,8 @@ var init_esm = __esm({
|
|
|
4379
4546
|
const par = this.parent;
|
|
4380
4547
|
const fil = par && par.files;
|
|
4381
4548
|
if (fil && fil.length > 0) {
|
|
4382
|
-
const { path:
|
|
4383
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
4549
|
+
const { path: path15, depth } = par;
|
|
4550
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path15));
|
|
4384
4551
|
const awaited = await Promise.all(slice);
|
|
4385
4552
|
for (const entry of awaited) {
|
|
4386
4553
|
if (!entry)
|
|
@@ -4420,20 +4587,20 @@ var init_esm = __esm({
|
|
|
4420
4587
|
this.reading = false;
|
|
4421
4588
|
}
|
|
4422
4589
|
}
|
|
4423
|
-
async _exploreDir(
|
|
4590
|
+
async _exploreDir(path15, depth) {
|
|
4424
4591
|
let files;
|
|
4425
4592
|
try {
|
|
4426
|
-
files = await readdir(
|
|
4593
|
+
files = await readdir(path15, this._rdOptions);
|
|
4427
4594
|
} catch (error) {
|
|
4428
4595
|
this._onError(error);
|
|
4429
4596
|
}
|
|
4430
|
-
return { files, depth, path:
|
|
4597
|
+
return { files, depth, path: path15 };
|
|
4431
4598
|
}
|
|
4432
|
-
async _formatEntry(dirent,
|
|
4599
|
+
async _formatEntry(dirent, path15) {
|
|
4433
4600
|
let entry;
|
|
4434
4601
|
const basename3 = this._isDirent ? dirent.name : dirent;
|
|
4435
4602
|
try {
|
|
4436
|
-
const fullPath = presolve(pjoin(
|
|
4603
|
+
const fullPath = presolve(pjoin(path15, basename3));
|
|
4437
4604
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
|
|
4438
4605
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
4439
4606
|
} catch (err) {
|
|
@@ -4494,16 +4661,16 @@ import { watchFile, unwatchFile, watch as fs_watch } from "fs";
|
|
|
4494
4661
|
import { open, stat as stat2, lstat as lstat2, realpath as fsrealpath } from "fs/promises";
|
|
4495
4662
|
import * as sysPath from "path";
|
|
4496
4663
|
import { type as osType } from "os";
|
|
4497
|
-
function createFsWatchInstance(
|
|
4664
|
+
function createFsWatchInstance(path15, options, listener, errHandler, emitRaw) {
|
|
4498
4665
|
const handleEvent = (rawEvent, evPath) => {
|
|
4499
|
-
listener(
|
|
4500
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
4501
|
-
if (evPath &&
|
|
4502
|
-
fsWatchBroadcast(sysPath.resolve(
|
|
4666
|
+
listener(path15);
|
|
4667
|
+
emitRaw(rawEvent, evPath, { watchedPath: path15 });
|
|
4668
|
+
if (evPath && path15 !== evPath) {
|
|
4669
|
+
fsWatchBroadcast(sysPath.resolve(path15, evPath), KEY_LISTENERS, sysPath.join(path15, evPath));
|
|
4503
4670
|
}
|
|
4504
4671
|
};
|
|
4505
4672
|
try {
|
|
4506
|
-
return fs_watch(
|
|
4673
|
+
return fs_watch(path15, {
|
|
4507
4674
|
persistent: options.persistent
|
|
4508
4675
|
}, handleEvent);
|
|
4509
4676
|
} catch (error) {
|
|
@@ -4848,12 +5015,12 @@ var init_handler = __esm({
|
|
|
4848
5015
|
listener(val1, val2, val3);
|
|
4849
5016
|
});
|
|
4850
5017
|
};
|
|
4851
|
-
setFsWatchListener = (
|
|
5018
|
+
setFsWatchListener = (path15, fullPath, options, handlers) => {
|
|
4852
5019
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
4853
5020
|
let cont = FsWatchInstances.get(fullPath);
|
|
4854
5021
|
let watcher;
|
|
4855
5022
|
if (!options.persistent) {
|
|
4856
|
-
watcher = createFsWatchInstance(
|
|
5023
|
+
watcher = createFsWatchInstance(path15, options, listener, errHandler, rawEmitter);
|
|
4857
5024
|
if (!watcher)
|
|
4858
5025
|
return;
|
|
4859
5026
|
return watcher.close.bind(watcher);
|
|
@@ -4864,7 +5031,7 @@ var init_handler = __esm({
|
|
|
4864
5031
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
4865
5032
|
} else {
|
|
4866
5033
|
watcher = createFsWatchInstance(
|
|
4867
|
-
|
|
5034
|
+
path15,
|
|
4868
5035
|
options,
|
|
4869
5036
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
4870
5037
|
errHandler,
|
|
@@ -4879,7 +5046,7 @@ var init_handler = __esm({
|
|
|
4879
5046
|
cont.watcherUnusable = true;
|
|
4880
5047
|
if (isWindows && error.code === "EPERM") {
|
|
4881
5048
|
try {
|
|
4882
|
-
const fd = await open(
|
|
5049
|
+
const fd = await open(path15, "r");
|
|
4883
5050
|
await fd.close();
|
|
4884
5051
|
broadcastErr(error);
|
|
4885
5052
|
} catch (err) {
|
|
@@ -4910,7 +5077,7 @@ var init_handler = __esm({
|
|
|
4910
5077
|
};
|
|
4911
5078
|
};
|
|
4912
5079
|
FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
4913
|
-
setFsWatchFileListener = (
|
|
5080
|
+
setFsWatchFileListener = (path15, fullPath, options, handlers) => {
|
|
4914
5081
|
const { listener, rawEmitter } = handlers;
|
|
4915
5082
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
4916
5083
|
const copts = cont && cont.options;
|
|
@@ -4932,7 +5099,7 @@ var init_handler = __esm({
|
|
|
4932
5099
|
});
|
|
4933
5100
|
const currmtime = curr.mtimeMs;
|
|
4934
5101
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
4935
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
5102
|
+
foreach(cont.listeners, (listener2) => listener2(path15, curr));
|
|
4936
5103
|
}
|
|
4937
5104
|
})
|
|
4938
5105
|
};
|
|
@@ -4960,13 +5127,13 @@ var init_handler = __esm({
|
|
|
4960
5127
|
* @param listener on fs change
|
|
4961
5128
|
* @returns closer for the watcher instance
|
|
4962
5129
|
*/
|
|
4963
|
-
_watchWithNodeFs(
|
|
5130
|
+
_watchWithNodeFs(path15, listener) {
|
|
4964
5131
|
const opts = this.fsw.options;
|
|
4965
|
-
const directory = sysPath.dirname(
|
|
4966
|
-
const basename3 = sysPath.basename(
|
|
5132
|
+
const directory = sysPath.dirname(path15);
|
|
5133
|
+
const basename3 = sysPath.basename(path15);
|
|
4967
5134
|
const parent = this.fsw._getWatchedDir(directory);
|
|
4968
5135
|
parent.add(basename3);
|
|
4969
|
-
const absolutePath = sysPath.resolve(
|
|
5136
|
+
const absolutePath = sysPath.resolve(path15);
|
|
4970
5137
|
const options = {
|
|
4971
5138
|
persistent: opts.persistent
|
|
4972
5139
|
};
|
|
@@ -4976,12 +5143,12 @@ var init_handler = __esm({
|
|
|
4976
5143
|
if (opts.usePolling) {
|
|
4977
5144
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
4978
5145
|
options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
|
|
4979
|
-
closer = setFsWatchFileListener(
|
|
5146
|
+
closer = setFsWatchFileListener(path15, absolutePath, options, {
|
|
4980
5147
|
listener,
|
|
4981
5148
|
rawEmitter: this.fsw._emitRaw
|
|
4982
5149
|
});
|
|
4983
5150
|
} else {
|
|
4984
|
-
closer = setFsWatchListener(
|
|
5151
|
+
closer = setFsWatchListener(path15, absolutePath, options, {
|
|
4985
5152
|
listener,
|
|
4986
5153
|
errHandler: this._boundHandleError,
|
|
4987
5154
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -5003,7 +5170,7 @@ var init_handler = __esm({
|
|
|
5003
5170
|
let prevStats = stats;
|
|
5004
5171
|
if (parent.has(basename3))
|
|
5005
5172
|
return;
|
|
5006
|
-
const listener = async (
|
|
5173
|
+
const listener = async (path15, newStats) => {
|
|
5007
5174
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
5008
5175
|
return;
|
|
5009
5176
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -5017,11 +5184,11 @@ var init_handler = __esm({
|
|
|
5017
5184
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
5018
5185
|
}
|
|
5019
5186
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
5020
|
-
this.fsw._closeFile(
|
|
5187
|
+
this.fsw._closeFile(path15);
|
|
5021
5188
|
prevStats = newStats2;
|
|
5022
5189
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
5023
5190
|
if (closer2)
|
|
5024
|
-
this.fsw._addPathCloser(
|
|
5191
|
+
this.fsw._addPathCloser(path15, closer2);
|
|
5025
5192
|
} else {
|
|
5026
5193
|
prevStats = newStats2;
|
|
5027
5194
|
}
|
|
@@ -5053,7 +5220,7 @@ var init_handler = __esm({
|
|
|
5053
5220
|
* @param item basename of this item
|
|
5054
5221
|
* @returns true if no more processing is needed for this entry.
|
|
5055
5222
|
*/
|
|
5056
|
-
async _handleSymlink(entry, directory,
|
|
5223
|
+
async _handleSymlink(entry, directory, path15, item) {
|
|
5057
5224
|
if (this.fsw.closed) {
|
|
5058
5225
|
return;
|
|
5059
5226
|
}
|
|
@@ -5063,7 +5230,7 @@ var init_handler = __esm({
|
|
|
5063
5230
|
this.fsw._incrReadyCount();
|
|
5064
5231
|
let linkPath;
|
|
5065
5232
|
try {
|
|
5066
|
-
linkPath = await fsrealpath(
|
|
5233
|
+
linkPath = await fsrealpath(path15);
|
|
5067
5234
|
} catch (e) {
|
|
5068
5235
|
this.fsw._emitReady();
|
|
5069
5236
|
return true;
|
|
@@ -5073,12 +5240,12 @@ var init_handler = __esm({
|
|
|
5073
5240
|
if (dir.has(item)) {
|
|
5074
5241
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
5075
5242
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
5076
|
-
this.fsw._emit(EV.CHANGE,
|
|
5243
|
+
this.fsw._emit(EV.CHANGE, path15, entry.stats);
|
|
5077
5244
|
}
|
|
5078
5245
|
} else {
|
|
5079
5246
|
dir.add(item);
|
|
5080
5247
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
5081
|
-
this.fsw._emit(EV.ADD,
|
|
5248
|
+
this.fsw._emit(EV.ADD, path15, entry.stats);
|
|
5082
5249
|
}
|
|
5083
5250
|
this.fsw._emitReady();
|
|
5084
5251
|
return true;
|
|
@@ -5107,9 +5274,9 @@ var init_handler = __esm({
|
|
|
5107
5274
|
return;
|
|
5108
5275
|
}
|
|
5109
5276
|
const item = entry.path;
|
|
5110
|
-
let
|
|
5277
|
+
let path15 = sysPath.join(directory, item);
|
|
5111
5278
|
current.add(item);
|
|
5112
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
5279
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path15, item)) {
|
|
5113
5280
|
return;
|
|
5114
5281
|
}
|
|
5115
5282
|
if (this.fsw.closed) {
|
|
@@ -5118,8 +5285,8 @@ var init_handler = __esm({
|
|
|
5118
5285
|
}
|
|
5119
5286
|
if (item === target || !target && !previous.has(item)) {
|
|
5120
5287
|
this.fsw._incrReadyCount();
|
|
5121
|
-
|
|
5122
|
-
this._addToNodeFs(
|
|
5288
|
+
path15 = sysPath.join(dir, sysPath.relative(dir, path15));
|
|
5289
|
+
this._addToNodeFs(path15, initialAdd, wh, depth + 1);
|
|
5123
5290
|
}
|
|
5124
5291
|
}).on(EV.ERROR, this._boundHandleError);
|
|
5125
5292
|
return new Promise((resolve3, reject) => {
|
|
@@ -5188,13 +5355,13 @@ var init_handler = __esm({
|
|
|
5188
5355
|
* @param depth Child path actually targeted for watch
|
|
5189
5356
|
* @param target Child path actually targeted for watch
|
|
5190
5357
|
*/
|
|
5191
|
-
async _addToNodeFs(
|
|
5358
|
+
async _addToNodeFs(path15, initialAdd, priorWh, depth, target) {
|
|
5192
5359
|
const ready = this.fsw._emitReady;
|
|
5193
|
-
if (this.fsw._isIgnored(
|
|
5360
|
+
if (this.fsw._isIgnored(path15) || this.fsw.closed) {
|
|
5194
5361
|
ready();
|
|
5195
5362
|
return false;
|
|
5196
5363
|
}
|
|
5197
|
-
const wh = this.fsw._getWatchHelpers(
|
|
5364
|
+
const wh = this.fsw._getWatchHelpers(path15);
|
|
5198
5365
|
if (priorWh) {
|
|
5199
5366
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
5200
5367
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -5210,8 +5377,8 @@ var init_handler = __esm({
|
|
|
5210
5377
|
const follow = this.fsw.options.followSymlinks;
|
|
5211
5378
|
let closer;
|
|
5212
5379
|
if (stats.isDirectory()) {
|
|
5213
|
-
const absPath = sysPath.resolve(
|
|
5214
|
-
const targetPath = follow ? await fsrealpath(
|
|
5380
|
+
const absPath = sysPath.resolve(path15);
|
|
5381
|
+
const targetPath = follow ? await fsrealpath(path15) : path15;
|
|
5215
5382
|
if (this.fsw.closed)
|
|
5216
5383
|
return;
|
|
5217
5384
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -5221,29 +5388,29 @@ var init_handler = __esm({
|
|
|
5221
5388
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
5222
5389
|
}
|
|
5223
5390
|
} else if (stats.isSymbolicLink()) {
|
|
5224
|
-
const targetPath = follow ? await fsrealpath(
|
|
5391
|
+
const targetPath = follow ? await fsrealpath(path15) : path15;
|
|
5225
5392
|
if (this.fsw.closed)
|
|
5226
5393
|
return;
|
|
5227
5394
|
const parent = sysPath.dirname(wh.watchPath);
|
|
5228
5395
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
5229
5396
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
5230
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
5397
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path15, wh, targetPath);
|
|
5231
5398
|
if (this.fsw.closed)
|
|
5232
5399
|
return;
|
|
5233
5400
|
if (targetPath !== void 0) {
|
|
5234
|
-
this.fsw._symlinkPaths.set(sysPath.resolve(
|
|
5401
|
+
this.fsw._symlinkPaths.set(sysPath.resolve(path15), targetPath);
|
|
5235
5402
|
}
|
|
5236
5403
|
} else {
|
|
5237
5404
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
5238
5405
|
}
|
|
5239
5406
|
ready();
|
|
5240
5407
|
if (closer)
|
|
5241
|
-
this.fsw._addPathCloser(
|
|
5408
|
+
this.fsw._addPathCloser(path15, closer);
|
|
5242
5409
|
return false;
|
|
5243
5410
|
} catch (error) {
|
|
5244
5411
|
if (this.fsw._handleError(error)) {
|
|
5245
5412
|
ready();
|
|
5246
|
-
return
|
|
5413
|
+
return path15;
|
|
5247
5414
|
}
|
|
5248
5415
|
}
|
|
5249
5416
|
}
|
|
@@ -5282,26 +5449,26 @@ function createPattern(matcher) {
|
|
|
5282
5449
|
}
|
|
5283
5450
|
return () => false;
|
|
5284
5451
|
}
|
|
5285
|
-
function normalizePath2(
|
|
5286
|
-
if (typeof
|
|
5452
|
+
function normalizePath2(path15) {
|
|
5453
|
+
if (typeof path15 !== "string")
|
|
5287
5454
|
throw new Error("string expected");
|
|
5288
|
-
|
|
5289
|
-
|
|
5455
|
+
path15 = sysPath2.normalize(path15);
|
|
5456
|
+
path15 = path15.replace(/\\/g, "/");
|
|
5290
5457
|
let prepend = false;
|
|
5291
|
-
if (
|
|
5458
|
+
if (path15.startsWith("//"))
|
|
5292
5459
|
prepend = true;
|
|
5293
5460
|
const DOUBLE_SLASH_RE2 = /\/\//;
|
|
5294
|
-
while (
|
|
5295
|
-
|
|
5461
|
+
while (path15.match(DOUBLE_SLASH_RE2))
|
|
5462
|
+
path15 = path15.replace(DOUBLE_SLASH_RE2, "/");
|
|
5296
5463
|
if (prepend)
|
|
5297
|
-
|
|
5298
|
-
return
|
|
5464
|
+
path15 = "/" + path15;
|
|
5465
|
+
return path15;
|
|
5299
5466
|
}
|
|
5300
5467
|
function matchPatterns(patterns, testString, stats) {
|
|
5301
|
-
const
|
|
5468
|
+
const path15 = normalizePath2(testString);
|
|
5302
5469
|
for (let index = 0; index < patterns.length; index++) {
|
|
5303
5470
|
const pattern = patterns[index];
|
|
5304
|
-
if (pattern(
|
|
5471
|
+
if (pattern(path15, stats)) {
|
|
5305
5472
|
return true;
|
|
5306
5473
|
}
|
|
5307
5474
|
}
|
|
@@ -5362,19 +5529,19 @@ var init_esm2 = __esm({
|
|
|
5362
5529
|
}
|
|
5363
5530
|
return str;
|
|
5364
5531
|
};
|
|
5365
|
-
normalizePathToUnix = (
|
|
5366
|
-
normalizeIgnored = (cwd = "") => (
|
|
5367
|
-
if (typeof
|
|
5368
|
-
return normalizePathToUnix(sysPath2.isAbsolute(
|
|
5532
|
+
normalizePathToUnix = (path15) => toUnix(sysPath2.normalize(toUnix(path15)));
|
|
5533
|
+
normalizeIgnored = (cwd = "") => (path15) => {
|
|
5534
|
+
if (typeof path15 === "string") {
|
|
5535
|
+
return normalizePathToUnix(sysPath2.isAbsolute(path15) ? path15 : sysPath2.join(cwd, path15));
|
|
5369
5536
|
} else {
|
|
5370
|
-
return
|
|
5537
|
+
return path15;
|
|
5371
5538
|
}
|
|
5372
5539
|
};
|
|
5373
|
-
getAbsolutePath = (
|
|
5374
|
-
if (sysPath2.isAbsolute(
|
|
5375
|
-
return
|
|
5540
|
+
getAbsolutePath = (path15, cwd) => {
|
|
5541
|
+
if (sysPath2.isAbsolute(path15)) {
|
|
5542
|
+
return path15;
|
|
5376
5543
|
}
|
|
5377
|
-
return sysPath2.join(cwd,
|
|
5544
|
+
return sysPath2.join(cwd, path15);
|
|
5378
5545
|
};
|
|
5379
5546
|
EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
5380
5547
|
DirEntry = class {
|
|
@@ -5429,10 +5596,10 @@ var init_esm2 = __esm({
|
|
|
5429
5596
|
STAT_METHOD_F = "stat";
|
|
5430
5597
|
STAT_METHOD_L = "lstat";
|
|
5431
5598
|
WatchHelper = class {
|
|
5432
|
-
constructor(
|
|
5599
|
+
constructor(path15, follow, fsw) {
|
|
5433
5600
|
this.fsw = fsw;
|
|
5434
|
-
const watchPath =
|
|
5435
|
-
this.path =
|
|
5601
|
+
const watchPath = path15;
|
|
5602
|
+
this.path = path15 = path15.replace(REPLACER_RE, "");
|
|
5436
5603
|
this.watchPath = watchPath;
|
|
5437
5604
|
this.fullWatchPath = sysPath2.resolve(watchPath);
|
|
5438
5605
|
this.dirParts = [];
|
|
@@ -5554,20 +5721,20 @@ var init_esm2 = __esm({
|
|
|
5554
5721
|
this._closePromise = void 0;
|
|
5555
5722
|
let paths = unifyPaths(paths_);
|
|
5556
5723
|
if (cwd) {
|
|
5557
|
-
paths = paths.map((
|
|
5558
|
-
const absPath = getAbsolutePath(
|
|
5724
|
+
paths = paths.map((path15) => {
|
|
5725
|
+
const absPath = getAbsolutePath(path15, cwd);
|
|
5559
5726
|
return absPath;
|
|
5560
5727
|
});
|
|
5561
5728
|
}
|
|
5562
|
-
paths.forEach((
|
|
5563
|
-
this._removeIgnoredPath(
|
|
5729
|
+
paths.forEach((path15) => {
|
|
5730
|
+
this._removeIgnoredPath(path15);
|
|
5564
5731
|
});
|
|
5565
5732
|
this._userIgnored = void 0;
|
|
5566
5733
|
if (!this._readyCount)
|
|
5567
5734
|
this._readyCount = 0;
|
|
5568
5735
|
this._readyCount += paths.length;
|
|
5569
|
-
Promise.all(paths.map(async (
|
|
5570
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
5736
|
+
Promise.all(paths.map(async (path15) => {
|
|
5737
|
+
const res = await this._nodeFsHandler._addToNodeFs(path15, !_internal, void 0, 0, _origAdd);
|
|
5571
5738
|
if (res)
|
|
5572
5739
|
this._emitReady();
|
|
5573
5740
|
return res;
|
|
@@ -5589,17 +5756,17 @@ var init_esm2 = __esm({
|
|
|
5589
5756
|
return this;
|
|
5590
5757
|
const paths = unifyPaths(paths_);
|
|
5591
5758
|
const { cwd } = this.options;
|
|
5592
|
-
paths.forEach((
|
|
5593
|
-
if (!sysPath2.isAbsolute(
|
|
5759
|
+
paths.forEach((path15) => {
|
|
5760
|
+
if (!sysPath2.isAbsolute(path15) && !this._closers.has(path15)) {
|
|
5594
5761
|
if (cwd)
|
|
5595
|
-
|
|
5596
|
-
|
|
5762
|
+
path15 = sysPath2.join(cwd, path15);
|
|
5763
|
+
path15 = sysPath2.resolve(path15);
|
|
5597
5764
|
}
|
|
5598
|
-
this._closePath(
|
|
5599
|
-
this._addIgnoredPath(
|
|
5600
|
-
if (this._watched.has(
|
|
5765
|
+
this._closePath(path15);
|
|
5766
|
+
this._addIgnoredPath(path15);
|
|
5767
|
+
if (this._watched.has(path15)) {
|
|
5601
5768
|
this._addIgnoredPath({
|
|
5602
|
-
path:
|
|
5769
|
+
path: path15,
|
|
5603
5770
|
recursive: true
|
|
5604
5771
|
});
|
|
5605
5772
|
}
|
|
@@ -5663,38 +5830,38 @@ var init_esm2 = __esm({
|
|
|
5663
5830
|
* @param stats arguments to be passed with event
|
|
5664
5831
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
5665
5832
|
*/
|
|
5666
|
-
async _emit(event,
|
|
5833
|
+
async _emit(event, path15, stats) {
|
|
5667
5834
|
if (this.closed)
|
|
5668
5835
|
return;
|
|
5669
5836
|
const opts = this.options;
|
|
5670
5837
|
if (isWindows)
|
|
5671
|
-
|
|
5838
|
+
path15 = sysPath2.normalize(path15);
|
|
5672
5839
|
if (opts.cwd)
|
|
5673
|
-
|
|
5674
|
-
const args = [
|
|
5840
|
+
path15 = sysPath2.relative(opts.cwd, path15);
|
|
5841
|
+
const args = [path15];
|
|
5675
5842
|
if (stats != null)
|
|
5676
5843
|
args.push(stats);
|
|
5677
5844
|
const awf = opts.awaitWriteFinish;
|
|
5678
5845
|
let pw;
|
|
5679
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
5846
|
+
if (awf && (pw = this._pendingWrites.get(path15))) {
|
|
5680
5847
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
5681
5848
|
return this;
|
|
5682
5849
|
}
|
|
5683
5850
|
if (opts.atomic) {
|
|
5684
5851
|
if (event === EVENTS.UNLINK) {
|
|
5685
|
-
this._pendingUnlinks.set(
|
|
5852
|
+
this._pendingUnlinks.set(path15, [event, ...args]);
|
|
5686
5853
|
setTimeout(() => {
|
|
5687
|
-
this._pendingUnlinks.forEach((entry,
|
|
5854
|
+
this._pendingUnlinks.forEach((entry, path16) => {
|
|
5688
5855
|
this.emit(...entry);
|
|
5689
5856
|
this.emit(EVENTS.ALL, ...entry);
|
|
5690
|
-
this._pendingUnlinks.delete(
|
|
5857
|
+
this._pendingUnlinks.delete(path16);
|
|
5691
5858
|
});
|
|
5692
5859
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
5693
5860
|
return this;
|
|
5694
5861
|
}
|
|
5695
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
5862
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path15)) {
|
|
5696
5863
|
event = EVENTS.CHANGE;
|
|
5697
|
-
this._pendingUnlinks.delete(
|
|
5864
|
+
this._pendingUnlinks.delete(path15);
|
|
5698
5865
|
}
|
|
5699
5866
|
}
|
|
5700
5867
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -5712,16 +5879,16 @@ var init_esm2 = __esm({
|
|
|
5712
5879
|
this.emitWithAll(event, args);
|
|
5713
5880
|
}
|
|
5714
5881
|
};
|
|
5715
|
-
this._awaitWriteFinish(
|
|
5882
|
+
this._awaitWriteFinish(path15, awf.stabilityThreshold, event, awfEmit);
|
|
5716
5883
|
return this;
|
|
5717
5884
|
}
|
|
5718
5885
|
if (event === EVENTS.CHANGE) {
|
|
5719
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
5886
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path15, 50);
|
|
5720
5887
|
if (isThrottled)
|
|
5721
5888
|
return this;
|
|
5722
5889
|
}
|
|
5723
5890
|
if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
|
|
5724
|
-
const fullPath = opts.cwd ? sysPath2.join(opts.cwd,
|
|
5891
|
+
const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path15) : path15;
|
|
5725
5892
|
let stats2;
|
|
5726
5893
|
try {
|
|
5727
5894
|
stats2 = await stat3(fullPath);
|
|
@@ -5752,23 +5919,23 @@ var init_esm2 = __esm({
|
|
|
5752
5919
|
* @param timeout duration of time to suppress duplicate actions
|
|
5753
5920
|
* @returns tracking object or false if action should be suppressed
|
|
5754
5921
|
*/
|
|
5755
|
-
_throttle(actionType,
|
|
5922
|
+
_throttle(actionType, path15, timeout) {
|
|
5756
5923
|
if (!this._throttled.has(actionType)) {
|
|
5757
5924
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
5758
5925
|
}
|
|
5759
5926
|
const action = this._throttled.get(actionType);
|
|
5760
5927
|
if (!action)
|
|
5761
5928
|
throw new Error("invalid throttle");
|
|
5762
|
-
const actionPath = action.get(
|
|
5929
|
+
const actionPath = action.get(path15);
|
|
5763
5930
|
if (actionPath) {
|
|
5764
5931
|
actionPath.count++;
|
|
5765
5932
|
return false;
|
|
5766
5933
|
}
|
|
5767
5934
|
let timeoutObject;
|
|
5768
5935
|
const clear = () => {
|
|
5769
|
-
const item = action.get(
|
|
5936
|
+
const item = action.get(path15);
|
|
5770
5937
|
const count = item ? item.count : 0;
|
|
5771
|
-
action.delete(
|
|
5938
|
+
action.delete(path15);
|
|
5772
5939
|
clearTimeout(timeoutObject);
|
|
5773
5940
|
if (item)
|
|
5774
5941
|
clearTimeout(item.timeoutObject);
|
|
@@ -5776,7 +5943,7 @@ var init_esm2 = __esm({
|
|
|
5776
5943
|
};
|
|
5777
5944
|
timeoutObject = setTimeout(clear, timeout);
|
|
5778
5945
|
const thr = { timeoutObject, clear, count: 0 };
|
|
5779
|
-
action.set(
|
|
5946
|
+
action.set(path15, thr);
|
|
5780
5947
|
return thr;
|
|
5781
5948
|
}
|
|
5782
5949
|
_incrReadyCount() {
|
|
@@ -5790,44 +5957,44 @@ var init_esm2 = __esm({
|
|
|
5790
5957
|
* @param event
|
|
5791
5958
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
5792
5959
|
*/
|
|
5793
|
-
_awaitWriteFinish(
|
|
5960
|
+
_awaitWriteFinish(path15, threshold, event, awfEmit) {
|
|
5794
5961
|
const awf = this.options.awaitWriteFinish;
|
|
5795
5962
|
if (typeof awf !== "object")
|
|
5796
5963
|
return;
|
|
5797
5964
|
const pollInterval = awf.pollInterval;
|
|
5798
5965
|
let timeoutHandler;
|
|
5799
|
-
let fullPath =
|
|
5800
|
-
if (this.options.cwd && !sysPath2.isAbsolute(
|
|
5801
|
-
fullPath = sysPath2.join(this.options.cwd,
|
|
5966
|
+
let fullPath = path15;
|
|
5967
|
+
if (this.options.cwd && !sysPath2.isAbsolute(path15)) {
|
|
5968
|
+
fullPath = sysPath2.join(this.options.cwd, path15);
|
|
5802
5969
|
}
|
|
5803
5970
|
const now = /* @__PURE__ */ new Date();
|
|
5804
5971
|
const writes = this._pendingWrites;
|
|
5805
5972
|
function awaitWriteFinishFn(prevStat) {
|
|
5806
5973
|
statcb(fullPath, (err, curStat) => {
|
|
5807
|
-
if (err || !writes.has(
|
|
5974
|
+
if (err || !writes.has(path15)) {
|
|
5808
5975
|
if (err && err.code !== "ENOENT")
|
|
5809
5976
|
awfEmit(err);
|
|
5810
5977
|
return;
|
|
5811
5978
|
}
|
|
5812
5979
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
5813
5980
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
5814
|
-
writes.get(
|
|
5981
|
+
writes.get(path15).lastChange = now2;
|
|
5815
5982
|
}
|
|
5816
|
-
const pw = writes.get(
|
|
5983
|
+
const pw = writes.get(path15);
|
|
5817
5984
|
const df = now2 - pw.lastChange;
|
|
5818
5985
|
if (df >= threshold) {
|
|
5819
|
-
writes.delete(
|
|
5986
|
+
writes.delete(path15);
|
|
5820
5987
|
awfEmit(void 0, curStat);
|
|
5821
5988
|
} else {
|
|
5822
5989
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
5823
5990
|
}
|
|
5824
5991
|
});
|
|
5825
5992
|
}
|
|
5826
|
-
if (!writes.has(
|
|
5827
|
-
writes.set(
|
|
5993
|
+
if (!writes.has(path15)) {
|
|
5994
|
+
writes.set(path15, {
|
|
5828
5995
|
lastChange: now,
|
|
5829
5996
|
cancelWait: () => {
|
|
5830
|
-
writes.delete(
|
|
5997
|
+
writes.delete(path15);
|
|
5831
5998
|
clearTimeout(timeoutHandler);
|
|
5832
5999
|
return event;
|
|
5833
6000
|
}
|
|
@@ -5838,8 +6005,8 @@ var init_esm2 = __esm({
|
|
|
5838
6005
|
/**
|
|
5839
6006
|
* Determines whether user has asked to ignore this path.
|
|
5840
6007
|
*/
|
|
5841
|
-
_isIgnored(
|
|
5842
|
-
if (this.options.atomic && DOT_RE.test(
|
|
6008
|
+
_isIgnored(path15, stats) {
|
|
6009
|
+
if (this.options.atomic && DOT_RE.test(path15))
|
|
5843
6010
|
return true;
|
|
5844
6011
|
if (!this._userIgnored) {
|
|
5845
6012
|
const { cwd } = this.options;
|
|
@@ -5849,17 +6016,17 @@ var init_esm2 = __esm({
|
|
|
5849
6016
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
5850
6017
|
this._userIgnored = anymatch(list, void 0);
|
|
5851
6018
|
}
|
|
5852
|
-
return this._userIgnored(
|
|
6019
|
+
return this._userIgnored(path15, stats);
|
|
5853
6020
|
}
|
|
5854
|
-
_isntIgnored(
|
|
5855
|
-
return !this._isIgnored(
|
|
6021
|
+
_isntIgnored(path15, stat4) {
|
|
6022
|
+
return !this._isIgnored(path15, stat4);
|
|
5856
6023
|
}
|
|
5857
6024
|
/**
|
|
5858
6025
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
5859
6026
|
* @param path file or directory pattern being watched
|
|
5860
6027
|
*/
|
|
5861
|
-
_getWatchHelpers(
|
|
5862
|
-
return new WatchHelper(
|
|
6028
|
+
_getWatchHelpers(path15) {
|
|
6029
|
+
return new WatchHelper(path15, this.options.followSymlinks, this);
|
|
5863
6030
|
}
|
|
5864
6031
|
// Directory helpers
|
|
5865
6032
|
// -----------------
|
|
@@ -5891,63 +6058,63 @@ var init_esm2 = __esm({
|
|
|
5891
6058
|
* @param item base path of item/directory
|
|
5892
6059
|
*/
|
|
5893
6060
|
_remove(directory, item, isDirectory) {
|
|
5894
|
-
const
|
|
5895
|
-
const fullPath = sysPath2.resolve(
|
|
5896
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
5897
|
-
if (!this._throttle("remove",
|
|
6061
|
+
const path15 = sysPath2.join(directory, item);
|
|
6062
|
+
const fullPath = sysPath2.resolve(path15);
|
|
6063
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path15) || this._watched.has(fullPath);
|
|
6064
|
+
if (!this._throttle("remove", path15, 100))
|
|
5898
6065
|
return;
|
|
5899
6066
|
if (!isDirectory && this._watched.size === 1) {
|
|
5900
6067
|
this.add(directory, item, true);
|
|
5901
6068
|
}
|
|
5902
|
-
const wp = this._getWatchedDir(
|
|
6069
|
+
const wp = this._getWatchedDir(path15);
|
|
5903
6070
|
const nestedDirectoryChildren = wp.getChildren();
|
|
5904
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
6071
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path15, nested));
|
|
5905
6072
|
const parent = this._getWatchedDir(directory);
|
|
5906
6073
|
const wasTracked = parent.has(item);
|
|
5907
6074
|
parent.remove(item);
|
|
5908
6075
|
if (this._symlinkPaths.has(fullPath)) {
|
|
5909
6076
|
this._symlinkPaths.delete(fullPath);
|
|
5910
6077
|
}
|
|
5911
|
-
let relPath =
|
|
6078
|
+
let relPath = path15;
|
|
5912
6079
|
if (this.options.cwd)
|
|
5913
|
-
relPath = sysPath2.relative(this.options.cwd,
|
|
6080
|
+
relPath = sysPath2.relative(this.options.cwd, path15);
|
|
5914
6081
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
5915
6082
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
5916
6083
|
if (event === EVENTS.ADD)
|
|
5917
6084
|
return;
|
|
5918
6085
|
}
|
|
5919
|
-
this._watched.delete(
|
|
6086
|
+
this._watched.delete(path15);
|
|
5920
6087
|
this._watched.delete(fullPath);
|
|
5921
6088
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
5922
|
-
if (wasTracked && !this._isIgnored(
|
|
5923
|
-
this._emit(eventName,
|
|
5924
|
-
this._closePath(
|
|
6089
|
+
if (wasTracked && !this._isIgnored(path15))
|
|
6090
|
+
this._emit(eventName, path15);
|
|
6091
|
+
this._closePath(path15);
|
|
5925
6092
|
}
|
|
5926
6093
|
/**
|
|
5927
6094
|
* Closes all watchers for a path
|
|
5928
6095
|
*/
|
|
5929
|
-
_closePath(
|
|
5930
|
-
this._closeFile(
|
|
5931
|
-
const dir = sysPath2.dirname(
|
|
5932
|
-
this._getWatchedDir(dir).remove(sysPath2.basename(
|
|
6096
|
+
_closePath(path15) {
|
|
6097
|
+
this._closeFile(path15);
|
|
6098
|
+
const dir = sysPath2.dirname(path15);
|
|
6099
|
+
this._getWatchedDir(dir).remove(sysPath2.basename(path15));
|
|
5933
6100
|
}
|
|
5934
6101
|
/**
|
|
5935
6102
|
* Closes only file-specific watchers
|
|
5936
6103
|
*/
|
|
5937
|
-
_closeFile(
|
|
5938
|
-
const closers = this._closers.get(
|
|
6104
|
+
_closeFile(path15) {
|
|
6105
|
+
const closers = this._closers.get(path15);
|
|
5939
6106
|
if (!closers)
|
|
5940
6107
|
return;
|
|
5941
6108
|
closers.forEach((closer) => closer());
|
|
5942
|
-
this._closers.delete(
|
|
6109
|
+
this._closers.delete(path15);
|
|
5943
6110
|
}
|
|
5944
|
-
_addPathCloser(
|
|
6111
|
+
_addPathCloser(path15, closer) {
|
|
5945
6112
|
if (!closer)
|
|
5946
6113
|
return;
|
|
5947
|
-
let list = this._closers.get(
|
|
6114
|
+
let list = this._closers.get(path15);
|
|
5948
6115
|
if (!list) {
|
|
5949
6116
|
list = [];
|
|
5950
|
-
this._closers.set(
|
|
6117
|
+
this._closers.set(path15, list);
|
|
5951
6118
|
}
|
|
5952
6119
|
list.push(closer);
|
|
5953
6120
|
}
|
|
@@ -5974,23 +6141,36 @@ var init_esm2 = __esm({
|
|
|
5974
6141
|
});
|
|
5975
6142
|
|
|
5976
6143
|
// src/cli/watcher.ts
|
|
6144
|
+
import path12 from "path";
|
|
5977
6145
|
function startWatcher(options) {
|
|
5978
6146
|
const { rootDir, patterns, appDir } = options;
|
|
5979
6147
|
let rebuildTimer = null;
|
|
6148
|
+
let pendingFiles = /* @__PURE__ */ new Set();
|
|
5980
6149
|
async function rebuildRoutes() {
|
|
5981
6150
|
try {
|
|
5982
6151
|
const timestamp = Date.now();
|
|
5983
6152
|
globalThis.__FAAPI_LOAD_TS__ = timestamp;
|
|
5984
6153
|
invalidateMiddlewareCache();
|
|
5985
6154
|
invalidateProgramCache();
|
|
5986
|
-
const
|
|
6155
|
+
const filesToCompile = Array.from(pendingFiles);
|
|
6156
|
+
pendingFiles = /* @__PURE__ */ new Set();
|
|
6157
|
+
if (filesToCompile.length > 0) {
|
|
6158
|
+
await compileRoutes({
|
|
6159
|
+
rootDir,
|
|
6160
|
+
appDir,
|
|
6161
|
+
outDir: DEV_OUT_DIR,
|
|
6162
|
+
files: filesToCompile
|
|
6163
|
+
});
|
|
6164
|
+
}
|
|
6165
|
+
const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, appDir, DEV_OUT_DIR);
|
|
5987
6166
|
const sorted = sortRoutes(routes);
|
|
5988
|
-
const
|
|
5989
|
-
|
|
6167
|
+
const schemaPath = path12.resolve(rootDir, DEV_OUT_DIR, "faapi-schema.js");
|
|
6168
|
+
await generateSchemaFile(sorted, rootDir, schemaPath);
|
|
6169
|
+
await loadSchemaToRegistry(schemaPath, rootDir, DEV_OUT_DIR, false);
|
|
5990
6170
|
updateServerRoutes(sorted, wsRoutes);
|
|
5991
|
-
const
|
|
6171
|
+
const recompiledCount = filesToCompile.length;
|
|
5992
6172
|
console.log(
|
|
5993
|
-
`- Routes rebuilt: ${sorted.length} route(s), ${
|
|
6173
|
+
`- Routes rebuilt: ${sorted.length} route(s), ${wsRoutes.length} WS route(s)${recompiledCount > 0 ? `, ${recompiledCount} file(s) recompiled` : ""}`
|
|
5994
6174
|
);
|
|
5995
6175
|
} catch (err) {
|
|
5996
6176
|
console.error("- Error rebuilding routes:", err instanceof Error ? err.message : String(err));
|
|
@@ -6003,21 +6183,45 @@ function startWatcher(options) {
|
|
|
6003
6183
|
void rebuildRoutes();
|
|
6004
6184
|
}, 100);
|
|
6005
6185
|
}
|
|
6006
|
-
const
|
|
6007
|
-
const watcher = esm_default.watch(watchPatterns, {
|
|
6186
|
+
const watcher = esm_default.watch(appDir, {
|
|
6008
6187
|
cwd: rootDir,
|
|
6009
6188
|
ignoreInitial: true,
|
|
6010
|
-
ignored:
|
|
6189
|
+
ignored: (filePath, stats) => {
|
|
6190
|
+
if (filePath.includes("node_modules") || filePath.includes(".faapi") || filePath.includes("dist") || filePath.includes(".git")) {
|
|
6191
|
+
return true;
|
|
6192
|
+
}
|
|
6193
|
+
if (!stats) return false;
|
|
6194
|
+
if (stats.isDirectory()) return false;
|
|
6195
|
+
return !filePath.endsWith(".ts");
|
|
6196
|
+
}
|
|
6197
|
+
});
|
|
6198
|
+
watcher.on("add", (file) => {
|
|
6199
|
+
pendingFiles.add(path12.resolve(rootDir, file));
|
|
6200
|
+
scheduleRebuild();
|
|
6201
|
+
});
|
|
6202
|
+
watcher.on("change", (file) => {
|
|
6203
|
+
pendingFiles.add(path12.resolve(rootDir, file));
|
|
6204
|
+
scheduleRebuild();
|
|
6205
|
+
});
|
|
6206
|
+
watcher.on("unlink", () => {
|
|
6207
|
+
scheduleRebuild();
|
|
6208
|
+
});
|
|
6209
|
+
watcher.on("error", (err) => {
|
|
6210
|
+
console.error("- Watcher error:", err instanceof Error ? err.message : String(err));
|
|
6211
|
+
});
|
|
6212
|
+
watcher.on("ready", () => {
|
|
6213
|
+
const watched = watcher.getWatched();
|
|
6214
|
+
const dirCount = Object.keys(watched).length;
|
|
6215
|
+
const fileCount = Object.values(watched).reduce((sum, files) => sum + files.length, 0);
|
|
6216
|
+
console.log(`- Watcher ready: ${dirCount} dir(s), ${fileCount} file(s) watched`);
|
|
6011
6217
|
});
|
|
6012
|
-
watcher.on("add", () => scheduleRebuild());
|
|
6013
|
-
watcher.on("change", () => scheduleRebuild());
|
|
6014
|
-
watcher.on("unlink", () => scheduleRebuild());
|
|
6015
6218
|
console.log("- Watch mode enabled");
|
|
6016
6219
|
}
|
|
6017
6220
|
function updateServerRoutes(routes, wsRoutes) {
|
|
6018
6221
|
globalThis.__FAAPI_ROUTES__ = routes;
|
|
6019
6222
|
globalThis.__FAAPI_WS_ROUTES__ = wsRoutes;
|
|
6020
6223
|
}
|
|
6224
|
+
var DEV_OUT_DIR;
|
|
6021
6225
|
var init_watcher = __esm({
|
|
6022
6226
|
"src/cli/watcher.ts"() {
|
|
6023
6227
|
"use strict";
|
|
@@ -6026,14 +6230,15 @@ var init_watcher = __esm({
|
|
|
6026
6230
|
init_sortRoutes();
|
|
6027
6231
|
init_loadMiddlewares();
|
|
6028
6232
|
init_createProgram();
|
|
6029
|
-
init_schemaRegistry();
|
|
6030
6233
|
init_generateSchema();
|
|
6234
|
+
init_compileRoutes();
|
|
6235
|
+
DEV_OUT_DIR = ".faapi/dev";
|
|
6031
6236
|
}
|
|
6032
6237
|
});
|
|
6033
6238
|
|
|
6034
6239
|
// src/config/loadConfig.ts
|
|
6035
|
-
import
|
|
6036
|
-
import
|
|
6240
|
+
import path13 from "path";
|
|
6241
|
+
import fs8 from "fs";
|
|
6037
6242
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
6038
6243
|
function getEnv() {
|
|
6039
6244
|
return process.env.FAAPI_ENV || process.env.NODE_ENV || "development";
|
|
@@ -6059,7 +6264,7 @@ function deepMerge(base, override) {
|
|
|
6059
6264
|
return result;
|
|
6060
6265
|
}
|
|
6061
6266
|
async function loadConfigFile(filePath) {
|
|
6062
|
-
if (!
|
|
6267
|
+
if (!fs8.existsSync(filePath)) {
|
|
6063
6268
|
return null;
|
|
6064
6269
|
}
|
|
6065
6270
|
try {
|
|
@@ -6075,15 +6280,15 @@ async function loadConfigFile(filePath) {
|
|
|
6075
6280
|
}
|
|
6076
6281
|
async function loadConfig(rootDir, configPath) {
|
|
6077
6282
|
if (configPath) {
|
|
6078
|
-
const resolvedPath =
|
|
6079
|
-
if (!
|
|
6283
|
+
const resolvedPath = path13.resolve(rootDir, configPath);
|
|
6284
|
+
if (!fs8.existsSync(resolvedPath)) {
|
|
6080
6285
|
throw new Error(`Config file not found: ${configPath}`);
|
|
6081
6286
|
}
|
|
6082
6287
|
return loadConfigFile(resolvedPath);
|
|
6083
6288
|
}
|
|
6084
6289
|
let baseConfig = null;
|
|
6085
6290
|
for (const fileName of BASE_CONFIG_FILES) {
|
|
6086
|
-
const filePath =
|
|
6291
|
+
const filePath = path13.join(rootDir, fileName);
|
|
6087
6292
|
baseConfig = await loadConfigFile(filePath);
|
|
6088
6293
|
if (baseConfig) break;
|
|
6089
6294
|
}
|
|
@@ -6093,7 +6298,7 @@ async function loadConfig(rootDir, configPath) {
|
|
|
6093
6298
|
const env = getEnv();
|
|
6094
6299
|
const envFiles = [`faapi.config.${env}.ts`, `faapi.config.${env}.js`];
|
|
6095
6300
|
for (const envFile of envFiles) {
|
|
6096
|
-
const envConfig = await loadConfigFile(
|
|
6301
|
+
const envConfig = await loadConfigFile(path13.join(rootDir, envFile));
|
|
6097
6302
|
if (envConfig) {
|
|
6098
6303
|
baseConfig = deepMerge(baseConfig, envConfig);
|
|
6099
6304
|
break;
|
|
@@ -6178,17 +6383,17 @@ var startCommand_exports = {};
|
|
|
6178
6383
|
__export(startCommand_exports, {
|
|
6179
6384
|
startCommand: () => startCommand
|
|
6180
6385
|
});
|
|
6181
|
-
import
|
|
6182
|
-
import
|
|
6183
|
-
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
6386
|
+
import fs9 from "fs";
|
|
6387
|
+
import path14 from "path";
|
|
6184
6388
|
async function startCommand(argv) {
|
|
6185
6389
|
const args = parseArgs(argv);
|
|
6186
6390
|
const rootDir = process.cwd();
|
|
6187
6391
|
const isProd = args.mode === "start";
|
|
6392
|
+
const prodDir = isProd ? PROD_OUT_DIR : DEV_OUT_DIR2;
|
|
6188
6393
|
if (isProd) {
|
|
6189
|
-
const routesPath =
|
|
6190
|
-
const
|
|
6191
|
-
if (!
|
|
6394
|
+
const routesPath = path14.resolve(rootDir, PROD_OUT_DIR, "faapi-routes.js");
|
|
6395
|
+
const schemaPath2 = path14.resolve(rootDir, PROD_OUT_DIR, "faapi-schema.js");
|
|
6396
|
+
if (!fs9.existsSync(routesPath) || !fs9.existsSync(schemaPath2)) {
|
|
6192
6397
|
console.error(
|
|
6193
6398
|
"[faapi] dist/faapi-routes.js \u6216 dist/faapi-schema.js \u4E0D\u5B58\u5728\uFF0C\u8BF7\u5148\u6267\u884C `faapi build` \u6784\u5EFA\u751F\u4EA7\u4EA7\u7269\u3002"
|
|
6194
6399
|
);
|
|
@@ -6210,15 +6415,16 @@ async function startCommand(argv) {
|
|
|
6210
6415
|
let routes;
|
|
6211
6416
|
let wsRoutes;
|
|
6212
6417
|
if (isProd) {
|
|
6213
|
-
const routesPath =
|
|
6214
|
-
const serialized = await
|
|
6418
|
+
const routesPath = path14.resolve(rootDir, PROD_OUT_DIR, "faapi-routes.js");
|
|
6419
|
+
const serialized = await importWithCacheBust(routesPath);
|
|
6215
6420
|
const hydrated = await hydrateRoutes(serialized);
|
|
6216
6421
|
routes = hydrated.routes;
|
|
6217
6422
|
wsRoutes = hydrated.wsRoutes;
|
|
6218
6423
|
console.log(`- Routes loaded: ${routes.length} routes, ${wsRoutes.length} WS routes`);
|
|
6219
6424
|
} else {
|
|
6220
|
-
|
|
6221
|
-
|
|
6425
|
+
console.log("- Compiling TypeScript...");
|
|
6426
|
+
await compileRoutes({ rootDir, appDir: args.appDir, outDir: DEV_OUT_DIR2 });
|
|
6427
|
+
const scanned = await scanRoutes(rootDir, args.patterns, args.appDir, DEV_OUT_DIR2);
|
|
6222
6428
|
routes = scanned.routes;
|
|
6223
6429
|
wsRoutes = scanned.wsRoutes;
|
|
6224
6430
|
console.log(`- Routes scanned: ${routes.length} routes, ${wsRoutes.length} WS routes`);
|
|
@@ -6233,19 +6439,14 @@ async function startCommand(argv) {
|
|
|
6233
6439
|
}
|
|
6234
6440
|
}
|
|
6235
6441
|
}
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
const remapped = remapManifestKeys(manifest, rootDir);
|
|
6240
|
-
schemaRegistry.loadManifest(remapped);
|
|
6241
|
-
console.log(`- Schema loaded: ${schemaPath}`);
|
|
6242
|
-
} else {
|
|
6243
|
-
const manifest = extractSchemasForRoutes(sorted, rootDir);
|
|
6244
|
-
schemaRegistry.loadManifest(manifest);
|
|
6245
|
-
console.log(`- Schema extracted: ${manifest.size} file(s)`);
|
|
6442
|
+
const schemaPath = path14.resolve(rootDir, prodDir, "faapi-schema.js");
|
|
6443
|
+
if (!isProd) {
|
|
6444
|
+
await generateSchemaFile(sorted, rootDir, schemaPath);
|
|
6246
6445
|
}
|
|
6446
|
+
await loadSchemaToRegistry(schemaPath, rootDir, prodDir, isProd);
|
|
6447
|
+
console.log(`- Schema loaded: ${schemaPath}`);
|
|
6247
6448
|
if (!isProd && args.types) {
|
|
6248
|
-
const typesPath =
|
|
6449
|
+
const typesPath = path14.resolve(rootDir, args.types);
|
|
6249
6450
|
await generateTypes(sorted, rootDir, typesPath);
|
|
6250
6451
|
console.log(`- Types generated: ${typesPath}`);
|
|
6251
6452
|
}
|
|
@@ -6309,23 +6510,7 @@ async function startCommand(argv) {
|
|
|
6309
6510
|
function isFaapiConfigKey(key) {
|
|
6310
6511
|
return FAAPI_CONFIG_KEYS.has(key);
|
|
6311
6512
|
}
|
|
6312
|
-
|
|
6313
|
-
const remapped = /* @__PURE__ */ new Map();
|
|
6314
|
-
const rootPrefix = rootDir + path11.sep;
|
|
6315
|
-
for (const [filePath, fileSchemas] of manifest) {
|
|
6316
|
-
let rel = filePath;
|
|
6317
|
-
if (filePath.startsWith(rootPrefix)) {
|
|
6318
|
-
rel = filePath.slice(rootPrefix.length);
|
|
6319
|
-
} else if (filePath.startsWith(rootDir)) {
|
|
6320
|
-
rel = filePath.slice(rootDir.length).replace(/^[/\\]/, "");
|
|
6321
|
-
}
|
|
6322
|
-
const prodRel = `dist/${rel.replace(/\.ts$/, ".js")}`;
|
|
6323
|
-
const prodAbs = path11.resolve(rootDir, prodRel);
|
|
6324
|
-
remapped.set(prodAbs, fileSchemas);
|
|
6325
|
-
}
|
|
6326
|
-
return remapped;
|
|
6327
|
-
}
|
|
6328
|
-
var FAAPI_CONFIG_KEYS;
|
|
6513
|
+
var DEV_OUT_DIR2, PROD_OUT_DIR, FAAPI_CONFIG_KEYS;
|
|
6329
6514
|
var init_startCommand = __esm({
|
|
6330
6515
|
"src/cli/startCommand.ts"() {
|
|
6331
6516
|
"use strict";
|
|
@@ -6337,10 +6522,13 @@ var init_startCommand = __esm({
|
|
|
6337
6522
|
init_generateTypes();
|
|
6338
6523
|
init_watcher();
|
|
6339
6524
|
init_loadConfig();
|
|
6340
|
-
init_schemaRegistry();
|
|
6341
6525
|
init_generateSchema();
|
|
6342
6526
|
init_generateRoutes();
|
|
6527
|
+
init_compileRoutes();
|
|
6343
6528
|
init_loadPlugins();
|
|
6529
|
+
init_importWithCacheBust();
|
|
6530
|
+
DEV_OUT_DIR2 = ".faapi/dev";
|
|
6531
|
+
PROD_OUT_DIR = "dist";
|
|
6344
6532
|
FAAPI_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
6345
6533
|
"port",
|
|
6346
6534
|
"staticDir",
|
|
@@ -6360,25 +6548,32 @@ var init_startCommand = __esm({
|
|
|
6360
6548
|
function isTsxPreloaded() {
|
|
6361
6549
|
return process.execArgv.some((arg) => arg.includes("tsx")) || (process.env.NODE_OPTIONS ?? "").includes("tsx");
|
|
6362
6550
|
}
|
|
6551
|
+
async function ensureTsx() {
|
|
6552
|
+
if (isTsxPreloaded()) return true;
|
|
6553
|
+
try {
|
|
6554
|
+
const { register } = await import("tsx/esm/api");
|
|
6555
|
+
register();
|
|
6556
|
+
return true;
|
|
6557
|
+
} catch {
|
|
6558
|
+
console.warn(
|
|
6559
|
+
"[faapi] tsx \u672A\u5B89\u88C5\uFF0C\u65E0\u6CD5\u52A0\u8F7D faapi.config.ts\u3002\u8BF7\u5B89\u88C5 tsx \u6216\u4F7F\u7528 faapi.config.js\u3002"
|
|
6560
|
+
);
|
|
6561
|
+
return false;
|
|
6562
|
+
}
|
|
6563
|
+
}
|
|
6363
6564
|
async function main() {
|
|
6364
6565
|
const argv = process.argv.slice(2);
|
|
6365
6566
|
const firstArg = argv.find((a) => !a.startsWith("-"));
|
|
6567
|
+
const isBuildMode = firstArg === "build";
|
|
6568
|
+
if (!isBuildMode) {
|
|
6569
|
+
await ensureTsx();
|
|
6570
|
+
}
|
|
6366
6571
|
if (firstArg === "build") {
|
|
6367
6572
|
const { parseBuildArgs: parseBuildArgs2, buildCommand: buildCommand2 } = await Promise.resolve().then(() => (init_buildCommand(), buildCommand_exports));
|
|
6368
6573
|
const options = parseBuildArgs2(argv.filter((a) => a !== "build"));
|
|
6369
6574
|
await buildCommand2(options);
|
|
6370
6575
|
return;
|
|
6371
6576
|
}
|
|
6372
|
-
const isStartMode = firstArg === "start";
|
|
6373
|
-
if (!isStartMode && !isTsxPreloaded()) {
|
|
6374
|
-
try {
|
|
6375
|
-
const { register } = await import("tsx/esm/api");
|
|
6376
|
-
register();
|
|
6377
|
-
} catch (err) {
|
|
6378
|
-
console.error("[faapi] tsx \u52A0\u8F7D\u5931\u8D25\uFF0C\u8BF7\u786E\u8BA4\u5DF2\u5B89\u88C5 tsx\uFF1A", err);
|
|
6379
|
-
process.exit(1);
|
|
6380
|
-
}
|
|
6381
|
-
}
|
|
6382
6577
|
const { startCommand: startCommand2 } = await Promise.resolve().then(() => (init_startCommand(), startCommand_exports));
|
|
6383
6578
|
await startCommand2(argv);
|
|
6384
6579
|
}
|