@brass-build/client 0.1.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/AGENTS.md +255 -0
- package/CHANGELOG.md +15 -0
- package/LICENSE +21 -0
- package/README.md +836 -0
- package/dist/api-fetch.d.ts +16 -0
- package/dist/api-fetch.d.ts.map +1 -0
- package/dist/api-fetch.js +97 -0
- package/dist/api-fetch.js.map +1 -0
- package/dist/api-headers.d.ts +3 -0
- package/dist/api-headers.d.ts.map +1 -0
- package/dist/api-headers.js +25 -0
- package/dist/api-headers.js.map +1 -0
- package/dist/auth-flow.d.ts +23 -0
- package/dist/auth-flow.d.ts.map +1 -0
- package/dist/auth-flow.js +76 -0
- package/dist/auth-flow.js.map +1 -0
- package/dist/client.d.ts +106 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +1278 -0
- package/dist/client.js.map +1 -0
- package/dist/doc-url-sync.d.ts +19 -0
- package/dist/doc-url-sync.d.ts.map +1 -0
- package/dist/doc-url-sync.js +181 -0
- package/dist/doc-url-sync.js.map +1 -0
- package/dist/document-branches.d.ts +32 -0
- package/dist/document-branches.d.ts.map +1 -0
- package/dist/document-branches.js +89 -0
- package/dist/document-branches.js.map +1 -0
- package/dist/document-checkpoints.d.ts +24 -0
- package/dist/document-checkpoints.d.ts.map +1 -0
- package/dist/document-checkpoints.js +115 -0
- package/dist/document-checkpoints.js.map +1 -0
- package/dist/document-commands.d.ts +93 -0
- package/dist/document-commands.d.ts.map +1 -0
- package/dist/document-commands.js +423 -0
- package/dist/document-commands.js.map +1 -0
- package/dist/document-signals.d.ts +53 -0
- package/dist/document-signals.d.ts.map +1 -0
- package/dist/document-signals.js +114 -0
- package/dist/document-signals.js.map +1 -0
- package/dist/document-streams.d.ts +22 -0
- package/dist/document-streams.d.ts.map +1 -0
- package/dist/document-streams.js +98 -0
- package/dist/document-streams.js.map +1 -0
- package/dist/document-thumbnail-capture.d.ts +27 -0
- package/dist/document-thumbnail-capture.d.ts.map +1 -0
- package/dist/document-thumbnail-capture.js +160 -0
- package/dist/document-thumbnail-capture.js.map +1 -0
- package/dist/documents-namespace.d.ts +134 -0
- package/dist/documents-namespace.d.ts.map +1 -0
- package/dist/documents-namespace.js +190 -0
- package/dist/documents-namespace.js.map +1 -0
- package/dist/documents.d.ts +146 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +2045 -0
- package/dist/documents.js.map +1 -0
- package/dist/emitter.d.ts +15 -0
- package/dist/emitter.d.ts.map +1 -0
- package/dist/emitter.js +90 -0
- package/dist/emitter.js.map +1 -0
- package/dist/errors.d.ts +18 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +22 -0
- package/dist/errors.js.map +1 -0
- package/dist/export-guest.d.ts +9 -0
- package/dist/export-guest.d.ts.map +1 -0
- package/dist/export-guest.js +225 -0
- package/dist/export-guest.js.map +1 -0
- package/dist/fetch-retry.d.ts +16 -0
- package/dist/fetch-retry.d.ts.map +1 -0
- package/dist/fetch-retry.js +90 -0
- package/dist/fetch-retry.js.map +1 -0
- package/dist/guest-link.d.ts +3 -0
- package/dist/guest-link.d.ts.map +1 -0
- package/dist/guest-link.js +13 -0
- package/dist/guest-link.js.map +1 -0
- package/dist/handles.d.ts +57 -0
- package/dist/handles.d.ts.map +1 -0
- package/dist/handles.js +1375 -0
- package/dist/handles.js.map +1 -0
- package/dist/hosted-runtime.d.ts +33 -0
- package/dist/hosted-runtime.d.ts.map +1 -0
- package/dist/hosted-runtime.js +99 -0
- package/dist/hosted-runtime.js.map +1 -0
- package/dist/hosted-ui.d.ts +84 -0
- package/dist/hosted-ui.d.ts.map +1 -0
- package/dist/hosted-ui.js +673 -0
- package/dist/hosted-ui.js.map +1 -0
- package/dist/importer.d.ts +22 -0
- package/dist/importer.d.ts.map +1 -0
- package/dist/importer.js +54 -0
- package/dist/importer.js.map +1 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +132 -0
- package/dist/index.js.map +1 -0
- package/dist/init-keys.d.ts +3 -0
- package/dist/init-keys.d.ts.map +1 -0
- package/dist/init-keys.js +13 -0
- package/dist/init-keys.js.map +1 -0
- package/dist/internal.d.ts +6 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +17 -0
- package/dist/internal.js.map +1 -0
- package/dist/jwt.d.ts +3 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +22 -0
- package/dist/jwt.js.map +1 -0
- package/dist/launch-context.d.ts +4 -0
- package/dist/launch-context.d.ts.map +1 -0
- package/dist/launch-context.js +44 -0
- package/dist/launch-context.js.map +1 -0
- package/dist/login-hint.d.ts +4 -0
- package/dist/login-hint.d.ts.map +1 -0
- package/dist/login-hint.js +63 -0
- package/dist/login-hint.js.map +1 -0
- package/dist/mutate-batching.d.ts +14 -0
- package/dist/mutate-batching.d.ts.map +1 -0
- package/dist/mutate-batching.js +65 -0
- package/dist/mutate-batching.js.map +1 -0
- package/dist/open-document.d.ts +127 -0
- package/dist/open-document.d.ts.map +1 -0
- package/dist/open-document.js +2121 -0
- package/dist/open-document.js.map +1 -0
- package/dist/page-title.d.ts +3 -0
- package/dist/page-title.d.ts.map +1 -0
- package/dist/page-title.js +32 -0
- package/dist/page-title.js.map +1 -0
- package/dist/perf.d.ts +9 -0
- package/dist/perf.d.ts.map +1 -0
- package/dist/perf.js +39 -0
- package/dist/perf.js.map +1 -0
- package/dist/presence.d.ts +62 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +331 -0
- package/dist/presence.js.map +1 -0
- package/dist/record-paging.d.ts +99 -0
- package/dist/record-paging.d.ts.map +1 -0
- package/dist/record-paging.js +220 -0
- package/dist/record-paging.js.map +1 -0
- package/dist/schema-conflict-dialog.d.ts +10 -0
- package/dist/schema-conflict-dialog.d.ts.map +1 -0
- package/dist/schema-conflict-dialog.js +164 -0
- package/dist/schema-conflict-dialog.js.map +1 -0
- package/dist/selection-persistence.d.ts +5 -0
- package/dist/selection-persistence.d.ts.map +1 -0
- package/dist/selection-persistence.js +90 -0
- package/dist/selection-persistence.js.map +1 -0
- package/dist/serializer.d.ts +3 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/serializer.js +2 -0
- package/dist/serializer.js.map +1 -0
- package/dist/session-persistence.d.ts +4 -0
- package/dist/session-persistence.d.ts.map +1 -0
- package/dist/session-persistence.js +68 -0
- package/dist/session-persistence.js.map +1 -0
- package/dist/session-toolbar.d.ts +62 -0
- package/dist/session-toolbar.d.ts.map +1 -0
- package/dist/session-toolbar.js +1205 -0
- package/dist/session-toolbar.js.map +1 -0
- package/dist/session.d.ts +69 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +926 -0
- package/dist/session.js.map +1 -0
- package/dist/tail-ops.d.ts +28 -0
- package/dist/tail-ops.d.ts.map +1 -0
- package/dist/tail-ops.js +46 -0
- package/dist/tail-ops.js.map +1 -0
- package/dist/topic-lifecycle.d.ts +41 -0
- package/dist/topic-lifecycle.d.ts.map +1 -0
- package/dist/topic-lifecycle.js +169 -0
- package/dist/topic-lifecycle.js.map +1 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/undo.d.ts +18 -0
- package/dist/undo.d.ts.map +1 -0
- package/dist/undo.js +110 -0
- package/dist/undo.js.map +1 -0
- package/dist/version-bar.d.ts +22 -0
- package/dist/version-bar.d.ts.map +1 -0
- package/dist/version-bar.js +169 -0
- package/dist/version-bar.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +8 -0
- package/dist/version.js.map +1 -0
- package/dist/vite.d.ts +15 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +36 -0
- package/dist/vite.js.map +1 -0
- package/dist/ws-client.d.ts +53 -0
- package/dist/ws-client.d.ts.map +1 -0
- package/dist/ws-client.js +604 -0
- package/dist/ws-client.js.map +1 -0
- package/dist/ws-frame-limit.d.ts +21 -0
- package/dist/ws-frame-limit.d.ts.map +1 -0
- package/dist/ws-frame-limit.js +40 -0
- package/dist/ws-frame-limit.js.map +1 -0
- package/docs/app-manifest.md +65 -0
- package/docs/custom-chrome.md +147 -0
- package/docs/hosted-ui-advanced.md +77 -0
- package/docs/imported-documents.md +261 -0
- package/docs/presence.md +129 -0
- package/docs/schemas.md +135 -0
- package/docs/streams.md +248 -0
- package/package.json +60 -0
- package/scripts/prepare.mjs +50 -0
- package/src/api-fetch.ts +127 -0
- package/src/api-headers.ts +28 -0
- package/src/auth-flow.ts +125 -0
- package/src/client.ts +1457 -0
- package/src/doc-url-sync.ts +183 -0
- package/src/document-branches.ts +124 -0
- package/src/document-checkpoints.ts +135 -0
- package/src/document-commands.ts +651 -0
- package/src/document-signals.ts +234 -0
- package/src/document-streams.ts +132 -0
- package/src/document-thumbnail-capture.ts +191 -0
- package/src/documents-namespace.ts +516 -0
- package/src/documents.ts +2426 -0
- package/src/emitter.ts +129 -0
- package/src/errors.ts +45 -0
- package/src/export-guest.ts +282 -0
- package/src/fetch-retry.ts +119 -0
- package/src/guest-link.ts +14 -0
- package/src/handles.ts +1731 -0
- package/src/hosted-runtime.ts +109 -0
- package/src/hosted-ui.ts +1020 -0
- package/src/importer.ts +89 -0
- package/src/index.ts +267 -0
- package/src/init-keys.ts +15 -0
- package/src/internal.ts +23 -0
- package/src/jwt.ts +18 -0
- package/src/launch-context.ts +43 -0
- package/src/login-hint.ts +60 -0
- package/src/mutate-batching.ts +63 -0
- package/src/open-document.ts +2869 -0
- package/src/page-title.ts +33 -0
- package/src/perf.ts +46 -0
- package/src/presence.ts +471 -0
- package/src/record-paging.ts +270 -0
- package/src/schema-conflict-dialog.ts +176 -0
- package/src/selection-persistence.ts +98 -0
- package/src/serializer.ts +15 -0
- package/src/session-persistence.ts +69 -0
- package/src/session-toolbar.ts +1457 -0
- package/src/session.ts +1262 -0
- package/src/tail-ops.ts +63 -0
- package/src/topic-lifecycle.ts +217 -0
- package/src/types.ts +75 -0
- package/src/undo.ts +125 -0
- package/src/version-bar.ts +221 -0
- package/src/version.ts +8 -0
- package/src/vite.ts +49 -0
- package/src/ws-client.ts +678 -0
- package/src/ws-frame-limit.ts +52 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
// One WS connection per SDK instance, lazily opened on the first `subscribe`.
|
|
2
|
+
// Multiplexes N user callbacks onto one WS-level subscription per topic: the
|
|
3
|
+
// subscribe frame is sent on the first callback for a topic, the unsubscribe
|
|
4
|
+
// frame when the last detaches.
|
|
5
|
+
//
|
|
6
|
+
// Reconnect: on `close` back off (1s..30s) and replay the "wanted-state" set
|
|
7
|
+
// of topics. After ~5 minutes of failure *while foregrounded* bail and fire
|
|
8
|
+
// `onError`, keeping the wanted-state so a later resume / `subscribe`
|
|
9
|
+
// recovers.
|
|
10
|
+
//
|
|
11
|
+
// Background-tab resilience: a hidden tab throttles timers but wall-clock
|
|
12
|
+
// (which the give-up budget measures) keeps advancing, so running the budget
|
|
13
|
+
// while hidden would bail on a healthy connection. The reconnect loop pauses
|
|
14
|
+
// while `document.hidden`; foregrounding (`visibilitychange`) or coming back
|
|
15
|
+
// `online` resets the backoff + budget and reconnects with a fresh token.
|
|
16
|
+
//
|
|
17
|
+
// Heartbeat: send `{ type: 'ping' }` every 60 seconds while any topic is
|
|
18
|
+
// subscribed. One interval serves two purposes: it resets the server's
|
|
19
|
+
// 10-minute idle timeout (only client-originated frames do), and it is the
|
|
20
|
+
// presence liveness signal (the server refreshes `last_seen` on each ping;
|
|
21
|
+
// the staleness threshold is 3 missed heartbeats). The server replies
|
|
22
|
+
// `pong`; a topic-less `{ type: 'error', reason: 'bad_message' }` is
|
|
23
|
+
// accepted as a no-op too, so a deploy without the `ping` handler is
|
|
24
|
+
// tolerated. Both are dropped.
|
|
25
|
+
import { IDLE_TOPIC, holdsFrames, step, } from './topic-lifecycle.js';
|
|
26
|
+
import { SDK_CLIENT_ID } from './version.js';
|
|
27
|
+
const RECONNECT_DELAYS_MS = [1000, 2000, 4000, 8000, 16000, 30000];
|
|
28
|
+
const RECONNECT_GIVE_UP_MS = 5 * 60 * 1000;
|
|
29
|
+
const HEARTBEAT_INTERVAL_MS = 60_000;
|
|
30
|
+
// Cap on the frames held for one topic's unacked subscribe, matching the
|
|
31
|
+
// `outboundQueue` bound. Both hold the same kind of mid-reconnect frames
|
|
32
|
+
// (a disconnect rolls every wanted topic back to `subscribing`, so ops sent
|
|
33
|
+
// during the reconnect window are HELD here rather than in `outboundQueue`),
|
|
34
|
+
// so both need the same memory ceiling or a doc edited through a long outage
|
|
35
|
+
// grows one queue without bound. Generous for a single user's in-flight ops.
|
|
36
|
+
const MAX_QUEUED_FRAMES = 1024;
|
|
37
|
+
export class BrassWsClient {
|
|
38
|
+
opts;
|
|
39
|
+
ws = null;
|
|
40
|
+
connecting = false;
|
|
41
|
+
// Monotonic connect-attempt token. `close()` and each new attempt bump it,
|
|
42
|
+
// so a continuation parked on the token / assertion awaits in
|
|
43
|
+
// `ensureConnected` can tell its attempt was superseded and bail instead
|
|
44
|
+
// of adopting a socket nobody wants (a zombie the heartbeat would keep
|
|
45
|
+
// alive past the gateway idle timeout). Mechanical: any await added to
|
|
46
|
+
// the connect path is covered by re-checking the epoch after it.
|
|
47
|
+
connectEpoch = 0;
|
|
48
|
+
// Per-topic lifecycle state, driven exclusively through the pure
|
|
49
|
+
// `step` transition table in `topic-lifecycle.ts`: handlers translate
|
|
50
|
+
// frames / user calls into events via `dispatch` and execute the
|
|
51
|
+
// returned effects. Wanted topics (the ones a reconnect replays) are
|
|
52
|
+
// the entries in a non-`tornDown` phase; the listener map below is the
|
|
53
|
+
// callback data those decisions act on.
|
|
54
|
+
topics = new Map();
|
|
55
|
+
listeners = new Map();
|
|
56
|
+
// Outbound frames queued while the socket is not OPEN. Drained on `open`
|
|
57
|
+
// (after subscribe replay) so an optimistic `mutate()` survives a reconnect
|
|
58
|
+
// window.
|
|
59
|
+
outboundQueue = [];
|
|
60
|
+
// Per-topic queues of frames held while `holdsFrames` says the topic's
|
|
61
|
+
// subscribe is unacked: a topic-scoped frame (`presence`, `op`) sent
|
|
62
|
+
// before the `subscribed` reply races the SUB# row write and the
|
|
63
|
+
// server's conditional update fails as a spurious `forbidden`. The
|
|
64
|
+
// machine's `flush_held` / `drop_held` effects drain or discard them.
|
|
65
|
+
// Bounded per topic (`MAX_QUEUED_FRAMES`, drop-oldest) so a doc edited
|
|
66
|
+
// through a long outage can't grow it without limit.
|
|
67
|
+
pendingTopicSends = new Map();
|
|
68
|
+
// Warn-once latch for held-queue overflow drops, reset when a topic's held
|
|
69
|
+
// frames flush on the `subscribed` ack, so each unacked window reports once.
|
|
70
|
+
heldOverflowReported = false;
|
|
71
|
+
reconnectAttempt = 0;
|
|
72
|
+
reconnectStartedAt = null;
|
|
73
|
+
reconnectTimer = null;
|
|
74
|
+
// Warn-once latch for outbound-queue overflow drops; reset when the queue
|
|
75
|
+
// drains on a successful (re)connect, so each disconnect reports at most
|
|
76
|
+
// once.
|
|
77
|
+
outboundOverflowReported = false;
|
|
78
|
+
heartbeatTimer = null;
|
|
79
|
+
closedByUser = false;
|
|
80
|
+
// Page-lifecycle listeners, installed lazily on first connect and removed on
|
|
81
|
+
// user close. Guarded for non-DOM environments (Node, SSR).
|
|
82
|
+
visibilityListener = null;
|
|
83
|
+
onlineListener = null;
|
|
84
|
+
constructor(opts) {
|
|
85
|
+
this.opts = opts;
|
|
86
|
+
}
|
|
87
|
+
// Returns an unsubscribe that detaches THIS callback only; the WS-level
|
|
88
|
+
// subscription stays open while other callbacks remain, and the unsubscribe
|
|
89
|
+
// frame is sent only when the last detaches.
|
|
90
|
+
subscribe(topic, callback) {
|
|
91
|
+
let set = this.listeners.get(topic);
|
|
92
|
+
const firstForTopic = !set;
|
|
93
|
+
if (!set) {
|
|
94
|
+
set = new Set();
|
|
95
|
+
this.listeners.set(topic, set);
|
|
96
|
+
}
|
|
97
|
+
set.add(callback);
|
|
98
|
+
if (firstForTopic) {
|
|
99
|
+
this.dispatch(topic, { type: 'user_subscribe' });
|
|
100
|
+
void this.ensureConnected();
|
|
101
|
+
}
|
|
102
|
+
else if (!this.ws && !this.connecting && !this.reconnectTimer) {
|
|
103
|
+
// The wanted-state held this topic but there's no socket and no
|
|
104
|
+
// reconnect in flight (a prior give-up or a drop while hidden): a fresh
|
|
105
|
+
// subscribe is the retry signal. (A pending reconnect timer is left
|
|
106
|
+
// alone so its backoff isn't reset.)
|
|
107
|
+
this.resume();
|
|
108
|
+
}
|
|
109
|
+
return () => {
|
|
110
|
+
const current = this.listeners.get(topic);
|
|
111
|
+
if (!current)
|
|
112
|
+
return;
|
|
113
|
+
current.delete(callback);
|
|
114
|
+
if (current.size > 0)
|
|
115
|
+
return;
|
|
116
|
+
this.listeners.delete(topic);
|
|
117
|
+
this.dispatch(topic, { type: 'user_unsubscribe' });
|
|
118
|
+
// Drop the connection once no topic is wanted, so no idle socket lingers.
|
|
119
|
+
if (this.listeners.size === 0) {
|
|
120
|
+
this.close();
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// Feed one event through the topic's machine, commit the new state, then
|
|
125
|
+
// execute the effects. `idle` results are pruned; `tornDown` entries are
|
|
126
|
+
// kept so a same-connection re-subscribe inherits the spent retry, and
|
|
127
|
+
// the next socket transition prunes them.
|
|
128
|
+
dispatch(topic, event) {
|
|
129
|
+
const prior = this.topics.get(topic) ?? IDLE_TOPIC;
|
|
130
|
+
const { state, effects } = step(prior, event);
|
|
131
|
+
if (state.phase === 'idle') {
|
|
132
|
+
this.topics.delete(topic);
|
|
133
|
+
}
|
|
134
|
+
else if (event.type === 'user_subscribe') {
|
|
135
|
+
// Re-insert so the map's order (the reconnect replay order) tracks
|
|
136
|
+
// subscription order even for a topic re-subscribed after teardown.
|
|
137
|
+
this.topics.delete(topic);
|
|
138
|
+
this.topics.set(topic, state);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
this.topics.set(topic, state);
|
|
142
|
+
}
|
|
143
|
+
for (const effect of effects) {
|
|
144
|
+
this.runEffect(topic, effect);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
runEffect(topic, effect) {
|
|
148
|
+
switch (effect.kind) {
|
|
149
|
+
case 'send_subscribe':
|
|
150
|
+
// Dropped when the socket is not open; `socket_open` re-derives a
|
|
151
|
+
// `send_subscribe` for every wanted topic, so nothing is lost.
|
|
152
|
+
this.sendIfOpen({ type: 'subscribe', topic });
|
|
153
|
+
return;
|
|
154
|
+
case 'send_unsubscribe':
|
|
155
|
+
this.sendIfOpen({ type: 'unsubscribe', topic });
|
|
156
|
+
return;
|
|
157
|
+
case 'flush_held': {
|
|
158
|
+
const queued = this.pendingTopicSends.get(topic);
|
|
159
|
+
if (queued) {
|
|
160
|
+
this.pendingTopicSends.delete(topic);
|
|
161
|
+
for (const f of queued)
|
|
162
|
+
this.sendFrame(f);
|
|
163
|
+
}
|
|
164
|
+
// The unacked window closed; re-arm the overflow warning for the next.
|
|
165
|
+
this.heldOverflowReported = false;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
case 'drop_held':
|
|
169
|
+
this.pendingTopicSends.delete(topic);
|
|
170
|
+
return;
|
|
171
|
+
case 'drop_listeners':
|
|
172
|
+
this.listeners.delete(topic);
|
|
173
|
+
return;
|
|
174
|
+
case 'fire_error':
|
|
175
|
+
this.opts.fireError(new Error(`Brass WebSocket subscribe(${topic}): ${effect.reason}`));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Send an arbitrary frame. Queued when the socket is not open and drained on
|
|
180
|
+
// the next `open` (covering a mutate during the subscribe handshake gap and
|
|
181
|
+
// ops that must survive a mid-session reconnect). Failures (overflow, closed
|
|
182
|
+
// transport) are silently dropped.
|
|
183
|
+
send(frame) {
|
|
184
|
+
// Topic-scoped frames sent before the `subscribed` ack race the SUB# row
|
|
185
|
+
// write and surface as spurious `forbidden`, so hold them until the ack
|
|
186
|
+
// (see `pendingTopicSends`). The machine's `holdsFrames` covers every
|
|
187
|
+
// unacked wanted state (fresh subscribe, reconnect replay, retry).
|
|
188
|
+
const topic = topicOfFrame(frame);
|
|
189
|
+
const topicState = topic !== undefined ? this.topics.get(topic) : undefined;
|
|
190
|
+
if (topic !== undefined && topicState && holdsFrames(topicState)) {
|
|
191
|
+
let queue = this.pendingTopicSends.get(topic);
|
|
192
|
+
if (!queue) {
|
|
193
|
+
queue = [];
|
|
194
|
+
this.pendingTopicSends.set(topic, queue);
|
|
195
|
+
}
|
|
196
|
+
// Bounded like `outboundQueue`: drop the oldest held frame on
|
|
197
|
+
// overflow. Safe to drop here because an op is re-asserted by the
|
|
198
|
+
// per-doc resend tracker (keyed independently on `client_op_id`), and
|
|
199
|
+
// a dropped presence frame is superseded by the next `set()`.
|
|
200
|
+
if (queue.length >= MAX_QUEUED_FRAMES) {
|
|
201
|
+
queue.shift();
|
|
202
|
+
if (!this.heldOverflowReported) {
|
|
203
|
+
this.heldOverflowReported = true;
|
|
204
|
+
this.opts.fireError(new Error('Brass WebSocket: held-frame queue overflowed while a subscribe was unacked; the oldest queued frames were dropped'));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
queue.push(frame);
|
|
208
|
+
// Ensure a connection is in flight; the machine re-emits the
|
|
209
|
+
// subscribe on `socket_open`.
|
|
210
|
+
void this.ensureConnected();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (this.ws && this.ws.readyState === this.ws.OPEN) {
|
|
214
|
+
this.sendFrame(frame);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
// Bounded so a misbehaving caller can't leak memory; 1024 is generous for
|
|
218
|
+
// a single user's mid-session ops during a reconnect (each typically <1KB).
|
|
219
|
+
if (this.outboundQueue.length >= 1024) {
|
|
220
|
+
this.outboundQueue.shift();
|
|
221
|
+
// A dropped frame can be an op the caller believes is saved-pending;
|
|
222
|
+
// surface it, once per overflow episode so a runaway producer can't
|
|
223
|
+
// flood the error channel.
|
|
224
|
+
if (!this.outboundOverflowReported) {
|
|
225
|
+
this.outboundOverflowReported = true;
|
|
226
|
+
this.opts.fireError(new Error('Brass WebSocket: outbound queue overflowed while disconnected; the oldest queued frames were dropped and will not reach the server'));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
this.outboundQueue.push(frame);
|
|
230
|
+
// Kick the connect path if no socket is open yet (a caller that `send`s
|
|
231
|
+
// without ever subscribing), or the queue would never drain.
|
|
232
|
+
if (!this.ws && !this.connecting && !this.closedByUser) {
|
|
233
|
+
void this.ensureConnected();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
close() {
|
|
237
|
+
this.closedByUser = true;
|
|
238
|
+
// Supersede any in-flight connect attempt, and clear `connecting` so a
|
|
239
|
+
// subscribe / send that lands after this close starts its own fresh
|
|
240
|
+
// attempt through the normal entry path (fresh token, lifecycle
|
|
241
|
+
// listeners re-armed) instead of finding the entry guard held by a
|
|
242
|
+
// continuation that is about to bail.
|
|
243
|
+
this.connectEpoch++;
|
|
244
|
+
this.connecting = false;
|
|
245
|
+
this.removeLifecycleListeners();
|
|
246
|
+
if (this.reconnectTimer) {
|
|
247
|
+
clearTimeout(this.reconnectTimer);
|
|
248
|
+
this.reconnectTimer = null;
|
|
249
|
+
}
|
|
250
|
+
this.stopHeartbeat();
|
|
251
|
+
const ws = this.ws;
|
|
252
|
+
this.ws = null;
|
|
253
|
+
// `socket_closed` per topic: drops held frames and rolls wanted topics
|
|
254
|
+
// back to `subscribing` so a later resume replays them.
|
|
255
|
+
for (const topic of this.topics.keys()) {
|
|
256
|
+
this.dispatch(topic, { type: 'socket_closed' });
|
|
257
|
+
}
|
|
258
|
+
this.outboundQueue = [];
|
|
259
|
+
if (ws && ws.readyState !== ws.CLOSED && ws.readyState !== ws.CLOSING) {
|
|
260
|
+
try {
|
|
261
|
+
ws.close();
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// Closing an already-closing socket is fine.
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
async ensureConnected() {
|
|
269
|
+
if (this.ws && this.ws.readyState === this.ws.OPEN)
|
|
270
|
+
return;
|
|
271
|
+
if (this.connecting)
|
|
272
|
+
return;
|
|
273
|
+
this.ensureLifecycleListeners();
|
|
274
|
+
this.connecting = true;
|
|
275
|
+
this.closedByUser = false;
|
|
276
|
+
const epoch = ++this.connectEpoch;
|
|
277
|
+
try {
|
|
278
|
+
const token = await this.opts.getAccessToken();
|
|
279
|
+
if (epoch !== this.connectEpoch)
|
|
280
|
+
return;
|
|
281
|
+
// Anonymous link guest: no access token, but a link token authorises a
|
|
282
|
+
// read-only connection.
|
|
283
|
+
const guestToken = token ? null : this.opts.getGuestLinkToken?.() ?? null;
|
|
284
|
+
if (!token && !guestToken) {
|
|
285
|
+
this.connecting = false;
|
|
286
|
+
this.opts.fireError(new Error('Brass WebSocket subscribe: not signed in'));
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const Ctor = this.opts.webSocketImpl ?? globalThis.WebSocket;
|
|
290
|
+
if (!Ctor) {
|
|
291
|
+
this.connecting = false;
|
|
292
|
+
this.opts.fireError(new Error('Brass WebSocket subscribe: WebSocket is not available'));
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const assertion = token && this.opts.getAppAssertion
|
|
296
|
+
? await this.opts.getAppAssertion()
|
|
297
|
+
: null;
|
|
298
|
+
if (epoch !== this.connectEpoch)
|
|
299
|
+
return;
|
|
300
|
+
const url = token
|
|
301
|
+
? this.buildUrl(token, assertion)
|
|
302
|
+
: this.buildGuestUrl(guestToken);
|
|
303
|
+
// The OPEN guard at the top returns only for a socket that is OPEN; a
|
|
304
|
+
// CONNECTING/CLOSING one falls through to here, so a prior socket can
|
|
305
|
+
// still be around. Adopt the new socket as `this.ws` FIRST, then close
|
|
306
|
+
// the old one: its `close` then fails the identity guard below and is
|
|
307
|
+
// ignored, rather than nulling out the live socket.
|
|
308
|
+
const prev = this.ws;
|
|
309
|
+
const ws = new Ctor(url);
|
|
310
|
+
this.ws = ws;
|
|
311
|
+
// Guard every lifecycle handler on socket identity. A superseded
|
|
312
|
+
// socket's delayed `close` (common on a throttled hidden tab) would
|
|
313
|
+
// otherwise run `onClose`, null out the live `this.ws`, roll every
|
|
314
|
+
// topic back to `subscribing`, and schedule a spurious reconnect.
|
|
315
|
+
ws.addEventListener('open', () => {
|
|
316
|
+
if (this.ws === ws)
|
|
317
|
+
this.onOpen();
|
|
318
|
+
});
|
|
319
|
+
ws.addEventListener('message', (ev) => {
|
|
320
|
+
if (this.ws === ws)
|
|
321
|
+
this.onMessage(ev);
|
|
322
|
+
});
|
|
323
|
+
ws.addEventListener('close', () => {
|
|
324
|
+
if (this.ws === ws)
|
|
325
|
+
this.onClose();
|
|
326
|
+
});
|
|
327
|
+
ws.addEventListener('error', () => {
|
|
328
|
+
// The `close` handler runs after `error` and drives reconnect;
|
|
329
|
+
// swallowing keeps a transient network error from bubbling.
|
|
330
|
+
});
|
|
331
|
+
if (prev) {
|
|
332
|
+
try {
|
|
333
|
+
prev.close();
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
// Closing an already-closing socket is fine.
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
// A superseded attempt's failure is not this client's failure: the
|
|
342
|
+
// `connecting` flag now belongs to the newer attempt, so leave it.
|
|
343
|
+
if (epoch !== this.connectEpoch)
|
|
344
|
+
return;
|
|
345
|
+
this.connecting = false;
|
|
346
|
+
this.opts.fireError(err instanceof Error ? err : new Error(String(err)));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
buildUrl(token, assertion) {
|
|
350
|
+
const base = this.opts.wsBaseUrl.replace(/\/$/, '');
|
|
351
|
+
const sep = base.includes('?') ? '&' : '?';
|
|
352
|
+
const assertionParam = assertion
|
|
353
|
+
? `&app_assertion=${encodeURIComponent(assertion)}`
|
|
354
|
+
: '';
|
|
355
|
+
// `client` feeds the ws.connect audit so the deployed SDK version
|
|
356
|
+
// distribution is measurable per live connection.
|
|
357
|
+
return `${base}${sep}app_id=${encodeURIComponent(this.opts.appId)}&token=${encodeURIComponent(token)}${assertionParam}&client=${encodeURIComponent(SDK_CLIENT_ID)}`;
|
|
358
|
+
}
|
|
359
|
+
// Anonymous link-guest connect URL: the link token authorises the socket
|
|
360
|
+
// (no access token, no app assertion). The server resolves it to a
|
|
361
|
+
// read-only, single-doc-scoped connection.
|
|
362
|
+
buildGuestUrl(guestToken) {
|
|
363
|
+
const base = this.opts.wsBaseUrl.replace(/\/$/, '');
|
|
364
|
+
const sep = base.includes('?') ? '&' : '?';
|
|
365
|
+
return `${base}${sep}app_id=${encodeURIComponent(this.opts.appId)}&brass_link=${encodeURIComponent(guestToken)}`;
|
|
366
|
+
}
|
|
367
|
+
onOpen() {
|
|
368
|
+
this.connecting = false;
|
|
369
|
+
this.reconnectAttempt = 0;
|
|
370
|
+
this.reconnectStartedAt = null;
|
|
371
|
+
// `socket_open` per topic: re-subscribes every wanted topic (in
|
|
372
|
+
// subscription order) with a fresh retry grant, and prunes `tornDown`
|
|
373
|
+
// leftovers. Runs before the outbound drain so the server sees the
|
|
374
|
+
// subscribes first; otherwise an op right after a reconnect races its
|
|
375
|
+
// subscribe and gets `bad_message` (not subscribed yet).
|
|
376
|
+
for (const topic of this.topics.keys()) {
|
|
377
|
+
this.dispatch(topic, { type: 'socket_open' });
|
|
378
|
+
}
|
|
379
|
+
this.flushOutbound();
|
|
380
|
+
this.startHeartbeat();
|
|
381
|
+
}
|
|
382
|
+
flushOutbound() {
|
|
383
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
384
|
+
return;
|
|
385
|
+
const drained = this.outboundQueue;
|
|
386
|
+
this.outboundQueue = [];
|
|
387
|
+
this.outboundOverflowReported = false;
|
|
388
|
+
for (const frame of drained) {
|
|
389
|
+
this.sendFrame(frame);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
onMessage(ev) {
|
|
393
|
+
let frame;
|
|
394
|
+
try {
|
|
395
|
+
frame = JSON.parse(typeof ev.data === 'string' ? ev.data : String(ev.data));
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
// Drop garbage; the server protocol is strict JSON.
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
if (!isObject(frame))
|
|
402
|
+
return;
|
|
403
|
+
const type = frame.type;
|
|
404
|
+
if (typeof type !== 'string')
|
|
405
|
+
return;
|
|
406
|
+
if (type === 'subscribed' && typeof frame.topic === 'string') {
|
|
407
|
+
// `flush_held` drains the frames held back for the unacked subscribe.
|
|
408
|
+
this.dispatch(frame.topic, { type: 'subscribed_ack' });
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (type === 'unsubscribed') {
|
|
412
|
+
// No callbacks fire; the user-side unsubscribe already returned.
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
if (type === 'pong') {
|
|
416
|
+
// Heartbeat reply; drop silently (the round-trip already reset API
|
|
417
|
+
// Gateway's idle-timeout).
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (type === 'error') {
|
|
421
|
+
const topic = typeof frame.topic === 'string' ? frame.topic : undefined;
|
|
422
|
+
const reason = typeof frame.reason === 'string' ? frame.reason : 'error';
|
|
423
|
+
// A deploy without the `ping` handler replies to our ping with a
|
|
424
|
+
// topic-less `bad_message` instead of `pong`; treat it as a heartbeat
|
|
425
|
+
// ack (see the header).
|
|
426
|
+
if (reason === 'bad_message' && !topic)
|
|
427
|
+
return;
|
|
428
|
+
if (topic) {
|
|
429
|
+
// The machine decides: silent retry, swallow, or teardown +
|
|
430
|
+
// `fire_error`. A topic with no live state (already unsubscribed
|
|
431
|
+
// or torn down our side) swallows: the canonical case is a
|
|
432
|
+
// presence-update racing a `doc_gone` eviction, whose late
|
|
433
|
+
// `forbidden` reply would otherwise surface as a misleading
|
|
434
|
+
// "subscribe(...): forbidden" with no subscribe in flight to fail.
|
|
435
|
+
this.dispatch(topic, { type: 'error', reason });
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
this.opts.fireError(new Error(`Brass WebSocket subscribe: ${reason}`));
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
// Topic-scoped server frames: fan out to every callback for the topic.
|
|
442
|
+
if (typeof frame.topic === 'string') {
|
|
443
|
+
const set = this.listeners.get(frame.topic);
|
|
444
|
+
if (!set)
|
|
445
|
+
return;
|
|
446
|
+
for (const cb of set) {
|
|
447
|
+
try {
|
|
448
|
+
cb(frame);
|
|
449
|
+
}
|
|
450
|
+
catch {
|
|
451
|
+
// A bad listener must not break the others or kill the read loop.
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
onClose() {
|
|
457
|
+
this.stopHeartbeat();
|
|
458
|
+
this.ws = null;
|
|
459
|
+
// Held frames are tied to the connection's unacked subscribes;
|
|
460
|
+
// `socket_closed` drops them and preserves wantedness for the replay.
|
|
461
|
+
for (const topic of this.topics.keys()) {
|
|
462
|
+
this.dispatch(topic, { type: 'socket_closed' });
|
|
463
|
+
}
|
|
464
|
+
this.connecting = false;
|
|
465
|
+
if (this.closedByUser)
|
|
466
|
+
return;
|
|
467
|
+
if (this.listeners.size === 0)
|
|
468
|
+
return;
|
|
469
|
+
this.scheduleReconnect();
|
|
470
|
+
}
|
|
471
|
+
scheduleReconnect() {
|
|
472
|
+
// Don't run the loop while hidden: throttled timers but advancing
|
|
473
|
+
// wall-clock would burn the give-up budget without real attempts and bail
|
|
474
|
+
// on a healthy connection. `resume()` restarts with a fresh budget on
|
|
475
|
+
// foreground / network recovery.
|
|
476
|
+
if (this.isHidden())
|
|
477
|
+
return;
|
|
478
|
+
if (this.reconnectStartedAt === null) {
|
|
479
|
+
this.reconnectStartedAt = Date.now();
|
|
480
|
+
}
|
|
481
|
+
if (Date.now() - this.reconnectStartedAt >= RECONNECT_GIVE_UP_MS) {
|
|
482
|
+
this.opts.fireError(new Error('Brass WebSocket subscribe: reconnect gave up after 5 minutes'));
|
|
483
|
+
// Keep the wanted-state: a resume / fresh `subscribe` recovers by
|
|
484
|
+
// replaying it. The budget resets so resume starts clean; clearing
|
|
485
|
+
// listeners here would make the bail permanent (the background-tab bug
|
|
486
|
+
// this guards against).
|
|
487
|
+
this.reconnectAttempt = 0;
|
|
488
|
+
this.reconnectStartedAt = null;
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const idx = Math.min(this.reconnectAttempt, RECONNECT_DELAYS_MS.length - 1);
|
|
492
|
+
const delay = RECONNECT_DELAYS_MS[idx];
|
|
493
|
+
this.reconnectAttempt += 1;
|
|
494
|
+
this.reconnectTimer = setTimeout(() => {
|
|
495
|
+
this.reconnectTimer = null;
|
|
496
|
+
void this.ensureConnected();
|
|
497
|
+
}, delay);
|
|
498
|
+
}
|
|
499
|
+
// Foreground return / network recovery: reset the backoff + give-up budget
|
|
500
|
+
// and reconnect promptly with a fresh token instead of waiting out a long
|
|
501
|
+
// delay or staying dead after a bail. No-op when nothing is wanted, the user
|
|
502
|
+
// closed us, or a socket is already open.
|
|
503
|
+
resume() {
|
|
504
|
+
if (this.closedByUser)
|
|
505
|
+
return;
|
|
506
|
+
if (this.listeners.size === 0)
|
|
507
|
+
return;
|
|
508
|
+
if (this.ws && this.ws.readyState === this.ws.OPEN)
|
|
509
|
+
return;
|
|
510
|
+
if (this.reconnectTimer) {
|
|
511
|
+
clearTimeout(this.reconnectTimer);
|
|
512
|
+
this.reconnectTimer = null;
|
|
513
|
+
}
|
|
514
|
+
this.reconnectAttempt = 0;
|
|
515
|
+
this.reconnectStartedAt = null;
|
|
516
|
+
void this.ensureConnected();
|
|
517
|
+
}
|
|
518
|
+
isHidden() {
|
|
519
|
+
const doc = globalThis.document;
|
|
520
|
+
return doc?.hidden === true;
|
|
521
|
+
}
|
|
522
|
+
ensureLifecycleListeners() {
|
|
523
|
+
if (this.visibilityListener || this.onlineListener)
|
|
524
|
+
return;
|
|
525
|
+
const doc = globalThis.document;
|
|
526
|
+
if (doc && typeof doc.addEventListener === 'function') {
|
|
527
|
+
const listener = () => {
|
|
528
|
+
if (doc.hidden !== true)
|
|
529
|
+
this.resume();
|
|
530
|
+
};
|
|
531
|
+
this.visibilityListener = listener;
|
|
532
|
+
doc.addEventListener('visibilitychange', listener);
|
|
533
|
+
}
|
|
534
|
+
if (typeof globalThis.addEventListener === 'function') {
|
|
535
|
+
const listener = () => this.resume();
|
|
536
|
+
this.onlineListener = listener;
|
|
537
|
+
globalThis.addEventListener('online', listener);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
removeLifecycleListeners() {
|
|
541
|
+
const doc = globalThis.document;
|
|
542
|
+
if (this.visibilityListener &&
|
|
543
|
+
doc &&
|
|
544
|
+
typeof doc.removeEventListener === 'function') {
|
|
545
|
+
doc.removeEventListener('visibilitychange', this.visibilityListener);
|
|
546
|
+
}
|
|
547
|
+
this.visibilityListener = null;
|
|
548
|
+
if (this.onlineListener &&
|
|
549
|
+
typeof globalThis.removeEventListener === 'function') {
|
|
550
|
+
globalThis.removeEventListener('online', this.onlineListener);
|
|
551
|
+
}
|
|
552
|
+
this.onlineListener = null;
|
|
553
|
+
}
|
|
554
|
+
startHeartbeat() {
|
|
555
|
+
this.stopHeartbeat();
|
|
556
|
+
this.heartbeatTimer = setInterval(() => {
|
|
557
|
+
this.sendIfOpen({ type: 'ping' });
|
|
558
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
559
|
+
}
|
|
560
|
+
stopHeartbeat() {
|
|
561
|
+
if (this.heartbeatTimer) {
|
|
562
|
+
clearInterval(this.heartbeatTimer);
|
|
563
|
+
this.heartbeatTimer = null;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
sendIfOpen(frame) {
|
|
567
|
+
if (!this.ws || this.ws.readyState !== this.ws.OPEN)
|
|
568
|
+
return;
|
|
569
|
+
this.sendFrame(frame);
|
|
570
|
+
}
|
|
571
|
+
sendFrame(frame) {
|
|
572
|
+
if (!this.ws)
|
|
573
|
+
return;
|
|
574
|
+
try {
|
|
575
|
+
this.ws.send(JSON.stringify(frame));
|
|
576
|
+
}
|
|
577
|
+
catch {
|
|
578
|
+
// A send failure means the connection is half-dead; `close` fires
|
|
579
|
+
// shortly and drives reconnect.
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
function isObject(value) {
|
|
584
|
+
return typeof value === 'object' && value !== null;
|
|
585
|
+
}
|
|
586
|
+
// The frame's topic, if any. `send()` uses it to decide whether the frame is
|
|
587
|
+
// subject to the subscribe-pending hold (see `pendingTopicSends`). Returns
|
|
588
|
+
// `undefined` for transport frames (`subscribe` / `unsubscribe` / `ping` /
|
|
589
|
+
// `pong`), which the hold must NOT apply to (the subscribe is what it waits
|
|
590
|
+
// for).
|
|
591
|
+
function topicOfFrame(frame) {
|
|
592
|
+
if (!isObject(frame))
|
|
593
|
+
return undefined;
|
|
594
|
+
const type = frame['type'];
|
|
595
|
+
if (type === 'subscribe' ||
|
|
596
|
+
type === 'unsubscribe' ||
|
|
597
|
+
type === 'ping' ||
|
|
598
|
+
type === 'pong') {
|
|
599
|
+
return undefined;
|
|
600
|
+
}
|
|
601
|
+
const topic = frame['topic'];
|
|
602
|
+
return typeof topic === 'string' ? topic : undefined;
|
|
603
|
+
}
|
|
604
|
+
//# sourceMappingURL=ws-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-client.js","sourceRoot":"","sources":["../src/ws-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface OpFrame {
|
|
2
|
+
type: 'op';
|
|
3
|
+
topic: string;
|
|
4
|
+
op_blob: string;
|
|
5
|
+
client_op_id: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* True when the serialized frame would exceed what the socket carries.
|
|
9
|
+
*
|
|
10
|
+
* The envelope is measured with the blob emptied, so a multi-megabyte
|
|
11
|
+
* value is never serialized just to be counted. Every byte is ASCII
|
|
12
|
+
* (base64 blob, topic, id), so length is byte length.
|
|
13
|
+
*/
|
|
14
|
+
export declare function opFrameExceedsWsLimit(frame: OpFrame): boolean;
|
|
15
|
+
/** True when the blob is too large to ride in a request body, so it is
|
|
16
|
+
* uploaded to storage first and the write names the staged object. */
|
|
17
|
+
export declare function opBlobNeedsStaging(opBlobB64: string): boolean;
|
|
18
|
+
/** True when no single op can hold the blob, so the server could only
|
|
19
|
+
* refuse it. Checked locally to save an upload that ends in a 400. */
|
|
20
|
+
export declare function opBlobExceedsDurableLimit(opBlobB64: string): boolean;
|
|
21
|
+
//# sourceMappingURL=ws-frame-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws-frame-limit.d.ts","sourceRoot":"","sources":["../src/ws-frame-limit.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAG7D;AAED;uEACuE;AACvE,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;uEACuE;AACvE,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEpE"}
|