@getuserfeedback/react-native 1.3.4 → 1.3.6

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 CHANGED
@@ -1,7 +1,8 @@
1
1
  import type { AppEventExternalId, AppEventJsonValue, PublicCommandPayload } from "@getuserfeedback/protocol";
2
- import type { WebViewCommandEnvelope, WebViewTransportHostEvent, WebViewTransportNativeMessage, WebViewTransportWebMessage } from "@getuserfeedback/protocol/webview-transport";
2
+ import type { WebViewCommandEnvelope, WebViewTransportNativeMessage, WebViewTransportWebMessage } from "@getuserfeedback/protocol/webview-transport";
3
3
  import type { ConfigureOptions, InitOptions } from "@getuserfeedback/sdk";
4
4
  import { type ReactElement, type ReactNode } from "react";
5
+ import { type CommandSettledEvent, type CommandSettledSuccessDetail } from "./provider-command-helpers.js";
5
6
  import { WidgetHost, type WidgetHostProps, type WidgetHostSource } from "./widget-host.js";
6
7
  export { REACT_NATIVE_SDK_VERSION } from "./version.js";
7
8
  export { buildNativeMessageInjectionScript, buildWebViewBridgeScript, } from "./webview-bridge-script.js";
@@ -29,12 +30,6 @@ type PrerenderOptions = Pick<PrerenderCommand, "hideCloseButton">;
29
30
  type HostContext = Extract<PublicCommandPayload, {
30
31
  kind: "updateHostContext";
31
32
  }>["context"];
32
- type CommandSettledEvent = Extract<WebViewTransportHostEvent, {
33
- name: "instance:command:settled";
34
- }>;
35
- type CommandSettledSuccessDetail = Extract<CommandSettledEvent["detail"], {
36
- ok: true;
37
- }>;
38
33
  export interface ReactNativeCommandResult {
39
34
  envelope: ReactNativeWidgetCommandEnvelope;
40
35
  message: ReactNativeWidgetNativeMessage;
package/dist/index.js CHANGED
@@ -9,10 +9,12 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { assertNoSegmentExternalIdsInValueArgument, CommandSettlementError, CommandSettlementTimeoutError, } from "@getuserfeedback/protocol/host";
12
+ import { assertNoSegmentExternalIdsInValueArgument, CommandSettlementTimeoutError, } from "@getuserfeedback/protocol/host";
13
13
  import { parseWebViewTransportNativeMessage } from "@getuserfeedback/protocol/webview-transport";
14
14
  import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
15
15
  import { useResolvedHostViewport } from "./host-viewport.js";
16
+ import { toCommandFlowId, toCommandSettlementError, toRetryWithoutFlowHandle, } from "./provider-command-helpers.js";
17
+ import { clearProviderFlowHandleCache, createProviderFlowHandleCache, deleteCachedProviderFlowHandleId, deleteCachedProviderFlowId, getCachedProviderFlowHandleId, snapshotProviderFlowCommand, writeSettledProviderFlowHandleId, } from "./provider-flow-handle-cache.js";
16
18
  import { applyProviderOverlayFlowState, applyProviderOverlayHandleInvalidated, createDefaultProviderOverlayState, DEFAULT_PROVIDER_OVERLAY_VISIBILITY, updateProviderOverlayReadySequence, } from "./provider-overlay-state.js";
17
19
  import { ProviderWidgetHostOverlay, toProviderWidgetHostStyle, toProviderWidgetSheetHostViewport, } from "./provider-widget-host-overlay.js";
18
20
  import { REACT_NATIVE_SDK_VERSION } from "./version.js";
@@ -104,42 +106,6 @@ const toIdentifyCommandPayload = (identifyInput, traitsOrOptions, options) => {
104
106
  options: options !== null && options !== void 0 ? options : traitsOrOptions,
105
107
  };
106
108
  };
