owebjs 1.2.3 → 1.3.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.
Files changed (37) hide show
  1. package/README.md +224 -4
  2. package/dist/index.d.ts +23 -8
  3. package/dist/index.js +3 -3
  4. package/dist/plugins/ChunkUpload.js +9 -9
  5. package/dist/plugins/index.d.ts +1 -1
  6. package/dist/structures/Hook.js +1 -1
  7. package/dist/structures/Oweb.js +40 -11
  8. package/dist/structures/Route.js +1 -4
  9. package/dist/utils/assignRoutes.js +157 -46
  10. package/dist/utils/generateFunctionFromTypescript.js +45 -0
  11. package/dist/utils/logger.js +38 -0
  12. package/dist/utils/utils.js +1 -1
  13. package/dist/utils/walk.js +1 -1
  14. package/dist/utils/watchRoutes.js +31 -0
  15. package/dist/uwebsocket/request.js +1 -1
  16. package/dist/uwebsocket/response.js +4 -7
  17. package/dist/uwebsocket/responseSocket.js +1 -1
  18. package/dist/uwebsocket/server.js +4 -7
  19. package/dist/uwebsocket/utils/object.js +1 -1
  20. package/dist/uwebsocket/utils/string.js +1 -1
  21. package/package.json +21 -6
  22. package/dist/uws_darwin_arm64_108.node +0 -0
  23. package/dist/uws_darwin_arm64_83.node +0 -0
  24. package/dist/uws_darwin_arm64_93.node +0 -0
  25. package/dist/uws_darwin_x64_108.node +0 -0
  26. package/dist/uws_darwin_x64_83.node +0 -0
  27. package/dist/uws_darwin_x64_93.node +0 -0
  28. package/dist/uws_linux_arm64_108.node +0 -0
  29. package/dist/uws_linux_arm64_83.node +0 -0
  30. package/dist/uws_linux_arm64_93.node +0 -0
  31. package/dist/uws_linux_x64_108.node +0 -0
  32. package/dist/uws_linux_x64_83.node +0 -0
  33. package/dist/uws_linux_x64_93.node +0 -0
  34. package/dist/uws_win32_x64_108.node +0 -0
  35. package/dist/uws_win32_x64_83.node +0 -0
  36. package/dist/uws_win32_x64_93.node +0 -0
  37. /package/dist/{chunk-NH24FRB3.js → chunk-SHUYVCID.js} +0 -0
package/README.md CHANGED
@@ -1,13 +1,233 @@
1
1
  # Oweb
2
2
 
3
- Flexible handler that built on top of Fastify
3
+ A flexible and modern web framework built on top of Fastify, designed for creating scalable and maintainable web applications with file-based routing and hot module replacement.
4
+
5
+ <p align="center">
6
+ <img src="https://img.shields.io/npm/v/owebjs" alt="npm version">
7
+ <img src="https://img.shields.io/npm/l/owebjs" alt="license">
8
+ <img src="https://img.shields.io/npm/dt/owebjs" alt="downloads">
9
+ </p>
10
+
11
+ ## Features
12
+
13
+ - **File-based Routing**: Automatically generate routes based on your file structure
14
+ - **Hot Module Replacement (HMR)**: Update your routes without restarting the server
15
+ - **Middleware Support**: Use hooks to add middleware functionality
16
+ - **Error Handling**: Global and route-specific error handling
17
+ - **TypeScript Support**: Built with TypeScript for better developer experience
18
+ - **Plugin System**: Extend functionality with plugins
19
+ - **uWebSockets.js Support**: Optional high-performance WebSocket server
4
20
 
5
21
  ## Installation
6
22
 
