@lark-apaas/devtool-kits 1.2.20 → 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/index.cjs CHANGED
@@ -1769,12 +1769,135 @@ function createOpenapiHandler(openapiFilePath, enableEnhancement, serverDir) {
1769
1769
  }
1770
1770
  __name(createOpenapiHandler, "createOpenapiHandler");
1771
1771
 
1772
+ // src/middlewares/openapi/spec-controller.ts
1773
+ var import_promises3 = __toESM(require("fs/promises"), 1);
1774
+ var import_node_crypto2 = __toESM(require("crypto"), 1);
1775
+ var MAX_REF_DEPTH = 5;
1776
+ function createOpenapiSpecHandler(openapiSpecFilePath) {
1777
+ let cache = null;
1778
+ return async (_req, res, context) => {
1779
+ try {
1780
+ const fileBuffer = await import_promises3.default.readFile(openapiSpecFilePath, "utf-8");
1781
+ const currentHash = import_node_crypto2.default.createHash("md5").update(fileBuffer).digest("hex");
1782
+ if (cache && cache.fileHash === currentHash) {
1783
+ return res.json(cache.data);
1784
+ }
1785
+ let parsed;
1786
+ try {
1787
+ parsed = JSON.parse(fileBuffer);
1788
+ } catch (err) {
1789
+ const message = err instanceof Error ? err.message : "Invalid JSON";
1790
+ return res.status(500).json({
1791
+ error: "Failed to load OpenAPI spec",
1792
+ message: `${openapiSpecFilePath} invalid: ${message}`
1793
+ });
1794
+ }
1795
+ if (!parsed || typeof parsed !== "object" || !parsed.paths || typeof parsed.paths !== "object") {
1796
+ return res.status(500).json({
1797
+ error: "Failed to load OpenAPI spec",
1798
+ message: `${openapiSpecFilePath} missing "paths"`
1799
+ });
1800
+ }
1801
+ const result = buildFilteredSpec(parsed, context.basePath ?? "");
1802
+ cache = {
1803
+ data: result,
1804
+ fileHash: currentHash
1805
+ };
1806
+ res.json(result);
1807
+ } catch (error) {
1808
+ const message = error instanceof Error ? error.message : "Unknown error";
1809
+ res.status(500).json({
1810
+ error: "Failed to load OpenAPI spec",
1811
+ message
1812
+ });
1813
+ }
1814
+ };
1815
+ }
1816
+ __name(createOpenapiSpecHandler, "createOpenapiSpecHandler");
1817
+ function buildFilteredSpec(spec, globalPrefix) {
1818
+ const schemas = spec.components?.schemas ?? {};
1819
+ const paths = spec.paths ?? {};
1820
+ const filteredPaths = {};
1821
+ const cleanPrefix = globalPrefix.replace(/\/+$/, "");
1822
+ for (const [pathKey, methods] of Object.entries(paths)) {
1823
+ const cleanPath = stripPrefix(pathKey, cleanPrefix);
1824
+ if (!cleanPath.startsWith("/openapi")) continue;
1825
+ filteredPaths[cleanPath] = {};
1826
+ for (const [method, operation] of Object.entries(methods)) {
1827
+ if ([
1828
+ "parameters",
1829
+ "summary",
1830
+ "description",
1831
+ "servers"
1832
+ ].includes(method)) continue;
1833
+ filteredPaths[cleanPath][method] = resolveRefsDeep(structuredClone(operation), schemas);
1834
+ }
1835
+ }
1836
+ return {
1837
+ openapi: spec.openapi ?? "3.0.0",
1838
+ paths: filteredPaths,
1839
+ components: {
1840
+ schemas: resolveAllSchemaRefs(structuredClone(schemas))
1841
+ }
1842
+ };
1843
+ }
1844
+ __name(buildFilteredSpec, "buildFilteredSpec");
1845
+ function stripPrefix(pathStr, prefix) {
1846
+ if (prefix && pathStr.startsWith(prefix)) {
1847
+ return pathStr.slice(prefix.length);
1848
+ }
1849
+ return pathStr;
1850
+ }
1851
+ __name(stripPrefix, "stripPrefix");
1852
+ function resolveRefsDeep(obj, schemas, depth = 0) {
1853
+ if (depth > MAX_REF_DEPTH || obj === null || obj === void 0) return obj;
1854
+ if (typeof obj !== "object") return obj;
1855
+ if (Array.isArray(obj)) {
1856
+ return obj.map((item) => resolveRefsDeep(item, schemas, depth));
1857
+ }
1858
+ const record = obj;
1859
+ if (typeof record["$ref"] === "string") {
1860
+ const resolved = resolveRef(record["$ref"], schemas);
1861
+ if (resolved !== void 0) {
1862
+ return resolveRefsDeep(structuredClone(resolved), schemas, depth + 1);
1863
+ }
1864
+ return record;
1865
+ }
1866
+ const result = {};
1867
+ for (const [key, value] of Object.entries(record)) {
1868
+ result[key] = resolveRefsDeep(value, schemas, depth);
1869
+ }
1870
+ return result;
1871
+ }
1872
+ __name(resolveRefsDeep, "resolveRefsDeep");
1873
+ function resolveRef(ref, schemas) {
1874
+ const prefix = "#/components/schemas/";
1875
+ if (ref.startsWith(prefix)) {
1876
+ const name = ref.slice(prefix.length);
1877
+ return schemas[name];
1878
+ }
1879
+ return void 0;
1880
+ }
1881
+ __name(resolveRef, "resolveRef");
1882
+ function resolveAllSchemaRefs(schemas) {
1883
+ const result = {};
1884
+ for (const [name, schema] of Object.entries(schemas)) {
1885
+ result[name] = resolveRefsDeep(schema, schemas);
1886
+ }
1887
+ return result;
1888
+ }
1889
+ __name(resolveAllSchemaRefs, "resolveAllSchemaRefs");
1890
+
1772
1891
  // src/middlewares/openapi/router.ts
