@getuserfeedback/react-native 3.0.34 → 3.0.36

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.
@@ -0,0 +1,23 @@
1
+ type NativeViewActivationState = "idle" | "prerendering" | "activation-requested" | "prerendering-activation-requested" | "prerendered" | "active";
2
+ type NativeViewRecord = Readonly<{
3
+ allocationId: number;
4
+ activationState: NativeViewActivationState;
5
+ generation: number;
6
+ instanceId: string;
7
+ srcdoc: string;
8
+ viewId: string;
9
+ }>;
10
+ export type NativeViewRecordController = {
11
+ dispose(): void;
12
+ getSnapshot(): readonly NativeViewRecord[];
13
+ handleCoreMessage(value: unknown): void;
14
+ markPrepared(input: {
15
+ allocationId: number;
16
+ viewId: string;
17
+ }): boolean;
18
+ subscribe(listener: () => void): () => void;
19
+ };
20
+ export declare function createNativeViewRecordController(input: {
21
+ generation: number;
22
+ }): NativeViewRecordController;
23
+ export {};
@@ -0,0 +1,159 @@
1
+ import { coreViewHostActionSchema, } from "@getuserfeedback/protocol/internal/core-view-host-actions";
2
+ import { createViewActivationController, createViewPreparationRegistry, } from "./view-orchestration-runtime.js";
3
+ export function createNativeViewRecordController(input) {
4
+ const entries = new Map();
5
+ const listeners = new Set();
6
+ const pendingActivations = new Map();
7
+ const preparations = createViewPreparationRegistry();
8
+ let disposed = false;
9
+ let nextAllocationId = 0;
10
+ let snapshot = Object.freeze([]);
11
+ const notify = () => {
12
+ snapshot = Object.freeze(Array.from(entries.values(), toRecord));
13
+ for (const listener of listeners) {
14
+ listener();
15
+ }
16
+ };
17
+ const ensurePendingActivation = (viewId) => {
18
+ const existing = pendingActivations.get(viewId);
19
+ if (existing) {
20
+ return existing;
21
+ }
22
+ const created = createViewActivationController();
23
+ pendingActivations.set(viewId, created);
24
+ return created;
25
+ };
26
+ const removeEntry = (entry) => {
27
+ if (entries.get(entry.viewId) !== entry) {
28
+ return false;
29
+ }
30
+ entries.delete(entry.viewId);
31
+ notify();
32
+ return true;
33
+ };
34
+ const handlePrerender = (action) => {
35
+ var _a;
36
+ if (entries.has(action.viewId)) {
37
+ return;
38
+ }
39
+ const preparation = preparations.claim(action.viewId);
40
+ if (!preparation) {
41
+ return;
42
+ }
43
+ const activation = (_a = pendingActivations.get(action.viewId)) !== null && _a !== void 0 ? _a : createViewActivationController();
44
+ pendingActivations.delete(action.viewId);
45
+ activation.startPrerender();
46
+ nextAllocationId += 1;
47
+ const entry = {
48
+ activation,
49
+ allocationId: nextAllocationId,
50
+ generation: action.gen,
51
+ instanceId: action.instanceId,
52
+ preparation,
53
+ srcdoc: action.srcdoc,
54
+ viewId: action.viewId,
55
+ };
56
+ entries.set(action.viewId, entry);
57
+ preparation.addCleanup(() => {
58
+ removeEntry(entry);
59
+ });
60
+ notify();
61
+ };
62
+ const handleActivate = (action) => {
63
+ var _a;
64
+ const entry = entries.get(action.viewId);
65
+ const activation = (_a = entry === null || entry === void 0 ? void 0 : entry.activation) !== null && _a !== void 0 ? _a : ensurePendingActivation(action.viewId);
66
+ const previousState = activation.getState();
67
+ activation.requestActivation();
68
+ if (entry && activation.getState() !== previousState) {
69
+ notify();
70
+ }
71
+ };
72
+ const handleClose = (action) => {
73
+ if (preparations.cancel(action.viewId)) {
74
+ return;
75
+ }
76
+ pendingActivations.delete(action.viewId);
77
+ const entry = entries.get(action.viewId);
78
+ if (entry) {
79
+ removeEntry(entry);
80
+ }
81
+ };
82
+ return {
83
+ dispose() {
84
+ if (disposed) {
85
+ return;
86
+ }
87
+ disposed = true;
88
+ const hadEntries = entries.size > 0;
89
+ entries.clear();
90
+ pendingActivations.clear();
91
+ preparations.cancelAll();
92
+ if (hadEntries) {
93
+ snapshot = Object.freeze([]);
94
+ for (const listener of listeners) {
95
+ listener();
96
+ }
97
+ }
98
+ listeners.clear();
99
+ },
100
+ getSnapshot: () => snapshot,
101
+ handleCoreMessage(value) {
102
+ if (disposed) {
103
+ return;
104
+ }
105
+ const parsed = coreViewHostActionSchema.safeParse(value);
106
+ if (!parsed.success || parsed.data.gen !== input.generation) {
107
+ return;
108
+ }
109
+ switch (parsed.data.t) {
110
+ case "getuserfeedback:action:prerenderView":
111
+ handlePrerender(parsed.data);
112
+ break;
113
+ case "getuserfeedback:action:activateView":
114
+ handleActivate(parsed.data);
115
+ break;
116
+ case "getuserfeedback:action:closeView":
117
+ handleClose(parsed.data);
118
+ break;
119
+ }
120
+ },
121
+ markPrepared({ allocationId, viewId }) {
122
+ if (disposed) {
123
+ return false;
124
+ }
125
+ const entry = entries.get(viewId);
126
+ if (!entry || entry.allocationId !== allocationId) {
127
+ return false;
128
+ }
129
+ if (!entry.preparation.commit()) {
130
+ return false;
131
+ }
132
+ const previousState = entry.activation.getState();
133
+ entry.activation.markPrerendered();
134
+ if (entry.activation.getState() !== previousState) {
135
+ notify();
136
+ }
137
+ return true;
138
+ },
139
+ subscribe(listener) {
140
+ if (disposed) {
141
+ return () => { };
142
+ }
143
+ listeners.add(listener);
144
+ return () => listeners.delete(listener);
145
+ },
146
+ };
147
+ }
148
+ function toRecord(entry) {
149
+ return Object.freeze({
150
+ activationState: entry.activation.getState(),
151
+ allocationId: entry.allocationId,
152
+ generation: entry.generation,
153
+ instanceId: entry.instanceId,
154
+ srcdoc: entry.srcdoc,
155
+ viewId: entry.viewId,
156
+ });
157
+ }
158
+ // TODO(migration): Subscribe this controller to the native core transport, render its identity-safe records through ViewWebViewHost, and complete preparation only after protocol-owned view readiness is observed. Keep the provider runtime active during this proof.
159
+ // [from=private-native-view-allocation-state] [to=private-native-view-readiness-routing] [scope=slice] [priority=high] [impact=high] [risk=high] [epic=runtime-neutral-view-host-orchestration] [epic_order=30]
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.34";
6
+ export const REACT_NATIVE_SDK_VERSION = reactNativeSdkVersion.length > 0 ? reactNativeSdkVersion : "3.0.36";
@@ -79,6 +79,90 @@ function readViewActivationState(value) {
79
79
  }
