@clerk/electron 0.1.8-snapshot.v20261029220359 → 0.1.8

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.
@@ -26,7 +26,7 @@ function createClerkInstance(publishableKey, passkeys) {
26
26
  clerk.__internal_onBeforeRequest(async (request) => {
27
27
  request.credentials = "omit";
28
28
  request.url?.searchParams.append("_is_native", "1");
29
- request.url?.searchParams.append("_electron_sdk_version", "0.1.8-snapshot.v20261029220359");
29
+ request.url?.searchParams.append("_electron_sdk_version", "0.1.8");
30
30
  const token = await window.__clerk_internal_electron?.tokenCache.getToken(CLERK_CLIENT_JWT_KEY);
31
31
  if (token) {
32
32
  const headers = new Headers(request.headers);
@@ -51,7 +51,7 @@ function createClerkInstance(publishableKey, passkeys) {
51
51
  //#region src/react/index.tsx
52
52
  const SDK_METADATA = {
53
53
  name: "@clerk/electron",
54
- version: "0.1.8-snapshot.v20261029220359"
54
+ version: "0.1.8"
55
55
  };
56
56
  let cachedClerkUI = null;
57
57
  function loadClerkUI(publishableKey, props) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["Clerk","ALLOWED_PROTOCOLS","ReactClerkProvider"],"sources":["../../../src/react/create-clerk-instance.ts","../../../src/react/index.tsx"],"sourcesContent":["import { Clerk } from '@clerk/clerk-js';\n\n// Type-only import: the passkeys module is only bundled when the app imports\n// `@clerk/electron/passkeys` itself.\nimport type { PasskeySupport } from '../passkeys';\n\nconst CLERK_CLIENT_JWT_KEY = '__clerk_client_jwt';\n\ntype ClerkInstance = InstanceType<typeof Clerk>;\n\nlet cached: { instance: ClerkInstance; publishableKey: string } | null = null;\n\nfunction attachPasskeys(clerk: ClerkInstance, passkeys: PasskeySupport): void {\n clerk.__internal_createPublicCredentials = passkeys.create;\n clerk.__internal_getPublicCredentials = passkeys.get;\n clerk.__internal_isWebAuthnSupported = passkeys.isSupported;\n clerk.__internal_isWebAuthnAutofillSupported = passkeys.isAutoFillSupported;\n clerk.__internal_isWebAuthnPlatformAuthenticatorSupported = passkeys.isPlatformAuthenticatorSupported;\n}\n\nexport function createClerkInstance(publishableKey: string, passkeys?: PasskeySupport): ClerkInstance {\n if (cached?.publishableKey === publishableKey) {\n if (passkeys) {\n attachPasskeys(cached.instance, passkeys);\n }\n return cached.instance;\n }\n\n const clerk = new Clerk(publishableKey);\n\n if (passkeys) {\n attachPasskeys(clerk, passkeys);\n }\n\n clerk.__internal_onBeforeRequest(async request => {\n request.credentials = 'omit';\n request.url?.searchParams.append('_is_native', '1');\n request.url?.searchParams.append('_electron_sdk_version', PACKAGE_VERSION);\n\n const token = await window.__clerk_internal_electron?.tokenCache.getToken(CLERK_CLIENT_JWT_KEY);\n if (token) {\n const headers = new Headers(request.headers);\n headers.set('Authorization', `Bearer ${token}`);\n request.headers = headers;\n }\n });\n\n clerk.__internal_onAfterResponse(async (_request, response) => {\n const authorization = (response as Response).headers.get('Authorization');\n if (!authorization) {\n return;\n }\n\n const token = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : authorization;\n await window.__clerk_internal_electron?.tokenCache.saveToken(CLERK_CLIENT_JWT_KEY, token);\n });\n\n cached = { instance: clerk, publishableKey };\n return clerk;\n}\n","import type { ClerkProviderProps as ReactClerkProviderProps } from '@clerk/react';\nimport { InternalClerkProvider as ReactClerkProvider } from '@clerk/react/internal';\nimport { isVirtualRouterPath } from '@clerk/shared/internal/clerk-js/url';\nimport { ALLOWED_PROTOCOLS } from '@clerk/shared/internal/clerk-js/windowNavigate';\nimport { loadClerkUIScript } from '@clerk/shared/loadClerkJsScript';\nimport type { ClerkUIConstructor } from '@clerk/shared/ui';\nimport type { ReactNode } from 'react';\n\nimport type { PasskeySupport } from '../passkeys';\nimport { createClerkInstance } from './create-clerk-instance';\n\ntype ClerkOAuthTransport = NonNullable<ReactClerkProviderProps['__internal_oauthTransport']>;\n\nexport type ClerkProviderProps = Omit<\n ReactClerkProviderProps,\n 'Clerk' | 'children' | 'publishableKey' | 'standardBrowser' | 'ui' | '__internal_oauthTransport'\n> & {\n children: ReactNode;\n /**\n * Your Clerk publishable key, available in the Clerk Dashboard.\n */\n publishableKey: string;\n /**\n * Enables passkey support. Pass the `passkeys` export from `@clerk/electron/passkeys`;\n * when omitted, no passkey code is bundled or initialized.\n *\n * @example\n * ```tsx\n * import { passkeys } from '@clerk/electron/passkeys';\n *\n * <ClerkProvider publishableKey={publishableKey} passkeys={passkeys} />\n * ```\n */\n passkeys?: PasskeySupport;\n};\n\nconst SDK_METADATA = {\n name: PACKAGE_NAME,\n version: PACKAGE_VERSION,\n};\n\nlet cachedClerkUI: { promise: Promise<ClerkUIConstructor>; publishableKey: string } | null = null;\n\nfunction loadClerkUI(publishableKey: string, props: Partial<ClerkProviderProps>): Promise<ClerkUIConstructor> {\n if (cachedClerkUI?.publishableKey === publishableKey) {\n return cachedClerkUI.promise;\n }\n\n // Undocumented escape hatch for self-hosting/proxying the UI bundle; not part of the public props.\n const { __internal_clerkUIUrl, __internal_clerkUIVersion } = props as {\n __internal_clerkUIUrl?: string;\n __internal_clerkUIVersion?: string;\n };\n\n const promise = loadClerkUIScript({\n publishableKey,\n proxyUrl: typeof props.proxyUrl === 'string' ? props.proxyUrl : undefined,\n domain: typeof props.domain === 'string' ? props.domain : undefined,\n nonce: props.nonce,\n __internal_clerkUIUrl,\n __internal_clerkUIVersion,\n }).then(() => {\n if (!window.__internal_ClerkUICtor) {\n throw new Error(\n 'Clerk: Failed to load Clerk UI from the CDN. Ensure your Content Security Policy allows the Clerk Frontend API host in `script-src`. Contact support@clerk.com.',\n );\n }\n return window.__internal_ClerkUICtor;\n });\n\n cachedClerkUI = { promise, publishableKey };\n return promise;\n}\n\nfunction createOAuthTransport(): ClerkOAuthTransport | undefined {\n const bridge = window.__clerk_internal_electron?.oauthTransport;\n\n if (!bridge) {\n return undefined;\n }\n\n return {\n getRedirectUrl: () => bridge.getRedirectUrl(),\n open: url => bridge.open(url.href),\n };\n}\n\ntype ClerkRouterFn = NonNullable<ReactClerkProviderProps['routerPush']>;\n\n/** Always supplied, so clerk-js never reaches `window.location` and reloads the renderer. */\nfunction createRouterHandlers(\n routerPush: ClerkRouterFn | undefined,\n routerReplace: ClerkRouterFn | undefined,\n): { routerPush: ClerkRouterFn; routerReplace: ClerkRouterFn } {\n const wrap =\n (delegate: ClerkRouterFn | undefined): ClerkRouterFn =>\n (to, metadata) => {\n if (isVirtualRouterPath(to)) {\n return;\n }\n\n if (delegate) {\n return delegate(to, metadata);\n }\n\n metadata?.windowNavigate(to);\n };\n\n return { routerPush: wrap(routerPush), routerReplace: wrap(routerReplace) };\n}\n\n/**\n * Infer the custom renderer scheme registered with `createClerkBridge({ renderer })`.\n * Built-in Clerk protocols and local file renderers are not inferred.\n */\nfunction defaultAllowedRedirectProtocols(): string[] | undefined {\n const protocol = typeof window !== 'undefined' ? window.location?.protocol : undefined;\n\n if (!protocol || ALLOWED_PROTOCOLS.includes(protocol) || protocol === 'file:') {\n return undefined;\n }\n\n return [protocol];\n}\n\nexport function ClerkProvider({\n children,\n publishableKey,\n passkeys,\n allowedRedirectProtocols,\n routerPush,\n routerReplace,\n ...props\n}: ClerkProviderProps): JSX.Element {\n const clerk = createClerkInstance(publishableKey, passkeys);\n const oauthTransport = createOAuthTransport();\n const clerkUI = loadClerkUI(publishableKey, props);\n const routerHandlers = createRouterHandlers(routerPush, routerReplace);\n\n return (\n <ReactClerkProvider\n {...props}\n {...routerHandlers}\n sdkMetadata={SDK_METADATA}\n Clerk={clerk}\n __internal_oauthTransport={oauthTransport}\n allowedRedirectProtocols={allowedRedirectProtocols ?? defaultAllowedRedirectProtocols()}\n publishableKey={publishableKey}\n standardBrowser={false}\n ui={{ ClerkUI: clerkUI }}\n >\n {children}\n </ReactClerkProvider>\n );\n}\n\nexport * from '@clerk/react';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["Clerk","ALLOWED_PROTOCOLS","ReactClerkProvider"],"sources":["../../../src/react/create-clerk-instance.ts","../../../src/react/index.tsx"],"sourcesContent":["import { Clerk } from '@clerk/clerk-js';\n\n// Type-only import: the passkeys module is only bundled when the app imports\n// `@clerk/electron/passkeys` itself.\nimport type { PasskeySupport } from '../passkeys';\n\nconst CLERK_CLIENT_JWT_KEY = '__clerk_client_jwt';\n\ntype ClerkInstance = InstanceType<typeof Clerk>;\n\nlet cached: { instance: ClerkInstance; publishableKey: string } | null = null;\n\nfunction attachPasskeys(clerk: ClerkInstance, passkeys: PasskeySupport): void {\n clerk.__internal_createPublicCredentials = passkeys.create;\n clerk.__internal_getPublicCredentials = passkeys.get;\n clerk.__internal_isWebAuthnSupported = passkeys.isSupported;\n clerk.__internal_isWebAuthnAutofillSupported = passkeys.isAutoFillSupported;\n clerk.__internal_isWebAuthnPlatformAuthenticatorSupported = passkeys.isPlatformAuthenticatorSupported;\n}\n\nexport function createClerkInstance(publishableKey: string, passkeys?: PasskeySupport): ClerkInstance {\n if (cached?.publishableKey === publishableKey) {\n if (passkeys) {\n attachPasskeys(cached.instance, passkeys);\n }\n return cached.instance;\n }\n\n const clerk = new Clerk(publishableKey);\n\n if (passkeys) {\n attachPasskeys(clerk, passkeys);\n }\n\n clerk.__internal_onBeforeRequest(async request => {\n request.credentials = 'omit';\n request.url?.searchParams.append('_is_native', '1');\n request.url?.searchParams.append('_electron_sdk_version', PACKAGE_VERSION);\n\n const token = await window.__clerk_internal_electron?.tokenCache.getToken(CLERK_CLIENT_JWT_KEY);\n if (token) {\n const headers = new Headers(request.headers);\n headers.set('Authorization', `Bearer ${token}`);\n request.headers = headers;\n }\n });\n\n clerk.__internal_onAfterResponse(async (_request, response) => {\n const authorization = (response as Response).headers.get('Authorization');\n if (!authorization) {\n return;\n }\n\n const token = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : authorization;\n await window.__clerk_internal_electron?.tokenCache.saveToken(CLERK_CLIENT_JWT_KEY, token);\n });\n\n cached = { instance: clerk, publishableKey };\n return clerk;\n}\n","import type { ClerkProviderProps as ReactClerkProviderProps } from '@clerk/react';\nimport { InternalClerkProvider as ReactClerkProvider } from '@clerk/react/internal';\nimport { isVirtualRouterPath } from '@clerk/shared/internal/clerk-js/url';\nimport { ALLOWED_PROTOCOLS } from '@clerk/shared/internal/clerk-js/windowNavigate';\nimport { loadClerkUIScript } from '@clerk/shared/loadClerkJsScript';\nimport type { ClerkUIConstructor } from '@clerk/shared/ui';\nimport type { ReactNode } from 'react';\n\nimport type { PasskeySupport } from '../passkeys';\nimport { createClerkInstance } from './create-clerk-instance';\n\ntype ClerkOAuthTransport = NonNullable<ReactClerkProviderProps['__internal_oauthTransport']>;\n\nexport type ClerkProviderProps = Omit<\n ReactClerkProviderProps,\n 'Clerk' | 'children' | 'publishableKey' | 'standardBrowser' | 'ui' | '__internal_oauthTransport'\n> & {\n children: ReactNode;\n /**\n * Your Clerk publishable key, available in the Clerk Dashboard.\n */\n publishableKey: string;\n /**\n * Enables passkey support. Pass the `passkeys` export from `@clerk/electron/passkeys`;\n * when omitted, no passkey code is bundled or initialized.\n *\n * @example\n * ```tsx\n * import { passkeys } from '@clerk/electron/passkeys';\n *\n * <ClerkProvider publishableKey={publishableKey} passkeys={passkeys} />\n * ```\n */\n passkeys?: PasskeySupport;\n};\n\nconst SDK_METADATA = {\n name: PACKAGE_NAME,\n version: PACKAGE_VERSION,\n};\n\nlet cachedClerkUI: { promise: Promise<ClerkUIConstructor>; publishableKey: string } | null = null;\n\nfunction loadClerkUI(publishableKey: string, props: Partial<ClerkProviderProps>): Promise<ClerkUIConstructor> {\n if (cachedClerkUI?.publishableKey === publishableKey) {\n return cachedClerkUI.promise;\n }\n\n // Undocumented escape hatch for self-hosting/proxying the UI bundle; not part of the public props.\n const { __internal_clerkUIUrl, __internal_clerkUIVersion } = props as {\n __internal_clerkUIUrl?: string;\n __internal_clerkUIVersion?: string;\n };\n\n const promise = loadClerkUIScript({\n publishableKey,\n proxyUrl: typeof props.proxyUrl === 'string' ? props.proxyUrl : undefined,\n domain: typeof props.domain === 'string' ? props.domain : undefined,\n nonce: props.nonce,\n __internal_clerkUIUrl,\n __internal_clerkUIVersion,\n }).then(() => {\n if (!window.__internal_ClerkUICtor) {\n throw new Error(\n 'Clerk: Failed to load Clerk UI from the CDN. Ensure your Content Security Policy allows the Clerk Frontend API host in `script-src`. Contact support@clerk.com.',\n );\n }\n return window.__internal_ClerkUICtor;\n });\n\n cachedClerkUI = { promise, publishableKey };\n return promise;\n}\n\nfunction createOAuthTransport(): ClerkOAuthTransport | undefined {\n const bridge = window.__clerk_internal_electron?.oauthTransport;\n\n if (!bridge) {\n return undefined;\n }\n\n return {\n getRedirectUrl: () => bridge.getRedirectUrl(),\n open: url => bridge.open(url.href),\n };\n}\n\ntype ClerkRouterFn = NonNullable<ReactClerkProviderProps['routerPush']>;\n\n/** Always supplied, so clerk-js never reaches `window.location` and reloads the renderer. */\nfunction createRouterHandlers(\n routerPush: ClerkRouterFn | undefined,\n routerReplace: ClerkRouterFn | undefined,\n): { routerPush: ClerkRouterFn; routerReplace: ClerkRouterFn } {\n const wrap =\n (delegate: ClerkRouterFn | undefined): ClerkRouterFn =>\n (to, metadata) => {\n if (isVirtualRouterPath(to)) {\n return;\n }\n\n if (delegate) {\n return delegate(to, metadata);\n }\n\n metadata?.windowNavigate(to);\n };\n\n return { routerPush: wrap(routerPush), routerReplace: wrap(routerReplace) };\n}\n\n/**\n * Infer the custom renderer scheme registered with `createClerkBridge({ renderer })`.\n * Built-in Clerk protocols and local file renderers are not inferred.\n */\nfunction defaultAllowedRedirectProtocols(): string[] | undefined {\n const protocol = typeof window !== 'undefined' ? window.location?.protocol : undefined;\n\n if (!protocol || ALLOWED_PROTOCOLS.includes(protocol) || protocol === 'file:') {\n return undefined;\n }\n\n return [protocol];\n}\n\nexport function ClerkProvider({\n children,\n publishableKey,\n passkeys,\n allowedRedirectProtocols,\n routerPush,\n routerReplace,\n ...props\n}: ClerkProviderProps): JSX.Element {\n const clerk = createClerkInstance(publishableKey, passkeys);\n const oauthTransport = createOAuthTransport();\n const clerkUI = loadClerkUI(publishableKey, props);\n const routerHandlers = createRouterHandlers(routerPush, routerReplace);\n\n return (\n <ReactClerkProvider\n {...props}\n {...routerHandlers}\n sdkMetadata={SDK_METADATA}\n Clerk={clerk}\n __internal_oauthTransport={oauthTransport}\n allowedRedirectProtocols={allowedRedirectProtocols ?? defaultAllowedRedirectProtocols()}\n publishableKey={publishableKey}\n standardBrowser={false}\n ui={{ ClerkUI: clerkUI }}\n >\n {children}\n </ReactClerkProvider>\n );\n}\n\nexport * from '@clerk/react';\n"],"mappings":"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"}
@@ -27,6 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
27
27
 
28
28
  //#endregion
29
29
  let electron = require("electron");
30
+ let node_fs = require("node:fs");
30
31
  let electron_store = require("electron-store");
31
32
  electron_store = __toESM(electron_store);
32
33
 
@@ -98,8 +99,12 @@ async function resolveCipher() {
98
99
  function storage(options = {}) {
99
100
  const store = new electron_store.default({
100
101
  name: options.name ?? "clerk-tokens",
102
+ configFileMode: 384,
101
103
  ...options.path ? { cwd: options.path } : {}
102
104
  });
105
+ try {
106
+ (0, node_fs.chmodSync)(store.path, 384);
107
+ } catch {}
103
108
  const memoryFallback = /* @__PURE__ */ new Map();
104
109
  const mutationVersions = /* @__PURE__ */ new Map();
105
110
  const beginMutation = (key) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["safeStorage","Store"],"sources":["../../../src/storage/index.ts"],"sourcesContent":["import { safeStorage } from 'electron';\nimport Store from 'electron-store';\n\nimport type { TokenStorage } from '../shared/types';\n\nexport type StorageOptions = {\n /**\n * The name of the file (without extension) used to persist tokens.\n *\n * @default 'clerk-tokens'\n */\n name?: string;\n /**\n * The directory in which the token file is stored. Maps to `electron-store`'s `cwd` option.\n * When omitted, the OS default user config directory is used.\n */\n path?: string;\n /**\n * When OS-level encryption is unavailable (e.g. a Linux machine without a keyring), tokens are\n * not persisted by default, which signs the user out on the next app launch. Set this to `true`\n * to instead persist tokens **unencrypted** in that scenario, keeping the user signed in across\n * restarts at the cost of storing tokens in plaintext on disk.\n *\n * @default false\n */\n unencryptedFallback?: boolean;\n};\n\n/**\n * Marks a value that was encrypted via {@link safeStorage}; the remainder is base64 ciphertext.\n * values will be stored as: `enc:<encrypted value>`\n */\nconst ENCRYPTED_PREFIX = 'enc:';\n/**\n * Marks a value persisted unencrypted via the `unencryptedFallback` option.\n * values will be stored as: `raw:<value>`\n */\nconst RAW_PREFIX = 'raw:';\n\ntype Cipher = {\n encrypt: (value: string) => Promise<string>;\n /**\n * Decrypts a base64 payload. `shouldReEncrypt` is `true` (async backend only) when the OS key\n * has rotated and the payload should be re-encrypted with the new key.\n */\n decrypt: (payload: string) => Promise<{ value: string; shouldReEncrypt: boolean }>;\n};\n\ntype AsyncSafeStorage = {\n encryptStringAsync: (plainText: string) => Promise<Buffer>;\n decryptStringAsync: (encrypted: Buffer) => Promise<{ result: string; shouldReEncrypt: boolean }>;\n isAsyncEncryptionAvailable: () => Promise<boolean>;\n};\n\nconst syncCipher: Cipher = {\n encrypt: value => Promise.resolve(safeStorage.encryptString(value).toString('base64')),\n decrypt: payload =>\n Promise.resolve({ value: safeStorage.decryptString(Buffer.from(payload, 'base64')), shouldReEncrypt: false }),\n};\n\nfunction hasAsyncSafeStorage(storage: typeof safeStorage): storage is typeof safeStorage & AsyncSafeStorage {\n const candidate = storage as Partial<AsyncSafeStorage>;\n\n return (\n typeof candidate.encryptStringAsync === 'function' &&\n typeof candidate.decryptStringAsync === 'function' &&\n typeof candidate.isAsyncEncryptionAvailable === 'function'\n );\n}\n\nfunction createAsyncCipher(storage: AsyncSafeStorage): Cipher {\n return {\n encrypt: async value => (await storage.encryptStringAsync(value)).toString('base64'),\n decrypt: async payload => {\n const { result, shouldReEncrypt } = await storage.decryptStringAsync(Buffer.from(payload, 'base64'));\n return { value: result, shouldReEncrypt };\n },\n };\n}\n\n/**\n * Resolves the crypto backend to use, or `null` when no OS encryption is currently available.\n */\nasync function resolveCipher(): Promise<Cipher | null> {\n // Prefer the async API, but only when the full optional function surface exists.\n if (hasAsyncSafeStorage(safeStorage)) {\n try {\n if (await safeStorage.isAsyncEncryptionAvailable()) {\n return createAsyncCipher(safeStorage);\n }\n } catch {\n /* fall through to the synchronous API */\n }\n }\n\n // The synchronous API blocks the calling thread on the OS prompt during the first encrypt/decrypt,\n if (typeof safeStorage.isEncryptionAvailable === 'function' && safeStorage.isEncryptionAvailable()) {\n return syncCipher;\n }\n\n return null;\n}\n\n/**\n * Creates a secure {@link TokenStorage} adapter for the Electron main process.\n *\n * Tokens are persisted with `electron-store` and encrypted at rest using Electron's\n * {@link safeStorage} API, which is backed by the OS keystore (Keychain on macOS, DPAPI on\n * Windows, libsecret/kwallet on Linux). It uses Electron 42's async `safeStorage` API only when it\n * reports itself available (which generally requires a code-signed app) and otherwise falls back to\n * the synchronous API. Pass the result to `createClerkBridge({ storage: storage() })`.\n *\n * Behavior is secure by default: when OS encryption is unavailable the adapter does not persist\n * tokens (the user will be signed out on restart) unless {@link StorageOptions.unencryptedFallback}\n * is enabled. Undecryptable entries return `null`.\n *\n * @example\n * ```ts\n * import { createClerkBridge } from '@clerk/electron';\n * import { storage } from '@clerk/electron/storage';\n *\n * createClerkBridge({ storage: storage({ name: 'my-app-tokens' }) });\n * ```\n */\nexport function storage(options: StorageOptions = {}): TokenStorage {\n const store = new Store<Record<string, string>>({\n name: options.name ?? 'clerk-tokens',\n ...(options.path ? { cwd: options.path } : {}),\n });\n const memoryFallback = new Map<string, string>();\n // IPC requests can resolve out of order, so only the latest mutation may update storage.\n const mutationVersions = new Map<string, number>();\n\n const beginMutation = (key: string) => {\n const version = (mutationVersions.get(key) ?? 0) + 1;\n mutationVersions.set(key, version);\n return version;\n };\n const isCurrentMutation = (key: string, version: number) => (mutationVersions.get(key) ?? 0) === version;\n\n let cachedCipher: Cipher | null = null;\n let resolving: Promise<Cipher | null> | null = null;\n const getCipher = (): Promise<Cipher | null> => {\n if (cachedCipher) {\n return Promise.resolve(cachedCipher);\n }\n resolving ??= resolveCipher().then(resolved => {\n resolving = null;\n if (resolved) {\n cachedCipher = resolved;\n }\n return resolved;\n });\n return resolving;\n };\n\n let warned = false;\n const warnOnce = (message: string) => {\n if (warned) {\n return;\n }\n warned = true;\n console.warn(message);\n };\n\n return {\n async getItem(key) {\n const fallback = memoryFallback.get(key);\n if (fallback !== undefined) {\n return fallback;\n }\n\n const stored = store.get(key);\n\n if (!stored) {\n return null;\n }\n\n if (stored.startsWith(RAW_PREFIX)) {\n return stored.slice(RAW_PREFIX.length);\n }\n\n if (stored.startsWith(ENCRYPTED_PREFIX)) {\n const mutationVersion = mutationVersions.get(key) ?? 0;\n const cipher = await getCipher();\n\n // No usable OS encryption, preserve entry.\n if (!cipher) {\n return null;\n }\n\n const payload = stored.slice(ENCRYPTED_PREFIX.length);\n\n try {\n const { value, shouldReEncrypt } = await cipher.decrypt(payload);\n\n if (shouldReEncrypt) {\n // OS key has rotated, persist with new value\n try {\n const encrypted = await cipher.encrypt(value);\n if (isCurrentMutation(key, mutationVersion)) {\n store.set(key, ENCRYPTED_PREFIX + encrypted);\n }\n } catch {\n // keep the existing payload; it still decrypts for now\n }\n }\n\n return value;\n } catch {\n // Decryption failed, preserve the entry, new write on next sign-in.\n return null;\n }\n }\n\n // Unknown or unrecognized format, drop the entry so we don't repeatedly fail on it.\n store.delete(key);\n return null;\n },\n async setItem(key, value) {\n const mutationVersion = beginMutation(key);\n const cipher = await getCipher();\n\n if (!isCurrentMutation(key, mutationVersion)) {\n return;\n }\n\n if (!cipher) {\n if (options.unencryptedFallback) {\n try {\n store.set(key, RAW_PREFIX + value);\n memoryFallback.delete(key);\n warnOnce(\n 'Clerk: OS encryption is unavailable; falling back to unencrypted storage. Tokens are being stored unencrypted on local disk.',\n );\n } catch {\n memoryFallback.set(key, value);\n warnOnce('Clerk: failed to persist a token; it will only be available until the app exits.');\n }\n } else {\n memoryFallback.set(key, value);\n warnOnce(\n 'Clerk: OS encryption is unavailable and unencryptedFallback is not enabled, so tokens are not being persisted. The user will be signed out on the next launch. Pass `storage({ unencryptedFallback: true })` to persist unencrypted (less secure).',\n );\n }\n return;\n }\n\n try {\n const encrypted = await cipher.encrypt(value);\n if (!isCurrentMutation(key, mutationVersion)) {\n return;\n }\n store.set(key, ENCRYPTED_PREFIX + encrypted);\n memoryFallback.delete(key);\n } catch {\n if (!isCurrentMutation(key, mutationVersion)) {\n return;\n }\n memoryFallback.set(key, value);\n warnOnce('Clerk: failed to securely persist a token; it will only be available until the app exits.');\n }\n },\n removeItem(key) {\n beginMutation(key);\n memoryFallback.delete(key);\n store.delete(key);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["safeStorage","Store"],"sources":["../../../src/storage/index.ts"],"sourcesContent":["import { chmodSync } from 'node:fs';\n\nimport { safeStorage } from 'electron';\nimport Store from 'electron-store';\n\nimport type { TokenStorage } from '../shared/types';\n\nexport type StorageOptions = {\n /**\n * The name of the file (without extension) used to persist tokens.\n *\n * @default 'clerk-tokens'\n */\n name?: string;\n /**\n * The directory in which the token file is stored. Maps to `electron-store`'s `cwd` option.\n * When omitted, the OS default user config directory is used.\n */\n path?: string;\n /**\n * When OS-level encryption is unavailable (e.g. a Linux machine without a keyring), tokens are\n * not persisted by default, which signs the user out on the next app launch. Set this to `true`\n * to instead persist tokens **unencrypted** in that scenario, keeping the user signed in across\n * restarts at the cost of storing tokens in plaintext on disk.\n *\n * @default false\n */\n unencryptedFallback?: boolean;\n};\n\n/**\n * Marks a value that was encrypted via {@link safeStorage}; the remainder is base64 ciphertext.\n * values will be stored as: `enc:<encrypted value>`\n */\nconst ENCRYPTED_PREFIX = 'enc:';\n/**\n * Marks a value persisted unencrypted via the `unencryptedFallback` option.\n * values will be stored as: `raw:<value>`\n */\nconst RAW_PREFIX = 'raw:';\n\ntype Cipher = {\n encrypt: (value: string) => Promise<string>;\n /**\n * Decrypts a base64 payload. `shouldReEncrypt` is `true` (async backend only) when the OS key\n * has rotated and the payload should be re-encrypted with the new key.\n */\n decrypt: (payload: string) => Promise<{ value: string; shouldReEncrypt: boolean }>;\n};\n\ntype AsyncSafeStorage = {\n encryptStringAsync: (plainText: string) => Promise<Buffer>;\n decryptStringAsync: (encrypted: Buffer) => Promise<{ result: string; shouldReEncrypt: boolean }>;\n isAsyncEncryptionAvailable: () => Promise<boolean>;\n};\n\nconst syncCipher: Cipher = {\n encrypt: value => Promise.resolve(safeStorage.encryptString(value).toString('base64')),\n decrypt: payload =>\n Promise.resolve({ value: safeStorage.decryptString(Buffer.from(payload, 'base64')), shouldReEncrypt: false }),\n};\n\nfunction hasAsyncSafeStorage(storage: typeof safeStorage): storage is typeof safeStorage & AsyncSafeStorage {\n const candidate = storage as Partial<AsyncSafeStorage>;\n\n return (\n typeof candidate.encryptStringAsync === 'function' &&\n typeof candidate.decryptStringAsync === 'function' &&\n typeof candidate.isAsyncEncryptionAvailable === 'function'\n );\n}\n\nfunction createAsyncCipher(storage: AsyncSafeStorage): Cipher {\n return {\n encrypt: async value => (await storage.encryptStringAsync(value)).toString('base64'),\n decrypt: async payload => {\n const { result, shouldReEncrypt } = await storage.decryptStringAsync(Buffer.from(payload, 'base64'));\n return { value: result, shouldReEncrypt };\n },\n };\n}\n\n/**\n * Resolves the crypto backend to use, or `null` when no OS encryption is currently available.\n */\nasync function resolveCipher(): Promise<Cipher | null> {\n // Prefer the async API, but only when the full optional function surface exists.\n if (hasAsyncSafeStorage(safeStorage)) {\n try {\n if (await safeStorage.isAsyncEncryptionAvailable()) {\n return createAsyncCipher(safeStorage);\n }\n } catch {\n /* fall through to the synchronous API */\n }\n }\n\n // The synchronous API blocks the calling thread on the OS prompt during the first encrypt/decrypt,\n if (typeof safeStorage.isEncryptionAvailable === 'function' && safeStorage.isEncryptionAvailable()) {\n return syncCipher;\n }\n\n return null;\n}\n\n/**\n * Creates a secure {@link TokenStorage} adapter for the Electron main process.\n *\n * Tokens are persisted with `electron-store` and encrypted at rest using Electron's\n * {@link safeStorage} API, which is backed by the OS keystore (Keychain on macOS, DPAPI on\n * Windows, libsecret/kwallet on Linux). It uses Electron 42's async `safeStorage` API only when it\n * reports itself available (which generally requires a code-signed app) and otherwise falls back to\n * the synchronous API. Pass the result to `createClerkBridge({ storage: storage() })`.\n *\n * Behavior is secure by default: when OS encryption is unavailable the adapter does not persist\n * tokens (the user will be signed out on restart) unless {@link StorageOptions.unencryptedFallback}\n * is enabled. Undecryptable entries return `null`.\n *\n * @example\n * ```ts\n * import { createClerkBridge } from '@clerk/electron';\n * import { storage } from '@clerk/electron/storage';\n *\n * createClerkBridge({ storage: storage({ name: 'my-app-tokens' }) });\n * ```\n */\nexport function storage(options: StorageOptions = {}): TokenStorage {\n const store = new Store<Record<string, string>>({\n name: options.name ?? 'clerk-tokens',\n configFileMode: 0o600,\n ...(options.path ? { cwd: options.path } : {}),\n });\n try {\n chmodSync(store.path, 0o600);\n } catch {\n /* the file does not exist yet, or it belongs to another user */\n }\n const memoryFallback = new Map<string, string>();\n // IPC requests can resolve out of order, so only the latest mutation may update storage.\n const mutationVersions = new Map<string, number>();\n\n const beginMutation = (key: string) => {\n const version = (mutationVersions.get(key) ?? 0) + 1;\n mutationVersions.set(key, version);\n return version;\n };\n const isCurrentMutation = (key: string, version: number) => (mutationVersions.get(key) ?? 0) === version;\n\n let cachedCipher: Cipher | null = null;\n let resolving: Promise<Cipher | null> | null = null;\n const getCipher = (): Promise<Cipher | null> => {\n if (cachedCipher) {\n return Promise.resolve(cachedCipher);\n }\n resolving ??= resolveCipher().then(resolved => {\n resolving = null;\n if (resolved) {\n cachedCipher = resolved;\n }\n return resolved;\n });\n return resolving;\n };\n\n let warned = false;\n const warnOnce = (message: string) => {\n if (warned) {\n return;\n }\n warned = true;\n console.warn(message);\n };\n\n return {\n async getItem(key) {\n const fallback = memoryFallback.get(key);\n if (fallback !== undefined) {\n return fallback;\n }\n\n const stored = store.get(key);\n\n if (!stored) {\n return null;\n }\n\n if (stored.startsWith(RAW_PREFIX)) {\n return stored.slice(RAW_PREFIX.length);\n }\n\n if (stored.startsWith(ENCRYPTED_PREFIX)) {\n const mutationVersion = mutationVersions.get(key) ?? 0;\n const cipher = await getCipher();\n\n // No usable OS encryption, preserve entry.\n if (!cipher) {\n return null;\n }\n\n const payload = stored.slice(ENCRYPTED_PREFIX.length);\n\n try {\n const { value, shouldReEncrypt } = await cipher.decrypt(payload);\n\n if (shouldReEncrypt) {\n // OS key has rotated, persist with new value\n try {\n const encrypted = await cipher.encrypt(value);\n if (isCurrentMutation(key, mutationVersion)) {\n store.set(key, ENCRYPTED_PREFIX + encrypted);\n }\n } catch {\n // keep the existing payload; it still decrypts for now\n }\n }\n\n return value;\n } catch {\n // Decryption failed, preserve the entry, new write on next sign-in.\n return null;\n }\n }\n\n // Unknown or unrecognized format, drop the entry so we don't repeatedly fail on it.\n store.delete(key);\n return null;\n },\n async setItem(key, value) {\n const mutationVersion = beginMutation(key);\n const cipher = await getCipher();\n\n if (!isCurrentMutation(key, mutationVersion)) {\n return;\n }\n\n if (!cipher) {\n if (options.unencryptedFallback) {\n try {\n store.set(key, RAW_PREFIX + value);\n memoryFallback.delete(key);\n warnOnce(\n 'Clerk: OS encryption is unavailable; falling back to unencrypted storage. Tokens are being stored unencrypted on local disk.',\n );\n } catch {\n memoryFallback.set(key, value);\n warnOnce('Clerk: failed to persist a token; it will only be available until the app exits.');\n }\n } else {\n memoryFallback.set(key, value);\n warnOnce(\n 'Clerk: OS encryption is unavailable and unencryptedFallback is not enabled, so tokens are not being persisted. The user will be signed out on the next launch. Pass `storage({ unencryptedFallback: true })` to persist unencrypted (less secure).',\n );\n }\n return;\n }\n\n try {\n const encrypted = await cipher.encrypt(value);\n if (!isCurrentMutation(key, mutationVersion)) {\n return;\n }\n store.set(key, ENCRYPTED_PREFIX + encrypted);\n memoryFallback.delete(key);\n } catch {\n if (!isCurrentMutation(key, mutationVersion)) {\n return;\n }\n memoryFallback.set(key, value);\n warnOnce('Clerk: failed to securely persist a token; it will only be available until the app exits.');\n }\n },\n removeItem(key) {\n beginMutation(key);\n memoryFallback.delete(key);\n store.delete(key);\n },\n };\n}\n"],"mappings":"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"}
@@ -27,7 +27,7 @@ function createClerkInstance(publishableKey, passkeys) {
27
27
  clerk.__internal_onBeforeRequest(async (request) => {
28
28
  request.credentials = "omit";
29
29
  request.url?.searchParams.append("_is_native", "1");
30
- request.url?.searchParams.append("_electron_sdk_version", "0.1.8-snapshot.v20261029220359");
30
+ request.url?.searchParams.append("_electron_sdk_version", "0.1.8");
31
31
  const token = await window.__clerk_internal_electron?.tokenCache.getToken(CLERK_CLIENT_JWT_KEY);
32
32
  if (token) {
33
33
  const headers = new Headers(request.headers);
@@ -52,7 +52,7 @@ function createClerkInstance(publishableKey, passkeys) {
52
52
  //#region src/react/index.tsx
53
53
  const SDK_METADATA = {
54
54
  name: "@clerk/electron",
55
- version: "0.1.8-snapshot.v20261029220359"
55
+ version: "0.1.8"
56
56
  };
57
57
  let cachedClerkUI = null;
58
58
  function loadClerkUI(publishableKey, props) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["ReactClerkProvider"],"sources":["../../../src/react/create-clerk-instance.ts","../../../src/react/index.tsx"],"sourcesContent":["import { Clerk } from '@clerk/clerk-js';\n\n// Type-only import: the passkeys module is only bundled when the app imports\n// `@clerk/electron/passkeys` itself.\nimport type { PasskeySupport } from '../passkeys';\n\nconst CLERK_CLIENT_JWT_KEY = '__clerk_client_jwt';\n\ntype ClerkInstance = InstanceType<typeof Clerk>;\n\nlet cached: { instance: ClerkInstance; publishableKey: string } | null = null;\n\nfunction attachPasskeys(clerk: ClerkInstance, passkeys: PasskeySupport): void {\n clerk.__internal_createPublicCredentials = passkeys.create;\n clerk.__internal_getPublicCredentials = passkeys.get;\n clerk.__internal_isWebAuthnSupported = passkeys.isSupported;\n clerk.__internal_isWebAuthnAutofillSupported = passkeys.isAutoFillSupported;\n clerk.__internal_isWebAuthnPlatformAuthenticatorSupported = passkeys.isPlatformAuthenticatorSupported;\n}\n\nexport function createClerkInstance(publishableKey: string, passkeys?: PasskeySupport): ClerkInstance {\n if (cached?.publishableKey === publishableKey) {\n if (passkeys) {\n attachPasskeys(cached.instance, passkeys);\n }\n return cached.instance;\n }\n\n const clerk = new Clerk(publishableKey);\n\n if (passkeys) {\n attachPasskeys(clerk, passkeys);\n }\n\n clerk.__internal_onBeforeRequest(async request => {\n request.credentials = 'omit';\n request.url?.searchParams.append('_is_native', '1');\n request.url?.searchParams.append('_electron_sdk_version', PACKAGE_VERSION);\n\n const token = await window.__clerk_internal_electron?.tokenCache.getToken(CLERK_CLIENT_JWT_KEY);\n if (token) {\n const headers = new Headers(request.headers);\n headers.set('Authorization', `Bearer ${token}`);\n request.headers = headers;\n }\n });\n\n clerk.__internal_onAfterResponse(async (_request, response) => {\n const authorization = (response as Response).headers.get('Authorization');\n if (!authorization) {\n return;\n }\n\n const token = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : authorization;\n await window.__clerk_internal_electron?.tokenCache.saveToken(CLERK_CLIENT_JWT_KEY, token);\n });\n\n cached = { instance: clerk, publishableKey };\n return clerk;\n}\n","import type { ClerkProviderProps as ReactClerkProviderProps } from '@clerk/react';\nimport { InternalClerkProvider as ReactClerkProvider } from '@clerk/react/internal';\nimport { isVirtualRouterPath } from '@clerk/shared/internal/clerk-js/url';\nimport { ALLOWED_PROTOCOLS } from '@clerk/shared/internal/clerk-js/windowNavigate';\nimport { loadClerkUIScript } from '@clerk/shared/loadClerkJsScript';\nimport type { ClerkUIConstructor } from '@clerk/shared/ui';\nimport type { ReactNode } from 'react';\n\nimport type { PasskeySupport } from '../passkeys';\nimport { createClerkInstance } from './create-clerk-instance';\n\ntype ClerkOAuthTransport = NonNullable<ReactClerkProviderProps['__internal_oauthTransport']>;\n\nexport type ClerkProviderProps = Omit<\n ReactClerkProviderProps,\n 'Clerk' | 'children' | 'publishableKey' | 'standardBrowser' | 'ui' | '__internal_oauthTransport'\n> & {\n children: ReactNode;\n /**\n * Your Clerk publishable key, available in the Clerk Dashboard.\n */\n publishableKey: string;\n /**\n * Enables passkey support. Pass the `passkeys` export from `@clerk/electron/passkeys`;\n * when omitted, no passkey code is bundled or initialized.\n *\n * @example\n * ```tsx\n * import { passkeys } from '@clerk/electron/passkeys';\n *\n * <ClerkProvider publishableKey={publishableKey} passkeys={passkeys} />\n * ```\n */\n passkeys?: PasskeySupport;\n};\n\nconst SDK_METADATA = {\n name: PACKAGE_NAME,\n version: PACKAGE_VERSION,\n};\n\nlet cachedClerkUI: { promise: Promise<ClerkUIConstructor>; publishableKey: string } | null = null;\n\nfunction loadClerkUI(publishableKey: string, props: Partial<ClerkProviderProps>): Promise<ClerkUIConstructor> {\n if (cachedClerkUI?.publishableKey === publishableKey) {\n return cachedClerkUI.promise;\n }\n\n // Undocumented escape hatch for self-hosting/proxying the UI bundle; not part of the public props.\n const { __internal_clerkUIUrl, __internal_clerkUIVersion } = props as {\n __internal_clerkUIUrl?: string;\n __internal_clerkUIVersion?: string;\n };\n\n const promise = loadClerkUIScript({\n publishableKey,\n proxyUrl: typeof props.proxyUrl === 'string' ? props.proxyUrl : undefined,\n domain: typeof props.domain === 'string' ? props.domain : undefined,\n nonce: props.nonce,\n __internal_clerkUIUrl,\n __internal_clerkUIVersion,\n }).then(() => {\n if (!window.__internal_ClerkUICtor) {\n throw new Error(\n 'Clerk: Failed to load Clerk UI from the CDN. Ensure your Content Security Policy allows the Clerk Frontend API host in `script-src`. Contact support@clerk.com.',\n );\n }\n return window.__internal_ClerkUICtor;\n });\n\n cachedClerkUI = { promise, publishableKey };\n return promise;\n}\n\nfunction createOAuthTransport(): ClerkOAuthTransport | undefined {\n const bridge = window.__clerk_internal_electron?.oauthTransport;\n\n if (!bridge) {\n return undefined;\n }\n\n return {\n getRedirectUrl: () => bridge.getRedirectUrl(),\n open: url => bridge.open(url.href),\n };\n}\n\ntype ClerkRouterFn = NonNullable<ReactClerkProviderProps['routerPush']>;\n\n/** Always supplied, so clerk-js never reaches `window.location` and reloads the renderer. */\nfunction createRouterHandlers(\n routerPush: ClerkRouterFn | undefined,\n routerReplace: ClerkRouterFn | undefined,\n): { routerPush: ClerkRouterFn; routerReplace: ClerkRouterFn } {\n const wrap =\n (delegate: ClerkRouterFn | undefined): ClerkRouterFn =>\n (to, metadata) => {\n if (isVirtualRouterPath(to)) {\n return;\n }\n\n if (delegate) {\n return delegate(to, metadata);\n }\n\n metadata?.windowNavigate(to);\n };\n\n return { routerPush: wrap(routerPush), routerReplace: wrap(routerReplace) };\n}\n\n/**\n * Infer the custom renderer scheme registered with `createClerkBridge({ renderer })`.\n * Built-in Clerk protocols and local file renderers are not inferred.\n */\nfunction defaultAllowedRedirectProtocols(): string[] | undefined {\n const protocol = typeof window !== 'undefined' ? window.location?.protocol : undefined;\n\n if (!protocol || ALLOWED_PROTOCOLS.includes(protocol) || protocol === 'file:') {\n return undefined;\n }\n\n return [protocol];\n}\n\nexport function ClerkProvider({\n children,\n publishableKey,\n passkeys,\n allowedRedirectProtocols,\n routerPush,\n routerReplace,\n ...props\n}: ClerkProviderProps): JSX.Element {\n const clerk = createClerkInstance(publishableKey, passkeys);\n const oauthTransport = createOAuthTransport();\n const clerkUI = loadClerkUI(publishableKey, props);\n const routerHandlers = createRouterHandlers(routerPush, routerReplace);\n\n return (\n <ReactClerkProvider\n {...props}\n {...routerHandlers}\n sdkMetadata={SDK_METADATA}\n Clerk={clerk}\n __internal_oauthTransport={oauthTransport}\n allowedRedirectProtocols={allowedRedirectProtocols ?? defaultAllowedRedirectProtocols()}\n publishableKey={publishableKey}\n standardBrowser={false}\n ui={{ ClerkUI: clerkUI }}\n >\n {children}\n </ReactClerkProvider>\n );\n}\n\nexport * from '@clerk/react';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["ReactClerkProvider"],"sources":["../../../src/react/create-clerk-instance.ts","../../../src/react/index.tsx"],"sourcesContent":["import { Clerk } from '@clerk/clerk-js';\n\n// Type-only import: the passkeys module is only bundled when the app imports\n// `@clerk/electron/passkeys` itself.\nimport type { PasskeySupport } from '../passkeys';\n\nconst CLERK_CLIENT_JWT_KEY = '__clerk_client_jwt';\n\ntype ClerkInstance = InstanceType<typeof Clerk>;\n\nlet cached: { instance: ClerkInstance; publishableKey: string } | null = null;\n\nfunction attachPasskeys(clerk: ClerkInstance, passkeys: PasskeySupport): void {\n clerk.__internal_createPublicCredentials = passkeys.create;\n clerk.__internal_getPublicCredentials = passkeys.get;\n clerk.__internal_isWebAuthnSupported = passkeys.isSupported;\n clerk.__internal_isWebAuthnAutofillSupported = passkeys.isAutoFillSupported;\n clerk.__internal_isWebAuthnPlatformAuthenticatorSupported = passkeys.isPlatformAuthenticatorSupported;\n}\n\nexport function createClerkInstance(publishableKey: string, passkeys?: PasskeySupport): ClerkInstance {\n if (cached?.publishableKey === publishableKey) {\n if (passkeys) {\n attachPasskeys(cached.instance, passkeys);\n }\n return cached.instance;\n }\n\n const clerk = new Clerk(publishableKey);\n\n if (passkeys) {\n attachPasskeys(clerk, passkeys);\n }\n\n clerk.__internal_onBeforeRequest(async request => {\n request.credentials = 'omit';\n request.url?.searchParams.append('_is_native', '1');\n request.url?.searchParams.append('_electron_sdk_version', PACKAGE_VERSION);\n\n const token = await window.__clerk_internal_electron?.tokenCache.getToken(CLERK_CLIENT_JWT_KEY);\n if (token) {\n const headers = new Headers(request.headers);\n headers.set('Authorization', `Bearer ${token}`);\n request.headers = headers;\n }\n });\n\n clerk.__internal_onAfterResponse(async (_request, response) => {\n const authorization = (response as Response).headers.get('Authorization');\n if (!authorization) {\n return;\n }\n\n const token = authorization.startsWith('Bearer ') ? authorization.slice('Bearer '.length) : authorization;\n await window.__clerk_internal_electron?.tokenCache.saveToken(CLERK_CLIENT_JWT_KEY, token);\n });\n\n cached = { instance: clerk, publishableKey };\n return clerk;\n}\n","import type { ClerkProviderProps as ReactClerkProviderProps } from '@clerk/react';\nimport { InternalClerkProvider as ReactClerkProvider } from '@clerk/react/internal';\nimport { isVirtualRouterPath } from '@clerk/shared/internal/clerk-js/url';\nimport { ALLOWED_PROTOCOLS } from '@clerk/shared/internal/clerk-js/windowNavigate';\nimport { loadClerkUIScript } from '@clerk/shared/loadClerkJsScript';\nimport type { ClerkUIConstructor } from '@clerk/shared/ui';\nimport type { ReactNode } from 'react';\n\nimport type { PasskeySupport } from '../passkeys';\nimport { createClerkInstance } from './create-clerk-instance';\n\ntype ClerkOAuthTransport = NonNullable<ReactClerkProviderProps['__internal_oauthTransport']>;\n\nexport type ClerkProviderProps = Omit<\n ReactClerkProviderProps,\n 'Clerk' | 'children' | 'publishableKey' | 'standardBrowser' | 'ui' | '__internal_oauthTransport'\n> & {\n children: ReactNode;\n /**\n * Your Clerk publishable key, available in the Clerk Dashboard.\n */\n publishableKey: string;\n /**\n * Enables passkey support. Pass the `passkeys` export from `@clerk/electron/passkeys`;\n * when omitted, no passkey code is bundled or initialized.\n *\n * @example\n * ```tsx\n * import { passkeys } from '@clerk/electron/passkeys';\n *\n * <ClerkProvider publishableKey={publishableKey} passkeys={passkeys} />\n * ```\n */\n passkeys?: PasskeySupport;\n};\n\nconst SDK_METADATA = {\n name: PACKAGE_NAME,\n version: PACKAGE_VERSION,\n};\n\nlet cachedClerkUI: { promise: Promise<ClerkUIConstructor>; publishableKey: string } | null = null;\n\nfunction loadClerkUI(publishableKey: string, props: Partial<ClerkProviderProps>): Promise<ClerkUIConstructor> {\n if (cachedClerkUI?.publishableKey === publishableKey) {\n return cachedClerkUI.promise;\n }\n\n // Undocumented escape hatch for self-hosting/proxying the UI bundle; not part of the public props.\n const { __internal_clerkUIUrl, __internal_clerkUIVersion } = props as {\n __internal_clerkUIUrl?: string;\n __internal_clerkUIVersion?: string;\n };\n\n const promise = loadClerkUIScript({\n publishableKey,\n proxyUrl: typeof props.proxyUrl === 'string' ? props.proxyUrl : undefined,\n domain: typeof props.domain === 'string' ? props.domain : undefined,\n nonce: props.nonce,\n __internal_clerkUIUrl,\n __internal_clerkUIVersion,\n }).then(() => {\n if (!window.__internal_ClerkUICtor) {\n throw new Error(\n 'Clerk: Failed to load Clerk UI from the CDN. Ensure your Content Security Policy allows the Clerk Frontend API host in `script-src`. Contact support@clerk.com.',\n );\n }\n return window.__internal_ClerkUICtor;\n });\n\n cachedClerkUI = { promise, publishableKey };\n return promise;\n}\n\nfunction createOAuthTransport(): ClerkOAuthTransport | undefined {\n const bridge = window.__clerk_internal_electron?.oauthTransport;\n\n if (!bridge) {\n return undefined;\n }\n\n return {\n getRedirectUrl: () => bridge.getRedirectUrl(),\n open: url => bridge.open(url.href),\n };\n}\n\ntype ClerkRouterFn = NonNullable<ReactClerkProviderProps['routerPush']>;\n\n/** Always supplied, so clerk-js never reaches `window.location` and reloads the renderer. */\nfunction createRouterHandlers(\n routerPush: ClerkRouterFn | undefined,\n routerReplace: ClerkRouterFn | undefined,\n): { routerPush: ClerkRouterFn; routerReplace: ClerkRouterFn } {\n const wrap =\n (delegate: ClerkRouterFn | undefined): ClerkRouterFn =>\n (to, metadata) => {\n if (isVirtualRouterPath(to)) {\n return;\n }\n\n if (delegate) {\n return delegate(to, metadata);\n }\n\n metadata?.windowNavigate(to);\n };\n\n return { routerPush: wrap(routerPush), routerReplace: wrap(routerReplace) };\n}\n\n/**\n * Infer the custom renderer scheme registered with `createClerkBridge({ renderer })`.\n * Built-in Clerk protocols and local file renderers are not inferred.\n */\nfunction defaultAllowedRedirectProtocols(): string[] | undefined {\n const protocol = typeof window !== 'undefined' ? window.location?.protocol : undefined;\n\n if (!protocol || ALLOWED_PROTOCOLS.includes(protocol) || protocol === 'file:') {\n return undefined;\n }\n\n return [protocol];\n}\n\nexport function ClerkProvider({\n children,\n publishableKey,\n passkeys,\n allowedRedirectProtocols,\n routerPush,\n routerReplace,\n ...props\n}: ClerkProviderProps): JSX.Element {\n const clerk = createClerkInstance(publishableKey, passkeys);\n const oauthTransport = createOAuthTransport();\n const clerkUI = loadClerkUI(publishableKey, props);\n const routerHandlers = createRouterHandlers(routerPush, routerReplace);\n\n return (\n <ReactClerkProvider\n {...props}\n {...routerHandlers}\n sdkMetadata={SDK_METADATA}\n Clerk={clerk}\n __internal_oauthTransport={oauthTransport}\n allowedRedirectProtocols={allowedRedirectProtocols ?? defaultAllowedRedirectProtocols()}\n publishableKey={publishableKey}\n standardBrowser={false}\n ui={{ ClerkUI: clerkUI }}\n >\n {children}\n </ReactClerkProvider>\n );\n}\n\nexport * from '@clerk/react';\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { safeStorage } from "electron";
2
+ import { chmodSync } from "node:fs";
2
3
  import Store from "electron-store";
3
4
 
4
5
  //#region src/storage/index.ts
@@ -69,8 +70,12 @@ async function resolveCipher() {
69
70
  function storage(options = {}) {
70
71
  const store = new Store({
71
72
  name: options.name ?? "clerk-tokens",
73
+ configFileMode: 384,
72
74
  ...options.path ? { cwd: options.path } : {}
73
75
  });
76
+ try {
77
+ chmodSync(store.path, 384);
78
+ } catch {}
74
79
  const memoryFallback = /* @__PURE__ */ new Map();
75
80
  const mutationVersions = /* @__PURE__ */ new Map();
76
81
  const beginMutation = (key) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/storage/index.ts"],"sourcesContent":["import { safeStorage } from 'electron';\nimport Store from 'electron-store';\n\nimport type { TokenStorage } from '../shared/types';\n\nexport type StorageOptions = {\n /**\n * The name of the file (without extension) used to persist tokens.\n *\n * @default 'clerk-tokens'\n */\n name?: string;\n /**\n * The directory in which the token file is stored. Maps to `electron-store`'s `cwd` option.\n * When omitted, the OS default user config directory is used.\n */\n path?: string;\n /**\n * When OS-level encryption is unavailable (e.g. a Linux machine without a keyring), tokens are\n * not persisted by default, which signs the user out on the next app launch. Set this to `true`\n * to instead persist tokens **unencrypted** in that scenario, keeping the user signed in across\n * restarts at the cost of storing tokens in plaintext on disk.\n *\n * @default false\n */\n unencryptedFallback?: boolean;\n};\n\n/**\n * Marks a value that was encrypted via {@link safeStorage}; the remainder is base64 ciphertext.\n * values will be stored as: `enc:<encrypted value>`\n */\nconst ENCRYPTED_PREFIX = 'enc:';\n/**\n * Marks a value persisted unencrypted via the `unencryptedFallback` option.\n * values will be stored as: `raw:<value>`\n */\nconst RAW_PREFIX = 'raw:';\n\ntype Cipher = {\n encrypt: (value: string) => Promise<string>;\n /**\n * Decrypts a base64 payload. `shouldReEncrypt` is `true` (async backend only) when the OS key\n * has rotated and the payload should be re-encrypted with the new key.\n */\n decrypt: (payload: string) => Promise<{ value: string; shouldReEncrypt: boolean }>;\n};\n\ntype AsyncSafeStorage = {\n encryptStringAsync: (plainText: string) => Promise<Buffer>;\n decryptStringAsync: (encrypted: Buffer) => Promise<{ result: string; shouldReEncrypt: boolean }>;\n isAsyncEncryptionAvailable: () => Promise<boolean>;\n};\n\nconst syncCipher: Cipher = {\n encrypt: value => Promise.resolve(safeStorage.encryptString(value).toString('base64')),\n decrypt: payload =>\n Promise.resolve({ value: safeStorage.decryptString(Buffer.from(payload, 'base64')), shouldReEncrypt: false }),\n};\n\nfunction hasAsyncSafeStorage(storage: typeof safeStorage): storage is typeof safeStorage & AsyncSafeStorage {\n const candidate = storage as Partial<AsyncSafeStorage>;\n\n return (\n typeof candidate.encryptStringAsync === 'function' &&\n typeof candidate.decryptStringAsync === 'function' &&\n typeof candidate.isAsyncEncryptionAvailable === 'function'\n );\n}\n\nfunction createAsyncCipher(storage: AsyncSafeStorage): Cipher {\n return {\n encrypt: async value => (await storage.encryptStringAsync(value)).toString('base64'),\n decrypt: async payload => {\n const { result, shouldReEncrypt } = await storage.decryptStringAsync(Buffer.from(payload, 'base64'));\n return { value: result, shouldReEncrypt };\n },\n };\n}\n\n/**\n * Resolves the crypto backend to use, or `null` when no OS encryption is currently available.\n */\nasync function resolveCipher(): Promise<Cipher | null> {\n // Prefer the async API, but only when the full optional function surface exists.\n if (hasAsyncSafeStorage(safeStorage)) {\n try {\n if (await safeStorage.isAsyncEncryptionAvailable()) {\n return createAsyncCipher(safeStorage);\n }\n } catch {\n /* fall through to the synchronous API */\n }\n }\n\n // The synchronous API blocks the calling thread on the OS prompt during the first encrypt/decrypt,\n if (typeof safeStorage.isEncryptionAvailable === 'function' && safeStorage.isEncryptionAvailable()) {\n return syncCipher;\n }\n\n return null;\n}\n\n/**\n * Creates a secure {@link TokenStorage} adapter for the Electron main process.\n *\n * Tokens are persisted with `electron-store` and encrypted at rest using Electron's\n * {@link safeStorage} API, which is backed by the OS keystore (Keychain on macOS, DPAPI on\n * Windows, libsecret/kwallet on Linux). It uses Electron 42's async `safeStorage` API only when it\n * reports itself available (which generally requires a code-signed app) and otherwise falls back to\n * the synchronous API. Pass the result to `createClerkBridge({ storage: storage() })`.\n *\n * Behavior is secure by default: when OS encryption is unavailable the adapter does not persist\n * tokens (the user will be signed out on restart) unless {@link StorageOptions.unencryptedFallback}\n * is enabled. Undecryptable entries return `null`.\n *\n * @example\n * ```ts\n * import { createClerkBridge } from '@clerk/electron';\n * import { storage } from '@clerk/electron/storage';\n *\n * createClerkBridge({ storage: storage({ name: 'my-app-tokens' }) });\n * ```\n */\nexport function storage(options: StorageOptions = {}): TokenStorage {\n const store = new Store<Record<string, string>>({\n name: options.name ?? 'clerk-tokens',\n ...(options.path ? { cwd: options.path } : {}),\n });\n const memoryFallback = new Map<string, string>();\n // IPC requests can resolve out of order, so only the latest mutation may update storage.\n const mutationVersions = new Map<string, number>();\n\n const beginMutation = (key: string) => {\n const version = (mutationVersions.get(key) ?? 0) + 1;\n mutationVersions.set(key, version);\n return version;\n };\n const isCurrentMutation = (key: string, version: number) => (mutationVersions.get(key) ?? 0) === version;\n\n let cachedCipher: Cipher | null = null;\n let resolving: Promise<Cipher | null> | null = null;\n const getCipher = (): Promise<Cipher | null> => {\n if (cachedCipher) {\n return Promise.resolve(cachedCipher);\n }\n resolving ??= resolveCipher().then(resolved => {\n resolving = null;\n if (resolved) {\n cachedCipher = resolved;\n }\n return resolved;\n });\n return resolving;\n };\n\n let warned = false;\n const warnOnce = (message: string) => {\n if (warned) {\n return;\n }\n warned = true;\n console.warn(message);\n };\n\n return {\n async getItem(key) {\n const fallback = memoryFallback.get(key);\n if (fallback !== undefined) {\n return fallback;\n }\n\n const stored = store.get(key);\n\n if (!stored) {\n return null;\n }\n\n if (stored.startsWith(RAW_PREFIX)) {\n return stored.slice(RAW_PREFIX.length);\n }\n\n if (stored.startsWith(ENCRYPTED_PREFIX)) {\n const mutationVersion = mutationVersions.get(key) ?? 0;\n const cipher = await getCipher();\n\n // No usable OS encryption, preserve entry.\n if (!cipher) {\n return null;\n }\n\n const payload = stored.slice(ENCRYPTED_PREFIX.length);\n\n try {\n const { value, shouldReEncrypt } = await cipher.decrypt(payload);\n\n if (shouldReEncrypt) {\n // OS key has rotated, persist with new value\n try {\n const encrypted = await cipher.encrypt(value);\n if (isCurrentMutation(key, mutationVersion)) {\n store.set(key, ENCRYPTED_PREFIX + encrypted);\n }\n } catch {\n // keep the existing payload; it still decrypts for now\n }\n }\n\n return value;\n } catch {\n // Decryption failed, preserve the entry, new write on next sign-in.\n return null;\n }\n }\n\n // Unknown or unrecognized format, drop the entry so we don't repeatedly fail on it.\n store.delete(key);\n return null;\n },\n async setItem(key, value) {\n const mutationVersion = beginMutation(key);\n const cipher = await getCipher();\n\n if (!isCurrentMutation(key, mutationVersion)) {\n return;\n }\n\n if (!cipher) {\n if (options.unencryptedFallback) {\n try {\n store.set(key, RAW_PREFIX + value);\n memoryFallback.delete(key);\n warnOnce(\n 'Clerk: OS encryption is unavailable; falling back to unencrypted storage. Tokens are being stored unencrypted on local disk.',\n );\n } catch {\n memoryFallback.set(key, value);\n warnOnce('Clerk: failed to persist a token; it will only be available until the app exits.');\n }\n } else {\n memoryFallback.set(key, value);\n warnOnce(\n 'Clerk: OS encryption is unavailable and unencryptedFallback is not enabled, so tokens are not being persisted. The user will be signed out on the next launch. Pass `storage({ unencryptedFallback: true })` to persist unencrypted (less secure).',\n );\n }\n return;\n }\n\n try {\n const encrypted = await cipher.encrypt(value);\n if (!isCurrentMutation(key, mutationVersion)) {\n return;\n }\n store.set(key, ENCRYPTED_PREFIX + encrypted);\n memoryFallback.delete(key);\n } catch {\n if (!isCurrentMutation(key, mutationVersion)) {\n return;\n }\n memoryFallback.set(key, value);\n warnOnce('Clerk: failed to securely persist a token; it will only be available until the app exits.');\n }\n },\n removeItem(key) {\n beginMutation(key);\n memoryFallback.delete(key);\n store.delete(key);\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/storage/index.ts"],"sourcesContent":["import { chmodSync } from 'node:fs';\n\nimport { safeStorage } from 'electron';\nimport Store from 'electron-store';\n\nimport type { TokenStorage } from '../shared/types';\n\nexport type StorageOptions = {\n /**\n * The name of the file (without extension) used to persist tokens.\n *\n * @default 'clerk-tokens'\n */\n name?: string;\n /**\n * The directory in which the token file is stored. Maps to `electron-store`'s `cwd` option.\n * When omitted, the OS default user config directory is used.\n */\n path?: string;\n /**\n * When OS-level encryption is unavailable (e.g. a Linux machine without a keyring), tokens are\n * not persisted by default, which signs the user out on the next app launch. Set this to `true`\n * to instead persist tokens **unencrypted** in that scenario, keeping the user signed in across\n * restarts at the cost of storing tokens in plaintext on disk.\n *\n * @default false\n */\n unencryptedFallback?: boolean;\n};\n\n/**\n * Marks a value that was encrypted via {@link safeStorage}; the remainder is base64 ciphertext.\n * values will be stored as: `enc:<encrypted value>`\n */\nconst ENCRYPTED_PREFIX = 'enc:';\n/**\n * Marks a value persisted unencrypted via the `unencryptedFallback` option.\n * values will be stored as: `raw:<value>`\n */\nconst RAW_PREFIX = 'raw:';\n\ntype Cipher = {\n encrypt: (value: string) => Promise<string>;\n /**\n * Decrypts a base64 payload. `shouldReEncrypt` is `true` (async backend only) when the OS key\n * has rotated and the payload should be re-encrypted with the new key.\n */\n decrypt: (payload: string) => Promise<{ value: string; shouldReEncrypt: boolean }>;\n};\n\ntype AsyncSafeStorage = {\n encryptStringAsync: (plainText: string) => Promise<Buffer>;\n decryptStringAsync: (encrypted: Buffer) => Promise<{ result: string; shouldReEncrypt: boolean }>;\n isAsyncEncryptionAvailable: () => Promise<boolean>;\n};\n\nconst syncCipher: Cipher = {\n encrypt: value => Promise.resolve(safeStorage.encryptString(value).toString('base64')),\n decrypt: payload =>\n Promise.resolve({ value: safeStorage.decryptString(Buffer.from(payload, 'base64')), shouldReEncrypt: false }),\n};\n\nfunction hasAsyncSafeStorage(storage: typeof safeStorage): storage is typeof safeStorage & AsyncSafeStorage {\n const candidate = storage as Partial<AsyncSafeStorage>;\n\n return (\n typeof candidate.encryptStringAsync === 'function' &&\n typeof candidate.decryptStringAsync === 'function' &&\n typeof candidate.isAsyncEncryptionAvailable === 'function'\n );\n}\n\nfunction createAsyncCipher(storage: AsyncSafeStorage): Cipher {\n return {\n encrypt: async value => (await storage.encryptStringAsync(value)).toString('base64'),\n decrypt: async payload => {\n const { result, shouldReEncrypt } = await storage.decryptStringAsync(Buffer.from(payload, 'base64'));\n return { value: result, shouldReEncrypt };\n },\n };\n}\n\n/**\n * Resolves the crypto backend to use, or `null` when no OS encryption is currently available.\n */\nasync function resolveCipher(): Promise<Cipher | null> {\n // Prefer the async API, but only when the full optional function surface exists.\n if (hasAsyncSafeStorage(safeStorage)) {\n try {\n if (await safeStorage.isAsyncEncryptionAvailable()) {\n return createAsyncCipher(safeStorage);\n }\n } catch {\n /* fall through to the synchronous API */\n }\n }\n\n // The synchronous API blocks the calling thread on the OS prompt during the first encrypt/decrypt,\n if (typeof safeStorage.isEncryptionAvailable === 'function' && safeStorage.isEncryptionAvailable()) {\n return syncCipher;\n }\n\n return null;\n}\n\n/**\n * Creates a secure {@link TokenStorage} adapter for the Electron main process.\n *\n * Tokens are persisted with `electron-store` and encrypted at rest using Electron's\n * {@link safeStorage} API, which is backed by the OS keystore (Keychain on macOS, DPAPI on\n * Windows, libsecret/kwallet on Linux). It uses Electron 42's async `safeStorage` API only when it\n * reports itself available (which generally requires a code-signed app) and otherwise falls back to\n * the synchronous API. Pass the result to `createClerkBridge({ storage: storage() })`.\n *\n * Behavior is secure by default: when OS encryption is unavailable the adapter does not persist\n * tokens (the user will be signed out on restart) unless {@link StorageOptions.unencryptedFallback}\n * is enabled. Undecryptable entries return `null`.\n *\n * @example\n * ```ts\n * import { createClerkBridge } from '@clerk/electron';\n * import { storage } from '@clerk/electron/storage';\n *\n * createClerkBridge({ storage: storage({ name: 'my-app-tokens' }) });\n * ```\n */\nexport function storage(options: StorageOptions = {}): TokenStorage {\n const store = new Store<Record<string, string>>({\n name: options.name ?? 'clerk-tokens',\n configFileMode: 0o600,\n ...(options.path ? { cwd: options.path } : {}),\n });\n try {\n chmodSync(store.path, 0o600);\n } catch {\n /* the file does not exist yet, or it belongs to another user */\n }\n const memoryFallback = new Map<string, string>();\n // IPC requests can resolve out of order, so only the latest mutation may update storage.\n const mutationVersions = new Map<string, number>();\n\n const beginMutation = (key: string) => {\n const version = (mutationVersions.get(key) ?? 0) + 1;\n mutationVersions.set(key, version);\n return version;\n };\n const isCurrentMutation = (key: string, version: number) => (mutationVersions.get(key) ?? 0) === version;\n\n let cachedCipher: Cipher | null = null;\n let resolving: Promise<Cipher | null> | null = null;\n const getCipher = (): Promise<Cipher | null> => {\n if (cachedCipher) {\n return Promise.resolve(cachedCipher);\n }\n resolving ??= resolveCipher().then(resolved => {\n resolving = null;\n if (resolved) {\n cachedCipher = resolved;\n }\n return resolved;\n });\n return resolving;\n };\n\n let warned = false;\n const warnOnce = (message: string) => {\n if (warned) {\n return;\n }\n warned = true;\n console.warn(message);\n };\n\n return {\n async getItem(key) {\n const fallback = memoryFallback.get(key);\n if (fallback !== undefined) {\n return fallback;\n }\n\n const stored = store.get(key);\n\n if (!stored) {\n return null;\n }\n\n if (stored.startsWith(RAW_PREFIX)) {\n return stored.slice(RAW_PREFIX.length);\n }\n\n if (stored.startsWith(ENCRYPTED_PREFIX)) {\n const mutationVersion = mutationVersions.get(key) ?? 0;\n const cipher = await getCipher();\n\n // No usable OS encryption, preserve entry.\n if (!cipher) {\n return null;\n }\n\n const payload = stored.slice(ENCRYPTED_PREFIX.length);\n\n try {\n const { value, shouldReEncrypt } = await cipher.decrypt(payload);\n\n if (shouldReEncrypt) {\n // OS key has rotated, persist with new value\n try {\n const encrypted = await cipher.encrypt(value);\n if (isCurrentMutation(key, mutationVersion)) {\n store.set(key, ENCRYPTED_PREFIX + encrypted);\n }\n } catch {\n // keep the existing payload; it still decrypts for now\n }\n }\n\n return value;\n } catch {\n // Decryption failed, preserve the entry, new write on next sign-in.\n return null;\n }\n }\n\n // Unknown or unrecognized format, drop the entry so we don't repeatedly fail on it.\n store.delete(key);\n return null;\n },\n async setItem(key, value) {\n const mutationVersion = beginMutation(key);\n const cipher = await getCipher();\n\n if (!isCurrentMutation(key, mutationVersion)) {\n return;\n }\n\n if (!cipher) {\n if (options.unencryptedFallback) {\n try {\n store.set(key, RAW_PREFIX + value);\n memoryFallback.delete(key);\n warnOnce(\n 'Clerk: OS encryption is unavailable; falling back to unencrypted storage. Tokens are being stored unencrypted on local disk.',\n );\n } catch {\n memoryFallback.set(key, value);\n warnOnce('Clerk: failed to persist a token; it will only be available until the app exits.');\n }\n } else {\n memoryFallback.set(key, value);\n warnOnce(\n 'Clerk: OS encryption is unavailable and unencryptedFallback is not enabled, so tokens are not being persisted. The user will be signed out on the next launch. Pass `storage({ unencryptedFallback: true })` to persist unencrypted (less secure).',\n );\n }\n return;\n }\n\n try {\n const encrypted = await cipher.encrypt(value);\n if (!isCurrentMutation(key, mutationVersion)) {\n return;\n }\n store.set(key, ENCRYPTED_PREFIX + encrypted);\n memoryFallback.delete(key);\n } catch {\n if (!isCurrentMutation(key, mutationVersion)) {\n return;\n }\n memoryFallback.set(key, value);\n warnOnce('Clerk: failed to securely persist a token; it will only be available until the app exits.');\n }\n },\n removeItem(key) {\n beginMutation(key);\n memoryFallback.delete(key);\n store.delete(key);\n },\n };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AA6EF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,YAAY,CAiJlE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/storage/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD,MAAM,MAAM,cAAc,GAAG;IAC3B;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AA6EF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,YAAY,CAuJlE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clerk/electron",
3
- "version": "0.1.8-snapshot.v20261029220359",
3
+ "version": "0.1.8",
4
4
  "description": "Clerk SDK for Electron",
5
5
  "keywords": [
6
6
  "clerk",
@@ -93,19 +93,19 @@
93
93
  "dist"
94
94
  ],
95
95
  "dependencies": {
96
- "tslib": "2.8.1",
97
- "@clerk/clerk-js": "6.42.1-snapshot.v20261029220359",
98
- "@clerk/react": "6.18.4-snapshot.v20261029220359",
99
- "@clerk/shared": "4.43.1-snapshot.v20261029220359"
96
+ "@clerk/clerk-js": "^6.42.1",
97
+ "@clerk/react": "^6.18.4",
98
+ "@clerk/shared": "^4.43.1"
100
99
  },
101
100
  "devDependencies": {
102
101
  "@types/node": "^22.20.1",
103
102
  "electron": "^41.0.0",
104
103
  "electron-store": "^8.2.0",
105
- "@clerk/electron-passkeys": "0.0.4-snapshot.v20261029220359"
104
+ "tslib": "2.8.1",
105
+ "@clerk/electron-passkeys": "0.0.3"
106
106
  },
107
107
  "peerDependencies": {
108
- "@clerk/electron-passkeys": "0.0.4-snapshot.v20261029220359",
108
+ "@clerk/electron-passkeys": "*",
109
109
  "electron": ">=28",
110
110
  "electron-store": "^8.2.0",
111
111
  "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0",