@better-auth/expo 1.7.1 → 1.7.3
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 +13 -19
- package/dist/client.js +200 -69
- package/dist/index.js +1 -1
- package/dist/plugins/index.js +1 -1
- package/dist/{version-BdmvArPe.js → version-DddGjO8z.js} +1 -1
- package/package.json +6 -6
package/dist/client.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ declare function setupExpoOnlineManager(): OnlineManager;
|
|
|
14
14
|
//#region src/client.d.ts
|
|
15
15
|
/**
|
|
16
16
|
* Storage used by the Expo client for cookies and cached session data.
|
|
17
|
+
* Write coordination is scoped to the provided object, so reuse it across
|
|
18
|
+
* clients that access the same stored data.
|
|
17
19
|
*/
|
|
18
20
|
type ExpoClientStorage = Pick<typeof SecureStore, "setItem" | "setItemAsync" | "getItem" | "getItemAsync">;
|
|
19
21
|
interface ExpoClientOptions {
|
|
@@ -84,25 +86,17 @@ declare function hasBetterAuthCookies(setCookieHeader: string, cookiePrefix: str
|
|
|
84
86
|
* @returns normalized cookie name
|
|
85
87
|
*/
|
|
86
88
|
declare function normalizeCookieName(name: string): string;
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
-
};
|
|
89
|
+
interface ExpoStorageAdapter {
|
|
90
|
+
getItem(name: string): string | null;
|
|
91
|
+
getItemAsync(name: string): Promise<string | null>;
|
|
92
|
+
setItem(name: string, value: string): void;
|
|
93
|
+
setItemAsync(name: string, value: string): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Wraps Expo storage with chunking, recoverable writes, and serialized async
|
|
97
|
+
* updates.
|
|
98
|
+
*/
|
|
99
|
+
declare function storageAdapter(storage: ExpoClientStorage): ExpoStorageAdapter;
|
|
106
100
|
declare const expoClient: (opts: ExpoClientOptions) => {
|
|
107
101
|
id: "expo";
|
|
108
102
|
version: string;
|
package/dist/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as PACKAGE_VERSION } from "./version-
|
|
1
|
+
import { t as PACKAGE_VERSION } from "./version-DddGjO8z.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";
|
|
@@ -204,82 +204,219 @@ function normalizeCookieName(name) {
|
|
|
204
204
|
* @see https://github.com/better-auth/better-auth/issues/9151
|
|
205
205
|
*/
|
|
206
206
|
const STORAGE_VALUE_LIMIT = 1800;
|
|
207
|
+
const MAX_STORAGE_CHUNKS = 100;
|
|
207
208
|
/**
|
|
208
|
-
* Marks a base key whose value is split across
|
|
209
|
-
*
|
|
210
|
-
*
|
|
209
|
+
* Marks a base key whose value is split across multiple storage keys. Legacy
|
|
210
|
+
* markers contain only the chunk count. Current markers also identify the
|
|
211
|
+
* active slot and retain the previous slot's chunk count for recovery.
|
|
212
|
+
*
|
|
213
|
+
* @see https://github.com/better-auth/better-auth/issues/11082
|
|
211
214
|
*/
|
|
212
215
|
const CHUNK_MARKER = "ba-chunks:";
|
|
213
|
-
function
|
|
216
|
+
function parseChunkCount(value) {
|
|
217
|
+
if (value === void 0) return null;
|
|
218
|
+
const count = Number(value);
|
|
219
|
+
if (!Number.isInteger(count) || count < 1 || count > MAX_STORAGE_CHUNKS) return null;
|
|
220
|
+
return count;
|
|
221
|
+
}
|
|
222
|
+
function parseChunkMarker(baseValue) {
|
|
223
|
+
const parts = baseValue.slice(11).split(":");
|
|
224
|
+
if (parts.length > 3) return null;
|
|
225
|
+
const [countValue, slotValue, fallbackCountValue] = parts;
|
|
226
|
+
const count = parseChunkCount(countValue);
|
|
227
|
+
if (count === null) return null;
|
|
228
|
+
if (slotValue === void 0) return fallbackCountValue === void 0 ? {
|
|
229
|
+
count,
|
|
230
|
+
slot: null,
|
|
231
|
+
fallbackCount: null
|
|
232
|
+
} : null;
|
|
233
|
+
if (slotValue !== "0" && slotValue !== "1") return null;
|
|
234
|
+
const fallbackCount = parseChunkCount(fallbackCountValue);
|
|
235
|
+
if (fallbackCountValue !== void 0 && fallbackCount === null) return null;
|
|
236
|
+
return {
|
|
237
|
+
count,
|
|
238
|
+
slot: slotValue === "0" ? 0 : 1,
|
|
239
|
+
fallbackCount
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function getChunkKey(key, marker, index) {
|
|
243
|
+
return marker.slot === null ? `${key}.${index}` : `${key}.${marker.slot}.${index}`;
|
|
244
|
+
}
|
|
245
|
+
function getOtherSlot(slot) {
|
|
246
|
+
return slot === 0 ? 1 : 0;
|
|
247
|
+
}
|
|
248
|
+
function serializeChunkMarker(marker) {
|
|
249
|
+
if (marker.slot === null) return `${CHUNK_MARKER}${marker.count}`;
|
|
250
|
+
const fallback = marker.fallbackCount === null ? "" : `:${marker.fallbackCount}`;
|
|
251
|
+
return `${CHUNK_MARKER}${marker.count}:${marker.slot}${fallback}`;
|
|
252
|
+
}
|
|
253
|
+
function readChunks(storage, key, marker) {
|
|
254
|
+
let value = "";
|
|
255
|
+
for (let i = 0; i < marker.count; i++) {
|
|
256
|
+
const chunk = storage.getItem(getChunkKey(key, marker, i));
|
|
257
|
+
if (chunk == null) return null;
|
|
258
|
+
value += chunk;
|
|
259
|
+
}
|
|
260
|
+
return value;
|
|
261
|
+
}
|
|
262
|
+
async function readChunksAsync(storage, key, marker) {
|
|
263
|
+
let value = "";
|
|
264
|
+
for (let i = 0; i < marker.count; i++) {
|
|
265
|
+
const chunk = await storage.getItemAsync(getChunkKey(key, marker, i));
|
|
266
|
+
if (chunk == null) return null;
|
|
267
|
+
value += chunk;
|
|
268
|
+
}
|
|
269
|
+
return value;
|
|
270
|
+
}
|
|
271
|
+
function readStoredValue(storage, key, baseValue) {
|
|
272
|
+
if (baseValue == null || !baseValue.startsWith(CHUNK_MARKER)) return baseValue;
|
|
273
|
+
const marker = parseChunkMarker(baseValue);
|
|
274
|
+
if (!marker) return null;
|
|
275
|
+
const value = readChunks(storage, key, marker);
|
|
276
|
+
if (value !== null || marker.slot === null || marker.fallbackCount === null) return value;
|
|
277
|
+
return readChunks(storage, key, {
|
|
278
|
+
count: marker.fallbackCount,
|
|
279
|
+
slot: getOtherSlot(marker.slot),
|
|
280
|
+
fallbackCount: null
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
async function readStoredValueAsync(storage, key, baseValue) {
|
|
284
|
+
if (baseValue == null || !baseValue.startsWith(CHUNK_MARKER)) return baseValue;
|
|
285
|
+
const marker = parseChunkMarker(baseValue);
|
|
286
|
+
if (!marker) return null;
|
|
287
|
+
const value = await readChunksAsync(storage, key, marker);
|
|
288
|
+
if (value !== null || marker.slot === null || marker.fallbackCount === null) return value;
|
|
289
|
+
return readChunksAsync(storage, key, {
|
|
290
|
+
count: marker.fallbackCount,
|
|
291
|
+
slot: getOtherSlot(marker.slot),
|
|
292
|
+
fallbackCount: null
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
function getStorageWrites(key, value, currentBaseValue) {
|
|
214
296
|
if (value.length <= STORAGE_VALUE_LIMIT) return [[key, value]];
|
|
215
297
|
const count = Math.ceil(value.length / STORAGE_VALUE_LIMIT);
|
|
216
|
-
|
|
298
|
+
if (count > MAX_STORAGE_CHUNKS) throw new Error(`Storage value requires ${count} chunks, exceeding the limit of ${MAX_STORAGE_CHUNKS}`);
|
|
299
|
+
const currentMarker = currentBaseValue?.startsWith(CHUNK_MARKER) ? parseChunkMarker(currentBaseValue) : null;
|
|
300
|
+
const marker = {
|
|
301
|
+
count,
|
|
302
|
+
slot: currentMarker?.slot === 0 ? 1 : 0,
|
|
303
|
+
fallbackCount: currentMarker?.slot == null ? null : currentMarker.count
|
|
304
|
+
};
|
|
305
|
+
const writes = [];
|
|
306
|
+
if (currentMarker?.slot != null && currentMarker.fallbackCount !== null) writes.push([key, serializeChunkMarker({
|
|
307
|
+
...currentMarker,
|
|
308
|
+
fallbackCount: null
|
|
309
|
+
})]);
|
|
217
310
|
for (let i = 0; i < count; i++) {
|
|
218
311
|
const start = i * STORAGE_VALUE_LIMIT;
|
|
219
|
-
writes.push([
|
|
312
|
+
writes.push([getChunkKey(key, marker, i), value.slice(start, start + STORAGE_VALUE_LIMIT)]);
|
|
220
313
|
}
|
|
221
|
-
writes.push([key,
|
|
314
|
+
writes.push([key, serializeChunkMarker(marker)]);
|
|
222
315
|
return writes;
|
|
223
316
|
}
|
|
224
|
-
function
|
|
317
|
+
function createKeyedWriteQueue() {
|
|
318
|
+
const tails = /* @__PURE__ */ new Map();
|
|
225
319
|
return {
|
|
226
|
-
|
|
227
|
-
|
|
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;
|
|
320
|
+
pending(key) {
|
|
321
|
+
return tails.has(key);
|
|
244
322
|
},
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
323
|
+
enqueue(key, operation) {
|
|
324
|
+
const queued = (tails.get(key) ?? Promise.resolve()).then(operation, operation);
|
|
325
|
+
tails.set(key, queued);
|
|
326
|
+
const cleanup = () => {
|
|
327
|
+
if (tails.get(key) === queued) tails.delete(key);
|
|
328
|
+
};
|
|
329
|
+
queued.then(cleanup, cleanup);
|
|
330
|
+
return queued;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
const storageWriteQueues = /* @__PURE__ */ new WeakMap();
|
|
335
|
+
function getStorageWriteQueue(storage) {
|
|
336
|
+
const existing = storageWriteQueues.get(storage);
|
|
337
|
+
if (existing) return existing;
|
|
338
|
+
const queue = createKeyedWriteQueue();
|
|
339
|
+
storageWriteQueues.set(storage, queue);
|
|
340
|
+
return queue;
|
|
341
|
+
}
|
|
342
|
+
function createManagedStorage(storage) {
|
|
343
|
+
const writeQueue = getStorageWriteQueue(storage);
|
|
344
|
+
const logWriteError = (key, error) => {
|
|
345
|
+
console.error(`[better-auth/expo] failed to persist "${key}" to storage`, error);
|
|
346
|
+
};
|
|
347
|
+
const getItem = (name) => {
|
|
348
|
+
const key = normalizeCookieName(name);
|
|
349
|
+
return readStoredValue(storage, key, storage.getItem(key));
|
|
350
|
+
};
|
|
351
|
+
const getItemAsync = async (name) => {
|
|
352
|
+
const key = normalizeCookieName(name);
|
|
353
|
+
return readStoredValueAsync(storage, key, await storage.getItemAsync(key));
|
|
354
|
+
};
|
|
355
|
+
const writeItem = (key, value, currentBaseValue) => {
|
|
356
|
+
for (const [writeKey, writeValue] of getStorageWrites(key, value, currentBaseValue)) storage.setItem(writeKey, writeValue);
|
|
357
|
+
};
|
|
358
|
+
const writeItemAsync = async (key, value, currentBaseValue) => {
|
|
359
|
+
for (const [writeKey, writeValue] of getStorageWrites(key, value, currentBaseValue)) await storage.setItemAsync(writeKey, writeValue);
|
|
360
|
+
};
|
|
361
|
+
const setItem = (name, value) => {
|
|
362
|
+
const key = normalizeCookieName(name);
|
|
363
|
+
if (writeQueue.pending(key)) {
|
|
364
|
+
logWriteError(key, /* @__PURE__ */ new Error("Cannot write synchronously while an async write is pending"));
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
try {
|
|
368
|
+
writeItem(key, value, value.length > STORAGE_VALUE_LIMIT ? storage.getItem(key) : null);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
logWriteError(key, error);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
const setItemAsync = (name, value) => {
|
|
374
|
+
const key = normalizeCookieName(name);
|
|
375
|
+
return writeQueue.enqueue(key, async () => {
|
|
269
376
|
try {
|
|
270
|
-
|
|
377
|
+
await writeItemAsync(key, value, value.length > STORAGE_VALUE_LIMIT ? await storage.getItemAsync(key) : null);
|
|
271
378
|
} catch (error) {
|
|
272
|
-
|
|
379
|
+
logWriteError(key, error);
|
|
273
380
|
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
|
|
381
|
+
});
|
|
382
|
+
};
|
|
383
|
+
const updateItemAsync = (name, update) => {
|
|
384
|
+
const key = normalizeCookieName(name);
|
|
385
|
+
return writeQueue.enqueue(key, async () => {
|
|
277
386
|
try {
|
|
278
|
-
|
|
387
|
+
const currentBaseValue = await storage.getItemAsync(key);
|
|
388
|
+
const previousValue = await readStoredValueAsync(storage, key, currentBaseValue);
|
|
389
|
+
const value = update(previousValue);
|
|
390
|
+
await writeItemAsync(key, value, currentBaseValue);
|
|
391
|
+
return {
|
|
392
|
+
previousValue,
|
|
393
|
+
value
|
|
394
|
+
};
|
|
279
395
|
} catch (error) {
|
|
280
|
-
|
|
396
|
+
logWriteError(key, error);
|
|
397
|
+
return null;
|
|
281
398
|
}
|
|
282
|
-
}
|
|
399
|
+
});
|
|
400
|
+
};
|
|
401
|
+
return {
|
|
402
|
+
getItem,
|
|
403
|
+
getItemAsync,
|
|
404
|
+
setItem,
|
|
405
|
+
setItemAsync,
|
|
406
|
+
updateItemAsync
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Wraps Expo storage with chunking, recoverable writes, and serialized async
|
|
411
|
+
* updates.
|
|
412
|
+
*/
|
|
413
|
+
function storageAdapter(storage) {
|
|
414
|
+
const managedStorage = createManagedStorage(storage);
|
|
415
|
+
return {
|
|
416
|
+
getItem: managedStorage.getItem,
|
|
417
|
+
getItemAsync: managedStorage.getItemAsync,
|
|
418
|
+
setItem: managedStorage.setItem,
|
|
419
|
+
setItemAsync: managedStorage.setItemAsync
|
|
283
420
|
};
|
|
284
421
|
}
|
|
285
422
|
const expoClient = (opts) => {
|
|
@@ -287,7 +424,7 @@ const expoClient = (opts) => {
|
|
|
287
424
|
const storagePrefix = opts?.storagePrefix || "better-auth";
|
|
288
425
|
const cookieName = `${storagePrefix}_cookie`;
|
|
289
426
|
const localCacheName = `${storagePrefix}_session_data`;
|
|
290
|
-
const storage =
|
|
427
|
+
const storage = createManagedStorage(opts.storage);
|
|
291
428
|
const isWeb = Platform.OS === "web";
|
|
292
429
|
const cookiePrefix = opts?.cookiePrefix || "better-auth";
|
|
293
430
|
let sessionCacheHydration;
|
|
@@ -359,12 +496,8 @@ getCookie: async () => {
|
|
|
359
496
|
const setCookie = context.response.headers.get("set-cookie");
|
|
360
497
|
if (setCookie) {
|
|
361
498
|
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);
|
|
499
|
+
const update = await storage.updateItemAsync(cookieName, (currentValue) => getSetCookie(setCookie, currentValue ?? void 0));
|
|
500
|
+
if (update && hasSessionCookieChanged(update.previousValue, update.value)) store?.notify("$sessionSignal");
|
|
368
501
|
}
|
|
369
502
|
}
|
|
370
503
|
if (pathname.endsWith("/get-session") && !opts?.disableCache) {
|
|
@@ -398,9 +531,7 @@ getCookie: async () => {
|
|
|
398
531
|
if (result.type !== "success") return;
|
|
399
532
|
const cookie = new URL(result.url).searchParams.get("cookie");
|
|
400
533
|
if (!cookie) return;
|
|
401
|
-
|
|
402
|
-
await storage.setItemAsync(cookieName, toSetCookie);
|
|
403
|
-
store?.notify("$sessionSignal");
|
|
534
|
+
if (await storage.updateItemAsync(cookieName, (currentValue) => getSetCookie(cookie, currentValue ?? void 0))) store?.notify("$sessionSignal");
|
|
404
535
|
}
|
|
405
536
|
} },
|
|
406
537
|
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-DddGjO8z.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.3",
|
|
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.3",
|
|
75
|
+
"better-auth": "1.7.3"
|
|
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.3",
|
|
84
|
+
"better-auth": "^1.7.3"
|
|
85
85
|
},
|
|
86
86
|
"peerDependenciesMeta": {
|
|
87
87
|
"expo-constants": {
|