@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.
@@ -0,0 +1,196 @@
1
+ import * as z from "zod/mini";
2
+ import { appEventPayloadSchema } from "./app-event.js";
3
+ import { clientMetaSchema } from "./client-meta.js";
4
+ import {
5
+ HANDLE_INVALIDATED_REASON_CODES,
6
+ HANDLE_INVALIDATED_SOURCES,
7
+ } from "./host/host-event-contract.js";
8
+ import { publicCommandPayloadSchema } from "./widget-commands.js";
9
+
10
+ const nonEmptyStringSchema = z.string().check(z.trim(), z.minLength(1));
11
+ const transportMessageIdSchema = nonEmptyStringSchema;
12
+ const commandErrorSchema = z.strictObject({
13
+ message: nonEmptyStringSchema,
14
+ code: z.optional(nonEmptyStringSchema),
15
+ });
16
+
17
+ export const webViewCommandEnvelopeSchema = z.object({
18
+ version: z.literal("1"),
19
+ instanceId: nonEmptyStringSchema,
20
+ requestId: nonEmptyStringSchema,
21
+ idempotencyKey: nonEmptyStringSchema,
22
+ clientMeta: z.optional(clientMetaSchema),
23
+ command: publicCommandPayloadSchema,
24
+ });
25
+
26
+ const commandSettledDetailBase = {
27
+ requestId: nonEmptyStringSchema,
28
+ instanceId: z.nullable(nonEmptyStringSchema),
29
+ kind: nonEmptyStringSchema,
30
+ } as const;
31
+
32
+ const commandSettledDetailSchema = z.discriminatedUnion("ok", [
33
+ z.strictObject({
34
+ ...commandSettledDetailBase,
35
+ ok: z.literal(true),
36
+ result: z.optional(z.unknown()),
37
+ }),
38
+ z.strictObject({
39
+ ...commandSettledDetailBase,
40
+ ok: z.literal(false),
41
+ error: commandErrorSchema,
42
+ }),
43
+ ]);
44
+
45
+ const flowStateBase = {
46
+ instanceId: nonEmptyStringSchema,
47
+ isOpen: z.boolean(),
48
+ isLoading: z.boolean(),
49
+ width: z.optional(z.number()),
50
+ height: z.optional(z.number()),
51
+ } as const;
52
+
53
+ const flowStateChangedDetailSchema = z.strictObject({
54
+ ...flowStateBase,
55
+ flowHandleId: nonEmptyStringSchema,
56
+ });
57
+
58
+ const instanceFlowStateChangedDetailSchema = z.strictObject(flowStateBase);
59
+
60
+ const openRequestedDetailSchema = z.strictObject({
61
+ instanceId: nonEmptyStringSchema,
62
+ source: z.enum(["command", "targeting"]),
63
+ flowId: nonEmptyStringSchema,
64
+ flowHandleId: z.optional(nonEmptyStringSchema),
65
+ hideCloseButton: z.optional(z.boolean()),
66
+ });
67
+
68
+ const handleInvalidatedDetailSchema = z.strictObject({
69
+ instanceId: nonEmptyStringSchema,
70
+ handleKind: nonEmptyStringSchema,
71
+ handleId: nonEmptyStringSchema,
72
+ reasonCode: z.enum(HANDLE_INVALIDATED_REASON_CODES),
73
+ reasonMessage: z.optional(nonEmptyStringSchema),
74
+ relatedRequestId: z.optional(nonEmptyStringSchema),
75
+ source: z.enum(HANDLE_INVALIDATED_SOURCES),
76
+ at: z.number(),
77
+ });
78
+
79
+ const unsupportedCommandDetailSchema = z.strictObject({
80
+ requestId: nonEmptyStringSchema,
81
+ instanceId: z.nullable(nonEmptyStringSchema),
82
+ kind: nonEmptyStringSchema,
83
+ code: z.literal("UNSUPPORTED_COMMAND"),
84
+ message: nonEmptyStringSchema,
85
+ issues: z.optional(z.array(z.string())),
86
+ });
87
+
88
+ const instanceErrorDetailSchema = z.strictObject({
89
+ instanceId: nonEmptyStringSchema,
90
+ code: nonEmptyStringSchema,
91
+ details: z.optional(z.unknown()),
92
+ recovery: z.optional(z.unknown()),
93
+ at: z.number(),
94
+ });
95
+
96
+ const loaderErrorDetailSchema = z.strictObject({
97
+ code: nonEmptyStringSchema,
98
+ details: z.optional(z.unknown()),
99
+ recovery: z.optional(z.unknown()),
100
+ at: z.number(),
101
+ });
102
+
103
+ const appEventDetailSchema = z.strictObject({
104
+ instanceId: nonEmptyStringSchema,
105
+ payload: appEventPayloadSchema,
106
+ });
107
+
108
+ export const webViewTransportNativeMessageSchema = z.discriminatedUnion(
109
+ "kind",
110
+ [
111
+ z.strictObject({
112
+ kind: z.literal("enqueueCommand"),
113
+ id: transportMessageIdSchema,
114
+ envelope: webViewCommandEnvelopeSchema,
115
+ }),
116
+ ],
117
+ );
118
+
119
+ export const webViewTransportHostEventSchema = z.discriminatedUnion("name", [
120
+ z.strictObject({
121
+ name: z.literal("instance:command:settled"),
122
+ detail: commandSettledDetailSchema,
123
+ }),
124
+ z.strictObject({
125
+ name: z.literal("instance:flow:state-changed"),
126
+ detail: flowStateChangedDetailSchema,
127
+ }),
128
+ z.strictObject({
129
+ name: z.literal("instance:flow:state-changed:instance"),
130
+ detail: instanceFlowStateChangedDetailSchema,
131
+ }),
132
+ z.strictObject({
133
+ name: z.literal("instance:open:requested"),
134
+ detail: openRequestedDetailSchema,
135
+ }),
136
+ z.strictObject({
137
+ name: z.literal("instance:handle:invalidated"),
138
+ detail: handleInvalidatedDetailSchema,
139
+ }),
140
+ z.strictObject({
141
+ name: z.literal("instance:command:unsupported"),
142
+ detail: unsupportedCommandDetailSchema,
143
+ }),
144
+ z.strictObject({
145
+ name: z.literal("instance:error"),
146
+ detail: instanceErrorDetailSchema,
147
+ }),
148
+ z.strictObject({
149
+ name: z.literal("instance:app-event"),
150
+ detail: appEventDetailSchema,
151
+ }),
152
+ z.strictObject({
153
+ name: z.literal("loader:error"),
154
+ detail: loaderErrorDetailSchema,
155
+ }),
156
+ ]);
157
+
158
+ export const webViewTransportWebMessageSchema = z.discriminatedUnion("kind", [
159
+ z.strictObject({
160
+ kind: z.literal("ready"),
161
+ instanceId: z.optional(nonEmptyStringSchema),
162
+ }),
163
+ z.strictObject({
164
+ kind: z.literal("hostEvent"),
165
+ event: webViewTransportHostEventSchema,
166
+ }),
167
+ z.strictObject({
168
+ kind: z.literal("error"),
169
+ error: commandErrorSchema,
170
+ }),
171
+ ]);
172
+
173
+ export type WebViewCommandEnvelope = z.output<
174
+ typeof webViewCommandEnvelopeSchema
175
+ >;
176
+ export type WebViewTransportNativeMessage = z.output<
177
+ typeof webViewTransportNativeMessageSchema
178
+ >;
179
+ export type WebViewTransportHostEvent = z.output<
180
+ typeof webViewTransportHostEventSchema
181
+ >;
182
+ export type WebViewTransportWebMessage = z.output<
183
+ typeof webViewTransportWebMessageSchema
184
+ >;
185
+
186
+ export function parseWebViewTransportNativeMessage(
187
+ input: unknown,
188
+ ): WebViewTransportNativeMessage {
189
+ return webViewTransportNativeMessageSchema.parse(input);
190
+ }
191
+
192
+ export function parseWebViewTransportWebMessage(
193
+ input: unknown,
194
+ ): WebViewTransportWebMessage {
195
+ return webViewTransportWebMessageSchema.parse(input);
196
+ }
@@ -19,6 +19,8 @@ const publicWidgetCommandKinds = [
19
19
  "prefetch",
20
20
  "prerender",
21
21
  "identify",
22
+ "updateHostContext",
23
+ "emitHostSignal",
22
24
  "setContainer",
23
25
  "setDefaultContainerPolicy",
24
26
  "configure",
@@ -72,6 +74,19 @@ const identifyPayloadSchema = z.union([
72
74
  }),
73
75
  ]);