107
- const isFlowCommand = (command) => command.kind === "open" ||
108
- command.kind === "prefetch" ||
109
- command.kind === "prerender";
110
- const toSettledFlowHandleId = (result) => {
111
- if (typeof result !== "object" || result === null) {
112
- return undefined;
113
- }
114
- const flowHandleId = result.flowHandleId;
115
- return typeof flowHandleId === "string" && flowHandleId.trim().length > 0
116
- ? flowHandleId
117
- : undefined;
118
- };
119
- const toCommandFlowId = (command) => {
120
- if (!isFlowCommand(command)) {
121
- return undefined;
122
- }
123
- return command.flowId;
124
- };
125
- const toRetryWithoutFlowHandle = (command) => {
126
- if (!isFlowCommand(command) || command.flowHandleId === undefined) {
127
- return null;
128
- }
129
- return Object.assign(Object.assign({}, command), { flowHandleId: undefined });
130
- };
131
- const toCommandSettlementError = (event) => {
132
- if (event.detail.ok) {
133
- return null;
134
- }
135
- return new CommandSettlementError({
136
- code: event.detail.error.code,
137
- instanceId: event.detail.instanceId,
138
- kind: event.detail.kind,
139
- message: event.detail.error.message,
140
- requestId: event.detail.requestId,
141
- });
142
- };
143
109
  export function GetUserFeedbackProvider({ autoInit = true, children, commandTimeoutMs = DEFAULT_COMMAND_TIMEOUT_MS, configureOptions, hostViewport, instanceId: instanceIdProp, initOptions, loaderUrl, onCommandError, onInvalidWebMessage, onWebMessage, source, webViewComponent, webViewProps, }) {
144
110
  var _a, _b;
145
111
  const NativeView = useMemo(resolveNativeView, []);
@@ -150,9 +116,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
150
116
  const nativeMessagesRef = useRef([]);
151
117
  const pendingCommandsRef = useRef(new Map());
152
118
  const hostEpochRef = useRef(0);
153
- const flowHandleCacheEpochRef = useRef(0);
154
- const flowHandleIdsByFlowIdRef = useRef(new Map());
155
- const flowCommandSequencesByFlowIdRef = useRef(new Map());
119
+ const flowHandleCacheRef = useRef(createProviderFlowHandleCache());
156
120
  const providerOverlayStateRef = useRef(createDefaultProviderOverlayState());
157
121
  const initOptionsRef = useRef(initOptions);
158
122
  const configureOptionsRef = useRef(configureOptions);
@@ -178,8 +142,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
178
142
  }
179
143
  lastReadyRef.current = null;
180
144
  startupQueuedKeyRef.current = null;
181
- flowHandleCacheEpochRef.current += 1;
182
- flowHandleIdsByFlowIdRef.current.clear();
145
+ clearProviderFlowHandleCache(flowHandleCacheRef.current);
183
146
  providerOverlayStateRef.current = createDefaultProviderOverlayState();
184
147
  }
185
148
  initOptionsRef.current = initOptions;
@@ -238,7 +201,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
238
201
  }
239
202
  }, []);
