princejs 1.9.1 → 1.9.3

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
@@ -53,18 +53,18 @@ app
53
53
  })));
54
54
  ```
55
55
 
56
- ### Middleware
56
+ ### Middleware
57
57
 
58
58
  * CORS
59
59
  * Logger
60
60
  * Rate Limiting
61
61
  * Static Files
62
62
 
63
- ### Validation (Zod)
63
+ ### Validation (Zod)
64
64
 
65
- ### File Uploads
65
+ ### File Uploads
66
66
 
67
- ### Response Builder
67
+ ### Response Builder
68
68
 
69
69
  ### WebSocket Support
70
70
 
@@ -105,8 +105,89 @@ const app = prince();
105
105
  app.plugin(usersPlugin, { prefix: "/api" });
106
106
  ```
107
107
 
108
+ ### OpenAPI + Scalar Docs
109
+
110
+ Auto-generate an OpenAPI 3.0 spec and serve a beautiful [Scalar](https://scalar.com) API reference UI — all from a single `app.openapi()` call. Routes, validation, and docs stay in sync automatically.
111
+
112
+ ```ts
113
+ import { prince } from "princejs";
114
+ import { z } from "zod";
115
+
116
+ const app = prince();
117
+
118
+ const api = app.openapi({ title: "My API", version: "1.0.0" }, "/docs", { theme: "moon" });
119
+
120
+ api.route("GET", "/users/:id", {
121
+ summary: "Get user by ID",
122
+ tags: ["users"],
123
+ schema: {
124
+ response: z.object({ id: z.string(), name: z.string() }),
125
+ },
126
+ }, (req) => ({ id: req.params!.id, name: "Alice" }));
127
+
128
+ api.route("POST", "/users", {
129
+ summary: "Create user",
130
+ tags: ["users"],
131
+ schema: {
132
+ body: z.object({ name: z.string().min(2), email: z.string().email() }),
133
+ response: z.object({ id: z.string(), name: z.string(), email: z.string() }),
134
+ },
135
+ }, (req) => ({ id: crypto.randomUUID(), ...req.parsedBody }));
136
+
137
+ app.listen(3000);
138
+ // → GET /docs Scalar UI
139
+ // → GET /docs.json Raw OpenAPI JSON
140
+ ```
141
+
142
+ **`api.route()` does three things at once:**
143
+ - Registers the route on PrinceJS (same as `app.get()` / `app.post()`)
144
+ - Auto-wires `validate(schema.body)` middleware — no separate import needed
145
+ - Writes the full OpenAPI spec entry including path params, request body, query params, and response schema
146
+
147
+ | `schema` key | Runtime effect | Scalar docs |
148
+ |---|---|---|
149
+ | `body` | ✅ Validates & strips via `validate()` | ✅ requestBody model |
150
+ | `query` | ❌ docs only | ✅ typed query params |
151
+ | `response` | ❌ docs only | ✅ 200 response model |
152
+
153
+ Routes registered with `app.get()` / `app.post()` directly never appear in the docs — useful for internal health checks, webhooks, and admin endpoints.
154
+
155
+ **Available themes:** `default` · `moon` · `purple` · `solarized` · `bluePlanet` · `deepSpace` · `saturn` · `kepler` · `mars`
156
+
108
157
  ### Database (SQLite)
109
158
 
159
+ ### End to End Type-Safety
160
+
161
+ PrinceJS supports contract-based type safety to sync your frontend and backend seamlessly. By defining an API contract, your client receives full TypeScript autocompletion and type-checking for routes, parameters, and responses.
162
+
163
+
164
+ **Define Your Contract**
165
+
166
+ ```ts
167
+ type ApiContract = {
168
+ "GET /users/:id": {
169
+ params: { id: string };
170
+ response: { id: string; name: string };
171
+ };
172
+ "POST /users": {
173
+ body: { name: string };
174
+ response: { id: string; ok: boolean };
175
+ };
176
+ };
177
+ ```
178
+
179
+ **Initialize The Client**
180
+
181
+ ```ts
182
+ import { createClient } from "princejs/client";
183
+
184
+ const client = createClient<ApiContract>("http://localhost:3000");
185
+
186
+ // Fully typed request and response
187
+ const user = await client.get("/users/:id", { params: { id: "42" } });
188
+ console.log(user.name); // Typed as string
189
+ ```
190
+
110
191
  ---
111
192
 
112
193
  ## Deploy (Vercel, Workers, Deno)
@@ -171,7 +252,7 @@ import { prince } from "princejs";
171
252
  import { cors, logger, rateLimit, auth, apiKey, jwt, session, compress, serve } from "princejs/middleware";
172
253
  import { validate } from "princejs/validation";
173
254
  import { cache, upload, sse } from "princejs/helpers";
174
- import { cron } from "princejs/scheduler";
255
+ import { cron, openapi } from "princejs/scheduler";
175
256
  import { Html, Head, Body, H1, P, render } from "princejs/jsx"
176
257
  import { db } from "princejs/db";
177
258
  import { z } from "zod";
@@ -253,6 +334,17 @@ app.get("/users", () => users.query("SELECT * FROM users"));
253
334
 
254
335
  cron("*/1 * * * *", () => console.log("PrinceJS heartbeat"));
255
336
 
337
+ // OpenAPI + Scalar
338
+ const api = app.openapi({ title: "PrinceJS App", version: "1.0.0" }, "/docs");
339
+ api.route("GET", "/items", {
340
+ summary: "List items",
341
+ tags: ["items"],
342
+ schema: {
343
+ query: z.object({ q: z.string().optional() }),
344
+ response: z.array(z.object({ id: z.string(), name: z.string() })),
345
+ },
346
+ }, () => [{ id: "1", name: "Widget" }]);
347
+
256
348
  app.listen(3000);
257
349
  ```
258
350
 
@@ -0,0 +1,95 @@
1
+ /**
2
+ * princejs/client - End-to-end type-safe API client
3
+ *
4
+ * Define an API contract shared between server and client. The client infers
5
+ * request/response types from the contract—no code generation required.
6
+ *
7
+ * @example
8
+ * // shared/api.ts - Define contract (shared or imported by both server & client)
9
+ * export type ApiContract = {
10
+ * "GET /health": { response: { ok: boolean } };
11
+ * "GET /users/:id": { params: { id: string }; response: { id: string; name: string } };
12
+ * "POST /users": { body: { name: string }; response: { id: string; name: string } };
13
+ * };
14
+ *
15
+ * @example
16
+ * // client.ts
17
+ * import { createClient } from "princejs/client";
18
+ * import type { ApiContract } from "./shared/api";
19
+ *
20
+ * const client = createClient<ApiContract>("http://localhost:3000");
21
+ *
22
+ * const health = await client.get("/health"); // { ok: boolean }
23
+ * const user = await client.get("/users/:id", { params: { id: "1" } });
24
+ * const created = await client.post("/users", { body: { name: "Alice" } });
25
+ */
26
+ /** A single route in the API contract. Key format: "METHOD /path/:param" */
27
+ export interface RouteDef {
28
+ /** Path params (e.g. { id: string } for /users/:id) */
29
+ params?: Record<string, string>;
30
+ /** Query params shape (optional, for type hints) */
31
+ query?: Record<string, string>;
32
+ /** Request body for POST/PUT/PATCH */
33
+ body?: unknown;
34
+ /** Response type (default: unknown) */
35
+ response?: unknown;
36
+ }
37
+ /** API contract: maps "METHOD /path" to route definition */
38
+ export type PrinceApiContract = Record<string, RouteDef>;
39
+ type Method = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
40
+ /** Extract route keys for a method */
41
+ type KeysForMethod<C extends PrinceApiContract, M extends Method> = Extract<keyof C, `${M} ${string}`>;
42
+ /** Path part from key "GET /users/:id" -> "/users/:id" */
43
+ type PathFromKey<K extends string> = K extends `${Method} ${infer P}` ? P : never;
44
+ /** Route def for a key */
45
+ type DefFor<C extends PrinceApiContract, K extends keyof C> = C[K] extends RouteDef ? C[K] : never;
46
+ /** Response type from route def */
47
+ type ResponseType<D> = D extends {
48
+ response?: infer R;
49
+ } ? R : unknown;
50
+ /** Params type from route def */
51
+ type ParamsType<D> = D extends {
52
+ params?: infer P;
53
+ } ? P : Record<string, never>;
54
+ /** Body type from route def */
55
+ type BodyType<D> = D extends {
56
+ body?: infer B;
57
+ } ? B : undefined;
58
+ /** Options for GET/DELETE (params, query) */
59
+ type GetOpts<D> = ParamsType<D> extends Record<string, never> ? {
60
+ params?: never;
61
+ query?: Record<string, string>;
62
+ } : {
63
+ params: ParamsType<D>;
64
+ query?: Record<string, string>;
65
+ };
66
+ /** Options for POST/PUT/PATCH */
67
+ type MutateOpts<D> = ParamsType<D> extends Record<string, never> ? {
68
+ body: BodyType<D>;
69
+ query?: Record<string, string>;
70
+ } : {
71
+ params: ParamsType<D>;
72
+ body: BodyType<D>;
73
+ query?: Record<string, string>;
74
+ };
75
+ export interface ClientOptions {
76
+ /** Base fetch init (headers, credentials, etc.) */
77
+ init?: RequestInit;
78
+ }
79
+ /**
80
+ * Create an end-to-end type-safe API client from a contract.
81
+ * Share the contract type between server and client for full type safety.
82
+ */
83
+ export declare function createClient<C extends PrinceApiContract>(baseUrl: string, options?: ClientOptions): PrinceClient<C>;
84
+ /** Typed client interface */
85
+ export interface PrinceClient<C extends PrinceApiContract> {
86
+ get<K extends KeysForMethod<C, "GET">>(path: PathFromKey<K>, opts?: GetOpts<DefFor<C, K>>): Promise<ResponseType<DefFor<C, K>>>;
87
+ post<K extends KeysForMethod<C, "POST">>(path: PathFromKey<K>, opts: MutateOpts<DefFor<C, K>>): Promise<ResponseType<DefFor<C, K>>>;
88
+ put<K extends KeysForMethod<C, "PUT">>(path: PathFromKey<K>, opts: MutateOpts<DefFor<C, K>>): Promise<ResponseType<DefFor<C, K>>>;
89
+ patch<K extends KeysForMethod<C, "PATCH">>(path: PathFromKey<K>, opts: MutateOpts<DefFor<C, K>>): Promise<ResponseType<DefFor<C, K>>>;
90
+ delete<K extends KeysForMethod<C, "DELETE">>(path: PathFromKey<K>, opts?: GetOpts<DefFor<C, K>>): Promise<ResponseType<DefFor<C, K>>>;
91
+ /** Raw fetch (untyped) for custom requests */
92
+ fetch(path: string, init?: RequestInit): Promise<Response>;
93
+ }
94
+ export {};
95
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js ADDED
@@ -0,0 +1,99 @@
1
+ // src/client.ts
2
+ function buildPath(pattern, params) {
3
+ if (!params)
4
+ return pattern;
5
+ let out = pattern;
6
+ for (const [k, v] of Object.entries(params)) {
7
+ out = out.replace(`:${k}`, encodeURIComponent(v));
8
+ }
9
+ return out;
10
+ }
11
+ function buildUrl(base, path, query) {
12
+ const url = new URL(path, base);
13
+ if (query) {
14
+ for (const [k, v] of Object.entries(query)) {
15
+ url.searchParams.set(k, v);
16
+ }
17
+ }
18
+ return url.toString();
19
+ }
20
+ async function fetchJson(url, init) {
21
+ const res = await fetch(url, {
22
+ ...init,
23
+ headers: {
24
+ "Content-Type": "application/json",
25
+ ...init?.headers
26
+ }
27
+ });
28
+ if (!res.ok) {
29
+ const text = await res.text();
30
+ throw new Error(`API error ${res.status}: ${text}`);
31
+ }
32
+ return res.json();
33
+ }
34
+ function createClient(baseUrl, options) {
35
+ const base = baseUrl.replace(/\/$/, "");
36
+ const init = options?.init ?? {};
37
+ const get = async (path, opts) => {
38
+ const def = {};
39
+ const params = opts && "params" in opts ? opts.params : undefined;
40
+ const query = opts?.query;
41
+ const builtPath = buildPath(path, params);
42
+ const url = buildUrl(base, builtPath, query);
43
+ return fetchJson(url, init);
44
+ };
45
+ const post = async (path, opts) => {
46
+ const params = opts && "params" in opts ? opts.params : undefined;
47
+ const body = opts && "body" in opts ? opts.body : undefined;
48
+ const query = opts?.query;
49
+ const builtPath = buildPath(path, params);
50
+ const url = buildUrl(base, builtPath, query);
51
+ return fetchJson(url, {
52
+ ...init,
53
+ method: "POST",
54
+ body: body !== undefined ? JSON.stringify(body) : undefined
55
+ });
56
+ };
57
+ const put = async (path, opts) => {
58
+ const params = opts && "params" in opts ? opts.params : undefined;
59
+ const body = opts && "body" in opts ? opts.body : undefined;
60
+ const query = opts?.query;
61
+ const builtPath = buildPath(path, params);
62
+ const url = buildUrl(base, builtPath, query);
63
+ return fetchJson(url, {
64
+ ...init,
65
+ method: "PUT",
66
+ body: body !== undefined ? JSON.stringify(body) : undefined
67
+ });
68
+ };
69
+ const patch = async (path, opts) => {
70
+ const params = opts && "params" in opts ? opts.params : undefined;
71
+ const body = opts && "body" in opts ? opts.body : undefined;
72
+ const query = opts?.query;
73
+ const builtPath = buildPath(path, params);
74
+ const url = buildUrl(base, builtPath, query);
75
+ return fetchJson(url, {
76
+ ...init,
77
+ method: "PATCH",
78
+ body: body !== undefined ? JSON.stringify(body) : undefined
79
+ });
80
+ };
81
+ const del = async (path, opts) => {
82
+ const params = opts && opts.params ? opts.params : undefined;
83
+ const query = opts?.query;
84
+ const builtPath = buildPath(path, params);
85
+ const url = buildUrl(base, builtPath, query);
86
+ return fetchJson(url, { ...init, method: "DELETE" });
87
+ };
88
+ return {
89
+ get,
90
+ post,
91
+ put,
92
+ patch,
93
+ delete: del,
94
+ fetch: (path, reqInit) => globalThis.fetch(`${base}${path}`, { ...init, ...reqInit })
95
+ };
96
+ }
97
+ export {
98
+ createClient
99
+ };
package/dist/prince.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { OpenAPIBuilder, ScalarOptions } from "./scheduler";
2
+ import { z } from "zod";
1
3
  type Next = () => Promise<Response>;
2
4
  type Middleware = (req: PrinceRequest, next: Next) => Promise<Response | undefined> | Response | undefined;
3
5
  type HandlerResult = Response | Record<string, any> | string | Uint8Array;
@@ -32,13 +34,30 @@ declare class ResponseBuilder {
32
34
  redirect(url: string, status?: number): Response;
33
35
  build(): Response;
34
36
  }
37
+ export interface RouteSchema {
38
+ /** Zod schema for the request body — auto-wires validate() middleware */
39
+ body?: z.ZodTypeAny;
40
+ /** Zod schema for query parameters — written into the OpenAPI spec */
41
+ query?: z.ZodObject<any>;
42
+ /** Zod schema for the response body — written into the OpenAPI spec */
43
+ response?: z.ZodTypeAny;
44
+ }
45
+ export interface RouteOperation {
46
+ summary?: string;
47
+ description?: string;
48
+ tags?: string[];
49
+ /** Attach Zod schemas to auto-generate request/response models in Scalar */
50
+ schema?: RouteSchema;
51
+ responses?: Record<string, unknown>;
52
+ [key: string]: unknown;
53
+ }
35
54
  export declare class Prince {
36
55
  private devMode;
37
56
  private rawRoutes;
38
57
  private middlewares;
39
58
  private errorHandler?;
40
59
  private wsRoutes;
41
- private openapiData;
60
+ private openapiSpec;
42
61
  private router;
43
62
  private staticRoutes;
44
63
  private staticMiddlewares;
@@ -77,6 +96,37 @@ export declare class Prince {
77
96
  private executeHandler;
78
97
  handleFetch(req: Request): Promise<Response>;
79
98
  fetch(req: Request): Promise<Response>;
99
+ /**
100
+ * Mounts an OpenAPI spec + Scalar UI onto this Prince app.
101
+ *
102
+ * Calling this method returns an `OpenAPIBuilder` whose `.route()` method
103
+ * registers a route on the Prince app **and** writes the matching path entry
104
+ * into the OpenAPI spec simultaneously — so the two can never drift.
105
+ *
106
+ * Two routes are auto-registered:
107
+ * GET `docsPath` → Scalar UI (e.g. /docs)
108
+ * GET `docsPath`.json → Raw spec (e.g. /docs.json)
109
+ *
110
+ * @example
111
+ * const app = prince();
112
+ * const api = app.openapi({ title: "My API", version: "1.0.0" }, "/docs");
113
+ *
114
+ * // Registers GET /hello on Prince AND adds it to the OpenAPI spec
115
+ * api.route("GET", "/hello", {
116
+ * summary: "Say hello",
117
+ * responses: { 200: { description: "OK" } },
118
+ * }, (req) => ({ message: "hello" }));
119
+ *
120
+ * app.listen(3000);
121
+ * // → GET /docs → Scalar UI
122
+ * // → GET /docs.json → raw OpenAPI JSON
123
+ */
124
+ openapi(info: {
125
+ title: string;
126
+ version: string;
127
+ }, docsPath?: string, scalarOptions?: ScalarOptions): OpenAPIBuilder & {
128
+ route: (method: string, path: string, operation: Record<string, unknown>, ...args: (RouteHandler | Middleware)[]) => OpenAPIBuilder;
129
+ };
80
130
  listen(port?: number): void;
81
131
  }
82
132
  export declare const prince: (dev?: boolean) => Prince;
@@ -1 +1 @@
1
- {"version":3,"file":"prince.d.ts","sourceRoot":"","sources":["../src/prince.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prince.d.ts","sourceRoot":"","sources":["../src/prince.ts"],"names":[],"mappings":"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"}