74
76
 
77
+ const hostContextSchema = z.strictObject({
78
+ url: z.string().check(z.url()),
79
+ env: z.optional(z.record(z.string(), z.string())),
80
+ storage: z.optional(z.record(z.string(), z.string())),
81
+ page: z.optional(
82
+ z.strictObject({
83
+ path: z.optional(z.string()),
84
+ referrer: z.optional(z.string()),
85
+ search: z.optional(z.string()),
86
+ }),
87
+ ),
88
+ });
89
+
75
90
  // 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.
76
91
  // [from=legacy-open-container-requirement] [to=strict-public-open-only] [scope=slice] [priority=low] [impact=medium] [risk=low]
77
92
  const legacyOpenContainerRequirementSchema = z.enum(["any", "hostOnly"]);
@@ -114,6 +129,15 @@ export const publicCommandPayloadSchema = z.union([
114
129
  flowHandleId: z.optional(flowHandleIdSchema),
115
130
  hideCloseButton: z.optional(z.boolean()),
116
131
  }),
132
+ z.strictObject({
133
+ kind: z.literal("updateHostContext"),
134
+ context: hostContextSchema,
135
+ }),
136
+ z.strictObject({
137
+ kind: z.literal("emitHostSignal"),
138
+ name: z.string().check(z.trim(), z.minLength(1)),
139
+ data: z.optional(z.unknown()),
140
+ }),
117
141
  z.strictObject({
118
142
  kind: z.literal("setContainer"),
119
143
  flowHandleId: flowHandleIdSchema,