@getuserfeedback/react-native 3.0.80 → 3.0.90

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.
Files changed (29) hide show
  1. package/dist/index.js +29 -89
  2. package/dist/native-direct-command-session-controller.d.ts +9 -0
  3. package/dist/native-direct-command-session-controller.js +78 -0
  4. package/dist/native-direct-command-session.js +2 -2
  5. package/dist/native-provider-client.d.ts +64 -0
  6. package/dist/native-provider-client.js +76 -0
  7. package/dist/native-runtime-root.d.ts +5 -3
  8. package/dist/native-runtime-root.js +31 -7
  9. package/dist/native-view-host-projection.d.ts +2 -1
  10. package/dist/native-view-host-projection.js +13 -9
  11. package/dist/native-view-presentation-surface.d.ts +7 -0
  12. package/dist/native-view-presentation-surface.js +82 -0
  13. package/dist/native-view-record-controller.d.ts +2 -0
  14. package/dist/native-view-record-controller.js +58 -8
  15. package/dist/native-widget-corner-radii.d.ts +10 -0
  16. package/dist/native-widget-corner-radii.js +54 -0
  17. package/dist/native-widget-frame-border.d.ts +7 -0
  18. package/dist/native-widget-frame-border.js +21 -0
  19. package/dist/native-widget-frame-shadow.d.ts +30 -0
  20. package/dist/native-widget-frame-shadow.js +49 -0
  21. package/dist/provider-widget-border-radius.js +5 -18
  22. package/dist/provider-widget-frame-appearance.d.ts +5 -34
  23. package/dist/provider-widget-frame-appearance.js +10 -69
  24. package/dist/provider-widget-host-overlay.d.ts +2 -1
  25. package/dist/provider-widget-host-overlay.js +7 -6
  26. package/dist/use-native-direct-command-session-controller.d.ts +2 -0
  27. package/dist/use-native-direct-command-session-controller.js +21 -0
  28. package/dist/version.js +1 -1
  29. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -9,10 +9,11 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { assertNoSegmentExternalIdsInValueArgument, CommandSettlementError, CommandSettlementTimeoutError, createFlowCommandHandleController, getFlowHandleFromSettlementDetail, getWidgetLayoutPlanNowMs, } from "@getuserfeedback/protocol/host";
12
+ import { CommandSettlementError, CommandSettlementTimeoutError, createFlowCommandHandleController, getFlowHandleFromSettlementDetail, getWidgetLayoutPlanNowMs, } from "@getuserfeedback/protocol/host";
13
13
  import { parseWebViewTransportNativeMessage } from "@getuserfeedback/protocol/webview-transport";
14
14
  import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
15
15
  import { useResolvedHostViewport } from "./host-viewport.js";
16
+ import { createNativeProviderClient, parseRequiredString, } from "./native-provider-client.js";
16
17
  import { createProviderCommandEnvelope, REACT_NATIVE_LOADER_CLIENT_META, toCommandSettlementError, toReactNativeOneShotCommand, } from "./provider-command-helpers.js";
17
18
  import { useProviderCommandTransport } from "./provider-command-transport.js";
18
19
  import { createProviderWidgetHostLifecycleController, HIDDEN_PROVIDER_WIDGET_HOST_VISIBILITY, } from "./provider-widget-host-lifecycle.js";
@@ -136,13 +137,6 @@ const normalizeOptionalString = (value) => {
136
137
  const normalized = value.trim();
137
138
  return normalized.length > 0 ? normalized : undefined;
138
139
  };
139
- const resolveRequiredString = (name, value) => {
140
- const normalized = normalizeOptionalString(value);
141
- if (normalized === undefined) {
142
- throw new Error(`${name} must be a non-empty string`);
143
- }
144
- return normalized;
145
- };
146
140
  const toJsonKey = (value) => JSON.stringify(value);
147
141
  const toStableHash = (value) => {
148
142
  const json = toJsonKey(value);
@@ -171,33 +165,6 @@ function resolveReactNativeComponents() {
171
165
  };
172
166
  }
173
167
  }
