@client-compose/client 0.5.1 → 0.7.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 +8 -1
- package/dist/index.d.ts +39 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +181 -21
- 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 +19 -2
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +118 -20
- package/dist/testing.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +218 -20
- package/src/proxy.ts +107 -25
- package/src/testing.ts +147 -20
package/README.md
CHANGED
|
@@ -66,6 +66,13 @@ Props aren't validated at runtime; generate types with
|
|
|
66
66
|
[`@client-compose/codegen`](https://www.npmjs.com/package/@client-compose/codegen) to keep them in
|
|
67
67
|
sync with the composer.
|
|
68
68
|
|
|
69
|
+
## Islands with pages
|
|
70
|
+
|
|
71
|
+
An island entry carries `next` while the island has more pages (spec §4.5).
|
|
72
|
+
`fetchIsland(baseUrl, buildIslandRequest(screen, params, entry, client, entry.next))` fetches the
|
|
73
|
+
next one: the island's entry again, with that page's props and its own `next`. Without a cursor, it
|
|
74
|
+
resolves the island again from its first page. The React binding's `usePages` does this for you.
|
|
75
|
+
|
|
69
76
|
## Testing
|
|
70
77
|
|
|
71
78
|
`@client-compose/client/testing` is a fake composer for tests, with no test framework of its own.
|
|
@@ -90,7 +97,7 @@ For React, `@client-compose/react/testing` adds a provider config built around i
|
|
|
90
97
|
|
|
91
98
|
## Through your own server
|
|
92
99
|
|
|
93
|
-
`@client-compose/client/proxy` passes a browser's screen and action requests to the composer
|
|
100
|
+
`@client-compose/client/proxy` passes a browser's screen, island and action requests to the composer
|
|
94
101
|
through your server, so it can add what the browser mustn't hold, such as a session kept in an
|
|
95
102
|
httpOnly cookie. `createComposerProxy({ baseUrl, client, headers, onActionResponse })` checks each
|
|
96
103
|
request (anyone can call a server endpoint with anything), sets the client context and headers,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ActionDescriptor, type ActionRequest, type ActionResponse, type Capabilities, type ClientContext, type DeclaredError, type IslandDescriptor, type IslandEntry, type ScreenRequest, type ScreenResponse } from "@client-compose/protocol";
|
|
1
|
+
import { type ActionDescriptor, type ActionRequest, type ActionResponse, type Capabilities, type ClientContext, type DeclaredError, type IslandDescriptor, type IslandEntry, type IslandRequest, type ScreenRequest, type ScreenResponse } from "@client-compose/protocol";
|
|
2
2
|
type OkEntry<P = unknown> = IslandEntry<P> & {
|
|
3
3
|
status: "ok";
|
|
4
4
|
};
|
|
@@ -11,6 +11,7 @@ export declare class IslandRegistry<R> {
|
|
|
11
11
|
/** Register a renderer for a generated island descriptor: `register(ProfileHeaderV2, (props) => …)`. */
|
|
12
12
|
register<P>(island: IslandDescriptor<P>, render: IslandRenderer<R, P>): this;
|
|
13
13
|
register<P>(type: string, version: number, render: IslandRenderer<R, P>): this;
|
|
14
|
+
private add;
|
|
14
15
|
capabilities(): Capabilities;
|
|
15
16
|
get(type: string, version: number): IslandRenderer<R> | undefined;
|
|
16
17
|
}
|
|
@@ -36,6 +37,32 @@ export declare class ScreenFetchError extends Error {
|
|
|
36
37
|
constructor(status: number, body: string);
|
|
37
38
|
}
|
|
38
39
|
export declare function fetchScreen(baseUrl: string, request: ScreenRequest, init?: RequestInit, fetchImpl?: typeof fetch): Promise<ScreenResponse>;
|
|
40
|
+
/**
|
|
41
|
+
* A request for one island of a screen: the page `cursor` asks for (an entry's `next`), or the
|
|
42
|
+
* island from its first page without one. `params` are the screen's own.
|
|
43
|
+
*/
|
|
44
|
+
export declare function buildIslandRequest(screen: string, params: Record<string, string> | undefined, entry: {
|
|
45
|
+
key: string;
|
|
46
|
+
version: number;
|
|
47
|
+
}, client: ClientContext, cursor?: string): IslandRequest;
|
|
48
|
+
/**
|
|
49
|
+
* POST an island request. Resolves with the island's entry: its props (and `next`, if it has more
|
|
50
|
+
* pages) or the error it failed with. Throws IslandFetchError for a non-2xx response (e.g.
|
|
51
|
+
* `invalid_cursor`, `unknown_island`, `unauthenticated`), or an answer that isn't this island.
|
|
52
|
+
*/
|
|
53
|
+
export declare function fetchIsland(baseUrl: string, request: IslandRequest, init?: RequestInit, fetchImpl?: typeof fetch): Promise<IslandEntry>;
|
|
54
|
+
/**
|
|
55
|
+
* An island request failed: a non-2xx response such as `invalid_cursor`, `unknown_island` or
|
|
56
|
+
* `unauthenticated`, or an answer that isn't the island asked for. An island that failed on the
|
|
57
|
+
* composer isn't thrown: it's an entry with `status: "error"`.
|
|
58
|
+
*/
|
|
59
|
+
export declare class IslandFetchError extends Error {
|
|
60
|
+
readonly status: number;
|
|
61
|
+
readonly body: string;
|
|
62
|
+
/** The protocol error code from the response body (e.g. "invalid_cursor"), if there was one. */
|
|
63
|
+
readonly code: string | undefined;
|
|
64
|
+
constructor(status: number, body: string);
|
|
65
|
+
}
|
|
39
66
|
/**
|
|
40
67
|
* The composer didn't perform the action: a non-2xx response such as `invalid_input`,
|
|
41
68
|
* `unauthenticated` or `action_failed`. Declared errors are not thrown; they're outcomes.
|
|
@@ -50,8 +77,18 @@ export declare class ActionFetchError extends Error {
|
|
|
50
77
|
/** A fresh idempotency key: one per user intent, reused when retrying it (spec §5.2). */
|
|
51
78
|
export declare function newIdempotencyKey(): string;
|
|
52
79
|
export declare function buildActionRequest<I>(action: ActionDescriptor<I, unknown, DeclaredError>, input: I, client: ClientContext, idempotencyKey?: string): ActionRequest<I>;
|
|
53
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* POST an action request. Resolves with the outcome the composer sent (ok or an error); throws
|
|
82
|
+
* ActionFetchError for a non-2xx response or a body that isn't an outcome. Check the outcome against
|
|
83
|
+
* the action with `checkActionResponse` (callAction does).
|
|
84
|
+
*/
|
|
54
85
|
export declare function fetchAction(baseUrl: string, request: ActionRequest, init?: RequestInit, fetchImpl?: typeof fetch): Promise<ActionResponse>;
|
|
86
|
+
/**
|
|
87
|
+
* The response, if it's a result or one of this action's own declared errors. Anything else throws
|
|
88
|
+
* ActionFetchError (status 200, with the code if there is one): an outcome the client can't read,
|
|
89
|
+
* or a code the action doesn't declare, such as one added after this app was built (spec §5.1).
|
|
90
|
+
*/
|
|
91
|
+
export declare function checkActionResponse<I, R, E extends DeclaredError>(action: ActionDescriptor<I, R, E>, response: unknown): ActionResponse<R, E>;
|
|
55
92
|
export interface CallActionOptions {
|
|
56
93
|
client: ClientContext;
|
|
57
94
|
/** Default: a new key. Pass the same key again to retry the same user intent. */
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -4,13 +4,24 @@ import { PROTOCOL_VERSION, } from "@client-compose/protocol";
|
|
|
4
4
|
export class IslandRegistry {
|
|
5
5
|
renderers = new Map();
|
|
6
6
|
register(islandOrType, versionOrRender, maybeRender) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
this.
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
if (typeof islandOrType !== "string") {
|
|
8
|
+
if (typeof versionOrRender === "number") {
|
|
9
|
+
throw new Error("register(descriptor, render): pass the renderer, not a version");
|
|
10
|
+
}
|
|
11
|
+
return this.add(islandOrType.type, islandOrType.version, versionOrRender);
|
|
12
|
+
}
|
|
13
|
+
if (typeof versionOrRender !== "number" || !maybeRender) {
|
|
14
|
+
throw new Error("register(type, version, render): pass a version and a renderer");
|
|
15
|
+
}
|
|
16
|
+
return this.add(islandOrType, versionOrRender, maybeRender);
|
|
17
|
+
}
|
|
18
|
+
add(type, version, render) {
|
|
19
|
+
const versions = this.renderers.get(type) ?? new Map();
|
|
20
|
+
// The registry hands each renderer the props of its own island type and version, so the
|
|
21
|
+
// renderer's props type holds. TypeScript can't see that through one map of every island.
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
23
|
+
versions.set(version, render);
|
|
24
|
+
this.renderers.set(type, versions);
|
|
14
25
|
return this;
|
|
15
26
|
}
|
|
16
27
|
capabilities() {
|
|
@@ -24,7 +35,13 @@ export class IslandRegistry {
|
|
|
24
35
|
}
|
|
25
36
|
}
|
|
26
37
|
export function buildRequest(registry, screen, client, params) {
|
|
27
|
-
return {
|
|
38
|
+
return {
|
|
39
|
+
protocol: PROTOCOL_VERSION,
|
|
40
|
+
screen,
|
|
41
|
+
params,
|
|
42
|
+
client,
|
|
43
|
+
islands: registry.capabilities(),
|
|
44
|
+
};
|
|
28
45
|
}
|
|
29
46
|
/** Turn a response into an ordered list of renderable islands. */
|
|
30
47
|
export function plan(registry, response, options = {}) {
|
|
@@ -32,8 +49,9 @@ export function plan(registry, response, options = {}) {
|
|
|
32
49
|
for (const entry of response.islands) {
|
|
33
50
|
if (entry.status === "error") {
|
|
34
51
|
const fallback = options.onError?.(entry);
|
|
35
|
-
if (fallback !== undefined)
|
|
52
|
+
if (fallback !== undefined) {
|
|
36
53
|
out.push({ key: entry.key, render: () => fallback });
|
|
54
|
+
}
|
|
37
55
|
continue;
|
|
38
56
|
}
|
|
39
57
|
if (entry.status !== "ok") {
|
|
@@ -49,6 +67,36 @@ export function plan(registry, response, options = {}) {
|
|
|
49
67
|
}
|
|
50
68
|
return out;
|
|
51
69
|
}
|
|
70
|
+
// ---- Reading what the composer answered ----
|
|
71
|
+
function isRecord(value) {
|
|
72
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
73
|
+
}
|
|
74
|
+
/** The body as JSON, or undefined if it isn't JSON. */
|
|
75
|
+
function parseJson(text) {
|
|
76
|
+
try {
|
|
77
|
+
return JSON.parse(text);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function isIslandEntry(value) {
|
|
84
|
+
if (!isRecord(value)) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const named = typeof value.key === "string" && typeof value.type === "string";
|
|
88
|
+
return named && typeof value.version === "number" && typeof value.status === "string";
|
|
89
|
+
}
|
|
90
|
+
function isScreenResponse(value) {
|
|
91
|
+
if (!isRecord(value) || typeof value.screen !== "string") {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return Array.isArray(value.islands) && value.islands.every(isIslandEntry);
|
|
95
|
+
}
|
|
96
|
+
/** An action's outcome: what a client needs of one is its status (the client fixtures, spec §8.2). */
|
|
97
|
+
function isActionResponse(value) {
|
|
98
|
+
return isRecord(value) && (value.status === "ok" || value.status === "error");
|
|
99
|
+
}
|
|
52
100
|
export class ScreenFetchError extends Error {
|
|
53
101
|
status;
|
|
54
102
|
body;
|
|
@@ -63,23 +111,105 @@ export class ScreenFetchError extends Error {
|
|
|
63
111
|
}
|
|
64
112
|
function errorCode(body) {
|
|
65
113
|
try {
|
|
66
|
-
const
|
|
114
|
+
const parsed = JSON.parse(body);
|
|
115
|
+
const error = isRecord(parsed) ? parsed.error : undefined;
|
|
116
|
+
const code = isRecord(error) ? error.code : undefined;
|
|
67
117
|
return typeof code === "string" ? code : undefined;
|
|
68
118
|
}
|
|
69
119
|
catch {
|
|
70
120
|
return undefined;
|
|
71
121
|
}
|
|
72
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* The caller's headers, given any way fetch takes them, sent as JSON unless they say otherwise.
|
|
125
|
+
* (Spreading a `Headers` object would drop them all.)
|
|
126
|
+
*/
|
|
127
|
+
function jsonHeaders(init) {
|
|
128
|
+
const headers = new Headers(init);
|
|
129
|
+
if (!headers.has("content-type")) {
|
|
130
|
+
headers.set("content-type", "application/json");
|
|
131
|
+
}
|
|
132
|
+
return headers;
|
|
133
|
+
}
|
|
73
134
|
export async function fetchScreen(baseUrl, request, init = {}, fetchImpl = fetch) {
|
|
74
135
|
const res = await fetchImpl(`${baseUrl}/screens/${encodeURIComponent(request.screen)}`, {
|
|
75
136
|
...init,
|
|
76
137
|
method: "POST",
|
|
77
|
-
headers:
|
|
138
|
+
headers: jsonHeaders(init.headers),
|
|
78
139
|
body: JSON.stringify(request),
|
|
79
140
|
});
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
141
|
+
const text = await res.text();
|
|
142
|
+
if (!res.ok) {
|
|
143
|
+
throw new ScreenFetchError(res.status, text);
|
|
144
|
+
}
|
|
145
|
+
const response = parseJson(text);
|
|
146
|
+
if (!isScreenResponse(response)) {
|
|
147
|
+
throw new ScreenFetchError(res.status, text);
|
|
148
|
+
}
|
|
149
|
+
return response;
|
|
150
|
+
}
|
|
151
|
+
// ---- Island requests (spec §4.5) ----
|
|
152
|
+
/**
|
|
153
|
+
* A request for one island of a screen: the page `cursor` asks for (an entry's `next`), or the
|
|
154
|
+
* island from its first page without one. `params` are the screen's own.
|
|
155
|
+
*/
|
|
156
|
+
export function buildIslandRequest(screen, params, entry, client, cursor) {
|
|
157
|
+
return {
|
|
158
|
+
protocol: PROTOCOL_VERSION,
|
|
159
|
+
screen,
|
|
160
|
+
key: entry.key,
|
|
161
|
+
version: entry.version,
|
|
162
|
+
params,
|
|
163
|
+
client,
|
|
164
|
+
...(cursor !== undefined && { cursor }),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* POST an island request. Resolves with the island's entry: its props (and `next`, if it has more
|
|
169
|
+
* pages) or the error it failed with. Throws IslandFetchError for a non-2xx response (e.g.
|
|
170
|
+
* `invalid_cursor`, `unknown_island`, `unauthenticated`), or an answer that isn't this island.
|
|
171
|
+
*/
|
|
172
|
+
export async function fetchIsland(baseUrl, request, init = {}, fetchImpl = fetch) {
|
|
173
|
+
const path = `${encodeURIComponent(request.screen)}/islands/${encodeURIComponent(request.key)}`;
|
|
174
|
+
const res = await fetchImpl(`${baseUrl}/screens/${path}`, {
|
|
175
|
+
...init,
|
|
176
|
+
method: "POST",
|
|
177
|
+
headers: jsonHeaders(init.headers),
|
|
178
|
+
body: JSON.stringify(request),
|
|
179
|
+
});
|
|
180
|
+
const text = await res.text();
|
|
181
|
+
if (!res.ok) {
|
|
182
|
+
throw new IslandFetchError(res.status, text);
|
|
183
|
+
}
|
|
184
|
+
const response = parseJson(text);
|
|
185
|
+
if (!isIslandResponse(response) || !isIslandAsked(response.island, request)) {
|
|
186
|
+
throw new IslandFetchError(res.status, text);
|
|
187
|
+
}
|
|
188
|
+
return response.island;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* An island request failed: a non-2xx response such as `invalid_cursor`, `unknown_island` or
|
|
192
|
+
* `unauthenticated`, or an answer that isn't the island asked for. An island that failed on the
|
|
193
|
+
* composer isn't thrown: it's an entry with `status: "error"`.
|
|
194
|
+
*/
|
|
195
|
+
export class IslandFetchError extends Error {
|
|
196
|
+
status;
|
|
197
|
+
body;
|
|
198
|
+
/** The protocol error code from the response body (e.g. "invalid_cursor"), if there was one. */
|
|
199
|
+
code;
|
|
200
|
+
constructor(status, body) {
|
|
201
|
+
super(`Island request failed with status ${status}`);
|
|
202
|
+
this.status = status;
|
|
203
|
+
this.body = body;
|
|
204
|
+
this.code = errorCode(body);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function isIslandResponse(value) {
|
|
208
|
+
return isRecord(value) && typeof value.screen === "string" && isIslandEntry(value.island);
|
|
209
|
+
}
|
|
210
|
+
/** Whether the answer is the island the request asked for, at the version it asked for. */
|
|
211
|
+
function isIslandAsked(island, request) {
|
|
212
|
+
return island.key === request.key && island.version === request.version;
|
|
83
213
|
}
|
|
84
214
|
// ---- Actions (spec §5) ----
|
|
85
215
|
/**
|
|
@@ -100,9 +230,11 @@ export class ActionFetchError extends Error {
|
|
|
100
230
|
}
|
|
101
231
|
/** A fresh idempotency key: one per user intent, reused when retrying it (spec §5.2). */
|
|
102
232
|
export function newIdempotencyKey() {
|
|
233
|
+
// React Native's Hermes may have no crypto, or crypto without randomUUID.
|
|
103
234
|
const c = globalThis.crypto;
|
|
104
|
-
if (c?.randomUUID)
|
|
235
|
+
if (c?.randomUUID) {
|
|
105
236
|
return c.randomUUID();
|
|
237
|
+
}
|
|
106
238
|
// React Native's Hermes has no crypto.randomUUID. Uniqueness, not secrecy, is what matters here.
|
|
107
239
|
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (ch) => {
|
|
108
240
|
const r = (Math.random() * 16) | 0;
|
|
@@ -119,17 +251,45 @@ export function buildActionRequest(action, input, client, idempotencyKey) {
|
|
|
119
251
|
...(idempotencyKey !== undefined && { idempotencyKey }),
|
|
120
252
|
};
|
|
121
253
|
}
|
|
122
|
-
/**
|
|
254
|
+
/**
|
|
255
|
+
* POST an action request. Resolves with the outcome the composer sent (ok or an error); throws
|
|
256
|
+
* ActionFetchError for a non-2xx response or a body that isn't an outcome. Check the outcome against
|
|
257
|
+
* the action with `checkActionResponse` (callAction does).
|
|
258
|
+
*/
|
|
123
259
|
export async function fetchAction(baseUrl, request, init = {}, fetchImpl = fetch) {
|
|
124
260
|
const res = await fetchImpl(`${baseUrl}/actions/${encodeURIComponent(request.action)}`, {
|
|
125
261
|
...init,
|
|
126
262
|
method: "POST",
|
|
127
|
-
headers:
|
|
263
|
+
headers: jsonHeaders(init.headers),
|
|
128
264
|
body: JSON.stringify(request),
|
|
129
265
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
266
|
+
const text = await res.text();
|
|
267
|
+
if (!res.ok) {
|
|
268
|
+
throw new ActionFetchError(res.status, text);
|
|
269
|
+
}
|
|
270
|
+
const response = parseJson(text);
|
|
271
|
+
if (!isActionResponse(response)) {
|
|
272
|
+
throw new ActionFetchError(res.status, text);
|
|
273
|
+
}
|
|
274
|
+
return response;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* The response, if it's a result or one of this action's own declared errors. Anything else throws
|
|
278
|
+
* ActionFetchError (status 200, with the code if there is one): an outcome the client can't read,
|
|
279
|
+
* or a code the action doesn't declare, such as one added after this app was built (spec §5.1).
|
|
280
|
+
*/
|
|
281
|
+
export function checkActionResponse(action, response) {
|
|
282
|
+
const error = isRecord(response) ? response.error : undefined;
|
|
283
|
+
const code = isRecord(error) ? error.code : undefined;
|
|
284
|
+
const status = isRecord(response) ? response.status : undefined;
|
|
285
|
+
const declared = status === "error" && typeof code === "string" && action.errors.includes(code);
|
|
286
|
+
if (isActionResponse(response) && (status === "ok" || declared)) {
|
|
287
|
+
// The result's and error's types are the action's: generated from its manifest, which the
|
|
288
|
+
// composer checks its outcomes against. They exist only at the type level.
|
|
289
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
290
|
+
return response;
|
|
291
|
+
}
|
|
292
|
+
throw new ActionFetchError(200, JSON.stringify(response));
|
|
133
293
|
}
|
|
134
294
|
/**
|
|
135
295
|
* Perform an action with its generated descriptor:
|
|
@@ -141,6 +301,6 @@ export async function fetchAction(baseUrl, request, init = {}, fetchImpl = fetch
|
|
|
141
301
|
*/
|
|
142
302
|
export async function callAction(baseUrl, action, input, options) {
|
|
143
303
|
const request = buildActionRequest(action, input, options.client, options.idempotencyKey ?? newIdempotencyKey());
|
|
144
|
-
return (await fetchAction(baseUrl, request, options.init, options.fetch));
|
|
304
|
+
return checkActionResponse(action, await fetchAction(baseUrl, request, options.init, options.fetch));
|
|
145
305
|
}
|
|
146
306
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,kFAAkF;AAElF,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/proxy.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { type ActionRequest, type ActionResponse, type ClientContext, type ScreenRequest, type ScreenResponse } from "@client-compose/protocol";
|
|
1
|
+
import { type ActionRequest, type ActionResponse, type ClientContext, type IslandEntry, type IslandRequest, type ScreenRequest, type ScreenResponse } from "@client-compose/protocol";
|
|
2
2
|
/** A screen request from the browser: the server sets the client context. */
|
|
3
3
|
export type ProxiedScreenRequest = Omit<ScreenRequest, "client">;
|
|
4
|
+
/** An island request from the browser: the server sets the client context. */
|
|
5
|
+
export type ProxiedIslandRequest = Omit<IslandRequest, "client">;
|
|
4
6
|
/** An action request from the browser: the server sets the client context. */
|
|
5
7
|
export type ProxiedActionRequest = Omit<ActionRequest, "client">;
|
|
6
8
|
/** The composer's response, or the status and body it failed with. */
|
|
@@ -34,8 +36,14 @@ export interface ComposerProxy {
|
|
|
34
36
|
performAction(body: unknown): Promise<ProxyResult<ActionResponse>>;
|
|
35
37
|
/** Checks a browser's screen request, then loads it with the server's context and headers. */
|
|
36
38
|
loadScreen(body: unknown): Promise<ProxyResult<ScreenResponse>>;
|
|
39
|
+
/**
|
|
40
|
+
* Checks a browser's island request (a page of an island, spec §4.5), then loads it with the
|
|
41
|
+
* server's context and headers. Resolves with the island's entry.
|
|
42
|
+
*/
|
|
43
|
+
loadIsland(body: unknown): Promise<ProxyResult<IslandEntry>>;
|
|
37
44
|
}
|
|
38
45
|
export declare function isProxiedScreenRequest(value: unknown): value is ProxiedScreenRequest;
|
|
46
|
+
export declare function isProxiedIslandRequest(value: unknown): value is ProxiedIslandRequest;
|
|
39
47
|
export declare function isProxiedActionRequest(value: unknown): value is ProxiedActionRequest;
|
|
40
48
|
export declare function createComposerProxy(options: ComposerProxyOptions): ComposerProxy;
|
|
41
49
|
//# sourceMappingURL=proxy.d.ts.map
|
package/dist/proxy.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,KAAK,aAAa,EAElB,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,aAAa,EAElB,KAAK,aAAa,EAClB,KAAK,cAAc,EACpB,MAAM,0BAA0B,CAAC;AAWlC,6EAA6E;AAC7E,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;AACjE,8EAA8E;AAC9E,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;AACjE,8EAA8E;AAC9E,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;AAEjE,sEAAsE;AACtE,MAAM,MAAM,WAAW,CAAC,CAAC,IACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1E,MAAM,WAAW,oBAAoB;IACnC,yBAAyB;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,4FAA4F;IAC5F,MAAM,EAAE,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;IACrD,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACzE,qEAAqE;IACrE,WAAW,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpD,mEAAmE;IACnE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,CACjB,OAAO,EAAE,aAAa,EACtB,QAAQ,EAAE,cAAc,KACrB,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,aAAa;IAC5B,iGAAiG;IACjG,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC;IACnE,8FAA8F;IAC9F,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,CAAC;IAChE;;;OAGG;IACH,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,CAAC;CAC9D;AAoBD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAWpF;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAcpF;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAWpF;AASD,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAiFhF"}
|
package/dist/proxy.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// Errors come back as data, since frameworks often strip thrown errors' details on the way to the
|
|
14
14
|
// browser.
|
|
15
15
|
import { PROTOCOL_VERSION, } from "@client-compose/protocol";
|
|
16
|
-
import { ActionFetchError, fetchAction, fetchScreen, ScreenFetchError } from "./index.js";
|
|
16
|
+
import { ActionFetchError, fetchAction, fetchIsland, fetchScreen, IslandFetchError, ScreenFetchError, } from "./index.js";
|
|
17
17
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18
18
|
function isCapabilities(value) {
|
|
19
19
|
return (isRecord(value) &&
|
|
@@ -22,20 +22,45 @@ function isCapabilities(value) {
|
|
|
22
22
|
function isStringRecord(value) {
|
|
23
23
|
return isRecord(value) && Object.values(value).every((v) => typeof v === "string");
|
|
24
24
|
}
|
|
25
|
+
// Each guard returns at the first thing a request gets wrong, so it checks no more than it must.
|
|
25
26
|
export function isProxiedScreenRequest(value) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
if (!isRecord(value) || value.protocol !== PROTOCOL_VERSION) {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
if (typeof value.screen !== "string") {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (value.params !== undefined && !isStringRecord(value.params)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return isCapabilities(value.islands);
|
|
37
|
+
}
|
|
38
|
+
export function isProxiedIslandRequest(value) {
|
|
39
|
+
if (!isRecord(value) || value.protocol !== PROTOCOL_VERSION) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
if (typeof value.screen !== "string" || typeof value.key !== "string") {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
if (typeof value.version !== "number" || !Number.isInteger(value.version)) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
if (value.params !== undefined && !isStringRecord(value.params)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return value.cursor === undefined || typeof value.cursor === "string";
|
|
31
52
|
}
|
|
32
53
|
export function isProxiedActionRequest(value) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
54
|
+
if (!isRecord(value) || value.protocol !== PROTOCOL_VERSION) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (typeof value.action !== "string" || typeof value.version !== "number") {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (!("input" in value)) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return value.idempotencyKey === undefined || typeof value.idempotencyKey === "string";
|
|
39
64
|
}
|
|
40
65
|
/** What the composer itself answers to a malformed request. */
|
|
41
66
|
const BAD_REQUEST = {
|
|
@@ -45,12 +70,16 @@ const BAD_REQUEST = {
|
|
|
45
70
|
};
|
|
46
71
|
export function createComposerProxy(options) {
|
|
47
72
|
async function init() {
|
|
48
|
-
const headers = (
|
|
49
|
-
|
|
73
|
+
const headers = new Headers(options.requestInit?.headers);
|
|
74
|
+
for (const [name, value] of Object.entries((await options.headers?.()) ?? {})) {
|
|
75
|
+
headers.set(name, value);
|
|
76
|
+
}
|
|
77
|
+
return { ...options.requestInit, headers };
|
|
50
78
|
}
|
|
51
79
|
async function performAction(body) {
|
|
52
|
-
if (!isProxiedActionRequest(body))
|
|
80
|
+
if (!isProxiedActionRequest(body)) {
|
|
53
81
|
return BAD_REQUEST;
|
|
82
|
+
}
|
|
54
83
|
const request = { ...body, client: await options.client() };
|
|
55
84
|
try {
|
|
56
85
|
const response = await fetchAction(options.baseUrl, request, await init(), options.fetch);
|
|
@@ -60,14 +89,20 @@ export function createComposerProxy(options) {
|
|
|
60
89
|
};
|
|
61
90
|
}
|
|
62
91
|
catch (error) {
|
|
63
|
-
if (error instanceof ActionFetchError)
|
|
64
|
-
return {
|
|
92
|
+
if (error instanceof ActionFetchError) {
|
|
93
|
+
return {
|
|
94
|
+
ok: false,
|
|
95
|
+
status: error.status,
|
|
96
|
+
body: error.body,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
65
99
|
throw error;
|
|
66
100
|
}
|
|
67
101
|
}
|
|
68
102
|
async function loadScreen(body) {
|
|
69
|
-
if (!isProxiedScreenRequest(body))
|
|
103
|
+
if (!isProxiedScreenRequest(body)) {
|
|
70
104
|
return BAD_REQUEST;
|
|
105
|
+
}
|
|
71
106
|
const request = { ...body, client: await options.client() };
|
|
72
107
|
try {
|
|
73
108
|
return {
|
|
@@ -76,11 +111,42 @@ export function createComposerProxy(options) {
|
|
|
76
111
|
};
|
|
77
112
|
}
|
|
78
113
|
catch (error) {
|
|
79
|
-
if (error instanceof ScreenFetchError)
|
|
80
|
-
return {
|
|
114
|
+
if (error instanceof ScreenFetchError) {
|
|
115
|
+
return {
|
|
116
|
+
ok: false,
|
|
117
|
+
status: error.status,
|
|
118
|
+
body: error.body,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function loadIsland(body) {
|
|
125
|
+
if (!isProxiedIslandRequest(body)) {
|
|
126
|
+
return BAD_REQUEST;
|
|
127
|
+
}
|
|
128
|
+
const request = { ...body, client: await options.client() };
|
|
129
|
+
try {
|
|
130
|
+
return {
|
|
131
|
+
ok: true,
|
|
132
|
+
response: await fetchIsland(options.baseUrl, request, await init(), options.fetch),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
if (error instanceof IslandFetchError) {
|
|
137
|
+
return {
|
|
138
|
+
ok: false,
|
|
139
|
+
status: error.status,
|
|
140
|
+
body: error.body,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
81
143
|
throw error;
|
|
82
144
|
}
|
|
83
145
|
}
|
|
84
|
-
return {
|
|
146
|
+
return {
|
|
147
|
+
performAction,
|
|
148
|
+
loadScreen,
|
|
149
|
+
loadIsland,
|
|
150
|
+
};
|
|
85
151
|
}
|
|
86
152
|
//# sourceMappingURL=proxy.js.map
|
package/dist/proxy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,oGAAoG;AACpG,+FAA+F;AAC/F,EAAE;AACF,0CAA0C;AAC1C,+BAA+B;AAC/B,0FAA0F;AAC1F,uFAAuF;AACvF,UAAU;AACV,+FAA+F;AAC/F,EAAE;AACF,mGAAmG;AACnG,kGAAkG;AAClG,WAAW;AAEX,OAAO,
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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"}
|