@getuserfeedback/react-native 5.3.4 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +25 -3
- package/dist/core-webview-bridge-adapter.js +1 -1
- package/dist/native-core-command-channel.d.ts +8 -0
- package/dist/native-core-command-channel.js +28 -0
- package/dist/native-host-action-rpc.d.ts +15 -0
- package/dist/native-host-action-rpc.js +180 -0
- package/dist/native-provider-client-controller.d.ts +2 -0
- package/dist/native-provider-client-controller.js +16 -0
- package/dist/native-provider-client.d.ts +3 -1
- package/dist/native-provider-client.js +3 -1
- package/dist/native-provider.d.ts +1 -1
- package/dist/native-provider.js +33 -5
- package/dist/native-runtime-endpoints.d.ts +1 -1
- package/dist/native-runtime-endpoints.js +1 -1
- package/dist/native-runtime-root.d.ts +10 -2
- package/dist/native-runtime-root.js +22 -3
- package/dist/native-view-record-controller.d.ts +3 -0
- package/dist/native-view-record-controller.js +40 -2
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AppEventExternalId, PublicCommandPayload } from "@getuserfeedback/protocol";
|
|
2
|
-
import type { CapabilitiesInput, ConfigureOptions, EventOptions, EventProperties, GraphOperations, InitOptions } from "@getuserfeedback/sdk";
|
|
3
|
-
export type { EventOptions, EventProperties, TrackOptions, } from "@getuserfeedback/sdk";
|
|
2
|
+
import type { ActionRegistration, CapabilitiesInput, ConfigureOptions, EventOptions, EventProperties, GraphOperations, InitOptions, OpenRequestedCallback } from "@getuserfeedback/sdk";
|
|
3
|
+
export type { EventOptions, EventProperties, OpenRequestedCallback, OpenRequestedEvent, TrackOptions, } from "@getuserfeedback/sdk";
|
|
4
4
|
type IdentifyTraits = Record<string, unknown>;
|
|
5
5
|
type OpenCommand = Extract<PublicCommandPayload, {
|
|
6
6
|
kind: "open";
|
|
@@ -19,8 +19,14 @@ type HostContext = Extract<PublicCommandPayload, {
|
|
|
19
19
|
type CommandOptions = {
|
|
20
20
|
idempotencyKey?: string;
|
|
21
21
|
};
|
|
22
|
+
/** A customer-defined action implemented by the React Native host app. */
|
|
23
|
+
export type NativeActionRegistration = Extract<ActionRegistration, {
|
|
24
|
+
kind: "custom";
|
|
25
|
+
}>;
|
|
22
26
|
/** React Native provider initialization options. */
|
|
23
|
-
export type ClientOptions = Omit<InitOptions, "capabilities" | "hostCapabilities"> & {
|
|
27
|
+
export type ClientOptions = Omit<InitOptions, "capabilities" | "hostActions" | "hostCapabilities"> & {
|
|
28
|
+
/** Widget actions implemented by the current app version. */
|
|
29
|
+
actions?: NativeActionRegistration[] | undefined;
|
|
24
30
|
/** Capabilities supported by the current app version. */
|
|
25
31
|
capabilities?: CapabilitiesInput | undefined;
|
|
26
32
|
};
|
|
@@ -65,6 +71,22 @@ export interface Client {
|
|
|
65
71
|
flow: (flowId: string) => FlowRun;
|
|
66
72
|
/** Closes every open flow owned by this provider. */
|
|
67
73
|
close: () => Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Subscribe to explicit SDK and client-side targeting open requests, or
|
|
76
|
+
* suppress them, for example when the user should not be interrupted.
|
|
77
|
+
* Prevention applies only to that presentation attempt; it does not create a
|
|
78
|
+
* public retry schedule.
|
|
79
|
+
*
|
|
80
|
+
* @example Suppress open requests while the user is busy
|
|
81
|
+
* ```ts
|
|
82
|
+
* client.onOpenRequested((event) => {
|
|
83
|
+
* if (userIsBusy()) {
|
|
84
|
+
* event.preventDefault();
|
|
85
|
+
* }
|
|
86
|
+
* });
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
onOpenRequested: (callback: OpenRequestedCallback) => () => void;
|
|
68
90
|
/** Resets widget state, authentication, and the current user identity. */
|
|
69
91
|
reset: (options?: CommandOptions) => Promise<void>;
|
|
70
92
|
/** Updates app navigation and environment context used by targeting. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CORE_HOST_BOOTSTRAP_GLOBAL_KEY, CORE_HOST_BOOTSTRAP_PROTOCOL_VERSION, CORE_HOST_BOOTSTRAP_READY_EVENT_NAME, CORE_HOST_BOOTSTRAP_REQUIRED_FRAGMENT, } from "@getuserfeedback/protocol/internal/core-host-bootstrap";
|
|
1
|
+
import { CORE_HOST_BOOTSTRAP_GLOBAL_KEY, CORE_HOST_BOOTSTRAP_PROTOCOL_VERSION, CORE_HOST_BOOTSTRAP_READY_EVENT_NAME, CORE_HOST_BOOTSTRAP_REQUIRED_FRAGMENT, } from "@getuserfeedback/protocol/internal/core-host-bootstrap-v3";
|
|
2
2
|
import { parseWebViewConnectionBridgeMessage, } from "./webview-connection-bridge-message.js";
|
|
3
3
|
import { buildWebViewConnectionBridgeScript, } from "./webview-connection-bridge-script.js";
|
|
4
4
|
import { buildWebViewConnectionHostMessageScript, serializeForJavaScript, } from "./webview-connection-host-message-script.js";
|
|
@@ -2,6 +2,7 @@ import { type CoreCommandEnvelope } from "@getuserfeedback/protocol";
|
|
|
2
2
|
import { type EventHandleInvalidated } from "@getuserfeedback/protocol/internal/core-handle-invalidation";
|
|
3
3
|
import { type EventCommandSettled } from "@getuserfeedback/protocol/internal/core-host-command-settlement";
|
|
4
4
|
import type { CoreWebViewHostConnection } from "./core-webview-host-controller.js";
|
|
5
|
+
import { createNativeHostActionRpcHandler } from "./native-host-action-rpc.js";
|
|
5
6
|
export type NativeCoreCommandChannel = {
|
|
6
7
|
post: (envelope: CoreCommandEnvelope) => void;
|
|
7
8
|
subscribe: (listener: (message: EventCommandSettled) => void) => () => void;
|
|
@@ -12,7 +13,14 @@ type OwnedNativeCoreCommandChannel = {
|
|
|
12
13
|
};
|
|
13
14
|
export declare function createNativeCoreCommandChannel(input: {
|
|
14
15
|
coreConnection: CoreWebViewHostConnection;
|
|
16
|
+
hostActions?: {
|
|
17
|
+
getHandler: Parameters<typeof createNativeHostActionRpcHandler>[0]["getHandler"];
|
|
18
|
+
getActiveViewIds: () => readonly string[];
|
|
19
|
+
instanceId: string;
|
|
20
|
+
subscribeToViewActivity: (listener: () => void) => () => void;
|
|
21
|
+
};
|
|
15
22
|
onHandleInvalidated?: (message: EventHandleInvalidated) => void;
|
|
23
|
+
onHostActionError?: (error: Error) => void;
|
|
16
24
|
onInvalidMessage?: (error: Error, value: unknown) => void;
|
|
17
25
|
onSessionFailure?: (error: Error) => void;
|
|
18
26
|
}): OwnedNativeCoreCommandChannel;
|
|
@@ -3,11 +3,34 @@ import { eventHandleInvalidatedSchema, } from "@getuserfeedback/protocol/interna
|
|
|
3
3
|
import { eventCommandSettledSchema, } from "@getuserfeedback/protocol/internal/core-host-command-settlement";
|
|
4
4
|
import { CORE_SESSION_FAILED_CODE, eventCoreSessionFailedSchema, } from "@getuserfeedback/protocol/internal/core-session-failure";
|
|
5
5
|
import { buildRpcResponse, rpcRequestSchema, } from "@getuserfeedback/protocol/internal/v3-rpc-messages";
|
|
6
|
+
import { createNativeHostActionRpcHandler } from "./native-host-action-rpc.js";
|
|
6
7
|
const DISPOSED_MESSAGE = "Core command channel is disposed";
|
|
7
8
|
const isRecord = (value) => typeof value === "object" && value !== null;
|
|
8
9
|
export function createNativeCoreCommandChannel(input) {
|
|
10
|
+
var _a;
|
|
9
11
|
const listeners = new Set();
|
|
10
12
|
let disposed = false;
|
|
13
|
+
const hostActions = input.hostActions;
|
|
14
|
+
const hostActionRpcHandler = hostActions
|
|
15
|
+
? createNativeHostActionRpcHandler({
|
|
16
|
+
generation: input.coreConnection.generation,
|
|
17
|
+
getHandler: hostActions.getHandler,
|
|
18
|
+
instanceId: hostActions.instanceId,
|
|
19
|
+
isViewActive: (viewId) => hostActions.getActiveViewIds().includes(viewId),
|
|
20
|
+
onError: input.onHostActionError,
|
|
21
|
+
postToCore: input.coreConnection.transport.postMessage,
|
|
22
|
+
})
|
|
23
|
+
: null;
|
|
24
|
+
let activeViewIds = new Set((_a = hostActions === null || hostActions === void 0 ? void 0 : hostActions.getActiveViewIds()) !== null && _a !== void 0 ? _a : []);
|
|
25
|
+
const unsubscribeViewActivity = hostActions === null || hostActions === void 0 ? void 0 : hostActions.subscribeToViewActivity(() => {
|
|
26
|
+
const nextActiveViewIds = new Set(hostActions.getActiveViewIds());
|
|
27
|
+
for (const viewId of activeViewIds) {
|
|
28
|
+
if (!nextActiveViewIds.has(viewId)) {
|
|
29
|
+
hostActionRpcHandler === null || hostActionRpcHandler === void 0 ? void 0 : hostActionRpcHandler.releaseView(viewId);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
activeViewIds = nextActiveViewIds;
|
|
33
|
+
});
|
|
11
34
|
const handleMessage = (value) => {
|
|
12
35
|
var _a, _b, _c, _d, _e, _f;
|
|
13
36
|
if (disposed || !isRecord(value)) {
|
|
@@ -19,6 +42,9 @@ export function createNativeCoreCommandChannel(input) {
|
|
|
19
42
|
parsed.data.gen !== input.coreConnection.generation) {
|
|
20
43
|
return;
|
|
21
44
|
}
|
|
45
|
+
if (hostActionRpcHandler === null || hostActionRpcHandler === void 0 ? void 0 : hostActionRpcHandler.handle(parsed.data)) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
22
48
|
let result;
|
|
23
49
|
if (parsed.data.method === TRAFFIC_TELEMETRY_RPC_METHOD) {
|
|
24
50
|
const params = trafficTelemetryRpcParamsSchema.safeParse(parsed.data.params);
|
|
@@ -133,6 +159,8 @@ export function createNativeCoreCommandChannel(input) {
|
|
|
133
159
|
return;
|
|
134
160
|
}
|
|
135
161
|
disposed = true;
|
|
162
|
+
unsubscribeViewActivity === null || unsubscribeViewActivity === void 0 ? void 0 : unsubscribeViewActivity();
|
|
163
|
+
hostActionRpcHandler === null || hostActionRpcHandler === void 0 ? void 0 : hostActionRpcHandler.dispose();
|
|
136
164
|
unsubscribeTransport();
|
|
137
165
|
listeners.clear();
|
|
138
166
|
},
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HostActionDefinition } from "@getuserfeedback/protocol/host";
|
|
2
|
+
import { type RpcRequest } from "@getuserfeedback/protocol/internal/v3-rpc-messages";
|
|
3
|
+
export declare const createNativeHostActionRpcHandler: (input: {
|
|
4
|
+
generation: number;
|
|
5
|
+
getHandler: (definition: HostActionDefinition) => (() => void | Promise<void>) | undefined;
|
|
6
|
+
instanceId: string;
|
|
7
|
+
isViewActive: (viewId: string) => boolean;
|
|
8
|
+
now?: () => number;
|
|
9
|
+
onError?: (error: Error) => void;
|
|
10
|
+
postToCore: (message: unknown) => void;
|
|
11
|
+
}) => {
|
|
12
|
+
dispose: () => void;
|
|
13
|
+
handle: (request: RpcRequest) => boolean;
|
|
14
|
+
releaseView: (viewId: string) => void;
|
|
15
|
+
};
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { buildFlowActionSucceededV1 } from "@getuserfeedback/protocol";
|
|
13
|
+
import { HOST_ACTION_EXECUTE_RPC_METHOD, HOST_ACTION_SETTLEMENT_AVAILABILITY_RPC_METHOD, toHostActionExecuteParams, } from "@getuserfeedback/protocol/internal/host-action-rpc";
|
|
14
|
+
import { buildEventFlowActionSucceeded } from "@getuserfeedback/protocol/internal/host-core-flow-action-succeeded";
|
|
15
|
+
import { buildRpcResponse, } from "@getuserfeedback/protocol/internal/v3-rpc-messages";
|
|
16
|
+
const isSameExecution = (execution, request, params) => execution.attemptId === request.idemKey &&
|
|
17
|
+
execution.params.viewId === params.viewId &&
|
|
18
|
+
execution.params.generation === params.generation &&
|
|
19
|
+
execution.params.instanceId === params.instanceId &&
|
|
20
|
+
execution.params.definition.key === params.definition.key &&
|
|
21
|
+
execution.params.definition.version === params.definition.version;
|
|
22
|
+
export const createNativeHostActionRpcHandler = (input) => {
|
|
23
|
+
let disposed = false;
|
|
24
|
+
const executions = new Map();
|
|
25
|
+
const reportError = (error) => {
|
|
26
|
+
var _a;
|
|
27
|
+
try {
|
|
28
|
+
(_a = input.onError) === null || _a === void 0 ? void 0 : _a.call(input, error instanceof Error ? error : new Error(String(error)));
|
|
29
|
+
}
|
|
30
|
+
catch (_b) {
|
|
31
|
+
// Diagnostics cannot change host action settlement.
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const postToCore = (message) => {
|
|
35
|
+
try {
|
|
36
|
+
input.postToCore(message);
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
reportError(error);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const respond = (request, outcome) => {
|
|
43
|
+
postToCore(buildRpcResponse({
|
|
44
|
+
gen: request.gen,
|
|
45
|
+
ok: true,
|
|
46
|
+
result: outcome
|
|
47
|
+
? { handled: outcome === "success", outcome }
|
|
48
|
+
: { handled: false },
|
|
49
|
+
rpcId: request.rpcId,
|
|
50
|
+
viewId: request.viewId,
|
|
51
|
+
}));
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
dispose: () => {
|
|
55
|
+
disposed = true;
|
|
56
|
+
for (const execution of executions.values()) {
|
|
57
|
+
execution.active = false;
|
|
58
|
+
execution.waiters.splice(0);
|
|
59
|
+
}
|
|
60
|
+
executions.clear();
|
|
61
|
+
},
|
|
62
|
+
handle: (request) => {
|
|
63
|
+
var _a;
|
|
64
|
+
if (request.method === HOST_ACTION_SETTLEMENT_AVAILABILITY_RPC_METHOD) {
|
|
65
|
+
postToCore(buildRpcResponse({
|
|
66
|
+
gen: request.gen,
|
|
67
|
+
ok: true,
|
|
68
|
+
result: { close: true, link: false, remain: true },
|
|
69
|
+
rpcId: request.rpcId,
|
|
70
|
+
viewId: request.viewId,
|
|
71
|
+
}));
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
if (request.method !== HOST_ACTION_EXECUTE_RPC_METHOD) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
const parsedParams = toHostActionExecuteParams(request.params);
|
|
78
|
+
const params = parsedParams
|
|
79
|
+
? Object.assign(Object.assign({}, parsedParams), { invocationId: (_a = parsedParams.invocationId) !== null && _a !== void 0 ? _a : request.idemKey }) : null;
|
|
80
|
+
const existing = params ? executions.get(params.invocationId) : undefined;
|
|
81
|
+
if (params && existing) {
|
|
82
|
+
if (existing.released || !isSameExecution(existing, request, params)) {
|
|
83
|
+
respond(request, null);
|
|
84
|
+
}
|
|
85
|
+
else if (existing.outcome) {
|
|
86
|
+
respond(request, existing.outcome);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
existing.waiters.push(request);
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
if (params === null ||
|
|
94
|
+
params.generation !== input.generation ||
|
|
95
|
+
params.instanceId !== input.instanceId ||
|
|
96
|
+
!input.isViewActive(params.viewId)) {
|
|
97
|
+
respond(request, null);
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
const { invocationId: _invocationId } = params, boundParams = __rest(params, ["invocationId"]);
|
|
101
|
+
const execution = {
|
|
102
|
+
active: true,
|
|
103
|
+
attemptId: request.idemKey,
|
|
104
|
+
hasExplicitInvocationId: typeof (parsedParams === null || parsedParams === void 0 ? void 0 : parsedParams.invocationId) === "string",
|
|
105
|
+
invocationId: params.invocationId,
|
|
106
|
+
outcome: null,
|
|
107
|
+
params: Object.assign(Object.assign({}, boundParams), { definition: Object.assign({}, boundParams.definition) }),
|
|
108
|
+
released: false,
|
|
109
|
+
started: false,
|
|
110
|
+
waiters: [request],
|
|
111
|
+
};
|
|
112
|
+
executions.set(execution.invocationId, execution);
|
|
113
|
+
const settle = (outcome) => {
|
|
114
|
+
var _a;
|
|
115
|
+
if (disposed || !execution.active || execution.outcome)
|
|
116
|
+
return;
|
|
117
|
+
execution.outcome = outcome;
|
|
118
|
+
for (const waiter of execution.waiters.splice(0)) {
|
|
119
|
+
respond(waiter, outcome);
|
|
120
|
+
}
|
|
121
|
+
const seed = execution.params.flowActionSucceededSeed;
|
|
122
|
+
if (outcome !== "success" ||
|
|
123
|
+
seed === undefined ||
|
|
124
|
+
!execution.hasExplicitInvocationId)
|
|
125
|
+
return;
|
|
126
|
+
try {
|
|
127
|
+
postToCore(buildEventFlowActionSucceeded({
|
|
128
|
+
event: buildFlowActionSucceededV1({
|
|
129
|
+
actionInvocationId: execution.invocationId,
|
|
130
|
+
actionKey: execution.params.definition.key,
|
|
131
|
+
actionKind: seed.actionKind,
|
|
132
|
+
actionPlacementId: seed.actionPlacementId,
|
|
133
|
+
actionVersion: execution.params.definition.version,
|
|
134
|
+
flowId: seed.flowId,
|
|
135
|
+
flowRunId: seed.flowRunId,
|
|
136
|
+
flowSurface: "widget",
|
|
137
|
+
flowVersionId: seed.flowVersionId,
|
|
138
|
+
flowVersionNumber: seed.flowVersionNumber,
|
|
139
|
+
}),
|
|
140
|
+
gen: request.gen,
|
|
141
|
+
instanceId: execution.params.instanceId,
|
|
142
|
+
timestamp: ((_a = input.now) !== null && _a !== void 0 ? _a : Date.now)(),
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
reportError(error);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const handler = input.getHandler(params.definition);
|
|
150
|
+
if (handler === undefined) {
|
|
151
|
+
settle("unavailable");
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
void Promise.resolve()
|
|
155
|
+
.then(() => {
|
|
156
|
+
if (!execution.active || !input.isViewActive(params.viewId))
|
|
157
|
+
return;
|
|
158
|
+
execution.started = true;
|
|
159
|
+
return handler();
|
|
160
|
+
})
|
|
161
|
+
.then(() => {
|
|
162
|
+
if (execution.started)
|
|
163
|
+
settle("success");
|
|
164
|
+
}, () => settle("failure"));
|
|
165
|
+
return true;
|
|
166
|
+
},
|
|
167
|
+
releaseView: (viewId) => {
|
|
168
|
+
for (const [invocationId, execution] of executions) {
|
|
169
|
+
if (execution.params.viewId !== viewId)
|
|
170
|
+
continue;
|
|
171
|
+
execution.released = true;
|
|
172
|
+
execution.waiters.splice(0);
|
|
173
|
+
if (execution.started || execution.outcome !== null)
|
|
174
|
+
continue;
|
|
175
|
+
execution.active = false;
|
|
176
|
+
executions.delete(invocationId);
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { EventHandleInvalidated } from "@getuserfeedback/protocol/internal/core-handle-invalidation";
|
|
2
2
|
import type { ConfigureOptions } from "@getuserfeedback/sdk";
|
|
3
|
+
import { type OpenRequestDispatcher } from "@getuserfeedback/sdk/internal/open-request-dispatcher";
|
|
3
4
|
export declare function createNativeProviderClientController(input: {
|
|
4
5
|
instanceId: string;
|
|
5
6
|
onError?: (error: Error) => void;
|
|
@@ -7,6 +8,7 @@ export declare function createNativeProviderClientController(input: {
|
|
|
7
8
|
client: import("./client.js").Client;
|
|
8
9
|
directSessionController: import("./native-direct-command-session-controller.js").NativeDirectCommandSessionController;
|
|
9
10
|
getRuntimeConfiguration: () => ConfigureOptions;
|
|
11
|
+
observeOpenRequest: (payload: Parameters<OpenRequestDispatcher["dispatch"]>[0]) => ReturnType<OpenRequestDispatcher["dispatch"]>;
|
|
10
12
|
dispose: () => void;
|
|
11
13
|
onHandleInvalidated: (message: EventHandleInvalidated) => void;
|
|
12
14
|
onSessionEnded: () => void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parsePublicCommand } from "@getuserfeedback/protocol";
|
|
2
|
+
import { createOpenRequestDispatcher, } from "@getuserfeedback/sdk/internal/open-request-dispatcher";
|
|
2
3
|
import { createNativeProviderClient, parseRequiredString, } from "./native-provider-client.js";
|
|
3
4
|
import { createNativeProviderCommandController } from "./native-provider-command-controller.js";
|
|
4
5
|
import { createNativeProviderFlowRegistry } from "./native-provider-flow-registry.js";
|
|
@@ -14,6 +15,19 @@ export function createNativeProviderClientController(input) {
|
|
|
14
15
|
const commands = createNativeProviderCommandController(input);
|
|
15
16
|
const flows = createNativeProviderFlowRegistry(commands);
|
|
16
17
|
const session = commands.directSessionController;
|
|
18
|
+
const openRequestDispatcher = createOpenRequestDispatcher();
|
|
19
|
+
const reportOpenRequestObserverError = (error) => {
|
|
20
|
+
var _a;
|
|
21
|
+
try {
|
|
22
|
+
(_a = input.onError) === null || _a === void 0 ? void 0 : _a.call(input, error instanceof Error ? error : new Error(String(error)));
|
|
23
|
+
}
|
|
24
|
+
catch (_b) {
|
|
25
|
+
// Error observers cannot become open-request authority.
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const observeOpenRequest = (payload) => openRequestDispatcher.dispatch(payload, {
|
|
29
|
+
onListenerError: reportOpenRequestObserverError,
|
|
30
|
+
});
|
|
17
31
|
const client = createNativeProviderClient({
|
|
18
32
|
dispatchCommand: async (command, options) => {
|
|
19
33
|
if (command.kind === "close" || command.kind === "reset") {
|
|
@@ -37,11 +51,13 @@ export function createNativeProviderClientController(input) {
|
|
|
37
51
|
},
|
|
38
52
|
flow: (flowId) => flows.flow(parseRequiredString("flowId", flowId)),
|
|
39
53
|
instanceId: input.instanceId,
|
|
54
|
+
openRequestDispatcher,
|
|
40
55
|
});
|
|
41
56
|
return {
|
|
42
57
|
client,
|
|
43
58
|
directSessionController: session,
|
|
44
59
|
getRuntimeConfiguration: () => (Object.assign({}, runtimeConfiguration)),
|
|
60
|
+
observeOpenRequest,
|
|
45
61
|
dispose: () => {
|
|
46
62
|
flows.reset();
|
|
47
63
|
commands.dispose();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PublicCommandPayload } from "@getuserfeedback/protocol";
|
|
2
|
+
import { type OpenRequestDispatcher } from "@getuserfeedback/sdk/internal/open-request-dispatcher";
|
|
2
3
|
import type { Client } from "./client.js";
|
|
3
4
|
type NativeProviderClientCommand = Extract<PublicCommandPayload, {
|
|
4
5
|
kind: "configure" | "identify" | "track" | "close" | "reset" | "updateHostContext" | "emitHostSignal";
|
|
@@ -8,7 +9,8 @@ type NativeProviderClientOptions = {
|
|
|
8
9
|
dispatchCommand: (command: NativeProviderClientCommand, options?: NativeProviderCommandOptions) => Promise<void>;
|
|
9
10
|
flow: Client["flow"];
|
|
10
11
|
instanceId: string;
|
|
12
|
+
openRequestDispatcher?: OpenRequestDispatcher;
|
|
11
13
|
};
|
|
12
14
|
export declare const parseRequiredString: (name: string, value?: string) => string;
|
|
13
|
-
export declare function createNativeProviderClient({ dispatchCommand, flow, instanceId, }: NativeProviderClientOptions): Client;
|
|
15
|
+
export declare function createNativeProviderClient({ dispatchCommand, flow, instanceId, openRequestDispatcher, }: NativeProviderClientOptions): Client;
|
|
14
16
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assertNoSegmentExternalIdsInValueArgument } from "@getuserfeedback/protocol/host";
|
|
2
2
|
import { buildCustomerOccurrenceCommand } from "@getuserfeedback/sdk/internal/customer-occurrence-command";
|
|
3
|
+
import { createOpenRequestDispatcher, } from "@getuserfeedback/sdk/internal/open-request-dispatcher";
|
|
3
4
|
export const parseRequiredString = (name, value) => {
|
|
4
5
|
const normalized = value === null || value === void 0 ? void 0 : value.trim();
|
|
5
6
|
if (!normalized) {
|
|
@@ -34,7 +35,7 @@ const toIdentifyCommandPayload = (identifyInput, traitsOrOptions, options) => {
|
|
|
34
35
|
options: options !== null && options !== void 0 ? options : traitsOrOptions,
|
|
35
36
|
};
|
|
36
37
|
};
|
|
37
|
-
export function createNativeProviderClient({ dispatchCommand, flow, instanceId, }) {
|
|
38
|
+
export function createNativeProviderClient({ dispatchCommand, flow, instanceId, openRequestDispatcher = createOpenRequestDispatcher(), }) {
|
|
38
39
|
const track = (eventName, properties, options) => {
|
|
39
40
|
return dispatchCommand(buildCustomerOccurrenceCommand({
|
|
40
41
|
type: "track",
|
|
@@ -61,6 +62,7 @@ export function createNativeProviderClient({ dispatchCommand, flow, instanceId,
|
|
|
61
62
|
},
|
|
62
63
|
flow,
|
|
63
64
|
close: () => dispatchCommand({ kind: "close" }),
|
|
65
|
+
onOpenRequested: openRequestDispatcher.subscribe,
|
|
64
66
|
reset: (options) => dispatchCommand({ kind: "reset" }, options),
|
|
65
67
|
updateHostContext: (context, options) => dispatchCommand({ kind: "updateHostContext", context }, options),
|
|
66
68
|
emitHostSignal: (name, data, options) => dispatchCommand(Object.assign({ kind: "emitHostSignal", name: parseRequiredString("name", name) }, (data === undefined ? {} : { data })), options),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ComponentType, type ReactElement, type ReactNode } from "react";
|
|
2
2
|
import type { Client, ClientOptions } from "./client.js";
|
|
3
|
-
export type { Client, ClientOptions, EventOptions, EventProperties, FlowRun, TrackOptions, } from "./client.js";
|
|
3
|
+
export type { Client, ClientOptions, EventOptions, EventProperties, FlowRun, NativeActionRegistration, OpenRequestedCallback, OpenRequestedEvent, TrackOptions, } from "./client.js";
|
|
4
4
|
export type { UseFlowContainerReturn } from "./flow-container.js";
|
|
5
5
|
export { FlowContent, useFlowContainer } from "./flow-container.js";
|
|
6
6
|
/**
|
package/dist/native-provider.js
CHANGED
|
@@ -11,7 +11,8 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
};
|
|
12
12
|
import { parseInitOptions } from "@getuserfeedback/protocol";
|
|
13
13
|
import { createUniqueId } from "@getuserfeedback/protocol/host";
|
|
14
|
-
import {
|
|
14
|
+
import { createActionRegistry, } from "@getuserfeedback/sdk/internal/action-registry";
|
|
15
|
+
import { createContext, createElement, Fragment, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
15
16
|
import { NativeStandardFlowContainer } from "./flow-container.js";
|
|
16
17
|
import { NativeFlowContainerContext, } from "./native-flow-container-context.js";
|
|
17
18
|
import { createNativeProviderClientController } from "./native-provider-client-controller.js";
|
|
@@ -34,9 +35,33 @@ const toClientOptionsKey = (value) => {
|
|
|
34
35
|
.join(",")}}`;
|
|
35
36
|
};
|
|
36
37
|
const compareClientOptionsKeys = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
const haveSameActionRegistrations = (left, right) => left.length === right.length &&
|
|
39
|
+
left.every((identity, index) => identity === right[index]);
|
|
40
|
+
const useNativeActionRegistry = (actions) => {
|
|
41
|
+
const nextRegistry = createActionRegistry(actions);
|
|
42
|
+
if (nextRegistry.getOpenUrlHandler() !== undefined) {
|
|
43
|
+
throw new TypeError('React Native actions support only registrations with kind "custom"');
|
|
44
|
+
}
|
|
45
|
+
const registryRef = useRef(null);
|
|
46
|
+
const currentRegistry = registryRef.current;
|
|
47
|
+
if (currentRegistry !== null &&
|
|
48
|
+
!haveSameActionRegistrations(currentRegistry.listRegistrationIdentities(), nextRegistry.listRegistrationIdentities())) {
|
|
49
|
+
throw new Error("actions definitions cannot be changed after widget init; keeping original registrations");
|
|
50
|
+
}
|
|
51
|
+
if (currentRegistry === null) {
|
|
52
|
+
registryRef.current = nextRegistry;
|
|
53
|
+
}
|
|
54
|
+
useLayoutEffect(() => {
|
|
55
|
+
registryRef.current = nextRegistry;
|
|
56
|
+
}, [nextRegistry]);
|
|
57
|
+
const getHandler = useCallback((definition) => { var _a; return (_a = registryRef.current) === null || _a === void 0 ? void 0 : _a.getHandler(definition); }, []);
|
|
58
|
+
return { definitions: nextRegistry.listDefinitions(), getHandler };
|
|
59
|
+
};
|
|
60
|
+
const parseNativeInitOptions = (_a, hostActions) => {
|
|
61
|
+
var { actions: _actions, capabilities } = _a, clientOptions = __rest(_a, ["actions", "capabilities"]);
|
|
62
|
+
const initOptions = parseInitOptions(Object.assign(Object.assign(Object.assign({}, clientOptions), (hostActions !== undefined && hostActions.length > 0
|
|
63
|
+
? { hostActions }
|
|
64
|
+
: {})), (capabilities !== undefined
|
|
40
65
|
? {
|
|
41
66
|
hostCapabilities: capabilities.map((capability) => typeof capability === "string"
|
|
42
67
|
? { key: capability }
|
|
@@ -64,7 +89,8 @@ export function GetUserFeedbackProvider({ children, clientOptions, instanceId: i
|
|
|
64
89
|
const [contentOwner, setContentOwner] = useState(null);
|
|
65
90
|
const [projection, setProjection] = useState(null);
|
|
66
91
|
const contentOwnerRef = useRef(null);
|
|
67
|
-
const
|
|
92
|
+
const actionRegistry = useNativeActionRegistry(clientOptions.actions);
|
|
93
|
+
const normalizedClientOptions = parseNativeInitOptions(clientOptions, actionRegistry.definitions);
|
|
68
94
|
const clientOptionsKey = toClientOptionsKey(normalizedClientOptions);
|
|
69
95
|
const coreUrl = (_b = (_a = normalizedClientOptions.runtimeEndpoints) === null || _a === void 0 ? void 0 : _a.coreUrl) !== null && _b !== void 0 ? _b : REACT_NATIVE_PINNED_CORE_URL;
|
|
70
96
|
const onErrorRef = useRef(onError);
|
|
@@ -143,9 +169,11 @@ export function GetUserFeedbackProvider({ children, clientOptions, instanceId: i
|
|
|
143
169
|
instanceId,
|
|
144
170
|
key: clientOptionsKey,
|
|
145
171
|
getRuntimeConfiguration,
|
|
172
|
+
getHostActionHandler: actionRegistry.getHandler,
|
|
146
173
|
onCommandSessionChange: handleCommandSessionChange,
|
|
147
174
|
onError: reportError,
|
|
148
175
|
onHandleInvalidated: handleHandleInvalidated,
|
|
176
|
+
observeOpenRequest: controller.observeOpenRequest,
|
|
149
177
|
viewWebViewComponent: webViewComponent,
|
|
150
178
|
}))));
|
|
151
179
|
}
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
* docs/specs/2026-08-02-flow-interaction-model.md (Projection protocol and
|
|
6
6
|
* rollout).
|
|
7
7
|
*/
|
|
8
|
-
export declare const REACT_NATIVE_PINNED_CORE_URL: "https://cdn.getuserfeedback.com/widget/core/
|
|
8
|
+
export declare const REACT_NATIVE_PINNED_CORE_URL: "https://cdn.getuserfeedback.com/widget/core/v6/core.html";
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
* docs/specs/2026-08-02-flow-interaction-model.md (Projection protocol and
|
|
6
6
|
* rollout).
|
|
7
7
|
*/
|
|
8
|
-
export const REACT_NATIVE_PINNED_CORE_URL = "https://cdn.getuserfeedback.com/widget/core/
|
|
8
|
+
export const REACT_NATIVE_PINNED_CORE_URL = "https://cdn.getuserfeedback.com/widget/core/v6/core.html";
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { CoreCommandEnvelope } from "@getuserfeedback/protocol";
|
|
2
2
|
import type { EventHandleInvalidated } from "@getuserfeedback/protocol/internal/core-handle-invalidation";
|
|
3
3
|
import type { ConfigureOptions, InitOptions } from "@getuserfeedback/sdk";
|
|
4
|
+
import type { ActionRegistry } from "@getuserfeedback/sdk/internal/action-registry";
|
|
5
|
+
import type { OpenRequestDispatcher } from "@getuserfeedback/sdk/internal/open-request-dispatcher";
|
|
4
6
|
import { type ReactElement } from "react";
|
|
5
7
|
import { type CoreWebViewHostProps } from "./core-webview-host.js";
|
|
6
8
|
import { type NativeCoreCommandSession } from "./native-core-command-session.js";
|
|
@@ -11,10 +13,15 @@ export type NativeRuntimeRootProps = {
|
|
|
11
13
|
coreUrl: string;
|
|
12
14
|
coreWebViewComponent?: CoreWebViewHostProps["webViewComponent"];
|
|
13
15
|
createStartupCommand?: () => NativeRuntimeInitCommand;
|
|
16
|
+
hostActions?: {
|
|
17
|
+
getHandler: ActionRegistry["getHandler"];
|
|
18
|
+
instanceId: string;
|
|
19
|
+
};
|
|
14
20
|
onCommandSessionChange?: (session: NativeCoreCommandSession | null) => void;
|
|
15
21
|
onError?: (error: Error) => void;
|
|
16
22
|
onHandleInvalidated?: (message: EventHandleInvalidated) => void;
|
|
17
23
|
onInvalidMessage?: (error: Error, value: unknown) => void;
|
|
24
|
+
observeOpenRequest?: (payload: Parameters<OpenRequestDispatcher["dispatch"]>[0]) => ReturnType<OpenRequestDispatcher["dispatch"]>;
|
|
18
25
|
onStartupTerminalError?: (error: Error) => void;
|
|
19
26
|
startupCommandSettlementTimeoutMs?: number;
|
|
20
27
|
attachViewProjection?: (projection: NativeViewProjection) => () => void;
|
|
@@ -32,8 +39,9 @@ type NativeProviderRuntimeProps = Omit<NativeRuntimeRootProps, "createStartupCom
|
|
|
32
39
|
initOptions: InitOptions;
|
|
33
40
|
instanceId: string;
|
|
34
41
|
getRuntimeConfiguration?: () => ConfigureOptions;
|
|
42
|
+
getHostActionHandler?: ActionRegistry["getHandler"];
|
|
35
43
|
onCommandSessionChange?: (session: NativeDirectCommandSession | null) => void;
|
|
36
44
|
};
|
|
37
|
-
export declare function NativeRuntimeRoot({ attachViewProjection, coreUrl, coreWebViewComponent, createStartupCommand, onCommandSessionChange, onError, onHandleInvalidated, onInvalidMessage, onStartupTerminalError, startupCommandSettlementTimeoutMs, viewNativeViewComponent, viewWebViewComponent, }: NativeRuntimeRootProps): ReactElement;
|
|
38
|
-
export declare function NativeProviderRuntime({ directSessionController, getRuntimeConfiguration, initOptions, instanceId, onCommandSessionChange, onError, ...runtimeProps }: NativeProviderRuntimeProps): ReactElement;
|
|
45
|
+
export declare function NativeRuntimeRoot({ attachViewProjection, coreUrl, coreWebViewComponent, createStartupCommand, hostActions, onCommandSessionChange, onError, onHandleInvalidated, onInvalidMessage, observeOpenRequest, onStartupTerminalError, startupCommandSettlementTimeoutMs, viewNativeViewComponent, viewWebViewComponent, }: NativeRuntimeRootProps): ReactElement;
|
|
46
|
+
export declare function NativeProviderRuntime({ directSessionController, getHostActionHandler, getRuntimeConfiguration, initOptions, instanceId, onCommandSessionChange, onError, ...runtimeProps }: NativeProviderRuntimeProps): ReactElement;
|
|
39
47
|
export {};
|
|
@@ -20,7 +20,7 @@ import { createNativeViewRecordController, } from "./native-view-record-controll
|
|
|
20
20
|
import { createProviderCommandEnvelope, REACT_NATIVE_CLIENT_META, } from "./provider-command-helpers.js";
|
|
21
21
|
const DEFAULT_STARTUP_COMMAND_SETTLEMENT_TIMEOUT_MS = 30000;
|
|
22
22
|
const getEmptyRuntimeConfiguration = () => ({});
|
|
23
|
-
export function NativeRuntimeRoot({ attachViewProjection, coreUrl, coreWebViewComponent, createStartupCommand, onCommandSessionChange, onError, onHandleInvalidated, onInvalidMessage, onStartupTerminalError, startupCommandSettlementTimeoutMs = DEFAULT_STARTUP_COMMAND_SETTLEMENT_TIMEOUT_MS, viewNativeViewComponent, viewWebViewComponent, }) {
|
|
23
|
+
export function NativeRuntimeRoot({ attachViewProjection, coreUrl, coreWebViewComponent, createStartupCommand, hostActions, onCommandSessionChange, onError, onHandleInvalidated, onInvalidMessage, observeOpenRequest, onStartupTerminalError, startupCommandSettlementTimeoutMs = DEFAULT_STARTUP_COMMAND_SETTLEMENT_TIMEOUT_MS, viewNativeViewComponent, viewWebViewComponent, }) {
|
|
24
24
|
const [coreHostRevision, setCoreHostRevision] = useState(0);
|
|
25
25
|
const [session, setSession] = useState(null);
|
|
26
26
|
const sessionRef = useRef(null);
|
|
@@ -34,10 +34,14 @@ export function NativeRuntimeRoot({ attachViewProjection, coreUrl, coreWebViewCo
|
|
|
34
34
|
onHandleInvalidatedRef.current = onHandleInvalidated;
|
|
35
35
|
const onInvalidMessageRef = useRef(onInvalidMessage);
|
|
36
36
|
onInvalidMessageRef.current = onInvalidMessage;
|
|
37
|
+
const observeOpenRequestRef = useRef(observeOpenRequest);
|
|
38
|
+
observeOpenRequestRef.current = observeOpenRequest;
|
|
37
39
|
const onStartupTerminalErrorRef = useRef(onStartupTerminalError);
|
|
38
40
|
onStartupTerminalErrorRef.current = onStartupTerminalError;
|
|
39
41
|
const createStartupCommandRef = useRef(createStartupCommand);
|
|
40
42
|
createStartupCommandRef.current = createStartupCommand;
|
|
43
|
+
const hostActionsRef = useRef(hostActions);
|
|
44
|
+
hostActionsRef.current = hostActions;
|
|
41
45
|
const reportError = useCallback((error) => {
|
|
42
46
|
var _a;
|
|
43
47
|
try {
|
|
@@ -81,9 +85,22 @@ export function NativeRuntimeRoot({ attachViewProjection, coreUrl, coreWebViewCo
|
|
|
81
85
|
const controller = createNativeViewRecordController({
|
|
82
86
|
coreConnection,
|
|
83
87
|
onError: reportError,
|
|
88
|
+
observeOpenRequest: (payload) => { var _a, _b; return (_b = (_a = observeOpenRequestRef.current) === null || _a === void 0 ? void 0 : _a.call(observeOpenRequestRef, payload)) !== null && _b !== void 0 ? _b : { allowed: true }; },
|
|
84
89
|
});
|
|
85
90
|
const commandChannelOwner = createNativeCoreCommandChannel({
|
|
86
91
|
coreConnection,
|
|
92
|
+
hostActions: hostActionsRef.current
|
|
93
|
+
? {
|
|
94
|
+
getHandler: (definition) => { var _a; return (_a = hostActionsRef.current) === null || _a === void 0 ? void 0 : _a.getHandler(definition); },
|
|
95
|
+
getActiveViewIds: () => controller
|
|
96
|
+
.getSnapshot()
|
|
97
|
+
.filter((record) => record.isActive)
|
|
98
|
+
.map((record) => record.viewId),
|
|
99
|
+
instanceId: hostActionsRef.current.instanceId,
|
|
100
|
+
subscribeToViewActivity: controller.subscribe,
|
|
101
|
+
}
|
|
102
|
+
: undefined,
|
|
103
|
+
onHostActionError: reportError,
|
|
87
104
|
onSessionFailure: (error) => {
|
|
88
105
|
reportError(error);
|
|
89
106
|
coreConnection.disconnect();
|
|
@@ -240,7 +257,7 @@ export function NativeRuntimeRoot({ attachViewProjection, coreUrl, coreWebViewCo
|
|
|
240
257
|
}));
|
|
241
258
|
}
|
|
242
259
|
export function NativeProviderRuntime(_a) {
|
|
243
|
-
var { directSessionController, getRuntimeConfiguration = getEmptyRuntimeConfiguration, initOptions, instanceId, onCommandSessionChange, onError } = _a, runtimeProps = __rest(_a, ["directSessionController", "getRuntimeConfiguration", "initOptions", "instanceId", "onCommandSessionChange", "onError"]);
|
|
260
|
+
var { directSessionController, getHostActionHandler, getRuntimeConfiguration = getEmptyRuntimeConfiguration, initOptions, instanceId, onCommandSessionChange, onError } = _a, runtimeProps = __rest(_a, ["directSessionController", "getHostActionHandler", "getRuntimeConfiguration", "initOptions", "instanceId", "onCommandSessionChange", "onError"]);
|
|
244
261
|
const directSessionRef = useRef(null);
|
|
245
262
|
useLayoutEffect(() => {
|
|
246
263
|
directSessionController.beginAttachmentAttempt();
|
|
@@ -279,5 +296,7 @@ export function NativeProviderRuntime(_a) {
|
|
|
279
296
|
instanceId,
|
|
280
297
|
});
|
|
281
298
|
}, [getRuntimeConfiguration, initOptions, instanceId]);
|
|
282
|
-
return createElement(NativeRuntimeRoot, Object.assign(Object.assign({}, runtimeProps), {
|
|
299
|
+
return createElement(NativeRuntimeRoot, Object.assign(Object.assign({}, runtimeProps), { hostActions: getHostActionHandler
|
|
300
|
+
? { getHandler: getHostActionHandler, instanceId }
|
|
301
|
+
: undefined, onCommandSessionChange: handleCommandSessionChange, onError, onStartupTerminalError: handleStartupTerminalError, createStartupCommand }));
|
|
283
302
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type EventViewSize } from "@getuserfeedback/protocol/internal/view-host-control-messages";
|
|
2
|
+
import type { OpenRequestDispatcher } from "@getuserfeedback/sdk/internal/open-request-dispatcher";
|
|
2
3
|
import type { CoreWebViewHostConnection } from "./core-webview-host-controller.js";
|
|
3
4
|
import type { ViewWebViewHostConnection } from "./view-webview-host-controller.js";
|
|
4
5
|
type NativeViewSizeReport = Omit<EventViewSize, "gen" | "t" | "viewId">;
|
|
@@ -35,8 +36,10 @@ export type NativeViewRecordController = {
|
|
|
35
36
|
getSnapshot(): readonly NativeViewRecord[];
|
|
36
37
|
subscribe(listener: () => void): () => void;
|
|
37
38
|
};
|
|
39
|
+
type NativeOpenRequestObserver = (payload: Parameters<OpenRequestDispatcher["dispatch"]>[0]) => ReturnType<OpenRequestDispatcher["dispatch"]>;
|
|
38
40
|
export declare function createNativeViewRecordController(input: {
|
|
39
41
|
coreConnection: CoreWebViewHostConnection;
|
|
40
42
|
onError: (error: Error) => void;
|
|
43
|
+
observeOpenRequest?: NativeOpenRequestObserver;
|
|
41
44
|
}): NativeViewRecordController;
|
|
42
45
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { coreViewHostActionSchema, } from "@getuserfeedback/protocol/internal/core-view-host-actions";
|
|
1
|
+
import { coreViewHostActionSchema, } from "@getuserfeedback/protocol/internal/core-view-host-actions-v3";
|
|
2
2
|
import { actionRequestCloseViewSchema, actionViewConnectSchema, buildEventLayoutTransactionCommitted, buildEventViewSize, eventViewSizeSchema, } from "@getuserfeedback/protocol/internal/view-host-control-messages";
|
|
3
|
-
import { buildEventViewActivated, buildEventViewAllocationFailed, buildEventViewClosed, buildEventViewCloseFailed, buildEventViewPrerendered, } from "@getuserfeedback/protocol/internal/view-host-lifecycle-events";
|
|
3
|
+
import { buildEventViewActivated, buildEventViewActivationDecided, buildEventViewAllocationFailed, buildEventViewClosed, buildEventViewCloseFailed, buildEventViewPrerendered, } from "@getuserfeedback/protocol/internal/view-host-lifecycle-events-v3";
|
|
4
4
|
import { bindViewHostControlEnvelope, bindViewToCoreRelayEnvelope, parseCoreToViewRelayEnvelope, } from "@getuserfeedback/protocol/internal/view-relay-contract";
|
|
5
5
|
import { createViewPreparationReadinessRegistry, createViewPreparationRegistry, } from "./view-orchestration-runtime.js";
|
|
6
6
|
const createNativeViewReadinessRegistry = () => createViewPreparationReadinessRegistry();
|
|
@@ -157,6 +157,21 @@ export function createNativeViewRecordController(input) {
|
|
|
157
157
|
failCoreConnection(error, "Native view close failure delivery failed");
|
|
158
158
|
}
|
|
159
159
|
};
|
|
160
|
+
const postViewActivationDecision = (action, allowed) => {
|
|
161
|
+
try {
|
|
162
|
+
input.coreConnection.transport.postMessage(buildEventViewActivationDecided({
|
|
163
|
+
gen: action.gen,
|
|
164
|
+
viewId: action.viewId,
|
|
165
|
+
activationAttemptId: action.activationAttemptId,
|
|
166
|
+
allowed,
|
|
167
|
+
}));
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
failCoreConnection(error, "Native view activation decision delivery failed");
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
160
175
|
const failAllocation = (entry, allocationError) => {
|
|
161
176
|
const resourceErrors = [];
|
|
162
177
|
const observerErrors = [];
|
|
@@ -255,7 +270,9 @@ export function createNativeViewRecordController(input) {
|
|
|
255
270
|
failAllocation(entry, error);
|
|
256
271
|
}
|
|
257
272
|
};
|
|
273
|
+
// Governing contract: docs/specs/2026-08-29-sdk-open-request-interception.md.
|
|
258
274
|
const handleActivate = (action) => {
|
|
275
|
+
var _a;
|
|
259
276
|
const entry = entries.get(action.viewId);
|
|
260
277
|
if (!entry ||
|
|
261
278
|
entry.instanceId !== action.instanceId ||
|
|
@@ -263,6 +280,27 @@ export function createNativeViewRecordController(input) {
|
|
|
263
280
|
failCoreConnection(new Error(`Unknown native projection binding ${action.viewId}`), "Unknown native projection binding");
|
|
264
281
|
return;
|
|
265
282
|
}
|
|
283
|
+
let allowed = true;
|
|
284
|
+
if (((_a = action.openRequest) === null || _a === void 0 ? void 0 : _a.flowId) && input.observeOpenRequest) {
|
|
285
|
+
const payload = Object.assign({ instanceId: action.instanceId, source: action.openRequest.source, flowId: action.openRequest.flowId }, (action.openRequest.source === "command"
|
|
286
|
+
? {
|
|
287
|
+
flowHandleId: action.openRequest.flowHandleId,
|
|
288
|
+
hideCloseButton: action.openRequest.hideCloseButton,
|
|
289
|
+
}
|
|
290
|
+
: {}));
|
|
291
|
+
try {
|
|
292
|
+
allowed = input.observeOpenRequest(payload).allowed;
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
reportError(error, "Native open request observer failed");
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (!postViewActivationDecision(action, allowed)) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
if (!allowed) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
266
304
|
if (!entry.isActive) {
|
|
267
305
|
entry.isActive = true;
|
|
268
306
|
notify();
|
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 : "5.
|
|
6
|
+
export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "5.5.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getuserfeedback/react-native",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.0",
|
|
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": "^5.
|
|
48
|
-
"@getuserfeedback/sdk": "^0.
|
|
47
|
+
"@getuserfeedback/protocol": "^5.4.0",
|
|
48
|
+
"@getuserfeedback/sdk": "^0.17.0",
|
|
49
49
|
"robot3": "^1.2.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|