@faapi/faapi 0.0.0-canary.c6dc0f7 → 0.0.0-canary.ca5abac
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 +644 -496
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +45 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
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,
|
|
@@ -969,32 +984,6 @@ function runtimeTypeToExpected(type) {
|
|
|
969
984
|
return type.name;
|
|
970
985
|
}
|
|
971
986
|
}
|
|
972
|
-
function generateValidatorSource(typeInfo, resolveType) {
|
|
973
|
-
const ctx = new CodeGenContext(resolveType);
|
|
974
|
-
collectNamedTypes(typeInfo.runtimeType, typeInfo.name, ctx);
|
|
975
|
-
const resetVisited = Array.from(ctx.namedTypes.keys()).map((name) => `validate_${name}.__visited = new WeakSet();`).join("\n ");
|
|
976
|
-
const entryBody = generateObjectValidation(typeInfo.runtimeType, "input", "issues", ctx, "''");
|
|
977
|
-
const entryFn = `function validate(input) {
|
|
978
|
-
const issues = [];
|
|
979
|
-
${resetVisited}
|
|
980
|
-
${entryBody}
|
|
981
|
-
return { valid: issues.length === 0, issues, data: (typeof input === 'object' && input !== null && !Array.isArray(input)) ? input : {} };
|
|
982
|
-
}`;
|
|
983
|
-
const namedFns = Array.from(ctx.namedTypes.entries()).map(([name, type]) => {
|
|
984
|
-
const body = generateObjectValidation(type, "value", "issues", ctx, "path");
|
|
985
|
-
return `function validate_${name}(value, path, issues) {
|
|
986
|
-
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
987
|
-
issues.push({ path, code: 'TYPE_MISMATCH', expected: 'object', received: typeof value, message: '\u671F\u671B\u5BF9\u8C61' });
|
|
988
|
-
return;
|
|
989
|
-
}
|
|
990
|
-
if (!validate_${name}.__visited) validate_${name}.__visited = new WeakSet();
|
|
991
|
-
if (validate_${name}.__visited.has(value)) return;
|
|
992
|
-
validate_${name}.__visited.add(value);
|
|
993
|
-
${body}
|
|
994
|
-
}`;
|
|
995
|
-
});
|
|
996
|
-
return [...namedFns, entryFn].join("\n\n");
|
|
997
|
-
}
|
|
998
987
|
function generateSchemaModule(entries, resolveType) {
|
|
999
988
|
const ctx = new CodeGenContext(resolveType);
|
|
1000
989
|
const validatorEntries = [];
|
|
@@ -1122,35 +1111,35 @@ function generateObjectValidation(type, varName, issuesVar, ctx, pathExpr) {
|
|
|
1122
1111
|
return lines.join("\n");
|
|
1123
1112
|
}
|
|
1124
1113
|
function generateValueValidation(type, varName, issuesVar, ctx, pathExpr) {
|
|
1125
|
-
const
|
|
1114
|
+
const path15 = pathExpr ?? "''";
|
|
1126
1115
|
switch (type.kind) {
|
|
1127
1116
|
case "any":
|
|
1128
1117
|
case "unknown":
|
|
1129
1118
|
return "";
|
|
1130
1119
|
// 不校验
|
|
1131
1120
|
case "string":
|
|
1132
|
-
return `if (typeof ${varName} !== 'string') ${issuesVar}.push({ path: ${
|
|
1121
|
+
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
1122
|
case "number":
|
|
1134
|
-
return `if (typeof ${varName} !== 'number' || Number.isNaN(${varName})) ${issuesVar}.push({ path: ${
|
|
1123
|
+
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
1124
|
case "boolean":
|
|
1136
|
-
return `if (typeof ${varName} !== 'boolean') ${issuesVar}.push({ path: ${
|
|
1125
|
+
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
1126
|
case "bigint":
|
|
1138
|
-
return `if (typeof ${varName} !== 'bigint') ${issuesVar}.push({ path: ${
|
|
1127
|
+
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
1128
|
case "null":
|
|
1140
|
-
return `if (${varName} !== null) ${issuesVar}.push({ path: ${
|
|
1129
|
+
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
1130
|
case "undefined":
|
|
1142
|
-
return `if (${varName} !== undefined) ${issuesVar}.push({ path: ${
|
|
1131
|
+
return `if (${varName} !== undefined) ${issuesVar}.push({ path: ${path15}, code: 'TYPE_MISMATCH', expected: 'undefined', received: typeof ${varName}, message: '\u671F\u671B undefined' });`;
|
|
1143
1132
|
case "literal":
|
|
1144
|
-
return `if (${varName} !== ${JSON.stringify(type.value)}) ${issuesVar}.push({ path: ${
|
|
1133
|
+
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
1134
|
case "date":
|
|
1146
1135
|
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: ${
|
|
1136
|
+
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} });
|
|
1137
|
+
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
1138
|
case "array": {
|
|
1150
1139
|
const id = ctx.nextVarId();
|
|
1151
1140
|
const itemVar = `item${id}`;
|
|
1152
1141
|
const indexVar = `i${id}`;
|
|
1153
|
-
const elemPath = `${
|
|
1142
|
+
const elemPath = `${path15} + '[' + ${indexVar} + ']'`;
|
|
1154
1143
|
const elemValidation = generateValueValidation(
|
|
1155
1144
|
type.element,
|
|
1156
1145
|
itemVar,
|
|
@@ -1158,7 +1147,7 @@ else if (!/^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,3})?(Z|[+-]\\d{2
|
|
|
1158
1147
|
ctx,
|
|
1159
1148
|
elemPath
|
|
1160
1149
|
);
|
|
1161
|
-
return `if (!Array.isArray(${varName})) ${issuesVar}.push({ path: ${
|
|
1150
|
+
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
1151
|
else for (let ${indexVar} = 0; ${indexVar} < ${varName}.length; ${indexVar}++) { const ${itemVar} = ${varName}[${indexVar}]; ${elemValidation} }`;
|
|
1163
1152
|
}
|
|
1164
1153
|
case "tuple": {
|
|
@@ -1170,21 +1159,21 @@ else for (let ${indexVar} = 0; ${indexVar} < ${varName}.length; ${indexVar}++) {
|
|
|
1170
1159
|
const indexVar = `ti${id}`;
|
|
1171
1160
|
const lines = [];
|
|
1172
1161
|
lines.push(
|
|
1173
|
-
`if (!Array.isArray(${varName})) ${issuesVar}.push({ path: ${
|
|
1162
|
+
`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
1163
|
);
|
|
1175
1164
|
lines.push(`else {`);
|
|
1176
1165
|
lines.push(
|
|
1177
|
-
`if (${varName}.length < ${minRequired}) ${issuesVar}.push({ path: ${
|
|
1166
|
+
`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
1167
|
);
|
|
1179
1168
|
if (!restElement) {
|
|
1180
1169
|
lines.push(
|
|
1181
|
-
`if (${varName}.length > ${fixedCount}) ${issuesVar}.push({ path: ${
|
|
1170
|
+
`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
1171
|
);
|
|
1183
1172
|
}
|
|
1184
1173
|
for (let i = 0; i < type.elements.length; i++) {
|
|
1185
1174
|
const elem = type.elements[i];
|
|
1186
1175
|
if (elem.rest) continue;
|
|
1187
|
-
const elemPath = `${
|
|
1176
|
+
const elemPath = `${path15} + '[' + ${i} + ']'`;
|
|
1188
1177
|
const elemAccess = `${varName}[${i}]`;
|
|
1189
1178
|
if (elem.optional) {
|
|
1190
1179
|
lines.push(`if (${i} < ${varName}.length && ${elemAccess} !== undefined) {`);
|
|
@@ -1195,7 +1184,7 @@ else for (let ${indexVar} = 0; ${indexVar} < ${varName}.length; ${indexVar}++) {
|
|
|
1195
1184
|
lines.push(`}`);
|
|
1196
1185
|
}
|
|
1197
1186
|
if (restElement) {
|
|
1198
|
-
const restPath = `${
|
|
1187
|
+
const restPath = `${path15} + '[' + ${indexVar} + ']'`;
|
|
1199
1188
|
const restValidation = generateValueValidation(
|
|
1200
1189
|
restElement.type,
|
|
1201
1190
|
itemVar,
|
|
@@ -1213,23 +1202,23 @@ else for (let ${indexVar} = 0; ${indexVar} < ${varName}.length; ${indexVar}++) {
|
|
|
1213
1202
|
case "union": {
|
|
1214
1203
|
const tempVar = `tempIssues_${Math.random().toString(36).slice(2, 8)}`;
|
|
1215
1204
|
const memberChecks = type.members.map((member) => {
|
|
1216
|
-
const check = generateValueValidation(member, varName, tempVar, ctx,
|
|
1205
|
+
const check = generateValueValidation(member, varName, tempVar, ctx, path15);
|
|
1217
1206
|
return `(() => { const ${tempVar} = []; ${check}; return ${tempVar}.length === 0; })()`;
|
|
1218
1207
|
});
|
|
1219
1208
|
const expected = runtimeTypeToExpected(type);
|
|
1220
|
-
return `if (!(${memberChecks.join(" || ")})) ${issuesVar}.push({ path: ${
|
|
1209
|
+
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
1210
|
}
|
|
1222
1211
|
case "record": {
|
|
1223
|
-
const valuePath = `${
|
|
1212
|
+
const valuePath = `${path15} + '.' + key`;
|
|
1224
1213
|
const valueValidation = generateValueValidation(type.value, "val", issuesVar, ctx, valuePath);
|
|
1225
|
-
return `if (typeof ${varName} !== 'object' || ${varName} === null || Array.isArray(${varName})) ${issuesVar}.push({ path: ${
|
|
1214
|
+
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
1215
|
else for (const [key, val] of Object.entries(${varName})) { ${valueValidation} }`;
|
|
1227
1216
|
}
|
|
1228
1217
|
case "object": {
|
|
1229
|
-
return generateInlineObjectValidation(type, varName, issuesVar, ctx,
|
|
1218
|
+
return generateInlineObjectValidation(type, varName, issuesVar, ctx, path15);
|
|
1230
1219
|
}
|
|
1231
1220
|
case "ref": {
|
|
1232
|
-
return `validate_${type.name}(${varName}, ${
|
|
1221
|
+
return `validate_${type.name}(${varName}, ${path15}, ${issuesVar});`;
|
|
1233
1222
|
}
|
|
1234
1223
|
}
|
|
1235
1224
|
}
|
|
@@ -1289,6 +1278,75 @@ var init_generateValidatorCode = __esm({
|
|
|
1289
1278
|
}
|
|
1290
1279
|
});
|
|
1291
1280
|
|
|
1281
|
+
// src/validator/schemaRegistry.ts
|
|
1282
|
+
var SchemaRegistry, schemaRegistry;
|
|
1283
|
+
var init_schemaRegistry = __esm({
|
|
1284
|
+
"src/validator/schemaRegistry.ts"() {
|
|
1285
|
+
"use strict";
|
|
1286
|
+
SchemaRegistry = class {
|
|
1287
|
+
manifest = /* @__PURE__ */ new Map();
|
|
1288
|
+
/**
|
|
1289
|
+
* 批量加载 manifest
|
|
1290
|
+
* 覆盖已有数据
|
|
1291
|
+
*/
|
|
1292
|
+
loadManifest(manifest) {
|
|
1293
|
+
this.manifest.clear();
|
|
1294
|
+
for (const [filePath, fileSchemas] of manifest) {
|
|
1295
|
+
const copy = /* @__PURE__ */ new Map();
|
|
1296
|
+
fileSchemas.forEach((value, key) => copy.set(key, value));
|
|
1297
|
+
this.manifest.set(filePath, copy);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* 查询单条 schema
|
|
1302
|
+
* @returns SchemaEntry | null | undefined
|
|
1303
|
+
* - SchemaEntry:有类型声明
|
|
1304
|
+
* - null:无类型声明(跳过校验)
|
|
1305
|
+
* - undefined:manifest 不完整(抛错)
|
|
1306
|
+
*/
|
|
1307
|
+
get(filePath, schemaName) {
|
|
1308
|
+
const fileSchemas = this.manifest.get(filePath);
|
|
1309
|
+
if (!fileSchemas) return void 0;
|
|
1310
|
+
return fileSchemas.get(schemaName);
|
|
1311
|
+
}
|
|
1312
|
+
/**
|
|
1313
|
+
* 设置单个文件的所有 schema
|
|
1314
|
+
* 覆盖该文件的已有数据
|
|
1315
|
+
*/
|
|
1316
|
+
set(filePath, schemas) {
|
|
1317
|
+
const copy = /* @__PURE__ */ new Map();
|
|
1318
|
+
schemas.forEach((value, key) => copy.set(key, value));
|
|
1319
|
+
this.manifest.set(filePath, copy);
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* 删除单个文件(文件被删除时)
|
|
1323
|
+
*/
|
|
1324
|
+
delete(filePath) {
|
|
1325
|
+
this.manifest.delete(filePath);
|
|
1326
|
+
}
|
|
1327
|
+
/**
|
|
1328
|
+
* 判断文件是否已注册
|
|
1329
|
+
*/
|
|
1330
|
+
hasFile(filePath) {
|
|
1331
|
+
return this.manifest.has(filePath);
|
|
1332
|
+
}
|
|
1333
|
+
/**
|
|
1334
|
+
* 清空(测试用 / watch 全量重建前)
|
|
1335
|
+
*/
|
|
1336
|
+
clear() {
|
|
1337
|
+
this.manifest.clear();
|
|
1338
|
+
}
|
|
1339
|
+
/**
|
|
1340
|
+
* 已注册的文件数量
|
|
1341
|
+
*/
|
|
1342
|
+
get size() {
|
|
1343
|
+
return this.manifest.size;
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
schemaRegistry = new SchemaRegistry();
|
|
1347
|
+
}
|
|
1348
|
+
});
|
|
1349
|
+
|
|
1292
1350
|
// src/ast/createProgram.ts
|
|
1293
1351
|
import ts4 from "typescript";
|
|
1294
1352
|
function invalidateProgramCache() {
|
|
@@ -1487,29 +1545,6 @@ var init_collectRouteSchemaSources = __esm({
|
|
|
1487
1545
|
// src/cli/generateSchema.ts
|
|
1488
1546
|
import fs3 from "fs/promises";
|
|
1489
1547
|
import path4 from "path";
|
|
1490
|
-
function extractSchemasForRoutes(routes, rootDir) {
|
|
1491
|
-
const { sources, mergedAllTypes } = collectRouteSchemaSources(routes, rootDir);
|
|
1492
|
-
const manifest = /* @__PURE__ */ new Map();
|
|
1493
|
-
for (const { filePath, schemaName, typeInfo } of sources) {
|
|
1494
|
-
let fileSchemas = manifest.get(filePath);
|
|
1495
|
-
if (!fileSchemas) {
|
|
1496
|
-
fileSchemas = /* @__PURE__ */ new Map();
|
|
1497
|
-
manifest.set(filePath, fileSchemas);
|
|
1498
|
-
}
|
|
1499
|
-
fileSchemas.set(schemaName, typeInfoToSchemaEntry(typeInfo, mergedAllTypes));
|
|
1500
|
-
}
|
|
1501
|
-
return manifest;
|
|
1502
|
-
}
|
|
1503
|
-
function typeInfoToSchemaEntry(typeInfo, allTypes) {
|
|
1504
|
-
if (typeInfo === null) return null;
|
|
1505
|
-
const source = generateValidatorSource(typeInfo, (name) => allTypes.get(name)?.runtimeType);
|
|
1506
|
-
const validator = new Function("input", `${source}
|
|
1507
|
-
return validate(input);`);
|
|
1508
|
-
return {
|
|
1509
|
-
properties: typeInfo.properties,
|
|
1510
|
-
validator
|
|
1511
|
-
};
|
|
1512
|
-
}
|
|
1513
1548
|
async function writeSchemaModule(entries, allTypesMap, outputPath) {
|
|
1514
1549
|
const allTypes = /* @__PURE__ */ new Map();
|
|
1515
1550
|
for (const types of allTypesMap.values()) {
|
|
@@ -1521,9 +1556,17 @@ async function writeSchemaModule(entries, allTypesMap, outputPath) {
|
|
|
1521
1556
|
await fs3.mkdir(path4.dirname(outputPath), { recursive: true });
|
|
1522
1557
|
await fs3.writeFile(outputPath, source, "utf-8");
|
|
1523
1558
|
}
|
|
1559
|
+
async function generateSchemaFile(routes, rootDir, outputPath) {
|
|
1560
|
+
const { sources, allTypesByFile } = collectRouteSchemaSources(routes, rootDir);
|
|
1561
|
+
const entries = sources.map(({ filePath, schemaName, typeInfo }) => ({
|
|
1562
|
+
filePath,
|
|
1563
|
+
schemaName,
|
|
1564
|
+
typeInfo
|
|
1565
|
+
}));
|
|
1566
|
+
await writeSchemaModule(entries, allTypesByFile, outputPath);
|
|
1567
|
+
}
|
|
1524
1568
|
async function readManifestFile(inputPath) {
|
|
1525
|
-
const
|
|
1526
|
-
const mod = await import(fileUrl);
|
|
1569
|
+
const mod = await importWithCacheBust(inputPath);
|
|
1527
1570
|
const validators = mod.validators;
|
|
1528
1571
|
const properties = mod.properties ?? {};
|
|
1529
1572
|
const manifest = /* @__PURE__ */ new Map();
|
|
@@ -1546,27 +1589,51 @@ async function readManifestFile(inputPath) {
|
|
|
1546
1589
|
}
|
|
1547
1590
|
return manifest;
|
|
1548
1591
|
}
|
|
1592
|
+
function remapManifestKeys(manifest, rootDir, prodDir) {
|
|
1593
|
+
const remapped = /* @__PURE__ */ new Map();
|
|
1594
|
+
const rootPrefix = rootDir + path4.sep;
|
|
1595
|
+
for (const [filePath, fileSchemas] of manifest) {
|
|
1596
|
+
let rel = filePath;
|
|
1597
|
+
if (filePath.startsWith(rootPrefix)) {
|
|
1598
|
+
rel = filePath.slice(rootPrefix.length);
|
|
1599
|
+
} else if (filePath.startsWith(rootDir)) {
|
|
1600
|
+
rel = filePath.slice(rootDir.length).replace(/^[/\\]/, "");
|
|
1601
|
+
}
|
|
1602
|
+
const prodRel = `${prodDir}/${rel.replace(/\.ts$/, ".js")}`;
|
|
1603
|
+
const prodAbs = path4.resolve(rootDir, prodRel);
|
|
1604
|
+
remapped.set(prodAbs, fileSchemas);
|
|
1605
|
+
}
|
|
1606
|
+
return remapped;
|
|
1607
|
+
}
|
|
1608
|
+
async function loadSchemaToRegistry(schemaPath, rootDir, prodDir, remap = true) {
|
|
1609
|
+
const manifest = await readManifestFile(schemaPath);
|
|
1610
|
+
const finalManifest = remap ? remapManifestKeys(manifest, rootDir, prodDir) : manifest;
|
|
1611
|
+
schemaRegistry.loadManifest(finalManifest);
|
|
1612
|
+
return finalManifest;
|
|
1613
|
+
}
|
|
1549
1614
|
var init_generateSchema = __esm({
|
|
1550
1615
|
"src/cli/generateSchema.ts"() {
|
|
1551
1616
|
"use strict";
|
|
1552
1617
|
init_generateValidatorCode();
|
|
1618
|
+
init_schemaRegistry();
|
|
1553
1619
|
init_collectRouteSchemaSources();
|
|
1620
|
+
init_importWithCacheBust();
|
|
1554
1621
|
}
|
|
1555
1622
|
});
|
|
1556
1623
|
|
|
1557
1624
|
// src/cli/generateRoutes.ts
|
|
1558
1625
|
import fs4 from "fs";
|
|
1559
1626
|
import path5 from "path";
|
|
1560
|
-
function toProdFilePath(filePath) {
|
|
1627
|
+
function toProdFilePath(filePath, prodDir) {
|
|
1561
1628
|
const jsPath = filePath.replace(/\.ts$/, ".js");
|
|
1562
|
-
return jsPath.startsWith(
|
|
1629
|
+
return jsPath.startsWith(`${prodDir}/`) ? jsPath : `${prodDir}/${jsPath}`;
|
|
1563
1630
|
}
|
|
1564
|
-
function serializeRoutes(routes, wsRoutes, rootDir) {
|
|
1631
|
+
function serializeRoutes(routes, wsRoutes, rootDir, prodDir = "dist") {
|
|
1565
1632
|
const serialize = (route) => {
|
|
1566
|
-
const middlewarePaths = extractMiddlewarePaths(route.filePath, rootDir);
|
|
1633
|
+
const middlewarePaths = extractMiddlewarePaths(route.filePath, rootDir, prodDir);
|
|
1567
1634
|
const serialized = {
|
|
1568
1635
|
urlPath: route.urlPath,
|
|
1569
|
-
filePath: toProdFilePath(route.filePath),
|
|
1636
|
+
filePath: toProdFilePath(route.filePath, prodDir),
|
|
1570
1637
|
paramNames: route.paramNames,
|
|
1571
1638
|
isDynamic: route.isDynamic,
|
|
1572
1639
|
isCatchAll: route.isCatchAll,
|
|
@@ -1582,21 +1649,21 @@ function serializeRoutes(routes, wsRoutes, rootDir) {
|
|
|
1582
1649
|
wsRoutes: wsRoutes.map(serialize)
|
|
1583
1650
|
};
|
|
1584
1651
|
}
|
|
1585
|
-
function extractMiddlewarePaths(routeFilePath, rootDir) {
|
|
1652
|
+
function extractMiddlewarePaths(routeFilePath, rootDir, prodDir) {
|
|
1586
1653
|
const routeDir = path5.dirname(routeFilePath);
|
|
1587
1654
|
const resolvedRoot = path5.resolve(rootDir);
|
|
1588
1655
|
const paths = [];
|
|
1589
1656
|
let currentDir = path5.resolve(rootDir, routeDir);
|
|
1590
1657
|
while (true) {
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1658
|
+
const mwTsPath = path5.join(currentDir, "middlewares.ts");
|
|
1659
|
+
const mwJsPath = path5.join(currentDir, "middlewares.js");
|
|
1660
|
+
const absTsPath = path5.resolve(rootDir, mwTsPath);
|
|
1661
|
+
const absJsPath = path5.resolve(rootDir, mwJsPath);
|
|
1662
|
+
const absMwPath = fs4.existsSync(absTsPath) ? absTsPath : fs4.existsSync(absJsPath) ? absJsPath : null;
|
|
1663
|
+
if (absMwPath) {
|
|
1664
|
+
const relMwPath = path5.relative(rootDir, absMwPath);
|
|
1665
|
+
const prodAbsPath = path5.resolve(rootDir, toProdFilePath(relMwPath, prodDir));
|
|
1666
|
+
paths.push(prodAbsPath);
|
|
1600
1667
|
}
|
|
1601
1668
|
if (currentDir === resolvedRoot) break;
|
|
1602
1669
|
const parentDir = path5.dirname(currentDir);
|
|
@@ -1669,23 +1736,184 @@ var init_generateRoutes = __esm({
|
|
|
1669
1736
|
}
|
|
1670
1737
|
});
|
|
1671
1738
|
|
|
1739
|
+
// src/utils/readTsconfig.ts
|
|
1740
|
+
import ts6 from "typescript";
|
|
1741
|
+
import path6 from "path";
|
|
1742
|
+
import fs5 from "fs";
|
|
1743
|
+
function readTsconfig(rootDir) {
|
|
1744
|
+
const tsconfigPath = path6.resolve(rootDir, "tsconfig.json");
|
|
1745
|
+
if (!fs5.existsSync(tsconfigPath)) return null;
|
|
1746
|
+
const configFile = ts6.readConfigFile(tsconfigPath, ts6.sys.readFile);
|
|
1747
|
+
if (configFile.error || !configFile.config) return null;
|
|
1748
|
+
const parsed = ts6.parseJsonConfigFileContent(configFile.config, ts6.sys, rootDir);
|
|
1749
|
+
const baseUrl = parsed.options.baseUrl ?? rootDir;
|
|
1750
|
+
const rawPaths = parsed.options.paths;
|
|
1751
|
+
if (!rawPaths) return null;
|
|
1752
|
+
const paths = {};
|
|
1753
|
+
for (const [pattern, targets] of Object.entries(rawPaths)) {
|
|
1754
|
+
paths[pattern] = targets.map((t) => path6.resolve(baseUrl, t));
|
|
1755
|
+
}
|
|
1756
|
+
return { baseUrl, paths };
|
|
1757
|
+
}
|
|
1758
|
+
var init_readTsconfig = __esm({
|
|
1759
|
+
"src/utils/readTsconfig.ts"() {
|
|
1760
|
+
"use strict";
|
|
1761
|
+
}
|
|
1762
|
+
});
|
|
1763
|
+
|
|
1764
|
+
// src/utils/resolveAlias.ts
|
|
1765
|
+
function resolveAlias(specifier, config) {
|
|
1766
|
+
const candidates = [];
|
|
1767
|
+
for (const [pattern, targets] of Object.entries(config.paths)) {
|
|
1768
|
+
const wildcardIndex = pattern.indexOf("*");
|
|
1769
|
+
if (wildcardIndex === -1) {
|
|
1770
|
+
if (specifier === pattern) {
|
|
1771
|
+
candidates.push(...targets);
|
|
1772
|
+
}
|
|
1773
|
+
continue;
|
|
1774
|
+
}
|
|
1775
|
+
const prefix = pattern.slice(0, wildcardIndex);
|
|
1776
|
+
const suffix = pattern.slice(wildcardIndex + 1);
|
|
1777
|
+
if (specifier.startsWith(prefix) && specifier.endsWith(suffix) && specifier.length >= prefix.length + suffix.length) {
|
|
1778
|
+
const captured = specifier.slice(prefix.length, specifier.length - suffix.length);
|
|
1779
|
+
for (const target of targets) {
|
|
1780
|
+
candidates.push(target.replace("*", captured));
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
return candidates;
|
|
1785
|
+
}
|
|
1786
|
+
var init_resolveAlias = __esm({
|
|
1787
|
+
"src/utils/resolveAlias.ts"() {
|
|
1788
|
+
"use strict";
|
|
1789
|
+
}
|
|
1790
|
+
});
|
|
1791
|
+
|
|
1792
|
+
// src/cli/compileRoutes.ts
|
|
1793
|
+
import path7 from "path";
|
|
1794
|
+
import fs6 from "fs";
|
|
1795
|
+
import fg2 from "fast-glob";
|
|
1796
|
+
function toProdExtension(filePath) {
|
|
1797
|
+
if (filePath.endsWith(".ts")) return filePath.slice(0, -3) + ".js";
|
|
1798
|
+
if (filePath.endsWith(".tsx")) return filePath.slice(0, -4) + ".js";
|
|
1799
|
+
if (filePath.endsWith(".jsx")) return filePath.slice(0, -4) + ".js";
|
|
1800
|
+
return filePath;
|
|
1801
|
+
}
|
|
1802
|
+
function toProdImportPath(sourceFile, importer) {
|
|
1803
|
+
const importerDir = path7.dirname(importer);
|
|
1804
|
+
let rel = path7.relative(importerDir, sourceFile);
|
|
1805
|
+
rel = rel.split(path7.sep).join("/");
|
|
1806
|
+
if (!rel.startsWith(".")) rel = "./" + rel;
|
|
1807
|
+
return toProdExtension(rel);
|
|
1808
|
+
}
|
|
1809
|
+
function createAliasPlugin(config) {
|
|
1810
|
+
const EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
|
1811
|
+
const INDEX_EXTS = [
|
|
1812
|
+
"/index.ts",
|
|
1813
|
+
"/index.tsx",
|
|
1814
|
+
"/index.js",
|
|
1815
|
+
"/index.jsx",
|
|
1816
|
+
"/index.mjs",
|
|
1817
|
+
"/index.cjs"
|
|
1818
|
+
];
|
|
1819
|
+
const SPEC_RE = /(\bfrom\s*|import\s*\(\s*)(['"])([^'"]+)\2/g;
|
|
1820
|
+
return {
|
|
1821
|
+
name: "faapi-alias",
|
|
1822
|
+
setup(build) {
|
|
1823
|
+
build.onLoad({ filter: /\.(ts|tsx|js|jsx|mjs|cjs)$/ }, (args) => {
|
|
1824
|
+
let source;
|
|
1825
|
+
try {
|
|
1826
|
+
source = fs6.readFileSync(args.path, "utf8");
|
|
1827
|
+
} catch {
|
|
1828
|
+
return void 0;
|
|
1829
|
+
}
|
|
1830
|
+
const importer = args.path;
|
|
1831
|
+
let modified = false;
|
|
1832
|
+
const newSource = source.replace(SPEC_RE, (full, prefix, quote, specifier) => {
|
|
1833
|
+
if (specifier.startsWith(".") || specifier.startsWith("/") || specifier.startsWith("file:") || specifier.startsWith("node:")) {
|
|
1834
|
+
return full;
|
|
1835
|
+
}
|
|
1836
|
+
const candidates = resolveAlias(specifier, config);
|
|
1837
|
+
for (const candidate of candidates) {
|
|
1838
|
+
for (const ext of EXTS) {
|
|
1839
|
+
const file = candidate + ext;
|
|
1840
|
+
if (fs6.existsSync(file)) {
|
|
1841
|
+
modified = true;
|
|
1842
|
+
return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
for (const indexExt of INDEX_EXTS) {
|
|
1846
|
+
const file = candidate + indexExt;
|
|
1847
|
+
if (fs6.existsSync(file)) {
|
|
1848
|
+
modified = true;
|
|
1849
|
+
return `${prefix}${quote}${toProdImportPath(file, importer)}${quote}`;
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
return full;
|
|
1854
|
+
});
|
|
1855
|
+
if (!modified) return void 0;
|
|
1856
|
+
return { contents: newSource, loader: "default" };
|
|
1857
|
+
});
|
|
1858
|
+
}
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
async function compileRoutes(options) {
|
|
1862
|
+
const { rootDir, appDir, outDir, files, logLevel = "silent" } = options;
|
|
1863
|
+
const allFiles = files ?? await fg2([`${appDir}/**/*.ts`], {
|
|
1864
|
+
cwd: rootDir,
|
|
1865
|
+
onlyFiles: true,
|
|
1866
|
+
absolute: true,
|
|
1867
|
+
ignore: ["**/*.test.ts", "**/*.e2e.test.ts", "**/*.d.ts"]
|
|
1868
|
+
});
|
|
1869
|
+
if (allFiles.length === 0) {
|
|
1870
|
+
return { compiledFiles: [] };
|
|
1871
|
+
}
|
|
1872
|
+
const absOutDir = path7.resolve(rootDir, outDir);
|
|
1873
|
+
await fs6.promises.mkdir(absOutDir, { recursive: true });
|
|
1874
|
+
const tsconfig = readTsconfig(rootDir);
|
|
1875
|
+
const plugins = tsconfig ? [createAliasPlugin(tsconfig)] : [];
|
|
1876
|
+
const esbuild = await import("esbuild");
|
|
1877
|
+
await esbuild.build({
|
|
1878
|
+
entryPoints: allFiles,
|
|
1879
|
+
outdir: absOutDir,
|
|
1880
|
+
outbase: rootDir,
|
|
1881
|
+
bundle: false,
|
|
1882
|
+
platform: "node",
|
|
1883
|
+
format: "esm",
|
|
1884
|
+
sourcemap: true,
|
|
1885
|
+
packages: "external",
|
|
1886
|
+
plugins,
|
|
1887
|
+
logLevel
|
|
1888
|
+
});
|
|
1889
|
+
return { compiledFiles: allFiles };
|
|
1890
|
+
}
|
|
1891
|
+
var init_compileRoutes = __esm({
|
|
1892
|
+
"src/cli/compileRoutes.ts"() {
|
|
1893
|
+
"use strict";
|
|
1894
|
+
init_readTsconfig();
|
|
1895
|
+
init_resolveAlias();
|
|
1896
|
+
}
|
|
1897
|
+
});
|
|
1898
|
+
|
|
1672
1899
|
// src/cli/buildCommand.ts
|
|
1673
1900
|
var buildCommand_exports = {};
|
|
1674
1901
|
__export(buildCommand_exports, {
|
|
1675
1902
|
buildCommand: () => buildCommand,
|
|
1676
1903
|
parseBuildArgs: () => parseBuildArgs
|
|
1677
1904
|
});
|
|
1678
|
-
import
|
|
1679
|
-
import fs5 from "fs";
|
|
1680
|
-
import fg2 from "fast-glob";
|
|
1905
|
+
import path8 from "path";
|
|
1681
1906
|
async function buildCommand(options) {
|
|
1682
1907
|
const { rootDir, patterns, appDir, outdir, types } = options;
|
|
1683
1908
|
console.log("faapi build started");
|
|
1684
1909
|
console.log(`- Root: ${rootDir}`);
|
|
1685
1910
|
console.log(`- Patterns: ${patterns.join(", ")}`);
|
|
1686
1911
|
console.log(`- Output: ${outdir}`);
|
|
1687
|
-
console.log("\n[1/5]
|
|
1688
|
-
const
|
|
1912
|
+
console.log("\n[1/5] Compiling TypeScript...");
|
|
1913
|
+
const result = await compileRoutes({ rootDir, appDir, outDir: outdir, logLevel: "silent" });
|
|
1914
|
+
console.log(` Compiled ${result.compiledFiles.length} file(s)`);
|
|
1915
|
+
console.log("\n[2/5] Scanning routes...");
|
|
1916
|
+
const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, appDir, outdir);
|
|
1689
1917
|
const sorted = sortRoutes(routes);
|
|
1690
1918
|
console.log(` Found ${sorted.length} routes, ${wsRoutes.length} WS routes`);
|
|
1691
1919
|
const conflicts = detectRouteConflicts(sorted);
|
|
@@ -1698,62 +1926,21 @@ async function buildCommand(options) {
|
|
|
1698
1926
|
}
|
|
1699
1927
|
}
|
|
1700
1928
|
}
|
|
1701
|
-
console.log("\n[
|
|
1702
|
-
const typesPath = types ?
|
|
1929
|
+
console.log("\n[3/5] Generating types...");
|
|
1930
|
+
const typesPath = types ? path8.resolve(rootDir, types) : path8.resolve(rootDir, "faapi-types.ts");
|
|
1703
1931
|
await generateTypes(sorted, rootDir, typesPath);
|
|
1704
1932
|
console.log(` Written to ${typesPath}`);
|
|
1705
|
-
console.log("\n[
|
|
1706
|
-
const schemaPath =
|
|
1707
|
-
|
|
1708
|
-
await writeSchemaModule(entries, allTypesByFile, schemaPath);
|
|
1933
|
+
console.log("\n[4/5] Generating schema module...");
|
|
1934
|
+
const schemaPath = path8.resolve(rootDir, outdir, "faapi-schema.js");
|
|
1935
|
+
await generateSchemaFile(sorted, rootDir, schemaPath);
|
|
1709
1936
|
console.log(` Written to ${schemaPath}`);
|
|
1710
|
-
console.log("\n[
|
|
1711
|
-
const routesPath =
|
|
1712
|
-
const serialized = serializeRoutes(sorted, wsRoutes, rootDir);
|
|
1937
|
+
console.log("\n[5/5] Generating routes manifest...");
|
|
1938
|
+
const routesPath = path8.resolve(rootDir, outdir, "faapi-routes.js");
|
|
1939
|
+
const serialized = serializeRoutes(sorted, wsRoutes, rootDir, outdir);
|
|
1713
1940
|
await writeRoutesModule(serialized, routesPath);
|
|
1714
1941
|
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
1942
|
console.log("\nfaapi build completed");
|
|
1719
1943
|
}
|
|
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
1944
|
function parseBuildArgs(argv) {
|
|
1758
1945
|
const rootDir = process.cwd();
|
|
1759
1946
|
let outdir = "dist";
|
|
@@ -1790,7 +1977,7 @@ var init_buildCommand = __esm({
|
|
|
1790
1977
|
init_generateTypes();
|
|
1791
1978
|
init_generateSchema();
|
|
1792
1979
|
init_generateRoutes();
|
|
1793
|
-
|
|
1980
|
+
init_compileRoutes();
|
|
1794
1981
|
}
|
|
1795
1982
|
});
|
|
1796
1983
|
|
|
@@ -2441,42 +2628,42 @@ var init_parseArgs = __esm({
|
|
|
2441
2628
|
});
|
|
2442
2629
|
|
|
2443
2630
|
// src/router/matchRoute.ts
|
|
2444
|
-
function matchRoute(routes, method,
|
|
2631
|
+
function matchRoute(routes, method, path15) {
|
|
2445
2632
|
for (const route of routes) {
|
|
2446
2633
|
if (route.method !== method) {
|
|
2447
2634
|
continue;
|
|
2448
2635
|
}
|
|
2449
2636
|
if (!route.isDynamic) {
|
|
2450
|
-
if (route.urlPath ===
|
|
2637
|
+
if (route.urlPath === path15) {
|
|
2451
2638
|
return { route, params: {} };
|
|
2452
2639
|
}
|
|
2453
2640
|
continue;
|
|
2454
2641
|
}
|
|
2455
|
-
const params = matchDynamicPath(route.urlPath,
|
|
2642
|
+
const params = matchDynamicPath(route.urlPath, path15, route.paramNames, route.isCatchAll);
|
|
2456
2643
|
if (params !== null) {
|
|
2457
2644
|
return { route, params };
|
|
2458
2645
|
}
|
|
2459
2646
|
}
|
|
2460
2647
|
return null;
|
|
2461
2648
|
}
|
|
2462
|
-
function matchWsRoute(wsRoutes,
|
|
2649
|
+
function matchWsRoute(wsRoutes, path15) {
|
|
2463
2650
|
for (const route of wsRoutes) {
|
|
2464
2651
|
if (!route.isDynamic) {
|
|
2465
|
-
if (route.urlPath ===
|
|
2652
|
+
if (route.urlPath === path15) {
|
|
2466
2653
|
return { route, params: {} };
|
|
2467
2654
|
}
|
|
2468
2655
|
continue;
|
|
2469
2656
|
}
|
|
2470
|
-
const params = matchDynamicPath(route.urlPath,
|
|
2657
|
+
const params = matchDynamicPath(route.urlPath, path15, route.paramNames, route.isCatchAll);
|
|
2471
2658
|
if (params !== null) {
|
|
2472
2659
|
return { route, params };
|
|
2473
2660
|
}
|
|
2474
2661
|
}
|
|
2475
2662
|
return null;
|
|
2476
2663
|
}
|
|
2477
|
-
function matchDynamicPath(pattern,
|
|
2664
|
+
function matchDynamicPath(pattern, path15, paramNames, isCatchAll) {
|
|
2478
2665
|
const patternSegments = pattern.split("/").filter(Boolean);
|
|
2479
|
-
const pathSegments =
|
|
2666
|
+
const pathSegments = path15.split("/").filter(Boolean);
|
|
2480
2667
|
if (isCatchAll) {
|
|
2481
2668
|
const nonCatchAllCount = patternSegments.length - 1;
|
|
2482
2669
|
if (pathSegments.length <= nonCatchAllCount) {
|
|
@@ -2895,14 +3082,14 @@ var init_httpErrors = __esm({
|
|
|
2895
3082
|
issues;
|
|
2896
3083
|
};
|
|
2897
3084
|
RouteNotFoundError = class extends FaapiError {
|
|
2898
|
-
constructor(
|
|
2899
|
-
super("ROUTE_NOT_FOUND", `Route not found: ${
|
|
3085
|
+
constructor(path15) {
|
|
3086
|
+
super("ROUTE_NOT_FOUND", `Route not found: ${path15}`, 404);
|
|
2900
3087
|
this.name = "RouteNotFoundError";
|
|
2901
3088
|
}
|
|
2902
3089
|
};
|
|
2903
3090
|
MethodNotAllowedError = class extends FaapiError {
|
|
2904
|
-
constructor(method,
|
|
2905
|
-
super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${
|
|
3091
|
+
constructor(method, path15, allowedMethods) {
|
|
3092
|
+
super("METHOD_NOT_ALLOWED", `Method ${method} not allowed for ${path15}`, 405);
|
|
2906
3093
|
this.allowedMethods = allowedMethods;
|
|
2907
3094
|
this.name = "MethodNotAllowedError";
|
|
2908
3095
|
}
|
|
@@ -3261,75 +3448,6 @@ var init_sendNodeResponse = __esm({
|
|
|
3261
3448
|
}
|
|
3262
3449
|
});
|
|
3263
3450
|
|
|
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
3451
|
// src/validator/coerceInput.ts
|
|
3334
3452
|
function coerceInput(input, properties) {
|
|
3335
3453
|
const issues = [];
|
|
@@ -3342,16 +3460,16 @@ function coerceInput(input, properties) {
|
|
|
3342
3460
|
}
|
|
3343
3461
|
return { data, issues };
|
|
3344
3462
|
}
|
|
3345
|
-
function coerceValue(value, type,
|
|
3463
|
+
function coerceValue(value, type, path15, issues) {
|
|
3346
3464
|
switch (type.kind) {
|
|
3347
3465
|
case "number":
|
|
3348
3466
|
if (typeof value === "string") {
|
|
3349
|
-
return coerceStringToNumber(value,
|
|
3467
|
+
return coerceStringToNumber(value, path15, issues);
|
|
3350
3468
|
}
|
|
3351
3469
|
return value;
|
|
3352
3470
|
case "boolean":
|
|
3353
3471
|
if (typeof value === "string") {
|
|
3354
|
-
return coerceStringToBoolean(value,
|
|
3472
|
+
return coerceStringToBoolean(value, path15, issues);
|
|
3355
3473
|
}
|
|
3356
3474
|
return value;
|
|
3357
3475
|
case "array":
|
|
@@ -3359,7 +3477,7 @@ function coerceValue(value, type, path12, issues) {
|
|
|
3359
3477
|
return value;
|
|
3360
3478
|
}
|
|
3361
3479
|
if (Array.isArray(value)) {
|
|
3362
|
-
return value.map((item, i) => coerceValue(item, type.element, `${
|
|
3480
|
+
return value.map((item, i) => coerceValue(item, type.element, `${path15}[${i}]`, issues));
|
|
3363
3481
|
}
|
|
3364
3482
|
return value;
|
|
3365
3483
|
case "tuple": {
|
|
@@ -3368,10 +3486,10 @@ function coerceValue(value, type, path12, issues) {
|
|
|
3368
3486
|
return value.map((item, i) => {
|
|
3369
3487
|
const elem = type.elements[i];
|
|
3370
3488
|
if (elem && !elem.rest) {
|
|
3371
|
-
return coerceValue(item, elem.type, `${
|
|
3489
|
+
return coerceValue(item, elem.type, `${path15}[${i}]`, issues);
|
|
3372
3490
|
}
|
|
3373
3491
|
if (restElement) {
|
|
3374
|
-
return coerceValue(item, restElement.type, `${
|
|
3492
|
+
return coerceValue(item, restElement.type, `${path15}[${i}]`, issues);
|
|
3375
3493
|
}
|
|
3376
3494
|
return item;
|
|
3377
3495
|
});
|
|
@@ -3379,7 +3497,7 @@ function coerceValue(value, type, path12, issues) {
|
|
|
3379
3497
|
case "union":
|
|
3380
3498
|
for (const member of type.members) {
|
|
3381
3499
|
const tempIssues = [];
|
|
3382
|
-
const coerced = coerceValue(value, member,
|
|
3500
|
+
const coerced = coerceValue(value, member, path15, tempIssues);
|
|
3383
3501
|
if (tempIssues.length === 0) {
|
|
3384
3502
|
return coerced;
|
|
3385
3503
|
}
|
|
@@ -3400,7 +3518,7 @@ function coerceValue(value, type, path12, issues) {
|
|
|
3400
3518
|
result[prop.name] = coerceValue(
|
|
3401
3519
|
obj[prop.name],
|
|
3402
3520
|
prop.type,
|
|
3403
|
-
`${
|
|
3521
|
+
`${path15}.${prop.name}`,
|
|
3404
3522
|
issues
|
|
3405
3523
|
);
|
|
3406
3524
|
}
|
|
@@ -3412,31 +3530,31 @@ function coerceValue(value, type, path12, issues) {
|
|
|
3412
3530
|
return value;
|
|
3413
3531
|
}
|
|
3414
3532
|
}
|
|
3415
|
-
function coerceStringToNumber(value,
|
|
3533
|
+
function coerceStringToNumber(value, path15, issues) {
|
|
3416
3534
|
if (value.trim() === "") {
|
|
3417
3535
|
issues.push({
|
|
3418
|
-
path:
|
|
3536
|
+
path: path15,
|
|
3419
3537
|
code: "COERCE_FAILED",
|
|
3420
3538
|
expected: "number",
|
|
3421
3539
|
received: "string",
|
|
3422
|
-
message: `\u5B57\u6BB5 "${
|
|
3540
|
+
message: `\u5B57\u6BB5 "${path15}" \u7C7B\u578B\u8F6C\u6362\u5931\u8D25\uFF1A\u65E0\u6CD5\u5C06 "${value}" \u8F6C\u4E3A number`
|
|
3423
3541
|
});
|
|
3424
3542
|
return value;
|
|
3425
3543
|
}
|
|
3426
3544
|
const num = Number(value);
|
|
3427
3545
|
if (Number.isNaN(num)) {
|
|
3428
3546
|
issues.push({
|
|
3429
|
-
path:
|
|
3547
|
+
path: path15,
|
|
3430
3548
|
code: "COERCE_FAILED",
|
|
3431
3549
|
expected: "number",
|
|
3432
3550
|
received: "string",
|
|
3433
|
-
message: `\u5B57\u6BB5 "${
|
|
3551
|
+
message: `\u5B57\u6BB5 "${path15}" \u7C7B\u578B\u8F6C\u6362\u5931\u8D25\uFF1A\u65E0\u6CD5\u5C06 "${value}" \u8F6C\u4E3A number`
|
|
3434
3552
|
});
|
|
3435
3553
|
return value;
|
|
3436
3554
|
}
|
|
3437
3555
|
return num;
|
|
3438
3556
|
}
|
|
3439
|
-
function coerceStringToBoolean(value,
|
|
3557
|
+
function coerceStringToBoolean(value, path15, issues) {
|
|
3440
3558
|
if (value === "true" || value === "1") {
|
|
3441
3559
|
return true;
|
|
3442
3560
|
}
|
|
@@ -3444,11 +3562,11 @@ function coerceStringToBoolean(value, path12, issues) {
|
|
|
3444
3562
|
return false;
|
|
3445
3563
|
}
|
|
3446
3564
|
issues.push({
|
|
3447
|
-
path:
|
|
3565
|
+
path: path15,
|
|
3448
3566
|
code: "COERCE_FAILED",
|
|
3449
3567
|
expected: "boolean",
|
|
3450
3568
|
received: "string",
|
|
3451
|
-
message: `\u5B57\u6BB5 "${
|
|
3569
|
+
message: `\u5B57\u6BB5 "${path15}" \u7C7B\u578B\u8F6C\u6362\u5931\u8D25\uFF1A\u65E0\u6CD5\u5C06 "${value}" \u8F6C\u4E3A boolean`
|
|
3452
3570
|
});
|
|
3453
3571
|
return value;
|
|
3454
3572
|
}
|
|
@@ -3588,26 +3706,26 @@ var init_cors = __esm({
|
|
|
3588
3706
|
});
|
|
3589
3707
|
|
|
3590
3708
|
// src/server/serveStatic.ts
|
|
3591
|
-
import
|
|
3592
|
-
import
|
|
3709
|
+
import fs7 from "fs";
|
|
3710
|
+
import path9 from "path";
|
|
3593
3711
|
import { createReadStream } from "fs";
|
|
3594
3712
|
import { Readable as Readable2 } from "stream";
|
|
3595
3713
|
async function serveStatic(urlPath, staticDir) {
|
|
3596
|
-
const resolved =
|
|
3597
|
-
const relative3 =
|
|
3598
|
-
if (relative3.startsWith("..") ||
|
|
3714
|
+
const resolved = path9.resolve(staticDir, "." + urlPath);
|
|
3715
|
+
const relative3 = path9.relative(staticDir, resolved);
|
|
3716
|
+
if (relative3.startsWith("..") || path9.isAbsolute(relative3)) {
|
|
3599
3717
|
return null;
|
|
3600
3718
|
}
|
|
3601
3719
|
let stat4;
|
|
3602
3720
|
try {
|
|
3603
|
-
stat4 = await
|
|
3721
|
+
stat4 = await fs7.promises.stat(resolved);
|
|
3604
3722
|
} catch {
|
|
3605
3723
|
return null;
|
|
3606
3724
|
}
|
|
3607
3725
|
if (stat4.isDirectory()) {
|
|
3608
|
-
const indexPath =
|
|
3726
|
+
const indexPath = path9.join(resolved, "index.html");
|
|
3609
3727
|
try {
|
|
3610
|
-
const indexStat = await
|
|
3728
|
+
const indexStat = await fs7.promises.stat(indexPath);
|
|
3611
3729
|
if (indexStat.isFile()) {
|
|
3612
3730
|
return serveFile(indexPath, indexStat.size);
|
|
3613
3731
|
}
|
|
@@ -3621,7 +3739,7 @@ async function serveStatic(urlPath, staticDir) {
|
|
|
3621
3739
|
return serveFile(resolved, stat4.size);
|
|
3622
3740
|
}
|
|
3623
3741
|
function serveFile(filePath, size) {
|
|
3624
|
-
const ext =
|
|
3742
|
+
const ext = path9.extname(filePath).toLowerCase();
|
|
3625
3743
|
const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
|
|
3626
3744
|
const stream = createReadStream(filePath);
|
|
3627
3745
|
const webStream = Readable2.toWeb(stream);
|
|
@@ -3821,7 +3939,7 @@ var init_wsHandler = __esm({
|
|
|
3821
3939
|
|
|
3822
3940
|
// src/server/handleWsUpgrade.ts
|
|
3823
3941
|
import { WebSocketServer, WebSocket } from "ws";
|
|
3824
|
-
import
|
|
3942
|
+
import path10 from "path";
|
|
3825
3943
|
function getPathname(req) {
|
|
3826
3944
|
const url = req.url ?? "/";
|
|
3827
3945
|
const idx = url.indexOf("?");
|
|
@@ -3904,7 +4022,7 @@ function attachWebSocket(options) {
|
|
|
3904
4022
|
const finalHandler = async () => {
|
|
3905
4023
|
let handlers;
|
|
3906
4024
|
try {
|
|
3907
|
-
const absoluteFilePath =
|
|
4025
|
+
const absoluteFilePath = path10.resolve(rootDir, route.filePath);
|
|
3908
4026
|
handlers = await loadWsHandler(absoluteFilePath, ctx);
|
|
3909
4027
|
} catch (err) {
|
|
3910
4028
|
const reason = err instanceof Error ? err.message : String(err);
|
|
@@ -3966,7 +4084,7 @@ import {
|
|
|
3966
4084
|
createServer as createHttpServer
|
|
3967
4085
|
} from "http";
|
|
3968
4086
|
import { Readable as Readable3 } from "stream";
|
|
3969
|
-
import
|
|
4087
|
+
import path11 from "path";
|
|
3970
4088
|
function toWebRequest(req) {
|
|
3971
4089
|
const forwardedProto = req.headers["x-forwarded-proto"];
|
|
3972
4090
|
const protocol = Array.isArray(forwardedProto) ? forwardedProto[0]?.split(",")[0]?.trim() ?? "http" : forwardedProto?.split(",")[0]?.trim() ?? "http";
|
|
@@ -4010,15 +4128,15 @@ function limitStreamSize(stream, maxSize) {
|
|
|
4010
4128
|
}
|
|
4011
4129
|
});
|
|
4012
4130
|
}
|
|
4013
|
-
function findAllowedMethods(routes,
|
|
4131
|
+
function findAllowedMethods(routes, path15) {
|
|
4014
4132
|
const methods = /* @__PURE__ */ new Set();
|
|
4015
4133
|
for (const route of routes) {
|
|
4016
|
-
if (route.urlPath ===
|
|
4134
|
+
if (route.urlPath === path15) {
|
|
4017
4135
|
methods.add(route.method);
|
|
4018
4136
|
continue;
|
|
4019
4137
|
}
|
|
4020
4138
|
if (route.isDynamic) {
|
|
4021
|
-
const params = matchDynamicPath(route.urlPath,
|
|
4139
|
+
const params = matchDynamicPath(route.urlPath, path15, route.paramNames, route.isCatchAll);
|
|
4022
4140
|
if (params !== null) {
|
|
4023
4141
|
methods.add(route.method);
|
|
4024
4142
|
}
|
|
@@ -4045,10 +4163,6 @@ function createServer(options) {
|
|
|
4045
4163
|
if (wsRoutes) {
|
|
4046
4164
|
globalRef.__FAAPI_WS_ROUTES__ = wsRoutes;
|
|
4047
4165
|
}
|
|
4048
|
-
if (schemaRegistry.size === 0 && routes.length > 0) {
|
|
4049
|
-
const manifest = extractSchemasForRoutes(routes, rootDir);
|
|
4050
|
-
schemaRegistry.loadManifest(manifest);
|
|
4051
|
-
}
|
|
4052
4166
|
const corsMiddleware = corsOption === false ? null : corsOption === true || corsOption === void 0 ? cors() : cors(corsOption);
|
|
4053
4167
|
const server = createHttpServer((req, res) => {
|
|
4054
4168
|
const currentRoutes = globalRef.__FAAPI_ROUTES__ ?? routes;
|
|
@@ -4085,7 +4199,7 @@ async function handleRequest(routes, rootDir, req, res, corsMiddleware, staticDi
|
|
|
4085
4199
|
const match = matchRoute(routes, method, urlPath);
|
|
4086
4200
|
if (!match) {
|
|
4087
4201
|
if (staticDir) {
|
|
4088
|
-
const absStaticDir =
|
|
4202
|
+
const absStaticDir = path11.resolve(rootDir, staticDir);
|
|
4089
4203
|
const staticResponse = await serveStatic(urlPath, absStaticDir);
|
|
4090
4204
|
if (staticResponse) {
|
|
4091
4205
|
return mergeMeta(staticResponse, meta);
|
|
@@ -4099,7 +4213,7 @@ async function handleRequest(routes, rootDir, req, res, corsMiddleware, staticDi
|
|
|
4099
4213
|
}
|
|
4100
4214
|
ctx.params = match.params;
|
|
4101
4215
|
const { route } = match;
|
|
4102
|
-
const absoluteFilePath =
|
|
4216
|
+
const absoluteFilePath = path11.resolve(rootDir, route.filePath);
|
|
4103
4217
|
const routeModule = await loadRouteModule(absoluteFilePath, route.method);
|
|
4104
4218
|
const input = await resolveInput(route.method, request);
|
|
4105
4219
|
const inputType = getInputTypeForMethod(route.method);
|
|
@@ -4167,8 +4281,6 @@ var init_createServer = __esm({
|
|
|
4167
4281
|
init_getClientIp();
|
|
4168
4282
|
init_cors();
|
|
4169
4283
|
init_serveStatic();
|
|
4170
|
-
init_schemaRegistry();
|
|
4171
|
-
init_generateSchema();
|
|
4172
4284
|
init_handleWsUpgrade();
|
|
4173
4285
|
init_serverUtils();
|
|
4174
4286
|
MAX_BODY_SIZE = 10 * 1024 * 1024;
|
|
@@ -4354,7 +4466,7 @@ var init_esm = __esm({
|
|
|
4354
4466
|
this._directoryFilter = normalizeFilter(opts.directoryFilter);
|
|
4355
4467
|
const statMethod = opts.lstat ? lstat : stat;
|
|
4356
4468
|
if (wantBigintFsStats) {
|
|
4357
|
-
this._stat = (
|
|
4469
|
+
this._stat = (path15) => statMethod(path15, { bigint: true });
|
|
4358
4470
|
} else {
|
|
4359
4471
|
this._stat = statMethod;
|
|
4360
4472
|
}
|
|
@@ -4379,8 +4491,8 @@ var init_esm = __esm({
|
|
|
4379
4491
|
const par = this.parent;
|
|
4380
4492
|
const fil = par && par.files;
|
|
4381
4493
|
if (fil && fil.length > 0) {
|
|
4382
|
-
const { path:
|
|
4383
|
-
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent,
|
|
4494
|
+
const { path: path15, depth } = par;
|
|
4495
|
+
const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path15));
|
|
4384
4496
|
const awaited = await Promise.all(slice);
|
|
4385
4497
|
for (const entry of awaited) {
|
|
4386
4498
|
if (!entry)
|
|
@@ -4420,20 +4532,20 @@ var init_esm = __esm({
|
|
|
4420
4532
|
this.reading = false;
|
|
4421
4533
|
}
|
|
4422
4534
|
}
|
|
4423
|
-
async _exploreDir(
|
|
4535
|
+
async _exploreDir(path15, depth) {
|
|
4424
4536
|
let files;
|
|
4425
4537
|
try {
|
|
4426
|
-
files = await readdir(
|
|
4538
|
+
files = await readdir(path15, this._rdOptions);
|
|
4427
4539
|
} catch (error) {
|
|
4428
4540
|
this._onError(error);
|
|
4429
4541
|
}
|
|
4430
|
-
return { files, depth, path:
|
|
4542
|
+
return { files, depth, path: path15 };
|
|
4431
4543
|
}
|
|
4432
|
-
async _formatEntry(dirent,
|
|
4544
|
+
async _formatEntry(dirent, path15) {
|
|
4433
4545
|
let entry;
|
|
4434
4546
|
const basename3 = this._isDirent ? dirent.name : dirent;
|
|
4435
4547
|
try {
|
|
4436
|
-
const fullPath = presolve(pjoin(
|
|
4548
|
+
const fullPath = presolve(pjoin(path15, basename3));
|
|
4437
4549
|
entry = { path: prelative(this._root, fullPath), fullPath, basename: basename3 };
|
|
4438
4550
|
entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
|
|
4439
4551
|
} catch (err) {
|
|
@@ -4494,16 +4606,16 @@ import { watchFile, unwatchFile, watch as fs_watch } from "fs";
|
|
|
4494
4606
|
import { open, stat as stat2, lstat as lstat2, realpath as fsrealpath } from "fs/promises";
|
|
4495
4607
|
import * as sysPath from "path";
|
|
4496
4608
|
import { type as osType } from "os";
|
|
4497
|
-
function createFsWatchInstance(
|
|
4609
|
+
function createFsWatchInstance(path15, options, listener, errHandler, emitRaw) {
|
|
4498
4610
|
const handleEvent = (rawEvent, evPath) => {
|
|
4499
|
-
listener(
|
|
4500
|
-
emitRaw(rawEvent, evPath, { watchedPath:
|
|
4501
|
-
if (evPath &&
|
|
4502
|
-
fsWatchBroadcast(sysPath.resolve(
|
|
4611
|
+
listener(path15);
|
|
4612
|
+
emitRaw(rawEvent, evPath, { watchedPath: path15 });
|
|
4613
|
+
if (evPath && path15 !== evPath) {
|
|
4614
|
+
fsWatchBroadcast(sysPath.resolve(path15, evPath), KEY_LISTENERS, sysPath.join(path15, evPath));
|
|
4503
4615
|
}
|
|
4504
4616
|
};
|
|
4505
4617
|
try {
|
|
4506
|
-
return fs_watch(
|
|
4618
|
+
return fs_watch(path15, {
|
|
4507
4619
|
persistent: options.persistent
|
|
4508
4620
|
}, handleEvent);
|
|
4509
4621
|
} catch (error) {
|
|
@@ -4848,12 +4960,12 @@ var init_handler = __esm({
|
|
|
4848
4960
|
listener(val1, val2, val3);
|
|
4849
4961
|
});
|
|
4850
4962
|
};
|
|
4851
|
-
setFsWatchListener = (
|
|
4963
|
+
setFsWatchListener = (path15, fullPath, options, handlers) => {
|
|
4852
4964
|
const { listener, errHandler, rawEmitter } = handlers;
|
|
4853
4965
|
let cont = FsWatchInstances.get(fullPath);
|
|
4854
4966
|
let watcher;
|
|
4855
4967
|
if (!options.persistent) {
|
|
4856
|
-
watcher = createFsWatchInstance(
|
|
4968
|
+
watcher = createFsWatchInstance(path15, options, listener, errHandler, rawEmitter);
|
|
4857
4969
|
if (!watcher)
|
|
4858
4970
|
return;
|
|
4859
4971
|
return watcher.close.bind(watcher);
|
|
@@ -4864,7 +4976,7 @@ var init_handler = __esm({
|
|
|
4864
4976
|
addAndConvert(cont, KEY_RAW, rawEmitter);
|
|
4865
4977
|
} else {
|
|
4866
4978
|
watcher = createFsWatchInstance(
|
|
4867
|
-
|
|
4979
|
+
path15,
|
|
4868
4980
|
options,
|
|
4869
4981
|
fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
|
|
4870
4982
|
errHandler,
|
|
@@ -4879,7 +4991,7 @@ var init_handler = __esm({
|
|
|
4879
4991
|
cont.watcherUnusable = true;
|
|
4880
4992
|
if (isWindows && error.code === "EPERM") {
|
|
4881
4993
|
try {
|
|
4882
|
-
const fd = await open(
|
|
4994
|
+
const fd = await open(path15, "r");
|
|
4883
4995
|
await fd.close();
|
|
4884
4996
|
broadcastErr(error);
|
|
4885
4997
|
} catch (err) {
|
|
@@ -4910,7 +5022,7 @@ var init_handler = __esm({
|
|
|
4910
5022
|
};
|
|
4911
5023
|
};
|
|
4912
5024
|
FsWatchFileInstances = /* @__PURE__ */ new Map();
|
|
4913
|
-
setFsWatchFileListener = (
|
|
5025
|
+
setFsWatchFileListener = (path15, fullPath, options, handlers) => {
|
|
4914
5026
|
const { listener, rawEmitter } = handlers;
|
|
4915
5027
|
let cont = FsWatchFileInstances.get(fullPath);
|
|
4916
5028
|
const copts = cont && cont.options;
|
|
@@ -4932,7 +5044,7 @@ var init_handler = __esm({
|
|
|
4932
5044
|
});
|
|
4933
5045
|
const currmtime = curr.mtimeMs;
|
|
4934
5046
|
if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
|
|
4935
|
-
foreach(cont.listeners, (listener2) => listener2(
|
|
5047
|
+
foreach(cont.listeners, (listener2) => listener2(path15, curr));
|
|
4936
5048
|
}
|
|
4937
5049
|
})
|
|
4938
5050
|
};
|
|
@@ -4960,13 +5072,13 @@ var init_handler = __esm({
|
|
|
4960
5072
|
* @param listener on fs change
|
|
4961
5073
|
* @returns closer for the watcher instance
|
|
4962
5074
|
*/
|
|
4963
|
-
_watchWithNodeFs(
|
|
5075
|
+
_watchWithNodeFs(path15, listener) {
|
|
4964
5076
|
const opts = this.fsw.options;
|
|
4965
|
-
const directory = sysPath.dirname(
|
|
4966
|
-
const basename3 = sysPath.basename(
|
|
5077
|
+
const directory = sysPath.dirname(path15);
|
|
5078
|
+
const basename3 = sysPath.basename(path15);
|
|
4967
5079
|
const parent = this.fsw._getWatchedDir(directory);
|
|
4968
5080
|
parent.add(basename3);
|
|
4969
|
-
const absolutePath = sysPath.resolve(
|
|
5081
|
+
const absolutePath = sysPath.resolve(path15);
|
|
4970
5082
|
const options = {
|
|
4971
5083
|
persistent: opts.persistent
|
|
4972
5084
|
};
|
|
@@ -4976,12 +5088,12 @@ var init_handler = __esm({
|
|
|
4976
5088
|
if (opts.usePolling) {
|
|
4977
5089
|
const enableBin = opts.interval !== opts.binaryInterval;
|
|
4978
5090
|
options.interval = enableBin && isBinaryPath(basename3) ? opts.binaryInterval : opts.interval;
|
|
4979
|
-
closer = setFsWatchFileListener(
|
|
5091
|
+
closer = setFsWatchFileListener(path15, absolutePath, options, {
|
|
4980
5092
|
listener,
|
|
4981
5093
|
rawEmitter: this.fsw._emitRaw
|
|
4982
5094
|
});
|
|
4983
5095
|
} else {
|
|
4984
|
-
closer = setFsWatchListener(
|
|
5096
|
+
closer = setFsWatchListener(path15, absolutePath, options, {
|
|
4985
5097
|
listener,
|
|
4986
5098
|
errHandler: this._boundHandleError,
|
|
4987
5099
|
rawEmitter: this.fsw._emitRaw
|
|
@@ -5003,7 +5115,7 @@ var init_handler = __esm({
|
|
|
5003
5115
|
let prevStats = stats;
|
|
5004
5116
|
if (parent.has(basename3))
|
|
5005
5117
|
return;
|
|
5006
|
-
const listener = async (
|
|
5118
|
+
const listener = async (path15, newStats) => {
|
|
5007
5119
|
if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
|
|
5008
5120
|
return;
|
|
5009
5121
|
if (!newStats || newStats.mtimeMs === 0) {
|
|
@@ -5017,11 +5129,11 @@ var init_handler = __esm({
|
|
|
5017
5129
|
this.fsw._emit(EV.CHANGE, file, newStats2);
|
|
5018
5130
|
}
|
|
5019
5131
|
if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
|
|
5020
|
-
this.fsw._closeFile(
|
|
5132
|
+
this.fsw._closeFile(path15);
|
|
5021
5133
|
prevStats = newStats2;
|
|
5022
5134
|
const closer2 = this._watchWithNodeFs(file, listener);
|
|
5023
5135
|
if (closer2)
|
|
5024
|
-
this.fsw._addPathCloser(
|
|
5136
|
+
this.fsw._addPathCloser(path15, closer2);
|
|
5025
5137
|
} else {
|
|
5026
5138
|
prevStats = newStats2;
|
|
5027
5139
|
}
|
|
@@ -5053,7 +5165,7 @@ var init_handler = __esm({
|
|
|
5053
5165
|
* @param item basename of this item
|
|
5054
5166
|
* @returns true if no more processing is needed for this entry.
|
|
5055
5167
|
*/
|
|
5056
|
-
async _handleSymlink(entry, directory,
|
|
5168
|
+
async _handleSymlink(entry, directory, path15, item) {
|
|
5057
5169
|
if (this.fsw.closed) {
|
|
5058
5170
|
return;
|
|
5059
5171
|
}
|
|
@@ -5063,7 +5175,7 @@ var init_handler = __esm({
|
|
|
5063
5175
|
this.fsw._incrReadyCount();
|
|
5064
5176
|
let linkPath;
|
|
5065
5177
|
try {
|
|
5066
|
-
linkPath = await fsrealpath(
|
|
5178
|
+
linkPath = await fsrealpath(path15);
|
|
5067
5179
|
} catch (e) {
|
|
5068
5180
|
this.fsw._emitReady();
|
|
5069
5181
|
return true;
|
|
@@ -5073,12 +5185,12 @@ var init_handler = __esm({
|
|
|
5073
5185
|
if (dir.has(item)) {
|
|
5074
5186
|
if (this.fsw._symlinkPaths.get(full) !== linkPath) {
|
|
5075
5187
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
5076
|
-
this.fsw._emit(EV.CHANGE,
|
|
5188
|
+
this.fsw._emit(EV.CHANGE, path15, entry.stats);
|
|
5077
5189
|
}
|
|
5078
5190
|
} else {
|
|
5079
5191
|
dir.add(item);
|
|
5080
5192
|
this.fsw._symlinkPaths.set(full, linkPath);
|
|
5081
|
-
this.fsw._emit(EV.ADD,
|
|
5193
|
+
this.fsw._emit(EV.ADD, path15, entry.stats);
|
|
5082
5194
|
}
|
|
5083
5195
|
this.fsw._emitReady();
|
|
5084
5196
|
return true;
|
|
@@ -5107,9 +5219,9 @@ var init_handler = __esm({
|
|
|
5107
5219
|
return;
|
|
5108
5220
|
}
|
|
5109
5221
|
const item = entry.path;
|
|
5110
|
-
let
|
|
5222
|
+
let path15 = sysPath.join(directory, item);
|
|
5111
5223
|
current.add(item);
|
|
5112
|
-
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory,
|
|
5224
|
+
if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path15, item)) {
|
|
5113
5225
|
return;
|
|
5114
5226
|
}
|
|
5115
5227
|
if (this.fsw.closed) {
|
|
@@ -5118,8 +5230,8 @@ var init_handler = __esm({
|
|
|
5118
5230
|
}
|
|
5119
5231
|
if (item === target || !target && !previous.has(item)) {
|
|
5120
5232
|
this.fsw._incrReadyCount();
|
|
5121
|
-
|
|
5122
|
-
this._addToNodeFs(
|
|
5233
|
+
path15 = sysPath.join(dir, sysPath.relative(dir, path15));
|
|
5234
|
+
this._addToNodeFs(path15, initialAdd, wh, depth + 1);
|
|
5123
5235
|
}
|
|
5124
5236
|
}).on(EV.ERROR, this._boundHandleError);
|
|
5125
5237
|
return new Promise((resolve3, reject) => {
|
|
@@ -5188,13 +5300,13 @@ var init_handler = __esm({
|
|
|
5188
5300
|
* @param depth Child path actually targeted for watch
|
|
5189
5301
|
* @param target Child path actually targeted for watch
|
|
5190
5302
|
*/
|
|
5191
|
-
async _addToNodeFs(
|
|
5303
|
+
async _addToNodeFs(path15, initialAdd, priorWh, depth, target) {
|
|
5192
5304
|
const ready = this.fsw._emitReady;
|
|
5193
|
-
if (this.fsw._isIgnored(
|
|
5305
|
+
if (this.fsw._isIgnored(path15) || this.fsw.closed) {
|
|
5194
5306
|
ready();
|
|
5195
5307
|
return false;
|
|
5196
5308
|
}
|
|
5197
|
-
const wh = this.fsw._getWatchHelpers(
|
|
5309
|
+
const wh = this.fsw._getWatchHelpers(path15);
|
|
5198
5310
|
if (priorWh) {
|
|
5199
5311
|
wh.filterPath = (entry) => priorWh.filterPath(entry);
|
|
5200
5312
|
wh.filterDir = (entry) => priorWh.filterDir(entry);
|
|
@@ -5210,8 +5322,8 @@ var init_handler = __esm({
|
|
|
5210
5322
|
const follow = this.fsw.options.followSymlinks;
|
|
5211
5323
|
let closer;
|
|
5212
5324
|
if (stats.isDirectory()) {
|
|
5213
|
-
const absPath = sysPath.resolve(
|
|
5214
|
-
const targetPath = follow ? await fsrealpath(
|
|
5325
|
+
const absPath = sysPath.resolve(path15);
|
|
5326
|
+
const targetPath = follow ? await fsrealpath(path15) : path15;
|
|
5215
5327
|
if (this.fsw.closed)
|
|
5216
5328
|
return;
|
|
5217
5329
|
closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
|
|
@@ -5221,29 +5333,29 @@ var init_handler = __esm({
|
|
|
5221
5333
|
this.fsw._symlinkPaths.set(absPath, targetPath);
|
|
5222
5334
|
}
|
|
5223
5335
|
} else if (stats.isSymbolicLink()) {
|
|
5224
|
-
const targetPath = follow ? await fsrealpath(
|
|
5336
|
+
const targetPath = follow ? await fsrealpath(path15) : path15;
|
|
5225
5337
|
if (this.fsw.closed)
|
|
5226
5338
|
return;
|
|
5227
5339
|
const parent = sysPath.dirname(wh.watchPath);
|
|
5228
5340
|
this.fsw._getWatchedDir(parent).add(wh.watchPath);
|
|
5229
5341
|
this.fsw._emit(EV.ADD, wh.watchPath, stats);
|
|
5230
|
-
closer = await this._handleDir(parent, stats, initialAdd, depth,
|
|
5342
|
+
closer = await this._handleDir(parent, stats, initialAdd, depth, path15, wh, targetPath);
|
|
5231
5343
|
if (this.fsw.closed)
|
|
5232
5344
|
return;
|
|
5233
5345
|
if (targetPath !== void 0) {
|
|
5234
|
-
this.fsw._symlinkPaths.set(sysPath.resolve(
|
|
5346
|
+
this.fsw._symlinkPaths.set(sysPath.resolve(path15), targetPath);
|
|
5235
5347
|
}
|
|
5236
5348
|
} else {
|
|
5237
5349
|
closer = this._handleFile(wh.watchPath, stats, initialAdd);
|
|
5238
5350
|
}
|
|
5239
5351
|
ready();
|
|
5240
5352
|
if (closer)
|
|
5241
|
-
this.fsw._addPathCloser(
|
|
5353
|
+
this.fsw._addPathCloser(path15, closer);
|
|
5242
5354
|
return false;
|
|
5243
5355
|
} catch (error) {
|
|
5244
5356
|
if (this.fsw._handleError(error)) {
|
|
5245
5357
|
ready();
|
|
5246
|
-
return
|
|
5358
|
+
return path15;
|
|
5247
5359
|
}
|
|
5248
5360
|
}
|
|
5249
5361
|
}
|
|
@@ -5282,26 +5394,26 @@ function createPattern(matcher) {
|
|
|
5282
5394
|
}
|
|
5283
5395
|
return () => false;
|
|
5284
5396
|
}
|
|
5285
|
-
function normalizePath2(
|
|
5286
|
-
if (typeof
|
|
5397
|
+
function normalizePath2(path15) {
|
|
5398
|
+
if (typeof path15 !== "string")
|
|
5287
5399
|
throw new Error("string expected");
|
|
5288
|
-
|
|
5289
|
-
|
|
5400
|
+
path15 = sysPath2.normalize(path15);
|
|
5401
|
+
path15 = path15.replace(/\\/g, "/");
|
|
5290
5402
|
let prepend = false;
|
|
5291
|
-
if (
|
|
5403
|
+
if (path15.startsWith("//"))
|
|
5292
5404
|
prepend = true;
|
|
5293
5405
|
const DOUBLE_SLASH_RE2 = /\/\//;
|
|
5294
|
-
while (
|
|
5295
|
-
|
|
5406
|
+
while (path15.match(DOUBLE_SLASH_RE2))
|
|
5407
|
+
path15 = path15.replace(DOUBLE_SLASH_RE2, "/");
|
|
5296
5408
|
if (prepend)
|
|
5297
|
-
|
|
5298
|
-
return
|
|
5409
|
+
path15 = "/" + path15;
|
|
5410
|
+
return path15;
|
|
5299
5411
|
}
|
|
5300
5412
|
function matchPatterns(patterns, testString, stats) {
|
|
5301
|
-
const
|
|
5413
|
+
const path15 = normalizePath2(testString);
|
|
5302
5414
|
for (let index = 0; index < patterns.length; index++) {
|
|
5303
5415
|
const pattern = patterns[index];
|
|
5304
|
-
if (pattern(
|
|
5416
|
+
if (pattern(path15, stats)) {
|
|
5305
5417
|
return true;
|
|
5306
5418
|
}
|
|
5307
5419
|
}
|
|
@@ -5362,19 +5474,19 @@ var init_esm2 = __esm({
|
|
|
5362
5474
|
}
|
|
5363
5475
|
return str;
|
|
5364
5476
|
};
|
|
5365
|
-
normalizePathToUnix = (
|
|
5366
|
-
normalizeIgnored = (cwd = "") => (
|
|
5367
|
-
if (typeof
|
|
5368
|
-
return normalizePathToUnix(sysPath2.isAbsolute(
|
|
5477
|
+
normalizePathToUnix = (path15) => toUnix(sysPath2.normalize(toUnix(path15)));
|
|
5478
|
+
normalizeIgnored = (cwd = "") => (path15) => {
|
|
5479
|
+
if (typeof path15 === "string") {
|
|
5480
|
+
return normalizePathToUnix(sysPath2.isAbsolute(path15) ? path15 : sysPath2.join(cwd, path15));
|
|
5369
5481
|
} else {
|
|
5370
|
-
return
|
|
5482
|
+
return path15;
|
|
5371
5483
|
}
|
|
5372
5484
|
};
|
|
5373
|
-
getAbsolutePath = (
|
|
5374
|
-
if (sysPath2.isAbsolute(
|
|
5375
|
-
return
|
|
5485
|
+
getAbsolutePath = (path15, cwd) => {
|
|
5486
|
+
if (sysPath2.isAbsolute(path15)) {
|
|
5487
|
+
return path15;
|
|
5376
5488
|
}
|
|
5377
|
-
return sysPath2.join(cwd,
|
|
5489
|
+
return sysPath2.join(cwd, path15);
|
|
5378
5490
|
};
|
|
5379
5491
|
EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
|
|
5380
5492
|
DirEntry = class {
|
|
@@ -5429,10 +5541,10 @@ var init_esm2 = __esm({
|
|
|
5429
5541
|
STAT_METHOD_F = "stat";
|
|
5430
5542
|
STAT_METHOD_L = "lstat";
|
|
5431
5543
|
WatchHelper = class {
|
|
5432
|
-
constructor(
|
|
5544
|
+
constructor(path15, follow, fsw) {
|
|
5433
5545
|
this.fsw = fsw;
|
|
5434
|
-
const watchPath =
|
|
5435
|
-
this.path =
|
|
5546
|
+
const watchPath = path15;
|
|
5547
|
+
this.path = path15 = path15.replace(REPLACER_RE, "");
|
|
5436
5548
|
this.watchPath = watchPath;
|
|
5437
5549
|
this.fullWatchPath = sysPath2.resolve(watchPath);
|
|
5438
5550
|
this.dirParts = [];
|
|
@@ -5554,20 +5666,20 @@ var init_esm2 = __esm({
|
|
|
5554
5666
|
this._closePromise = void 0;
|
|
5555
5667
|
let paths = unifyPaths(paths_);
|
|
5556
5668
|
if (cwd) {
|
|
5557
|
-
paths = paths.map((
|
|
5558
|
-
const absPath = getAbsolutePath(
|
|
5669
|
+
paths = paths.map((path15) => {
|
|
5670
|
+
const absPath = getAbsolutePath(path15, cwd);
|
|
5559
5671
|
return absPath;
|
|
5560
5672
|
});
|
|
5561
5673
|
}
|
|
5562
|
-
paths.forEach((
|
|
5563
|
-
this._removeIgnoredPath(
|
|
5674
|
+
paths.forEach((path15) => {
|
|
5675
|
+
this._removeIgnoredPath(path15);
|
|
5564
5676
|
});
|
|
5565
5677
|
this._userIgnored = void 0;
|
|
5566
5678
|
if (!this._readyCount)
|
|
5567
5679
|
this._readyCount = 0;
|
|
5568
5680
|
this._readyCount += paths.length;
|
|
5569
|
-
Promise.all(paths.map(async (
|
|
5570
|
-
const res = await this._nodeFsHandler._addToNodeFs(
|
|
5681
|
+
Promise.all(paths.map(async (path15) => {
|
|
5682
|
+
const res = await this._nodeFsHandler._addToNodeFs(path15, !_internal, void 0, 0, _origAdd);
|
|
5571
5683
|
if (res)
|
|
5572
5684
|
this._emitReady();
|
|
5573
5685
|
return res;
|
|
@@ -5589,17 +5701,17 @@ var init_esm2 = __esm({
|
|
|
5589
5701
|
return this;
|
|
5590
5702
|
const paths = unifyPaths(paths_);
|
|
5591
5703
|
const { cwd } = this.options;
|
|
5592
|
-
paths.forEach((
|
|
5593
|
-
if (!sysPath2.isAbsolute(
|
|
5704
|
+
paths.forEach((path15) => {
|
|
5705
|
+
if (!sysPath2.isAbsolute(path15) && !this._closers.has(path15)) {
|
|
5594
5706
|
if (cwd)
|
|
5595
|
-
|
|
5596
|
-
|
|
5707
|
+
path15 = sysPath2.join(cwd, path15);
|
|
5708
|
+
path15 = sysPath2.resolve(path15);
|
|
5597
5709
|
}
|
|
5598
|
-
this._closePath(
|
|
5599
|
-
this._addIgnoredPath(
|
|
5600
|
-
if (this._watched.has(
|
|
5710
|
+
this._closePath(path15);
|
|
5711
|
+
this._addIgnoredPath(path15);
|
|
5712
|
+
if (this._watched.has(path15)) {
|
|
5601
5713
|
this._addIgnoredPath({
|
|
5602
|
-
path:
|
|
5714
|
+
path: path15,
|
|
5603
5715
|
recursive: true
|
|
5604
5716
|
});
|
|
5605
5717
|
}
|
|
@@ -5663,38 +5775,38 @@ var init_esm2 = __esm({
|
|
|
5663
5775
|
* @param stats arguments to be passed with event
|
|
5664
5776
|
* @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
|
|
5665
5777
|
*/
|
|
5666
|
-
async _emit(event,
|
|
5778
|
+
async _emit(event, path15, stats) {
|
|
5667
5779
|
if (this.closed)
|
|
5668
5780
|
return;
|
|
5669
5781
|
const opts = this.options;
|
|
5670
5782
|
if (isWindows)
|
|
5671
|
-
|
|
5783
|
+
path15 = sysPath2.normalize(path15);
|
|
5672
5784
|
if (opts.cwd)
|
|
5673
|
-
|
|
5674
|
-
const args = [
|
|
5785
|
+
path15 = sysPath2.relative(opts.cwd, path15);
|
|
5786
|
+
const args = [path15];
|
|
5675
5787
|
if (stats != null)
|
|
5676
5788
|
args.push(stats);
|
|
5677
5789
|
const awf = opts.awaitWriteFinish;
|
|
5678
5790
|
let pw;
|
|
5679
|
-
if (awf && (pw = this._pendingWrites.get(
|
|
5791
|
+
if (awf && (pw = this._pendingWrites.get(path15))) {
|
|
5680
5792
|
pw.lastChange = /* @__PURE__ */ new Date();
|
|
5681
5793
|
return this;
|
|
5682
5794
|
}
|
|
5683
5795
|
if (opts.atomic) {
|
|
5684
5796
|
if (event === EVENTS.UNLINK) {
|
|
5685
|
-
this._pendingUnlinks.set(
|
|
5797
|
+
this._pendingUnlinks.set(path15, [event, ...args]);
|
|
5686
5798
|
setTimeout(() => {
|
|
5687
|
-
this._pendingUnlinks.forEach((entry,
|
|
5799
|
+
this._pendingUnlinks.forEach((entry, path16) => {
|
|
5688
5800
|
this.emit(...entry);
|
|
5689
5801
|
this.emit(EVENTS.ALL, ...entry);
|
|
5690
|
-
this._pendingUnlinks.delete(
|
|
5802
|
+
this._pendingUnlinks.delete(path16);
|
|
5691
5803
|
});
|
|
5692
5804
|
}, typeof opts.atomic === "number" ? opts.atomic : 100);
|
|
5693
5805
|
return this;
|
|
5694
5806
|
}
|
|
5695
|
-
if (event === EVENTS.ADD && this._pendingUnlinks.has(
|
|
5807
|
+
if (event === EVENTS.ADD && this._pendingUnlinks.has(path15)) {
|
|
5696
5808
|
event = EVENTS.CHANGE;
|
|
5697
|
-
this._pendingUnlinks.delete(
|
|
5809
|
+
this._pendingUnlinks.delete(path15);
|
|
5698
5810
|
}
|
|
5699
5811
|
}
|
|
5700
5812
|
if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
|
|
@@ -5712,16 +5824,16 @@ var init_esm2 = __esm({
|
|
|
5712
5824
|
this.emitWithAll(event, args);
|
|
5713
5825
|
}
|
|
5714
5826
|
};
|
|
5715
|
-
this._awaitWriteFinish(
|
|
5827
|
+
this._awaitWriteFinish(path15, awf.stabilityThreshold, event, awfEmit);
|
|
5716
5828
|
return this;
|
|
5717
5829
|
}
|
|
5718
5830
|
if (event === EVENTS.CHANGE) {
|
|
5719
|
-
const isThrottled = !this._throttle(EVENTS.CHANGE,
|
|
5831
|
+
const isThrottled = !this._throttle(EVENTS.CHANGE, path15, 50);
|
|
5720
5832
|
if (isThrottled)
|
|
5721
5833
|
return this;
|
|
5722
5834
|
}
|
|
5723
5835
|
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,
|
|
5836
|
+
const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path15) : path15;
|
|
5725
5837
|
let stats2;
|
|
5726
5838
|
try {
|
|
5727
5839
|
stats2 = await stat3(fullPath);
|
|
@@ -5752,23 +5864,23 @@ var init_esm2 = __esm({
|
|
|
5752
5864
|
* @param timeout duration of time to suppress duplicate actions
|
|
5753
5865
|
* @returns tracking object or false if action should be suppressed
|
|
5754
5866
|
*/
|
|
5755
|
-
_throttle(actionType,
|
|
5867
|
+
_throttle(actionType, path15, timeout) {
|
|
5756
5868
|
if (!this._throttled.has(actionType)) {
|
|
5757
5869
|
this._throttled.set(actionType, /* @__PURE__ */ new Map());
|
|
5758
5870
|
}
|
|
5759
5871
|
const action = this._throttled.get(actionType);
|
|
5760
5872
|
if (!action)
|
|
5761
5873
|
throw new Error("invalid throttle");
|
|
5762
|
-
const actionPath = action.get(
|
|
5874
|
+
const actionPath = action.get(path15);
|
|
5763
5875
|
if (actionPath) {
|
|
5764
5876
|
actionPath.count++;
|
|
5765
5877
|
return false;
|
|
5766
5878
|
}
|
|
5767
5879
|
let timeoutObject;
|
|
5768
5880
|
const clear = () => {
|
|
5769
|
-
const item = action.get(
|
|
5881
|
+
const item = action.get(path15);
|
|
5770
5882
|
const count = item ? item.count : 0;
|
|
5771
|
-
action.delete(
|
|
5883
|
+
action.delete(path15);
|
|
5772
5884
|
clearTimeout(timeoutObject);
|
|
5773
5885
|
if (item)
|
|
5774
5886
|
clearTimeout(item.timeoutObject);
|
|
@@ -5776,7 +5888,7 @@ var init_esm2 = __esm({
|
|
|
5776
5888
|
};
|
|
5777
5889
|
timeoutObject = setTimeout(clear, timeout);
|
|
5778
5890
|
const thr = { timeoutObject, clear, count: 0 };
|
|
5779
|
-
action.set(
|
|
5891
|
+
action.set(path15, thr);
|
|
5780
5892
|
return thr;
|
|
5781
5893
|
}
|
|
5782
5894
|
_incrReadyCount() {
|
|
@@ -5790,44 +5902,44 @@ var init_esm2 = __esm({
|
|
|
5790
5902
|
* @param event
|
|
5791
5903
|
* @param awfEmit Callback to be called when ready for event to be emitted.
|
|
5792
5904
|
*/
|
|
5793
|
-
_awaitWriteFinish(
|
|
5905
|
+
_awaitWriteFinish(path15, threshold, event, awfEmit) {
|
|
5794
5906
|
const awf = this.options.awaitWriteFinish;
|
|
5795
5907
|
if (typeof awf !== "object")
|
|
5796
5908
|
return;
|
|
5797
5909
|
const pollInterval = awf.pollInterval;
|
|
5798
5910
|
let timeoutHandler;
|
|
5799
|
-
let fullPath =
|
|
5800
|
-
if (this.options.cwd && !sysPath2.isAbsolute(
|
|
5801
|
-
fullPath = sysPath2.join(this.options.cwd,
|
|
5911
|
+
let fullPath = path15;
|
|
5912
|
+
if (this.options.cwd && !sysPath2.isAbsolute(path15)) {
|
|
5913
|
+
fullPath = sysPath2.join(this.options.cwd, path15);
|
|
5802
5914
|
}
|
|
5803
5915
|
const now = /* @__PURE__ */ new Date();
|
|
5804
5916
|
const writes = this._pendingWrites;
|
|
5805
5917
|
function awaitWriteFinishFn(prevStat) {
|
|
5806
5918
|
statcb(fullPath, (err, curStat) => {
|
|
5807
|
-
if (err || !writes.has(
|
|
5919
|
+
if (err || !writes.has(path15)) {
|
|
5808
5920
|
if (err && err.code !== "ENOENT")
|
|
5809
5921
|
awfEmit(err);
|
|
5810
5922
|
return;
|
|
5811
5923
|
}
|
|
5812
5924
|
const now2 = Number(/* @__PURE__ */ new Date());
|
|
5813
5925
|
if (prevStat && curStat.size !== prevStat.size) {
|
|
5814
|
-
writes.get(
|
|
5926
|
+
writes.get(path15).lastChange = now2;
|
|
5815
5927
|
}
|
|
5816
|
-
const pw = writes.get(
|
|
5928
|
+
const pw = writes.get(path15);
|
|
5817
5929
|
const df = now2 - pw.lastChange;
|
|
5818
5930
|
if (df >= threshold) {
|
|
5819
|
-
writes.delete(
|
|
5931
|
+
writes.delete(path15);
|
|
5820
5932
|
awfEmit(void 0, curStat);
|
|
5821
5933
|
} else {
|
|
5822
5934
|
timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
|
|
5823
5935
|
}
|
|
5824
5936
|
});
|
|
5825
5937
|
}
|
|
5826
|
-
if (!writes.has(
|
|
5827
|
-
writes.set(
|
|
5938
|
+
if (!writes.has(path15)) {
|
|
5939
|
+
writes.set(path15, {
|
|
5828
5940
|
lastChange: now,
|
|
5829
5941
|
cancelWait: () => {
|
|
5830
|
-
writes.delete(
|
|
5942
|
+
writes.delete(path15);
|
|
5831
5943
|
clearTimeout(timeoutHandler);
|
|
5832
5944
|
return event;
|
|
5833
5945
|
}
|
|
@@ -5838,8 +5950,8 @@ var init_esm2 = __esm({
|
|
|
5838
5950
|
/**
|
|
5839
5951
|
* Determines whether user has asked to ignore this path.
|
|
5840
5952
|
*/
|
|
5841
|
-
_isIgnored(
|
|
5842
|
-
if (this.options.atomic && DOT_RE.test(
|
|
5953
|
+
_isIgnored(path15, stats) {
|
|
5954
|
+
if (this.options.atomic && DOT_RE.test(path15))
|
|
5843
5955
|
return true;
|
|
5844
5956
|
if (!this._userIgnored) {
|
|
5845
5957
|
const { cwd } = this.options;
|
|
@@ -5849,17 +5961,17 @@ var init_esm2 = __esm({
|
|
|
5849
5961
|
const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
|
|
5850
5962
|
this._userIgnored = anymatch(list, void 0);
|
|
5851
5963
|
}
|
|
5852
|
-
return this._userIgnored(
|
|
5964
|
+
return this._userIgnored(path15, stats);
|
|
5853
5965
|
}
|
|
5854
|
-
_isntIgnored(
|
|
5855
|
-
return !this._isIgnored(
|
|
5966
|
+
_isntIgnored(path15, stat4) {
|
|
5967
|
+
return !this._isIgnored(path15, stat4);
|
|
5856
5968
|
}
|
|
5857
5969
|
/**
|
|
5858
5970
|
* Provides a set of common helpers and properties relating to symlink handling.
|
|
5859
5971
|
* @param path file or directory pattern being watched
|
|
5860
5972
|
*/
|
|
5861
|
-
_getWatchHelpers(
|
|
5862
|
-
return new WatchHelper(
|
|
5973
|
+
_getWatchHelpers(path15) {
|
|
5974
|
+
return new WatchHelper(path15, this.options.followSymlinks, this);
|
|
5863
5975
|
}
|
|
5864
5976
|
// Directory helpers
|
|
5865
5977
|
// -----------------
|
|
@@ -5891,63 +6003,63 @@ var init_esm2 = __esm({
|
|
|
5891
6003
|
* @param item base path of item/directory
|
|
5892
6004
|
*/
|
|
5893
6005
|
_remove(directory, item, isDirectory) {
|
|
5894
|
-
const
|
|
5895
|
-
const fullPath = sysPath2.resolve(
|
|
5896
|
-
isDirectory = isDirectory != null ? isDirectory : this._watched.has(
|
|
5897
|
-
if (!this._throttle("remove",
|
|
6006
|
+
const path15 = sysPath2.join(directory, item);
|
|
6007
|
+
const fullPath = sysPath2.resolve(path15);
|
|
6008
|
+
isDirectory = isDirectory != null ? isDirectory : this._watched.has(path15) || this._watched.has(fullPath);
|
|
6009
|
+
if (!this._throttle("remove", path15, 100))
|
|
5898
6010
|
return;
|
|
5899
6011
|
if (!isDirectory && this._watched.size === 1) {
|
|
5900
6012
|
this.add(directory, item, true);
|
|
5901
6013
|
}
|
|
5902
|
-
const wp = this._getWatchedDir(
|
|
6014
|
+
const wp = this._getWatchedDir(path15);
|
|
5903
6015
|
const nestedDirectoryChildren = wp.getChildren();
|
|
5904
|
-
nestedDirectoryChildren.forEach((nested) => this._remove(
|
|
6016
|
+
nestedDirectoryChildren.forEach((nested) => this._remove(path15, nested));
|
|
5905
6017
|
const parent = this._getWatchedDir(directory);
|
|
5906
6018
|
const wasTracked = parent.has(item);
|
|
5907
6019
|
parent.remove(item);
|
|
5908
6020
|
if (this._symlinkPaths.has(fullPath)) {
|
|
5909
6021
|
this._symlinkPaths.delete(fullPath);
|
|
5910
6022
|
}
|
|
5911
|
-
let relPath =
|
|
6023
|
+
let relPath = path15;
|
|
5912
6024
|
if (this.options.cwd)
|
|
5913
|
-
relPath = sysPath2.relative(this.options.cwd,
|
|
6025
|
+
relPath = sysPath2.relative(this.options.cwd, path15);
|
|
5914
6026
|
if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
|
|
5915
6027
|
const event = this._pendingWrites.get(relPath).cancelWait();
|
|
5916
6028
|
if (event === EVENTS.ADD)
|
|
5917
6029
|
return;
|
|
5918
6030
|
}
|
|
5919
|
-
this._watched.delete(
|
|
6031
|
+
this._watched.delete(path15);
|
|
5920
6032
|
this._watched.delete(fullPath);
|
|
5921
6033
|
const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
|
|
5922
|
-
if (wasTracked && !this._isIgnored(
|
|
5923
|
-
this._emit(eventName,
|
|
5924
|
-
this._closePath(
|
|
6034
|
+
if (wasTracked && !this._isIgnored(path15))
|
|
6035
|
+
this._emit(eventName, path15);
|
|
6036
|
+
this._closePath(path15);
|
|
5925
6037
|
}
|
|
5926
6038
|
/**
|
|
5927
6039
|
* Closes all watchers for a path
|
|
5928
6040
|
*/
|
|
5929
|
-
_closePath(
|
|
5930
|
-
this._closeFile(
|
|
5931
|
-
const dir = sysPath2.dirname(
|
|
5932
|
-
this._getWatchedDir(dir).remove(sysPath2.basename(
|
|
6041
|
+
_closePath(path15) {
|
|
6042
|
+
this._closeFile(path15);
|
|
6043
|
+
const dir = sysPath2.dirname(path15);
|
|
6044
|
+
this._getWatchedDir(dir).remove(sysPath2.basename(path15));
|
|
5933
6045
|
}
|
|
5934
6046
|
/**
|
|
5935
6047
|
* Closes only file-specific watchers
|
|
5936
6048
|
*/
|
|
5937
|
-
_closeFile(
|
|
5938
|
-
const closers = this._closers.get(
|
|
6049
|
+
_closeFile(path15) {
|
|
6050
|
+
const closers = this._closers.get(path15);
|
|
5939
6051
|
if (!closers)
|
|
5940
6052
|
return;
|
|
5941
6053
|
closers.forEach((closer) => closer());
|
|
5942
|
-
this._closers.delete(
|
|
6054
|
+
this._closers.delete(path15);
|
|
5943
6055
|
}
|
|
5944
|
-
_addPathCloser(
|
|
6056
|
+
_addPathCloser(path15, closer) {
|
|
5945
6057
|
if (!closer)
|
|
5946
6058
|
return;
|
|
5947
|
-
let list = this._closers.get(
|
|
6059
|
+
let list = this._closers.get(path15);
|
|
5948
6060
|
if (!list) {
|
|
5949
6061
|
list = [];
|
|
5950
|
-
this._closers.set(
|
|
6062
|
+
this._closers.set(path15, list);
|
|
5951
6063
|
}
|
|
5952
6064
|
list.push(closer);
|
|
5953
6065
|
}
|
|
@@ -5974,23 +6086,36 @@ var init_esm2 = __esm({
|
|
|
5974
6086
|
});
|
|
5975
6087
|
|
|
5976
6088
|
// src/cli/watcher.ts
|
|
6089
|
+
import path12 from "path";
|
|
5977
6090
|
function startWatcher(options) {
|
|
5978
6091
|
const { rootDir, patterns, appDir } = options;
|
|
5979
6092
|
let rebuildTimer = null;
|
|
6093
|
+
let pendingFiles = /* @__PURE__ */ new Set();
|
|
5980
6094
|
async function rebuildRoutes() {
|
|
5981
6095
|
try {
|
|
5982
6096
|
const timestamp = Date.now();
|
|
5983
6097
|
globalThis.__FAAPI_LOAD_TS__ = timestamp;
|
|
5984
6098
|
invalidateMiddlewareCache();
|
|
5985
6099
|
invalidateProgramCache();
|
|
5986
|
-
const
|
|
6100
|
+
const filesToCompile = Array.from(pendingFiles);
|
|
6101
|
+
pendingFiles = /* @__PURE__ */ new Set();
|
|
6102
|
+
if (filesToCompile.length > 0) {
|
|
6103
|
+
await compileRoutes({
|
|
6104
|
+
rootDir,
|
|
6105
|
+
appDir,
|
|
6106
|
+
outDir: DEV_OUT_DIR,
|
|
6107
|
+
files: filesToCompile
|
|
6108
|
+
});
|
|
6109
|
+
}
|
|
6110
|
+
const { routes, wsRoutes } = await scanRoutes(rootDir, patterns, appDir, DEV_OUT_DIR);
|
|
5987
6111
|
const sorted = sortRoutes(routes);
|
|
5988
|
-
const
|
|
5989
|
-
|
|
6112
|
+
const schemaPath = path12.resolve(rootDir, DEV_OUT_DIR, "faapi-schema.js");
|
|
6113
|
+
await generateSchemaFile(sorted, rootDir, schemaPath);
|
|
6114
|
+
await loadSchemaToRegistry(schemaPath, rootDir, DEV_OUT_DIR, false);
|
|
5990
6115
|
updateServerRoutes(sorted, wsRoutes);
|
|
5991
|
-
const
|
|
6116
|
+
const recompiledCount = filesToCompile.length;
|
|
5992
6117
|
console.log(
|
|
5993
|
-
`- Routes rebuilt: ${sorted.length} route(s), ${
|
|
6118
|
+
`- Routes rebuilt: ${sorted.length} route(s), ${wsRoutes.length} WS route(s)${recompiledCount > 0 ? `, ${recompiledCount} file(s) recompiled` : ""}`
|
|
5994
6119
|
);
|
|
5995
6120
|
} catch (err) {
|
|
5996
6121
|
console.error("- Error rebuilding routes:", err instanceof Error ? err.message : String(err));
|
|
@@ -6003,21 +6128,45 @@ function startWatcher(options) {
|
|
|
6003
6128
|
void rebuildRoutes();
|
|
6004
6129
|
}, 100);
|
|
6005
6130
|
}
|
|
6006
|
-
const
|
|
6007
|
-
const watcher = esm_default.watch(watchPatterns, {
|
|
6131
|
+
const watcher = esm_default.watch(appDir, {
|
|
6008
6132
|
cwd: rootDir,
|
|
6009
6133
|
ignoreInitial: true,
|
|
6010
|
-
ignored:
|
|
6134
|
+
ignored: (filePath, stats) => {
|
|
6135
|
+
if (filePath.includes("node_modules") || filePath.includes(".faapi") || filePath.includes("dist") || filePath.includes(".git")) {
|
|
6136
|
+
return true;
|
|
6137
|
+
}
|
|
6138
|
+
if (!stats) return false;
|
|
6139
|
+
if (stats.isDirectory()) return false;
|
|
6140
|
+
return !filePath.endsWith(".ts");
|
|
6141
|
+
}
|
|
6142
|
+
});
|
|
6143
|
+
watcher.on("add", (file) => {
|
|
6144
|
+
pendingFiles.add(path12.resolve(rootDir, file));
|
|
6145
|
+
scheduleRebuild();
|
|
6146
|
+
});
|
|
6147
|
+
watcher.on("change", (file) => {
|
|
6148
|
+
pendingFiles.add(path12.resolve(rootDir, file));
|
|
6149
|
+
scheduleRebuild();
|
|
6150
|
+
});
|
|
6151
|
+
watcher.on("unlink", () => {
|
|
6152
|
+
scheduleRebuild();
|
|
6153
|
+
});
|
|
6154
|
+
watcher.on("error", (err) => {
|
|
6155
|
+
console.error("- Watcher error:", err instanceof Error ? err.message : String(err));
|
|
6156
|
+
});
|
|
6157
|
+
watcher.on("ready", () => {
|
|
6158
|
+
const watched = watcher.getWatched();
|
|
6159
|
+
const dirCount = Object.keys(watched).length;
|
|
6160
|
+
const fileCount = Object.values(watched).reduce((sum, files) => sum + files.length, 0);
|
|
6161
|
+
console.log(`- Watcher ready: ${dirCount} dir(s), ${fileCount} file(s) watched`);
|
|
6011
6162
|
});
|
|
6012
|
-
watcher.on("add", () => scheduleRebuild());
|
|
6013
|
-
watcher.on("change", () => scheduleRebuild());
|
|
6014
|
-
watcher.on("unlink", () => scheduleRebuild());
|
|
6015
6163
|
console.log("- Watch mode enabled");
|
|
6016
6164
|
}
|
|
6017
6165
|
function updateServerRoutes(routes, wsRoutes) {
|
|
6018
6166
|
globalThis.__FAAPI_ROUTES__ = routes;
|
|
6019
6167
|
globalThis.__FAAPI_WS_ROUTES__ = wsRoutes;
|
|
6020
6168
|
}
|
|
6169
|
+
var DEV_OUT_DIR;
|
|
6021
6170
|
var init_watcher = __esm({
|
|
6022
6171
|
"src/cli/watcher.ts"() {
|
|
6023
6172
|
"use strict";
|
|
@@ -6026,15 +6175,16 @@ var init_watcher = __esm({
|
|
|
6026
6175
|
init_sortRoutes();
|
|
6027
6176
|
init_loadMiddlewares();
|
|
6028
6177
|
init_createProgram();
|
|
6029
|
-
init_schemaRegistry();
|
|
6030
6178
|
init_generateSchema();
|
|
6179
|
+
init_compileRoutes();
|
|
6180
|
+
DEV_OUT_DIR = ".faapi/dev";
|
|
6031
6181
|
}
|
|
6032
6182
|
});
|
|
6033
6183
|
|
|
6034
6184
|
// src/config/loadConfig.ts
|
|
6035
|
-
import
|
|
6036
|
-
import
|
|
6037
|
-
import
|
|
6185
|
+
import path13 from "path";
|
|
6186
|
+
import fs8 from "fs";
|
|
6187
|
+
import os from "os";
|
|
6038
6188
|
function getEnv() {
|
|
6039
6189
|
return process.env.FAAPI_ENV || process.env.NODE_ENV || "development";
|
|
6040
6190
|
}
|
|
@@ -6059,12 +6209,15 @@ function deepMerge(base, override) {
|
|
|
6059
6209
|
return result;
|
|
6060
6210
|
}
|
|
6061
6211
|
async function loadConfigFile(filePath) {
|
|
6062
|
-
if (!
|
|
6212
|
+
if (!fs8.existsSync(filePath)) {
|
|
6063
6213
|
return null;
|
|
6064
6214
|
}
|
|
6065
6215
|
try {
|
|
6066
|
-
|
|
6067
|
-
|
|
6216
|
+
let modulePath = filePath;
|
|
6217
|
+
if (filePath.endsWith(".ts")) {
|
|
6218
|
+
modulePath = await compileConfigFile(filePath);
|
|
6219
|
+
}
|
|
6220
|
+
const module = await importWithCacheBust(modulePath);
|
|
6068
6221
|
return module.default ?? {};
|
|
6069
6222
|
} catch (err) {
|
|
6070
6223
|
throw new Error(
|
|
@@ -6073,17 +6226,41 @@ async function loadConfigFile(filePath) {
|
|
|
6073
6226
|
);
|
|
6074
6227
|
}
|
|
6075
6228
|
}
|
|
6229
|
+
async function compileConfigFile(tsPath) {
|
|
6230
|
+
const { createHash } = await import("crypto");
|
|
6231
|
+
const content = await fs8.promises.readFile(tsPath, "utf8");
|
|
6232
|
+
const hash = createHash("sha1").update(content).digest("hex").slice(0, 12);
|
|
6233
|
+
const tmpDir = path13.join(os.tmpdir(), "faapi-config");
|
|
6234
|
+
await fs8.promises.mkdir(tmpDir, { recursive: true });
|
|
6235
|
+
const outFile = path13.join(tmpDir, `config-${hash}.mjs`);
|
|
6236
|
+
if (fs8.existsSync(outFile)) {
|
|
6237
|
+
return outFile;
|
|
6238
|
+
}
|
|
6239
|
+
const esbuild = await import("esbuild");
|
|
6240
|
+
await esbuild.build({
|
|
6241
|
+
entryPoints: [tsPath],
|
|
6242
|
+
outfile: outFile,
|
|
6243
|
+
bundle: true,
|
|
6244
|
+
format: "esm",
|
|
6245
|
+
platform: "node",
|
|
6246
|
+
target: "node20",
|
|
6247
|
+
sourcemap: true,
|
|
6248
|
+
packages: "external",
|
|
6249
|
+
logLevel: "silent"
|
|
6250
|
+
});
|
|
6251
|
+
return outFile;
|
|
6252
|
+
}
|
|
6076
6253
|
async function loadConfig(rootDir, configPath) {
|
|
6077
6254
|
if (configPath) {
|
|
6078
|
-
const resolvedPath =
|
|
6079
|
-
if (!
|
|
6255
|
+
const resolvedPath = path13.resolve(rootDir, configPath);
|
|
6256
|
+
if (!fs8.existsSync(resolvedPath)) {
|
|
6080
6257
|
throw new Error(`Config file not found: ${configPath}`);
|
|
6081
6258
|
}
|
|
6082
6259
|
return loadConfigFile(resolvedPath);
|
|
6083
6260
|
}
|
|
6084
6261
|
let baseConfig = null;
|
|
6085
6262
|
for (const fileName of BASE_CONFIG_FILES) {
|
|
6086
|
-
const filePath =
|
|
6263
|
+
const filePath = path13.join(rootDir, fileName);
|
|
6087
6264
|
baseConfig = await loadConfigFile(filePath);
|
|
6088
6265
|
if (baseConfig) break;
|
|
6089
6266
|
}
|
|
@@ -6093,7 +6270,7 @@ async function loadConfig(rootDir, configPath) {
|
|
|
6093
6270
|
const env = getEnv();
|
|
6094
6271
|
const envFiles = [`faapi.config.${env}.ts`, `faapi.config.${env}.js`];
|
|
6095
6272
|
for (const envFile of envFiles) {
|
|
6096
|
-
const envConfig = await loadConfigFile(
|
|
6273
|
+
const envConfig = await loadConfigFile(path13.join(rootDir, envFile));
|
|
6097
6274
|
if (envConfig) {
|
|
6098
6275
|
baseConfig = deepMerge(baseConfig, envConfig);
|
|
6099
6276
|
break;
|
|
@@ -6105,6 +6282,7 @@ var BASE_CONFIG_FILES;
|
|
|
6105
6282
|
var init_loadConfig = __esm({
|
|
6106
6283
|
"src/config/loadConfig.ts"() {
|
|
6107
6284
|
"use strict";
|
|
6285
|
+
init_importWithCacheBust();
|
|
6108
6286
|
BASE_CONFIG_FILES = ["faapi.config.ts", "faapi.config.js"];
|
|
6109
6287
|
}
|
|
6110
6288
|
});
|
|
@@ -6178,17 +6356,17 @@ var startCommand_exports = {};
|
|
|
6178
6356
|
__export(startCommand_exports, {
|
|
6179
6357
|
startCommand: () => startCommand
|
|
6180
6358
|
});
|
|
6181
|
-
import
|
|
6182
|
-
import
|
|
6183
|
-
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
6359
|
+
import fs9 from "fs";
|
|
6360
|
+
import path14 from "path";
|
|
6184
6361
|
async function startCommand(argv) {
|
|
6185
6362
|
const args = parseArgs(argv);
|
|
6186
6363
|
const rootDir = process.cwd();
|
|
6187
6364
|
const isProd = args.mode === "start";
|
|
6365
|
+
const prodDir = isProd ? PROD_OUT_DIR : DEV_OUT_DIR2;
|
|
6188
6366
|
if (isProd) {
|
|
6189
|
-
const routesPath =
|
|
6190
|
-
const
|
|
6191
|
-
if (!
|
|
6367
|
+
const routesPath = path14.resolve(rootDir, PROD_OUT_DIR, "faapi-routes.js");
|
|
6368
|
+
const schemaPath2 = path14.resolve(rootDir, PROD_OUT_DIR, "faapi-schema.js");
|
|
6369
|
+
if (!fs9.existsSync(routesPath) || !fs9.existsSync(schemaPath2)) {
|
|
6192
6370
|
console.error(
|
|
6193
6371
|
"[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
6372
|
);
|
|
@@ -6210,15 +6388,16 @@ async function startCommand(argv) {
|
|
|
6210
6388
|
let routes;
|
|
6211
6389
|
let wsRoutes;
|
|
6212
6390
|
if (isProd) {
|
|
6213
|
-
const routesPath =
|
|
6214
|
-
const serialized = await
|
|
6391
|
+
const routesPath = path14.resolve(rootDir, PROD_OUT_DIR, "faapi-routes.js");
|
|
6392
|
+
const serialized = await importWithCacheBust(routesPath);
|
|
6215
6393
|
const hydrated = await hydrateRoutes(serialized);
|
|
6216
6394
|
routes = hydrated.routes;
|
|
6217
6395
|
wsRoutes = hydrated.wsRoutes;
|
|
6218
6396
|
console.log(`- Routes loaded: ${routes.length} routes, ${wsRoutes.length} WS routes`);
|
|
6219
6397
|
} else {
|
|
6220
|
-
|
|
6221
|
-
|
|
6398
|
+
console.log("- Compiling TypeScript...");
|
|
6399
|
+
await compileRoutes({ rootDir, appDir: args.appDir, outDir: DEV_OUT_DIR2 });
|
|
6400
|
+
const scanned = await scanRoutes(rootDir, args.patterns, args.appDir, DEV_OUT_DIR2);
|
|
6222
6401
|
routes = scanned.routes;
|
|
6223
6402
|
wsRoutes = scanned.wsRoutes;
|
|
6224
6403
|
console.log(`- Routes scanned: ${routes.length} routes, ${wsRoutes.length} WS routes`);
|
|
@@ -6233,19 +6412,14 @@ async function startCommand(argv) {
|
|
|
6233
6412
|
}
|
|
6234
6413
|
}
|
|
6235
6414
|
}
|
|
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)`);
|
|
6415
|
+
const schemaPath = path14.resolve(rootDir, prodDir, "faapi-schema.js");
|
|
6416
|
+
if (!isProd) {
|
|
6417
|
+
await generateSchemaFile(sorted, rootDir, schemaPath);
|
|
6246
6418
|
}
|
|
6419
|
+
await loadSchemaToRegistry(schemaPath, rootDir, prodDir, isProd);
|
|
6420
|
+
console.log(`- Schema loaded: ${schemaPath}`);
|
|
6247
6421
|
if (!isProd && args.types) {
|
|
6248
|
-
const typesPath =
|
|
6422
|
+
const typesPath = path14.resolve(rootDir, args.types);
|
|
6249
6423
|
await generateTypes(sorted, rootDir, typesPath);
|
|
6250
6424
|
console.log(`- Types generated: ${typesPath}`);
|
|
6251
6425
|
}
|
|
@@ -6309,23 +6483,7 @@ async function startCommand(argv) {
|
|
|
6309
6483
|
function isFaapiConfigKey(key) {
|
|
6310
6484
|
return FAAPI_CONFIG_KEYS.has(key);
|
|
6311
6485
|
}
|
|
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;
|
|
6486
|
+
var DEV_OUT_DIR2, PROD_OUT_DIR, FAAPI_CONFIG_KEYS;
|
|
6329
6487
|
var init_startCommand = __esm({
|
|
6330
6488
|
"src/cli/startCommand.ts"() {
|
|
6331
6489
|
"use strict";
|
|
@@ -6337,10 +6495,13 @@ var init_startCommand = __esm({
|
|
|
6337
6495
|
init_generateTypes();
|
|
6338
6496
|
init_watcher();
|
|
6339
6497
|
init_loadConfig();
|
|
6340
|
-
init_schemaRegistry();
|
|
6341
6498
|
init_generateSchema();
|
|
6342
6499
|
init_generateRoutes();
|
|
6500
|
+
init_compileRoutes();
|
|
6343
6501
|
init_loadPlugins();
|
|
6502
|
+
init_importWithCacheBust();
|
|
6503
|
+
DEV_OUT_DIR2 = ".faapi/dev";
|
|
6504
|
+
PROD_OUT_DIR = "dist";
|
|
6344
6505
|
FAAPI_CONFIG_KEYS = /* @__PURE__ */ new Set([
|
|
6345
6506
|
"port",
|
|
6346
6507
|
"staticDir",
|
|
@@ -6357,9 +6518,6 @@ var init_startCommand = __esm({
|
|
|
6357
6518
|
});
|
|
6358
6519
|
|
|
6359
6520
|
// src/cli/index.ts
|
|
6360
|
-
function isTsxPreloaded() {
|
|
6361
|
-
return process.execArgv.some((arg) => arg.includes("tsx")) || (process.env.NODE_OPTIONS ?? "").includes("tsx");
|
|
6362
|
-
}
|
|
6363
6521
|
async function main() {
|
|
6364
6522
|
const argv = process.argv.slice(2);
|
|
6365
6523
|
const firstArg = argv.find((a) => !a.startsWith("-"));
|
|
@@ -6369,16 +6527,6 @@ async function main() {
|
|
|
6369
6527
|
await buildCommand2(options);
|
|
6370
6528
|
return;
|
|
6371
6529
|
}
|
|
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
6530
|
const { startCommand: startCommand2 } = await Promise.resolve().then(() => (init_startCommand(), startCommand_exports));
|
|
6383
6531
|
await startCommand2(argv);
|
|
6384
6532
|
}
|