174
- const toIdentifyCommandPayload = (identifyInput, traitsOrOptions, options) => {
175
- if (typeof identifyInput === "string") {
176
- assertNoSegmentExternalIdsInValueArgument({
177
- argument: traitsOrOptions,
178
- commandName: "identify",
179
- valueArgumentName: "traits",
180
- callShape: "identify(userId, traits, { externalIds })",
181
- });
182
- return {
183
- kind: "identify",
184
- userId: resolveRequiredString("userId", identifyInput),
185
- traits: traitsOrOptions,
186
- options,
187
- };
188
- }
189
- assertNoSegmentExternalIdsInValueArgument({
190
- argument: identifyInput,
191
- commandName: "identify",
192
- valueArgumentName: "traits",
193
- callShape: "identify(traits, { externalIds })",
194
- });
195
- return {
196
- kind: "identify",
197
- traits: identifyInput,
198
- options: options !== null && options !== void 0 ? options : traitsOrOptions,
199
- };
200
- };
201
168
  export function GetUserFeedbackProvider({ autoInit = true, children, commandTimeoutMs = DEFAULT_COMMAND_TIMEOUT_MS, configureOptions, hostViewport, instanceId: instanceIdProp, initOptions, loaderUrl, onCommandError, onInvalidWebMessage, onWebMessage, source, webViewComponent, webViewProps, }) {
202
169
  var _a;
203
170
  const { View: NativeView } = useMemo(resolveReactNativeComponents, []);
@@ -383,7 +350,7 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
383
350
  reportCommandError,
384
351
  };