1773
1892
  function createOpenapiRouter(options, context) {
1774
- const { openapiFilePath, enableEnhancement, serverDir } = options;
1893
+ const { openapiFilePath, openapiSpecFilePath, enableEnhancement, serverDir } = options;
1775
1894
  const router = import_express.default.Router();
1776
1895
  const handler = createOpenapiHandler(openapiFilePath, enableEnhancement, serverDir);
1777
1896
  router.get("/openapi.json", (req, res) => handler(req, res, context));
1897
+ if (openapiSpecFilePath) {
1898
+ const specHandler = createOpenapiSpecHandler(openapiSpecFilePath);
1899
+ router.get("/openapi/spec", (req, res) => specHandler(req, res, context));
1900
+ }
1778
1901
  return router;
1779
1902
  }
1780
1903
  __name(createOpenapiRouter, "createOpenapiRouter");
@@ -1785,18 +1908,25 @@ var OPENAPI_ROUTES = [
1785
1908
  method: "GET",
1786
1909
  path: "/openapi.json",
1787
1910
  description: "Serve enhanced OpenAPI specification with source code references"
1911
+ },
1912
+ {
1913
+ method: "GET",
1914
+ path: "/openapi/spec",
1915
+ description: "Serve OpenAPI specification for /openapi/*"
1788
1916
  }
1789
1917
  ];
