@cfworker/web 5.0.0 → 5.0.2

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.
Files changed (54) hide show
  1. package/dist/{types → commonjs}/normalize-pathname.d.ts +1 -1
  2. package/dist/{cjs → commonjs}/normalize-pathname.js +2 -2
  3. package/dist/commonjs/package.json +3 -0
  4. package/dist/esm/accepts.d.ts +12 -0
  5. package/dist/esm/application.d.ts +11 -0
  6. package/dist/esm/context.d.ts +15 -0
  7. package/dist/esm/cookies.d.ts +8 -0
  8. package/dist/esm/http-error.d.ts +7 -0
  9. package/dist/esm/middleware.d.ts +3 -0
  10. package/dist/esm/normalize-pathname.d.ts +3 -0
  11. package/dist/esm/normalize-pathname.js +1 -1
  12. package/dist/esm/package.json +3 -0
  13. package/dist/esm/req.d.ts +24 -0
  14. package/dist/esm/response-builder.d.ts +22 -0
  15. package/dist/esm/router.d.ts +39 -0
  16. package/dist/esm/validate.d.ts +21 -0
  17. package/package.json +34 -30
  18. package/dist/tsconfig.cjs.tsbuildinfo +0 -1
  19. package/dist/tsconfig.tsbuildinfo +0 -1
  20. package/src/accepts.ts +0 -87
  21. package/src/application.ts +0 -55
  22. package/src/context.ts +0 -27
  23. package/src/cookies.ts +0 -30
  24. package/src/declarations.d.ts +0 -10
  25. package/src/http-error.ts +0 -36
  26. package/src/middleware.ts +0 -34
  27. package/src/normalize-pathname.ts +0 -22
  28. package/src/req.ts +0 -59
  29. package/src/response-builder.ts +0 -158
  30. package/src/router.ts +0 -146
  31. package/src/validate.ts +0 -121
  32. /package/dist/{types → commonjs}/accepts.d.ts +0 -0
  33. /package/dist/{cjs → commonjs}/accepts.js +0 -0
  34. /package/dist/{types → commonjs}/application.d.ts +0 -0
  35. /package/dist/{cjs → commonjs}/application.js +0 -0
  36. /package/dist/{types → commonjs}/context.d.ts +0 -0
  37. /package/dist/{cjs → commonjs}/context.js +0 -0
  38. /package/dist/{types → commonjs}/cookies.d.ts +0 -0
  39. /package/dist/{cjs → commonjs}/cookies.js +0 -0
  40. /package/dist/{types → commonjs}/http-error.d.ts +0 -0
  41. /package/dist/{cjs → commonjs}/http-error.js +0 -0
  42. /package/dist/{types → commonjs}/index.d.ts +0 -0
  43. /package/dist/{cjs → commonjs}/index.js +0 -0
  44. /package/dist/{types → commonjs}/middleware.d.ts +0 -0
  45. /package/dist/{cjs → commonjs}/middleware.js +0 -0
  46. /package/dist/{types → commonjs}/req.d.ts +0 -0
  47. /package/dist/{cjs → commonjs}/req.js +0 -0
  48. /package/dist/{types → commonjs}/response-builder.d.ts +0 -0
  49. /package/dist/{cjs → commonjs}/response-builder.js +0 -0
  50. /package/dist/{types → commonjs}/router.d.ts +0 -0
  51. /package/dist/{cjs → commonjs}/router.js +0 -0
  52. /package/dist/{types → commonjs}/validate.d.ts +0 -0
  53. /package/dist/{cjs → commonjs}/validate.js +0 -0
  54. /package/{src/index.ts → dist/esm/index.d.ts} +0 -0
