@better-auth/expo 1.7.2 → 1.7.4
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/dist/client.d.ts +30 -34
- package/dist/client.js +385 -110
- package/dist/index.js +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/{version-D-tGaVc4.js → version-CuQDLAg7.js} +1 -1
- package/package.json +6 -6
package/dist/client.d.ts
CHANGED
|
@@ -5,6 +5,35 @@ import * as _$_better_fetch_fetch0 from "@better-fetch/fetch";
|
|
|
5
5
|
import { ClientFetchOption, ClientStore } from "@better-auth/core";
|
|
6
6
|
import * as SecureStore from "expo-secure-store";
|
|
7
7
|
|
|
8
|
+
//#region src/client-storage.d.ts
|
|
9
|
+
/**
|
|
10
|
+
* Storage used by the Expo client for cookies and cached session data.
|
|
11
|
+
* Async access is coordinated through the provided object, so reuse it across
|
|
12
|
+
* clients that access the same stored data.
|
|
13
|
+
*/
|
|
14
|
+
type ExpoClientStorage = Pick<typeof SecureStore, "setItem" | "setItemAsync" | "getItem" | "getItemAsync">;
|
|
15
|
+
/**
|
|
16
|
+
* Expo secure store does not support colons in the keys.
|
|
17
|
+
* This function replaces colons with underscores.
|
|
18
|
+
*
|
|
19
|
+
* @see https://github.com/better-auth/better-auth/issues/5426
|
|
20
|
+
*
|
|
21
|
+
* @param name cookie name to be saved in the storage
|
|
22
|
+
* @returns normalized cookie name
|
|
23
|
+
*/
|
|
24
|
+
declare function normalizeCookieName(name: string): string;
|
|
25
|
+
interface ExpoStorageAdapter {
|
|
26
|
+
getItem(name: string): string | null;
|
|
27
|
+
getItemAsync(name: string): Promise<string | null>;
|
|
28
|
+
setItem(name: string, value: string): void;
|
|
29
|
+
setItemAsync(name: string, value: string): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Wraps Expo storage with chunking, recoverable writes, and serialized async
|
|
33
|
+
* access.
|
|
34
|
+
*/
|
|
35
|
+
declare function storageAdapter(storage: ExpoClientStorage): ExpoStorageAdapter;
|
|
36
|
+
//#endregion
|
|
8
37
|
//#region src/focus-manager.d.ts
|
|
9
38
|
declare function setupExpoFocusManager(): FocusManager;
|
|
10
39
|
//#endregion
|
|
@@ -12,10 +41,6 @@ declare function setupExpoFocusManager(): FocusManager;
|
|
|
12
41
|
declare function setupExpoOnlineManager(): OnlineManager;
|
|
13
42
|
//#endregion
|
|
14
43
|
//#region src/client.d.ts
|
|
15
|
-
/**
|
|
16
|
-
* Storage used by the Expo client for cookies and cached session data.
|
|
17
|
-
*/
|
|
18
|
-
type ExpoClientStorage = Pick<typeof SecureStore, "setItem" | "setItemAsync" | "getItem" | "getItemAsync">;
|
|
19
44
|
interface ExpoClientOptions {
|
|
20
45
|
scheme?: string | undefined;
|
|
21
46
|
storage: ExpoClientStorage;
|
|
@@ -74,35 +99,6 @@ declare function getCookie(cookie: string | null): string;
|
|
|
74
99
|
* @returns true if the header contains better-auth cookies, false otherwise
|
|
75
100
|
*/
|
|
76
101
|
declare function hasBetterAuthCookies(setCookieHeader: string, cookiePrefix: string | string[]): boolean;
|
|
77
|
-
/**
|
|
78
|
-
* Expo secure store does not support colons in the keys.
|
|
79
|
-
* This function replaces colons with underscores.
|
|
80
|
-
*
|
|
81
|
-
* @see https://github.com/better-auth/better-auth/issues/5426
|
|
82
|
-
*
|
|
83
|
-
* @param name cookie name to be saved in the storage
|
|
84
|
-
* @returns normalized cookie name
|
|
85
|
-
*/
|
|
86
|
-
declare function normalizeCookieName(name: string): string;
|
|
87
|
-
declare function storageAdapter(storage: ExpoClientStorage): {
|
|
88
|
-
/**
|
|
89
|
-
* Reads a value, reassembling it if it was split across chunk keys. A value
|
|
90
|
-
* that fit is returned as-is (values written before chunking still read
|
|
91
|
-
* back); a missing chunk returns `null` so a torn write fails closed.
|
|
92
|
-
*/
|
|
93
|
-
getItem: (name: string) => string | null;
|
|
94
|
-
getItemAsync: (name: string) => Promise<string | null>;
|
|
95
|
-
/**
|
|
96
|
-
* Stores `value`, splitting it across chunk keys when it exceeds the
|
|
97
|
-
* per-write limit. The base key is cleared before the chunks are rewritten
|
|
98
|
-
* and set to the marker last, as the commit point, so a write interrupted
|
|
99
|
-
* partway through reads as absent rather than a mix of old and new chunks.
|
|
100
|
-
* Failures are logged, not thrown: persistence is best-effort and must not
|
|
101
|
-
* break the request.
|
|
102
|
-
*/
|
|
103
|
-
setItem: (name: string, value: string) => void;
|
|
104
|
-
setItemAsync: (name: string, value: string) => Promise<void>;
|
|
105
|
-
};
|
|
106
102
|
declare const expoClient: (opts: ExpoClientOptions) => {
|
|
107
103
|
id: "expo";
|
|
108
104
|
version: string;
|
|
@@ -189,4 +185,4 @@ declare const expoClient: (opts: ExpoClientOptions) => {
|
|
|
189
185
|
}[];
|
|
190
186
|
};
|
|
191
187
|
//#endregion
|
|
192
|
-
export { ExpoClientStorage, expoClient, getCookie, getSetCookie, hasBetterAuthCookies, normalizeCookieName, parseSetCookieHeader, setupExpoFocusManager, setupExpoOnlineManager, storageAdapter };
|
|
188
|
+
export { type ExpoClientStorage, expoClient, getCookie, getSetCookie, hasBetterAuthCookies, normalizeCookieName, parseSetCookieHeader, setupExpoFocusManager, setupExpoOnlineManager, storageAdapter };
|
package/dist/client.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { t as PACKAGE_VERSION } from "./version-CuQDLAg7.js";
|
|
2
2
|
import { safeJSONParse } from "@better-auth/core/utils/json";
|
|
3
3
|
import { SECURE_COOKIE_PREFIX, parseSetCookieHeader, parseSetCookieHeader as parseSetCookieHeader$1, stripSecureCookiePrefix } from "better-auth/cookies/utils";
|
|
4
4
|
import Constants from "expo-constants";
|
|
5
5
|
import * as Linking from "expo-linking";
|
|
6
6
|
import { AppState, Platform } from "react-native";
|
|
7
|
+
import { logger } from "@better-auth/core/env";
|
|
7
8
|
import { kFocusManager, kOnlineManager } from "better-auth/client";
|
|
8
9
|
//#region \0rolldown/runtime.js
|
|
9
10
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) {
|
|
@@ -11,6 +12,385 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
11
12
|
throw Error("Calling `require` for \"" + x + "\" in an environment that doesn't expose the `require` function. See https://rolldown.rs/in-depth/bundling-cjs#require-external-modules for more details.");
|
|
12
13
|
});
|
|
13
14
|
//#endregion
|
|
15
|
+
//#region src/client-storage.ts
|
|
16
|
+
/**
|
|
17
|
+
* Expo secure store does not support colons in the keys.
|
|
18
|
+
* This function replaces colons with underscores.
|
|
19
|
+
*
|
|
20
|
+
* @see https://github.com/better-auth/better-auth/issues/5426
|
|
21
|
+
*
|
|
22
|
+
* @param name cookie name to be saved in the storage
|
|
23
|
+
* @returns normalized cookie name
|
|
24
|
+
*/
|
|
25
|
+
function normalizeCookieName(name) {
|
|
26
|
+
return name.replace(/:/g, "_");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* UTF-8 byte budget per stored chunk. Some native stores reject large values,
|
|
30
|
+
* so larger strings are split across keys.
|
|
31
|
+
*
|
|
32
|
+
* @see https://github.com/better-auth/better-auth/issues/9151
|
|
33
|
+
*/
|
|
34
|
+
const STORAGE_VALUE_LIMIT = 1800;
|
|
35
|
+
const MAX_STORAGE_CHUNKS = 100;
|
|
36
|
+
function getUtf8ByteLength(character) {
|
|
37
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
38
|
+
if (codePoint <= 127) return 1;
|
|
39
|
+
if (codePoint <= 2047) return 2;
|
|
40
|
+
if (codePoint <= 65535) return 3;
|
|
41
|
+
return 4;
|
|
42
|
+
}
|
|
43
|
+
function splitStorageValue(value) {
|
|
44
|
+
const chunks = [];
|
|
45
|
+
let start = 0;
|
|
46
|
+
let end = 0;
|
|
47
|
+
let bytes = 0;
|
|
48
|
+
for (const character of value) {
|
|
49
|
+
const characterBytes = getUtf8ByteLength(character);
|
|
50
|
+
if (bytes + characterBytes > STORAGE_VALUE_LIMIT) {
|
|
51
|
+
chunks.push(value.slice(start, end));
|
|
52
|
+
start = end;
|
|
53
|
+
bytes = 0;
|
|
54
|
+
}
|
|
55
|
+
bytes += characterBytes;
|
|
56
|
+
end += character.length;
|
|
57
|
+
}
|
|
58
|
+
chunks.push(value.slice(start));
|
|
59
|
+
return chunks;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Marks a base key whose value is split across multiple storage keys. Legacy
|
|
63
|
+
* markers contain only the chunk count. Current markers also identify the
|
|
64
|
+
* active slot and retain the previous slot's chunk count for recovery.
|
|
65
|
+
*
|
|
66
|
+
* @see https://github.com/better-auth/better-auth/issues/11082
|
|
67
|
+
*/
|
|
68
|
+
const CHUNK_MARKER = "ba-chunks:";
|
|
69
|
+
const CHUNK_SLOTS = [
|
|
70
|
+
null,
|
|
71
|
+
0,
|
|
72
|
+
1
|
|
73
|
+
];
|
|
74
|
+
function parseChunkCount(value) {
|
|
75
|
+
if (value === void 0) return null;
|
|
76
|
+
const count = Number(value);
|
|
77
|
+
if (!Number.isInteger(count) || count < 1 || count > MAX_STORAGE_CHUNKS) return null;
|
|
78
|
+
return count;
|
|
79
|
+
}
|
|
80
|
+
function parseChunkMarker(baseValue) {
|
|
81
|
+
const parts = baseValue.slice(11).split(":");
|
|
82
|
+
if (parts.length > 3) return null;
|
|
83
|
+
const [countValue, slotValue, fallbackCountValue] = parts;
|
|
84
|
+
const count = parseChunkCount(countValue);
|
|
85
|
+
if (count === null) return null;
|
|
86
|
+
if (slotValue === void 0) return fallbackCountValue === void 0 ? {
|
|
87
|
+
count,
|
|
88
|
+
slot: null,
|
|
89
|
+
fallbackCount: null
|
|
90
|
+
} : null;
|
|
91
|
+
if (slotValue !== "0" && slotValue !== "1") return null;
|
|
92
|
+
const fallbackCount = parseChunkCount(fallbackCountValue);
|
|
93
|
+
if (fallbackCountValue !== void 0 && fallbackCount === null) return null;
|
|
94
|
+
return {
|
|
95
|
+
count,
|
|
96
|
+
slot: slotValue === "0" ? 0 : 1,
|
|
97
|
+
fallbackCount
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function getChunkKey(key, marker, index) {
|
|
101
|
+
return marker.slot === null ? `${key}.${index}` : `${key}.${marker.slot}.${index}`;
|
|
102
|
+
}
|
|
103
|
+
function getOtherSlot(slot) {
|
|
104
|
+
return slot === 0 ? 1 : 0;
|
|
105
|
+
}
|
|
106
|
+
function serializeChunkMarker(marker) {
|
|
107
|
+
if (marker.slot === null) return `${CHUNK_MARKER}${marker.count}`;
|
|
108
|
+
const fallback = marker.fallbackCount === null ? "" : `:${marker.fallbackCount}`;
|
|
109
|
+
return `${CHUNK_MARKER}${marker.count}:${marker.slot}${fallback}`;
|
|
110
|
+
}
|
|
111
|
+
function readChunks(storage, key, marker) {
|
|
112
|
+
let value = "";
|
|
113
|
+
for (let i = 0; i < marker.count; i++) {
|
|
114
|
+
const chunk = storage.getItem(getChunkKey(key, marker, i));
|
|
115
|
+
if (!chunk) return null;
|
|
116
|
+
value += chunk;
|
|
117
|
+
}
|
|
118
|
+
return value;
|
|
119
|
+
}
|
|
120
|
+
async function readChunksAsync(storage, key, marker) {
|
|
121
|
+
let value = "";
|
|
122
|
+
for (let i = 0; i < marker.count; i++) {
|
|
123
|
+
const chunk = await storage.getItemAsync(getChunkKey(key, marker, i));
|
|
124
|
+
if (!chunk) return null;
|
|
125
|
+
value += chunk;
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
function readStoredValue(storage, key, baseValue) {
|
|
130
|
+
if (baseValue == null || !baseValue.startsWith(CHUNK_MARKER)) return baseValue;
|
|
131
|
+
const marker = parseChunkMarker(baseValue);
|
|
132
|
+
if (!marker) return null;
|
|
133
|
+
const value = readChunks(storage, key, marker);
|
|
134
|
+
if (value !== null || marker.slot === null || marker.fallbackCount === null) return value;
|
|
135
|
+
return readChunks(storage, key, {
|
|
136
|
+
count: marker.fallbackCount,
|
|
137
|
+
slot: getOtherSlot(marker.slot),
|
|
138
|
+
fallbackCount: null
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
async function readStoredValueAsync(storage, key, baseValue) {
|
|
142
|
+
if (baseValue == null || !baseValue.startsWith(CHUNK_MARKER)) return baseValue;
|
|
143
|
+
const marker = parseChunkMarker(baseValue);
|
|
144
|
+
if (!marker) return null;
|
|
145
|
+
const value = await readChunksAsync(storage, key, marker);
|
|
146
|
+
if (value !== null || marker.slot === null || marker.fallbackCount === null) return value;
|
|
147
|
+
return readChunksAsync(storage, key, {
|
|
148
|
+
count: marker.fallbackCount,
|
|
149
|
+
slot: getOtherSlot(marker.slot),
|
|
150
|
+
fallbackCount: null
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
function getStorageWritePlan(key, value, currentBaseValue) {
|
|
154
|
+
const currentMarker = currentBaseValue?.startsWith(CHUNK_MARKER) ? parseChunkMarker(currentBaseValue) : null;
|
|
155
|
+
const chunks = splitStorageValue(value);
|
|
156
|
+
if (chunks.length === 1) return {
|
|
157
|
+
writes: [[key, value]],
|
|
158
|
+
cleanup: getUnusedChunkRanges(key, currentMarker, null)
|
|
159
|
+
};
|
|
160
|
+
const count = chunks.length;
|
|
161
|
+
if (count > MAX_STORAGE_CHUNKS) throw new Error(`Storage value requires ${count} chunks, exceeding the limit of ${MAX_STORAGE_CHUNKS}`);
|
|
162
|
+
const marker = {
|
|
163
|
+
count,
|
|
164
|
+
slot: currentMarker?.slot === 0 ? 1 : 0,
|
|
165
|
+
fallbackCount: currentMarker?.slot == null ? null : currentMarker.count
|
|
166
|
+
};
|
|
167
|
+
const writes = [];
|
|
168
|
+
if (currentMarker?.slot != null && currentMarker.fallbackCount !== null) writes.push([key, serializeChunkMarker({
|
|
169
|
+
...currentMarker,
|
|
170
|
+
fallbackCount: null
|
|
171
|
+
})]);
|
|
172
|
+
for (const [index, chunk] of chunks.entries()) writes.push([getChunkKey(key, marker, index), chunk]);
|
|
173
|
+
writes.push([key, serializeChunkMarker(marker)]);
|
|
174
|
+
return {
|
|
175
|
+
writes,
|
|
176
|
+
cleanup: getUnusedChunkRanges(key, currentMarker, marker)
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function getSlotChunkCount(marker, slot) {
|
|
180
|
+
if (!marker) return 0;
|
|
181
|
+
if (marker.slot === slot) return marker.count;
|
|
182
|
+
if (marker.slot === null || slot === null) return 0;
|
|
183
|
+
return marker.fallbackCount ?? 0;
|
|
184
|
+
}
|
|
185
|
+
function getUnusedChunkRanges(key, previousMarker, marker) {
|
|
186
|
+
return CHUNK_SLOTS.map((slot) => ({
|
|
187
|
+
prefix: slot === null ? key : `${key}.${slot}`,
|
|
188
|
+
start: getSlotChunkCount(marker, slot),
|
|
189
|
+
end: getSlotChunkCount(previousMarker, slot)
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
const storageStates = /* @__PURE__ */ new WeakMap();
|
|
193
|
+
function getStorageState(storage, key) {
|
|
194
|
+
let states = storageStates.get(storage);
|
|
195
|
+
if (!states) {
|
|
196
|
+
states = /* @__PURE__ */ new Map();
|
|
197
|
+
storageStates.set(storage, states);
|
|
198
|
+
}
|
|
199
|
+
let state = states.get(key);
|
|
200
|
+
if (!state) {
|
|
201
|
+
state = {
|
|
202
|
+
pending: null,
|
|
203
|
+
pendingWrites: 0,
|
|
204
|
+
activeRead: null,
|
|
205
|
+
cleanupComplete: false
|
|
206
|
+
};
|
|
207
|
+
states.set(key, state);
|
|
208
|
+
}
|
|
209
|
+
return state;
|
|
210
|
+
}
|
|
211
|
+
function enqueueStorageOperation(state, operation) {
|
|
212
|
+
const queued = (state.pending ?? Promise.resolve()).then(operation, operation);
|
|
213
|
+
state.pending = queued;
|
|
214
|
+
const cleanup = () => {
|
|
215
|
+
if (state.pending === queued) state.pending = null;
|
|
216
|
+
};
|
|
217
|
+
queued.then(cleanup, cleanup);
|
|
218
|
+
return queued;
|
|
219
|
+
}
|
|
220
|
+
function enqueueStorageWrite(state, operation) {
|
|
221
|
+
state.pendingWrites++;
|
|
222
|
+
return enqueueStorageOperation(state, async () => {
|
|
223
|
+
try {
|
|
224
|
+
return await operation();
|
|
225
|
+
} finally {
|
|
226
|
+
state.pendingWrites--;
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
/** @internal */
|
|
231
|
+
function createManagedStorage(storage) {
|
|
232
|
+
const logWriteError = (key, error) => {
|
|
233
|
+
logger.error(`[better-auth/expo] failed to persist "${key}" to storage`, error);
|
|
234
|
+
};
|
|
235
|
+
const logCleanupError = (key, error) => {
|
|
236
|
+
logger.error(`[better-auth/expo] failed to clear unused chunks for "${key}"`, error);
|
|
237
|
+
};
|
|
238
|
+
const getItem = (name) => {
|
|
239
|
+
const key = normalizeCookieName(name);
|
|
240
|
+
return readStoredValue(storage, key, storage.getItem(key));
|
|
241
|
+
};
|
|
242
|
+
const getItemAsync = (name) => {
|
|
243
|
+
const key = normalizeCookieName(name);
|
|
244
|
+
const state = getStorageState(storage, key);
|
|
245
|
+
return enqueueStorageOperation(state, async () => {
|
|
246
|
+
const read = { snapshot: null };
|
|
247
|
+
state.activeRead = read;
|
|
248
|
+
try {
|
|
249
|
+
const value = await readStoredValueAsync(storage, key, await storage.getItemAsync(key));
|
|
250
|
+
return read.snapshot ? read.snapshot.value : value;
|
|
251
|
+
} finally {
|
|
252
|
+
state.activeRead = null;
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
};
|
|
256
|
+
const writeItem = (key, value, currentBaseValue) => {
|
|
257
|
+
const state = getStorageState(storage, key);
|
|
258
|
+
const { writes, cleanup } = getStorageWritePlan(key, value, currentBaseValue);
|
|
259
|
+
for (const [writeKey, writeValue] of writes) storage.setItem(writeKey, writeValue);
|
|
260
|
+
try {
|
|
261
|
+
const scanContiguousOrphans = !state.cleanupComplete;
|
|
262
|
+
for (const { prefix, start, end } of cleanup) {
|
|
263
|
+
if (scanContiguousOrphans) {
|
|
264
|
+
const orphanStart = Math.max(start, end);
|
|
265
|
+
let orphanEnd = orphanStart;
|
|
266
|
+
for (; orphanEnd < MAX_STORAGE_CHUNKS; orphanEnd++) if (!storage.getItem(`${prefix}.${orphanEnd}`)) break;
|
|
267
|
+
for (let i = orphanEnd - 1; i >= orphanStart; i--) storage.setItem(`${prefix}.${i}`, "");
|
|
268
|
+
}
|
|
269
|
+
for (let i = end - 1; i >= start; i--) storage.setItem(`${prefix}.${i}`, "");
|
|
270
|
+
}
|
|
271
|
+
state.cleanupComplete = true;
|
|
272
|
+
} catch (error) {
|
|
273
|
+
state.cleanupComplete = false;
|
|
274
|
+
logCleanupError(key, error);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
const writeItemAsync = async (key, value, currentBaseValue) => {
|
|
278
|
+
const state = getStorageState(storage, key);
|
|
279
|
+
const { writes, cleanup } = getStorageWritePlan(key, value, currentBaseValue);
|
|
280
|
+
for (const [writeKey, writeValue] of writes) await storage.setItemAsync(writeKey, writeValue);
|
|
281
|
+
try {
|
|
282
|
+
const scanContiguousOrphans = !state.cleanupComplete;
|
|
283
|
+
for (const { prefix, start, end } of cleanup) {
|
|
284
|
+
if (scanContiguousOrphans) {
|
|
285
|
+
const orphanStart = Math.max(start, end);
|
|
286
|
+
let orphanEnd = orphanStart;
|
|
287
|
+
for (; orphanEnd < MAX_STORAGE_CHUNKS; orphanEnd++) if (!await storage.getItemAsync(`${prefix}.${orphanEnd}`)) break;
|
|
288
|
+
for (let i = orphanEnd - 1; i >= orphanStart; i--) await storage.setItemAsync(`${prefix}.${i}`, "");
|
|
289
|
+
}
|
|
290
|
+
for (let i = end - 1; i >= start; i--) await storage.setItemAsync(`${prefix}.${i}`, "");
|
|
291
|
+
}
|
|
292
|
+
state.cleanupComplete = true;
|
|
293
|
+
} catch (error) {
|
|
294
|
+
state.cleanupComplete = false;
|
|
295
|
+
logCleanupError(key, error);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
const setItem = (name, value) => {
|
|
299
|
+
const key = normalizeCookieName(name);
|
|
300
|
+
const state = getStorageState(storage, key);
|
|
301
|
+
if (state.pendingWrites > 0) {
|
|
302
|
+
logWriteError(key, /* @__PURE__ */ new Error("Cannot write synchronously while an async write is pending"));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
let currentBaseValue = null;
|
|
306
|
+
try {
|
|
307
|
+
currentBaseValue = storage.getItem(key);
|
|
308
|
+
} catch (error) {
|
|
309
|
+
state.cleanupComplete = false;
|
|
310
|
+
if (splitStorageValue(value).length > 1) {
|
|
311
|
+
logWriteError(key, error);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const read = state.activeRead;
|
|
316
|
+
if (read && read.snapshot === null) try {
|
|
317
|
+
read.snapshot = { value: readStoredValue(storage, key, currentBaseValue) };
|
|
318
|
+
} catch {
|
|
319
|
+
read.snapshot = { value: null };
|
|
320
|
+
}
|
|
321
|
+
try {
|
|
322
|
+
writeItem(key, value, currentBaseValue);
|
|
323
|
+
} catch (error) {
|
|
324
|
+
state.cleanupComplete = false;
|
|
325
|
+
logWriteError(key, error);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
if (read) read.snapshot = { value };
|
|
329
|
+
};
|
|
330
|
+
const setItemAsync = (name, value) => {
|
|
331
|
+
const key = normalizeCookieName(name);
|
|
332
|
+
const state = getStorageState(storage, key);
|
|
333
|
+
return enqueueStorageWrite(state, async () => {
|
|
334
|
+
let currentBaseValue = null;
|
|
335
|
+
try {
|
|
336
|
+
currentBaseValue = await storage.getItemAsync(key);
|
|
337
|
+
} catch (error) {
|
|
338
|
+
state.cleanupComplete = false;
|
|
339
|
+
if (splitStorageValue(value).length > 1) {
|
|
340
|
+
logWriteError(key, error);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
try {
|
|
345
|
+
await writeItemAsync(key, value, currentBaseValue);
|
|
346
|
+
} catch (error) {
|
|
347
|
+
state.cleanupComplete = false;
|
|
348
|
+
logWriteError(key, error);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
};
|
|
352
|
+
const updateItemAsync = (name, update) => {
|
|
353
|
+
const key = normalizeCookieName(name);
|
|
354
|
+
const state = getStorageState(storage, key);
|
|
355
|
+
return enqueueStorageWrite(state, async () => {
|
|
356
|
+
try {
|
|
357
|
+
const currentBaseValue = await storage.getItemAsync(key);
|
|
358
|
+
const previousValue = await readStoredValueAsync(storage, key, currentBaseValue);
|
|
359
|
+
const value = update(previousValue);
|
|
360
|
+
await writeItemAsync(key, value, currentBaseValue);
|
|
361
|
+
return {
|
|
362
|
+
previousValue,
|
|
363
|
+
value
|
|
364
|
+
};
|
|
365
|
+
} catch (error) {
|
|
366
|
+
state.cleanupComplete = false;
|
|
367
|
+
logWriteError(key, error);
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
};
|
|
372
|
+
return {
|
|
373
|
+
getItem,
|
|
374
|
+
getItemAsync,
|
|
375
|
+
setItem,
|
|
376
|
+
setItemAsync,
|
|
377
|
+
updateItemAsync
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Wraps Expo storage with chunking, recoverable writes, and serialized async
|
|
382
|
+
* access.
|
|
383
|
+
*/
|
|
384
|
+
function storageAdapter(storage) {
|
|
385
|
+
const managedStorage = createManagedStorage(storage);
|
|
386
|
+
return {
|
|
387
|
+
getItem: managedStorage.getItem,
|
|
388
|
+
getItemAsync: managedStorage.getItemAsync,
|
|
389
|
+
setItem: managedStorage.setItem,
|
|
390
|
+
setItemAsync: managedStorage.setItemAsync
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
//#endregion
|
|
14
394
|
//#region src/focus-manager.ts
|
|
15
395
|
var ExpoFocusManager = class {
|
|
16
396
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -183,111 +563,12 @@ function hasBetterAuthCookies(setCookieHeader, cookiePrefix) {
|
|
|
183
563
|
}
|
|
184
564
|
return false;
|
|
185
565
|
}
|
|
186
|
-
/**
|
|
187
|
-
* Expo secure store does not support colons in the keys.
|
|
188
|
-
* This function replaces colons with underscores.
|
|
189
|
-
*
|
|
190
|
-
* @see https://github.com/better-auth/better-auth/issues/5426
|
|
191
|
-
*
|
|
192
|
-
* @param name cookie name to be saved in the storage
|
|
193
|
-
* @returns normalized cookie name
|
|
194
|
-
*/
|
|
195
|
-
function normalizeCookieName(name) {
|
|
196
|
-
return name.replace(/:/g, "_");
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Max characters written per `setItem`. Native secure stores silently reject
|
|
200
|
-
* oversized writes (iOS Keychain refuses values above ~2KB), losing the cookie,
|
|
201
|
-
* so a larger value is split across keys here. Mirrors the server's
|
|
202
|
-
* `chunkCookie`/`joinChunks` in `session-store.ts`; keep the two in sync.
|
|
203
|
-
*
|
|
204
|
-
* @see https://github.com/better-auth/better-auth/issues/9151
|
|
205
|
-
*/
|
|
206
|
-
const STORAGE_VALUE_LIMIT = 1800;
|
|
207
|
-
/**
|
|
208
|
-
* Marks a base key whose value is split across `<key>.0..N` chunks. The leading
|
|
209
|
-
* control char can't start a JSON value (so it never collides) and, unlike NUL,
|
|
210
|
-
* survives the native storage bridge without C-string truncation.
|
|
211
|
-
*/
|
|
212
|
-
const CHUNK_MARKER = "ba-chunks:";
|
|
213
|
-
function getStorageWrites(key, value) {
|
|
214
|
-
if (value.length <= STORAGE_VALUE_LIMIT) return [[key, value]];
|
|
215
|
-
const count = Math.ceil(value.length / STORAGE_VALUE_LIMIT);
|
|
216
|
-
const writes = [[key, ""]];
|
|
217
|
-
for (let i = 0; i < count; i++) {
|
|
218
|
-
const start = i * STORAGE_VALUE_LIMIT;
|
|
219
|
-
writes.push([`${key}.${i}`, value.slice(start, start + STORAGE_VALUE_LIMIT)]);
|
|
220
|
-
}
|
|
221
|
-
writes.push([key, `${CHUNK_MARKER}${count}`]);
|
|
222
|
-
return writes;
|
|
223
|
-
}
|
|
224
|
-
function storageAdapter(storage) {
|
|
225
|
-
return {
|
|
226
|
-
/**
|
|
227
|
-
* Reads a value, reassembling it if it was split across chunk keys. A value
|
|
228
|
-
* that fit is returned as-is (values written before chunking still read
|
|
229
|
-
* back); a missing chunk returns `null` so a torn write fails closed.
|
|
230
|
-
*/
|
|
231
|
-
getItem: (name) => {
|
|
232
|
-
const key = normalizeCookieName(name);
|
|
233
|
-
const stored = storage.getItem(key);
|
|
234
|
-
if (stored == null || !stored.startsWith(CHUNK_MARKER)) return stored;
|
|
235
|
-
const count = Number(stored.slice(11));
|
|
236
|
-
if (!Number.isInteger(count) || count < 1) return null;
|
|
237
|
-
let value = "";
|
|
238
|
-
for (let i = 0; i < count; i++) {
|
|
239
|
-
const chunk = storage.getItem(`${key}.${i}`);
|
|
240
|
-
if (chunk == null) return null;
|
|
241
|
-
value += chunk;
|
|
242
|
-
}
|
|
243
|
-
return value;
|
|
244
|
-
},
|
|
245
|
-
getItemAsync: async (name) => {
|
|
246
|
-
const key = normalizeCookieName(name);
|
|
247
|
-
const stored = await storage.getItemAsync(key);
|
|
248
|
-
if (stored == null || !stored.startsWith(CHUNK_MARKER)) return stored;
|
|
249
|
-
const count = Number(stored.slice(11));
|
|
250
|
-
if (!Number.isInteger(count) || count < 1) return null;
|
|
251
|
-
let value = "";
|
|
252
|
-
for (let i = 0; i < count; i++) {
|
|
253
|
-
const chunk = await storage.getItemAsync(`${key}.${i}`);
|
|
254
|
-
if (chunk == null) return null;
|
|
255
|
-
value += chunk;
|
|
256
|
-
}
|
|
257
|
-
return value;
|
|
258
|
-
},
|
|
259
|
-
/**
|
|
260
|
-
* Stores `value`, splitting it across chunk keys when it exceeds the
|
|
261
|
-
* per-write limit. The base key is cleared before the chunks are rewritten
|
|
262
|
-
* and set to the marker last, as the commit point, so a write interrupted
|
|
263
|
-
* partway through reads as absent rather than a mix of old and new chunks.
|
|
264
|
-
* Failures are logged, not thrown: persistence is best-effort and must not
|
|
265
|
-
* break the request.
|
|
266
|
-
*/
|
|
267
|
-
setItem: (name, value) => {
|
|
268
|
-
const key = normalizeCookieName(name);
|
|
269
|
-
try {
|
|
270
|
-
for (const [writeKey, writeValue] of getStorageWrites(key, value)) storage.setItem(writeKey, writeValue);
|
|
271
|
-
} catch (error) {
|
|
272
|
-
console.error(`[better-auth/expo] failed to persist "${key}" to storage`, error);
|
|
273
|
-
}
|
|
274
|
-
},
|
|
275
|
-
setItemAsync: async (name, value) => {
|
|
276
|
-
const key = normalizeCookieName(name);
|
|
277
|
-
try {
|
|
278
|
-
for (const [writeKey, writeValue] of getStorageWrites(key, value)) await storage.setItemAsync(writeKey, writeValue);
|
|
279
|
-
} catch (error) {
|
|
280
|
-
console.error(`[better-auth/expo] failed to persist "${key}" to storage`, error);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
566
|
const expoClient = (opts) => {
|
|
286
567
|
let store = null;
|
|
287
568
|
const storagePrefix = opts?.storagePrefix || "better-auth";
|
|
288
569
|
const cookieName = `${storagePrefix}_cookie`;
|
|
289
570
|
const localCacheName = `${storagePrefix}_session_data`;
|
|
290
|
-
const storage =
|
|
571
|
+
const storage = createManagedStorage(opts.storage);
|
|
291
572
|
const isWeb = Platform.OS === "web";
|
|
292
573
|
const cookiePrefix = opts?.cookiePrefix || "better-auth";
|
|
293
574
|
let sessionCacheHydration;
|
|
@@ -359,12 +640,8 @@ getCookie: async () => {
|
|
|
359
640
|
const setCookie = context.response.headers.get("set-cookie");
|
|
360
641
|
if (setCookie) {
|
|
361
642
|
if (hasBetterAuthCookies(setCookie, cookiePrefix)) {
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
if (hasSessionCookieChanged(prevCookie, toSetCookie)) {
|
|
365
|
-
await storage.setItemAsync(cookieName, toSetCookie);
|
|
366
|
-
store?.notify("$sessionSignal");
|
|
367
|
-
} else await storage.setItemAsync(cookieName, toSetCookie);
|
|
643
|
+
const update = await storage.updateItemAsync(cookieName, (currentValue) => getSetCookie(setCookie, currentValue ?? void 0));
|
|
644
|
+
if (update && hasSessionCookieChanged(update.previousValue, update.value)) store?.notify("$sessionSignal");
|
|
368
645
|
}
|
|
369
646
|
}
|
|
370
647
|
if (pathname.endsWith("/get-session") && !opts?.disableCache) {
|
|
@@ -398,9 +675,7 @@ getCookie: async () => {
|
|
|
398
675
|
if (result.type !== "success") return;
|
|
399
676
|
const cookie = new URL(result.url).searchParams.get("cookie");
|
|
400
677
|
if (!cookie) return;
|
|
401
|
-
|
|
402
|
-
await storage.setItemAsync(cookieName, toSetCookie);
|
|
403
|
-
store?.notify("$sessionSignal");
|
|
678
|
+
if (await storage.updateItemAsync(cookieName, (currentValue) => getSetCookie(cookie, currentValue ?? void 0))) store?.notify("$sessionSignal");
|
|
404
679
|
}
|
|
405
680
|
} },
|
|
406
681
|
async init(url, options) {
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { t as PACKAGE_VERSION } from "./version-CuQDLAg7.js";
|
|
2
2
|
import { createAuthMiddleware } from "@better-auth/core/api";
|
|
3
3
|
import { HIDE_METADATA } from "better-auth";
|
|
4
4
|
import { APIError, createAuthEndpoint } from "better-auth/api";
|
package/dist/plugins/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth/expo",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.4",
|
|
4
4
|
"description": "Better Auth integration for Expo and React Native applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@better-fetch/fetch": "1.3.1",
|
|
62
62
|
"better-call": "1.4.0",
|
|
63
|
-
"zod": "^4.
|
|
63
|
+
"zod": "^4.5.4"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@better-fetch/fetch": "1.3.1",
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
"expo-web-browser": "~56.0.5",
|
|
72
72
|
"react-native": "~0.86.0",
|
|
73
73
|
"tsdown": "0.21.10",
|
|
74
|
-
"@better-auth/core": "1.7.
|
|
75
|
-
"better-auth": "1.7.
|
|
74
|
+
"@better-auth/core": "1.7.4",
|
|
75
|
+
"better-auth": "1.7.4"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
78
|
"expo-constants": ">=17.0.0",
|
|
@@ -80,8 +80,8 @@
|
|
|
80
80
|
"expo-network": ">=8.0.7",
|
|
81
81
|
"expo-secure-store": ">=12.5.0",
|
|
82
82
|
"expo-web-browser": ">=14.0.0",
|
|
83
|
-
"@better-auth/core": "^1.7.
|
|
84
|
-
"better-auth": "^1.7.
|
|
83
|
+
"@better-auth/core": "^1.7.4",
|
|
84
|
+
"better-auth": "^1.7.4"
|
|
85
85
|
},
|
|
86
86
|
"peerDependenciesMeta": {
|
|
87
87
|
"expo-constants": {
|