@weifuwujs/weifuwu 0.9.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/README.md +605 -0
- package/dist/app.d.ts +122 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +484 -0
- package/dist/app.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +506 -0
- package/dist/cli.js.map +1 -0
- package/dist/compose.d.ts +12 -0
- package/dist/compose.d.ts.map +1 -0
- package/dist/compose.js +29 -0
- package/dist/compose.js.map +1 -0
- package/dist/env.d.ts +12 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +46 -0
- package/dist/env.js.map +1 -0
- package/dist/group.d.ts +32 -0
- package/dist/group.d.ts.map +1 -0
- package/dist/group.js +66 -0
- package/dist/group.js.map +1 -0
- package/dist/helpers.d.ts +37 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +101 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/ioredis.d.ts +16 -0
- package/dist/ioredis.d.ts.map +1 -0
- package/dist/ioredis.js +42 -0
- package/dist/ioredis.js.map +1 -0
- package/dist/methods.d.ts +3 -0
- package/dist/methods.d.ts.map +1 -0
- package/dist/methods.js +10 -0
- package/dist/methods.js.map +1 -0
- package/dist/middleware/cors.d.ts +24 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/cors.js +77 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/index.d.ts +7 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +4 -0
- package/dist/middleware/index.js.map +1 -0
- package/dist/middleware/logger.d.ts +27 -0
- package/dist/middleware/logger.d.ts.map +1 -0
- package/dist/middleware/logger.js +48 -0
- package/dist/middleware/logger.js.map +1 -0
- package/dist/middleware/static.d.ts +27 -0
- package/dist/middleware/static.d.ts.map +1 -0
- package/dist/middleware/static.js +150 -0
- package/dist/middleware/static.js.map +1 -0
- package/dist/node.d.ts +27 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +193 -0
- package/dist/node.js.map +1 -0
- package/dist/path.d.ts +9 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +31 -0
- package/dist/path.js.map +1 -0
- package/dist/postgres.d.ts +19 -0
- package/dist/postgres.d.ts.map +1 -0
- package/dist/postgres.js +27 -0
- package/dist/postgres.js.map +1 -0
- package/dist/react-router.d.ts +31 -0
- package/dist/react-router.d.ts.map +1 -0
- package/dist/react-router.js +25 -0
- package/dist/react-router.js.map +1 -0
- package/dist/resources.d.ts +35 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +54 -0
- package/dist/resources.js.map +1 -0
- package/dist/router.d.ts +30 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +122 -0
- package/dist/router.js.map +1 -0
- package/dist/types.d.ts +124 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/vite.d.ts +21 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +101 -0
- package/dist/vite.js.map +1 -0
- package/dist/websocket/accept.d.ts +22 -0
- package/dist/websocket/accept.d.ts.map +1 -0
- package/dist/websocket/accept.js +104 -0
- package/dist/websocket/accept.js.map +1 -0
- package/dist/websocket/close-code.d.ts +22 -0
- package/dist/websocket/close-code.d.ts.map +1 -0
- package/dist/websocket/close-code.js +35 -0
- package/dist/websocket/close-code.js.map +1 -0
- package/dist/websocket/connection.d.ts +71 -0
- package/dist/websocket/connection.d.ts.map +1 -0
- package/dist/websocket/connection.js +287 -0
- package/dist/websocket/connection.js.map +1 -0
- package/dist/websocket/index.d.ts +7 -0
- package/dist/websocket/index.d.ts.map +1 -0
- package/dist/websocket/index.js +4 -0
- package/dist/websocket/index.js.map +1 -0
- package/dist/websocket/types.d.ts +66 -0
- package/dist/websocket/types.d.ts.map +1 -0
- package/dist/websocket/types.js +2 -0
- package/dist/websocket/types.js.map +1 -0
- package/docs/ai.md +175 -0
- package/docs/data.md +81 -0
- package/docs/migrating.md +116 -0
- package/docs/react-router.md +187 -0
- package/docs/styling.md +69 -0
- package/package.json +144 -0
- package/templates/react-router/_gitignore +3 -0
- package/templates/react-router/app/app.css +1 -0
- package/templates/react-router/app/context.ts +4 -0
- package/templates/react-router/app/root.tsx +50 -0
- package/templates/react-router/app/routes/home.tsx +17 -0
- package/templates/react-router/app/routes.ts +3 -0
- package/templates/react-router/app/server.ts +29 -0
- package/templates/react-router/package.json +29 -0
- package/templates/react-router/react-router.config.ts +5 -0
- package/templates/react-router/tsconfig.json +19 -0
- package/templates/react-router/vite.config.ts +6 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** `App.use()` 识别该 symbol 上的 dispose 并注册到 `app.close()`(内部协议) */
|
|
2
|
+
export const RESOURCE_DISPOSE = Symbol('weifuwu.resource.dispose');
|
|
3
|
+
/**
|
|
4
|
+
* 懒初始化的共享资源中间件。
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* const db = resource('db', () => openDatabase(), { close: (db) => db.end() })
|
|
8
|
+
* const app = createApp().use(db)
|
|
9
|
+
* app.get('/', (req, ctx) => json(ctx.db.query()))
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export function resource(key, create, options = {}) {
|
|
13
|
+
let value;
|
|
14
|
+
let created = false;
|
|
15
|
+
let pending;
|
|
16
|
+
const load = () => {
|
|
17
|
+
if (created)
|
|
18
|
+
return Promise.resolve(value);
|
|
19
|
+
pending ??= Promise.resolve()
|
|
20
|
+
.then(() => create())
|
|
21
|
+
.then((resolved) => {
|
|
22
|
+
value = resolved;
|
|
23
|
+
created = true;
|
|
24
|
+
pending = undefined;
|
|
25
|
+
return resolved;
|
|
26
|
+
}, (error) => {
|
|
27
|
+
pending = undefined;
|
|
28
|
+
throw error;
|
|
29
|
+
});
|
|
30
|
+
return pending;
|
|
31
|
+
};
|
|
32
|
+
const close = async () => {
|
|
33
|
+
if (!created) {
|
|
34
|
+
pending = undefined;
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const resolved = value;
|
|
38
|
+
value = undefined;
|
|
39
|
+
created = false;
|
|
40
|
+
pending = undefined;
|
|
41
|
+
await options.close?.(resolved);
|
|
42
|
+
};
|
|
43
|
+
const middleware = (async (req, ctx, next) => {
|
|
44
|
+
const resolved = await load();
|
|
45
|
+
return next(req, { ...ctx, [key]: resolved });
|
|
46
|
+
});
|
|
47
|
+
Object.defineProperty(middleware, 'key', { value: key });
|
|
48
|
+
middleware.peek = () => (created ? value : undefined);
|
|
49
|
+
middleware.load = load;
|
|
50
|
+
middleware.close = close;
|
|
51
|
+
Object.defineProperty(middleware, RESOURCE_DISPOSE, { value: close });
|
|
52
|
+
return middleware;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAEA,iEAAiE;AACjE,MAAM,CAAC,MAAM,gBAAgB,GAAkB,MAAM,CAAC,0BAA0B,CAAC,CAAA;AA6BjF;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ,CAKtB,GAAM,EACN,MAA4B,EAC5B,UAA8B,EAAE;IAEhC,IAAI,KAAoB,CAAA;IACxB,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,IAAI,OAA+B,CAAA;IAEnC,MAAM,IAAI,GAAG,GAAe,EAAE;QAC5B,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,KAAU,CAAC,CAAA;QAC/C,OAAO,KAAK,OAAO,CAAC,OAAO,EAAE;aAC1B,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC;aACpB,IAAI,CACH,CAAC,QAAQ,EAAE,EAAE;YACX,KAAK,GAAG,QAAQ,CAAA;YAChB,OAAO,GAAG,IAAI,CAAA;YACd,OAAO,GAAG,SAAS,CAAA;YACnB,OAAO,QAAQ,CAAA;QACjB,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;YACjB,OAAO,GAAG,SAAS,CAAA;YACnB,MAAM,KAAK,CAAA;QACb,CAAC,CACF,CAAA;QACH,OAAO,OAAO,CAAA;IAChB,CAAC,CAAA;IAED,MAAM,KAAK,GAAG,KAAK,IAAmB,EAAE;QACtC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,SAAS,CAAA;YACnB,OAAM;QACR,CAAC;QACD,MAAM,QAAQ,GAAG,KAAU,CAAA;QAC3B,KAAK,GAAG,SAAS,CAAA;QACjB,OAAO,GAAG,KAAK,CAAA;QACf,OAAO,GAAG,SAAS,CAAA;QACnB,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,CAAA;IACjC,CAAC,CAAA;IAED,MAAM,UAAU,GAAG,CAAC,KAAK,EACvB,GAAY,EACZ,GAAM,EACN,IAA+B,EAC/B,EAAE;QACF,MAAM,QAAQ,GAAG,MAAM,IAAI,EAAE,CAAA;QAC7B,OAAO,IAAI,CAAC,GAAG,EAAE,EAAE,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAsB,CAAC,CAAA;IACnE,CAAC,CAAsB,CAAA;IAEvB,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAA;IACxD,UAAU,CAAC,IAAI,GAAG,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAE,KAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;IAC5D,UAAU,CAAC,IAAI,GAAG,IAAI,CAAA;IACtB,UAAU,CAAC,KAAK,GAAG,KAAK,CAAA;IACxB,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,gBAAgB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;IAErE,OAAO,UAAU,CAAA;AACnB,CAAC"}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Context, Handler, Middleware } from './types.ts';
|
|
2
|
+
export interface RouteLike {
|
|
3
|
+
method: string;
|
|
4
|
+
path: string;
|
|
5
|
+
}
|
|
6
|
+
export interface RouteEntry<C extends Context = Context, H = Handler<any>> extends RouteLike {
|
|
7
|
+
middlewares: Array<Middleware<any, any>>;
|
|
8
|
+
handler: H;
|
|
9
|
+
}
|
|
10
|
+
export type RouteMatch<C extends Context, E extends RouteLike = RouteEntry<C>> = {
|
|
11
|
+
type: 'match';
|
|
12
|
+
entry: E;
|
|
13
|
+
params: Record<string, string>;
|
|
14
|
+
} | {
|
|
15
|
+
type: 'method';
|
|
16
|
+
allowed: string[];
|
|
17
|
+
} | {
|
|
18
|
+
type: 'none';
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* 路径树路由器。
|
|
22
|
+
*
|
|
23
|
+
* 匹配优先级:静态段 > `:param` > `*` 通配符。
|
|
24
|
+
*/
|
|
25
|
+
export declare class Router<C extends Context = Context, E extends RouteLike = RouteEntry<C>> {
|
|
26
|
+
#private;
|
|
27
|
+
add(entry: E): void;
|
|
28
|
+
match(method: string, path: string): RouteMatch<C, E>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE9D,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,WAAW,UAAU,CACzB,CAAC,SAAS,OAAO,GAAG,OAAO,EAC3B,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAChB,SAAQ,SAAS;IACjB,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;IACxC,OAAO,EAAE,CAAC,CAAA;CACX;AAED,MAAM,MAAM,UAAU,CACpB,CAAC,SAAS,OAAO,EACjB,CAAC,SAAS,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,IAEjC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AA4BpB;;;;GAIG;AACH,qBAAa,MAAM,CACjB,CAAC,SAAS,OAAO,GAAG,OAAO,EAC3B,CAAC,SAAS,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC;;IAInC,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IA2CnB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC;CA0DtD"}
|
package/dist/router.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { splitPath } from "./path.js";
|
|
2
|
+
function createNode() {
|
|
3
|
+
return {
|
|
4
|
+
literals: new Map(),
|
|
5
|
+
params: [],
|
|
6
|
+
wildcard: null,
|
|
7
|
+
wildcardName: '*',
|
|
8
|
+
routes: new Map(),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function decodeSegment(segment) {
|
|
12
|
+
try {
|
|
13
|
+
return decodeURIComponent(segment);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return segment;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 路径树路由器。
|
|
21
|
+
*
|
|
22
|
+
* 匹配优先级:静态段 > `:param` > `*` 通配符。
|
|
23
|
+
*/
|
|
24
|
+
export class Router {
|
|
25
|
+
#root = createNode();
|
|
26
|
+
add(entry) {
|
|
27
|
+
if (!entry.path.startsWith('/')) {
|
|
28
|
+
throw new Error(`Route path must start with "/": ${entry.path}`);
|
|
29
|
+
}
|
|
30
|
+
const segments = splitPath(entry.path);
|
|
31
|
+
let node = this.#root;
|
|
32
|
+
for (let i = 0; i < segments.length; i++) {
|
|
33
|
+
const segment = segments[i];
|
|
34
|
+
if (segment.startsWith(':')) {
|
|
35
|
+
const name = segment.slice(1);
|
|
36
|
+
if (!name)
|
|
37
|
+
throw new Error(`Invalid route parameter: ${entry.path}`);
|
|
38
|
+
let child = node.params.find((param) => param.name === name);
|
|
39
|
+
if (!child) {
|
|
40
|
+
child = { name, node: createNode() };
|
|
41
|
+
node.params.push(child);
|
|
42
|
+
}
|
|
43
|
+
node = child.node;
|
|
44
|
+
}
|
|
45
|
+
else if (segment.startsWith('*')) {
|
|
46
|
+
if (i !== segments.length - 1) {
|
|
47
|
+
throw new Error(`Wildcard "*" must be the last segment: ${entry.path}`);
|
|
48
|
+
}
|
|
49
|
+
node.wildcard ??= createNode();
|
|
50
|
+
node.wildcard.wildcardName = segment.slice(1) || '*';
|
|
51
|
+
node = node.wildcard;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
let child = node.literals.get(segment);
|
|
55
|
+
if (!child) {
|
|
56
|
+
child = createNode();
|
|
57
|
+
node.literals.set(segment, child);
|
|
58
|
+
}
|
|
59
|
+
node = child;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (node.routes.has(entry.method)) {
|
|
63
|
+
throw new Error(`Duplicate route: ${entry.method} ${entry.path}`);
|
|
64
|
+
}
|
|
65
|
+
node.routes.set(entry.method, entry);
|
|
66
|
+
}
|
|
67
|
+
match(method, path) {
|
|
68
|
+
const params = {};
|
|
69
|
+
const node = this.#find(this.#root, splitPath(path), 0, params);
|
|
70
|
+
if (!node)
|
|
71
|
+
return { type: 'none' };
|
|
72
|
+
let entry = node.routes.get(method);
|
|
73
|
+
if (!entry && method === 'HEAD')
|
|
74
|
+
entry = node.routes.get('GET');
|
|
75
|
+
const allowed = new Set(node.routes.keys());
|
|
76
|
+
if (allowed.has('GET'))
|
|
77
|
+
allowed.add('HEAD');
|
|
78
|
+
if (allowed.size > 0 && !allowed.has('OPTIONS'))
|
|
79
|
+
allowed.add('OPTIONS');
|
|
80
|
+
if (entry)
|
|
81
|
+
return { type: 'match', entry, params };
|
|
82
|
+
return { type: 'method', allowed: [...allowed].sort() };
|
|
83
|
+
}
|
|
84
|
+
#find(node, segments, index, params) {
|
|
85
|
+
if (index === segments.length) {
|
|
86
|
+
if (node.routes.size > 0)
|
|
87
|
+
return node;
|
|
88
|
+
if (node.wildcard && node.wildcard.routes.size > 0) {
|
|
89
|
+
params[node.wildcard.wildcardName] = '';
|
|
90
|
+
return node.wildcard;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
const segment = segments[index];
|
|
95
|
+
const literal = node.literals.get(segment);
|
|
96
|
+
if (literal) {
|
|
97
|
+
const found = this.#find(literal, segments, index + 1, params);
|
|
98
|
+
if (found)
|
|
99
|
+
return found;
|
|
100
|
+
}
|
|
101
|
+
for (const param of node.params) {
|
|
102
|
+
const previous = params[param.name];
|
|
103
|
+
params[param.name] = decodeSegment(segment);
|
|
104
|
+
const found = this.#find(param.node, segments, index + 1, params);
|
|
105
|
+
if (found)
|
|
106
|
+
return found;
|
|
107
|
+
if (previous === undefined)
|
|
108
|
+
delete params[param.name];
|
|
109
|
+
else
|
|
110
|
+
params[param.name] = previous;
|
|
111
|
+
}
|
|
112
|
+
if (node.wildcard && node.wildcard.routes.size > 0) {
|
|
113
|
+
params[node.wildcard.wildcardName] = segments
|
|
114
|
+
.slice(index)
|
|
115
|
+
.map(decodeSegment)
|
|
116
|
+
.join('/');
|
|
117
|
+
return node.wildcard;
|
|
118
|
+
}
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 请求上下文。
|
|
3
|
+
*
|
|
4
|
+
* - `params` 由路由匹配填充(例如 `/users/:id` 会得到 `params.id`)
|
|
5
|
+
* - `query` 由 URL 查询串解析填充
|
|
6
|
+
* - 索引签名允许任意中间件向上下文注入数据
|
|
7
|
+
*
|
|
8
|
+
* 也可以通过声明合并来全局扩展字段:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* declare module '@weifuwujs/weifuwu' {
|
|
12
|
+
* interface Context {
|
|
13
|
+
* user: User
|
|
14
|
+
* }
|
|
15
|
+
* }
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export interface Context {
|
|
19
|
+
params: Record<string, string>;
|
|
20
|
+
query: Record<string, string>;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}
|
|
23
|
+
/** 链上的一环:`next(req, ctx)` 的类型(HTTP 的 next 返回 Response,WS 还可能是 void) */
|
|
24
|
+
export type Next<In extends Context = Context, R = Response> = (req: Request, ctx: In) => R | Promise<R>;
|
|
25
|
+
/** 终端处理器:请求进,响应出 */
|
|
26
|
+
export type Handler<T extends Context = Context> = Next<T, Response>;
|
|
27
|
+
/**
|
|
28
|
+
* 中间件。HTTP 与 WebSocket 共用同一类型:
|
|
29
|
+
*
|
|
30
|
+
* - `R` 是**注册位实例化**的链结果类型:HTTP 默认 `Response`,WebSocket 为 `Response | void`
|
|
31
|
+
* - 返回 `Response` 表示短路(HTTP 直接响应 / WS 拒绝升级)
|
|
32
|
+
* - 调用 `next(req, ctx?)` 继续,并可通过新的 `ctx` 累积类型
|
|
33
|
+
*
|
|
34
|
+
* 只在一端使用时保持默认(`R = Response`)即可;同一个实例要在 HTTP 与 WS
|
|
35
|
+
* 两处复用(如鉴权)时写 `Middleware<In, Out, Response | void>`。
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* const auth: Middleware<Context, Context & { user: User }, Response | void> =
|
|
39
|
+
* async (req, ctx, next) => {
|
|
40
|
+
* const user = await verify(req)
|
|
41
|
+
* return user
|
|
42
|
+
* ? next(req, { ...ctx, user })
|
|
43
|
+
* : new Response('Unauthorized', { status: 401 })
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export type Middleware<In extends Context = Context, Out extends Context = In, R = Response> = (req: Request, ctx: In, next: Next<Out, R>) => Response | R | Promise<Response | R>;
|
|
48
|
+
/**
|
|
49
|
+
* 全局错误处理器。返回 `Response` 表示接管错误;
|
|
50
|
+
* 返回 `undefined`(或不返回)则使用默认行为。
|
|
51
|
+
*/
|
|
52
|
+
export type ErrorHandler<C extends Context = Context> = (error: unknown, req: Request, ctx: C) => Response | void | Promise<Response | void>;
|
|
53
|
+
export type NotFoundHandler<C extends Context = Context> = Handler<C>;
|
|
54
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
|
|
55
|
+
/** `app.mount()` 需要的路由形状(结构化:不依赖具体 API 实现) */
|
|
56
|
+
export interface MountableRoute {
|
|
57
|
+
method: string;
|
|
58
|
+
path: string;
|
|
59
|
+
handler: Handler<any>;
|
|
60
|
+
}
|
|
61
|
+
/** `app.mount()` 需要的最小接口:`routes()`,可选 GraphQL handler */
|
|
62
|
+
export interface MountableApi<Ctx extends Context = Context> {
|
|
63
|
+
routes(): MountableRoute[];
|
|
64
|
+
graphqlHandler?: () => Handler<any>;
|
|
65
|
+
}
|
|
66
|
+
/** `app.mount()` 选项 */
|
|
67
|
+
export interface MountOptions {
|
|
68
|
+
/** 是否挂载 REST 路由,默认 true */
|
|
69
|
+
rest?: boolean;
|
|
70
|
+
/** GraphQL 端点路径;不传则不挂载 */
|
|
71
|
+
graphql?: string | false;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 从路径字符串中提取参数类型。
|
|
75
|
+
*
|
|
76
|
+
* ```ts
|
|
77
|
+
* PathParams<'/users/:id/posts/:postId'>
|
|
78
|
+
* // => Record<string, string> & { id: string } & { postId: string }
|
|
79
|
+
* PathParams<'/files/*'>
|
|
80
|
+
* // => Record<string, string> & { '*': string }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
export type PathParams<Path extends string> = Record<string, string> & ExtractParams<Path>;
|
|
84
|
+
type ExtractParams<Path extends string> = string extends Path ? unknown : Path extends `${string}:${infer Rest}` ? Rest extends `${infer Name}/${infer Tail}` ? {
|
|
85
|
+
[K in Name]: string;
|
|
86
|
+
} & ExtractParams<`/${Tail}`> : {
|
|
87
|
+
[K in Rest]: string;
|
|
88
|
+
} : Path extends `${string}*${infer Name}` ? {
|
|
89
|
+
[K in Name extends '' ? '*' : Name]: string;
|
|
90
|
+
} : unknown;
|
|
91
|
+
/** 在上下文基础上叠加路由参数类型 */
|
|
92
|
+
export type ContextWithParams<C extends Context, Path extends string> = C & {
|
|
93
|
+
params: PathParams<Path>;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* 一条路由的处理链:任意个中间件 + 最后一个处理器。
|
|
97
|
+
*
|
|
98
|
+
* ```ts
|
|
99
|
+
* app.get('/users/:id', auth, (req, ctx) => json(ctx.user))
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export type RouteHandlers<C extends Context, Path extends string> = [
|
|
103
|
+
...middlewares: Middleware<ContextWithParams<C, Path>, any, any>[],
|
|
104
|
+
handler: Handler<ContextWithParams<C, Path>>
|
|
105
|
+
];
|
|
106
|
+
/** 路由级中间件:与 {@link Middleware} 同一个类型,保留别名以兼容既有导入 */
|
|
107
|
+
export type RouteMiddleware<In extends Context, Out extends Context = In> = Middleware<In, Out>;
|
|
108
|
+
/** 路由注册方法(`app.get` / `app.post` / ...)的调用签名 */
|
|
109
|
+
export interface RouteRegistrar<C extends Context, Prefix extends string, R> {
|
|
110
|
+
<P extends string>(path: P, handler: Handler<ContextWithParams<C, `${Prefix}${P}`>>): R;
|
|
111
|
+
<P extends string, O1 extends Context>(path: P, middleware: Middleware<ContextWithParams<C, `${Prefix}${P}`>, O1, any>, handler: Handler<O1 & ContextWithParams<C, `${Prefix}${P}`>>): R;
|
|
112
|
+
<P extends string, O1 extends Context, O2 extends Context>(path: P, middleware1: Middleware<ContextWithParams<C, `${Prefix}${P}`>, O1, any>, middleware2: Middleware<O1 & ContextWithParams<C, `${Prefix}${P}`>, O2, any>, handler: Handler<O2 & ContextWithParams<C, `${Prefix}${P}`>>): R;
|
|
113
|
+
/** 3 个及以上中间件:类型不再逐步累积,最后一个参数必须是处理器 */
|
|
114
|
+
<P extends string>(path: P, ...handlers: RouteHandlers<C, `${Prefix}${P}`>): R;
|
|
115
|
+
}
|
|
116
|
+
/** `app.on(method, path, ...handlers)` 的调用签名 */
|
|
117
|
+
export interface OnRegistrar<C extends Context, Prefix extends string, R> {
|
|
118
|
+
<M extends string, P extends string>(method: M, path: P, handler: Handler<ContextWithParams<C, `${Prefix}${P}`>>): R;
|
|
119
|
+
<M extends string, P extends string, O1 extends Context>(method: M, path: P, middleware: Middleware<ContextWithParams<C, `${Prefix}${P}`>, O1, any>, handler: Handler<O1 & ContextWithParams<C, `${Prefix}${P}`>>): R;
|
|
120
|
+
<M extends string, P extends string, O1 extends Context, O2 extends Context>(method: M, path: P, middleware1: Middleware<ContextWithParams<C, `${Prefix}${P}`>, O1, any>, middleware2: Middleware<O1 & ContextWithParams<C, `${Prefix}${P}`>, O2, any>, handler: Handler<O2 & ContextWithParams<C, `${Prefix}${P}`>>): R;
|
|
121
|
+
<M extends string, P extends string>(method: M, path: P, ...handlers: RouteHandlers<C, `${Prefix}${P}`>): R;
|
|
122
|
+
}
|
|
123
|
+
export {};
|
|
124
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/dist/vite.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { reactRouter } from '@react-router/dev/vite';
|
|
2
|
+
import type { UserConfig } from 'vite';
|
|
3
|
+
export { reactRouter };
|
|
4
|
+
export interface WeifuwuConfig extends UserConfig {
|
|
5
|
+
/** 自定义 server 入口(相对项目根目录),默认 `./app/server.ts` */
|
|
6
|
+
serverEntry?: string;
|
|
7
|
+
/** 追加需要强制打进 SSR bundle 的包 */
|
|
8
|
+
ssrNoExternal?: Array<string | RegExp>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* weifuwu + React Router 的 Vite 预设。
|
|
12
|
+
*
|
|
13
|
+
* 自动完成:
|
|
14
|
+
* - 注册 `reactRouter()` 插件(未手动注册时)
|
|
15
|
+
* - 把 `app/server.ts` 设为 SSR 构建入口,使 `react-router build`
|
|
16
|
+
* 产出的 `build/server/index.js` 就是 weifuwu 应用
|
|
17
|
+
* - 把 `@weifuwujs/weifuwu` 打进 SSR bundle,让
|
|
18
|
+
* `virtual:react-router/server-build` 在构建期被解析
|
|
19
|
+
*/
|
|
20
|
+
export declare function defineConfig(config?: WeifuwuConfig): UserConfig;
|
|
21
|
+
//# sourceMappingURL=vite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAEpD,OAAO,KAAK,EAAwB,UAAU,EAAE,MAAM,MAAM,CAAA;AAE5D,OAAO,EAAE,WAAW,EAAE,CAAA;AAEtB,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6BAA6B;IAC7B,aAAa,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAA;CACvC;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,aAAkB,GAAG,UAAU,CAgDnE"}
|
package/dist/vite.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { reactRouter } from '@react-router/dev/vite';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
export { reactRouter };
|
|
4
|
+
/**
|
|
5
|
+
* weifuwu + React Router 的 Vite 预设。
|
|
6
|
+
*
|
|
7
|
+
* 自动完成:
|
|
8
|
+
* - 注册 `reactRouter()` 插件(未手动注册时)
|
|
9
|
+
* - 把 `app/server.ts` 设为 SSR 构建入口,使 `react-router build`
|
|
10
|
+
* 产出的 `build/server/index.js` 就是 weifuwu 应用
|
|
11
|
+
* - 把 `@weifuwujs/weifuwu` 打进 SSR bundle,让
|
|
12
|
+
* `virtual:react-router/server-build` 在构建期被解析
|
|
13
|
+
*/
|
|
14
|
+
export function defineConfig(config = {}) {
|
|
15
|
+
const { serverEntry = './app/server.ts', ssrNoExternal = [], ...userConfig } = config;
|
|
16
|
+
const plugins = userConfig.plugins ?? [];
|
|
17
|
+
const hasReactRouter = plugins.some((plugin) => containsPlugin(plugin, 'react-router'));
|
|
18
|
+
const extraPlugins = [];
|
|
19
|
+
if (!hasTildeAlias(userConfig))
|
|
20
|
+
extraPlugins.push(tildeAppAlias());
|
|
21
|
+
if (!hasReactRouter)
|
|
22
|
+
extraPlugins.push(...reactRouter());
|
|
23
|
+
const noExternal = userConfig.ssr?.noExternal === true
|
|
24
|
+
? true
|
|
25
|
+
: [
|
|
26
|
+
...new Set([
|
|
27
|
+
...normalizeNoExternal(userConfig.ssr?.noExternal),
|
|
28
|
+
'@weifuwujs/weifuwu',
|
|
29
|
+
...ssrNoExternal,
|
|
30
|
+
]),
|
|
31
|
+
];
|
|
32
|
+
const environments = userConfig.environments ?? {};
|
|
33
|
+
const ssrEnvironment = environments.ssr ?? {};
|
|
34
|
+
const rollupOptions = ssrEnvironment.build?.rollupOptions ?? {};
|
|
35
|
+
return {
|
|
36
|
+
...userConfig,
|
|
37
|
+
plugins: [...plugins, ...extraPlugins],
|
|
38
|
+
ssr: { ...userConfig.ssr, noExternal },
|
|
39
|
+
environments: {
|
|
40
|
+
...environments,
|
|
41
|
+
ssr: {
|
|
42
|
+
...ssrEnvironment,
|
|
43
|
+
build: {
|
|
44
|
+
...ssrEnvironment.build,
|
|
45
|
+
rollupOptions: {
|
|
46
|
+
...rollupOptions,
|
|
47
|
+
input: rollupOptions.input ?? serverEntry,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function normalizeNoExternal(value) {
|
|
55
|
+
if (!value || value === true)
|
|
56
|
+
return [];
|
|
57
|
+
return Array.isArray(value) ? value : [value];
|
|
58
|
+
}
|
|
59
|
+
function containsPlugin(option, name) {
|
|
60
|
+
if (!option)
|
|
61
|
+
return false;
|
|
62
|
+
if (Array.isArray(option)) {
|
|
63
|
+
return option.some((item) => containsPlugin(item, name));
|
|
64
|
+
}
|
|
65
|
+
if (typeof option === 'object' && 'name' in option) {
|
|
66
|
+
return option.name === name;
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* `~/` → `<root>/app`(React Router 官方模板约定)。
|
|
72
|
+
*
|
|
73
|
+
* tsconfig `paths` 只覆盖类型检查与 build;dev 的 SSR module runner
|
|
74
|
+
* 不解析 TS paths,所以这里用 resolver 插件补齐,保证 dev/prod 一致。
|
|
75
|
+
*/
|
|
76
|
+
function tildeAppAlias() {
|
|
77
|
+
let root = process.cwd();
|
|
78
|
+
return {
|
|
79
|
+
name: 'weifuwu:tilde-app-alias',
|
|
80
|
+
configResolved(config) {
|
|
81
|
+
root = config.root;
|
|
82
|
+
},
|
|
83
|
+
async resolveId(source, importer) {
|
|
84
|
+
if (!source.startsWith('~/'))
|
|
85
|
+
return null;
|
|
86
|
+
return this.resolve(join(root, 'app', source.slice(2)), importer, {
|
|
87
|
+
skipSelf: true,
|
|
88
|
+
});
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function hasTildeAlias(config) {
|
|
93
|
+
const alias = config.resolve?.alias;
|
|
94
|
+
if (!alias)
|
|
95
|
+
return false;
|
|
96
|
+
if (Array.isArray(alias)) {
|
|
97
|
+
return alias.some((entry) => 'find' in entry && entry.find === '~');
|
|
98
|
+
}
|
|
99
|
+
return '~' in alias;
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=vite.js.map
|
package/dist/vite.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.js","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'node:http';
|
|
2
|
+
import type { Duplex } from 'node:stream';
|
|
3
|
+
import { WebSocketConnection } from './connection.ts';
|
|
4
|
+
import type { WebSocketOptions } from './types.ts';
|
|
5
|
+
export interface AcceptOptions extends WebSocketOptions {
|
|
6
|
+
/** 升级请求头之后已经读到的字节(交给 `ws` 处理) */
|
|
7
|
+
head?: Uint8Array;
|
|
8
|
+
/** Node 原始升级请求;`ws.handleUpgrade` 需要它完成握手 */
|
|
9
|
+
rawRequest?: IncomingMessage;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 校验升级请求;返回 `null` 表示可以升级,
|
|
13
|
+
* 否则返回应当写回的 HTTP 响应。
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateUpgradeRequest(request: Request): Response | null;
|
|
16
|
+
/**
|
|
17
|
+
* 完成握手并创建连接。调用前应先用 `validateUpgradeRequest` 校验。
|
|
18
|
+
*
|
|
19
|
+
* 握手 / 子协议协商 / 帧编解码由 `ws` 负责,`WebSocketConnection` 只做适配。
|
|
20
|
+
*/
|
|
21
|
+
export declare function acceptWebSocket(socket: Duplex, _request: Request, options?: AcceptOptions): WebSocketConnection;
|
|
22
|
+
//# sourceMappingURL=accept.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accept.d.ts","sourceRoot":"","sources":["../../src/websocket/accept.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAEhD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,MAAM,WAAW,aAAc,SAAQ,gBAAgB;IACrD,iCAAiC;IACjC,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,6CAA6C;IAC7C,UAAU,CAAC,EAAE,eAAe,CAAA;CAC7B;AAkBD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CA4BxE;AAUD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,OAAO,EACjB,OAAO,GAAE,aAAkB,GAC1B,mBAAmB,CAyCrB"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { WebSocketConnection } from "./connection.js";
|
|
3
|
+
/** 默认 4 MiB,与 `WebSocketOptions.maxMessageSize` 一致 */
|
|
4
|
+
const DEFAULT_MAX_MESSAGE_SIZE = 4 * 1024 * 1024;
|
|
5
|
+
let wsModule;
|
|
6
|
+
/**
|
|
7
|
+
* 懒加载 `ws`:根入口导出 WebSocket API,但不应该让 HTTP-only 用户
|
|
8
|
+
* 承担依赖加载;首次真正升级(`acceptWebSocket`)时才 require。
|
|
9
|
+
*/
|
|
10
|
+
function loadWs() {
|
|
11
|
+
wsModule ??= createRequire(import.meta.url)('ws');
|
|
12
|
+
return wsModule;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* 校验升级请求;返回 `null` 表示可以升级,
|
|
16
|
+
* 否则返回应当写回的 HTTP 响应。
|
|
17
|
+
*/
|
|
18
|
+
export function validateUpgradeRequest(request) {
|
|
19
|
+
const upgrade = request.headers.get('upgrade')?.toLowerCase().trim();
|
|
20
|
+
const connection = request.headers.get('connection')?.toLowerCase() ?? '';
|
|
21
|
+
const connectionTokens = connection.split(',').map((token) => token.trim());
|
|
22
|
+
if (request.method.toUpperCase() !== 'GET') {
|
|
23
|
+
return new Response('Method Not Allowed', {
|
|
24
|
+
status: 405,
|
|
25
|
+
headers: { allow: 'GET' },
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
if (upgrade !== 'websocket' || !connectionTokens.includes('upgrade')) {
|
|
29
|
+
return new Response('Upgrade Required', {
|
|
30
|
+
status: 426,
|
|
31
|
+
headers: { upgrade: 'websocket' },
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (request.headers.get('sec-websocket-version') !== '13') {
|
|
35
|
+
return new Response('Unsupported WebSocket Version', {
|
|
36
|
+
status: 426,
|
|
37
|
+
headers: { 'sec-websocket-version': '13' },
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
const key = request.headers.get('sec-websocket-key');
|
|
41
|
+
if (!key || !isValidKey(key)) {
|
|
42
|
+
return new Response('Bad Request', { status: 400 });
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function isValidKey(key) {
|
|
47
|
+
try {
|
|
48
|
+
return Buffer.from(key, 'base64').length === 16;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 完成握手并创建连接。调用前应先用 `validateUpgradeRequest` 校验。
|
|
56
|
+
*
|
|
57
|
+
* 握手 / 子协议协商 / 帧编解码由 `ws` 负责,`WebSocketConnection` 只做适配。
|
|
58
|
+
*/
|
|
59
|
+
export function acceptWebSocket(socket, _request, options = {}) {
|
|
60
|
+
const { rawRequest } = options;
|
|
61
|
+
if (!rawRequest) {
|
|
62
|
+
throw new Error('acceptWebSocket requires options.rawRequest (the Node IncomingMessage)');
|
|
63
|
+
}
|
|
64
|
+
const { WebSocketServer } = loadWs();
|
|
65
|
+
const server = new WebSocketServer({
|
|
66
|
+
noServer: true,
|
|
67
|
+
maxPayload: options.maxMessageSize ?? DEFAULT_MAX_MESSAGE_SIZE,
|
|
68
|
+
autoPong: options.autoPong !== false,
|
|
69
|
+
perMessageDeflate: false,
|
|
70
|
+
handleProtocols: (requested) => negotiateProtocol(requested, options.protocols),
|
|
71
|
+
// ws 运行时支持 closeTimeout,但 @types/ws 未把它写进 ServerOptions
|
|
72
|
+
closeTimeout: options.closeTimeout ?? 5000,
|
|
73
|
+
});
|
|
74
|
+
let connection;
|
|
75
|
+
server.handleUpgrade(rawRequest, socket, Buffer.from(options.head ?? new Uint8Array(0)), (ws) => {
|
|
76
|
+
connection = new WebSocketConnection(ws, {
|
|
77
|
+
protocol: ws.protocol,
|
|
78
|
+
maxMessageSize: options.maxMessageSize,
|
|
79
|
+
autoPong: options.autoPong,
|
|
80
|
+
closeTimeout: options.closeTimeout,
|
|
81
|
+
pingInterval: options.pingInterval,
|
|
82
|
+
idleTimeout: options.idleTimeout,
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
server.close();
|
|
86
|
+
if (!connection) {
|
|
87
|
+
throw new Error('WebSocket upgrade failed');
|
|
88
|
+
}
|
|
89
|
+
return connection;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 从客户端子协议中挑选一个服务端支持的,没有则返回 `false`(不发该响应头)。
|
|
93
|
+
* 与旧语义一致:按客户端优先级取第一个交集。
|
|
94
|
+
*/
|
|
95
|
+
function negotiateProtocol(requested, supported) {
|
|
96
|
+
if (!supported || supported.length === 0)
|
|
97
|
+
return false;
|
|
98
|
+
for (const protocol of requested) {
|
|
99
|
+
if (supported.includes(protocol))
|
|
100
|
+
return protocol;
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=accept.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"accept.js","sourceRoot":"","sources":["../../src/websocket/accept.ts"],"names":[],"mappings":"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"}
|