react-native-webview-stream-chunks 0.3.0 → 0.4.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.
@@ -20,7 +20,172 @@ export const WebViewStreamEventTypes = {
20
20
  Instead, we compile the receiver function to JS at build time and inject it as a string into the final bundle.
21
21
  See scripts/injectReceiver.mjs for details. The placeholder below is replaced in dist/streamChunksPreloadScript.js after tsc build.
22
22
  */
23
- const webViewStreamReceiverIIFECode = '__WEBVIEW_RECEIVER_CODE__';
23
+ const webViewStreamReceiverIIFECode = `function webViewStreamReceiverIIFE(options, eventTypes) {
24
+ let payloadChunks = [];
25
+ let contentMutationObserved = false;
26
+ const injectedPayloadScriptClasses = new Set();
27
+ function resetState() {
28
+ payloadChunks = [];
29
+ contentMutationObserved = false;
30
+ }
31
+ const targetWindow = window;
32
+ // -- receiver-ready callback resolver -------------------------------------
33
+ function runReceiverReadyCallback() {
34
+ if (!options.receiverReadyCallbackPath.trim())
35
+ return;
36
+ const segments = options.receiverReadyCallbackPath.split('.').filter(Boolean);
37
+ let current = targetWindow;
38
+ for (const segment of segments) {
39
+ if (current === null || current === undefined || typeof current !== 'object') {
40
+ current = undefined;
41
+ break;
42
+ }
43
+ current = current[segment];
44
+ }
45
+ if (typeof current === 'function') {
46
+ current();
47
+ }
48
+ }
49
+ // -- event dispatcher -----------------------------------------------------
50
+ targetWindow.onStreamChunksToWebView = function (event) {
51
+ var _a;
52
+ switch (event.type) {
53
+ case eventTypes.SET_CONTENT: {
54
+ resetState();
55
+ replaceBodyContent((_a = event.data) !== null && _a !== void 0 ? _a : '');
56
+ break;
57
+ }
58
+ case eventTypes.APPEND_CHUNK: {
59
+ if (event.data)
60
+ payloadChunks.push(event.data);
61
+ break;
62
+ }
63
+ case eventTypes.FINALIZE_PAYLOAD: {
64
+ const waitAndInject = () => {
65
+ if (contentMutationObserved) {
66
+ injectPayloadScript(event.data);
67
+ }
68
+ else {
69
+ setTimeout(waitAndInject, 100);
70
+ }
71
+ };
72
+ waitAndInject();
73
+ break;
74
+ }
75
+ case eventTypes.REEXECUTE_SCRIPTS: {
76
+ const waitAndExec = () => {
77
+ if (contentMutationObserved) {
78
+ reexecuteScripts(event.data);
79
+ }
80
+ else {
81
+ setTimeout(waitAndExec, 100);
82
+ }
83
+ };
84
+ waitAndExec();
85
+ break;
86
+ }
87
+ case eventTypes.CHECK_RECEIVER_READY: {
88
+ runReceiverReadyCallback();
89
+ break;
90
+ }
91
+ }
92
+ };
93
+ // -- DOM helpers ----------------------------------------------------------
94
+ function replaceBodyContent(newInnerHTML) {
95
+ const observer = new MutationObserver((mutationsList, currentObserver) => {
96
+ for (const mutation of mutationsList) {
97
+ if (mutation.type === 'childList') {
98
+ currentObserver.disconnect();
99
+ contentMutationObserved = true;
100
+ return;
101
+ }
102
+ }
103
+ });
104
+ observer.observe(document.body, { childList: true });
105
+ document.body.innerHTML = newInnerHTML;
106
+ }
107
+ const defaultInjectionOptions = {
108
+ scriptType: 'application/json',
109
+ scriptClassName: 'webview-stream-payload',
110
+ scriptTagName: 'default-payload',
111
+ anchorSelector: '#styleArea',
112
+ };
113
+ function resolveInjectionOptions(rawData) {
114
+ var _a, _b, _c, _d;
115
+ if (!rawData) {
116
+ return defaultInjectionOptions;
117
+ }
118
+ try {
119
+ const parsed = JSON.parse(rawData);
120
+ return {
121
+ scriptType: (_a = parsed.scriptType) !== null && _a !== void 0 ? _a : defaultInjectionOptions.scriptType,
122
+ scriptClassName: (_b = parsed.scriptClassName) !== null && _b !== void 0 ? _b : defaultInjectionOptions.scriptClassName,
123
+ scriptTagName: (_c = parsed.scriptTagName) !== null && _c !== void 0 ? _c : defaultInjectionOptions.scriptTagName,
124
+ anchorSelector: (_d = parsed.anchorSelector) !== null && _d !== void 0 ? _d : defaultInjectionOptions.anchorSelector,
125
+ };
126
+ }
127
+ catch (_e) {
128
+ return defaultInjectionOptions;
129
+ }
130
+ }
131
+ function injectPayloadScript(rawData) {
132
+ try {
133
+ const injectionOptions = resolveInjectionOptions(rawData);
134
+ const fullPayload = payloadChunks.join('');
135
+ const scriptElement = document.createElement('script');
136
+ scriptElement.type = injectionOptions.scriptType;
137
+ scriptElement.classList.add(injectionOptions.scriptClassName, injectionOptions.scriptTagName);
138
+ scriptElement.textContent = fullPayload;
139
+ const anchor = document.querySelector(injectionOptions.anchorSelector);
140
+ anchor === null || anchor === void 0 ? void 0 : anchor.insertAdjacentElement('afterend', scriptElement);
141
+ injectedPayloadScriptClasses.add(injectionOptions.scriptClassName);
142
+ }
143
+ catch (error) {
144
+ console.error('[webview-stream] injectPayloadScript error', error);
145
+ }
146
+ payloadChunks = [];
147
+ }
148
+ function reexecuteScripts(selector) {
149
+ try {
150
+ const query = (selector === null || selector === void 0 ? void 0 : selector.trim()) || 'script';
151
+ const elements = Array.from(document.querySelectorAll(query));
152
+ for (const element of elements) {
153
+ if (!(element instanceof HTMLScriptElement))
154
+ continue;
155
+ let isInjectedPayloadScript = false;
156
+ for (const payloadClassName of injectedPayloadScriptClasses) {
157
+ if (element.classList.contains(payloadClassName)) {
158
+ isInjectedPayloadScript = true;
159
+ break;
160
+ }
161
+ }
162
+ if (isInjectedPayloadScript)
163
+ continue;
164
+ const replacement = document.createElement('script');
165
+ for (const { name, value } of Array.from(element.attributes)) {
166
+ replacement.setAttribute(name, value);
167
+ }
168
+ if (element.src) {
169
+ replacement.src = element.src;
170
+ }
171
+ else {
172
+ replacement.textContent = element.textContent;
173
+ }
174
+ if (element.parentNode !== null) {
175
+ try {
176
+ element.parentNode.replaceChild(replacement, element);
177
+ }
178
+ catch (error) {
179
+ console.error('[webview-stream] Failed to re-execute script tag', replacement, element, error);
180
+ }
181
+ }
182
+ }
183
+ }
184
+ catch (error) {
185
+ console.error('[webview-stream] reexecuteScripts error', error);
186
+ }
187
+ }
188
+ }`;
24
189
  // ---------------------------------------------------------------------------
25
190
  // Preload script generators
26
191
  // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-webview-stream-chunks",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Reusable preload script and event types for streaming large HTML/chunks into WebView",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",