385
352
  const flow = useCallback((flowId) => {
386
- const normalizedFlowId = resolveRequiredString("flowId", flowId);
353
+ const normalizedFlowId = parseRequiredString("flowId", flowId);
387
354
  const existing = flowRunsRef.current.get(normalizedFlowId);
388
355
  if (existing) {
389
356
  return existing;
@@ -541,59 +508,32 @@ export function GetUserFeedbackProvider({ autoInit = true, children, commandTime
541
508
  disposeCommandTransport(new Error(PROVIDER_UNMOUNTED_ERROR_MESSAGE));
542
509
  }, [disposeCommandTransport]);
543
510
  const getNativeMessages = useCallback(() => nativeMessagesRef.current, [nativeMessagesRef]);
544
- const contextValue = useMemo(() => ({
545
- instanceId,
546
- get nativeMessages() {
547
- return getNativeMessages();
548
- },
549
- enqueueCommand,
550
- configure: (opts, options) => enqueueCommand({ kind: "configure", opts }, options),
551
- identify: ((identifyInput, traitsOrOptions, options) => enqueueCommand(toIdentifyCommandPayload(identifyInput, traitsOrOptions, options))),
552
- track: (eventName, properties, options) => {
553
- assertNoSegmentExternalIdsInValueArgument({
554
- argument: properties,
555
- commandName: "track",
556
- valueArgumentName: "properties",
557
- callShape: "track(eventName, properties, { externalIds })",
558
- });
559
- return enqueueCommand(Object.assign(Object.assign({ kind: "track", origin: "customer", event: resolveRequiredString("eventName", eventName) }, (properties === undefined ? {} : { properties })), ((options === null || options === void 0 ? void 0 : options.externalIds) === undefined
560
- ? {}
561
- : { context: { externalIds: options.externalIds } })));
562
- },
563
- open: (flowId, options, commandOptions) => {
564
- const normalizedFlowId = resolveRequiredString("flowId", flowId);
565
- return enqueueCommand({
566
- kind: "open",
567
- flowId: normalizedFlowId,
568
- hideCloseButton: options === null || options === void 0 ? void 0 : options.hideCloseButton,
569
- }, commandOptions);
570
- },
571
- prefetch: (flowId, options) => {
572
- const normalizedFlowId = resolveRequiredString("flowId", flowId);
573
- return enqueueCommand({
574
- kind: "prefetch",
575
- flowId: normalizedFlowId,
576
- }, options);
577
- },
578
- prerender: (flowId, options, commandOptions) => {
579
- const normalizedFlowId = resolveRequiredString("flowId", flowId);
580
- return enqueueCommand({
581
- kind: "prerender",
582
- flowId: normalizedFlowId,
583
- hideCloseButton: options === null || options === void 0 ? void 0 : options.hideCloseButton,
584
- }, commandOptions);
585
- },
586
- flow,
587
- close: (flowHandleId, options) => enqueueCommand(Object.assign({ kind: "close" }, (flowHandleId === undefined
588
- ? {}
589
- : {
590
- flowHandleId: resolveRequiredString("flowHandleId", flowHandleId),
591
- })), options),
592
- reset: (options) => enqueueCommand({ kind: "reset" }, options),
593
- updateHostContext: (context, options) => enqueueCommand({ kind: "updateHostContext", context }, options),
594
- emitHostSignal: (name, data, options) => enqueueCommand(Object.assign({ kind: "emitHostSignal", name: resolveRequiredString("name", name) }, (data === undefined ? {} : { data })), options),
595
- handleWebMessage,
596
- }), [enqueueCommand, flow, getNativeMessages, handleWebMessage, instanceId]);
511
+ const contextValue = useMemo(() => {
512
+ const client = createNativeProviderClient({
513
+ dispatchCommand: enqueueCommand,
514
+ flow,
515
+ instanceId,
516
+ });
517
+ return {
518
+ close: client.close,
519
+ configure: client.configure,
520
+ emitHostSignal: client.emitHostSignal,
521
+ enqueueCommand,
522
+ flow: client.flow,
523
+ handleWebMessage,
524
+ identify: client.identify,
525
+ instanceId: client.instanceId,
526
+ get nativeMessages() {
527
+ return getNativeMessages();
528
+ },
529
+ open: client.open,
530
+ prefetch: client.prefetch,
531
+ prerender: client.prerender,
532
+ reset: client.reset,
533
+ track: client.track,
534
+ updateHostContext: client.updateHostContext,
535
+ };
536
+ }, [enqueueCommand, flow, getNativeMessages, handleWebMessage, instanceId]);
597
537
  const previousSheetHostLayoutUpdateRef = useRef(undefined);
598
538
  const consumedTransitionPolicySequenceRef = useRef(undefined);
599
539
  useEffect(() => {
@@ -0,0 +1,9 @@
1
+ import type { NativeDirectCommandSession } from "./native-direct-command-session.js";
2
+ export type NativeDirectCommandSessionController = {
3
+ attach: (session: NativeDirectCommandSession) => void;
4
+ detach: (session: NativeDirectCommandSession) => void;
5
+ dispose: (error: Error) => void;
6
+ rejectUntilAttached: (error: Error) => void;
7
+ session: NativeDirectCommandSession;
8
+ };
9
+ export declare function createNativeDirectCommandSessionController(): NativeDirectCommandSessionController;
@@ -0,0 +1,78 @@
1
+ export function createNativeDirectCommandSessionController() {
2
+ let attachedSession = null;
3
+ let disposed = false;
4
+ let disposalError;
5
+ let pendingCommands = [];
6
+ let unavailableError;
7
+ const dispatchThrough = (session, envelope) => {
8
+ try {
9
+ return Promise.resolve(session.dispatch(envelope));
10
+ }
11
+ catch (error) {
12
+ return Promise.reject(error);
13
+ }
14
+ };
15
+ const session = {
16
+ dispatch: (envelope) => {
17
+ if (disposed) {
18
+ return Promise.reject(disposalError);
19
+ }
20
+ if (attachedSession !== null) {
21
+ return dispatchThrough(attachedSession, envelope);
22
+ }
23
+ if (unavailableError !== undefined) {
24
+ return Promise.reject(unavailableError);
25
+ }
26
+ return new Promise((resolve, reject) => {
27
+ pendingCommands.push({ envelope, reject, resolve });
28
+ });
29
+ },
30
+ };
31
+ const dispose = (error) => {
32
+ if (disposed) {
33
+ return;
34
+ }
35
+ disposed = true;
36
+ disposalError = error;
37
+ attachedSession = null;
38
+ const queuedCommands = pendingCommands;
39
+ pendingCommands = [];
40
+ for (const command of queuedCommands) {
41
+ command.reject(error);
42
+ }
43
+ };
44
+ return {
45
+ attach: (nextSession) => {
46
+ if (disposed) {
47
+ return;
48
+ }
49
+ attachedSession = nextSession;
50
+ unavailableError = undefined;
51
+ const queuedCommands = pendingCommands;
52
+ pendingCommands = [];
53
+ for (const command of queuedCommands) {
54
+ if (disposed) {
55
+ command.reject(disposalError);
56
+ continue;
57
+ }
58
+ void dispatchThrough(nextSession, command.envelope).then(command.resolve, command.reject);
59
+ }
60
+ },
61
+ detach: (sessionToDetach) => {
62
+ if (attachedSession === sessionToDetach) {
63
+ attachedSession = null;
64
+ }
65
+ },
66
+ dispose,
67
+ rejectUntilAttached: (error) => {
68
+ attachedSession = null;
69
+ unavailableError = error;
70
+ const queuedCommands = pendingCommands;
71
+ pendingCommands = [];
72
+ for (const command of queuedCommands) {
73
+ command.reject(error);
74
+ }
75
+ },
76
+ session,
77
+ };
78
+ }
@@ -1,7 +1,7 @@
1
1
  import { getFlowHandleFromSettlementDetail } from "@getuserfeedback/protocol/host";
2
2
  import { toCoreCommandPayload, } from "@getuserfeedback/protocol/internal/public-core-command";
3
- // TODO(migration): Route the public provider client through this core-ordered direct session.
4
- // [from=native-core-ordered-command-session] [to=native-provider-direct-session] [scope=slice] [principle=core-owned-flow-lifecycle] [priority=high] [impact=high] [risk=high] [epic=runtime-neutral-view-host-orchestration] [epic_order=180]
3
+ // TODO(migration): Route the public provider client through this core-ordered direct session after native per-view presentation is complete.
4
+ // [from=native-view-presentation-behavior] [to=native-provider-direct-session] [scope=slice] [principle=core-owned-flow-lifecycle] [priority=high] [impact=high] [risk=high] [epic=runtime-neutral-view-host-orchestration] [epic_order=190]
5
5
  export function createNativeDirectCommandSession(input) {
6
6
  const dispatchToCore = (envelope, command) => {
7
7
  const coreEnvelope = Object.assign(Object.assign({}, envelope), { command: toCoreCommandPayload(command) });
@@ -0,0 +1,64 @@
1
+ import type { AppEventExternalId, AppEventJsonValue, PublicCommandPayload } from "@getuserfeedback/protocol";
2
+ import type { ConfigureOptions } from "@getuserfeedback/sdk";
3
+ type IdentifyTraits = Record<string, unknown>;
4
+ type TrackProperties = Record<string, AppEventJsonValue>;
5
+ type OpenCommand = Extract<PublicCommandPayload, {
6
+ kind: "open";
7
+ }>;
8
+ type PrerenderCommand = Extract<PublicCommandPayload, {
9
+ kind: "prerender";
10
+ }>;
11
+ type NativeProviderClientCommand = Exclude<Extract<PublicCommandPayload, {
12
+ kind: "configure" | "identify" | "track" | "open" | "prefetch" | "prerender" | "close" | "reset" | "updateHostContext" | "emitHostSignal";
13
+ }>, OpenCommand> | (Omit<OpenCommand, "container"> & {
14
+ container?: never;
15
+ });
16
+ type IdentifyOptions = {
17
+ externalIds?: AppEventExternalId[];
18
+ };
19
+ type TrackOptions = {
20
+ externalIds?: AppEventExternalId[];
21
+ };
22
+ type OpenOptions = Pick<OpenCommand, "hideCloseButton">;
23
+ type PrerenderOptions = Pick<PrerenderCommand, "hideCloseButton">;
24
+ type OpenFlowOptions = Pick<OpenCommand, "hideCloseButton" | "metadata">;
25
+ type PrerenderFlowOptions = Pick<PrerenderCommand, "hideCloseButton">;
26
+ type HostContext = Extract<PublicCommandPayload, {
27
+ kind: "updateHostContext";
28
+ }>["context"];
29
+ interface NativeProviderCommandOptions {
30
+ idempotencyKey?: string;
31
+ }
32
+ export interface ReactNativeFlowRun {
33
+ readonly flowId: string;
34
+ open: (options?: OpenFlowOptions) => Promise<void>;
35
+ prefetch: () => Promise<void>;
36
+ prerender: (options?: PrerenderFlowOptions) => Promise<void>;
37
+ close: () => Promise<void>;
38
+ }
39
+ export interface NativeProviderClient<TResult> {
40
+ readonly instanceId: string;
41
+ configure: (opts: ConfigureOptions, options?: NativeProviderCommandOptions) => Promise<TResult>;
42
+ identify: {
43
+ (userId: string, traits?: IdentifyTraits, options?: IdentifyOptions): Promise<TResult>;
44
+ (traits: IdentifyTraits, placeholder: undefined, options?: IdentifyOptions): Promise<TResult>;
45
+ (traits: IdentifyTraits, options?: IdentifyOptions): Promise<TResult>;
46
+ };
47
+ track: (eventName: string, properties?: TrackProperties, options?: TrackOptions) => Promise<TResult>;
48
+ open: (flowId: string, options?: OpenOptions, commandOptions?: NativeProviderCommandOptions) => Promise<TResult>;
49
+ prefetch: (flowId: string, options?: NativeProviderCommandOptions) => Promise<TResult>;
50
+ prerender: (flowId: string, options?: PrerenderOptions, commandOptions?: NativeProviderCommandOptions) => Promise<TResult>;
51
+ flow: (flowId: string) => ReactNativeFlowRun;
52
+ close: (flowHandleId?: string, options?: NativeProviderCommandOptions) => Promise<TResult>;
53
+ reset: (options?: NativeProviderCommandOptions) => Promise<TResult>;
54
+ updateHostContext: (context: HostContext, options?: NativeProviderCommandOptions) => Promise<TResult>;
55
+ emitHostSignal: (name: string, data?: unknown, options?: NativeProviderCommandOptions) => Promise<TResult>;
56
+ }
57
+ type NativeProviderClientOptions<TResult> = {
58
+ dispatchCommand: (command: NativeProviderClientCommand, options?: NativeProviderCommandOptions) => Promise<TResult>;
59
+ flow: (flowId: string) => ReactNativeFlowRun;
60
+ instanceId: string;
61
+ };
62
+ export declare const parseRequiredString: (name: string, value?: string) => string;
63
+ export declare function createNativeProviderClient<TResult>({ dispatchCommand, flow, instanceId, }: NativeProviderClientOptions<TResult>): NativeProviderClient<TResult>;
64
+ export {};
@@ -0,0 +1,76 @@
1
+ import { assertNoSegmentExternalIdsInValueArgument } from "@getuserfeedback/protocol/host";
2
+ export const parseRequiredString = (name, value) => {
3
+ const normalized = value === null || value === void 0 ? void 0 : value.trim();
4
+ if (!normalized) {
5
+ throw new Error(`${name} must be a non-empty string`);
6
+ }
7
+ return normalized;
8
+ };
9
+ const toIdentifyCommandPayload = (identifyInput, traitsOrOptions, options) => {
10
+ if (typeof identifyInput === "string") {
11
+ assertNoSegmentExternalIdsInValueArgument({
12
+ argument: traitsOrOptions,
13
+ commandName: "identify",
14
+ valueArgumentName: "traits",
15
+ callShape: "identify(userId, traits, { externalIds })",
16
+ });
17
+ return {
18
+ kind: "identify",
19
+ userId: parseRequiredString("userId", identifyInput),
20
+ traits: traitsOrOptions,
21
+ options,
22
+ };
23
+ }
24
+ assertNoSegmentExternalIdsInValueArgument({
25
+ argument: identifyInput,
26
+ commandName: "identify",
27
+ valueArgumentName: "traits",
28
+ callShape: "identify(traits, { externalIds })",
29
+ });
30
+ return {
31
+ kind: "identify",
32
+ traits: identifyInput,
33
+ options: options !== null && options !== void 0 ? options : traitsOrOptions,
34
+ };
35
+ };
36
+ export function createNativeProviderClient({ dispatchCommand, flow, instanceId, }) {
37
+ return {
38
+ instanceId,
39
+ configure: (opts, options) => dispatchCommand({ kind: "configure", opts }, options),
40
+ identify: ((identifyInput, traitsOrOptions, options) => dispatchCommand(toIdentifyCommandPayload(identifyInput, traitsOrOptions, options))),
41
+ track: (eventName, properties, options) => {
42
+ assertNoSegmentExternalIdsInValueArgument({
43
+ argument: properties,
44
+ commandName: "track",
45
+ valueArgumentName: "properties",
46
+ callShape: "track(eventName, properties, { externalIds })",
47
+ });
48
+ return dispatchCommand(Object.assign(Object.assign({ kind: "track", origin: "customer", event: parseRequiredString("eventName", eventName) }, (properties === undefined ? {} : { properties })), ((options === null || options === void 0 ? void 0 : options.externalIds) === undefined
49
+ ? {}
50
+ : { context: { externalIds: options.externalIds } })));
51
+ },
52
+ open: (flowId, options, commandOptions) => dispatchCommand({
53
+ kind: "open",
54
+ flowId: parseRequiredString("flowId", flowId),
55
+ hideCloseButton: options === null || options === void 0 ? void 0 : options.hideCloseButton,
56
+ }, commandOptions),
57
+ prefetch: (flowId, options) => dispatchCommand({
58
+ kind: "prefetch",
59
+ flowId: parseRequiredString("flowId", flowId),
60
+ }, options),
61
+ prerender: (flowId, options, commandOptions) => dispatchCommand({
62
+ kind: "prerender",
63
+ flowId: parseRequiredString("flowId", flowId),
64
+ hideCloseButton: options === null || options === void 0 ? void 0 : options.hideCloseButton,
65
+ }, commandOptions),
66
+ flow,
67
+ close: (flowHandleId, options) => dispatchCommand(Object.assign({ kind: "close" }, (flowHandleId === undefined
68
+ ? {}
69
+ : {
70
+ flowHandleId: parseRequiredString("flowHandleId", flowHandleId),
71
+ })), options),
72
+ reset: (options) => dispatchCommand({ kind: "reset" }, options),
73
+ updateHostContext: (context, options) => dispatchCommand({ kind: "updateHostContext", context }, options),
74
+ emitHostSignal: (name, data, options) => dispatchCommand(Object.assign({ kind: "emitHostSignal", name: parseRequiredString("name", name) }, (data === undefined ? {} : { data })), options),
75
+ };
76
+ }
@@ -13,7 +13,9 @@ export type NativeRuntimeRootProps = {
13
13
  onCommandSessionChange?: (session: NativeCoreCommandSession | null) => void;
14
14
  onError?: (error: Error) => void;
15
15
  onInvalidMessage?: (error: Error, value: unknown) => void;
16
+ onStartupTerminalError?: (error: Error) => void;
16
17
  startupCommandSettlementTimeoutMs?: number;
18
+ viewNativeViewComponent?: NativeViewHostProjectionProps["nativeViewComponent"];
17
19
  viewWebViewComponent?: NativeViewHostProjectionProps["webViewComponent"];
18
20
  viewWebViewProps?: NativeViewHostProjectionProps["webViewProps"];
19
21
  };
@@ -28,12 +30,12 @@ type NativeRuntimeConfigureCommand = Omit<CoreCommandEnvelope, "command"> & {
28
30
  }>;
29
31
  };
30
32
  type NativeRuntimeStartupCommands = readonly [NativeRuntimeInitCommand] | readonly [NativeRuntimeInitCommand, NativeRuntimeConfigureCommand];
31
- type NativeProviderRuntimeProps = Omit<NativeRuntimeRootProps, "onCommandSessionChange" | "startupCommands"> & {
33
+ type NativeProviderRuntimeProps = Omit<NativeRuntimeRootProps, "onCommandSessionChange" | "onStartupTerminalError" | "startupCommands"> & {
32
34
  initialConfigureOptions?: ConfigureOptions;
33
35
  initOptions: InitOptions;
34
36
  instanceId: string;
35
37
  onCommandSessionChange?: (session: NativeDirectCommandSession | null) => void;
36
38
  };
37
- export declare function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewProps, onCommandSessionChange, onError, onInvalidMessage, startupCommandSettlementTimeoutMs, startupCommands, viewWebViewComponent, viewWebViewProps, }: NativeRuntimeRootProps): ReactElement;
38
- export declare function NativeProviderRuntime({ initOptions, initialConfigureOptions, instanceId, onCommandSessionChange, ...runtimeProps }: NativeProviderRuntimeProps): ReactElement;
39
+ export declare function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewProps, onCommandSessionChange, onError, onInvalidMessage, onStartupTerminalError, startupCommandSettlementTimeoutMs, startupCommands, viewNativeViewComponent, viewWebViewComponent, viewWebViewProps, }: NativeRuntimeRootProps): ReactElement;
40
+ export declare function NativeProviderRuntime({ initOptions, initialConfigureOptions, instanceId, onCommandSessionChange, onError, ...runtimeProps }: NativeProviderRuntimeProps): ReactElement;
39
41
  export {};
@@ -18,6 +18,7 @@ import { createNativeDirectCommandSession, } from "./native-direct-command-sessi
18
18
  import { NativeViewHostProjection, } from "./native-view-host-projection.js";
19
19
  import { createNativeViewRecordController, } from "./native-view-record-controller.js";
20
20
  import { createProviderCommandEnvelope, REACT_NATIVE_CLIENT_META, } from "./provider-command-helpers.js";
21
+ import { useNativeDirectCommandSessionController } from "./use-native-direct-command-session-controller.js";
21
22
  const DEFAULT_STARTUP_COMMAND_SETTLEMENT_TIMEOUT_MS = 30000;
22
23
  function dispatchStartupCommand(input) {
23
24
  let timeoutId;
@@ -42,7 +43,7 @@ async function dispatchStartupCommands(input) {
42
43
  });
43
44
  }
44
45
  }
45
- export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewProps, onCommandSessionChange, onError, onInvalidMessage, startupCommandSettlementTimeoutMs = DEFAULT_STARTUP_COMMAND_SETTLEMENT_TIMEOUT_MS, startupCommands, viewWebViewComponent, viewWebViewProps, }) {
46
+ export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewProps, onCommandSessionChange, onError, onInvalidMessage, onStartupTerminalError, startupCommandSettlementTimeoutMs = DEFAULT_STARTUP_COMMAND_SETTLEMENT_TIMEOUT_MS, startupCommands, viewNativeViewComponent, viewWebViewComponent, viewWebViewProps, }) {
46
47
  const [coreHostRevision, setCoreHostRevision] = useState(0);
47
48
  const [session, setSession] = useState(null);
48
49
  const sessionRef = useRef(null);
@@ -54,6 +55,8 @@ export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewPr
54
55
  onCommandSessionChangeRef.current = onCommandSessionChange;
55
56
  const onInvalidMessageRef = useRef(onInvalidMessage);
56
57
  onInvalidMessageRef.current = onInvalidMessage;
58
+ const onStartupTerminalErrorRef = useRef(onStartupTerminalError);
59
+ onStartupTerminalErrorRef.current = onStartupTerminalError;
57
60
  const startupCommandsRef = useRef(startupCommands);
58
61
  startupCommandsRef.current = startupCommands;
59
62
  const reportError = useCallback((error) => {
@@ -124,6 +127,7 @@ export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewPr
124
127
  })
125
128
  .then(commitSession)
126
129
  .catch((error) => {
130
+ var _a;
127
131
  if (sessionRef.current !== nextSession) {
128
132
  return;
129
133
  }
@@ -131,11 +135,21 @@ export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewPr
131
135
  commandSessionOwner.dispose();
132
136
  commandChannelOwner.dispose();
133
137
  controller.dispose();
134
- if (!(error instanceof CommandSettlementError) &&
135
- !startupRetryAttemptedRef.current) {
138
+ const shouldRetry = !(error instanceof CommandSettlementError) &&
139
+ !startupRetryAttemptedRef.current;
140
+ if (shouldRetry) {
136
141
  startupRetryAttemptedRef.current = true;
137
142
  setCoreHostRevision((revision) => revision + 1);
138
143
  }
144
+ else {
145
+ startupRetryAttemptedRef.current = false;
146
+ try {
147
+ (_a = onStartupTerminalErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onStartupTerminalErrorRef, error instanceof Error ? error : new Error(String(error)));
148
+ }
149
+ catch (observerError) {
150
+ reportError(observerError);
151
+ }
152
+ }
139
153
  reportError(error);
140
154
  });
141
155
  return;
@@ -199,17 +213,26 @@ export function NativeRuntimeRoot({ coreUrl, coreWebViewComponent, coreWebViewPr
199
213
  : createElement(NativeViewHostProjection, {
200
214
  controller: session.controller,
201
215
  key: session.key,
216
+ nativeViewComponent: viewNativeViewComponent,
202
217
  onInvalidMessage,
203
218
  webViewComponent: viewWebViewComponent,
204
219
  webViewProps: viewWebViewProps,
205
220
  }));
206
221
  }
207
222
  export function NativeProviderRuntime(_a) {
208
- var { initOptions, initialConfigureOptions, instanceId, onCommandSessionChange } = _a, runtimeProps = __rest(_a, ["initOptions", "initialConfigureOptions", "instanceId", "onCommandSessionChange"]);
223
+ var { initOptions, initialConfigureOptions, instanceId, onCommandSessionChange, onError } = _a, runtimeProps = __rest(_a, ["initOptions", "initialConfigureOptions", "instanceId", "onCommandSessionChange", "onError"]);
224
+ const directSessionController = useNativeDirectCommandSessionController();
209
225
  const directSessionRef = useRef(null);
226
+ const handleStartupTerminalError = useCallback((error) => {
227
+ directSessionController.rejectUntilAttached(error);
228
+ }, [directSessionController]);
210
229
  const handleCommandSessionChange = useCallback((session) => {
211
230
  var _a;
212
231
  if (session === null) {
232
+ if (directSessionRef.current !== null) {
233
+ directSessionController.detach(directSessionRef.current.directSession);
234
+ directSessionRef.current = null;
235
+ }
213
236
  onCommandSessionChange === null || onCommandSessionChange === void 0 ? void 0 : onCommandSessionChange(null);
214
237
  return;
215
238
  }
@@ -220,9 +243,10 @@ export function NativeProviderRuntime(_a) {
220
243
  coreSession: session,
221
244
  }),
222
245
  };
246
+ directSessionController.attach(directSessionRef.current.directSession);
223
247
  }
224
- onCommandSessionChange === null || onCommandSessionChange === void 0 ? void 0 : onCommandSessionChange(directSessionRef.current.directSession);
225
- }, [onCommandSessionChange]);
248
+ onCommandSessionChange === null || onCommandSessionChange === void 0 ? void 0 : onCommandSessionChange(directSessionController.session);
249
+ }, [directSessionController, onCommandSessionChange]);
226
250
  const startupCommands = useMemo(() => {
227
251
  const initCommand = createProviderCommandEnvelope({
228
252
  clientMeta: REACT_NATIVE_CLIENT_META,
@@ -240,5 +264,5 @@ export function NativeProviderRuntime(_a) {
240
264
  }),
241
265
  ];
242
266
  }, [initialConfigureOptions, initOptions, instanceId]);