80
80
  return value;
81
81
  }
82
+ // ../view-orchestration/src/view-preparation-registry.ts
83
+ class ViewPreparationCancelledError extends Error {
84
+ viewId;
85
+ constructor(viewId) {
86
+ super(`View preparation for "${viewId}" was cancelled`);
87
+ this.name = "AbortError";
88
+ this.viewId = viewId;
89
+ }
90
+ }
91
+ function runCleanups(entry) {
92
+ for (const cleanup of Array.from(entry.cleanups)) {
93
+ entry.cleanups.delete(cleanup);
94
+ try {
95
+ cleanup();
96
+ } catch {}
97
+ }
98
+ }
99
+ function createViewPreparationRegistry() {
100
+ const entries = new Map;
101
+ return {
102
+ claim(viewId) {
103
+ if (entries.has(viewId)) {
104
+ return null;
105
+ }
106
+ const entry = {
107
+ controller: new AbortController,
108
+ cleanups: new Set
109
+ };
110
+ entries.set(viewId, entry);
111
+ return {
112
+ signal: entry.controller.signal,
113
+ addCleanup: (cleanup) => {
114
+ if (entry.controller.signal.aborted) {
115
+ try {
116
+ cleanup();
117
+ } catch {}
118
+ return () => {};
119
+ }
120
+ entry.cleanups.add(cleanup);
121
+ return () => entry.cleanups.delete(cleanup);
122
+ },
123
+ commit: () => {
124
+ if (entry.controller.signal.aborted || entries.get(viewId) !== entry) {
125
+ return false;
126
+ }
127
+ entries.delete(viewId);
128
+ entry.cleanups.clear();
129
+ return true;
130
+ },
131
+ isCancelled: () => entry.controller.signal.aborted,
132
+ release: () => {
133
+ if (entries.get(viewId) === entry) {
134
+ entries.delete(viewId);
135
+ }
136
+ runCleanups(entry);
137
+ }
138
+ };
139
+ },
140
+ cancel(viewId) {
141
+ const entry = entries.get(viewId);
142
+ if (!entry) {
143
+ return false;
144
+ }
145
+ entries.delete(viewId);
146
+ entry.controller.abort(new ViewPreparationCancelledError(viewId));
147
+ runCleanups(entry);
148
+ return true;
149
+ },
150
+ cancelAll() {
151
+ const viewIds = Array.from(entries.keys());
152
+ for (const viewId of viewIds) {
153
+ const entry = entries.get(viewId);
154
+ entries.delete(viewId);
155
+ entry?.controller.abort(new ViewPreparationCancelledError(viewId));
156
+ if (entry) {
157
+ runCleanups(entry);
158
+ }
159
+ }
160
+ return viewIds;
161
+ },
162
+ has: (viewId) => entries.has(viewId),
163
+ isEmpty: () => entries.size === 0
164
+ };
165
+ }
82
166
  // src/view-orchestration-runtime.ts
