@tumbaland/backend-core 1.45.0 → 1.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/shutdown.d.ts +3 -3
- package/dist/app/shutdown.d.ts.map +1 -1
- package/dist/app/shutdown.js +6 -3
- package/dist/app/shutdown.js.map +1 -1
- package/dist/fileService/client.d.ts +54 -0
- package/dist/fileService/client.d.ts.map +1 -0
- package/dist/fileService/client.js +137 -0
- package/dist/fileService/client.js.map +1 -0
- package/dist/fileService/index.d.ts +4 -0
- package/dist/fileService/index.d.ts.map +1 -0
- package/dist/fileService/index.js +40 -0
- package/dist/fileService/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/middleware/corsMiddleware.d.ts.map +1 -1
- package/dist/middleware/corsMiddleware.js +4 -1
- package/dist/middleware/corsMiddleware.js.map +1 -1
- package/dist/middleware/errorHandler.d.ts.map +1 -1
- package/dist/middleware/errorHandler.js +5 -8
- package/dist/middleware/errorHandler.js.map +1 -1
- package/dist/sse/index.d.ts +3 -0
- package/dist/sse/index.d.ts.map +1 -0
- package/dist/sse/index.js +11 -0
- package/dist/sse/index.js.map +1 -0
- package/dist/sse/sseStream.d.ts +57 -0
- package/dist/sse/sseStream.d.ts.map +1 -0
- package/dist/sse/sseStream.js +143 -0
- package/dist/sse/sseStream.js.map +1 -0
- package/dist/utils/escapeRegex.d.ts +8 -0
- package/dist/utils/escapeRegex.d.ts.map +1 -0
- package/dist/utils/escapeRegex.js +12 -0
- package/dist/utils/escapeRegex.js.map +1 -0
- package/package.json +1 -1
package/dist/app/shutdown.d.ts
CHANGED
|
@@ -19,9 +19,9 @@ export interface RegisterShutdownOptions {
|
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Registers `SIGINT` and `SIGTERM` handlers that drain the service before the
|
|
22
|
-
* process exits: run the optional cleanup hook,
|
|
23
|
-
* connections, then close the MongoDB connection.
|
|
24
|
-
* it's the signal Docker/Kubernetes send on stop and rollout, and without a
|
|
22
|
+
* process exits: end any open event streams, run the optional cleanup hook,
|
|
23
|
+
* stop accepting new HTTP connections, then close the MongoDB connection.
|
|
24
|
+
* `SIGTERM` matters most — it's the signal Docker/Kubernetes send on stop and rollout, and without a
|
|
25
25
|
* handler the process is force-killed with its DB connection still open.
|
|
26
26
|
*
|
|
27
27
|
* The handler is idempotent (a second signal while shutting down is ignored)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../../src/app/shutdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../../src/app/shutdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAKnC,MAAM,WAAW,uBAAuB;IACtC,gEAAgE;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxC;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,2EAM9B,uBAAuB,KAAG,IA2C5B,CAAC"}
|
package/dist/app/shutdown.js
CHANGED
|
@@ -6,11 +6,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.registerShutdown = void 0;
|
|
7
7
|
const logger_1 = __importDefault(require("../logging/logger"));
|
|
8
8
|
const connection_1 = require("../database/connection");
|
|
9
|
+
const sseStream_1 = require("../sse/sseStream");
|
|
9
10
|
/**
|
|
10
11
|
* Registers `SIGINT` and `SIGTERM` handlers that drain the service before the
|
|
11
|
-
* process exits: run the optional cleanup hook,
|
|
12
|
-
* connections, then close the MongoDB connection.
|
|
13
|
-
* it's the signal Docker/Kubernetes send on stop and rollout, and without a
|
|
12
|
+
* process exits: end any open event streams, run the optional cleanup hook,
|
|
13
|
+
* stop accepting new HTTP connections, then close the MongoDB connection.
|
|
14
|
+
* `SIGTERM` matters most — it's the signal Docker/Kubernetes send on stop and rollout, and without a
|
|
14
15
|
* handler the process is force-killed with its DB connection still open.
|
|
15
16
|
*
|
|
16
17
|
* The handler is idempotent (a second signal while shutting down is ignored)
|
|
@@ -30,6 +31,8 @@ const registerShutdown = ({ serviceName, server, disconnectDatabase = true, onSh
|
|
|
30
31
|
// Don't let the timer itself keep the event loop alive.
|
|
31
32
|
forceExit.unref?.();
|
|
32
33
|
try {
|
|
34
|
+
// An event stream never finishes by itself, and server.close() waits for it.
|
|
35
|
+
(0, sseStream_1.closeAllSseStreams)();
|
|
33
36
|
if (onShutdown)
|
|
34
37
|
await onShutdown();
|
|
35
38
|
if (server) {
|
package/dist/app/shutdown.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../../src/app/shutdown.ts"],"names":[],"mappings":";;;;;;AACA,+DAAuC;AACvC,uDAAsD;AAqBtD;;;;;;;;;GASG;AACI,MAAM,gBAAgB,GAAG,CAAC,EAC/B,WAAW,EACX,MAAM,EACN,kBAAkB,GAAG,IAAI,EACzB,UAAU,EACV,gBAAgB,GAAG,MAAM,EACD,EAAQ,EAAE;IAClC,IAAI,YAAY,GAAG,KAAK,CAAC;IAEzB,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAc,EAAiB,EAAE;QACvD,IAAI,YAAY;YAAE,OAAO;QACzB,YAAY,GAAG,IAAI,CAAC;QAEpB,gBAAM,CAAC,IAAI,CAAC,8CAA8C,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QAE9F,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAChC,gBAAM,CAAC,KAAK,CAAC,2CAA2C,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;YACpF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACrB,wDAAwD;QACxD,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;QAEpB,IAAI,CAAC;YACH,IAAI,UAAU;gBAAE,MAAM,UAAU,EAAE,CAAC;YAEnC,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;oBAC1C,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBACzD,CAAC,CAAC,CAAC;YACL,CAAC;YAED,IAAI,kBAAkB;gBAAE,MAAM,IAAA,yBAAY,EAAC,WAAW,CAAC,CAAC;YAExD,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,gBAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE;gBAC7C,OAAO,EAAE,WAAW;gBACpB,KAAK,EAAG,KAAe,EAAE,OAAO;aACjC,CAAC,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AACxD,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../../src/app/shutdown.ts"],"names":[],"mappings":";;;;;;AACA,+DAAuC;AACvC,uDAAsD;AACtD,gDAAsD;AAqBtD;;;;;;;;;GASG;AACI,MAAM,gBAAgB,GAAG,CAAC,EAC/B,WAAW,EACX,MAAM,EACN,kBAAkB,GAAG,IAAI,EACzB,UAAU,EACV,gBAAgB,GAAG,MAAM,EACD,EAAQ,EAAE;IAClC,IAAI,YAAY,GAAG,KAAK,CAAC;IAEzB,MAAM,QAAQ,GAAG,KAAK,EAAE,MAAc,EAAiB,EAAE;QACvD,IAAI,YAAY;YAAE,OAAO;QACzB,YAAY,GAAG,IAAI,CAAC;QAEpB,gBAAM,CAAC,IAAI,CAAC,8CAA8C,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;QAE9F,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAChC,gBAAM,CAAC,KAAK,CAAC,2CAA2C,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;YACpF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACrB,wDAAwD;QACxD,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;QAEpB,IAAI,CAAC;YACH,6EAA6E;YAC7E,IAAA,8BAAkB,GAAE,CAAC;YACrB,IAAI,UAAU;gBAAE,MAAM,UAAU,EAAE,CAAC;YAEnC,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;oBAC1C,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBACzD,CAAC,CAAC,CAAC;YACL,CAAC;YAED,IAAI,kBAAkB;gBAAE,MAAM,IAAA,yBAAY,EAAC,WAAW,CAAC,CAAC;YAExD,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,gBAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE;gBAC7C,OAAO,EAAE,WAAW;gBACpB,KAAK,EAAG,KAAe,EAAE,OAAO;aACjC,CAAC,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,KAAK,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AACxD,CAAC,CAAC;AAjDW,QAAA,gBAAgB,oBAiD3B"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-to-server calls to file-service's `/internal` API, authenticated with
|
|
3
|
+
* `FILE_SERVICE_API_KEY`. Browsers never come through here: they get a signed
|
|
4
|
+
* URL from this client and upload or download against file-service directly.
|
|
5
|
+
*
|
|
6
|
+
* This is the protocol only. What a failure means is the caller's decision (a
|
|
7
|
+
* thread that renders without its images beats one that fails to load), so
|
|
8
|
+
* every call throws a `FileServiceError` and leaves degrading to the caller.
|
|
9
|
+
*/
|
|
10
|
+
export declare class FileServiceError extends Error {
|
|
11
|
+
readonly status: number;
|
|
12
|
+
constructor(status: number, message: string);
|
|
13
|
+
}
|
|
14
|
+
export interface DownloadUrl {
|
|
15
|
+
downloadUrl: string;
|
|
16
|
+
/** When the URL stops working, in epoch milliseconds. */
|
|
17
|
+
expiresMs: number;
|
|
18
|
+
expiresInMinutes: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A signed PUT URL the browser uploads to directly.
|
|
22
|
+
*
|
|
23
|
+
* `maxBytes` is signed into the URL and file-service refuses a bigger body, so
|
|
24
|
+
* the size a storage quota was checked against is an upper bound on what lands.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getUploadUrl(fileId: string, options?: {
|
|
27
|
+
mimetype?: string;
|
|
28
|
+
expiresInMinutes?: number;
|
|
29
|
+
maxBytes?: number;
|
|
30
|
+
}): Promise<string>;
|
|
31
|
+
/** A signed GET URL for one file. */
|
|
32
|
+
export declare function getDownloadUrl(fileId: string, expiresInMinutes?: number): Promise<DownloadUrl>;
|
|
33
|
+
/**
|
|
34
|
+
* Signed GET URLs for many files, keyed by fileId, in one request per thousand.
|
|
35
|
+
*
|
|
36
|
+
* Signing is pure HMAC and file-service snaps the expiry to a bucket, so the
|
|
37
|
+
* same file yields a byte-identical URL for a while. That is what lets the
|
|
38
|
+
* browser's cache work; a per-file request for each one tripped the rate
|
|
39
|
+
* limiter on a 100-photo collage.
|
|
40
|
+
*/
|
|
41
|
+
export declare function getDownloadUrls(fileIds: string[], expiresInMinutes?: number): Promise<Record<string, DownloadUrl>>;
|
|
42
|
+
/** Stores bytes this service produced itself, such as a thumbnail. */
|
|
43
|
+
export declare function putFile(fileId: string, bytes: Buffer, mimetype: string): Promise<void>;
|
|
44
|
+
/** Reads a stored file back. A missing file is a `FileServiceError` with status 404. */
|
|
45
|
+
export declare function getFile(fileId: string): Promise<Buffer>;
|
|
46
|
+
export declare function deleteFile(fileId: string): Promise<void>;
|
|
47
|
+
/** Deletes every file whose id starts with `prefix`. */
|
|
48
|
+
export declare function deleteFolder(prefix: string): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* How many bytes a stored file really takes, or null when there is no such
|
|
51
|
+
* file (an upload that never finished).
|
|
52
|
+
*/
|
|
53
|
+
export declare function storedSize(fileId: string): Promise<number | null>;
|
|
54
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/fileService/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EAC9B,OAAO,EAAE,MAAM;CAKlB;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AA0CD;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAChF,OAAO,CAAC,MAAM,CAAC,CASjB;AAED,qCAAqC;AACrC,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,SAAK,GAAG,OAAO,CAAC,WAAW,CAAC,CAEhG;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,gBAAgB,SAAK,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAWpH;AAED,sEAAsE;AACtE,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE5F;AAED,wFAAwF;AACxF,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG7D;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9D;AAED,wDAAwD;AACxD,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEhE;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOvE"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Server-to-server calls to file-service's `/internal` API, authenticated with
|
|
4
|
+
* `FILE_SERVICE_API_KEY`. Browsers never come through here: they get a signed
|
|
5
|
+
* URL from this client and upload or download against file-service directly.
|
|
6
|
+
*
|
|
7
|
+
* This is the protocol only. What a failure means is the caller's decision (a
|
|
8
|
+
* thread that renders without its images beats one that fails to load), so
|
|
9
|
+
* every call throws a `FileServiceError` and leaves degrading to the caller.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.FileServiceError = void 0;
|
|
13
|
+
exports.getUploadUrl = getUploadUrl;
|
|
14
|
+
exports.getDownloadUrl = getDownloadUrl;
|
|
15
|
+
exports.getDownloadUrls = getDownloadUrls;
|
|
16
|
+
exports.putFile = putFile;
|
|
17
|
+
exports.getFile = getFile;
|
|
18
|
+
exports.deleteFile = deleteFile;
|
|
19
|
+
exports.deleteFolder = deleteFolder;
|
|
20
|
+
exports.storedSize = storedSize;
|
|
21
|
+
/** Abort a call that gets no response, so callers degrade instead of hanging. */
|
|
22
|
+
const REQUEST_TIMEOUT_MS = 10_000;
|
|
23
|
+
/** The most fileIds file-service signs in one `/internal/download-urls` request. */
|
|
24
|
+
const DOWNLOAD_URLS_PER_REQUEST = 1000;
|
|
25
|
+
class FileServiceError extends Error {
|
|
26
|
+
status;
|
|
27
|
+
constructor(status, message) {
|
|
28
|
+
super(message);
|
|
29
|
+
this.status = status;
|
|
30
|
+
this.name = 'FileServiceError';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.FileServiceError = FileServiceError;
|
|
34
|
+
const baseUrl = () => (process.env.FILE_SERVICE_URL || 'http://localhost:5002').replace(/\/$/, '');
|
|
35
|
+
const apiKey = () => {
|
|
36
|
+
const key = process.env.FILE_SERVICE_API_KEY;
|
|
37
|
+
if (!key)
|
|
38
|
+
throw new Error('FILE_SERVICE_API_KEY env var is required');
|
|
39
|
+
return key;
|
|
40
|
+
};
|
|
41
|
+
async function send(method, path, body) {
|
|
42
|
+
const headers = { 'X-Internal-Api-Key': apiKey() };
|
|
43
|
+
let payload;
|
|
44
|
+
if (body && 'json' in body) {
|
|
45
|
+
headers['Content-Type'] = 'application/json';
|
|
46
|
+
payload = JSON.stringify(body.json);
|
|
47
|
+
}
|
|
48
|
+
else if (body) {
|
|
49
|
+
headers['Content-Type'] = body.mimetype;
|
|
50
|
+
payload = new Uint8Array(body.bytes);
|
|
51
|
+
}
|
|
52
|
+
const response = await fetch(`${baseUrl()}${path}`, {
|
|
53
|
+
method,
|
|
54
|
+
headers,
|
|
55
|
+
body: payload,
|
|
56
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new FileServiceError(response.status, `file-service ${method} ${path} -> ${response.status}`);
|
|
60
|
+
}
|
|
61
|
+
return response;
|
|
62
|
+
}
|
|
63
|
+
async function data(method, path, json) {
|
|
64
|
+
const response = await send(method, path, json === undefined ? undefined : { json });
|
|
65
|
+
return (await response.json()).data;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A signed PUT URL the browser uploads to directly.
|
|
69
|
+
*
|
|
70
|
+
* `maxBytes` is signed into the URL and file-service refuses a bigger body, so
|
|
71
|
+
* the size a storage quota was checked against is an upper bound on what lands.
|
|
72
|
+
*/
|
|
73
|
+
async function getUploadUrl(fileId, options = {}) {
|
|
74
|
+
const { mimetype, expiresInMinutes = 15, maxBytes } = options;
|
|
75
|
+
const result = await data('POST', '/internal/upload-url', {
|
|
76
|
+
fileId,
|
|
77
|
+
expiresInMinutes,
|
|
78
|
+
...(mimetype === undefined ? {} : { mimetype }),
|
|
79
|
+
...(maxBytes === undefined ? {} : { maxBytes })
|
|
80
|
+
});
|
|
81
|
+
return result.uploadUrl;
|
|
82
|
+
}
|
|
83
|
+
/** A signed GET URL for one file. */
|
|
84
|
+
async function getDownloadUrl(fileId, expiresInMinutes = 60) {
|
|
85
|
+
return data('POST', '/internal/download-url', { fileId, expiresInMinutes });
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Signed GET URLs for many files, keyed by fileId, in one request per thousand.
|
|
89
|
+
*
|
|
90
|
+
* Signing is pure HMAC and file-service snaps the expiry to a bucket, so the
|
|
91
|
+
* same file yields a byte-identical URL for a while. That is what lets the
|
|
92
|
+
* browser's cache work; a per-file request for each one tripped the rate
|
|
93
|
+
* limiter on a 100-photo collage.
|
|
94
|
+
*/
|
|
95
|
+
async function getDownloadUrls(fileIds, expiresInMinutes = 60) {
|
|
96
|
+
const unique = [...new Set(fileIds.filter(Boolean))];
|
|
97
|
+
const urls = {};
|
|
98
|
+
for (let i = 0; i < unique.length; i += DOWNLOAD_URLS_PER_REQUEST) {
|
|
99
|
+
const result = await data('POST', '/internal/download-urls', {
|
|
100
|
+
fileIds: unique.slice(i, i + DOWNLOAD_URLS_PER_REQUEST),
|
|
101
|
+
expiresInMinutes
|
|
102
|
+
});
|
|
103
|
+
Object.assign(urls, result.urls);
|
|
104
|
+
}
|
|
105
|
+
return urls;
|
|
106
|
+
}
|
|
107
|
+
/** Stores bytes this service produced itself, such as a thumbnail. */
|
|
108
|
+
async function putFile(fileId, bytes, mimetype) {
|
|
109
|
+
await send('PUT', `/internal/files/${fileId}`, { bytes, mimetype });
|
|
110
|
+
}
|
|
111
|
+
/** Reads a stored file back. A missing file is a `FileServiceError` with status 404. */
|
|
112
|
+
async function getFile(fileId) {
|
|
113
|
+
const response = await send('GET', `/internal/files/${fileId}`);
|
|
114
|
+
return Buffer.from(await response.arrayBuffer());
|
|
115
|
+
}
|
|
116
|
+
async function deleteFile(fileId) {
|
|
117
|
+
await send('DELETE', `/internal/files/${fileId}`);
|
|
118
|
+
}
|
|
119
|
+
/** Deletes every file whose id starts with `prefix`. */
|
|
120
|
+
async function deleteFolder(prefix) {
|
|
121
|
+
await send('DELETE', '/internal/files/folder', { json: { prefix } });
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* How many bytes a stored file really takes, or null when there is no such
|
|
125
|
+
* file (an upload that never finished).
|
|
126
|
+
*/
|
|
127
|
+
async function storedSize(fileId) {
|
|
128
|
+
try {
|
|
129
|
+
return (await data('GET', `/internal/meta/${fileId}`)).size;
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
if (error instanceof FileServiceError && error.status === 404)
|
|
133
|
+
return null;
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/fileService/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/fileService/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,WAAW,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.FileServiceError = exports.fileService = void 0;
|
|
37
|
+
exports.fileService = __importStar(require("./client"));
|
|
38
|
+
var client_1 = require("./client");
|
|
39
|
+
Object.defineProperty(exports, "FileServiceError", { enumerable: true, get: function () { return client_1.FileServiceError; } });
|
|
40
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/fileService/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,wDAAwC;AACxC,mCAA4C;AAAnC,0GAAA,gBAAgB,OAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,8 +13,10 @@ export type { PaymentRequiredDetails } from './errors/HttpError';
|
|
|
13
13
|
export * from './entitlements';
|
|
14
14
|
export * from './groups';
|
|
15
15
|
export * from './apiKeys';
|
|
16
|
+
export * from './fileService';
|
|
16
17
|
export * from './oauth';
|
|
17
18
|
export * from './audit';
|
|
19
|
+
export * from './sse';
|
|
18
20
|
export { authenticateToken, optionalAuth } from './middleware/authMiddleware';
|
|
19
21
|
export { USER_SESSIONS_COLLECTION, isCurrentSession, isLegacySessionlessToken } from './auth/session';
|
|
20
22
|
export { requireInternalServiceToken, internalServiceTokenBypass, allowUserOrInternalService, verifyInternalToken, INTERNAL_TOKEN_HEADER, SERVICE_ID_HEADER } from './middleware/internalServiceAuth';
|
|
@@ -28,6 +30,7 @@ export { generateCorrelationId, correlationMiddleware } from './utils/correlatio
|
|
|
28
30
|
export { createApiResponse, sendSuccess, sendError } from './utils/response';
|
|
29
31
|
export type { ApiResponse } from './utils/response';
|
|
30
32
|
export { buildAccessQuery, canAccessResource } from './utils/permissionUtils';
|
|
33
|
+
export { escapeRegex } from './utils/escapeRegex';
|
|
31
34
|
export { metricsMiddleware, httpRequestDuration, httpRequestsTotal, databaseQueryDuration, databaseQueriesTotal, businessMetrics, register } from './metrics';
|
|
32
35
|
export { initTracer, getTracer, startSpan, tracingMiddleware, createChildSpan, logToSpan, setSpanTag, injectHeaders, extractSpanContext } from './tracing';
|
|
33
36
|
//# 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":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAG9D,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,YAAY,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGpE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGhE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC7K,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAGjE,cAAc,gBAAgB,CAAC;AAG/B,cAAc,UAAU,CAAC;AAGzB,cAAc,WAAW,CAAC;AAG1B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AAGxB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AACtG,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,0BAA0B,EAC1B,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAC1G,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACnH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7E,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAG9D,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,YAAY,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAGpE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGhE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC7K,YAAY,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAGjE,cAAc,gBAAgB,CAAC;AAG/B,cAAc,UAAU,CAAC;AAGzB,cAAc,WAAW,CAAC;AAG1B,cAAc,eAAe,CAAC;AAG9B,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AAGxB,cAAc,OAAO,CAAC;AAGtB,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC9E,OAAO,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AACtG,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,0BAA0B,EAC1B,mBAAmB,EACnB,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAC1G,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AACnH,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGjD,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7E,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGlD,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,EACf,QAAQ,EACT,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,UAAU,EACV,SAAS,EACT,SAAS,EACT,iBAAiB,EACjB,eAAe,EACf,SAAS,EACT,UAAU,EACV,aAAa,EACb,kBAAkB,EACnB,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -18,8 +18,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
18
18
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
19
|
};
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.
|
|
22
|
-
exports.extractSpanContext = exports.injectHeaders = exports.setSpanTag = exports.logToSpan = exports.createChildSpan = exports.tracingMiddleware = exports.startSpan = exports.getTracer = exports.initTracer = exports.register = exports.businessMetrics = exports.databaseQueriesTotal = void 0;
|
|
21
|
+
exports.httpRequestsTotal = exports.httpRequestDuration = exports.metricsMiddleware = exports.escapeRegex = exports.canAccessResource = exports.buildAccessQuery = exports.sendError = exports.sendSuccess = exports.createApiResponse = exports.correlationMiddleware = exports.generateCorrelationId = exports.validate = exports.strictRateLimiter = exports.standardRateLimiter = exports.createRateLimiter = exports.securityHeaders = exports.simpleRequestLogger = exports.requestLoggerWithMetrics = exports.requestLogger = exports.errorHandler = exports.createCorsMiddleware = exports.SERVICE_ID_HEADER = exports.INTERNAL_TOKEN_HEADER = exports.verifyInternalToken = exports.allowUserOrInternalService = exports.internalServiceTokenBypass = exports.requireInternalServiceToken = exports.isLegacySessionlessToken = exports.isCurrentSession = exports.USER_SESSIONS_COLLECTION = exports.optionalAuth = exports.authenticateToken = exports.PaymentRequiredError = exports.TooManyRequestsError = exports.ConflictError = exports.NotFoundError = exports.ForbiddenError = exports.UnauthorizedError = exports.BadRequestError = exports.HttpError = exports.requireEnv = exports.disconnectDB = exports.connectDB = exports.createHealthCheck = exports.metricsHandler = exports.healthLive = exports.healthCheck = exports.logger = exports.registerShutdown = exports.createBaseApp = void 0;
|
|
22
|
+
exports.extractSpanContext = exports.injectHeaders = exports.setSpanTag = exports.logToSpan = exports.createChildSpan = exports.tracingMiddleware = exports.startSpan = exports.getTracer = exports.initTracer = exports.register = exports.businessMetrics = exports.databaseQueriesTotal = exports.databaseQueryDuration = void 0;
|
|
23
23
|
// App bootstrap
|
|
24
24
|
var createBaseApp_1 = require("./app/createBaseApp");
|
|
25
25
|
Object.defineProperty(exports, "createBaseApp", { enumerable: true, get: function () { return createBaseApp_1.createBaseApp; } });
|
|
@@ -58,9 +58,13 @@ __exportStar(require("./entitlements"), exports);
|
|
|
58
58
|
__exportStar(require("./groups"), exports);
|
|
59
59
|
// API keys (non-browser clients: MCP servers, scripts, agents)
|
|
60
60
|
__exportStar(require("./apiKeys"), exports);
|
|
61
|
+
// file-service's internal API (signed URLs, server-side file reads and writes)
|
|
62
|
+
__exportStar(require("./fileService"), exports);
|
|
61
63
|
// OAuth 2.1 authorization server, for assistants connecting over MCP
|
|
62
64
|
__exportStar(require("./oauth"), exports);
|
|
63
65
|
__exportStar(require("./audit"), exports);
|
|
66
|
+
// Server-Sent Events streams (headers, heartbeat, lifetime, shutdown)
|
|
67
|
+
__exportStar(require("./sse"), exports);
|
|
64
68
|
// Middleware
|
|
65
69
|
var authMiddleware_1 = require("./middleware/authMiddleware");
|
|
66
70
|
Object.defineProperty(exports, "authenticateToken", { enumerable: true, get: function () { return authMiddleware_1.authenticateToken; } });
|
|
@@ -102,6 +106,8 @@ Object.defineProperty(exports, "sendError", { enumerable: true, get: function ()
|
|
|
102
106
|
var permissionUtils_1 = require("./utils/permissionUtils");
|
|
103
107
|
Object.defineProperty(exports, "buildAccessQuery", { enumerable: true, get: function () { return permissionUtils_1.buildAccessQuery; } });
|
|
104
108
|
Object.defineProperty(exports, "canAccessResource", { enumerable: true, get: function () { return permissionUtils_1.canAccessResource; } });
|
|
109
|
+
var escapeRegex_1 = require("./utils/escapeRegex");
|
|
110
|
+
Object.defineProperty(exports, "escapeRegex", { enumerable: true, get: function () { return escapeRegex_1.escapeRegex; } });
|
|
105
111
|
// Metrics
|
|
106
112
|
var metrics_1 = require("./metrics");
|
|
107
113
|
Object.defineProperty(exports, "metricsMiddleware", { enumerable: true, get: function () { return metrics_1.metricsMiddleware; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,qFAAqF;;;;;;;;;;;;;;;;;;;;;AAErF,gBAAgB;AAChB,qDAAoD;AAA3C,8GAAA,aAAa,OAAA;AAEtB,2CAAkD;AAAzC,4GAAA,gBAAgB,OAAA;AAGzB,UAAU;AACV,2CAAqD;AAA5C,iHAAA,OAAO,OAAU;AAE1B,gBAAgB;AAChB,8DAAoF;AAA3E,+GAAA,WAAW,OAAA;AAAE,8GAAA,UAAU,OAAA;AAAE,kHAAA,cAAc,OAAA;AAChD,gEAA+D;AAAtD,sHAAA,iBAAiB,OAAA;AAG1B,WAAW;AACX,oDAAgE;AAAvD,uGAAA,SAAS,OAAA;AAAE,0GAAA,YAAY,OAAA;AAEhC,SAAS;AACT,oCAA0C;AAAjC,iGAAA,UAAU,OAAA;AAEnB,SAAS;AACT,gDAA6K;AAApK,sGAAA,SAAS,OAAA;AAAE,4GAAA,eAAe,OAAA;AAAE,8GAAA,iBAAiB,OAAA;AAAE,2GAAA,cAAc,OAAA;AAAE,0GAAA,aAAa,OAAA;AAAE,0GAAA,aAAa,OAAA;AAAE,iHAAA,oBAAoB,OAAA;AAAE,iHAAA,oBAAoB,OAAA;AAGhJ,wDAAwD;AACxD,iDAA+B;AAE/B,0DAA0D;AAC1D,2CAAyB;AAEzB,+DAA+D;AAC/D,4CAA0B;AAE1B,qEAAqE;AACrE,0CAAwB;AACxB,0CAAwB;AAExB,aAAa;AACb,8DAA8E;AAArE,mHAAA,iBAAiB,OAAA;AAAE,8GAAA,YAAY,OAAA;AACxC,0CAAsG;AAA7F,mHAAA,wBAAwB,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAAE,mHAAA,wBAAwB,OAAA;AAC7E,wEAO0C;AANxC,kIAAA,2BAA2B,OAAA;AAC3B,iIAAA,0BAA0B,OAAA;AAC1B,iIAAA,0BAA0B,OAAA;AAC1B,0HAAA,mBAAmB,OAAA;AACnB,4HAAA,qBAAqB,OAAA;AACrB,wHAAA,iBAAiB,OAAA;AAEnB,8DAAmE;AAA1D,sHAAA,oBAAoB,OAAA;AAC7B,0DAAyD;AAAhD,4GAAA,YAAY,OAAA;AACrB,4DAA0G;AAAjG,8GAAA,aAAa,OAAA;AAAE,yHAAA,wBAAwB,OAAA;AAAE,oHAAA,mBAAmB,OAAA;AACrE,kDAAmH;AAA1G,2GAAA,eAAe,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AAAE,+GAAA,mBAAmB,OAAA;AAAE,6GAAA,iBAAiB,OAAA;AACnF,kDAAiD;AAAxC,oGAAA,QAAQ,OAAA;AAKjB,QAAQ;AACR,mDAAmF;AAA1E,oHAAA,qBAAqB,OAAA;AAAE,oHAAA,qBAAqB,OAAA;AACrD,6CAA6E;AAApE,6GAAA,iBAAiB,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,qGAAA,SAAS,OAAA;AAElD,2DAA8E;AAArE,mHAAA,gBAAgB,OAAA;AAAE,oHAAA,iBAAiB,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"corsMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware/corsMiddleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"corsMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware/corsMiddleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAI9C,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;;;;;;;;;;;;;OAaG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAiCxF"}
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.createCorsMiddleware = createCorsMiddleware;
|
|
7
7
|
const cors_1 = __importDefault(require("cors"));
|
|
8
8
|
const logger_1 = __importDefault(require("../logging/logger"));
|
|
9
|
+
const HttpError_1 = require("../errors/HttpError");
|
|
9
10
|
/**
|
|
10
11
|
* Shared CORS middleware with an explicit origin whitelist.
|
|
11
12
|
* Requests without an Origin header (server-to-server, curl, health checks)
|
|
@@ -23,7 +24,9 @@ function createCorsMiddleware(options = {}) {
|
|
|
23
24
|
return callback(null, true);
|
|
24
25
|
}
|
|
25
26
|
logger_1.default.warn('Blocked CORS origin', { origin });
|
|
26
|
-
|
|
27
|
+
// A 403, not a plain Error: that became a 500 logged as a server bug
|
|
28
|
+
// for what is only a page on another site calling in.
|
|
29
|
+
return callback(new HttpError_1.ForbiddenError('Origin not allowed'));
|
|
27
30
|
},
|
|
28
31
|
credentials: true,
|
|
29
32
|
// PATCH belongs here as much as PUT: leaving it out lets the preflight
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"corsMiddleware.js","sourceRoot":"","sources":["../../src/middleware/corsMiddleware.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"corsMiddleware.js","sourceRoot":"","sources":["../../src/middleware/corsMiddleware.ts"],"names":[],"mappings":";;;;;AAkCA,oDAiCC;AAnED,gDAAwB;AAExB,+DAAuC;AACvC,mDAAqD;AA0BrD;;;;GAIG;AACH,SAAgB,oBAAoB,CAAC,UAAiC,EAAE;IACtE,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC;IAE9C,MAAM,WAAW,GAAG,IAAA,cAAI,EAAC;QACvB,MAAM,EAAE,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE;YAC3B,MAAM,cAAc,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;iBACtE,KAAK,CAAC,GAAG,CAAC;iBACV,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;iBAClB,MAAM,CAAC,OAAO,CAAC,CAAC;YACnB,IAAI,CAAC,MAAM,IAAI,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC/C,OAAO,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC9B,CAAC;YACD,gBAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;YAC/C,qEAAqE;YACrE,sDAAsD;YACtD,OAAO,QAAQ,CAAC,IAAI,0BAAc,CAAC,oBAAoB,CAAC,CAAC,CAAC;QAC5D,CAAC;QACD,WAAW,EAAE,IAAI;QACjB,uEAAuE;QACvE,uEAAuE;QACvE,wEAAwE;QACxE,kDAAkD;QAClD,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC;QAC7D,cAAc,EAAE,CAAC,cAAc,EAAE,eAAe,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,cAAc,CAAC;QACzG,cAAc,EAAE,OAAO,CAAC,cAAc;KACvC,CAAC,CAAC;IAEH,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,WAA6B,CAAC;IAEnE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxB,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAAE,OAAO,IAAI,EAAE,CAAC;QAC7E,OAAQ,WAA8B,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,CAAC;IACzD,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,mBAAmB,EAAE,MAAM,SAAS,CAAC;AA8B/E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,mBAAmB,EAAE,MAAM,SAAS,CAAC;AA8B/E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,EAAE,mBA0D1B,CAAC"}
|
|
@@ -58,14 +58,11 @@ const errorHandler = (error, req, res, _next) => {
|
|
|
58
58
|
correlationId: req.correlationId
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
67
|
-
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, x-correlation-id, x-session-id');
|
|
68
|
-
}
|
|
61
|
+
// No CORS headers here. The CORS middleware sets them for an allowed origin
|
|
62
|
+
// before the route runs, so an error response already carries them. This
|
|
63
|
+
// used to add them again for any origin that merely *contained*
|
|
64
|
+
// "tumbaland.eu" or "localhost", which let https://tumbaland.eu.example.com
|
|
65
|
+
// read error responses with the visitor's cookies.
|
|
69
66
|
if (known) {
|
|
70
67
|
res.status(known.statusCode).json({
|
|
71
68
|
// Spread first so a stray details key can't shadow the envelope fields
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":";;;;;;AACA,+DAAuC;AACvC,mDAAgD;AAUhD;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,KAAY;IACjC,IAAI,KAAK,YAAY,qBAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;IAC5G,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACnE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;IACvE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACI,MAAM,YAAY,GAAwB,CAC/C,KAAY,EACZ,GAAY,EACZ,GAAa,EACb,KAAmB,EACb,EAAE;IACR,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEnC,IAAI,KAAK,EAAE,CAAC;QACV,uEAAuE;QACvE,kEAAkE;QAClE,gBAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;YAC7B,KAAK,EAAE,KAAK,CAAC,OAAO;YACpB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,aAAa,EAAE,GAAG,CAAC,aAAa;SACjC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,gBAAM,CAAC,KAAK,CAAC,kBAAkB,EAAE;YAC/B,KAAK,EAAE,KAAK,CAAC,OAAO;YACpB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;YAChC,aAAa,EAAE,GAAG,CAAC,aAAa;SACjC,CAAC,CAAC;IACL,CAAC;IAED,
|
|
1
|
+
{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":";;;;;;AACA,+DAAuC;AACvC,mDAAgD;AAUhD;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,KAAY;IACjC,IAAI,KAAK,YAAY,qBAAS,EAAE,CAAC;QAC/B,OAAO,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;IAC5G,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACnE,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;IACvE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACI,MAAM,YAAY,GAAwB,CAC/C,KAAY,EACZ,GAAY,EACZ,GAAa,EACb,KAAmB,EACb,EAAE;IACR,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAEnC,IAAI,KAAK,EAAE,CAAC;QACV,uEAAuE;QACvE,kEAAkE;QAClE,gBAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;YAC7B,KAAK,EAAE,KAAK,CAAC,OAAO;YACpB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,aAAa,EAAE,GAAG,CAAC,aAAa;SACjC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,gBAAM,CAAC,KAAK,CAAC,kBAAkB,EAAE;YAC/B,KAAK,EAAE,KAAK,CAAC,OAAO;YACpB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC;YAChC,aAAa,EAAE,GAAG,CAAC,aAAa;SACjC,CAAC,CAAC;IACL,CAAC;IAED,4EAA4E;IAC5E,yEAAyE;IACzE,gEAAgE;IAChE,4EAA4E;IAC5E,mDAAmD;IAEnD,IAAI,KAAK,EAAE,CAAC;QACV,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC;YAChC,uEAAuE;YACvE,kCAAkC;YAClC,GAAG,KAAK,CAAC,OAAO;YAChB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,KAAK,CAAC,IAAI;YACjB,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,+DAA+D;IAC/D,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC;IAC7D,MAAM,YAAY,GAAG,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC;IAE5E,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QACnB,OAAO,EAAE,KAAK;QACd,KAAK,EAAE,uBAAuB;QAC9B,OAAO,EAAE,YAAY;QACrB,GAAG,CAAC,aAAa,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;KAC7C,CAAC,CAAC;AACL,CAAC,CAAC;AA1DW,QAAA,YAAY,gBA0DvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sse/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.closeAllSseStreams = exports.openSseStreamCount = exports.openSseStream = exports.formatSseFrame = exports.SSE_MAX_STREAM_AGE_MS = exports.SSE_HEARTBEAT_MS = void 0;
|
|
4
|
+
var sseStream_1 = require("./sseStream");
|
|
5
|
+
Object.defineProperty(exports, "SSE_HEARTBEAT_MS", { enumerable: true, get: function () { return sseStream_1.SSE_HEARTBEAT_MS; } });
|
|
6
|
+
Object.defineProperty(exports, "SSE_MAX_STREAM_AGE_MS", { enumerable: true, get: function () { return sseStream_1.SSE_MAX_STREAM_AGE_MS; } });
|
|
7
|
+
Object.defineProperty(exports, "formatSseFrame", { enumerable: true, get: function () { return sseStream_1.formatSseFrame; } });
|
|
8
|
+
Object.defineProperty(exports, "openSseStream", { enumerable: true, get: function () { return sseStream_1.openSseStream; } });
|
|
9
|
+
Object.defineProperty(exports, "openSseStreamCount", { enumerable: true, get: function () { return sseStream_1.openSseStreamCount; } });
|
|
10
|
+
Object.defineProperty(exports, "closeAllSseStreams", { enumerable: true, get: function () { return sseStream_1.closeAllSseStreams; } });
|
|
11
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sse/index.ts"],"names":[],"mappings":";;;AAAA,yCAOqB;AANnB,6GAAA,gBAAgB,OAAA;AAChB,kHAAA,qBAAqB,OAAA;AACrB,2GAAA,cAAc,OAAA;AACd,0GAAA,aAAa,OAAA;AACb,+GAAA,kBAAkB,OAAA;AAClB,+GAAA,kBAAkB,OAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Response } from 'express';
|
|
2
|
+
/**
|
|
3
|
+
* The plumbing of a Server-Sent Events response: headers, heartbeat, a bounded
|
|
4
|
+
* lifetime, cleanup when the client goes, and closing everything at shutdown.
|
|
5
|
+
* What goes down the stream, and to whom, stays with the service.
|
|
6
|
+
*
|
|
7
|
+
* Three things here are not optional:
|
|
8
|
+
*
|
|
9
|
+
* - `X-Accel-Buffering: no` plus `flushHeaders()`. Without them nginx holds the
|
|
10
|
+
* response in its buffer and the client sees an open connection that never
|
|
11
|
+
* delivers anything, which looks exactly like a broken server.
|
|
12
|
+
* - The heartbeat. The proxy closes an idle connection at `proxy_read_timeout`,
|
|
13
|
+
* and a client reconnecting every time looks like a reconnect loop.
|
|
14
|
+
* - The lifetime. Access is checked when a stream opens, and a stream carries
|
|
15
|
+
* that answer for as long as it runs. Recycling it bounds how long someone
|
|
16
|
+
* taken out of a group could keep receiving; EventSource reconnects on its
|
|
17
|
+
* own, re-authenticates, and the client sees nothing.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Heartbeat interval. A named event rather than a comment line: comments are
|
|
21
|
+
* invisible to browser JavaScript, and the client's watchdog needs to see it.
|
|
22
|
+
* iOS suspends a backgrounded EventSource without closing it, so a client that
|
|
23
|
+
* waits for an error event waits forever.
|
|
24
|
+
*/
|
|
25
|
+
export declare const SSE_HEARTBEAT_MS = 25000;
|
|
26
|
+
/** How long one stream runs before the server closes it and the client reconnects. */
|
|
27
|
+
export declare const SSE_MAX_STREAM_AGE_MS: number;
|
|
28
|
+
export interface SseStream {
|
|
29
|
+
/** Writes a frame. False once the stream is closed, or if the write failed (which closes it). */
|
|
30
|
+
send(frame: string): boolean;
|
|
31
|
+
/** Ends the response, after one last frame if given. */
|
|
32
|
+
end(lastFrame?: string): void;
|
|
33
|
+
/** Runs once when the stream closes, for whatever reason. At once if it already has. */
|
|
34
|
+
onClose(handler: () => void): void;
|
|
35
|
+
readonly closed: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface OpenSseStreamOptions {
|
|
38
|
+
heartbeatMs?: number;
|
|
39
|
+
maxAgeMs?: number;
|
|
40
|
+
}
|
|
41
|
+
/** Serialises one event in the SSE wire format. */
|
|
42
|
+
export declare function formatSseFrame(event: string, data: unknown, id?: string | number): string;
|
|
43
|
+
/**
|
|
44
|
+
* Turns a response into an event stream. Check access before calling it: once
|
|
45
|
+
* the headers are out, an error can no longer be a status code, only a frame.
|
|
46
|
+
*/
|
|
47
|
+
export declare function openSseStream(res: Response, options?: OpenSseStreamOptions): SseStream;
|
|
48
|
+
/** Open stream count, for `/metrics` and tests. */
|
|
49
|
+
export declare function openSseStreamCount(): number;
|
|
50
|
+
/**
|
|
51
|
+
* Ends every open stream, for shutdown. `server.close()` waits for in-flight
|
|
52
|
+
* responses and an event stream never finishes by itself, so without this a
|
|
53
|
+
* rollout times out and force-exits. The browsers reconnect to the new
|
|
54
|
+
* container on their own. `registerShutdown` calls it.
|
|
55
|
+
*/
|
|
56
|
+
export declare function closeAllSseStreams(): void;
|
|
57
|
+
//# sourceMappingURL=sseStream.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sseStream.d.ts","sourceRoot":"","sources":["../../src/sse/sseStream.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGxC;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAEvC,sFAAsF;AACtF,eAAO,MAAM,qBAAqB,QAAiB,CAAC;AAIpD,MAAM,WAAW,SAAS;IACxB,iGAAiG;IACjG,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B,wDAAwD;IACxD,GAAG,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,wFAAwF;IACxF,OAAO,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,mDAAmD;AACnD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAGzF;AAID;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,GAAE,oBAAyB,GAAG,SAAS,CA+E1F;AAED,mDAAmD;AACnD,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SSE_MAX_STREAM_AGE_MS = exports.SSE_HEARTBEAT_MS = void 0;
|
|
7
|
+
exports.formatSseFrame = formatSseFrame;
|
|
8
|
+
exports.openSseStream = openSseStream;
|
|
9
|
+
exports.openSseStreamCount = openSseStreamCount;
|
|
10
|
+
exports.closeAllSseStreams = closeAllSseStreams;
|
|
11
|
+
const logger_1 = __importDefault(require("../logging/logger"));
|
|
12
|
+
/**
|
|
13
|
+
* The plumbing of a Server-Sent Events response: headers, heartbeat, a bounded
|
|
14
|
+
* lifetime, cleanup when the client goes, and closing everything at shutdown.
|
|
15
|
+
* What goes down the stream, and to whom, stays with the service.
|
|
16
|
+
*
|
|
17
|
+
* Three things here are not optional:
|
|
18
|
+
*
|
|
19
|
+
* - `X-Accel-Buffering: no` plus `flushHeaders()`. Without them nginx holds the
|
|
20
|
+
* response in its buffer and the client sees an open connection that never
|
|
21
|
+
* delivers anything, which looks exactly like a broken server.
|
|
22
|
+
* - The heartbeat. The proxy closes an idle connection at `proxy_read_timeout`,
|
|
23
|
+
* and a client reconnecting every time looks like a reconnect loop.
|
|
24
|
+
* - The lifetime. Access is checked when a stream opens, and a stream carries
|
|
25
|
+
* that answer for as long as it runs. Recycling it bounds how long someone
|
|
26
|
+
* taken out of a group could keep receiving; EventSource reconnects on its
|
|
27
|
+
* own, re-authenticates, and the client sees nothing.
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Heartbeat interval. A named event rather than a comment line: comments are
|
|
31
|
+
* invisible to browser JavaScript, and the client's watchdog needs to see it.
|
|
32
|
+
* iOS suspends a backgrounded EventSource without closing it, so a client that
|
|
33
|
+
* waits for an error event waits forever.
|
|
34
|
+
*/
|
|
35
|
+
exports.SSE_HEARTBEAT_MS = 25_000;
|
|
36
|
+
/** How long one stream runs before the server closes it and the client reconnects. */
|
|
37
|
+
exports.SSE_MAX_STREAM_AGE_MS = 30 * 60 * 1000;
|
|
38
|
+
const HEARTBEAT_FRAME = 'event: heartbeat\ndata: {}\n\n';
|
|
39
|
+
/** Serialises one event in the SSE wire format. */
|
|
40
|
+
function formatSseFrame(event, data, id) {
|
|
41
|
+
const idLine = id === undefined ? '' : `id: ${id}\n`;
|
|
42
|
+
return `${idLine}event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
|
|
43
|
+
}
|
|
44
|
+
const open = new Set();
|
|
45
|
+
/**
|
|
46
|
+
* Turns a response into an event stream. Check access before calling it: once
|
|
47
|
+
* the headers are out, an error can no longer be a status code, only a frame.
|
|
48
|
+
*/
|
|
49
|
+
function openSseStream(res, options = {}) {
|
|
50
|
+
const { heartbeatMs = exports.SSE_HEARTBEAT_MS, maxAgeMs = exports.SSE_MAX_STREAM_AGE_MS } = options;
|
|
51
|
+
res.writeHead(200, {
|
|
52
|
+
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
53
|
+
'Cache-Control': 'no-cache, no-transform',
|
|
54
|
+
Connection: 'keep-alive',
|
|
55
|
+
'X-Accel-Buffering': 'no'
|
|
56
|
+
});
|
|
57
|
+
res.flushHeaders?.();
|
|
58
|
+
let closed = false;
|
|
59
|
+
const handlers = [];
|
|
60
|
+
const runHandler = (handler) => {
|
|
61
|
+
try {
|
|
62
|
+
handler();
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
// One failing cleanup must not skip the others.
|
|
66
|
+
logger_1.default.warn('SSE close handler failed', { error: error.message });
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const close = () => {
|
|
70
|
+
if (closed)
|
|
71
|
+
return;
|
|
72
|
+
closed = true;
|
|
73
|
+
clearInterval(heartbeat);
|
|
74
|
+
clearTimeout(recycle);
|
|
75
|
+
open.delete(stream);
|
|
76
|
+
for (const handler of handlers.splice(0))
|
|
77
|
+
runHandler(handler);
|
|
78
|
+
};
|
|
79
|
+
const send = (frame) => {
|
|
80
|
+
if (closed)
|
|
81
|
+
return false;
|
|
82
|
+
try {
|
|
83
|
+
res.write(frame);
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
// A half-closed socket throws rather than returning an error.
|
|
88
|
+
logger_1.default.warn('Closing SSE stream after a failed write', { error: error.message });
|
|
89
|
+
close();
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const end = (lastFrame) => {
|
|
94
|
+
if (closed)
|
|
95
|
+
return;
|
|
96
|
+
if (lastFrame)
|
|
97
|
+
send(lastFrame);
|
|
98
|
+
try {
|
|
99
|
+
res.end();
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Already gone.
|
|
103
|
+
}
|
|
104
|
+
close();
|
|
105
|
+
};
|
|
106
|
+
const heartbeat = setInterval(() => send(HEARTBEAT_FRAME), heartbeatMs);
|
|
107
|
+
// `retry:` tells the browser how soon to reconnect.
|
|
108
|
+
const recycle = setTimeout(() => end('retry: 1000\n\n'), maxAgeMs);
|
|
109
|
+
const stream = {
|
|
110
|
+
send,
|
|
111
|
+
end,
|
|
112
|
+
onClose(handler) {
|
|
113
|
+
if (closed)
|
|
114
|
+
runHandler(handler);
|
|
115
|
+
else
|
|
116
|
+
handlers.push(handler);
|
|
117
|
+
},
|
|
118
|
+
get closed() {
|
|
119
|
+
return closed;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
open.add(stream);
|
|
123
|
+
// The response, not the request: a GET's request side can report closed as
|
|
124
|
+
// soon as its empty body has been read.
|
|
125
|
+
res.on('close', close);
|
|
126
|
+
res.on('error', close);
|
|
127
|
+
return stream;
|
|
128
|
+
}
|
|
129
|
+
/** Open stream count, for `/metrics` and tests. */
|
|
130
|
+
function openSseStreamCount() {
|
|
131
|
+
return open.size;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Ends every open stream, for shutdown. `server.close()` waits for in-flight
|
|
135
|
+
* responses and an event stream never finishes by itself, so without this a
|
|
136
|
+
* rollout times out and force-exits. The browsers reconnect to the new
|
|
137
|
+
* container on their own. `registerShutdown` calls it.
|
|
138
|
+
*/
|
|
139
|
+
function closeAllSseStreams() {
|
|
140
|
+
for (const stream of [...open])
|
|
141
|
+
stream.end('retry: 2000\n\n');
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=sseStream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sseStream.js","sourceRoot":"","sources":["../../src/sse/sseStream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Makes typed search text match literally inside a Mongo `$regex`.
|
|
3
|
+
*
|
|
4
|
+
* Unescaped, a search for "c++" or ":)" is an invalid pattern and the request
|
|
5
|
+
* fails with a 500, and a crafted one can keep the database busy backtracking.
|
|
6
|
+
*/
|
|
7
|
+
export declare const escapeRegex: (text: string) => string;
|
|
8
|
+
//# sourceMappingURL=escapeRegex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escapeRegex.d.ts","sourceRoot":"","sources":["../../src/utils/escapeRegex.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,WAAW,GAAI,MAAM,MAAM,KAAG,MAAqD,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.escapeRegex = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Makes typed search text match literally inside a Mongo `$regex`.
|
|
6
|
+
*
|
|
7
|
+
* Unescaped, a search for "c++" or ":)" is an invalid pattern and the request
|
|
8
|
+
* fails with a 500, and a crafted one can keep the database busy backtracking.
|
|
9
|
+
*/
|
|
10
|
+
const escapeRegex = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
11
|
+
exports.escapeRegex = escapeRegex;
|
|
12
|
+
//# sourceMappingURL=escapeRegex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"escapeRegex.js","sourceRoot":"","sources":["../../src/utils/escapeRegex.ts"],"names":[],"mappings":";;;AAAA;;;;;GAKG;AACI,MAAM,WAAW,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAApF,QAAA,WAAW,eAAyE"}
|