@bepalo/router 1.11.32 → 1.12.33
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/cjs/framework.d.ts +2 -4
- package/dist/cjs/framework.d.ts.map +1 -1
- package/dist/cjs/framework.js +4 -6
- package/dist/cjs/framework.js.map +1 -1
- package/dist/cjs/helpers.d.ts +2 -2
- package/dist/cjs/helpers.d.ts.map +1 -1
- package/dist/cjs/helpers.js +1 -1
- package/dist/cjs/helpers.js.map +1 -1
- package/dist/cjs/index.d.ts +5 -5
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +5 -5
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/middlewares.d.ts +2 -2
- package/dist/cjs/middlewares.d.ts.map +1 -1
- package/dist/cjs/middlewares.js +24 -24
- package/dist/cjs/middlewares.js.map +1 -1
- package/dist/cjs/router.d.ts +2 -2
- package/dist/cjs/router.d.ts.map +1 -1
- package/dist/cjs/router.js +8 -8
- package/dist/cjs/router.js.map +1 -1
- package/dist/cjs/types.d.ts +1 -1
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/cjs/upload-stream.d.ts +1 -1
- package/dist/cjs/upload-stream.d.ts.map +1 -1
- package/dist/cjs/upload-stream.js +7 -7
- package/dist/cjs/upload-stream.js.map +1 -1
- package/dist/framework.d.ts +2 -4
- package/dist/framework.d.ts.map +1 -1
- package/dist/framework.js +4 -6
- package/dist/framework.js.map +1 -1
- package/dist/helpers.d.ts +2 -2
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +1 -1
- package/dist/helpers.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/middlewares.d.ts +2 -2
- package/dist/middlewares.d.ts.map +1 -1
- package/dist/middlewares.js +24 -24
- package/dist/middlewares.js.map +1 -1
- package/dist/router.d.ts +2 -2
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +8 -8
- package/dist/router.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/upload-stream.d.ts +1 -1
- package/dist/upload-stream.d.ts.map +1 -1
- package/dist/upload-stream.js +7 -7
- package/dist/upload-stream.js.map +1 -1
- package/package.json +8 -1
- package/src/framework.deno.ts +194 -0
- package/src/framework.ts +197 -0
- package/src/helpers.ts +829 -0
- package/src/index.ts +5 -0
- package/src/list.ts +462 -0
- package/src/middlewares.deno.ts +851 -0
- package/src/middlewares.ts +851 -0
- package/src/router.ts +993 -0
- package/src/tree.ts +139 -0
- package/src/types.ts +197 -0
- package/src/upload-stream.ts +661 -0
- package/dist/cjs/framework.deno.d.ts +0 -31
- package/dist/cjs/framework.deno.d.ts.map +0 -1
- package/dist/cjs/framework.deno.js +0 -245
- package/dist/cjs/framework.deno.js.map +0 -1
- package/dist/framework.deno.d.ts +0 -31
- package/dist/framework.deno.d.ts.map +0 -1
- package/dist/framework.deno.js +0 -245
- package/dist/framework.deno.js.map +0 -1
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import Router, {
|
|
2
|
+
isValidHttpMethod,
|
|
3
|
+
RouterConfig,
|
|
4
|
+
RouterContext,
|
|
5
|
+
} from "./router.ts";
|
|
6
|
+
import type {
|
|
7
|
+
Handler,
|
|
8
|
+
HandlerType,
|
|
9
|
+
HttpMethod,
|
|
10
|
+
MethodPath,
|
|
11
|
+
Pipeline,
|
|
12
|
+
CTXError,
|
|
13
|
+
} from "./types.ts";
|
|
14
|
+
|
|
15
|
+
const defaultValidExtensions = [".ts", ".js", ".tsx", ".jsx"];
|
|
16
|
+
const defaultNodeFilter = (node: DirWalkNode) =>
|
|
17
|
+
defaultValidExtensions.some((ext) => node.name.endsWith(ext));
|
|
18
|
+
const defaultNodeProcessor = (node: DirWalkNode) => {
|
|
19
|
+
const extensionIndex = node.name.lastIndexOf(".");
|
|
20
|
+
if (extensionIndex !== -1) node.name = node.name.slice(0, extensionIndex);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type UCHandlerType =
|
|
24
|
+
| "FILTER"
|
|
25
|
+
| "HOOK"
|
|
26
|
+
| "HANDLER"
|
|
27
|
+
| "FALLBACK"
|
|
28
|
+
| "CATCHER"
|
|
29
|
+
| "AFTER";
|
|
30
|
+
|
|
31
|
+
export type RouterHandlers<
|
|
32
|
+
CommonXContext = {},
|
|
33
|
+
MethodContexts extends Partial<
|
|
34
|
+
Record<HttpMethod | "ALL" | "CRUD", Record<string, any>>
|
|
35
|
+
> = {},
|
|
36
|
+
> = {
|
|
37
|
+
[K in HttpMethod | "ALL" | "CRUD" as K]?: {
|
|
38
|
+
[H in UCHandlerType as H]?: H extends "CATCHER"
|
|
39
|
+
?
|
|
40
|
+
| Handler<CommonXContext & CTXError & MethodContexts[K]>
|
|
41
|
+
| Pipeline<CommonXContext & CTXError & MethodContexts[K]>
|
|
42
|
+
:
|
|
43
|
+
| Handler<CommonXContext & MethodContexts[K]>
|
|
44
|
+
| Pipeline<CommonXContext & MethodContexts[K]>;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export interface RouterFrameworkConfig<
|
|
49
|
+
Context extends RouterContext = RouterContext,
|
|
50
|
+
> extends RouterConfig<Context> {
|
|
51
|
+
rootPath?: string;
|
|
52
|
+
filterNode?: (node: DirWalkNode) => boolean;
|
|
53
|
+
processNode?: (node: DirWalkNode) => void;
|
|
54
|
+
onDir?: (node: DirWalkNode) => void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class RouterFramework<
|
|
58
|
+
EXTContext = {},
|
|
59
|
+
Context extends RouterContext<EXTContext> = RouterContext<EXTContext>,
|
|
60
|
+
> extends Router<Context> {
|
|
61
|
+
#rootPath: string;
|
|
62
|
+
#filterNode?: (node: DirWalkNode) => boolean;
|
|
63
|
+
#processNode?: (node: DirWalkNode) => void;
|
|
64
|
+
#onDir?: (node: DirWalkNode) => void;
|
|
65
|
+
#loading: boolean = false;
|
|
66
|
+
#loaded: boolean = false;
|
|
67
|
+
|
|
68
|
+
get loading(): boolean {
|
|
69
|
+
return this.#loading;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get loaded(): boolean {
|
|
73
|
+
return this.#loaded;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
constructor(config?: RouterFrameworkConfig<Context>) {
|
|
77
|
+
const { rootPath, filterNode, processNode, onDir, ...baseConfig } =
|
|
78
|
+
config ?? {};
|
|
79
|
+
super(baseConfig);
|
|
80
|
+
this.#rootPath = rootPath || "./routes";
|
|
81
|
+
this.#filterNode = filterNode ?? defaultNodeFilter;
|
|
82
|
+
this.#processNode = processNode ?? defaultNodeProcessor;
|
|
83
|
+
this.#onDir = onDir;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async load(): Promise<RouterFramework<Context>> {
|
|
87
|
+
if (this.#loading || this.#loaded) {
|
|
88
|
+
throw new Error("RouterFramework already loading or loaded");
|
|
89
|
+
}
|
|
90
|
+
this.#loading = true;
|
|
91
|
+
for await (let node of walk(this.#rootPath)) {
|
|
92
|
+
if (node.type !== "dir") {
|
|
93
|
+
let handlersImp;
|
|
94
|
+
let handlers: RouterHandlers | undefined;
|
|
95
|
+
try {
|
|
96
|
+
handlersImp = await import(`file://${node.fullPath}`);
|
|
97
|
+
handlers = handlersImp?.default satisfies RouterHandlers;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error(`Failed to import route at ${node.fullPath}:`, error);
|
|
100
|
+
}
|
|
101
|
+
// filter the node
|
|
102
|
+
if (!handlers || !(this.#filterNode && this.#filterNode(node))) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// process the node
|
|
106
|
+
if (this.#processNode) this.#processNode(node);
|
|
107
|
+
const name = node.name;
|
|
108
|
+
let path;
|
|
109
|
+
switch (name) {
|
|
110
|
+
case "[$$]":
|
|
111
|
+
path = `${node.parent ? "/" + node.parent + "/" : "/"}.**`;
|
|
112
|
+
break;
|
|
113
|
+
case "($$)":
|
|
114
|
+
path = `${node.parent ? "/" + node.parent + "/" : "/"}**`;
|
|
115
|
+
break;
|
|
116
|
+
case "[$]":
|
|
117
|
+
path = `${node.parent ? "/" + node.parent + "/" : "/"}.*`;
|
|
118
|
+
break;
|
|
119
|
+
case "($)":
|
|
120
|
+
path = `${node.parent ? "/" + node.parent + "/" : "/"}*`;
|
|
121
|
+
break;
|
|
122
|
+
case "index":
|
|
123
|
+
path = "/" + node.parent;
|
|
124
|
+
break;
|
|
125
|
+
default:
|
|
126
|
+
path = `${node.parent ? "/" + node.parent + "/" : "/"}${name}`;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
// replace (param) with :param
|
|
130
|
+
path = path.replace(/\[([^\/]+?)\]/g, ":$1");
|
|
131
|
+
// set handlers
|
|
132
|
+
for (const [method, methodHandlers] of Object.entries(handlers)) {
|
|
133
|
+
for (const [uchandlerType, pipeline] of Object.entries(
|
|
134
|
+
methodHandlers,
|
|
135
|
+
)) {
|
|
136
|
+
const handlerType = uchandlerType.toLowerCase() as HandlerType;
|
|
137
|
+
if (
|
|
138
|
+
isValidHttpMethod(method) ||
|
|
139
|
+
method === "ALL" ||
|
|
140
|
+
method === "CRUD"
|
|
141
|
+
) {
|
|
142
|
+
this.setRoutes(
|
|
143
|
+
handlerType,
|
|
144
|
+
`${method} ${path}` as MethodPath,
|
|
145
|
+
pipeline as unknown as
|
|
146
|
+
| Handler<Context>
|
|
147
|
+
| Pipeline<Context>
|
|
148
|
+
| Handler<Context & CTXError>
|
|
149
|
+
| Pipeline<Context & CTXError>,
|
|
150
|
+
);
|
|
151
|
+
} else {
|
|
152
|
+
throw new Error(`Invalid http method '${method}'`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} else if (this.#onDir) {
|
|
157
|
+
this.#onDir(node);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
this.#loading = false;
|
|
161
|
+
this.#loaded = true;
|
|
162
|
+
return this;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface DirWalkNode {
|
|
167
|
+
type: string;
|
|
168
|
+
name: string;
|
|
169
|
+
path: string;
|
|
170
|
+
parent: string;
|
|
171
|
+
fullPath: string;
|
|
172
|
+
relativePath: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function* walk(
|
|
176
|
+
dir: string,
|
|
177
|
+
rootPath?: string,
|
|
178
|
+
): AsyncGenerator<DirWalkNode> {
|
|
179
|
+
rootPath = rootPath || dir;
|
|
180
|
+
const { join, resolve, relative } = await import("jsr:@std/path@1");
|
|
181
|
+
for await (const entry of Deno.readDir(dir)) {
|
|
182
|
+
const name = entry.name;
|
|
183
|
+
const parent = relative(rootPath, dir).replace(/\\/g, "/");
|
|
184
|
+
const path = join(dir, name).replace(/\\/g, "/");
|
|
185
|
+
const fullPath = resolve(dir, name).replace(/\\/g, "/");
|
|
186
|
+
const relativePath = relative(rootPath, path).replace(/\\/g, "/");
|
|
187
|
+
if (entry.isDirectory) {
|
|
188
|
+
yield { type: "dir", name, path, parent, fullPath, relativePath };
|
|
189
|
+
yield* walk(path, rootPath);
|
|
190
|
+
} else {
|
|
191
|
+
yield { type: "file", name, path, parent, fullPath, relativePath };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
package/src/framework.ts
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import Router, {
|
|
2
|
+
isValidHttpMethod,
|
|
3
|
+
RouterConfig,
|
|
4
|
+
RouterContext,
|
|
5
|
+
} from "./router.ts";
|
|
6
|
+
import type {
|
|
7
|
+
Handler,
|
|
8
|
+
HandlerType,
|
|
9
|
+
HttpMethod,
|
|
10
|
+
MethodPath,
|
|
11
|
+
Pipeline,
|
|
12
|
+
CTXError,
|
|
13
|
+
} from "./types.ts";
|
|
14
|
+
|
|
15
|
+
const defaultValidExtensions = [".ts", ".js", ".tsx", ".jsx"];
|
|
16
|
+
const defaultNodeFilter = (node: DirWalkNode) =>
|
|
17
|
+
defaultValidExtensions.some((ext) => node.name.endsWith(ext));
|
|
18
|
+
const defaultNodeProcessor = (node: DirWalkNode) => {
|
|
19
|
+
const extensionIndex = node.name.lastIndexOf(".");
|
|
20
|
+
if (extensionIndex !== -1) node.name = node.name.slice(0, extensionIndex);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type UCHandlerType =
|
|
24
|
+
| "FILTER"
|
|
25
|
+
| "HOOK"
|
|
26
|
+
| "HANDLER"
|
|
27
|
+
| "FALLBACK"
|
|
28
|
+
| "CATCHER"
|
|
29
|
+
| "AFTER";
|
|
30
|
+
|
|
31
|
+
export type RouterHandlers<
|
|
32
|
+
CommonXContext = {},
|
|
33
|
+
MethodContexts extends Partial<
|
|
34
|
+
Record<HttpMethod | "ALL" | "CRUD", Record<string, any>>
|
|
35
|
+
> = {},
|
|
36
|
+
> = {
|
|
37
|
+
[K in HttpMethod | "ALL" | "CRUD" as K]?: {
|
|
38
|
+
[H in UCHandlerType as H]?: H extends "CATCHER"
|
|
39
|
+
?
|
|
40
|
+
| Handler<CommonXContext & CTXError & MethodContexts[K]>
|
|
41
|
+
| Pipeline<CommonXContext & CTXError & MethodContexts[K]>
|
|
42
|
+
:
|
|
43
|
+
| Handler<CommonXContext & MethodContexts[K]>
|
|
44
|
+
| Pipeline<CommonXContext & MethodContexts[K]>;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export interface RouterFrameworkConfig<
|
|
49
|
+
Context extends RouterContext = RouterContext,
|
|
50
|
+
> extends RouterConfig<Context> {
|
|
51
|
+
rootPath?: string;
|
|
52
|
+
filterNode?: (node: DirWalkNode) => boolean;
|
|
53
|
+
processNode?: (node: DirWalkNode) => void;
|
|
54
|
+
onDir?: (node: DirWalkNode) => void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class RouterFramework<
|
|
58
|
+
EXTContext = {},
|
|
59
|
+
Context extends RouterContext<EXTContext> = RouterContext<EXTContext>,
|
|
60
|
+
> extends Router<Context> {
|
|
61
|
+
#rootPath: string;
|
|
62
|
+
#filterNode?: (node: DirWalkNode) => boolean;
|
|
63
|
+
#processNode?: (node: DirWalkNode) => void;
|
|
64
|
+
#onDir?: (node: DirWalkNode) => void;
|
|
65
|
+
#loading: boolean = false;
|
|
66
|
+
#loaded: boolean = false;
|
|
67
|
+
|
|
68
|
+
get loading(): boolean {
|
|
69
|
+
return this.#loading;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
get loaded(): boolean {
|
|
73
|
+
return this.#loaded;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
constructor(config?: RouterFrameworkConfig<Context>) {
|
|
77
|
+
const { rootPath, filterNode, processNode, onDir, ...baseConfig } =
|
|
78
|
+
config ?? {};
|
|
79
|
+
super(baseConfig);
|
|
80
|
+
this.#rootPath = rootPath || "./routes";
|
|
81
|
+
this.#filterNode = filterNode ?? defaultNodeFilter;
|
|
82
|
+
this.#processNode = processNode ?? defaultNodeProcessor;
|
|
83
|
+
this.#onDir = onDir;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async load(): Promise<RouterFramework<Context>> {
|
|
87
|
+
if (this.#loading || this.#loaded) {
|
|
88
|
+
throw new Error("RouterFramework already loading or loaded");
|
|
89
|
+
}
|
|
90
|
+
this.#loading = true;
|
|
91
|
+
for await (let node of walk(this.#rootPath)) {
|
|
92
|
+
if (node.type !== "dir") {
|
|
93
|
+
let handlersImp;
|
|
94
|
+
let handlers: RouterHandlers | undefined;
|
|
95
|
+
try {
|
|
96
|
+
handlersImp = await import(node.fullPath);
|
|
97
|
+
handlers = handlersImp?.default satisfies RouterHandlers;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error(`Failed to import route at ${node.fullPath}:`, error);
|
|
100
|
+
}
|
|
101
|
+
// filter the node
|
|
102
|
+
if (!handlers || !(this.#filterNode && this.#filterNode(node))) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// process the node
|
|
106
|
+
if (this.#processNode) this.#processNode(node);
|
|
107
|
+
const name = node.name;
|
|
108
|
+
let path;
|
|
109
|
+
switch (name) {
|
|
110
|
+
case "[$$]":
|
|
111
|
+
path = `${node.parent ? "/" + node.parent + "/" : "/"}.**`;
|
|
112
|
+
break;
|
|
113
|
+
case "($$)":
|
|
114
|
+
path = `${node.parent ? "/" + node.parent + "/" : "/"}**`;
|
|
115
|
+
break;
|
|
116
|
+
case "[$]":
|
|
117
|
+
path = `${node.parent ? "/" + node.parent + "/" : "/"}.*`;
|
|
118
|
+
break;
|
|
119
|
+
case "($)":
|
|
120
|
+
path = `${node.parent ? "/" + node.parent + "/" : "/"}*`;
|
|
121
|
+
break;
|
|
122
|
+
case "index":
|
|
123
|
+
path = "/" + node.parent;
|
|
124
|
+
break;
|
|
125
|
+
default:
|
|
126
|
+
path = `${node.parent ? "/" + node.parent + "/" : "/"}${name}`;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
// replace (param) with :param
|
|
130
|
+
path = path.replace(/\[([^\/]+?)\]/g, ":$1");
|
|
131
|
+
// set handlers
|
|
132
|
+
for (const [method, methodHandlers] of Object.entries(handlers)) {
|
|
133
|
+
for (const [uchandlerType, pipeline] of Object.entries(
|
|
134
|
+
methodHandlers,
|
|
135
|
+
)) {
|
|
136
|
+
const handlerType = uchandlerType.toLowerCase() as HandlerType;
|
|
137
|
+
if (
|
|
138
|
+
isValidHttpMethod(method) ||
|
|
139
|
+
method === "ALL" ||
|
|
140
|
+
method === "CRUD"
|
|
141
|
+
) {
|
|
142
|
+
this.setRoutes(
|
|
143
|
+
handlerType,
|
|
144
|
+
`${method} ${path}` as MethodPath,
|
|
145
|
+
pipeline as unknown as
|
|
146
|
+
| Handler<Context>
|
|
147
|
+
| Pipeline<Context>
|
|
148
|
+
| Handler<Context & CTXError>
|
|
149
|
+
| Pipeline<Context & CTXError>,
|
|
150
|
+
);
|
|
151
|
+
} else {
|
|
152
|
+
throw new Error(`Invalid http method '${method}'`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} else if (this.#onDir) {
|
|
157
|
+
this.#onDir(node);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
this.#loading = false;
|
|
161
|
+
this.#loaded = true;
|
|
162
|
+
return this;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface DirWalkNode {
|
|
167
|
+
type: string;
|
|
168
|
+
name: string;
|
|
169
|
+
path: string;
|
|
170
|
+
parent: string;
|
|
171
|
+
fullPath: string;
|
|
172
|
+
relativePath: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function* walk(
|
|
176
|
+
dir: string,
|
|
177
|
+
rootPath?: string,
|
|
178
|
+
): AsyncGenerator<DirWalkNode> {
|
|
179
|
+
rootPath = rootPath || dir;
|
|
180
|
+
const fs = await import("fs");
|
|
181
|
+
const { join, resolve, relative } = await import("path");
|
|
182
|
+
for (const entry of await fs.promises.readdir(dir, {
|
|
183
|
+
withFileTypes: true,
|
|
184
|
+
})) {
|
|
185
|
+
const name = entry.name;
|
|
186
|
+
const parent = relative(rootPath, dir).replace(/\\/g, "/");
|
|
187
|
+
const path = join(dir, name).replace(/\\/g, "/");
|
|
188
|
+
const fullPath = resolve(dir, name).replace(/\\/g, "/");
|
|
189
|
+
const relativePath = relative(rootPath, path).replace(/\\/g, "/");
|
|
190
|
+
if (entry.isDirectory()) {
|
|
191
|
+
yield { type: "dir", name, path, parent, fullPath, relativePath };
|
|
192
|
+
yield* walk(path, rootPath);
|
|
193
|
+
} else {
|
|
194
|
+
yield { type: "file", name, path, parent, fullPath, relativePath };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|