240
203
  const enqueueTransportCommand = useCallback((command, options, { isStartup = false, prioritizeStartup = false, } = {}) => {
241
- var _a, _b;
204
+ var _a;
242
205
  const requestId = createCommandRequestId();
243
206
  const idempotencyKey = (_a = normalizeOptionalString(options === null || options === void 0 ? void 0 : options.idempotencyKey)) !== null && _a !== void 0 ? _a : requestId;
244
207
  const envelope = {
@@ -268,17 +231,11 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
268
231
  envelope: validatedMessage.envelope,
269
232
  message: validatedMessage,
270
233
  };
271
- const flowId = toCommandFlowId(validatedMessage.envelope.command);
272
- const flowCommandSequence = flowId === undefined
273
- ? null
274
- : ((_b = flowCommandSequencesByFlowIdRef.current.get(flowId)) !== null && _b !== void 0 ? _b : 0) + 1;
275
- if (flowId !== undefined && flowCommandSequence !== null) {
276
- flowCommandSequencesByFlowIdRef.current.set(flowId, flowCommandSequence);
277
- }
234
+ const flowCommandSnapshot = snapshotProviderFlowCommand(flowHandleCacheRef.current, validatedMessage.envelope.command);
278
235
  const promise = new Promise((resolve, reject) => {
279
236
  pendingCommandsRef.current.set(requestId, {
280
- flowCommandSequence,
281
- flowHandleCacheEpoch: flowHandleCacheEpochRef.current,
237
+ flowCommandSequence: flowCommandSnapshot.flowCommandSequence,
238
+ flowHandleCacheEpoch: flowCommandSnapshot.flowHandleCacheEpoch,
282
239
  hostEpoch: null,
283
240
  isStartup,
284
241
  messageId: validatedMessage.id,
@@ -332,7 +289,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
332
289
  envelope: nextEnvelope,
333
290
  });
334
291
  const previousMessageId = pendingCommand.messageId;
335
- pendingCommand.flowHandleCacheEpoch = flowHandleCacheEpochRef.current;
292
+ pendingCommand.flowHandleCacheEpoch = flowHandleCacheRef.current.epoch;
336
293
  pendingCommand.hostEpoch = null;
337
294
  pendingCommand.messageId = nextMessage.id;
338
295
  pendingCommand.result = {
@@ -410,8 +367,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
410
367
  if (previous !== null || hasDeliveredPendingCommands) {
411
368
  hostEpochRef.current += 1;
412
369
  startupQueuedKeyRef.current = null;
413
- flowHandleCacheEpochRef.current += 1;
414
- flowHandleIdsByFlowIdRef.current.clear();
370
+ clearProviderFlowHandleCache(flowHandleCacheRef.current);
415
371
  didReissueDeliveredCommands = reissueDeliveredPendingActionCommands();
416
372
  }
417
373
  lastReadyRef.current = Object.assign(Object.assign({}, event), { hostEpoch: hostEpochRef.current });
@@ -446,8 +402,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
446
402
  if (lastReadyRef.current !== null) {
447
403
  hostEpochRef.current += 1;
448
404
  startupQueuedKeyRef.current = null;
449
- flowHandleCacheEpochRef.current += 1;
450
- flowHandleIdsByFlowIdRef.current.clear();
405
+ clearProviderFlowHandleCache(flowHandleCacheRef.current);
451
406
  reissueDeliveredPendingActionCommands();
452
407
  }
453
408
  lastReadyRef.current = null;
@@ -460,12 +415,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
460
415
  reissueDeliveredPendingActionCommands,
461
416
  ]);
462
417
  const deleteCachedFlowHandleId = useCallback((flowHandleId) => {
463
- for (const [flowId, cachedFlowHandleId,] of flowHandleIdsByFlowIdRef.current) {
464
- if (cachedFlowHandleId === flowHandleId) {
465
- flowHandleIdsByFlowIdRef.current.delete(flowId);
466
- }
467
- }
468
- flowHandleCacheEpochRef.current += 1;
418
+ deleteCachedProviderFlowHandleId(flowHandleCacheRef.current, flowHandleId);
469
419
  }, []);
470
420
  const handleFlowHandleInvalidated = useCallback((flowHandleId) => {
471
421
  deleteCachedFlowHandleId(flowHandleId);
@@ -535,7 +485,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
535
485
  const isStaleFlowHandleError = error.code === "STALE_HANDLE" ||
536
486
  error.message.includes("flowHandleId is stale");
537
487
  if (flowId && isStaleFlowHandleError) {
538
- flowHandleIdsByFlowIdRef.current.delete(flowId);
488
+ deleteCachedProviderFlowId(flowHandleCacheRef.current, flowId);
539
489
  }
540
490
  const retryCommand = isStaleFlowHandleError
541
491
  ? toRetryWithoutFlowHandle(pendingCommand.result.envelope.command)
@@ -549,20 +499,14 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
549
499
  else {
550
500
  const settlement = message.event
551
501
  .detail;
552
- const flowId = toCommandFlowId(pendingCommand.result.envelope.command);
553
- const flowHandleId = toSettledFlowHandleId(settlement.result);
554
- const canWriteFlowHandle = flowId &&
555
- flowHandleId &&
556
- pendingCommand.flowHandleCacheEpoch ===
557
- flowHandleCacheEpochRef.current &&
558
- pendingCommand.flowCommandSequence ===
559
- flowCommandSequencesByFlowIdRef.current.get(flowId);
560
- if (canWriteFlowHandle) {
561
- flowHandleIdsByFlowIdRef.current.set(flowId, flowHandleId);
562
- }
502
+ writeSettledProviderFlowHandleId(flowHandleCacheRef.current, {
503
+ command: pendingCommand.result.envelope.command,
504
+ flowCommandSequence: pendingCommand.flowCommandSequence,
505
+ flowHandleCacheEpoch: pendingCommand.flowHandleCacheEpoch,
506
+ settlementResult: settlement.result,
507
+ });
563
508
  if (pendingCommand.result.envelope.command.kind === "reset") {
564
- flowHandleCacheEpochRef.current += 1;
565
- flowHandleIdsByFlowIdRef.current.clear();
509
+ clearProviderFlowHandleCache(flowHandleCacheRef.current);
566
510
  }
567
511
  const settledResult = Object.assign(Object.assign({}, pendingCommand.result), { settlement, settlementResult: settlement.result });
568
512
  pendingCommand.resolve(settledResult);
@@ -617,7 +561,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
617
561
  return enqueueCommand({
618
562
  kind: "open",
619
563
  flowId: normalizedFlowId,
620
- flowHandleId: flowHandleIdsByFlowIdRef.current.get(normalizedFlowId),
564
+ flowHandleId: getCachedProviderFlowHandleId(flowHandleCacheRef.current, normalizedFlowId),
621
565
  hideCloseButton: options === null || options === void 0 ? void 0 : options.hideCloseButton,
622
566
  }, commandOptions);
623
567
  },
@@ -626,7 +570,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
626
570
  return enqueueCommand({
627
571
  kind: "prefetch",
628
572
  flowId: normalizedFlowId,
629
- flowHandleId: flowHandleIdsByFlowIdRef.current.get(normalizedFlowId),
573
+ flowHandleId: getCachedProviderFlowHandleId(flowHandleCacheRef.current, normalizedFlowId),
630
574
  }, options);
631
575
  },
632
576
  prerender: (flowId, options, commandOptions) => {
@@ -634,7 +578,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
634
578
  return enqueueCommand({
635
579
  kind: "prerender",
636
580
  flowId: normalizedFlowId,
637
- flowHandleId: flowHandleIdsByFlowIdRef.current.get(normalizedFlowId),
581
+ flowHandleId: getCachedProviderFlowHandleId(flowHandleCacheRef.current, normalizedFlowId),
638
582
  hideCloseButton: options === null || options === void 0 ? void 0 : options.hideCloseButton,
639
583
  }, commandOptions);
640
584
  },
@@ -0,0 +1,14 @@
1
+ import { CommandSettlementError } from "@getuserfeedback/protocol/host";
2
+ import type { WebViewCommandEnvelope, WebViewTransportHostEvent } from "@getuserfeedback/protocol/webview-transport";
3
+ type ReactNativeWidgetCommand = WebViewCommandEnvelope["command"];
4
+ export type CommandSettledEvent = Extract<WebViewTransportHostEvent, {
5
+ name: "instance:command:settled";
6
+ }>;
7
+ export type CommandSettledSuccessDetail = Extract<CommandSettledEvent["detail"], {
8
+ ok: true;
9
+ }>;
10
+ export declare function toSettledFlowHandleId(result: unknown): string | undefined;
11
+ export declare function toCommandFlowId(command: ReactNativeWidgetCommand): string | undefined;
12
+ export declare function toRetryWithoutFlowHandle(command: ReactNativeWidgetCommand): ReactNativeWidgetCommand | null;
13
+ export declare function toCommandSettlementError(event: CommandSettledEvent): CommandSettlementError | null;
14
+ export {};
@@ -0,0 +1,40 @@
1
+ import { CommandSettlementError } from "@getuserfeedback/protocol/host";
2
+ function isReactNativeFlowCommand(command) {
3
+ return (command.kind === "open" ||
4
+ command.kind === "prefetch" ||
5
+ command.kind === "prerender");
6
+ }
7
+ export function toSettledFlowHandleId(result) {
8
+ if (typeof result !== "object" || result === null) {
9
+ return undefined;
10
+ }
11
+ const flowHandleId = result.flowHandleId;
12
+ return typeof flowHandleId === "string" && flowHandleId.trim().length > 0
13
+ ? flowHandleId
14
+ : undefined;
15
+ }
16
+ export function toCommandFlowId(command) {
17
+ if (!isReactNativeFlowCommand(command)) {
18
+ return undefined;
19
+ }
20
+ return command.flowId;
21
+ }
22
+ export function toRetryWithoutFlowHandle(command) {
23
+ if (!isReactNativeFlowCommand(command) ||
24
+ command.flowHandleId === undefined) {
25
+ return null;
26
+ }
27
+ return Object.assign(Object.assign({}, command), { flowHandleId: undefined });
28
+ }
29
+ export function toCommandSettlementError(event) {
30
+ if (event.detail.ok) {
31
+ return null;
32
+ }
33
+ return new CommandSettlementError({
34
+ code: event.detail.error.code,
35
+ instanceId: event.detail.instanceId,
36
+ kind: event.detail.kind,
37
+ message: event.detail.error.message,
38
+ requestId: event.detail.requestId,
39
+ });
40
+ }
@@ -0,0 +1,24 @@
1
+ import type { WebViewCommandEnvelope } from "@getuserfeedback/protocol/webview-transport";
2
+ type ReactNativeWidgetCommand = WebViewCommandEnvelope["command"];
3
+ export type ProviderFlowCommandSnapshot = {
4
+ flowCommandSequence: number | null;
5
+ flowHandleCacheEpoch: number;
6
+ };
7
+ export type ProviderFlowHandleCache = {
8
+ epoch: number;
9
+ flowCommandSequencesByFlowId: Map<string, number>;
10
+ flowHandleIdsByFlowId: Map<string, string>;
11
+ };
12
+ export declare function createProviderFlowHandleCache(): ProviderFlowHandleCache;
13
+ export declare function clearProviderFlowHandleCache(cache: ProviderFlowHandleCache): void;
14
+ export declare function deleteCachedProviderFlowHandleId(cache: ProviderFlowHandleCache, flowHandleId: string): void;
15
+ export declare function deleteCachedProviderFlowId(cache: ProviderFlowHandleCache, flowId: string): void;
16
+ export declare function getCachedProviderFlowHandleId(cache: ProviderFlowHandleCache, flowId: string): string | undefined;
17
+ export declare function snapshotProviderFlowCommand(cache: ProviderFlowHandleCache, command: ReactNativeWidgetCommand): ProviderFlowCommandSnapshot;
18
+ export declare function writeSettledProviderFlowHandleId(cache: ProviderFlowHandleCache, { command, flowCommandSequence, flowHandleCacheEpoch, settlementResult, }: {
19
+ command: ReactNativeWidgetCommand;
20
+ flowCommandSequence: number | null;
21
+ flowHandleCacheEpoch: number;
22
+ settlementResult: unknown;
23
+ }): boolean;
24
+ export {};
@@ -0,0 +1,53 @@
1
+ import { toCommandFlowId, toSettledFlowHandleId, } from "./provider-command-helpers.js";
2
+ export function createProviderFlowHandleCache() {
3
+ return {
4
+ epoch: 0,
5
+ flowCommandSequencesByFlowId: new Map(),
6
+ flowHandleIdsByFlowId: new Map(),
7
+ };
8
+ }
9
+ export function clearProviderFlowHandleCache(cache) {
10
+ cache.epoch += 1;
11
+ cache.flowHandleIdsByFlowId.clear();
12
+ }
13
+ export function deleteCachedProviderFlowHandleId(cache, flowHandleId) {
14
+ for (const [flowId, cachedFlowHandleId] of cache.flowHandleIdsByFlowId) {
15
+ if (cachedFlowHandleId === flowHandleId) {
16
+ cache.flowHandleIdsByFlowId.delete(flowId);
17
+ }
18
+ }
19
+ cache.epoch += 1;
20
+ }
21
+ export function deleteCachedProviderFlowId(cache, flowId) {
22
+ cache.flowHandleIdsByFlowId.delete(flowId);
23
+ }
24
+ export function getCachedProviderFlowHandleId(cache, flowId) {
25
+ return cache.flowHandleIdsByFlowId.get(flowId);
26
+ }
27
+ export function snapshotProviderFlowCommand(cache, command) {
28
+ var _a;
29
+ const flowId = toCommandFlowId(command);
30
+ const flowCommandSequence = flowId === undefined
31
+ ? null
32
+ : ((_a = cache.flowCommandSequencesByFlowId.get(flowId)) !== null && _a !== void 0 ? _a : 0) + 1;
33
+ if (flowId !== undefined && flowCommandSequence !== null) {
34
+ cache.flowCommandSequencesByFlowId.set(flowId, flowCommandSequence);
35
+ }
36
+ return {
37
+ flowCommandSequence,
38
+ flowHandleCacheEpoch: cache.epoch,
39
+ };
40
+ }
41
+ export function writeSettledProviderFlowHandleId(cache, { command, flowCommandSequence, flowHandleCacheEpoch, settlementResult, }) {
42
+ const flowId = toCommandFlowId(command);
43
+ const flowHandleId = toSettledFlowHandleId(settlementResult);
44
+ const canWriteFlowHandle = flowId &&
45
+ flowHandleId &&
46
+ flowHandleCacheEpoch === cache.epoch &&
47
+ flowCommandSequence === cache.flowCommandSequencesByFlowId.get(flowId);
48
+ if (!canWriteFlowHandle) {
49
+ return false;
50
+ }
51
+ cache.flowHandleIdsByFlowId.set(flowId, flowHandleId);
52
+ return true;
53
+ }
package/dist/version.js CHANGED
@@ -3,4 +3,4 @@ const reactNativeSdkVersion = typeof __GX_REACT_NATIVE_SDK_VERSION__ === "string
3
3
  : "";
4
4
  // Build scripts patch this fallback to the package version for published artifacts.
5
5
  // Source-linked workspace usage keeps the local fallback.
6
- export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "1.3.4";
6
+ export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "1.3.6";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getuserfeedback/react-native",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "getuserfeedback React Native SDK",
5
5
  "keywords": [
6
6
  "getuserfeedback",
@@ -40,6 +40,7 @@
40
40
  "build": "bun scripts/build.ts",
41
41
  "typecheck": "tsc -b tsconfig.json",
42
42
  "test": "bun test --dots",
43
+ "test:changed": "bun test --changed=${TEST_CHANGED_BASE:-HEAD} --pass-with-no-tests --dots",
43
44
  "lint": "ultracite check ."
44
45
  },
45
46
  "dependencies": {