From f38e91b51ab043c74ac501ffdcc49cea20605e7f Mon Sep 17 00:00:00 2001 From: melvinchia3636 Date: Tue, 9 Jun 2026 19:30:49 +0800 Subject: [PATCH] refactor: move traceRouteStack to server-utils --- .vscode/settings.json | 6 +-- bun.lock | 46 ++++++++++--------- packages/server-utils/src/index.ts | 7 +++ .../src/routes}/traceRouteStack.ts | 35 ++++++++++---- server/src/core/routes/index.ts | 28 +---------- server/src/index.ts | 2 +- 6 files changed, 64 insertions(+), 60 deletions(-) rename {server/src/core/functions/initialization => packages/server-utils/src/routes}/traceRouteStack.ts (67%) diff --git a/.vscode/settings.json b/.vscode/settings.json index cac2b5243..8fdc3d4ca 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,6 +11,6 @@ "vars" ], "typescript.tsdk": "node_modules/typescript/lib", - "typescript.experimental.useTsgo": false, - "js/ts.tsdk.path": "node_modules/typescript/lib" -} + "js/ts.tsdk.path": "node_modules/typescript/lib", + "js/ts.experimental.useTsgo": true +} \ No newline at end of file diff --git a/bun.lock b/bun.lock index ccc8b951f..1bc97e9a9 100644 --- a/bun.lock +++ b/bun.lock @@ -38,6 +38,7 @@ "@types/tinycolor2": "^1.4.6", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", + "@typescript/native-preview": "^7.0.0-dev.20260609.1", "@vitejs/plugin-react": "^4.4.1", "bun-types": "latest", "concurrently": "^9.1.2", @@ -69,6 +70,17 @@ "@lifeforge/ui": "workspace:*", }, }, + "apps/lifeforge--api--endpoint-explorer": { + "name": "@lifeforge/lifeforge--api-endpoint-explorer", + "version": "0.0.5", + "peerDependencies": { + "@lifeforge/api": "workspace:*", + "@lifeforge/configs": "workspace:*", + "@lifeforge/federation": "workspace:*", + "@lifeforge/server-utils": "workspace:*", + "@lifeforge/ui": "workspace:*", + }, + }, "apps/lifeforge--utility-widgets": { "name": "@lifeforge/lifeforge--utility-widgets", "version": "0.0.1", @@ -137,7 +149,6 @@ "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", - "@iconify/react": "^6.0.2", "@lifeforge/api": "workspace:*", "@lifeforge/federation": "workspace:*", "@lifeforge/ui": "workspace:*", @@ -161,7 +172,6 @@ "react-virtualized": "^9.22.6", "recharts": "^2.15.0", "socket.io-client": "^4.8.1", - "typescript": "^5.9.3", "uuid": "^13.0.0", }, "devDependencies": { @@ -236,8 +246,8 @@ }, "devDependencies": { "@types/crypto-js": "^4.2.2", - "@types/react": "^19.2.0", - "typescript": "^5.9.3", + "@vitejs/plugin-react": "^4.7.0", + "vite-plugin-dts": "^4.5.4", }, "peerDependencies": { "@tanstack/react-query": "^5.90.2", @@ -248,18 +258,15 @@ "packages/configs": { "name": "@lifeforge/configs", "version": "0.0.1", - "dependencies": { - "zod": "4.3.5", - }, "devDependencies": { "@types/react": "^19.2.0", - "typescript": "^5.9.3", }, "peerDependencies": { "@originjs/vite-plugin-federation": "^1.4.1", "@vitejs/plugin-react": "^4.4.1", "react": "^19.2.0", "vite": "^7.1.9", + "zod": "4.3.5", }, }, "packages/federation": { @@ -267,11 +274,9 @@ "version": "0.0.1", "dependencies": { "@lifeforge/api": "workspace:*", - "zod": "4.3.5", }, "devDependencies": { "@types/react": "^19.2.0", - "typescript": "^5.9.3", }, "peerDependencies": { "react": "^19.2.0", @@ -288,7 +293,6 @@ }, "devDependencies": { "@types/node": "^25.0.6", - "typescript": "^5.9.0", "vitest": "^4.0.0", }, }, @@ -310,7 +314,6 @@ "socket.io": "^4.8.3", "tesseract.js": "^5.1.1", "uuid": "^11.1.0", - "zod": "4.3.5", }, "devDependencies": { "@types/crypto-js": "^4.2.2", @@ -318,7 +321,6 @@ "@types/express-serve-static-core": "4", "@types/lodash": "^4.17.21", "@types/multer": "^1.4.13", - "typescript": "^5.8.3", "vitest": "^4.1.7", }, "peerDependencies": { @@ -760,6 +762,8 @@ "@lifeforge/lifeforge--achievements": ["@lifeforge/lifeforge--achievements@workspace:apps/lifeforge--achievements"], + "@lifeforge/lifeforge--api-endpoint-explorer": ["@lifeforge/lifeforge--api-endpoint-explorer@workspace:apps/lifeforge--api--endpoint-explorer"], + "@lifeforge/lifeforge--lang-en": ["@lifeforge/lifeforge--lang-en@workspace:locales/lifeforge--lang-en"], "@lifeforge/lifeforge--utility-widgets": ["@lifeforge/lifeforge--utility-widgets@workspace:apps/lifeforge--utility-widgets"], @@ -1300,21 +1304,21 @@ "@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@7.18.0", "", { "dependencies": { "@typescript-eslint/types": "7.18.0", "eslint-visitor-keys": "^3.4.3" } }, "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg=="], - "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260608.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260608.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260608.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260608.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260608.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260608.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260608.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260608.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-nJyuPQwFn/VlP7JvFA0jPHJMMHaDy5vWM5xRYUwjZZd3dcY7LldfZ1EkdPOLc+GW0trXcmvRemQ4XCsBkOGOUQ=="], + "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260609.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260609.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260609.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260609.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260609.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260609.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260609.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260609.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-1HOuH/u/451O3hx4Z9fesNqarpeit6UfkgwK96sCVWi5p69F0N3v+6bI969lLIjF7K9dbYQNiWUaZ6Wik87iKg=="], - "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260608.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZGs+yhsPWFDkSHydJyF8I3d4witpXiD69auWZtSotNWG3gBR5Ne291m38rvxoLFDRlFHwAQpPT7q3yStp+cQSQ=="], + "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260609.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Yf/zHEadP/yUiWUdM/mZVfEVFJuGMf6nhRSFif0vp+FwtfGU4jmlpNF7BTJJdOHrrcWkwEJKzAoMCtEtyxhuyQ=="], - "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260608.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-cxmBCl1+fvGgom2vwPTbZ43bW68rfjDIU1ZDQGtjXvj1bpW9fEozskJqgZdNNHu8WQIl17zj4Ke+noENwgGu9w=="], + "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260609.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-z4dYWI57CPHs0wV/FWFth8fWmqYH7iOm7THOfZ5Fv0jo/SWK6kE1kEUIqIAExqo7ueRNqSrCw0I8U1J4TJszAw=="], - "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260608.1", "", { "os": "linux", "cpu": "arm" }, "sha512-tWFLuzAWg8XnOkN7f94MAW8qLTMG7Q74IXHSG18cVmftuBxwvn1Lov+0Rnd9rsH7VAuSIu00Co6G3/PbGvGrxA=="], + "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260609.1", "", { "os": "linux", "cpu": "arm" }, "sha512-mEtN8BbAgVtBu/5MVomYquXNvgok2C0KG6V0D4SV1jfBJNtlcqbp0WuIqT0bnM9DA4TgzcHvnFMpwGSK/dqI5A=="], - "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260608.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-/JPrQqa36CSMdXeEXhZ0mYUmufehIL8ujagXvU8z/3/b/CeWjgoZLJCuy76k9NLZCG7wGvcFWFK5kgOEKyDQdw=="], + "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260609.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-OxNVWH9IhrMAzNlDyDit1dPO64GFIDPOUKoruIkJ9A1ZEONfIHXG5f+V3si9jtuNmuomiz9FjpbzOqLsgaxt+w=="], - "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260608.1", "", { "os": "linux", "cpu": "x64" }, "sha512-QfVLSH1NMAOa+N6Ey9cFHWppLFISOeCG3c0nJER3LXrDwkE4WtsVGHQ4IRDpIYT3Q2iC7QWUFWwrHayNKlrGYA=="], + "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260609.1", "", { "os": "linux", "cpu": "x64" }, "sha512-KO8WO1gBIC09T3255RlTY42TGu8en5mEkLPQu2wkMn+dX2T8KYL64zXrCeLeUWa0NvmVdJUeyWu3pFOn3zKemw=="], - "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260608.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-DvpbX0aPCWCe4bJEP0cOky4cCwF+5QJZHsLoGV4gYd+0EFEkarTOxZG0CezP30E/iX6u1RUOrtTeMZOQthqDpQ=="], + "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260609.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-+8q19LWjnMKK6SF3PLeMEalbfWDYWHs0AU8kSFCBCke/RLoDG4FjQzVtLgUo+KWhsmZMosiEyqEnZmSlED2tIQ=="], - "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260608.1", "", { "os": "win32", "cpu": "x64" }, "sha512-J9nQB87sU+YJE3fq3XKwQvgLNWVXiEWyOP1L/FuyOlHb77UkFXmpaUbFTJ1w7rfd91nlKb2EECfsdTxbELUT0Q=="], + "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260609.1", "", { "os": "win32", "cpu": "x64" }, "sha512-qNPcss+6yRoNFfFIKQbPwJWYxDfOZwyL8JBJh4J+yMLOad/+/AOjsO4EtZsIpv5PMCjpnD75coBoDkw+5NkItw=="], "@uidotdev/usehooks": ["@uidotdev/usehooks@2.4.1", "", { "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg=="], diff --git a/packages/server-utils/src/index.ts b/packages/server-utils/src/index.ts index 71b1e4bc3..ebbbf9635 100644 --- a/packages/server-utils/src/index.ts +++ b/packages/server-utils/src/index.ts @@ -110,3 +110,10 @@ export { serializeRoutes, writeContractFileToClient } from './utils/writeContractFile' + +export { + default as traceRouteStack, + type Route, + type RouteStackLayer +} from './routes/traceRouteStack' + diff --git a/server/src/core/functions/initialization/traceRouteStack.ts b/packages/server-utils/src/routes/traceRouteStack.ts similarity index 67% rename from server/src/core/functions/initialization/traceRouteStack.ts rename to packages/server-utils/src/routes/traceRouteStack.ts index 4e5c3d5ee..4526805af 100644 --- a/server/src/core/functions/initialization/traceRouteStack.ts +++ b/packages/server-utils/src/routes/traceRouteStack.ts @@ -1,6 +1,6 @@ import z from 'zod' -type Route = { +export type Route = { method: string path: string description: string @@ -12,13 +12,29 @@ type Route = { } } +export interface RouteStackLayer { + regexp: { + toString(): string + } + route?: { + path?: string + methods?: Record + stack?: RouteStackLayer[] + } + handle?: unknown +} + export default function traceRouteStack( - stack: any, - path = '', + stack: RouteStackLayer[], + path: string = '', routes: Route[] = [] -) { +): Route[] { for (const layer of stack) { - if (layer.handle?.stack && Array.isArray(layer.handle.stack)) { + const handle = layer.handle as + | { stack?: RouteStackLayer[]; meta?: Route } + | undefined + + if (handle?.stack && Array.isArray(handle.stack)) { const pathName = layer.regexp .toString() @@ -27,7 +43,7 @@ export default function traceRouteStack( const fullPath = [path, pathName].filter(Boolean).join('/') - traceRouteStack(layer.handle.stack, fullPath, routes) + traceRouteStack(handle.stack, fullPath, routes) } // Check for route dispatch by looking for .route property @@ -55,8 +71,11 @@ export default function traceRouteStack( const routeStack = layer.route.stack || [] - const controllerLayerMeta = - routeStack[routeStack.length - 1]?.handle?.meta + const controllerLayerMeta = ( + routeStack[routeStack.length - 1]?.handle as + | { meta?: Route } + | undefined + )?.meta if (!controllerLayerMeta) { continue diff --git a/server/src/core/routes/index.ts b/server/src/core/routes/index.ts index b345144b1..d8d3d4ce1 100644 --- a/server/src/core/routes/index.ts +++ b/server/src/core/routes/index.ts @@ -1,16 +1,13 @@ import { ROOT_DIR } from '@constants' -import traceRouteStack from '@functions/initialization/traceRouteStack' import { loadModuleRoutes } from '@functions/modules/loadModuleRoutes' import { registerRoutes } from '@functions/routes/functions/forgeRouter' import { clientError } from '@functions/routes/utils/response' import express from 'express' import path from 'path' -import z from 'zod' import { forgeRouter } from '@lifeforge/server-utils' import coreRoutes from './core.routes' -import forge from './forge' const router = express.Router() @@ -18,35 +15,12 @@ const router = express.Router() // Type assertion ensures TypeScript uses generated types for inference const appRoutes = await loadModuleRoutes() -const listRoutes = forge - .query({ - description: 'List all available API routes', - input: {}, - output: { - OK: z.array( - z.object({ - method: z.string(), - path: z.string(), - description: z.string(), - schema: z.object({ - response: z.unknown(), - params: z.unknown().optional(), - body: z.unknown().optional(), - query: z.unknown().optional() - }) - }) - ) - } - }) - .callback(async ({ response }) => response.ok(traceRouteStack(router.stack))) - const mainRoutes = forgeRouter({ ...coreRoutes, modules: forgeRouter({ ...coreRoutes.modules, ...appRoutes - }), - listRoutes + }) }) router.get('/hello', (_, res) => { diff --git a/server/src/index.ts b/server/src/index.ts index 865956729..b07a15db1 100755 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -2,8 +2,8 @@ import { PORT } from '@constants' import checkDB from '@functions/database/dbUtils' import ensureCredentials from '@functions/initialization/ensureCredentials' import { LocaleService } from '@functions/initialization/localeService' -import traceRouteStack from '@functions/initialization/traceRouteStack' import { LOG_LEVELS, type LogLevel, coreLogger } from '@functions/logging' +import { traceRouteStack } from '@lifeforge/server-utils' import createSocketServer from '@functions/socketio/createSocketServer' import chalk from 'chalk' import { program } from 'commander'