@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.
Files changed (72) hide show
  1. package/dist/cjs/framework.d.ts +2 -4
  2. package/dist/cjs/framework.d.ts.map +1 -1
  3. package/dist/cjs/framework.js +4 -6
  4. package/dist/cjs/framework.js.map +1 -1
  5. package/dist/cjs/helpers.d.ts +2 -2
  6. package/dist/cjs/helpers.d.ts.map +1 -1
  7. package/dist/cjs/helpers.js +1 -1
  8. package/dist/cjs/helpers.js.map +1 -1
  9. package/dist/cjs/index.d.ts +5 -5
  10. package/dist/cjs/index.d.ts.map +1 -1
  11. package/dist/cjs/index.js +5 -5
  12. package/dist/cjs/index.js.map +1 -1
  13. package/dist/cjs/middlewares.d.ts +2 -2
  14. package/dist/cjs/middlewares.d.ts.map +1 -1
  15. package/dist/cjs/middlewares.js +24 -24
  16. package/dist/cjs/middlewares.js.map +1 -1
  17. package/dist/cjs/router.d.ts +2 -2
  18. package/dist/cjs/router.d.ts.map +1 -1
  19. package/dist/cjs/router.js +8 -8
  20. package/dist/cjs/router.js.map +1 -1
  21. package/dist/cjs/types.d.ts +1 -1
  22. package/dist/cjs/types.d.ts.map +1 -1
  23. package/dist/cjs/upload-stream.d.ts +1 -1
  24. package/dist/cjs/upload-stream.d.ts.map +1 -1
  25. package/dist/cjs/upload-stream.js +7 -7
  26. package/dist/cjs/upload-stream.js.map +1 -1
  27. package/dist/framework.d.ts +2 -4
  28. package/dist/framework.d.ts.map +1 -1
  29. package/dist/framework.js +4 -6
  30. package/dist/framework.js.map +1 -1
  31. package/dist/helpers.d.ts +2 -2
  32. package/dist/helpers.d.ts.map +1 -1
  33. package/dist/helpers.js +1 -1
  34. package/dist/helpers.js.map +1 -1
  35. package/dist/index.d.ts +5 -5
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +5 -5
  38. package/dist/index.js.map +1 -1
  39. package/dist/middlewares.d.ts +2 -2
  40. package/dist/middlewares.d.ts.map +1 -1
  41. package/dist/middlewares.js +24 -24
  42. package/dist/middlewares.js.map +1 -1
  43. package/dist/router.d.ts +2 -2
  44. package/dist/router.d.ts.map +1 -1
  45. package/dist/router.js +8 -8
  46. package/dist/router.js.map +1 -1
  47. package/dist/types.d.ts +1 -1
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/upload-stream.d.ts +1 -1
  50. package/dist/upload-stream.d.ts.map +1 -1
  51. package/dist/upload-stream.js +7 -7
  52. package/dist/upload-stream.js.map +1 -1
  53. package/package.json +8 -1
  54. package/src/framework.deno.ts +194 -0
  55. package/src/framework.ts +197 -0
  56. package/src/helpers.ts +829 -0
  57. package/src/index.ts +5 -0
  58. package/src/list.ts +462 -0
  59. package/src/middlewares.deno.ts +851 -0
  60. package/src/middlewares.ts +851 -0
  61. package/src/router.ts +993 -0
  62. package/src/tree.ts +139 -0
  63. package/src/types.ts +197 -0
  64. package/src/upload-stream.ts +661 -0
  65. package/dist/cjs/framework.deno.d.ts +0 -31
  66. package/dist/cjs/framework.deno.d.ts.map +0 -1
  67. package/dist/cjs/framework.deno.js +0 -245
  68. package/dist/cjs/framework.deno.js.map +0 -1
  69. package/dist/framework.deno.d.ts +0 -31
  70. package/dist/framework.deno.d.ts.map +0 -1
  71. package/dist/framework.deno.js +0 -245
  72. 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
+ }
@@ -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
+ }