@lark-apaas/devtool-kits 1.2.19 → 1.2.21
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/bin/generate-api-routes.cjs +189 -0
- package/dist/bin/generate-api-routes.cjs.map +1 -0
- package/dist/bin/generate-api-routes.d.cts +1 -0
- package/dist/bin/generate-api-routes.d.ts +1 -0
- package/dist/bin/generate-api-routes.js +43 -0
- package/dist/bin/generate-api-routes.js.map +1 -0
- package/dist/bin/generate-page-routes.cjs +227 -0
- package/dist/bin/generate-page-routes.cjs.map +1 -0
- package/dist/bin/generate-page-routes.d.cts +1 -0
- package/dist/bin/generate-page-routes.d.ts +1 -0
- package/dist/bin/generate-page-routes.js +43 -0
- package/dist/bin/generate-page-routes.js.map +1 -0
- package/dist/chunk-7QVYU63E.js +7 -0
- package/dist/chunk-7QVYU63E.js.map +1 -0
- package/dist/chunk-LSHFHCDF.js +350 -0
- package/dist/chunk-LSHFHCDF.js.map +1 -0
- package/dist/chunk-YPNLFWHQ.js +189 -0
- package/dist/chunk-YPNLFWHQ.js.map +1 -0
- package/dist/index.cjs +475 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +41 -3
- package/dist/index.d.ts +41 -3
- package/dist/index.js +202 -276
- package/dist/index.js.map +1 -1
- package/package.json +9 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
enhanceOpenApiWithSourceInfo,
|
|
3
|
+
parseApiRoutes,
|
|
4
|
+
transformOpenapiPaths
|
|
5
|
+
} from "./chunk-LSHFHCDF.js";
|
|
6
|
+
import {
|
|
7
|
+
buildFullPath,
|
|
8
|
+
calculateFileHash,
|
|
9
|
+
evaluateTemplateLiteral,
|
|
10
|
+
extractPageRouteInfo,
|
|
11
|
+
isRouteComponent,
|
|
12
|
+
parseRoutesFromFile,
|
|
13
|
+
routeParserLog
|
|
14
|
+
} from "./chunk-YPNLFWHQ.js";
|
|
15
|
+
import {
|
|
16
|
+
__name
|
|
17
|
+
} from "./chunk-7QVYU63E.js";
|
|
3
18
|
|
|
4
19
|
// src/utils/index.ts
|
|
5
20
|
function normalizeBasePath(basePath) {
|
|
@@ -1329,246 +1344,19 @@ function handleDevProxyError(err, req, res, options) {
|
|
|
1329
1344
|
__name(handleDevProxyError, "handleDevProxyError");
|
|
1330
1345
|
|
|
1331
1346
|
// src/middlewares/index.ts
|
|
1332
|
-
import
|
|
1347
|
+
import path4 from "path";
|
|
1333
1348
|
|
|
1334
1349
|
// src/middlewares/openapi/router.ts
|
|
1335
1350
|
import express from "express";
|
|
1336
1351
|
|
|
1337
1352
|
// src/middlewares/openapi/controller.ts
|
|
1338
|
-
import
|
|
1353
|
+
import fs3 from "fs/promises";
|
|
1339
1354
|
import crypto from "crypto";
|
|
1340
|
-
|
|
1341
|
-
// src/middlewares/openapi/services.ts
|
|
1342
|
-
import { promises as fs4 } from "fs";
|
|
1343
|
-
import path4 from "path";
|
|
1344
|
-
import ts from "typescript";
|
|
1345
|
-
|
|
1346
|
-
// src/middlewares/openapi/utils.ts
|
|
1347
|
-
import path3 from "path";
|
|
1348
|
-
import { promises as fs3 } from "fs";
|
|
1349
|
-
async function findControllerFiles(dir) {
|
|
1350
|
-
const files = [];
|
|
1351
|
-
async function scan(currentDir) {
|
|
1352
|
-
const entries = await fs3.readdir(currentDir, {
|
|
1353
|
-
withFileTypes: true
|
|
1354
|
-
});
|
|
1355
|
-
for (const entry of entries) {
|
|
1356
|
-
const fullPath = path3.join(currentDir, entry.name);
|
|
1357
|
-
if (entry.isDirectory()) {
|
|
1358
|
-
await scan(fullPath);
|
|
1359
|
-
} else if (entry.isFile() && entry.name.endsWith(".controller.ts")) {
|
|
1360
|
-
files.push(fullPath);
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
}
|
|
1364
|
-
__name(scan, "scan");
|
|
1365
|
-
await scan(dir);
|
|
1366
|
-
return files;
|
|
1367
|
-
}
|
|
1368
|
-
__name(findControllerFiles, "findControllerFiles");
|
|
1369
|
-
async function buildSourceMap(controllerFiles, processFile) {
|
|
1370
|
-
const sourceMap = /* @__PURE__ */ new Map();
|
|
1371
|
-
const concurrency = 10;
|
|
1372
|
-
const results = [];
|
|
1373
|
-
for (let i = 0; i < controllerFiles.length; i += concurrency) {
|
|
1374
|
-
const batch = controllerFiles.slice(i, i + concurrency);
|
|
1375
|
-
const batchResults = await Promise.all(batch.map((filePath) => processFile(filePath)));
|
|
1376
|
-
results.push(...batchResults);
|
|
1377
|
-
}
|
|
1378
|
-
for (const metadata of results) {
|
|
1379
|
-
for (const [operationId, info] of metadata.entries()) {
|
|
1380
|
-
sourceMap.set(operationId, info);
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
return sourceMap;
|
|
1384
|
-
}
|
|
1385
|
-
__name(buildSourceMap, "buildSourceMap");
|
|
1386
|
-
function findSourceInfo(operationId, sourceMap) {
|
|
1387
|
-
const directMatch = sourceMap.get(operationId);
|
|
1388
|
-
if (directMatch) {
|
|
1389
|
-
return directMatch;
|
|
1390
|
-
}
|
|
1391
|
-
for (const [key, value] of sourceMap.entries()) {
|
|
1392
|
-
const [className, methodName] = key.split("_");
|
|
1393
|
-
if (!className || !methodName) continue;
|
|
1394
|
-
const camelCaseId = className.charAt(0).toLowerCase() + className.slice(1) + methodName.charAt(0).toUpperCase() + methodName.slice(1);
|
|
1395
|
-
if (operationId === camelCaseId) {
|
|
1396
|
-
return value;
|
|
1397
|
-
}
|
|
1398
|
-
if (operationId === methodName) {
|
|
1399
|
-
return value;
|
|
1400
|
-
}
|
|
1401
|
-
}
|
|
1402
|
-
return void 0;
|
|
1403
|
-
}
|
|
1404
|
-
__name(findSourceInfo, "findSourceInfo");
|
|
1405
|
-
function enhanceOpenApiPaths(openapi, sourceMap) {
|
|
1406
|
-
let enhancedCount = 0;
|
|
1407
|
-
if (!openapi.paths) {
|
|
1408
|
-
return enhancedCount;
|
|
1409
|
-
}
|
|
1410
|
-
for (const pathItem of Object.values(openapi.paths)) {
|
|
1411
|
-
if (!pathItem || typeof pathItem !== "object") continue;
|
|
1412
|
-
for (const operation of Object.values(pathItem)) {
|
|
1413
|
-
if (operation && typeof operation === "object" && "operationId" in operation) {
|
|
1414
|
-
const sourceInfo = findSourceInfo(operation.operationId, sourceMap);
|
|
1415
|
-
if (sourceInfo) {
|
|
1416
|
-
operation["x-source"] = {
|
|
1417
|
-
file: sourceInfo.file,
|
|
1418
|
-
line: sourceInfo.line
|
|
1419
|
-
};
|
|
1420
|
-
enhancedCount++;
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
return enhancedCount;
|
|
1426
|
-
}
|
|
1427
|
-
__name(enhanceOpenApiPaths, "enhanceOpenApiPaths");
|
|
1428
|
-
function transformOpenapiPaths(openapi, basePath) {
|
|
1429
|
-
if (basePath === "/" || !openapi.paths) {
|
|
1430
|
-
return openapi;
|
|
1431
|
-
}
|
|
1432
|
-
const newPaths = {};
|
|
1433
|
-
Object.keys(openapi.paths).forEach((key) => {
|
|
1434
|
-
const staticApiKey = key.startsWith(basePath) ? key.slice(basePath.length) : key;
|
|
1435
|
-
newPaths[staticApiKey] = openapi.paths[key];
|
|
1436
|
-
});
|
|
1437
|
-
return {
|
|
1438
|
-
...openapi,
|
|
1439
|
-
paths: newPaths,
|
|
1440
|
-
basePath
|
|
1441
|
-
};
|
|
1442
|
-
}
|
|
1443
|
-
__name(transformOpenapiPaths, "transformOpenapiPaths");
|
|
1444
|
-
|
|
1445
|
-
// src/middlewares/openapi/services.ts
|
|
1446
|
-
async function enhanceOpenApiWithSourceInfo(options = {}) {
|
|
1447
|
-
const startTime = Date.now();
|
|
1448
|
-
const openapiPath = options.openapiPath || path4.resolve(__dirname, "../client/src/api/gen/openapi.json");
|
|
1449
|
-
const serverDir = options.serverDir || path4.resolve(__dirname, "../server");
|
|
1450
|
-
const writeFile2 = options.writeFile !== false;
|
|
1451
|
-
let openapi;
|
|
1452
|
-
if (options.openapiData) {
|
|
1453
|
-
openapi = JSON.parse(JSON.stringify(options.openapiData));
|
|
1454
|
-
} else {
|
|
1455
|
-
const openapiContent = await fs4.readFile(openapiPath, "utf-8");
|
|
1456
|
-
openapi = JSON.parse(openapiContent);
|
|
1457
|
-
}
|
|
1458
|
-
const controllerFiles = await findControllerFiles(serverDir);
|
|
1459
|
-
const sourceMap = await buildSourceMap(controllerFiles, processControllerFile);
|
|
1460
|
-
const enhanced = enhanceOpenApiPaths(openapi, sourceMap);
|
|
1461
|
-
if (writeFile2) {
|
|
1462
|
-
await fs4.writeFile(openapiPath, JSON.stringify(openapi, null, 2) + "\n", "utf-8");
|
|
1463
|
-
}
|
|
1464
|
-
const duration = Date.now() - startTime;
|
|
1465
|
-
return {
|
|
1466
|
-
openapi,
|
|
1467
|
-
stats: {
|
|
1468
|
-
duration,
|
|
1469
|
-
controllersFound: controllerFiles.length,
|
|
1470
|
-
endpointsExtracted: sourceMap.size,
|
|
1471
|
-
endpointsEnhanced: enhanced
|
|
1472
|
-
}
|
|
1473
|
-
};
|
|
1474
|
-
}
|
|
1475
|
-
__name(enhanceOpenApiWithSourceInfo, "enhanceOpenApiWithSourceInfo");
|
|
1476
|
-
async function processControllerFile(filePath) {
|
|
1477
|
-
const relativePath = path4.relative(process.cwd(), filePath);
|
|
1478
|
-
const content = await fs4.readFile(filePath, "utf-8");
|
|
1479
|
-
const sourceFile = ts.createSourceFile(filePath, content, ts.ScriptTarget.Latest, true);
|
|
1480
|
-
return extractControllerMetadata(sourceFile, relativePath);
|
|
1481
|
-
}
|
|
1482
|
-
__name(processControllerFile, "processControllerFile");
|
|
1483
|
-
function extractControllerMetadata(sourceFile, filePath) {
|
|
1484
|
-
const metadata = /* @__PURE__ */ new Map();
|
|
1485
|
-
let controllerPath = "";
|
|
1486
|
-
let className = "";
|
|
1487
|
-
function getDecorators(node) {
|
|
1488
|
-
if ("modifiers" in node && Array.isArray(node.modifiers)) {
|
|
1489
|
-
return node.modifiers.filter((mod) => mod.kind === ts.SyntaxKind.Decorator);
|
|
1490
|
-
}
|
|
1491
|
-
if ("decorators" in node && Array.isArray(node.decorators)) {
|
|
1492
|
-
return node.decorators;
|
|
1493
|
-
}
|
|
1494
|
-
return [];
|
|
1495
|
-
}
|
|
1496
|
-
__name(getDecorators, "getDecorators");
|
|
1497
|
-
function visit(node) {
|
|
1498
|
-
if (ts.isClassDeclaration(node)) {
|
|
1499
|
-
const decorators = getDecorators(node);
|
|
1500
|
-
if (node.name) {
|
|
1501
|
-
className = node.name.getText(sourceFile);
|
|
1502
|
-
}
|
|
1503
|
-
for (const decorator of decorators) {
|
|
1504
|
-
if (ts.isCallExpression(decorator.expression)) {
|
|
1505
|
-
const expression = decorator.expression;
|
|
1506
|
-
const decoratorName = expression.expression.getText(sourceFile);
|
|
1507
|
-
if (decoratorName === "Controller") {
|
|
1508
|
-
if (expression.arguments.length > 0) {
|
|
1509
|
-
const arg = expression.arguments[0];
|
|
1510
|
-
if (ts.isStringLiteral(arg)) {
|
|
1511
|
-
controllerPath = arg.text;
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
}
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
if (ts.isMethodDeclaration(node) && node.name) {
|
|
1519
|
-
const methodName = node.name.getText(sourceFile);
|
|
1520
|
-
let httpMethod = "";
|
|
1521
|
-
let routePath = "";
|
|
1522
|
-
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
1523
|
-
const decorators = getDecorators(node);
|
|
1524
|
-
for (const decorator of decorators) {
|
|
1525
|
-
if (ts.isCallExpression(decorator.expression)) {
|
|
1526
|
-
const decoratorName = decorator.expression.expression.getText(sourceFile);
|
|
1527
|
-
if ([
|
|
1528
|
-
"Get",
|
|
1529
|
-
"Post",
|
|
1530
|
-
"Put",
|
|
1531
|
-
"Delete",
|
|
1532
|
-
"Patch",
|
|
1533
|
-
"Options",
|
|
1534
|
-
"Head",
|
|
1535
|
-
"All"
|
|
1536
|
-
].includes(decoratorName)) {
|
|
1537
|
-
httpMethod = decoratorName.toLowerCase();
|
|
1538
|
-
if (decorator.expression.arguments.length > 0) {
|
|
1539
|
-
const arg = decorator.expression.arguments[0];
|
|
1540
|
-
if (ts.isStringLiteral(arg)) {
|
|
1541
|
-
routePath = arg.text;
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
}
|
|
1547
|
-
if (httpMethod && methodName && className) {
|
|
1548
|
-
const operationId = `${className}_${methodName}`;
|
|
1549
|
-
metadata.set(operationId, {
|
|
1550
|
-
file: filePath,
|
|
1551
|
-
line: line + 1,
|
|
1552
|
-
method: httpMethod,
|
|
1553
|
-
controllerPath,
|
|
1554
|
-
routePath
|
|
1555
|
-
});
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
ts.forEachChild(node, visit);
|
|
1559
|
-
}
|
|
1560
|
-
__name(visit, "visit");
|
|
1561
|
-
visit(sourceFile);
|
|
1562
|
-
return metadata;
|
|
1563
|
-
}
|
|
1564
|
-
__name(extractControllerMetadata, "extractControllerMetadata");
|
|
1565
|
-
|
|
1566
|
-
// src/middlewares/openapi/controller.ts
|
|
1567
1355
|
function createOpenapiHandler(openapiFilePath, enableEnhancement, serverDir) {
|
|
1568
1356
|
let cache = null;
|
|
1569
1357
|
return async (_req, res, context) => {
|
|
1570
1358
|
try {
|
|
1571
|
-
const fileBuffer = await
|
|
1359
|
+
const fileBuffer = await fs3.readFile(openapiFilePath, "utf-8");
|
|
1572
1360
|
const currentHash = crypto.createHash("md5").update(fileBuffer).digest("hex");
|
|
1573
1361
|
if (cache && cache.fileHash === currentHash) {
|
|
1574
1362
|
return res.json(cache.data);
|
|
@@ -1600,12 +1388,135 @@ function createOpenapiHandler(openapiFilePath, enableEnhancement, serverDir) {
|
|
|
1600
1388
|
}
|
|
1601
1389
|
__name(createOpenapiHandler, "createOpenapiHandler");
|
|
1602
1390
|
|
|
1391
|
+
// src/middlewares/openapi/spec-controller.ts
|
|
1392
|
+
import fs4 from "fs/promises";
|
|
1393
|
+
import crypto2 from "crypto";
|
|
1394
|
+
var MAX_REF_DEPTH = 5;
|
|
1395
|
+
function createOpenapiSpecHandler(openapiSpecFilePath) {
|
|
1396
|
+
let cache = null;
|
|
1397
|
+
return async (_req, res, context) => {
|
|
1398
|
+
try {
|
|
1399
|
+
const fileBuffer = await fs4.readFile(openapiSpecFilePath, "utf-8");
|
|
1400
|
+
const currentHash = crypto2.createHash("md5").update(fileBuffer).digest("hex");
|
|
1401
|
+
if (cache && cache.fileHash === currentHash) {
|
|
1402
|
+
return res.json(cache.data);
|
|
1403
|
+
}
|
|
1404
|
+
let parsed;
|
|
1405
|
+
try {
|
|
1406
|
+
parsed = JSON.parse(fileBuffer);
|
|
1407
|
+
} catch (err) {
|
|
1408
|
+
const message = err instanceof Error ? err.message : "Invalid JSON";
|
|
1409
|
+
return res.status(500).json({
|
|
1410
|
+
error: "Failed to load OpenAPI spec",
|
|
1411
|
+
message: `${openapiSpecFilePath} invalid: ${message}`
|
|
1412
|
+
});
|
|
1413
|
+
}
|
|
1414
|
+
if (!parsed || typeof parsed !== "object" || !parsed.paths || typeof parsed.paths !== "object") {
|
|
1415
|
+
return res.status(500).json({
|
|
1416
|
+
error: "Failed to load OpenAPI spec",
|
|
1417
|
+
message: `${openapiSpecFilePath} missing "paths"`
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
const result = buildFilteredSpec(parsed, context.basePath ?? "");
|
|
1421
|
+
cache = {
|
|
1422
|
+
data: result,
|
|
1423
|
+
fileHash: currentHash
|
|
1424
|
+
};
|
|
1425
|
+
res.json(result);
|
|
1426
|
+
} catch (error) {
|
|
1427
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1428
|
+
res.status(500).json({
|
|
1429
|
+
error: "Failed to load OpenAPI spec",
|
|
1430
|
+
message
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
__name(createOpenapiSpecHandler, "createOpenapiSpecHandler");
|
|
1436
|
+
function buildFilteredSpec(spec, globalPrefix) {
|
|
1437
|
+
const schemas = spec.components?.schemas ?? {};
|
|
1438
|
+
const paths = spec.paths ?? {};
|
|
1439
|
+
const filteredPaths = {};
|
|
1440
|
+
const cleanPrefix = globalPrefix.replace(/\/+$/, "");
|
|
1441
|
+
for (const [pathKey, methods] of Object.entries(paths)) {
|
|
1442
|
+
const cleanPath = stripPrefix(pathKey, cleanPrefix);
|
|
1443
|
+
if (!cleanPath.startsWith("/openapi")) continue;
|
|
1444
|
+
filteredPaths[cleanPath] = {};
|
|
1445
|
+
for (const [method, operation] of Object.entries(methods)) {
|
|
1446
|
+
if ([
|
|
1447
|
+
"parameters",
|
|
1448
|
+
"summary",
|
|
1449
|
+
"description",
|
|
1450
|
+
"servers"
|
|
1451
|
+
].includes(method)) continue;
|
|
1452
|
+
filteredPaths[cleanPath][method] = resolveRefsDeep(structuredClone(operation), schemas);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
return {
|
|
1456
|
+
openapi: spec.openapi ?? "3.0.0",
|
|
1457
|
+
paths: filteredPaths,
|
|
1458
|
+
components: {
|
|
1459
|
+
schemas: resolveAllSchemaRefs(structuredClone(schemas))
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
__name(buildFilteredSpec, "buildFilteredSpec");
|
|
1464
|
+
function stripPrefix(pathStr, prefix) {
|
|
1465
|
+
if (prefix && pathStr.startsWith(prefix)) {
|
|
1466
|
+
return pathStr.slice(prefix.length);
|
|
1467
|
+
}
|
|
1468
|
+
return pathStr;
|
|
1469
|
+
}
|
|
1470
|
+
__name(stripPrefix, "stripPrefix");
|
|
1471
|
+
function resolveRefsDeep(obj, schemas, depth = 0) {
|
|
1472
|
+
if (depth > MAX_REF_DEPTH || obj === null || obj === void 0) return obj;
|
|
1473
|
+
if (typeof obj !== "object") return obj;
|
|
1474
|
+
if (Array.isArray(obj)) {
|
|
1475
|
+
return obj.map((item) => resolveRefsDeep(item, schemas, depth));
|
|
1476
|
+
}
|
|
1477
|
+
const record = obj;
|
|
1478
|
+
if (typeof record["$ref"] === "string") {
|
|
1479
|
+
const resolved = resolveRef(record["$ref"], schemas);
|
|
1480
|
+
if (resolved !== void 0) {
|
|
1481
|
+
return resolveRefsDeep(structuredClone(resolved), schemas, depth + 1);
|
|
1482
|
+
}
|
|
1483
|
+
return record;
|
|
1484
|
+
}
|
|
1485
|
+
const result = {};
|
|
1486
|
+
for (const [key, value] of Object.entries(record)) {
|
|
1487
|
+
result[key] = resolveRefsDeep(value, schemas, depth);
|
|
1488
|
+
}
|
|
1489
|
+
return result;
|
|
1490
|
+
}
|
|
1491
|
+
__name(resolveRefsDeep, "resolveRefsDeep");
|
|
1492
|
+
function resolveRef(ref, schemas) {
|
|
1493
|
+
const prefix = "#/components/schemas/";
|
|
1494
|
+
if (ref.startsWith(prefix)) {
|
|
1495
|
+
const name = ref.slice(prefix.length);
|
|
1496
|
+
return schemas[name];
|
|
1497
|
+
}
|
|
1498
|
+
return void 0;
|
|
1499
|
+
}
|
|
1500
|
+
__name(resolveRef, "resolveRef");
|
|
1501
|
+
function resolveAllSchemaRefs(schemas) {
|
|
1502
|
+
const result = {};
|
|
1503
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
1504
|
+
result[name] = resolveRefsDeep(schema, schemas);
|
|
1505
|
+
}
|
|
1506
|
+
return result;
|
|
1507
|
+
}
|
|
1508
|
+
__name(resolveAllSchemaRefs, "resolveAllSchemaRefs");
|
|
1509
|
+
|
|
1603
1510
|
// src/middlewares/openapi/router.ts
|
|
1604
1511
|
function createOpenapiRouter(options, context) {
|
|
1605
|
-
const { openapiFilePath, enableEnhancement, serverDir } = options;
|
|
1512
|
+
const { openapiFilePath, openapiSpecFilePath, enableEnhancement, serverDir } = options;
|
|
1606
1513
|
const router = express.Router();
|
|
1607
1514
|
const handler = createOpenapiHandler(openapiFilePath, enableEnhancement, serverDir);
|
|
1608
1515
|
router.get("/openapi.json", (req, res) => handler(req, res, context));
|
|
1516
|
+
if (openapiSpecFilePath) {
|
|
1517
|
+
const specHandler = createOpenapiSpecHandler(openapiSpecFilePath);
|
|
1518
|
+
router.get("/openapi/spec", (req, res) => specHandler(req, res, context));
|
|
1519
|
+
}
|
|
1609
1520
|
return router;
|
|
1610
1521
|
}
|
|
1611
1522
|
__name(createOpenapiRouter, "createOpenapiRouter");
|
|
@@ -1616,18 +1527,25 @@ var OPENAPI_ROUTES = [
|
|
|
1616
1527
|
method: "GET",
|
|
1617
1528
|
path: "/openapi.json",
|
|
1618
1529
|
description: "Serve enhanced OpenAPI specification with source code references"
|
|
1530
|
+
},
|
|
1531
|
+
{
|
|
1532
|
+
method: "GET",
|
|
1533
|
+
path: "/openapi/spec",
|
|
1534
|
+
description: "Serve OpenAPI specification for /openapi/*"
|
|
1619
1535
|
}
|
|
1620
1536
|
];
|
|
1621
1537
|
function createOpenapiMiddleware(options) {
|
|
1622
|
-
const { openapiFilePath, enableEnhancement = true, serverDir } = options;
|
|
1538
|
+
const { openapiFilePath, openapiSpecFilePath, enableEnhancement = true, serverDir } = options;
|
|
1539
|
+
const routes = openapiSpecFilePath ? OPENAPI_ROUTES : OPENAPI_ROUTES.filter((route) => route.path !== "/openapi/spec");
|
|
1623
1540
|
return {
|
|
1624
1541
|
name: "openapi",
|
|
1625
1542
|
mountPath: "/dev",
|
|
1626
|
-
routes
|
|
1543
|
+
routes,
|
|
1627
1544
|
enabled: /* @__PURE__ */ __name((context) => context.isDev, "enabled"),
|
|
1628
1545
|
createRouter: /* @__PURE__ */ __name((context) => {
|
|
1629
1546
|
return createOpenapiRouter({
|
|
1630
1547
|
openapiFilePath,
|
|
1548
|
+
openapiSpecFilePath,
|
|
1631
1549
|
enableEnhancement,
|
|
1632
1550
|
serverDir
|
|
1633
1551
|
}, context);
|
|
@@ -1640,7 +1558,7 @@ __name(createOpenapiMiddleware, "createOpenapiMiddleware");
|
|
|
1640
1558
|
import express2 from "express";
|
|
1641
1559
|
|
|
1642
1560
|
// src/middlewares/dev-logs/utils.ts
|
|
1643
|
-
import { promises as
|
|
1561
|
+
import { promises as fs5 } from "fs";
|
|
1644
1562
|
import { isAbsolute, join as join2, relative } from "path";
|
|
1645
1563
|
|
|
1646
1564
|
// src/middlewares/dev-logs/helper/path-matcher.ts
|
|
@@ -1673,8 +1591,8 @@ function hasSpecialPatterns(pattern) {
|
|
|
1673
1591
|
return /[{*]/.test(pattern);
|
|
1674
1592
|
}
|
|
1675
1593
|
__name(hasSpecialPatterns, "hasSpecialPatterns");
|
|
1676
|
-
function normalizePathForMatching(
|
|
1677
|
-
return
|
|
1594
|
+
function normalizePathForMatching(path5) {
|
|
1595
|
+
return path5.replace(/\/+/g, "/").replace(/\/+$/, "");
|
|
1678
1596
|
}
|
|
1679
1597
|
__name(normalizePathForMatching, "normalizePathForMatching");
|
|
1680
1598
|
|
|
@@ -1692,7 +1610,7 @@ function getRelativePath(filePath) {
|
|
|
1692
1610
|
__name(getRelativePath, "getRelativePath");
|
|
1693
1611
|
async function fileExists(filePath) {
|
|
1694
1612
|
try {
|
|
1695
|
-
await
|
|
1613
|
+
await fs5.access(filePath);
|
|
1696
1614
|
return true;
|
|
1697
1615
|
} catch {
|
|
1698
1616
|
return false;
|
|
@@ -1775,9 +1693,9 @@ __name(serializeError, "serializeError");
|
|
|
1775
1693
|
import { join as join4 } from "path";
|
|
1776
1694
|
|
|
1777
1695
|
// src/middlewares/dev-logs/services/file-reader.ts
|
|
1778
|
-
import { promises as
|
|
1696
|
+
import { promises as fs6 } from "fs";
|
|
1779
1697
|
async function readFileReverse(filePath, chunkSize, processLine) {
|
|
1780
|
-
const handle = await
|
|
1698
|
+
const handle = await fs6.open(filePath, "r");
|
|
1781
1699
|
try {
|
|
1782
1700
|
const stats = await handle.stat();
|
|
1783
1701
|
let position = stats.size;
|
|
@@ -2022,8 +1940,8 @@ async function readRecentTraceCalls(filePath, page, pageSize, pathFilter, method
|
|
|
2022
1940
|
const handleRequestCompleted = /* @__PURE__ */ __name((builder, entry, message) => {
|
|
2023
1941
|
builder.hasCompleted = true;
|
|
2024
1942
|
builder.endTime = entry.time;
|
|
2025
|
-
builder.statusCode = extractNumber(message, /status_code:\s*(\d+)/);
|
|
2026
|
-
builder.durationMs = extractNumber(message, /duration_ms:\s*(\d+)/);
|
|
1943
|
+
builder.statusCode = typeof entry.status_code === "number" ? entry.status_code : extractNumber(message, /status_code:\s*(\d+)/);
|
|
1944
|
+
builder.durationMs = typeof entry.duration_ms === "number" ? entry.duration_ms : extractNumber(message, /duration_ms:\s*(\d+)/);
|
|
2027
1945
|
if (!builder.path && entry.path) {
|
|
2028
1946
|
builder.path = String(entry.path);
|
|
2029
1947
|
}
|
|
@@ -2223,7 +2141,7 @@ __name(readLogsBySource, "readLogsBySource");
|
|
|
2223
2141
|
// src/middlewares/dev-logs/services/trigger.service.ts
|
|
2224
2142
|
import { createReadStream as createReadStream3 } from "fs";
|
|
2225
2143
|
import { createInterface as createInterface3 } from "readline";
|
|
2226
|
-
async function readTriggerList(filePath, trigger,
|
|
2144
|
+
async function readTriggerList(filePath, trigger, path5, limit, triggerID) {
|
|
2227
2145
|
if (!await fileExists(filePath)) {
|
|
2228
2146
|
return void 0;
|
|
2229
2147
|
}
|
|
@@ -2249,7 +2167,7 @@ async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
|
|
|
2249
2167
|
if (alreadyAdded) {
|
|
2250
2168
|
return false;
|
|
2251
2169
|
}
|
|
2252
|
-
const isAutomationTrigger = builder.path?.endsWith(
|
|
2170
|
+
const isAutomationTrigger = builder.path?.endsWith(path5);
|
|
2253
2171
|
if (!isAutomationTrigger) {
|
|
2254
2172
|
return false;
|
|
2255
2173
|
}
|
|
@@ -2332,7 +2250,7 @@ async function readTriggerList(filePath, trigger, path7, limit, triggerID) {
|
|
|
2332
2250
|
};
|
|
2333
2251
|
}
|
|
2334
2252
|
__name(readTriggerList, "readTriggerList");
|
|
2335
|
-
async function readTriggerDetail(filePath,
|
|
2253
|
+
async function readTriggerDetail(filePath, path5, instanceID) {
|
|
2336
2254
|
const exists = await fileExists(filePath);
|
|
2337
2255
|
if (!exists) {
|
|
2338
2256
|
return void 0;
|
|
@@ -2348,7 +2266,7 @@ async function readTriggerDetail(filePath, path7, instanceID) {
|
|
|
2348
2266
|
for await (const line of rl) {
|
|
2349
2267
|
const entry = parseLogLine(line);
|
|
2350
2268
|
if (!entry) continue;
|
|
2351
|
-
const isAutomationTrigger = entry.path?.endsWith(
|
|
2269
|
+
const isAutomationTrigger = entry.path?.endsWith(path5);
|
|
2352
2270
|
const hasInstanceID = entry.instance_id === instanceID && entry.trigger;
|
|
2353
2271
|
if (!isAutomationTrigger || !hasInstanceID) continue;
|
|
2354
2272
|
matches.push(entry);
|
|
@@ -2602,16 +2520,16 @@ function createGetTriggerListHandler(logDir) {
|
|
|
2602
2520
|
});
|
|
2603
2521
|
}
|
|
2604
2522
|
const triggerID = typeof req.query.triggerID === "string" ? req.query.triggerID.trim() : void 0;
|
|
2605
|
-
const
|
|
2523
|
+
const path5 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
|
|
2606
2524
|
const limit = parseLimit(req.query.limit, 10, 200);
|
|
2607
2525
|
try {
|
|
2608
|
-
const result = await readTriggerList(traceLogPath, trigger,
|
|
2526
|
+
const result = await readTriggerList(traceLogPath, trigger, path5, limit, triggerID);
|
|
2609
2527
|
if (!result) {
|
|
2610
2528
|
return handleNotFound(res, traceLogPath);
|
|
2611
2529
|
}
|
|
2612
2530
|
res.json({
|
|
2613
2531
|
file: getRelativePath(traceLogPath),
|
|
2614
|
-
path:
|
|
2532
|
+
path: path5,
|
|
2615
2533
|
...result
|
|
2616
2534
|
});
|
|
2617
2535
|
} catch (error) {
|
|
@@ -2629,9 +2547,9 @@ function createGetTriggerDetailHandler(logDir) {
|
|
|
2629
2547
|
message: "instanceID is required"
|
|
2630
2548
|
});
|
|
2631
2549
|
}
|
|
2632
|
-
const
|
|
2550
|
+
const path5 = typeof req.query.path === "string" ? req.query.path.trim() : "/__innerapi__/automation/invoke";
|
|
2633
2551
|
try {
|
|
2634
|
-
const result = await readTriggerDetail(traceLogPath,
|
|
2552
|
+
const result = await readTriggerDetail(traceLogPath, path5, instanceID);
|
|
2635
2553
|
if (!result) {
|
|
2636
2554
|
return handleNotFound(res, traceLogPath);
|
|
2637
2555
|
}
|
|
@@ -2739,8 +2657,8 @@ function createHealthCheckHandler(options = {}) {
|
|
|
2739
2657
|
__name(createHealthCheckHandler, "createHealthCheckHandler");
|
|
2740
2658
|
|
|
2741
2659
|
// src/middlewares/dev-logs/sse/log-watcher.ts
|
|
2742
|
-
import * as
|
|
2743
|
-
import * as
|
|
2660
|
+
import * as fs7 from "fs";
|
|
2661
|
+
import * as path3 from "path";
|
|
2744
2662
|
function mapPinoLevelToServerLogLevel2(pinoLevel) {
|
|
2745
2663
|
if (typeof pinoLevel === "string") {
|
|
2746
2664
|
const lower = pinoLevel.toLowerCase();
|
|
@@ -2957,13 +2875,13 @@ var LogWatcher = class {
|
|
|
2957
2875
|
* Watch a single log file
|
|
2958
2876
|
*/
|
|
2959
2877
|
watchFile(config) {
|
|
2960
|
-
const filePath =
|
|
2961
|
-
if (!
|
|
2878
|
+
const filePath = path3.join(this.logDir, config.fileName);
|
|
2879
|
+
if (!fs7.existsSync(filePath)) {
|
|
2962
2880
|
this.log(`File not found, skipping: ${config.fileName}`);
|
|
2963
2881
|
return;
|
|
2964
2882
|
}
|
|
2965
2883
|
try {
|
|
2966
|
-
const stats =
|
|
2884
|
+
const stats = fs7.statSync(filePath);
|
|
2967
2885
|
this.filePositions.set(config.fileName, stats.size);
|
|
2968
2886
|
this.log(`Initialized position for ${config.fileName}: ${stats.size} bytes`);
|
|
2969
2887
|
} catch (error) {
|
|
@@ -2971,7 +2889,7 @@ var LogWatcher = class {
|
|
|
2971
2889
|
this.filePositions.set(config.fileName, 0);
|
|
2972
2890
|
}
|
|
2973
2891
|
try {
|
|
2974
|
-
const watcher =
|
|
2892
|
+
const watcher = fs7.watch(filePath, (eventType) => {
|
|
2975
2893
|
if (eventType === "change") {
|
|
2976
2894
|
this.handleFileChange(config);
|
|
2977
2895
|
}
|
|
@@ -3006,10 +2924,10 @@ var LogWatcher = class {
|
|
|
3006
2924
|
* Handle file change event - read new content
|
|
3007
2925
|
*/
|
|
3008
2926
|
handleFileChange(config) {
|
|
3009
|
-
const filePath =
|
|
2927
|
+
const filePath = path3.join(this.logDir, config.fileName);
|
|
3010
2928
|
const lastPosition = this.filePositions.get(config.fileName) || 0;
|
|
3011
2929
|
try {
|
|
3012
|
-
const stats =
|
|
2930
|
+
const stats = fs7.statSync(filePath);
|
|
3013
2931
|
const currentSize = stats.size;
|
|
3014
2932
|
if (currentSize < lastPosition) {
|
|
3015
2933
|
this.log(`File ${config.fileName} was truncated, resetting position`);
|
|
@@ -3022,11 +2940,11 @@ var LogWatcher = class {
|
|
|
3022
2940
|
}
|
|
3023
2941
|
const readSize = currentSize - lastPosition;
|
|
3024
2942
|
const buffer = Buffer.alloc(readSize);
|
|
3025
|
-
const fd =
|
|
2943
|
+
const fd = fs7.openSync(filePath, "r");
|
|
3026
2944
|
try {
|
|
3027
|
-
|
|
2945
|
+
fs7.readSync(fd, buffer, 0, readSize, lastPosition);
|
|
3028
2946
|
} finally {
|
|
3029
|
-
|
|
2947
|
+
fs7.closeSync(fd);
|
|
3030
2948
|
}
|
|
3031
2949
|
this.filePositions.set(config.fileName, currentSize);
|
|
3032
2950
|
const content = buffer.toString("utf8");
|
|
@@ -3257,8 +3175,8 @@ __name(createSSEHandler, "createSSEHandler");
|
|
|
3257
3175
|
|
|
3258
3176
|
// src/middlewares/dev-logs/api-list-handler.ts
|
|
3259
3177
|
var SERVER_PORT = process.env.SERVER_PORT || "3000";
|
|
3260
|
-
function extractModuleFromPath(
|
|
3261
|
-
const segments =
|
|
3178
|
+
function extractModuleFromPath(path5) {
|
|
3179
|
+
const segments = path5.split("/").filter(Boolean);
|
|
3262
3180
|
let startIndex = 0;
|
|
3263
3181
|
if (segments[0] === "api") {
|
|
3264
3182
|
startIndex = 1;
|
|
@@ -3273,8 +3191,8 @@ function extractModuleFromPath(path7) {
|
|
|
3273
3191
|
return moduleName;
|
|
3274
3192
|
}
|
|
3275
3193
|
__name(extractModuleFromPath, "extractModuleFromPath");
|
|
3276
|
-
function generateRouteId(method,
|
|
3277
|
-
const cleanPath =
|
|
3194
|
+
function generateRouteId(method, path5) {
|
|
3195
|
+
const cleanPath = path5.replace(/[/:]/g, "_").replace(/^_+|_+$/g, "");
|
|
3278
3196
|
return `${method.toLowerCase()}_${cleanPath}`;
|
|
3279
3197
|
}
|
|
3280
3198
|
__name(generateRouteId, "generateRouteId");
|
|
@@ -3432,11 +3350,11 @@ import express3 from "express";
|
|
|
3432
3350
|
|
|
3433
3351
|
// src/middlewares/collect-logs/controller.ts
|
|
3434
3352
|
import { join as join7 } from "path";
|
|
3435
|
-
import
|
|
3353
|
+
import fs9 from "fs";
|
|
3436
3354
|
|
|
3437
3355
|
// src/middlewares/collect-logs/utils.ts
|
|
3438
3356
|
import { isAbsolute as isAbsolute2, join as join6 } from "path";
|
|
3439
|
-
import
|
|
3357
|
+
import fs8 from "fs";
|
|
3440
3358
|
function resolveLogDir2(provided) {
|
|
3441
3359
|
if (!provided) {
|
|
3442
3360
|
return join6(process.cwd(), "logs");
|
|
@@ -3445,8 +3363,8 @@ function resolveLogDir2(provided) {
|
|
|
3445
3363
|
}
|
|
3446
3364
|
__name(resolveLogDir2, "resolveLogDir");
|
|
3447
3365
|
function ensureDir(dir) {
|
|
3448
|
-
if (!
|
|
3449
|
-
|
|
3366
|
+
if (!fs8.existsSync(dir)) {
|
|
3367
|
+
fs8.mkdirSync(dir, {
|
|
3450
3368
|
recursive: true
|
|
3451
3369
|
});
|
|
3452
3370
|
}
|
|
@@ -3478,7 +3396,7 @@ function collectLogsHandler(logDir, fileName) {
|
|
|
3478
3396
|
...logContent,
|
|
3479
3397
|
server_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
3480
3398
|
}) + "\n";
|
|
3481
|
-
await
|
|
3399
|
+
await fs9.promises.appendFile(filePath, logLine);
|
|
3482
3400
|
res.json({
|
|
3483
3401
|
success: true
|
|
3484
3402
|
});
|
|
@@ -3506,7 +3424,7 @@ function collectLogsBatchHandler(logDir, fileName) {
|
|
|
3506
3424
|
server_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
3507
3425
|
}) + "\n");
|
|
3508
3426
|
}
|
|
3509
|
-
await
|
|
3427
|
+
await fs9.promises.appendFile(filePath, logLines.join(""));
|
|
3510
3428
|
res.json({
|
|
3511
3429
|
success: true
|
|
3512
3430
|
});
|
|
@@ -3525,7 +3443,7 @@ function handleError2(res, error, message = "Failed to collect logs") {
|
|
|
3525
3443
|
__name(handleError2, "handleError");
|
|
3526
3444
|
|
|
3527
3445
|
// src/middlewares/collect-logs/router.ts
|
|
3528
|
-
var DEFAULT_BODY_SIZE_LIMIT = "
|
|
3446
|
+
var DEFAULT_BODY_SIZE_LIMIT = "20mb";
|
|
3529
3447
|
function getBodySizeLimit() {
|
|
3530
3448
|
return process.env.BODY_SIZE_LIMIT || DEFAULT_BODY_SIZE_LIMIT;
|
|
3531
3449
|
}
|
|
@@ -3672,7 +3590,7 @@ function isGlobalMiddleware(middleware) {
|
|
|
3672
3590
|
}
|
|
3673
3591
|
__name(isGlobalMiddleware, "isGlobalMiddleware");
|
|
3674
3592
|
function computeMountPath(basePath, mountPath) {
|
|
3675
|
-
const routePath =
|
|
3593
|
+
const routePath = path4.posix.join(basePath, mountPath);
|
|
3676
3594
|
return routePath.startsWith("/") ? routePath : `/${routePath}`;
|
|
3677
3595
|
}
|
|
3678
3596
|
__name(computeMountPath, "computeMountPath");
|
|
@@ -3680,7 +3598,7 @@ function logMiddlewareRegistration(middleware, fullMountPath) {
|
|
|
3680
3598
|
if (middleware.routes && middleware.routes.length > 0) {
|
|
3681
3599
|
console.log(`[Middleware] Registered: ${middleware.name} at ${fullMountPath}`);
|
|
3682
3600
|
middleware.routes.forEach((route) => {
|
|
3683
|
-
const routePath = route.path === "/" ? fullMountPath :
|
|
3601
|
+
const routePath = route.path === "/" ? fullMountPath : path4.posix.join(fullMountPath, route.path);
|
|
3684
3602
|
console.log(` ${route.method} ${routePath} - ${route.description}`);
|
|
3685
3603
|
});
|
|
3686
3604
|
} else {
|
|
@@ -3749,16 +3667,24 @@ async function registerMiddlewares(server, middlewares, options) {
|
|
|
3749
3667
|
}
|
|
3750
3668
|
__name(registerMiddlewares, "registerMiddlewares");
|
|
3751
3669
|
export {
|
|
3670
|
+
buildFullPath,
|
|
3671
|
+
calculateFileHash,
|
|
3752
3672
|
createCollectLogsMiddleware,
|
|
3753
3673
|
createDevLogsMiddleware,
|
|
3754
3674
|
createOpenapiMiddleware,
|
|
3675
|
+
evaluateTemplateLiteral,
|
|
3676
|
+
extractPageRouteInfo,
|
|
3755
3677
|
getQuery,
|
|
3756
3678
|
getQueryParam,
|
|
3757
3679
|
handleDevProxyError,
|
|
3680
|
+
isRouteComponent,
|
|
3758
3681
|
normalizeBasePath,
|
|
3759
3682
|
parseAndGenerateNestResourceTemplate,
|
|
3683
|
+
parseApiRoutes,
|
|
3684
|
+
parseRoutesFromFile,
|
|
3760
3685
|
postprocessDrizzleSchema,
|
|
3761
3686
|
registerMiddlewares,
|
|
3687
|
+
routeParserLog,
|
|
3762
3688
|
sendError,
|
|
3763
3689
|
sendJson,
|
|
3764
3690
|
sendSuccess
|