react-native-nitro-fetch 1.3.0 → 1.3.2
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/build.gradle +12 -0
- package/android/src/main/java/com/margelo/nitro/nitrofetch/AutoPrefetcher.kt +258 -82
- package/android/src/main/java/com/margelo/nitro/nitrofetch/NitroFetchClient.kt +4 -4
- package/ios/NitroAutoPrefetcher.h +21 -0
- package/ios/NitroAutoPrefetcher.swift +292 -74
- package/ios/NitroFetchClient.swift +4 -3
- package/lib/module/CurlGenerator.js.map +2 -1
- package/lib/module/NitroCronet.nitro.js.map +1 -1
- package/lib/module/NitroFetch.nitro.js.map +1 -1
- package/lib/module/Request.js.map +2 -1
- package/lib/module/Response.js.map +2 -1
- package/lib/module/fetch.js +30 -11
- package/lib/module/fetch.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +0 -1
- package/lib/module/index.web.js.map +2 -1
- package/lib/module/tokenRefresh.js +1 -4
- package/lib/module/tokenRefresh.js.map +2 -1
- package/lib/module/utf8.js.map +2 -1
- package/lib/typescript/src/NitroFetch.nitro.d.ts +1 -0
- package/lib/typescript/src/NitroFetch.nitro.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/tokenRefresh.d.ts +14 -0
- package/lib/typescript/src/tokenRefresh.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/JNitroRequest.hpp +5 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrofetch/NitroRequest.kt +5 -2
- package/nitrogen/generated/ios/swift/NitroRequest.swift +19 -1
- package/nitrogen/generated/shared/c++/NitroRequest.hpp +5 -1
- package/package.json +1 -1
- package/src/CurlGenerator.js +28 -0
- package/src/Headers.js +119 -0
- package/src/HermesProfiler.js +20 -0
- package/src/NetworkInspector.js +175 -0
- package/src/NitroCronet.nitro.js +1 -0
- package/src/NitroFetch.nitro.js +1 -0
- package/src/NitroFetch.nitro.ts +3 -0
- package/src/NitroInstances.js +7 -0
- package/src/Request.js +176 -0
- package/src/Response.js +260 -0
- package/src/fetch.js +787 -0
- package/src/fetch.ts +55 -17
- package/src/index.js +25 -0
- package/src/index.tsx +1 -0
- package/src/index.web.js +106 -0
- package/src/tokenRefresh.js +102 -0
- package/src/tokenRefresh.ts +16 -0
- package/src/utf8.js +41 -0
package/src/fetch.ts
CHANGED
|
@@ -167,7 +167,11 @@ function ensureClient() {
|
|
|
167
167
|
|
|
168
168
|
function buildNitroRequest(
|
|
169
169
|
input: RequestInfo | URL,
|
|
170
|
-
init?: RequestInit & {
|
|
170
|
+
init?: RequestInit & {
|
|
171
|
+
redirect?: RequestRedirect;
|
|
172
|
+
cache?: RequestCache;
|
|
173
|
+
prefetchCacheTtlMs?: number;
|
|
174
|
+
}
|
|
171
175
|
): NitroRequestNative {
|
|
172
176
|
'worklet';
|
|
173
177
|
let url: string;
|
|
@@ -216,6 +220,11 @@ function buildNitroRequest(
|
|
|
216
220
|
// Determine followRedirects based on redirect option
|
|
217
221
|
const followRedirects = redirectOption === 'follow';
|
|
218
222
|
|
|
223
|
+
const prefetchCacheTtlMs =
|
|
224
|
+
typeof init?.prefetchCacheTtlMs === 'number'
|
|
225
|
+
? init.prefetchCacheTtlMs
|
|
226
|
+
: undefined;
|
|
227
|
+
|
|
219
228
|
return {
|
|
220
229
|
url,
|
|
221
230
|
method: (method?.toUpperCase() as any) ?? 'GET',
|
|
@@ -224,6 +233,7 @@ function buildNitroRequest(
|
|
|
224
233
|
bodyBytes: undefined as any,
|
|
225
234
|
bodyFormData: normalized?.bodyFormData,
|
|
226
235
|
followRedirects,
|
|
236
|
+
prefetchCacheTtlMs,
|
|
227
237
|
};
|
|
228
238
|
}
|
|
229
239
|
|
|
@@ -324,7 +334,7 @@ function normalizeBodyPure(
|
|
|
324
334
|
// Pure JS version of buildNitroRequest that doesnt use anything that breaks worklets
|
|
325
335
|
export function buildNitroRequestPure(
|
|
326
336
|
input: RequestInfo | URL,
|
|
327
|
-
init?: RequestInit
|
|
337
|
+
init?: RequestInit & { prefetchCacheTtlMs?: number }
|
|
328
338
|
): NitroRequestNative {
|
|
329
339
|
'worklet';
|
|
330
340
|
let url: string;
|
|
@@ -356,6 +366,11 @@ export function buildNitroRequestPure(
|
|
|
356
366
|
const headers = headersToPairsPure(headersInit);
|
|
357
367
|
const normalized = normalizeBodyPure(body);
|
|
358
368
|
|
|
369
|
+
const prefetchCacheTtlMs =
|
|
370
|
+
typeof init?.prefetchCacheTtlMs === 'number'
|
|
371
|
+
? init.prefetchCacheTtlMs
|
|
372
|
+
: undefined;
|
|
373
|
+
|
|
359
374
|
return {
|
|
360
375
|
url,
|
|
361
376
|
method: (method?.toUpperCase() as any) ?? 'GET',
|
|
@@ -364,6 +379,7 @@ export function buildNitroRequestPure(
|
|
|
364
379
|
// Only include bodyBytes when provided to avoid signaling upload data unintentionally
|
|
365
380
|
bodyBytes: undefined as any,
|
|
366
381
|
followRedirects: true,
|
|
382
|
+
prefetchCacheTtlMs,
|
|
367
383
|
};
|
|
368
384
|
}
|
|
369
385
|
|
|
@@ -742,6 +758,8 @@ export async function prefetch(
|
|
|
742
758
|
await client.prefetch(req);
|
|
743
759
|
}
|
|
744
760
|
|
|
761
|
+
const AUTOPREFETCH_QUEUE_KEY = 'nitrofetch_autoprefetch_queue';
|
|
762
|
+
|
|
745
763
|
// Persist a request to storage so native can prefetch it on app start.
|
|
746
764
|
export async function prefetchOnAppStart(
|
|
747
765
|
input: RequestInfo | URL,
|
|
@@ -769,20 +787,27 @@ export async function prefetchOnAppStart(
|
|
|
769
787
|
{} as Record<string, string>
|
|
770
788
|
);
|
|
771
789
|
|
|
772
|
-
const entry = {
|
|
790
|
+
const entry: Record<string, any> = {
|
|
773
791
|
url: req.url,
|
|
774
792
|
prefetchKey,
|
|
775
793
|
headers: headersObj,
|
|
776
|
-
}
|
|
794
|
+
};
|
|
795
|
+
if (req.method && req.method !== 'GET') entry.method = req.method;
|
|
796
|
+
if (req.bodyString !== undefined) entry.bodyString = req.bodyString;
|
|
797
|
+
if (typeof req.bodyBytes === 'string' && req.bodyBytes.length > 0)
|
|
798
|
+
entry.bodyBytes = req.bodyBytes;
|
|
799
|
+
if (req.bodyFormData && req.bodyFormData.length > 0)
|
|
800
|
+
entry.bodyFormData = req.bodyFormData;
|
|
801
|
+
if (typeof req.timeoutMs === 'number') entry.timeoutMs = req.timeoutMs;
|
|
802
|
+
if (req.followRedirects === false) entry.followRedirects = false;
|
|
803
|
+
if (typeof req.prefetchCacheTtlMs === 'number')
|
|
804
|
+
entry.prefetchCacheTtlMs = req.prefetchCacheTtlMs;
|
|
777
805
|
|
|
778
806
|
// Write or append to storage queue
|
|
779
807
|
try {
|
|
780
|
-
const KEY = 'nitrofetch_autoprefetch_queue';
|
|
781
808
|
let arr: any[] = [];
|
|
782
809
|
try {
|
|
783
|
-
const raw = NativeStorageSingleton.getString(
|
|
784
|
-
'nitrofetch_autoprefetch_queue'
|
|
785
|
-
);
|
|
810
|
+
const raw = NativeStorageSingleton.getString(AUTOPREFETCH_QUEUE_KEY);
|
|
786
811
|
if (raw) arr = JSON.parse(raw);
|
|
787
812
|
if (!Array.isArray(arr)) arr = [];
|
|
788
813
|
} catch {
|
|
@@ -792,7 +817,10 @@ export async function prefetchOnAppStart(
|
|
|
792
817
|
arr = arr.filter((e) => e && e.prefetchKey !== prefetchKey);
|
|
793
818
|
}
|
|
794
819
|
arr.push(entry);
|
|
795
|
-
NativeStorageSingleton.setString(
|
|
820
|
+
NativeStorageSingleton.setString(
|
|
821
|
+
AUTOPREFETCH_QUEUE_KEY,
|
|
822
|
+
JSON.stringify(arr)
|
|
823
|
+
);
|
|
796
824
|
} catch (e) {
|
|
797
825
|
console.warn('Failed to persist prefetch queue', e);
|
|
798
826
|
}
|
|
@@ -803,12 +831,9 @@ export async function removeFromAutoPrefetch(
|
|
|
803
831
|
prefetchKey: string
|
|
804
832
|
): Promise<void> {
|
|
805
833
|
try {
|
|
806
|
-
const KEY = 'nitrofetch_autoprefetch_queue';
|
|
807
834
|
let arr: any[] = [];
|
|
808
835
|
try {
|
|
809
|
-
const raw = NativeStorageSingleton.getString(
|
|
810
|
-
'nitrofetch_autoprefetch_queue'
|
|
811
|
-
);
|
|
836
|
+
const raw = NativeStorageSingleton.getString(AUTOPREFETCH_QUEUE_KEY);
|
|
812
837
|
if (raw) arr = JSON.parse(raw);
|
|
813
838
|
if (!Array.isArray(arr)) arr = [];
|
|
814
839
|
} catch {
|
|
@@ -816,9 +841,12 @@ export async function removeFromAutoPrefetch(
|
|
|
816
841
|
}
|
|
817
842
|
const next = arr.filter((e) => e && e.prefetchKey !== prefetchKey);
|
|
818
843
|
if (next.length === 0) {
|
|
819
|
-
NativeStorageSingleton.removeString(
|
|
844
|
+
NativeStorageSingleton.removeString(AUTOPREFETCH_QUEUE_KEY);
|
|
820
845
|
} else if (next.length !== arr.length) {
|
|
821
|
-
NativeStorageSingleton.setString(
|
|
846
|
+
NativeStorageSingleton.setString(
|
|
847
|
+
AUTOPREFETCH_QUEUE_KEY,
|
|
848
|
+
JSON.stringify(next)
|
|
849
|
+
);
|
|
822
850
|
}
|
|
823
851
|
} catch (e) {
|
|
824
852
|
console.warn('Failed to remove from prefetch queue', e);
|
|
@@ -827,8 +855,18 @@ export async function removeFromAutoPrefetch(
|
|
|
827
855
|
|
|
828
856
|
// Remove all entries from the auto-prefetch queue.
|
|
829
857
|
export async function removeAllFromAutoprefetch(): Promise<void> {
|
|
830
|
-
|
|
831
|
-
|
|
858
|
+
NativeStorageSingleton.setString(AUTOPREFETCH_QUEUE_KEY, JSON.stringify([]));
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
export function __readAutoPrefetchQueue(): Array<Record<string, any>> {
|
|
862
|
+
try {
|
|
863
|
+
const raw = NativeStorageSingleton.getString(AUTOPREFETCH_QUEUE_KEY);
|
|
864
|
+
if (!raw) return [];
|
|
865
|
+
const parsed = JSON.parse(raw);
|
|
866
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
867
|
+
} catch {
|
|
868
|
+
return [];
|
|
869
|
+
}
|
|
832
870
|
}
|
|
833
871
|
|
|
834
872
|
// Optional off-thread processing using react-native-worklets
|
package/src/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export {
|
|
2
|
+
nitroFetch as fetch,
|
|
3
|
+
nitroFetchOnWorklet,
|
|
4
|
+
prefetch,
|
|
5
|
+
prefetchOnAppStart,
|
|
6
|
+
removeFromAutoPrefetch,
|
|
7
|
+
removeAllFromAutoprefetch,
|
|
8
|
+
__readAutoPrefetchQueue,
|
|
9
|
+
} from './fetch';
|
|
10
|
+
export { NitroHeaders as Headers } from './Headers';
|
|
11
|
+
export { NitroResponse as Response } from './Response';
|
|
12
|
+
export { NitroRequest as Request } from './Request';
|
|
13
|
+
export { NitroFetch } from './NitroInstances';
|
|
14
|
+
export {
|
|
15
|
+
registerTokenRefresh,
|
|
16
|
+
clearTokenRefresh,
|
|
17
|
+
callRefreshEndpoint,
|
|
18
|
+
getStoredTokenRefreshConfig,
|
|
19
|
+
getNestedField,
|
|
20
|
+
applyTemplate,
|
|
21
|
+
} from './tokenRefresh';
|
|
22
|
+
export { NetworkInspector } from './NetworkInspector';
|
|
23
|
+
export { generateCurl } from './CurlGenerator';
|
|
24
|
+
export { profileFetch } from './HermesProfiler';
|
|
25
|
+
import './fetch';
|
package/src/index.tsx
CHANGED
package/src/index.web.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
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 ?? input.headers,
|
|
27
|
+
body,
|
|
28
|
+
redirect: init?.redirect ?? input.redirect,
|
|
29
|
+
cache: init?.cache ?? input.cache,
|
|
30
|
+
signal: init?.signal ?? input.signal,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return globalThis.fetch(resolvedInput, resolvedInit);
|
|
34
|
+
}
|
|
35
|
+
export async function nitroFetchOnWorklet(input, init, mapWorklet, _options) {
|
|
36
|
+
console.warn(
|
|
37
|
+
'nitroFetchOnWorklet: worklets are not available on web; running on the JS thread'
|
|
38
|
+
);
|
|
39
|
+
const res = await globalThis.fetch(input, init);
|
|
40
|
+
const bodyBytes = await res.clone().arrayBuffer();
|
|
41
|
+
let bodyString;
|
|
42
|
+
try {
|
|
43
|
+
bodyString = await res.clone().text();
|
|
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
|
+
{},
|
|
74
|
+
{
|
|
75
|
+
get(_target, prop) {
|
|
76
|
+
console.warn(`NitroFetch.${String(prop)} is not available on web`);
|
|
77
|
+
return undefined;
|
|
78
|
+
},
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
export function registerTokenRefresh(_options) {
|
|
82
|
+
console.warn('registerTokenRefresh is not available on web');
|
|
83
|
+
}
|
|
84
|
+
export function clearTokenRefresh(_target) {
|
|
85
|
+
console.warn('clearTokenRefresh is not available on web');
|
|
86
|
+
}
|
|
87
|
+
export async function callRefreshEndpoint(_config) {
|
|
88
|
+
console.warn('callRefreshEndpoint is not available on web');
|
|
89
|
+
return {};
|
|
90
|
+
}
|
|
91
|
+
export function getStoredTokenRefreshConfig(_target) {
|
|
92
|
+
console.warn('getStoredTokenRefreshConfig is not available on web');
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
export function getNestedField(obj, dotPath) {
|
|
96
|
+
const parts = dotPath.split('.');
|
|
97
|
+
let current = obj;
|
|
98
|
+
for (const part of parts) {
|
|
99
|
+
if (current == null || typeof current !== 'object') return undefined;
|
|
100
|
+
current = current[part];
|
|
101
|
+
}
|
|
102
|
+
return current != null ? String(current) : undefined;
|
|
103
|
+
}
|
|
104
|
+
export function applyTemplate(template, value) {
|
|
105
|
+
return template.replace(/\{\{value\}\}/g, value);
|
|
106
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
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') return undefined;
|
|
16
|
+
current = current[part];
|
|
17
|
+
}
|
|
18
|
+
return current != null ? String(current) : undefined;
|
|
19
|
+
}
|
|
20
|
+
export function applyTemplate(template, value) {
|
|
21
|
+
return template.replace(/\{\{value\}\}/g, value);
|
|
22
|
+
}
|
|
23
|
+
function applyCompositeTemplate(template, values) {
|
|
24
|
+
return template.replace(/\{\{(\w+)\}\}/g, (_, key) => values[key] ?? '');
|
|
25
|
+
}
|
|
26
|
+
export async function callRefreshEndpoint(config) {
|
|
27
|
+
const method = config.method ?? 'POST';
|
|
28
|
+
const response = await fetch(config.url, {
|
|
29
|
+
method,
|
|
30
|
+
headers: config.headers,
|
|
31
|
+
body: config.body,
|
|
32
|
+
});
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Token refresh failed: ${response.status} ${response.statusText}`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
const headers = {};
|
|
39
|
+
if (config.responseType === 'text') {
|
|
40
|
+
const text = await response.text();
|
|
41
|
+
if (config.textHeader) {
|
|
42
|
+
headers[config.textHeader] = config.textTemplate
|
|
43
|
+
? applyTemplate(config.textTemplate, text)
|
|
44
|
+
: text;
|
|
45
|
+
}
|
|
46
|
+
return headers;
|
|
47
|
+
}
|
|
48
|
+
// Default: json
|
|
49
|
+
const json = await response.json();
|
|
50
|
+
if (config.mappings) {
|
|
51
|
+
for (const mapping of config.mappings) {
|
|
52
|
+
const value = getNestedField(json, mapping.jsonPath);
|
|
53
|
+
if (value != null) {
|
|
54
|
+
headers[mapping.header] = mapping.valueTemplate
|
|
55
|
+
? applyTemplate(mapping.valueTemplate, value)
|
|
56
|
+
: value;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (config.compositeHeaders) {
|
|
61
|
+
for (const comp of config.compositeHeaders) {
|
|
62
|
+
const values = {};
|
|
63
|
+
for (const [placeholder, jsonPath] of Object.entries(comp.paths)) {
|
|
64
|
+
const val = getNestedField(json, jsonPath);
|
|
65
|
+
if (val != null) 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) return null;
|
|
98
|
+
return JSON.parse(raw);
|
|
99
|
+
} catch {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
}
|
package/src/tokenRefresh.ts
CHANGED
|
@@ -20,6 +20,18 @@ type TokenRefreshCompositeHeader = {
|
|
|
20
20
|
paths: Record<string, string>;
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
+
type TokenRefreshBodyMapping = {
|
|
24
|
+
jsonPath: string;
|
|
25
|
+
bodyPath: string;
|
|
26
|
+
valueTemplate?: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
type TokenRefreshFormDataMapping = {
|
|
30
|
+
jsonPath: string;
|
|
31
|
+
field: string;
|
|
32
|
+
valueTemplate?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
23
35
|
export type TokenRefreshConfig = {
|
|
24
36
|
url: string;
|
|
25
37
|
method?: 'GET' | 'POST' | 'PUT' | 'PATCH';
|
|
@@ -30,6 +42,10 @@ export type TokenRefreshConfig = {
|
|
|
30
42
|
compositeHeaders?: TokenRefreshCompositeHeader[];
|
|
31
43
|
textHeader?: string;
|
|
32
44
|
textTemplate?: string;
|
|
45
|
+
bodyMappings?: TokenRefreshBodyMapping[];
|
|
46
|
+
formDataMappings?: TokenRefreshFormDataMapping[];
|
|
47
|
+
bodyTextPath?: string;
|
|
48
|
+
formDataTextField?: string;
|
|
33
49
|
onFailure?: 'skip' | 'useStoredHeaders';
|
|
34
50
|
};
|
|
35
51
|
|
package/src/utf8.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
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
|
+
} catch {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
if (typeof TextEncoder !== 'undefined') {
|
|
15
|
+
_TextEncoder = TextEncoder;
|
|
16
|
+
} else {
|
|
17
|
+
_TextEncoder = loadOptionalTextCodec().TextEncoder;
|
|
18
|
+
}
|
|
19
|
+
if (typeof TextDecoder !== 'undefined') {
|
|
20
|
+
_TextDecoder = TextDecoder;
|
|
21
|
+
} else {
|
|
22
|
+
_TextDecoder = loadOptionalTextCodec().TextDecoder;
|
|
23
|
+
}
|
|
24
|
+
export function stringToUTF8(str) {
|
|
25
|
+
if (!_TextEncoder) {
|
|
26
|
+
console.warn(
|
|
27
|
+
'stringToUTF8: TextEncoder not available. Install react-native-nitro-text-decoder.'
|
|
28
|
+
);
|
|
29
|
+
return new Uint8Array(0);
|
|
30
|
+
}
|
|
31
|
+
return new _TextEncoder().encode(str);
|
|
32
|
+
}
|
|
33
|
+
export function utf8ToString(bytes) {
|
|
34
|
+
if (!_TextDecoder) {
|
|
35
|
+
console.warn(
|
|
36
|
+
'utf8ToString: TextDecoder not available. Install react-native-nitro-text-decoder.'
|
|
37
|
+
);
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
return new _TextDecoder().decode(bytes);
|
|
41
|
+
}
|