@revojs/server 0.2.65 → 0.3.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/dist/index.d.ts +15 -0
- package/dist/index.js +90 -31
- package/dist/package.js +3 -3
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,10 @@ export interface FetchOptions extends RequestInit {
|
|
|
27
27
|
json?: unknown;
|
|
28
28
|
forwardHeaders?: Array<string>;
|
|
29
29
|
}
|
|
30
|
+
export interface ApiMiddlewareOptions {
|
|
31
|
+
sameOrigin?: boolean;
|
|
32
|
+
contentTypes?: Array<string>;
|
|
33
|
+
}
|
|
30
34
|
export interface ServerContext {
|
|
31
35
|
states: Record<string, unknown>;
|
|
32
36
|
request: Request;
|
|
@@ -60,6 +64,12 @@ export interface Server extends Route {
|
|
|
60
64
|
schedules: Array<Schedule>;
|
|
61
65
|
schedule: (scope: Scope) => Promise<void>;
|
|
62
66
|
}
|
|
67
|
+
export declare class FetchError<T = unknown> extends Error {
|
|
68
|
+
readonly status: number;
|
|
69
|
+
readonly response: Response;
|
|
70
|
+
readonly data: T | undefined;
|
|
71
|
+
constructor(response: Response, data?: T);
|
|
72
|
+
}
|
|
63
73
|
export declare function defineRoute<T extends Route>(route: T): T;
|
|
64
74
|
export declare function defineMiddleware<T extends Middleware>(middleware: T): T;
|
|
65
75
|
export declare function defineSchedule<T extends Schedule>(schedule: T): T;
|
|
@@ -76,6 +86,7 @@ export declare function createServerScope(request: Request, variables: object, s
|
|
|
76
86
|
[x: string]: {} | undefined;
|
|
77
87
|
} | undefined;
|
|
78
88
|
} & Scope>;
|
|
89
|
+
export declare function waitUntil(scope: Scope, promise: Promise<unknown>): void;
|
|
79
90
|
export declare function useSchedule(scope: Scope): ScheduleContext;
|
|
80
91
|
export declare function useUrl(scope: Scope, base?: string): URL;
|
|
81
92
|
export declare function requireSameOrigin(scope: Scope): void;
|
|
@@ -115,6 +126,9 @@ export declare function setState<T>(scope: Scope, name: string, value: T): void;
|
|
|
115
126
|
export declare function fetch<T>(scope: Scope, input: string | URL, options?: FetchOptions): Promise<T>;
|
|
116
127
|
export declare function invoke(scope: Scope, pipeline: Array<Middleware>, index?: number): Promise<Result>;
|
|
117
128
|
export declare function invokeWebSocket(webSocket: WebSocket, connection: WebSocketConnection, invoke: () => Awaitable<void>): Promise<void>;
|
|
129
|
+
export declare function createApiMiddleware(options?: ApiMiddlewareOptions): {
|
|
130
|
+
fetch(scope: Scope, next: (() => Result) | undefined): Promise<void | Response | WebSocket>;
|
|
131
|
+
};
|
|
118
132
|
export declare function createStaticMiddleware(resolve: (path: string | URL) => Awaitable<Blob>): {
|
|
119
133
|
fetch(scope: Scope, next: (() => Result) | undefined): Promise<void | Response | WebSocket>;
|
|
120
134
|
};
|
|
@@ -122,6 +136,7 @@ export declare function createServer(): Promise<Server>;
|
|
|
122
136
|
export declare const SERVER_CONTEXT: import("revojs").Descriptor<ServerContext>;
|
|
123
137
|
export declare const SERVER_ROUTER_CONTEXT: import("revojs").Descriptor<RouterContext<Route>>;
|
|
124
138
|
export declare const SCHEDULE_CONTEXT: import("revojs").Descriptor<ScheduleContext>;
|
|
139
|
+
export declare const WAIT_UNTIL_CONTEXT: import("revojs").Descriptor<(promise: Promise<unknown>) => void>;
|
|
125
140
|
export declare const cookiePriorities: readonly ["Low", "Medium", "High"];
|
|
126
141
|
export declare const cookieSameSites: readonly ["Lax", "Strict", "None"];
|
|
127
142
|
export declare const httpMethods: readonly ["GET", "HEAD", "PATCH", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE"];
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,18 @@ import { isClient, isServer } from "#variables";
|
|
|
5
5
|
import { Router, STATE_CONTEXT, VARIABLE_CONTEXT, createScope, defineContext, getContext, setContext, useRoute, useVariables } from "revojs";
|
|
6
6
|
import { scheduleAliases } from "revojs/shared";
|
|
7
7
|
//#region src/index.ts
|
|
8
|
+
var FetchError = class extends Error {
|
|
9
|
+
status;
|
|
10
|
+
response;
|
|
11
|
+
data;
|
|
12
|
+
constructor(response, data) {
|
|
13
|
+
super(`Request to ${response.url || "server"} failed with ${response.status}`);
|
|
14
|
+
this.name = "FetchError";
|
|
15
|
+
this.status = response.status;
|
|
16
|
+
this.response = response;
|
|
17
|
+
this.data = data;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
8
20
|
function defineRoute(route) {
|
|
9
21
|
return route;
|
|
10
22
|
}
|
|
@@ -52,6 +64,16 @@ function createServerScope(request, variables, states = {}) {
|
|
|
52
64
|
}
|
|
53
65
|
} });
|
|
54
66
|
}
|
|
67
|
+
function waitUntil(scope, promise) {
|
|
68
|
+
const tracked = (async () => {
|
|
69
|
+
try {
|
|
70
|
+
await promise;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.error(error);
|
|
73
|
+
}
|
|
74
|
+
})();
|
|
75
|
+
if (Object.hasOwn(scope.context, WAIT_UNTIL_CONTEXT)) getContext(scope, WAIT_UNTIL_CONTEXT)(tracked);
|
|
76
|
+
}
|
|
55
77
|
function useSchedule(scope) {
|
|
56
78
|
return getContext(scope, SCHEDULE_CONTEXT);
|
|
57
79
|
}
|
|
@@ -174,10 +196,8 @@ function sendResponse(scope, body, config) {
|
|
|
174
196
|
const { response } = useServer(scope);
|
|
175
197
|
if (body !== null && body !== void 0 && !response.headers.has("Content-Type")) {
|
|
176
198
|
if (typeof body === "object" && !ArrayBuffer.isView(body)) {
|
|
177
|
-
if (body instanceof
|
|
178
|
-
else if (body instanceof URLSearchParams)
|
|
179
|
-
else if (body instanceof ReadableStream) response.headers.set("Content-Type", "application/octet-stream");
|
|
180
|
-
else if (!(body instanceof Blob)) {
|
|
199
|
+
if (body instanceof ReadableStream) response.headers.set("Content-Type", "application/octet-stream");
|
|
200
|
+
else if (!(body instanceof Blob || body instanceof FormData || body instanceof URLSearchParams)) {
|
|
181
201
|
response.headers.set("Content-Type", "application/json");
|
|
182
202
|
body = JSON.stringify(body);
|
|
183
203
|
}
|
|
@@ -256,6 +276,15 @@ function getState(scope, name) {
|
|
|
256
276
|
function setState(scope, name, value) {
|
|
257
277
|
useStates(scope)[name] = value;
|
|
258
278
|
}
|
|
279
|
+
async function readResponse(response) {
|
|
280
|
+
const contentType = response.headers.get("Content-Type")?.split(";").shift()?.trim().toLowerCase() ?? "";
|
|
281
|
+
if (response.status === 204 || response.status === 205 || response.body === null) return;
|
|
282
|
+
if (contentType === "application/json" || contentType.endsWith("+json")) return response.json();
|
|
283
|
+
if (contentType === "" || contentType === "text/event-stream") return response;
|
|
284
|
+
if (contentType.startsWith("text/") || contentType === "application/javascript" || contentType === "application/xml" || contentType.endsWith("+xml")) return response.text();
|
|
285
|
+
if (contentType === "multipart/form-data") return response.formData();
|
|
286
|
+
return response.blob();
|
|
287
|
+
}
|
|
259
288
|
async function fetch(scope, input, options = {}) {
|
|
260
289
|
const { json, forwardHeaders, ...init } = options;
|
|
261
290
|
const headers = new Headers(init.headers);
|
|
@@ -276,33 +305,23 @@ async function fetch(scope, input, options = {}) {
|
|
|
276
305
|
const value = request.headers.get(name);
|
|
277
306
|
if (value !== null && !headers.has(name)) headers.set(name, value);
|
|
278
307
|
}
|
|
279
|
-
const
|
|
308
|
+
const child = await createServerScope(new Request(url, init), useVariables(scope), states);
|
|
309
|
+
if (Object.hasOwn(scope.context, WAIT_UNTIL_CONTEXT)) setContext(child, WAIT_UNTIL_CONTEXT, getContext(scope, WAIT_UNTIL_CONTEXT));
|
|
310
|
+
const result = await (await import("#server")).default.fetch(child);
|
|
280
311
|
if (isResponse(result)) response = result;
|
|
281
312
|
}
|
|
282
313
|
}
|
|
283
314
|
response ??= await globalThis.fetch(input, init);
|
|
284
|
-
if (response.ok
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
case "application/javascript": return response.text();
|
|
293
|
-
case "application/octet-stream":
|
|
294
|
-
case "application/pdf":
|
|
295
|
-
case "application/zip":
|
|
296
|
-
case "image/png":
|
|
297
|
-
case "image/jpeg":
|
|
298
|
-
case "image/gif":
|
|
299
|
-
case "image/webp":
|
|
300
|
-
case "image/svg+xml":
|
|
301
|
-
case "audio/mpeg":
|
|
302
|
-
case "video/mp4": return response.blob();
|
|
303
|
-
case "multipart/form-data": return response.formData();
|
|
304
|
-
default: return response;
|
|
315
|
+
if (!response.ok) {
|
|
316
|
+
let data;
|
|
317
|
+
try {
|
|
318
|
+
data = await readResponse(response.clone());
|
|
319
|
+
} catch {
|
|
320
|
+
data = void 0;
|
|
321
|
+
}
|
|
322
|
+
throw new FetchError(response, data);
|
|
305
323
|
}
|
|
324
|
+
return await readResponse(response);
|
|
306
325
|
}
|
|
307
326
|
async function invoke(scope, pipeline, index = 0) {
|
|
308
327
|
return await pipeline.at(index)?.fetch(scope, async () => await invoke(scope, pipeline, index + 1));
|
|
@@ -311,10 +330,31 @@ async function invokeWebSocket(webSocket, connection, invoke) {
|
|
|
311
330
|
try {
|
|
312
331
|
await invoke();
|
|
313
332
|
} catch (error) {
|
|
314
|
-
|
|
315
|
-
|
|
333
|
+
let cause = new Error(String(error), { cause: error });
|
|
334
|
+
if (error instanceof Error) cause = error;
|
|
335
|
+
try {
|
|
336
|
+
await webSocket.error(connection, cause);
|
|
337
|
+
} catch (failure) {
|
|
338
|
+
console.error(failure);
|
|
339
|
+
}
|
|
316
340
|
}
|
|
317
341
|
}
|
|
342
|
+
function createApiMiddleware(options = {}) {
|
|
343
|
+
return defineMiddleware({ async fetch(scope, next) {
|
|
344
|
+
const { request, response } = useServer(scope);
|
|
345
|
+
response.headers.set("Cache-Control", "no-store");
|
|
346
|
+
if (![
|
|
347
|
+
"GET",
|
|
348
|
+
"HEAD",
|
|
349
|
+
"OPTIONS"
|
|
350
|
+
].includes(request.method)) {
|
|
351
|
+
if (options.sameOrigin !== false) requireSameOrigin(scope);
|
|
352
|
+
const contentType = request.headers.get("Content-Type")?.split(";").shift()?.trim().toLowerCase();
|
|
353
|
+
if (options.contentTypes && request.body !== null && (contentType === void 0 || !options.contentTypes.includes(contentType))) return sendResponse(scope, `Content-Type must be ${options.contentTypes.join(" or ")}`, { status: 415 });
|
|
354
|
+
}
|
|
355
|
+
return next?.();
|
|
356
|
+
} });
|
|
357
|
+
}
|
|
318
358
|
function createStaticMiddleware(resolve) {
|
|
319
359
|
return defineMiddleware({ async fetch(scope, next) {
|
|
320
360
|
const { pathname } = useUrl(scope);
|
|
@@ -345,7 +385,18 @@ async function createServer() {
|
|
|
345
385
|
const segments = path.split("/");
|
|
346
386
|
for (const attribute of segments.pop()?.split(".") ?? []) if (httpMethods.includes(attribute.toUpperCase())) segments.unshift(attribute);
|
|
347
387
|
else segments.push(attribute);
|
|
348
|
-
|
|
388
|
+
let loading;
|
|
389
|
+
const route = { async fetch(scope, next) {
|
|
390
|
+
loading ??= resolve();
|
|
391
|
+
let loaded;
|
|
392
|
+
try {
|
|
393
|
+
loaded = await loading;
|
|
394
|
+
} catch (error) {
|
|
395
|
+
loading = void 0;
|
|
396
|
+
throw error;
|
|
397
|
+
}
|
|
398
|
+
return await loaded?.fetch(scope, next);
|
|
399
|
+
} };
|
|
349
400
|
if (segments.at(-1)?.startsWith("+")) for (const method of httpMethods) router.use([method.toLowerCase(), ...segments], route);
|
|
350
401
|
else router.use(segments, route);
|
|
351
402
|
}
|
|
@@ -371,16 +422,24 @@ async function createServer() {
|
|
|
371
422
|
async schedule(scope) {
|
|
372
423
|
const { when } = useSchedule(scope);
|
|
373
424
|
const expression = scheduleAliases[when.trim().toLowerCase()] ?? when.trim();
|
|
425
|
+
const errors = new Array();
|
|
374
426
|
for (const schedule of scheduled) {
|
|
375
427
|
if ((scheduleAliases[schedule.when.trim().toLowerCase()] ?? schedule.when.trim()) !== expression) continue;
|
|
376
|
-
|
|
428
|
+
try {
|
|
429
|
+
await schedule.run(scope);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
console.error(error);
|
|
432
|
+
errors.push(error);
|
|
433
|
+
}
|
|
377
434
|
}
|
|
435
|
+
if (errors.length > 0) throw new AggregateError(errors, `${errors.length} schedules failed for "${when}"`);
|
|
378
436
|
}
|
|
379
437
|
};
|
|
380
438
|
}
|
|
381
439
|
var SERVER_CONTEXT = defineContext("SERVER_CONTEXT");
|
|
382
440
|
var SERVER_ROUTER_CONTEXT = defineContext("SERVER_ROUTER_CONTEXT");
|
|
383
441
|
var SCHEDULE_CONTEXT = defineContext("SCHEDULE_CONTEXT");
|
|
442
|
+
var WAIT_UNTIL_CONTEXT = defineContext("WAIT_UNTIL_CONTEXT");
|
|
384
443
|
var cookiePriorities = [
|
|
385
444
|
"Low",
|
|
386
445
|
"Medium",
|
|
@@ -490,4 +549,4 @@ var statusCodes = [
|
|
|
490
549
|
599
|
|
491
550
|
];
|
|
492
551
|
//#endregion
|
|
493
|
-
export { SCHEDULE_CONTEXT, SERVER_CONTEXT, SERVER_ROUTER_CONTEXT, cookiePriorities, cookieSameSites, createServer, createServerScope, createStaticMiddleware, createWebSocket, defineMiddleware, defineRoute, defineSchedule, deleteCookie, encodings, fetch, getState, httpMethods, invoke, invokeWebSocket, isResponse, isWebSocket, parseCookiePair, requireSameOrigin, sendBadRequest, sendCreated, sendForbidden, sendNoContent, sendNotFound, sendOk, sendRedirect, sendResponse, sendUnauthorized, setCookie, setState, statusCodes, useBody, useBodyAsync, useCookies, useCookiesAsync, useHeaders, useHeadersAsync, useParameters, useParametersAsync, useQuery, useQueryAsync, useRouter, useSchedule, useServer, useSetCookies, useSetCookiesAsync, useUrl, withQuery };
|
|
552
|
+
export { FetchError, SCHEDULE_CONTEXT, SERVER_CONTEXT, SERVER_ROUTER_CONTEXT, WAIT_UNTIL_CONTEXT, cookiePriorities, cookieSameSites, createApiMiddleware, createServer, createServerScope, createStaticMiddleware, createWebSocket, defineMiddleware, defineRoute, defineSchedule, deleteCookie, encodings, fetch, getState, httpMethods, invoke, invokeWebSocket, isResponse, isWebSocket, parseCookiePair, requireSameOrigin, sendBadRequest, sendCreated, sendForbidden, sendNoContent, sendNotFound, sendOk, sendRedirect, sendResponse, sendUnauthorized, setCookie, setState, statusCodes, useBody, useBodyAsync, useCookies, useCookiesAsync, useHeaders, useHeadersAsync, useParameters, useParametersAsync, useQuery, useQueryAsync, useRouter, useSchedule, useServer, useSetCookies, useSetCookiesAsync, useUrl, waitUntil, withQuery };
|
package/dist/package.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region package.json
|
|
2
2
|
var name = "@revojs/server";
|
|
3
|
-
var version = "0.
|
|
3
|
+
var version = "0.3.0";
|
|
4
4
|
var repository = {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/tellua/revojs.git"
|
|
@@ -28,11 +28,11 @@ var scripts = {
|
|
|
28
28
|
var dependencies = {
|
|
29
29
|
"@standard-schema/spec": "^1.1.0",
|
|
30
30
|
"croner": "^10.0.1",
|
|
31
|
-
"revojs": "
|
|
31
|
+
"revojs": "0.3.0",
|
|
32
32
|
"ws": "^8.22.0"
|
|
33
33
|
};
|
|
34
34
|
var devDependencies = {
|
|
35
|
-
"@revojs/tsconfig": "
|
|
35
|
+
"@revojs/tsconfig": "0.3.0",
|
|
36
36
|
"@types/ws": "^8.18.2"
|
|
37
37
|
};
|
|
38
38
|
var package_default = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@revojs/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,11 +31,11 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@standard-schema/spec": "^1.1.0",
|
|
33
33
|
"croner": "^10.0.1",
|
|
34
|
-
"revojs": "
|
|
34
|
+
"revojs": "0.3.0",
|
|
35
35
|
"ws": "^8.22.0"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@revojs/tsconfig": "
|
|
38
|
+
"@revojs/tsconfig": "0.3.0",
|
|
39
39
|
"@types/ws": "^8.18.2"
|
|
40
40
|
}
|
|
41
41
|
}
|