@getuserfeedback/react-native 3.0.67 → 3.0.71

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.
@@ -1,9 +1,11 @@
1
1
  import { type CoreCommandEnvelope } from "@getuserfeedback/protocol";
2
+ import { type EventHandleInvalidated } from "@getuserfeedback/protocol/internal/core-handle-invalidation";
2
3
  import { type EventCommandSettled } from "@getuserfeedback/protocol/internal/core-host-command-settlement";
3
4
  import type { CoreWebViewHostConnection } from "./core-webview-host-controller.js";
4
5
  export type NativeCoreCommandChannel = {
5
6
  post: (envelope: CoreCommandEnvelope) => void;
6
7
  subscribe: (listener: (message: EventCommandSettled) => void) => () => void;
8
+ subscribeHandleInvalidated: (listener: (message: EventHandleInvalidated) => void) => () => void;
7
9
  };
8
10
  type OwnedNativeCoreCommandChannel = {
9
11
  channel: NativeCoreCommandChannel;
@@ -1,28 +1,47 @@
1
1
  import { coreCommandEnvelopeSchema, } from "@getuserfeedback/protocol";
2
+ import { eventHandleInvalidatedSchema, } from "@getuserfeedback/protocol/internal/core-handle-invalidation";
2
3
  import { eventCommandSettledSchema, } from "@getuserfeedback/protocol/internal/core-host-command-settlement";
3
4
  const DISPOSED_MESSAGE = "Core command channel is disposed";
4
5
  const isRecord = (value) => typeof value === "object" && value !== null;
5
6
  export function createNativeCoreCommandChannel(input) {
6
7
  const listeners = new Set();
8
+ const handleInvalidatedListeners = new Set();
7
9
  let disposed = false;
8
10
  const handleMessage = (value) => {
9
- var _a;
10
- if (disposed ||
11
- !isRecord(value) ||
12
- value.t !== "getuserfeedback:event:commandSettled") {
11
+ var _a, _b;
12
+ if (disposed || !isRecord(value)) {
13
13
  return;
14
14
  }
15
- const parsed = eventCommandSettledSchema.safeParse(value);
15
+ if (value.t === "getuserfeedback:event:commandSettled") {
16
+ const parsed = eventCommandSettledSchema.safeParse(value);
17
+ if (!parsed.success) {
18
+ (_a = input.onInvalidMessage) === null || _a === void 0 ? void 0 : _a.call(input, parsed.error instanceof Error
19
+ ? parsed.error
20
+ : new Error("Invalid core command settlement message"), value);
21
+ return;
22
+ }
23
+ if (parsed.data.gen !== input.coreConnection.generation) {
24
+ return;
25
+ }
26
+ for (const listener of [...listeners]) {
27
+ listener(parsed.data);
28
+ }
29
+ return;
30
+ }
31
+ if (value.t !== "getuserfeedback:event:handleInvalidated") {
32
+ return;
33
+ }
34
+ const parsed = eventHandleInvalidatedSchema.safeParse(value);
16
35
  if (!parsed.success) {
17
- (_a = input.onInvalidMessage) === null || _a === void 0 ? void 0 : _a.call(input, parsed.error instanceof Error
36
+ (_b = input.onInvalidMessage) === null || _b === void 0 ? void 0 : _b.call(input, parsed.error instanceof Error
18
37
  ? parsed.error
19
- : new Error("Invalid core command settlement message"), value);
38
+ : new Error("Invalid core handle invalidation message"), value);
20
39
  return;
21
40
  }
22
41
  if (parsed.data.gen !== input.coreConnection.generation) {
23
42
  return;
24
43
  }
25
- for (const listener of [...listeners]) {
44
+ for (const listener of [...handleInvalidatedListeners]) {
26
45
  listener(parsed.data);
27
46
  }
28
47
  };
@@ -44,6 +63,15 @@ export function createNativeCoreCommandChannel(input) {
44
63
  listeners.delete(listener);
45
64
  };
46
65
  },
66
+ subscribeHandleInvalidated: (listener) => {
67
+ if (disposed) {
68
+ return () => { };
69
+ }
70
+ handleInvalidatedListeners.add(listener);
71
+ return () => {
72
+ handleInvalidatedListeners.delete(listener);
73
+ };
74
+ },
47
75
  };
48
76
  return {
49
77
  channel,
@@ -54,6 +82,7 @@ export function createNativeCoreCommandChannel(input) {
54
82
  disposed = true;
55
83
  unsubscribeTransport();
56
84
  listeners.clear();
85
+ handleInvalidatedListeners.clear();
57
86
  },
58
87
  };
59
88
  }
@@ -1,8 +1,10 @@
1
1
  import { type CoreCommandEnvelope } from "@getuserfeedback/protocol";
2
2
  import { type CommandSettledDetail } from "@getuserfeedback/protocol/host";
3
+ import type { EventHandleInvalidated } from "@getuserfeedback/protocol/internal/core-handle-invalidation";
3
4
  import type { NativeCoreCommandChannel } from "./native-core-command-channel.js";
4
5
  export type NativeCoreCommandSession = {
5
6
  dispatch: (envelope: CoreCommandEnvelope) => Promise<CommandSettledDetail>;
7
+ subscribeHandleInvalidated: (listener: (message: EventHandleInvalidated) => void) => () => void;
6
8
  };
7
9
  type NativeCoreCommandSessionOwner = {
8
10
  dispose: () => void;
@@ -23,8 +23,9 @@ function toCommandSettledDetail(settlement) {
23
23
  }
24
24
  export function createNativeCoreCommandSession(input) {
25
25
  const pendingCommands = createPendingCommandTracker();
26
+ const handleInvalidatedListeners = new Set();
26
27
  let disposed = false;
27
- const unsubscribe = input.channel.subscribe((settlement) => {
28
+ const unsubscribeSettlements = input.channel.subscribe((settlement) => {
28
29
  const detail = toCommandSettledDetail(settlement);
29
30
  if (detail.ok) {
30
31
  pendingCommands.resolve(detail.requestId, detail);
@@ -32,6 +33,11 @@ export function createNativeCoreCommandSession(input) {
32
33
  }
33
34
  pendingCommands.reject(detail.requestId, toCommandSettlementError(detail));
34
35
  });
36
+ const unsubscribeHandleInvalidated = input.channel.subscribeHandleInvalidated((message) => {
37
+ for (const listener of [...handleInvalidatedListeners]) {
38
+ listener(message);
39
+ }
40
+ });
35
41
  return {
36
42
  session: {
37
43
  dispatch: async (envelope) => {
@@ -48,13 +54,24 @@ export function createNativeCoreCommandSession(input) {
48
54
  }
49
55
  return settlement;
50
56
  },
57
+ subscribeHandleInvalidated: (listener) => {
58
+ if (disposed) {
59
+ return () => { };
60
+ }
61
+ handleInvalidatedListeners.add(listener);
62
+ return () => {
63
+ handleInvalidatedListeners.delete(listener);
64
+ };
65
+ },
51
66
  },
52
67
  dispose: () => {
53
68
  if (disposed) {
54
69
  return;
55
70
  }
56
71
  disposed = true;
57
- unsubscribe();
72
+ unsubscribeSettlements();
73
+ unsubscribeHandleInvalidated();
74
+ handleInvalidatedListeners.clear();
58
75
  pendingCommands.reset((requestId) => new NativeCoreCommandSessionClosedError(requestId));
59
76
  },
60
77
  };
@@ -1,13 +1,22 @@
1
1
  import type { CommandEnvelopeWithInstanceId, CommandSettledDetail } from "@getuserfeedback/protocol/host";
2
2
  import { type CoreTranslatablePublicCommandPayload } from "@getuserfeedback/protocol/internal/public-core-command";
3
3
  import type { NativeCoreCommandSession } from "./native-core-command-session.js";
4
- type FlowLifecycleCommandPayload = Extract<CoreTranslatablePublicCommandPayload, {
5
- kind: "open" | "prefetch" | "prerender" | "close";
6
- }>;
7
- /** Public commands that can be sent directly to the settled native core session. */
8
- type NativeDirectCommandPayload = Exclude<CoreTranslatablePublicCommandPayload, FlowLifecycleCommandPayload | {
9
- kind: "init";
4
+ type NativeFlowAllocationCommand = (Omit<Extract<CoreTranslatablePublicCommandPayload, {
5
+ kind: "open";
6
+ }>, "container"> & {
7
+ container?: never;
8
+ }) | Extract<CoreTranslatablePublicCommandPayload, {
9
+ kind: "prefetch" | "prerender";
10
10
  }>;
11
+ type NativeScopedCloseCommand = Extract<CoreTranslatablePublicCommandPayload, {
12
+ kind: "close";
13
+ }> & {
14
+ flowHandleId: string;
15
+ };
16
+ /** Public commands accepted by the direct native provider session. */
17
+ type NativeDirectCommandPayload = Exclude<CoreTranslatablePublicCommandPayload, {
18
+ kind: "init" | "open" | "prefetch" | "prerender" | "close" | "reset";
19
+ }> | NativeFlowAllocationCommand | NativeScopedCloseCommand;
11
20
  /** Public command envelope accepted by the direct native provider session. */
12
21
  export type NativeDirectCommandEnvelope = Omit<CommandEnvelopeWithInstanceId, "command"> & {
13
22
  command: NativeDirectCommandPayload;
@@ -15,12 +24,6 @@ export type NativeDirectCommandEnvelope = Omit<CommandEnvelopeWithInstanceId, "c
15
24
  export type NativeDirectCommandSession = {
16
25
  dispatch: (envelope: NativeDirectCommandEnvelope) => Promise<CommandSettledDetail>;
17
26
  };
18
- /**
19
- * Adapt direct public commands to the already-settled native core session.
20
- *
21
- * Flow lifecycle and container commands stay outside this adapter until their
22
- * shared orchestration contract is ready.
23
- */
24
27
  export declare function createNativeDirectCommandSession(input: {
25
28
  coreSession: NativeCoreCommandSession;
26
29
  }): NativeDirectCommandSession;
@@ -1,15 +1,157 @@
1
+ import { getFlowHandleFromSettlementDetail } from "@getuserfeedback/protocol/host";
1
2
  import { toCoreCommandPayload, } from "@getuserfeedback/protocol/internal/public-core-command";
2
- /**
3
- * Adapt direct public commands to the already-settled native core session.
4
- *
5
- * Flow lifecycle and container commands stay outside this adapter until their
6
- * shared orchestration contract is ready.
7
- */
3
+ import { createFlowHandleCommandStateStore, planFlowHandleCommand, } from "./view-orchestration-runtime.js";
4
+ // TODO(migration): Add native instance-wide command ordering and authoritative handle invalidation before routing reset, unscoped close, and the public provider client through this direct session.
5
+ // [from=native-flow-handle-command-session] [to=native-instance-flow-command-session] [scope=slice] [principle=core-owned-flow-lifecycle] [priority=high] [impact=high] [risk=high] [epic=runtime-neutral-view-host-orchestration] [epic_order=180]
8
6
  export function createNativeDirectCommandSession(input) {
7
+ const flowHandleCommandStateStore = createFlowHandleCommandStateStore();
8
+ const dispatchToCore = (envelope, command) => {
9
+ const coreEnvelope = Object.assign(Object.assign({}, envelope), { command: toCoreCommandPayload(command) });
10
+ return input.coreSession.dispatch(coreEnvelope);
11
+ };
12
+ const assertSettlementMatches = (input) => {
13
+ if (input.settlement.requestId !== input.envelope.requestId ||
14
+ input.settlement.instanceId !== input.envelope.instanceId ||
15
+ input.settlement.kind !== input.expectedKind) {
16
+ throw new Error(`Expected ${input.expectedKind} settlement for requestId "${input.envelope.requestId}" and instanceId "${input.envelope.instanceId}"`);
17
+ }
18
+ };
19
+ const dispatchAllocation = async (envelope, command) => {
20
+ if (command.flowHandleId === undefined) {
21
+ const settlement = await dispatchToCore(envelope, command);
22
+ if (!settlement.ok) {
23
+ return settlement;
24
+ }
25
+ assertSettlementMatches({
26
+ envelope,
27
+ expectedKind: command.kind,
28
+ settlement,
29
+ });
30
+ const allocation = getFlowHandleFromSettlementDetail(settlement);
31
+ if (!(allocation === null || allocation === void 0 ? void 0 : allocation.flowRunId)) {
32
+ throw new Error(`Expected ${command.kind} settlement to include flowHandleId and flowRunId`);
33
+ }
34
+ const { flowHandleId, flowRunId } = allocation;
35
+ await flowHandleCommandStateStore.enqueue(flowHandleId, async (state) => {
36
+ state.runtime.bindAffinity({
37
+ instanceId: envelope.instanceId,
38
+ flowId: command.flowId,
39
+ });
40
+ state.runtime.bindFlowRun({
41
+ flowRunId,
42
+ activationPolicy: "never",
43
+ });
44
+ });
45
+ return settlement;
46
+ }
47
+ const flowHandleId = command.flowHandleId;
48
+ return flowHandleCommandStateStore.enqueue(flowHandleId, async (state) => {
49
+ const runtime = state.runtime.getSnapshot();
50
+ const decision = planFlowHandleCommand({
51
+ command: command.kind,
52
+ runtime,
53
+ instanceId: envelope.instanceId,
54
+ flowId: command.flowId,
55
+ });
56
+ if (decision === "forward-to-core") {
57
+ return dispatchToCore(envelope, Object.assign(Object.assign({}, command), { flowHandleId }));
58
+ }
59
+ if (decision !== "orchestrate") {
60
+ throw new Error(`Unexpected flow-handle command decision for ${command.kind}: ${decision}`);
61
+ }
62
+ state.runtime.bindAffinity({
63
+ instanceId: envelope.instanceId,
64
+ flowId: command.flowId,
65
+ });
66
+ const shouldResolveFlowRun = runtime.flowRunId === null;
67
+ if (shouldResolveFlowRun &&
68
+ !state.runtime.beginFlowRunResolution(envelope.requestId)) {
69
+ return dispatchToCore(envelope, Object.assign(Object.assign({}, command), { flowHandleId }));
70
+ }
71
+ try {
72
+ const settlement = await dispatchToCore(envelope, Object.assign(Object.assign({}, command), { flowHandleId }));
73
+ if (!settlement.ok) {
74
+ if (shouldResolveFlowRun) {
75
+ state.runtime.finishFlowRunResolution(envelope.requestId);
76
+ }
77
+ return settlement;
78
+ }
79
+ assertSettlementMatches({
80
+ envelope,
81
+ expectedKind: command.kind,
82
+ settlement,
83
+ });
84
+ const allocation = getFlowHandleFromSettlementDetail(settlement);
85
+ if ((allocation === null || allocation === void 0 ? void 0 : allocation.flowHandleId) !== flowHandleId ||
86
+ allocation.flowRunId === undefined) {
87
+ throw new Error(`Expected ${command.kind} settlement to include matching flowHandleId and flowRunId`);
88
+ }
89
+ if (shouldResolveFlowRun &&
90
+ !state.runtime.finishFlowRunResolution(envelope.requestId)) {
91
+ throw new Error(`Could not finish flow-run resolution for requestId "${envelope.requestId}"`);
92
+ }
93
+ state.runtime.bindFlowRun({
94
+ flowRunId: allocation.flowRunId,
95
+ activationPolicy: "never",
96
+ });
97
+ return settlement;
98
+ }
99
+ catch (error) {
100
+ if (shouldResolveFlowRun) {
101
+ state.runtime.finishFlowRunResolution(envelope.requestId);
102
+ }
103
+ throw error;
104
+ }
105
+ });
106
+ };
107
+ const dispatchHandleClose = async (envelope, command) => {
108
+ const flowHandleId = command.flowHandleId;
109
+ return flowHandleCommandStateStore.enqueue(flowHandleId, async (state) => {
110
+ const decision = planFlowHandleCommand({
111
+ command: "close",
112
+ runtime: state.runtime.getSnapshot(),
113
+ instanceId: envelope.instanceId,
114
+ });
115
+ if (decision === "ignore") {
116
+ return {
117
+ instanceId: envelope.instanceId,
118
+ kind: "close",
119
+ ok: true,
120
+ requestId: envelope.requestId,
121
+ };
122
+ }
123
+ if (decision === "forward-to-core") {
124
+ return dispatchToCore(envelope, command);
125
+ }
126
+ if (decision !== "orchestrate") {
127
+ throw new Error(`Unexpected flow-handle command decision for close: ${decision}`);
128
+ }
129
+ state.runtime.bindAffinity({ instanceId: envelope.instanceId });
130
+ const settlement = await dispatchToCore(envelope, command);
131
+ if (settlement.ok) {
132
+ assertSettlementMatches({
133
+ envelope,
134
+ expectedKind: "close",
135
+ settlement,
136
+ });
137
+ flowHandleCommandStateStore.invalidate(flowHandleId);
138
+ }
139
+ return settlement;
140
+ });
141
+ };
9
142
  return {
10
143
  dispatch: (envelope) => {
11
- const coreEnvelope = Object.assign(Object.assign({}, envelope), { command: toCoreCommandPayload(envelope.command) });
12
- return input.coreSession.dispatch(coreEnvelope);
144
+ switch (envelope.command.kind) {
145
+ case "open":
146
+ case "prerender":
147
+ return dispatchAllocation(envelope, envelope.command);
148
+ case "prefetch":
149
+ return dispatchToCore(envelope, envelope.command);
150
+ case "close":
151
+ return dispatchHandleClose(envelope, envelope.command);
152
+ default:
153
+ return dispatchToCore(envelope, envelope.command);
154
+ }
13
155
  },
14
156
  };
15
157
  }
@@ -197,10 +197,22 @@ export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewPr
197
197
  }
198
198
  export function NativeProviderRuntime(_a) {
199
199
  var { initOptions, instanceId, onCommandSessionChange } = _a, runtimeProps = __rest(_a, ["initOptions", "instanceId", "onCommandSessionChange"]);
200
+ const directSessionRef = useRef(null);
200
201
  const handleCommandSessionChange = useCallback((session) => {
201
- onCommandSessionChange === null || onCommandSessionChange === void 0 ? void 0 : onCommandSessionChange(session === null
202
- ? null
203
- : createNativeDirectCommandSession({ coreSession: session }));
202
+ var _a;
203
+ if (session === null) {
204
+ onCommandSessionChange === null || onCommandSessionChange === void 0 ? void 0 : onCommandSessionChange(null);
205
+ return;
206
+ }
207
+ if (((_a = directSessionRef.current) === null || _a === void 0 ? void 0 : _a.coreSession) !== session) {
208
+ directSessionRef.current = {
209
+ coreSession: session,
210
+ directSession: createNativeDirectCommandSession({
211
+ coreSession: session,
212
+ }),
213
+ };
214
+ }
215
+ onCommandSessionChange === null || onCommandSessionChange === void 0 ? void 0 : onCommandSessionChange(directSessionRef.current.directSession);
204
216
  }, [onCommandSessionChange]);
205
217
  const instanceRegistrationEnvelope = useMemo(() => createProviderCommandEnvelope({
206
218
  clientMeta: REACT_NATIVE_CLIENT_META,
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 : "3.0.67";
6
+ export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "3.0.71";
@@ -32,6 +32,209 @@ function createActiveViewSnapshotListController() {
32
32
  }
33
33
  };
34
34
  }
35
+ // ../view-orchestration/src/flow-handle-runtime-machine.ts
36
+ import { createMachine, interpret, reduce, state, transition } from "robot3";
37
+ var FLOW_HANDLE_RUNTIME_STATE_SET = {
38
+ active: true,
39
+ invalidated: true
40
+ };
41
+ var hasFlowHandleAffinityConflict = (input) => input.boundInstanceId !== undefined && input.boundInstanceId !== input.instanceId || input.flowId !== undefined && input.boundFlowId !== undefined && input.boundFlowId !== input.flowId;
42
+ var assertAffinityCompatible = (input) => {
43
+ const { context, event } = input;
44
+ if (!hasFlowHandleAffinityConflict({
45
+ boundInstanceId: context.instanceId,
46
+ boundFlowId: context.flowId,
47
+ instanceId: event.instanceId,
48
+ flowId: event.flowId
49
+ })) {
50
+ return;
51
+ }
52
+ if (context.instanceId !== undefined && context.instanceId !== event.instanceId) {
53
+ throw new Error(`Flow handle affinity conflict: instanceId already bound to "${context.instanceId}" and cannot be rebound to "${event.instanceId}"`);
54
+ }
55
+ throw new Error(`Flow handle affinity conflict: flowId already bound to "${context.flowId}" and cannot be rebound to "${event.flowId}"`);
56
+ };
57
+ var bindAffinity = reduce((context, event) => {
58
+ assertAffinityCompatible({ context, event });
59
+ return {
60
+ ...context,
61
+ instanceId: event.instanceId,
62
+ flowId: event.flowId ?? context.flowId
63
+ };
64
+ });
65
+ var bindFlowRun = reduce((context, event) => {
66
+ if (context.instanceId === undefined) {
67
+ throw new Error("Cannot bind flowRunId before instance affinity is bound for flow handle runtime");
68
+ }
69
+ return {
70
+ ...context,
71
+ flowRunId: event.flowRunId
72
+ };
73
+ });
74
+ var beginFlowRunResolution = reduce((context, event) => {
75
+ if (context.instanceId === undefined) {
76
+ throw new Error("Cannot begin flowRun resolution before instance affinity is bound for flow handle runtime");
77
+ }
78
+ if (context.pendingFlowRunResolutionRequestId !== null && context.pendingFlowRunResolutionRequestId !== event.requestId) {
79
+ throw new Error(`Flow handle already has a pending flowRun resolution for requestId "${context.pendingFlowRunResolutionRequestId}"`);
80
+ }
81
+ return {
82
+ ...context,
83
+ pendingFlowRunResolutionRequestId: event.requestId
84
+ };
85
+ });
86
+ var clearFlowRunResolution = reduce((context, event) => context.pendingFlowRunResolutionRequestId === event.requestId ? { ...context, pendingFlowRunResolutionRequestId: null } : context);
87
+ var setContainerMode = reduce((context, event) => {
88
+ if (context.containerMode !== undefined && context.containerMode !== event.mode) {
89
+ throw new Error(`Flow handle container mode conflict: already locked to "${context.containerMode}" and cannot switch to "${event.mode}"`);
90
+ }
91
+ return {
92
+ ...context,
93
+ containerMode: event.mode
94
+ };
95
+ });
96
+ var invalidateRuntime = reduce((context) => ({
97
+ ...context,
98
+ instanceId: undefined,
99
+ flowId: undefined,
100
+ flowRunId: null,
101
+ pendingFlowRunResolutionRequestId: null,
102
+ containerMode: undefined
103
+ }));
104
+ function createFlowHandleRuntimeMachine() {
105
+ const states = {
106
+ active: state(transition("bindAffinity", "active", bindAffinity), transition("bindFlowRun", "active", bindFlowRun), transition("beginFlowRunResolution", "active", beginFlowRunResolution), transition("finishFlowRunResolution", "active", clearFlowRunResolution), transition("setContainerMode", "active", setContainerMode), transition("invalidate", "invalidated", invalidateRuntime)),
107
+ invalidated: state()
108
+ };
109
+ return createMachine(states, () => ({
110
+ instanceId: undefined,
111
+ flowId: undefined,
112
+ flowRunId: null,
113
+ pendingFlowRunResolutionRequestId: null,
114
+ containerMode: undefined
115
+ }));
116
+ }
117
+ function createFlowHandleRuntimeController() {
118
+ const machine = createFlowHandleRuntimeMachine();
119
+ const service = interpret(machine, () => {});
120
+ const buildSnapshot = (svc) => {
121
+ const context = svc.context;
122
+ return {
123
+ state: readFlowHandleRuntimeState(svc.machine.current),
124
+ instanceId: context.instanceId,
125
+ flowId: context.flowId,
126
+ flowRunId: context.flowRunId,
127
+ pendingFlowRunResolutionRequestId: context.pendingFlowRunResolutionRequestId,
128
+ containerMode: context.containerMode
129
+ };
130
+ };
131
+ return {
132
+ getSnapshot: () => buildSnapshot(service),
133
+ bindAffinity: (input) => {
134
+ service.send({
135
+ type: "bindAffinity",
136
+ instanceId: input.instanceId,
137
+ ...input.flowId !== undefined ? { flowId: input.flowId } : {}
138
+ });
139
+ },
140
+ bindFlowRun: (input) => {
141
+ if (readFlowHandleRuntimeState(service.machine.current) === "invalidated") {
142
+ return "ignore";
143
+ }
144
+ const previousFlowRunId = service.context.flowRunId;
145
+ service.send({
146
+ type: "bindFlowRun",
147
+ flowRunId: input.flowRunId
148
+ });
149
+ if (input.activationPolicy === "always" || input.activationPolicy === "on-flow-run-change" && previousFlowRunId !== input.flowRunId) {
150
+ return "request-activation";
151
+ }
152
+ return "register";
153
+ },
154
+ beginFlowRunResolution: (requestId) => {
155
+ if (readFlowHandleRuntimeState(service.machine.current) === "invalidated") {
156
+ return false;
157
+ }
158
+ service.send({ type: "beginFlowRunResolution", requestId });
159
+ return true;
160
+ },
161
+ finishFlowRunResolution: (requestId) => {
162
+ if (readFlowHandleRuntimeState(service.machine.current) === "invalidated" || service.context.pendingFlowRunResolutionRequestId !== requestId) {
163
+ return false;
164
+ }
165
+ service.send({ type: "finishFlowRunResolution", requestId });
166
+ return true;
167
+ },
168
+ setContainerMode: (mode) => {
169
+ service.send({ type: "setContainerMode", mode });
170
+ },
171
+ invalidate: () => {
172
+ service.send({ type: "invalidate" });
173
+ }
174
+ };
175
+ }
176
+ function readFlowHandleRuntimeState(value) {
177
+ if (!isFlowHandleRuntimeState(value)) {
178
+ throw new Error(`Unknown flow-handle runtime state: ${String(value)}`);
179
+ }
180
+ return value;
181
+ }
182
+ function isFlowHandleRuntimeState(value) {
183
+ return typeof value === "string" && value in FLOW_HANDLE_RUNTIME_STATE_SET;
184
+ }
185
+
186
+ // ../view-orchestration/src/flow-handle-command-planner.ts
187
+ var planFlowHandleCommand = (input) => {
188
+ if (input.runtime.state === "invalidated") {
189
+ return input.command === "open" || input.command === "prerender" ? "forward-to-core" : "ignore";
190
+ }
191
+ if (hasFlowHandleAffinityConflict({
192
+ boundInstanceId: input.runtime.instanceId,
193
+ boundFlowId: input.runtime.flowId,
194
+ instanceId: input.instanceId,
195
+ ...input.command === "open" || input.command === "prerender" ? { flowId: input.flowId } : {}
196
+ })) {
197
+ return input.command === "setContainer" ? "reject" : "forward-to-core";
198
+ }
199
+ return "orchestrate";
200
+ };
201
+ // ../view-orchestration/src/flow-handle-command-state-store.ts
202
+ var createInitialFlowHandleCommandState = () => ({
203
+ runtime: createFlowHandleRuntimeController()
204
+ });
205
+
206
+ class FlowHandleCommandStateStore {
207
+ flowHandles = new Map;
208
+ getOrCreate(flowHandleId) {
209
+ const existing = this.flowHandles.get(flowHandleId);
210
+ if (existing) {
211
+ return existing;
212
+ }
213
+ const created = {
214
+ state: createInitialFlowHandleCommandState(),
215
+ tail: Promise.resolve()
216
+ };
217
+ this.flowHandles.set(flowHandleId, created);
218
+ return created;
219
+ }
220
+ enqueue(flowHandleId, task) {
221
+ const entry = this.getOrCreate(flowHandleId);
222
+ const nextTask = entry.tail.then(() => task(entry.state), () => task(entry.state));
223
+ entry.tail = nextTask.then(() => {
224
+ return;
225
+ }, () => {
226
+ return;
227
+ });
228
+ return nextTask;
229
+ }
230
+ invalidate(flowHandleId) {
231
+ const state2 = this.getOrCreate(flowHandleId).state;
232
+ if (state2.runtime.getSnapshot().state !== "invalidated") {
233
+ state2.runtime.invalidate();
234
+ }
235
+ return state2;
236
+ }
237
+ }
35
238
  // ../view-orchestration/src/pending-command-tracker.ts
36
239
  var createDeferred = () => {
37
240
  let resolve;
@@ -84,7 +287,7 @@ class PendingCommandTracker {
84
287
  }
85
288
  }
86
289
  // ../view-orchestration/src/view-activation-machine.ts
87
- import { createMachine, interpret, state, transition } from "robot3";
290
+ import { createMachine as createMachine2, interpret as interpret2, state as state2, transition as transition2 } from "robot3";
88
291
  var VIEW_ACTIVATION_STATES = {
89
292
  idle: true,
90
293
  prerendering: true,
@@ -95,18 +298,18 @@ var VIEW_ACTIVATION_STATES = {
95
298
  };
96
299
  function createViewActivationMachine() {
97
300
  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()
301
+ idle: state2(transition2("startPrerender", "prerendering"), transition2("requestActivation", "activation-requested")),
302
+ prerendering: state2(transition2("requestActivation", "prerendering-activation-requested"), transition2("markPrerendered", "prerendered")),
303
+ "activation-requested": state2(transition2("startPrerender", "prerendering-activation-requested")),
304
+ "prerendering-activation-requested": state2(transition2("markPrerendered", "active")),
305
+ prerendered: state2(transition2("requestActivation", "active")),
306
+ active: state2()
104
307
  };
105
- return createMachine(states, () => ({}));
308
+ return createMachine2(states, () => ({}));
106
309
  }
107
310
  function createViewActivationController() {
108
311
  const machine = createViewActivationMachine();
109
- const service = interpret(machine, () => {});
312
+ const service = interpret2(machine, () => {});
110
313
  const getState = () => readViewActivationState(service.machine.current);
111
314
  const send = (event) => {
112
315
  service.send(event);
@@ -133,22 +336,22 @@ function readViewActivationState(value) {
133
336
  // ../view-orchestration/src/view-preparation-readiness-registry.ts
134
337
  function createViewPreparationReadinessRegistry() {
135
338
  const states = new Map;
136
- const resolveReadySize = (state2) => {
137
- if (!state2 || state2.status === "ready" || !state2.hostPrepared || state2.size === null) {
339
+ const resolveReadySize = (state3) => {
340
+ if (!state3 || state3.status === "ready" || !state3.hostPrepared || state3.size === null) {
138
341
  return null;
139
342
  }
140
- state2.status = "ready";
141
- return state2.size;
343
+ state3.status = "ready";
344
+ return state3.size;
142
345
  };
143
346
  return {
144
347
  begin(viewId) {
145
- const state2 = {
348
+ const state3 = {
146
349
  hostPrepared: false,
147
350
  size: null,
148
351
  status: "pending"
149
352
  };
150
- states.set(viewId, state2);
151
- const getCurrentState = () => states.get(viewId) === state2 ? state2 : null;
353
+ states.set(viewId, state3);
354
+ const getCurrentState = () => states.get(viewId) === state3 ? state3 : null;
152
355
  return {
153
356
  markHostPrepared() {
154
357
  const current = getCurrentState();
@@ -266,6 +469,12 @@ function createViewPreparationRegistry() {
266
469
  function createPendingCommandTracker() {
267
470
  return new PendingCommandTracker;
268
471
  }
472
+ function createFlowHandleCommandStateStore() {
473
+ return new FlowHandleCommandStateStore;
474
+ }
475
+ function planFlowHandleCommand2(input) {
476
+ return planFlowHandleCommand(input);
477
+ }
269
478
  function createActiveViewSnapshotListController2() {
270
479
  return createActiveViewSnapshotListController();
271
480
  }
@@ -279,9 +488,11 @@ function createViewPreparationReadinessRegistry2() {
279
488
  return createViewPreparationReadinessRegistry();
280
489
  }
281
490
  export {
491
+ planFlowHandleCommand2 as planFlowHandleCommand,
282
492
  createViewPreparationRegistry2 as createViewPreparationRegistry,
283
493
  createViewPreparationReadinessRegistry2 as createViewPreparationReadinessRegistry,
284
494
  createViewActivationController2 as createViewActivationController,
285
495
  createPendingCommandTracker,
496
+ createFlowHandleCommandStateStore,
286
497
  createActiveViewSnapshotListController2 as createActiveViewSnapshotListController
287
498
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getuserfeedback/react-native",
3
- "version": "3.0.67",
3
+ "version": "3.0.71",
4
4
  "description": "getuserfeedback React Native SDK",
5
5
  "keywords": [
6
6
  "getuserfeedback",
@@ -44,8 +44,8 @@
44
44
  "lint": "biome check ."
45
45
  },
46
46
  "dependencies": {
47
- "@getuserfeedback/protocol": "^3.18.1",
48
- "@getuserfeedback/sdk": "^0.12.1",
47
+ "@getuserfeedback/protocol": "^3.18.3",
48
+ "@getuserfeedback/sdk": "^0.12.3",
49
49
  "robot3": "^1.2.0"
50
50
  },
51
51
  "peerDependencies": {