package/src/cookies.ts DELETED
@@ -1,30 +0,0 @@
1
- import {
2
- parse as parseCookie,
3
- serialize as serializeCookie,
4
- SerializeOptions
5
- } from 'cookie';
6
-
7
- const noCookies = Object.create(null);
8
-
9
- export class Cookies {
10
- private readonly requestCookies: Record<string, string>;
11
-
12
- constructor(
13
- requestHeaders: Headers,
14
- private readonly responseHeaders: Headers
15
- ) {
16
- const cookie = requestHeaders.get('cookie');
17
- this.requestCookies = cookie ? parseCookie(cookie) : noCookies;
18
- }
19
-
20
- public get(name: string): string | null {
21
- return this.requestCookies[name] || null;
22
- }
23
-
24
- public set(name: string, val: string, options?: SerializeOptions): void {
25
- this.responseHeaders.append(
26
- 'Set-Cookie',
27
- serializeCookie(name, val, options)
28
- );
29
- }
30
- }
@@ -1,10 +0,0 @@
1
- // secure-json-parse's types rely on node's Buffer
2
- type Buffer = never;
3
-
4
- interface WorkerGlobalScopeEventMap {
5
- fetch: FetchEvent;
6
- }
7
-
8
- interface ErrorConstructor {
9
- captureStackTrace(thisArg: any, func: any): void;
10
- }
package/src/http-error.ts DELETED
@@ -1,36 +0,0 @@
1
- import statuses from 'statuses';
2
-
3
- export class HttpError extends Error {
4
- public readonly status: number;
5
- public readonly statusText: string;
6
- public readonly body: string | object;
7
-
8
- constructor(status: number, body: string | object) {
9
- const statusText = statuses.message[status]!;
10
-
11
- super(statusText);
12
-
13
- this.name = this.constructor.name;
14
- if (typeof Error.captureStackTrace === 'function') {
15
- Error.captureStackTrace(this, this.constructor);
16
- }
17
-
18
- this.status = status;
19
- this.statusText = statusText;
20
- this.body = body;
21
- }
22
-
23
- public toResponse(): Response {
24
- let body = this.body || this.statusText;
25
- let contentType: string;
26
- if (typeof body === 'string') {
27
- contentType = 'text/plain';
28
- } else {
29
- contentType = 'application/json';
30
- body = JSON.stringify(body);
31
- }
32
- const { status, statusText } = this;
33
- const headers = { 'content-type': contentType };
34
- return new Response(body, { status, statusText, headers });
35
- }
36
- }
package/src/middleware.ts DELETED
@@ -1,34 +0,0 @@
1
- import { Context } from './context.js';
2
-
3
- export type Middleware = (
4
- context: Context,
5
- next: () => Promise<void>
6
- ) => Promise<void> | void;
7
-
8
- export function composeMiddleware(middleware: Middleware[]): Middleware {
9
- if (!Array.isArray(middleware))
10
- throw new TypeError('Middleware stack must be an array!');
11
- for (const fn of middleware) {
12
- if (typeof fn !== 'function')
13
- throw new TypeError('Middleware must be composed of functions!');
14
- }
15
-
16
- return function (context, next) {
17
- // last called middleware #
18
- let index = -1;
19
- return dispatch(0);
20
- function dispatch(i: number): Promise<void> {
21
- if (i <= index)
22
- return Promise.reject(new Error('next() called multiple times'));
23
- index = i;
24
- let fn = middleware[i];
25
- if (i === middleware.length) fn = next;
26
- if (!fn) return Promise.resolve();
27
- try {
28
- return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
29
- } catch (err) {
30
- return Promise.reject(err);
31
- }
32
- }
33
- };
34
- }
@@ -1,22 +0,0 @@
1
- import { HttpError } from './http-error';
2
- import { Middleware } from './middleware';
3
-
4
- export function normalizePathname(pathname: string): string {
5
- return decodeURIComponent(pathname).replace(/\/\/+/g, '/').normalize();
6
- }
7
-
8
- export const normalizePathnameMiddleware: Middleware = async (
9
- { req },
10
- next
11
- ) => {
12
- const pathname = req.url.pathname;
13
- try {
14
- req.url.pathname = normalizePathname(pathname);
15
- } catch (err) {
16
- if (err instanceof URIError) {
17
- throw new HttpError(400, `Unable to decode pathname "${pathname}".`);
18
- }
19
- throw err;
20
- }
21
- await next();
22
- };
package/src/req.ts DELETED
@@ -1,59 +0,0 @@
1
- import { Reviver, safeParse } from 'secure-json-parse';
2
- import { Accepts } from './accepts.js';
3
-
4
- export class Req {
5
- public readonly raw: Request;
6
- public readonly method: string;
7
- public readonly url: URL;
8
- public readonly headers: Headers;
9
- public readonly params: Record<string, string>;
10
- public readonly accepts: Accepts;
11
- public readonly body: ReqBody;
12
-
13
- constructor(request: Request) {
14
- this.raw = request;
15
- this.method = request.method;
16
- this.url = new URL(request.url);
17
- this.headers = request.headers;
18
- this.params = Object.create(null);
19
- this.accepts = new Accepts(request.headers);
20
- this.body = new ReqBody(request);
21
- }
22
- }
23
-
24
- export class ReqBody {
25
- private _arrayBuffer: Promise<ArrayBuffer> | undefined;
26
- private _formData: Promise<FormData> | undefined;
27
- private _json: Promise<any> | undefined;
28
- private _text: Promise<string> | undefined;
29
-
30
- constructor(private readonly request: Request) {}
31
-
32
- public arrayBuffer(): Promise<ArrayBuffer> {
33
- if (!this._arrayBuffer) {
34
- this._arrayBuffer = this.request.arrayBuffer();
35
- }
36
- return this._arrayBuffer;
37
- }
38
-
39
- public formData(): Promise<FormData> {
40
- if (!this._formData) {
41
- this._formData = this.request.formData();
42
- }
43
- return this._formData;
44
- }
45
-
46
- public json(reviver?: Reviver): Promise<any> {
47
- if (!this._json) {
48
- this._json = this.text().then(text => safeParse(text, reviver));
49
- }
50
- return this._json;
51
- }
52
-
53
- public text(): Promise<string> {
54
- if (!this._text) {
55
- this._text = this.request.text();
56
- }
57
- return this._text;
58
- }
59
- }
@@ -1,158 +0,0 @@
1
- import statuses from 'statuses';
2
-
3
- export type ExtendedBodyInit =
4
- | BodyInit
5
- | boolean
6
- | Date
7
- | number
8
- | object
9
- | null;
10
-
11
- export class ResponseBuilder {
12
- public readonly headers: Headers = new Headers();
13
- private _status = 404;
14
- private _explicitStatus = false;
15
- private _implicitType = false;
16
- private _body: ExtendedBodyInit = null;
17
- private _stringifyBody = false;
18
-
19
- get status(): number {
20
- return this._status;
21
- }
22
- set status(value) {
23
- this._explicitStatus = true;
24
- this._status = value;
25
- if (this.body && statuses.empty[value]) {
26
- this.body = null;
27
- }
28
- }
29
-
30
- get statusText(): string {
31
- return statuses.message[this._status]!;
32
- }
33
-
34
- get body() {
35
- return this._body;
36
- }
37
- set body(value: ExtendedBodyInit | null) {
38
- this._body = value;
39
-
40
- // no content
41
- if (value === null) {
42
- if (!statuses.empty[this.status]) {
43
- this._status = 204;
44
- }
45
- this.headers.delete('content-type');
46
- this.headers.delete('content-length');
47
- this.headers.delete('transfer-encoding');
48
- return;
49
- }
50
-
51
- // set the status
52
- if (!this._explicitStatus) {
53
- this._status = 200;
54
- }
55
-
56
- // set Content-Type
57
- if (
58
- // Response will automatically use Blob's type.
59
- value instanceof Blob ||
60
- // Response will automatically use multipart/form-data.
61
- value instanceof FormData ||
62
- // Response will automatically use application/x-www-form-urlencoded.
63
- value instanceof URLSearchParams ||
64
- // Cannot determine Content-Type.
65
- ArrayBuffer.isView(value) ||
66
- value instanceof ArrayBuffer ||
67
- value instanceof ReadableStream
68
- ) {
69
- this._stringifyBody = false;
70
- if (this._implicitType) {
71
- this._implicitType = false;
72
- this.headers.delete('content-type');
73
- }
74
- } else if (typeof value === 'string') {
75
- this._stringifyBody = false;
76
- if (!this.headers.has('content-type') || this._implicitType) {
77
- this._implicitType = true;
78
- if (/^\s*</.test(value)) {
79
- this.headers.set('content-type', 'text/html;charset=UTF-8');
80
- } else {
81
- this.headers.set('content-type', 'text/plain;charset=UTF-8');
82
- }
83
- }
84
- } else {
85
- this._stringifyBody = true;
86
- this._implicitType = true;
87
- this.headers.set('content-type', 'application/json;charset=UTF-8');
88
- }
89
- }
90
-
91
- redirect(url: string | URL): void {
92
- if (url instanceof URL) {
93
- url = url.href;
94
- }
95
-
96
- this.headers.set('location', url);
97
-
98
- if (!statuses.redirect[this.status]) {
99
- this.status = 302;
100
- }
101
-
102
- this.type = 'text/plain;charset=UTF-8';
103
- this.body = `Redirecting to ${url}.`;
104
- }
105
-
106
- get type(): string {
107
- const type = this.headers.get('content-type');
108
- if (!type) {
109
- return '';
110
- }
111
- return type.split(';', 1)[0];
112
- }
113
- set type(value) {
114
- this._implicitType = false;
115
- if (value) {
116
- this.headers.set('content-type', value);
117
- } else {
118
- this.headers.delete('content-type');
119
- }
120
- }
121
-
122
- get lastModified() {
123
- const date = this.headers.get('last-modified');
124
- return date ? new Date(date) : null;
125
- }
126
- set lastModified(value: string | Date | null) {
127
- if (value === null) {
128
- this.headers.delete('last-modified');
129
- return;
130
- }
131
- if (typeof value === 'string') {
132
- value = new Date(value);
133
- }
134
- this.headers.set('last-modified', value.toUTCString());
135
- }
136
-
137
- get etag(): string | null {
138
- return this.headers.get('etag');
139
- }
140
- set etag(value) {
141
- if (value) {
142
- if (!/^(W\/)?"/.test(value)) {
143
- value = `"${value}"`;
144
- }
145
- this.headers.set('etag', value);
146
- } else {
147
- this.headers.delete('etag');
148
- }
149
- }
150
-
151
- create(): Response {
152
- const { body: rawBody, status, statusText, headers } = this;
153
- const body = this._stringifyBody
154
- ? JSON.stringify(rawBody)
155
- : (rawBody as BodyInit);
156
- return new Response(body, { status, statusText, headers });
157
- }
158
- }
package/src/router.ts DELETED
@@ -1,146 +0,0 @@
1
- import { Key, pathToRegexp, PathToRegexpOptions } from 'path-to-regexp';
2
- import { Context } from './context.js';
3
- import { composeMiddleware, Middleware } from './middleware.js';
4
-
5
- export type Predicate = (context: Context) => boolean;
6
-
7
- export const Method: (method: string) => Predicate = (method: string) => {
8
- method = method.toUpperCase();
9
- return ({ req }: Context) => req.method === method;
10
- };
11
- export const Get: Predicate = Method('get');
12
- export const Post: Predicate = Method('post');
13
- export const Put: Predicate = Method('put');
14
- export const Patch: Predicate = Method('patch');
15
- export const Delete: Predicate = Method('delete');
16
- export const Head: Predicate = Method('head');
17
- export const Options: Predicate = Method('options');
18
-
19
- export const Header: (header: string, value: string) => Predicate = (
20
- header: string,
21
- value: string
22
- ) => {
23
- value = value.toLowerCase();
24
- return ({ req }: Context) => req.headers.get(header) === value;
25
- };
26
- export const Host: (host: string) => Predicate = (host: string) =>
27
- Header('host', host);
28
-
29
- export const Path: (
30
- pattern: string,
31
- options?: PathToRegexpOptions
32
- ) => Predicate = (pattern: string, options?: PathToRegexpOptions) => {
33
- const regexp = pathToRegexp(pattern, options);
34
-
35
- return ({ req: { url, params } }: Context) => {
36
- const match = url.pathname.match(regexp);
37
- if (!match) {
38
- return false;
39
- }
40
- collectParameters(regexp.keys, match, params);
41
- return true;
42
- };
43
- };
44
-
45
- export type RouteCondition = (context: Context) => boolean;
46
-
47
- export interface Route {
48
- conditions: RouteCondition[];
49
- middleware: Middleware;
50
- }
51
-
52
- export interface RouterOptions {
53
- pathToRegExpOptions?: PathToRegexpOptions;
54
- }
55
-
56
- export const defaultRouterOptions: RouterOptions = {
57
- pathToRegExpOptions: {
58
- strict: true
59
- }
60
- };
61
-
62
- export class Router {
63
- private readonly routes: Route[];
64
-
65
- constructor(private readonly options: RouterOptions = defaultRouterOptions) {
66
- this.routes = [];
67
- }
68
-
69
- public get(pathname: string, ...middleware: Middleware[]): Router {
70
- const opts = this.options.pathToRegExpOptions;
71
- return this.compose([Get, Path(pathname, opts)], ...middleware);
72
- }
73
-
74
- public post(pathname: string, ...middleware: Middleware[]): Router {
75
- const opts = this.options.pathToRegExpOptions;
76
- return this.compose([Post, Path(pathname, opts)], ...middleware);
77
- }
78
-
79
- public put(pathname: string, ...middleware: Middleware[]): Router {
80
- const opts = this.options.pathToRegExpOptions;
81
- return this.compose([Put, Path(pathname, opts)], ...middleware);
82
- }
83
-
84
- public patch(pathname: string, ...middleware: Middleware[]): Router {
85
- const opts = this.options.pathToRegExpOptions;
86
- return this.compose([Patch, Path(pathname, opts)], ...middleware);
87
- }
88
-
89
- public delete(pathname: string, ...middleware: Middleware[]): Router {
90
- const opts = this.options.pathToRegExpOptions;
91
- return this.compose([Delete, Path(pathname, opts)], ...middleware);
92
- }
93
-
94
- public head(pathname: string, ...middleware: Middleware[]): Router {
95
- const opts = this.options.pathToRegExpOptions;
96
- return this.compose([Head, Path(pathname, opts)], ...middleware);
97
- }
98
-
99
- public all(pathname: string, ...middleware: Middleware[]): Router {
100
- const opts = this.options.pathToRegExpOptions;
101
- return this.compose([Path(pathname, opts)], ...middleware);
102
- }
103
-
104
- public middleware: Middleware = async (ctx, next) => {
105
- const resolved = this.resolve(ctx);
106
-
107
- if (resolved) {
108
- await resolved.middleware(ctx, next);
109
- } else {
110
- await next();
111
- }
112
- };
113
-
114
- public compose(
115
- conditions: RouteCondition[],
116
- ...middleware: Middleware[]
117
- ): Router {
118
- this.routes.push({
119
- conditions,
120
- middleware: composeMiddleware(middleware)
121
- });
122
- return this;
123
- }
124
-
125
- public resolve(ctx: Context): Route | undefined {
126
- return this.routes.find(
127
- ({ conditions }) =>
128
- conditions.length === 0 || conditions.every(c => c(ctx))
129
- );
130
- }
131
- }
132
-
133
- function collectParameters(
134
- keys: Key[],
135
- match: RegExpMatchArray,
136
- params: Record<string, string>
137
- ) {
138
- for (let i = 1; i < match.length; i++) {
139
- const name = keys[i - 1].name;
140
- const value = match[i];
141
- if (!value) {
142
- continue;
143
- }
144
- params[name] = decodeURIComponent(value);
145
- }
146
- }
package/src/validate.ts DELETED
@@ -1,121 +0,0 @@
1
- import {
2
- dereference,
3
- OutputUnit,
4
- Schema,
5
- validate as schemaValidate
6
- } from '@cfworker/json-schema';
7
- import { HttpError } from './http-error.js';
8
- import { Middleware } from './middleware.js';
9
-
10
- export interface RequestSchemas {
11
- body?: Schema;
12
- headers?: Schema;
13
- params?: Schema;
14
- search?: Schema;
15
- }
16
-
17
- export type RequestPart = keyof RequestSchemas;
18
-
19
- export type RequestParser = (data: URLSearchParams | FormData) => any;
20
-
21
- const draft = '2019-09';
22
-
23
- const hasBody: Record<string, true> = {
24
- POST: true,
25
- PUT: true,
26
- DELETE: true,
27
- PATCH: true
28
- };
29
-
30
- function middlewareFactory(
31
- schemas: RequestSchemas,
32
- parser: RequestParser = Object.fromEntries,
33
- lookup: Record<string, Schema | boolean> = Object.create(null)
34
- ): Middleware {
35
- const {
36
- headers: $headers,
37
- search: $search,
38
- params: $params,
39
- body: $body
40
- } = schemas;
41
-
42
- if ($headers) {
43
- dereference($headers, { ...lookup });
44
- }
45
- if ($search) {
46
- dereference($search, { ...lookup });
47
- }
48
- if ($params) {
49
- dereference($params, { ...lookup });
50
- }
51
- if ($body) {
52
- dereference($body, { ...lookup });
53
- }
54
-
55
- return async ({ req }, next) => {
56
- if ($params) {
57
- validateRequestPart('params', req.params, $params, lookup);
58
- }
59
- if ($headers) {
60
- const headers = Object.fromEntries(req.headers);
61
- validateRequestPart('headers', headers, $headers, lookup);
62
- }
63
- if ($search) {
64
- const search = parser(req.url.searchParams);
65
- validateRequestPart('search', search, $search, lookup);
66
- }
67
- if ($body && hasBody[req.method]) {
68
- let body: any;
69
- const contentType = req.headers.get('content-type') || '';
70
- if (
71
- contentType === 'application/x-www-form-urlencoded' ||
72
- contentType.startsWith('application/x-www-form-urlencoded;') ||
73
- contentType.startsWith('multipart/form-data;')
74
- ) {
75
- const form = await req.body.formData();
76
- body = parser(form);
77
- } else {
78
- body = await req.body.json();
79
- }
80
- validateRequestPart('body', body, $body, lookup);
81
- }
82
- await next();
83
- };
84
- }
85
-
86
- export const messages: Record<RequestPart, string> = {
87
- params: 'Parameters in request path are invalid.',
88
- headers: 'Headers are invalid.',
89
- search: 'Query string parameters are invalid.',
90
- body: 'Body is invalid.'
91
- };
92
-
93
- export interface RequestValidationErrorBody {
94
- status: 400;
95
- part: RequestPart;
96
- message: string;
97
- errors: OutputUnit[];
98
- schema: Schema;
99
- }
100
-
101
- function validateRequestPart(
102
- part: RequestPart,
103
- instance: any,
104
- schema: Schema,
105
- lookup: Record<string, boolean | Schema>
106
- ) {
107
- const result = schemaValidate(instance, schema, draft, lookup);
108
- if (result.valid) {
109
- return;
110
- }
111
- const errorBody: RequestValidationErrorBody = {
112
- status: 400,
113
- part,
114
- message: messages[part],
115
- errors: result.errors,
116
- schema
117
- };
118
- throw new HttpError(400, errorBody);
119
- }
120
-
121
- export const validate: typeof middlewareFactory = middlewareFactory;
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes