@client-compose/client 0.6.0 → 0.8.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/README.md +40 -4
- package/dist/index.d.ts +71 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +288 -25
- package/dist/index.js.map +1 -1
- package/dist/proxy.d.ts +9 -1
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +87 -21
- package/dist/proxy.js.map +1 -1
- package/dist/testing.d.ts +49 -6
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +202 -21
- package/dist/testing.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +362 -28
- package/src/proxy.ts +107 -25
- package/src/testing.ts +256 -23
package/dist/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@client-compose/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Framework-agnostic Client Compose client: island registry, screen requests and render planning.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"client-compose",
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"LICENSE"
|
|
45
45
|
],
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@client-compose/protocol": "0.
|
|
47
|
+
"@client-compose/protocol": "0.5.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@client-compose/conformance": "0.
|
|
50
|
+
"@client-compose/conformance": "0.8.0"
|
|
51
51
|
},
|
|
52
52
|
"publishConfig": {
|
|
53
53
|
"access": "public"
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,10 @@ import {
|
|
|
10
10
|
type DeclaredError,
|
|
11
11
|
type IslandDescriptor,
|
|
12
12
|
type IslandEntry,
|
|
13
|
+
type IslandRequest,
|
|
14
|
+
type IslandResponse,
|
|
13
15
|
PROTOCOL_VERSION,
|
|
16
|
+
type ScreenHead,
|
|
14
17
|
type ScreenRequest,
|
|
15
18
|
type ScreenResponse,
|
|
16
19
|
} from "@client-compose/protocol";
|
|
@@ -20,8 +23,26 @@ type ErrorEntry = IslandEntry & { status: "error" };
|
|
|
20
23
|
|
|
21
24
|
export type IslandRenderer<R, P = unknown> = (props: P, entry: OkEntry<P>) => R;
|
|
22
25
|
|
|
26
|
+
/** What a client shows for an island still pending in a stream's head (spec §4.6). */
|
|
27
|
+
export type IslandPlaceholder<R> = () => R;
|
|
28
|
+
|
|
23
29
|
export class IslandRegistry<R> {
|
|
24
30
|
private renderers = new Map<string, Map<number, IslandRenderer<R>>>();
|
|
31
|
+
private placeholders = new Map<string, IslandPlaceholder<R>>();
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Register a placeholder for an island version, shown while it's pending in a stream's head
|
|
35
|
+
* (spec §4.6). Each app draws its own: the protocol says nothing about how one looks.
|
|
36
|
+
*/
|
|
37
|
+
registerPlaceholder(island: IslandDescriptor<unknown>, placeholder: IslandPlaceholder<R>): this {
|
|
38
|
+
this.placeholders.set(`${island.type}@${island.version}`, placeholder);
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The placeholder for an island version, if one was registered. */
|
|
43
|
+
placeholder(type: string, version: number): IslandPlaceholder<R> | undefined {
|
|
44
|
+
return this.placeholders.get(`${type}@${version}`);
|
|
45
|
+
}
|
|
25
46
|
|
|
26
47
|
/** Register a renderer for a generated island descriptor: `register(ProfileHeaderV2, (props) => …)`. */
|
|
27
48
|
register<P>(island: IslandDescriptor<P>, render: IslandRenderer<R, P>): this;
|
|
@@ -31,13 +52,25 @@ export class IslandRegistry<R> {
|
|
|
31
52
|
versionOrRender: number | IslandRenderer<R, P>,
|
|
32
53
|
maybeRender?: IslandRenderer<R, P>,
|
|
33
54
|
): this {
|
|
34
|
-
|
|
35
|
-
typeof
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
if (typeof islandOrType !== "string") {
|
|
56
|
+
if (typeof versionOrRender === "number") {
|
|
57
|
+
throw new Error("register(descriptor, render): pass the renderer, not a version");
|
|
58
|
+
}
|
|
59
|
+
return this.add(islandOrType.type, islandOrType.version, versionOrRender);
|
|
60
|
+
}
|
|
61
|
+
if (typeof versionOrRender !== "number" || !maybeRender) {
|
|
62
|
+
throw new Error("register(type, version, render): pass a version and a renderer");
|
|
63
|
+
}
|
|
64
|
+
return this.add(islandOrType, versionOrRender, maybeRender);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
private add<P>(type: string, version: number, render: IslandRenderer<R, P>): this {
|
|
68
|
+
const versions = this.renderers.get(type) ?? new Map<number, IslandRenderer<R>>();
|
|
69
|
+
// The registry hands each renderer the props of its own island type and version, so the
|
|
70
|
+
// renderer's props type holds. TypeScript can't see that through one map of every island.
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
72
|
+
versions.set(version, render as IslandRenderer<R>);
|
|
73
|
+
this.renderers.set(type, versions);
|
|
41
74
|
return this;
|
|
42
75
|
}
|
|
43
76
|
|
|
@@ -61,12 +94,20 @@ export function buildRequest(
|
|
|
61
94
|
client: ClientContext,
|
|
62
95
|
params?: Record<string, string>,
|
|
63
96
|
): ScreenRequest {
|
|
64
|
-
return {
|
|
97
|
+
return {
|
|
98
|
+
protocol: PROTOCOL_VERSION,
|
|
99
|
+
screen,
|
|
100
|
+
params,
|
|
101
|
+
client,
|
|
102
|
+
islands: registry.capabilities(),
|
|
103
|
+
};
|
|
65
104
|
}
|
|
66
105
|
|
|
67
106
|
export interface PlannedIsland<R> {
|
|
68
107
|
key: string;
|
|
69
108
|
render: () => R;
|
|
109
|
+
/** It's a placeholder, for an island still pending in a stream's head (spec §4.6). */
|
|
110
|
+
pending?: boolean;
|
|
70
111
|
}
|
|
71
112
|
|
|
72
113
|
export type SkipReason = "unknown_island" | "unknown_status";
|
|
@@ -78,17 +119,35 @@ export interface PlanOptions<R> {
|
|
|
78
119
|
onSkipped?: (entry: IslandEntry, reason: SkipReason) => void;
|
|
79
120
|
}
|
|
80
121
|
|
|
81
|
-
/**
|
|
122
|
+
/**
|
|
123
|
+
* Turn a screen, or a stream's head (spec §4.6), into an ordered list of renderable islands. An
|
|
124
|
+
* island still pending is its registered placeholder, or left out until it arrives.
|
|
125
|
+
*/
|
|
82
126
|
export function plan<R>(
|
|
83
127
|
registry: IslandRegistry<R>,
|
|
84
|
-
response: ScreenResponse,
|
|
128
|
+
response: ScreenResponse | ScreenHead,
|
|
85
129
|
options: PlanOptions<R> = {},
|
|
86
130
|
): PlannedIsland<R>[] {
|
|
87
131
|
const out: PlannedIsland<R>[] = [];
|
|
88
132
|
for (const entry of response.islands) {
|
|
133
|
+
if (entry.status === "pending") {
|
|
134
|
+
const placeholder = registry.get(entry.type, entry.version)
|
|
135
|
+
? registry.placeholder(entry.type, entry.version)
|
|
136
|
+
: undefined;
|
|
137
|
+
if (placeholder) {
|
|
138
|
+
out.push({
|
|
139
|
+
key: entry.key,
|
|
140
|
+
render: placeholder,
|
|
141
|
+
pending: true,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
89
146
|
if (entry.status === "error") {
|
|
90
147
|
const fallback = options.onError?.(entry);
|
|
91
|
-
if (fallback !== undefined)
|
|
148
|
+
if (fallback !== undefined) {
|
|
149
|
+
out.push({ key: entry.key, render: () => fallback });
|
|
150
|
+
}
|
|
92
151
|
continue;
|
|
93
152
|
}
|
|
94
153
|
if (entry.status !== "ok") {
|
|
@@ -105,6 +164,34 @@ export function plan<R>(
|
|
|
105
164
|
return out;
|
|
106
165
|
}
|
|
107
166
|
|
|
167
|
+
// ---- Reading what the composer answered ----
|
|
168
|
+
|
|
169
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
170
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The body as JSON, or undefined if it isn't JSON. */
|
|
174
|
+
function parseJson(text: string): unknown {
|
|
175
|
+
try {
|
|
176
|
+
return JSON.parse(text);
|
|
177
|
+
} catch {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function isIslandEntry(value: unknown): value is IslandEntry {
|
|
183
|
+
if (!isRecord(value)) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
const named = typeof value.key === "string" && typeof value.type === "string";
|
|
187
|
+
return named && typeof value.version === "number" && typeof value.status === "string";
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** An action's outcome: what a client needs of one is its status (the client fixtures, spec §8.2). */
|
|
191
|
+
function isActionResponse(value: unknown): value is ActionResponse {
|
|
192
|
+
return isRecord(value) && (value.status === "ok" || value.status === "error");
|
|
193
|
+
}
|
|
194
|
+
|
|
108
195
|
export class ScreenFetchError extends Error {
|
|
109
196
|
readonly status: number;
|
|
110
197
|
readonly body: string;
|
|
@@ -121,27 +208,265 @@ export class ScreenFetchError extends Error {
|
|
|
121
208
|
|
|
122
209
|
function errorCode(body: string): string | undefined {
|
|
123
210
|
try {
|
|
124
|
-
const
|
|
211
|
+
const parsed: unknown = JSON.parse(body);
|
|
212
|
+
const error = isRecord(parsed) ? parsed.error : undefined;
|
|
213
|
+
const code = isRecord(error) ? error.code : undefined;
|
|
125
214
|
return typeof code === "string" ? code : undefined;
|
|
126
215
|
} catch {
|
|
127
216
|
return undefined;
|
|
128
217
|
}
|
|
129
218
|
}
|
|
130
219
|
|
|
220
|
+
/**
|
|
221
|
+
* The caller's headers, given any way fetch takes them, sent as JSON unless they say otherwise.
|
|
222
|
+
* (Spreading a `Headers` object would drop them all.)
|
|
223
|
+
*/
|
|
224
|
+
function jsonHeaders(init: HeadersInit | undefined): Headers {
|
|
225
|
+
const headers = new Headers(init);
|
|
226
|
+
if (!headers.has("content-type")) {
|
|
227
|
+
headers.set("content-type", "application/json");
|
|
228
|
+
}
|
|
229
|
+
return headers;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Fetch a screen, all of it: its stream (spec §4.6) read to the end. Throws ScreenFetchError for a
|
|
234
|
+
* non-2xx response (e.g. `not_found`) or one that isn't a screen. To show its islands as they
|
|
235
|
+
* arrive, use openScreen.
|
|
236
|
+
*/
|
|
131
237
|
export async function fetchScreen(
|
|
132
238
|
baseUrl: string,
|
|
133
239
|
request: ScreenRequest,
|
|
134
240
|
init: RequestInit = {},
|
|
135
241
|
fetchImpl: typeof fetch = fetch,
|
|
136
242
|
): Promise<ScreenResponse> {
|
|
243
|
+
return collectScreen(await openScreen(baseUrl, request, init, fetchImpl));
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// ---- Streams (spec §4.6) ----
|
|
247
|
+
|
|
248
|
+
/** A screen as it arrives: its head, then each pending island as the composer resolves it. */
|
|
249
|
+
export interface ScreenStream {
|
|
250
|
+
/** The screen, its islands resolved or pending. */
|
|
251
|
+
head: ScreenHead;
|
|
252
|
+
/**
|
|
253
|
+
* Each pending island's entry, as it arrives. If the stream ends early, those that never came
|
|
254
|
+
* arrive as errors: `incomplete`, retryable.
|
|
255
|
+
*/
|
|
256
|
+
islands: AsyncIterable<IslandEntry>;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Request a screen, and resolve once its head has arrived. Throws ScreenFetchError for a non-2xx
|
|
261
|
+
* response (e.g. `not_found`) or one that isn't a screen's stream.
|
|
262
|
+
*/
|
|
263
|
+
export async function openScreen(
|
|
264
|
+
baseUrl: string,
|
|
265
|
+
request: ScreenRequest,
|
|
266
|
+
init: RequestInit = {},
|
|
267
|
+
fetchImpl: typeof fetch = fetch,
|
|
268
|
+
): Promise<ScreenStream> {
|
|
137
269
|
const res = await fetchImpl(`${baseUrl}/screens/${encodeURIComponent(request.screen)}`, {
|
|
138
270
|
...init,
|
|
139
271
|
method: "POST",
|
|
140
|
-
headers:
|
|
272
|
+
headers: jsonHeaders(init.headers),
|
|
273
|
+
body: JSON.stringify(request),
|
|
274
|
+
});
|
|
275
|
+
if (!res.ok) {
|
|
276
|
+
throw new ScreenFetchError(res.status, await res.text());
|
|
277
|
+
}
|
|
278
|
+
const lines = linesOf(res)[Symbol.asyncIterator]();
|
|
279
|
+
const first = await lines.next();
|
|
280
|
+
const head = first.done ? undefined : parseJson(first.value);
|
|
281
|
+
if (!isRecord(head) || head.kind !== "head" || !isScreenHead(head)) {
|
|
282
|
+
await lines.return?.();
|
|
283
|
+
throw new ScreenFetchError(res.status, first.done ? "" : first.value);
|
|
284
|
+
}
|
|
285
|
+
const { kind: _kind, ...screen } = head;
|
|
286
|
+
return { head: screen, islands: pendingIslands(screen, lines) };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** Reads the rest of a stream, and returns the screen with every island in place. */
|
|
290
|
+
export async function collectScreen(stream: ScreenStream): Promise<ScreenResponse> {
|
|
291
|
+
let head = stream.head;
|
|
292
|
+
for await (const entry of stream.islands) {
|
|
293
|
+
head = withIsland(head, entry);
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
...head,
|
|
297
|
+
islands: head.islands.filter((e): e is IslandEntry => e.status !== "pending"),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** The head with a pending island's entry in its place; unchanged if the key isn't pending. */
|
|
302
|
+
export function withIsland(head: ScreenHead, entry: IslandEntry): ScreenHead {
|
|
303
|
+
return {
|
|
304
|
+
...head,
|
|
305
|
+
islands: head.islands.map((e) => (e.key === entry.key && e.status === "pending" ? entry : e)),
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/** A screen's head and end as a stream, for a screen that's already complete (spec §4.6). */
|
|
310
|
+
export function screenLines(screen: ScreenResponse): string {
|
|
311
|
+
return `${JSON.stringify({ kind: "head", ...screen })}\n${JSON.stringify({ kind: "end" })}\n`;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function isScreenHead(
|
|
315
|
+
value: Record<string, unknown>,
|
|
316
|
+
): value is Record<string, unknown> & ScreenHead {
|
|
317
|
+
return (
|
|
318
|
+
typeof value.screen === "string" &&
|
|
319
|
+
Array.isArray(value.islands) &&
|
|
320
|
+
value.islands.every(isIslandEntry)
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** The islands pending in `head`, as `lines` bring them, until the end line. */
|
|
325
|
+
async function* pendingIslands(
|
|
326
|
+
head: ScreenHead,
|
|
327
|
+
lines: AsyncIterator<string>,
|
|
328
|
+
): AsyncIterable<IslandEntry> {
|
|
329
|
+
const waiting = new Map(
|
|
330
|
+
head.islands.filter((e) => e.status === "pending").map((e) => [e.key, e]),
|
|
331
|
+
);
|
|
332
|
+
try {
|
|
333
|
+
while (waiting.size > 0) {
|
|
334
|
+
const next = await lines.next();
|
|
335
|
+
if (next.done) {
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
338
|
+
const line = parseJson(next.value);
|
|
339
|
+
if (!isRecord(line) || line.kind === "end") {
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
// Lines of a kind it doesn't know, or for an island that isn't pending, are skipped (spec §4.6).
|
|
343
|
+
const entry = line.kind === "island" && isRecord(line.island) ? line.island : undefined;
|
|
344
|
+
if (entry?.status !== "pending" && isIslandEntry(entry) && waiting.delete(entry.key)) {
|
|
345
|
+
yield entry;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
} finally {
|
|
349
|
+
await lines.return?.();
|
|
350
|
+
}
|
|
351
|
+
for (const e of waiting.values()) {
|
|
352
|
+
yield {
|
|
353
|
+
key: e.key,
|
|
354
|
+
type: e.type,
|
|
355
|
+
version: e.version,
|
|
356
|
+
status: "error",
|
|
357
|
+
error: { code: "incomplete", retryable: true },
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** A body's lines, as they arrive if the body can be read as a stream, or all at once. */
|
|
363
|
+
async function* linesOf(res: Response): AsyncIterable<string> {
|
|
364
|
+
if (!res.body) {
|
|
365
|
+
yield* (await res.text()).split("\n").filter((line) => line !== "");
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const reader = res.body.getReader();
|
|
369
|
+
const decoder = new TextDecoder();
|
|
370
|
+
let buffered = "";
|
|
371
|
+
try {
|
|
372
|
+
for (;;) {
|
|
373
|
+
const { done, value } = await reader.read();
|
|
374
|
+
if (done) {
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
buffered += decoder.decode(value, { stream: true });
|
|
378
|
+
const lines = buffered.split("\n");
|
|
379
|
+
buffered = lines.pop() ?? "";
|
|
380
|
+
yield* lines.filter((line) => line !== "");
|
|
381
|
+
}
|
|
382
|
+
buffered += decoder.decode();
|
|
383
|
+
if (buffered.trim() !== "") {
|
|
384
|
+
yield buffered;
|
|
385
|
+
}
|
|
386
|
+
} finally {
|
|
387
|
+
await reader.cancel().catch(() => undefined);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ---- Island requests (spec §4.5) ----
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* A request for one island of a screen: the page `cursor` asks for (an entry's `next`), or the
|
|
395
|
+
* island from its first page without one. `params` are the screen's own.
|
|
396
|
+
*/
|
|
397
|
+
export function buildIslandRequest(
|
|
398
|
+
screen: string,
|
|
399
|
+
params: Record<string, string> | undefined,
|
|
400
|
+
entry: { key: string; version: number },
|
|
401
|
+
client: ClientContext,
|
|
402
|
+
cursor?: string,
|
|
403
|
+
): IslandRequest {
|
|
404
|
+
return {
|
|
405
|
+
protocol: PROTOCOL_VERSION,
|
|
406
|
+
screen,
|
|
407
|
+
key: entry.key,
|
|
408
|
+
version: entry.version,
|
|
409
|
+
params,
|
|
410
|
+
client,
|
|
411
|
+
...(cursor !== undefined && { cursor }),
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* POST an island request. Resolves with the island's entry: its props (and `next`, if it has more
|
|
417
|
+
* pages) or the error it failed with. Throws IslandFetchError for a non-2xx response (e.g.
|
|
418
|
+
* `invalid_cursor`, `unknown_island`, `unauthenticated`), or an answer that isn't this island.
|
|
419
|
+
*/
|
|
420
|
+
export async function fetchIsland(
|
|
421
|
+
baseUrl: string,
|
|
422
|
+
request: IslandRequest,
|
|
423
|
+
init: RequestInit = {},
|
|
424
|
+
fetchImpl: typeof fetch = fetch,
|
|
425
|
+
): Promise<IslandEntry> {
|
|
426
|
+
const path = `${encodeURIComponent(request.screen)}/islands/${encodeURIComponent(request.key)}`;
|
|
427
|
+
const res = await fetchImpl(`${baseUrl}/screens/${path}`, {
|
|
428
|
+
...init,
|
|
429
|
+
method: "POST",
|
|
430
|
+
headers: jsonHeaders(init.headers),
|
|
141
431
|
body: JSON.stringify(request),
|
|
142
432
|
});
|
|
143
|
-
|
|
144
|
-
|
|
433
|
+
const text = await res.text();
|
|
434
|
+
if (!res.ok) {
|
|
435
|
+
throw new IslandFetchError(res.status, text);
|
|
436
|
+
}
|
|
437
|
+
const response = parseJson(text);
|
|
438
|
+
if (!isIslandResponse(response) || !isIslandAsked(response.island, request)) {
|
|
439
|
+
throw new IslandFetchError(res.status, text);
|
|
440
|
+
}
|
|
441
|
+
return response.island;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* An island request failed: a non-2xx response such as `invalid_cursor`, `unknown_island` or
|
|
446
|
+
* `unauthenticated`, or an answer that isn't the island asked for. An island that failed on the
|
|
447
|
+
* composer isn't thrown: it's an entry with `status: "error"`.
|
|
448
|
+
*/
|
|
449
|
+
export class IslandFetchError extends Error {
|
|
450
|
+
readonly status: number;
|
|
451
|
+
readonly body: string;
|
|
452
|
+
/** The protocol error code from the response body (e.g. "invalid_cursor"), if there was one. */
|
|
453
|
+
readonly code: string | undefined;
|
|
454
|
+
|
|
455
|
+
constructor(status: number, body: string) {
|
|
456
|
+
super(`Island request failed with status ${status}`);
|
|
457
|
+
this.status = status;
|
|
458
|
+
this.body = body;
|
|
459
|
+
this.code = errorCode(body);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function isIslandResponse(value: unknown): value is IslandResponse {
|
|
464
|
+
return isRecord(value) && typeof value.screen === "string" && isIslandEntry(value.island);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/** Whether the answer is the island the request asked for, at the version it asked for. */
|
|
468
|
+
function isIslandAsked(island: IslandEntry, request: IslandRequest): boolean {
|
|
469
|
+
return island.key === request.key && island.version === request.version;
|
|
145
470
|
}
|
|
146
471
|
|
|
147
472
|
// ---- Actions (spec §5) ----
|
|
@@ -166,8 +491,11 @@ export class ActionFetchError extends Error {
|
|
|
166
491
|
|
|
167
492
|
/** A fresh idempotency key: one per user intent, reused when retrying it (spec §5.2). */
|
|
168
493
|
export function newIdempotencyKey(): string {
|
|
169
|
-
|
|
170
|
-
|
|
494
|
+
// React Native's Hermes may have no crypto, or crypto without randomUUID.
|
|
495
|
+
const c: Partial<Pick<Crypto, "randomUUID">> | undefined = globalThis.crypto;
|
|
496
|
+
if (c?.randomUUID) {
|
|
497
|
+
return c.randomUUID();
|
|
498
|
+
}
|
|
171
499
|
// React Native's Hermes has no crypto.randomUUID. Uniqueness, not secrecy, is what matters here.
|
|
172
500
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (ch) => {
|
|
173
501
|
const r = (Math.random() * 16) | 0;
|
|
@@ -205,16 +533,18 @@ export async function fetchAction(
|
|
|
205
533
|
const res = await fetchImpl(`${baseUrl}/actions/${encodeURIComponent(request.action)}`, {
|
|
206
534
|
...init,
|
|
207
535
|
method: "POST",
|
|
208
|
-
headers:
|
|
536
|
+
headers: jsonHeaders(init.headers),
|
|
209
537
|
body: JSON.stringify(request),
|
|
210
538
|
});
|
|
211
539
|
const text = await res.text();
|
|
212
|
-
if (!res.ok)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
540
|
+
if (!res.ok) {
|
|
541
|
+
throw new ActionFetchError(res.status, text);
|
|
542
|
+
}
|
|
543
|
+
const response = parseJson(text);
|
|
544
|
+
if (!isActionResponse(response)) {
|
|
216
545
|
throw new ActionFetchError(res.status, text);
|
|
217
546
|
}
|
|
547
|
+
return response;
|
|
218
548
|
}
|
|
219
549
|
|
|
220
550
|
/**
|
|
@@ -226,12 +556,16 @@ export function checkActionResponse<I, R, E extends DeclaredError>(
|
|
|
226
556
|
action: ActionDescriptor<I, R, E>,
|
|
227
557
|
response: unknown,
|
|
228
558
|
): ActionResponse<R, E> {
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
559
|
+
const error = isRecord(response) ? response.error : undefined;
|
|
560
|
+
const code = isRecord(error) ? error.code : undefined;
|
|
561
|
+
const status = isRecord(response) ? response.status : undefined;
|
|
562
|
+
const declared = status === "error" && typeof code === "string" && action.errors.includes(code);
|
|
563
|
+
if (isActionResponse(response) && (status === "ok" || declared)) {
|
|
564
|
+
// The result's and error's types are the action's: generated from its manifest, which the
|
|
565
|
+
// composer checks its outcomes against. They exist only at the type level.
|
|
566
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
567
|
+
return response as ActionResponse<R, E>;
|
|
568
|
+
}
|
|
235
569
|
throw new ActionFetchError(200, JSON.stringify(response));
|
|
236
570
|
}
|
|
237
571
|
|