23
+ ```bash
24
+ npm install owebjs
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ```javascript
30
+ import Oweb from 'owebjs';
31
+
32
+ // Create and setup the app
33
+ const app = await new Oweb().setup();
34
+
35
+ // Load routes from a directory
36
+ await app.loadRoutes({
37
+ directory: 'routes',
38
+ hmr: {
39
+ enabled: true, // Enable hot module replacement
40
+ },
41
+ });
42
+
43
+ // Start the server
44
+ await app.start({ port: 3000 });
45
+ console.log('Server running at http://localhost:3000');
46
+ ```
47
+
48
+ ## Creating Routes
49
+
50
+ Routes are automatically generated based on your file structure. Create a file in your routes directory:
51
+
52
+ ```javascript
53
+ // routes/hello.js
54
+ import { Route } from 'owebjs';
55
+
56
+ export default class extends Route {
57
+ async handle(req, res) {
58
+ res.send({ message: 'Hello, World!' });
59
+ }
60
+ }
61
+ ```
62
+
63
+ This will create a GET route at `/hello`.
64
+
65
+ ### Dynamic Routes
66
+
67
+ Use brackets to create dynamic route parameters:
68
+
69
+ ```javascript
70
+ // routes/users/[id].js
71
+ import { Route } from 'owebjs';
72
+
73
+ export default class extends Route {
74
+ async handle(req, res) {
75
+ res.send({ userId: req.params.id });
76
+ }
77
+ }
78
+ ```
79
+
80
+ This will create a GET route at `/users/:id`.
81
+
82
+ ### HTTP Methods
83
+
84
+ Specify the HTTP method in the filename:
85
+
86
+ ```javascript
87
+ // routes/api/users.post.js
88
+ import { Route } from 'owebjs';
89
+
90
+ export default class extends Route {
91
+ async handle(req, res) {
92
+ // Create a new user
93
+ const user = req.body;
94
+ res.status(201).send({ id: 1, ...user });
95
+ }
96
+ }
97
+ ```
98
+
99
+ ## Middleware (Hooks)
100
+
101
+ Create hooks to add middleware functionality:
102
+
103
+ ```javascript
104
+ // routes/_hooks.js
105
+ import { Hook } from 'owebjs';
106
+
107
+ export default class extends Hook {
108
+ handle(req, res, done) {
109
+ console.log(`${req.method} ${req.url}`);
110
+ done(); // Continue to the next hook or route handler
111
+ }
112
+ }
113
+ ```
114
+
115
+ Hooks are applied to all routes in the current directory and its subdirectories.
116
+
117
+ ## Error Handling
118
+
119
+ ### Global Error Handler
120
+
121
+ ```javascript
122
+ app.setInternalErrorHandler((req, res, error) => {
123
+ console.error(error);
124
+ res.status(500).send({
125
+ error: 'Internal Server Error',
126
+ message: error.message,
127
+ });
128
+ });
129
+ ```
130
+
131
+ ### Route-specific Error Handler
132
+
133
+ ```javascript
134
+ import { Route } from 'owebjs';
135
+
136
+ export default class extends Route {
137
+ async handle(req, res) {
138
+ throw new Error('Something went wrong');
139
+ }
140
+
141
+ handleError(req, res, error) {
142
+ res.status(500).send({
143
+ error: 'Route Error',
144
+ message: error.message,
145
+ });
146
+ }
147
+ }
148
+ ```
149
+
150
+ ## Plugins
151
+
152
+ Oweb supports Fastify plugins and comes with some built-in plugins:
153
+
154
+ ### Using Fastify Plugins
155
+
156
+ ```javascript
157
+ import Oweb from 'owebjs';
158
+ import fastifyMultipart from '@fastify/multipart';
159
+
160
+ const app = await new Oweb().setup();
161
+
162
+ // Register Fastify plugin
163
+ await app.register(fastifyMultipart, {
164
+ limits: {
165
+ fileSize: 10 * 1024 * 1024, // 10MB
166
+ },
167
+ });
7
168
  ```
8
- npm i owebjs
169
+
170
+ ### Using Built-in Plugins
171
+
172
+ ```javascript
173
+ import { Route } from 'owebjs';
174
+ import { ChunkUpload } from 'owebjs/dist/plugins';
175
+
176
+ export default class extends Route {
177
+ async handle(req, res) {
178
+ const file = await req.file();
179
+ const buffer = await file.toBuffer();
180
+
181
+ await ChunkUpload(
182
+ {
183
+ buffer,
184
+ fileName: file.filename,
185
+ currentChunk: +req.query.currentChunk,
186
+ totalChunks: +req.query.totalChunks,
187
+ },
188
+ {
189
+ path: './uploads',
190
+ maxChunkSize: 5 * 1024 * 1024, // 5MB
191
+ },
192
+ );
193
+
194
+ return res.status(204).send();
195
+ }
196
+ }
9
197
  ```
10
198
 
11
- ## Usage
199
+ ## Advanced Configuration
12
200
 
13
- This package is still WIP. However, if you want to check out the usage head over to [test directory](https://github.com/owebjs/oweb/tree/main/test)
201
+ ### uWebSockets.js Support
202
+
203
+ ```javascript
204
+ const app = await new Oweb({ uWebSocketsEnabled: true }).setup();
205
+ ```
206
+
207
+ ### Custom Route Options
208
+
209
+ ```javascript
210
+ import { Route } from 'owebjs';
211
+
212
+ export default class extends Route {
213
+ constructor() {
214
+ super({
215
+ schema: {
216
+ body: {
217
+ type: 'object',
218
+ required: ['username', 'password'],
219
+ properties: {
220
+ username: { type: 'string' },
221
+ password: { type: 'string' },
222
+ },
223
+ },
224
+ },
225
+ });
226
+ }
227
+
228
+ async handle(req, res) {
229
+ // Body is validated according to the schema
230
+ res.send({ success: true });
231
+ }
232
+ }
233
+ ```
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { FastifyServerOptions, FastifyRequest, FastifyReply, FastifyListenOptions, FastifyInstance } from 'fastify';
1
+ import { FastifyInstance, FastifyServerOptions, FastifyRequest, FastifyReply, FastifyListenOptions } from 'fastify';
2
2
  import { FastifyReply as FastifyReply$1 } from 'fastify/types/reply';
3
3
  export { FastifyReply } from 'fastify/types/reply';
4
4
  import { FastifyRequest as FastifyRequest$1 } from 'fastify/types/request';
@@ -24,6 +24,13 @@ interface OwebOptions extends FastifyServerOptions {
24
24
  }
25
25
  interface LoadRoutesOptions {
26
26
  directory: string;
27
+ hmr?: {
28
+ /**
29
+ * The directory to watch for changes. If not specified, it will use the routes directory.
30
+ */
31
+ directory?: string;
32
+ enabled: boolean;
33
+ };
27
34
  }
28
35
  interface _FastifyInstance extends FastifyInstance {
29
36
  }
@@ -31,17 +38,27 @@ declare class _FastifyInstance {
31
38
  }
32
39
  declare class Oweb extends _FastifyInstance {
33
40
  _options: OwebOptions;
41
+ private hmrDirectory;
42
+ routes: Map<string, any>;
34
43
  constructor(options?: OwebOptions);
35
44
  /**
36
45
  *
37
46
  * Returns a fastify instance with the Oweb prototype methods
38
47
  */
39
48
  setup(): Promise<Oweb>;
49
+ /**
50
+ * Loads routes from a directory.
51
+ * @param options.directory The directory to load routes from.
52
+ * @param options.hmr Configuration for Hot Module Replacement.
53
+ * @param options.hmr.enabled Whether to enable HMR. HMR is disabled if NODE_ENV is set to production.
54
+ * @param options.hmr.directory The directory to watch for changes. If not specified, it will use the routes directory.
55
+ */
56
+ loadRoutes({ directory, hmr }: LoadRoutesOptions): Promise<void>;
40
57
  /**
41
58
  *
42
- * Loads routes from a directory
59
+ * Watches for changes in the routes directory
43
60
  */
44
- loadRoutes({ directory }: LoadRoutesOptions): Promise<void>;
61
+ private watch;
45
62
  /**
46
63
  *
47
64
  * Sets the internal error handler
@@ -55,13 +72,11 @@ declare class Oweb extends _FastifyInstance {
55
72
 
56
73
  type Awaitable<T> = T | Promise<T>;
57
74
 
58
- declare interface Route {
59
- handle(req: FastifyRequest$1, res: FastifyReply$1): Awaitable<any>;
60
- handleError?(req: FastifyRequest$1, res: FastifyReply$1, err: Error): Awaitable<any>;
61
- }
62
75
  declare abstract class Route {
63
76
  _options: RouteShorthandOptions;
64
77
  constructor(options?: RouteShorthandOptions);
78
+ abstract handle(req: FastifyRequest$1, res: FastifyReply$1): Awaitable<any>;
79
+ handleError?(req: FastifyRequest$1, res: FastifyReply$1, err: Error): Awaitable<any>;
65
80
  }
66
81
 
67
82
  declare interface Hook {
@@ -70,4 +85,4 @@ declare interface Hook {
70
85
  declare abstract class Hook {
71
86
  }
72
87
 
73
- export { Awaitable, Hook, LoadRoutesOptions, Oweb, OwebOptions, Route, Oweb as default };
88
+ export { type Awaitable, Hook, type LoadRoutesOptions, Oweb, type OwebOptions, Route, Oweb as default };
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import "./chunk-NH24FRB3.js";
1
+ import "./chunk-SHUYVCID.js";
2
2
  import { Oweb } from './structures/Oweb.js';
3
- var src_default = Oweb;
3
+ var index_default = Oweb;
4
4
  export * from './structures/Oweb.js';
5
5
  export * from './structures/Route.js';
6
6
  export * from './structures/Hook.js';
7
7
  export * from './types.js';
8
8
  export {
9
- src_default as default
9
+ index_default as default
10
10
  };
@@ -1,26 +1,26 @@
1
1
  import {
2
2
  __name
3
- } from "../chunk-NH24FRB3.js";
3
+ } from "../chunk-SHUYVCID.js";
4
4
  import fs from "node:fs";
5
5
  import nodePath from "node:path";
6
- var ChunkUploadStatus;
7
- (function(ChunkUploadStatus2) {
6
+ var ChunkUploadStatus = /* @__PURE__ */ function(ChunkUploadStatus2) {
8
7
  ChunkUploadStatus2["Success"] = "SUCCESS";
9
8
  ChunkUploadStatus2["FileTooLarge"] = "FILE_TOO_LARGE";
10
9
  ChunkUploadStatus2["ChunkTooLarge"] = "CHUNK_TOO_LARGE";
11
10
  ChunkUploadStatus2["InvalidChunk"] = "INVALID_CHUNK";
12
- })(ChunkUploadStatus || (ChunkUploadStatus = {}));
11
+ return ChunkUploadStatus2;
12
+ }({});
13
13
  async function ChunkUpload(file, options) {
14
14
  const { buffer, fileName, currentChunk, totalChunks } = file;
15
15
  const { path, maxChunkSize, maxFileSize } = options;
16
16
  if (typeof currentChunk !== "number" || typeof totalChunks !== "number" || currentChunk < 1 || totalChunks < 1 || currentChunk > totalChunks) {
17
17
  return {
18
- status: ChunkUploadStatus.InvalidChunk
18
+ status: "INVALID_CHUNK"
19
19
  };
20
20
  }
21
21
  if (buffer.length > maxChunkSize) {
22
22
  return {
23
- status: ChunkUploadStatus.ChunkTooLarge
23
+ status: "CHUNK_TOO_LARGE"
24
24
  };
25
25
  }
26
26
  const filePath = nodePath.join(path, fileName);
@@ -39,11 +39,11 @@ async function ChunkUpload(file, options) {
39
39
  if (typeof maxFileSize === "number" && fileSize > maxFileSize) {
40
40
  fs.unlinkSync(filePath);
41
41
  return {
42
- status: ChunkUploadStatus.FileTooLarge
42
+ status: "FILE_TOO_LARGE"
43
43
  };
44
44
  }
45
45
  return {
46
- status: ChunkUploadStatus.Success,
46
+ status: "SUCCESS",
47
47
  uploadCompleted: true
48
48
  };
49
49
  } else if (currentChunk === 1) {
@@ -52,7 +52,7 @@ async function ChunkUpload(file, options) {
52
52
  fs.appendFileSync(chunkPath, buffer);
53
53
  }
54
54
  return {
55
- status: ChunkUploadStatus.Success,
55
+ status: "SUCCESS",
56
56
  currentChunk
57
57
  };
58
58
  }
@@ -31,4 +31,4 @@ declare enum ChunkUploadStatus {
31
31
  }
32
32
  declare function ChunkUpload(file: FileData, options: ChunkUploadOptions): Promise<ChunkUploadResult>;
33
33
 
34
- export { ChunkUpload, ChunkUploadOptions, ChunkUploadResult, ChunkUploadStatus, FileData };
34
+ export { ChunkUpload, type ChunkUploadOptions, type ChunkUploadResult, ChunkUploadStatus, type FileData };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  __name
3
- } from "../chunk-NH24FRB3.js";
3
+ } from "../chunk-SHUYVCID.js";
4
4
  class Hook {
5
5
  static {
6
6
  __name(this, "Hook");
@@ -1,8 +1,10 @@
1
1
  import {
2
2
  __name
3
- } from "../chunk-NH24FRB3.js";
3
+ } from "../chunk-SHUYVCID.js";
4
4
  import Fastify from "fastify";
5
- import { assignRoutes } from '../utils/assignRoutes.js';
5
+ import { applyHMR, assignRoutes } from '../utils/assignRoutes.js';
6
+ import { watchRoutes } from '../utils/watchRoutes.js';
7
+ import { info, success, warn } from '../utils/logger.js';
6
8
  let _FastifyInstance = class _FastifyInstance2 {
7
9
  static {
8
10
  __name(this, "_FastifyInstance");
@@ -13,6 +15,8 @@ class Oweb extends _FastifyInstance {
13
15
  __name(this, "Oweb");
14
16
  }
15
17
  _options = {};
18
+ hmrDirectory;
19
+ routes = /* @__PURE__ */ new Map();
16
20
  constructor(options) {
17
21
  super();
18
22
  this._options = options ?? {};
@@ -42,8 +46,7 @@ class Oweb extends _FastifyInstance {
42
46
  done();
43
47
  });
44
48
  for (const key in Object.getOwnPropertyDescriptors(Oweb.prototype)) {
45
- if (key === "constructor")
46
- continue;
49
+ if (key === "constructor") continue;
47
50
  Object.defineProperty(fastify, key, Object.getOwnPropertyDescriptor(Oweb.prototype, key));
48
51
  }
49
52
  Object.defineProperty(fastify, "_options", {
@@ -55,12 +58,31 @@ class Oweb extends _FastifyInstance {
55
58
  return fastify;
56
59
  }
57
60
  /**
58
- *
59
- * Loads routes from a directory
61
+ * Loads routes from a directory.
62
+ * @param options.directory The directory to load routes from.
63
+ * @param options.hmr Configuration for Hot Module Replacement.
64
+ * @param options.hmr.enabled Whether to enable HMR. HMR is disabled if NODE_ENV is set to production.
65
+ * @param options.hmr.directory The directory to watch for changes. If not specified, it will use the routes directory.
60
66
  */
61
- loadRoutes({ directory }) {
67
+ loadRoutes({ directory, hmr }) {
68
+ if (hmr && !hmr.directory) hmr.directory = directory;
69
+ if (hmr?.enabled) {
70
+ this.hmrDirectory = hmr.directory;
71
+ success(`Hot Module Replacement enabled. Watching changes in ${hmr.directory}`, "HMR");
72
+ } else {
73
+ warn('Hot Module Replacement is disabled. Use "await app.loadRoutes({ hmr: { enabled: true, directory: path } })" to enable it.', "HMR");
74
+ }
62
75
  return assignRoutes(this, directory);
63
76
  }
77
+ /**
78
+ *
79
+ * Watches for changes in the routes directory
80
+ */
81
+ watch() {
82
+ return watchRoutes(this.hmrDirectory, (op, path, content) => {
83
+ applyHMR(this, op, this.hmrDirectory, path, content);
84
+ });
85
+ }
64
86
  /**
65
87
  *
66
88
  * Sets the internal error handler
@@ -73,10 +95,17 @@ class Oweb extends _FastifyInstance {
73
95
  this.listen({
74
96
  port,
75
97
  host
76
- }, (err, address) => resolve({
77
- err,
78
- address
79
- }));
98
+ }, (err, address) => {
99
+ if (process.env.NODE_ENV !== "production") {
100
+ if (this.hmrDirectory) this.watch();
101
+ } else {
102
+ info("Hot Module Replacement is disabled in production mode. NODE_ENV is set to production.", "HMR");
103
+ }
104
+ resolve({
105
+ err,
106
+ address
107
+ });
108
+ });
80
109
  });
81
110
  }
82
111
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  __name
3
- } from "../chunk-NH24FRB3.js";
3
+ } from "../chunk-SHUYVCID.js";
4
4
  class Route {
5
5
  static {
6
6
  __name(this, "Route");
@@ -9,9 +9,6 @@ class Route {
9
9
  constructor(options) {
10
10
  this._options = options ?? {};
11
11
  }
12
- handle() {
13
- throw new Error("Route#handle must be implemented");
14
- }
15
12
  }
16
13
  export {
17
14
  Route
@@ -1,12 +1,71 @@
1
1
  import {
2
2
  __name
3
- } from "../chunk-NH24FRB3.js";
3
+ } from "../chunk-SHUYVCID.js";
4
4
  import path from "node:path";
5
5
  import { dirname } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { buildRoutePath, buildRouteURL } from './utils.js';
8
8
  import { walk } from './walk.js';
9
+ import { success, warn } from './logger.js';
10
+ import { match } from "path-to-regexp";
11
+ import generateFunctionFromTypescript from './generateFunctionFromTypescript.js';
9
12
  const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ let routeFunctions = {};
14
+ const temporaryRequests = {
15
+ get: {},
16
+ post: {},
17
+ put: {},
18
+ delete: {},
19
+ patch: {},
20
+ options: {}
21
+ };
22
+ let routesCache = [];
23
+ const compiledRoutes = {};
24
+ const applyHMR = /* @__PURE__ */ __name(async (oweb, op, workingDir, path2, content) => {
25
+ if (path2.endsWith("hooks.js") || path2.endsWith("hooks.ts")) {
26
+ warn(`Hot Module Replacement is not supported for hooks. Restart the server for changes to take effect.`, "HMR");
27
+ return;
28
+ }
29
+ if (path2.endsWith(".ts")) {
30
+ const start = Date.now();
31
+ compiledRoutes[path2] = content.length ? await generateFunctionFromTypescript(content, path2) : void 0;
32
+ const end = Date.now() - start;
33
+ success(`File ${path2} compiled in ${end}ms`, "HMR");
34
+ }
35
+ if (op === "new-file") {
36
+ const start = Date.now();
37
+ const files = await walk(workingDir);
38
+ const routes = await generateRoutes(files);
39
+ routesCache = routes;
40
+ const f = routes.find((x) => x.fileInfo.filePath == path2);
41
+ temporaryRequests[f.method.toLowerCase()][f.url] = inner(oweb, f);
42
+ const end = Date.now() - start;
43
+ success(`Route ${f.method.toUpperCase()}:${f.url} created in ${end}ms`, "HMR");
44
+ } else if (op === "modify-file") {
45
+ const start = Date.now();
46
+ const files = await walk(workingDir);
47
+ const routes = await generateRoutes(files);
48
+ routesCache = routes;
49
+ const f = routes.find((x) => x.fileInfo.filePath == path2);
50
+ if (f.url in temporaryRequests[f.method.toLowerCase()]) {
51
+ temporaryRequests[f.method.toLowerCase()][f.url] = inner(oweb, f);
52
+ } else {
53
+ routeFunctions[f.fileInfo.filePath] = inner(oweb, f);
54
+ }
55
+ const end = Date.now() - start;
56
+ success(`Route ${f.method.toUpperCase()}:${f.url} reloaded in ${end}ms`, "HMR");
57
+ } else if (op === "delete-file") {
58
+ const start = Date.now();
59
+ const f = routesCache.find((x) => x.fileInfo.filePath == path2);
60
+ if (f.url in temporaryRequests[f.method.toLowerCase()]) {
61
+ delete temporaryRequests[f.method.toLowerCase()][f.url];
62
+ } else {
63
+ delete routeFunctions[f.fileInfo.filePath];
64
+ }
65
+ const end = Date.now() - start;
66
+ success(`Route ${f.method.toUpperCase()}:${f.url} removed in ${end}ms`, "HMR");
67
+ }
68
+ }, "applyHMR");
10
69
  const generateRoutes = /* @__PURE__ */ __name(async (files) => {
11
70
  const routes = [];
12
71
  for (const file of files) {
@@ -15,7 +74,17 @@ const generateRoutes = /* @__PURE__ */ __name(async (files) => {
15
74
  const packageURL = new URL(path.resolve(filePath), `file://${__dirname}`).pathname.replaceAll("\\", "/");
