@lark-apaas/devtool-kits 1.2.20 → 1.2.22-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +168 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +172 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -166,6 +166,8 @@ type Middleware = RouteMiddleware | GlobalMiddleware;
|
|
|
166
166
|
interface OpenapiMiddlewareOptions {
|
|
167
167
|
/** Path to the openapi.json file */
|
|
168
168
|
openapiFilePath: string;
|
|
169
|
+
/** Path to the OpenAPI spec source file (when set, serves /openapi/spec with /openapi/* filtered and $refs resolved) */
|
|
170
|
+
openapiSpecFilePath?: string;
|
|
169
171
|
/** Enable source code enhancement */
|
|
170
172
|
enableEnhancement?: boolean;
|
|
171
173
|
/** Server directory for source code scanning (defaults to context.rootDir) */
|
package/dist/index.d.ts
CHANGED
|
@@ -166,6 +166,8 @@ type Middleware = RouteMiddleware | GlobalMiddleware;
|
|
|
166
166
|
interface OpenapiMiddlewareOptions {
|
|
167
167
|
/** Path to the openapi.json file */
|
|
168
168
|
openapiFilePath: string;
|
|
169
|
+
/** Path to the OpenAPI spec source file (when set, serves /openapi/spec with /openapi/* filtered and $refs resolved) */
|
|
170
|
+
openapiSpecFilePath?: string;
|
|
169
171
|
/** Enable source code enhancement */
|
|
170
172
|
enableEnhancement?: boolean;
|
|
171
173
|
/** Server directory for source code scanning (defaults to context.rootDir) */
|
package/dist/index.js
CHANGED
|
@@ -1388,12 +1388,154 @@ function createOpenapiHandler(openapiFilePath, enableEnhancement, serverDir) {
|
|
|
1388
1388
|
}
|
|
1389
1389
|
__name(createOpenapiHandler, "createOpenapiHandler");
|
|
1390
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
|
+
var EMPTY_OPENAPI_SPEC = {
|
|
1396
|
+
openapi: "3.0.0",
|
|
1397
|
+
info: {
|
|
1398
|
+
title: "\u5F00\u653E API",
|
|
1399
|
+
version: "0.0.0"
|
|
1400
|
+
},
|
|
1401
|
+
paths: {},
|
|
1402
|
+
components: {
|
|
1403
|
+
schemas: {}
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
function createOpenapiSpecHandler(openapiSpecFilePath) {
|
|
1407
|
+
let cache = null;
|
|
1408
|
+
return async (_req, res, context) => {
|
|
1409
|
+
try {
|
|
1410
|
+
let fileBuffer;
|
|
1411
|
+
try {
|
|
1412
|
+
fileBuffer = await fs4.readFile(openapiSpecFilePath, "utf-8");
|
|
1413
|
+
} catch (err) {
|
|
1414
|
+
if (err?.code === "ENOENT") {
|
|
1415
|
+
return res.json(EMPTY_OPENAPI_SPEC);
|
|
1416
|
+
}
|
|
1417
|
+
throw err;
|
|
1418
|
+
}
|
|
1419
|
+
const currentHash = crypto2.createHash("md5").update(fileBuffer).digest("hex");
|
|
1420
|
+
if (cache && cache.fileHash === currentHash) {
|
|
1421
|
+
return res.json(cache.data);
|
|
1422
|
+
}
|
|
1423
|
+
let parsed;
|
|
1424
|
+
try {
|
|
1425
|
+
parsed = JSON.parse(fileBuffer);
|
|
1426
|
+
} catch (err) {
|
|
1427
|
+
const message = err instanceof Error ? err.message : "Invalid JSON";
|
|
1428
|
+
return res.status(500).json({
|
|
1429
|
+
error: "Failed to load OpenAPI spec",
|
|
1430
|
+
message: `${openapiSpecFilePath} invalid: ${message}`
|
|
1431
|
+
});
|
|
1432
|
+
}
|
|
1433
|
+
if (!parsed || typeof parsed !== "object" || !parsed.paths || typeof parsed.paths !== "object") {
|
|
1434
|
+
return res.status(500).json({
|
|
1435
|
+
error: "Failed to load OpenAPI spec",
|
|
1436
|
+
message: `${openapiSpecFilePath} missing "paths"`
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
const result = buildFilteredSpec(parsed, context.basePath ?? "");
|
|
1440
|
+
cache = {
|
|
1441
|
+
data: result,
|
|
1442
|
+
fileHash: currentHash
|
|
1443
|
+
};
|
|
1444
|
+
res.json(result);
|
|
1445
|
+
} catch (error) {
|
|
1446
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
1447
|
+
res.status(500).json({
|
|
1448
|
+
error: "Failed to load OpenAPI spec",
|
|
1449
|
+
message
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
};
|
|
1453
|
+
}
|
|
1454
|
+
__name(createOpenapiSpecHandler, "createOpenapiSpecHandler");
|
|
1455
|
+
function buildFilteredSpec(spec, globalPrefix) {
|
|
1456
|
+
const schemas = spec.components?.schemas ?? {};
|
|
1457
|
+
const paths = spec.paths ?? {};
|
|
1458
|
+
const filteredPaths = {};
|
|
1459
|
+
const cleanPrefix = globalPrefix.replace(/\/+$/, "");
|
|
1460
|
+
for (const [pathKey, methods] of Object.entries(paths)) {
|
|
1461
|
+
const cleanPath = stripPrefix(pathKey, cleanPrefix);
|
|
1462
|
+
if (!cleanPath.startsWith("/openapi")) continue;
|
|
1463
|
+
filteredPaths[cleanPath] = {};
|
|
1464
|
+
for (const [method, operation] of Object.entries(methods)) {
|
|
1465
|
+
if ([
|
|
1466
|
+
"parameters",
|
|
1467
|
+
"summary",
|
|
1468
|
+
"description",
|
|
1469
|
+
"servers"
|
|
1470
|
+
].includes(method)) continue;
|
|
1471
|
+
filteredPaths[cleanPath][method] = resolveRefsDeep(structuredClone(operation), schemas);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
return {
|
|
1475
|
+
openapi: spec.openapi ?? "3.0.0",
|
|
1476
|
+
paths: filteredPaths,
|
|
1477
|
+
components: {
|
|
1478
|
+
schemas: resolveAllSchemaRefs(structuredClone(schemas))
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
}
|
|
1482
|
+
__name(buildFilteredSpec, "buildFilteredSpec");
|
|
1483
|
+
function stripPrefix(pathStr, prefix) {
|
|
1484
|
+
if (prefix && pathStr.startsWith(prefix)) {
|
|
1485
|
+
return pathStr.slice(prefix.length);
|
|
1486
|
+
}
|
|
1487
|
+
return pathStr;
|
|
1488
|
+
}
|
|
1489
|
+
__name(stripPrefix, "stripPrefix");
|
|
1490
|
+
function resolveRefsDeep(obj, schemas, depth = 0) {
|
|
1491
|
+
if (depth > MAX_REF_DEPTH || obj === null || obj === void 0) return obj;
|
|
1492
|
+
if (typeof obj !== "object") return obj;
|
|
1493
|
+
if (Array.isArray(obj)) {
|
|
1494
|
+
return obj.map((item) => resolveRefsDeep(item, schemas, depth));
|
|
1495
|
+
}
|
|
1496
|
+
const record = obj;
|
|
1497
|
+
if (typeof record["$ref"] === "string") {
|
|
1498
|
+
const resolved = resolveRef(record["$ref"], schemas);
|
|
1499
|
+
if (resolved !== void 0) {
|
|
1500
|
+
return resolveRefsDeep(structuredClone(resolved), schemas, depth + 1);
|
|
1501
|
+
}
|
|
1502
|
+
return record;
|
|
1503
|
+
}
|
|
1504
|
+
const result = {};
|
|
1505
|
+
for (const [key, value] of Object.entries(record)) {
|
|
1506
|
+
result[key] = resolveRefsDeep(value, schemas, depth);
|
|
1507
|
+
}
|
|
1508
|
+
return result;
|
|
1509
|
+
}
|
|
1510
|
+
__name(resolveRefsDeep, "resolveRefsDeep");
|
|
1511
|
+
function resolveRef(ref, schemas) {
|
|
1512
|
+
const prefix = "#/components/schemas/";
|
|
1513
|
+
if (ref.startsWith(prefix)) {
|
|
1514
|
+
const name = ref.slice(prefix.length);
|
|
1515
|
+
return schemas[name];
|
|
1516
|
+
}
|
|
1517
|
+
return void 0;
|
|
1518
|
+
}
|
|
1519
|
+
__name(resolveRef, "resolveRef");
|
|
1520
|
+
function resolveAllSchemaRefs(schemas) {
|
|
1521
|
+
const result = {};
|
|
1522
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
1523
|
+
result[name] = resolveRefsDeep(schema, schemas);
|
|
1524
|
+
}
|
|
1525
|
+
return result;
|
|
1526
|
+
}
|
|
1527
|
+
__name(resolveAllSchemaRefs, "resolveAllSchemaRefs");
|
|
1528
|
+
|
|
1391
1529
|
// src/middlewares/openapi/router.ts
|
|
1392
1530
|
function createOpenapiRouter(options, context) {
|
|
1393
|
-
const { openapiFilePath, enableEnhancement, serverDir } = options;
|
|
1531
|
+
const { openapiFilePath, openapiSpecFilePath, enableEnhancement, serverDir } = options;
|
|
1394
1532
|
const router = express.Router();
|
|
1395
1533
|
const handler = createOpenapiHandler(openapiFilePath, enableEnhancement, serverDir);
|
|
1396
1534
|
router.get("/openapi.json", (req, res) => handler(req, res, context));
|
|
1535
|
+
if (openapiSpecFilePath) {
|
|
1536
|
+
const specHandler = createOpenapiSpecHandler(openapiSpecFilePath);
|
|
1537
|
+
router.get("/openapi/spec", (req, res) => specHandler(req, res, context));
|
|
1538
|
+
}
|
|
1397
1539
|
return router;
|
|
1398
1540
|
}
|
|
1399
1541
|
__name(createOpenapiRouter, "createOpenapiRouter");
|
|
@@ -1404,18 +1546,25 @@ var OPENAPI_ROUTES = [
|
|
|
1404
1546
|
method: "GET",
|
|
1405
1547
|
path: "/openapi.json",
|
|
1406
1548
|
description: "Serve enhanced OpenAPI specification with source code references"
|
|
1549
|
+
},
|
|
1550
|
+
{
|
|
1551
|
+
method: "GET",
|
|
1552
|
+
path: "/openapi/spec",
|
|
1553
|
+
description: "Serve OpenAPI specification for /openapi/*"
|
|
1407
1554
|
}
|
|
1408
1555
|
];
|
|
1409
1556
|
function createOpenapiMiddleware(options) {
|
|
1410
|
-
const { openapiFilePath, enableEnhancement = true, serverDir } = options;
|
|
1557
|
+
const { openapiFilePath, openapiSpecFilePath, enableEnhancement = true, serverDir } = options;
|
|
1558
|
+
const routes = openapiSpecFilePath ? OPENAPI_ROUTES : OPENAPI_ROUTES.filter((route) => route.path !== "/openapi/spec");
|
|
1411
1559
|
return {
|
|
1412
1560
|
name: "openapi",
|
|
1413
1561
|
mountPath: "/dev",
|
|
1414
|
-
routes
|
|
1562
|
+
routes,
|
|
1415
1563
|
enabled: /* @__PURE__ */ __name((context) => context.isDev, "enabled"),
|
|
1416
1564
|
createRouter: /* @__PURE__ */ __name((context) => {
|
|
1417
1565
|
return createOpenapiRouter({
|
|
1418
1566
|
openapiFilePath,
|
|
1567
|
+
openapiSpecFilePath,
|
|
1419
1568
|
enableEnhancement,
|
|
1420
1569
|
serverDir
|
|
1421
1570
|
}, context);
|
|
@@ -1428,7 +1577,7 @@ __name(createOpenapiMiddleware, "createOpenapiMiddleware");
|
|
|
1428
1577
|
import express2 from "express";
|
|
1429
1578
|
|
|
1430
1579
|
// src/middlewares/dev-logs/utils.ts
|
|
1431
|
-
import { promises as
|
|
1580
|
+
import { promises as fs5 } from "fs";
|
|
1432
1581
|
import { isAbsolute, join as join2, relative } from "path";
|
|
1433
1582
|
|
|
1434
1583
|
// src/middlewares/dev-logs/helper/path-matcher.ts
|
|
@@ -1480,7 +1629,7 @@ function getRelativePath(filePath) {
|
|
|
1480
1629
|
__name(getRelativePath, "getRelativePath");
|
|
1481
1630
|
async function fileExists(filePath) {
|
|
1482
1631
|
try {
|
|
1483
|
-
await
|
|
1632
|
+
await fs5.access(filePath);
|
|
1484
1633
|
return true;
|
|
1485
1634
|
} catch {
|
|
1486
1635
|
return false;
|
|
@@ -1563,9 +1712,9 @@ __name(serializeError, "serializeError");
|
|
|
1563
1712
|
import { join as join4 } from "path";
|
|
1564
1713
|
|
|
1565
1714
|
// src/middlewares/dev-logs/services/file-reader.ts
|
|
1566
|
-
import { promises as
|
|
1715
|
+
import { promises as fs6 } from "fs";
|
|
1567
1716
|
async function readFileReverse(filePath, chunkSize, processLine) {
|
|
1568
|
-
const handle = await
|
|
1717
|
+
const handle = await fs6.open(filePath, "r");
|
|
1569
1718
|
try {
|
|
1570
1719
|
const stats = await handle.stat();
|
|
1571
1720
|
let position = stats.size;
|
|
@@ -1810,8 +1959,8 @@ async function readRecentTraceCalls(filePath, page, pageSize, pathFilter, method
|
|
|
1810
1959
|
const handleRequestCompleted = /* @__PURE__ */ __name((builder, entry, message) => {
|
|
1811
1960
|
builder.hasCompleted = true;
|
|
1812
1961
|
builder.endTime = entry.time;
|
|
1813
|
-
builder.statusCode = extractNumber(message, /status_code:\s*(\d+)/);
|
|
1814
|
-
builder.durationMs = extractNumber(message, /duration_ms:\s*(\d+)/);
|
|
1962
|
+
builder.statusCode = typeof entry.status_code === "number" ? entry.status_code : extractNumber(message, /status_code:\s*(\d+)/);
|
|
1963
|
+
builder.durationMs = typeof entry.duration_ms === "number" ? entry.duration_ms : extractNumber(message, /duration_ms:\s*(\d+)/);
|
|
1815
1964
|
if (!builder.path && entry.path) {
|
|
1816
1965
|
builder.path = String(entry.path);
|
|
1817
1966
|
}
|
|
@@ -2527,7 +2676,7 @@ function createHealthCheckHandler(options = {}) {
|
|
|
2527
2676
|
__name(createHealthCheckHandler, "createHealthCheckHandler");
|
|
2528
2677
|
|
|
2529
2678
|
// src/middlewares/dev-logs/sse/log-watcher.ts
|
|
2530
|
-
import * as
|
|
2679
|
+
import * as fs7 from "fs";
|
|
2531
2680
|
import * as path3 from "path";
|
|
2532
2681
|
function mapPinoLevelToServerLogLevel2(pinoLevel) {
|
|
2533
2682
|
if (typeof pinoLevel === "string") {
|
|
@@ -2746,12 +2895,12 @@ var LogWatcher = class {
|
|
|
2746
2895
|
*/
|
|
2747
2896
|
watchFile(config) {
|
|
2748
2897
|
const filePath = path3.join(this.logDir, config.fileName);
|
|
2749
|
-
if (!
|
|
2898
|
+
if (!fs7.existsSync(filePath)) {
|
|
2750
2899
|
this.log(`File not found, skipping: ${config.fileName}`);
|
|
2751
2900
|
return;
|
|
2752
2901
|
}
|
|
2753
2902
|
try {
|
|
2754
|
-
const stats =
|
|
2903
|
+
const stats = fs7.statSync(filePath);
|
|
2755
2904
|
this.filePositions.set(config.fileName, stats.size);
|
|
2756
2905
|
this.log(`Initialized position for ${config.fileName}: ${stats.size} bytes`);
|
|
2757
2906
|
} catch (error) {
|
|
@@ -2759,7 +2908,7 @@ var LogWatcher = class {
|
|
|
2759
2908
|
this.filePositions.set(config.fileName, 0);
|
|
2760
2909
|
}
|
|
2761
2910
|
try {
|
|
2762
|
-
const watcher =
|
|
2911
|
+
const watcher = fs7.watch(filePath, (eventType) => {
|
|
2763
2912
|
if (eventType === "change") {
|
|
2764
2913
|
this.handleFileChange(config);
|
|
2765
2914
|
}
|
|
@@ -2797,7 +2946,7 @@ var LogWatcher = class {
|
|
|
2797
2946
|
const filePath = path3.join(this.logDir, config.fileName);
|
|
2798
2947
|
const lastPosition = this.filePositions.get(config.fileName) || 0;
|
|
2799
2948
|
try {
|
|
2800
|
-
const stats =
|
|
2949
|
+
const stats = fs7.statSync(filePath);
|
|
2801
2950
|
const currentSize = stats.size;
|
|
2802
2951
|
if (currentSize < lastPosition) {
|
|
2803
2952
|
this.log(`File ${config.fileName} was truncated, resetting position`);
|
|
@@ -2810,11 +2959,11 @@ var LogWatcher = class {
|
|
|
2810
2959
|
}
|
|
2811
2960
|
const readSize = currentSize - lastPosition;
|
|
2812
2961
|
const buffer = Buffer.alloc(readSize);
|
|
2813
|
-
const fd =
|
|
2962
|
+
const fd = fs7.openSync(filePath, "r");
|
|
2814
2963
|
try {
|
|
2815
|
-
|
|
2964
|
+
fs7.readSync(fd, buffer, 0, readSize, lastPosition);
|
|
2816
2965
|
} finally {
|
|
2817
|
-
|
|
2966
|
+
fs7.closeSync(fd);
|
|
2818
2967
|
}
|
|
2819
2968
|
this.filePositions.set(config.fileName, currentSize);
|
|
2820
2969
|
const content = buffer.toString("utf8");
|
|
@@ -3220,11 +3369,11 @@ import express3 from "express";
|
|
|
3220
3369
|
|
|
3221
3370
|
// src/middlewares/collect-logs/controller.ts
|
|
3222
3371
|
import { join as join7 } from "path";
|
|
3223
|
-
import
|
|
3372
|
+
import fs9 from "fs";
|
|
3224
3373
|
|
|
3225
3374
|
// src/middlewares/collect-logs/utils.ts
|
|
3226
3375
|
import { isAbsolute as isAbsolute2, join as join6 } from "path";
|
|
3227
|
-
import
|
|
3376
|
+
import fs8 from "fs";
|
|
3228
3377
|
function resolveLogDir2(provided) {
|
|
3229
3378
|
if (!provided) {
|
|
3230
3379
|
return join6(process.cwd(), "logs");
|
|
@@ -3233,8 +3382,8 @@ function resolveLogDir2(provided) {
|
|
|
3233
3382
|
}
|
|
3234
3383
|
__name(resolveLogDir2, "resolveLogDir");
|
|
3235
3384
|
function ensureDir(dir) {
|
|
3236
|
-
if (!
|
|
3237
|
-
|
|
3385
|
+
if (!fs8.existsSync(dir)) {
|
|
3386
|
+
fs8.mkdirSync(dir, {
|
|
3238
3387
|
recursive: true
|
|
3239
3388
|
});
|
|
3240
3389
|
}
|
|
@@ -3266,7 +3415,7 @@ function collectLogsHandler(logDir, fileName) {
|
|
|
3266
3415
|
...logContent,
|
|
3267
3416
|
server_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
3268
3417
|
}) + "\n";
|
|
3269
|
-
await
|
|
3418
|
+
await fs9.promises.appendFile(filePath, logLine);
|
|
3270
3419
|
res.json({
|
|
3271
3420
|
success: true
|
|
3272
3421
|
});
|
|
@@ -3294,7 +3443,7 @@ function collectLogsBatchHandler(logDir, fileName) {
|
|
|
3294
3443
|
server_time: (/* @__PURE__ */ new Date()).toISOString()
|
|
3295
3444
|
}) + "\n");
|
|
3296
3445
|
}
|
|
3297
|
-
await
|
|
3446
|
+
await fs9.promises.appendFile(filePath, logLines.join(""));
|
|
3298
3447
|
res.json({
|
|
3299
3448
|
success: true
|
|
3300
3449
|
});
|