@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 CHANGED
@@ -1769,12 +1769,154 @@ 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
+ var EMPTY_OPENAPI_SPEC = {
1777
+ openapi: "3.0.0",
1778
+ info: {
1779
+ title: "\u5F00\u653E API",
1780
+ version: "0.0.0"
1781
+ },
1782
+ paths: {},
1783
+ components: {
1784
+ schemas: {}
1785
+ }
1786
+ };
1787
+ function createOpenapiSpecHandler(openapiSpecFilePath) {
1788
+ let cache = null;
1789
+ return async (_req, res, context) => {
1790
+ try {
1791
+ let fileBuffer;
1792
+ try {
1793
+ fileBuffer = await import_promises3.default.readFile(openapiSpecFilePath, "utf-8");
1794
+ } catch (err) {
1795
+ if (err?.code === "ENOENT") {
1796
+ return res.json(EMPTY_OPENAPI_SPEC);
1797
+ }
1798
+ throw err;
1799
+ }
1800
+ const currentHash = import_node_crypto2.default.createHash("md5").update(fileBuffer).digest("hex");
1801
+ if (cache && cache.fileHash === currentHash) {
1802
+ return res.json(cache.data);
1803
+ }
1804
+ let parsed;
1805
+ try {
1806
+ parsed = JSON.parse(fileBuffer);
1807
+ } catch (err) {
1808
+ const message = err instanceof Error ? err.message : "Invalid JSON";
1809
+ return res.status(500).json({
1810
+ error: "Failed to load OpenAPI spec",
1811
+ message: `${openapiSpecFilePath} invalid: ${message}`
1812
+ });
1813
+ }
1814
+ if (!parsed || typeof parsed !== "object" || !parsed.paths || typeof parsed.paths !== "object") {
1815
+ return res.status(500).json({
1816
+ error: "Failed to load OpenAPI spec",
1817
+ message: `${openapiSpecFilePath} missing "paths"`
1818
+ });
1819
+ }
1820
+ const result = buildFilteredSpec(parsed, context.basePath ?? "");
1821
+ cache = {
1822
+ data: result,
1823
+ fileHash: currentHash
1824
+ };
1825
+ res.json(result);
1826
+ } catch (error) {
1827
+ const message = error instanceof Error ? error.message : "Unknown error";
1828
+ res.status(500).json({
1829
+ error: "Failed to load OpenAPI spec",
1830
+ message
1831
+ });
1832
+ }
1833
+ };
1834
+ }
1835
+ __name(createOpenapiSpecHandler, "createOpenapiSpecHandler");
1836
+ function buildFilteredSpec(spec, globalPrefix) {
1837
+ const schemas = spec.components?.schemas ?? {};
1838
+ const paths = spec.paths ?? {};
1839
+ const filteredPaths = {};
1840
+ const cleanPrefix = globalPrefix.replace(/\/+$/, "");
1841
+ for (const [pathKey, methods] of Object.entries(paths)) {
1842
+ const cleanPath = stripPrefix(pathKey, cleanPrefix);
1843
+ if (!cleanPath.startsWith("/openapi")) continue;
1844
+ filteredPaths[cleanPath] = {};
1845
+ for (const [method, operation] of Object.entries(methods)) {
1846
+ if ([
1847
+ "parameters",
1848
+ "summary",
1849
+ "description",
1850
+ "servers"
1851
+ ].includes(method)) continue;
1852
+ filteredPaths[cleanPath][method] = resolveRefsDeep(structuredClone(operation), schemas);
1853
+ }
1854
+ }
1855
+ return {
1856
+ openapi: spec.openapi ?? "3.0.0",
1857
+ paths: filteredPaths,
1858
+ components: {
1859
+ schemas: resolveAllSchemaRefs(structuredClone(schemas))
1860
+ }
1861
+ };
1862
+ }
1863
+ __name(buildFilteredSpec, "buildFilteredSpec");
1864
+ function stripPrefix(pathStr, prefix) {
1865
+ if (prefix && pathStr.startsWith(prefix)) {
1866
+ return pathStr.slice(prefix.length);
1867
+ }
1868
+ return pathStr;
1869
+ }
1870
+ __name(stripPrefix, "stripPrefix");
1871
+ function resolveRefsDeep(obj, schemas, depth = 0) {
1872
+ if (depth > MAX_REF_DEPTH || obj === null || obj === void 0) return obj;
1873
+ if (typeof obj !== "object") return obj;
1874
+ if (Array.isArray(obj)) {
1875
+ return obj.map((item) => resolveRefsDeep(item, schemas, depth));
1876
+ }
1877
+ const record = obj;
1878
+ if (typeof record["$ref"] === "string") {
1879
+ const resolved = resolveRef(record["$ref"], schemas);
1880
+ if (resolved !== void 0) {
1881
+ return resolveRefsDeep(structuredClone(resolved), schemas, depth + 1);
1882
+ }
1883
+ return record;
1884
+ }
1885
+ const result = {};
1886
+ for (const [key, value] of Object.entries(record)) {
1887
+ result[key] = resolveRefsDeep(value, schemas, depth);
1888
+ }
1889
+ return result;
1890
+ }
1891
+ __name(resolveRefsDeep, "resolveRefsDeep");
1892
+ function resolveRef(ref, schemas) {
1893
+ const prefix = "#/components/schemas/";
1894
+ if (ref.startsWith(prefix)) {
1895
+ const name = ref.slice(prefix.length);
1896
+ return schemas[name];
1897
+ }
1898
+ return void 0;
1899
+ }
1900
+ __name(resolveRef, "resolveRef");
1901
+ function resolveAllSchemaRefs(schemas) {
1902
+ const result = {};
1903
+ for (const [name, schema] of Object.entries(schemas)) {
1904
+ result[name] = resolveRefsDeep(schema, schemas);
1905
+ }
1906
+ return result;
1907
+ }
1908
+ __name(resolveAllSchemaRefs, "resolveAllSchemaRefs");
1909
+
1772
1910
  // src/middlewares/openapi/router.ts
