react-native-nitro-fetch 1.2.1 → 1.3.1
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/android/src/main/java/com/margelo/nitro/nitrofetch/AutoPrefetcher.kt +114 -31
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchClient.kt +43 -6
- package/ios/NitroAutoPrefetcher.h +21 -0
- package/ios/NitroAutoPrefetcher.swift +149 -27
- package/ios/NitroFetchClient.swift +17 -6
- package/lib/module/CurlGenerator.js.map +1 -1
- package/lib/module/HermesProfiler.js.map +2 -1
- package/lib/module/NetworkInspector.js +0 -4
- package/lib/module/NetworkInspector.js.map +1 -1
- package/lib/module/NitroCronet.nitro.js.map +1 -1
- package/lib/module/NitroFetch.nitro.js.map +1 -0
- package/lib/module/NitroInstances.js.map +1 -1
- package/lib/module/Response.js +3 -4
- package/lib/module/Response.js.map +2 -1
- package/lib/module/fetch.js +26 -6
- package/lib/module/fetch.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +2 -1
- package/lib/module/index.web.js +1 -2
- package/lib/module/utf8.js +19 -8
- package/lib/module/utf8.js.map +2 -1
- package/lib/typescript/src/NitroFetch.nitro.d.ts +2 -1
- package/lib/typescript/src/NitroFetch.nitro.d.ts.map +1 -1
- package/lib/typescript/src/Response.d.ts.map +1 -1
- package/lib/typescript/src/fetch.d.ts +4 -1
- package/lib/typescript/src/fetch.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/utf8.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JHybridNitroFetchClientSpec.cpp +2 -0
- package/nitrogen/generated/android/c++/JNitroRequest.hpp +5 -1
- package/nitrogen/generated/android/c++/JNitroResponse.hpp +7 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroRequest.kt +5 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroResponse.kt +3 -3
- package/nitrogen/generated/ios/NitroFetch-Swift-Cxx-Bridge.hpp +15 -0
- package/nitrogen/generated/ios/c++/HybridNitroFetchClientSpecSwift.hpp +4 -0
- package/nitrogen/generated/ios/swift/NitroRequest.swift +19 -1
- package/nitrogen/generated/ios/swift/NitroResponse.swift +8 -8
- package/nitrogen/generated/shared/c++/NitroRequest.hpp +5 -1
- package/nitrogen/generated/shared/c++/NitroResponse.hpp +6 -5
- package/package.json +18 -3
- package/src/CurlGenerator.js +31 -0
- package/src/Headers.js +127 -0
- package/src/HermesProfiler.js +22 -0
- package/src/NetworkInspector.js +183 -0
- package/src/NitroCronet.nitro.js +1 -0
- package/src/NitroFetch.nitro.js +1 -0
- package/src/NitroFetch.nitro.ts +4 -2
- package/src/NitroInstances.js +6 -0
- package/src/Request.js +173 -0
- package/src/Response.js +258 -0
- package/src/Response.ts +2 -1
- package/src/fetch.js +772 -0
- package/src/fetch.ts +46 -9
- package/src/index.js +10 -0
- package/src/index.tsx +1 -0
- package/src/index.web.js +104 -0
- package/src/tokenRefresh.js +104 -0
- package/src/utf8.js +40 -0
- package/src/utf8.ts +29 -14
package/src/fetch.ts
CHANGED
|
@@ -7,7 +7,6 @@ import type {
|
|
|
7
7
|
NitroResponse as NitroResponseNative,
|
|
8
8
|
} from './NitroFetch.nitro';
|
|
9
9
|
import {
|
|
10
|
-
boxedNitroFetch,
|
|
11
10
|
NitroFetch as NitroFetchSingleton,
|
|
12
11
|
NitroCronetSingleton,
|
|
13
12
|
} from './NitroInstances';
|
|
@@ -168,7 +167,11 @@ function ensureClient() {
|
|
|
168
167
|
|
|
169
168
|
function buildNitroRequest(
|
|
170
169
|
input: RequestInfo | URL,
|
|
171
|
-
init?: RequestInit & {
|
|
170
|
+
init?: RequestInit & {
|
|
171
|
+
redirect?: RequestRedirect;
|
|
172
|
+
cache?: RequestCache;
|
|
173
|
+
prefetchCacheTtlMs?: number;
|
|
174
|
+
}
|
|
172
175
|
): NitroRequestNative {
|
|
173
176
|
'worklet';
|
|
174
177
|
let url: string;
|
|
@@ -217,6 +220,11 @@ function buildNitroRequest(
|
|
|
217
220
|
// Determine followRedirects based on redirect option
|
|
218
221
|
const followRedirects = redirectOption === 'follow';
|
|
219
222
|
|
|
223
|
+
const prefetchCacheTtlMs =
|
|
224
|
+
typeof init?.prefetchCacheTtlMs === 'number'
|
|
225
|
+
? init.prefetchCacheTtlMs
|
|
226
|
+
: undefined;
|
|
227
|
+
|
|
220
228
|
return {
|
|
221
229
|
url,
|
|
222
230
|
method: (method?.toUpperCase() as any) ?? 'GET',
|
|
@@ -225,6 +233,7 @@ function buildNitroRequest(
|
|
|
225
233
|
bodyBytes: undefined as any,
|
|
226
234
|
bodyFormData: normalized?.bodyFormData,
|
|
227
235
|
followRedirects,
|
|
236
|
+
prefetchCacheTtlMs,
|
|
228
237
|
};
|
|
229
238
|
}
|
|
230
239
|
|
|
@@ -325,7 +334,7 @@ function normalizeBodyPure(
|
|
|
325
334
|
// Pure JS version of buildNitroRequest that doesnt use anything that breaks worklets
|
|
326
335
|
export function buildNitroRequestPure(
|
|
327
336
|
input: RequestInfo | URL,
|
|
328
|
-
init?: RequestInit
|
|
337
|
+
init?: RequestInit & { prefetchCacheTtlMs?: number }
|
|
329
338
|
): NitroRequestNative {
|
|
330
339
|
'worklet';
|
|
331
340
|
let url: string;
|
|
@@ -357,6 +366,11 @@ export function buildNitroRequestPure(
|
|
|
357
366
|
const headers = headersToPairsPure(headersInit);
|
|
358
367
|
const normalized = normalizeBodyPure(body);
|
|
359
368
|
|
|
369
|
+
const prefetchCacheTtlMs =
|
|
370
|
+
typeof init?.prefetchCacheTtlMs === 'number'
|
|
371
|
+
? init.prefetchCacheTtlMs
|
|
372
|
+
: undefined;
|
|
373
|
+
|
|
360
374
|
return {
|
|
361
375
|
url,
|
|
362
376
|
method: (method?.toUpperCase() as any) ?? 'GET',
|
|
@@ -365,6 +379,7 @@ export function buildNitroRequestPure(
|
|
|
365
379
|
// Only include bodyBytes when provided to avoid signaling upload data unintentionally
|
|
366
380
|
bodyBytes: undefined as any,
|
|
367
381
|
followRedirects: true,
|
|
382
|
+
prefetchCacheTtlMs,
|
|
368
383
|
};
|
|
369
384
|
}
|
|
370
385
|
|
|
@@ -703,7 +718,7 @@ export async function nitroFetch(
|
|
|
703
718
|
ok: res.ok,
|
|
704
719
|
redirected: res.redirected,
|
|
705
720
|
headers: res.headers,
|
|
706
|
-
bodyBytes: res.bodyBytes
|
|
721
|
+
bodyBytes: res.bodyBytes,
|
|
707
722
|
bodyString: res.bodyString,
|
|
708
723
|
});
|
|
709
724
|
return response as unknown as Response;
|
|
@@ -770,11 +785,21 @@ export async function prefetchOnAppStart(
|
|
|
770
785
|
{} as Record<string, string>
|
|
771
786
|
);
|
|
772
787
|
|
|
773
|
-
const entry = {
|
|
788
|
+
const entry: Record<string, any> = {
|
|
774
789
|
url: req.url,
|
|
775
790
|
prefetchKey,
|
|
776
791
|
headers: headersObj,
|
|
777
|
-
}
|
|
792
|
+
};
|
|
793
|
+
if (req.method && req.method !== 'GET') entry.method = req.method;
|
|
794
|
+
if (req.bodyString !== undefined) entry.bodyString = req.bodyString;
|
|
795
|
+
if (typeof req.bodyBytes === 'string' && req.bodyBytes.length > 0)
|
|
796
|
+
entry.bodyBytes = req.bodyBytes;
|
|
797
|
+
if (req.bodyFormData && req.bodyFormData.length > 0)
|
|
798
|
+
entry.bodyFormData = req.bodyFormData;
|
|
799
|
+
if (typeof req.timeoutMs === 'number') entry.timeoutMs = req.timeoutMs;
|
|
800
|
+
if (req.followRedirects === false) entry.followRedirects = false;
|
|
801
|
+
if (typeof req.prefetchCacheTtlMs === 'number')
|
|
802
|
+
entry.prefetchCacheTtlMs = req.prefetchCacheTtlMs;
|
|
778
803
|
|
|
779
804
|
// Write or append to storage queue
|
|
780
805
|
try {
|
|
@@ -832,6 +857,19 @@ export async function removeAllFromAutoprefetch(): Promise<void> {
|
|
|
832
857
|
NativeStorageSingleton.setString(KEY, JSON.stringify([]));
|
|
833
858
|
}
|
|
834
859
|
|
|
860
|
+
export function __readAutoPrefetchQueue(): Array<Record<string, any>> {
|
|
861
|
+
try {
|
|
862
|
+
const raw = NativeStorageSingleton.getString(
|
|
863
|
+
'nitrofetch_autoprefetch_queue'
|
|
864
|
+
);
|
|
865
|
+
if (!raw) return [];
|
|
866
|
+
const parsed = JSON.parse(raw);
|
|
867
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
868
|
+
} catch {
|
|
869
|
+
return [];
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
835
873
|
// Optional off-thread processing using react-native-worklets
|
|
836
874
|
|
|
837
875
|
export type NitroWorkletMapper<T> = (payload: {
|
|
@@ -891,10 +929,9 @@ export async function nitroFetchOnWorklet<T>(
|
|
|
891
929
|
}
|
|
892
930
|
return await runOnRuntimeAsync(rt, () => {
|
|
893
931
|
'worklet';
|
|
894
|
-
const
|
|
895
|
-
const unboxedClient = unboxedNitroFetch.createClient();
|
|
932
|
+
const nitroFetchClient = NitroFetchHybrid.createClient();
|
|
896
933
|
const request = buildNitroRequestPure(input, init);
|
|
897
|
-
const res =
|
|
934
|
+
const res = nitroFetchClient.requestSync(request);
|
|
898
935
|
const payload = {
|
|
899
936
|
url: res.url,
|
|
900
937
|
status: res.status,
|
package/src/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { nitroFetch as fetch, nitroFetchOnWorklet, prefetch, prefetchOnAppStart, removeFromAutoPrefetch, removeAllFromAutoprefetch, __readAutoPrefetchQueue, } from './fetch';
|
|
2
|
+
export { NitroHeaders as Headers } from './Headers';
|
|
3
|
+
export { NitroResponse as Response } from './Response';
|
|
4
|
+
export { NitroRequest as Request } from './Request';
|
|
5
|
+
export { NitroFetch } from './NitroInstances';
|
|
6
|
+
export { registerTokenRefresh, clearTokenRefresh, callRefreshEndpoint, getStoredTokenRefreshConfig, getNestedField, applyTemplate, } from './tokenRefresh';
|
|
7
|
+
export { NetworkInspector } from './NetworkInspector';
|
|
8
|
+
export { generateCurl } from './CurlGenerator';
|
|
9
|
+
export { profileFetch } from './HermesProfiler';
|
|
10
|
+
import './fetch';
|
package/src/index.tsx
CHANGED
package/src/index.web.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Web entry for react-native-nitro-fetch.
|
|
2
|
+
// Native (Cronet/URLSession) is unavailable on the web, so we delegate to the
|
|
3
|
+
// browser's built-in fetch and stub native-only APIs with console.warn.
|
|
4
|
+
import { NitroRequest as NitroRequestClass } from './Request';
|
|
5
|
+
export { NitroHeaders as Headers } from './Headers';
|
|
6
|
+
export { NitroResponse as Response } from './Response';
|
|
7
|
+
export { NitroRequest as Request } from './Request';
|
|
8
|
+
export { NetworkInspector } from './NetworkInspector';
|
|
9
|
+
export { generateCurl } from './CurlGenerator';
|
|
10
|
+
export { profileFetch } from './HermesProfiler';
|
|
11
|
+
export async function fetch(input, init) {
|
|
12
|
+
let resolvedInput = input;
|
|
13
|
+
let resolvedInit = init;
|
|
14
|
+
if (input instanceof NitroRequestClass) {
|
|
15
|
+
const method = (init?.method ?? input.method).toUpperCase();
|
|
16
|
+
const hasBodyMethod = method !== 'GET' && method !== 'HEAD';
|
|
17
|
+
let body = init?.body;
|
|
18
|
+
if (body === undefined && hasBodyMethod) {
|
|
19
|
+
const bytes = await input.arrayBuffer().catch(() => undefined);
|
|
20
|
+
body = bytes && bytes.byteLength > 0 ? bytes : null;
|
|
21
|
+
}
|
|
22
|
+
resolvedInput = input.url;
|
|
23
|
+
resolvedInit = {
|
|
24
|
+
...init,
|
|
25
|
+
method,
|
|
26
|
+
headers: (init?.headers ??
|
|
27
|
+
input.headers),
|
|
28
|
+
body,
|
|
29
|
+
redirect: init?.redirect ?? input.redirect,
|
|
30
|
+
cache: init?.cache ?? input.cache,
|
|
31
|
+
signal: init?.signal ?? input.signal,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return globalThis.fetch(resolvedInput, resolvedInit);
|
|
35
|
+
}
|
|
36
|
+
export async function nitroFetchOnWorklet(input, init, mapWorklet, _options) {
|
|
37
|
+
console.warn('nitroFetchOnWorklet: worklets are not available on web; running on the JS thread');
|
|
38
|
+
const res = await globalThis.fetch(input, init);
|
|
39
|
+
const bodyBytes = await res.clone().arrayBuffer();
|
|
40
|
+
let bodyString;
|
|
41
|
+
try {
|
|
42
|
+
bodyString = await res.clone().text();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
bodyString = undefined;
|
|
46
|
+
}
|
|
47
|
+
const headers = [];
|
|
48
|
+
res.headers.forEach((v, k) => headers.push({ key: k, value: v }));
|
|
49
|
+
return mapWorklet({
|
|
50
|
+
url: res.url,
|
|
51
|
+
status: res.status,
|
|
52
|
+
statusText: res.statusText,
|
|
53
|
+
ok: res.ok,
|
|
54
|
+
redirected: res.redirected ?? false,
|
|
55
|
+
headers,
|
|
56
|
+
bodyBytes,
|
|
57
|
+
bodyString,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
export async function prefetch(_input, _init) {
|
|
61
|
+
console.warn('prefetch is not available on web');
|
|
62
|
+
}
|
|
63
|
+
export async function prefetchOnAppStart(_input, _init) {
|
|
64
|
+
console.warn('prefetchOnAppStart is not available on web');
|
|
65
|
+
}
|
|
66
|
+
export async function removeFromAutoPrefetch(_prefetchKey) {
|
|
67
|
+
console.warn('removeFromAutoPrefetch is not available on web');
|
|
68
|
+
}
|
|
69
|
+
export async function removeAllFromAutoprefetch() {
|
|
70
|
+
console.warn('removeAllFromAutoprefetch is not available on web');
|
|
71
|
+
}
|
|
72
|
+
export const NitroFetch = new Proxy({}, {
|
|
73
|
+
get(_target, prop) {
|
|
74
|
+
console.warn(`NitroFetch.${String(prop)} is not available on web`);
|
|
75
|
+
return undefined;
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
export function registerTokenRefresh(_options) {
|
|
79
|
+
console.warn('registerTokenRefresh is not available on web');
|
|
80
|
+
}
|
|
81
|
+
export function clearTokenRefresh(_target) {
|
|
82
|
+
console.warn('clearTokenRefresh is not available on web');
|
|
83
|
+
}
|
|
84
|
+
export async function callRefreshEndpoint(_config) {
|
|
85
|
+
console.warn('callRefreshEndpoint is not available on web');
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
88
|
+
export function getStoredTokenRefreshConfig(_target) {
|
|
89
|
+
console.warn('getStoredTokenRefreshConfig is not available on web');
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
export function getNestedField(obj, dotPath) {
|
|
93
|
+
const parts = dotPath.split('.');
|
|
94
|
+
let current = obj;
|
|
95
|
+
for (const part of parts) {
|
|
96
|
+
if (current == null || typeof current !== 'object')
|
|
97
|
+
return undefined;
|
|
98
|
+
current = current[part];
|
|
99
|
+
}
|
|
100
|
+
return current != null ? String(current) : undefined;
|
|
101
|
+
}
|
|
102
|
+
export function applyTemplate(template, value) {
|
|
103
|
+
return template.replace(/\{\{value\}\}/g, value);
|
|
104
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { NativeStorage as NativeStorageSingleton } from './NitroInstances';
|
|
2
|
+
// Storage keys
|
|
3
|
+
const KEY_WS = 'nitro_token_refresh_websocket';
|
|
4
|
+
const KEY_FETCH = 'nitro_token_refresh_fetch';
|
|
5
|
+
const KEY_WS_CACHE = 'nitro_token_refresh_ws_cache';
|
|
6
|
+
const KEY_FETCH_CACHE = 'nitro_token_refresh_fetch_cache';
|
|
7
|
+
// — Helpers —
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a dot-notation path inside a parsed JSON object.
|
|
10
|
+
*/
|
|
11
|
+
export function getNestedField(obj, dotPath) {
|
|
12
|
+
const parts = dotPath.split('.');
|
|
13
|
+
let current = obj;
|
|
14
|
+
for (const part of parts) {
|
|
15
|
+
if (current == null || typeof current !== 'object')
|
|
16
|
+
return undefined;
|
|
17
|
+
current = current[part];
|
|
18
|
+
}
|
|
19
|
+
return current != null ? String(current) : undefined;
|
|
20
|
+
}
|
|
21
|
+
export function applyTemplate(template, value) {
|
|
22
|
+
return template.replace(/\{\{value\}\}/g, value);
|
|
23
|
+
}
|
|
24
|
+
function applyCompositeTemplate(template, values) {
|
|
25
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => values[key] ?? '');
|
|
26
|
+
}
|
|
27
|
+
export async function callRefreshEndpoint(config) {
|
|
28
|
+
const method = config.method ?? 'POST';
|
|
29
|
+
const response = await fetch(config.url, {
|
|
30
|
+
method,
|
|
31
|
+
headers: config.headers,
|
|
32
|
+
body: config.body,
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
throw new Error(`Token refresh failed: ${response.status} ${response.statusText}`);
|
|
36
|
+
}
|
|
37
|
+
const headers = {};
|
|
38
|
+
if (config.responseType === 'text') {
|
|
39
|
+
const text = await response.text();
|
|
40
|
+
if (config.textHeader) {
|
|
41
|
+
headers[config.textHeader] = config.textTemplate
|
|
42
|
+
? applyTemplate(config.textTemplate, text)
|
|
43
|
+
: text;
|
|
44
|
+
}
|
|
45
|
+
return headers;
|
|
46
|
+
}
|
|
47
|
+
// Default: json
|
|
48
|
+
const json = await response.json();
|
|
49
|
+
if (config.mappings) {
|
|
50
|
+
for (const mapping of config.mappings) {
|
|
51
|
+
const value = getNestedField(json, mapping.jsonPath);
|
|
52
|
+
if (value != null) {
|
|
53
|
+
headers[mapping.header] = mapping.valueTemplate
|
|
54
|
+
? applyTemplate(mapping.valueTemplate, value)
|
|
55
|
+
: value;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (config.compositeHeaders) {
|
|
60
|
+
for (const comp of config.compositeHeaders) {
|
|
61
|
+
const values = {};
|
|
62
|
+
for (const [placeholder, jsonPath] of Object.entries(comp.paths)) {
|
|
63
|
+
const val = getNestedField(json, jsonPath);
|
|
64
|
+
if (val != null)
|
|
65
|
+
values[placeholder] = val;
|
|
66
|
+
}
|
|
67
|
+
headers[comp.header] = applyCompositeTemplate(comp.template, values);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return headers;
|
|
71
|
+
}
|
|
72
|
+
export function registerTokenRefresh(options) {
|
|
73
|
+
const { target, ...config } = options;
|
|
74
|
+
const raw = JSON.stringify(config);
|
|
75
|
+
if (target === 'websocket' || target === 'all') {
|
|
76
|
+
NativeStorageSingleton.setSecureString(KEY_WS, raw);
|
|
77
|
+
}
|
|
78
|
+
if (target === 'fetch' || target === 'all') {
|
|
79
|
+
NativeStorageSingleton.setSecureString(KEY_FETCH, raw);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export function clearTokenRefresh(target) {
|
|
83
|
+
const t = target ?? 'all';
|
|
84
|
+
if (t === 'websocket' || t === 'all') {
|
|
85
|
+
NativeStorageSingleton.removeSecureString(KEY_WS);
|
|
86
|
+
NativeStorageSingleton.removeSecureString(KEY_WS_CACHE);
|
|
87
|
+
}
|
|
88
|
+
if (t === 'fetch' || t === 'all') {
|
|
89
|
+
NativeStorageSingleton.removeSecureString(KEY_FETCH);
|
|
90
|
+
NativeStorageSingleton.removeSecureString(KEY_FETCH_CACHE);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export function getStoredTokenRefreshConfig(target) {
|
|
94
|
+
const key = target === 'websocket' ? KEY_WS : KEY_FETCH;
|
|
95
|
+
try {
|
|
96
|
+
const raw = NativeStorageSingleton.getSecureString(key);
|
|
97
|
+
if (!raw)
|
|
98
|
+
return null;
|
|
99
|
+
return JSON.parse(raw);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
package/src/utf8.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
let _TextEncoder;
|
|
2
|
+
let _TextDecoder;
|
|
3
|
+
const NITRO_TEXT_DECODER_PKG = 'react-native-nitro-text-decoder';
|
|
4
|
+
function loadOptionalTextCodec() {
|
|
5
|
+
try {
|
|
6
|
+
// Hide require from the bundler so the package stays truly optional.
|
|
7
|
+
// eslint-disable-next-line no-new-func
|
|
8
|
+
const dynamicRequire = new Function('mod', 'return require(mod);');
|
|
9
|
+
return dynamicRequire(NITRO_TEXT_DECODER_PKG);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
if (typeof TextEncoder !== 'undefined') {
|
|
16
|
+
_TextEncoder = TextEncoder;
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
_TextEncoder = loadOptionalTextCodec().TextEncoder;
|
|
20
|
+
}
|
|
21
|
+
if (typeof TextDecoder !== 'undefined') {
|
|
22
|
+
_TextDecoder = TextDecoder;
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
_TextDecoder = loadOptionalTextCodec().TextDecoder;
|
|
26
|
+
}
|
|
27
|
+
export function stringToUTF8(str) {
|
|
28
|
+
if (!_TextEncoder) {
|
|
29
|
+
console.warn('stringToUTF8: TextEncoder not available. Install react-native-nitro-text-decoder.');
|
|
30
|
+
return new Uint8Array(0);
|
|
31
|
+
}
|
|
32
|
+
return new _TextEncoder().encode(str);
|
|
33
|
+
}
|
|
34
|
+
export function utf8ToString(bytes) {
|
|
35
|
+
if (!_TextDecoder) {
|
|
36
|
+
console.warn('utf8ToString: TextDecoder not available. Install react-native-nitro-text-decoder.');
|
|
37
|
+
return '';
|
|
38
|
+
}
|
|
39
|
+
return new _TextDecoder().decode(bytes);
|
|
40
|
+
}
|
package/src/utf8.ts
CHANGED
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
let _TextEncoder: typeof TextEncoder | undefined;
|
|
2
2
|
let _TextDecoder: typeof TextDecoder | undefined;
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
4
|
+
const NITRO_TEXT_DECODER_PKG = 'react-native-nitro-text-decoder';
|
|
5
|
+
|
|
6
|
+
function loadOptionalTextCodec(): {
|
|
7
|
+
TextEncoder?: typeof TextEncoder;
|
|
8
|
+
TextDecoder?: typeof TextDecoder;
|
|
9
|
+
} {
|
|
10
|
+
try {
|
|
11
|
+
// Hide require from the bundler so the package stays truly optional.
|
|
12
|
+
// eslint-disable-next-line no-new-func
|
|
13
|
+
const dynamicRequire = new Function('mod', 'return require(mod);') as (
|
|
14
|
+
m: string
|
|
15
|
+
) => unknown;
|
|
16
|
+
return dynamicRequire(NITRO_TEXT_DECODER_PKG) as {
|
|
17
|
+
TextEncoder?: typeof TextEncoder;
|
|
18
|
+
TextDecoder?: typeof TextDecoder;
|
|
19
|
+
};
|
|
20
|
+
} catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (typeof TextEncoder !== 'undefined') {
|
|
26
|
+
_TextEncoder = TextEncoder;
|
|
27
|
+
} else {
|
|
28
|
+
_TextEncoder = loadOptionalTextCodec().TextEncoder;
|
|
11
29
|
}
|
|
12
30
|
|
|
13
|
-
|
|
14
|
-
_TextDecoder =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
: require('react-native-nitro-text-decoder').TextDecoder;
|
|
18
|
-
} catch {
|
|
19
|
-
/* resolved at first use */
|
|
31
|
+
if (typeof TextDecoder !== 'undefined') {
|
|
32
|
+
_TextDecoder = TextDecoder;
|
|
33
|
+
} else {
|
|
34
|
+
_TextDecoder = loadOptionalTextCodec().TextDecoder;
|
|
20
35
|
}
|
|
21
36
|
|
|
22
37
|
export function stringToUTF8(str: string): Uint8Array {
|