@clerk/expo 3.5.0-canary.v20260619100729 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/expo/modules/clerk/ClerkExpoModule.kt +109 -41
- package/dist/hooks/useNativeClientEvents.d.ts +5 -1
- package/dist/hooks/useNativeClientEvents.d.ts.map +1 -1
- package/dist/hooks/useNativeClientEvents.js +4 -0
- package/dist/hooks/useNativeClientEvents.js.map +1 -1
- package/dist/provider/ClerkProvider.js +2 -2
- package/dist/provider/ClerkProvider.js.map +1 -1
- package/dist/provider/nativeClientSync.d.ts +7 -7
- package/dist/provider/nativeClientSync.d.ts.map +1 -1
- package/dist/provider/nativeClientSync.js +76 -64
- package/dist/provider/nativeClientSync.js.map +1 -1
- package/dist/specs/NativeClerkModule.android.d.ts +1 -1
- package/dist/specs/NativeClerkModule.android.d.ts.map +1 -1
- package/dist/specs/NativeClerkModule.android.js.map +1 -1
- package/dist/specs/NativeClerkModule.d.ts +1 -1
- package/dist/specs/NativeClerkModule.d.ts.map +1 -1
- package/dist/specs/NativeClerkModule.js.map +1 -1
- package/dist/utils/native-module.d.ts +1 -1
- package/dist/utils/native-module.d.ts.map +1 -1
- package/dist/utils/native-module.js +1 -1
- package/dist/utils/native-module.js.map +1 -1
- package/ios/ClerkExpoModule.m +3 -2
- package/ios/ClerkExpoModule.swift +9 -9
- package/ios/ClerkNativeBridge.swift +43 -11
- package/package.json +6 -6
- package/src/specs/NativeClerkModule.android.ts +4 -3
- package/src/specs/NativeClerkModule.ts +4 -3
|
@@ -35,16 +35,32 @@ private fun debugLog(tag: String, message: String) {
|
|
|
35
35
|
class ClerkExpoModule : Module() {
|
|
36
36
|
private val coroutineScope = CoroutineScope(Dispatchers.Main)
|
|
37
37
|
private var clientStateObserverJob: Job? = null
|
|
38
|
-
private var
|
|
38
|
+
private var lastObservedClientState: ClientStateSnapshot? = null
|
|
39
39
|
private var jsOriginatedClientSyncDepth = 0
|
|
40
40
|
private var configuredPublishableKey: String? = null
|
|
41
41
|
|
|
42
|
+
private data class ClientStateSnapshot(
|
|
43
|
+
val client: Client?,
|
|
44
|
+
val deviceToken: String?
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
private data class ClientStateChanges(
|
|
48
|
+
val client: Boolean,
|
|
49
|
+
val deviceToken: Boolean
|
|
50
|
+
)
|
|
51
|
+
|
|
42
52
|
companion object {
|
|
43
53
|
private var sharedInstance: ClerkExpoModule? = null
|
|
44
54
|
|
|
45
55
|
fun emitClientChanged(sourceId: String? = null) {
|
|
46
56
|
val instance = sharedInstance ?: return
|
|
47
|
-
instance.sendEvent(
|
|
57
|
+
instance.sendEvent(
|
|
58
|
+
NATIVE_CLIENT_CHANGED_EVENT,
|
|
59
|
+
instance.clientChangedPayload(
|
|
60
|
+
sourceId = sourceId,
|
|
61
|
+
changes = ClientStateChanges(client = true, deviceToken = true)
|
|
62
|
+
)
|
|
63
|
+
)
|
|
48
64
|
}
|
|
49
65
|
}
|
|
50
66
|
|
|
@@ -73,11 +89,17 @@ class ClerkExpoModule : Module() {
|
|
|
73
89
|
getClientToken(promise)
|
|
74
90
|
}
|
|
75
91
|
|
|
76
|
-
AsyncFunction("
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
AsyncFunction("syncClientStateFromJs") {
|
|
93
|
+
deviceToken: String?,
|
|
94
|
+
sourceId: String?,
|
|
95
|
+
didChangeClient: Boolean,
|
|
96
|
+
didChangeDeviceToken: Boolean,
|
|
97
|
+
promise: Promise ->
|
|
98
|
+
syncClientStateFromJs(
|
|
99
|
+
deviceToken,
|
|
79
100
|
sourceId,
|
|
80
|
-
|
|
101
|
+
didChangeClient,
|
|
102
|
+
didChangeDeviceToken,
|
|
81
103
|
promise
|
|
82
104
|
)
|
|
83
105
|
}
|
|
@@ -91,42 +113,80 @@ class ClerkExpoModule : Module() {
|
|
|
91
113
|
return
|
|
92
114
|
}
|
|
93
115
|
|
|
94
|
-
|
|
116
|
+
lastObservedClientState = clientStateSnapshot()
|
|
95
117
|
|
|
96
118
|
clientStateObserverJob = coroutineScope.launch {
|
|
97
119
|
Clerk.clientFlow.collect { client ->
|
|
98
|
-
|
|
120
|
+
val previousClientState = lastObservedClientState
|
|
121
|
+
val newClientState = clientStateSnapshot(client)
|
|
122
|
+
|
|
123
|
+
if (newClientState == previousClientState) {
|
|
99
124
|
return@collect
|
|
100
125
|
}
|
|
101
126
|
|
|
102
|
-
|
|
127
|
+
lastObservedClientState = newClientState
|
|
103
128
|
if (jsOriginatedClientSyncDepth > 0) {
|
|
104
129
|
return@collect
|
|
105
130
|
}
|
|
106
131
|
|
|
107
|
-
|
|
132
|
+
sendEvent(
|
|
133
|
+
NATIVE_CLIENT_CHANGED_EVENT,
|
|
134
|
+
clientChangedPayload(
|
|
135
|
+
deviceToken = newClientState.deviceToken,
|
|
136
|
+
changes = ClientStateChanges(
|
|
137
|
+
client = newClientState.client != previousClientState?.client,
|
|
138
|
+
deviceToken = newClientState.deviceToken != previousClientState?.deviceToken
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
)
|
|
108
142
|
}
|
|
109
143
|
}
|
|
110
144
|
}
|
|
111
145
|
|
|
112
|
-
private fun
|
|
113
|
-
|
|
114
|
-
|
|
146
|
+
private fun clientStateSnapshot(client: Client? = Clerk.clientFlow.value): ClientStateSnapshot {
|
|
147
|
+
return ClientStateSnapshot(
|
|
148
|
+
client = client,
|
|
149
|
+
deviceToken = try {
|
|
115
150
|
Clerk.getDeviceToken()
|
|
116
151
|
} catch (e: Exception) {
|
|
117
|
-
debugLog(TAG, "
|
|
152
|
+
debugLog(TAG, "clientStateSnapshot - getDeviceToken failed: ${e.message}")
|
|
118
153
|
null
|
|
119
154
|
}
|
|
120
155
|
)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private fun clientChangedPayload(
|
|
159
|
+
sourceId: String? = null,
|
|
160
|
+
changes: ClientStateChanges,
|
|
161
|
+
deviceToken: String? = clientStateSnapshot().deviceToken
|
|
162
|
+
): Map<String, Any?> {
|
|
163
|
+
val result = mutableMapOf<String, Any?>(
|
|
164
|
+
"changed" to mapOf(
|
|
165
|
+
"client" to changes.client,
|
|
166
|
+
"deviceToken" to changes.deviceToken
|
|
167
|
+
),
|
|
168
|
+
"deviceToken" to deviceToken
|
|
169
|
+
)
|
|
121
170
|
if (!sourceId.isNullOrEmpty()) {
|
|
122
171
|
result["sourceId"] = sourceId
|
|
123
172
|
}
|
|
124
173
|
return result
|
|
125
174
|
}
|
|
126
175
|
|
|
127
|
-
private fun emitSyncedClientChanged(
|
|
128
|
-
|
|
129
|
-
|
|
176
|
+
private fun emitSyncedClientChanged(
|
|
177
|
+
sourceId: String?,
|
|
178
|
+
changes: ClientStateChanges,
|
|
179
|
+
snapshot: ClientStateSnapshot = clientStateSnapshot()
|
|
180
|
+
) {
|
|
181
|
+
lastObservedClientState = snapshot
|
|
182
|
+
sendEvent(
|
|
183
|
+
NATIVE_CLIENT_CHANGED_EVENT,
|
|
184
|
+
clientChangedPayload(
|
|
185
|
+
sourceId = sourceId,
|
|
186
|
+
changes = changes,
|
|
187
|
+
deviceToken = snapshot.deviceToken
|
|
188
|
+
)
|
|
189
|
+
)
|
|
130
190
|
}
|
|
131
191
|
|
|
132
192
|
// MARK: - configure
|
|
@@ -288,12 +348,13 @@ class ClerkExpoModule : Module() {
|
|
|
288
348
|
}
|
|
289
349
|
}
|
|
290
350
|
|
|
291
|
-
// MARK: -
|
|
351
|
+
// MARK: - syncClientStateFromJs
|
|
292
352
|
|
|
293
|
-
private fun
|
|
294
|
-
|
|
353
|
+
private fun syncClientStateFromJs(
|
|
354
|
+
deviceToken: String?,
|
|
295
355
|
sourceId: String?,
|
|
296
|
-
|
|
356
|
+
didChangeClient: Boolean,
|
|
357
|
+
didChangeDeviceToken: Boolean,
|
|
297
358
|
promise: Promise
|
|
298
359
|
) {
|
|
299
360
|
if (!Clerk.isInitialized.value) {
|
|
@@ -304,25 +365,21 @@ class ClerkExpoModule : Module() {
|
|
|
304
365
|
coroutineScope.launch {
|
|
305
366
|
try {
|
|
306
367
|
jsOriginatedClientSyncDepth += 1
|
|
307
|
-
|
|
368
|
+
val previousClientState = clientStateSnapshot()
|
|
369
|
+
|
|
370
|
+
if (didChangeDeviceToken && !deviceToken.isNullOrBlank()) {
|
|
308
371
|
val currentDeviceToken = try {
|
|
309
372
|
Clerk.getDeviceToken()
|
|
310
373
|
} catch (_: Exception) {
|
|
311
374
|
null
|
|
312
375
|
}
|
|
313
376
|
|
|
314
|
-
if (currentDeviceToken
|
|
315
|
-
|
|
316
|
-
emitSyncedClientChanged(sourceId)
|
|
317
|
-
promise.resolve(null)
|
|
318
|
-
return@launch
|
|
319
|
-
}
|
|
320
|
-
} else {
|
|
321
|
-
when (val result = Clerk.updateDeviceToken(clientToken)) {
|
|
377
|
+
if (currentDeviceToken != deviceToken) {
|
|
378
|
+
when (val result = Clerk.updateDeviceToken(deviceToken)) {
|
|
322
379
|
is ClerkResult.Failure -> {
|
|
323
380
|
promise.reject(
|
|
324
381
|
"E_SYNC_FROM_JS_FAILED",
|
|
325
|
-
result.error?.firstMessage() ?: result.throwable?.message ?: "
|
|
382
|
+
result.error?.firstMessage() ?: result.throwable?.message ?: "Device token sync failed",
|
|
326
383
|
null
|
|
327
384
|
)
|
|
328
385
|
return@launch
|
|
@@ -333,19 +390,14 @@ class ClerkExpoModule : Module() {
|
|
|
333
390
|
Clerk.clientFlow.first { it != null }
|
|
334
391
|
}
|
|
335
392
|
} catch (_: TimeoutCancellationException) {
|
|
336
|
-
debugLog(TAG, "
|
|
337
|
-
}
|
|
338
|
-
if (!shouldRefreshClient) {
|
|
339
|
-
emitSyncedClientChanged(sourceId)
|
|
340
|
-
promise.resolve(null)
|
|
341
|
-
return@launch
|
|
393
|
+
debugLog(TAG, "syncClientStateFromJs - client did not appear after token update")
|
|
342
394
|
}
|
|
343
395
|
}
|
|
344
396
|
}
|
|
345
397
|
}
|
|
346
398
|
}
|
|
347
399
|
|
|
348
|
-
if (
|
|
400
|
+
if (didChangeClient || didChangeDeviceToken) {
|
|
349
401
|
when (val result = Clerk.refreshClient()) {
|
|
350
402
|
is ClerkResult.Failure -> {
|
|
351
403
|
promise.reject(
|
|
@@ -355,17 +407,33 @@ class ClerkExpoModule : Module() {
|
|
|
355
407
|
)
|
|
356
408
|
}
|
|
357
409
|
is ClerkResult.Success -> {
|
|
358
|
-
|
|
410
|
+
val newClientState = clientStateSnapshot()
|
|
411
|
+
emitSyncedClientChanged(
|
|
412
|
+
sourceId,
|
|
413
|
+
ClientStateChanges(
|
|
414
|
+
client = newClientState.client != previousClientState.client,
|
|
415
|
+
deviceToken = newClientState.deviceToken != previousClientState.deviceToken
|
|
416
|
+
),
|
|
417
|
+
newClientState
|
|
418
|
+
)
|
|
359
419
|
promise.resolve(null)
|
|
360
420
|
}
|
|
361
421
|
}
|
|
362
422
|
return@launch
|
|
363
423
|
}
|
|
364
424
|
|
|
365
|
-
|
|
425
|
+
val newClientState = clientStateSnapshot()
|
|
426
|
+
emitSyncedClientChanged(
|
|
427
|
+
sourceId,
|
|
428
|
+
ClientStateChanges(
|
|
429
|
+
client = newClientState.client != previousClientState.client,
|
|
430
|
+
deviceToken = newClientState.deviceToken != previousClientState.deviceToken
|
|
431
|
+
),
|
|
432
|
+
newClientState
|
|
433
|
+
)
|
|
366
434
|
promise.resolve(null)
|
|
367
435
|
} catch (e: Exception) {
|
|
368
|
-
promise.reject("E_SYNC_FROM_JS_FAILED", e.message ?: "Client
|
|
436
|
+
promise.reject("E_SYNC_FROM_JS_FAILED", e.message ?: "Client state sync failed", e)
|
|
369
437
|
} finally {
|
|
370
438
|
jsOriginatedClientSyncDepth = maxOf(0, jsOriginatedClientSyncDepth - 1)
|
|
371
439
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNativeClientEvents.d.ts","sourceRoot":"","sources":["../../src/hooks/useNativeClientEvents.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,oBAAoB;IACnC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"useNativeClientEvents.d.ts","sourceRoot":"","sources":["../../src/hooks/useNativeClientEvents.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE;QACP,MAAM,EAAE,OAAO,CAAC;QAChB,WAAW,EAAE,OAAO,CAAC;KACtB,CAAC;IACF,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,oBAAoB;IAC7D,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,2BAA2B;IACnC,iBAAiB,EAAE,iBAAiB,GAAG,IAAI,CAAC;CAC7C;AAiCD;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,2BAA2B,CAsCnE"}
|
|
@@ -10,6 +10,9 @@ function getNativeClientEventEmitter() {
|
|
|
10
10
|
if (src_utils_native_module.ClerkExpoModule && typeof src_utils_native_module.ClerkExpoModule.addListener === "function") return src_utils_native_module.ClerkExpoModule;
|
|
11
11
|
return null;
|
|
12
12
|
}
|
|
13
|
+
function isNativeClientSnapshot(snapshot) {
|
|
14
|
+
return typeof snapshot?.changed?.client === "boolean" && typeof snapshot.changed.deviceToken === "boolean" && (typeof snapshot.deviceToken === "string" || snapshot.deviceToken === null);
|
|
15
|
+
}
|
|
13
16
|
/**
|
|
14
17
|
* Listens for native client events that should sync JS client state.
|
|
15
18
|
*/
|
|
@@ -22,6 +25,7 @@ function useNativeClientEvents() {
|
|
|
22
25
|
const eventEmitter = getNativeClientEventEmitter();
|
|
23
26
|
if (!eventEmitter) return;
|
|
24
27
|
subscription = eventEmitter.addListener(nativeClientChangedEvent, (snapshot) => {
|
|
28
|
+
if (!isNativeClientSnapshot(snapshot)) return;
|
|
25
29
|
setNativeClientEvent({
|
|
26
30
|
issuedAt: Date.now(),
|
|
27
31
|
...snapshot
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNativeClientEvents.js","names":["Platform","DeviceEventEmitter","ClerkExpo","isNativeSupported"],"sources":["../../src/hooks/useNativeClientEvents.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { DeviceEventEmitter, Platform } from 'react-native';\n\nimport { ClerkExpoModule as ClerkExpo, isNativeSupported } from '../utils/native-module';\n\nconst nativeClientChangedEvent = 'clerkNativeClientChanged';\n\nexport interface NativeClientSnapshot {\n
|
|
1
|
+
{"version":3,"file":"useNativeClientEvents.js","names":["Platform","DeviceEventEmitter","ClerkExpo","isNativeSupported"],"sources":["../../src/hooks/useNativeClientEvents.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { DeviceEventEmitter, Platform } from 'react-native';\n\nimport { ClerkExpoModule as ClerkExpo, isNativeSupported } from '../utils/native-module';\n\nconst nativeClientChangedEvent = 'clerkNativeClientChanged';\n\nexport interface NativeClientSnapshot {\n changed: {\n client: boolean;\n deviceToken: boolean;\n };\n deviceToken: string | null;\n sourceId?: string | null;\n}\n\n/**\n * Local marker for a native client event.\n */\nexport interface NativeClientEvent extends NativeClientSnapshot {\n issuedAt: number;\n}\n\ninterface UseNativeClientEventsReturn {\n nativeClientEvent: NativeClientEvent | null;\n}\n\ntype RefreshClientEventSubscription = {\n remove: () => void;\n};\n\ntype RefreshClientEventEmitter = {\n addListener: (\n eventName: typeof nativeClientChangedEvent,\n listener: (snapshot?: NativeClientSnapshot) => void,\n ) => RefreshClientEventSubscription;\n};\n\nfunction getNativeClientEventEmitter(): RefreshClientEventEmitter | null {\n if (Platform.OS === 'ios') {\n return DeviceEventEmitter;\n }\n\n if (ClerkExpo && typeof ClerkExpo.addListener === 'function') {\n return ClerkExpo as RefreshClientEventEmitter;\n }\n\n return null;\n}\n\nfunction isNativeClientSnapshot(snapshot: NativeClientSnapshot | undefined): snapshot is NativeClientSnapshot {\n return (\n typeof snapshot?.changed?.client === 'boolean' &&\n typeof snapshot.changed.deviceToken === 'boolean' &&\n (typeof snapshot.deviceToken === 'string' || snapshot.deviceToken === null)\n );\n}\n\n/**\n * Listens for native client events that should sync JS client state.\n */\nexport function useNativeClientEvents(): UseNativeClientEventsReturn {\n const [nativeClientEvent, setNativeClientEvent] = useState<NativeClientEvent | null>(null);\n\n useEffect(() => {\n if (!isNativeSupported || !ClerkExpo) {\n return;\n }\n\n let subscription: { remove: () => void } | null = null;\n\n try {\n const eventEmitter = getNativeClientEventEmitter();\n\n if (!eventEmitter) {\n return;\n }\n\n subscription = eventEmitter.addListener(nativeClientChangedEvent, snapshot => {\n if (!isNativeClientSnapshot(snapshot)) {\n return;\n }\n\n setNativeClientEvent({ issuedAt: Date.now(), ...snapshot });\n });\n } catch (error) {\n if (__DEV__) {\n console.error('[useNativeClientEvents] Failed to set up event listener:', error);\n }\n }\n\n return () => {\n subscription?.remove();\n };\n }, []);\n\n return {\n nativeClientEvent,\n };\n}\n"],"mappings":";;;;;;AAKA,MAAM,2BAA2B;AAiCjC,SAAS,8BAAgE;CACvE,IAAIA,sBAAS,OAAO,OAClB,OAAOC;CAGT,IAAIC,2CAAa,OAAOA,wCAAU,gBAAgB,YAChD,OAAOA;CAGT,OAAO;AACT;AAEA,SAAS,uBAAuB,UAA8E;CAC5G,OACE,OAAO,UAAU,SAAS,WAAW,aACrC,OAAO,SAAS,QAAQ,gBAAgB,cACvC,OAAO,SAAS,gBAAgB,YAAY,SAAS,gBAAgB;AAE1E;;;;AAKA,SAAgB,wBAAqD;CACnE,MAAM,CAAC,mBAAmB,4CAA2D,IAAI;CAEzF,2BAAgB;EACd,IAAI,CAACC,6CAAqB,CAACD,yCACzB;EAGF,IAAI,eAA8C;EAElD,IAAI;GACF,MAAM,eAAe,4BAA4B;GAEjD,IAAI,CAAC,cACH;GAGF,eAAe,aAAa,YAAY,2BAA0B,aAAY;IAC5E,IAAI,CAAC,uBAAuB,QAAQ,GAClC;IAGF,qBAAqB;KAAE,UAAU,KAAK,IAAI;KAAG,GAAG;IAAS,CAAC;GAC5D,CAAC;EACH,SAAS,OAAO,CAIhB;EAEA,aAAa;GACX,cAAc,OAAO;EACvB;CACF,GAAG,CAAC,CAAC;CAEL,OAAO,EACL,kBACF;AACF"}
|
|
@@ -11,13 +11,13 @@ let src_provider_nativeClientSync = require("./nativeClientSync");
|
|
|
11
11
|
//#region src/provider/ClerkProvider.tsx
|
|
12
12
|
const SDK_METADATA = {
|
|
13
13
|
name: "@clerk/expo",
|
|
14
|
-
version: "3.5.0
|
|
14
|
+
version: "3.5.0"
|
|
15
15
|
};
|
|
16
16
|
function ClerkProvider(props) {
|
|
17
17
|
const { children, tokenCache, publishableKey, proxyUrl, domain, __experimental_passkeys, experimental, __experimental_resourceCache, ...rest } = props;
|
|
18
18
|
const pk = publishableKey;
|
|
19
19
|
const tokenCacheListenersRef = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
20
|
-
const suppressTokenCacheNotificationsRef = (0, react.useRef)(
|
|
20
|
+
const suppressTokenCacheNotificationsRef = (0, react.useRef)(0);
|
|
21
21
|
const nativeRefreshFromJsControllerRef = (0, react.useRef)(null);
|
|
22
22
|
const syncableTokenCache = (0, src_provider_nativeClientSync.useSyncableTokenCache)({
|
|
23
23
|
suppressTokenCacheNotificationsRef,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ClerkProvider.js","names":["ClerkReactProvider","NativeClientSync"],"sources":["../../src/provider/ClerkProvider.tsx"],"sourcesContent":["import '../polyfills';\n\nimport type { ClerkProviderProps as ReactClerkProviderProps } from '@clerk/react';\nimport { InternalClerkProvider as ClerkReactProvider, type Ui } from '@clerk/react/internal';\nimport { useRef } from 'react';\n\nimport type { TokenCache } from '../cache/types';\nimport { isNative, isWeb } from '../utils/runtime';\nimport { maybeCompleteAuthSession } from './maybeCompleteAuthSession';\nimport {\n type
|
|
1
|
+
{"version":3,"file":"ClerkProvider.js","names":["ClerkReactProvider","NativeClientSync"],"sources":["../../src/provider/ClerkProvider.tsx"],"sourcesContent":["import '../polyfills';\n\nimport type { ClerkProviderProps as ReactClerkProviderProps } from '@clerk/react';\nimport { InternalClerkProvider as ClerkReactProvider, type Ui } from '@clerk/react/internal';\nimport { useRef } from 'react';\n\nimport type { TokenCache } from '../cache/types';\nimport { isNative, isWeb } from '../utils/runtime';\nimport { maybeCompleteAuthSession } from './maybeCompleteAuthSession';\nimport {\n type DeviceTokenCacheListener,\n NativeClientSync,\n type NativeRefreshFromJsController,\n useNativeClientBootstrap,\n useNativeClientEventSync,\n useSyncableTokenCache,\n} from './nativeClientSync';\nimport { getClerkInstance } from './singleton';\nimport type { BuildClerkOptions } from './singleton/types';\n\nexport type ClerkProviderProps<TUi extends Ui = Ui> = Omit<ReactClerkProviderProps<TUi>, 'publishableKey'> & {\n /**\n * Your Clerk publishable key, available in the Clerk Dashboard.\n * This is required for React Native / Expo apps. Environment variables inside node_modules\n * are not inlined during production builds, so the key must be passed explicitly.\n *\n * @example\n * ```tsx\n * const publishableKey = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY!;\n * <ClerkProvider publishableKey={publishableKey}>\n * ```\n */\n publishableKey: string;\n /**\n * The token cache is used to persist the active user's session token. Clerk stores this token in memory by default, however it is recommended to use a token cache for production applications.\n * @see https://clerk.com/docs/quickstarts/expo#configure-the-token-cache-with-expo\n */\n tokenCache?: TokenCache;\n /**\n * Note: Passkey support in Expo is currently in a limited rollout phase.\n * If you're interested in using this feature, please contact us for early access or additional details.\n *\n * @experimental This API is experimental and may change at any moment.\n */\n __experimental_passkeys?: BuildClerkOptions['__experimental_passkeys'];\n /**\n * This cache is used to store the resources that Clerk fetches from the server when the network is offline.\n *\n * @experimental This API is experimental and may change at any moment.\n */\n __experimental_resourceCache?: BuildClerkOptions['__experimental_resourceCache'];\n};\n\nconst SDK_METADATA = {\n name: PACKAGE_NAME,\n version: PACKAGE_VERSION,\n};\n\nexport function ClerkProvider<TUi extends Ui = Ui>(props: ClerkProviderProps<TUi>): JSX.Element {\n const {\n children,\n tokenCache,\n publishableKey,\n proxyUrl,\n domain,\n __experimental_passkeys,\n experimental,\n __experimental_resourceCache,\n ...rest\n } = props;\n const pk = publishableKey;\n const tokenCacheListenersRef = useRef<Set<DeviceTokenCacheListener>>(new Set());\n const suppressTokenCacheNotificationsRef = useRef(0);\n const nativeRefreshFromJsControllerRef = useRef<NativeRefreshFromJsController | null>(null);\n const syncableTokenCache = useSyncableTokenCache({\n suppressTokenCacheNotificationsRef,\n tokenCache,\n tokenCacheListenersRef,\n });\n\n const clerkInstance = isNative()\n ? getClerkInstance({\n publishableKey: pk,\n tokenCache: syncableTokenCache,\n proxyUrl,\n domain,\n __experimental_passkeys,\n __experimental_resourceCache,\n })\n : null;\n\n const suppressJsClientChangedRef = useRef(0);\n const isMountedRef = useNativeClientBootstrap({\n publishableKey: pk,\n suppressTokenCacheNotificationsRef,\n tokenCache: syncableTokenCache,\n clerkInstance,\n });\n useNativeClientEventSync({\n clerkInstance,\n isMountedRef,\n nativeRefreshFromJsControllerRef,\n suppressJsClientChangedRef,\n suppressTokenCacheNotificationsRef,\n tokenCache: syncableTokenCache,\n });\n\n // Needed for `useOAuth` / `useSSO` to work correctly on web — must stay synchronous during render\n // so the redirect URL is caught before children mount. Resolves to a no-op on native via the\n // sibling `maybeCompleteAuthSession.ts`, which keeps Metro from statically bundling\n // `expo-web-browser` (an optional peer) for native consumers.\n if (isWeb()) {\n maybeCompleteAuthSession();\n }\n\n return (\n <ClerkReactProvider\n // Force reset the state when the provided key changes, this ensures that the provider does not retain stale state.\n // See JS-598 for additional context.\n key={pk}\n {...rest}\n publishableKey={pk}\n proxyUrl={proxyUrl}\n domain={domain}\n sdkMetadata={SDK_METADATA}\n Clerk={clerkInstance}\n standardBrowser={!isNative()}\n experimental={{\n ...experimental,\n // force the rethrowOfflineNetworkErrors flag to true if the asyncStorage is provided\n rethrowOfflineNetworkErrors: !!__experimental_resourceCache || experimental?.rethrowOfflineNetworkErrors,\n ...(isNative() && { runtimeEnvironment: 'headless' as const }),\n }}\n >\n {isNative() && (\n <NativeClientSync\n clerkInstance={clerkInstance}\n nativeRefreshFromJsControllerRef={nativeRefreshFromJsControllerRef}\n suppressJsClientChangedRef={suppressJsClientChangedRef}\n suppressTokenCacheNotificationsRef={suppressTokenCacheNotificationsRef}\n tokenCache={syncableTokenCache}\n tokenCacheListenersRef={tokenCacheListenersRef}\n />\n )}\n {children}\n </ClerkReactProvider>\n );\n}\n"],"mappings":";;;;;;;;;;;AAqDA,MAAM,eAAe;CACnB;CACA;AACF;AAEA,SAAgB,cAAmC,OAA6C;CAC9F,MAAM,EACJ,UACA,YACA,gBACA,UACA,QACA,yBACA,cACA,8BACA,GAAG,SACD;CACJ,MAAM,KAAK;CACX,MAAM,2DAA+D,IAAI,IAAI,CAAC;CAC9E,MAAM,uDAA4C,CAAC;CACnD,MAAM,qDAAgF,IAAI;CAC1F,MAAM,8EAA2C;EAC/C;EACA;EACA;CACF,CAAC;CAED,MAAM,gDAAyB,iDACV;EACf,gBAAgB;EAChB,YAAY;EACZ;EACA;EACA;EACA;CACF,CAAC,IACD;CAEJ,MAAM,+CAAoC,CAAC;CAO3C,4DAAyB;EACvB;EACA,0EAR4C;GAC5C,gBAAgB;GAChB;GACA,YAAY;GACZ;EACF,CAGa;EACX;EACA;EACA;EACA,YAAY;CACd,CAAC;CAMD,iCAAU,GACR,oEAAyB;CAG3B,OACE,4CAACA,6CAAD;EAIE,GAAI;EACJ,gBAAgB;EACN;EACF;EACR,aAAa;EACb,OAAO;EACP,iBAAiB,iCAAU;EAC3B,cAAc;GACZ,GAAG;GAEH,6BAA6B,CAAC,CAAC,gCAAgC,cAAc;GAC7E,mCAAa,KAAK,EAAE,oBAAoB,WAAoB;EAC9D;YAhBF,iCAkBY,KACR,2CAACC,gDAAD;GACiB;GACmB;GACN;GACQ;GACpC,YAAY;GACY;EACzB,IAEF,QACiB;IA1Bb,EA0Ba;AAExB"}
|
|
@@ -23,11 +23,11 @@ export type SyncableClerkInstance = {
|
|
|
23
23
|
export type NativeRefreshFromJsController = {
|
|
24
24
|
cancel: () => void;
|
|
25
25
|
};
|
|
26
|
-
export type
|
|
26
|
+
export type DeviceTokenCacheListener = (deviceToken: string | null) => void;
|
|
27
27
|
export declare function useSyncableTokenCache({ suppressTokenCacheNotificationsRef, tokenCache, tokenCacheListenersRef, }: {
|
|
28
|
-
suppressTokenCacheNotificationsRef: MutableRefObject<
|
|
28
|
+
suppressTokenCacheNotificationsRef: MutableRefObject<number>;
|
|
29
29
|
tokenCache: TokenCache | undefined;
|
|
30
|
-
tokenCacheListenersRef: MutableRefObject<Set<
|
|
30
|
+
tokenCacheListenersRef: MutableRefObject<Set<DeviceTokenCacheListener>>;
|
|
31
31
|
}): TokenCache | undefined;
|
|
32
32
|
/**
|
|
33
33
|
* Syncs JS SDK client changes to the native Clerk SDK so native components
|
|
@@ -40,13 +40,13 @@ export declare function NativeClientSync({ clerkInstance, nativeRefreshFromJsCon
|
|
|
40
40
|
clerkInstance: SyncableClerkInstance | null | undefined;
|
|
41
41
|
nativeRefreshFromJsControllerRef: MutableRefObject<NativeRefreshFromJsController | null>;
|
|
42
42
|
suppressJsClientChangedRef: MutableRefObject<number>;
|
|
43
|
-
suppressTokenCacheNotificationsRef: MutableRefObject<
|
|
43
|
+
suppressTokenCacheNotificationsRef: MutableRefObject<number>;
|
|
44
44
|
tokenCache: TokenCache | undefined;
|
|
45
|
-
tokenCacheListenersRef: MutableRefObject<Set<
|
|
45
|
+
tokenCacheListenersRef: MutableRefObject<Set<DeviceTokenCacheListener>>;
|
|
46
46
|
}): null;
|
|
47
47
|
export declare function useNativeClientBootstrap({ publishableKey, suppressTokenCacheNotificationsRef, tokenCache, clerkInstance, }: {
|
|
48
48
|
publishableKey: string;
|
|
49
|
-
suppressTokenCacheNotificationsRef: MutableRefObject<
|
|
49
|
+
suppressTokenCacheNotificationsRef: MutableRefObject<number>;
|
|
50
50
|
tokenCache: TokenCache | undefined;
|
|
51
51
|
clerkInstance: SyncableClerkInstance | null | undefined;
|
|
52
52
|
}): MutableRefObject<boolean>;
|
|
@@ -55,7 +55,7 @@ export declare function useNativeClientEventSync({ clerkInstance, isMountedRef,
|
|
|
55
55
|
isMountedRef: MutableRefObject<boolean>;
|
|
56
56
|
nativeRefreshFromJsControllerRef: MutableRefObject<NativeRefreshFromJsController | null>;
|
|
57
57
|
suppressJsClientChangedRef: MutableRefObject<number>;
|
|
58
|
-
suppressTokenCacheNotificationsRef: MutableRefObject<
|
|
58
|
+
suppressTokenCacheNotificationsRef: MutableRefObject<number>;
|
|
59
59
|
tokenCache: TokenCache | undefined;
|
|
60
60
|
}): void;
|
|
61
61
|
//# sourceMappingURL=nativeClientSync.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nativeClientSync.d.ts","sourceRoot":"","sources":["../../src/provider/nativeClientSync.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AACnF,OAAO,EAAE,KAAK,gBAAgB,EAA2C,MAAM,OAAO,CAAC;AAIvF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"nativeClientSync.d.ts","sourceRoot":"","sources":["../../src/provider/nativeClientSync.tsx"],"names":[],"mappings":"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"}
|
|
@@ -8,8 +8,8 @@ let src_hooks_useNativeClientEvents = require("../hooks/useNativeClientEvents");
|
|
|
8
8
|
|
|
9
9
|
//#region src/provider/nativeClientSync.tsx
|
|
10
10
|
const tokenCacheReadTimeoutMs = 1e3;
|
|
11
|
-
const
|
|
12
|
-
const
|
|
11
|
+
const nativeDeviceTokenPollIntervalMs = 100;
|
|
12
|
+
const nativeDeviceTokenAvailabilityTimeoutMs = 3e3;
|
|
13
13
|
const nativeClientSyncSourceIdPrefix = "clerk-expo-js-sync";
|
|
14
14
|
function delay(ms) {
|
|
15
15
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -18,19 +18,19 @@ function useSyncableTokenCache({ suppressTokenCacheNotificationsRef, tokenCache,
|
|
|
18
18
|
return (0, react.useMemo)(() => {
|
|
19
19
|
const effectiveTokenCache = tokenCache ?? (react_native.Platform.OS === "ios" || react_native.Platform.OS === "android" ? src_cache.MemoryTokenCache : void 0);
|
|
20
20
|
if (!effectiveTokenCache) return;
|
|
21
|
-
const
|
|
22
|
-
if (suppressTokenCacheNotificationsRef.current) return;
|
|
23
|
-
for (const listener of tokenCacheListenersRef.current) listener(
|
|
21
|
+
const notifyDeviceTokenListeners = (deviceToken) => {
|
|
22
|
+
if (suppressTokenCacheNotificationsRef.current > 0) return;
|
|
23
|
+
for (const listener of tokenCacheListenersRef.current) listener(deviceToken);
|
|
24
24
|
};
|
|
25
25
|
return {
|
|
26
26
|
getToken: (key) => effectiveTokenCache.getToken(key),
|
|
27
27
|
saveToken: async (key, token) => {
|
|
28
28
|
await effectiveTokenCache.saveToken(key, token);
|
|
29
|
-
if (key === src_constants.CLERK_CLIENT_JWT_KEY)
|
|
29
|
+
if (key === src_constants.CLERK_CLIENT_JWT_KEY) notifyDeviceTokenListeners(token);
|
|
30
30
|
},
|
|
31
31
|
clearToken: async (key) => {
|
|
32
32
|
await effectiveTokenCache.clearToken?.(key);
|
|
33
|
-
if (key === src_constants.CLERK_CLIENT_JWT_KEY)
|
|
33
|
+
if (key === src_constants.CLERK_CLIENT_JWT_KEY) notifyDeviceTokenListeners(null);
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
36
|
}, [
|
|
@@ -39,50 +39,46 @@ function useSyncableTokenCache({ suppressTokenCacheNotificationsRef, tokenCache,
|
|
|
39
39
|
tokenCacheListenersRef
|
|
40
40
|
]);
|
|
41
41
|
}
|
|
42
|
-
function
|
|
43
|
-
if (!snapshot || !("clientToken" in snapshot)) return;
|
|
44
|
-
return snapshot.clientToken ?? null;
|
|
45
|
-
}
|
|
46
|
-
async function readNativeClientToken({ waitForToken }) {
|
|
42
|
+
async function readNativeDeviceToken({ waitForToken }) {
|
|
47
43
|
const ClerkExpo = src_utils_native_module.ClerkExpoModule;
|
|
48
44
|
if (!ClerkExpo?.getClientToken) return null;
|
|
49
45
|
const startedAt = Date.now();
|
|
50
|
-
let remainingMs =
|
|
46
|
+
let remainingMs = nativeDeviceTokenAvailabilityTimeoutMs;
|
|
51
47
|
do {
|
|
52
|
-
const
|
|
53
|
-
if (
|
|
48
|
+
const nativeDeviceToken = await ClerkExpo.getClientToken();
|
|
49
|
+
if (nativeDeviceToken) return nativeDeviceToken;
|
|
54
50
|
if (!waitForToken) return null;
|
|
55
|
-
remainingMs =
|
|
51
|
+
remainingMs = nativeDeviceTokenAvailabilityTimeoutMs - (Date.now() - startedAt);
|
|
56
52
|
if (remainingMs <= 0) return null;
|
|
57
|
-
await delay(Math.min(
|
|
53
|
+
await delay(Math.min(nativeDeviceTokenPollIntervalMs, remainingMs));
|
|
58
54
|
} while (remainingMs > 0);
|
|
59
55
|
return null;
|
|
60
56
|
}
|
|
61
|
-
async function
|
|
62
|
-
if (
|
|
63
|
-
await tokenCache?.saveToken(src_constants.CLERK_CLIENT_JWT_KEY,
|
|
57
|
+
async function syncDeviceTokenToCache(tokenCache, deviceToken) {
|
|
58
|
+
if (deviceToken) {
|
|
59
|
+
await tokenCache?.saveToken(src_constants.CLERK_CLIENT_JWT_KEY, deviceToken);
|
|
64
60
|
return;
|
|
65
61
|
}
|
|
66
62
|
await tokenCache?.clearToken?.(src_constants.CLERK_CLIENT_JWT_KEY);
|
|
67
63
|
}
|
|
68
|
-
async function
|
|
69
|
-
suppressTokenCacheNotificationsRef.current
|
|
64
|
+
async function syncDeviceTokenToCacheWithoutNotifying({ deviceToken, suppressTokenCacheNotificationsRef, tokenCache }) {
|
|
65
|
+
suppressTokenCacheNotificationsRef.current += 1;
|
|
70
66
|
try {
|
|
71
|
-
await
|
|
67
|
+
await syncDeviceTokenToCache(tokenCache, deviceToken);
|
|
72
68
|
} finally {
|
|
73
|
-
suppressTokenCacheNotificationsRef.current =
|
|
69
|
+
suppressTokenCacheNotificationsRef.current = Math.max(0, suppressTokenCacheNotificationsRef.current - 1);
|
|
74
70
|
}
|
|
75
71
|
}
|
|
76
|
-
async function
|
|
72
|
+
async function syncNativeDeviceTokenToCache({ deviceToken, suppressTokenCacheNotificationsRef, tokenCache }) {
|
|
77
73
|
if (suppressTokenCacheNotificationsRef) {
|
|
78
|
-
await
|
|
79
|
-
|
|
74
|
+
await syncDeviceTokenToCacheWithoutNotifying({
|
|
75
|
+
deviceToken,
|
|
80
76
|
suppressTokenCacheNotificationsRef,
|
|
81
77
|
tokenCache
|
|
82
78
|
});
|
|
83
79
|
return;
|
|
84
80
|
}
|
|
85
|
-
await
|
|
81
|
+
await syncDeviceTokenToCache(tokenCache, deviceToken);
|
|
86
82
|
}
|
|
87
83
|
function getDefaultSignedInSession(client) {
|
|
88
84
|
if (!client) return null;
|
|
@@ -92,21 +88,20 @@ function getDefaultSignedInSession(client) {
|
|
|
92
88
|
}
|
|
93
89
|
return client.signedInSessions[0] ?? null;
|
|
94
90
|
}
|
|
95
|
-
async function
|
|
91
|
+
async function refreshJsClientFromServer(clerkInstance) {
|
|
96
92
|
const client = clerkInstance.client;
|
|
97
93
|
if (typeof client?.fetch !== "function" || typeof clerkInstance.updateClient !== "function") return null;
|
|
98
94
|
const refreshedClient = await client.fetch({ fetchMaxTries: 1 });
|
|
99
|
-
|
|
100
|
-
clerkInstance.updateClient(refreshedClient, { __internal_dangerouslySkipEmit: Boolean(nextSession) });
|
|
95
|
+
clerkInstance.updateClient(refreshedClient);
|
|
101
96
|
return refreshedClient;
|
|
102
97
|
}
|
|
103
|
-
async function refreshJsClientFromNativeState({ clerkInstance,
|
|
104
|
-
await
|
|
105
|
-
|
|
98
|
+
async function refreshJsClientFromNativeState({ clerkInstance, nativeDeviceToken, reloadInitialResources, shouldSyncDeviceToken = true, suppressTokenCacheNotificationsRef, tokenCache }) {
|
|
99
|
+
if (shouldSyncDeviceToken) await syncNativeDeviceTokenToCache({
|
|
100
|
+
deviceToken: nativeDeviceToken,
|
|
106
101
|
suppressTokenCacheNotificationsRef,
|
|
107
102
|
tokenCache
|
|
108
103
|
});
|
|
109
|
-
if (await
|
|
104
|
+
if (await refreshJsClientFromServer(clerkInstance)) {
|
|
110
105
|
await reconcileJsActiveSessionFromClient({ clerkInstance });
|
|
111
106
|
return true;
|
|
112
107
|
}
|
|
@@ -117,9 +112,9 @@ async function refreshJsClientFromNativeState({ clerkInstance, nativeClientToken
|
|
|
117
112
|
}
|
|
118
113
|
return false;
|
|
119
114
|
}
|
|
120
|
-
async function reloadJsClientFromNativeState({ clerkInstance,
|
|
121
|
-
await
|
|
122
|
-
|
|
115
|
+
async function reloadJsClientFromNativeState({ clerkInstance, nativeDeviceToken, suppressTokenCacheNotificationsRef, tokenCache }) {
|
|
116
|
+
await syncNativeDeviceTokenToCache({
|
|
117
|
+
deviceToken: nativeDeviceToken,
|
|
123
118
|
suppressTokenCacheNotificationsRef,
|
|
124
119
|
tokenCache
|
|
125
120
|
});
|
|
@@ -127,13 +122,13 @@ async function reloadJsClientFromNativeState({ clerkInstance, nativeClientToken,
|
|
|
127
122
|
await reconcileJsActiveSessionFromClient({ clerkInstance });
|
|
128
123
|
return Boolean(getDefaultSignedInSession(clerkInstance.client));
|
|
129
124
|
}
|
|
130
|
-
async function
|
|
131
|
-
const
|
|
132
|
-
if (!
|
|
125
|
+
async function recoverJsClientFromNativeDeviceToken({ clerkInstance, error, suppressTokenCacheNotificationsRef, tokenCache }) {
|
|
126
|
+
const nativeDeviceToken = await readNativeDeviceToken({ waitForToken: false });
|
|
127
|
+
if (!nativeDeviceToken) return false;
|
|
133
128
|
try {
|
|
134
129
|
return await reloadJsClientFromNativeState({
|
|
135
130
|
clerkInstance,
|
|
136
|
-
|
|
131
|
+
nativeDeviceToken,
|
|
137
132
|
suppressTokenCacheNotificationsRef,
|
|
138
133
|
tokenCache
|
|
139
134
|
});
|
|
@@ -159,12 +154,15 @@ async function runWithSuppressedJsClientChanges(suppressJsClientChangedRef, task
|
|
|
159
154
|
}
|
|
160
155
|
function mergePendingNativeRefreshOptions(current, next) {
|
|
161
156
|
if (!current) return next;
|
|
162
|
-
const merged = {
|
|
163
|
-
|
|
164
|
-
|
|
157
|
+
const merged = {
|
|
158
|
+
didChangeClient: current.didChangeClient || next.didChangeClient,
|
|
159
|
+
didChangeDeviceToken: current.didChangeDeviceToken || next.didChangeDeviceToken
|
|
160
|
+
};
|
|
161
|
+
if ("deviceToken" in current) merged.deviceToken = current.deviceToken ?? null;
|
|
162
|
+
if ("deviceToken" in next) merged.deviceToken = next.deviceToken ?? null;
|
|
165
163
|
return merged;
|
|
166
164
|
}
|
|
167
|
-
async function
|
|
165
|
+
async function getCachedDeviceToken(tokenCache) {
|
|
168
166
|
if (!tokenCache) return null;
|
|
169
167
|
let timeoutId;
|
|
170
168
|
try {
|
|
@@ -176,15 +174,18 @@ async function getCachedClientToken(tokenCache) {
|
|
|
176
174
|
}
|
|
177
175
|
}
|
|
178
176
|
async function syncNativeClientToJs({ clerkInstance, nativeRefreshFromJsControllerRef, nativeClientEvent, suppressJsClientChangedRef, suppressTokenCacheNotificationsRef, tokenCache }) {
|
|
179
|
-
const
|
|
180
|
-
const
|
|
181
|
-
if (!
|
|
177
|
+
const didChangeClient = nativeClientEvent?.changed.client ?? true;
|
|
178
|
+
const didChangeDeviceToken = nativeClientEvent?.changed.deviceToken ?? true;
|
|
179
|
+
if (!didChangeClient && !didChangeDeviceToken) return;
|
|
180
|
+
const nativeDeviceToken = nativeClientEvent ? nativeClientEvent.deviceToken : await readNativeDeviceToken({ waitForToken: true });
|
|
181
|
+
if (!nativeDeviceToken && !nativeClientEvent) return;
|
|
182
182
|
await runWithSuppressedJsClientChanges(suppressJsClientChangedRef, async () => {
|
|
183
183
|
nativeRefreshFromJsControllerRef?.current?.cancel();
|
|
184
184
|
await refreshJsClientFromNativeState({
|
|
185
185
|
clerkInstance,
|
|
186
|
-
|
|
186
|
+
nativeDeviceToken,
|
|
187
187
|
reloadInitialResources: true,
|
|
188
|
+
shouldSyncDeviceToken: didChangeDeviceToken,
|
|
188
189
|
suppressTokenCacheNotificationsRef,
|
|
189
190
|
tokenCache
|
|
190
191
|
});
|
|
@@ -236,7 +237,11 @@ function NativeClientSync({ clerkInstance, nativeRefreshFromJsControllerRef, sup
|
|
|
236
237
|
});
|
|
237
238
|
return;
|
|
238
239
|
}
|
|
239
|
-
|
|
240
|
+
if (options) {
|
|
241
|
+
originalUpdateClient(newClient, options);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
originalUpdateClient(newClient);
|
|
240
245
|
};
|
|
241
246
|
clerkInstance.updateClient = updateClient;
|
|
242
247
|
return () => {
|
|
@@ -255,10 +260,10 @@ function NativeClientSync({ clerkInstance, nativeRefreshFromJsControllerRef, sup
|
|
|
255
260
|
const refreshNativeFromJsClient = async (options, generation) => {
|
|
256
261
|
const ClerkExpo = src_utils_native_module.ClerkExpoModule;
|
|
257
262
|
if (!ClerkExpo || generation !== nativeRefreshGenerationRef.current) return;
|
|
258
|
-
const
|
|
263
|
+
const deviceToken = options.didChangeDeviceToken ? options.deviceToken ?? null : null;
|
|
259
264
|
if (generation !== nativeRefreshGenerationRef.current) return;
|
|
260
265
|
const sourceId = `${nativeClientSyncSourceIdPrefix}-${generation}`;
|
|
261
|
-
await ClerkExpo.
|
|
266
|
+
await ClerkExpo.syncClientStateFromJs(deviceToken, sourceId, options.didChangeClient, options.didChangeDeviceToken);
|
|
262
267
|
};
|
|
263
268
|
let latestRunGeneration = initialGeneration;
|
|
264
269
|
(async () => {
|
|
@@ -270,7 +275,10 @@ function NativeClientSync({ clerkInstance, nativeRefreshFromJsControllerRef, sup
|
|
|
270
275
|
try {
|
|
271
276
|
await refreshNativeFromJsClient(pendingOptions, generation);
|
|
272
277
|
} catch (error) {}
|
|
273
|
-
pendingOptions = pendingNativeRefreshRef.current ?? {
|
|
278
|
+
pendingOptions = pendingNativeRefreshRef.current ?? {
|
|
279
|
+
didChangeClient: false,
|
|
280
|
+
didChangeDeviceToken: false
|
|
281
|
+
};
|
|
274
282
|
if (pendingNativeRefreshRef.current !== null) {
|
|
275
283
|
generation = nativeRefreshGenerationRef.current + 1;
|
|
276
284
|
nativeRefreshGenerationRef.current = generation;
|
|
@@ -281,10 +289,11 @@ function NativeClientSync({ clerkInstance, nativeRefreshFromJsControllerRef, sup
|
|
|
281
289
|
});
|
|
282
290
|
}, []);
|
|
283
291
|
(0, react.useEffect)(() => {
|
|
284
|
-
const listener = (
|
|
292
|
+
const listener = (deviceToken) => {
|
|
285
293
|
queueNativeRefreshFromJs({
|
|
286
|
-
|
|
287
|
-
|
|
294
|
+
deviceToken,
|
|
295
|
+
didChangeClient: false,
|
|
296
|
+
didChangeDeviceToken: true
|
|
288
297
|
});
|
|
289
298
|
};
|
|
290
299
|
const tokenCacheListeners = tokenCacheListenersRef.current;
|
|
@@ -305,13 +314,13 @@ function NativeClientSync({ clerkInstance, nativeRefreshFromJsControllerRef, sup
|
|
|
305
314
|
try {
|
|
306
315
|
if (await refreshJsClientFromNativeState({
|
|
307
316
|
clerkInstance,
|
|
308
|
-
|
|
317
|
+
nativeDeviceToken: await readNativeDeviceToken({ waitForToken: false }),
|
|
309
318
|
reloadInitialResources: false,
|
|
310
319
|
suppressTokenCacheNotificationsRef,
|
|
311
320
|
tokenCache
|
|
312
321
|
})) return;
|
|
313
322
|
} catch (error) {
|
|
314
|
-
if (await
|
|
323
|
+
if (await recoverJsClientFromNativeDeviceToken({
|
|
315
324
|
clerkInstance,
|
|
316
325
|
error,
|
|
317
326
|
suppressTokenCacheNotificationsRef,
|
|
@@ -338,7 +347,10 @@ function NativeClientSync({ clerkInstance, nativeRefreshFromJsControllerRef, sup
|
|
|
338
347
|
if (!clerkInstance || typeof clerkInstance.addListener !== "function") return;
|
|
339
348
|
const unsubscribe = clerkInstance.addListener(() => {
|
|
340
349
|
if (suppressJsClientChangedRef.current > 0) return;
|
|
341
|
-
queueNativeRefreshFromJs({
|
|
350
|
+
queueNativeRefreshFromJs({
|
|
351
|
+
didChangeClient: true,
|
|
352
|
+
didChangeDeviceToken: false
|
|
353
|
+
});
|
|
342
354
|
}, { skipInitialEmit: true });
|
|
343
355
|
return () => {
|
|
344
356
|
unsubscribe();
|
|
@@ -368,12 +380,12 @@ function useNativeClientBootstrap({ publishableKey, suppressTokenCacheNotificati
|
|
|
368
380
|
if (ClerkExpo?.configure) {
|
|
369
381
|
await ClerkExpo.configure(publishableKey, null);
|
|
370
382
|
if (!isMountedRef.current) return;
|
|
371
|
-
let
|
|
383
|
+
let cachedDeviceToken = null;
|
|
372
384
|
try {
|
|
373
|
-
|
|
385
|
+
cachedDeviceToken = await getCachedDeviceToken(tokenCache);
|
|
374
386
|
} catch (e) {}
|
|
375
|
-
if (
|
|
376
|
-
await ClerkExpo.configure(publishableKey,
|
|
387
|
+
if (cachedDeviceToken) {
|
|
388
|
+
await ClerkExpo.configure(publishableKey, cachedDeviceToken);
|
|
377
389
|
if (!isMountedRef.current) return;
|
|
378
390
|
}
|
|
379
391
|
if (clerkInstance) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nativeClientSync.js","names":["Platform","MemoryTokenCache","CLERK_CLIENT_JWT_KEY","NativeClerkModule"],"sources":["../../src/provider/nativeClientSync.tsx"],"sourcesContent":["import type { ClientResource, SignedInSessionResource } from '@clerk/shared/types';\nimport { type MutableRefObject, useCallback, useEffect, useMemo, useRef } from 'react';\nimport { Platform } from 'react-native';\n\nimport { MemoryTokenCache } from '../cache';\nimport type { TokenCache } from '../cache/types';\nimport { CLERK_CLIENT_JWT_KEY } from '../constants';\nimport {\n type NativeClientEvent,\n type NativeClientSnapshot,\n useNativeClientEvents,\n} from '../hooks/useNativeClientEvents';\nimport { ClerkExpoModule as NativeClerkModule } from '../utils/native-module';\n\nconst tokenCacheReadTimeoutMs = 1_000;\nconst clientTokenPollIntervalMs = 100;\nconst clientTokenAvailabilityTimeoutMs = 3_000;\nconst nativeClientSyncSourceIdPrefix = 'clerk-expo-js-sync';\n\nexport type SyncableClerkInstance = {\n addListener?: (listener: () => void, options?: { skipInitialEmit?: boolean }) => () => void;\n addOnLoaded?: (listener: () => void) => void;\n client?: ClientResource;\n handleUnauthenticated?: (options?: { broadcast?: boolean }) => Promise<unknown>;\n loaded?: boolean;\n session?: SignedInSessionResource | null;\n setActive?: (params: { session: SignedInSessionResource | string | null }) => Promise<void>;\n updateClient?: (client: ClientResource, options?: { __internal_dangerouslySkipEmit?: boolean }) => void;\n __internal_reloadInitialResources?: () => void | Promise<void>;\n};\n\ntype RefreshableClientResource = ClientResource & {\n fetch?: (options?: { fetchMaxTries?: number }) => Promise<ClientResource>;\n};\n\ntype NativeRefreshFromJsOptions = {\n clientToken?: string | null;\n shouldRefreshClient: boolean;\n};\n\nexport type NativeRefreshFromJsController = {\n cancel: () => void;\n};\n\nexport type ClientTokenCacheListener = (clientToken: string | null) => void;\n\nfunction delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nexport function useSyncableTokenCache({\n suppressTokenCacheNotificationsRef,\n tokenCache,\n tokenCacheListenersRef,\n}: {\n suppressTokenCacheNotificationsRef: MutableRefObject<boolean>;\n tokenCache: TokenCache | undefined;\n tokenCacheListenersRef: MutableRefObject<Set<ClientTokenCacheListener>>;\n}): TokenCache | undefined {\n return useMemo(() => {\n const effectiveTokenCache =\n tokenCache ?? (Platform.OS === 'ios' || Platform.OS === 'android' ? MemoryTokenCache : undefined);\n if (!effectiveTokenCache) {\n return undefined;\n }\n\n const notifyClientTokenListeners = (clientToken: string | null) => {\n if (suppressTokenCacheNotificationsRef.current) {\n return;\n }\n\n for (const listener of tokenCacheListenersRef.current) {\n listener(clientToken);\n }\n };\n\n return {\n getToken: key => effectiveTokenCache.getToken(key),\n saveToken: async (key, token) => {\n await effectiveTokenCache.saveToken(key, token);\n if (key === CLERK_CLIENT_JWT_KEY) {\n notifyClientTokenListeners(token);\n }\n },\n clearToken: async key => {\n await effectiveTokenCache.clearToken?.(key);\n if (key === CLERK_CLIENT_JWT_KEY) {\n notifyClientTokenListeners(null);\n }\n },\n };\n }, [suppressTokenCacheNotificationsRef, tokenCache, tokenCacheListenersRef]);\n}\n\nfunction getNativeClientTokenFromSnapshot(\n snapshot: NativeClientSnapshot | null | undefined,\n): string | null | undefined {\n if (!snapshot || !('clientToken' in snapshot)) {\n return undefined;\n }\n\n return snapshot.clientToken ?? null;\n}\n\nasync function readNativeClientToken({ waitForToken }: { waitForToken: boolean }): Promise<string | null> {\n const ClerkExpo = NativeClerkModule;\n if (!ClerkExpo?.getClientToken) {\n return null;\n }\n\n const startedAt = Date.now();\n let remainingMs = clientTokenAvailabilityTimeoutMs;\n\n do {\n const nativeClientToken = await ClerkExpo.getClientToken();\n if (nativeClientToken) {\n return nativeClientToken;\n }\n\n if (!waitForToken) {\n return null;\n }\n\n remainingMs = clientTokenAvailabilityTimeoutMs - (Date.now() - startedAt);\n if (remainingMs <= 0) {\n return null;\n }\n\n await delay(Math.min(clientTokenPollIntervalMs, remainingMs));\n } while (remainingMs > 0);\n\n return null;\n}\n\nasync function syncClientTokenToCache(tokenCache: TokenCache | undefined, clientToken: string | null): Promise<void> {\n if (clientToken) {\n await tokenCache?.saveToken(CLERK_CLIENT_JWT_KEY, clientToken);\n return;\n }\n\n await tokenCache?.clearToken?.(CLERK_CLIENT_JWT_KEY);\n}\n\nasync function syncClientTokenToCacheWithoutNotifying({\n clientToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n clientToken: string | null;\n suppressTokenCacheNotificationsRef: MutableRefObject<boolean>;\n tokenCache: TokenCache | undefined;\n}): Promise<void> {\n suppressTokenCacheNotificationsRef.current = true;\n try {\n await syncClientTokenToCache(tokenCache, clientToken);\n } finally {\n suppressTokenCacheNotificationsRef.current = false;\n }\n}\n\nasync function syncNativeTokenToCache({\n clientToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n clientToken: string | null;\n suppressTokenCacheNotificationsRef?: MutableRefObject<boolean>;\n tokenCache: TokenCache | undefined;\n}): Promise<void> {\n if (suppressTokenCacheNotificationsRef) {\n await syncClientTokenToCacheWithoutNotifying({\n clientToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n return;\n }\n\n await syncClientTokenToCache(tokenCache, clientToken);\n}\n\nfunction getDefaultSignedInSession(client: ClientResource | null | undefined): SignedInSessionResource | null {\n if (!client) {\n return null;\n }\n\n if (client.lastActiveSessionId) {\n const lastActiveSession = client.signedInSessions.find(session => session.id === client.lastActiveSessionId);\n if (lastActiveSession) {\n return lastActiveSession;\n }\n }\n\n return client.signedInSessions[0] ?? null;\n}\n\nasync function refreshJsClientWithoutEmitting(clerkInstance: SyncableClerkInstance): Promise<ClientResource | null> {\n const client = clerkInstance.client as RefreshableClientResource | undefined;\n\n if (typeof client?.fetch !== 'function' || typeof clerkInstance.updateClient !== 'function') {\n return null;\n }\n\n const refreshedClient = await client.fetch({ fetchMaxTries: 1 });\n const nextSession = getDefaultSignedInSession(refreshedClient);\n\n clerkInstance.updateClient(refreshedClient, {\n __internal_dangerouslySkipEmit: Boolean(nextSession),\n });\n\n return refreshedClient;\n}\n\nasync function refreshJsClientFromNativeState({\n clerkInstance,\n nativeClientToken,\n reloadInitialResources,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n clerkInstance: SyncableClerkInstance;\n nativeClientToken: string | null;\n reloadInitialResources: boolean;\n suppressTokenCacheNotificationsRef?: MutableRefObject<boolean>;\n tokenCache: TokenCache | undefined;\n}): Promise<boolean> {\n await syncNativeTokenToCache({\n clientToken: nativeClientToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n\n const refreshedClient = await refreshJsClientWithoutEmitting(clerkInstance);\n if (refreshedClient) {\n await reconcileJsActiveSessionFromClient({\n clerkInstance,\n });\n return true;\n }\n\n if (reloadInitialResources && typeof clerkInstance.__internal_reloadInitialResources === 'function') {\n await clerkInstance.__internal_reloadInitialResources();\n await reconcileJsActiveSessionFromClient({\n clerkInstance,\n });\n return Boolean(getDefaultSignedInSession(clerkInstance.client));\n }\n\n return false;\n}\n\nasync function reloadJsClientFromNativeState({\n clerkInstance,\n nativeClientToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n clerkInstance: SyncableClerkInstance;\n nativeClientToken: string;\n suppressTokenCacheNotificationsRef?: MutableRefObject<boolean>;\n tokenCache: TokenCache | undefined;\n}): Promise<boolean> {\n await syncNativeTokenToCache({\n clientToken: nativeClientToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n\n await clerkInstance.__internal_reloadInitialResources?.();\n await reconcileJsActiveSessionFromClient({\n clerkInstance,\n });\n return Boolean(getDefaultSignedInSession(clerkInstance.client));\n}\n\nasync function recoverJsClientFromNativeToken({\n clerkInstance,\n error,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n clerkInstance: SyncableClerkInstance;\n error: unknown;\n suppressTokenCacheNotificationsRef: MutableRefObject<boolean>;\n tokenCache: TokenCache | undefined;\n}): Promise<boolean> {\n const nativeClientToken = await readNativeClientToken({ waitForToken: false });\n if (!nativeClientToken) {\n return false;\n }\n\n if (__DEV__) {\n console.warn('[NativeClientSync] Failed to refresh JS client with native client token:', error);\n }\n\n try {\n return await reloadJsClientFromNativeState({\n clerkInstance,\n nativeClientToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n } catch (recoveryError) {\n if (__DEV__) {\n console.warn('[NativeClientSync] Failed to recover JS client after unauthenticated state:', recoveryError);\n }\n return false;\n }\n}\n\nasync function reconcileJsActiveSessionFromClient({\n clerkInstance,\n}: {\n clerkInstance: SyncableClerkInstance;\n}): Promise<void> {\n const fallbackSession = getDefaultSignedInSession(clerkInstance.client);\n if (!fallbackSession || typeof clerkInstance.setActive !== 'function') {\n return;\n }\n\n const currentSession = clerkInstance.session;\n const currentSessionStillExists = currentSession\n ? clerkInstance.client?.signedInSessions.some(session => session.id === currentSession.id)\n : false;\n\n if (currentSessionStillExists && currentSession?.id === fallbackSession.id) {\n return;\n }\n\n await clerkInstance.setActive({ session: fallbackSession });\n}\n\nasync function runWithSuppressedJsClientChanges<T>(\n suppressJsClientChangedRef: MutableRefObject<number> | undefined,\n task: () => Promise<T>,\n): Promise<T> {\n if (!suppressJsClientChangedRef) {\n return task();\n }\n\n suppressJsClientChangedRef.current += 1;\n try {\n return await task();\n } finally {\n suppressJsClientChangedRef.current = Math.max(0, suppressJsClientChangedRef.current - 1);\n }\n}\n\nfunction mergePendingNativeRefreshOptions(\n current: NativeRefreshFromJsOptions | null,\n next: NativeRefreshFromJsOptions,\n): NativeRefreshFromJsOptions {\n if (!current) {\n return next;\n }\n\n const merged: NativeRefreshFromJsOptions = {\n shouldRefreshClient: current.shouldRefreshClient || next.shouldRefreshClient,\n };\n\n if ('clientToken' in current) {\n merged.clientToken = current.clientToken ?? null;\n }\n\n if ('clientToken' in next) {\n merged.clientToken = next.clientToken ?? null;\n }\n\n return merged;\n}\n\nasync function getCachedClientToken(tokenCache: TokenCache | undefined): Promise<string | null> {\n if (!tokenCache) {\n return null;\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n return (\n (await Promise.race([\n tokenCache.getToken(CLERK_CLIENT_JWT_KEY),\n new Promise<null>(resolve => {\n timeoutId = setTimeout(() => resolve(null), tokenCacheReadTimeoutMs);\n }),\n ])) ?? null\n );\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n}\n\nasync function syncNativeClientToJs({\n clerkInstance,\n nativeRefreshFromJsControllerRef,\n nativeClientEvent,\n suppressJsClientChangedRef,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n clerkInstance: SyncableClerkInstance;\n nativeRefreshFromJsControllerRef?: MutableRefObject<NativeRefreshFromJsController | null>;\n nativeClientEvent?: NativeClientEvent | null;\n suppressJsClientChangedRef?: MutableRefObject<number>;\n suppressTokenCacheNotificationsRef?: MutableRefObject<boolean>;\n tokenCache: TokenCache | undefined;\n}): Promise<void> {\n const nativeClientTokenFromEvent = getNativeClientTokenFromSnapshot(nativeClientEvent);\n const nativeClientToken =\n nativeClientTokenFromEvent !== undefined\n ? nativeClientTokenFromEvent\n : await readNativeClientToken({\n waitForToken: !nativeClientEvent,\n });\n\n if (!nativeClientToken && !nativeClientEvent) {\n return;\n }\n\n await runWithSuppressedJsClientChanges(suppressJsClientChangedRef, async () => {\n nativeRefreshFromJsControllerRef?.current?.cancel();\n\n await refreshJsClientFromNativeState({\n clerkInstance,\n nativeClientToken,\n reloadInitialResources: true,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n });\n}\n\n/**\n * Syncs JS SDK client changes to the native Clerk SDK so native components\n * (UserButton, UserProfileView) stay in sync after JS-owned resource changes.\n *\n * Must be rendered inside `ClerkReactProvider` so the Clerk instance has loaded\n * resources to emit.\n */\nexport function NativeClientSync({\n clerkInstance,\n nativeRefreshFromJsControllerRef,\n suppressJsClientChangedRef,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n tokenCacheListenersRef,\n}: {\n clerkInstance: SyncableClerkInstance | null | undefined;\n nativeRefreshFromJsControllerRef: MutableRefObject<NativeRefreshFromJsController | null>;\n suppressJsClientChangedRef: MutableRefObject<number>;\n suppressTokenCacheNotificationsRef: MutableRefObject<boolean>;\n tokenCache: TokenCache | undefined;\n tokenCacheListenersRef: MutableRefObject<Set<ClientTokenCacheListener>>;\n}): null {\n const isRefreshingNativeFromJsRef = useRef(false);\n const pendingNativeRefreshRef = useRef<NativeRefreshFromJsOptions | null>(null);\n const nativeRefreshGenerationRef = useRef(0);\n\n const cancelNativeRefreshFromJs = useCallback(() => {\n pendingNativeRefreshRef.current = null;\n nativeRefreshGenerationRef.current += 1;\n isRefreshingNativeFromJsRef.current = false;\n }, []);\n\n useEffect(() => {\n nativeRefreshFromJsControllerRef.current = {\n cancel: cancelNativeRefreshFromJs,\n };\n\n return () => {\n if (nativeRefreshFromJsControllerRef.current?.cancel === cancelNativeRefreshFromJs) {\n nativeRefreshFromJsControllerRef.current = null;\n }\n };\n }, [cancelNativeRefreshFromJs, nativeRefreshFromJsControllerRef]);\n\n useEffect(() => {\n if (\n !clerkInstance ||\n typeof clerkInstance.updateClient !== 'function' ||\n typeof clerkInstance.setActive !== 'function'\n ) {\n return;\n }\n\n const originalUpdateClient = clerkInstance.updateClient.bind(clerkInstance);\n let isReconcilingRemovedActiveSession = false;\n\n const updateClient: SyncableClerkInstance['updateClient'] = (newClient, options) => {\n const currentSessionId = clerkInstance.session?.id;\n const fallbackSession = getDefaultSignedInSession(newClient);\n const currentSessionWasRemoved = currentSessionId\n ? !newClient.signedInSessions.some(session => session.id === currentSessionId)\n : false;\n const alreadyReconcilingRemovedActiveSession = isReconcilingRemovedActiveSession;\n\n if ((currentSessionWasRemoved || alreadyReconcilingRemovedActiveSession) && fallbackSession) {\n // Clerk JS briefly emits signed-out when the active session disappears,\n // even if the refreshed client still has another signed-in session.\n // Keep that transient state internal so native session switching does\n // not dismiss mounted native UI before setActive settles on JS.\n isReconcilingRemovedActiveSession = true;\n originalUpdateClient(newClient, { __internal_dangerouslySkipEmit: true });\n\n if (alreadyReconcilingRemovedActiveSession) {\n return;\n }\n\n void runWithSuppressedJsClientChanges(suppressJsClientChangedRef, async () => {\n try {\n await clerkInstance.setActive?.({ session: fallbackSession });\n } catch (error) {\n if (__DEV__) {\n console.warn('[NativeClientSync] Failed to set remaining active JS session:', error);\n }\n originalUpdateClient(newClient, options);\n } finally {\n isReconcilingRemovedActiveSession = false;\n }\n });\n return;\n }\n\n originalUpdateClient(newClient, options);\n };\n\n clerkInstance.updateClient = updateClient;\n\n return () => {\n if (clerkInstance.updateClient === updateClient) {\n clerkInstance.updateClient = originalUpdateClient;\n }\n };\n }, [clerkInstance, suppressJsClientChangedRef]);\n\n const queueNativeRefreshFromJs = useCallback((options: NativeRefreshFromJsOptions): void => {\n if (isRefreshingNativeFromJsRef.current) {\n pendingNativeRefreshRef.current = mergePendingNativeRefreshOptions(pendingNativeRefreshRef.current, options);\n nativeRefreshGenerationRef.current += 1;\n return;\n }\n\n const initialGeneration = nativeRefreshGenerationRef.current + 1;\n nativeRefreshGenerationRef.current = initialGeneration;\n isRefreshingNativeFromJsRef.current = true;\n\n const refreshNativeFromJsClient = async (\n options: NativeRefreshFromJsOptions,\n generation: number,\n ): Promise<void> => {\n const ClerkExpo = NativeClerkModule;\n if (!ClerkExpo || generation !== nativeRefreshGenerationRef.current) {\n return;\n }\n\n const bearerToken = 'clientToken' in options ? (options.clientToken ?? null) : null;\n if (generation !== nativeRefreshGenerationRef.current) {\n return;\n }\n\n const sourceId = `${nativeClientSyncSourceIdPrefix}-${generation}`;\n await ClerkExpo.syncFromJsClientToken(bearerToken, sourceId, options.shouldRefreshClient);\n };\n\n let latestRunGeneration = initialGeneration;\n\n void (async () => {\n let pendingOptions = options;\n let generation = initialGeneration;\n do {\n latestRunGeneration = generation;\n pendingNativeRefreshRef.current = null;\n try {\n await refreshNativeFromJsClient(pendingOptions, generation);\n } catch (error: unknown) {\n if (__DEV__) {\n console.warn('[NativeClientSync] Failed to refresh native client from JS client change:', error);\n }\n }\n pendingOptions = pendingNativeRefreshRef.current ?? { shouldRefreshClient: false };\n if (pendingNativeRefreshRef.current !== null) {\n generation = nativeRefreshGenerationRef.current + 1;\n nativeRefreshGenerationRef.current = generation;\n }\n } while (pendingNativeRefreshRef.current !== null);\n })().finally(() => {\n if (latestRunGeneration === nativeRefreshGenerationRef.current || pendingNativeRefreshRef.current === null) {\n isRefreshingNativeFromJsRef.current = false;\n }\n });\n }, []);\n\n useEffect(() => {\n const listener: ClientTokenCacheListener = clientToken => {\n queueNativeRefreshFromJs({ clientToken, shouldRefreshClient: clientToken === null });\n };\n const tokenCacheListeners = tokenCacheListenersRef.current;\n\n tokenCacheListeners.add(listener);\n return () => {\n tokenCacheListeners.delete(listener);\n };\n }, [queueNativeRefreshFromJs, tokenCacheListenersRef]);\n\n useEffect(() => {\n if (!clerkInstance || typeof clerkInstance.handleUnauthenticated !== 'function') {\n return;\n }\n\n const originalHandleUnauthenticated = clerkInstance.handleUnauthenticated.bind(clerkInstance);\n let isHandlingUnauthenticated = false;\n\n const handleUnauthenticated: SyncableClerkInstance['handleUnauthenticated'] = async options => {\n if (isHandlingUnauthenticated) {\n return;\n }\n\n isHandlingUnauthenticated = true;\n try {\n return await runWithSuppressedJsClientChanges(suppressJsClientChangedRef, async () => {\n try {\n const nativeClientToken = await readNativeClientToken({ waitForToken: false });\n // Native may have already moved the server-side client to a new\n // active session. Refresh JS before allowing Clerk JS' stale-session\n // 401 path to collapse the whole client to signed out.\n const didRecover = await refreshJsClientFromNativeState({\n clerkInstance,\n nativeClientToken,\n reloadInitialResources: false,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n if (didRecover) {\n return;\n }\n } catch (error) {\n const didRecover = await recoverJsClientFromNativeToken({\n clerkInstance,\n error,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n if (didRecover) {\n return;\n }\n }\n\n return originalHandleUnauthenticated(options);\n });\n } finally {\n isHandlingUnauthenticated = false;\n }\n };\n\n clerkInstance.handleUnauthenticated = handleUnauthenticated;\n\n return () => {\n if (clerkInstance.handleUnauthenticated === handleUnauthenticated) {\n clerkInstance.handleUnauthenticated = originalHandleUnauthenticated;\n }\n };\n }, [clerkInstance, suppressJsClientChangedRef, suppressTokenCacheNotificationsRef, tokenCache]);\n\n useEffect(() => {\n if (!clerkInstance || typeof clerkInstance.addListener !== 'function') {\n return;\n }\n\n const unsubscribe = clerkInstance.addListener(\n () => {\n if (suppressJsClientChangedRef.current > 0) {\n return;\n }\n\n queueNativeRefreshFromJs({ shouldRefreshClient: true });\n },\n { skipInitialEmit: true },\n );\n\n return () => {\n unsubscribe();\n };\n }, [clerkInstance, queueNativeRefreshFromJs, suppressJsClientChangedRef]);\n\n return null;\n}\n\nexport function useNativeClientBootstrap({\n publishableKey,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n clerkInstance,\n}: {\n publishableKey: string;\n suppressTokenCacheNotificationsRef: MutableRefObject<boolean>;\n tokenCache: TokenCache | undefined;\n clerkInstance: SyncableClerkInstance | null | undefined;\n}) {\n const initStartedRef = useRef(false);\n const nativeClientSyncedRef = useRef(false);\n const isMountedRef = useRef(true);\n\n useEffect(() => {\n initStartedRef.current = false;\n nativeClientSyncedRef.current = false;\n }, [publishableKey]);\n\n useEffect(() => {\n isMountedRef.current = true;\n\n if ((Platform.OS === 'ios' || Platform.OS === 'android') && publishableKey && !initStartedRef.current) {\n initStartedRef.current = true;\n\n const configureNativeClerk = async () => {\n try {\n const ClerkExpo = NativeClerkModule;\n\n if (ClerkExpo?.configure) {\n await ClerkExpo.configure(publishableKey, null);\n\n if (!isMountedRef.current) {\n return;\n }\n\n let bearerToken: string | null = null;\n try {\n bearerToken = await getCachedClientToken(tokenCache);\n } catch (e) {\n if (__DEV__) {\n console.warn('[ClerkProvider] Token cache read failed:', e);\n }\n }\n\n if (bearerToken) {\n await ClerkExpo.configure(publishableKey, bearerToken);\n\n if (!isMountedRef.current) {\n return;\n }\n }\n\n if (clerkInstance) {\n const waitForLoad = (): Promise<void> => {\n return new Promise(resolve => {\n if (clerkInstance.loaded) {\n resolve();\n } else if (typeof clerkInstance.addOnLoaded === 'function') {\n clerkInstance.addOnLoaded(() => resolve());\n } else {\n if (__DEV__) {\n console.warn('[ClerkProvider] Clerk instance has no loaded property or addOnLoaded method');\n }\n resolve();\n }\n });\n };\n\n await waitForLoad();\n\n if (!isMountedRef.current) {\n return;\n }\n\n if (!nativeClientSyncedRef.current) {\n nativeClientSyncedRef.current = true;\n await syncNativeClientToJs({\n clerkInstance,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n }\n }\n }\n } catch (error) {\n const isNativeModuleNotFound =\n error instanceof Error &&\n (error.message.includes('Cannot find native module') ||\n error.message.includes(\"TurboModuleRegistry.getEnforcing(...): 'ClerkExpo'\"));\n if (isNativeModuleNotFound) {\n if (__DEV__) {\n console.debug(\n `[ClerkProvider] Native Clerk module not available. ` +\n `To enable native features, add \"@clerk/expo\" to your app.json plugins array.`,\n );\n }\n } else if (__DEV__) {\n console.error(`[ClerkProvider] Failed to configure Clerk ${Platform.OS}:`, error);\n }\n }\n };\n void configureNativeClerk();\n }\n\n return () => {\n isMountedRef.current = false;\n };\n }, [publishableKey, suppressTokenCacheNotificationsRef, tokenCache, clerkInstance]);\n\n return isMountedRef;\n}\n\nexport function useNativeClientEventSync({\n clerkInstance,\n isMountedRef,\n nativeRefreshFromJsControllerRef,\n suppressJsClientChangedRef,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n clerkInstance: SyncableClerkInstance | null | undefined;\n isMountedRef: MutableRefObject<boolean>;\n nativeRefreshFromJsControllerRef: MutableRefObject<NativeRefreshFromJsController | null>;\n suppressJsClientChangedRef: MutableRefObject<number>;\n suppressTokenCacheNotificationsRef: MutableRefObject<boolean>;\n tokenCache: TokenCache | undefined;\n}) {\n const { nativeClientEvent } = useNativeClientEvents();\n\n useEffect(() => {\n if (!nativeClientEvent || !clerkInstance) {\n return;\n }\n\n if (nativeClientEvent.sourceId?.startsWith(nativeClientSyncSourceIdPrefix)) {\n return;\n }\n\n const syncNativeClientStateToJs = async () => {\n try {\n if (!isMountedRef.current) {\n return;\n }\n await syncNativeClientToJs({\n clerkInstance,\n nativeRefreshFromJsControllerRef,\n nativeClientEvent,\n suppressJsClientChangedRef,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n } catch (error) {\n console.error(`[ClerkProvider] Failed to sync native client state:`, error);\n }\n };\n\n void syncNativeClientStateToJs();\n }, [\n nativeClientEvent,\n clerkInstance,\n isMountedRef,\n nativeRefreshFromJsControllerRef,\n suppressJsClientChangedRef,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n ]);\n}\n"],"mappings":";;;;;;;;;AAcA,MAAM,0BAA0B;AAChC,MAAM,4BAA4B;AAClC,MAAM,mCAAmC;AACzC,MAAM,iCAAiC;AA6BvC,SAAS,MAAM,IAA2B;CACxC,OAAO,IAAI,SAAQ,YAAW,WAAW,SAAS,EAAE,CAAC;AACvD;AAEA,SAAgB,sBAAsB,EACpC,oCACA,YACA,0BAKyB;CACzB,gCAAqB;EACnB,MAAM,sBACJ,eAAeA,sBAAS,OAAO,SAASA,sBAAS,OAAO,YAAYC,6BAAmB;EACzF,IAAI,CAAC,qBACH;EAGF,MAAM,8BAA8B,gBAA+B;GACjE,IAAI,mCAAmC,SACrC;GAGF,KAAK,MAAM,YAAY,uBAAuB,SAC5C,SAAS,WAAW;EAExB;EAEA,OAAO;GACL,WAAU,QAAO,oBAAoB,SAAS,GAAG;GACjD,WAAW,OAAO,KAAK,UAAU;IAC/B,MAAM,oBAAoB,UAAU,KAAK,KAAK;IAC9C,IAAI,QAAQC,oCACV,2BAA2B,KAAK;GAEpC;GACA,YAAY,OAAM,QAAO;IACvB,MAAM,oBAAoB,aAAa,GAAG;IAC1C,IAAI,QAAQA,oCACV,2BAA2B,IAAI;GAEnC;EACF;CACF,GAAG;EAAC;EAAoC;EAAY;CAAsB,CAAC;AAC7E;AAEA,SAAS,iCACP,UAC2B;CAC3B,IAAI,CAAC,YAAY,EAAE,iBAAiB,WAClC;CAGF,OAAO,SAAS,eAAe;AACjC;AAEA,eAAe,sBAAsB,EAAE,gBAAmE;CACxG,MAAM,YAAYC;CAClB,IAAI,CAAC,WAAW,gBACd,OAAO;CAGT,MAAM,YAAY,KAAK,IAAI;CAC3B,IAAI,cAAc;CAElB,GAAG;EACD,MAAM,oBAAoB,MAAM,UAAU,eAAe;EACzD,IAAI,mBACF,OAAO;EAGT,IAAI,CAAC,cACH,OAAO;EAGT,cAAc,oCAAoC,KAAK,IAAI,IAAI;EAC/D,IAAI,eAAe,GACjB,OAAO;EAGT,MAAM,MAAM,KAAK,IAAI,2BAA2B,WAAW,CAAC;CAC9D,SAAS,cAAc;CAEvB,OAAO;AACT;AAEA,eAAe,uBAAuB,YAAoC,aAA2C;CACnH,IAAI,aAAa;EACf,MAAM,YAAY,UAAUD,oCAAsB,WAAW;EAC7D;CACF;CAEA,MAAM,YAAY,aAAaA,kCAAoB;AACrD;AAEA,eAAe,uCAAuC,EACpD,aACA,oCACA,cAKgB;CAChB,mCAAmC,UAAU;CAC7C,IAAI;EACF,MAAM,uBAAuB,YAAY,WAAW;CACtD,UAAU;EACR,mCAAmC,UAAU;CAC/C;AACF;AAEA,eAAe,uBAAuB,EACpC,aACA,oCACA,cAKgB;CAChB,IAAI,oCAAoC;EACtC,MAAM,uCAAuC;GAC3C;GACA;GACA;EACF,CAAC;EACD;CACF;CAEA,MAAM,uBAAuB,YAAY,WAAW;AACtD;AAEA,SAAS,0BAA0B,QAA2E;CAC5G,IAAI,CAAC,QACH,OAAO;CAGT,IAAI,OAAO,qBAAqB;EAC9B,MAAM,oBAAoB,OAAO,iBAAiB,MAAK,YAAW,QAAQ,OAAO,OAAO,mBAAmB;EAC3G,IAAI,mBACF,OAAO;CAEX;CAEA,OAAO,OAAO,iBAAiB,MAAM;AACvC;AAEA,eAAe,+BAA+B,eAAsE;CAClH,MAAM,SAAS,cAAc;CAE7B,IAAI,OAAO,QAAQ,UAAU,cAAc,OAAO,cAAc,iBAAiB,YAC/E,OAAO;CAGT,MAAM,kBAAkB,MAAM,OAAO,MAAM,EAAE,eAAe,EAAE,CAAC;CAC/D,MAAM,cAAc,0BAA0B,eAAe;CAE7D,cAAc,aAAa,iBAAiB,EAC1C,gCAAgC,QAAQ,WAAW,EACrD,CAAC;CAED,OAAO;AACT;AAEA,eAAe,+BAA+B,EAC5C,eACA,mBACA,wBACA,oCACA,cAOmB;CACnB,MAAM,uBAAuB;EAC3B,aAAa;EACb;EACA;CACF,CAAC;CAGD,IAAI,MAD0B,+BAA+B,aAAa,GACrD;EACnB,MAAM,mCAAmC,EACvC,cACF,CAAC;EACD,OAAO;CACT;CAEA,IAAI,0BAA0B,OAAO,cAAc,sCAAsC,YAAY;EACnG,MAAM,cAAc,kCAAkC;EACtD,MAAM,mCAAmC,EACvC,cACF,CAAC;EACD,OAAO,QAAQ,0BAA0B,cAAc,MAAM,CAAC;CAChE;CAEA,OAAO;AACT;AAEA,eAAe,8BAA8B,EAC3C,eACA,mBACA,oCACA,cAMmB;CACnB,MAAM,uBAAuB;EAC3B,aAAa;EACb;EACA;CACF,CAAC;CAED,MAAM,cAAc,oCAAoC;CACxD,MAAM,mCAAmC,EACvC,cACF,CAAC;CACD,OAAO,QAAQ,0BAA0B,cAAc,MAAM,CAAC;AAChE;AAEA,eAAe,+BAA+B,EAC5C,eACA,OACA,oCACA,cAMmB;CACnB,MAAM,oBAAoB,MAAM,sBAAsB,EAAE,cAAc,MAAM,CAAC;CAC7E,IAAI,CAAC,mBACH,OAAO;CAOT,IAAI;EACF,OAAO,MAAM,8BAA8B;GACzC;GACA;GACA;GACA;EACF,CAAC;CACH,SAAS,eAAe;EAItB,OAAO;CACT;AACF;AAEA,eAAe,mCAAmC,EAChD,iBAGgB;CAChB,MAAM,kBAAkB,0BAA0B,cAAc,MAAM;CACtE,IAAI,CAAC,mBAAmB,OAAO,cAAc,cAAc,YACzD;CAGF,MAAM,iBAAiB,cAAc;CAKrC,KAJkC,iBAC9B,cAAc,QAAQ,iBAAiB,MAAK,YAAW,QAAQ,OAAO,eAAe,EAAE,IACvF,UAE6B,gBAAgB,OAAO,gBAAgB,IACtE;CAGF,MAAM,cAAc,UAAU,EAAE,SAAS,gBAAgB,CAAC;AAC5D;AAEA,eAAe,iCACb,4BACA,MACY;CACZ,IAAI,CAAC,4BACH,OAAO,KAAK;CAGd,2BAA2B,WAAW;CACtC,IAAI;EACF,OAAO,MAAM,KAAK;CACpB,UAAU;EACR,2BAA2B,UAAU,KAAK,IAAI,GAAG,2BAA2B,UAAU,CAAC;CACzF;AACF;AAEA,SAAS,iCACP,SACA,MAC4B;CAC5B,IAAI,CAAC,SACH,OAAO;CAGT,MAAM,SAAqC,EACzC,qBAAqB,QAAQ,uBAAuB,KAAK,oBAC3D;CAEA,IAAI,iBAAiB,SACnB,OAAO,cAAc,QAAQ,eAAe;CAG9C,IAAI,iBAAiB,MACnB,OAAO,cAAc,KAAK,eAAe;CAG3C,OAAO;AACT;AAEA,eAAe,qBAAqB,YAA4D;CAC9F,IAAI,CAAC,YACH,OAAO;CAGT,IAAI;CACJ,IAAI;EACF,OACG,MAAM,QAAQ,KAAK,CAClB,WAAW,SAASA,kCAAoB,GACxC,IAAI,SAAc,YAAW;GAC3B,YAAY,iBAAiB,QAAQ,IAAI,GAAG,uBAAuB;EACrE,CAAC,CACH,CAAC,KAAM;CAEX,UAAU;EACR,IAAI,WACF,aAAa,SAAS;CAE1B;AACF;AAEA,eAAe,qBAAqB,EAClC,eACA,kCACA,mBACA,4BACA,oCACA,cAQgB;CAChB,MAAM,6BAA6B,iCAAiC,iBAAiB;CACrF,MAAM,oBACJ,+BAA+B,SAC3B,6BACA,MAAM,sBAAsB,EAC1B,cAAc,CAAC,kBACjB,CAAC;CAEP,IAAI,CAAC,qBAAqB,CAAC,mBACzB;CAGF,MAAM,iCAAiC,4BAA4B,YAAY;EAC7E,kCAAkC,SAAS,OAAO;EAElD,MAAM,+BAA+B;GACnC;GACA;GACA,wBAAwB;GACxB;GACA;EACF,CAAC;CACH,CAAC;AACH;;;;;;;;AASA,SAAgB,iBAAiB,EAC/B,eACA,kCACA,4BACA,oCACA,YACA,0BAQO;CACP,MAAM,gDAAqC,KAAK;CAChD,MAAM,4CAAoE,IAAI;CAC9E,MAAM,+CAAoC,CAAC;CAE3C,MAAM,yDAA8C;EAClD,wBAAwB,UAAU;EAClC,2BAA2B,WAAW;EACtC,4BAA4B,UAAU;CACxC,GAAG,CAAC,CAAC;CAEL,2BAAgB;EACd,iCAAiC,UAAU,EACzC,QAAQ,0BACV;EAEA,aAAa;GACX,IAAI,iCAAiC,SAAS,WAAW,2BACvD,iCAAiC,UAAU;EAE/C;CACF,GAAG,CAAC,2BAA2B,gCAAgC,CAAC;CAEhE,2BAAgB;EACd,IACE,CAAC,iBACD,OAAO,cAAc,iBAAiB,cACtC,OAAO,cAAc,cAAc,YAEnC;EAGF,MAAM,uBAAuB,cAAc,aAAa,KAAK,aAAa;EAC1E,IAAI,oCAAoC;EAExC,MAAM,gBAAuD,WAAW,YAAY;GAClF,MAAM,mBAAmB,cAAc,SAAS;GAChD,MAAM,kBAAkB,0BAA0B,SAAS;GAC3D,MAAM,2BAA2B,mBAC7B,CAAC,UAAU,iBAAiB,MAAK,YAAW,QAAQ,OAAO,gBAAgB,IAC3E;GACJ,MAAM,yCAAyC;GAE/C,KAAK,4BAA4B,2CAA2C,iBAAiB;IAK3F,oCAAoC;IACpC,qBAAqB,WAAW,EAAE,gCAAgC,KAAK,CAAC;IAExE,IAAI,wCACF;IAGF,AAAK,iCAAiC,4BAA4B,YAAY;KAC5E,IAAI;MACF,MAAM,cAAc,YAAY,EAAE,SAAS,gBAAgB,CAAC;KAC9D,SAAS,OAAO;MAId,qBAAqB,WAAW,OAAO;KACzC,UAAU;MACR,oCAAoC;KACtC;IACF,CAAC;IACD;GACF;GAEA,qBAAqB,WAAW,OAAO;EACzC;EAEA,cAAc,eAAe;EAE7B,aAAa;GACX,IAAI,cAAc,iBAAiB,cACjC,cAAc,eAAe;EAEjC;CACF,GAAG,CAAC,eAAe,0BAA0B,CAAC;CAE9C,MAAM,mDAAwC,YAA8C;EAC1F,IAAI,4BAA4B,SAAS;GACvC,wBAAwB,UAAU,iCAAiC,wBAAwB,SAAS,OAAO;GAC3G,2BAA2B,WAAW;GACtC;EACF;EAEA,MAAM,oBAAoB,2BAA2B,UAAU;EAC/D,2BAA2B,UAAU;EACrC,4BAA4B,UAAU;EAEtC,MAAM,4BAA4B,OAChC,SACA,eACkB;GAClB,MAAM,YAAYC;GAClB,IAAI,CAAC,aAAa,eAAe,2BAA2B,SAC1D;GAGF,MAAM,cAAc,iBAAiB,UAAW,QAAQ,eAAe,OAAQ;GAC/E,IAAI,eAAe,2BAA2B,SAC5C;GAGF,MAAM,WAAW,GAAG,+BAA+B,GAAG;GACtD,MAAM,UAAU,sBAAsB,aAAa,UAAU,QAAQ,mBAAmB;EAC1F;EAEA,IAAI,sBAAsB;EAE1B,CAAM,YAAY;GAChB,IAAI,iBAAiB;GACrB,IAAI,aAAa;GACjB,GAAG;IACD,sBAAsB;IACtB,wBAAwB,UAAU;IAClC,IAAI;KACF,MAAM,0BAA0B,gBAAgB,UAAU;IAC5D,SAAS,OAAgB,CAIzB;IACA,iBAAiB,wBAAwB,WAAW,EAAE,qBAAqB,MAAM;IACjF,IAAI,wBAAwB,YAAY,MAAM;KAC5C,aAAa,2BAA2B,UAAU;KAClD,2BAA2B,UAAU;IACvC;GACF,SAAS,wBAAwB,YAAY;EAC/C,EAAC,CAAE,CAAC,CAAC,cAAc;GACjB,IAAI,wBAAwB,2BAA2B,WAAW,wBAAwB,YAAY,MACpG,4BAA4B,UAAU;EAE1C,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,2BAAgB;EACd,MAAM,YAAqC,gBAAe;GACxD,yBAAyB;IAAE;IAAa,qBAAqB,gBAAgB;GAAK,CAAC;EACrF;EACA,MAAM,sBAAsB,uBAAuB;EAEnD,oBAAoB,IAAI,QAAQ;EAChC,aAAa;GACX,oBAAoB,OAAO,QAAQ;EACrC;CACF,GAAG,CAAC,0BAA0B,sBAAsB,CAAC;CAErD,2BAAgB;EACd,IAAI,CAAC,iBAAiB,OAAO,cAAc,0BAA0B,YACnE;EAGF,MAAM,gCAAgC,cAAc,sBAAsB,KAAK,aAAa;EAC5F,IAAI,4BAA4B;EAEhC,MAAM,wBAAwE,OAAM,YAAW;GAC7F,IAAI,2BACF;GAGF,4BAA4B;GAC5B,IAAI;IACF,OAAO,MAAM,iCAAiC,4BAA4B,YAAY;KACpF,IAAI;MAYF,IAAI,MAPqB,+BAA+B;OACtD;OACA,yBAN8B,sBAAsB,EAAE,cAAc,MAAM,CAAC;OAO3E,wBAAwB;OACxB;OACA;MACF,CAAC,GAEC;KAEJ,SAAS,OAAO;MAOd,IAAI,MANqB,+BAA+B;OACtD;OACA;OACA;OACA;MACF,CAAC,GAEC;KAEJ;KAEA,OAAO,8BAA8B,OAAO;IAC9C,CAAC;GACH,UAAU;IACR,4BAA4B;GAC9B;EACF;EAEA,cAAc,wBAAwB;EAEtC,aAAa;GACX,IAAI,cAAc,0BAA0B,uBAC1C,cAAc,wBAAwB;EAE1C;CACF,GAAG;EAAC;EAAe;EAA4B;EAAoC;CAAU,CAAC;CAE9F,2BAAgB;EACd,IAAI,CAAC,iBAAiB,OAAO,cAAc,gBAAgB,YACzD;EAGF,MAAM,cAAc,cAAc,kBAC1B;GACJ,IAAI,2BAA2B,UAAU,GACvC;GAGF,yBAAyB,EAAE,qBAAqB,KAAK,CAAC;EACxD,GACA,EAAE,iBAAiB,KAAK,CAC1B;EAEA,aAAa;GACX,YAAY;EACd;CACF,GAAG;EAAC;EAAe;EAA0B;CAA0B,CAAC;CAExE,OAAO;AACT;AAEA,SAAgB,yBAAyB,EACvC,gBACA,oCACA,YACA,iBAMC;CACD,MAAM,mCAAwB,KAAK;CACnC,MAAM,0CAA+B,KAAK;CAC1C,MAAM,iCAAsB,IAAI;CAEhC,2BAAgB;EACd,eAAe,UAAU;EACzB,sBAAsB,UAAU;CAClC,GAAG,CAAC,cAAc,CAAC;CAEnB,2BAAgB;EACd,aAAa,UAAU;EAEvB,KAAKH,sBAAS,OAAO,SAASA,sBAAS,OAAO,cAAc,kBAAkB,CAAC,eAAe,SAAS;GACrG,eAAe,UAAU;GAEzB,MAAM,uBAAuB,YAAY;IACvC,IAAI;KACF,MAAM,YAAYG;KAElB,IAAI,WAAW,WAAW;MACxB,MAAM,UAAU,UAAU,gBAAgB,IAAI;MAE9C,IAAI,CAAC,aAAa,SAChB;MAGF,IAAI,cAA6B;MACjC,IAAI;OACF,cAAc,MAAM,qBAAqB,UAAU;MACrD,SAAS,GAAG,CAIZ;MAEA,IAAI,aAAa;OACf,MAAM,UAAU,UAAU,gBAAgB,WAAW;OAErD,IAAI,CAAC,aAAa,SAChB;MAEJ;MAEA,IAAI,eAAe;OACjB,MAAM,oBAAmC;QACvC,OAAO,IAAI,SAAQ,YAAW;SAC5B,IAAI,cAAc,QAChB,QAAQ;cACH,IAAI,OAAO,cAAc,gBAAgB,YAC9C,cAAc,kBAAkB,QAAQ,CAAC;cAKzC,QAAQ;QAEZ,CAAC;OACH;OAEA,MAAM,YAAY;OAElB,IAAI,CAAC,aAAa,SAChB;OAGF,IAAI,CAAC,sBAAsB,SAAS;QAClC,sBAAsB,UAAU;QAChC,MAAM,qBAAqB;SACzB;SACA;SACA;QACF,CAAC;OACH;MACF;KACF;IACF,SAAS,OAAO;KAKd,IAHE,iBAAiB,UAChB,MAAM,QAAQ,SAAS,2BAA2B,KACjD,MAAM,QAAQ,SAAS,oDAAoD,IACnD,CAO5B;IAGF;GACF;GACA,AAAK,qBAAqB;EAC5B;EAEA,aAAa;GACX,aAAa,UAAU;EACzB;CACF,GAAG;EAAC;EAAgB;EAAoC;EAAY;CAAa,CAAC;CAElF,OAAO;AACT;AAEA,SAAgB,yBAAyB,EACvC,eACA,cACA,kCACA,4BACA,oCACA,cAQC;CACD,MAAM,EAAE,iFAA4C;CAEpD,2BAAgB;EACd,IAAI,CAAC,qBAAqB,CAAC,eACzB;EAGF,IAAI,kBAAkB,UAAU,WAAW,8BAA8B,GACvE;EAGF,MAAM,4BAA4B,YAAY;GAC5C,IAAI;IACF,IAAI,CAAC,aAAa,SAChB;IAEF,MAAM,qBAAqB;KACzB;KACA;KACA;KACA;KACA;KACA;IACF,CAAC;GACH,SAAS,OAAO;IACd,QAAQ,MAAM,uDAAuD,KAAK;GAC5E;EACF;EAEA,AAAK,0BAA0B;CACjC,GAAG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;AACH"}
|
|
1
|
+
{"version":3,"file":"nativeClientSync.js","names":["Platform","MemoryTokenCache","CLERK_CLIENT_JWT_KEY","NativeClerkModule"],"sources":["../../src/provider/nativeClientSync.tsx"],"sourcesContent":["import type { ClientResource, SignedInSessionResource } from '@clerk/shared/types';\nimport { type MutableRefObject, useCallback, useEffect, useMemo, useRef } from 'react';\nimport { Platform } from 'react-native';\n\nimport { MemoryTokenCache } from '../cache';\nimport type { TokenCache } from '../cache/types';\nimport { CLERK_CLIENT_JWT_KEY } from '../constants';\nimport { type NativeClientEvent, useNativeClientEvents } from '../hooks/useNativeClientEvents';\nimport { ClerkExpoModule as NativeClerkModule } from '../utils/native-module';\n\nconst tokenCacheReadTimeoutMs = 1_000;\nconst nativeDeviceTokenPollIntervalMs = 100;\nconst nativeDeviceTokenAvailabilityTimeoutMs = 3_000;\nconst nativeClientSyncSourceIdPrefix = 'clerk-expo-js-sync';\n\nexport type SyncableClerkInstance = {\n addListener?: (listener: () => void, options?: { skipInitialEmit?: boolean }) => () => void;\n addOnLoaded?: (listener: () => void) => void;\n client?: ClientResource;\n handleUnauthenticated?: (options?: { broadcast?: boolean }) => Promise<unknown>;\n loaded?: boolean;\n session?: SignedInSessionResource | null;\n setActive?: (params: { session: SignedInSessionResource | string | null }) => Promise<void>;\n updateClient?: (client: ClientResource, options?: { __internal_dangerouslySkipEmit?: boolean }) => void;\n __internal_reloadInitialResources?: () => void | Promise<void>;\n};\n\ntype RefreshableClientResource = ClientResource & {\n fetch?: (options?: { fetchMaxTries?: number }) => Promise<ClientResource>;\n};\n\ntype NativeRefreshFromJsOptions = {\n deviceToken?: string | null;\n didChangeClient: boolean;\n didChangeDeviceToken: boolean;\n};\n\nexport type NativeRefreshFromJsController = {\n cancel: () => void;\n};\n\nexport type DeviceTokenCacheListener = (deviceToken: string | null) => void;\n\nfunction delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n\nexport function useSyncableTokenCache({\n suppressTokenCacheNotificationsRef,\n tokenCache,\n tokenCacheListenersRef,\n}: {\n suppressTokenCacheNotificationsRef: MutableRefObject<number>;\n tokenCache: TokenCache | undefined;\n tokenCacheListenersRef: MutableRefObject<Set<DeviceTokenCacheListener>>;\n}): TokenCache | undefined {\n return useMemo(() => {\n const effectiveTokenCache =\n tokenCache ?? (Platform.OS === 'ios' || Platform.OS === 'android' ? MemoryTokenCache : undefined);\n if (!effectiveTokenCache) {\n return undefined;\n }\n\n const notifyDeviceTokenListeners = (deviceToken: string | null) => {\n if (suppressTokenCacheNotificationsRef.current > 0) {\n return;\n }\n\n for (const listener of tokenCacheListenersRef.current) {\n listener(deviceToken);\n }\n };\n\n return {\n getToken: key => effectiveTokenCache.getToken(key),\n saveToken: async (key, token) => {\n await effectiveTokenCache.saveToken(key, token);\n if (key === CLERK_CLIENT_JWT_KEY) {\n notifyDeviceTokenListeners(token);\n }\n },\n clearToken: async key => {\n await effectiveTokenCache.clearToken?.(key);\n if (key === CLERK_CLIENT_JWT_KEY) {\n notifyDeviceTokenListeners(null);\n }\n },\n };\n }, [suppressTokenCacheNotificationsRef, tokenCache, tokenCacheListenersRef]);\n}\n\nasync function readNativeDeviceToken({ waitForToken }: { waitForToken: boolean }): Promise<string | null> {\n const ClerkExpo = NativeClerkModule;\n if (!ClerkExpo?.getClientToken) {\n return null;\n }\n\n const startedAt = Date.now();\n let remainingMs = nativeDeviceTokenAvailabilityTimeoutMs;\n\n do {\n const nativeDeviceToken = await ClerkExpo.getClientToken();\n if (nativeDeviceToken) {\n return nativeDeviceToken;\n }\n\n if (!waitForToken) {\n return null;\n }\n\n remainingMs = nativeDeviceTokenAvailabilityTimeoutMs - (Date.now() - startedAt);\n if (remainingMs <= 0) {\n return null;\n }\n\n await delay(Math.min(nativeDeviceTokenPollIntervalMs, remainingMs));\n } while (remainingMs > 0);\n\n return null;\n}\n\nasync function syncDeviceTokenToCache(tokenCache: TokenCache | undefined, deviceToken: string | null): Promise<void> {\n if (deviceToken) {\n await tokenCache?.saveToken(CLERK_CLIENT_JWT_KEY, deviceToken);\n return;\n }\n\n await tokenCache?.clearToken?.(CLERK_CLIENT_JWT_KEY);\n}\n\nasync function syncDeviceTokenToCacheWithoutNotifying({\n deviceToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n deviceToken: string | null;\n suppressTokenCacheNotificationsRef: MutableRefObject<number>;\n tokenCache: TokenCache | undefined;\n}): Promise<void> {\n suppressTokenCacheNotificationsRef.current += 1;\n try {\n await syncDeviceTokenToCache(tokenCache, deviceToken);\n } finally {\n suppressTokenCacheNotificationsRef.current = Math.max(0, suppressTokenCacheNotificationsRef.current - 1);\n }\n}\n\nasync function syncNativeDeviceTokenToCache({\n deviceToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n deviceToken: string | null;\n suppressTokenCacheNotificationsRef?: MutableRefObject<number>;\n tokenCache: TokenCache | undefined;\n}): Promise<void> {\n if (suppressTokenCacheNotificationsRef) {\n await syncDeviceTokenToCacheWithoutNotifying({\n deviceToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n return;\n }\n\n await syncDeviceTokenToCache(tokenCache, deviceToken);\n}\n\nfunction getDefaultSignedInSession(client: ClientResource | null | undefined): SignedInSessionResource | null {\n if (!client) {\n return null;\n }\n\n if (client.lastActiveSessionId) {\n const lastActiveSession = client.signedInSessions.find(session => session.id === client.lastActiveSessionId);\n if (lastActiveSession) {\n return lastActiveSession;\n }\n }\n\n return client.signedInSessions[0] ?? null;\n}\n\nasync function refreshJsClientFromServer(clerkInstance: SyncableClerkInstance): Promise<ClientResource | null> {\n const client = clerkInstance.client as RefreshableClientResource | undefined;\n\n if (typeof client?.fetch !== 'function' || typeof clerkInstance.updateClient !== 'function') {\n return null;\n }\n\n const refreshedClient = await client.fetch({ fetchMaxTries: 1 });\n clerkInstance.updateClient(refreshedClient);\n\n return refreshedClient;\n}\n\nasync function refreshJsClientFromNativeState({\n clerkInstance,\n nativeDeviceToken,\n reloadInitialResources,\n shouldSyncDeviceToken = true,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n clerkInstance: SyncableClerkInstance;\n nativeDeviceToken: string | null;\n reloadInitialResources: boolean;\n shouldSyncDeviceToken?: boolean;\n suppressTokenCacheNotificationsRef?: MutableRefObject<number>;\n tokenCache: TokenCache | undefined;\n}): Promise<boolean> {\n if (shouldSyncDeviceToken) {\n await syncNativeDeviceTokenToCache({\n deviceToken: nativeDeviceToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n }\n\n const refreshedClient = await refreshJsClientFromServer(clerkInstance);\n if (refreshedClient) {\n await reconcileJsActiveSessionFromClient({\n clerkInstance,\n });\n return true;\n }\n\n if (reloadInitialResources && typeof clerkInstance.__internal_reloadInitialResources === 'function') {\n await clerkInstance.__internal_reloadInitialResources();\n await reconcileJsActiveSessionFromClient({\n clerkInstance,\n });\n return Boolean(getDefaultSignedInSession(clerkInstance.client));\n }\n\n return false;\n}\n\nasync function reloadJsClientFromNativeState({\n clerkInstance,\n nativeDeviceToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n clerkInstance: SyncableClerkInstance;\n nativeDeviceToken: string;\n suppressTokenCacheNotificationsRef?: MutableRefObject<number>;\n tokenCache: TokenCache | undefined;\n}): Promise<boolean> {\n await syncNativeDeviceTokenToCache({\n deviceToken: nativeDeviceToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n\n await clerkInstance.__internal_reloadInitialResources?.();\n await reconcileJsActiveSessionFromClient({\n clerkInstance,\n });\n return Boolean(getDefaultSignedInSession(clerkInstance.client));\n}\n\nasync function recoverJsClientFromNativeDeviceToken({\n clerkInstance,\n error,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n clerkInstance: SyncableClerkInstance;\n error: unknown;\n suppressTokenCacheNotificationsRef: MutableRefObject<number>;\n tokenCache: TokenCache | undefined;\n}): Promise<boolean> {\n const nativeDeviceToken = await readNativeDeviceToken({ waitForToken: false });\n if (!nativeDeviceToken) {\n return false;\n }\n\n if (__DEV__) {\n console.warn('[NativeClientSync] Failed to refresh JS client with native device token:', error);\n }\n\n try {\n return await reloadJsClientFromNativeState({\n clerkInstance,\n nativeDeviceToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n } catch (recoveryError) {\n if (__DEV__) {\n console.warn('[NativeClientSync] Failed to recover JS client after unauthenticated state:', recoveryError);\n }\n return false;\n }\n}\n\nasync function reconcileJsActiveSessionFromClient({\n clerkInstance,\n}: {\n clerkInstance: SyncableClerkInstance;\n}): Promise<void> {\n const fallbackSession = getDefaultSignedInSession(clerkInstance.client);\n if (!fallbackSession || typeof clerkInstance.setActive !== 'function') {\n return;\n }\n\n const currentSession = clerkInstance.session;\n const currentSessionStillExists = currentSession\n ? clerkInstance.client?.signedInSessions.some(session => session.id === currentSession.id)\n : false;\n\n if (currentSessionStillExists && currentSession?.id === fallbackSession.id) {\n return;\n }\n\n await clerkInstance.setActive({ session: fallbackSession });\n}\n\nasync function runWithSuppressedJsClientChanges<T>(\n suppressJsClientChangedRef: MutableRefObject<number> | undefined,\n task: () => Promise<T>,\n): Promise<T> {\n if (!suppressJsClientChangedRef) {\n return task();\n }\n\n suppressJsClientChangedRef.current += 1;\n try {\n return await task();\n } finally {\n suppressJsClientChangedRef.current = Math.max(0, suppressJsClientChangedRef.current - 1);\n }\n}\n\nfunction mergePendingNativeRefreshOptions(\n current: NativeRefreshFromJsOptions | null,\n next: NativeRefreshFromJsOptions,\n): NativeRefreshFromJsOptions {\n if (!current) {\n return next;\n }\n\n const merged: NativeRefreshFromJsOptions = {\n didChangeClient: current.didChangeClient || next.didChangeClient,\n didChangeDeviceToken: current.didChangeDeviceToken || next.didChangeDeviceToken,\n };\n\n if ('deviceToken' in current) {\n merged.deviceToken = current.deviceToken ?? null;\n }\n\n if ('deviceToken' in next) {\n merged.deviceToken = next.deviceToken ?? null;\n }\n\n return merged;\n}\n\nasync function getCachedDeviceToken(tokenCache: TokenCache | undefined): Promise<string | null> {\n if (!tokenCache) {\n return null;\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n return (\n (await Promise.race([\n tokenCache.getToken(CLERK_CLIENT_JWT_KEY),\n new Promise<null>(resolve => {\n timeoutId = setTimeout(() => resolve(null), tokenCacheReadTimeoutMs);\n }),\n ])) ?? null\n );\n } finally {\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n }\n}\n\nasync function syncNativeClientToJs({\n clerkInstance,\n nativeRefreshFromJsControllerRef,\n nativeClientEvent,\n suppressJsClientChangedRef,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n clerkInstance: SyncableClerkInstance;\n nativeRefreshFromJsControllerRef?: MutableRefObject<NativeRefreshFromJsController | null>;\n nativeClientEvent?: NativeClientEvent | null;\n suppressJsClientChangedRef?: MutableRefObject<number>;\n suppressTokenCacheNotificationsRef?: MutableRefObject<number>;\n tokenCache: TokenCache | undefined;\n}): Promise<void> {\n const didChangeClient = nativeClientEvent?.changed.client ?? true;\n const didChangeDeviceToken = nativeClientEvent?.changed.deviceToken ?? true;\n\n if (!didChangeClient && !didChangeDeviceToken) {\n return;\n }\n\n const nativeDeviceToken = nativeClientEvent\n ? nativeClientEvent.deviceToken\n : await readNativeDeviceToken({\n waitForToken: true,\n });\n\n if (!nativeDeviceToken && !nativeClientEvent) {\n return;\n }\n\n await runWithSuppressedJsClientChanges(suppressJsClientChangedRef, async () => {\n nativeRefreshFromJsControllerRef?.current?.cancel();\n\n await refreshJsClientFromNativeState({\n clerkInstance,\n nativeDeviceToken,\n reloadInitialResources: true,\n shouldSyncDeviceToken: didChangeDeviceToken,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n });\n}\n\n/**\n * Syncs JS SDK client changes to the native Clerk SDK so native components\n * (UserButton, UserProfileView) stay in sync after JS-owned resource changes.\n *\n * Must be rendered inside `ClerkReactProvider` so the Clerk instance has loaded\n * resources to emit.\n */\nexport function NativeClientSync({\n clerkInstance,\n nativeRefreshFromJsControllerRef,\n suppressJsClientChangedRef,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n tokenCacheListenersRef,\n}: {\n clerkInstance: SyncableClerkInstance | null | undefined;\n nativeRefreshFromJsControllerRef: MutableRefObject<NativeRefreshFromJsController | null>;\n suppressJsClientChangedRef: MutableRefObject<number>;\n suppressTokenCacheNotificationsRef: MutableRefObject<number>;\n tokenCache: TokenCache | undefined;\n tokenCacheListenersRef: MutableRefObject<Set<DeviceTokenCacheListener>>;\n}): null {\n const isRefreshingNativeFromJsRef = useRef(false);\n const pendingNativeRefreshRef = useRef<NativeRefreshFromJsOptions | null>(null);\n const nativeRefreshGenerationRef = useRef(0);\n\n const cancelNativeRefreshFromJs = useCallback(() => {\n pendingNativeRefreshRef.current = null;\n nativeRefreshGenerationRef.current += 1;\n isRefreshingNativeFromJsRef.current = false;\n }, []);\n\n useEffect(() => {\n nativeRefreshFromJsControllerRef.current = {\n cancel: cancelNativeRefreshFromJs,\n };\n\n return () => {\n if (nativeRefreshFromJsControllerRef.current?.cancel === cancelNativeRefreshFromJs) {\n nativeRefreshFromJsControllerRef.current = null;\n }\n };\n }, [cancelNativeRefreshFromJs, nativeRefreshFromJsControllerRef]);\n\n useEffect(() => {\n if (\n !clerkInstance ||\n typeof clerkInstance.updateClient !== 'function' ||\n typeof clerkInstance.setActive !== 'function'\n ) {\n return;\n }\n\n const originalUpdateClient = clerkInstance.updateClient.bind(clerkInstance);\n let isReconcilingRemovedActiveSession = false;\n\n const updateClient: SyncableClerkInstance['updateClient'] = (newClient, options) => {\n const currentSessionId = clerkInstance.session?.id;\n const fallbackSession = getDefaultSignedInSession(newClient);\n const currentSessionWasRemoved = currentSessionId\n ? !newClient.signedInSessions.some(session => session.id === currentSessionId)\n : false;\n const alreadyReconcilingRemovedActiveSession = isReconcilingRemovedActiveSession;\n\n if ((currentSessionWasRemoved || alreadyReconcilingRemovedActiveSession) && fallbackSession) {\n // Clerk JS briefly emits signed-out when the active session disappears,\n // even if the refreshed client still has another signed-in session.\n // Keep that transient state internal so native session switching does\n // not dismiss mounted native UI before setActive settles on JS.\n isReconcilingRemovedActiveSession = true;\n originalUpdateClient(newClient, { __internal_dangerouslySkipEmit: true });\n\n if (alreadyReconcilingRemovedActiveSession) {\n return;\n }\n\n void runWithSuppressedJsClientChanges(suppressJsClientChangedRef, async () => {\n try {\n await clerkInstance.setActive?.({ session: fallbackSession });\n } catch (error) {\n if (__DEV__) {\n console.warn('[NativeClientSync] Failed to set remaining active JS session:', error);\n }\n originalUpdateClient(newClient, options);\n } finally {\n isReconcilingRemovedActiveSession = false;\n }\n });\n return;\n }\n\n if (options) {\n originalUpdateClient(newClient, options);\n return;\n }\n\n originalUpdateClient(newClient);\n };\n\n clerkInstance.updateClient = updateClient;\n\n return () => {\n if (clerkInstance.updateClient === updateClient) {\n clerkInstance.updateClient = originalUpdateClient;\n }\n };\n }, [clerkInstance, suppressJsClientChangedRef]);\n\n const queueNativeRefreshFromJs = useCallback((options: NativeRefreshFromJsOptions): void => {\n if (isRefreshingNativeFromJsRef.current) {\n pendingNativeRefreshRef.current = mergePendingNativeRefreshOptions(pendingNativeRefreshRef.current, options);\n nativeRefreshGenerationRef.current += 1;\n return;\n }\n\n const initialGeneration = nativeRefreshGenerationRef.current + 1;\n nativeRefreshGenerationRef.current = initialGeneration;\n isRefreshingNativeFromJsRef.current = true;\n\n const refreshNativeFromJsClient = async (\n options: NativeRefreshFromJsOptions,\n generation: number,\n ): Promise<void> => {\n const ClerkExpo = NativeClerkModule;\n if (!ClerkExpo || generation !== nativeRefreshGenerationRef.current) {\n return;\n }\n\n const deviceToken = options.didChangeDeviceToken ? (options.deviceToken ?? null) : null;\n if (generation !== nativeRefreshGenerationRef.current) {\n return;\n }\n\n const sourceId = `${nativeClientSyncSourceIdPrefix}-${generation}`;\n await ClerkExpo.syncClientStateFromJs(\n deviceToken,\n sourceId,\n options.didChangeClient,\n options.didChangeDeviceToken,\n );\n };\n\n let latestRunGeneration = initialGeneration;\n\n void (async () => {\n let pendingOptions = options;\n let generation = initialGeneration;\n do {\n latestRunGeneration = generation;\n pendingNativeRefreshRef.current = null;\n try {\n await refreshNativeFromJsClient(pendingOptions, generation);\n } catch (error: unknown) {\n if (__DEV__) {\n console.warn('[NativeClientSync] Failed to refresh native client from JS client change:', error);\n }\n }\n pendingOptions = pendingNativeRefreshRef.current ?? {\n didChangeClient: false,\n didChangeDeviceToken: false,\n };\n if (pendingNativeRefreshRef.current !== null) {\n generation = nativeRefreshGenerationRef.current + 1;\n nativeRefreshGenerationRef.current = generation;\n }\n } while (pendingNativeRefreshRef.current !== null);\n })().finally(() => {\n if (latestRunGeneration === nativeRefreshGenerationRef.current || pendingNativeRefreshRef.current === null) {\n isRefreshingNativeFromJsRef.current = false;\n }\n });\n }, []);\n\n useEffect(() => {\n const listener: DeviceTokenCacheListener = deviceToken => {\n queueNativeRefreshFromJs({\n deviceToken,\n didChangeClient: false,\n didChangeDeviceToken: true,\n });\n };\n const tokenCacheListeners = tokenCacheListenersRef.current;\n\n tokenCacheListeners.add(listener);\n return () => {\n tokenCacheListeners.delete(listener);\n };\n }, [queueNativeRefreshFromJs, tokenCacheListenersRef]);\n\n useEffect(() => {\n if (!clerkInstance || typeof clerkInstance.handleUnauthenticated !== 'function') {\n return;\n }\n\n const originalHandleUnauthenticated = clerkInstance.handleUnauthenticated.bind(clerkInstance);\n let isHandlingUnauthenticated = false;\n\n const handleUnauthenticated: SyncableClerkInstance['handleUnauthenticated'] = async options => {\n if (isHandlingUnauthenticated) {\n return;\n }\n\n isHandlingUnauthenticated = true;\n try {\n return await runWithSuppressedJsClientChanges(suppressJsClientChangedRef, async () => {\n try {\n const nativeDeviceToken = await readNativeDeviceToken({ waitForToken: false });\n // Native may have already moved the server-side client to a new\n // active session. Refresh JS before allowing Clerk JS' stale-session\n // 401 path to collapse the whole client to signed out.\n const didRecover = await refreshJsClientFromNativeState({\n clerkInstance,\n nativeDeviceToken,\n reloadInitialResources: false,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n if (didRecover) {\n return;\n }\n } catch (error) {\n const didRecover = await recoverJsClientFromNativeDeviceToken({\n clerkInstance,\n error,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n if (didRecover) {\n return;\n }\n }\n\n return originalHandleUnauthenticated(options);\n });\n } finally {\n isHandlingUnauthenticated = false;\n }\n };\n\n clerkInstance.handleUnauthenticated = handleUnauthenticated;\n\n return () => {\n if (clerkInstance.handleUnauthenticated === handleUnauthenticated) {\n clerkInstance.handleUnauthenticated = originalHandleUnauthenticated;\n }\n };\n }, [clerkInstance, suppressJsClientChangedRef, suppressTokenCacheNotificationsRef, tokenCache]);\n\n useEffect(() => {\n if (!clerkInstance || typeof clerkInstance.addListener !== 'function') {\n return;\n }\n\n const unsubscribe = clerkInstance.addListener(\n () => {\n if (suppressJsClientChangedRef.current > 0) {\n return;\n }\n\n queueNativeRefreshFromJs({\n didChangeClient: true,\n didChangeDeviceToken: false,\n });\n },\n { skipInitialEmit: true },\n );\n\n return () => {\n unsubscribe();\n };\n }, [clerkInstance, queueNativeRefreshFromJs, suppressJsClientChangedRef]);\n\n return null;\n}\n\nexport function useNativeClientBootstrap({\n publishableKey,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n clerkInstance,\n}: {\n publishableKey: string;\n suppressTokenCacheNotificationsRef: MutableRefObject<number>;\n tokenCache: TokenCache | undefined;\n clerkInstance: SyncableClerkInstance | null | undefined;\n}) {\n const initStartedRef = useRef(false);\n const nativeClientSyncedRef = useRef(false);\n const isMountedRef = useRef(true);\n\n useEffect(() => {\n initStartedRef.current = false;\n nativeClientSyncedRef.current = false;\n }, [publishableKey]);\n\n useEffect(() => {\n isMountedRef.current = true;\n\n if ((Platform.OS === 'ios' || Platform.OS === 'android') && publishableKey && !initStartedRef.current) {\n initStartedRef.current = true;\n\n const configureNativeClerk = async () => {\n try {\n const ClerkExpo = NativeClerkModule;\n\n if (ClerkExpo?.configure) {\n await ClerkExpo.configure(publishableKey, null);\n\n if (!isMountedRef.current) {\n return;\n }\n\n let cachedDeviceToken: string | null = null;\n try {\n cachedDeviceToken = await getCachedDeviceToken(tokenCache);\n } catch (e) {\n if (__DEV__) {\n console.warn('[ClerkProvider] Token cache read failed:', e);\n }\n }\n\n if (cachedDeviceToken) {\n await ClerkExpo.configure(publishableKey, cachedDeviceToken);\n\n if (!isMountedRef.current) {\n return;\n }\n }\n\n if (clerkInstance) {\n const waitForLoad = (): Promise<void> => {\n return new Promise(resolve => {\n if (clerkInstance.loaded) {\n resolve();\n } else if (typeof clerkInstance.addOnLoaded === 'function') {\n clerkInstance.addOnLoaded(() => resolve());\n } else {\n if (__DEV__) {\n console.warn('[ClerkProvider] Clerk instance has no loaded property or addOnLoaded method');\n }\n resolve();\n }\n });\n };\n\n await waitForLoad();\n\n if (!isMountedRef.current) {\n return;\n }\n\n if (!nativeClientSyncedRef.current) {\n nativeClientSyncedRef.current = true;\n await syncNativeClientToJs({\n clerkInstance,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n }\n }\n }\n } catch (error) {\n const isNativeModuleNotFound =\n error instanceof Error &&\n (error.message.includes('Cannot find native module') ||\n error.message.includes(\"TurboModuleRegistry.getEnforcing(...): 'ClerkExpo'\"));\n if (isNativeModuleNotFound) {\n if (__DEV__) {\n console.debug(\n `[ClerkProvider] Native Clerk module not available. ` +\n `To enable native features, add \"@clerk/expo\" to your app.json plugins array.`,\n );\n }\n } else if (__DEV__) {\n console.error(`[ClerkProvider] Failed to configure Clerk ${Platform.OS}:`, error);\n }\n }\n };\n void configureNativeClerk();\n }\n\n return () => {\n isMountedRef.current = false;\n };\n }, [publishableKey, suppressTokenCacheNotificationsRef, tokenCache, clerkInstance]);\n\n return isMountedRef;\n}\n\nexport function useNativeClientEventSync({\n clerkInstance,\n isMountedRef,\n nativeRefreshFromJsControllerRef,\n suppressJsClientChangedRef,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n}: {\n clerkInstance: SyncableClerkInstance | null | undefined;\n isMountedRef: MutableRefObject<boolean>;\n nativeRefreshFromJsControllerRef: MutableRefObject<NativeRefreshFromJsController | null>;\n suppressJsClientChangedRef: MutableRefObject<number>;\n suppressTokenCacheNotificationsRef: MutableRefObject<number>;\n tokenCache: TokenCache | undefined;\n}) {\n const { nativeClientEvent } = useNativeClientEvents();\n\n useEffect(() => {\n if (!nativeClientEvent || !clerkInstance) {\n return;\n }\n\n if (nativeClientEvent.sourceId?.startsWith(nativeClientSyncSourceIdPrefix)) {\n return;\n }\n\n const syncNativeClientStateToJs = async () => {\n try {\n if (!isMountedRef.current) {\n return;\n }\n await syncNativeClientToJs({\n clerkInstance,\n nativeRefreshFromJsControllerRef,\n nativeClientEvent,\n suppressJsClientChangedRef,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n });\n } catch (error) {\n console.error(`[ClerkProvider] Failed to sync native client state:`, error);\n }\n };\n\n void syncNativeClientStateToJs();\n }, [\n nativeClientEvent,\n clerkInstance,\n isMountedRef,\n nativeRefreshFromJsControllerRef,\n suppressJsClientChangedRef,\n suppressTokenCacheNotificationsRef,\n tokenCache,\n ]);\n}\n"],"mappings":";;;;;;;;;AAUA,MAAM,0BAA0B;AAChC,MAAM,kCAAkC;AACxC,MAAM,yCAAyC;AAC/C,MAAM,iCAAiC;AA8BvC,SAAS,MAAM,IAA2B;CACxC,OAAO,IAAI,SAAQ,YAAW,WAAW,SAAS,EAAE,CAAC;AACvD;AAEA,SAAgB,sBAAsB,EACpC,oCACA,YACA,0BAKyB;CACzB,gCAAqB;EACnB,MAAM,sBACJ,eAAeA,sBAAS,OAAO,SAASA,sBAAS,OAAO,YAAYC,6BAAmB;EACzF,IAAI,CAAC,qBACH;EAGF,MAAM,8BAA8B,gBAA+B;GACjE,IAAI,mCAAmC,UAAU,GAC/C;GAGF,KAAK,MAAM,YAAY,uBAAuB,SAC5C,SAAS,WAAW;EAExB;EAEA,OAAO;GACL,WAAU,QAAO,oBAAoB,SAAS,GAAG;GACjD,WAAW,OAAO,KAAK,UAAU;IAC/B,MAAM,oBAAoB,UAAU,KAAK,KAAK;IAC9C,IAAI,QAAQC,oCACV,2BAA2B,KAAK;GAEpC;GACA,YAAY,OAAM,QAAO;IACvB,MAAM,oBAAoB,aAAa,GAAG;IAC1C,IAAI,QAAQA,oCACV,2BAA2B,IAAI;GAEnC;EACF;CACF,GAAG;EAAC;EAAoC;EAAY;CAAsB,CAAC;AAC7E;AAEA,eAAe,sBAAsB,EAAE,gBAAmE;CACxG,MAAM,YAAYC;CAClB,IAAI,CAAC,WAAW,gBACd,OAAO;CAGT,MAAM,YAAY,KAAK,IAAI;CAC3B,IAAI,cAAc;CAElB,GAAG;EACD,MAAM,oBAAoB,MAAM,UAAU,eAAe;EACzD,IAAI,mBACF,OAAO;EAGT,IAAI,CAAC,cACH,OAAO;EAGT,cAAc,0CAA0C,KAAK,IAAI,IAAI;EACrE,IAAI,eAAe,GACjB,OAAO;EAGT,MAAM,MAAM,KAAK,IAAI,iCAAiC,WAAW,CAAC;CACpE,SAAS,cAAc;CAEvB,OAAO;AACT;AAEA,eAAe,uBAAuB,YAAoC,aAA2C;CACnH,IAAI,aAAa;EACf,MAAM,YAAY,UAAUD,oCAAsB,WAAW;EAC7D;CACF;CAEA,MAAM,YAAY,aAAaA,kCAAoB;AACrD;AAEA,eAAe,uCAAuC,EACpD,aACA,oCACA,cAKgB;CAChB,mCAAmC,WAAW;CAC9C,IAAI;EACF,MAAM,uBAAuB,YAAY,WAAW;CACtD,UAAU;EACR,mCAAmC,UAAU,KAAK,IAAI,GAAG,mCAAmC,UAAU,CAAC;CACzG;AACF;AAEA,eAAe,6BAA6B,EAC1C,aACA,oCACA,cAKgB;CAChB,IAAI,oCAAoC;EACtC,MAAM,uCAAuC;GAC3C;GACA;GACA;EACF,CAAC;EACD;CACF;CAEA,MAAM,uBAAuB,YAAY,WAAW;AACtD;AAEA,SAAS,0BAA0B,QAA2E;CAC5G,IAAI,CAAC,QACH,OAAO;CAGT,IAAI,OAAO,qBAAqB;EAC9B,MAAM,oBAAoB,OAAO,iBAAiB,MAAK,YAAW,QAAQ,OAAO,OAAO,mBAAmB;EAC3G,IAAI,mBACF,OAAO;CAEX;CAEA,OAAO,OAAO,iBAAiB,MAAM;AACvC;AAEA,eAAe,0BAA0B,eAAsE;CAC7G,MAAM,SAAS,cAAc;CAE7B,IAAI,OAAO,QAAQ,UAAU,cAAc,OAAO,cAAc,iBAAiB,YAC/E,OAAO;CAGT,MAAM,kBAAkB,MAAM,OAAO,MAAM,EAAE,eAAe,EAAE,CAAC;CAC/D,cAAc,aAAa,eAAe;CAE1C,OAAO;AACT;AAEA,eAAe,+BAA+B,EAC5C,eACA,mBACA,wBACA,wBAAwB,MACxB,oCACA,cAQmB;CACnB,IAAI,uBACF,MAAM,6BAA6B;EACjC,aAAa;EACb;EACA;CACF,CAAC;CAIH,IAAI,MAD0B,0BAA0B,aAAa,GAChD;EACnB,MAAM,mCAAmC,EACvC,cACF,CAAC;EACD,OAAO;CACT;CAEA,IAAI,0BAA0B,OAAO,cAAc,sCAAsC,YAAY;EACnG,MAAM,cAAc,kCAAkC;EACtD,MAAM,mCAAmC,EACvC,cACF,CAAC;EACD,OAAO,QAAQ,0BAA0B,cAAc,MAAM,CAAC;CAChE;CAEA,OAAO;AACT;AAEA,eAAe,8BAA8B,EAC3C,eACA,mBACA,oCACA,cAMmB;CACnB,MAAM,6BAA6B;EACjC,aAAa;EACb;EACA;CACF,CAAC;CAED,MAAM,cAAc,oCAAoC;CACxD,MAAM,mCAAmC,EACvC,cACF,CAAC;CACD,OAAO,QAAQ,0BAA0B,cAAc,MAAM,CAAC;AAChE;AAEA,eAAe,qCAAqC,EAClD,eACA,OACA,oCACA,cAMmB;CACnB,MAAM,oBAAoB,MAAM,sBAAsB,EAAE,cAAc,MAAM,CAAC;CAC7E,IAAI,CAAC,mBACH,OAAO;CAOT,IAAI;EACF,OAAO,MAAM,8BAA8B;GACzC;GACA;GACA;GACA;EACF,CAAC;CACH,SAAS,eAAe;EAItB,OAAO;CACT;AACF;AAEA,eAAe,mCAAmC,EAChD,iBAGgB;CAChB,MAAM,kBAAkB,0BAA0B,cAAc,MAAM;CACtE,IAAI,CAAC,mBAAmB,OAAO,cAAc,cAAc,YACzD;CAGF,MAAM,iBAAiB,cAAc;CAKrC,KAJkC,iBAC9B,cAAc,QAAQ,iBAAiB,MAAK,YAAW,QAAQ,OAAO,eAAe,EAAE,IACvF,UAE6B,gBAAgB,OAAO,gBAAgB,IACtE;CAGF,MAAM,cAAc,UAAU,EAAE,SAAS,gBAAgB,CAAC;AAC5D;AAEA,eAAe,iCACb,4BACA,MACY;CACZ,IAAI,CAAC,4BACH,OAAO,KAAK;CAGd,2BAA2B,WAAW;CACtC,IAAI;EACF,OAAO,MAAM,KAAK;CACpB,UAAU;EACR,2BAA2B,UAAU,KAAK,IAAI,GAAG,2BAA2B,UAAU,CAAC;CACzF;AACF;AAEA,SAAS,iCACP,SACA,MAC4B;CAC5B,IAAI,CAAC,SACH,OAAO;CAGT,MAAM,SAAqC;EACzC,iBAAiB,QAAQ,mBAAmB,KAAK;EACjD,sBAAsB,QAAQ,wBAAwB,KAAK;CAC7D;CAEA,IAAI,iBAAiB,SACnB,OAAO,cAAc,QAAQ,eAAe;CAG9C,IAAI,iBAAiB,MACnB,OAAO,cAAc,KAAK,eAAe;CAG3C,OAAO;AACT;AAEA,eAAe,qBAAqB,YAA4D;CAC9F,IAAI,CAAC,YACH,OAAO;CAGT,IAAI;CACJ,IAAI;EACF,OACG,MAAM,QAAQ,KAAK,CAClB,WAAW,SAASA,kCAAoB,GACxC,IAAI,SAAc,YAAW;GAC3B,YAAY,iBAAiB,QAAQ,IAAI,GAAG,uBAAuB;EACrE,CAAC,CACH,CAAC,KAAM;CAEX,UAAU;EACR,IAAI,WACF,aAAa,SAAS;CAE1B;AACF;AAEA,eAAe,qBAAqB,EAClC,eACA,kCACA,mBACA,4BACA,oCACA,cAQgB;CAChB,MAAM,kBAAkB,mBAAmB,QAAQ,UAAU;CAC7D,MAAM,uBAAuB,mBAAmB,QAAQ,eAAe;CAEvE,IAAI,CAAC,mBAAmB,CAAC,sBACvB;CAGF,MAAM,oBAAoB,oBACtB,kBAAkB,cAClB,MAAM,sBAAsB,EAC1B,cAAc,KAChB,CAAC;CAEL,IAAI,CAAC,qBAAqB,CAAC,mBACzB;CAGF,MAAM,iCAAiC,4BAA4B,YAAY;EAC7E,kCAAkC,SAAS,OAAO;EAElD,MAAM,+BAA+B;GACnC;GACA;GACA,wBAAwB;GACxB,uBAAuB;GACvB;GACA;EACF,CAAC;CACH,CAAC;AACH;;;;;;;;AASA,SAAgB,iBAAiB,EAC/B,eACA,kCACA,4BACA,oCACA,YACA,0BAQO;CACP,MAAM,gDAAqC,KAAK;CAChD,MAAM,4CAAoE,IAAI;CAC9E,MAAM,+CAAoC,CAAC;CAE3C,MAAM,yDAA8C;EAClD,wBAAwB,UAAU;EAClC,2BAA2B,WAAW;EACtC,4BAA4B,UAAU;CACxC,GAAG,CAAC,CAAC;CAEL,2BAAgB;EACd,iCAAiC,UAAU,EACzC,QAAQ,0BACV;EAEA,aAAa;GACX,IAAI,iCAAiC,SAAS,WAAW,2BACvD,iCAAiC,UAAU;EAE/C;CACF,GAAG,CAAC,2BAA2B,gCAAgC,CAAC;CAEhE,2BAAgB;EACd,IACE,CAAC,iBACD,OAAO,cAAc,iBAAiB,cACtC,OAAO,cAAc,cAAc,YAEnC;EAGF,MAAM,uBAAuB,cAAc,aAAa,KAAK,aAAa;EAC1E,IAAI,oCAAoC;EAExC,MAAM,gBAAuD,WAAW,YAAY;GAClF,MAAM,mBAAmB,cAAc,SAAS;GAChD,MAAM,kBAAkB,0BAA0B,SAAS;GAC3D,MAAM,2BAA2B,mBAC7B,CAAC,UAAU,iBAAiB,MAAK,YAAW,QAAQ,OAAO,gBAAgB,IAC3E;GACJ,MAAM,yCAAyC;GAE/C,KAAK,4BAA4B,2CAA2C,iBAAiB;IAK3F,oCAAoC;IACpC,qBAAqB,WAAW,EAAE,gCAAgC,KAAK,CAAC;IAExE,IAAI,wCACF;IAGF,AAAK,iCAAiC,4BAA4B,YAAY;KAC5E,IAAI;MACF,MAAM,cAAc,YAAY,EAAE,SAAS,gBAAgB,CAAC;KAC9D,SAAS,OAAO;MAId,qBAAqB,WAAW,OAAO;KACzC,UAAU;MACR,oCAAoC;KACtC;IACF,CAAC;IACD;GACF;GAEA,IAAI,SAAS;IACX,qBAAqB,WAAW,OAAO;IACvC;GACF;GAEA,qBAAqB,SAAS;EAChC;EAEA,cAAc,eAAe;EAE7B,aAAa;GACX,IAAI,cAAc,iBAAiB,cACjC,cAAc,eAAe;EAEjC;CACF,GAAG,CAAC,eAAe,0BAA0B,CAAC;CAE9C,MAAM,mDAAwC,YAA8C;EAC1F,IAAI,4BAA4B,SAAS;GACvC,wBAAwB,UAAU,iCAAiC,wBAAwB,SAAS,OAAO;GAC3G,2BAA2B,WAAW;GACtC;EACF;EAEA,MAAM,oBAAoB,2BAA2B,UAAU;EAC/D,2BAA2B,UAAU;EACrC,4BAA4B,UAAU;EAEtC,MAAM,4BAA4B,OAChC,SACA,eACkB;GAClB,MAAM,YAAYC;GAClB,IAAI,CAAC,aAAa,eAAe,2BAA2B,SAC1D;GAGF,MAAM,cAAc,QAAQ,uBAAwB,QAAQ,eAAe,OAAQ;GACnF,IAAI,eAAe,2BAA2B,SAC5C;GAGF,MAAM,WAAW,GAAG,+BAA+B,GAAG;GACtD,MAAM,UAAU,sBACd,aACA,UACA,QAAQ,iBACR,QAAQ,oBACV;EACF;EAEA,IAAI,sBAAsB;EAE1B,CAAM,YAAY;GAChB,IAAI,iBAAiB;GACrB,IAAI,aAAa;GACjB,GAAG;IACD,sBAAsB;IACtB,wBAAwB,UAAU;IAClC,IAAI;KACF,MAAM,0BAA0B,gBAAgB,UAAU;IAC5D,SAAS,OAAgB,CAIzB;IACA,iBAAiB,wBAAwB,WAAW;KAClD,iBAAiB;KACjB,sBAAsB;IACxB;IACA,IAAI,wBAAwB,YAAY,MAAM;KAC5C,aAAa,2BAA2B,UAAU;KAClD,2BAA2B,UAAU;IACvC;GACF,SAAS,wBAAwB,YAAY;EAC/C,EAAC,CAAE,CAAC,CAAC,cAAc;GACjB,IAAI,wBAAwB,2BAA2B,WAAW,wBAAwB,YAAY,MACpG,4BAA4B,UAAU;EAE1C,CAAC;CACH,GAAG,CAAC,CAAC;CAEL,2BAAgB;EACd,MAAM,YAAqC,gBAAe;GACxD,yBAAyB;IACvB;IACA,iBAAiB;IACjB,sBAAsB;GACxB,CAAC;EACH;EACA,MAAM,sBAAsB,uBAAuB;EAEnD,oBAAoB,IAAI,QAAQ;EAChC,aAAa;GACX,oBAAoB,OAAO,QAAQ;EACrC;CACF,GAAG,CAAC,0BAA0B,sBAAsB,CAAC;CAErD,2BAAgB;EACd,IAAI,CAAC,iBAAiB,OAAO,cAAc,0BAA0B,YACnE;EAGF,MAAM,gCAAgC,cAAc,sBAAsB,KAAK,aAAa;EAC5F,IAAI,4BAA4B;EAEhC,MAAM,wBAAwE,OAAM,YAAW;GAC7F,IAAI,2BACF;GAGF,4BAA4B;GAC5B,IAAI;IACF,OAAO,MAAM,iCAAiC,4BAA4B,YAAY;KACpF,IAAI;MAYF,IAAI,MAPqB,+BAA+B;OACtD;OACA,yBAN8B,sBAAsB,EAAE,cAAc,MAAM,CAAC;OAO3E,wBAAwB;OACxB;OACA;MACF,CAAC,GAEC;KAEJ,SAAS,OAAO;MAOd,IAAI,MANqB,qCAAqC;OAC5D;OACA;OACA;OACA;MACF,CAAC,GAEC;KAEJ;KAEA,OAAO,8BAA8B,OAAO;IAC9C,CAAC;GACH,UAAU;IACR,4BAA4B;GAC9B;EACF;EAEA,cAAc,wBAAwB;EAEtC,aAAa;GACX,IAAI,cAAc,0BAA0B,uBAC1C,cAAc,wBAAwB;EAE1C;CACF,GAAG;EAAC;EAAe;EAA4B;EAAoC;CAAU,CAAC;CAE9F,2BAAgB;EACd,IAAI,CAAC,iBAAiB,OAAO,cAAc,gBAAgB,YACzD;EAGF,MAAM,cAAc,cAAc,kBAC1B;GACJ,IAAI,2BAA2B,UAAU,GACvC;GAGF,yBAAyB;IACvB,iBAAiB;IACjB,sBAAsB;GACxB,CAAC;EACH,GACA,EAAE,iBAAiB,KAAK,CAC1B;EAEA,aAAa;GACX,YAAY;EACd;CACF,GAAG;EAAC;EAAe;EAA0B;CAA0B,CAAC;CAExE,OAAO;AACT;AAEA,SAAgB,yBAAyB,EACvC,gBACA,oCACA,YACA,iBAMC;CACD,MAAM,mCAAwB,KAAK;CACnC,MAAM,0CAA+B,KAAK;CAC1C,MAAM,iCAAsB,IAAI;CAEhC,2BAAgB;EACd,eAAe,UAAU;EACzB,sBAAsB,UAAU;CAClC,GAAG,CAAC,cAAc,CAAC;CAEnB,2BAAgB;EACd,aAAa,UAAU;EAEvB,KAAKH,sBAAS,OAAO,SAASA,sBAAS,OAAO,cAAc,kBAAkB,CAAC,eAAe,SAAS;GACrG,eAAe,UAAU;GAEzB,MAAM,uBAAuB,YAAY;IACvC,IAAI;KACF,MAAM,YAAYG;KAElB,IAAI,WAAW,WAAW;MACxB,MAAM,UAAU,UAAU,gBAAgB,IAAI;MAE9C,IAAI,CAAC,aAAa,SAChB;MAGF,IAAI,oBAAmC;MACvC,IAAI;OACF,oBAAoB,MAAM,qBAAqB,UAAU;MAC3D,SAAS,GAAG,CAIZ;MAEA,IAAI,mBAAmB;OACrB,MAAM,UAAU,UAAU,gBAAgB,iBAAiB;OAE3D,IAAI,CAAC,aAAa,SAChB;MAEJ;MAEA,IAAI,eAAe;OACjB,MAAM,oBAAmC;QACvC,OAAO,IAAI,SAAQ,YAAW;SAC5B,IAAI,cAAc,QAChB,QAAQ;cACH,IAAI,OAAO,cAAc,gBAAgB,YAC9C,cAAc,kBAAkB,QAAQ,CAAC;cAKzC,QAAQ;QAEZ,CAAC;OACH;OAEA,MAAM,YAAY;OAElB,IAAI,CAAC,aAAa,SAChB;OAGF,IAAI,CAAC,sBAAsB,SAAS;QAClC,sBAAsB,UAAU;QAChC,MAAM,qBAAqB;SACzB;SACA;SACA;QACF,CAAC;OACH;MACF;KACF;IACF,SAAS,OAAO;KAKd,IAHE,iBAAiB,UAChB,MAAM,QAAQ,SAAS,2BAA2B,KACjD,MAAM,QAAQ,SAAS,oDAAoD,IACnD,CAO5B;IAGF;GACF;GACA,AAAK,qBAAqB;EAC5B;EAEA,aAAa;GACX,aAAa,UAAU;EACzB;CACF,GAAG;EAAC;EAAgB;EAAoC;EAAY;CAAa,CAAC;CAElF,OAAO;AACT;AAEA,SAAgB,yBAAyB,EACvC,eACA,cACA,kCACA,4BACA,oCACA,cAQC;CACD,MAAM,EAAE,iFAA4C;CAEpD,2BAAgB;EACd,IAAI,CAAC,qBAAqB,CAAC,eACzB;EAGF,IAAI,kBAAkB,UAAU,WAAW,8BAA8B,GACvE;EAGF,MAAM,4BAA4B,YAAY;GAC5C,IAAI;IACF,IAAI,CAAC,aAAa,SAChB;IAEF,MAAM,qBAAqB;KACzB;KACA;KACA;KACA;KACA;KACA;IACF,CAAC;GACH,SAAS,OAAO;IACd,QAAQ,MAAM,uDAAuD,KAAK;GAC5E;EACF;EAEA,AAAK,0BAA0B;CACjC,GAAG;EACD;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;AACH"}
|
|
@@ -2,7 +2,7 @@ interface Spec {
|
|
|
2
2
|
addListener?(eventName: string): void;
|
|
3
3
|
configure(publishableKey: string, bearerToken: string | null): Promise<void>;
|
|
4
4
|
getClientToken(): Promise<string | null>;
|
|
5
|
-
|
|
5
|
+
syncClientStateFromJs(deviceToken: string | null, sourceId: string | null, didChangeClient: boolean, didChangeDeviceToken: boolean): Promise<void>;
|
|
6
6
|
removeListeners?(count: number): void;
|
|
7
7
|
}
|
|
8
8
|
declare const _default: Spec;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeClerkModule.android.d.ts","sourceRoot":"","sources":["../../src/specs/NativeClerkModule.android.ts"],"names":[],"mappings":"AAEA,UAAU,IAAI;IAGZ,WAAW,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,qBAAqB,CACnB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,
|
|
1
|
+
{"version":3,"file":"NativeClerkModule.android.d.ts","sourceRoot":"","sources":["../../src/specs/NativeClerkModule.android.ts"],"names":[],"mappings":"AAEA,UAAU,IAAI;IAGZ,WAAW,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,qBAAqB,CACnB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,eAAe,EAAE,OAAO,EACxB,oBAAoB,EAAE,OAAO,GAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,eAAe,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;;AAED,wBAAsD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeClerkModule.android.js","names":[],"sources":["../../src/specs/NativeClerkModule.android.ts"],"sourcesContent":["import { requireNativeModule } from 'expo';\n\ninterface Spec {\n // addListener/removeListeners are present on the iOS RN event emitter module.\n // Android uses Expo Modules EventEmitter instead.\n addListener?(eventName: string): void;\n configure(publishableKey: string, bearerToken: string | null): Promise<void>;\n getClientToken(): Promise<string | null>;\n
|
|
1
|
+
{"version":3,"file":"NativeClerkModule.android.js","names":[],"sources":["../../src/specs/NativeClerkModule.android.ts"],"sourcesContent":["import { requireNativeModule } from 'expo';\n\ninterface Spec {\n // addListener/removeListeners are present on the iOS RN event emitter module.\n // Android uses Expo Modules EventEmitter instead.\n addListener?(eventName: string): void;\n configure(publishableKey: string, bearerToken: string | null): Promise<void>;\n getClientToken(): Promise<string | null>;\n syncClientStateFromJs(\n deviceToken: string | null,\n sourceId: string | null,\n didChangeClient: boolean,\n didChangeDeviceToken: boolean,\n ): Promise<void>;\n removeListeners?(count: number): void;\n}\n\nexport default requireNativeModule<Spec>('ClerkExpo');\n"],"mappings":";;;AAiBA,sEAAyC,WAAW"}
|
|
@@ -3,7 +3,7 @@ export interface Spec extends TurboModule {
|
|
|
3
3
|
addListener(eventName: string): void;
|
|
4
4
|
configure(publishableKey: string, bearerToken: string | null): Promise<void>;
|
|
5
5
|
getClientToken(): Promise<string | null>;
|
|
6
|
-
|
|
6
|
+
syncClientStateFromJs(deviceToken: string | null, sourceId: string | null, didChangeClient: boolean, didChangeDeviceToken: boolean): Promise<void>;
|
|
7
7
|
removeListeners(count: number): void;
|
|
8
8
|
}
|
|
9
9
|
declare const _default: Spec | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeClerkModule.d.ts","sourceRoot":"","sources":["../../src/specs/NativeClerkModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IAGvC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,qBAAqB,CACnB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,
|
|
1
|
+
{"version":3,"file":"NativeClerkModule.d.ts","sourceRoot":"","sources":["../../src/specs/NativeClerkModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IAGvC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,qBAAqB,CACnB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,eAAe,EAAE,OAAO,EACxB,oBAAoB,EAAE,OAAO,GAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;IAGjB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;;AAED,wBAA0D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeClerkModule.js","names":["TurboModuleRegistry"],"sources":["../../src/specs/NativeClerkModule.ts"],"sourcesContent":["import type { TurboModule } from 'react-native';\nimport { TurboModuleRegistry } from 'react-native';\n\nexport interface Spec extends TurboModule {\n // Required by NativeEventEmitter for internal native client change events.\n // This is not part of the public @clerk/expo API.\n addListener(eventName: string): void;\n configure(publishableKey: string, bearerToken: string | null): Promise<void>;\n getClientToken(): Promise<string | null>;\n
|
|
1
|
+
{"version":3,"file":"NativeClerkModule.js","names":["TurboModuleRegistry"],"sources":["../../src/specs/NativeClerkModule.ts"],"sourcesContent":["import type { TurboModule } from 'react-native';\nimport { TurboModuleRegistry } from 'react-native';\n\nexport interface Spec extends TurboModule {\n // Required by NativeEventEmitter for internal native client change events.\n // This is not part of the public @clerk/expo API.\n addListener(eventName: string): void;\n configure(publishableKey: string, bearerToken: string | null): Promise<void>;\n getClientToken(): Promise<string | null>;\n syncClientStateFromJs(\n deviceToken: string | null,\n sourceId: string | null,\n didChangeClient: boolean,\n didChangeDeviceToken: boolean,\n ): Promise<void>;\n // Required by NativeEventEmitter for internal native client change events.\n // This is not part of the public @clerk/expo API.\n removeListeners(count: number): void;\n}\n\nexport default TurboModuleRegistry.get<Spec>('ClerkExpo');\n"],"mappings":";;;AAoBA,gCAAeA,iCAAoB,IAAU,WAAW"}
|
|
@@ -6,7 +6,7 @@ type ClerkExpoNativeModule = {
|
|
|
6
6
|
configure(publishableKey: string, bearerToken: string | null): Promise<void>;
|
|
7
7
|
getClientToken(): Promise<string | null>;
|
|
8
8
|
removeListeners?(count: number): void;
|
|
9
|
-
|
|
9
|
+
syncClientStateFromJs(deviceToken: string | null, sourceId: string | null, didChangeClient: boolean, didChangeDeviceToken: boolean): Promise<void>;
|
|
10
10
|
};
|
|
11
11
|
export declare const ClerkExpoModule: ClerkExpoNativeModule | null;
|
|
12
12
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-module.d.ts","sourceRoot":"","sources":["../../src/utils/native-module.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB,SAAqD,CAAC;AAEpF,KAAK,qBAAqB,GAAG;IAC3B,WAAW,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG;QAAE,MAAM,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IACjG,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,eAAe,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,qBAAqB,CACnB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,
|
|
1
|
+
{"version":3,"file":"native-module.d.ts","sourceRoot":"","sources":["../../src/utils/native-module.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,iBAAiB,SAAqD,CAAC;AAEpF,KAAK,qBAAqB,GAAG;IAC3B,WAAW,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG;QAAE,MAAM,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IACjG,SAAS,CAAC,cAAc,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,eAAe,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,qBAAqB,CACnB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,eAAe,EAAE,OAAO,EACxB,oBAAoB,EAAE,OAAO,GAC5B,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB,CAAC;AAgDF,eAAO,MAAM,eAAe,8BAAqB,CAAC"}
|
|
@@ -9,7 +9,7 @@ const isNativeSupported = react_native.Platform.OS === "ios" || react_native.Pla
|
|
|
9
9
|
function isClerkExpoModule(module) {
|
|
10
10
|
if (!module || typeof module !== "object") return false;
|
|
11
11
|
const maybeModule = module;
|
|
12
|
-
return typeof maybeModule.configure === "function" && typeof maybeModule.getClientToken === "function" && typeof maybeModule.
|
|
12
|
+
return typeof maybeModule.configure === "function" && typeof maybeModule.getClientToken === "function" && typeof maybeModule.syncClientStateFromJs === "function";
|
|
13
13
|
}
|
|
14
14
|
function loadNativeModule() {
|
|
15
15
|
if (!isNativeSupported) return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-module.js","names":["Platform","NativeClerkModule"],"sources":["../../src/utils/native-module.ts"],"sourcesContent":["import { Platform } from 'react-native';\n\nimport NativeClerkModule from '../specs/NativeClerkModule';\n\nexport const isNativeSupported = Platform.OS === 'ios' || Platform.OS === 'android';\n\ntype ClerkExpoNativeModule = {\n addListener?(eventName: string, listener?: (...args: unknown[]) => void): { remove: () => void };\n configure(publishableKey: string, bearerToken: string | null): Promise<void>;\n getClientToken(): Promise<string | null>;\n removeListeners?(count: number): void;\n
|
|
1
|
+
{"version":3,"file":"native-module.js","names":["Platform","NativeClerkModule"],"sources":["../../src/utils/native-module.ts"],"sourcesContent":["import { Platform } from 'react-native';\n\nimport NativeClerkModule from '../specs/NativeClerkModule';\n\nexport const isNativeSupported = Platform.OS === 'ios' || Platform.OS === 'android';\n\ntype ClerkExpoNativeModule = {\n addListener?(eventName: string, listener?: (...args: unknown[]) => void): { remove: () => void };\n configure(publishableKey: string, bearerToken: string | null): Promise<void>;\n getClientToken(): Promise<string | null>;\n removeListeners?(count: number): void;\n syncClientStateFromJs(\n deviceToken: string | null,\n sourceId: string | null,\n didChangeClient: boolean,\n didChangeDeviceToken: boolean,\n ): Promise<void>;\n};\n\nfunction isClerkExpoModule(module: unknown): module is ClerkExpoNativeModule {\n if (!module || typeof module !== 'object') {\n return false;\n }\n const maybeModule = module as Record<string, unknown>;\n\n return (\n typeof maybeModule.configure === 'function' &&\n typeof maybeModule.getClientToken === 'function' &&\n typeof maybeModule.syncClientStateFromJs === 'function'\n );\n}\n\nfunction loadNativeModule(): ClerkExpoNativeModule | null {\n if (!isNativeSupported) {\n return null;\n }\n let nativeModule: unknown = null;\n\n try {\n nativeModule = NativeClerkModule;\n } catch (e) {\n if (__DEV__) {\n console.warn('[ClerkExpo] Native module not available:', e);\n }\n }\n\n if (isClerkExpoModule(nativeModule)) {\n return nativeModule;\n }\n\n try {\n // Expo SDK 54 can expose installed modules through Expo's module registry even\n // when the generated TurboModule object is incomplete.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { requireNativeModule } = require('expo');\n const expoModule = requireNativeModule('ClerkExpo');\n return isClerkExpoModule(expoModule) ? expoModule : null;\n } catch (e) {\n if (__DEV__ && !nativeModule) {\n console.warn('[ClerkExpo] Native module not available:', e);\n }\n return null;\n }\n}\n\nexport const ClerkExpoModule = loadNativeModule();\n"],"mappings":";;;;;;;AAIA,MAAa,oBAAoBA,sBAAS,OAAO,SAASA,sBAAS,OAAO;AAe1E,SAAS,kBAAkB,QAAkD;CAC3E,IAAI,CAAC,UAAU,OAAO,WAAW,UAC/B,OAAO;CAET,MAAM,cAAc;CAEpB,OACE,OAAO,YAAY,cAAc,cACjC,OAAO,YAAY,mBAAmB,cACtC,OAAO,YAAY,0BAA0B;AAEjD;AAEA,SAAS,mBAAiD;CACxD,IAAI,CAAC,mBACH,OAAO;CAET,IAAI,eAAwB;CAE5B,IAAI;EACF,eAAeC;CACjB,SAAS,GAAG,CAIZ;CAEA,IAAI,kBAAkB,YAAY,GAChC,OAAO;CAGT,IAAI;EAIF,MAAM,EAAE,wBAAwB,QAAQ,MAAM;EAC9C,MAAM,aAAa,oBAAoB,WAAW;EAClD,OAAO,kBAAkB,UAAU,IAAI,aAAa;CACtD,SAAS,GAAG;EAIV,OAAO;CACT;AACF;AAEA,MAAa,kBAAkB,iBAAiB"}
|
package/ios/ClerkExpoModule.m
CHANGED
|
@@ -11,9 +11,10 @@ RCT_EXTERN_METHOD(configure:(NSString *)publishableKey
|
|
|
11
11
|
RCT_EXTERN_METHOD(getClientToken:(RCTPromiseResolveBlock)resolve
|
|
12
12
|
reject:(RCTPromiseRejectBlock)reject)
|
|
13
13
|
|
|
14
|
-
RCT_EXTERN_METHOD(
|
|
14
|
+
RCT_EXTERN_METHOD(syncClientStateFromJs:(id)deviceToken
|
|
15
15
|
sourceId:(id)sourceId
|
|
16
|
-
|
|
16
|
+
didChangeClient:(BOOL)didChangeClient
|
|
17
|
+
didChangeDeviceToken:(BOOL)didChangeDeviceToken
|
|
17
18
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
18
19
|
reject:(RCTPromiseRejectBlock)reject)
|
|
19
20
|
|
|
@@ -86,23 +86,23 @@ class ClerkExpoModule: RCTEventEmitter {
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
// MARK: -
|
|
89
|
+
// MARK: - syncClientStateFromJs
|
|
90
90
|
|
|
91
|
-
@objc func
|
|
91
|
+
@objc func syncClientStateFromJs(_ deviceToken: Any?,
|
|
92
92
|
sourceId: Any?,
|
|
93
|
-
|
|
93
|
+
didChangeClient: Bool,
|
|
94
|
+
didChangeDeviceToken: Bool,
|
|
94
95
|
resolve: @escaping RCTPromiseResolveBlock,
|
|
95
96
|
reject: @escaping RCTPromiseRejectBlock) {
|
|
96
|
-
let
|
|
97
|
+
let normalizedDeviceToken = deviceToken as? String
|
|
97
98
|
let normalizedSourceId = sourceId as? String
|
|
98
|
-
let defaultShouldRefreshClient = normalizedClientToken?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true
|
|
99
|
-
let normalizedShouldRefreshClient = (shouldRefreshClient as? Bool) ?? defaultShouldRefreshClient
|
|
100
99
|
Task {
|
|
101
100
|
do {
|
|
102
|
-
try await ClerkNativeBridge.shared.
|
|
103
|
-
|
|
101
|
+
try await ClerkNativeBridge.shared.syncClientStateFromJs(
|
|
102
|
+
deviceToken: normalizedDeviceToken,
|
|
104
103
|
sourceId: normalizedSourceId,
|
|
105
|
-
|
|
104
|
+
didChangeClient: didChangeClient,
|
|
105
|
+
didChangeDeviceToken: didChangeDeviceToken
|
|
106
106
|
)
|
|
107
107
|
resolve(nil)
|
|
108
108
|
} catch {
|
|
@@ -56,6 +56,13 @@ final class ClerkNativeBridge {
|
|
|
56
56
|
let deviceToken: String?
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
private struct ClientStateChanges {
|
|
60
|
+
let client: Bool
|
|
61
|
+
let deviceToken: Bool
|
|
62
|
+
|
|
63
|
+
static let all = ClientStateChanges(client: true, deviceToken: true)
|
|
64
|
+
}
|
|
65
|
+
|
|
59
66
|
/// Resolves the keychain service name, checking ClerkKeychainService in Info.plist first
|
|
60
67
|
/// (for extension apps sharing a keychain group), then falling back to the bundle identifier.
|
|
61
68
|
private static var keychainService: String? {
|
|
@@ -104,7 +111,7 @@ final class ClerkNativeBridge {
|
|
|
104
111
|
@MainActor
|
|
105
112
|
private static func emitClientChangedIfReceivedToken(_ bearerToken: String?) {
|
|
106
113
|
guard let token = bearerToken, !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
|
|
107
|
-
Self.emitClientChanged(Self.clientChangedPayload())
|
|
114
|
+
Self.emitClientChanged(Self.clientChangedPayload(changes: .init(client: false, deviceToken: true)))
|
|
108
115
|
}
|
|
109
116
|
|
|
110
117
|
@MainActor
|
|
@@ -130,9 +137,14 @@ final class ClerkNativeBridge {
|
|
|
130
137
|
guard let self, generation == self.clientObservationGeneration else { return }
|
|
131
138
|
|
|
132
139
|
let newClientState = Self.clientStateSnapshot()
|
|
133
|
-
if
|
|
140
|
+
if let previousClientState = self.lastObservedClientState, newClientState != previousClientState {
|
|
134
141
|
self.lastObservedClientState = newClientState
|
|
135
|
-
let payload = Self.clientChangedPayload(
|
|
142
|
+
let payload = Self.clientChangedPayload(
|
|
143
|
+
changes: .init(
|
|
144
|
+
client: newClientState.client != previousClientState.client,
|
|
145
|
+
deviceToken: newClientState.deviceToken != previousClientState.deviceToken
|
|
146
|
+
)
|
|
147
|
+
)
|
|
136
148
|
Self.emitClientChanged(payload)
|
|
137
149
|
}
|
|
138
150
|
|
|
@@ -152,9 +164,13 @@ final class ClerkNativeBridge {
|
|
|
152
164
|
}
|
|
153
165
|
|
|
154
166
|
@MainActor
|
|
155
|
-
private static func clientChangedPayload(sourceId: String? = nil) -> [String: Any] {
|
|
167
|
+
private static func clientChangedPayload(sourceId: String? = nil, changes: ClientStateChanges = .all) -> [String: Any] {
|
|
156
168
|
var payload: [String: Any] = [:]
|
|
157
|
-
payload["
|
|
169
|
+
payload["changed"] = [
|
|
170
|
+
"client": changes.client,
|
|
171
|
+
"deviceToken": changes.deviceToken,
|
|
172
|
+
]
|
|
173
|
+
payload["deviceToken"] = Clerk.shared.deviceToken ?? NSNull()
|
|
158
174
|
if let sourceId, !sourceId.isEmpty {
|
|
159
175
|
payload["sourceId"] = sourceId
|
|
160
176
|
}
|
|
@@ -187,7 +203,7 @@ final class ClerkNativeBridge {
|
|
|
187
203
|
@MainActor
|
|
188
204
|
private static func waitForLoadedClient() async {
|
|
189
205
|
// Wait for Clerk to finish loading client state from cached data + API refresh.
|
|
190
|
-
// The bridge sync contract is
|
|
206
|
+
// The bridge sync contract is device-token based, not session based.
|
|
191
207
|
for _ in 0..<clerkLoadMaxAttempts {
|
|
192
208
|
if Clerk.shared.isLoaded {
|
|
193
209
|
return
|
|
@@ -256,23 +272,39 @@ final class ClerkNativeBridge {
|
|
|
256
272
|
}
|
|
257
273
|
|
|
258
274
|
@MainActor
|
|
259
|
-
func
|
|
275
|
+
func syncClientStateFromJs(
|
|
276
|
+
deviceToken: String?,
|
|
277
|
+
sourceId: String?,
|
|
278
|
+
didChangeClient: Bool,
|
|
279
|
+
didChangeDeviceToken: Bool
|
|
280
|
+
) async throws {
|
|
260
281
|
guard Self.clerkConfigured else { return }
|
|
261
282
|
|
|
262
|
-
|
|
283
|
+
let previousClientState = Self.clientStateSnapshot()
|
|
284
|
+
|
|
285
|
+
if didChangeDeviceToken, let token = deviceToken?.trimmingCharacters(in: .whitespacesAndNewlines), !token.isEmpty {
|
|
263
286
|
if Clerk.shared.deviceToken != token {
|
|
264
287
|
_ = try await Clerk.shared.updateDeviceToken(token)
|
|
265
288
|
await Self.waitForLoadedClient()
|
|
266
289
|
}
|
|
267
290
|
}
|
|
268
291
|
|
|
269
|
-
if
|
|
292
|
+
if didChangeClient || didChangeDeviceToken {
|
|
270
293
|
_ = try await Clerk.shared.refreshClient()
|
|
271
294
|
await Self.waitForLoadedClient()
|
|
272
295
|
}
|
|
273
296
|
|
|
274
|
-
|
|
275
|
-
|
|
297
|
+
let newClientState = Self.clientStateSnapshot()
|
|
298
|
+
lastObservedClientState = newClientState
|
|
299
|
+
Self.emitClientChanged(
|
|
300
|
+
Self.clientChangedPayload(
|
|
301
|
+
sourceId: sourceId,
|
|
302
|
+
changes: .init(
|
|
303
|
+
client: newClientState.client != previousClientState.client,
|
|
304
|
+
deviceToken: newClientState.deviceToken != previousClientState.deviceToken
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
)
|
|
276
308
|
}
|
|
277
309
|
|
|
278
310
|
private static func postConfiguredNotification() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clerk/expo",
|
|
3
|
-
"version": "3.5.0
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Clerk React Native/Expo library",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -104,9 +104,9 @@
|
|
|
104
104
|
"base-64": "^1.0.0",
|
|
105
105
|
"react-native-url-polyfill": "2.0.0",
|
|
106
106
|
"tslib": "2.8.1",
|
|
107
|
-
"@clerk/clerk-js": "6.18.
|
|
108
|
-
"@clerk/react": "6.10.
|
|
109
|
-
"@clerk/shared": "4.19.
|
|
107
|
+
"@clerk/clerk-js": "^6.18.1",
|
|
108
|
+
"@clerk/react": "^6.10.3",
|
|
109
|
+
"@clerk/shared": "^4.19.1"
|
|
110
110
|
},
|
|
111
111
|
"devDependencies": {
|
|
112
112
|
"@expo/config-plugins": "^54.0.4",
|
|
@@ -120,10 +120,10 @@
|
|
|
120
120
|
"expo-secure-store": "^12.8.1",
|
|
121
121
|
"expo-web-browser": "^12.8.2",
|
|
122
122
|
"react-native": "^0.85.2",
|
|
123
|
-
"@clerk/expo-passkeys": "1.1.
|
|
123
|
+
"@clerk/expo-passkeys": "1.1.7"
|
|
124
124
|
},
|
|
125
125
|
"peerDependencies": {
|
|
126
|
-
"@clerk/expo-passkeys": "
|
|
126
|
+
"@clerk/expo-passkeys": ">=0.0.6",
|
|
127
127
|
"expo": ">=53 <57",
|
|
128
128
|
"expo-apple-authentication": ">=7.0.0",
|
|
129
129
|
"expo-auth-session": ">=5",
|
|
@@ -6,10 +6,11 @@ interface Spec {
|
|
|
6
6
|
addListener?(eventName: string): void;
|
|
7
7
|
configure(publishableKey: string, bearerToken: string | null): Promise<void>;
|
|
8
8
|
getClientToken(): Promise<string | null>;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
syncClientStateFromJs(
|
|
10
|
+
deviceToken: string | null,
|
|
11
11
|
sourceId: string | null,
|
|
12
|
-
|
|
12
|
+
didChangeClient: boolean,
|
|
13
|
+
didChangeDeviceToken: boolean,
|
|
13
14
|
): Promise<void>;
|
|
14
15
|
removeListeners?(count: number): void;
|
|
15
16
|
}
|
|
@@ -7,10 +7,11 @@ export interface Spec extends TurboModule {
|
|
|
7
7
|
addListener(eventName: string): void;
|
|
8
8
|
configure(publishableKey: string, bearerToken: string | null): Promise<void>;
|
|
9
9
|
getClientToken(): Promise<string | null>;
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
syncClientStateFromJs(
|
|
11
|
+
deviceToken: string | null,
|
|
12
12
|
sourceId: string | null,
|
|
13
|
-
|
|
13
|
+
didChangeClient: boolean,
|
|
14
|
+
didChangeDeviceToken: boolean,
|
|
14
15
|
): Promise<void>;
|
|
15
16
|
// Required by NativeEventEmitter for internal native client change events.
|
|
16
17
|
// This is not part of the public @clerk/expo API.
|