princejs 1.6.2 → 1.6.6

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 CHANGED
@@ -127,13 +127,12 @@ app
127
127
  ## New Tree-Shakable Features
128
128
 
129
129
  ```ts
130
- import { cache, email, ai, upload } from "princejs/helpers";
130
+ import { cache, email, upload } from "princejs/helpers";
131
131
  import { cron, openapi } from "princejs/scheduler";
132
132
  ```
133
133
 
134
134
  * `cache(60)(handler)` — In-memory cache
135
135
  * `email(to, subject, html)` — Resend.com
136
- * `ai(prompt)` — Grok / OpenAI / Hugging Face
137
136
  * `upload()` — 1-line file upload
138
137
  * `cron("*/2 * * * *", task)` — Cron support
139
138
  * `openapi({ title, version })` — Auto docs
@@ -0,0 +1,8 @@
1
+ import type { PrinceRequest } from "./prince";
2
+ export declare const cache: (ttl: number) => (handler: any) => (req: PrinceRequest) => Promise<any>;
3
+ export declare const email: (to: string, subject: string, html: string) => Promise<void>;
4
+ export declare const upload: () => (req: PrinceRequest) => Promise<{
5
+ name: string;
6
+ size: number;
7
+ }>;
8
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG9C,eAAO,MAAM,KAAK,GAAI,KAAK,MAAM,MAEvB,SAAS,GAAG,MAAY,KAAK,aAAa,iBASnD,CAAC;AAGF,eAAO,MAAM,KAAK,GAAU,IAAI,MAAM,EAAE,SAAS,MAAM,EAAE,MAAM,MAAM,kBAMpE,CAAC;AAGF,eAAO,MAAM,MAAM,SACH,KAAK,aAAa;;;EAKjC,CAAC"}
package/dist/helpers.js CHANGED
@@ -20,47 +20,6 @@ var email = async (to, subject, html) => {
20
20
  body: JSON.stringify({ from: "no-reply@princejs.dev", to, subject, html })
21
21
  });
22
22
  };