1790
1918
  function createOpenapiMiddleware(options) {
1791
- const { openapiFilePath, enableEnhancement = true, serverDir } = options;
1919
+ const { openapiFilePath, openapiSpecFilePath, enableEnhancement = true, serverDir } = options;
1920
+ const routes = openapiSpecFilePath ? OPENAPI_ROUTES : OPENAPI_ROUTES.filter((route) => route.path !== "/openapi/spec");
1792
1921
  return {
1793
1922
  name: "openapi",
1794
1923
  mountPath: "/dev",
1795
- routes: OPENAPI_ROUTES,
1924
+ routes,
1796
1925
  enabled: /* @__PURE__ */ __name((context) => context.isDev, "enabled"),
1797
1926
  createRouter: /* @__PURE__ */ __name((context) => {
1798
1927
  return createOpenapiRouter({
1799
1928
  openapiFilePath,
1929
+ openapiSpecFilePath,
1800
1930
  enableEnhancement,
1801
1931
  serverDir
1802
1932
  }, context);
@@ -2191,8 +2321,8 @@ async function readRecentTraceCalls(filePath, page, pageSize, pathFilter, method
2191
2321
  const handleRequestCompleted = /* @__PURE__ */ __name((builder, entry, message) => {
2192
2322
  builder.hasCompleted = true;
2193
2323
  builder.endTime = entry.time;
2194
- builder.statusCode = extractNumber(message, /status_code:\s*(\d+)/);
2195
- builder.durationMs = extractNumber(message, /duration_ms:\s*(\d+)/);
2324
+ builder.statusCode = typeof entry.status_code === "number" ? entry.status_code : extractNumber(message, /status_code:\s*(\d+)/);
2325
+ builder.durationMs = typeof entry.duration_ms === "number" ? entry.duration_ms : extractNumber(message, /duration_ms:\s*(\d+)/);
2196
2326
  if (!builder.path && entry.path) {
2197
2327
  builder.path = String(entry.path);
2198
2328
  }
@@ -2908,7 +3038,7 @@ function createHealthCheckHandler(options = {}) {
2908
3038
  __name(createHealthCheckHandler, "createHealthCheckHandler");
2909
3039
 
2910
3040
  // src/middlewares/dev-logs/sse/log-watcher.ts
2911
- var fs8 = __toESM(require("fs"), 1);
3041
+ var fs9 = __toESM(require("fs"), 1);
2912
3042
  var path5 = __toESM(require("path"), 1);
2913
3043
  function mapPinoLevelToServerLogLevel2(pinoLevel) {
2914
3044
  if (typeof pinoLevel === "string") {
@@ -3127,12 +3257,12 @@ var LogWatcher = class {
3127
3257
  */
3128
3258
  watchFile(config) {
3129
3259
  const filePath = path5.join(this.logDir, config.fileName);
3130
- if (!fs8.existsSync(filePath)) {
3260
+ if (!fs9.existsSync(filePath)) {
3131
3261
  this.log(`File not found, skipping: ${config.fileName}`);
3132
3262
  return;
3133
3263
  }
3134
3264
  try {
3135
- const stats = fs8.statSync(filePath);
3265
+ const stats = fs9.statSync(filePath);
3136
3266
  this.filePositions.set(config.fileName, stats.size);
3137
3267
  this.log(`Initialized position for ${config.fileName}: ${stats.size} bytes`);
3138
3268
  } catch (error) {
@@ -3140,7 +3270,7 @@ var LogWatcher = class {
3140
3270
  this.filePositions.set(config.fileName, 0);
3141
3271
  }
3142
3272
  try {
3143
- const watcher = fs8.watch(filePath, (eventType) => {
3273
+ const watcher = fs9.watch(filePath, (eventType) => {
3144
3274
  if (eventType === "change") {
3145
3275
  this.handleFileChange(config);
3146
3276
  }
@@ -3178,7 +3308,7 @@ var LogWatcher = class {
3178
3308
  const filePath = path5.join(this.logDir, config.fileName);
3179
3309
  const lastPosition = this.filePositions.get(config.fileName) || 0;
3180
3310
  try {
3181
- const stats = fs8.statSync(filePath);
3311
+ const stats = fs9.statSync(filePath);
3182
3312
  const currentSize = stats.size;
3183
3313
  if (currentSize < lastPosition) {
3184
3314
  this.log(`File ${config.fileName} was truncated, resetting position`);
@@ -3191,11 +3321,11 @@ var LogWatcher = class {
3191
3321
  }
3192
3322
  const readSize = currentSize - lastPosition;
3193
3323
  const buffer = Buffer.alloc(readSize);
3194
- const fd = fs8.openSync(filePath, "r");
3324
+ const fd = fs9.openSync(filePath, "r");
3195
3325
  try {
3196
- fs8.readSync(fd, buffer, 0, readSize, lastPosition);
3326
+ fs9.readSync(fd, buffer, 0, readSize, lastPosition);
3197
3327
  } finally {
3198
- fs8.closeSync(fd);
3328
+ fs9.closeSync(fd);
3199
3329
  }
3200
3330
  this.filePositions.set(config.fileName, currentSize);
3201
3331
  const content = buffer.toString("utf8");
@@ -3919,9 +4049,9 @@ async function registerMiddlewares(server, middlewares, options) {
3919
4049
  __name(registerMiddlewares, "registerMiddlewares");
3920
4050
 
3921
4051
  // src/route-parser.ts
3922
- var fs11 = __toESM(require("fs"), 1);
4052
+ var fs12 = __toESM(require("fs"), 1);
3923
4053
  var path7 = __toESM(require("path"), 1);
3924
- var crypto2 = __toESM(require("crypto"), 1);
4054
+ var crypto3 = __toESM(require("crypto"), 1);
3925
4055
  var import_parser = require("@babel/parser");
3926
4056
  var import_traverse = __toESM(require("@babel/traverse"), 1);
3927
4057
  var t = __toESM(require("@babel/types"), 1);
@@ -3946,8 +4076,8 @@ function routeParserLog(level, message, ...args) {
3946
4076
  __name(routeParserLog, "routeParserLog");
3947
4077
  function calculateFileHash(filePath) {
3948
4078
  try {
3949
- const content = fs11.readFileSync(filePath, "utf-8");
3950
- return crypto2.createHash("md5").update(content).digest("hex");
4079
+ const content = fs12.readFileSync(filePath, "utf-8");
4080
+ return crypto3.createHash("md5").update(content).digest("hex");
3951
4081
  } catch (error) {
3952
4082
  routeParserLog("warn", "Failed to calculate file hash:", error.message);
3953
4083
  return null;
@@ -4031,10 +4161,10 @@ function parseRoutesFromFile(appPath, basePath, options = {}) {
4031
4161
  const defaultPath = applyBasePath && basePath ? `${basePath}/` : "/";
4032
4162
  try {
4033
4163
  const appFilePath = path7.resolve(process.cwd(), appPath);
4034
- if (!fs11.existsSync(appFilePath)) {
4164
+ if (!fs12.existsSync(appFilePath)) {
4035
4165
  throw new Error(`App file does not exist: ${appFilePath}`);
4036
4166
  }
4037
- const sourceCode = fs11.readFileSync(appFilePath, "utf-8");
4167
+ const sourceCode = fs12.readFileSync(appFilePath, "utf-8");
4038
4168
  const ast = (0, import_parser.parse)(sourceCode, {
4039
4169
  sourceType: "module",
4040
4170
  plugins: [