83
167
  function createActiveViewSnapshotListController2() {
84
168
  return createActiveViewSnapshotListController();
@@ -86,7 +170,11 @@ function createActiveViewSnapshotListController2() {
86
170
  function createViewActivationController2() {
87
171
  return createViewActivationController();
88
172
  }
173
+ function createViewPreparationRegistry2() {
174
+ return createViewPreparationRegistry();
175
+ }
89
176
  export {
177
+ createViewPreparationRegistry2 as createViewPreparationRegistry,
90
178
  createViewActivationController2 as createViewActivationController,
91
179
  createActiveViewSnapshotListController2 as createActiveViewSnapshotListController
92
180
  };
@@ -50,5 +50,3 @@ function ViewWebViewHostDocument({ generation, onConnected, onDisconnected, onIn
50
50
  export function ViewWebViewHost(props) {
51
51
  return createElement(ViewWebViewHostDocument, Object.assign(Object.assign({}, props), { key: JSON.stringify([props.viewId, props.generation, props.srcdoc]) }));
52
52
  }
53
- // TODO(migration): Wire core-native view allocation and message routing through the preparation registry into this private per-view host. Keep the provider runtime active while proving one native-owned view end to end.
54
- // [from=private-native-per-view-webview-adapter] [to=private-native-view-allocation-routing] [scope=slice] [priority=high] [impact=high] [risk=high] [epic=runtime-neutral-view-host-orchestration] [epic_order=20]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getuserfeedback/react-native",
3
- "version": "3.0.34",
3
+ "version": "3.0.36",
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.8.0",
48
- "@getuserfeedback/sdk": "^0.10.4",
47
+ "@getuserfeedback/protocol": "^3.9.0",
48
+ "@getuserfeedback/sdk": "^0.10.5",
49
49
  "robot3": "^1.2.0"
50
50
  },
51
51
  "peerDependencies": {