1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
// The client half of the boundary the compiler derived.
//
// Nothing here decides anything. Which endpoints exist, what they are
// called and what they take is settled by the tier split (spec §17.2);
// this file only moves the bytes and keeps the `Remote of T` variant
// honest while they are in flight.
import from './signal.js';
import from './wire.js';
const LOADING = ;
/**
* The closed set of `Failed` codes, mirrored in `zdc-types`'s
* `FailureCode` (crates/zdc-types/src/failure.rs) and pinned against it by
* a test. Three, not four: `Malformed` was specified and dropped, because
* a code a server selects by choosing what it writes into a body is a bit
* of channel at a public label.
*
* These are the arms of the built-in `choice` called `Code`, so each
* string below is a variant *tag* rather than a value a program compares
* text against. The pinning test reads them off that choice, so it fails
* if this object and the language's arms ever name different sets.
*
* `code` is public by construction, and this is the construction: every
* one of these is decided by *this file's* control flow. `Unreachable`
* means no response object came back at all; `Timeout` means the deadline
* below fired, which is our own `setTimeout` and our own boolean;
* `Rejected` means a response object came back and the status line or the
* decoder rejected it. None of them is read out of the response body, and
* none of them is read out of an error's text.
*/
const CODES = Object.;
/**
* A failure the transport classified. Nothing else constructs one.
*
* The code travels on the *object*, chosen at the throw site, so nothing
* downstream has to parse a message to recover it — which is the only way
* a server's bytes could have reached the field.
*/
/**
* Which code a rejection carries.
*
* A transport this runtime did not write — a test's, or a host page's —
* rejects with whatever it likes. An abort is a `Timeout` because that is
* what an abort of an RPC is; anything else is `Unreachable`, because no
* answer was obtained and the runtime has no evidence of one. The message
* is never consulted.
*/
/**
* The `Failed` payload: two fields at two labels.
*
* `message` is host text and carries §14G.1.3(d)'s join — as secret as
* whatever the endpoint read. `code` is the runtime's own verdict on the
* transport and is `public`. The compiler enforces the difference; this
* file's job is to make the second one true.
*
* `code` is a value of `Code`, a built-in `choice`, so it travels in the
* same shape every other variant does — `{ tag, fields }`, as `variant()`
* in `dom.js` builds and as `whenInto` dispatches on. It was a bare
* string until `Code` became a type, and a bare string is what let
* `error.code is "Timout"` compile.
*/
/**
* A `server` or `durable` signal read from the browser.
*
* Returns a getter of `Remote of T`, which is exactly what §5.2 says the
* read yields: the network is in the value because the network is there,
* and the caller cannot reach the value without eliminating the variant.
*
* `inputs` are the getters for the endpoint's parameters, in the wire
* order the manifest records. Reading them inside the effect is what
* makes the call re-run when — and only when — one of them changes.
*/
export
/**
* The same cell, with the two handles live sync needs.
*
* Returns `[read, apply, refetch]`:
*
* - `read` is the getter `remote` returns.
* - `apply(value)` writes a value straight in, for an update the server
* *pushed*. Without it a second window would have to re-fetch on every
* announcement, which is the round trip §17.2.5 fatal 4's `LiveValue`
* edge exists to avoid.
* - `refetch()` re-runs the call, for a `resync` — the case where the
* server cannot prove it has the whole tail a client missed and the
* only honest answer is to ask again.
*
* Both bump the generation counter, so a push that lands while a request
* is in flight is not overwritten by that request's late answer.
*/
export
/**
* A cross-region write: the browser asks the server to perform it.
*
* The right-hand side and every index were evaluated in the browser and
* are shipped as arguments; only the place resolution and the store
* operator run on the other side (spec §17.2.7's command rule).
*
* Returns a promise, and generated handlers `await` it. That is not a
* convenience: a discarded promise means the handler cannot order two
* writes, cannot see either fail, and half-applies in silence.
*
* **Generated handlers no longer call this.** One write per request is one
* store operation per request, and a handler with three of them can
* half-apply however carefully each one is awaited. `atomic` replaced it.
* The export stays because it is the one-write shape of the same request
* and a host page or a test may want it.
*/
export
/** The reserved name the batch is posted to. `~` cannot appear in a ZD
* identifier, so it can never collide with an endpoint. */
export const ATOMIC = '~atomic';
/**
* Every durable write one handler asked for, as one transaction.
*
* `commands` is `[[endpoint, args], ...]` in source order, which is the
* list the generated handler accumulated in `$tx`. The server runs all of
* them and commits them in a single store transaction, so they all land or
* none does — and a failure part way through leaves the store as it was
* rather than half-written.
*
* The list can be built in the browser at all because §17.2.7's Command
* rule evaluated every right-hand side and index here, so by the time this
* is called the whole transaction is decided and nothing in it depends on
* reading the server's state. That is what lets the server use a
* non-interactive atomic batch, which is the only kind Deno KV and
* DynamoDB have.
*
* An empty list is not a request. A handler whose only write sits inside
* an `if` that did not fire has nothing to commit, and a round trip to say
* so would be a request per click on every conditional write in a program.
*/
export
/**
* Where a write's failure goes.
*
* A generated handler wraps its awaited writes in `try`/`catch` and calls
* this. It exists because the alternative is an unhandled rejection: the
* DOM layer invokes a listener and discards what it returns, so an async
* handler that rejects produces — at best — a console entry nobody reads,
* and at worst nothing at all.
*
* This is deliberately not "show the user an error". The language has no
* global error surface, and inventing one here would be a UI decision made
* in the runtime. What it guarantees is that the failure is *reachable*:
* the default reports it through the platform's own channel, and an
* application — or a test — can replace the sink.
*/
let failureSink = defaultFailureSink;
// Named `reportFailure` and not `failed`: `failed` is already the private
// constructor for the `Failed` variant three lines from the top of this
// file, and a second declaration of that name silently replaces it — so
// `write(failed(error))` would store `undefined` and the page would sit in
// `Loading` for ever. That is exactly the bug this whole sink exists to
// prevent, arriving through the fix for it.
export
/** Replace the failure sink. Used by tests, and by a host page that has
* somewhere better to put it than the console. */
export
/** Which endpoint URL a name maps to. One place, so the shape is one decision. */
export
let transport = defaultTransport;
/** Replace the transport. Used by tests, which record calls rather than make them. */
export
/**
* How long a call may take before the runtime stops waiting.
*
* A deadline is what makes `Timeout` a thing this file decides rather
* than a thing it reports. Without one, a stalled server is
* indistinguishable from a slow one for ever, and the arm never fires.
*/
const DEADLINE_MS = 30000;
/** A cancellable deadline, or a no-op where the platform has no timers. */