@chatcops/server 0.5.0 → 0.6.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 +4 -4
- package/dist/adapters/ai-sdk.d.ts +12 -0
- package/dist/adapters/ai-sdk.d.ts.map +1 -0
- package/dist/adapters/ai-sdk.js +27 -0
- package/dist/adapters/ai-sdk.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +9 -83
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/express.d.ts +6 -2
- package/dist/adapters/express.d.ts.map +1 -1
- package/dist/adapters/express.js +101 -30
- package/dist/adapters/express.js.map +1 -1
- package/dist/adapters/index.d.ts +1 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +1 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/vercel.d.ts.map +1 -1
- package/dist/adapters/vercel.js +7 -74
- package/dist/adapters/vercel.js.map +1 -1
- package/dist/client-ip.d.ts +36 -0
- package/dist/client-ip.d.ts.map +1 -0
- package/dist/client-ip.js +62 -0
- package/dist/client-ip.js.map +1 -0
- package/dist/config.d.ts +110 -8
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +5 -1
- package/dist/config.js.map +1 -1
- package/dist/handler.d.ts +50 -1
- package/dist/handler.d.ts.map +1 -1
- package/dist/handler.js +384 -130
- package/dist/handler.js.map +1 -1
- package/dist/http.d.ts +66 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +262 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/rate-limit.d.ts +5 -0
- package/dist/rate-limit.d.ts.map +1 -1
- package/dist/rate-limit.js +5 -0
- package/dist/rate-limit.js.map +1 -1
- package/dist/security/origin-policy.d.ts +19 -0
- package/dist/security/origin-policy.d.ts.map +1 -0
- package/dist/security/origin-policy.js +58 -0
- package/dist/security/origin-policy.js.map +1 -0
- package/dist/security/origin.d.ts +5 -0
- package/dist/security/origin.d.ts.map +1 -0
- package/dist/security/origin.js +47 -0
- package/dist/security/origin.js.map +1 -0
- package/dist/security/session-token.d.ts +65 -0
- package/dist/security/session-token.d.ts.map +1 -0
- package/dist/security/session-token.js +338 -0
- package/dist/security/session-token.js.map +1 -0
- package/dist/security/sessions.d.ts +49 -0
- package/dist/security/sessions.d.ts.map +1 -0
- package/dist/security/sessions.js +159 -0
- package/dist/security/sessions.js.map +1 -0
- package/dist/security/startup-checks.d.ts +44 -0
- package/dist/security/startup-checks.d.ts.map +1 -0
- package/dist/security/startup-checks.js +134 -0
- package/dist/security/startup-checks.js.map +1 -0
- package/dist/security/turnstile.d.ts +24 -0
- package/dist/security/turnstile.d.ts.map +1 -0
- package/dist/security/turnstile.js +93 -0
- package/dist/security/turnstile.js.map +1 -0
- package/dist/visitor-context.d.ts.map +1 -1
- package/dist/visitor-context.js +1 -0
- package/dist/visitor-context.js.map +1 -1
- package/package.json +22 -5
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type ChatHandler } from './handler.js';
|
|
2
|
+
import type { OriginPolicy } from './security/origin-policy.js';
|
|
3
|
+
/**
|
|
4
|
+
* The HTTP side of a chat request, shared by every adapter: which stream
|
|
5
|
+
* format to answer in, real statuses for refusals in protocol v1, SSE
|
|
6
|
+
* framing, keep-alive pings and the one `[DONE]` at the end. Adapters only
|
|
7
|
+
* move the bytes.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PROTOCOL_HEADER = "x-chatcops-protocol";
|
|
10
|
+
/** Requests over this size get 413. Every field has a ceiling, so a real request is far smaller. */
|
|
11
|
+
export declare const MAX_BODY_BYTES: number;
|
|
12
|
+
/** A comment line after this long with nothing sent, so the widget's inactivity timer doesn't fire during a slow tool. */
|
|
13
|
+
export declare const PING_INTERVAL_MS = 10000;
|
|
14
|
+
/**
|
|
15
|
+
* Protocol v1 when the body has `protocol: 1` (what the widget sends) or the
|
|
16
|
+
* `x-chatcops-protocol` header asks for it. Anything else gets the 0.5 format.
|
|
17
|
+
*/
|
|
18
|
+
export declare function wantsProtocolV1(body: unknown, header: string | null | undefined): boolean;
|
|
19
|
+
export interface ChatReplyOptions {
|
|
20
|
+
signal: AbortSignal;
|
|
21
|
+
/** The `x-chatcops-protocol` request header. */
|
|
22
|
+
protocolHeader?: string | null;
|
|
23
|
+
pingIntervalMs?: number;
|
|
24
|
+
/** Answer in the AI SDK's UI message stream instead of choosing by the request. */
|
|
25
|
+
aiSdk?: boolean;
|
|
26
|
+
/** The `Authorization` request header, for session tokens. */
|
|
27
|
+
authorization?: string | null;
|
|
28
|
+
/** The `Origin` request header. */
|
|
29
|
+
origin?: string | null;
|
|
30
|
+
}
|
|
31
|
+
export type ChatReply = {
|
|
32
|
+
kind: 'json';
|
|
33
|
+
status: number;
|
|
34
|
+
headers: Record<string, string>;
|
|
35
|
+
body: unknown;
|
|
36
|
+
} | {
|
|
37
|
+
kind: 'stream';
|
|
38
|
+
headers: Record<string, string>;
|
|
39
|
+
frames: AsyncGenerator<string, void>;
|
|
40
|
+
};
|
|
41
|
+
/** What to send back for a parsed request body. */
|
|
42
|
+
export declare function chatReply(handler: Pick<ChatHandler, 'startChat'>, body: unknown, clientIp: string | undefined, options: ChatReplyOptions): Promise<ChatReply>;
|
|
43
|
+
type ReadBody = {
|
|
44
|
+
ok: true;
|
|
45
|
+
body: unknown;
|
|
46
|
+
} | {
|
|
47
|
+
ok: false;
|
|
48
|
+
reason: 'too_large' | 'invalid_json';
|
|
49
|
+
};
|
|
50
|
+
/** Reads and parses a JSON body, stopping at `maxBytes`. */
|
|
51
|
+
export declare function readJsonBody(request: Request, maxBytes?: number): Promise<ReadBody>;
|
|
52
|
+
export interface FetchChatOptions {
|
|
53
|
+
/** Called only for chat requests, after the method and origin checks. */
|
|
54
|
+
clientIp(request: Request): string | undefined;
|
|
55
|
+
/** Workers' `ctx.waitUntil`, so lead webhooks finish after the visitor leaves. */
|
|
56
|
+
waitUntil?(promise: Promise<unknown>): void;
|
|
57
|
+
pingIntervalMs?: number;
|
|
58
|
+
/** Read `useChat` requests and answer in the AI SDK's UI message stream. */
|
|
59
|
+
aiSdk?: boolean;
|
|
60
|
+
}
|
|
61
|
+
/** Whether a request is for the session endpoint: the chat URL plus `/session`. */
|
|
62
|
+
export declare function isSessionPath(pathname: string): boolean;
|
|
63
|
+
/** The whole request for Fetch API runtimes (Vercel, Cloudflare, Next.js, Bun, Deno). */
|
|
64
|
+
export declare function fetchChatResponse(handler: Pick<ChatHandler, 'startChat' | 'startSession'>, origins: OriginPolicy, request: Request, options: FetchChatOptions): Promise<Response>;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAWA,OAAO,EAAmC,KAAK,WAAW,EAAkB,MAAM,cAAc,CAAC;AACjG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE;;;;;GAKG;AAEH,eAAO,MAAM,eAAe,wBAAwB,CAAC;AACrD,oGAAoG;AACpG,eAAO,MAAM,cAAc,QAAa,CAAC;AACzC,0HAA0H;AAC1H,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAKvC;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAIzF;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,WAAW,CAAC;IACpB,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mFAAmF;IACnF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAChF;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;CAAE,CAAC;AAE9F,mDAAmD;AACnD,wBAAsB,SAAS,CAC7B,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,EACvC,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,SAAS,CAAC,CA8BpB;AAiGD,KAAK,QAAQ,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,WAAW,GAAG,cAAc,CAAA;CAAE,CAAC;AAElG,4DAA4D;AAC5D,wBAAsB,YAAY,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,SAAiB,GAAG,OAAO,CAAC,QAAQ,CAAC,CA8BjG;AAED,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IAC/C,kFAAkF;IAClF,SAAS,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,yFAAyF;AACzF,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,cAAc,CAAC,EACxD,OAAO,EAAE,YAAY,EACrB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,QAAQ,CAAC,CA4CnB"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { AI_SDK_STREAM_HEADER, SSE_DONE, SSE_HEADERS, SSE_PING, aiSdkEncoder, encodeSSE, errorBody, fromAiSdkRequest, } from '@chatcops/core';
|
|
2
|
+
import { toLegacyStream } from './handler.js';
|
|
3
|
+
/**
|
|
4
|
+
* The HTTP side of a chat request, shared by every adapter: which stream
|
|
5
|
+
* format to answer in, real statuses for refusals in protocol v1, SSE
|
|
6
|
+
* framing, keep-alive pings and the one `[DONE]` at the end. Adapters only
|
|
7
|
+
* move the bytes.
|
|
8
|
+
*/
|
|
9
|
+
export const PROTOCOL_HEADER = 'x-chatcops-protocol';
|
|
10
|
+
/** Requests over this size get 413. Every field has a ceiling, so a real request is far smaller. */
|
|
11
|
+
export const MAX_BODY_BYTES = 256 * 1024;
|
|
12
|
+
/** A comment line after this long with nothing sent, so the widget's inactivity timer doesn't fire during a slow tool. */
|
|
13
|
+
export const PING_INTERVAL_MS = 10_000;
|
|
14
|
+
/** Lets browsers read these on cross-origin replies. */
|
|
15
|
+
const EXPOSE_HEADERS = { 'Access-Control-Expose-Headers': 'Retry-After, X-ChatCops-Protocol' };
|
|
16
|
+
/**
|
|
17
|
+
* Protocol v1 when the body has `protocol: 1` (what the widget sends) or the
|
|
18
|
+
* `x-chatcops-protocol` header asks for it. Anything else gets the 0.5 format.
|
|
19
|
+
*/
|
|
20
|
+
export function wantsProtocolV1(body, header) {
|
|
21
|
+
if (header != null && Number(header) >= 1)
|
|
22
|
+
return true;
|
|
23
|
+
const protocol = body !== null && typeof body === 'object' ? body.protocol : undefined;
|
|
24
|
+
return typeof protocol === 'number' && protocol >= 1;
|
|
25
|
+
}
|
|
26
|
+
/** What to send back for a parsed request body. */
|
|
27
|
+
export async function chatReply(handler, body, clientIp, options) {
|
|
28
|
+
const format = options.aiSdk ? 'ai-sdk' : wantsProtocolV1(body, options.protocolHeader) ? 'v1' : 'legacy';
|
|
29
|
+
// Both typed formats refuse with a status; only 0.5 clients need a 200 stream.
|
|
30
|
+
const typed = format !== 'legacy';
|
|
31
|
+
const { signal } = options;
|
|
32
|
+
const pingIntervalMs = options.pingIntervalMs ?? PING_INTERVAL_MS;
|
|
33
|
+
let start;
|
|
34
|
+
try {
|
|
35
|
+
start = await handler.startChat(body, clientIp, { signal, format, authorization: options.authorization, origin: options.origin });
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
if (!signal.aborted)
|
|
39
|
+
console.error('[chatcops] Unhandled error while handling chat:', err);
|
|
40
|
+
if (typed)
|
|
41
|
+
return { kind: 'json', status: 500, headers: EXPOSE_HEADERS, body: errorBody('internal_error') };
|
|
42
|
+
// What 0.5 servers send, with no [DONE] after it.
|
|
43
|
+
return stream(format, only(LEGACY_INTERNAL_ERROR));
|
|
44
|
+
}
|
|
45
|
+
if (!start.ok) {
|
|
46
|
+
if (typed) {
|
|
47
|
+
const headers = { ...EXPOSE_HEADERS };
|
|
48
|
+
if (start.error.retryAfterMs !== undefined)
|
|
49
|
+
headers['Retry-After'] = String(Math.ceil(start.error.retryAfterMs / 1000));
|
|
50
|
+
return { kind: 'json', status: start.status, headers, body: { error: start.error } };
|
|
51
|
+
}
|
|
52
|
+
// 0.5 widgets read refusals from a 200 stream.
|
|
53
|
+
return stream(format, only(`data: ${JSON.stringify(start.legacy)}\n\n`, SSE_DONE));
|
|
54
|
+
}
|
|
55
|
+
const source = format === 'v1' ? encodeParts(start.parts) : format === 'ai-sdk' ? encodeAiSdk(start.parts) : encodeLegacy(start.parts);
|
|
56
|
+
return stream(format, frames(source, format, signal, pingIntervalMs));
|
|
57
|
+
}
|
|
58
|
+
const FORMAT_HEADERS = {
|
|
59
|
+
v1: { [PROTOCOL_HEADER]: '1' },
|
|
60
|
+
'ai-sdk': { [AI_SDK_STREAM_HEADER]: 'v1' },
|
|
61
|
+
legacy: {},
|
|
62
|
+
};
|
|
63
|
+
function stream(format, frames) {
|
|
64
|
+
return { kind: 'stream', headers: { ...SSE_HEADERS, ...EXPOSE_HEADERS, ...FORMAT_HEADERS[format] }, frames };
|
|
65
|
+
}
|
|
66
|
+
async function* encodeParts(parts) {
|
|
67
|
+
for await (const part of parts)
|
|
68
|
+
yield encodeSSE(part);
|
|
69
|
+
}
|
|
70
|
+
async function* encodeAiSdk(parts) {
|
|
71
|
+
const encode = aiSdkEncoder();
|
|
72
|
+
for await (const part of parts) {
|
|
73
|
+
for (const chunk of encode(part))
|
|
74
|
+
yield encodeSSE(chunk);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function* encodeLegacy(parts) {
|
|
78
|
+
for await (const chunk of toLegacyStream(parts))
|
|
79
|
+
yield `data: ${chunk}\n\n`;
|
|
80
|
+
}
|
|
81
|
+
const LEGACY_INTERNAL_ERROR = `data: ${JSON.stringify({ error: 'internal_error' })}\n\n`;
|
|
82
|
+
async function* only(...frames) {
|
|
83
|
+
yield* frames;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The frames to write, with pings while nothing else is sent, `[DONE]` once
|
|
87
|
+
* at the end, and nothing at all once the visitor has gone.
|
|
88
|
+
*/
|
|
89
|
+
async function* frames(source, format, signal, pingIntervalMs) {
|
|
90
|
+
try {
|
|
91
|
+
for await (const frame of withPings(source, pingIntervalMs)) {
|
|
92
|
+
if (signal.aborted)
|
|
93
|
+
return;
|
|
94
|
+
yield frame;
|
|
95
|
+
}
|
|
96
|
+
if (!signal.aborted)
|
|
97
|
+
yield SSE_DONE;
|
|
98
|
+
}
|
|
99
|
+
catch (err) {
|
|
100
|
+
if (signal.aborted)
|
|
101
|
+
return;
|
|
102
|
+
console.error('[chatcops] Unhandled error while handling chat:', err);
|
|
103
|
+
if (format !== 'legacy') {
|
|
104
|
+
const tail = [
|
|
105
|
+
{ type: 'error', code: 'internal_error', retryable: true },
|
|
106
|
+
{ type: 'finish', reason: 'error' },
|
|
107
|
+
];
|
|
108
|
+
const encode = format === 'ai-sdk' ? aiSdkEncoder() : (part) => [part];
|
|
109
|
+
for (const part of tail)
|
|
110
|
+
for (const chunk of encode(part))
|
|
111
|
+
yield encodeSSE(chunk);
|
|
112
|
+
yield SSE_DONE;
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
yield LEGACY_INTERNAL_ERROR;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const PING = Symbol('ping');
|
|
120
|
+
async function* withPings(source, intervalMs) {
|
|
121
|
+
if (!(intervalMs > 0 && Number.isFinite(intervalMs))) {
|
|
122
|
+
yield* source;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// The next frame stays pending across pings, so none is lost.
|
|
126
|
+
let pending = source.next();
|
|
127
|
+
try {
|
|
128
|
+
while (true) {
|
|
129
|
+
let timer;
|
|
130
|
+
const tick = new Promise((resolve) => {
|
|
131
|
+
timer = setTimeout(resolve, intervalMs, PING);
|
|
132
|
+
});
|
|
133
|
+
const result = await Promise.race([pending, tick]).finally(() => clearTimeout(timer));
|
|
134
|
+
if (result === PING) {
|
|
135
|
+
yield SSE_PING;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (result.done)
|
|
139
|
+
return;
|
|
140
|
+
yield result.value;
|
|
141
|
+
pending = source.next();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
finally {
|
|
145
|
+
// When the reader stops early, end the reply too; it takes effect once
|
|
146
|
+
// the step in progress finishes.
|
|
147
|
+
source.return(undefined).catch(() => { });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/** Reads and parses a JSON body, stopping at `maxBytes`. */
|
|
151
|
+
export async function readJsonBody(request, maxBytes = MAX_BODY_BYTES) {
|
|
152
|
+
const declared = Number(request.headers.get('content-length'));
|
|
153
|
+
if (declared > maxBytes)
|
|
154
|
+
return { ok: false, reason: 'too_large' };
|
|
155
|
+
const chunks = [];
|
|
156
|
+
let size = 0;
|
|
157
|
+
if (request.body) {
|
|
158
|
+
const reader = request.body.getReader();
|
|
159
|
+
while (true) {
|
|
160
|
+
const { done, value } = await reader.read();
|
|
161
|
+
if (done)
|
|
162
|
+
break;
|
|
163
|
+
size += value.byteLength;
|
|
164
|
+
if (size > maxBytes) {
|
|
165
|
+
await reader.cancel().catch(() => { });
|
|
166
|
+
return { ok: false, reason: 'too_large' };
|
|
167
|
+
}
|
|
168
|
+
chunks.push(value);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const bytes = new Uint8Array(size);
|
|
172
|
+
let offset = 0;
|
|
173
|
+
for (const chunk of chunks) {
|
|
174
|
+
bytes.set(chunk, offset);
|
|
175
|
+
offset += chunk.byteLength;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
return { ok: true, body: JSON.parse(new TextDecoder().decode(bytes)) };
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
return { ok: false, reason: 'invalid_json' };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** Whether a request is for the session endpoint: the chat URL plus `/session`. */
|
|
185
|
+
export function isSessionPath(pathname) {
|
|
186
|
+
return /\/session\/?$/.test(pathname);
|
|
187
|
+
}
|
|
188
|
+
/** The whole request for Fetch API runtimes (Vercel, Cloudflare, Next.js, Bun, Deno). */
|
|
189
|
+
export async function fetchChatResponse(handler, origins, request, options) {
|
|
190
|
+
const origin = request.headers.get('origin');
|
|
191
|
+
const cors = origins.headers(origin);
|
|
192
|
+
const protocolHeader = request.headers.get(PROTOCOL_HEADER);
|
|
193
|
+
const json = (status, body, headers = {}) => new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json', ...cors, ...headers } });
|
|
194
|
+
if (request.method === 'OPTIONS') {
|
|
195
|
+
return new Response(null, { status: 204, headers: origins.preflightHeaders(origin) });
|
|
196
|
+
}
|
|
197
|
+
if (request.method !== 'POST')
|
|
198
|
+
return json(405, { error: 'Method not allowed' });
|
|
199
|
+
if (!origins.allows(origin))
|
|
200
|
+
return json(403, { error: 'origin_not_allowed' });
|
|
201
|
+
if (isSessionPath(new URL(request.url).pathname)) {
|
|
202
|
+
if (!handler.startSession)
|
|
203
|
+
return json(404, errorBody('invalid_request', { message: 'Session tokens are off. Set `sessionSecrets` to turn them on.' }));
|
|
204
|
+
const read = await readJsonBody(request, 16 * 1024);
|
|
205
|
+
if (!read.ok)
|
|
206
|
+
return json(400, errorBody('invalid_request', { message: 'Invalid session request' }));
|
|
207
|
+
const reply = await handler.startSession(read.body, origin, options.clientIp(request));
|
|
208
|
+
return json(reply.status, reply.body);
|
|
209
|
+
}
|
|
210
|
+
const clientIp = options.clientIp(request);
|
|
211
|
+
const read = await readJsonBody(request);
|
|
212
|
+
if (!read.ok) {
|
|
213
|
+
if (read.reason === 'too_large') {
|
|
214
|
+
return json(413, errorBody('payload_too_large', { message: `The request body is over ${MAX_BODY_BYTES} bytes.` }), EXPOSE_HEADERS);
|
|
215
|
+
}
|
|
216
|
+
return json(400, options.aiSdk || wantsProtocolV1(undefined, protocolHeader) ? errorBody('invalid_request', { message: 'Invalid JSON body' }) : { error: 'Invalid JSON body' });
|
|
217
|
+
}
|
|
218
|
+
const abort = new AbortController();
|
|
219
|
+
const reply = await chatReply(handler, options.aiSdk ? fromAiSdkRequest(read.body) : read.body, clientIp, {
|
|
220
|
+
signal: abort.signal,
|
|
221
|
+
protocolHeader,
|
|
222
|
+
pingIntervalMs: options.pingIntervalMs,
|
|
223
|
+
aiSdk: options.aiSdk,
|
|
224
|
+
authorization: request.headers.get('authorization'),
|
|
225
|
+
origin,
|
|
226
|
+
});
|
|
227
|
+
if (reply.kind === 'json')
|
|
228
|
+
return json(reply.status, reply.body, reply.headers);
|
|
229
|
+
return sseResponse(reply.frames, { ...reply.headers, ...cors }, abort, options.waitUntil);
|
|
230
|
+
}
|
|
231
|
+
function sseResponse(frames, headers, abort, waitUntil) {
|
|
232
|
+
const { readable, writable } = new TransformStream();
|
|
233
|
+
const writer = writable.getWriter();
|
|
234
|
+
const encoder = new TextEncoder();
|
|
235
|
+
// When the visitor disconnects the runtime cancels the response, which
|
|
236
|
+
// errors the writer right away; abort then, without waiting for the next write.
|
|
237
|
+
writer.closed.catch(() => abort.abort());
|
|
238
|
+
const pump = (async () => {
|
|
239
|
+
try {
|
|
240
|
+
for await (const frame of frames) {
|
|
241
|
+
if (abort.signal.aborted)
|
|
242
|
+
break;
|
|
243
|
+
try {
|
|
244
|
+
await writer.write(encoder.encode(frame));
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
abort.abort();
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
finally {
|
|
253
|
+
if (abort.signal.aborted)
|
|
254
|
+
await writer.abort().catch(() => { });
|
|
255
|
+
else
|
|
256
|
+
await writer.close().catch(() => { });
|
|
257
|
+
}
|
|
258
|
+
})();
|
|
259
|
+
waitUntil?.(pump);
|
|
260
|
+
return new Response(readable, { headers });
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=http.js.map
|
package/dist/http.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { createChatHandler } from './handler.js';
|
|
2
|
+
export type { ChatFormat, ChatHandler, ChatStart, StartChatOptions } from './handler.js';
|
|
2
3
|
export type { ChatCopsServerConfig, ValidatedChatRequest } from './config.js';
|
|
4
|
+
export type { ClientIpResolver } from './client-ip.js';
|
|
3
5
|
export { chatRequestSchema } from './config.js';
|
|
4
6
|
export { RateLimiter } from './rate-limit.js';
|
|
5
7
|
export type { RateLimitConfig } from './rate-limit.js';
|
|
@@ -7,5 +9,6 @@ export { WebhookDispatcher, verifyWebhook, signWebhook, assertSafeWebhookUrl, SI
|
|
|
7
9
|
export { chatcopsMiddleware, chatcopsAnalyticsHandler } from './adapters/express.js';
|
|
8
10
|
export { chatcopsVercelHandler } from './adapters/vercel.js';
|
|
9
11
|
export { chatcopsCloudflareHandler } from './adapters/cloudflare.js';
|
|
12
|
+
export { chatcopsAiSdkHandler } from './adapters/ai-sdk.js';
|
|
10
13
|
export { ServerAnalytics } from './analytics/server.js';
|
|
11
14
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzF,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC9E,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -5,5 +5,6 @@ export { WebhookDispatcher, verifyWebhook, signWebhook, assertSafeWebhookUrl, SI
|
|
|
5
5
|
export { chatcopsMiddleware, chatcopsAnalyticsHandler } from './adapters/express.js';
|
|
6
6
|
export { chatcopsVercelHandler } from './adapters/vercel.js';
|
|
7
7
|
export { chatcopsCloudflareHandler } from './adapters/cloudflare.js';
|
|
8
|
+
export { chatcopsAiSdkHandler } from './adapters/ai-sdk.js';
|
|
8
9
|
export { ServerAnalytics } from './analytics/server.js';
|
|
9
10
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAIjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,oBAAoB,EACpB,gBAAgB,GAEjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/rate-limit.d.ts
CHANGED
|
@@ -2,6 +2,11 @@ export interface RateLimitConfig {
|
|
|
2
2
|
maxRequests: number;
|
|
3
3
|
windowMs: number;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* @deprecated The server now limits with `createRateLimiter` from
|
|
7
|
+
* `@chatcops/core`, on the storage's counters, so limits hold across
|
|
8
|
+
* instances. This in-process limiter is kept for code that uses it directly.
|
|
9
|
+
*/
|
|
5
10
|
export declare class RateLimiter {
|
|
6
11
|
private store;
|
|
7
12
|
private config;
|
package/dist/rate-limit.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAqC;IAClD,OAAO,CAAC,MAAM,CAAkB;gBAEpB,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC;IAU7C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAkB7D,OAAO,IAAI,IAAI;CAQhB"}
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAqC;IAClD,OAAO,CAAC,MAAM,CAAkB;gBAEpB,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC;IAU7C,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAkB7D,OAAO,IAAI,IAAI;CAQhB"}
|
package/dist/rate-limit.js
CHANGED
|
@@ -2,6 +2,11 @@ const DEFAULT_CONFIG = {
|
|
|
2
2
|
maxRequests: 30,
|
|
3
3
|
windowMs: 60_000,
|
|
4
4
|
};
|
|
5
|
+
/**
|
|
6
|
+
* @deprecated The server now limits with `createRateLimiter` from
|
|
7
|
+
* `@chatcops/core`, on the storage's counters, so limits hold across
|
|
8
|
+
* instances. This in-process limiter is kept for code that uses it directly.
|
|
9
|
+
*/
|
|
5
10
|
export class RateLimiter {
|
|
6
11
|
store = new Map();
|
|
7
12
|
config;
|
package/dist/rate-limit.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAUA,MAAM,cAAc,GAAoB;IACtC,WAAW,EAAE,EAAE;IACf,QAAQ,EAAE,MAAM;CACjB,CAAC;AAEF,MAAM,OAAO,WAAW;IACd,KAAK,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC1C,MAAM,CAAkB;IAEhC,YAAY,MAAiC;QAC3C,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;QAC/C,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAW;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAElC,IAAI,CAAC,KAAK,IAAI,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;YACvE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;QAED,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;YAC3D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QACxC,CAAC;QAED,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,OAAO;QACL,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,IAAI,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBACzB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAUA,MAAM,cAAc,GAAoB;IACtC,WAAW,EAAE,EAAE;IACf,QAAQ,EAAE,MAAM;CACjB,CAAC;AAEF;;;;GAIG;AACH,MAAM,OAAO,WAAW;IACd,KAAK,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC1C,MAAM,CAAkB;IAEhC,YAAY,MAAiC;QAC3C,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;QAC/C,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAW;QACf,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAElC,IAAI,CAAC,KAAK,IAAI,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;YACvE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC3B,CAAC;QAED,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;YAC3D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC;QACxC,CAAC;QAED,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,OAAO;QACL,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,IAAI,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBACzB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ChatCopsServerConfig } from '../config.js';
|
|
2
|
+
/** `allowedOrigins`, or the older `cors` as a one-item list, or any origin. */
|
|
3
|
+
export declare function allowedOriginsOf(config: Pick<ChatCopsServerConfig, 'allowedOrigins' | 'cors'>): string[];
|
|
4
|
+
export interface OriginPolicy {
|
|
5
|
+
/** False for a browser request from a page whose origin isn't allowed. Requests without `Origin` pass. */
|
|
6
|
+
allows(origin: string | null): boolean;
|
|
7
|
+
/** CORS headers for any response to this origin, including errors. */
|
|
8
|
+
headers(origin: string | null): Record<string, string>;
|
|
9
|
+
/** Headers for the preflight answer. */
|
|
10
|
+
preflightHeaders(origin: string | null): Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Which sites can use the chat. This stops other sites embedding your bot in
|
|
14
|
+
* their pages; it doesn't stop scripts, which can send any `Origin`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function originPolicy(config: Pick<ChatCopsServerConfig, 'allowedOrigins' | 'cors'>): OriginPolicy;
|
|
17
|
+
/** For tests. */
|
|
18
|
+
export declare function resetCorsWarning(): void;
|
|
19
|
+
//# sourceMappingURL=origin-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origin-policy.d.ts","sourceRoot":"","sources":["../../src/security/origin-policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAMzD,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,GAAG,MAAM,CAAC,GAAG,MAAM,EAAE,CAkBxG;AAED,MAAM,WAAW,YAAY;IAC3B,0GAA0G;IAC1G,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;IACvC,sEAAsE;IACtE,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvD,wCAAwC;IACxC,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,oBAAoB,EAAE,gBAAgB,GAAG,MAAM,CAAC,GAAG,YAAY,CA2BxG;AAED,iBAAiB;AACjB,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { corsHeaders, matchOrigin } from './origin.js';
|
|
2
|
+
let warnedCors = false;
|
|
3
|
+
const MAX_REFUSAL_LOGS = 20;
|
|
4
|
+
/** `allowedOrigins`, or the older `cors` as a one-item list, or any origin. */
|
|
5
|
+
export function allowedOriginsOf(config) {
|
|
6
|
+
if (config.allowedOrigins !== undefined && config.cors !== undefined) {
|
|
7
|
+
throw new Error('Set `allowedOrigins` or `cors`, not both. `allowedOrigins` replaces `cors`.');
|
|
8
|
+
}
|
|
9
|
+
if (config.allowedOrigins !== undefined) {
|
|
10
|
+
if (config.allowedOrigins.length === 0) {
|
|
11
|
+
throw new Error("`allowedOrigins` is empty, so no site could use the chat. List your sites, or use ['*'] for any site.");
|
|
12
|
+
}
|
|
13
|
+
return config.allowedOrigins;
|
|
14
|
+
}
|
|
15
|
+
if (config.cors !== undefined) {
|
|
16
|
+
if (!warnedCors) {
|
|
17
|
+
warnedCors = true;
|
|
18
|
+
console.warn(`[chatcops] \`cors\` is deprecated. Use \`allowedOrigins: ['${config.cors}']\`, which also takes several sites and wildcards like 'https://*.acme.com'.`);
|
|
19
|
+
}
|
|
20
|
+
return [config.cors];
|
|
21
|
+
}
|
|
22
|
+
return ['*'];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Which sites can use the chat. This stops other sites embedding your bot in
|
|
26
|
+
* their pages; it doesn't stop scripts, which can send any `Origin`.
|
|
27
|
+
*/
|
|
28
|
+
export function originPolicy(config) {
|
|
29
|
+
const allowed = allowedOriginsOf(config);
|
|
30
|
+
const logged = new Set();
|
|
31
|
+
const allows = (origin) => !origin || matchOrigin(origin, allowed) !== null;
|
|
32
|
+
const headers = (origin) => {
|
|
33
|
+
if (allows(origin))
|
|
34
|
+
return corsHeaders(origin, allowed);
|
|
35
|
+
// A refused page still gets its own origin back, so the widget can read
|
|
36
|
+
// the 403 and say why in the console.
|
|
37
|
+
if (logged.size < MAX_REFUSAL_LOGS && origin && !logged.has(origin)) {
|
|
38
|
+
logged.add(origin);
|
|
39
|
+
console.warn(`[chatcops] Refused a request from ${origin}, which isn't in allowedOrigins.`);
|
|
40
|
+
}
|
|
41
|
+
return { 'Access-Control-Allow-Origin': origin ?? '', Vary: 'Origin' };
|
|
42
|
+
};
|
|
43
|
+
return {
|
|
44
|
+
allows,
|
|
45
|
+
headers,
|
|
46
|
+
preflightHeaders: (origin) => ({
|
|
47
|
+
...headers(origin),
|
|
48
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
49
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-ChatCops-Protocol',
|
|
50
|
+
'Access-Control-Max-Age': '86400',
|
|
51
|
+
}),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** For tests. */
|
|
55
|
+
export function resetCorsWarning() {
|
|
56
|
+
warnedCors = false;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=origin-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origin-policy.js","sourceRoot":"","sources":["../../src/security/origin-policy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEvD,IAAI,UAAU,GAAG,KAAK,CAAC;AACvB,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B,+EAA+E;AAC/E,MAAM,UAAU,gBAAgB,CAAC,MAA6D;IAC5F,IAAI,MAAM,CAAC,cAAc,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACrE,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;IACjG,CAAC;IACD,IAAI,MAAM,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QACxC,IAAI,MAAM,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CAAC,uGAAuG,CAAC,CAAC;QAC3H,CAAC;QACD,OAAO,MAAM,CAAC,cAAc,CAAC;IAC/B,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,UAAU,GAAG,IAAI,CAAC;YAClB,OAAO,CAAC,IAAI,CAAC,8DAA8D,MAAM,CAAC,IAAI,+EAA+E,CAAC,CAAC;QACzK,CAAC;QACD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAWD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAA6D;IACxF,MAAM,OAAO,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IAEjC,MAAM,MAAM,GAAG,CAAC,MAAqB,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAE3F,MAAM,OAAO,GAAG,CAAC,MAAqB,EAA0B,EAAE;QAChE,IAAI,MAAM,CAAC,MAAM,CAAC;YAAE,OAAO,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACxD,wEAAwE;QACxE,sCAAsC;QACtC,IAAI,MAAM,CAAC,IAAI,GAAG,gBAAgB,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACpE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACnB,OAAO,CAAC,IAAI,CAAC,qCAAqC,MAAM,kCAAkC,CAAC,CAAC;QAC9F,CAAC;QACD,OAAO,EAAE,6BAA6B,EAAE,MAAM,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IACzE,CAAC,CAAC;IAEF,OAAO;QACL,MAAM;QACN,OAAO;QACP,gBAAgB,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAC7B,GAAG,OAAO,CAAC,MAAM,CAAC;YAClB,8BAA8B,EAAE,eAAe;YAC/C,8BAA8B,EAAE,kDAAkD;YAClF,wBAAwB,EAAE,OAAO;SAClC,CAAC;KACH,CAAC;AACJ,CAAC;AAED,iBAAiB;AACjB,MAAM,UAAU,gBAAgB;IAC9B,UAAU,GAAG,KAAK,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Returns the allowed entry that matches, or null. */
|
|
2
|
+
export declare function matchOrigin(origin: string | null, allowed: string[]): string | null;
|
|
3
|
+
/** Returns CORS headers for a response to `origin`. */
|
|
4
|
+
export declare function corsHeaders(origin: string | null, allowed: string[]): Record<string, string>;
|
|
5
|
+
//# sourceMappingURL=origin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origin.d.ts","sourceRoot":"","sources":["../../src/security/origin.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAInF;AAED,uDAAuD;AACvD,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAe5F"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Returns the allowed entry that matches, or null. */
|
|
2
|
+
export function matchOrigin(origin, allowed) {
|
|
3
|
+
if (allowed.includes('*'))
|
|
4
|
+
return '*';
|
|
5
|
+
if (origin === null || origin === '' || origin === 'null')
|
|
6
|
+
return null;
|
|
7
|
+
return allowed.find((entry) => matchesEntry(origin, entry)) ?? null;
|
|
8
|
+
}
|
|
9
|
+
/** Returns CORS headers for a response to `origin`. */
|
|
10
|
+
export function corsHeaders(origin, allowed) {
|
|
11
|
+
const match = matchOrigin(origin, allowed);
|
|
12
|
+
const headers = {};
|
|
13
|
+
if (match === '*') {
|
|
14
|
+
headers['Access-Control-Allow-Origin'] = '*';
|
|
15
|
+
}
|
|
16
|
+
else if (match !== null && origin !== null) {
|
|
17
|
+
headers['Access-Control-Allow-Origin'] = origin;
|
|
18
|
+
}
|
|
19
|
+
if (!(allowed.length === 1 && allowed[0] === '*')) {
|
|
20
|
+
headers.Vary = 'Origin';
|
|
21
|
+
}
|
|
22
|
+
return headers;
|
|
23
|
+
}
|
|
24
|
+
function matchesEntry(origin, configured) {
|
|
25
|
+
const normalized = configured.toLowerCase().replace(/\/+$/, '');
|
|
26
|
+
const separator = normalized.indexOf('://');
|
|
27
|
+
if (separator === -1)
|
|
28
|
+
return false;
|
|
29
|
+
const scheme = normalized.slice(0, separator);
|
|
30
|
+
const authority = normalized.slice(separator + 3);
|
|
31
|
+
const wildcardHost = authority.startsWith('*.');
|
|
32
|
+
try {
|
|
33
|
+
const requestUrl = new URL(origin);
|
|
34
|
+
if (requestUrl.protocol !== `${scheme}:` || requestUrl.username || requestUrl.password)
|
|
35
|
+
return false;
|
|
36
|
+
if (!wildcardHost)
|
|
37
|
+
return new URL(normalized).origin === requestUrl.origin;
|
|
38
|
+
const baseUrl = new URL(`${scheme}://${authority.slice(2)}`);
|
|
39
|
+
return (requestUrl.port === baseUrl.port &&
|
|
40
|
+
requestUrl.hostname.endsWith(`.${baseUrl.hostname}`) &&
|
|
41
|
+
requestUrl.hostname !== baseUrl.hostname);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=origin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"origin.js","sourceRoot":"","sources":["../../src/security/origin.ts"],"names":[],"mappings":"AAAA,uDAAuD;AACvD,MAAM,UAAU,WAAW,CAAC,MAAqB,EAAE,OAAiB;IAClE,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACtC,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,EAAE,IAAI,MAAM,KAAK,MAAM;QAAE,OAAO,IAAI,CAAC;IACvE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC;AACtE,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,WAAW,CAAC,MAAqB,EAAE,OAAiB;IAClE,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,MAAM,OAAO,GAA2B,EAAE,CAAC;IAE3C,IAAI,KAAK,KAAK,GAAG,EAAE,CAAC;QAClB,OAAO,CAAC,6BAA6B,CAAC,GAAG,GAAG,CAAC;IAC/C,CAAC;SAAM,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QAC7C,OAAO,CAAC,6BAA6B,CAAC,GAAG,MAAM,CAAC;IAClD,CAAC;IAED,IAAI,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC;QAClD,OAAO,CAAC,IAAI,GAAG,QAAQ,CAAC;IAC1B,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,YAAY,CAAC,MAAc,EAAE,UAAkB;IACtD,MAAM,UAAU,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAChE,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5C,IAAI,SAAS,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAEnC,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAEhD,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QACnC,IAAI,UAAU,CAAC,QAAQ,KAAK,GAAG,MAAM,GAAG,IAAI,UAAU,CAAC,QAAQ,IAAI,UAAU,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAC;QAErG,IAAI,CAAC,YAAY;YAAE,OAAO,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,MAAM,CAAC;QAE3E,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,GAAG,MAAM,MAAM,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC7D,OAAO,CACL,UAAU,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI;YAChC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACpD,UAAU,CAAC,QAAQ,KAAK,OAAO,CAAC,QAAQ,CACzC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|