dsh-pause-session 0.0.0-stage → 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/CHANGELOG.md +41 -0
- package/DEVELOPMENT.md +55 -0
- package/LICENSE +21 -0
- package/README.md +130 -2
- package/README.zh.md +91 -0
- package/REHEARSAL.md +90 -0
- package/cordis.patch.yml +3 -0
- package/docs/images/pause-controls.webp +0 -0
- package/icon.svg +5 -0
- package/lib/client.js +2299 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +2975 -0
- package/lib/index.js.map +1 -0
- package/lib/types/client/burst.d.ts +253 -0
- package/lib/types/client/control.d.ts +232 -0
- package/lib/types/client/global.d.ts +325 -0
- package/lib/types/client/icons.d.ts +24 -0
- package/lib/types/client/index.d.ts +77 -0
- package/lib/types/client/locales.d.ts +69 -0
- package/lib/types/client/pill.d.ts +49 -0
- package/lib/types/client/view.d.ts +137 -0
- package/lib/types/coldlog.d.ts +111 -0
- package/lib/types/composition.d.ts +94 -0
- package/lib/types/control.d.ts +114 -0
- package/lib/types/gate.d.ts +110 -0
- package/lib/types/global.d.ts +101 -0
- package/lib/types/history.d.ts +47 -0
- package/lib/types/index.d.ts +64 -0
- package/lib/types/park.d.ts +50 -0
- package/lib/types/registry.d.ts +117 -0
- package/lib/types/resume.d.ts +221 -0
- package/lib/types/shared.d.ts +324 -0
- package/lib/types/tree.d.ts +71 -0
- package/lib/types/triage.d.ts +121 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +148 -4
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The re-read burst both browser controls share: a handful of reads after
|
|
3
|
+
* something moved, and silence once it settles.
|
|
4
|
+
*
|
|
5
|
+
* What a control reads is published through the host's command channel, and
|
|
6
|
+
* every read is two durable `command/run|done` records in the address session's
|
|
7
|
+
* log plus an aggregate read on the host. Reading is therefore something the
|
|
8
|
+
* host announces and the user asks for, never a heartbeat:
|
|
9
|
+
*
|
|
10
|
+
* - the host announces a transition by registering or unregistering the paused
|
|
11
|
+
* session's marker commands, and — for a phase change, which no registration
|
|
12
|
+
* of its own would show — by registering that session's status verb again.
|
|
13
|
+
* Each of those emits `commands/change`, and that event, plus the user's own
|
|
14
|
+
* press or decision, is what starts a burst;
|
|
15
|
+
* - one transition announces itself several times: a park registers two or three
|
|
16
|
+
* marker commands per session and a phase change re-registers one, and the host
|
|
17
|
+
* emits one `commands/change` per step. The burst therefore waits out a short
|
|
18
|
+
* `windowMs` before its first read and any trigger inside that window
|
|
19
|
+
* postpones it, so the announcements of one transition start one burst rather
|
|
20
|
+
* than one each;
|
|
21
|
+
* - a change that arrives within `ACTION_ABSORB_MS` of the user's own action is
|
|
22
|
+
* that action's own consequence, not news: the action already read the host,
|
|
23
|
+
* so the answer is on screen, and the unregistrations it caused — one event
|
|
24
|
+
* per marker command, measured seconds apart, which the window above cannot
|
|
25
|
+
* merge — would only be read back. Such a change is absorbed, and each one
|
|
26
|
+
* absorbed pushes the window out, because a storm that keeps arriving is
|
|
27
|
+
* still the same storm. The window always closes: the first change after
|
|
28
|
+
* `ACTION_ABSORB_MS` of quiet is news again, and a user action reads on its
|
|
29
|
+
* own whatever the window says;
|
|
30
|
+
* - an absorbed change is deferred rather than lost: a control whose action left
|
|
31
|
+
* a phase still moving asks for one look at the moment the window closes
|
|
32
|
+
* ({@link ReadBurst.recheckAfterEcho}), which is the read that finds a
|
|
33
|
+
* transition the window hid — the park can reach its final phase inside that
|
|
34
|
+
* window and never announce anything again. One look per action, and only
|
|
35
|
+
* while the state it left is still moving;
|
|
36
|
+
* - a burst is a fixed short list of waits, so at most that many reads answer
|
|
37
|
+
* one transition, however long the page stays open;
|
|
38
|
+
* - a read that finds the verdict settled ends the burst at once, and the last
|
|
39
|
+
* reading stays on screen — "the park is settled" is not a state change, so
|
|
40
|
+
* it must not be re-asked;
|
|
41
|
+
* - a read that finds a phase still moving ends it just as fast. Every phase
|
|
42
|
+
* transition is announced at its source, the flush that resolves `parking`
|
|
43
|
+
* into `safe` included, and that announcement is the very event that starts
|
|
44
|
+
* the next burst — so watching a moving phase from here spends records on a
|
|
45
|
+
* transition that will speak for itself;
|
|
46
|
+
* - what the burst still owes after a read is delivery: the read one
|
|
47
|
+
* announcement bought, and one more when a change lands while a pass is in
|
|
48
|
+
* flight, which that pass cannot have answered. The bound is a bound, not a
|
|
49
|
+
* heartbeat: a phase that never settles is left on screen unread until
|
|
50
|
+
* something else moves;
|
|
51
|
+
* - the bound is a trade, taken knowingly: a reading left on screen when the
|
|
52
|
+
* burst runs out is the host's own last answer, and the next trigger — a
|
|
53
|
+
* forwarded change, a press, or the shell handing a control its session again
|
|
54
|
+
* — reads the truth again. A reading nobody answered is never rendered as a
|
|
55
|
+
* confident state. A longer watch is a longer list of waits, at a linear price
|
|
56
|
+
* in the address session's log;
|
|
57
|
+
* - while the page is hidden no read is armed at all, and the wait a burst
|
|
58
|
+
* still owed is armed again when the page comes back: hidden time is neither
|
|
59
|
+
* charged to the burst nor lost from it.
|
|
60
|
+
*
|
|
61
|
+
* A burst is not restarted by a trigger that lands while one is running: every
|
|
62
|
+
* read it still owes happens after that trigger, so absorbing it loses nothing
|
|
63
|
+
* and a storm of registry changes costs one burst rather than one each.
|
|
64
|
+
* @module dsh-pause-session/client/burst
|
|
65
|
+
*/
|
|
66
|
+
/** Whether the page is on screen, as the shell reports it. */
|
|
67
|
+
export type PageVisibility = () => boolean;
|
|
68
|
+
/**
|
|
69
|
+
* One control's burst policy.
|
|
70
|
+
*
|
|
71
|
+
* `waitsMs` is the whole bound: a burst spends at most one read per wait, and
|
|
72
|
+
* an empty list turns bursting off entirely — which is how a test or a
|
|
73
|
+
* composition disables it without touching anything else.
|
|
74
|
+
*/
|
|
75
|
+
export interface BurstPolicy {
|
|
76
|
+
/** How long the burst collects triggers before its first wait starts. */
|
|
77
|
+
readonly windowMs: number;
|
|
78
|
+
/** Waits between the reads of one burst, in order; the first follows a trigger. */
|
|
79
|
+
readonly waitsMs: readonly number[];
|
|
80
|
+
/** Whether the page is on screen now. */
|
|
81
|
+
readonly visible: PageVisibility;
|
|
82
|
+
}
|
|
83
|
+
/** The policy every control uses unless a caller says otherwise. */
|
|
84
|
+
export declare const DEFAULT_BURST_POLICY: BurstPolicy;
|
|
85
|
+
/**
|
|
86
|
+
* How long after a client action a registry change is that action's own echo.
|
|
87
|
+
*
|
|
88
|
+
* A press, a release, a decision, a goal switch and an opened panel each read
|
|
89
|
+
* the host as they run, so their answer is already on screen when the host
|
|
90
|
+
* announces what they did — one `commands/change` per marker command, measured
|
|
91
|
+
* 0.44 s to 2.04 s apart, a cadence the coalescing window cannot merge. Reading
|
|
92
|
+
* those back is paying for an answer the user already has.
|
|
93
|
+
*
|
|
94
|
+
* The window is what makes `commands/change` mean "something moved that this
|
|
95
|
+
* browser did not do". It is refreshed by every change it absorbs, so a storm
|
|
96
|
+
* that keeps arriving stays absorbed rather than leaking one read per change,
|
|
97
|
+
* and it ends for good `ACTION_ABSORB_MS` after the last change: a change after
|
|
98
|
+
* that is news, and so is the next user action, which reads on its own.
|
|
99
|
+
*
|
|
100
|
+
* The margin over the measured 2.04 s gap is deliberate: a transition's own
|
|
101
|
+
* storm has to fit inside the window for the whole storm to stay absorbed.
|
|
102
|
+
*/
|
|
103
|
+
export declare const ACTION_ABSORB_MS = 2500;
|
|
104
|
+
/**
|
|
105
|
+
* The waits one burst uses when its first wait is `first`: four reads spread
|
|
106
|
+
* over roughly ten times that. Long enough that a read a hidden page or an
|
|
107
|
+
* in-flight pass deferred is still taken, short enough that a state which never
|
|
108
|
+
* settles costs a handful of log records rather than a night of them.
|
|
109
|
+
* @param first - milliseconds before the burst's first read, after its window; `<= 0` disables bursting.
|
|
110
|
+
* @returns the waits, in order; empty when bursting is disabled.
|
|
111
|
+
*/
|
|
112
|
+
export declare function burstWaits(first: number): readonly number[];
|
|
113
|
+
/** Start and clear one timer, spelled so a test can drive the clock. */
|
|
114
|
+
export interface BurstClock {
|
|
115
|
+
/**
|
|
116
|
+
* @param callback - the read to run.
|
|
117
|
+
* @param ms - how long to wait.
|
|
118
|
+
* @returns the scheduled timer.
|
|
119
|
+
*/
|
|
120
|
+
set(callback: () => void, ms: number): ReturnType<typeof setTimeout>;
|
|
121
|
+
/**
|
|
122
|
+
* @param timer - the timer to clear.
|
|
123
|
+
*/
|
|
124
|
+
clear(timer: ReturnType<typeof setTimeout>): void;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* One control's re-read burst. Every method is safe to call in any order and as
|
|
128
|
+
* often as the caller likes: a trigger starts at most one burst, and one burst
|
|
129
|
+
* never arms more than one read at a time.
|
|
130
|
+
*/
|
|
131
|
+
export declare class ReadBurst {
|
|
132
|
+
private readonly onRead;
|
|
133
|
+
private readonly policy;
|
|
134
|
+
private readonly clock;
|
|
135
|
+
/** The armed wait, when one is counting down. */
|
|
136
|
+
private timer;
|
|
137
|
+
/** Whether the burst is waiting out its collection window rather than a wait between reads. */
|
|
138
|
+
private window;
|
|
139
|
+
/** Waits this burst has not spent yet, front first. */
|
|
140
|
+
private left;
|
|
141
|
+
/** Whether a burst is in progress: it may be collecting, waiting, reading, or reading again. */
|
|
142
|
+
private running;
|
|
143
|
+
/** The absorption window a client action opened, while one is open. */
|
|
144
|
+
private absorbed;
|
|
145
|
+
/** Whether a control asked for the one look owed when the echo window closes. */
|
|
146
|
+
private recheck;
|
|
147
|
+
/**
|
|
148
|
+
* @param onRead - one more read. Called only for a read the burst owes and the page can show.
|
|
149
|
+
* @param policy - the collection window, the waits, and the visibility source.
|
|
150
|
+
* @param clock - the timer implementation.
|
|
151
|
+
*/
|
|
152
|
+
constructor(onRead: () => void, policy: BurstPolicy, clock?: BurstClock);
|
|
153
|
+
/** Whether this control bursts at all. */
|
|
154
|
+
get enabled(): boolean;
|
|
155
|
+
/** Whether one more read is already counting down. */
|
|
156
|
+
get armed(): boolean;
|
|
157
|
+
/** Whether a burst is in progress, with or without a read counting down. */
|
|
158
|
+
get live(): boolean;
|
|
159
|
+
/** Whether a change right now is a client action's own echo. */
|
|
160
|
+
get absorbing(): boolean;
|
|
161
|
+
/**
|
|
162
|
+
* The user acted on this control and the action reads: a press, a release, a
|
|
163
|
+
* decision, a goal switch, an opened panel. Its read is the answer, and the
|
|
164
|
+
* registry changes it sets off announce themselves for a while afterwards, so
|
|
165
|
+
* until the window closes a trigger is that echo rather than news.
|
|
166
|
+
*
|
|
167
|
+
* Called as the action is taken, before its read lands, because the echo can
|
|
168
|
+
* arrive while the action is still running.
|
|
169
|
+
*/
|
|
170
|
+
absorb(): void;
|
|
171
|
+
/**
|
|
172
|
+
* Ask for one read at the moment the echo window closes, because the action's
|
|
173
|
+
* own read left a state that is still moving.
|
|
174
|
+
*
|
|
175
|
+
* The echo window swallows a transition announced inside it — the forwarded
|
|
176
|
+
* event carries no payload, so nothing distinguishes a marker registration
|
|
177
|
+
* from a phase change — and after the window no change is guaranteed to come:
|
|
178
|
+
* the transition that was hidden may be the last one this park makes. The
|
|
179
|
+
* look owed here is the read that finds it, one per action, and it is not a
|
|
180
|
+
* watch: once the window has closed, every further change is news and buys its
|
|
181
|
+
* own read. A control whose action left a settled state owes nothing and asks
|
|
182
|
+
* for nothing.
|
|
183
|
+
*/
|
|
184
|
+
recheckAfterEcho(): void;
|
|
185
|
+
/**
|
|
186
|
+
* Something moved — a registry change. The burst first waits out its
|
|
187
|
+
* collection window, and every trigger that lands inside it restarts that
|
|
188
|
+
* window: the two or three registrations of one park start one burst rather
|
|
189
|
+
* than one each. A burst already past its window takes this as covered —
|
|
190
|
+
* every read it still owes happens after the change.
|
|
191
|
+
*
|
|
192
|
+
* @returns whether the change is news rather than a client action's own echo.
|
|
193
|
+
* The echo announces what the action's read already answered, so only a news
|
|
194
|
+
* trigger is a change a control still owes a read for; a change that lands
|
|
195
|
+
* while a burst is under way is covered by the read that burst already owes.
|
|
196
|
+
* `false` also for a disabled burst, which reads nothing at all.
|
|
197
|
+
*/
|
|
198
|
+
trigger(): boolean;
|
|
199
|
+
/**
|
|
200
|
+
* The control knows it still owes a read: a change that is news has arrived,
|
|
201
|
+
* or a reading just revealed something only a read can answer. A call outside
|
|
202
|
+
* any burst starts one. Absorption never applies here — this is not a change
|
|
203
|
+
* some action caused, it is a read the control already knows it needs.
|
|
204
|
+
*/
|
|
205
|
+
next(): void;
|
|
206
|
+
/**
|
|
207
|
+
* Nothing is moving: end the burst and forget it. The last reading stays
|
|
208
|
+
* published, and the next trigger starts a fresh burst. Two things outlive
|
|
209
|
+
* this: an action's absorption window, which is about what the action caused
|
|
210
|
+
* rather than about the state, and the one look it may still owe — see
|
|
211
|
+
* {@link recheckAfterEcho}.
|
|
212
|
+
*/
|
|
213
|
+
stop(): void;
|
|
214
|
+
/** Drop the armed wait. A burst that is still owed its read survives it; see {@link checkVisibility}. */
|
|
215
|
+
cancel(): void;
|
|
216
|
+
/**
|
|
217
|
+
* Re-evaluate one page-visibility change. Going off screen drops the armed
|
|
218
|
+
* wait — a hidden page is not being read — and coming back arms it again, so
|
|
219
|
+
* the burst resumes where it stopped instead of being spent while nobody was
|
|
220
|
+
* looking. A burst still collecting resumes its window, restarted from the
|
|
221
|
+
* moment the page came back: a trigger that arrived while it was hidden still
|
|
222
|
+
* postpones the read.
|
|
223
|
+
*/
|
|
224
|
+
checkVisibility(): void;
|
|
225
|
+
/** Spend the next wait of the burst, or end it when the bound is reached. */
|
|
226
|
+
private owe;
|
|
227
|
+
/**
|
|
228
|
+
* (Re)start the collection window. While the page is hidden nothing is armed
|
|
229
|
+
* — hidden time is not charged to the burst — and a trigger that lands then
|
|
230
|
+
* is still collected, because coming back starts a whole window.
|
|
231
|
+
*/
|
|
232
|
+
private armWindow;
|
|
233
|
+
/** Put back whatever the burst still owed: its window, or the wait after a read. */
|
|
234
|
+
private arm;
|
|
235
|
+
/**
|
|
236
|
+
* (Re)start the absorption window a client action opened. A hidden page keeps
|
|
237
|
+
* absorbing: this window is about what the action caused, not about what the
|
|
238
|
+
* page is doing. When the window closes, the one look an unsettled action owes
|
|
239
|
+
* is taken; the window is restarted by every change it absorbs, so that look
|
|
240
|
+
* waits for the echo to actually stop.
|
|
241
|
+
*/
|
|
242
|
+
private armAbsorb;
|
|
243
|
+
/** Close the absorption window: changes are news again. */
|
|
244
|
+
private cancelAbsorb;
|
|
245
|
+
/**
|
|
246
|
+
* Arm one wait, spending it when it fires. The wait stays in `left` until
|
|
247
|
+
* then, so a page hidden mid-wait takes the whole wait again: hidden time is
|
|
248
|
+
* neither charged to the burst nor lost from it.
|
|
249
|
+
*/
|
|
250
|
+
private armWait;
|
|
251
|
+
/** Whether the page counts as on screen now, with an absent `document` read as on screen. */
|
|
252
|
+
private visibleNow;
|
|
253
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-side reading and driving of the host's park state for one session.
|
|
3
|
+
*
|
|
4
|
+
* The host publishes no push channel of its own — a third-party plugin cannot
|
|
5
|
+
* add a Client Remote namespace or a forwarded event — so this control reads
|
|
6
|
+
* the host's command registry, which is the only live surface it can publish
|
|
7
|
+
* through: `commands.list(sessionId)` is a Client-callable read, and the
|
|
8
|
+
* forwarded `commands/change` event says when to read it again. The resume
|
|
9
|
+
* definition's presence in that list IS the host's "this session is paused"
|
|
10
|
+
* fact, and the companion status definition answers with the park state as of
|
|
11
|
+
* the read; this module never derives either from its own press. That one
|
|
12
|
+
* answer carries the record's goal opt-out as well, so the switch beside the
|
|
13
|
+
* pause button can show it without a second host round-trip — and without it,
|
|
14
|
+
* the control renders no switch at all.
|
|
15
|
+
*
|
|
16
|
+
* Every phase the host reaches announces itself on that channel: a transition
|
|
17
|
+
* re-registers the status verb, so a transition is what buys the next read and a
|
|
18
|
+
* read that finds a phase still moving is the answer to the transition that
|
|
19
|
+
* produced it rather than a reason to keep asking (`./burst.ts`). One transition
|
|
20
|
+
* arrives as several registry changes — parking a session also registers its
|
|
21
|
+
* three commands — so the burst's own collection window is what turns a tick of
|
|
22
|
+
* changes into one read per shown session. A press or a goal switch reads as it
|
|
23
|
+
* runs, and the marker changes it causes are that action's own echo: those are
|
|
24
|
+
* absorbed for a short window (`./burst.ts`) rather than read back — except for
|
|
25
|
+
* the one look an action owes when that window closes, if the reading it left
|
|
26
|
+
* behind is still moving, because a transition announced inside the window would
|
|
27
|
+
* otherwise never be read at all.
|
|
28
|
+
*
|
|
29
|
+
* The whole-app control reads the same park and publishes what it found
|
|
30
|
+
* (`./global.ts`), and a header consults that answer before spending a read of
|
|
31
|
+
* its own: with nothing registered, no session can be paused, so a header that
|
|
32
|
+
* already says "running" is left saying it rather than paying a round-trip to
|
|
33
|
+
* hear it again. A header the host last showed on its way to a park is never
|
|
34
|
+
* answered that way — that reading is the newer fact — and the derivation is
|
|
35
|
+
* voided by the next whole-app answer that names a registered session, which
|
|
36
|
+
* the forwarded change a park causes is what brings in.
|
|
37
|
+
* @module dsh-pause-session/client/control
|
|
38
|
+
*/
|
|
39
|
+
import type { ClientRemote } from '@deepseek-ai/dsh-api-remotes/client';
|
|
40
|
+
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
41
|
+
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
42
|
+
import type { BurstPolicy } from './burst.js';
|
|
43
|
+
import type { GlobalReading } from './global.js';
|
|
44
|
+
/**
|
|
45
|
+
* The whole-app answer as the session headers read it, or `undefined` before
|
|
46
|
+
* that control has heard from the host. Reading it costs nothing: it is the
|
|
47
|
+
* published snapshot, not a host round-trip.
|
|
48
|
+
*/
|
|
49
|
+
export type AggregateSource = () => GlobalReading | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* What the Session-header control knows about one session: what the host
|
|
52
|
+
* reported, or that no answer has arrived (`undefined`) or the last read
|
|
53
|
+
* failed. Neither of the latter claims a state the host never reported.
|
|
54
|
+
*
|
|
55
|
+
* `draining` and `parking` are the host's own two answers on the way to
|
|
56
|
+
* `safe`; the control shows them rather than rounding them up, because only
|
|
57
|
+
* `safe` means the pending input is durably stored.
|
|
58
|
+
*/
|
|
59
|
+
export type PausePhase = 'active' | 'draining' | 'parking' | 'safe' | 'flush-failed' | 'unavailable';
|
|
60
|
+
/** One host answer about one session. */
|
|
61
|
+
export interface PauseReading {
|
|
62
|
+
readonly phase: PausePhase;
|
|
63
|
+
/** Tools the host reported in flight; empty unless the phase is `draining`. */
|
|
64
|
+
readonly runningTools: readonly string[];
|
|
65
|
+
/**
|
|
66
|
+
* Whether the host's own record lets the next resume re-open this session's
|
|
67
|
+
* goal. Absent — not `true` — when the host did not say: the control shows no
|
|
68
|
+
* switch rather than the product default, because a default here would tell
|
|
69
|
+
* the user their rounds are safe when nobody has answered.
|
|
70
|
+
*/
|
|
71
|
+
readonly rearmGoal?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** Per-session control state, keyed by Session id so a slot selector can watch one session. */
|
|
74
|
+
export interface PauseState {
|
|
75
|
+
/** Reading per Session id; absent means no host answer yet. Writable: the store updates through a draft. */
|
|
76
|
+
bySession: Record<string, PauseReading | undefined>;
|
|
77
|
+
}
|
|
78
|
+
/** Owns the published reading per session; nothing else writes that state. */
|
|
79
|
+
export declare class PauseControl {
|
|
80
|
+
private readonly commands;
|
|
81
|
+
private readonly lifetime;
|
|
82
|
+
private readonly aggregate;
|
|
83
|
+
/** State source the Session-header contribution binds its selector hook to. */
|
|
84
|
+
readonly store: SnapshotStore<PauseState>;
|
|
85
|
+
/** Sessions this control has shown; a host registry change re-reads every one of them. */
|
|
86
|
+
private readonly shown;
|
|
87
|
+
/** Highest read sequence requested per session: an answer older than it is dropped. */
|
|
88
|
+
private readonly latest;
|
|
89
|
+
/**
|
|
90
|
+
* Sessions a change has put back on the list of ones to read once — a
|
|
91
|
+
* transition this browser has been told about and has not answered yet.
|
|
92
|
+
*/
|
|
93
|
+
private readonly owed;
|
|
94
|
+
/**
|
|
95
|
+
* Whether the look owed at the close of an echo window is still to be taken.
|
|
96
|
+
* It reads every shown session, exactly as a change does: the look exists
|
|
97
|
+
* because a change may have been swallowed, and a swallowed change carries no
|
|
98
|
+
* payload either.
|
|
99
|
+
*/
|
|
100
|
+
private trailing;
|
|
101
|
+
/**
|
|
102
|
+
* Sessions whose header was answered from the whole-app "nothing is
|
|
103
|
+
* registered" instead of a read of their own, and which therefore have to be
|
|
104
|
+
* read for real if that answer stops holding.
|
|
105
|
+
*/
|
|
106
|
+
private readonly derived;
|
|
107
|
+
/** The bounded burst of reads one transition buys; see `./burst.ts`. */
|
|
108
|
+
private readonly burst;
|
|
109
|
+
private reads;
|
|
110
|
+
/**
|
|
111
|
+
* @param commands - the Client-callable command namespace.
|
|
112
|
+
* @param lifetime - aborts in-flight calls and the burst when the plugin unloads.
|
|
113
|
+
* @param windowMs - how long the burst collects the registrations of one
|
|
114
|
+
* transition before its first read; also the wait after a press.
|
|
115
|
+
* @param policy - the burst waits and visibility source; defaults to the real page.
|
|
116
|
+
* @param aggregate - the whole-app control's published answer; absent means it is never consulted.
|
|
117
|
+
*/
|
|
118
|
+
constructor(commands: ClientRemote['commands'], lifetime: AbortSignal, windowMs?: number, policy?: Omit<BurstPolicy, 'windowMs' | 'waitsMs'>, aggregate?: AggregateSource);
|
|
119
|
+
/**
|
|
120
|
+
* Read the host's state for one session.
|
|
121
|
+
* @param sessionId - session whose control was rendered.
|
|
122
|
+
* @returns after this read's answer is published or superseded by a newer one.
|
|
123
|
+
*/
|
|
124
|
+
refresh(sessionId: SessionId): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* The host's command registry changed, so every session this control has
|
|
127
|
+
* shown may have moved — the forwarded event carries no payload, so the only
|
|
128
|
+
* honest answer is to look at all of them once. The reads run on the burst's
|
|
129
|
+
* own schedule rather than here, which is what makes a tick's worth of changes
|
|
130
|
+
* — parking a session registers three commands, and a phase change re-registers
|
|
131
|
+
* one more — one burst instead of one each. A change that is this browser's own
|
|
132
|
+
* action's echo owes nothing: the action read the host as it ran.
|
|
133
|
+
*/
|
|
134
|
+
refreshAll(): void;
|
|
135
|
+
/**
|
|
136
|
+
* One pass of the burst: every shown session a change still owes a read. The
|
|
137
|
+
* forwarded event carries no payload, so a change may have touched any of
|
|
138
|
+
* them and each is read once. A session the whole-app answer has already
|
|
139
|
+
* spoken for is answered from it instead of being asked about; see
|
|
140
|
+
* {@link answerIsKnown}.
|
|
141
|
+
*/
|
|
142
|
+
private readAll;
|
|
143
|
+
/** One session's read, published unless a newer read of that session supersedes it. */
|
|
144
|
+
private readOne;
|
|
145
|
+
/**
|
|
146
|
+
* Press the control: pause an active session, or take back a pause that is
|
|
147
|
+
* still on its way in (or already settled). The phase shown afterwards is the
|
|
148
|
+
* host's answer, never an assumed flip.
|
|
149
|
+
* @param sessionId - session whose control was pressed.
|
|
150
|
+
* @returns after the control re-reads the host.
|
|
151
|
+
*/
|
|
152
|
+
toggle(sessionId: SessionId): Promise<void>;
|
|
153
|
+
/**
|
|
154
|
+
* Flip this session's goal opt-out. The verb belongs to the session it
|
|
155
|
+
* changes — the host registers it there, and it takes no other target — so
|
|
156
|
+
* this press needs no listed row. What the switch shows afterwards is the
|
|
157
|
+
* host's next answer, never a local flip: a refused switch leaves the record
|
|
158
|
+
* exactly as it was, and the re-read is what says so.
|
|
159
|
+
* @param sessionId - session whose switch was pressed.
|
|
160
|
+
* @returns after the control re-reads the host.
|
|
161
|
+
*/
|
|
162
|
+
toggleGoal(sessionId: SessionId): Promise<void>;
|
|
163
|
+
/** Ask the host what this session's park state is, in the browser's vocabulary. */
|
|
164
|
+
private read;
|
|
165
|
+
private publish;
|
|
166
|
+
/**
|
|
167
|
+
* Whether the park has already answered for this header: with nothing
|
|
168
|
+
* registered, no session can be paused, so a header that says this session is
|
|
169
|
+
* running carries the whole fact and the read that would confirm it is a host
|
|
170
|
+
* round-trip plus two records in that session's log for nothing.
|
|
171
|
+
*
|
|
172
|
+
* A header the host last showed on its way to a park is never answered this
|
|
173
|
+
* way: that reading is newer than the aggregate the derivation rests on, and
|
|
174
|
+
* only a fresh host answer may replace it.
|
|
175
|
+
*/
|
|
176
|
+
private answerIsKnown;
|
|
177
|
+
/**
|
|
178
|
+
* Whether the whole-app answer says the park holds nothing at all: the one
|
|
179
|
+
* predicate the derivation below and its revocation share.
|
|
180
|
+
*/
|
|
181
|
+
private nothingParked;
|
|
182
|
+
/**
|
|
183
|
+
* Answer one header from the whole-app verdict, and remember that it was that
|
|
184
|
+
* kind of answer: the sessions to read for real are exactly the ones the
|
|
185
|
+
* aggregate may have been wrong about.
|
|
186
|
+
*/
|
|
187
|
+
private deriveActive;
|
|
188
|
+
/**
|
|
189
|
+
* The whole-app answer moved. Whatever was answered from "nothing is
|
|
190
|
+
* registered" is read for real the moment that stops holding — the session a
|
|
191
|
+
* header stood in for may be the one the park just took, possibly parked from
|
|
192
|
+
* another window whose change this browser only sees as the forwarded event.
|
|
193
|
+
* Called on every published aggregate, so it must do nothing while the
|
|
194
|
+
* derivation still holds.
|
|
195
|
+
*/
|
|
196
|
+
reconsider(): void;
|
|
197
|
+
/**
|
|
198
|
+
* Decide what the burst owes after a read: another look while a change this
|
|
199
|
+
* browser has not answered is still outstanding, and nothing once none is.
|
|
200
|
+
*
|
|
201
|
+
* A phase that is still moving is left alone. The host announces every
|
|
202
|
+
* transition on its command channel — the flush that settles a park included,
|
|
203
|
+
* which changes no registration of its own — so a read that finds `draining`
|
|
204
|
+
* or `parking` is the answer to the transition that produced it, and watching
|
|
205
|
+
* that phase from here would spend reads on a move that is going to speak for
|
|
206
|
+
* itself. What the burst still owes is the delivery of an announcement: a read
|
|
207
|
+
* still armed when the change arrived, and a read for a change that landed
|
|
208
|
+
* while the last pass was in flight.
|
|
209
|
+
*
|
|
210
|
+
* The one transition that cannot speak for itself is the one this browser's
|
|
211
|
+
* own echo window swallowed: a press reads the host, the park moves while the
|
|
212
|
+
* window is still open, and the changes that would have announced it look
|
|
213
|
+
* exactly like the press's own registrations. So an action that left a phase
|
|
214
|
+
* still moving asks for one look when that window closes, and only then — the
|
|
215
|
+
* read is owed at a moment, not watched. Once nothing is owed, the last
|
|
216
|
+
* reading stays on screen; the next change, the next user action, or the shell
|
|
217
|
+
* handing this control a session again is what asks next.
|
|
218
|
+
*/
|
|
219
|
+
private follow;
|
|
220
|
+
/**
|
|
221
|
+
* Whether any shown session's last reading is a phase the host has not
|
|
222
|
+
* finished walking — the only states an action's own echo could be hiding a
|
|
223
|
+
* transition behind.
|
|
224
|
+
*/
|
|
225
|
+
private moving;
|
|
226
|
+
/**
|
|
227
|
+
* Follow the page in and out of view. A hidden page is not being read — and
|
|
228
|
+
* the read the burst was about to take is not lost, it waits for the page to
|
|
229
|
+
* come back.
|
|
230
|
+
*/
|
|
231
|
+
watchVisibility(): void;
|
|
232
|
+
}
|