@getuserfeedback/react-native 3.0.67 → 3.0.72
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/native-core-command-channel.d.ts +2 -0
- package/dist/native-core-command-channel.js +37 -8
- package/dist/native-core-command-session.d.ts +2 -0
- package/dist/native-core-command-session.js +19 -2
- package/dist/native-direct-command-session.d.ts +15 -12
- package/dist/native-direct-command-session.js +157 -8
- package/dist/native-runtime-root.js +15 -3
- package/dist/version.js +1 -1
- package/dist/view-orchestration-runtime.js +227 -16
- package/package.json +3 -3
|
@@ -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
|
-
|
|
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
|
-
(
|
|
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
|
|
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 [...
|
|
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
|
|
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
|
-
|
|
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
|
|
5
|
-
kind: "open"
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
kind: "
|
|
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,164 @@
|
|
|
1
|
+
import { getFlowHandleFromSettlementDetail } from "@getuserfeedback/protocol/host";
|
|
1
2
|
import { toCoreCommandPayload, } from "@getuserfeedback/protocol/internal/public-core-command";
|
|
2
|
-
|
|
3
|
-
|
|
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 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
|
+
// The direct session shares the core session lifetime; core-session disposal clears this listener.
|
|
9
|
+
input.coreSession.subscribeHandleInvalidated((message) => {
|
|
10
|
+
if (message.handleKind === "flow") {
|
|
11
|
+
flowHandleCommandStateStore.invalidate(message.handleId);
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
const dispatchToCore = (envelope, command) => {
|
|
15
|
+
const coreEnvelope = Object.assign(Object.assign({}, envelope), { command: toCoreCommandPayload(command) });
|
|
16
|
+
return input.coreSession.dispatch(coreEnvelope);
|
|
17
|
+
};
|
|
18
|
+
const assertSettlementMatches = (input) => {
|
|
19
|
+
if (input.settlement.requestId !== input.envelope.requestId ||
|
|
20
|
+
input.settlement.instanceId !== input.envelope.instanceId ||
|
|
21
|
+
input.settlement.kind !== input.expectedKind) {
|
|
22
|
+
throw new Error(`Expected ${input.expectedKind} settlement for requestId "${input.envelope.requestId}" and instanceId "${input.envelope.instanceId}"`);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const dispatchAllocation = async (envelope, command) => {
|
|
26
|
+
if (command.flowHandleId === undefined) {
|
|
27
|
+
const settlement = await dispatchToCore(envelope, command);
|
|
28
|
+
if (!settlement.ok) {
|
|
29
|
+
return settlement;
|
|
30
|
+
}
|
|
31
|
+
assertSettlementMatches({
|
|
32
|
+
envelope,
|
|
33
|
+
expectedKind: command.kind,
|
|
34
|
+
settlement,
|
|
35
|
+
});
|
|
36
|
+
const allocation = getFlowHandleFromSettlementDetail(settlement);
|
|
37
|
+
if (!(allocation === null || allocation === void 0 ? void 0 : allocation.flowRunId)) {
|
|
38
|
+
throw new Error(`Expected ${command.kind} settlement to include flowHandleId and flowRunId`);
|
|
39
|
+
}
|
|
40
|
+
const { flowHandleId, flowRunId } = allocation;
|
|
41
|
+
await flowHandleCommandStateStore.enqueue(flowHandleId, async (state) => {
|
|
42
|
+
state.runtime.bindAffinity({
|
|
43
|
+
instanceId: envelope.instanceId,
|
|
44
|
+
flowId: command.flowId,
|
|
45
|
+
});
|
|
46
|
+
state.runtime.bindFlowRun({
|
|
47
|
+
flowRunId,
|
|
48
|
+
activationPolicy: "never",
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
return settlement;
|
|
52
|
+
}
|
|
53
|
+
const flowHandleId = command.flowHandleId;
|
|
54
|
+
return flowHandleCommandStateStore.enqueue(flowHandleId, async (state) => {
|
|
55
|
+
const runtime = state.runtime.getSnapshot();
|
|
56
|
+
const decision = planFlowHandleCommand({
|
|
57
|
+
command: command.kind,
|
|
58
|
+
runtime,
|
|
59
|
+
instanceId: envelope.instanceId,
|
|
60
|
+
flowId: command.flowId,
|
|
61
|
+
});
|
|
62
|
+
if (decision === "forward-to-core") {
|
|
63
|
+
return dispatchToCore(envelope, Object.assign(Object.assign({}, command), { flowHandleId }));
|
|
64
|
+
}
|
|
65
|
+
if (decision !== "orchestrate") {
|
|
66
|
+
throw new Error(`Unexpected flow-handle command decision for ${command.kind}: ${decision}`);
|
|
67
|
+
}
|
|
68
|
+
state.runtime.bindAffinity({
|
|
69
|
+
instanceId: envelope.instanceId,
|
|
70
|
+
flowId: command.flowId,
|
|
71
|
+
});
|
|
72
|
+
const shouldResolveFlowRun = runtime.flowRunId === null;
|
|
73
|
+
if (shouldResolveFlowRun &&
|
|
74
|
+
!state.runtime.beginFlowRunResolution(envelope.requestId)) {
|
|
75
|
+
return dispatchToCore(envelope, Object.assign(Object.assign({}, command), { flowHandleId }));
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const settlement = await dispatchToCore(envelope, Object.assign(Object.assign({}, command), { flowHandleId }));
|
|
79
|
+
if (!settlement.ok) {
|
|
80
|
+
if (shouldResolveFlowRun) {
|
|
81
|
+
state.runtime.finishFlowRunResolution(envelope.requestId);
|
|
82
|
+
}
|
|
83
|
+
return settlement;
|
|
84
|
+
}
|
|
85
|
+
assertSettlementMatches({
|
|
86
|
+
envelope,
|
|
87
|
+
expectedKind: command.kind,
|
|
88
|
+
settlement,
|
|
89
|
+
});
|
|
90
|
+
const allocation = getFlowHandleFromSettlementDetail(settlement);
|
|
91
|
+
if ((allocation === null || allocation === void 0 ? void 0 : allocation.flowHandleId) !== flowHandleId ||
|
|
92
|
+
allocation.flowRunId === undefined) {
|
|
93
|
+
throw new Error(`Expected ${command.kind} settlement to include matching flowHandleId and flowRunId`);
|
|
94
|
+
}
|
|
95
|
+
if (shouldResolveFlowRun &&
|
|
96
|
+
!state.runtime.finishFlowRunResolution(envelope.requestId) &&
|
|
97
|
+
state.runtime.getSnapshot().state !== "invalidated") {
|
|
98
|
+
throw new Error(`Could not finish flow-run resolution for requestId "${envelope.requestId}"`);
|
|
99
|
+
}
|
|
100
|
+
state.runtime.bindFlowRun({
|
|
101
|
+
flowRunId: allocation.flowRunId,
|
|
102
|
+
activationPolicy: "never",
|
|
103
|
+
});
|
|
104
|
+
return settlement;
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
if (shouldResolveFlowRun) {
|
|
108
|
+
state.runtime.finishFlowRunResolution(envelope.requestId);
|
|
109
|
+
}
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
const dispatchHandleClose = async (envelope, command) => {
|
|
115
|
+
const flowHandleId = command.flowHandleId;
|
|
116
|
+
return flowHandleCommandStateStore.enqueue(flowHandleId, async (state) => {
|
|
117
|
+
const decision = planFlowHandleCommand({
|
|
118
|
+
command: "close",
|
|
119
|
+
runtime: state.runtime.getSnapshot(),
|
|
120
|
+
instanceId: envelope.instanceId,
|
|
121
|
+
});
|
|
122
|
+
if (decision === "ignore") {
|
|
123
|
+
return {
|
|
124
|
+
instanceId: envelope.instanceId,
|
|
125
|
+
kind: "close",
|
|
126
|
+
ok: true,
|
|
127
|
+
requestId: envelope.requestId,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (decision === "forward-to-core") {
|
|
131
|
+
return dispatchToCore(envelope, command);
|
|
132
|
+
}
|
|
133
|
+
if (decision !== "orchestrate") {
|
|
134
|
+
throw new Error(`Unexpected flow-handle command decision for close: ${decision}`);
|
|
135
|
+
}
|
|
136
|
+
state.runtime.bindAffinity({ instanceId: envelope.instanceId });
|
|
137
|
+
const settlement = await dispatchToCore(envelope, command);
|
|
138
|
+
if (settlement.ok) {
|
|
139
|
+
assertSettlementMatches({
|
|
140
|
+
envelope,
|
|
141
|
+
expectedKind: "close",
|
|
142
|
+
settlement,
|
|
143
|
+
});
|
|
144
|
+
flowHandleCommandStateStore.invalidate(flowHandleId);
|
|
145
|
+
}
|
|
146
|
+
return settlement;
|
|
147
|
+
});
|
|
148
|
+
};
|
|
9
149
|
return {
|
|
10
150
|
dispatch: (envelope) => {
|
|
11
|
-
|
|
12
|
-
|
|
151
|
+
switch (envelope.command.kind) {
|
|
152
|
+
case "open":
|
|
153
|
+
case "prerender":
|
|
154
|
+
return dispatchAllocation(envelope, envelope.command);
|
|
155
|
+
case "prefetch":
|
|
156
|
+
return dispatchToCore(envelope, envelope.command);
|
|
157
|
+
case "close":
|
|
158
|
+
return dispatchHandleClose(envelope, envelope.command);
|
|
159
|
+
default:
|
|
160
|
+
return dispatchToCore(envelope, envelope.command);
|
|
161
|
+
}
|
|
13
162
|
},
|
|
14
163
|
};
|
|
15
164
|
}
|
|
@@ -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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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.
|
|
6
|
+
export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "3.0.72";
|
|
@@ -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:
|
|
99
|
-
prerendering:
|
|
100
|
-
"activation-requested":
|
|
101
|
-
"prerendering-activation-requested":
|
|
102
|
-
prerendered:
|
|
103
|
-
active:
|
|
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
|
|
308
|
+
return createMachine2(states, () => ({}));
|
|
106
309
|
}
|
|
107
310
|
function createViewActivationController() {
|
|
108
311
|
const machine = createViewActivationMachine();
|
|
109
|
-
const service =
|
|
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 = (
|
|
137
|
-
if (!
|
|
339
|
+
const resolveReadySize = (state3) => {
|
|
340
|
+
if (!state3 || state3.status === "ready" || !state3.hostPrepared || state3.size === null) {
|
|
138
341
|
return null;
|
|
139
342
|
}
|
|
140
|
-
|
|
141
|
-
return
|
|
343
|
+
state3.status = "ready";
|
|
344
|
+
return state3.size;
|
|
142
345
|
};
|
|
143
346
|
return {
|
|
144
347
|
begin(viewId) {
|
|
145
|
-
const
|
|
348
|
+
const state3 = {
|
|
146
349
|
hostPrepared: false,
|
|
147
350
|
size: null,
|
|
148
351
|
status: "pending"
|
|
149
352
|
};
|
|
150
|
-
states.set(viewId,
|
|
151
|
-
const getCurrentState = () => states.get(viewId) ===
|
|
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.
|
|
3
|
+
"version": "3.0.72",
|
|
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.
|
|
48
|
-
"@getuserfeedback/sdk": "^0.12.
|
|
47
|
+
"@getuserfeedback/protocol": "^3.18.3",
|
|
48
|
+
"@getuserfeedback/sdk": "^0.12.3",
|
|
49
49
|
"robot3": "^1.2.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|