1773
1911
  function createOpenapiRouter(options, context) {
1774
- const { openapiFilePath, enableEnhancement, serverDir } = options;
1912
+ const { openapiFilePath, openapiSpecFilePath, enableEnhancement, serverDir } = options;
1775
1913
  const router = import_express.default.Router();
1776
1914
  const handler = createOpenapiHandler(openapiFilePath, enableEnhancement, serverDir);
1777
1915
  router.get("/openapi.json", (req, res) => handler(req, res, context));
1916
+ if (openapiSpecFilePath) {
1917
+ const specHandler = createOpenapiSpecHandler(openapiSpecFilePath);
1918
+ router.get("/openapi/spec", (req, res) => specHandler(req, res, context));
1919
+ }
1778
1920
  return router;
1779
1921
  }
1780
1922
  __name(createOpenapiRouter, "createOpenapiRouter");
@@ -1785,18 +1927,25 @@ var OPENAPI_ROUTES = [
1785
1927
  method: "GET",
1786
1928
  path: "/openapi.json",
1787
1929
  description: "Serve enhanced OpenAPI specification with source code references"
1930
+ },
1931
+ {
1932
+ method: "GET",
1933
+ path: "/openapi/spec",
1934
+ description: "Serve OpenAPI specification for /openapi/*"
1788
1935
  }
1789
1936
  ];
