@getuserfeedback/react-native 4.0.6 → 4.0.8

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.
@@ -2,6 +2,7 @@ import type { CoreHostBootstrapTransport } from "@getuserfeedback/protocol/inter
2
2
  import { type CoreWebViewBridgeMessage } from "./core-webview-bridge-adapter.js";
3
3
  import { type WebViewHostController } from "./webview-host-controller.js";
4
4
  export type CoreWebViewHostConnection = {
5
+ disconnect: () => void;
5
6
  generation: number;
6
7
  transport: CoreHostBootstrapTransport;
7
8
  };
@@ -2,6 +2,7 @@ import { buildCoreWebViewBridgeScript, buildCoreWebViewHostMessageScript, } from
2
2
  import { createWebViewHostController, } from "./webview-host-controller.js";
3
3
  export const createCoreWebViewHostController = (input) => createWebViewHostController(Object.assign(Object.assign({}, input), { connectionLabel: "Core WebView host", buildActivationScript: buildCoreWebViewBridgeScript, buildHostMessageScript: buildCoreWebViewHostMessageScript, onConnected: (connection) => {
4
4
  input.onConnected({
5
+ disconnect: connection.disconnect,
5
6
  generation: connection.generation,
6
7
  transport: {
7
8
  postMessage: connection.postMessage,
@@ -1,11 +1,8 @@
1
- import type { CoreHostBootstrapTransport } from "@getuserfeedback/protocol/internal/core-host-bootstrap";
2
1
  import { type ComponentType, type ReactElement } from "react";
2
+ import { type CoreWebViewHostConnection } from "./core-webview-host-controller.js";
3
3
  export type CoreWebViewHostProps = {
4
4
  coreUrl: string;
5
- onConnected: (connection: {
6
- generation: number;
7
- transport: CoreHostBootstrapTransport;
8
- }) => void;
5
+ onConnected: (connection: CoreWebViewHostConnection) => void;
9
6
  onDisconnected?: () => void;
10
7
  onInvalidMessage?: (error: Error, value: unknown) => void;
11
8
  webViewComponent?: ComponentType<Record<string, unknown>>;
@@ -53,7 +53,7 @@ const resolveNativeView = () => {
53
53
  export function NativeViewPresentationSurface({ children, nativeViewComponent, record, }) {
54
54
  var _a, _b, _c, _d, _e, _f, _g, _h;
55
55
  const NativeView = nativeViewComponent !== null && nativeViewComponent !== void 0 ? nativeViewComponent : resolveNativeView();
56
- const isPresented = record.activationState === "active" && record.size !== null;
56
+ const isPresented = record.isActive && record.size !== null;
57
57
  const cornerRadii = toNativeWidgetCornerRadii((_a = record.size) === null || _a === void 0 ? void 0 : _a.frameAppearance);
58
58
  const shadow = toNativeWidgetFrameShadow(((_c = (_b = record.size) === null || _b === void 0 ? void 0 : _b.frameAppearance) === null || _c === void 0 ? void 0 : _c.kind) === "page"
59
59
  ? record.size.frameAppearance.resolvedBoxShadow
@@ -2,13 +2,12 @@ import { type OpenRequestMetadata } from "@getuserfeedback/protocol/internal/cor
2
2
  import { type EventViewSize } from "@getuserfeedback/protocol/internal/view-host-control-messages";
3
3
  import type { CoreWebViewHostConnection } from "./core-webview-host-controller.js";
4
4
  import type { ViewWebViewHostConnection } from "./view-webview-host-controller.js";
5
- type NativeViewActivationState = "idle" | "prerendering" | "activation-requested" | "prerendering-activation-requested" | "prerendered" | "active";
6
5
  type NativeViewSizeReport = Omit<EventViewSize, "gen" | "t" | "viewId">;
7
6
  export type NativeViewRecord = Readonly<{
8
7
  allocationId: number;
9
- activationState: NativeViewActivationState;
10
8
  generation: number;
11
9
  instanceId: string;
10
+ isActive: boolean;
12
11
  openRequest: Readonly<OpenRequestMetadata> | undefined;
13
12
  size: NativeViewSizeReport | null;
14
13
  srcdoc: string;
@@ -26,6 +25,5 @@ export type NativeViewRecordController = {
26
25
  export declare function createNativeViewRecordController(input: {
27
26
  coreConnection: CoreWebViewHostConnection;
28
27
  onError: (error: Error) => void;
29
- readinessRetryDelayMs?: number;
30
28
  }): NativeViewRecordController;
31
29
  export {};
@@ -1,19 +1,16 @@
1
1
  import { coreViewHostActionSchema, } from "@getuserfeedback/protocol/internal/core-view-host-actions";
2
2
  import { buildEventViewSize, eventViewSizeSchema, } from "@getuserfeedback/protocol/internal/view-host-control-messages";
3
3
  import { buildEventViewPrerendered } from "@getuserfeedback/protocol/internal/view-host-lifecycle-events";
4
- import { createViewActivationController, createViewPreparationReadinessRegistry, createViewPreparationRegistry, } from "./view-orchestration-runtime.js";
4
+ import { createViewPreparationReadinessRegistry, createViewPreparationRegistry, } from "./view-orchestration-runtime.js";
5
5
  const createNativeViewReadinessRegistry = () => createViewPreparationReadinessRegistry();
6
- const DEFAULT_READINESS_RETRY_DELAY_MS = 50;
7
6
  export function createNativeViewRecordController(input) {
8
- var _a;
9
7
  const entries = new Map();
10
8
  const listeners = new Set();
11
- const pendingActivations = new Map();
12
9
  const preparations = createViewPreparationRegistry();
10
+ let coreConnectionFailed = false;
13
11
  let disposed = false;
14
12
  let nextAllocationId = 0;
15
13
  let snapshot = Object.freeze([]);
16
- const readinessRetryDelayMs = (_a = input.readinessRetryDelayMs) !== null && _a !== void 0 ? _a : DEFAULT_READINESS_RETRY_DELAY_MS;
17
14
  const reportError = (error, fallbackMessage) => {
18
15
  try {
19
16
  input.onError(error instanceof Error ? error : new Error(fallbackMessage));
@@ -28,19 +25,6 @@ export function createNativeViewRecordController(input) {
28
25
  listener();
29
26
  }
30
27
  };
31
- const ensurePendingActivation = (viewId, openRequest) => {
32
- const existing = pendingActivations.get(viewId);
33
- if (existing) {
34
- existing.openRequest = openRequest;
35
- return existing;
36
- }
37
- const created = {
38
- activation: createViewActivationController(),
39
- openRequest,
40
- };
41
- pendingActivations.set(viewId, created);
42
- return created;
43
- };
44
28
  const removeEntry = (entry) => {
45
29
  if (entries.get(entry.viewId) !== entry) {
46
30
  return false;
@@ -55,7 +39,6 @@ export function createNativeViewRecordController(input) {
55
39
  return true;
56
40
  };
57
41
  const handlePrerender = (action) => {
58
- var _a;
59
42
  if (entries.has(action.viewId)) {
60
43
  return;
61
44
  }
@@ -63,21 +46,15 @@ export function createNativeViewRecordController(input) {
63
46
  if (!preparation) {
64
47
  return;
65
48
  }
66
- const pendingActivation = pendingActivations.get(action.viewId);
67
- const activation = (_a = pendingActivation === null || pendingActivation === void 0 ? void 0 : pendingActivation.activation) !== null && _a !== void 0 ? _a : createViewActivationController();
68
- pendingActivations.delete(action.viewId);
69
- activation.startPrerender();
70
49
  nextAllocationId += 1;
71
50
  const entry = {
72
- activation,
73
51
  allocationId: nextAllocationId,
74
52
  generation: action.gen,
75
53
  instanceId: action.instanceId,
76
- openRequest: pendingActivation
77
- ? pendingActivation.openRequest
78
- : toOpenRequestMetadata(action.openRequest),
54
+ isActive: false,
55
+ isPreparationCommitted: false,
56
+ openRequest: toOpenRequestMetadata(action.openRequest),
79
57
  preparation,
80
- preparationState: "claimed",
81
58
  size: null,
82
59
  srcdoc: action.srcdoc,
83
60
  viewReadinessDetach: null,
@@ -91,18 +68,14 @@ export function createNativeViewRecordController(input) {
91
68
  };
92
69
  const handleActivate = (action) => {
93
70
  const entry = entries.get(action.viewId);
94
- const openRequest = toOpenRequestMetadata(action.openRequest);
95
- const activation = entry
96
- ? entry.activation
97
- : ensurePendingActivation(action.viewId, openRequest).activation;
98
- const previousState = activation.getState();
99
- activation.requestActivation();
100
71
  if (!entry) {
101
72
  return;
102
73
  }
74
+ const openRequest = toOpenRequestMetadata(action.openRequest);
103
75
  const openRequestChanged = !areOpenRequestsEqual(entry.openRequest, openRequest);
104
76
  entry.openRequest = openRequest;
105
- if (activation.getState() !== previousState || openRequestChanged) {
77
+ if (!entry.isActive || openRequestChanged) {
78
+ entry.isActive = true;
106
79
  notify();
107
80
  }
108
81
  };
@@ -110,14 +83,13 @@ export function createNativeViewRecordController(input) {
110
83
  if (preparations.cancel(action.viewId)) {
111
84
  return;
112
85
  }
113
- pendingActivations.delete(action.viewId);
114
86
  const entry = entries.get(action.viewId);
115
87
  if (entry) {
116
88
  removeEntry(entry);
117
89
  }
118
90
  };
119
91
  const handleCoreMessage = (value) => {
120
- if (disposed) {
92
+ if (coreConnectionFailed || disposed) {
121
93
  return;
122
94
  }
123
95
  const parsed = coreViewHostActionSchema.safeParse(value);
@@ -137,38 +109,26 @@ export function createNativeViewRecordController(input) {
137
109
  break;
138
110
  }
139
111
  };
140
- const completeConnectionReadiness = (delivery, entry, size) => {
141
- if (!size || disposed || entries.get(entry.viewId) !== entry) {
142
- return false;
112
+ const completeConnectionReadiness = (entry, size) => {
113
+ if (disposed || entries.get(entry.viewId) !== entry) {
114
+ return;
143
115
  }
144
- if (entry.preparationState === "claimed") {
116
+ if (!entry.isPreparationCommitted) {
145
117
  if (!entry.preparation.commit()) {
146
- return false;
118
+ return;
147
119
  }
148
- entry.preparationState = "committed";
149
- }
150
- if (delivery.stage === "pending-size") {
151
- input.coreConnection.transport.postMessage(buildEventViewSize(Object.assign({ gen: entry.generation, viewId: entry.viewId }, size)));
152
- delivery.stage = "pending-prerendered";
120
+ entry.isPreparationCommitted = true;
153
121
  }
154
- if (delivery.stage === "pending-prerendered") {
155
- input.coreConnection.transport.postMessage(buildEventViewPrerendered({
156
- gen: entry.generation,
157
- viewId: entry.viewId,
158
- }));
159
- delivery.stage = "delivered";
160
- }
161
- if (entry.preparationState !== "prepared") {
162
- entry.preparationState = "prepared";
163
- entry.activation.markPrerendered();
164
- }
165
- notify();
166
- return true;
122
+ input.coreConnection.transport.postMessage(buildEventViewSize(Object.assign({ gen: entry.generation, viewId: entry.viewId }, size)));
123
+ input.coreConnection.transport.postMessage(buildEventViewPrerendered({
124
+ gen: entry.generation,
125
+ viewId: entry.viewId,
126
+ }));
167
127
  };
168
128
  const stopCoreTransport = input.coreConnection.transport.subscribe(handleCoreMessage);
169
129
  return {
170
130
  attachViewReadinessConnection({ allocationId, connection }) {
171
- if (disposed) {
131
+ if (coreConnectionFailed || disposed) {
172
132
  return null;
173
133
  }
174
134
  const { viewId } = connection;
@@ -180,12 +140,6 @@ export function createNativeViewRecordController(input) {
180
140
  }
181
141
  const previousDetach = entry.viewReadinessDetach;
182
142
  const connectionReadiness = createNativeViewReadinessRegistry().begin(viewId);
183
- const readinessDelivery = {
184
- stage: "awaiting-readiness",
185
- };
186
- let latestReadySize = null;
187
- let retryState = "available";
188
- let retryTimer = null;
189
143
  let detached = false;
190
144
  let subscribed = false;
191
145
  let unsubscribe = () => { };
@@ -195,10 +149,6 @@ export function createNativeViewRecordController(input) {
195
149
  return;
196
150
  }
197
151
  detached = true;
198
- if (retryTimer !== null) {
199
- clearTimeout(retryTimer);
200
- retryTimer = null;
201
- }
202
152
  unsubscribe();
203
153
  connectionReadiness.release();
204
154
  if (entry.viewReadinessDetach === detach) {
@@ -211,49 +161,33 @@ export function createNativeViewRecordController(input) {
211
161
  }
212
162
  }
213
163
  };
214
- const attemptReadinessDelivery = (isRetry = false) => {
215
- if (!latestReadySize ||
216
- readinessDelivery.stage === "awaiting-readiness" ||
217
- readinessDelivery.stage === "delivered" ||
164
+ const deliverReadiness = (size) => {
165
+ if (coreConnectionFailed ||
218
166
  detached ||
219
167
  disposed ||
220
168
  entries.get(viewId) !== entry ||
221
169
  entry.viewReadinessDetach !== detach) {
222
- return "idle";
223
- }
224
- if ((isRetry && retryState !== "scheduled") ||
225
- (!isRetry && retryState !== "available")) {
226
- return "failed";
227
- }
228
- if (isRetry) {
229
- retryState = "exhausted";
170
+ return;
230
171
  }
231
172
  try {
232
- if (completeConnectionReadiness(readinessDelivery, entry, latestReadySize)) {
233
- if (retryTimer !== null) {
234
- clearTimeout(retryTimer);
235
- retryTimer = null;
236
- }
237
- return "delivered";
238
- }
239
- return "idle";
173
+ completeConnectionReadiness(entry, size);
240
174
  }
241
175
  catch (error) {
242
- if (!isRetry) {
243
- retryState = "scheduled";
244
- retryTimer = setTimeout(() => {
245
- retryTimer = null;
246
- attemptReadinessDelivery(true);
247
- }, readinessRetryDelayMs);
176
+ coreConnectionFailed = true;
177
+ try {
178
+ input.coreConnection.disconnect();
248
179
  }
249
- else {
180
+ catch (_a) {
181
+ // Disconnect observers cannot change readiness settlement.
182
+ }
183
+ finally {
250
184
  reportError(error, "Native view readiness delivery failed");
251
185
  }
252
- return "failed";
253
186
  }
254
187
  };
255
188
  const handleMessage = (value) => {
256
- if (detached ||
189
+ if (coreConnectionFailed ||
190
+ detached ||
257
191
  disposed ||
258
192
  entries.get(viewId) !== entry ||
259
193
  entry.viewReadinessDetach !== detach) {
@@ -269,14 +203,11 @@ export function createNativeViewRecordController(input) {
269
203
  entry.size = size;
270
204
  const readySize = connectionReadiness.recordSize(size);
271
205
  if (readySize) {
272
- latestReadySize = readySize;
273
- readinessDelivery.stage = "pending-size";
274
- }
275
- else if (readinessDelivery.stage === "pending-size" ||
276
- readinessDelivery.stage === "pending-prerendered") {
277
- latestReadySize = size;
206
+ deliverReadiness(readySize);
278
207
  }
279
- if (attemptReadinessDelivery() !== "delivered") {
208
+ if (!coreConnectionFailed &&
209
+ !disposed &&
210
+ entries.get(viewId) === entry) {
280
211
  notify();
281
212
  }
282
213
  };
@@ -316,10 +247,8 @@ export function createNativeViewRecordController(input) {
316
247
  }
317
248
  const readySize = connectionReadiness.markHostPrepared();
318
249
  if (readySize) {
319
- latestReadySize = readySize;
320
- readinessDelivery.stage = "pending-size";
250
+ deliverReadiness(readySize);
321
251
  }
322
- attemptReadinessDelivery();
323
252
  return detached ? null : detach;
324
253
  },
325
254
  dispose() {
@@ -333,7 +262,6 @@ export function createNativeViewRecordController(input) {
333
262
  for (const entry of Array.from(entries.values())) {
334
263
  removeEntry(entry);
335
264
  }
336
- pendingActivations.clear();
337
265
  if (hadEntries) {
338
266
  snapshot = Object.freeze([]);
339
267
  for (const listener of listeners) {
@@ -354,10 +282,10 @@ export function createNativeViewRecordController(input) {
354
282
  }
355
283
  function toRecord(entry) {
356
284
  return Object.freeze({
357
- activationState: entry.activation.getState(),
358
285
  allocationId: entry.allocationId,
359
286
  generation: entry.generation,
360
287
  instanceId: entry.instanceId,
288
+ isActive: entry.isActive,
361
289
  openRequest: entry.openRequest,
362
290
  size: entry.size,
363
291
  srcdoc: entry.srcdoc,
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 : "4.0.6";
6
+ export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "4.0.8";
@@ -83,72 +83,25 @@ class PendingCommandTracker {
83
83
  return this.pendingByRequestId.size;
84
84
  }
85
85
  }
86
- // ../view-orchestration/src/view-activation-machine.ts
87
- import { createMachine, interpret, state, transition } from "robot3";
88
- var VIEW_ACTIVATION_STATES = {
89
- idle: true,
90
- prerendering: true,
91
- "activation-requested": true,
92
- "prerendering-activation-requested": true,
93
- prerendered: true,
94
- active: true
95
- };
96
- function createViewActivationMachine() {
97
- const states = {
98
- idle: state(transition("startPrerender", "prerendering"), transition("requestActivation", "activation-requested")),
99
- prerendering: state(transition("requestActivation", "prerendering-activation-requested"), transition("markPrerendered", "prerendered")),
100
- "activation-requested": state(transition("startPrerender", "prerendering-activation-requested")),
101
- "prerendering-activation-requested": state(transition("markPrerendered", "active")),
102
- prerendered: state(transition("requestActivation", "active")),
103
- active: state()
104
- };
105
- return createMachine(states, () => ({}));
106
- }
107
- function createViewActivationController() {
108
- const machine = createViewActivationMachine();
109
- const service = interpret(machine, () => {});
110
- const getState = () => readViewActivationState(service.machine.current);
111
- const send = (event) => {
112
- service.send(event);
113
- };
114
- return {
115
- getState,
116
- startPrerender: () => {
117
- send({ type: "startPrerender" });
118
- },
119
- requestActivation: () => {
120
- send({ type: "requestActivation" });
121
- },
122
- markPrerendered: () => {
123
- send({ type: "markPrerendered" });
124
- }
125
- };
126
- }
127
- function readViewActivationState(value) {
128
- if (typeof value !== "string" || !(value in VIEW_ACTIVATION_STATES)) {
129
- throw new Error(`Unknown view activation state: ${String(value)}`);
130
- }
131
- return value;
132
- }
133
86
  // ../view-orchestration/src/view-preparation-readiness-registry.ts
134
87
  function createViewPreparationReadinessRegistry() {
135
88
  const states = new Map;
136
- const resolveReadySize = (state2) => {
137
- if (!state2 || state2.status === "ready" || !state2.hostPrepared || state2.size === null) {
89
+ const resolveReadySize = (state) => {
90
+ if (!state || state.status === "ready" || !state.hostPrepared || state.size === null) {
138
91
  return null;
139
92
  }
140
- state2.status = "ready";
141
- return state2.size;
93
+ state.status = "ready";
94
+ return state.size;
142
95
  };
143
96
  return {
144
97
  begin(viewId) {
145
- const state2 = {
98
+ const state = {
146
99
  hostPrepared: false,
147
100
  size: null,
148
101
  status: "pending"
149
102
  };
150
- states.set(viewId, state2);
151
- const getCurrentState = () => states.get(viewId) === state2 ? state2 : null;
103
+ states.set(viewId, state);
104
+ const getCurrentState = () => states.get(viewId) === state ? state : null;
152
105
  return {
153
106
  markHostPrepared() {
154
107
  const current = getCurrentState();
@@ -269,9 +222,6 @@ function createPendingCommandTracker() {
269
222
  function createActiveViewSnapshotListController2() {
270
223
  return createActiveViewSnapshotListController();
271
224
  }
272
- function createViewActivationController2() {
273
- return createViewActivationController();
274
- }
275
225
  function createViewPreparationRegistry2() {
276
226
  return createViewPreparationRegistry();
277
227
  }
@@ -281,7 +231,6 @@ function createViewPreparationReadinessRegistry2() {
281
231
  export {
282
232
  createViewPreparationRegistry2 as createViewPreparationRegistry,
283
233
  createViewPreparationReadinessRegistry2 as createViewPreparationReadinessRegistry,
284
- createViewActivationController2 as createViewActivationController,
285
234
  createPendingCommandTracker,
286
235
  createActiveViewSnapshotListController2 as createActiveViewSnapshotListController
287
236
  };
@@ -10,6 +10,7 @@ export type WebViewBridgeMessageIdentity = {
10
10
  };
11
11
  export type WebViewHostConnection<TMessage extends WebViewBridgeMessageIdentity> = {
12
12
  connectionId: string;
13
+ disconnect: () => void;
13
14
  epoch: number;
14
15
  generation: number;
15
16
  postMessage: (message: unknown) => void;
@@ -142,6 +142,11 @@ export const createWebViewHostController = (input) => {
142
142
  const listeners = new Set();
143
143
  const connection = {
144
144
  connectionId: message.connectionId,
145
+ disconnect: () => {
146
+ if (activeConnection === connection) {
147
+ disconnect();
148
+ }
149
+ },
145
150
  epoch: message.epoch,
146
151
  generation: message.generation,
147
152
  isAnnounced: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getuserfeedback/react-native",
3
- "version": "4.0.6",
3
+ "version": "4.0.8",
4
4
  "description": "getuserfeedback React Native SDK",
5
5
  "keywords": [
6
6
  "getuserfeedback",