@server/next 0.27.1 → 0.27.4

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/index.d.ts ADDED
@@ -0,0 +1,392 @@
1
+ type Method = "get" | "post" | "put" | "patch" | "delete" | "head" | "options" | "socket";
2
+ type ServerConfig = {
3
+ User?: Record<string, string | number | boolean | Date | null | undefined>;
4
+ };
5
+ type RouteOptions = {
6
+ tags?: string | string[];
7
+ title?: string;
8
+ description?: string;
9
+ };
10
+ type Cookie = {
11
+ value?: string;
12
+ path?: string;
13
+ expires?: number | string | Date;
14
+ };
15
+ type RouterMethod = "*" | Method;
16
+ type Bucket = {
17
+ read: (path: string) => Promise<ReadableStream | null>;
18
+ write: (path: string, data: string | Buffer) => Promise<void | string>;
19
+ delete: (path: string) => Promise<boolean>;
20
+ };
21
+ type CorsSettings = {
22
+ origin: string | boolean;
23
+ methods: string;
24
+ headers: string;
25
+ };
26
+ type CorsOptions = boolean | string | string[] | {
27
+ origin?: string | string[];
28
+ methods?: string | Method[];
29
+ headers?: string | string[];
30
+ };
31
+ type BasicValue = string | number | boolean | null;
32
+ type SerializableValue = BasicValue | {
33
+ [key: string]: SerializableValue;
34
+ } | Array<SerializableValue>;
35
+ type KVStore = {
36
+ name?: string;
37
+ prefix: (key: string) => KVStore;
38
+ get: <T = SerializableValue>(key: string) => Promise<T>;
39
+ set: <T = SerializableValue>(key: string, value: T, options?: {
40
+ expires: string | number;
41
+ }) => Promise<void | string>;
42
+ has: (key: string) => Promise<boolean>;
43
+ del: (key: string) => Promise<void | string>;
44
+ keys: () => Promise<string[]>;
45
+ };
46
+ type Provider = "email" | "github";
47
+ type Strategy = "cookie" | "jwt" | "token";
48
+ type AuthSession = {
49
+ id: string;
50
+ provider: Provider;
51
+ strategy: Strategy;
52
+ user: string;
53
+ };
54
+ type AuthUser<T = object> = T & {
55
+ id: string | number;
56
+ provider: Provider;
57
+ strategy: Strategy;
58
+ email: string;
59
+ };
60
+ type ProviderString = Provider | `${Provider}|${Provider}`;
61
+ type AuthOption = `${Strategy}:${Provider | ProviderString}` | {
62
+ provider: Provider | ProviderString | Provider[];
63
+ strategy: Strategy;
64
+ session?: KVStore;
65
+ store?: KVStore;
66
+ redirect?: string;
67
+ cleanUser?: <T = AuthUser>(user: T) => T | Promise<T>;
68
+ };
69
+ type AuthSettings = {
70
+ provider: Provider[];
71
+ strategy: Strategy;
72
+ store: KVStore;
73
+ session: KVStore;
74
+ cleanUser: <T = AuthUser>(user: T) => T | Promise<T>;
75
+ redirect: string;
76
+ };
77
+ type Options = {
78
+ port?: number;
79
+ secret?: string;
80
+ views?: string | Bucket;
81
+ public?: string | Bucket;
82
+ uploads?: string | Bucket;
83
+ store?: KVStore;
84
+ cookies?: KVStore;
85
+ session?: KVStore | {
86
+ store: KVStore;
87
+ };
88
+ cors?: CorsOptions;
89
+ auth?: AuthOption;
90
+ openapi?: any;
91
+ };
92
+ type Settings = {
93
+ port: number;
94
+ secret: string;
95
+ views?: Bucket;
96
+ public?: Bucket;
97
+ uploads?: Bucket;
98
+ store?: KVStore;
99
+ cookies?: KVStore;
100
+ session?: {
101
+ store: KVStore;
102
+ };
103
+ cors?: CorsSettings;
104
+ auth?: AuthSettings;
105
+ openapi?: any;
106
+ };
107
+ type Time = {
108
+ (name: string): void;
109
+ times: [string, number][];
110
+ headers: () => string;
111
+ };
112
+ type Platform = {
113
+ provider: string | null;
114
+ runtime: string | null;
115
+ production: boolean;
116
+ };
117
+ type ExtractPathParams<Path extends string> = Path extends `${string}:${infer Param}(${infer Type})?/${infer Rest}` ? `${Param}:${Type}?` | ExtractPathParams<`/${Rest}`> : Path extends `${string}:${infer Param}(${infer Type})?` ? `${Param}:${Type}?` : Path extends `${string}:${infer Param}(${infer Type})/${infer Rest}` ? `${Param}:${Type}` | ExtractPathParams<`/${Rest}`> : Path extends `${string}:${infer Param}(${infer Type})` ? `${Param}:${Type}` : Path extends `${string}:${infer Param}?/${infer Rest}` ? `${Param}?` | ExtractPathParams<`/${Rest}`> : Path extends `${string}:${infer Param}?` ? `${Param}?` : Path extends `${string}:${infer Param}/${infer Rest}` ? Param | ExtractPathParams<`/${Rest}`> : Path extends `${string}:${infer Param}` ? Param : never;
118
+ type ParamTypeMap = {
119
+ string: string;
120
+ number: number;
121
+ date: Date;
122
+ };
123
+ type InferParamType<T extends string> = T extends keyof ParamTypeMap ? ParamTypeMap[T] : string;
124
+ type ParamsToObject<Params extends string> = {
125
+ [K in Params as K extends `${infer Key}:${infer _Type}?` ? Key : K extends `${infer Key}:${infer _Type}` ? Key : K extends `${infer Key}?` ? Key : K]: K extends `${infer _Key}:${infer Type}?` ? InferParamType<Type> | undefined : K extends `${infer _Key}:${infer Type}` ? InferParamType<Type> : K extends `${infer _Key}?` ? string | undefined : string;
126
+ };
127
+ type PathToParams<Path extends string> = ParamsToObject<ExtractPathParams<Path>>;
128
+ type BunEnv = Record<string, string> & {
129
+ upgrade?: (req: Request) => boolean;
130
+ };
131
+ type EventCallback = (data: Context & SerializableValue) => void;
132
+ type Events = Record<string, EventCallback[]> & {
133
+ on?: (key: string, cb: (value?: Context & SerializableValue) => void) => void;
134
+ trigger?: (key: string, value?: Partial<Context & SerializableValue>) => void;
135
+ };
136
+ type Context<Params extends Record<string, string> = Record<string, string>, O extends ServerConfig = object> = {
137
+ method: Method;
138
+ headers: Record<string, string | string[]>;
139
+ cookies: Record<string, string>;
140
+ body?: SerializableValue;
141
+ url: URL & {
142
+ params: Params;
143
+ query: Record<string, string>;
144
+ };
145
+ options: Settings;
146
+ platform: Platform;
147
+ time?: Time;
148
+ socket?: WebSocket;
149
+ sockets?: WebSocket[];
150
+ session?: Record<string, BasicValue>;
151
+ user?: O extends {
152
+ User: infer U;
153
+ } ? U & AuthUser : AuthUser;
154
+ init: number;
155
+ events: Events;
156
+ req?: Request;
157
+ res?: Response & {
158
+ cookies?: Record<string, string>;
159
+ };
160
+ app: Server;
161
+ };
162
+ type InlineReply = Response | {
163
+ body: string;
164
+ headers?: Headers;
165
+ } | SerializableValue;
166
+ type Body = InlineReply;
167
+ type Middleware<O extends ServerConfig = object, Params extends Record<string, string> = Record<string, string>> = (ctx: Context<Params, O>) => InlineReply | Promise<InlineReply> | void | Promise<void>;
168
+
169
+ type Variables = Record<string, string | string[]>;
170
+ type ExtendError = string | {
171
+ message: string;
172
+ status: number;
173
+ };
174
+ interface ServerErrorConstructor {
175
+ extend(errors: Record<string, ExtendError>): Record<string, ExtendError>;
176
+ [key: string]: ((vars?: Variables) => ServerError) | any;
177
+ }
178
+ declare class ServerError extends Error {
179
+ code: string;
180
+ status: number;
181
+ constructor(code: string, status: number, message: string | ((vars: Variables) => string), vars?: Variables);
182
+ static extend(errors: Record<string, ExtendError>): Record<string, ExtendError>;
183
+ }
184
+ declare const TypedServerError: typeof ServerError & ServerErrorConstructor;
185
+
186
+ declare global {
187
+ var env: Record<string, any>;
188
+ }
189
+
190
+ type Mids<O, Path extends string> = Middleware<O, PathToParams<Path>>[];
191
+ type PathOrMiddle<O extends ServerConfig = object> = string | Middleware<O>;
192
+ type FullRoute = [RouterMethod, string, ...Middleware[]][];
193
+ declare class Router<O extends ServerConfig = object> {
194
+ handlers: Record<Method, FullRoute>;
195
+ self(): this;
196
+ handle(method: RouterMethod, path: PathOrMiddle<O>, ...middleware: Middleware<O>[]): this;
197
+ socket<Path extends string>(path: Path, ...middleware: Mids<O, Path>): this;
198
+ socket<Path extends string>(path: Path, options: RouteOptions, ...middleware: Mids<O, Path>): this;
199
+ socket(...middleware: Middleware<O>[]): this;
200
+ socket(options: RouteOptions, ...middleware: Middleware<O>[]): this;
201
+ get<Path extends string>(path: Path, ...middleware: Mids<O, Path>): this;
202
+ get<Path extends string>(path: Path, options: RouteOptions, ...middleware: Mids<O, Path>): this;
203
+ get(...middleware: Middleware<O>[]): this;
204
+ get(options: RouteOptions, ...middleware: Middleware<O>[]): this;
205
+ head<Path extends string>(path: Path, ...middleware: Mids<O, Path>): this;
206
+ head<Path extends string>(path: Path, options: RouteOptions, ...middleware: Mids<O, Path>): this;
207
+ head(...middleware: Middleware<O>[]): this;
208
+ head(options: RouteOptions, ...middleware: Middleware<O>[]): this;
209
+ post<Path extends string>(path: Path, ...middleware: Mids<O, Path>): this;
210
+ post<Path extends string>(path: Path, options: RouteOptions, ...middleware: Mids<O, Path>): this;
211
+ post(...middleware: Middleware<O>[]): this;
212
+ post(options: RouteOptions, ...middleware: Middleware<O>[]): this;
213
+ put<Path extends string>(path: Path, ...middleware: Mids<O, Path>): this;
214
+ put<Path extends string>(path: Path, options: RouteOptions, ...middleware: Mids<O, Path>): this;
215
+ put(...middleware: Middleware<O>[]): this;
216
+ put(options: RouteOptions, ...middleware: Middleware<O>[]): this;
217
+ patch<Path extends string>(path: Path, ...middleware: Mids<O, Path>): this;
218
+ patch<Path extends string>(path: Path, options: RouteOptions, ...middleware: Mids<O, Path>): this;
219
+ patch(...middleware: Middleware<O>[]): this;
220
+ patch(options: RouteOptions, ...middleware: Middleware<O>[]): this;
221
+ del<Path extends string>(path: Path, ...middleware: Mids<O, Path>): this;
222
+ del<Path extends string>(path: Path, options: RouteOptions, ...middleware: Mids<O, Path>): this;
223
+ del(...middleware: Middleware<O>[]): this;
224
+ del(options: RouteOptions, ...middleware: Middleware<O>[]): this;
225
+ options<Path extends string>(path: Path, ...middleware: Mids<O, Path>): this;
226
+ options<Path extends string>(path: Path, options: RouteOptions, ...mid: Mids<O, Path>): this;
227
+ options(...middleware: Middleware<O>[]): this;
228
+ options(options: RouteOptions, ...middleware: Middleware<O>[]): this;
229
+ use(...middleware: Middleware[]): this;
230
+ use(path: string, ...middleware: Middleware[]): this;
231
+ use(router: Router): this;
232
+ use(path: string, router: Router): this;
233
+ }
234
+ declare function router(): Router;
235
+
236
+ type CookieOptions = string | string[] | Cookie | Cookie[] | null;
237
+ interface ResponseData {
238
+ headers: Headers;
239
+ status?: number;
240
+ }
241
+ declare class Reply {
242
+ res: ResponseData;
243
+ constructor();
244
+ status(status: number): this;
245
+ type(type?: string): this;
246
+ download(name?: string): this;
247
+ headers(key: string | Record<string, string>, value?: string): this;
248
+ cookies(key: string | Record<string, CookieOptions>, value?: CookieOptions): this;
249
+ json(body: unknown): Response;
250
+ redirect(path: string): Response;
251
+ file(path: string): Promise<Response>;
252
+ send(body?: string | Buffer | ReadableStream | any): Response;
253
+ }
254
+ type Params<K extends keyof Reply> = Reply[K] extends (...args: infer A) => any ? A : never;
255
+ declare const status: (...args: Params<"status">) => Reply;
256
+ declare const headers: (...args: Params<"headers">) => Reply;
257
+ declare const type: (...args: Params<"type">) => Reply;
258
+ declare const download: (...args: Params<"download">) => Reply;
259
+ declare const cookies: (...args: Params<"cookies">) => Reply;
260
+ declare const send: (...args: Params<"send">) => Response;
261
+ declare const json: (...args: Params<"json">) => Response;
262
+ declare const file: (...args: Params<"file">) => Promise<Response>;
263
+ declare const redirect: (...args: Params<"redirect">) => Response;
264
+
265
+ declare class Server<O extends ServerConfig = object> extends Router<O> {
266
+ settings: Settings;
267
+ platform: Platform;
268
+ sockets: any[];
269
+ websocket: any;
270
+ port?: number;
271
+ constructor(options?: Options);
272
+ self(): this & ((request: any, context?: any) => any);
273
+ node(): Promise<void>;
274
+ fetch(request: Request, env?: BunEnv): Promise<Response>;
275
+ callback(request: Request, context: unknown): Promise<Response>;
276
+ test(): {
277
+ get: (path: string, options?: {
278
+ cache?: RequestCache;
279
+ credentials?: RequestCredentials;
280
+ headers?: HeadersInit;
281
+ integrity?: string;
282
+ keepalive?: boolean;
283
+ method?: string;
284
+ mode?: RequestMode;
285
+ priority?: RequestPriority;
286
+ redirect?: RequestRedirect;
287
+ referrer?: string;
288
+ referrerPolicy?: ReferrerPolicy;
289
+ signal?: AbortSignal | null;
290
+ window?: null;
291
+ }) => Promise<Response>;
292
+ head: (path: string, options?: {
293
+ cache?: RequestCache;
294
+ credentials?: RequestCredentials;
295
+ headers?: HeadersInit;
296
+ integrity?: string;
297
+ keepalive?: boolean;
298
+ method?: string;
299
+ mode?: RequestMode;
300
+ priority?: RequestPriority;
301
+ redirect?: RequestRedirect;
302
+ referrer?: string;
303
+ referrerPolicy?: ReferrerPolicy;
304
+ signal?: AbortSignal | null;
305
+ window?: null;
306
+ }) => Promise<Response>;
307
+ post: (path: string, body?: string | number | boolean | ArrayBuffer | {
308
+ [key: string]: SerializableValue;
309
+ } | SerializableValue[] | ReadableStream<any> | Blob | ArrayBufferView<ArrayBuffer> | FormData | URLSearchParams, options?: {
310
+ cache?: RequestCache;
311
+ credentials?: RequestCredentials;
312
+ headers?: HeadersInit;
313
+ integrity?: string;
314
+ keepalive?: boolean;
315
+ method?: string;
316
+ mode?: RequestMode;
317
+ priority?: RequestPriority;
318
+ redirect?: RequestRedirect;
319
+ referrer?: string;
320
+ referrerPolicy?: ReferrerPolicy;
321
+ signal?: AbortSignal | null;
322
+ window?: null;
323
+ }) => Promise<Response>;
324
+ put: (path: string, body?: string | number | boolean | ArrayBuffer | {
325
+ [key: string]: SerializableValue;
326
+ } | SerializableValue[] | ReadableStream<any> | Blob | ArrayBufferView<ArrayBuffer> | FormData | URLSearchParams, options?: {
327
+ cache?: RequestCache;
328
+ credentials?: RequestCredentials;
329
+ headers?: HeadersInit;
330
+ integrity?: string;
331
+ keepalive?: boolean;
332
+ method?: string;
333
+ mode?: RequestMode;
334
+ priority?: RequestPriority;
335
+ redirect?: RequestRedirect;
336
+ referrer?: string;
337
+ referrerPolicy?: ReferrerPolicy;
338
+ signal?: AbortSignal | null;
339
+ window?: null;
340
+ }) => Promise<Response>;
341
+ patch: (path: string, body?: string | number | boolean | ArrayBuffer | {
342
+ [key: string]: SerializableValue;
343
+ } | SerializableValue[] | ReadableStream<any> | Blob | ArrayBufferView<ArrayBuffer> | FormData | URLSearchParams, options?: {
344
+ cache?: RequestCache;
345
+ credentials?: RequestCredentials;
346
+ headers?: HeadersInit;
347
+ integrity?: string;
348
+ keepalive?: boolean;
349
+ method?: string;
350
+ mode?: RequestMode;
351
+ priority?: RequestPriority;
352
+ redirect?: RequestRedirect;
353
+ referrer?: string;
354
+ referrerPolicy?: ReferrerPolicy;
355
+ signal?: AbortSignal | null;
356
+ window?: null;
357
+ }) => Promise<Response>;
358
+ delete: (path: string, options?: {
359
+ cache?: RequestCache;
360
+ credentials?: RequestCredentials;
361
+ headers?: HeadersInit;
362
+ integrity?: string;
363
+ keepalive?: boolean;
364
+ method?: string;
365
+ mode?: RequestMode;
366
+ priority?: RequestPriority;
367
+ redirect?: RequestRedirect;
368
+ referrer?: string;
369
+ referrerPolicy?: ReferrerPolicy;
370
+ signal?: AbortSignal | null;
371
+ window?: null;
372
+ }) => Promise<Response>;
373
+ options: (path: string, options?: {
374
+ cache?: RequestCache;
375
+ credentials?: RequestCredentials;
376
+ headers?: HeadersInit;
377
+ integrity?: string;
378
+ keepalive?: boolean;
379
+ method?: string;
380
+ mode?: RequestMode;
381
+ priority?: RequestPriority;
382
+ redirect?: RequestRedirect;
383
+ referrer?: string;
384
+ referrerPolicy?: ReferrerPolicy;
385
+ signal?: AbortSignal | null;
386
+ window?: null;
387
+ }) => Promise<Response>;
388
+ };
389
+ }
390
+ declare function server<Options>(options?: {}): Server<Options> & ((request: any, context?: any) => any);
391
+
392
+ export { type AuthOption, type AuthSession, type AuthSettings, type AuthUser, type BasicValue, type Body, type Bucket, type BunEnv, type Context, type Cookie, type CorsSettings, type EventCallback, type ExtractPathParams, type InferParamType, type InlineReply, type KVStore, type Method, type Middleware, type Options, type ParamTypeMap, type ParamsToObject, type PathToParams, type Platform, type Provider, type RouteOptions, type RouterMethod, type SerializableValue, Server, type ServerConfig, TypedServerError as ServerError, type Settings, type Strategy, type Time, cookies, server as default, download, file, headers, json, redirect, router, send, status, type };
package/index.js CHANGED
@@ -172,21 +172,14 @@ var email_default = {
172
172
  };