23
- var DEFAULTS = {
24
- openai: { url: "https://api.openai.com/v1/chat/completions", model: "gpt-4o-mini" },
25
- grok: { url: "https://api.x.ai/v1/chat/completions", model: "grok-beta" },
26
- huggingface: { url: "" }
27
- };
28
- var ai = async (prompt, opts = {}) => {
29
- const provider = opts.provider || "openai";
30
- const config = DEFAULTS[provider];
31
- if (provider === "huggingface" && !opts.model) {
32
- throw new Error("Hugging Face requires 'model' option (e.g. meta-llama/Llama-3.3-70B-Instruct)");
33
- }
34
- const apiKey = opts.apiKey || Bun.env[`${provider.toUpperCase()}_API_KEY`];
35
- if (!apiKey) {
36
- throw new Error(`Missing ${provider.toUpperCase()}_API_KEY in environment`);
37
- }
38
- const url = provider === "huggingface" ? `https://api-inference.huggingface.co/models/${opts.model}` : config.url;
39
- const headers = {
40
- "Content-Type": "application/json",
41
- Authorization: `Bearer ${apiKey}`
42
- };
43
- const body = provider === "huggingface" ? { inputs: prompt, parameters: { temperature: opts.temperature ?? 0.7 } } : {
44
- model: opts.model || config.model,
45
- messages: [{ role: "user", content: prompt }],
46
- temperature: opts.temperature ?? 0.7,
47
- max_tokens: 1024
48
- };
49
- const res = await fetch(url, {
50
- method: "POST",
51
- headers,
52
- body: JSON.stringify(body)
53
- });
54
- if (!res.ok) {
55
- const err = await res.text();
56
- throw new Error(`${provider} API error ${res.status}: ${err}`);
57
- }
58
- const data = await res.json();
59
- if (provider === "huggingface") {
60
- return (data[0]?.generated_text || "").trim();
61
- }
62
- return data.choices?.[0]?.message?.content?.trim() || "No response";
63
- };
64
23
  var upload = () => {
65
24
  return async (req) => {
66
25
  const form = await req.formData();
@@ -71,6 +30,5 @@ var upload = () => {
71
30
  export {
72
31
  upload,
73
32
  email,
74
- cache,
75
- ai
33
+ cache
76
34
  };
@@ -0,0 +1,2 @@
1
+ export * from './prince';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { PrinceRequest } from "./prince";
2
+ import { z } from "zod";
3
+ type Next = () => Promise<Response | undefined>;
4
+ export declare const cors: (origin?: string) => (req: PrinceRequest, next: Next) => Promise<Response | undefined>;
5
+ export declare const logger: () => (req: PrinceRequest, next: Next) => Promise<Response | undefined>;
6
+ export declare const jwt: (secret: string) => (req: PrinceRequest, next: Next) => Promise<Response | undefined>;
7
+ export declare const rateLimit: (max: number, window?: number) => (req: PrinceRequest, next: Next) => Promise<Response | undefined>;
8
+ export declare const validate: <T>(schema: z.ZodSchema<T>) => (req: PrinceRequest, next: Next) => Promise<Response | undefined>;
9
+ export {};
10
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,KAAK,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;AAGhD,eAAO,MAAM,IAAI,GAAI,eAAY,MACjB,KAAK,aAAa,EAAE,MAAM,IAAI,kCAe7C,CAAC;AAGF,eAAO,MAAM,MAAM,SACH,KAAK,aAAa,EAAE,MAAM,IAAI,kCAM7C,CAAC;AAGF,eAAO,MAAM,GAAG,GAAI,QAAQ,MAAM,MAClB,KAAK,aAAa,EAAE,MAAM,IAAI,kCAS7C,CAAC;AAGF,eAAO,MAAM,SAAS,GAAI,KAAK,MAAM,EAAE,eAAW,MAElC,KAAK,aAAa,EAAE,MAAM,IAAI,kCAO7C,CAAC;AAGF,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAClC,KAAK,aAAa,EAAE,MAAM,IAAI,kCAQ7C,CAAC"}
@@ -0,0 +1,65 @@
1
+ type Next = () => Promise<Response>;
2
+ type Middleware = (req: PrinceRequest, next: Next) => Promise<Response | undefined> | Response | undefined;
3
+ type HandlerResult = Response | Record<string, any> | string | Uint8Array;
4
+ export interface PrinceRequest extends Request {
5
+ body: BodyInit | null;
6
+ json(): Promise<any>;
7
+ text(): Promise<string>;
8
+ formData(): Promise<FormData>;
9
+ arrayBuffer(): Promise<ArrayBuffer>;
10
+ user?: any;
11
+ params?: Record<string, string>;
12
+ query?: URLSearchParams;
13
+ [key: string]: any;
14
+ }
15
+ export interface WebSocketHandler {
16
+ open?: (ws: any) => void;
17
+ message?: (ws: any, msg: string | Buffer) => void;
18
+ close?: (ws: any, code?: number, reason?: string) => void;
19
+ drain?: (ws: any) => void;
20
+ }
21
+ type RouteHandler = (req: PrinceRequest) => Promise<HandlerResult> | HandlerResult;
22
+ declare class ResponseBuilder {
23
+ private _status;
24
+ private _headers;
25
+ private _body;
26
+ status(code: number): this;
27
+ header(key: string, value: string): this;
28
+ json(data: any): Response;
29
+ text(data: string): Response;
30
+ html(data: string): Response;
31
+ redirect(url: string, status?: number): Response;
32
+ stream(cb: (push: (chunk: string) => void, close: () => void) => void): Response;
33
+ build(): Response;
34
+ }
35
+ export declare class Prince {
36
+ private devMode;
37
+ private rawRoutes;
38
+ private middlewares;
39
+ private errorHandler?;
40
+ private wsRoutes;
41
+ private openapiData;
42
+ private router;
43
+ constructor(devMode?: boolean);
44
+ use(mw: Middleware): this;
45
+ error(fn: (err: any, req: PrinceRequest) => Response): this;
46
+ json(data: any, status?: number): Response;
47
+ response(): ResponseBuilder;
48
+ ws(path: string, options: Partial<WebSocketHandler>): this;
49
+ openapi(path?: string): this;
50
+ get(path: string, handler: RouteHandler): this;
51
+ post(path: string, handler: RouteHandler): this;
52
+ put(path: string, handler: RouteHandler): this;
53
+ delete(path: string, handler: RouteHandler): this;
54
+ patch(path: string, handler: RouteHandler): this;
55
+ private add;
56
+ private parseUrl;
57
+ private parseBody;
58
+ private buildRouter;
59
+ private compilePipeline;
60
+ handleFetch(req: Request): Promise<Response>;
61
+ listen(port?: number): void;
62
+ }
63
+ export declare const prince: (dev?: boolean) => Prince;
64
+ export {};
65
+ //# sourceMappingURL=prince.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prince.d.ts","sourceRoot":"","sources":["../src/prince.ts"],"names":[],"mappings":"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"}
package/dist/prince.js CHANGED
@@ -260,16 +260,16 @@ class Prince {
260
260
  },
261
261
  websocket: {
262
262
  open(ws) {
263
- ws.data.ws?.open?.(ws);
263
+ ws.data?.ws?.open?.(ws);
264
264
  },
265
265
  message(ws, msg) {
266
- ws.data.ws?.message?.(ws, msg);
266
+ ws.data?.ws?.message?.(ws, msg);
267
267
  },
268
268
  close(ws, code, reason) {
269
- ws.data.ws?.close?.(ws, code, reason);
269
+ ws.data?.ws?.close?.(ws, code, reason);
270
270
  },
271
271
  drain(ws) {
272
- ws.data.ws?.drain?.(ws);
272
+ ws.data?.ws?.drain?.(ws);
273
273
  }
274
274
  }
275
275
  });
@@ -0,0 +1,13 @@
1
+ export declare const cron: (pattern: string, task: () => void) => void;
2
+ export declare const openapi: (info: {
3
+ title: string;
4
+ version: string;
5
+ }) => {
6
+ openapi: string;
7
+ info: {
8
+ title: string;
9
+ version: string;
10
+ };
11
+ paths: {};
12
+ };
13
+ //# sourceMappingURL=scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,IAAI,GAAI,SAAS,MAAM,EAAE,MAAM,MAAM,IAAI,SAiCrD,CAAC;AAGF,eAAO,MAAM,OAAO,GAAI,MAAM;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;;;eAAzB,MAAM;iBAAW,MAAM;;;CAE7D,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "princejs",
3
- "version": "1.6.2",
3
+ "version": "1.6.6",
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",
@@ -62,8 +62,8 @@
62
62
  "devDependencies": {
63
63
  "@types/bun": "^1.3.2",
64
64
  "bun-types": "latest",
65
- "typescript": "^5.9.3",
66
65
  "fast-jwt": "^5.0.0",
66
+ "typescript": "^5.9.3",
67
67
  "zod": "^4.1.12"
68
68
  },
69
69
  "peerDependencies": {
@@ -75,6 +75,8 @@
75
75
  }
76
76
  },
