@getuserfeedback/protocol 0.10.2 → 1.1.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 @@
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,8 +2,8 @@ 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
8
  updateHostContext: "updateHostContext";
9
9
  emitHostSignal: "emitHostSignal";
@@ -105,14 +105,14 @@ export declare const publicCommandPayloadSchema: z.ZodMiniUnion<readonly [z.ZodM
105
105
  denied: "denied";
106
106
  revoked: "revoked";
107
107
  }>, z.ZodMiniArray<z.ZodMiniEnum<{
108
- "functionality.storage": "functionality.storage";
109
- "security.storage": "security.storage";
110
108
  "analytics.storage": "analytics.storage";
111
109
  "analytics.measurement": "analytics.measurement";
112
110
  "personalization.storage": "personalization.storage";
113
111
  "ads.storage": "ads.storage";
114
112
  "ads.user_data": "ads.user_data";
115
113
  "ads.personalization": "ads.personalization";
114
+ "functionality.storage": "functionality.storage";
115
+ "security.storage": "security.storage";
116
116
  }>>]>>;
117
117
  auth: z.ZodMiniOptional<z.ZodMiniObject<{
118
118
  jwt: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniObject<{
@@ -139,14 +139,14 @@ export declare const publicCommandPayloadSchema: z.ZodMiniUnion<readonly [z.ZodM
139
139
  denied: "denied";
140
140
  revoked: "revoked";
141
141
  }>, z.ZodMiniArray<z.ZodMiniEnum<{
142
- "functionality.storage": "functionality.storage";
143
- "security.storage": "security.storage";
144
142
  "analytics.storage": "analytics.storage";
145
143
  "analytics.measurement": "analytics.measurement";
146
144
  "personalization.storage": "personalization.storage";
147
145
  "ads.storage": "ads.storage";
148
146
  "ads.user_data": "ads.user_data";
149
147
  "ads.personalization": "ads.personalization";
148
+ "functionality.storage": "functionality.storage";
149
+ "security.storage": "security.storage";
150
150
  }>>]>>;
151
151
  clientMeta: z.ZodMiniOptional<z.ZodMiniObject<{
152
152
  loader: z.ZodMiniEnum<{
@@ -17,14 +17,14 @@ export declare const consentConfigInputSchema: z.ZodMiniUnion<readonly [z.ZodMin
17
17
  denied: "denied";
18
18
  revoked: "revoked";
19
19
  }>, z.ZodMiniArray<z.ZodMiniEnum<{
20
- "functionality.storage": "functionality.storage";
21
- "security.storage": "security.storage";
22
20
  "analytics.storage": "analytics.storage";
23
21
  "analytics.measurement": "analytics.measurement";
24
22
  "personalization.storage": "personalization.storage";
25
23
  "ads.storage": "ads.storage";
26
24
  "ads.user_data": "ads.user_data";
27
25
  "ads.personalization": "ads.personalization";
26
+ "functionality.storage": "functionality.storage";
27
+ "security.storage": "security.storage";
28
28
  }>>]>;
29
29
  export type ConsentConfigInput = z.output<typeof consentConfigInputSchema>;
30
30
  export declare const debugInputSchema: z.ZodMiniUnion<readonly [z.ZodMiniBoolean<boolean>, z.ZodMiniString<string>, z.ZodMiniArray<z.ZodMiniString<string>>]>;
@@ -144,14 +144,14 @@ export declare const configureOptionsSchema: z.ZodMiniObject<{
144
144
  denied: "denied";
145
145
  revoked: "revoked";
146
146
  }>, z.ZodMiniArray<z.ZodMiniEnum<{
147
- "functionality.storage": "functionality.storage";
148
- "security.storage": "security.storage";
149
147
  "analytics.storage": "analytics.storage";
150
148
  "analytics.measurement": "analytics.measurement";
151
149
  "personalization.storage": "personalization.storage";
152
150
  "ads.storage": "ads.storage";
153
151
  "ads.user_data": "ads.user_data";
154
152
  "ads.personalization": "ads.personalization";
153
+ "functionality.storage": "functionality.storage";
154
+ "security.storage": "security.storage";
155
155
  }>>]>>;
156
156
  auth: z.ZodMiniOptional<z.ZodMiniObject<{
157
157
  jwt: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniObject<{
@@ -177,14 +177,14 @@ export declare const initOptionsSchema: z.ZodMiniObject<{
177
177
  denied: "denied";
178
178
  revoked: "revoked";
179
179
  }>, z.ZodMiniArray<z.ZodMiniEnum<{
180
- "functionality.storage": "functionality.storage";
181
- "security.storage": "security.storage";
182
180
  "analytics.storage": "analytics.storage";
183
181
  "analytics.measurement": "analytics.measurement";
184
182
  "personalization.storage": "personalization.storage";
185
183
  "ads.storage": "ads.storage";
186
184
  "ads.user_data": "ads.user_data";
187
185
  "ads.personalization": "ads.personalization";
186
+ "functionality.storage": "functionality.storage";
187
+ "security.storage": "security.storage";
188
188
  }>>]>>;
189
189
  clientMeta: z.ZodMiniOptional<z.ZodMiniObject<{
190
190
  loader: z.ZodMiniEnum<{
@@ -326,14 +326,14 @@ export declare const instanceUpdateableConfigSchema: z.ZodMiniObject<{
326
326
  denied: "denied";
327
327
  revoked: "revoked";
328
328
  }>, z.ZodMiniArray<z.ZodMiniEnum<{
329
- "functionality.storage": "functionality.storage";
330
- "security.storage": "security.storage";
331
329
  "analytics.storage": "analytics.storage";
332
330
  "analytics.measurement": "analytics.measurement";
333
331
  "personalization.storage": "personalization.storage";
334
332
  "ads.storage": "ads.storage";
335
333
  "ads.user_data": "ads.user_data";
336
334
  "ads.personalization": "ads.personalization";
335
+ "functionality.storage": "functionality.storage";
336
+ "security.storage": "security.storage";
337
337
  }>>]>>;
338
338
  auth: z.ZodMiniOptional<z.ZodMiniObject<{
339
339
  jwt: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniObject<{
@@ -378,14 +378,14 @@ export declare const coreInstanceConfigSchema: z.ZodMiniObject<{
378
378
  denied: "denied";
379
379
  revoked: "revoked";
380
380
  }>, z.ZodMiniArray<z.ZodMiniEnum<{
381
- "functionality.storage": "functionality.storage";
382
- "security.storage": "security.storage";
383
381
  "analytics.storage": "analytics.storage";
384
382
  "analytics.measurement": "analytics.measurement";
385
383
  "personalization.storage": "personalization.storage";
386
384
  "ads.storage": "ads.storage";
387
385
  "ads.user_data": "ads.user_data";
388
386
  "ads.personalization": "ads.personalization";
387
+ "functionality.storage": "functionality.storage";
388
+ "security.storage": "security.storage";
389
389
  }>>]>>;
390
390
  auth: z.ZodMiniOptional<z.ZodMiniObject<{
391
391
  jwt: z.ZodMiniOptional<z.ZodMiniUnion<readonly [z.ZodMiniObject<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getuserfeedback/protocol",
3
- "version": "0.10.2",
3
+ "version": "1.1.0",
4
4
  "description": "getuserfeedback widget protocol — host surface and (later) wire protocol",
5
5
  "keywords": [
6
6
  "getuserfeedback",
@@ -88,11 +88,11 @@
88
88
  "import": "./dist/host/lazy-handle-client.js",
89
89
  "default": "./dist/host/lazy-handle-client.js"
90
90
  },
91
- "./mobile-bridge": {
92
- "types": "./dist/mobile-bridge.d.ts",
93
- "bun": "./src/mobile-bridge.ts",
94
- "import": "./dist/mobile-bridge.js",
95
- "default": "./dist/mobile-bridge.js"
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
96
  },
97
97
  "./version-resolution": {
98
98
  "types": "./dist/version-resolution.d.ts",
package/src/index.ts CHANGED
@@ -69,14 +69,6 @@ export {
69
69
  isIdentityTypeValue,
70
70
  type StandardIdentityTypeValue,
71
71
  } from "./identity-type.js";
72
- export {
73
- type MobileBridgeNativeMessage,
74
- type MobileBridgeWebMessage,
75
- mobileBridgeNativeMessageSchema,
76
- mobileBridgeWebMessageSchema,
77
- parseMobileBridgeNativeMessage,
78
- parseMobileBridgeWebMessage,
79
- } from "./mobile-bridge.js";
80
72
  export {
81
73
  isPublicGrantScope,
82
74
  type PublicGrantScope,
@@ -108,6 +100,18 @@ export {
108
100
  type ThemeVersionResolution,
109
101
  themeVersionResolutionSchema,
110
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";
111
115
  export {
112
116
  type PublicCommandPayload,
113
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,
@@ -17,9 +18,9 @@ import {
17
18
  listScopes,
18
19
  parseConfigureOptions,
19
20
  parseInitOptions,
20
- parseMobileBridgeNativeMessage,
21
- parseMobileBridgeWebMessage,
22
21
  parsePublicCommand,
22
+ parseWebViewTransportNativeMessage,
23
+ parseWebViewTransportWebMessage,
23
24
  publicGrantScopeIdSchema,
24
25
  publicWidgetCommandKindSchema,
25
26
  SCOPE_IDS,
@@ -163,7 +164,7 @@ describe("@getuserfeedback/protocol root contract", () => {
163
164
  });
164
165
  });
165
166
 
166
- it("parses public mobile host context and signal commands from the root contract", () => {
167
+ it("parses public embedded host context and signal commands from the root contract", () => {
167
168
  expect(
168
169
  parsePublicCommand({
169
170
  kind: "updateHostContext",
@@ -197,7 +198,7 @@ describe("@getuserfeedback/protocol root contract", () => {
197
198
  });
198
199
  });
199
200
 
200
- it("parses theme resolution payloads with optional runtime artifacts", () => {
201
+ it("parses theme resolution payloads with runtime artifacts", () => {
201
202
  expect(
202
203
  themeVersionResolutionSchema.parse({
203
204
  themeRuntimeArtifactId: "thra_xyz",
@@ -340,101 +341,190 @@ describe("@getuserfeedback/protocol root contract", () => {
340
341
  ).toThrow();
341
342
  });
342
343
 
343
- it("parses mobile bridge native and web messages from the root contract", () => {
344
- expect(
345
- parseMobileBridgeNativeMessage({
346
- kind: "command",
347
- id: "native_message_1",
348
- command: {
349
- kind: "updateHostContext",
350
- context: { url: "app://screen/checkout" },
351
- },
352
- }),
353
- ).toEqual({
354
- kind: "command",
355
- id: "native_message_1",
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
+ },
356
355
  command: {
357
356
  kind: "updateHostContext",
358
357
  context: { url: "app://screen/checkout" },
359
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,
360
371
  });
361
372
 
362
373
  expect(
363
- parseMobileBridgeWebMessage({
364
- kind: "flowStateChanged",
365
- isOpen: true,
366
- isLoading: false,
367
- width: 320,
368
- height: 480,
369
- flowId: "sur_123",
370
- flowHandleId: "handle_123",
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
+ },
371
387
  }),
372
388
  ).toEqual({
373
- kind: "flowStateChanged",
374
- isOpen: true,
375
- isLoading: false,
376
- width: 320,
377
- height: 480,
378
- flowId: "sur_123",
379
- flowHandleId: "handle_123",
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
+ },
380
401
  });
381
402
 
382
403
  expect(
383
- parseMobileBridgeWebMessage({
384
- kind: "commandSettled",
385
- id: "native_message_1",
386
- requestId: "request_123",
387
- ok: true,
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
+ },
388
415
  }),
389
416
  ).toEqual({
390
- kind: "commandSettled",
391
- id: "native_message_1",
392
- requestId: "request_123",
393
- ok: true,
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
+ },
394
427
  });
395
428
 
396
429
  expect(
397
- parseMobileBridgeWebMessage({
398
- kind: "commandSettled",
399
- id: "native_message_2",
400
- requestId: "request_456",
401
- ok: false,
402
- error: {
403
- message: "Command failed",
404
- code: "COMMAND_FAILED",
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
+ },
405
444
  },
406
445
  }),
407
446
  ).toEqual({
408
- kind: "commandSettled",
409
- id: "native_message_2",
410
- requestId: "request_456",
411
- ok: false,
412
- error: {
413
- message: "Command failed",
414
- code: "COMMAND_FAILED",
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
+ },
415
460
  },
416
461
  });
417
462
  });
418
463
 
419
- it("rejects mobile bridge command settlements with mismatched success state", () => {
464
+ it("rejects WebView transport commands and settlements with invalid state", () => {
420
465
  expect(() =>
421
- parseMobileBridgeWebMessage({
422
- kind: "commandSettled",
423
- id: "native_message_1",
424
- ok: false,
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
+ },
425
475
  }),
426
476
  ).toThrow();
427
477
 
428
478
  expect(() =>
429
- parseMobileBridgeWebMessage({
430
- kind: "commandSettled",
431
- id: "native_message_2",
432
- ok: true,
433
- error: {
434
- message: "Should not be present",
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
+ },
435
489
  },
436
490
  }),
437
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();
438
528
  });
439
529
 
440
530
  it("parses public API flow and theme version resolution payloads", () => {
@@ -442,8 +532,17 @@ describe("@getuserfeedback/protocol root contract", () => {
442
532
  flowVersionResolutionSchema.parse({ flowVersionId: "fv_abc" }),
443
533
  ).toEqual({ flowVersionId: "fv_abc" });
444
534
  expect(
535
+ themeVersionResolutionSchema.parse({
536
+ themeRuntimeArtifactId: "thra_xyz",
537
+ themeVersionId: "tv_xyz",
538
+ }),
539
+ ).toEqual({
540
+ themeRuntimeArtifactId: "thra_xyz",
541
+ themeVersionId: "tv_xyz",
542
+ });
543
+ expect(() =>
445
544
  themeVersionResolutionSchema.parse({ themeVersionId: "tv_xyz" }),
446
- ).toEqual({ themeVersionId: "tv_xyz" });
545
+ ).toThrow();
447
546
  expect(() =>
448
547
  flowVersionResolutionSchema.parse({ flowVersionId: " " }),
449
548
  ).toThrow();
@@ -7,7 +7,7 @@ export const flowVersionResolutionSchema = z.object({
7
7
  });
8
8
 
9
9
  export const themeVersionResolutionSchema = z.object({
10
- themeRuntimeArtifactId: z.optional(versionIdSchema),
10
+ themeRuntimeArtifactId: versionIdSchema,
11
11
  themeVersionId: versionIdSchema,
12
12
  });
13
13