princejs 1.5.4 → 1.6.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 +86 -45
- package/dist/helpers.js +44 -0
- package/dist/middleware.js +33 -126
- package/dist/scheduler.js +31 -0
- package/package.json +16 -8
package/Readme.md
CHANGED
|
@@ -9,13 +9,8 @@
|
|
|
9
9
|
## 🏆 World Record: Fastest Framework Under 3 kB
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
# Create a new PrinceJS app
|
|
13
12
|
bun create princejs my-app
|
|
14
|
-
|
|
15
|
-
# Move into the project
|
|
16
13
|
cd my-app
|
|
17
|
-
|
|
18
|
-
# Run in development mode
|
|
19
14
|
bun dev
|
|
20
15
|
```
|
|
21
16
|
|
|
@@ -38,52 +33,55 @@ app.listen(3000);
|
|
|
38
33
|
|
|
39
34
|
## ⚔️ Size War (Gzipped — Real World)
|
|
40
35
|
|
|
41
|
-
| Framework | Gzipped | Minified
|
|
42
|
-
| ------------ | ---------- |
|
|
43
|
-
| **PrinceJS** | **2.8 kB** |
|
|
44
|
-
|
|
|
45
|
-
|
|
|
36
|
+
| Framework | Gzipped | Minified | vs PrinceJS |
|
|
37
|
+
| ------------ | ---------- | -------- | --------------- |
|
|
38
|
+
| **PrinceJS** | **2.8 kB** | 7.8 kB | — |
|
|
39
|
+
| Hono | 7.3 kB | 18.7 kB | **2.6× bigger** |
|
|
40
|
+
| Elysia | 62.5 kB | 245 kB | **22× bigger** |
|
|
46
41
|
|
|
47
|
-
|
|
42
|
+
PrinceJS fits in a tweet. Elysia needs a ZIP file.
|
|
48
43
|
|
|
49
44
|
---
|
|
50
45
|
|
|
51
46
|
## ⚡ Benchmarks (autocannon -c 100 -d 30)
|
|
52
47
|
|
|
53
48
|
**Windows 11 • November 15, 2025 • 100 connections • 30 seconds**
|
|
49
|
+
**Route:** `GET /users/:id`
|
|
54
50
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
| 🥉 | Elysia | 15,862 | 476k | 1.94 MB/s |
|
|
62
|
-
| 4️⃣ | Express | 9,325 | 280k | 1.84 MB/s |
|
|
51
|
+
| Rank | Framework | Req/s | Requests (30s) | Throughput |
|
|
52
|
+
| --------------- | ---------- | ----- | -------------- | ---------- |
|
|
53
|
+
| 🥇 **PrinceJS** | **19,200** | 576k | 2.34 MB/s | |
|
|
54
|
+
| 🥈 Hono | 16,212 | 486k | 1.98 MB/s | |
|
|
55
|
+
| 🥉 Elysia | 15,862 | 476k | 1.94 MB/s | |
|
|
56
|
+
| 4️⃣ Express | 9,325 | 280k | 1.84 MB/s | |
|
|
63
57
|
|
|
64
58
|
### Summary
|
|
65
59
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
* PrinceJS beats **Elysia by 21%** (3,338 more req/s)
|
|
61
|
+
* PrinceJS beats **Hono by 18%** (2,988 more req/s)
|
|
62
|
+
* PrinceJS beats **Express by 106%** (over 2× faster)
|
|
69
63
|
|
|
70
|
-
>
|
|
64
|
+
> PrinceJS is the FASTEST framework under 10 kB. Period.
|
|
71
65
|
|
|
72
66
|
---
|
|
73
67
|
|
|
74
68
|
## 🔥 Why PrinceJS Wins
|
|
75
69
|
|
|
76
|
-
### 1. **Trie-Based Router
|
|
70
|
+
### 1. **Trie-Based Router (Cached)**
|
|
71
|
+
|
|
77
72
|
Most frameworks rebuild routes on every request. PrinceJS builds once and caches.
|
|
78
73
|
|
|
79
74
|
### 2. **Zero Overhead Middleware**
|
|
75
|
+
|
|
80
76
|
Middleware tracking prevents duplicate execution. No wasted cycles.
|
|
81
77
|
|
|
82
78
|
### 3. **Optimized for Bun**
|
|
83
|
-
|
|
79
|
+
|
|
80
|
+
Native `Bun.serve()` with WebSocket support. No abstraction layers.
|
|
84
81
|
|
|
85
82
|
### 4. **Smart Body Parsing**
|
|
86
|
-
|
|
83
|
+
|
|
84
|
+
Only parses body when needed. GET requests skip parsing entirely.
|
|
87
85
|
|
|
88
86
|
---
|
|
89
87
|
|
|
@@ -105,12 +103,42 @@ app
|
|
|
105
103
|
})));
|
|
106
104
|
```
|
|
107
105
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
106
|
+
### ✓ Middleware
|
|
107
|
+
|
|
108
|
+
* CORS
|
|
109
|
+
* Logger
|
|
110
|
+
* Rate Limiting
|
|
111
|
+
* Static Files
|
|
112
|
+
|
|
113
|
+
### ✓ Validation
|
|
114
|
+
|
|
115
|
+
* Zod schema validation
|
|
116
|
+
|
|
117
|
+
### ✓ WebSocket Support
|
|
118
|
+
|
|
119
|
+
### ✓ File Uploads
|
|
120
|
+
|
|
121
|
+
### ✓ Response Builder
|
|
122
|
+
|
|
123
|
+
### ✓ OpenAPI
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 🆕 v3.3.1 — New Tree-Shakable Features
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { cache, email, ai, upload } from "princejs/helpers";
|
|
131
|
+
import { cron, openapi } from "princejs/scheduler";
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
* `cache(60)(handler)` — In-memory cache
|
|
135
|
+
* `email(to, subject, html)` — Resend.com
|
|
136
|
+
* `ai(prompt)` — Grok-3 via xAI API
|
|
137
|
+
* `upload()` — 1-line file upload
|
|
138
|
+
* `cron("*/2 * * * *", task)` — Cron support
|
|
139
|
+
* `openapi({ title, version })` — Auto docs
|
|
140
|
+
|
|
141
|
+
**Tree-shakable = only what you import gets bundled**
|
|
114
142
|
|
|
115
143
|
---
|
|
116
144
|
|
|
@@ -132,6 +160,8 @@ yarn add princejs
|
|
|
132
160
|
import { prince } from "princejs";
|
|
133
161
|
import { cors, logger, rateLimit } from "princejs/middleware";
|
|
134
162
|
import { validate } from "princejs/validation";
|
|
163
|
+
import { cache, ai, upload } from "princejs/helpers";
|
|
164
|
+
import { cron } from "princejs/scheduler";
|
|
135
165
|
import { z } from "zod";
|
|
136
166
|
|
|
137
167
|
const app = prince(true); // dev mode
|
|
@@ -141,10 +171,13 @@ app.use(cors());
|
|
|
141
171
|
app.use(logger());
|
|
142
172
|
app.use(rateLimit({ max: 100, window: 60 }));
|
|
143
173
|
|
|
174
|
+
// Validation
|
|
175
|
+
app.use(validate(z.object({ name: z.string() })));
|
|
176
|
+
|
|
144
177
|
// Routes
|
|
145
178
|
app.get("/", () => ({
|
|
146
179
|
message: "Welcome to PrinceJS",
|
|
147
|
-
version: "
|
|
180
|
+
version: "3.3.1"
|
|
148
181
|
}));
|
|
149
182
|
|
|
150
183
|
app.get("/users/:id", (req) => ({
|
|
@@ -152,17 +185,26 @@ app.get("/users/:id", (req) => ({
|
|
|
152
185
|
name: "John Doe"
|
|
153
186
|
}));
|
|
154
187
|
|
|
155
|
-
//
|
|
156
|
-
app.
|
|
188
|
+
// New: Cache
|
|
189
|
+
app.get("/data", cache(60)(() => ({ time: Date.now() })));
|
|
190
|
+
|
|
191
|
+
// New: AI
|
|
192
|
+
app.post("/ai", async (req) => ({ reply: await ai(req.body.q) }));
|
|
193
|
+
|
|
194
|
+
// New: Upload
|
|
195
|
+
app.post("/upload", upload(), (req) => ({
|
|
157
196
|
files: Object.keys(req.files || {}),
|
|
158
197
|
body: req.body
|
|
159
198
|
}));
|
|
160
199
|
|
|
200
|
+
// New: Cron
|
|
201
|
+
cron("*/1 * * * *", () => console.log("PrinceJS heartbeat"));
|
|
202
|
+
|
|
161
203
|
// WebSocket
|
|
162
204
|
app.ws("/chat", {
|
|
163
205
|
open: (ws) => ws.send("Welcome!"),
|
|
164
206
|
message: (ws, msg) => ws.send(`Echo: ${msg}`),
|
|
165
|
-
close: (
|
|
207
|
+
close: () => console.log("Disconnected")
|
|
166
208
|
});
|
|
167
209
|
|
|
168
210
|
app.listen(3000);
|
|
@@ -172,13 +214,13 @@ app.listen(3000);
|
|
|
172
214
|
|
|
173
215
|
## 📚 Documentation
|
|
174
216
|
|
|
175
|
-
|
|
217
|
+
Visit: **princejs.vercel.app**
|
|
176
218
|
|
|
177
219
|
---
|
|
178
220
|
|
|
179
221
|
## 🤝 Contributing
|
|
180
222
|
|
|
181
|
-
Issues and PRs welcome!
|
|
223
|
+
Issues and PRs welcome!
|
|
182
224
|
|
|
183
225
|
```bash
|
|
184
226
|
git clone https://github.com/MatthewTheCoder1218/princejs
|
|
@@ -191,9 +233,8 @@ bun test
|
|
|
191
233
|
|
|
192
234
|
## 🇳🇬 Built in Nigeria
|
|
193
235
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
> *"2.8 kB. 19,200 req/s. The fastest framework under 10 kB."*
|
|
236
|
+
Made by **@Lil_Prince_1218 — Age 13**
|
|
237
|
+
*"2.8 kB. 19,200 req/s. The fastest framework under 10 kB."*
|
|
197
238
|
|
|
198
239
|
Inspired by the greats (Express, Hono, Elysia) but built to win.
|
|
199
240
|
|
|
@@ -201,15 +242,15 @@ Inspired by the greats (Express, Hono, Elysia) but built to win.
|
|
|
201
242
|
|
|
202
243
|
## 📄 License
|
|
203
244
|
|
|
204
|
-
MIT © 2025 Matthew Michael
|
|
245
|
+
MIT © 2025 **Matthew Michael**
|
|
205
246
|
|
|
206
247
|
---
|
|
207
248
|
|
|
208
249
|
## ⭐ Star This Repo
|
|
209
250
|
|
|
210
|
-
If PrinceJS helped you,
|
|
251
|
+
If PrinceJS helped you, star the repo!
|
|
211
252
|
|
|
212
|
-
|
|
253
|
+
GitHub: [github.com/MatthewTheCoder1218/princejs](https://github.com/MatthewTheCoder1218/princejs)
|
|
213
254
|
|
|
214
255
|
---
|
|
215
256
|
|
|
@@ -221,4 +262,4 @@ If PrinceJS helped you, give it a star! It helps other developers discover it.
|
|
|
221
262
|
|
|
222
263
|
---
|
|
223
264
|
|
|
224
|
-
**PrinceJS: Small in size. Giant in speed. 🚀**
|
|
265
|
+
**PrinceJS: Small in size. Giant in speed. 🚀**
|
package/dist/helpers.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/helpers.ts
|
|
3
|
+
var cache = (ttl) => {
|
|
4
|
+
const store = {};
|
|
5
|
+
return (handler) => async (req) => {
|
|
6
|
+
const key = req.url;
|
|
7
|
+
const now = Date.now();
|
|
8
|
+
if (store[key]?.exp > now)
|
|
9
|
+
return store[key].data;
|
|
10
|
+
const data = await handler(req);
|
|
11
|
+
store[key] = { data, exp: now + ttl * 1000 };
|
|
12
|
+
setTimeout(() => delete store[key], ttl * 1000 + 1000);
|
|
13
|
+
return data;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
var email = async (to, subject, html) => {
|
|
17
|
+
await fetch("https://api.resend.com/emails", {
|
|
18
|
+
method: "POST",
|
|
19
|
+
headers: { Authorization: `Bearer ${process.env.RESEND_KEY}` },
|
|
20
|
+
body: JSON.stringify({ from: "no-reply@princejs.dev", to, subject, html })
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
var ai = async (prompt) => {
|
|
24
|
+
const res = await fetch("https://api.x.ai/v1/chat/completions", {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: { Authorization: `Bearer ${process.env.XAI_KEY}` },
|
|
27
|
+
body: JSON.stringify({ model: "grok-beta", messages: [{ role: "user", content: prompt }] })
|
|
28
|
+
});
|
|
29
|
+
const json = await res.json();
|
|
30
|
+
return json.choices?.[0]?.message?.content || "";
|
|
31
|
+
};
|
|
32
|
+
var upload = () => {
|
|
33
|
+
return async (req) => {
|
|
34
|
+
const form = await req.formData();
|
|
35
|
+
const file = form.get("file");
|
|
36
|
+
return { name: file.name, size: file.size };
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
export {
|
|
40
|
+
upload,
|
|
41
|
+
email,
|
|
42
|
+
cache,
|
|
43
|
+
ai
|
|
44
|
+
};
|
package/dist/middleware.js
CHANGED
|
@@ -1,159 +1,66 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/middleware.ts
|
|
3
|
-
var
|
|
4
|
-
var cors = (options) => {
|
|
5
|
-
const origin = options?.origin || "*";
|
|
6
|
-
const methods = options?.methods || "GET,POST,PUT,DELETE,PATCH,OPTIONS";
|
|
7
|
-
const headers = options?.headers || "Content-Type,Authorization";
|
|
8
|
-
const credentials = options?.credentials || false;
|
|
3
|
+
var cors = (origin = "*") => {
|
|
9
4
|
return async (req, next) => {
|
|
10
|
-
if (req[MIDDLEWARE_EXECUTED]?.cors) {
|
|
11
|
-
return await next();
|
|
12
|
-
}
|
|
13
|
-
if (!req[MIDDLEWARE_EXECUTED]) {
|
|
14
|
-
req[MIDDLEWARE_EXECUTED] = {};
|
|
15
|
-
}
|
|
16
|
-
req[MIDDLEWARE_EXECUTED].cors = true;
|
|
17
5
|
if (req.method === "OPTIONS") {
|
|
18
6
|
return new Response(null, {
|
|
19
7
|
status: 204,
|
|
20
8
|
headers: {
|
|
21
9
|
"Access-Control-Allow-Origin": origin,
|
|
22
|
-
"Access-Control-Allow-Methods":
|
|
23
|
-
"Access-Control-Allow-Headers":
|
|
24
|
-
...credentials ? { "Access-Control-Allow-Credentials": "true" } : {}
|
|
10
|
+
"Access-Control-Allow-Methods": "GET,POST,PUT,DELETE,PATCH,OPTIONS",
|
|
11
|
+
"Access-Control-Allow-Headers": "Content-Type,Authorization"
|
|
25
12
|
}
|
|
26
13
|
});
|
|
27
14
|
}
|
|
28
15
|
const res = await next();
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const newHeaders = new Headers(res.headers);
|
|
32
|
-
newHeaders.set("Access-Control-Allow-Origin", origin);
|
|
33
|
-
if (credentials)
|
|
34
|
-
newHeaders.set("Access-Control-Allow-Credentials", "true");
|
|
35
|
-
return new Response(res.body, {
|
|
36
|
-
status: res.status,
|
|
37
|
-
statusText: res.statusText,
|
|
38
|
-
headers: newHeaders
|
|
39
|
-
});
|
|
16
|
+
res?.headers.set("Access-Control-Allow-Origin", origin);
|
|
17
|
+
return res;
|
|
40
18
|
};
|
|
41
19
|
};
|
|
42
|
-
var logger = (
|
|
43
|
-
const format = options?.format || "dev";
|
|
44
|
-
const colors = options?.colors !== false;
|
|
45
|
-
const colorize = (code, text) => {
|
|
46
|
-
if (!colors)
|
|
47
|
-
return text;
|
|
48
|
-
if (code >= 500)
|
|
49
|
-
return `\x1B[31m${text}\x1B[0m`;
|
|
50
|
-
if (code >= 400)
|
|
51
|
-
return `\x1B[33m${text}\x1B[0m`;
|
|
52
|
-
if (code >= 300)
|
|
53
|
-
return `\x1B[36m${text}\x1B[0m`;
|
|
54
|
-
if (code >= 200)
|
|
55
|
-
return `\x1B[32m${text}\x1B[0m`;
|
|
56
|
-
return text;
|
|
57
|
-
};
|
|
20
|
+
var logger = () => {
|
|
58
21
|
return async (req, next) => {
|
|
59
|
-
if (req[MIDDLEWARE_EXECUTED]?.logger) {
|
|
60
|
-
return await next();
|
|
61
|
-
}
|
|
62
|
-
if (!req[MIDDLEWARE_EXECUTED]) {
|
|
63
|
-
req[MIDDLEWARE_EXECUTED] = {};
|
|
64
|
-
}
|
|
65
|
-
req[MIDDLEWARE_EXECUTED].logger = true;
|
|
66
22
|
const start = Date.now();
|
|
67
|
-
const pathname = new URL(req.url).pathname;
|
|
68
23
|
const res = await next();
|
|
69
|
-
|
|
70
|
-
return res;
|
|
71
|
-
const duration = Date.now() - start;
|
|
72
|
-
const status = res.status;
|
|
73
|
-
if (format === "dev") {
|
|
74
|
-
console.log(`${colorize(status, req.method)} ${pathname} ${colorize(status, String(status))} ${duration}ms`);
|
|
75
|
-
} else if (format === "tiny") {
|
|
76
|
-
console.log(`${req.method} ${pathname} ${status} - ${duration}ms`);
|
|
77
|
-
} else {
|
|
78
|
-
const date = new Date().toISOString();
|
|
79
|
-
console.log(`[${date}] ${req.method} ${pathname} ${status} ${duration}ms`);
|
|
80
|
-
}
|
|
24
|
+
console.log(`${req.method} ${new URL(req.url).pathname} ${res?.status} ${Date.now() - start}ms`);
|
|
81
25
|
return res;
|
|
82
26
|
};
|
|
83
27
|
};
|
|
84
|
-
var
|
|
85
|
-
const store = new Map;
|
|
86
|
-
setInterval(() => {
|
|
87
|
-
const now = Date.now();
|
|
88
|
-
for (const [key, record] of store.entries()) {
|
|
89
|
-
if (now > record.resetAt) {
|
|
90
|
-
store.delete(key);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}, options.window * 1000);
|
|
28
|
+
var jwt = (secret) => {
|
|
94
29
|
return async (req, next) => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
req[MIDDLEWARE_EXECUTED].rateLimit = true;
|
|
102
|
-
const key = options.keyGenerator ? options.keyGenerator(req) : req.headers.get("x-forwarded-for") || req.headers.get("x-real-ip") || "unknown";
|
|
103
|
-
const now = Date.now();
|
|
104
|
-
const windowMs = options.window * 1000;
|
|
105
|
-
let record = store.get(key);
|
|
106
|
-
if (!record || now > record.resetAt) {
|
|
107
|
-
record = { count: 1, resetAt: now + windowMs };
|
|
108
|
-
store.set(key, record);
|
|
109
|
-
return await next();
|
|
30
|
+
const auth = req.headers.get("authorization");
|
|
31
|
+
if (auth?.startsWith("Bearer ")) {
|
|
32
|
+
try {
|
|
33
|
+
req.user = JSON.parse(atob(auth.slice(7).split(".")[1]));
|
|
34
|
+
} catch {}
|
|
110
35
|
}
|
|
111
|
-
|
|
112
|
-
const retryAfter = Math.ceil((record.resetAt - now) / 1000);
|
|
113
|
-
return new Response(JSON.stringify({
|
|
114
|
-
error: options.message || "Too many requests",
|
|
115
|
-
retryAfter
|
|
116
|
-
}), {
|
|
117
|
-
status: 429,
|
|
118
|
-
headers: {
|
|
119
|
-
"Content-Type": "application/json",
|
|
120
|
-
"Retry-After": String(retryAfter)
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
record.count++;
|
|
125
|
-
return await next();
|
|
36
|
+
return next();
|
|
126
37
|
};
|
|
127
38
|
};
|
|
128
|
-
var
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
39
|
+
var rateLimit = (max, window = 60) => {
|
|
40
|
+
const store = {};
|
|
41
|
+
return async (req, next) => {
|
|
42
|
+
const ip = req.headers.get("x-forwarded-for") || "unknown";
|
|
43
|
+
const key = `${ip}:${Math.floor(Date.now() / (window * 1000))}`;
|
|
44
|
+
store[key] = (store[key] || 0) + 1;
|
|
45
|
+
if (store[key] > max)
|
|
46
|
+
return new Response("Too many requests", { status: 429 });
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
var validate = (schema) => {
|
|
132
51
|
return async (req, next) => {
|
|
133
|
-
const url = new URL(req.url);
|
|
134
|
-
let filepath = url.pathname;
|
|
135
|
-
if (filepath.includes("..")) {
|
|
136
|
-
return new Response("Forbidden", { status: 403 });
|
|
137
|
-
}
|
|
138
|
-
if (dotfiles === "deny" && filepath.split("/").some((part) => part.startsWith("."))) {
|
|
139
|
-
return new Response("Forbidden", { status: 403 });
|
|
140
|
-
}
|
|
141
52
|
try {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (await indexFile.exists()) {
|
|
148
|
-
return new Response(indexFile);
|
|
149
|
-
}
|
|
150
|
-
} catch (err) {}
|
|
151
|
-
return await next();
|
|
53
|
+
req.body = schema.parse(req.body);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
return new Response(JSON.stringify({ error: "Invalid", details: e.errors }), { status: 400 });
|
|
56
|
+
}
|
|
57
|
+
return next();
|
|
152
58
|
};
|
|
153
59
|
};
|
|
154
60
|
export {
|
|
155
|
-
|
|
61
|
+
validate,
|
|
156
62
|
rateLimit,
|
|
157
63
|
logger,
|
|
64
|
+
jwt,
|
|
158
65
|
cors
|
|
159
66
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/scheduler.ts
|
|
3
|
+
var cron = (pattern, task) => {
|
|
4
|
+
const parts = pattern.trim().split(/\s+/);
|
|
5
|
+
const [minute, hour, day, month, dow] = parts;
|
|
6
|
+
console.log(`CRON REGISTERED: ${pattern} \u2192 ${task.toString().slice(0, 50)}...`);
|
|
7
|
+
const check = () => {
|
|
8
|
+
const now = new Date;
|
|
9
|
+
const m = now.getMinutes();
|
|
10
|
+
const h = now.getHours();
|
|
11
|
+
const matchMinute = minute === "*" ? true : minute.includes("/") ? m % parseInt(minute.split("/")[1]) === 0 : minute.includes(",") ? minute.split(",").map(Number).includes(m) : m === parseInt(minute);
|
|
12
|
+
const matchHour = hour === "*" ? true : hour.includes("/") ? h % parseInt(hour.split("/")[1]) === 0 : h === parseInt(hour);
|
|
13
|
+
if (matchMinute && matchHour) {
|
|
14
|
+
console.log(`CRON TRIGGERED: ${pattern} @ ${now.toLocaleTimeString()}`);
|
|
15
|
+
try {
|
|
16
|
+
task();
|
|
17
|
+
} catch (e) {
|
|
18
|
+
console.error("CRON ERROR:", e);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
check();
|
|
23
|
+
setInterval(check, 60000);
|
|
24
|
+
};
|
|
25
|
+
var openapi = (info) => {
|
|
26
|
+
return { openapi: "3.0.0", info, paths: {} };
|
|
27
|
+
};
|
|
28
|
+
export {
|
|
29
|
+
openapi,
|
|
30
|
+
cron
|
|
31
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "princejs",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "An easy and fast backend framework — by a 13yo developer, for developers.",
|
|
5
5
|
"main": "dist/prince.js",
|
|
6
6
|
"types": "dist/prince.d.ts",
|
|
@@ -17,9 +17,13 @@
|
|
|
17
17
|
"import": "./dist/middleware.js",
|
|
18
18
|
"types": "./dist/middleware.d.ts"
|
|
19
19
|
},
|
|
20
|
-
"./
|
|
21
|
-
"import": "./dist/
|
|
22
|
-
"types": "./dist/
|
|
20
|
+
"./helpers": {
|
|
21
|
+
"import": "./dist/helpers.js",
|
|
22
|
+
"types": "./dist/helpers.d.ts"
|
|
23
|
+
},
|
|
24
|
+
"./scheduler": {
|
|
25
|
+
"import": "./dist/scheduler.js",
|
|
26
|
+
"types": "./dist/scheduler.d.ts"
|
|
23
27
|
}
|
|
24
28
|
},
|
|
25
29
|
"files": [
|
|
@@ -38,7 +42,9 @@
|
|
|
38
42
|
"server",
|
|
39
43
|
"typescript",
|
|
40
44
|
"websocket",
|
|
41
|
-
"middleware"
|
|
45
|
+
"middleware",
|
|
46
|
+
"scheduler",
|
|
47
|
+
"fast"
|
|
42
48
|
],
|
|
43
49
|
"author": "Matthew Michael (MatthewTheCoder1218)",
|
|
44
50
|
"license": "MIT",
|
|
@@ -56,10 +62,12 @@
|
|
|
56
62
|
"devDependencies": {
|
|
57
63
|
"@types/bun": "^1.3.2",
|
|
58
64
|
"bun-types": "latest",
|
|
59
|
-
"typescript": "^5.9.3"
|
|
65
|
+
"typescript": "^5.9.3",
|
|
66
|
+
"fast-jwt": "^5.0.0",
|
|
67
|
+
"zod": "^4.1.12"
|
|
60
68
|
},
|
|
61
69
|
"peerDependencies": {
|
|
62
|
-
"zod": "^4.
|
|
70
|
+
"zod": "^4.1.12"
|
|
63
71
|
},
|
|
64
72
|
"peerDependenciesMeta": {
|
|
65
73
|
"zod": {
|
|
@@ -67,6 +75,6 @@
|
|
|
67
75
|
}
|
|
68
76
|
},
|
|
69
77
|
"scripts": {
|
|
70
|
-
"build": "bun build src/prince.ts --outdir dist --target bun && bun build src/middleware.ts --outdir dist --target bun && bun build src/
|
|
78
|
+
"build": "bun build src/prince.ts --outdir dist --target bun && bun build src/middleware.ts --outdir dist --target bun && bun build src/helpers.ts --outdir dist --target bun && bun build bin/create.ts --outdir dist --target bun --format esm && bun build src/scheduler.ts --outdir dist --target bun && bun build src/helpers --outdir dist --target bun"
|
|
71
79
|
}
|
|
72
80
|
}
|