173
173
 
174
174
  // src/reply.ts
175
+ var EXPIRED = (/* @__PURE__ */ new Date(0)).toUTCString();
175
176
  var Reply = class {
176
177
  res;
177
178
  constructor() {
178
179
  this.res = {
179
- headers: {},
180
- cookies: {}
180
+ headers: new Headers()
181
181
  };
182
182
  }
183
- generateHeaders() {
184
- const headers2 = new Headers(this.res.headers);
185
- for (const cookie of createCookies(this.res.cookies)) {
186
- headers2.append("set-cookie", cookie);
187
- }
188
- return headers2;
189
- }
190
183
  status(status2) {
191
184
  this.res.status = status2;
192
185
  return this;
@@ -194,46 +187,55 @@ var Reply = class {
194
187
  type(type2) {
195
188
  if (!type2) return this;
196
189
  type2 = types_default[type2.replace(/^\./, "")] || type2;
197
- return this.headers({ "content-type": type2 });
190
+ this.res.headers.set("content-type", type2);
191
+ return this;
198
192
  }
199
- download(name, type2) {
200
- if (name && !type2) type2 = name.split(".").pop();
201
- if (type2) this.type(type2);
202
- const filename = name ? `; filename="${name}"` : "";
203
- return this.headers({ "content-disposition": `attachment${filename}` });
193
+ download(name) {
194
+ const ext = name?.split(".").pop();
195
+ if (type && ext && !this.res.headers.get("content-type")) this.type(ext);
196
+ const filename = name ? `; filename="${encodeURIComponent(name)}"` : "";
197
+ return this.headers("content-disposition", `attachment${filename}`);
204
198
  }
205
- headers(headers2) {
206
- if (!headers2 || typeof headers2 !== "object") return this;
207
- for (const key in headers2) {
208
- this.res.headers[key] = headers2[key];
199
+ headers(key, value) {
200
+ if (typeof key !== "string") {
201
+ Object.entries(key).map(([key2, value2]) => this.headers(key2, value2));
202
+ return this;
209
203
  }
204
+ if (Array.isArray(value)) {
205
+ Object.values(value).map((val) => this.headers(key, val));
206
+ return this;
207
+ }
208
+ this.res.headers.append(key, value);
210
209
  return this;
211
210
  }
212
- cookies(cookies2) {
213
- if (!cookies2 || typeof cookies2 !== "object") return this;
214
- for (const key in cookies2) {
215
- if (typeof cookies2[key] === "string") {
216
- this.res.cookies[key] = { value: cookies2[key] };
217
- } else {
218
- this.res.cookies[key] = cookies2[key];
219
- }
211
+ cookies(key, value) {
212
+ if (typeof key === "object") {
213
+ Object.entries(key).map(([key2, value2]) => this.cookies(key2, value2));
214
+ return this;
220
215
  }
221
- return this;
216
+ if (Array.isArray(value)) {
217
+ Object.values(value).map((val) => this.cookies(key, val));
218
+ return this;
219
+ }
220
+ console.log(key, value);
221
+ if (value === null) return this.cookies(key, { expires: EXPIRED });
222
+ if (typeof value !== "object") return this.cookies(key, { value });
223
+ return this.headers("set-cookie", createCookies(key, value));
222
224
  }
223
225
  json(body) {
224
- return this.headers({
225
- "content-type": "application/json"
226
- }).send(JSON.stringify(body));
226
+ return this.headers("content-type", "application/json").send(
227
+ JSON.stringify(body)
228
+ );
227
229
  }
228
- redirect(Location) {
229
- return this.headers({ Location }).status(302).send();
230
+ redirect(path2) {
231
+ return this.headers("location", path2).status(302).send();
230
232
  }
231
- async file(path2, renderer = async (data) => data) {
233
+ async file(path2) {
232
234
  try {
233
- const fs2 = await import("fs/promises");
234
- const data = await fs2.readFile(path2);
235
+ const fs2 = await import("fs");
235
236
  const ext = path2.split(".").pop();
236
- return this.type(ext).send(await renderer(data));
237
+ const stream = fs2.createReadStream(path2);
238
+ return this.type(ext).send(stream);
237
239
  } catch (error) {
238
240
  if (error.code === "ENOENT") {
239
241
  return this.status(404).send();
@@ -241,50 +243,39 @@ var Reply = class {
241
243
  throw error;
242
244
  }
243
245
  }
244
- async view(path2, renderer = async (data) => data, ctx) {
245
- if (!ctx?.options.views) {
246
- throw new Error("Views not enabled");
247
- }
248
- const data = await ctx.options.views.read(path2);
249
- if (!data) return this.status(404).send();
250
- return this.type(path2.split(".").pop()).send(await renderer(data));
251
- }
252
246
  send(body = "") {
253
- const { status: status2 = 200 } = this.res;
247
+ const { status: status2 = 200, headers: headers2 } = this.res;
254
248
  if (typeof body === "string") {
255
- if (!this.res.headers["content-type"]) {
256
- const isHtml = body.startsWith("<");
257
- this.res.headers["content-type"] = isHtml ? "text/html" : "text/plain";
249
+ if (!headers2.get("content-type")) {
250
+ const isHtml = body.trim().startsWith("<");
251
+ headers2.set("content-type", isHtml ? "text/html" : "text/plain");
258
252
  }
259
- const headers2 = this.generateHeaders();
260
253
  return new Response(body, { status: status2, headers: headers2 });
261
254
  }
262
255
  const name = body?.constructor?.name;
263
256
  if (name === "Buffer") {
264
- const headers2 = this.generateHeaders();
265
257
  return new Response(body, { status: status2, headers: headers2 });
266
258
  }
267
- if (name === "ReadableStream") {
268
- const headers2 = this.generateHeaders();
259
+ if (typeof body?.getReader === "function") {
269
260
  return new Response(body, { status: status2, headers: headers2 });
270
261
  }
271
262
  if (name === "PassThrough" || name === "Readable") {
272
- const headers2 = this.generateHeaders();
273
263
  return new Response(toWeb(body), { status: status2, headers: headers2 });
274
264
  }
275
- return this.json(body);
265
+ headers2.set("content-type", "application/json");
266
+ return new Response(JSON.stringify(body), { status: status2, headers: headers2 });
276
267
  }
277
268
  };
278
- var status = (...args) => new Reply().status(...args);
279
- var headers = (...args) => new Reply().headers(...args);
280
- var type = (...args) => new Reply().type(...args);
281
- var download = (...args) => new Reply().download(...args);
282
- var cookies = (...args) => new Reply().cookies(...args);
283
- var send = (...args) => new Reply().send(...args);
284
- var json = (...args) => new Reply().json(...args);
285
- var file = (...args) => new Reply().file(...args);
286
- var redirect = (...args) => new Reply().redirect(...args);
287
- var view = (...args) => new Reply().view(...args);
269
+ var r = () => new Reply();
270
+ var status = (...args) => r().status(...args);
271
+ var headers = (...args) => r().headers(...args);
272
+ var type = (...args) => r().type(...args);
273
+ var download = (...args) => r().download(...args);
274
+ var cookies = (...args) => r().cookies(...args);
275
+ var send = (...args) => r().send(...args);
276
+ var json = (...args) => r().json(...args);
277
+ var file = (...args) => r().file(...args);
278
+ var redirect = (...args) => r().redirect(...args);
288
279
 
289
280
  // src/auth/providers/github.ts
290
281
  var oauth = async (code) => {
@@ -364,14 +355,30 @@ var github_default = { login, callback };
364
355
  var providers_default = { email: email_default, github: github_default };
365
356
 
366
357
  // src/auth/parseAuthOptions.ts
358
+ var defaultRedirect = "/user";
359
+ function defaultCleanUser(fullUser) {
360
+ const { password: _password, ...user } = fullUser;
361
+ return user;
362
+ }
363
+ var providersKeys = Object.keys(providers_default);
364
+ function getProviders(provider) {
365
+ if (typeof provider === "string") {
366
+ provider = provider.split("|");
367
+ }
368
+ const invalidProvider = provider.find((p) => !providersKeys.includes(p));
369
+ if (invalidProvider) {
370
+ throw new Error(
371
+ `Provider "${invalidProvider}" not found, available ones are "${providersKeys.join('", "')}"`
372
+ );
373
+ }
374
+ return provider;
375
+ }
367
376
  function parseAuthOptions(auth2, all) {
368
377
  if (!auth2) return null;
369
378
  if (typeof auth2 === "string") {
370
- const [strategy, provider] = auth2.split(":");
371
- auth2 = { strategy, provider };
372
- }
373
- if (typeof auth2.provider === "string") {
374
- auth2.provider = auth2.provider.split("|").filter(Boolean);
379
+ const [strategy2, providerRaw] = auth2.split(":");
380
+ const provider2 = providerRaw && providerRaw.split("|");
381
+ auth2 = { strategy: strategy2, provider: provider2 };
375
382
  }
376
383
  if (!auth2.strategy) {
377
384
  throw new Error("Auth options needs a strategy");
@@ -379,31 +386,32 @@ function parseAuthOptions(auth2, all) {
379
386
  if (!auth2.strategy.length) {
380
387
  throw new Error("Auth options needs a strategy");
381
388
  }
389
+ const strategy = auth2.strategy;
382
390
  if (!auth2.provider || !auth2.provider.length) {
383
391
  throw new Error("Auth options needs a provider");
384
392
  }
385
- const providerNotFound = auth2.provider.find((p) => !providers_default[p]);
386
- if (providerNotFound) {
387
- throw new Error(
388
- `Provider "${providerNotFound}" not found, available ones are "${Object.keys(providers_default).join('", "')}"`
389
- );
393
+ const provider = getProviders(auth2.provider);
394
+ const redirect2 = auth2.redirect || defaultRedirect;
395
+ const cleanUser = auth2.cleanUser || defaultCleanUser;
396
+ if (!auth2.store && !all.store) {
397
+ throw new Error("Need a userStore store for Auth");
390
398
  }
391
- if (!auth2.session && all.store) {
392
- auth2.session = all.store.prefix("auth:");
399
+ if (!auth2.session && !all.store) {
400
+ throw new Error("Need a sessionStore store for Auth");
393
401
  }
394
- if (!auth2.store && all.store) {
395
- auth2.store = all.store.prefix("user:");
396
- }
397
- if (!auth2.cleanUser) {
398
- auth2.cleanUser = (fullUser) => {
399
- const { password: _password, ...user } = fullUser;
400
- return user;
401
- };
402
- }
403
- if (!auth2.redirect) {
404
- auth2.redirect = "/user";
405
- }
406
- return auth2;
402
+ const store = auth2.store || all.store.prefix("user:");
403
+ const session2 = auth2.session || all.store.prefix("auth:");
404
+ return {
405
+ // Base main configuration
406
+ strategy,
407
+ provider,
408
+ // Extra configuration
409
+ redirect: redirect2,
410
+ cleanUser,
411
+ // Stores for the auth session and users
412
+ store,
413
+ session: session2
414
+ };
407
415
  }
408
416
 
409
417
  // src/helpers/bucket.ts
@@ -660,20 +668,52 @@ function cors(config2, origin = "") {
660
668
  }
661
669
 
662
670
  // src/helpers/createCookies.ts
663
- function createCookies(cookies2) {
664
- if (!cookies2 || !Object.keys(cookies2).length) return [];
665
- return Object.entries(cookies2).map(([key, val]) => {
666
- if (!val) {
667
- val = { value: "", expires: (/* @__PURE__ */ new Date(0)).toUTCString() };
668
- }
669
- if (typeof val === "string") {
670
- val = { value: val };
671
- }
672
- const { value, path: path2, expires } = val;
673
- const pathPart = `;Path=${path2 || "/"}`;
674
- const expiresPart = expires ? `;Expires=${expires}` : "";
675
- return `${key}=${value || ""}${pathPart}${expiresPart}`;
676
- });
671
+ var EXPIRED2 = (/* @__PURE__ */ new Date(0)).toUTCString();
672
+ var times = /(-?(?:\d+\.?\d*|\d*\.?\d+)(?:e[-+]?\d+)?)\s*([\p{L}]*)/iu;
673
+ parse.millisecond = parse.ms = 1e-3;
674
+ parse.second = parse.sec = parse.s = parse[""] = 1;
675
+ parse.minute = parse.min = parse.m = parse.s * 60;
676
+ parse.hour = parse.hr = parse.h = parse.m * 60;
677
+ parse.day = parse.d = parse.h * 24;
678
+ parse.week = parse.wk = parse.w = parse.d * 7;
679
+ parse.year = parse.yr = parse.y = parse.d * 365.25;
680
+ parse.month = parse.b = parse.y / 12;
681
+ function parse(str) {
682
+ if (str === null || str === void 0) return null;
683
+ if (typeof str === "number") return str;
684
+ str = str.toLowerCase().replace(/[,_]/g, "");
685
+ const [_, value, units] = times.exec(str) || [];
686
+ if (!units) return null;
687
+ const unitValue = parse[units] || parse[units.replace(/s$/, "")];
688
+ if (!unitValue) return null;
689
+ const result = unitValue * parseFloat(value);
690
+ return Math.abs(Math.round(result * 1e3));
691
+ }
692
+ function normalizeExpires(expires) {
693
+ if (expires === null || expires === void 0) return void 0;
694
+ if (expires === 0) return EXPIRED2;
695
+ if (typeof expires === "string") {
696
+ if (/^[\d._]+\w+$/.test(expires)) {
697
+ return new Date(Date.now() + parse(expires)).toUTCString();
698
+ } else {
699
+ return expires;
700
+ }
701
+ }
702
+ if (typeof expires === "number") {
703
+ return new Date(Date.now() + expires).toUTCString();
704
+ }
705
+ if (expires instanceof Date) {
706
+ return expires.toUTCString();
707
+ }
708
+ return void 0;
709
+ }
710
+ function createCookies(key, val) {
711
+ if (val.value === null) val.expires = EXPIRED2;
712
+ const { value, path: path2, expires } = val;
713
+ const pathPart = `;Path=${path2 || "/"}`;
714
+ const expiresStr = normalizeExpires(expires);
715
+ const expiresPart = typeof expires !== "undefined" ? `;Expires=${expiresStr}` : "";
716
+ return `${key}=${value || ""}${pathPart}${expiresPart}`;
677
717
  }
678
718
 
679
719
  // src/helpers/createWebsocket.ts
@@ -789,7 +829,7 @@ async function parseResponse(out, ctx) {
789
829
  }
790
830
  if (Object.keys(ctx.session || {}).length) {
791
831
  if (!ctx.options.session?.store) {
792
- throw ServerError_default.NO_STORE({});
832
+ throw ServerError_default.NO_STORE();
793
833
  }
794
834
  if (!ctx.cookies.session) {
795
835
  ctx.res.cookies.session = createId();
@@ -1378,7 +1418,7 @@ var encode = (str = "") => {
1378
1418
  };
1379
1419
  var getConfig = (routes) => {
1380
1420
  const config2 = routes.find(
1381
- (r) => typeof r !== "string" && typeof r !== "function" && typeof r === "object"
1421
+ (r2) => typeof r2 !== "string" && typeof r2 !== "function" && typeof r2 === "object"
1382
1422
  );
1383
1423
  if (!config2) return {};
1384
1424
  if (config2.tags) {
@@ -1527,13 +1567,13 @@ var openapi_default = async (ctx) => {
1527
1567
 
1528
1568
  // src/middle/timer.ts
1529
1569
  var createTime = () => {
1530
- const times = [["init", performance.now()]];
1531
- const time = (name) => times.push([name, performance.now()]);
1532
- time.times = times;
1570
+ const times2 = [["init", performance.now()]];
1571
+ const time = (name) => times2.push([name, performance.now()]);
1572
+ time.times = times2;
1533
1573
  time.headers = () => {
1534
- const r = (t) => Math.round(t);
1535
- const times2 = time.times;
1536
- const timing = times2.slice(1).map(([name, time2], i) => `${name};dur=${r(time2 - times2[i][1])}`).join(", ");
1574
+ const r2 = (t) => Math.round(t);
1575
+ const times3 = time.times;
1576
+ const timing = times3.slice(1).map(([name, time2], i) => `${name};dur=${r2(time2 - times3[i][1])}`).join(", ");
1537
1577
  return timing;
1538
1578
  };
1539
1579
  return time;
@@ -1833,39 +1873,27 @@ function isSerializable(body) {
1833
1873
  }
1834
1874
  function ServerTest(app) {
1835
1875
  const port = app.settings.port;
1836
- const fetch2 = async (path2, method, options = {}) => {
1837
- try {
1838
- if (!options.headers) options.headers = {};
1839
- if (isSerializable(options.body)) {
1840
- options.headers["content-type"] = "application/json";
1841
- options.body = JSON.stringify(options.body);
1842
- }
1843
- const res = await app.fetch(
1844
- new Request(`http://localhost:${port}${path2}`, {
1845
- method,
1846
- ...options
1847
- })
1848
- );
1849
- const headers2 = parseHeaders_default(res.headers);
1850
- let body;
1851
- if (headers2["content-type"]?.includes("application/json")) {
1852
- body = await res.json();
1853
- } else {
1854
- body = await res.text();
1855
- }
1856
- return { status: res.status, headers: headers2, body };
1857
- } catch (error) {
1858
- return { status: 500, headers: {}, body: error.message };
1859
- }
1876
+ const fetch2 = async (method, path2, options = {}) => {
1877
+ if (!options.headers) options.headers = {};
1878
+ if (isSerializable(options.body)) {
1879
+ options.headers["content-type"] = "application/json";
1880
+ options.body = JSON.stringify(options.body);
1881
+ }
1882
+ return await app.fetch(
1883
+ new Request(`http://localhost:${port}${path2}`, {
1884
+ method,
1885
+ ...options
1886
+ })
1887
+ );
1860
1888
  };
1861
1889
  return {
1862
- get: (path2, options) => fetch2(path2, "get", options),
1863
- head: (path2, options) => fetch2(path2, "head", options),
1864
- post: (path2, body, options) => fetch2(path2, "post", { body, ...options }),
1865
- put: (path2, body, options) => fetch2(path2, "put", { body, ...options }),
1866
- patch: (path2, body, options) => fetch2(path2, "patch", { body, ...options }),
1867
- delete: (path2, options) => fetch2(path2, "delete", options),
1868
- options: (path2, options) => fetch2(path2, "options", options)
1890
+ get: (path2, options) => fetch2("get", path2, options),
1891
+ head: (path2, options) => fetch2("head", path2, options),
1892
+ post: (path2, body, options) => fetch2("post", path2, { body, ...options }),
1893
+ put: (path2, body, options) => fetch2("put", path2, { body, ...options }),
1894
+ patch: (path2, body, options) => fetch2("patch", path2, { body, ...options }),
1895
+ delete: (path2, options) => fetch2("delete", path2, options),
1896
+ options: (path2, options) => fetch2("options", path2, options)
1869
1897
  };
1870
1898
  }
1871
1899
 
@@ -1934,7 +1962,6 @@ function server(options = {}) {
1934
1962
  return new Server(options).self();
1935
1963
  }
1936
1964
  export {
1937
- Reply,
1938
1965
  Server,
1939
1966
  ServerError_default as ServerError,
1940
1967
  cookies,
@@ -1947,6 +1974,5 @@ export {
1947
1974
  router,
1948
1975
  send,
1949
1976
  status,
1950
- type,
1951
- view
1977
+ type
1952
1978
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@server/next",
3
- "version": "0.27.1",
3
+ "version": "0.27.4",
4
4
  "description": "A fully-fledged web server with routing, file uploads, sessions, static files, schema validation, websockets, testing, etc.",
5
5
  "homepage": "https://server-js.com/",
6
6
  "repository": "https://github.com/franciscop/server-next.git",
@@ -20,7 +20,7 @@
20
20
  "build": "bunx tsup src/index.ts --format esm --dts --out-dir . --target node24",
21
21
  "start": "bun test --watch",
22
22
  "lint": "npx @biomejs/biome lint ./src --skip=lint/suspicious/noExplicitAny --skip=lint/style/noParameterAssign --skip=lint/suspicious/noConfusingVoidType",
23
- "types": "npx tsc --noEmit src/index.ts",
23
+ "types": "npx tsc --noEmit",
24
24
  "test": "npm run test:bun && tsc --noEmit",
25
25
  "test:bun": "bun test",
26
26
  "test:jest": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
@@ -34,11 +34,13 @@
34
34
  "main": "index.js",
35
35
  "types": "index.d.ts",
36
36
  "files": [
37
+ "index.d.ts",
37
38
  "src/jsx/"
38
39
  ],
39
40
  "exports": {
40
41
  ".": "./index.js",
41
- "./jsx-runtime": "./src/jsx/jsx-dev-runtime.js",
42
+ "./index.d.ts": "./index.d.ts",
43
+ "./jsx-runtime": "./src/jsx/jsx-runtime.js",
42
44
  "./jsx-dev-runtime": "./src/jsx/jsx-dev-runtime.js"
43
45
  },
44
46
  "devDependencies": {
@@ -1,91 +1 @@
1
- const entities = {
2
- "&": "&amp;",
3
- "<": "&lt;",
4
- ">": "&gt;",
5
- '"': "&quot;",
6
- };
7
-
8
- const encode = (str = "") => {
9
- if (typeof str === "number") str = String(str);
10
- if (typeof str !== "string") return "";
11
- return str.replace(/[&<>"]/g, (tag) => entities[tag]);
12
- };
13
-
14
- const SELFCLOSE = new Set(
15
- "area,base,br,col,embed,hr,img,input,link,meta,source,track,wbr".split(","),
16
- );
17
-
18
- const altAttrs = {
19
- classname: "class",
20
- };
21
-
22
- // "" and 0 are valid children, false and null and undefined are not
23
- const isValidChild = (child) => child || child === "" || child === 0;
24
-
25
- const escapeCSS = (value) => String(value).replace(/[<>&"'`]/g, "\\$&");
26
-
27
- const minifyCss = (str) =>
28
- str
29
- .replace(/\s+/g, " ")
30
- .replace(/(?!<")\/\*[^*]+\*\/(?!")/g, "")
31
- .replace(/(\w|\*) (\{)/g, "$1$2")
32
- .replace(/(\}) (\w|\*)/g, "$1$2")
33
- .replace(/(\{) (\w)/g, "$1$2")
34
- .replace(/(\w)(:) /g, "$1$2")
35
- .replace(/(;) (\})/g, "$1$2")
36
- .replace(/(;) (\w)/g, "$1$2")
37
- .replace(/;(\})/g, "$1")
38
- .replace(/(\w), (\w)/g, "$1,$2")
39
- .replace(/(\w), (\w)/g, "$1,$2")
40
- .replace(/(\{) (\w)/g, "$1$2")
41
- .trim();
42
-
43
- const jsx = (tag, { children, ...props }) => {
44
- if (typeof tag === "function") return tag({ children, ...props });
45
-
46
- if (tag === "script" && children) {
47
- const src = children;
48
- children = () => src;
49
- }
50
-
51
- if (tag === "style" && children && typeof children === "string") {
52
- const src = minifyCss(escapeCSS(children));
53
- children = () => src;
54
- }
55
-
56
- if (props?.dangerouslySetInnerHTML) {
57
- children = () => props.dangerouslySetInnerHTML.__html;
58
- }
59
-
60
- if (!isValidChild(children)) children = [];
61
- if (typeof children === "string") children = [children];
62
-
63
- children = (Array.isArray(children) ? children : [children])
64
- .flat()
65
- .map((c) => (typeof c === "function" ? c() : encode(c)))
66
- .join("");
67
-
68
- if (!tag) return () => children;
69
-
70
- let attrStr = Object.entries(props || {})
71
- .filter(([k]) => k !== "dangerouslySetInnerHTML")
72
- .filter(([k, v]) => !/on[A-Z]/.test(k) && typeof v !== "function")
73
- .filter(([, v]) => v !== false)
74
- .map(([k, v]) =>
75
- v === true
76
- ? altAttrs[k.toLowerCase()] || encode(k)
77
- : `${altAttrs[k.toLowerCase()] || encode(k)}="${encode(String(v))}"`,
78
- )
79
- .join(" ");
80
-
81
- if (attrStr) attrStr = ` ${attrStr}`;
82
-
83
- if (SELFCLOSE.has(tag)) return () => `<${tag}${attrStr} />`;
84
-
85
- const doctype = tag === "html" ? "<!DOCTYPE html>" : "";
86
- return () => `${doctype}<${tag}${attrStr}>${children}</${tag}>`;
87
- };
88
-
89
- const Fragment = "";
90
-
91
- export { jsx, jsx as jsxs, jsx as jsxDEV, Fragment };
1
+ export * from "./jsx-runtime.js";
@@ -0,0 +1,91 @@
1
+ const entities = {
2
+ "&": "&amp;",
3
+ "<": "&lt;",
4
+ ">": "&gt;",
5
+ '"': "&quot;",
6
+ };
7
+
8
+ const encode = (str = "") => {
9
+ if (typeof str === "number") str = String(str);
10
+ if (typeof str !== "string") return "";
11
+ return str.replace(/[&<>"]/g, (tag) => entities[tag]);
12
+ };
13
+
14
+ const SELFCLOSE = new Set(
15
+ "area,base,br,col,embed,hr,img,input,link,meta,source,track,wbr".split(","),
16
+ );
17
+
18
+ const altAttrs = {
19
+ classname: "class",
20
+ };
21
+
22
+ // "" and 0 are valid children, false and null and undefined are not
23
+ const isValidChild = (child) => child || child === "" || child === 0;
24
+
25
+ const escapeCSS = (value) => String(value).replace(/[<>&"'`]/g, "\\$&");
26
+
27
+ const minifyCss = (str) =>
28
+ str
29
+ .replace(/\s+/g, " ")
30
+ .replace(/(?!<")\/\*[^*]+\*\/(?!")/g, "")
31
+ .replace(/(\w|\*) (\{)/g, "$1$2")
32
+ .replace(/(\}) (\w|\*)/g, "$1$2")
33
+ .replace(/(\{) (\w)/g, "$1$2")
34
+ .replace(/(\w)(:) /g, "$1$2")
35
+ .replace(/(;) (\})/g, "$1$2")
36
+ .replace(/(;) (\w)/g, "$1$2")
37
+ .replace(/;(\})/g, "$1")
38
+ .replace(/(\w), (\w)/g, "$1,$2")
39
+ .replace(/(\w), (\w)/g, "$1,$2")
40
+ .replace(/(\{) (\w)/g, "$1$2")
41
+ .trim();
42
+
43
+ const jsx = (tag, { children, ...props }) => {
44
+ if (typeof tag === "function") return tag({ children, ...props });
45
+
46
+ if (tag === "script" && children) {
47
+ const src = children;
48
+ children = () => src;
49
+ }
50
+
51
+ if (tag === "style" && children && typeof children === "string") {
52
+ const src = minifyCss(escapeCSS(children));
53
+ children = () => src;
54
+ }
55
+
56
+ if (props?.dangerouslySetInnerHTML) {
57
+ children = () => props.dangerouslySetInnerHTML.__html;
58
+ }
59
+
60
+ if (!isValidChild(children)) children = [];
61
+ if (typeof children === "string") children = [children];
62
+
63
+ children = (Array.isArray(children) ? children : [children])
64
+ .flat()
65
+ .map((c) => (typeof c === "function" ? c() : encode(c)))
66
+ .join("");
67
+
68
+ if (!tag) return () => children;
69
+
70
+ let attrStr = Object.entries(props || {})
71
+ .filter(([k]) => k !== "dangerouslySetInnerHTML")
72
+ .filter(([k, v]) => !/on[A-Z]/.test(k) && typeof v !== "function")
73
+ .filter(([, v]) => v !== false)
74
+ .map(([k, v]) =>
75
+ v === true
76
+ ? altAttrs[k.toLowerCase()] || encode(k)
77
+ : `${altAttrs[k.toLowerCase()] || encode(k)}="${encode(String(v))}"`,
78
+ )
79
+ .join(" ");
80
+
81
+ if (attrStr) attrStr = ` ${attrStr}`;
82
+
83
+ if (SELFCLOSE.has(tag)) return () => `<${tag}${attrStr} />`;
84
+
85
+ const doctype = tag === "html" ? "<!DOCTYPE html>" : "";
86
+ return () => `${doctype}<${tag}${attrStr}>${children}</${tag}>`;
87
+ };
88
+
89
+ const Fragment = "";
90
+
91
+ export { jsx, jsx as jsxs, jsx as jsxDEV, Fragment };