77
77
  "scripts": {
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"
78
+ "build:js": "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 src/scheduler.ts --outdir dist --target bun && bun build bin/create.ts --outdir dist --target bun --format esm",
79
+ "build:types": "tsc --emitDeclarationOnly --skipLibCheck",
80
+ "build": "bun run build:js && bun run build:types"
79
81
  }
80
82
  }
package/dist/index.js DELETED
@@ -1,260 +0,0 @@
1
- // @bun
2
- // src/prince.ts
3
- class TrieNode {
4
- children = Object.create(null);
5
- paramChild;
6
- wildcardChild;
7
- catchAllChild;
8
- handlers = null;
9
- }
10
-
11
- class ResponseBuilder {
12
- _status = 200;
13
- _headers = {};
14
- _body = null;
15
- status(code) {
16
- this._status = code;
17
- return this;
18
- }
19
- header(key, value) {
20
- this._headers[key] = value;
21
- return this;
22
- }
23
- json(data) {
24
- this._headers["Content-Type"] = "application/json";
25
- this._body = JSON.stringify(data);
26
- return this.build();
27
- }
28
- text(data) {
29
- this._headers["Content-Type"] = "text/plain";
30
- this._body = data;
31
- return this.build();
32
- }
33
- html(data) {
34
- this._headers["Content-Type"] = "text/html";
35
- this._body = data;
36
- return this.build();
37
- }
38
- redirect(url, status = 302) {
39
- this._status = status;
40
- this._headers["Location"] = url;
41
- return this.build();
42
- }
43
- stream(cb) {
44
- const encoder = new TextEncoder;
45
- const stream = new ReadableStream({
46
- start(controller) {
47
- cb((chunk) => controller.enqueue(encoder.encode(chunk)), () => controller.close());
48
- }
49
- });
50
- return new Response(stream, { status: this._status, headers: this._headers });
51
- }
52
- build() {
53
- return new Response(this._body, { status: this._status, headers: this._headers });
54
- }
55
- }
56
-
57
- class Prince {
58
- devMode;
59
- rawRoutes = [];
60
- middlewares = [];
61
- errorHandler;
62
- wsRoutes = {};
63
- openapiData = null;
64
- constructor(devMode = false) {
65
- this.devMode = devMode;
66
- }
67
- use(mw) {
68
- this.middlewares.push(mw);
69
- return this;
70
- }
71
- error(fn) {
72
- this.errorHandler = fn;
73
- return this;
74
- }
75
- json(data, status = 200) {
76
- return new Response(JSON.stringify(data), {
77
- status,
78
- headers: { "Content-Type": "application/json" }
79
- });
80
- }
81
- response() {
82
- return new ResponseBuilder;
83
- }
84
- ws(path, options) {
85
- this.wsRoutes[path] = options;
86
- return this;
87
- }
88
- openapi(path = "/docs") {
89
- const paths = {};
90
- for (const route of this.rawRoutes) {
91
- paths[route.path] ??= {};
92
- paths[route.path][route.method.toLowerCase()] = {
93
- summary: "",
94
- responses: { 200: { description: "OK" } }
95
- };
96
- }
97
- this.openapiData = {
98
- openapi: "3.1.0",
99
- info: { title: "PrinceJS API", version: "1.0.0" },
100
- paths
101
- };
102
- this.get(path, () => this.openapiData);
103
- return this;
104
- }
105
- get(path, handler) {
106
- return this.add("GET", path, handler);
107
- }
108
- post(path, handler) {
109
- return this.add("POST", path, handler);
110
- }
111
- put(path, handler) {
112
- return this.add("PUT", path, handler);
113
- }
114
- delete(path, handler) {
115
- return this.add("DELETE", path, handler);
116
- }
117
- patch(path, handler) {
118
- return this.add("PATCH", path, handler);
119
- }
120
- add(method, path, handler) {
121
- if (!path.startsWith("/"))
122
- path = "/" + path;
123
- if (path !== "/" && path.endsWith("/"))
124
- path = path.slice(0, -1);
125
- const parts = path === "/" ? [""] : path.split("/").slice(1);
126
- this.rawRoutes.push({ method, path, parts, handler });
127
- return this;
128
- }
129
- parseUrl(req) {
130
- const url = new URL(req.url);
131
- const query = {};
132
- for (const [k, v] of url.searchParams.entries())
133
- query[k] = v;
134
- return { pathname: url.pathname, query };
135
- }
136
- async parseBody(req) {
137
- const ct = req.headers.get("content-type") || "";
138
- if (ct.includes("application/json"))
139
- return await req.json();
140
- if (ct.includes("application/x-www-form-urlencoded"))
141
- return Object.fromEntries(new URLSearchParams(await req.text()).entries());
142
- if (ct.startsWith("multipart/form-data")) {
143
- const fd = await req.formData();
144
- const files = {};
145
- const fields = {};
146
- for (const [k, v] of fd.entries()) {
147
- if (v instanceof File)
148
- files[k] = v;
149
- else
150
- fields[k] = v;
151
- }
152
- return { files, fields };
153
- }
154
- if (ct.startsWith("text/"))
155
- return await req.text();
156
- return null;
157
- }
158
- buildRouter() {
159
- const root = new TrieNode;
160
- for (const r of this.rawRoutes) {
161
- let node = root;
162
- if (r.parts.length === 1 && r.parts[0] === "") {
163
- node.handlers ??= {};
164
- node.handlers[r.method] = r.handler;
165
- continue;
166
- }
167
- for (const part of r.parts) {
168
- if (part.startsWith(":")) {
169
- const name = part.slice(1);
170
- node.paramChild ??= { name, node: new TrieNode };
171
- node = node.paramChild.node;
172
- } else {
173
- node.children[part] ??= new TrieNode;
174
- node = node.children[part];
175
- }
176
- }
177
- node.handlers ??= {};
178
- node.handlers[r.method] = r.handler;
179
- }
180
- return root;
181
- }
182
- compilePipeline(handler) {
183
- return async (req, params, query) => {
184
- req.params = params;
185
- req.query = query;
186
- let i = 0;
187
- const next = async () => {
188
- if (i < this.middlewares.length) {
189
- return await this.middlewares[i++](req, next) ?? new Response("");
190
- }
191
- if (["POST", "PUT", "PATCH"].includes(req.method))
192
- req.body = await this.parseBody(req);
193
- const res = await handler(req);
194
- if (res instanceof Response)
195
- return res;
196
- if (typeof res === "string")
197
- return new Response(res);
198
- return this.json(res);
199
- };
200
- return next();
201
- };
202
- }
203
- async handleFetch(req) {
204
- const { pathname, query } = this.parseUrl(req);
205
- const r = req;
206
- const segments = pathname === "/" ? [] : pathname.slice(1).split("/");
207
- let node = this.buildRouter();
208
- let params = {};
209
- for (const seg of segments) {
210
- if (node.children[seg])
211
- node = node.children[seg];
212
- else if (node.paramChild) {
213
- params[node.paramChild.name] = seg;
214
- node = node.paramChild.node;
215
- } else
216
- return this.json({ error: "Not Found" }, 404);
217
- }
218
- const handler = node.handlers?.[req.method];
219
- if (!handler)
220
- return this.json({ error: "Method Not Allowed" }, 405);
221
- const pipeline = this.compilePipeline(handler);
222
- return pipeline(r, params, query);
223
- }
224
- listen(port = 3000) {
225
- const self = this;
226
- Bun.serve({
227
- port,
228
- fetch(req, server) {
229
- const { pathname } = new URL(req.url);
230
- const ws = self.wsRoutes[pathname];
231
- if (ws) {
232
- server.upgrade(req, { data: { ws } });
233
- return;
234
- }
235
- return self.handleFetch(req).catch((err) => {
236
- if (self.errorHandler)
237
- return self.errorHandler(err, req);
238
- return self.json({ error: String(err) }, 500);
239
- });
240
- },
241
- websocket: {
242
- open(ws) {
243
- ws.data.ws?.open?.(ws);
244
- },
245
- message(ws, msg) {
246
- ws.data.ws?.message?.(ws, msg);
247
- },
248
- close(ws) {
249
- ws.data.ws?.close?.(ws);
250
- }
251
- }
252
- });
253
- console.log(`\uD83D\uDE80 PrinceJS running http://localhost:${port}`);
254
- }
255
- }
256
- var prince = (dev = false) => new Prince(dev);
257
- export {
258
- prince,
259
- Prince
260
- };
@@ -1,27 +0,0 @@
1
- // @bun
2
- // src/validation.ts
3
- var validate = (schema, source = "body") => {
4
- return async (req, next) => {
5
- try {
6
- const data = source === "body" ? req.body : source === "query" ? req.query : req.params;
7
- const validated = schema.parse(data);
8
- req[`validated${source.charAt(0).toUpperCase() + source.slice(1)}`] = validated;
9
- if (next) {
10
- const result = await next();
11
- return result;
12
- }
13
- return;
14
- } catch (err) {
15
- return new Response(JSON.stringify({
16
- error: "Validation failed",
17
- details: err.errors || err.message
18
- }), {
19
- status: 400,
20
- headers: { "Content-Type": "application/json" }
21
- });
22
- }
23
- };
24
- };
25
- export {
26
- validate
27
- };