@getuserfeedback/protocol 0.10.1 → 1.0.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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/webview-transport.d.ts +2016 -0
- package/dist/webview-transport.d.ts.map +1 -0
- package/dist/webview-transport.js +155 -0
- package/dist/widget-commands.d.ts +19 -1
- package/dist/widget-commands.d.ts.map +1 -1
- package/dist/widget-commands.js +21 -0
- package/package.json +7 -1
- package/src/index.ts +12 -0
- package/src/protocol-root.test.ts +223 -0
- package/src/webview-transport.ts +196 -0
- package/src/widget-commands.ts +24 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webview-transport.d.ts","sourceRoot":"","sources":["../src/webview-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import * as z from "zod/mini";
|
|
2
|
+
import { appEventPayloadSchema } from "./app-event.js";
|
|
3
|
+
import { clientMetaSchema } from "./client-meta.js";
|
|
4
|
+
import { HANDLE_INVALIDATED_REASON_CODES, HANDLE_INVALIDATED_SOURCES, } from "./host/host-event-contract.js";
|
|
5
|
+
import { publicCommandPayloadSchema } from "./widget-commands.js";
|
|
6
|
+
const nonEmptyStringSchema = z.string().check(z.trim(), z.minLength(1));
|
|
7
|
+
const transportMessageIdSchema = nonEmptyStringSchema;
|
|
8
|
+
const commandErrorSchema = z.strictObject({
|
|
9
|
+
message: nonEmptyStringSchema,
|
|
10
|
+
code: z.optional(nonEmptyStringSchema),
|
|
11
|
+
});
|
|
12
|
+
export const webViewCommandEnvelopeSchema = z.object({
|
|
13
|
+
version: z.literal("1"),
|
|
14
|
+
instanceId: nonEmptyStringSchema,
|
|
15
|
+
requestId: nonEmptyStringSchema,
|
|
16
|
+
idempotencyKey: nonEmptyStringSchema,
|
|
17
|
+
clientMeta: z.optional(clientMetaSchema),
|
|
18
|
+
command: publicCommandPayloadSchema,
|
|
19
|
+
});
|
|
20
|
+
const commandSettledDetailBase = {
|
|
21
|
+
requestId: nonEmptyStringSchema,
|
|
22
|
+
instanceId: z.nullable(nonEmptyStringSchema),
|
|
23
|
+
kind: nonEmptyStringSchema,
|
|
24
|
+
};
|
|
25
|
+
const commandSettledDetailSchema = z.discriminatedUnion("ok", [
|
|
26
|
+
z.strictObject({
|
|
27
|
+
...commandSettledDetailBase,
|
|
28
|
+
ok: z.literal(true),
|
|
29
|
+
result: z.optional(z.unknown()),
|
|
30
|
+
}),
|
|
31
|
+
z.strictObject({
|
|
32
|
+
...commandSettledDetailBase,
|
|
33
|
+
ok: z.literal(false),
|
|
34
|
+
error: commandErrorSchema,
|
|
35
|
+
}),
|
|
36
|
+
]);
|
|
37
|
+
const flowStateBase = {
|
|
38
|
+
instanceId: nonEmptyStringSchema,
|
|
39
|
+
isOpen: z.boolean(),
|
|
40
|
+
isLoading: z.boolean(),
|
|
41
|
+
width: z.optional(z.number()),
|
|
42
|
+
height: z.optional(z.number()),
|
|
43
|
+
};
|
|
44
|
+
const flowStateChangedDetailSchema = z.strictObject({
|
|
45
|
+
...flowStateBase,
|
|
46
|
+
flowHandleId: nonEmptyStringSchema,
|
|
47
|
+
});
|
|
48
|
+
const instanceFlowStateChangedDetailSchema = z.strictObject(flowStateBase);
|
|
49
|
+
const openRequestedDetailSchema = z.strictObject({
|
|
50
|
+
instanceId: nonEmptyStringSchema,
|
|
51
|
+
source: z.enum(["command", "targeting"]),
|
|
52
|
+
flowId: nonEmptyStringSchema,
|
|
53
|
+
flowHandleId: z.optional(nonEmptyStringSchema),
|
|
54
|
+
hideCloseButton: z.optional(z.boolean()),
|
|
55
|
+
});
|
|
56
|
+
const handleInvalidatedDetailSchema = z.strictObject({
|
|
57
|
+
instanceId: nonEmptyStringSchema,
|
|
58
|
+
handleKind: nonEmptyStringSchema,
|
|
59
|
+
handleId: nonEmptyStringSchema,
|
|
60
|
+
reasonCode: z.enum(HANDLE_INVALIDATED_REASON_CODES),
|
|
61
|
+
reasonMessage: z.optional(nonEmptyStringSchema),
|
|
62
|
+
relatedRequestId: z.optional(nonEmptyStringSchema),
|
|
63
|
+
source: z.enum(HANDLE_INVALIDATED_SOURCES),
|
|
64
|
+
at: z.number(),
|
|
65
|
+
});
|
|
66
|
+
const unsupportedCommandDetailSchema = z.strictObject({
|
|
67
|
+
requestId: nonEmptyStringSchema,
|
|
68
|
+
instanceId: z.nullable(nonEmptyStringSchema),
|
|
69
|
+
kind: nonEmptyStringSchema,
|
|
70
|
+
code: z.literal("UNSUPPORTED_COMMAND"),
|
|
71
|
+
message: nonEmptyStringSchema,
|
|
72
|
+
issues: z.optional(z.array(z.string())),
|
|
73
|
+
});
|
|
74
|
+
const instanceErrorDetailSchema = z.strictObject({
|
|
75
|
+
instanceId: nonEmptyStringSchema,
|
|
76
|
+
code: nonEmptyStringSchema,
|
|
77
|
+
details: z.optional(z.unknown()),
|
|
78
|
+
recovery: z.optional(z.unknown()),
|
|
79
|
+
at: z.number(),
|
|
80
|
+
});
|
|
81
|
+
const loaderErrorDetailSchema = z.strictObject({
|
|
82
|
+
code: nonEmptyStringSchema,
|
|
83
|
+
details: z.optional(z.unknown()),
|
|
84
|
+
recovery: z.optional(z.unknown()),
|
|
85
|
+
at: z.number(),
|
|
86
|
+
});
|
|
87
|
+
const appEventDetailSchema = z.strictObject({
|
|
88
|
+
instanceId: nonEmptyStringSchema,
|
|
89
|
+
payload: appEventPayloadSchema,
|
|
90
|
+
});
|
|
91
|
+
export const webViewTransportNativeMessageSchema = z.discriminatedUnion("kind", [
|
|
92
|
+
z.strictObject({
|
|
93
|
+
kind: z.literal("enqueueCommand"),
|
|
94
|
+
id: transportMessageIdSchema,
|
|
95
|
+
envelope: webViewCommandEnvelopeSchema,
|
|
96
|
+
}),
|
|
97
|
+
]);
|
|
98
|
+
export const webViewTransportHostEventSchema = z.discriminatedUnion("name", [
|
|
99
|
+
z.strictObject({
|
|
100
|
+
name: z.literal("instance:command:settled"),
|
|
101
|
+
detail: commandSettledDetailSchema,
|
|
102
|
+
}),
|
|
103
|
+
z.strictObject({
|
|
104
|
+
name: z.literal("instance:flow:state-changed"),
|
|
105
|
+
detail: flowStateChangedDetailSchema,
|
|
106
|
+
}),
|
|
107
|
+
z.strictObject({
|
|
108
|
+
name: z.literal("instance:flow:state-changed:instance"),
|
|
109
|
+
detail: instanceFlowStateChangedDetailSchema,
|
|
110
|
+
}),
|
|
111
|
+
z.strictObject({
|
|
112
|
+
name: z.literal("instance:open:requested"),
|
|
113
|
+
detail: openRequestedDetailSchema,
|
|
114
|
+
}),
|
|
115
|
+
z.strictObject({
|
|
116
|
+
name: z.literal("instance:handle:invalidated"),
|
|
117
|
+
detail: handleInvalidatedDetailSchema,
|
|
118
|
+
}),
|
|
119
|
+
z.strictObject({
|
|
120
|
+
name: z.literal("instance:command:unsupported"),
|
|
121
|
+
detail: unsupportedCommandDetailSchema,
|
|
122
|
+
}),
|
|
123
|
+
z.strictObject({
|
|
124
|
+
name: z.literal("instance:error"),
|
|
125
|
+
detail: instanceErrorDetailSchema,
|
|
126
|
+
}),
|
|
127
|
+
z.strictObject({
|
|
128
|
+
name: z.literal("instance:app-event"),
|
|
129
|
+
detail: appEventDetailSchema,
|
|
130
|
+
}),
|
|
131
|
+
z.strictObject({
|
|
132
|
+
name: z.literal("loader:error"),
|
|
133
|
+
detail: loaderErrorDetailSchema,
|
|
134
|
+
}),
|
|
135
|
+
]);
|
|
136
|
+
export const webViewTransportWebMessageSchema = z.discriminatedUnion("kind", [
|
|
137
|
+
z.strictObject({
|
|
138
|
+
kind: z.literal("ready"),
|
|
139
|
+
instanceId: z.optional(nonEmptyStringSchema),
|
|
140
|
+
}),
|
|
141
|
+
z.strictObject({
|
|
142
|
+
kind: z.literal("hostEvent"),
|
|
143
|
+
event: webViewTransportHostEventSchema,
|
|
144
|
+
}),
|
|
145
|
+
z.strictObject({
|
|
146
|
+
kind: z.literal("error"),
|
|
147
|
+
error: commandErrorSchema,
|
|
148
|
+
}),
|
|
149
|
+
]);
|
|
150
|
+
export function parseWebViewTransportNativeMessage(input) {
|
|
151
|
+
return webViewTransportNativeMessageSchema.parse(input);
|
|
152
|
+
}
|
|
153
|
+
export function parseWebViewTransportWebMessage(input) {
|
|
154
|
+
return webViewTransportWebMessageSchema.parse(input);
|
|
155
|
+
}
|
|
@@ -2,9 +2,11 @@ import * as z from "zod/mini";
|
|
|
2
2
|
import { type ConfigureOptions, type InitOptions } from "./widget-config.js";
|
|
3
3
|
export declare const publicWidgetCommandKindSchema: z.ZodMiniEnum<{
|
|
4
4
|
open: "open";
|
|
5
|
-
prefetch: "prefetch";
|
|
6
5
|
prerender: "prerender";
|
|
6
|
+
prefetch: "prefetch";
|
|
7
7
|
identify: "identify";
|
|
8
|
+
updateHostContext: "updateHostContext";
|
|
9
|
+
emitHostSignal: "emitHostSignal";
|
|
8
10
|
setContainer: "setContainer";
|
|
9
11
|
setDefaultContainerPolicy: "setDefaultContainerPolicy";
|
|
10
12
|
configure: "configure";
|
|
@@ -55,6 +57,22 @@ export declare const publicCommandPayloadSchema: z.ZodMiniUnion<readonly [z.ZodM
|
|
|
55
57
|
flowId: z.ZodMiniString<string>;
|
|
56
58
|
flowHandleId: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
57
59
|
hideCloseButton: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
60
|
+
}, z.core.$strict>, z.ZodMiniObject<{
|
|
61
|
+
kind: z.ZodMiniLiteral<"updateHostContext">;
|
|
62
|
+
context: z.ZodMiniObject<{
|
|
63
|
+
url: z.ZodMiniString<string>;
|
|
64
|
+
env: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>;
|
|
65
|
+
storage: z.ZodMiniOptional<z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniString<string>>>;
|
|
66
|
+
page: z.ZodMiniOptional<z.ZodMiniObject<{
|
|
67
|
+
path: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
68
|
+
referrer: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
69
|
+
search: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
70
|
+
}, z.core.$strict>>;
|
|
71
|
+
}, z.core.$strict>;
|
|
72
|
+
}, z.core.$strict>, z.ZodMiniObject<{
|
|
73
|
+
kind: z.ZodMiniLiteral<"emitHostSignal">;
|
|
74
|
+
name: z.ZodMiniString<string>;
|
|
75
|
+
data: z.ZodMiniOptional<z.ZodMiniUnknown>;
|
|
58
76
|
}, z.core.$strict>, z.ZodMiniObject<{
|
|
59
77
|
kind: z.ZodMiniLiteral<"setContainer">;
|
|
60
78
|
flowHandleId: z.ZodMiniString<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"widget-commands.d.ts","sourceRoot":"","sources":["../src/widget-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAK9B,OAAO,EACN,KAAK,gBAAgB,EAErB,KAAK,WAAW,EAEhB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"widget-commands.d.ts","sourceRoot":"","sources":["../src/widget-commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,UAAU,CAAC;AAK9B,OAAO,EACN,KAAK,gBAAgB,EAErB,KAAK,WAAW,EAEhB,MAAM,oBAAoB,CAAC;AAsB5B,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;EAAmC,CAAC;AA6D9E,eAAO,MAAM,qBAAqB;;;;;;;;;4BAShC,CAAC;AAeH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;sBAoDrC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE/E,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAErE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAE5D;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAEtE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAEvE"}
|
package/dist/widget-commands.js
CHANGED
|
@@ -9,6 +9,8 @@ const publicWidgetCommandKinds = [
|
|
|
9
9
|
"prefetch",
|
|
10
10
|
"prerender",
|
|
11
11
|
"identify",
|
|
12
|
+
"updateHostContext",
|
|
13
|
+
"emitHostSignal",
|
|
12
14
|
"setContainer",
|
|
13
15
|
"setDefaultContainerPolicy",
|
|
14
16
|
"configure",
|
|
@@ -52,6 +54,16 @@ const identifyPayloadSchema = z.union([
|
|
|
52
54
|
options: z.optional(identifyOptionsSchema),
|
|
53
55
|
}),
|
|
54
56
|
]);
|
|
57
|
+
const hostContextSchema = z.strictObject({
|
|
58
|
+
url: z.string().check(z.url()),
|
|
59
|
+
env: z.optional(z.record(z.string(), z.string())),
|
|
60
|
+
storage: z.optional(z.record(z.string(), z.string())),
|
|
61
|
+
page: z.optional(z.strictObject({
|
|
62
|
+
path: z.optional(z.string()),
|
|
63
|
+
referrer: z.optional(z.string()),
|
|
64
|
+
search: z.optional(z.string()),
|
|
65
|
+
})),
|
|
66
|
+
});
|
|
55
67
|
// TODO(migration): After telemetry confirms no remaining `open.containerRequirement` in host command queues, remove the optional field and transform so unknown keys fail under `.strict()` again.
|
|
56
68
|
// [from=legacy-open-container-requirement] [to=strict-public-open-only] [scope=slice] [priority=low] [impact=medium] [risk=low]
|
|
57
69
|
const legacyOpenContainerRequirementSchema = z.enum(["any", "hostOnly"]);
|
|
@@ -88,6 +100,15 @@ export const publicCommandPayloadSchema = z.union([
|
|
|
88
100
|
flowHandleId: z.optional(flowHandleIdSchema),
|
|
89
101
|
hideCloseButton: z.optional(z.boolean()),
|
|
90
102
|
}),
|
|
103
|
+
z.strictObject({
|
|
104
|
+
kind: z.literal("updateHostContext"),
|
|
105
|
+
context: hostContextSchema,
|
|
106
|
+
}),
|
|
107
|
+
z.strictObject({
|
|
108
|
+
kind: z.literal("emitHostSignal"),
|
|
109
|
+
name: z.string().check(z.trim(), z.minLength(1)),
|
|
110
|
+
data: z.optional(z.unknown()),
|
|
111
|
+
}),
|
|
91
112
|
z.strictObject({
|
|
92
113
|
kind: z.literal("setContainer"),
|
|
93
114
|
flowHandleId: flowHandleIdSchema,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getuserfeedback/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "getuserfeedback widget protocol — host surface and (later) wire protocol",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"getuserfeedback",
|
|
@@ -88,6 +88,12 @@
|
|
|
88
88
|
"import": "./dist/host/lazy-handle-client.js",
|
|
89
89
|
"default": "./dist/host/lazy-handle-client.js"
|
|
90
90
|
},
|
|
91
|
+
"./webview-transport": {
|
|
92
|
+
"types": "./dist/webview-transport.d.ts",
|
|
93
|
+
"bun": "./src/webview-transport.ts",
|
|
94
|
+
"import": "./dist/webview-transport.js",
|
|
95
|
+
"default": "./dist/webview-transport.js"
|
|
96
|
+
},
|
|
91
97
|
"./version-resolution": {
|
|
92
98
|
"types": "./dist/version-resolution.d.ts",
|
|
93
99
|
"bun": "./src/version-resolution.ts",
|
package/src/index.ts
CHANGED
|
@@ -100,6 +100,18 @@ export {
|
|
|
100
100
|
type ThemeVersionResolution,
|
|
101
101
|
themeVersionResolutionSchema,
|
|
102
102
|
} from "./version-resolution.js";
|
|
103
|
+
export {
|
|
104
|
+
parseWebViewTransportNativeMessage,
|
|
105
|
+
parseWebViewTransportWebMessage,
|
|
106
|
+
type WebViewCommandEnvelope,
|
|
107
|
+
type WebViewTransportHostEvent,
|
|
108
|
+
type WebViewTransportNativeMessage,
|
|
109
|
+
type WebViewTransportWebMessage,
|
|
110
|
+
webViewCommandEnvelopeSchema,
|
|
111
|
+
webViewTransportHostEventSchema,
|
|
112
|
+
webViewTransportNativeMessageSchema,
|
|
113
|
+
webViewTransportWebMessageSchema,
|
|
114
|
+
} from "./webview-transport.js";
|
|
103
115
|
export {
|
|
104
116
|
type PublicCommandPayload,
|
|
105
117
|
parseConfigureOptions,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import type { FlowDismissedPayload, FlowViewedPayload } from "./host";
|
|
3
3
|
import { createCommandEnvelope } from "./host";
|
|
4
|
+
import type { WebViewTransportWebMessage } from "./index";
|
|
4
5
|
import {
|
|
5
6
|
appEventCapabilitySchema,
|
|
6
7
|
appEventFlagSchema,
|
|
@@ -18,6 +19,8 @@ import {
|
|
|
18
19
|
parseConfigureOptions,
|
|
19
20
|
parseInitOptions,
|
|
20
21
|
parsePublicCommand,
|
|
22
|
+
parseWebViewTransportNativeMessage,
|
|
23
|
+
parseWebViewTransportWebMessage,
|
|
21
24
|
publicGrantScopeIdSchema,
|
|
22
25
|
publicWidgetCommandKindSchema,
|
|
23
26
|
SCOPE_IDS,
|
|
@@ -161,6 +164,40 @@ describe("@getuserfeedback/protocol root contract", () => {
|
|
|
161
164
|
});
|
|
162
165
|
});
|
|
163
166
|
|
|
167
|
+
it("parses public embedded host context and signal commands from the root contract", () => {
|
|
168
|
+
expect(
|
|
169
|
+
parsePublicCommand({
|
|
170
|
+
kind: "updateHostContext",
|
|
171
|
+
context: {
|
|
172
|
+
url: "app://screen/checkout",
|
|
173
|
+
page: { path: "/checkout" },
|
|
174
|
+
env: { locale: "en-US" },
|
|
175
|
+
storage: { cartId: "cart_123" },
|
|
176
|
+
},
|
|
177
|
+
}),
|
|
178
|
+
).toEqual({
|
|
179
|
+
kind: "updateHostContext",
|
|
180
|
+
context: {
|
|
181
|
+
url: "app://screen/checkout",
|
|
182
|
+
page: { path: "/checkout" },
|
|
183
|
+
env: { locale: "en-US" },
|
|
184
|
+
storage: { cartId: "cart_123" },
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
expect(
|
|
189
|
+
parsePublicCommand({
|
|
190
|
+
kind: "emitHostSignal",
|
|
191
|
+
name: "screenFocused",
|
|
192
|
+
data: { path: "/checkout" },
|
|
193
|
+
}),
|
|
194
|
+
).toEqual({
|
|
195
|
+
kind: "emitHostSignal",
|
|
196
|
+
name: "screenFocused",
|
|
197
|
+
data: { path: "/checkout" },
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
164
201
|
it("parses theme resolution payloads with optional runtime artifacts", () => {
|
|
165
202
|
expect(
|
|
166
203
|
themeVersionResolutionSchema.parse({
|
|
@@ -304,6 +341,192 @@ describe("@getuserfeedback/protocol root contract", () => {
|
|
|
304
341
|
).toThrow();
|
|
305
342
|
});
|
|
306
343
|
|
|
344
|
+
it("parses WebView transport queue and host-event messages from the root contract", () => {
|
|
345
|
+
const envelope = {
|
|
346
|
+
version: "1",
|
|
347
|
+
requestId: "request_123",
|
|
348
|
+
idempotencyKey: "request_123",
|
|
349
|
+
instanceId: "instance_123",
|
|
350
|
+
clientMeta: {
|
|
351
|
+
loader: "custom",
|
|
352
|
+
clientName: "@getuserfeedback/react-native",
|
|
353
|
+
transport: "loader",
|
|
354
|
+
},
|
|
355
|
+
command: {
|
|
356
|
+
kind: "updateHostContext",
|
|
357
|
+
context: { url: "app://screen/checkout" },
|
|
358
|
+
},
|
|
359
|
+
} as const;
|
|
360
|
+
|
|
361
|
+
expect(
|
|
362
|
+
parseWebViewTransportNativeMessage({
|
|
363
|
+
kind: "enqueueCommand",
|
|
364
|
+
id: "webview_message_1",
|
|
365
|
+
envelope,
|
|
366
|
+
}),
|
|
367
|
+
).toEqual({
|
|
368
|
+
kind: "enqueueCommand",
|
|
369
|
+
id: "webview_message_1",
|
|
370
|
+
envelope,
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
expect(
|
|
374
|
+
parseWebViewTransportWebMessage({
|
|
375
|
+
kind: "hostEvent",
|
|
376
|
+
event: {
|
|
377
|
+
name: "instance:flow:state-changed",
|
|
378
|
+
detail: {
|
|
379
|
+
instanceId: "instance_123",
|
|
380
|
+
isOpen: true,
|
|
381
|
+
isLoading: false,
|
|
382
|
+
width: 320,
|
|
383
|
+
height: 480,
|
|
384
|
+
flowHandleId: "handle_123",
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
}),
|
|
388
|
+
).toEqual({
|
|
389
|
+
kind: "hostEvent",
|
|
390
|
+
event: {
|
|
391
|
+
name: "instance:flow:state-changed",
|
|
392
|
+
detail: {
|
|
393
|
+
instanceId: "instance_123",
|
|
394
|
+
isOpen: true,
|
|
395
|
+
isLoading: false,
|
|
396
|
+
width: 320,
|
|
397
|
+
height: 480,
|
|
398
|
+
flowHandleId: "handle_123",
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
expect(
|
|
404
|
+
parseWebViewTransportWebMessage({
|
|
405
|
+
kind: "hostEvent",
|
|
406
|
+
event: {
|
|
407
|
+
name: "instance:command:settled",
|
|
408
|
+
detail: {
|
|
409
|
+
requestId: "request_123",
|
|
410
|
+
instanceId: "instance_123",
|
|
411
|
+
kind: "updateHostContext",
|
|
412
|
+
ok: true,
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
}),
|
|
416
|
+
).toEqual({
|
|
417
|
+
kind: "hostEvent",
|
|
418
|
+
event: {
|
|
419
|
+
name: "instance:command:settled",
|
|
420
|
+
detail: {
|
|
421
|
+
requestId: "request_123",
|
|
422
|
+
instanceId: "instance_123",
|
|
423
|
+
kind: "updateHostContext",
|
|
424
|
+
ok: true,
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
expect(
|
|
430
|
+
parseWebViewTransportWebMessage({
|
|
431
|
+
kind: "hostEvent",
|
|
432
|
+
event: {
|
|
433
|
+
name: "instance:command:settled",
|
|
434
|
+
detail: {
|
|
435
|
+
requestId: "request_456",
|
|
436
|
+
instanceId: "instance_123",
|
|
437
|
+
kind: "open",
|
|
438
|
+
ok: false,
|
|
439
|
+
error: {
|
|
440
|
+
message: "Command failed",
|
|
441
|
+
code: "COMMAND_FAILED",
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
},
|
|
445
|
+
}),
|
|
446
|
+
).toEqual({
|
|
447
|
+
kind: "hostEvent",
|
|
448
|
+
event: {
|
|
449
|
+
name: "instance:command:settled",
|
|
450
|
+
detail: {
|
|
451
|
+
requestId: "request_456",
|
|
452
|
+
instanceId: "instance_123",
|
|
453
|
+
kind: "open",
|
|
454
|
+
ok: false,
|
|
455
|
+
error: {
|
|
456
|
+
message: "Command failed",
|
|
457
|
+
code: "COMMAND_FAILED",
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
it("rejects WebView transport commands and settlements with invalid state", () => {
|
|
465
|
+
expect(() =>
|
|
466
|
+
parseWebViewTransportNativeMessage({
|
|
467
|
+
kind: "enqueueCommand",
|
|
468
|
+
id: "webview_message_1",
|
|
469
|
+
envelope: {
|
|
470
|
+
version: "1",
|
|
471
|
+
requestId: "request_123",
|
|
472
|
+
idempotencyKey: "request_123",
|
|
473
|
+
command: { kind: "close" },
|
|
474
|
+
},
|
|
475
|
+
}),
|
|
476
|
+
).toThrow();
|
|
477
|
+
|
|
478
|
+
expect(() =>
|
|
479
|
+
parseWebViewTransportWebMessage({
|
|
480
|
+
kind: "hostEvent",
|
|
481
|
+
event: {
|
|
482
|
+
name: "instance:command:settled",
|
|
483
|
+
detail: {
|
|
484
|
+
requestId: "request_123",
|
|
485
|
+
instanceId: "instance_123",
|
|
486
|
+
kind: "open",
|
|
487
|
+
ok: false,
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
}),
|
|
491
|
+
).toThrow();
|
|
492
|
+
|
|
493
|
+
expect(() =>
|
|
494
|
+
parseWebViewTransportWebMessage({
|
|
495
|
+
kind: "hostEvent",
|
|
496
|
+
event: {
|
|
497
|
+
name: "instance:command:settled",
|
|
498
|
+
detail: {
|
|
499
|
+
requestId: "request_456",
|
|
500
|
+
instanceId: "instance_123",
|
|
501
|
+
kind: "open",
|
|
502
|
+
ok: true,
|
|
503
|
+
error: {
|
|
504
|
+
message: "Should not be present",
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
}),
|
|
509
|
+
).toThrow();
|
|
510
|
+
|
|
511
|
+
const blankReasonMessage = {
|
|
512
|
+
kind: "hostEvent",
|
|
513
|
+
event: {
|
|
514
|
+
name: "instance:handle:invalidated",
|
|
515
|
+
detail: {
|
|
516
|
+
instanceId: "instance_123",
|
|
517
|
+
handleKind: "survey",
|
|
518
|
+
handleId: "handle_123",
|
|
519
|
+
reasonCode: "STALE_HANDLE",
|
|
520
|
+
reasonMessage: " ",
|
|
521
|
+
source: "loader",
|
|
522
|
+
at: 1_735_689_600_000,
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
} satisfies WebViewTransportWebMessage;
|
|
526
|
+
|
|
527
|
+
expect(() => parseWebViewTransportWebMessage(blankReasonMessage)).toThrow();
|
|
528
|
+
});
|
|
529
|
+
|
|
307
530
|
it("parses public API flow and theme version resolution payloads", () => {
|
|
308
531
|
expect(
|
|
309
532
|
flowVersionResolutionSchema.parse({ flowVersionId: "fv_abc" }),
|