@route-forge/core 0.0.1
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/README.md +34 -0
- package/dist/codegen.cjs +174 -0
- package/dist/codegen.cjs.map +1 -0
- package/dist/codegen.d.cts +47 -0
- package/dist/codegen.d.ts +47 -0
- package/dist/codegen.js +167 -0
- package/dist/codegen.js.map +1 -0
- package/dist/index.cjs +704 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +151 -0
- package/dist/index.d.ts +151 -0
- package/dist/index.js +690 -0
- package/dist/index.js.map +1 -0
- package/dist/types-UxdZAlmW.d.cts +187 -0
- package/dist/types-UxdZAlmW.d.ts +187 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @route-forge/core
|
|
2
|
+
|
|
3
|
+
框架无关的命名路由客户端核心:分级懒加载、隔离缓存、并发去重、登录态感知、拦截器。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @route-forge/core
|
|
9
|
+
# 可选:axios 适配器
|
|
10
|
+
pnpm add axios
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 基本用法
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createForge } from '@route-forge/core'
|
|
17
|
+
|
|
18
|
+
const forge = createForge({
|
|
19
|
+
endpoint: '/_forge/routes',
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const route = await forge.route('login.show')
|
|
23
|
+
// → { method: 'GET', uri: '/login', level: 'public' }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 文档
|
|
27
|
+
|
|
28
|
+
- 仓库主页: https://github.com/xyj2156/route-forge
|
|
29
|
+
- 设计文档: https://github.com/xyj2156/route-forge/blob/main/.docs/DESIGN.md
|
|
30
|
+
- 规范: https://github.com/xyj2156/route-forge/blob/main/.docs/SPEC.md
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
MIT
|
package/dist/codegen.cjs
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// src/codegen/index.ts
|
|
5
|
+
var BODY_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
|
|
6
|
+
function parseArgs(argv) {
|
|
7
|
+
const opts = {};
|
|
8
|
+
for (let i = 0; i < argv.length; i++) {
|
|
9
|
+
const arg = argv[i];
|
|
10
|
+
const next = () => {
|
|
11
|
+
const v = argv[++i];
|
|
12
|
+
if (v === void 0) {
|
|
13
|
+
throw new Error(`[route-forge/codegen] missing value for ${arg}`);
|
|
14
|
+
}
|
|
15
|
+
return v;
|
|
16
|
+
};
|
|
17
|
+
if (arg === "--endpoint") {
|
|
18
|
+
opts.endpoint = next();
|
|
19
|
+
} else if (arg?.startsWith("--endpoint=")) {
|
|
20
|
+
opts.endpoint = arg.slice("--endpoint=".length);
|
|
21
|
+
} else if (arg === "--levels") {
|
|
22
|
+
opts.levels = next().split(",").map((s) => s.trim()).filter(Boolean);
|
|
23
|
+
} else if (arg?.startsWith("--levels=")) {
|
|
24
|
+
opts.levels = arg.slice("--levels=".length).split(",").map((s) => s.trim()).filter(Boolean);
|
|
25
|
+
} else if (arg === "--out") {
|
|
26
|
+
opts.out = next();
|
|
27
|
+
} else if (arg?.startsWith("--out=")) {
|
|
28
|
+
opts.out = arg.slice("--out=".length);
|
|
29
|
+
} else if (arg === "--responseTypes") {
|
|
30
|
+
opts.responseTypes = next();
|
|
31
|
+
} else if (arg?.startsWith("--responseTypes=")) {
|
|
32
|
+
opts.responseTypes = arg.slice("--responseTypes=".length);
|
|
33
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
34
|
+
printHelp();
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (!opts.endpoint) {
|
|
39
|
+
console.error("[route-forge/codegen] --endpoint is required");
|
|
40
|
+
printHelp();
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
if (!opts.out) {
|
|
44
|
+
console.error("[route-forge/codegen] --out is required");
|
|
45
|
+
printHelp();
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
endpoint: opts.endpoint,
|
|
50
|
+
levels: opts.levels ?? [],
|
|
51
|
+
out: opts.out,
|
|
52
|
+
responseTypes: opts.responseTypes
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function printHelp() {
|
|
56
|
+
console.log(`
|
|
57
|
+
route-forge codegen - generate TS route types from backend summary endpoint
|
|
58
|
+
|
|
59
|
+
Usage:
|
|
60
|
+
npx @route-forge/core codegen --endpoint URL --out PATH [--levels a,b,c] [--responseTypes PATH]
|
|
61
|
+
|
|
62
|
+
Options:
|
|
63
|
+
--endpoint URL Backend summary endpoint (e.g. http://localhost/_forge/routes)
|
|
64
|
+
--levels a,b,c Optional: explicit level list (skip auto-discovery)
|
|
65
|
+
--out PATH Output .d.ts file path
|
|
66
|
+
--responseTypes PATH Optional: JSON file mapping route names to response types
|
|
67
|
+
-h, --help Show this help
|
|
68
|
+
`);
|
|
69
|
+
}
|
|
70
|
+
async function fetchSummary(endpoint) {
|
|
71
|
+
const resp = await fetch(endpoint, { method: "GET" });
|
|
72
|
+
if (!resp.ok) {
|
|
73
|
+
throw new Error(`summary endpoint ${endpoint} returned ${resp.status}`);
|
|
74
|
+
}
|
|
75
|
+
return await resp.json();
|
|
76
|
+
}
|
|
77
|
+
async function fetchLevel(endpoint, level) {
|
|
78
|
+
const base = endpoint.replace(/\/$/, "");
|
|
79
|
+
const url = `${base}/${encodeURIComponent(level)}`;
|
|
80
|
+
const resp = await fetch(url, { method: "GET" });
|
|
81
|
+
if (!resp.ok) {
|
|
82
|
+
throw new Error(`level endpoint ${url} returned ${resp.status}`);
|
|
83
|
+
}
|
|
84
|
+
const data = await resp.json();
|
|
85
|
+
return { routes: data.routes ?? {} };
|
|
86
|
+
}
|
|
87
|
+
function generateRouteTypes(routes) {
|
|
88
|
+
const entries = Object.entries(routes).map(([name, meta]) => {
|
|
89
|
+
const params = (meta.parameters ?? []).map((p) => `${p}: string | number;`).join(" ");
|
|
90
|
+
const method = meta.methods?.find((m) => m.toUpperCase() !== "HEAD") ?? "GET";
|
|
91
|
+
const methodUpper = method.toUpperCase();
|
|
92
|
+
const bodyField = BODY_METHODS.has(methodUpper) ? "\n body: unknown;" : "";
|
|
93
|
+
return ` ${JSON.stringify(name)}: {
|
|
94
|
+
method: ${JSON.stringify(methodUpper)};
|
|
95
|
+
params: { ${params} };${bodyField}
|
|
96
|
+
response: unknown;
|
|
97
|
+
};`;
|
|
98
|
+
});
|
|
99
|
+
return `// AUTO-GENERATED by @route-forge/core codegen. Do not edit.
|
|
100
|
+
export interface ForgeRoutes {
|
|
101
|
+
${entries.join("\n")}
|
|
102
|
+
}
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
async function main(argv = process.argv.slice(2)) {
|
|
106
|
+
const opts = parseArgs(argv);
|
|
107
|
+
let levels = opts.levels;
|
|
108
|
+
if (levels.length === 0) {
|
|
109
|
+
try {
|
|
110
|
+
const summary = await fetchSummary(opts.endpoint);
|
|
111
|
+
levels = Object.keys(summary.levels);
|
|
112
|
+
} catch (e) {
|
|
113
|
+
console.error(`[route-forge/codegen] failed to auto-discover levels from summary endpoint: ${e.message}`);
|
|
114
|
+
console.error("hint: pass --levels explicitly to skip auto-discovery");
|
|
115
|
+
process.exit(1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (levels.length === 0) {
|
|
119
|
+
console.error("[route-forge/codegen] no levels found; pass --levels explicitly");
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
const levelFetches = await Promise.allSettled(
|
|
123
|
+
levels.map((lvl) => fetchLevel(opts.endpoint, lvl))
|
|
124
|
+
);
|
|
125
|
+
const merged = {};
|
|
126
|
+
let failedLevels = 0;
|
|
127
|
+
levelFetches.forEach((res, idx) => {
|
|
128
|
+
if (res.status === "fulfilled") {
|
|
129
|
+
Object.assign(merged, res.value.routes);
|
|
130
|
+
} else {
|
|
131
|
+
failedLevels++;
|
|
132
|
+
console.warn(`[route-forge/codegen] failed to fetch level "${levels[idx]}": ${res.reason.message}`);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
if (Object.keys(merged).length === 0) {
|
|
136
|
+
console.error("[route-forge/codegen] no routes collected from any level");
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
const dts = generateRouteTypes(merged);
|
|
140
|
+
const fs = await import('fs/promises');
|
|
141
|
+
const path = await import('path');
|
|
142
|
+
const outPath = path.resolve(opts.out);
|
|
143
|
+
const dir = path.dirname(outPath);
|
|
144
|
+
await fs.mkdir(dir, { recursive: true });
|
|
145
|
+
await fs.writeFile(outPath, dts, "utf8");
|
|
146
|
+
console.log(`[route-forge/codegen] written ${Object.keys(merged).length} routes to ${outPath}`);
|
|
147
|
+
if (failedLevels > 0) {
|
|
148
|
+
console.warn(`[route-forge/codegen] ${failedLevels} level(s) failed; output may be incomplete`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
var invokedFromCli = (() => {
|
|
152
|
+
try {
|
|
153
|
+
if (!process.argv[1]) return false;
|
|
154
|
+
const argv1 = process.argv[1].replace(/\\/g, "/");
|
|
155
|
+
return argv1.endsWith("/codegen.js") || argv1.endsWith("/codegen.cjs") || argv1.endsWith("/codegen");
|
|
156
|
+
} catch {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
})();
|
|
160
|
+
if (invokedFromCli) {
|
|
161
|
+
main().catch((err) => {
|
|
162
|
+
console.error(err);
|
|
163
|
+
process.exit(1);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
exports.fetchLevel = fetchLevel;
|
|
168
|
+
exports.fetchSummary = fetchSummary;
|
|
169
|
+
exports.generateRouteTypes = generateRouteTypes;
|
|
170
|
+
exports.main = main;
|
|
171
|
+
exports.parseArgs = parseArgs;
|
|
172
|
+
exports.runCodegen = main;
|
|
173
|
+
//# sourceMappingURL=codegen.cjs.map
|
|
174
|
+
//# sourceMappingURL=codegen.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/codegen/index.ts"],"names":[],"mappings":"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","file":"codegen.cjs","sourcesContent":["#!/usr/bin/env node\n/**\n * @route-forge/core codegen CLI\n * @see .docs/SPEC.md §4.2\n *\n * 用法:\n * npx @route-forge/core codegen \\\n * --endpoint http://localhost/_forge/routes \\\n * --levels public,client,manage,admin \\\n * --out src/types/forge-routes.d.ts\n */\n\nimport type { RouteMeta } from '../types.js';\n\nexport interface CodegenOptions {\n endpoint: string;\n levels: string[];\n out: string;\n responseTypes?: string;\n}\n\nconst BODY_METHODS = new Set(['POST', 'PUT', 'PATCH']);\n\n/**\n * 解析 argv(最小手写实现,不引入 commander/yargs)\n * 支持:--endpoint VALUE / --endpoint=VALUE / --levels a,b,c / --out PATH / --responseTypes PATH\n */\nexport function parseArgs(argv: string[]): CodegenOptions {\n const opts: Partial<CodegenOptions> = {};\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n const next = (): string => {\n const v = argv[++i];\n if (v === undefined) {\n throw new Error(`[route-forge/codegen] missing value for ${arg}`);\n }\n return v;\n };\n\n if (arg === '--endpoint') {\n opts.endpoint = next();\n } else if (arg?.startsWith('--endpoint=')) {\n opts.endpoint = arg.slice('--endpoint='.length);\n } else if (arg === '--levels') {\n opts.levels = next().split(',').map((s) => s.trim()).filter(Boolean);\n } else if (arg?.startsWith('--levels=')) {\n opts.levels = arg.slice('--levels='.length).split(',').map((s) => s.trim()).filter(Boolean);\n } else if (arg === '--out') {\n opts.out = next();\n } else if (arg?.startsWith('--out=')) {\n opts.out = arg.slice('--out='.length);\n } else if (arg === '--responseTypes') {\n opts.responseTypes = next();\n } else if (arg?.startsWith('--responseTypes=')) {\n opts.responseTypes = arg.slice('--responseTypes='.length);\n } else if (arg === '--help' || arg === '-h') {\n printHelp();\n process.exit(0);\n }\n }\n\n if (!opts.endpoint) {\n console.error('[route-forge/codegen] --endpoint is required');\n printHelp();\n process.exit(1);\n }\n if (!opts.out) {\n console.error('[route-forge/codegen] --out is required');\n printHelp();\n process.exit(1);\n }\n\n return {\n endpoint: opts.endpoint,\n levels: opts.levels ?? [],\n out: opts.out,\n responseTypes: opts.responseTypes,\n };\n}\n\nfunction printHelp(): void {\n console.log(`\nroute-forge codegen - generate TS route types from backend summary endpoint\n\nUsage:\n npx @route-forge/core codegen --endpoint URL --out PATH [--levels a,b,c] [--responseTypes PATH]\n\nOptions:\n --endpoint URL Backend summary endpoint (e.g. http://localhost/_forge/routes)\n --levels a,b,c Optional: explicit level list (skip auto-discovery)\n --out PATH Output .d.ts file path\n --responseTypes PATH Optional: JSON file mapping route names to response types\n -h, --help Show this help\n`);\n}\n\n/**\n * 拉取摘要端点\n */\nexport async function fetchSummary(endpoint: string): Promise<{ levels: Record<string, unknown> }> {\n const resp = await fetch(endpoint, { method: 'GET' });\n if (!resp.ok) {\n throw new Error(`summary endpoint ${endpoint} returned ${resp.status}`);\n }\n return (await resp.json()) as { levels: Record<string, unknown> };\n}\n\n/**\n * 拉取单个层级的路由元信息\n */\nexport async function fetchLevel(endpoint: string, level: string): Promise<{ routes: Record<string, RouteMeta> }> {\n const base = endpoint.replace(/\\/$/, '');\n const url = `${base}/${encodeURIComponent(level)}`;\n const resp = await fetch(url, { method: 'GET' });\n if (!resp.ok) {\n throw new Error(`level endpoint ${url} returned ${resp.status}`);\n }\n const data = (await resp.json()) as { routes?: Record<string, RouteMeta> };\n return { routes: data.routes ?? {} };\n}\n\n/**\n * 生成 d.ts 内容\n */\nexport function generateRouteTypes(routes: Record<string, RouteMeta>): string {\n const entries = Object.entries(routes).map(([name, meta]) => {\n const params = (meta.parameters ?? []).map((p) => `${p}: string | number;`).join(' ');\n const method = meta.methods?.find((m) => m.toUpperCase() !== 'HEAD') ?? 'GET';\n const methodUpper = method.toUpperCase();\n const bodyField = BODY_METHODS.has(methodUpper) ? '\\n body: unknown;' : '';\n return ` ${JSON.stringify(name)}: {\n method: ${JSON.stringify(methodUpper)};\n params: { ${params} };${bodyField}\n response: unknown;\n };`;\n });\n return `// AUTO-GENERATED by @route-forge/core codegen. Do not edit.\nexport interface ForgeRoutes {\n${entries.join('\\n')}\n}\n`;\n}\n\n/**\n * CLI 主入口\n */\nexport async function main(argv: string[] = process.argv.slice(2)): Promise<void> {\n const opts = parseArgs(argv);\n\n let levels = opts.levels;\n if (levels.length === 0) {\n try {\n const summary = await fetchSummary(opts.endpoint);\n levels = Object.keys(summary.levels);\n } catch (e) {\n console.error(`[route-forge/codegen] failed to auto-discover levels from summary endpoint: ${(e as Error).message}`);\n console.error('hint: pass --levels explicitly to skip auto-discovery');\n process.exit(1);\n }\n }\n\n if (levels.length === 0) {\n console.error('[route-forge/codegen] no levels found; pass --levels explicitly');\n process.exit(1);\n }\n\n const levelFetches = await Promise.allSettled(\n levels.map((lvl) => fetchLevel(opts.endpoint, lvl)),\n );\n\n const merged: Record<string, RouteMeta> = {};\n let failedLevels = 0;\n levelFetches.forEach((res, idx) => {\n if (res.status === 'fulfilled') {\n Object.assign(merged, res.value.routes);\n } else {\n failedLevels++;\n console.warn(`[route-forge/codegen] failed to fetch level \"${levels[idx]}\": ${(res.reason as Error).message}`);\n }\n });\n\n if (Object.keys(merged).length === 0) {\n console.error('[route-forge/codegen] no routes collected from any level');\n process.exit(1);\n }\n\n const dts = generateRouteTypes(merged);\n\n const fs = await import('node:fs/promises');\n const path = await import('node:path');\n const outPath = path.resolve(opts.out);\n const dir = path.dirname(outPath);\n await fs.mkdir(dir, { recursive: true });\n await fs.writeFile(outPath, dts, 'utf8');\n\n console.log(`[route-forge/codegen] written ${Object.keys(merged).length} routes to ${outPath}`);\n if (failedLevels > 0) {\n console.warn(`[route-forge/codegen] ${failedLevels} level(s) failed; output may be incomplete`);\n }\n}\n\nconst invokedFromCli = (() => {\n try {\n if (!process.argv[1]) return false;\n const argv1 = process.argv[1].replace(/\\\\/g, '/');\n return argv1.endsWith('/codegen.js') || argv1.endsWith('/codegen.cjs') || argv1.endsWith('/codegen');\n } catch {\n return false;\n }\n})();\n\nif (invokedFromCli) {\n main().catch((err) => {\n console.error(err);\n process.exit(1);\n });\n}\n\nexport { main as runCodegen };\n"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { R as RouteMeta } from './types-UxdZAlmW.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @route-forge/core codegen CLI
|
|
6
|
+
* @see .docs/SPEC.md §4.2
|
|
7
|
+
*
|
|
8
|
+
* 用法:
|
|
9
|
+
* npx @route-forge/core codegen \
|
|
10
|
+
* --endpoint http://localhost/_forge/routes \
|
|
11
|
+
* --levels public,client,manage,admin \
|
|
12
|
+
* --out src/types/forge-routes.d.ts
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
interface CodegenOptions {
|
|
16
|
+
endpoint: string;
|
|
17
|
+
levels: string[];
|
|
18
|
+
out: string;
|
|
19
|
+
responseTypes?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 解析 argv(最小手写实现,不引入 commander/yargs)
|
|
23
|
+
* 支持:--endpoint VALUE / --endpoint=VALUE / --levels a,b,c / --out PATH / --responseTypes PATH
|
|
24
|
+
*/
|
|
25
|
+
declare function parseArgs(argv: string[]): CodegenOptions;
|
|
26
|
+
/**
|
|
27
|
+
* 拉取摘要端点
|
|
28
|
+
*/
|
|
29
|
+
declare function fetchSummary(endpoint: string): Promise<{
|
|
30
|
+
levels: Record<string, unknown>;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* 拉取单个层级的路由元信息
|
|
34
|
+
*/
|
|
35
|
+
declare function fetchLevel(endpoint: string, level: string): Promise<{
|
|
36
|
+
routes: Record<string, RouteMeta>;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* 生成 d.ts 内容
|
|
40
|
+
*/
|
|
41
|
+
declare function generateRouteTypes(routes: Record<string, RouteMeta>): string;
|
|
42
|
+
/**
|
|
43
|
+
* CLI 主入口
|
|
44
|
+
*/
|
|
45
|
+
declare function main(argv?: string[]): Promise<void>;
|
|
46
|
+
|
|
47
|
+
export { type CodegenOptions, fetchLevel, fetchSummary, generateRouteTypes, main, parseArgs, main as runCodegen };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { R as RouteMeta } from './types-UxdZAlmW.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @route-forge/core codegen CLI
|
|
6
|
+
* @see .docs/SPEC.md §4.2
|
|
7
|
+
*
|
|
8
|
+
* 用法:
|
|
9
|
+
* npx @route-forge/core codegen \
|
|
10
|
+
* --endpoint http://localhost/_forge/routes \
|
|
11
|
+
* --levels public,client,manage,admin \
|
|
12
|
+
* --out src/types/forge-routes.d.ts
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
interface CodegenOptions {
|
|
16
|
+
endpoint: string;
|
|
17
|
+
levels: string[];
|
|
18
|
+
out: string;
|
|
19
|
+
responseTypes?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 解析 argv(最小手写实现,不引入 commander/yargs)
|
|
23
|
+
* 支持:--endpoint VALUE / --endpoint=VALUE / --levels a,b,c / --out PATH / --responseTypes PATH
|
|
24
|
+
*/
|
|
25
|
+
declare function parseArgs(argv: string[]): CodegenOptions;
|
|
26
|
+
/**
|
|
27
|
+
* 拉取摘要端点
|
|
28
|
+
*/
|
|
29
|
+
declare function fetchSummary(endpoint: string): Promise<{
|
|
30
|
+
levels: Record<string, unknown>;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* 拉取单个层级的路由元信息
|
|
34
|
+
*/
|
|
35
|
+
declare function fetchLevel(endpoint: string, level: string): Promise<{
|
|
36
|
+
routes: Record<string, RouteMeta>;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* 生成 d.ts 内容
|
|
40
|
+
*/
|
|
41
|
+
declare function generateRouteTypes(routes: Record<string, RouteMeta>): string;
|
|
42
|
+
/**
|
|
43
|
+
* CLI 主入口
|
|
44
|
+
*/
|
|
45
|
+
declare function main(argv?: string[]): Promise<void>;
|
|
46
|
+
|
|
47
|
+
export { type CodegenOptions, fetchLevel, fetchSummary, generateRouteTypes, main, parseArgs, main as runCodegen };
|
package/dist/codegen.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// src/codegen/index.ts
|
|
3
|
+
var BODY_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
|
|
4
|
+
function parseArgs(argv) {
|
|
5
|
+
const opts = {};
|
|
6
|
+
for (let i = 0; i < argv.length; i++) {
|
|
7
|
+
const arg = argv[i];
|
|
8
|
+
const next = () => {
|
|
9
|
+
const v = argv[++i];
|
|
10
|
+
if (v === void 0) {
|
|
11
|
+
throw new Error(`[route-forge/codegen] missing value for ${arg}`);
|
|
12
|
+
}
|
|
13
|
+
return v;
|
|
14
|
+
};
|
|
15
|
+
if (arg === "--endpoint") {
|
|
16
|
+
opts.endpoint = next();
|
|
17
|
+
} else if (arg?.startsWith("--endpoint=")) {
|
|
18
|
+
opts.endpoint = arg.slice("--endpoint=".length);
|
|
19
|
+
} else if (arg === "--levels") {
|
|
20
|
+
opts.levels = next().split(",").map((s) => s.trim()).filter(Boolean);
|
|
21
|
+
} else if (arg?.startsWith("--levels=")) {
|
|
22
|
+
opts.levels = arg.slice("--levels=".length).split(",").map((s) => s.trim()).filter(Boolean);
|
|
23
|
+
} else if (arg === "--out") {
|
|
24
|
+
opts.out = next();
|
|
25
|
+
} else if (arg?.startsWith("--out=")) {
|
|
26
|
+
opts.out = arg.slice("--out=".length);
|
|
27
|
+
} else if (arg === "--responseTypes") {
|
|
28
|
+
opts.responseTypes = next();
|
|
29
|
+
} else if (arg?.startsWith("--responseTypes=")) {
|
|
30
|
+
opts.responseTypes = arg.slice("--responseTypes=".length);
|
|
31
|
+
} else if (arg === "--help" || arg === "-h") {
|
|
32
|
+
printHelp();
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (!opts.endpoint) {
|
|
37
|
+
console.error("[route-forge/codegen] --endpoint is required");
|
|
38
|
+
printHelp();
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
if (!opts.out) {
|
|
42
|
+
console.error("[route-forge/codegen] --out is required");
|
|
43
|
+
printHelp();
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
endpoint: opts.endpoint,
|
|
48
|
+
levels: opts.levels ?? [],
|
|
49
|
+
out: opts.out,
|
|
50
|
+
responseTypes: opts.responseTypes
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function printHelp() {
|
|
54
|
+
console.log(`
|
|
55
|
+
route-forge codegen - generate TS route types from backend summary endpoint
|
|
56
|
+
|
|
57
|
+
Usage:
|
|
58
|
+
npx @route-forge/core codegen --endpoint URL --out PATH [--levels a,b,c] [--responseTypes PATH]
|
|
59
|
+
|
|
60
|
+
Options:
|
|
61
|
+
--endpoint URL Backend summary endpoint (e.g. http://localhost/_forge/routes)
|
|
62
|
+
--levels a,b,c Optional: explicit level list (skip auto-discovery)
|
|
63
|
+
--out PATH Output .d.ts file path
|
|
64
|
+
--responseTypes PATH Optional: JSON file mapping route names to response types
|
|
65
|
+
-h, --help Show this help
|
|
66
|
+
`);
|
|
67
|
+
}
|
|
68
|
+
async function fetchSummary(endpoint) {
|
|
69
|
+
const resp = await fetch(endpoint, { method: "GET" });
|
|
70
|
+
if (!resp.ok) {
|
|
71
|
+
throw new Error(`summary endpoint ${endpoint} returned ${resp.status}`);
|
|
72
|
+
}
|
|
73
|
+
return await resp.json();
|
|
74
|
+
}
|
|
75
|
+
async function fetchLevel(endpoint, level) {
|
|
76
|
+
const base = endpoint.replace(/\/$/, "");
|
|
77
|
+
const url = `${base}/${encodeURIComponent(level)}`;
|
|
78
|
+
const resp = await fetch(url, { method: "GET" });
|
|
79
|
+
if (!resp.ok) {
|
|
80
|
+
throw new Error(`level endpoint ${url} returned ${resp.status}`);
|
|
81
|
+
}
|
|
82
|
+
const data = await resp.json();
|
|
83
|
+
return { routes: data.routes ?? {} };
|
|
84
|
+
}
|
|
85
|
+
function generateRouteTypes(routes) {
|
|
86
|
+
const entries = Object.entries(routes).map(([name, meta]) => {
|
|
87
|
+
const params = (meta.parameters ?? []).map((p) => `${p}: string | number;`).join(" ");
|
|
88
|
+
const method = meta.methods?.find((m) => m.toUpperCase() !== "HEAD") ?? "GET";
|
|
89
|
+
const methodUpper = method.toUpperCase();
|
|
90
|
+
const bodyField = BODY_METHODS.has(methodUpper) ? "\n body: unknown;" : "";
|
|
91
|
+
return ` ${JSON.stringify(name)}: {
|
|
92
|
+
method: ${JSON.stringify(methodUpper)};
|
|
93
|
+
params: { ${params} };${bodyField}
|
|
94
|
+
response: unknown;
|
|
95
|
+
};`;
|
|
96
|
+
});
|
|
97
|
+
return `// AUTO-GENERATED by @route-forge/core codegen. Do not edit.
|
|
98
|
+
export interface ForgeRoutes {
|
|
99
|
+
${entries.join("\n")}
|
|
100
|
+
}
|
|
101
|
+
`;
|
|
102
|
+
}
|
|
103
|
+
async function main(argv = process.argv.slice(2)) {
|
|
104
|
+
const opts = parseArgs(argv);
|
|
105
|
+
let levels = opts.levels;
|
|
106
|
+
if (levels.length === 0) {
|
|
107
|
+
try {
|
|
108
|
+
const summary = await fetchSummary(opts.endpoint);
|
|
109
|
+
levels = Object.keys(summary.levels);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
console.error(`[route-forge/codegen] failed to auto-discover levels from summary endpoint: ${e.message}`);
|
|
112
|
+
console.error("hint: pass --levels explicitly to skip auto-discovery");
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (levels.length === 0) {
|
|
117
|
+
console.error("[route-forge/codegen] no levels found; pass --levels explicitly");
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
const levelFetches = await Promise.allSettled(
|
|
121
|
+
levels.map((lvl) => fetchLevel(opts.endpoint, lvl))
|
|
122
|
+
);
|
|
123
|
+
const merged = {};
|
|
124
|
+
let failedLevels = 0;
|
|
125
|
+
levelFetches.forEach((res, idx) => {
|
|
126
|
+
if (res.status === "fulfilled") {
|
|
127
|
+
Object.assign(merged, res.value.routes);
|
|
128
|
+
} else {
|
|
129
|
+
failedLevels++;
|
|
130
|
+
console.warn(`[route-forge/codegen] failed to fetch level "${levels[idx]}": ${res.reason.message}`);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
if (Object.keys(merged).length === 0) {
|
|
134
|
+
console.error("[route-forge/codegen] no routes collected from any level");
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
const dts = generateRouteTypes(merged);
|
|
138
|
+
const fs = await import('fs/promises');
|
|
139
|
+
const path = await import('path');
|
|
140
|
+
const outPath = path.resolve(opts.out);
|
|
141
|
+
const dir = path.dirname(outPath);
|
|
142
|
+
await fs.mkdir(dir, { recursive: true });
|
|
143
|
+
await fs.writeFile(outPath, dts, "utf8");
|
|
144
|
+
console.log(`[route-forge/codegen] written ${Object.keys(merged).length} routes to ${outPath}`);
|
|
145
|
+
if (failedLevels > 0) {
|
|
146
|
+
console.warn(`[route-forge/codegen] ${failedLevels} level(s) failed; output may be incomplete`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
var invokedFromCli = (() => {
|
|
150
|
+
try {
|
|
151
|
+
if (!process.argv[1]) return false;
|
|
152
|
+
const argv1 = process.argv[1].replace(/\\/g, "/");
|
|
153
|
+
return argv1.endsWith("/codegen.js") || argv1.endsWith("/codegen.cjs") || argv1.endsWith("/codegen");
|
|
154
|
+
} catch {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
})();
|
|
158
|
+
if (invokedFromCli) {
|
|
159
|
+
main().catch((err) => {
|
|
160
|
+
console.error(err);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export { fetchLevel, fetchSummary, generateRouteTypes, main, parseArgs, main as runCodegen };
|
|
166
|
+
//# sourceMappingURL=codegen.js.map
|
|
167
|
+
//# sourceMappingURL=codegen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/codegen/index.ts"],"names":[],"mappings":"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","file":"codegen.js","sourcesContent":["#!/usr/bin/env node\n/**\n * @route-forge/core codegen CLI\n * @see .docs/SPEC.md §4.2\n *\n * 用法:\n * npx @route-forge/core codegen \\\n * --endpoint http://localhost/_forge/routes \\\n * --levels public,client,manage,admin \\\n * --out src/types/forge-routes.d.ts\n */\n\nimport type { RouteMeta } from '../types.js';\n\nexport interface CodegenOptions {\n endpoint: string;\n levels: string[];\n out: string;\n responseTypes?: string;\n}\n\nconst BODY_METHODS = new Set(['POST', 'PUT', 'PATCH']);\n\n/**\n * 解析 argv(最小手写实现,不引入 commander/yargs)\n * 支持:--endpoint VALUE / --endpoint=VALUE / --levels a,b,c / --out PATH / --responseTypes PATH\n */\nexport function parseArgs(argv: string[]): CodegenOptions {\n const opts: Partial<CodegenOptions> = {};\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n const next = (): string => {\n const v = argv[++i];\n if (v === undefined) {\n throw new Error(`[route-forge/codegen] missing value for ${arg}`);\n }\n return v;\n };\n\n if (arg === '--endpoint') {\n opts.endpoint = next();\n } else if (arg?.startsWith('--endpoint=')) {\n opts.endpoint = arg.slice('--endpoint='.length);\n } else if (arg === '--levels') {\n opts.levels = next().split(',').map((s) => s.trim()).filter(Boolean);\n } else if (arg?.startsWith('--levels=')) {\n opts.levels = arg.slice('--levels='.length).split(',').map((s) => s.trim()).filter(Boolean);\n } else if (arg === '--out') {\n opts.out = next();\n } else if (arg?.startsWith('--out=')) {\n opts.out = arg.slice('--out='.length);\n } else if (arg === '--responseTypes') {\n opts.responseTypes = next();\n } else if (arg?.startsWith('--responseTypes=')) {\n opts.responseTypes = arg.slice('--responseTypes='.length);\n } else if (arg === '--help' || arg === '-h') {\n printHelp();\n process.exit(0);\n }\n }\n\n if (!opts.endpoint) {\n console.error('[route-forge/codegen] --endpoint is required');\n printHelp();\n process.exit(1);\n }\n if (!opts.out) {\n console.error('[route-forge/codegen] --out is required');\n printHelp();\n process.exit(1);\n }\n\n return {\n endpoint: opts.endpoint,\n levels: opts.levels ?? [],\n out: opts.out,\n responseTypes: opts.responseTypes,\n };\n}\n\nfunction printHelp(): void {\n console.log(`\nroute-forge codegen - generate TS route types from backend summary endpoint\n\nUsage:\n npx @route-forge/core codegen --endpoint URL --out PATH [--levels a,b,c] [--responseTypes PATH]\n\nOptions:\n --endpoint URL Backend summary endpoint (e.g. http://localhost/_forge/routes)\n --levels a,b,c Optional: explicit level list (skip auto-discovery)\n --out PATH Output .d.ts file path\n --responseTypes PATH Optional: JSON file mapping route names to response types\n -h, --help Show this help\n`);\n}\n\n/**\n * 拉取摘要端点\n */\nexport async function fetchSummary(endpoint: string): Promise<{ levels: Record<string, unknown> }> {\n const resp = await fetch(endpoint, { method: 'GET' });\n if (!resp.ok) {\n throw new Error(`summary endpoint ${endpoint} returned ${resp.status}`);\n }\n return (await resp.json()) as { levels: Record<string, unknown> };\n}\n\n/**\n * 拉取单个层级的路由元信息\n */\nexport async function fetchLevel(endpoint: string, level: string): Promise<{ routes: Record<string, RouteMeta> }> {\n const base = endpoint.replace(/\\/$/, '');\n const url = `${base}/${encodeURIComponent(level)}`;\n const resp = await fetch(url, { method: 'GET' });\n if (!resp.ok) {\n throw new Error(`level endpoint ${url} returned ${resp.status}`);\n }\n const data = (await resp.json()) as { routes?: Record<string, RouteMeta> };\n return { routes: data.routes ?? {} };\n}\n\n/**\n * 生成 d.ts 内容\n */\nexport function generateRouteTypes(routes: Record<string, RouteMeta>): string {\n const entries = Object.entries(routes).map(([name, meta]) => {\n const params = (meta.parameters ?? []).map((p) => `${p}: string | number;`).join(' ');\n const method = meta.methods?.find((m) => m.toUpperCase() !== 'HEAD') ?? 'GET';\n const methodUpper = method.toUpperCase();\n const bodyField = BODY_METHODS.has(methodUpper) ? '\\n body: unknown;' : '';\n return ` ${JSON.stringify(name)}: {\n method: ${JSON.stringify(methodUpper)};\n params: { ${params} };${bodyField}\n response: unknown;\n };`;\n });\n return `// AUTO-GENERATED by @route-forge/core codegen. Do not edit.\nexport interface ForgeRoutes {\n${entries.join('\\n')}\n}\n`;\n}\n\n/**\n * CLI 主入口\n */\nexport async function main(argv: string[] = process.argv.slice(2)): Promise<void> {\n const opts = parseArgs(argv);\n\n let levels = opts.levels;\n if (levels.length === 0) {\n try {\n const summary = await fetchSummary(opts.endpoint);\n levels = Object.keys(summary.levels);\n } catch (e) {\n console.error(`[route-forge/codegen] failed to auto-discover levels from summary endpoint: ${(e as Error).message}`);\n console.error('hint: pass --levels explicitly to skip auto-discovery');\n process.exit(1);\n }\n }\n\n if (levels.length === 0) {\n console.error('[route-forge/codegen] no levels found; pass --levels explicitly');\n process.exit(1);\n }\n\n const levelFetches = await Promise.allSettled(\n levels.map((lvl) => fetchLevel(opts.endpoint, lvl)),\n );\n\n const merged: Record<string, RouteMeta> = {};\n let failedLevels = 0;\n levelFetches.forEach((res, idx) => {\n if (res.status === 'fulfilled') {\n Object.assign(merged, res.value.routes);\n } else {\n failedLevels++;\n console.warn(`[route-forge/codegen] failed to fetch level \"${levels[idx]}\": ${(res.reason as Error).message}`);\n }\n });\n\n if (Object.keys(merged).length === 0) {\n console.error('[route-forge/codegen] no routes collected from any level');\n process.exit(1);\n }\n\n const dts = generateRouteTypes(merged);\n\n const fs = await import('node:fs/promises');\n const path = await import('node:path');\n const outPath = path.resolve(opts.out);\n const dir = path.dirname(outPath);\n await fs.mkdir(dir, { recursive: true });\n await fs.writeFile(outPath, dts, 'utf8');\n\n console.log(`[route-forge/codegen] written ${Object.keys(merged).length} routes to ${outPath}`);\n if (failedLevels > 0) {\n console.warn(`[route-forge/codegen] ${failedLevels} level(s) failed; output may be incomplete`);\n }\n}\n\nconst invokedFromCli = (() => {\n try {\n if (!process.argv[1]) return false;\n const argv1 = process.argv[1].replace(/\\\\/g, '/');\n return argv1.endsWith('/codegen.js') || argv1.endsWith('/codegen.cjs') || argv1.endsWith('/codegen');\n } catch {\n return false;\n }\n})();\n\nif (invokedFromCli) {\n main().catch((err) => {\n console.error(err);\n process.exit(1);\n });\n}\n\nexport { main as runCodegen };\n"]}
|