16
75
  const routePath = buildRoutePath(parsedFile);
17
76
  const route = buildRouteURL(routePath);
18
- const def = await import(packageURL);
77
+ if (compiledRoutes[file.filePath]) {
78
+ routes.push({
79
+ url: route.url,
80
+ method: route.method,
81
+ fn: compiledRoutes[file.filePath],
82
+ fileInfo: file
83
+ });
84
+ continue;
85
+ }
86
+ const cacheBuster = `?t=${Date.now()}`;
87
+ const def = await import(packageURL + cacheBuster);
19
88
  const routeFuncs = def.default;
20
89
  routes.push({
21
90
  url: route.url,
@@ -26,57 +95,99 @@ const generateRoutes = /* @__PURE__ */ __name(async (files) => {
26
95
  }
27
96
  return routes;
28
97
  }, "generateRoutes");
29
- const assignRoutes = /* @__PURE__ */ __name(async (oweb, directory) => {
30
- const files = await walk(directory);
31
- const routes = await generateRoutes(files);
32
- for (const route of routes) {
33
- const routeFunc = new route.fn();
34
- oweb[route.method](route.url, routeFunc._options || {}, function(req, res) {
35
- const handle = /* @__PURE__ */ __name(() => {
36
- if (routeFunc.handle.constructor.name == "AsyncFunction") {
37
- routeFunc.handle(...arguments).catch((error) => {
38
- const handleErrorArgs = [
39
- ...arguments,
40
- error
41
- ];
42
- if (routeFunc?.handleError) {
43
- routeFunc.handleError(...handleErrorArgs);
44
- } else {
45
- oweb._options.OWEB_INTERNAL_ERROR_HANDLER(...handleErrorArgs);
46
- }
47
- });
48
- } else {
49
- try {
50
- routeFunc.handle(...arguments);
51
- } catch (error) {
52
- const handleErrorArgs = [
53
- ...arguments,
54
- error
55
- ];
56
- if (routeFunc?.handleError) {
57
- routeFunc.handleError(...handleErrorArgs);
58
- } else {
59
- oweb._options.OWEB_INTERNAL_ERROR_HANDLER(...handleErrorArgs);
60
- }
98
+ function inner(oweb, route) {
99
+ if (!route.fn) {
100
+ return;
101
+ }
102
+ const routeFunc = new route.fn();
103
+ return function(req, res) {
104
+ const handle = /* @__PURE__ */ __name(() => {
105
+ if (routeFunc.handle.constructor.name == "AsyncFunction") {
106
+ routeFunc.handle(req, res).catch((error) => {
107
+ if (routeFunc?.handleError) {
108
+ routeFunc.handleError(req, res, error);
109
+ } else {
110
+ oweb._options.OWEB_INTERNAL_ERROR_HANDLER(req, res, error);
61
111
  }
62
- }
63
- }, "handle");
64
- if (route.fileInfo.hooks.length) {
65
- for (let index = 0; index < route.fileInfo.hooks.length; index++) {
66
- const hookFun = route.fileInfo.hooks[index];
67
- new hookFun().handle(req, res, () => {
68
- if (index + 1 == route.fileInfo.hooks.length) {
69
- handle();
70
- }
71
- });
72
- }
112
+ });
73
113
  } else {
74
- handle();
114
+ try {
115
+ routeFunc.handle(req, res);
116
+ } catch (error) {
117
+ if (routeFunc?.handleError) {
118
+ routeFunc.handleError(req, res, error);
119
+ } else {
120
+ oweb._options.OWEB_INTERNAL_ERROR_HANDLER(req, res, error);
121
+ }
122
+ }
123
+ }
124
+ }, "handle");
125
+ if (route.fileInfo.hooks.length) {
126
+ for (let index = 0; index < route.fileInfo.hooks.length; index++) {
127
+ const hookFun = route.fileInfo.hooks[index];
128
+ hookFun.prototype.handle(req, res, () => {
129
+ if (index + 1 == route.fileInfo.hooks.length) {
130
+ handle();
131
+ }
132
+ });
75
133
  }
134
+ } else {
135
+ handle();
136
+ }
137
+ };
138
+ }
139
+ __name(inner, "inner");
140
+ function assignSpecificRoute(oweb, route) {
141
+ if (!route.fn) return;
142
+ const routeFunc = new route.fn();
143
+ routeFunctions[route.fileInfo.filePath] = inner(oweb, route);
144
+ oweb[route.method](route.url, routeFunc._options || {}, function(req, res) {
145
+ if (routeFunctions[route.fileInfo.filePath]) {
146
+ return routeFunctions[route.fileInfo.filePath](req, res);
147
+ } else {
148
+ return res.status(404).send({
149
+ message: `Route ${req.method}:${req.url} not found`,
150
+ error: "Not Found",
151
+ statusCode: 404
152
+ });
153
+ }
154
+ });
155
+ }
156
+ __name(assignSpecificRoute, "assignSpecificRoute");
157
+ const assignRoutes = /* @__PURE__ */ __name(async (oweb, directory) => {
158
+ const files = await walk(directory);
159
+ const routes = await generateRoutes(files);
160
+ routesCache = routes;
161
+ oweb.all("*", (req, res) => {
162
+ const vals = temporaryRequests[req.method.toLowerCase()];
163
+ const keys = Object.keys(vals);
164
+ if (!vals || !keys.length) {
165
+ return res.status(404).send({
166
+ message: `Route ${req.method}:${req.url} not found`,
167
+ error: "Not Found",
168
+ statusCode: 404
169
+ });
170
+ }
171
+ const f = keys.find((tempName) => {
172
+ const matcher = match(tempName);
173
+ return matcher(req.url);
76
174
  });
175
+ if (f && vals[f]) {
176
+ return vals[f](req, res);
177
+ } else {
178
+ return res.status(404).send({
179
+ message: `Route ${req.method}:${req.url} not found`,
180
+ error: "Not Found",
181
+ statusCode: 404
182
+ });
183
+ }
184
+ });
185
+ for (const route of routes) {
186
+ assignSpecificRoute(oweb, route);
77
187
  }
78
188
  }, "assignRoutes");
79
189
  export {
190
+ applyHMR,
80
191
  assignRoutes,
81
192
  generateRoutes
82
193
  };
@@ -0,0 +1,45 @@
1
+ import {
2
+ __name
3
+ } from "../chunk-SHUYVCID.js";
4
+ import { parse } from "@babel/parser";
5
+ import traverse from "@babel/traverse";
6
+ import generate from "@babel/generator";
7
+ import path from "node:path";
8
+ import babel from "@babel/core";
9
+ import { pathToFileURL } from "node:url";
10
+ async function generateFunctionFromTypescript(tsCode, filePath) {
11
+ const result = babel.transformSync(tsCode, {
12
+ presets: [
13
+ "@babel/preset-typescript"
14
+ ],
15
+ filename: filePath
16
+ });
17
+ const jsCode = result?.code ?? "";
18
+ const ast = parse(jsCode, {
19
+ sourceType: "module"
20
+ });
21
+ traverse.default(ast, {
22
+ ImportDeclaration(astPath) {
23
+ const importSourceNode = astPath.node.source;
24
+ let relativePath = importSourceNode.value;
25
+ if (relativePath.startsWith(".")) {
26
+ let resolvedPath = relativePath;
27
+ if (path.extname(relativePath) === "") {
28
+ resolvedPath = relativePath + ".ts";
29
+ }
30
+ const originalFileDir = path.dirname(filePath);
31
+ const absoluteDepPath = path.resolve(originalFileDir, resolvedPath);
32
+ const absoluteDepUrl = pathToFileURL(absoluteDepPath).href;
33
+ importSourceNode.value = absoluteDepUrl;
34
+ }
35
+ }
36
+ });
37
+ const { code: modifiedCode } = generate.default(ast);
38
+ const dataUrl = `data:text/javascript,${encodeURIComponent(modifiedCode)}`;
39
+ const module = await import(dataUrl);
40
+ return module.default;
41
+ }
42
+ __name(generateFunctionFromTypescript, "generateFunctionFromTypescript");
43
+ export {
44
+ generateFunctionFromTypescript as default
45
+ };
@@ -0,0 +1,38 @@
1
+ import {
2
+ __name
3
+ } from "../chunk-SHUYVCID.js";
4
+ import chalk from "chalk";
5
+ const successText = /* @__PURE__ */ __name((message, category = "success") => {
6
+ return `${chalk.bgGreen(` Oweb:${category} `)} ${message}`;
7
+ }, "successText");
8
+ const infoText = /* @__PURE__ */ __name((message, category = "info") => {
9
+ return `${chalk.bgBlue(` Oweb:${category} `)} ${message}`;
10
+ }, "infoText");
11
+ const warnText = /* @__PURE__ */ __name((message, category = "warn") => {
12
+ return `${chalk.bgYellow(` Oweb:${category} `)} ${message}`;
13
+ }, "warnText");
14
+ const errorText = /* @__PURE__ */ __name((message, category = "error") => {
15
+ return `${chalk.bgRed(` Oweb:${category} `)} ${message}`;
16
+ }, "errorText");
17
+ const success = /* @__PURE__ */ __name((message, category) => {
18
+ console.log(successText(message, category));
19
+ }, "success");
20
+ const info = /* @__PURE__ */ __name((message, category) => {
21
+ console.log(infoText(message, category));
22
+ }, "info");
23
+ const warn = /* @__PURE__ */ __name((message, category) => {
24
+ console.log(warnText(message, category));
25
+ }, "warn");
26
+ const error = /* @__PURE__ */ __name((message, category) => {
27
+ console.log(errorText(message, category));
28
+ }, "error");
29
+ export {
30
+ error,
31
+ errorText,
32
+ info,
33
+ infoText,
34
+ success,
35
+ successText,
36
+ warn,
37
+ warnText
38
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  __name
3
- } from "../chunk-NH24FRB3.js";
3
+ } from "../chunk-SHUYVCID.js";
4
4
  const mergePaths = /* @__PURE__ */ __name((...paths) => "/" + paths.map((path) => path.replace(/^\/|\/$/g, "")).filter((path) => path !== "").join("/"), "mergePaths");
5
5
  const regBackets = /\[([^}]*)\]/g;
6
6
  const transformBrackets = /* @__PURE__ */ __name((value) => regBackets.test(value) ? value.replace(regBackets, (_, s) => `:${s}`) : value, "transformBrackets");
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  __name
3
- } from "../chunk-NH24FRB3.js";
3
+ } from "../chunk-SHUYVCID.js";
4
4
  import { readdirSync, statSync } from "node:fs";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import path from "node:path";
@@ -0,0 +1,31 @@
1
+ import {
2
+ __name
3
+ } from "../chunk-SHUYVCID.js";
4
+ import chokidar from "chokidar";
5
+ import { readFileSync } from "fs";
6
+ function watchRoutes(dir, onUpdate) {
7
+ const watcher = chokidar.watch(dir, {
8
+ ignored: /([/\\]\.)|(node_modules)|(dist)/,
9
+ persistent: true,
10
+ ignoreInitial: true,
11
+ awaitWriteFinish: {
12
+ stabilityThreshold: 150,
13
+ pollInterval: 50
14
+ }
15
+ });
16
+ watcher.on("add", async (filePath) => {
17
+ const content = readFileSync(filePath, "utf-8");
18
+ onUpdate("new-file", filePath, content);
19
+ });
20
+ watcher.on("change", async (filePath) => {
21
+ const content = readFileSync(filePath, "utf-8");
22
+ onUpdate("modify-file", filePath, content);
23
+ });
24
+ watcher.on("unlink", (filePath) => {
25
+ onUpdate("delete-file", filePath, "");
26
+ });
27
+ }
28
+ __name(watchRoutes, "watchRoutes");
29
+ export {
30
+ watchRoutes
31
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  __name
3
- } from "../chunk-NH24FRB3.js";
3
+ } from "../chunk-SHUYVCID.js";
4
4
  import { Readable } from "stream";
5
5
  import { forEach } from './utils/object.js';
6
6
  class HttpRequest extends Readable {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  __name
3
- } from "../chunk-NH24FRB3.js";
3
+ } from "../chunk-SHUYVCID.js";
4
4
  import { Writable } from "stream";
5
5
  import { toLowerCase } from "./utils/string.js";
6
6
  import HttpResponseSocket from './responseSocket.js';
@@ -50,13 +50,11 @@ class HttpResponse extends Writable {
50
50
  }
51
51
  //@ts-ignore
52
52
  write(data) {
53
- if (this.finished)
54
- return;
53
+ if (this.finished) return;
55
54
  this.res.write(data);
56
55
  }
57
56
  writeHead(statusCode) {
58
- if (this.finished)
59
- return;
57
+ if (this.finished) return;
60
58
  this.statusCode = statusCode;
61
59
  let headers;
62
60
  if (arguments.length === 2) {
@@ -73,8 +71,7 @@ class HttpResponse extends Writable {
73
71
  }
74
72
  //@ts-ignore
75
73
  end(data) {
76
- if (this.finished)
77
- return;
74
+ if (this.finished) return;
78
75
  function doWrite(res) {
79
76
  res.res.writeStatus(`${res.statusCode} ${res.statusMessage}`);
80
77
  res.finished = true;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  __name
3
- } from "../chunk-NH24FRB3.js";
3
+ } from "../chunk-SHUYVCID.js";
4
4
  class HttpResponseSocket {
5
5
  static {
6
6
  __name(this, "HttpResponseSocket");
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  __name
3
- } from "../chunk-NH24FRB3.js";
3
+ } from "../chunk-SHUYVCID.js";
4
4
  import { EventEmitter } from "node:events";
5
5
  const REQUEST_EVENT = "request";
6
6
  import HttpRequest from './request.js';
@@ -67,16 +67,14 @@ async function server_default({ cert_file_name, key_file_name }) {
67
67
  }
68
68
  close(cb) {
69
69
  uWS.us_listen_socket_close(uServer._socket);
70
- if (!cb)
71
- return;
70
+ if (!cb) return;
72
71
  return cb();
73
72
  }
74
73
  start(host, port, cb) {
75
74
  let args;
76
75
  const callbackFunction = /* @__PURE__ */ __name(function(socket) {
77
76
  uServer._socket = socket;
78
- if (cb)
79
- cb(socket);
77
+ if (cb) cb(socket);
80
78
  }, "callbackFunction");
81
79
  if (host && port && cb) {
82
80
  args = [
@@ -103,8 +101,7 @@ async function server_default({ cert_file_name, key_file_name }) {
103
101
  host = listenOptions.host;
104
102
  return this.start(host, port, (socket) => {
105
103
  uServer._socket = socket;
106
- if (cb)
107
- cb(socket);
104
+ if (cb) cb(socket);
108
105
  });
109
106
  } else {
110
107
  if ((!port || typeof port === "function") && !cb) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  __name
3
- } from "../../chunk-NH24FRB3.js";
3
+ } from "../../chunk-SHUYVCID.js";
4
4
  const forEach = /* @__PURE__ */ __name((obj, cb) => {
5
5
  const keys = Object.keys(obj);
6
6
  for (let i = 0, length = keys.length; i < length; i++) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  __name
3
- } from "../../chunk-NH24FRB3.js";
3
+ } from "../../chunk-SHUYVCID.js";
4
4
  const toString = /* @__PURE__ */ __name((str) => {
5
5
  return str + "";
6
6
  }, "toString");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "owebjs",
3
- "version": "1.2.3",
4
- "description": "Flexible handler that built on top of Fastify",
3
+ "version": "1.3.1",
4
+ "description": "A flexible and modern web framework built on top of Fastify",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
@@ -32,17 +32,32 @@
32
32
  "author": "owebjs",
33
33
  "license": "MIT",
34
34
  "dependencies": {
35
- "fastify": "^4.23.2"
35
+ "@babel/core": "^7.28.0",
36
+ "@babel/generator": "^7.28.0",
37
+ "@babel/parser": "^7.28.0",
38
+ "@babel/preset-typescript": "^7.27.1",
39
+ "@babel/traverse": "^7.28.0",
40
+ "@babel/types": "^7.28.2",
41
+ "chalk": "^5.4.1",
42
+ "fastify": "4.23.2",
43
+ "path-to-regexp": "^8.2.0"
36
44
  },
37
45
  "devDependencies": {
38
46
  "@fastify/multipart": "^8.1.0",
39
47
  "@swc/core": "^1.3.85",
40
- "@types/node": "^20.6.2",
48
+ "@types/chokidar": "^2.1.3",
49
+ "@types/node": "^24.1.0",
50
+ "chokidar": "^3.5.3",
41
51
  "prettier": "^3.0.3",
42
52
  "tslib": "^2.6.2",
43
- "tsup": "^7.2.0",
53
+ "tsup": "^8.5.0",
44
54
  "typescript": "^5.2.2",
45
55
  "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.10.0"
46
56
  },
47
- "type": "module"
57
+ "type": "module",
58
+ "pnpm": {
59
+ "onlyBuiltDependencies": [
60
+ "esbuild"
61
+ ]
62
+ }
48
63
  }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
File without changes