1790
1937
  function createOpenapiMiddleware(options) {
1791
- const { openapiFilePath, enableEnhancement = true, serverDir } = options;
1938
+ const { openapiFilePath, openapiSpecFilePath, enableEnhancement = true, serverDir } = options;
1939
+ const routes = openapiSpecFilePath ? OPENAPI_ROUTES : OPENAPI_ROUTES.filter((route) => route.path !== "/openapi/spec");
1792
1940
  return {
1793
1941
  name: "openapi",
1794
1942
  mountPath: "/dev",
1795
- routes: OPENAPI_ROUTES,
1943
+ routes,
1796
1944
  enabled: /* @__PURE__ */ __name((context) => context.isDev, "enabled"),
1797
1945
  createRouter: /* @__PURE__ */ __name((context) => {
1798
1946
  return createOpenapiRouter({
1799
1947
  openapiFilePath,
1948
+ openapiSpecFilePath,
1800
1949
  enableEnhancement,
1801
1950
  serverDir
1802
1951
  }, context);
@@ -2191,8 +2340,8 @@ async function readRecentTraceCalls(filePath, page, pageSize, pathFilter, method
2191
2340
  const handleRequestCompleted = /* @__PURE__ */ __name((builder, entry, message) => {
2192
2341
  builder.hasCompleted = true;
2193
2342
  builder.endTime = entry.time;
2194
- builder.statusCode = extractNumber(message, /status_code:\s*(\d+)/);
2195
- builder.durationMs = extractNumber(message, /duration_ms:\s*(\d+)/);
2343
+ builder.statusCode = typeof entry.status_code === "number" ? entry.status_code : extractNumber(message, /status_code:\s*(\d+)/);
2344
+ builder.durationMs = typeof entry.duration_ms === "number" ? entry.duration_ms : extractNumber(message, /duration_ms:\s*(\d+)/);
2196
2345
  if (!builder.path && entry.path) {
2197
2346
  builder.path = String(entry.path);
2198
2347
  }
@@ -2908,7 +3057,7 @@ function createHealthCheckHandler(options = {}) {
2908
3057
  __name(createHealthCheckHandler, "createHealthCheckHandler");
2909
3058
 
2910
3059
  // src/middlewares/dev-logs/sse/log-watcher.ts
2911
- var fs8 = __toESM(require("fs"), 1);
3060
+ var fs9 = __toESM(require("fs"), 1);
2912
3061
  var path5 = __toESM(require("path"), 1);
2913
3062
  function mapPinoLevelToServerLogLevel2(pinoLevel) {
2914
3063
  if (typeof pinoLevel === "string") {
@@ -3127,12 +3276,12 @@ var LogWatcher = class {
3127
3276
  */
3128
3277
  watchFile(config) {
3129
3278
  const filePath = path5.join(this.logDir, config.fileName);
3130
- if (!fs8.existsSync(filePath)) {
3279
+ if (!fs9.existsSync(filePath)) {
3131
3280
  this.log(`File not found, skipping: ${config.fileName}`);
3132
3281
  return;
3133
3282
  }
3134
3283
  try {
3135
- const stats = fs8.statSync(filePath);
3284
+ const stats = fs9.statSync(filePath);
3136
3285
  this.filePositions.set(config.fileName, stats.size);
3137
3286
  this.log(`Initialized position for ${config.fileName}: ${stats.size} bytes`);
3138
3287
  } catch (error) {
@@ -3140,7 +3289,7 @@ var LogWatcher = class {
3140
3289
  this.filePositions.set(config.fileName, 0);
3141
3290
  }
3142
3291
  try {
3143
- const watcher = fs8.watch(filePath, (eventType) => {
3292
+ const watcher = fs9.watch(filePath, (eventType) => {
3144
3293
  if (eventType === "change") {
3145
3294
  this.handleFileChange(config);
3146
3295
  }
@@ -3178,7 +3327,7 @@ var LogWatcher = class {
3178
3327
  const filePath = path5.join(this.logDir, config.fileName);
3179
3328
  const lastPosition = this.filePositions.get(config.fileName) || 0;
3180
3329
  try {
3181
- const stats = fs8.statSync(filePath);
3330
+ const stats = fs9.statSync(filePath);
3182
3331
  const currentSize = stats.size;
3183
3332
  if (currentSize < lastPosition) {
3184
3333
  this.log(`File ${config.fileName} was truncated, resetting position`);
@@ -3191,11 +3340,11 @@ var LogWatcher = class {
3191
3340
  }
3192
3341
  const readSize = currentSize - lastPosition;
3193
3342
  const buffer = Buffer.alloc(readSize);
3194
- const fd = fs8.openSync(filePath, "r");
3343
+ const fd = fs9.openSync(filePath, "r");
3195
3344
  try {
3196
- fs8.readSync(fd, buffer, 0, readSize, lastPosition);
3345
+ fs9.readSync(fd, buffer, 0, readSize, lastPosition);
3197
3346
  } finally {
3198
- fs8.closeSync(fd);
3347
+ fs9.closeSync(fd);
3199
3348
  }
3200
3349
  this.filePositions.set(config.fileName, currentSize);
3201
3350
  const content = buffer.toString("utf8");
@@ -3919,9 +4068,9 @@ async function registerMiddlewares(server, middlewares, options) {
3919
4068
  __name(registerMiddlewares, "registerMiddlewares");
3920
4069
 
3921
4070
  // src/route-parser.ts
3922
- var fs11 = __toESM(require("fs"), 1);
4071
+ var fs12 = __toESM(require("fs"), 1);
3923
4072
  var path7 = __toESM(require("path"), 1);
3924
- var crypto2 = __toESM(require("crypto"), 1);
4073
+ var crypto3 = __toESM(require("crypto"), 1);
3925
4074
  var import_parser = require("@babel/parser");
3926
4075
  var import_traverse = __toESM(require("@babel/traverse"), 1);
3927
4076
  var t = __toESM(require("@babel/types"), 1);
@@ -3946,8 +4095,8 @@ function routeParserLog(level, message, ...args) {
3946
4095
  __name(routeParserLog, "routeParserLog");
3947
4096
  function calculateFileHash(filePath) {
3948
4097
  try {
3949
- const content = fs11.readFileSync(filePath, "utf-8");
3950
- return crypto2.createHash("md5").update(content).digest("hex");
4098
+ const content = fs12.readFileSync(filePath, "utf-8");
4099
+ return crypto3.createHash("md5").update(content).digest("hex");
3951
4100
  } catch (error) {
3952
4101
  routeParserLog("warn", "Failed to calculate file hash:", error.message);
3953
4102
  return null;
@@ -4031,10 +4180,10 @@ function parseRoutesFromFile(appPath, basePath, options = {}) {
4031
4180
  const defaultPath = applyBasePath && basePath ? `${basePath}/` : "/";
4032
4181
  try {
4033
4182
  const appFilePath = path7.resolve(process.cwd(), appPath);
4034
- if (!fs11.existsSync(appFilePath)) {
4183
+ if (!fs12.existsSync(appFilePath)) {
4035
4184
  throw new Error(`App file does not exist: ${appFilePath}`);
4036
4185
  }
4037
- const sourceCode = fs11.readFileSync(appFilePath, "utf-8");
4186
+ const sourceCode = fs12.readFileSync(appFilePath, "utf-8");
4038
4187
  const ast = (0, import_parser.parse)(sourceCode, {
4039
4188
  sourceType: "module",
4040
4189
  plugins: [