243
- return createElement(NativeRuntimeRoot, Object.assign(Object.assign({}, runtimeProps), { onCommandSessionChange: handleCommandSessionChange, startupCommands }));
267
+ return createElement(NativeRuntimeRoot, Object.assign(Object.assign({}, runtimeProps), { onCommandSessionChange: handleCommandSessionChange, onError, onStartupTerminalError: handleStartupTerminalError, startupCommands }));
244
268
  }
@@ -3,7 +3,8 @@ import type { NativeViewRecordController } from "./native-view-record-controller
3
3
  export type NativeViewHostProjectionProps = {
4
4
  controller: NativeViewRecordController;
5
5
  onInvalidMessage?: (error: Error, value: unknown) => void;
6
+ nativeViewComponent?: ComponentType<Record<string, unknown>>;
6
7
  webViewComponent?: ComponentType<Record<string, unknown>>;
7
8
  webViewProps?: Record<string, unknown>;
8
9
  };
9
- export declare function NativeViewHostProjection({ controller, onInvalidMessage, webViewComponent, webViewProps, }: NativeViewHostProjectionProps): ReactElement;
10
+ export declare function NativeViewHostProjection({ controller, nativeViewComponent, onInvalidMessage, webViewComponent, webViewProps, }: NativeViewHostProjectionProps): ReactElement;
@@ -1,6 +1,7 @@
1
- import { createElement, Fragment, useCallback, useEffect, useRef, useSyncExternalStore, } from "react";
1
+ import { createElement, Fragment, useCallback, useEffect, useMemo, useRef, useSyncExternalStore, } from "react";
2
+ import { NativeViewPresentationSurface } from "./native-view-presentation-surface.js";
2
3
  import { ViewWebViewHost } from "./view-webview-host.js";
