@fuzdev/fuz_app 0.133.0 → 0.135.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/actions/CLAUDE.md +91 -9
- package/dist/actions/register_action_ws.d.ts +23 -3
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +57 -37
- package/dist/actions/transports.d.ts +23 -0
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +23 -0
- package/dist/actions/transports_ws_backend.d.ts +132 -9
- package/dist/actions/transports_ws_backend.d.ts.map +1 -1
- package/dist/actions/transports_ws_backend.js +236 -33
- package/dist/actions/ws_endpoint_spec.d.ts +9 -0
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
- package/dist/actions/ws_outbound_writer.d.ts +206 -0
- package/dist/actions/ws_outbound_writer.d.ts.map +1 -0
- package/dist/actions/ws_outbound_writer.js +431 -0
- package/dist/auth/CLAUDE.md +2 -2
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +3 -1
- package/dist/auth/cell_actions.js +1 -1
- package/dist/auth/role_schema.d.ts +14 -3
- package/dist/auth/role_schema.d.ts.map +1 -1
- package/dist/auth/role_schema.js +14 -2
- package/dist/db/CLAUDE.md +10 -7
- package/dist/db/create_db.d.ts +8 -6
- package/dist/db/create_db.d.ts.map +1 -1
- package/dist/db/create_db.js +11 -10
- package/dist/db/db.d.ts +9 -7
- package/dist/db/db.d.ts.map +1 -1
- package/dist/db/db.js +5 -4
- package/dist/db/db_pglet.d.ts +43 -0
- package/dist/db/db_pglet.d.ts.map +1 -0
- package/dist/db/db_pglet.js +55 -0
- package/dist/db/migrate.d.ts +1 -1
- package/dist/db/migrate.js +2 -2
- package/dist/http/CLAUDE.md +2 -1
- package/dist/http/body_limit.d.ts +61 -0
- package/dist/http/body_limit.d.ts.map +1 -0
- package/dist/http/body_limit.js +87 -0
- package/dist/http/db_routes.js +2 -2
- package/dist/http/error_schemas.d.ts +2 -0
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +2 -0
- package/dist/realtime/outbound_budget.d.ts +91 -0
- package/dist/realtime/outbound_budget.d.ts.map +1 -0
- package/dist/realtime/outbound_budget.js +108 -0
- package/dist/realtime/sse.d.ts +36 -3
- package/dist/realtime/sse.d.ts.map +1 -1
- package/dist/realtime/sse.js +58 -6
- package/dist/realtime/sse_constants.d.ts +12 -0
- package/dist/realtime/sse_constants.d.ts.map +1 -1
- package/dist/realtime/sse_constants.js +12 -0
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +3 -6
- package/dist/testing/CLAUDE.md +87 -28
- package/dist/testing/admin_integration.js +1 -1
- package/dist/testing/app_server.d.ts +5 -5
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +21 -20
- package/dist/testing/audit_completeness.d.ts +1 -1
- package/dist/testing/audit_completeness.js +1 -1
- package/dist/testing/audit_drift_guard.d.ts +1 -1
- package/dist/testing/audit_drift_guard.js +1 -1
- package/dist/testing/cross_backend/body_size_smuggling.d.ts +9 -9
- package/dist/testing/cross_backend/body_size_smuggling.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size_smuggling.js +110 -60
- package/dist/testing/cross_backend/capabilities.d.ts +18 -15
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +1 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts +5 -5
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +6 -6
- package/dist/testing/cross_backend/in_process_setup.d.ts +2 -2
- package/dist/testing/cross_backend/in_process_setup.js +3 -3
- package/dist/testing/cross_backend/role_grant_role_grammar.d.ts +6 -0
- package/dist/testing/cross_backend/role_grant_role_grammar.d.ts.map +1 -0
- package/dist/testing/cross_backend/role_grant_role_grammar.js +117 -0
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +2 -3
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +2 -3
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +1 -1
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +8 -4
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +84 -4
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +102 -3
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +2 -2
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +9 -7
- package/dist/testing/cross_backend/ws_outbound_overflow.d.ts +29 -0
- package/dist/testing/cross_backend/ws_outbound_overflow.d.ts.map +1 -0
- package/dist/testing/cross_backend/ws_outbound_overflow.js +211 -0
- package/dist/testing/db.d.ts +49 -60
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +104 -101
- package/dist/testing/integration.js +1 -1
- package/dist/testing/rate_limiting.d.ts +2 -1
- package/dist/testing/rate_limiting.d.ts.map +1 -1
- package/dist/testing/rate_limiting.js +2 -2
- package/dist/testing/schema_introspect.d.ts +10 -2
- package/dist/testing/schema_introspect.d.ts.map +1 -1
- package/dist/testing/schema_introspect.js +15 -7
- package/dist/testing/sse_round_trip.d.ts +1 -1
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +2 -2
- package/dist/testing/stubs.js +1 -1
- package/dist/testing/transports/ws_transport.d.ts +4 -2
- package/dist/testing/transports/ws_transport.d.ts.map +1 -1
- package/dist/testing/transports/ws_transport.js +2 -1
- package/dist/testing/ws_round_trip.d.ts +32 -2
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +26 -4
- package/package.json +6 -1
|
@@ -53,6 +53,8 @@ export declare const ERROR_RATE_LIMIT_EXCEEDED: "rate_limit_exceeded";
|
|
|
53
53
|
export declare const ERROR_INVALID_CREDENTIALS: "invalid_credentials";
|
|
54
54
|
/** Request body exceeds the maximum allowed size. */
|
|
55
55
|
export declare const ERROR_PAYLOAD_TOO_LARGE: "payload_too_large";
|
|
56
|
+
/** Request `Content-Length` header is not a plain run of ASCII digits. */
|
|
57
|
+
export declare const ERROR_INVALID_CONTENT_LENGTH: "invalid_content_length";
|
|
56
58
|
/** Request origin not in allowlist. */
|
|
57
59
|
export declare const ERROR_FORBIDDEN_ORIGIN: "forbidden_origin";
|
|
58
60
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error_schemas.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/error_schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI9D,0CAA0C;AAC1C,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAE1E,uDAAuD;AACvD,eAAO,MAAM,uBAAuB,EAAG,mBAA4B,CAAC;AAEpE,6CAA6C;AAC7C,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAE1E,8CAA8C;AAC9C,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAI1E,wCAAwC;AACxC,eAAO,MAAM,6BAA6B,EAAG,yBAAkC,CAAC;AAEhF,+CAA+C;AAC/C,eAAO,MAAM,8BAA8B,EAAG,0BAAmC,CAAC;AAElF;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,EAAG,0BAAmC,CAAC;AAElF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,0BAA0B,EAAG,sBAA+B,CAAC;AAE1E,yCAAyC;AACzC,eAAO,MAAM,yBAAyB,EAAG,qBAA8B,CAAC;AAExE,sFAAsF;AACtF,eAAO,MAAM,yBAAyB,EAAG,qBAA8B,CAAC;AAExE,qDAAqD;AACrD,eAAO,MAAM,uBAAuB,EAAG,mBAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"error_schemas.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/http/error_schemas.ts"],"names":[],"mappings":"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"}
|
|
@@ -55,6 +55,8 @@ export const ERROR_RATE_LIMIT_EXCEEDED = 'rate_limit_exceeded';
|
|
|
55
55
|
export const ERROR_INVALID_CREDENTIALS = 'invalid_credentials';
|
|
56
56
|
/** Request body exceeds the maximum allowed size. */
|
|
57
57
|
export const ERROR_PAYLOAD_TOO_LARGE = 'payload_too_large';
|
|
58
|
+
/** Request `Content-Length` header is not a plain run of ASCII digits. */
|
|
59
|
+
export const ERROR_INVALID_CONTENT_LENGTH = 'invalid_content_length';
|
|
58
60
|
// --- Origin & bearer token verification ---
|
|
59
61
|
/** Request origin not in allowlist. */
|
|
60
62
|
export const ERROR_FORBIDDEN_ORIGIN = 'forbidden_origin';
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The byte budget of the frames waiting to be written to one connection — a
|
|
3
|
+
* WebSocket or an SSE stream.
|
|
4
|
+
*
|
|
5
|
+
* A producer never waits on a recipient: a broadcast is synchronous and called
|
|
6
|
+
* from anywhere, so a client that stopped reading makes the server hold what it
|
|
7
|
+
* has not taken. `OutboundBudget` is the bound on that, and it is accounting
|
|
8
|
+
* only — its owner holds the frames and decides what "waiting" means for its
|
|
9
|
+
* connection (`WsOutboundWriter` for a WebSocket, `create_sse_response` for an
|
|
10
|
+
* SSE stream).
|
|
11
|
+
*
|
|
12
|
+
* - **The charge.** Each waiting frame is charged its payload bytes — the
|
|
13
|
+
* UTF-8 length of its text — plus `OUTBOUND_FRAME_OVERHEAD_BYTES`.
|
|
14
|
+
* - **The rule.** A frame is admitted when the charges already waiting plus
|
|
15
|
+
* its own stay within the budget — and, whatever its size, when nothing is
|
|
16
|
+
* waiting, so a single message larger than the whole budget is deliverable.
|
|
17
|
+
*
|
|
18
|
+
* Nothing counts frames: any number may wait while their charges fit. The twin
|
|
19
|
+
* of the Rust spine's `fuz_realtime` outbound queue, with the same charge and
|
|
20
|
+
* the same rule.
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* What a waiting frame is charged against its budget on top of its payload
|
|
26
|
+
* bytes.
|
|
27
|
+
*
|
|
28
|
+
* It stands for what a queued frame costs beyond its text, so a frame with
|
|
29
|
+
* little or no payload is not free: a budget of `B` never holds more than `B`
|
|
30
|
+
* divided by this many frames, whatever their size. A fixed charge, not a
|
|
31
|
+
* measurement of memory use. Same value as the Rust spine's constant.
|
|
32
|
+
*/
|
|
33
|
+
export declare const OUTBOUND_FRAME_OVERHEAD_BYTES = 64;
|
|
34
|
+
/** What was waiting when a frame did not fit its connection's budget. */
|
|
35
|
+
export interface OutboundOverflow {
|
|
36
|
+
/** Payload bytes waiting, without the per-frame overhead the budget also charges. */
|
|
37
|
+
queued_bytes: number;
|
|
38
|
+
/** Frames waiting. */
|
|
39
|
+
queued_frames: number;
|
|
40
|
+
/** Payload bytes of the frame that did not fit, without its overhead. */
|
|
41
|
+
frame_bytes: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The accounting for one connection's waiting frames (module doc).
|
|
45
|
+
*
|
|
46
|
+
* `admit` charges a frame that fits, `release` frees the charge of one that is
|
|
47
|
+
* no longer waiting, and `clear` frees them all.
|
|
48
|
+
*/
|
|
49
|
+
export declare class OutboundBudget {
|
|
50
|
+
#private;
|
|
51
|
+
/** The budget, in bytes. */
|
|
52
|
+
readonly max_bytes: number;
|
|
53
|
+
/**
|
|
54
|
+
* @param max_bytes - the budget, in bytes; `0` admits one frame at a time
|
|
55
|
+
* @throws Error when `max_bytes` is not a non-negative safe integer
|
|
56
|
+
*/
|
|
57
|
+
constructor(max_bytes: number);
|
|
58
|
+
/** Payload bytes waiting, without the per-frame overhead. */
|
|
59
|
+
get queued_bytes(): number;
|
|
60
|
+
/** Frames waiting. */
|
|
61
|
+
get queued_frames(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Charge a frame of `frame_bytes` payload bytes if it fits: when nothing is
|
|
64
|
+
* waiting, or when the charges waiting plus its own stay within the budget.
|
|
65
|
+
*
|
|
66
|
+
* @param frame_bytes - the frame's payload length in UTF-8 bytes
|
|
67
|
+
* @returns whether the frame was admitted and charged
|
|
68
|
+
* @mutates this - adds the frame's charge when it is admitted
|
|
69
|
+
*/
|
|
70
|
+
admit(frame_bytes: number): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Free the charge of one admitted frame that is no longer waiting.
|
|
73
|
+
*
|
|
74
|
+
* @param frame_bytes - the payload length the frame was admitted with
|
|
75
|
+
* @mutates this - removes the frame's charge
|
|
76
|
+
*/
|
|
77
|
+
release(frame_bytes: number): void;
|
|
78
|
+
/**
|
|
79
|
+
* Free every charge — the owner discarded what was waiting.
|
|
80
|
+
*
|
|
81
|
+
* @mutates this - removes every charge
|
|
82
|
+
*/
|
|
83
|
+
clear(): void;
|
|
84
|
+
/**
|
|
85
|
+
* What is waiting, for the log line of a frame that did not fit.
|
|
86
|
+
*
|
|
87
|
+
* @param frame_bytes - the payload length of the frame that did not fit
|
|
88
|
+
*/
|
|
89
|
+
to_overflow(frame_bytes: number): OutboundOverflow;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=outbound_budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outbound_budget.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/outbound_budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,6BAA6B,KAAK,CAAC;AAEhD,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAChC,qFAAqF;IACrF,YAAY,EAAE,MAAM,CAAC;IACrB,sBAAsB;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,qBAAa,cAAc;;IAC1B,4BAA4B;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAK3B;;;OAGG;gBACS,SAAS,EAAE,MAAM;IAO7B,6DAA6D;IAC7D,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,sBAAsB;IACtB,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAanC;;;;;OAKG;IACH,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKlC;;;;OAIG;IACH,KAAK,IAAI,IAAI;IAKb;;;;OAIG;IACH,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB;CAGlD"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The byte budget of the frames waiting to be written to one connection — a
|
|
3
|
+
* WebSocket or an SSE stream.
|
|
4
|
+
*
|
|
5
|
+
* A producer never waits on a recipient: a broadcast is synchronous and called
|
|
6
|
+
* from anywhere, so a client that stopped reading makes the server hold what it
|
|
7
|
+
* has not taken. `OutboundBudget` is the bound on that, and it is accounting
|
|
8
|
+
* only — its owner holds the frames and decides what "waiting" means for its
|
|
9
|
+
* connection (`WsOutboundWriter` for a WebSocket, `create_sse_response` for an
|
|
10
|
+
* SSE stream).
|
|
11
|
+
*
|
|
12
|
+
* - **The charge.** Each waiting frame is charged its payload bytes — the
|
|
13
|
+
* UTF-8 length of its text — plus `OUTBOUND_FRAME_OVERHEAD_BYTES`.
|
|
14
|
+
* - **The rule.** A frame is admitted when the charges already waiting plus
|
|
15
|
+
* its own stay within the budget — and, whatever its size, when nothing is
|
|
16
|
+
* waiting, so a single message larger than the whole budget is deliverable.
|
|
17
|
+
*
|
|
18
|
+
* Nothing counts frames: any number may wait while their charges fit. The twin
|
|
19
|
+
* of the Rust spine's `fuz_realtime` outbound queue, with the same charge and
|
|
20
|
+
* the same rule.
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* What a waiting frame is charged against its budget on top of its payload
|
|
26
|
+
* bytes.
|
|
27
|
+
*
|
|
28
|
+
* It stands for what a queued frame costs beyond its text, so a frame with
|
|
29
|
+
* little or no payload is not free: a budget of `B` never holds more than `B`
|
|
30
|
+
* divided by this many frames, whatever their size. A fixed charge, not a
|
|
31
|
+
* measurement of memory use. Same value as the Rust spine's constant.
|
|
32
|
+
*/
|
|
33
|
+
export const OUTBOUND_FRAME_OVERHEAD_BYTES = 64;
|
|
34
|
+
/**
|
|
35
|
+
* The accounting for one connection's waiting frames (module doc).
|
|
36
|
+
*
|
|
37
|
+
* `admit` charges a frame that fits, `release` frees the charge of one that is
|
|
38
|
+
* no longer waiting, and `clear` frees them all.
|
|
39
|
+
*/
|
|
40
|
+
export class OutboundBudget {
|
|
41
|
+
/** The budget, in bytes. */
|
|
42
|
+
max_bytes;
|
|
43
|
+
#bytes = 0;
|
|
44
|
+
#frames = 0;
|
|
45
|
+
/**
|
|
46
|
+
* @param max_bytes - the budget, in bytes; `0` admits one frame at a time
|
|
47
|
+
* @throws Error when `max_bytes` is not a non-negative safe integer
|
|
48
|
+
*/
|
|
49
|
+
constructor(max_bytes) {
|
|
50
|
+
if (!Number.isSafeInteger(max_bytes) || max_bytes < 0) {
|
|
51
|
+
throw new Error(`OutboundBudget: max_bytes must be a non-negative integer, got ${max_bytes}`);
|
|
52
|
+
}
|
|
53
|
+
this.max_bytes = max_bytes;
|
|
54
|
+
}
|
|
55
|
+
/** Payload bytes waiting, without the per-frame overhead. */
|
|
56
|
+
get queued_bytes() {
|
|
57
|
+
return this.#bytes;
|
|
58
|
+
}
|
|
59
|
+
/** Frames waiting. */
|
|
60
|
+
get queued_frames() {
|
|
61
|
+
return this.#frames;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Charge a frame of `frame_bytes` payload bytes if it fits: when nothing is
|
|
65
|
+
* waiting, or when the charges waiting plus its own stay within the budget.
|
|
66
|
+
*
|
|
67
|
+
* @param frame_bytes - the frame's payload length in UTF-8 bytes
|
|
68
|
+
* @returns whether the frame was admitted and charged
|
|
69
|
+
* @mutates this - adds the frame's charge when it is admitted
|
|
70
|
+
*/
|
|
71
|
+
admit(frame_bytes) {
|
|
72
|
+
if (this.#frames !== 0 &&
|
|
73
|
+
this.#bytes + frame_bytes + (this.#frames + 1) * OUTBOUND_FRAME_OVERHEAD_BYTES >
|
|
74
|
+
this.max_bytes) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
this.#bytes += frame_bytes;
|
|
78
|
+
this.#frames++;
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Free the charge of one admitted frame that is no longer waiting.
|
|
83
|
+
*
|
|
84
|
+
* @param frame_bytes - the payload length the frame was admitted with
|
|
85
|
+
* @mutates this - removes the frame's charge
|
|
86
|
+
*/
|
|
87
|
+
release(frame_bytes) {
|
|
88
|
+
this.#bytes -= frame_bytes;
|
|
89
|
+
this.#frames--;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Free every charge — the owner discarded what was waiting.
|
|
93
|
+
*
|
|
94
|
+
* @mutates this - removes every charge
|
|
95
|
+
*/
|
|
96
|
+
clear() {
|
|
97
|
+
this.#bytes = 0;
|
|
98
|
+
this.#frames = 0;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* What is waiting, for the log line of a frame that did not fit.
|
|
102
|
+
*
|
|
103
|
+
* @param frame_bytes - the payload length of the frame that did not fit
|
|
104
|
+
*/
|
|
105
|
+
to_overflow(frame_bytes) {
|
|
106
|
+
return { queued_bytes: this.#bytes, queued_frames: this.#frames, frame_bytes };
|
|
107
|
+
}
|
|
108
|
+
}
|
package/dist/realtime/sse.d.ts
CHANGED
|
@@ -4,6 +4,22 @@
|
|
|
4
4
|
* Provides generic helpers for creating SSE response streams
|
|
5
5
|
* and a notification type aligned with JSON-RPC 2.0.
|
|
6
6
|
*
|
|
7
|
+
* ## Outbound bound
|
|
8
|
+
*
|
|
9
|
+
* `SseStream.send` never waits on its client: each frame is written to the
|
|
10
|
+
* response stream, which holds it until the client reads. What a stream holds
|
|
11
|
+
* unread is kept within `SSE_OUTBOUND_QUEUE_BYTES` — each frame charged its
|
|
12
|
+
* bytes as written (`data: ` prefix and terminator included) plus
|
|
13
|
+
* `OUTBOUND_FRAME_OVERHEAD_BYTES`, and a frame always accepted when nothing is
|
|
14
|
+
* unread, so a single frame larger than the budget is still deliverable. A
|
|
15
|
+
* frame that does not fit **ends the stream**: it is not written, what was
|
|
16
|
+
* unread is dropped, the body ends with no final frame, and the stream's
|
|
17
|
+
* `on_close` listeners run. One `warn` line names the stream's account (when
|
|
18
|
+
* the caller passed it) and what was unread; no audit row is written, since each would itself be fanned to every
|
|
19
|
+
* stream. A client that is reading reconnects and misses the frames in
|
|
20
|
+
* between. The twin of the Rust spine's `fuz_realtime` SSE bound — the same
|
|
21
|
+
* budget and charge.
|
|
22
|
+
*
|
|
7
23
|
* @module
|
|
8
24
|
*/
|
|
9
25
|
import type { Context } from 'hono';
|
|
@@ -15,7 +31,11 @@ import type { Logger } from '@fuzdev/fuz_util/log.ts';
|
|
|
15
31
|
* Transport-agnostic — works with any serializable type.
|
|
16
32
|
*/
|
|
17
33
|
export interface SseStream<T = unknown> {
|
|
18
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Send data to the client as a JSON SSE event. Never waits: a frame the
|
|
36
|
+
* stream's outbound budget does not fit ends the stream instead (module doc,
|
|
37
|
+
* "Outbound bound").
|
|
38
|
+
*/
|
|
19
39
|
send: (data: T) => void;
|
|
20
40
|
/** Send a comment (for keep-alive pings). */
|
|
21
41
|
comment: (text: string) => void;
|
|
@@ -40,6 +60,14 @@ export interface SseNotification {
|
|
|
40
60
|
/** Method-specific payload. */
|
|
41
61
|
params: unknown;
|
|
42
62
|
}
|
|
63
|
+
/** Options for `create_sse_response`. */
|
|
64
|
+
export interface SseResponseOptions {
|
|
65
|
+
/**
|
|
66
|
+
* The account the stream belongs to, named in the warning logged when the
|
|
67
|
+
* stream ends on its outbound budget.
|
|
68
|
+
*/
|
|
69
|
+
account_id?: string | null;
|
|
70
|
+
}
|
|
43
71
|
/**
|
|
44
72
|
* Create an SSE response for a Hono context.
|
|
45
73
|
*
|
|
@@ -59,10 +87,15 @@ export interface SseNotification {
|
|
|
59
87
|
* Uses `hono_stream.write()` directly (not `writeSSE`) to avoid
|
|
60
88
|
* Hono's HTML callback resolution — keeps the same `data: JSON\n\n` format.
|
|
61
89
|
*
|
|
62
|
-
*
|
|
90
|
+
* What the stream holds unread is bounded by `SSE_OUTBOUND_QUEUE_BYTES`; a
|
|
91
|
+
* frame past it ends the stream (module doc, "Outbound bound").
|
|
92
|
+
*
|
|
93
|
+
* @param log - logger for serialization and `on_close` listener errors, and
|
|
94
|
+
* for the warning when the stream ends on its outbound budget
|
|
95
|
+
* @param options - what the overflow warning names
|
|
63
96
|
* @returns object with the streaming `Response` and an `SseStream` controller
|
|
64
97
|
*/
|
|
65
|
-
export declare const create_sse_response: <T = unknown>(c: Context, log: Logger) => {
|
|
98
|
+
export declare const create_sse_response: <T = unknown>(c: Context, log: Logger, options?: SseResponseOptions) => {
|
|
66
99
|
response: Response;
|
|
67
100
|
stream: SseStream<T>;
|
|
68
101
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/sse.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAMtD;;;;GAIG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC,GAAG,OAAO;IACrC;;;;OAIG;IACH,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IACxB,6CAA6C;IAC7C,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,wBAAwB;IACxB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB;;;;;OAKG;IACH,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;CACnC;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC/B,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,yCAAyC;AACzC,MAAM,WAAW,kBAAkB;IAClC;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AA8DD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,GAAG,OAAO,EAC9C,GAAG,OAAO,EACV,KAAK,MAAM,EACX,UAAU,kBAAkB,KAC1B;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAA;CAyF5C,CAAC;AAEF,gFAAgF;AAChF,MAAM,WAAW,SAAS;IACzB,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC;IAClB,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GAAI,CAAC,SAAS,eAAe,EACrE,aAAa;IAAE,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,IAAI,CAAA;CAAE,EAC9D,aAAa,KAAK,CAAC,SAAS,CAAC,EAC7B,KAAK,MAAM,KACT;IAAE,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,IAAI,CAAA;CAmBjD,CAAC"}
|
package/dist/realtime/sse.js
CHANGED
|
@@ -4,12 +4,30 @@
|
|
|
4
4
|
* Provides generic helpers for creating SSE response streams
|
|
5
5
|
* and a notification type aligned with JSON-RPC 2.0.
|
|
6
6
|
*
|
|
7
|
+
* ## Outbound bound
|
|
8
|
+
*
|
|
9
|
+
* `SseStream.send` never waits on its client: each frame is written to the
|
|
10
|
+
* response stream, which holds it until the client reads. What a stream holds
|
|
11
|
+
* unread is kept within `SSE_OUTBOUND_QUEUE_BYTES` — each frame charged its
|
|
12
|
+
* bytes as written (`data: ` prefix and terminator included) plus
|
|
13
|
+
* `OUTBOUND_FRAME_OVERHEAD_BYTES`, and a frame always accepted when nothing is
|
|
14
|
+
* unread, so a single frame larger than the budget is still deliverable. A
|
|
15
|
+
* frame that does not fit **ends the stream**: it is not written, what was
|
|
16
|
+
* unread is dropped, the body ends with no final frame, and the stream's
|
|
17
|
+
* `on_close` listeners run. One `warn` line names the stream's account (when
|
|
18
|
+
* the caller passed it) and what was unread; no audit row is written, since each would itself be fanned to every
|
|
19
|
+
* stream. A client that is reading reconnects and misses the frames in
|
|
20
|
+
* between. The twin of the Rust spine's `fuz_realtime` SSE bound — the same
|
|
21
|
+
* budget and charge.
|
|
22
|
+
*
|
|
7
23
|
* @module
|
|
8
24
|
*/
|
|
9
25
|
import { streamSSE } from 'hono/streaming';
|
|
10
26
|
import { z } from 'zod';
|
|
11
27
|
import { DEV } from 'esm-env';
|
|
12
|
-
import {
|
|
28
|
+
import { utf8_length } from '../actions/transports.js';
|
|
29
|
+
import { OutboundBudget } from './outbound_budget.js';
|
|
30
|
+
import { SSE_CONNECTED_COMMENT, SSE_OUTBOUND_QUEUE_BYTES } from './sse_constants.js';
|
|
13
31
|
const is_node_connection_stream = (value) => {
|
|
14
32
|
if (typeof value !== 'object' || value === null)
|
|
15
33
|
return false;
|
|
@@ -85,10 +103,15 @@ const watch_client_disconnect = (c, on_gone) => {
|
|
|
85
103
|
* Uses `hono_stream.write()` directly (not `writeSSE`) to avoid
|
|
86
104
|
* Hono's HTML callback resolution — keeps the same `data: JSON\n\n` format.
|
|
87
105
|
*
|
|
88
|
-
*
|
|
106
|
+
* What the stream holds unread is bounded by `SSE_OUTBOUND_QUEUE_BYTES`; a
|
|
107
|
+
* frame past it ends the stream (module doc, "Outbound bound").
|
|
108
|
+
*
|
|
109
|
+
* @param log - logger for serialization and `on_close` listener errors, and
|
|
110
|
+
* for the warning when the stream ends on its outbound budget
|
|
111
|
+
* @param options - what the overflow warning names
|
|
89
112
|
* @returns object with the streaming `Response` and an `SseStream` controller
|
|
90
113
|
*/
|
|
91
|
-
export const create_sse_response = (c, log) => {
|
|
114
|
+
export const create_sse_response = (c, log, options) => {
|
|
92
115
|
const { promise, resolve } = Promise.withResolvers();
|
|
93
116
|
const close_listeners = [];
|
|
94
117
|
let resolved = false;
|
|
@@ -107,24 +130,53 @@ export const create_sse_response = (c, log) => {
|
|
|
107
130
|
}
|
|
108
131
|
};
|
|
109
132
|
let sse_stream;
|
|
133
|
+
// charges the frames written to the stream and not yet read from it
|
|
134
|
+
const budget = new OutboundBudget(SSE_OUTBOUND_QUEUE_BYTES);
|
|
110
135
|
const response = streamSSE(c, async (hono_stream) => {
|
|
136
|
+
// Write one frame within the budget, or end the stream. The write
|
|
137
|
+
// settles once the response body has taken the frame, which frees its
|
|
138
|
+
// charge.
|
|
139
|
+
const write = (frame) => {
|
|
140
|
+
const bytes = utf8_length(frame);
|
|
141
|
+
if (!budget.admit(bytes)) {
|
|
142
|
+
// same line as the Rust spine's registry, so one grep finds either
|
|
143
|
+
log.warn('sse: outbound queue overflow, ending the stream', {
|
|
144
|
+
account_id: options?.account_id ?? null,
|
|
145
|
+
...budget.to_overflow(bytes)
|
|
146
|
+
});
|
|
147
|
+
budget.clear();
|
|
148
|
+
// drops what the stream still holds and ends the body; `do_close`
|
|
149
|
+
// runs as its abort listener
|
|
150
|
+
hono_stream.abort();
|
|
151
|
+
do_close();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
void hono_stream.write(frame).then(() => {
|
|
155
|
+
// a stream that ended has had its charges cleared
|
|
156
|
+
if (!resolved && !hono_stream.aborted)
|
|
157
|
+
budget.release(bytes);
|
|
158
|
+
});
|
|
159
|
+
};
|
|
111
160
|
sse_stream = {
|
|
112
161
|
send(data) {
|
|
113
162
|
if (resolved || hono_stream.aborted)
|
|
114
163
|
return;
|
|
164
|
+
let frame;
|
|
115
165
|
try {
|
|
116
166
|
// JSON.stringify (no space arg) never produces literal newlines,
|
|
117
167
|
// so single-line `data:` framing is safe per the SSE spec.
|
|
118
|
-
|
|
168
|
+
frame = `data: ${JSON.stringify(data)}\n\n`;
|
|
119
169
|
}
|
|
120
170
|
catch (e) {
|
|
121
171
|
log.error('send failed to serialize data:', e);
|
|
172
|
+
return;
|
|
122
173
|
}
|
|
174
|
+
write(frame);
|
|
123
175
|
},
|
|
124
176
|
comment(text) {
|
|
125
177
|
if (resolved || hono_stream.aborted)
|
|
126
178
|
return;
|
|
127
|
-
|
|
179
|
+
write(`: ${text}\n`);
|
|
128
180
|
},
|
|
129
181
|
close: do_close,
|
|
130
182
|
on_close(fn) {
|
|
@@ -143,7 +195,7 @@ export const create_sse_response = (c, log) => {
|
|
|
143
195
|
hono_stream.onAbort(do_close);
|
|
144
196
|
// flush an SSE comment to push headers through proxies (Vite, nginx),
|
|
145
197
|
// ensuring EventSource fires onopen without waiting for the first data event
|
|
146
|
-
|
|
198
|
+
write(SSE_CONNECTED_COMMENT);
|
|
147
199
|
await promise;
|
|
148
200
|
});
|
|
149
201
|
// after the callback above has built `sse_stream`, which runs synchronously
|
|
@@ -14,4 +14,16 @@
|
|
|
14
14
|
* SSE tests assert the stream emits this on connect.
|
|
15
15
|
*/
|
|
16
16
|
export declare const SSE_CONNECTED_COMMENT = ": connected\n\n";
|
|
17
|
+
/**
|
|
18
|
+
* The byte budget of what one SSE stream holds for its client to read
|
|
19
|
+
* (`realtime/sse.ts` module doc, "Outbound bound").
|
|
20
|
+
*
|
|
21
|
+
* 1 MiB — equal to the default cap on one request body
|
|
22
|
+
* (`DEFAULT_MAX_BODY_SIZE`). An audit row is bounded by the request that
|
|
23
|
+
* produced it, so on a server that keeps that default the budget holds a burst
|
|
24
|
+
* of ordinary rows or one at the largest. The value is fixed and does not
|
|
25
|
+
* follow the server's body limit. Same value as the Rust spine's
|
|
26
|
+
* `SSE_OUTBOUND_QUEUE_BYTES`.
|
|
27
|
+
*/
|
|
28
|
+
export declare const SSE_OUTBOUND_QUEUE_BYTES: number;
|
|
17
29
|
//# sourceMappingURL=sse_constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse_constants.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/sse_constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"sse_constants.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/realtime/sse_constants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,oBAAoB,CAAC;AAEvD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,QAAc,CAAC"}
|
|
@@ -14,3 +14,15 @@
|
|
|
14
14
|
* SSE tests assert the stream emits this on connect.
|
|
15
15
|
*/
|
|
16
16
|
export const SSE_CONNECTED_COMMENT = `: connected\n\n`;
|
|
17
|
+
/**
|
|
18
|
+
* The byte budget of what one SSE stream holds for its client to read
|
|
19
|
+
* (`realtime/sse.ts` module doc, "Outbound bound").
|
|
20
|
+
*
|
|
21
|
+
* 1 MiB — equal to the default cap on one request body
|
|
22
|
+
* (`DEFAULT_MAX_BODY_SIZE`). An audit row is bounded by the request that
|
|
23
|
+
* produced it, so on a server that keeps that default the budget holds a burst
|
|
24
|
+
* of ordinary rows or one at the largest. The value is fixed and does not
|
|
25
|
+
* follow the server's body limit. Same value as the Rust spine's
|
|
26
|
+
* `SSE_OUTBOUND_QUEUE_BYTES`.
|
|
27
|
+
*/
|
|
28
|
+
export const SSE_OUTBOUND_QUEUE_BYTES = 1024 * 1024;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/server/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { Hono } from 'hono';
|
|
11
11
|
import { logger } from 'hono/logger';
|
|
12
|
-
import { bodyLimit } from 'hono/body-limit';
|
|
13
12
|
import { z } from 'zod';
|
|
14
13
|
import { session_cookie_options } from '../auth/session_cookie.js';
|
|
15
14
|
import { create_audit_log_sse, create_sse_connection_closer, audit_log_event_specs } from '../realtime/sse_auth_guard.js';
|
|
@@ -29,7 +28,7 @@ import { flush_pending_effects, flush_post_commit_effects } from '../http/pendin
|
|
|
29
28
|
import { assert_endpoint_in_auth_scope, assert_middleware_stack_mounted, create_auth_middleware_specs } from '../auth/middleware.js';
|
|
30
29
|
import { fuz_auth_guard_resolver } from '../auth/auth_guard_resolver.js';
|
|
31
30
|
import { create_fuz_authorization_handler } from '../auth/request_context.js';
|
|
32
|
-
import {
|
|
31
|
+
import { create_body_limit_middleware } from '../http/body_limit.js';
|
|
33
32
|
import { create_rpc_endpoint } from '../actions/action_rpc.js';
|
|
34
33
|
import { register_ws_endpoint } from '../actions/register_ws_endpoint.js';
|
|
35
34
|
import { resolve_ws_endpoints } from '../actions/ws_endpoint_spec.js';
|
|
@@ -483,10 +482,7 @@ const assemble_app_server = async (options, releases) => {
|
|
|
483
482
|
// Default 1 MiB; pass null to disable.
|
|
484
483
|
if (options.max_body_size !== null) {
|
|
485
484
|
const max_size = options.max_body_size ?? DEFAULT_MAX_BODY_SIZE;
|
|
486
|
-
app.use(
|
|
487
|
-
maxSize: max_size,
|
|
488
|
-
onError: (c) => c.json({ error: ERROR_PAYLOAD_TOO_LARGE }, 413)
|
|
489
|
-
}));
|
|
485
|
+
app.use(create_body_limit_middleware(max_size));
|
|
490
486
|
}
|
|
491
487
|
apply_middleware_specs(app, middleware_specs);
|
|
492
488
|
const authorize = create_fuz_authorization_handler({ db: deps.db });
|
|
@@ -524,6 +520,7 @@ const assemble_app_server = async (options, releases) => {
|
|
|
524
520
|
// added below instead, so the remover is the server's
|
|
525
521
|
connection_closer: null,
|
|
526
522
|
max_connections_per_account: endpoint.max_connections_per_account,
|
|
523
|
+
outbound_queue_bytes: endpoint.outbound_queue_bytes,
|
|
527
524
|
heartbeat: endpoint.heartbeat,
|
|
528
525
|
artificial_delay: endpoint.artificial_delay,
|
|
529
526
|
max_message_bytes: endpoint.max_message_bytes,
|