@lytjs/api 6.5.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/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # @lytjs/api
2
+
3
+ LytJS API 路由引擎,文件系统驱动的 API 路由解决方案。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ pnpm add @lytjs/api
9
+ ```
10
+
11
+ ## 快速开始
12
+
13
+ ```typescript
14
+ import { createApiRouter } from '@lytjs/api';
15
+
16
+ const apiRouter = createApiRouter({
17
+ apiDir: './src/api',
18
+ });
19
+
20
+ // 处理请求
21
+ const response = await apiRouter.handle({
22
+ url: 'http://localhost/api/users',
23
+ method: 'GET',
24
+ headers: {},
25
+ });
26
+ ```
27
+
28
+ ## 特性
29
+
30
+ - 文件系统 API 路由
31
+ - RESTful API 规范
32
+ - 支持中间件
33
+ - 零外部依赖
34
+
35
+ ## API
36
+
37
+ ### createApiRouter(options)
38
+
39
+ 创建 API 路由引擎实例。
40
+
41
+ ```typescript
42
+ import { createApiRouter } from '@lytjs/api';
43
+
44
+ const router = createApiRouter({
45
+ apiDir: './src/api',
46
+ });
47
+ ```
48
+
49
+ ### router.addRoute(route)
50
+
51
+ 添加 API 路由。
52
+
53
+ ### router.match(request)
54
+
55
+ 匹配 API 路由。
56
+
57
+ ### router.handle(request, context)
58
+
59
+ 处理 API 请求。
60
+
61
+ ## 文件结构约定
62
+
63
+ ```
64
+ src/
65
+ └── api/
66
+ ├── index.ts # GET /api
67
+ └── users/
68
+ ├── index.ts # GET /api/users, POST /api/users
69
+ └── [id].ts # GET /api/users/:id, PUT /api/users/:id, etc.
70
+ ```
71
+
72
+ ## 许可证
73
+
74
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createApiRouter: () => createApiRouter
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/utils.ts
28
+ var import_fs = require("fs");
29
+ var import_path = require("path");
30
+ function isDirectory(path) {
31
+ return (0, import_fs.existsSync)(path) && (0, import_fs.statSync)(path).isDirectory();
32
+ }
33
+ function isFile(path) {
34
+ return (0, import_fs.existsSync)(path) && (0, import_fs.statSync)(path).isFile();
35
+ }
36
+ var HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
37
+ function extractHttpMethods(filename) {
38
+ const lower = filename.toLowerCase();
39
+ const methods = [];
40
+ for (const method of HTTP_METHODS) {
41
+ if (lower.includes(method.toLowerCase())) {
42
+ methods.push(method);
43
+ }
44
+ }
45
+ return methods.length > 0 ? methods : ["GET"];
46
+ }
47
+ function filePathToApiPath(filePath, baseDir, extensions) {
48
+ let normalized = (0, import_path.normalize)((0, import_path.relative)(baseDir, filePath));
49
+ for (const ext of extensions) {
50
+ if (normalized.endsWith(ext)) {
51
+ normalized = normalized.slice(0, -ext.length);
52
+ break;
53
+ }
54
+ }
55
+ normalized = normalized.replace(/\\/g, "/");
56
+ for (const method of HTTP_METHODS) {
57
+ if (normalized.toLowerCase().endsWith("." + method.toLowerCase())) {
58
+ normalized = normalized.slice(0, -(method.length + 1));
59
+ }
60
+ }
61
+ if (normalized.endsWith("/index")) {
62
+ normalized = normalized.slice(0, -6);
63
+ } else if (normalized === "index") {
64
+ normalized = "";
65
+ }
66
+ normalized = normalized.replace(/\[(\w+)\]/g, ":$1");
67
+ return normalized.startsWith("/") ? normalized : `/${normalized}`;
68
+ }
69
+ function extractDynamicParams(apiPath) {
70
+ const matches = apiPath.match(/:(\w+)/g);
71
+ return matches ? matches.map((m) => m.slice(1)) : [];
72
+ }
73
+ function scanApiDirectory(dir, baseDir, extensions, ignorePatterns) {
74
+ const routes = [];
75
+ if (!isDirectory(dir)) return routes;
76
+ const files = (0, import_fs.readdirSync)(dir);
77
+ for (const file of files) {
78
+ const fullPath = (0, import_path.join)(dir, file);
79
+ const relativePath = (0, import_path.relative)(baseDir, fullPath);
80
+ if (ignorePatterns.some((pattern) => {
81
+ if (relativePath.includes(pattern)) return true;
82
+ return file.includes(pattern);
83
+ })) {
84
+ continue;
85
+ }
86
+ if (isDirectory(fullPath)) {
87
+ routes.push(...scanApiDirectory(fullPath, baseDir, extensions, ignorePatterns));
88
+ } else if (isFile(fullPath)) {
89
+ const ext = extensions.find((e) => file.endsWith(e));
90
+ if (ext) {
91
+ const apiPath = filePathToApiPath(fullPath, baseDir, extensions);
92
+ const params = extractDynamicParams(apiPath);
93
+ const methods = extractHttpMethods(file);
94
+ routes.push({
95
+ path: apiPath,
96
+ methods,
97
+ handlerPath: fullPath,
98
+ isDynamic: params.length > 0,
99
+ params
100
+ });
101
+ }
102
+ }
103
+ }
104
+ return routes;
105
+ }
106
+
107
+ // src/index.ts
108
+ var DEFAULT_OPTIONS = {
109
+ apiDir: "src/api",
110
+ extensions: [".ts", ".tsx", ".js", ".jsx"],
111
+ middlewarePattern: "_middleware",
112
+ ignorePatterns: ["node_modules", ".git", "dist"],
113
+ strictMode: false
114
+ };
115
+ function createApiRouter(options) {
116
+ const config = { ...DEFAULT_OPTIONS, ...options };
117
+ let routes = [];
118
+ async function refresh() {
119
+ routes = scanApiDirectory(
120
+ config.apiDir,
121
+ config.apiDir,
122
+ config.extensions,
123
+ config.ignorePatterns
124
+ );
125
+ }
126
+ function match(method, path) {
127
+ for (const route of routes) {
128
+ if (route.path === path && route.methods.includes(method)) {
129
+ return {
130
+ route,
131
+ method,
132
+ params: {},
133
+ path
134
+ };
135
+ }
136
+ }
137
+ const pathSegments = path.split("/").filter(Boolean);
138
+ for (const route of routes) {
139
+ if (!route.methods.includes(method)) continue;
140
+ const routeSegments = route.path.split("/").filter(Boolean);
141
+ if (routeSegments.length !== pathSegments.length) continue;
142
+ const params = {};
143
+ let match2 = true;
144
+ for (let i = 0; i < routeSegments.length; i++) {
145
+ const routeSeg = routeSegments[i];
146
+ const pathSeg = pathSegments[i];
147
+ if (!routeSeg || !pathSeg) continue;
148
+ if (routeSeg.startsWith(":")) {
149
+ params[routeSeg.slice(1)] = pathSeg;
150
+ } else if (routeSeg !== pathSeg) {
151
+ match2 = false;
152
+ break;
153
+ }
154
+ }
155
+ if (match2) {
156
+ return {
157
+ route,
158
+ method,
159
+ params,
160
+ path
161
+ };
162
+ }
163
+ }
164
+ return null;
165
+ }
166
+ async function handleRequest(method, path, _context) {
167
+ const matched = match(method, path);
168
+ if (!matched) {
169
+ return {
170
+ status: 404,
171
+ headers: { "Content-Type": "application/json" },
172
+ body: { error: "Not Found" }
173
+ };
174
+ }
175
+ try {
176
+ return {
177
+ status: 200,
178
+ headers: { "Content-Type": "application/json" },
179
+ body: {
180
+ message: "API Route Matched",
181
+ method,
182
+ path,
183
+ params: matched.params
184
+ }
185
+ };
186
+ } catch (error) {
187
+ return {
188
+ status: 500,
189
+ headers: { "Content-Type": "application/json" },
190
+ body: { error: "Internal Server Error", message: String(error) }
191
+ };
192
+ }
193
+ }
194
+ function getRoutes() {
195
+ return [...routes];
196
+ }
197
+ function addRoute(route) {
198
+ routes.push(route);
199
+ }
200
+ function removeRoute(path) {
201
+ routes = routes.filter((r) => r.path !== path);
202
+ }
203
+ function clearRoutes() {
204
+ routes = [];
205
+ }
206
+ refresh().catch((err) => {
207
+ console.warn("Failed to scan API routes:", err);
208
+ });
209
+ return {
210
+ getRoutes,
211
+ match,
212
+ addRoute,
213
+ removeRoute,
214
+ clearRoutes,
215
+ refresh,
216
+ handleRequest
217
+ };
218
+ }
219
+ // Annotate the CommonJS export names for ESM import in node:
220
+ 0 && (module.exports = {
221
+ createApiRouter
222
+ });
@@ -0,0 +1,104 @@
1
+ /**
2
+ * @lytjs/api - 类型定义
3
+ */
4
+ /** HTTP 请求方法 */
5
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';
6
+ /** API 路由配置 */
7
+ interface ApiRouteConfig {
8
+ /** 路由路径 */
9
+ path: string;
10
+ /** HTTP 方法 */
11
+ methods: HttpMethod[];
12
+ /** 处理函数路径 */
13
+ handlerPath: string;
14
+ /** 是否为动态路由 */
15
+ isDynamic: boolean;
16
+ /** 动态路由参数名 */
17
+ params?: string[];
18
+ /** 中间件路径 */
19
+ middlewarePaths?: string[];
20
+ }
21
+ /** API 路由器配置选项 */
22
+ interface ApiRouterOptions {
23
+ /** API 目录路径 */
24
+ apiDir: string;
25
+ /** API 处理函数扩展名 */
26
+ extensions?: string[];
27
+ /** 中间件文件名称模式 */
28
+ middlewarePattern?: string;
29
+ /** 忽略文件模式 */
30
+ ignorePatterns?: string[];
31
+ /** 是否启用严格模式 */
32
+ strictMode?: boolean;
33
+ }
34
+ /** API 请求上下文 */
35
+ interface ApiRequestContext {
36
+ /** 请求方法 */
37
+ method: HttpMethod;
38
+ /** 请求路径 */
39
+ path: string;
40
+ /** 请求头 */
41
+ headers: Record<string, string>;
42
+ /** 请求参数 */
43
+ params: Record<string, string>;
44
+ /** 查询参数 */
45
+ query: Record<string, string | string[]>;
46
+ /** 请求体 */
47
+ body?: unknown;
48
+ }
49
+ /** API 响应对象 */
50
+ interface ApiResponse {
51
+ /** 状态码 */
52
+ status: number;
53
+ /** 响应头 */
54
+ headers: Record<string, string>;
55
+ /** 响应体 */
56
+ body?: unknown;
57
+ }
58
+ /** API 处理函数 */
59
+ type ApiHandler = (context: ApiRequestContext) => Promise<ApiResponse> | ApiResponse;
60
+ /** API 中间件函数 */
61
+ type ApiMiddleware = (context: ApiRequestContext, next: () => Promise<ApiResponse>) => Promise<ApiResponse>;
62
+ /** API 匹配结果 */
63
+ interface ApiMatch {
64
+ /** 匹配的路由配置 */
65
+ route: ApiRouteConfig;
66
+ /** 请求方法 */
67
+ method: HttpMethod;
68
+ /** 路由参数 */
69
+ params: Record<string, string>;
70
+ /** 请求路径 */
71
+ path: string;
72
+ }
73
+ /** API 路由器接口 */
74
+ interface ApiRouter {
75
+ /** 获取所有 API 路由配置 */
76
+ getRoutes(): ApiRouteConfig[];
77
+ /** 匹配 API 路由 */
78
+ match(method: HttpMethod, path: string): ApiMatch | null;
79
+ /** 添加 API 路由 */
80
+ addRoute(route: ApiRouteConfig): void;
81
+ /** 移除 API 路由 */
82
+ removeRoute(path: string): void;
83
+ /** 清除所有 API 路由 */
84
+ clearRoutes(): void;
85
+ /** 重新扫描文件系统 */
86
+ refresh(): Promise<void>;
87
+ /** 处理 API 请求 */
88
+ handleRequest(method: HttpMethod, path: string, context: ApiRequestContext): Promise<ApiResponse>;
89
+ }
90
+
91
+ /**
92
+ * @lytjs/api
93
+ *
94
+ * LytJS API Router Engine
95
+ *
96
+ * @packageDocumentation
97
+ */
98
+
99
+ /**
100
+ * 创建 API 路由器
101
+ */
102
+ declare function createApiRouter(options?: ApiRouterOptions): ApiRouter;
103
+
104
+ export { type ApiHandler, type ApiMatch, type ApiMiddleware, type ApiRequestContext, type ApiResponse, type ApiRouteConfig, type ApiRouter, type ApiRouterOptions, type HttpMethod, createApiRouter };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * @lytjs/api - 类型定义
3
+ */
4
+ /** HTTP 请求方法 */
5
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';
6
+ /** API 路由配置 */
7
+ interface ApiRouteConfig {
8
+ /** 路由路径 */
9
+ path: string;
10
+ /** HTTP 方法 */
11
+ methods: HttpMethod[];
12
+ /** 处理函数路径 */
13
+ handlerPath: string;
14
+ /** 是否为动态路由 */
15
+ isDynamic: boolean;
16
+ /** 动态路由参数名 */
17
+ params?: string[];
18
+ /** 中间件路径 */
19
+ middlewarePaths?: string[];
20
+ }
21
+ /** API 路由器配置选项 */
22
+ interface ApiRouterOptions {
23
+ /** API 目录路径 */
24
+ apiDir: string;
25
+ /** API 处理函数扩展名 */
26
+ extensions?: string[];
27
+ /** 中间件文件名称模式 */
28
+ middlewarePattern?: string;
29
+ /** 忽略文件模式 */
30
+ ignorePatterns?: string[];
31
+ /** 是否启用严格模式 */
32
+ strictMode?: boolean;
33
+ }
34
+ /** API 请求上下文 */
35
+ interface ApiRequestContext {
36
+ /** 请求方法 */
37
+ method: HttpMethod;
38
+ /** 请求路径 */
39
+ path: string;
40
+ /** 请求头 */
41
+ headers: Record<string, string>;
42
+ /** 请求参数 */
43
+ params: Record<string, string>;
44
+ /** 查询参数 */
45
+ query: Record<string, string | string[]>;
46
+ /** 请求体 */
47
+ body?: unknown;
48
+ }
49
+ /** API 响应对象 */
50
+ interface ApiResponse {
51
+ /** 状态码 */
52
+ status: number;
53
+ /** 响应头 */
54
+ headers: Record<string, string>;
55
+ /** 响应体 */
56
+ body?: unknown;
57
+ }
58
+ /** API 处理函数 */
59
+ type ApiHandler = (context: ApiRequestContext) => Promise<ApiResponse> | ApiResponse;
60
+ /** API 中间件函数 */
61
+ type ApiMiddleware = (context: ApiRequestContext, next: () => Promise<ApiResponse>) => Promise<ApiResponse>;
62
+ /** API 匹配结果 */
63
+ interface ApiMatch {
64
+ /** 匹配的路由配置 */
65
+ route: ApiRouteConfig;
66
+ /** 请求方法 */
67
+ method: HttpMethod;
68
+ /** 路由参数 */
69
+ params: Record<string, string>;
70
+ /** 请求路径 */
71
+ path: string;
72
+ }
73
+ /** API 路由器接口 */
74
+ interface ApiRouter {
75
+ /** 获取所有 API 路由配置 */
76
+ getRoutes(): ApiRouteConfig[];
77
+ /** 匹配 API 路由 */
78
+ match(method: HttpMethod, path: string): ApiMatch | null;
79
+ /** 添加 API 路由 */
80
+ addRoute(route: ApiRouteConfig): void;
81
+ /** 移除 API 路由 */
82
+ removeRoute(path: string): void;
83
+ /** 清除所有 API 路由 */
84
+ clearRoutes(): void;
85
+ /** 重新扫描文件系统 */
86
+ refresh(): Promise<void>;
87
+ /** 处理 API 请求 */
88
+ handleRequest(method: HttpMethod, path: string, context: ApiRequestContext): Promise<ApiResponse>;
89
+ }
90
+
91
+ /**
92
+ * @lytjs/api
93
+ *
94
+ * LytJS API Router Engine
95
+ *
96
+ * @packageDocumentation
97
+ */
98
+
99
+ /**
100
+ * 创建 API 路由器
101
+ */
102
+ declare function createApiRouter(options?: ApiRouterOptions): ApiRouter;
103
+
104
+ export { type ApiHandler, type ApiMatch, type ApiMiddleware, type ApiRequestContext, type ApiResponse, type ApiRouteConfig, type ApiRouter, type ApiRouterOptions, type HttpMethod, createApiRouter };
package/dist/index.mjs ADDED
@@ -0,0 +1,195 @@
1
+ // src/utils.ts
2
+ import { existsSync, readdirSync, statSync } from "fs";
3
+ import { join, normalize, relative } from "path";
4
+ function isDirectory(path) {
5
+ return existsSync(path) && statSync(path).isDirectory();
6
+ }
7
+ function isFile(path) {
8
+ return existsSync(path) && statSync(path).isFile();
9
+ }
10
+ var HTTP_METHODS = ["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
11
+ function extractHttpMethods(filename) {
12
+ const lower = filename.toLowerCase();
13
+ const methods = [];
14
+ for (const method of HTTP_METHODS) {
15
+ if (lower.includes(method.toLowerCase())) {
16
+ methods.push(method);
17
+ }
18
+ }
19
+ return methods.length > 0 ? methods : ["GET"];
20
+ }
21
+ function filePathToApiPath(filePath, baseDir, extensions) {
22
+ let normalized = normalize(relative(baseDir, filePath));
23
+ for (const ext of extensions) {
24
+ if (normalized.endsWith(ext)) {
25
+ normalized = normalized.slice(0, -ext.length);
26
+ break;
27
+ }
28
+ }
29
+ normalized = normalized.replace(/\\/g, "/");
30
+ for (const method of HTTP_METHODS) {
31
+ if (normalized.toLowerCase().endsWith("." + method.toLowerCase())) {
32
+ normalized = normalized.slice(0, -(method.length + 1));
33
+ }
34
+ }
35
+ if (normalized.endsWith("/index")) {
36
+ normalized = normalized.slice(0, -6);
37
+ } else if (normalized === "index") {
38
+ normalized = "";
39
+ }
40
+ normalized = normalized.replace(/\[(\w+)\]/g, ":$1");
41
+ return normalized.startsWith("/") ? normalized : `/${normalized}`;
42
+ }
43
+ function extractDynamicParams(apiPath) {
44
+ const matches = apiPath.match(/:(\w+)/g);
45
+ return matches ? matches.map((m) => m.slice(1)) : [];
46
+ }
47
+ function scanApiDirectory(dir, baseDir, extensions, ignorePatterns) {
48
+ const routes = [];
49
+ if (!isDirectory(dir)) return routes;
50
+ const files = readdirSync(dir);
51
+ for (const file of files) {
52
+ const fullPath = join(dir, file);
53
+ const relativePath = relative(baseDir, fullPath);
54
+ if (ignorePatterns.some((pattern) => {
55
+ if (relativePath.includes(pattern)) return true;
56
+ return file.includes(pattern);
57
+ })) {
58
+ continue;
59
+ }
60
+ if (isDirectory(fullPath)) {
61
+ routes.push(...scanApiDirectory(fullPath, baseDir, extensions, ignorePatterns));
62
+ } else if (isFile(fullPath)) {
63
+ const ext = extensions.find((e) => file.endsWith(e));
64
+ if (ext) {
65
+ const apiPath = filePathToApiPath(fullPath, baseDir, extensions);
66
+ const params = extractDynamicParams(apiPath);
67
+ const methods = extractHttpMethods(file);
68
+ routes.push({
69
+ path: apiPath,
70
+ methods,
71
+ handlerPath: fullPath,
72
+ isDynamic: params.length > 0,
73
+ params
74
+ });
75
+ }
76
+ }
77
+ }
78
+ return routes;
79
+ }
80
+
81
+ // src/index.ts
82
+ var DEFAULT_OPTIONS = {
83
+ apiDir: "src/api",
84
+ extensions: [".ts", ".tsx", ".js", ".jsx"],
85
+ middlewarePattern: "_middleware",
86
+ ignorePatterns: ["node_modules", ".git", "dist"],
87
+ strictMode: false
88
+ };
89
+ function createApiRouter(options) {
90
+ const config = { ...DEFAULT_OPTIONS, ...options };
91
+ let routes = [];
92
+ async function refresh() {
93
+ routes = scanApiDirectory(
94
+ config.apiDir,
95
+ config.apiDir,
96
+ config.extensions,
97
+ config.ignorePatterns
98
+ );
99
+ }
100
+ function match(method, path) {
101
+ for (const route of routes) {
102
+ if (route.path === path && route.methods.includes(method)) {
103
+ return {
104
+ route,
105
+ method,
106
+ params: {},
107
+ path
108
+ };
109
+ }
110
+ }
111
+ const pathSegments = path.split("/").filter(Boolean);
112
+ for (const route of routes) {
113
+ if (!route.methods.includes(method)) continue;
114
+ const routeSegments = route.path.split("/").filter(Boolean);
115
+ if (routeSegments.length !== pathSegments.length) continue;
116
+ const params = {};
117
+ let match2 = true;
118
+ for (let i = 0; i < routeSegments.length; i++) {
119
+ const routeSeg = routeSegments[i];
120
+ const pathSeg = pathSegments[i];
121
+ if (!routeSeg || !pathSeg) continue;
122
+ if (routeSeg.startsWith(":")) {
123
+ params[routeSeg.slice(1)] = pathSeg;
124
+ } else if (routeSeg !== pathSeg) {
125
+ match2 = false;
126
+ break;
127
+ }
128
+ }
129
+ if (match2) {
130
+ return {
131
+ route,
132
+ method,
133
+ params,
134
+ path
135
+ };
136
+ }
137
+ }
138
+ return null;
139
+ }
140
+ async function handleRequest(method, path, _context) {
141
+ const matched = match(method, path);
142
+ if (!matched) {
143
+ return {
144
+ status: 404,
145
+ headers: { "Content-Type": "application/json" },
146
+ body: { error: "Not Found" }
147
+ };
148
+ }
149
+ try {
150
+ return {
151
+ status: 200,
152
+ headers: { "Content-Type": "application/json" },
153
+ body: {
154
+ message: "API Route Matched",
155
+ method,
156
+ path,
157
+ params: matched.params
158
+ }
159
+ };
160
+ } catch (error) {
161
+ return {
162
+ status: 500,
163
+ headers: { "Content-Type": "application/json" },
164
+ body: { error: "Internal Server Error", message: String(error) }
165
+ };
166
+ }
167
+ }
168
+ function getRoutes() {
169
+ return [...routes];
170
+ }
171
+ function addRoute(route) {
172
+ routes.push(route);
173
+ }
174
+ function removeRoute(path) {
175
+ routes = routes.filter((r) => r.path !== path);
176
+ }
177
+ function clearRoutes() {
178
+ routes = [];
179
+ }
180
+ refresh().catch((err) => {
181
+ console.warn("Failed to scan API routes:", err);
182
+ });
183
+ return {
184
+ getRoutes,
185
+ match,
186
+ addRoute,
187
+ removeRoute,
188
+ clearRoutes,
189
+ refresh,
190
+ handleRequest
191
+ };
192
+ }
193
+ export {
194
+ createApiRouter
195
+ };
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@lytjs/api",
3
+ "version": "6.5.0",
4
+ "description": "LytJS API Router Engine",
5
+ "author": "lytjs",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.mjs",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.mjs",
15
+ "require": "./dist/index.cjs"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "sideEffects": false,
24
+ "scripts": {
25
+ "dev": "tsup --watch",
26
+ "build": "tsup",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest",
29
+ "test:coverage": "vitest run --coverage",
30
+ "type-check": "tsc --noEmit",
31
+ "clean": "rm -rf dist node_modules .turbo"
32
+ },
33
+ "dependencies": {
34
+ "@lytjs/common-http": "^6.5.0"
35
+ },
36
+ "devDependencies": {
37
+ "tsup": "^8.3.6",
38
+ "typescript": "^5.7.3",
39
+ "vitest": "^3.0.0"
40
+ },
41
+ "peerDependencies": {},
42
+ "publishConfig": {
43
+ "access": "public",
44
+ "registry": "https://registry.npmjs.org/"
45
+ },
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://gitee.com/lytjs/lytjs.git",
49
+ "directory": "packages/ecosystem/packages/api"
50
+ },
51
+ "keywords": [
52
+ "lytjs",
53
+ "api",
54
+ "router",
55
+ "file-system"
56
+ ],
57
+ "homepage": "https://gitee.com/lytjs/lytjs",
58
+ "bugs": {
59
+ "url": "https://gitee.com/lytjs/lytjs/issues"
60
+ }
61
+ }