3
- function NativeViewHostProjectionEntry({ controller, onInvalidMessage, record, webViewComponent, webViewProps, }) {
4
+ function NativeViewHostProjectionEntry({ controller, nativeViewComponent, onInvalidMessage, record, webViewComponent, webViewProps, }) {
4
5
  const detachConnectionRef = useRef(null);
5
6
  const detachConnection = useCallback(() => {
6
7
  const detach = detachConnectionRef.current;
@@ -8,15 +9,17 @@ function NativeViewHostProjectionEntry({ controller, onInvalidMessage, record, w
8
9
  detach === null || detach === void 0 ? void 0 : detach();
9
10
  }, []);
10
11
  const handleConnected = useCallback((connection) => {
11
- detachConnection();
12
12
  const controllerDetach = controller.attachViewReadinessConnection({
13
13
  allocationId: record.allocationId,
14
14
  connection,
15
15
  });
16
- detachConnectionRef.current = controllerDetach;
17
- }, [controller, detachConnection, record.allocationId]);
16
+ if (controllerDetach) {
17
+ detachConnectionRef.current = controllerDetach;
18
+ }
19
+ }, [controller, record.allocationId]);
20
+ const presentedWebViewProps = useMemo(() => (Object.assign(Object.assign({}, webViewProps), { style: [webViewProps === null || webViewProps === void 0 ? void 0 : webViewProps.style, { flex: 1 }] })), [webViewProps]);
18
21
  useEffect(() => detachConnection, [detachConnection]);
19
- return createElement(ViewWebViewHost, {
22
+ return createElement(NativeViewPresentationSurface, { nativeViewComponent, record }, createElement(ViewWebViewHost, {
20
23
  generation: record.generation,
21
24
  onConnected: handleConnected,
22
25
  onDisconnected: detachConnection,
@@ -24,13 +27,14 @@ function NativeViewHostProjectionEntry({ controller, onInvalidMessage, record, w
24
27
  srcdoc: record.srcdoc,
25
28
  viewId: record.viewId,
26
29
  webViewComponent,
27
- webViewProps,
28
- });
30
+ webViewProps: presentedWebViewProps,
31
+ }));
29
32
  }
30
- export function NativeViewHostProjection({ controller, onInvalidMessage, webViewComponent, webViewProps, }) {
33
+ export function NativeViewHostProjection({ controller, nativeViewComponent, onInvalidMessage, webViewComponent, webViewProps, }) {
31
34
  const records = useSyncExternalStore(controller.subscribe, controller.getSnapshot, controller.getSnapshot);
32
35
  return createElement(Fragment, null, records.map((record) => createElement(NativeViewHostProjectionEntry, {
33
36
  controller,
37
+ nativeViewComponent,
34
38
  onInvalidMessage,
35
39
  record,
36
40
  webViewComponent,
@@ -0,0 +1,7 @@
1
+ import { type ComponentType, type ReactElement, type ReactNode } from "react";
2
+ import type { NativeViewRecord } from "./native-view-record-controller.js";
3
+ export declare function NativeViewPresentationSurface({ children, nativeViewComponent, record, }: {
4
+ children?: ReactNode;
5
+ nativeViewComponent?: ComponentType<Record<string, unknown>>;
6
+ record: NativeViewRecord;
7
+ }): ReactElement;