@pollar/core 0.9.0 → 0.10.0-rc.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/dist/adapters/expo-secure-store.js +1 -1
- package/dist/adapters/expo-secure-store.js.map +1 -1
- package/dist/adapters/expo-secure-store.mjs +1 -1
- package/dist/adapters/expo-secure-store.mjs.map +1 -1
- package/dist/adapters/react-native-appstate.js +1 -1
- package/dist/adapters/react-native-appstate.js.map +1 -1
- package/dist/adapters/react-native-appstate.mjs +1 -1
- package/dist/adapters/react-native-appstate.mjs.map +1 -1
- package/dist/adapters/react-native-keychain.js +1 -1
- package/dist/adapters/react-native-keychain.js.map +1 -1
- package/dist/adapters/react-native-keychain.mjs +1 -1
- package/dist/adapters/react-native-keychain.mjs.map +1 -1
- package/dist/index.d.mts +2308 -1446
- package/dist/index.d.ts +2308 -1446
- package/dist/index.js +628 -124
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +628 -124
- package/dist/index.mjs.map +1 -1
- package/dist/index.rn.d.mts +2 -2
- package/dist/index.rn.d.ts +2 -2
- package/dist/index.rn.js +605 -123
- package/dist/index.rn.js.map +1 -1
- package/dist/index.rn.mjs +605 -123
- package/dist/index.rn.mjs.map +1 -1
- package/package.json +6 -6
|
@@ -11,7 +11,7 @@ async function loadSecureStore() {
|
|
|
11
11
|
return mod;
|
|
12
12
|
} catch (error) {
|
|
13
13
|
const message = `[PollarClient:storage] Failed to load 'expo-secure-store'. Install it in your Expo app: \`npx expo install expo-secure-store\`. Original error: ${error instanceof Error ? error.message : String(error)}`;
|
|
14
|
-
throw new Error(message);
|
|
14
|
+
throw new Error(message, { cause: error });
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
function utf8ByteLength(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/expo-secure-store.ts"],"names":[],"mappings":";;;AAqCO,IAAM,4BAAA,GAA+B;AAWrC,SAAS,YAAY,GAAA,EAAqB;AAC/C,EAAA,OAAO,GAAA,CAAI,OAAA,CAAQ,kBAAA,EAAoB,GAAG,CAAA;AAC5C;AAWA,eAAe,eAAA,GAA2C;AACxD,EAAA,IAAI;AAGF,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,mBAAmB,CAAA;AAC5C,IAAA,OAAO,GAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,OAAA,GACJ,mJAEmB,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA;AAC3E,IAAA,MAAM,IAAI,
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/expo-secure-store.ts"],"names":[],"mappings":";;;AAqCO,IAAM,4BAAA,GAA+B;AAWrC,SAAS,YAAY,GAAA,EAAqB;AAC/C,EAAA,OAAO,GAAA,CAAI,OAAA,CAAQ,kBAAA,EAAoB,GAAG,CAAA;AAC5C;AAWA,eAAe,eAAA,GAA2C;AACxD,EAAA,IAAI;AAGF,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,mBAAmB,CAAA;AAC5C,IAAA,OAAO,GAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,OAAA,GACJ,mJAEmB,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA;AAC3E,IAAA,MAAM,IAAI,KAAA,CAAM,OAAA,EAAS,EAAE,KAAA,EAAO,OAAO,CAAA;AAAA,EAC3C;AACF;AAEA,SAAS,eAAe,KAAA,EAAuB;AAC7C,EAAA,IAAI,OAAO,gBAAgB,WAAA,EAAa,OAAO,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,MAAA;AAE/E,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,UAAA,CAAW,CAAC,CAAA;AAC/B,IAAA,IAAI,IAAA,GAAO,KAAM,KAAA,IAAS,CAAA;AAAA,SAAA,IACjB,IAAA,GAAO,MAAO,KAAA,IAAS,CAAA;AAAA,SAAA,IACvB,IAAA,IAAQ,KAAA,IAAU,IAAA,IAAQ,KAAA,EAAQ;AAEzC,MAAA,KAAA,IAAS,CAAA;AACT,MAAA,CAAA,EAAA;AAAA,IACF,OAAO,KAAA,IAAS,CAAA;AAAA,EAClB;AACA,EAAA,OAAO,KAAA;AACT;AAQA,eAAsB,wBAAA,CAAyB,OAAA,GAAqC,EAAC,EAAqB;AACxG,EAAA,MAAM,WAAA,GAAc,MAAM,eAAA,EAAgB;AAE1C,EAAA,MAAM,aACJ,OAAA,CAAQ,kBAAA,KAAuB,MAAA,GAAY,OAAA,CAAQ,qBAAqB,WAAA,CAAY,8BAAA;AAEtF,EAAA,OAAO;AAAA,IACL,MAAM,IAAI,GAAA,EAAK;AACb,MAAA,OAAO,WAAA,CAAY,YAAA,CAAa,WAAA,CAAY,GAAG,CAAC,CAAA;AAAA,IAClD,CAAA;AAAA,IACA,MAAM,GAAA,CAAI,GAAA,EAAK,KAAA,EAAO;AACpB,MAAA,MAAM,IAAA,GAAO,eAAe,KAAK,CAAA;AACjC,MAAA,IAAI,OAAO,4BAAA,EAA8B;AACvC,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,kCAAA,EAAqC,GAAG,CAAA,KAAA,EAAQ,IAAI,qCAAqC,4BAA4B,CAAA;AAAA,SACvH;AAAA,MACF;AACA,MAAA,MAAM,WAAA,CAAY,YAAA;AAAA,QAChB,YAAY,GAAG,CAAA;AAAA,QACf,KAAA;AAAA,QACA,UAAA,KAAe,MAAA,GAAY,EAAE,kBAAA,EAAoB,YAAW,GAAI;AAAA,OAClE;AAAA,IACF,CAAA;AAAA,IACA,MAAM,OAAO,GAAA,EAAK;AAChB,MAAA,MAAM,WAAA,CAAY,eAAA,CAAgB,WAAA,CAAY,GAAG,CAAC,CAAA;AAAA,IACpD;AAAA,GACF;AACF","file":"expo-secure-store.js","sourcesContent":["import type { Storage } from '../storage/types';\n\n/**\n * Adapter that persists session and key material in the iOS Keychain / Android\n * Keystore via [`expo-secure-store`](https://docs.expo.dev/versions/latest/sdk/securestore/).\n *\n * `expo-secure-store` is an optional peer dependency; install it in your Expo\n * project with `npx expo install expo-secure-store`.\n *\n * The module is loaded lazily via dynamic `import('expo-secure-store')` so web\n * bundlers strip the dependency from web builds entirely.\n */\n\n/**\n * Minimal structural type for the parts of `expo-secure-store` we use. We\n * type the surface here instead of importing the package's types because the\n * package is an optional peer dependency and may not be installed when this\n * SDK is type-checked (e.g. web-only consumers).\n */\ntype SecureStoreApi = {\n getItemAsync: (key: string) => Promise<string | null>;\n setItemAsync: (key: string, value: string, options?: { keychainAccessible?: number }) => Promise<void>;\n deleteItemAsync: (key: string) => Promise<void>;\n /**\n * Default we use: requires the device to be unlocked and disables iCloud\n * Keychain backup of the value (so a stolen iCloud backup cannot exfiltrate\n * the SDK's private key material to another device).\n */\n WHEN_UNLOCKED_THIS_DEVICE_ONLY?: number;\n};\n\n/**\n * Hard cap per stored value. Generously above what the SDK actually writes\n * (sessions ≈ 600–800 bytes, private scalars ≈ 43 chars), and well within\n * iOS Keychain's practical limit. Refuses oversized writes loudly rather\n * than letting the platform truncate or silently fail.\n */\nexport const SECURE_STORE_MAX_VALUE_BYTES = 4096;\n\n/**\n * Map a logical storage key onto one Expo SecureStore accepts. SecureStore keys\n * must match `[A-Za-z0-9._-]`, but the SDK namespaces its keys with `:`\n * (`pollar:<apiKeyHash>:session`, `pollar:dpop-key:<apiKeyHash>`, …), which\n * SecureStore rejects with an `Invalid key` error. Replace every disallowed\n * character with `_`. The transform is deterministic and collision-free for the\n * SDK's fixed key templates — their only variable segment is a hex hash — so a\n * given logical key always resolves to the same SecureStore key.\n */\nexport function sanitizeKey(key: string): string {\n return key.replace(/[^A-Za-z0-9._-]/g, '_');\n}\n\nexport interface SecureStoreAdapterOptions {\n /**\n * Override the iOS Keychain accessibility class. Defaults to\n * `WHEN_UNLOCKED_THIS_DEVICE_ONLY` when available on the loaded module.\n * On Android this is a no-op (the platform manages access via Keystore).\n */\n keychainAccessible?: number;\n}\n\nasync function loadSecureStore(): Promise<SecureStoreApi> {\n try {\n // @ts-expect-error -- optional peer dep; not present when the SDK is built or\n // when the SDK runs on web. Resolved at runtime in Expo / RN apps.\n const mod = await import('expo-secure-store');\n return mod as unknown as SecureStoreApi;\n } catch (error) {\n const message =\n `[PollarClient:storage] Failed to load 'expo-secure-store'. ` +\n `Install it in your Expo app: \\`npx expo install expo-secure-store\\`. ` +\n `Original error: ${error instanceof Error ? error.message : String(error)}`;\n throw new Error(message, { cause: error });\n }\n}\n\nfunction utf8ByteLength(value: string): number {\n if (typeof TextEncoder !== 'undefined') return new TextEncoder().encode(value).length;\n // Fallback: count UTF-8 bytes manually for environments without TextEncoder.\n let bytes = 0;\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i);\n if (code < 0x80) bytes += 1;\n else if (code < 0x800) bytes += 2;\n else if (code >= 0xd800 && code <= 0xdbff) {\n // Surrogate pair → 4 bytes; advance the index.\n bytes += 4;\n i++;\n } else bytes += 3;\n }\n return bytes;\n}\n\n/**\n * Create a `Storage` adapter backed by Expo SecureStore.\n *\n * Throws synchronously (via the returned Promise) at construction time if\n * `expo-secure-store` cannot be loaded.\n */\nexport async function createSecureStoreAdapter(options: SecureStoreAdapterOptions = {}): Promise<Storage> {\n const SecureStore = await loadSecureStore();\n\n const accessible =\n options.keychainAccessible !== undefined ? options.keychainAccessible : SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY;\n\n return {\n async get(key) {\n return SecureStore.getItemAsync(sanitizeKey(key));\n },\n async set(key, value) {\n const size = utf8ByteLength(value);\n if (size > SECURE_STORE_MAX_VALUE_BYTES) {\n throw new Error(\n `[PollarClient:storage] Value for \"${key}\" is ${size} bytes, exceeds SecureStore limit ${SECURE_STORE_MAX_VALUE_BYTES}`,\n );\n }\n await SecureStore.setItemAsync(\n sanitizeKey(key),\n value,\n accessible !== undefined ? { keychainAccessible: accessible } : undefined,\n );\n },\n async remove(key) {\n await SecureStore.deleteItemAsync(sanitizeKey(key));\n },\n };\n}\n"]}
|
|
@@ -9,7 +9,7 @@ async function loadSecureStore() {
|
|
|
9
9
|
return mod;
|
|
10
10
|
} catch (error) {
|
|
11
11
|
const message = `[PollarClient:storage] Failed to load 'expo-secure-store'. Install it in your Expo app: \`npx expo install expo-secure-store\`. Original error: ${error instanceof Error ? error.message : String(error)}`;
|
|
12
|
-
throw new Error(message);
|
|
12
|
+
throw new Error(message, { cause: error });
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
function utf8ByteLength(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/expo-secure-store.ts"],"names":[],"mappings":";AAqCO,IAAM,4BAAA,GAA+B;AAWrC,SAAS,YAAY,GAAA,EAAqB;AAC/C,EAAA,OAAO,GAAA,CAAI,OAAA,CAAQ,kBAAA,EAAoB,GAAG,CAAA;AAC5C;AAWA,eAAe,eAAA,GAA2C;AACxD,EAAA,IAAI;AAGF,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,mBAAmB,CAAA;AAC5C,IAAA,OAAO,GAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,OAAA,GACJ,mJAEmB,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA;AAC3E,IAAA,MAAM,IAAI,
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/expo-secure-store.ts"],"names":[],"mappings":";AAqCO,IAAM,4BAAA,GAA+B;AAWrC,SAAS,YAAY,GAAA,EAAqB;AAC/C,EAAA,OAAO,GAAA,CAAI,OAAA,CAAQ,kBAAA,EAAoB,GAAG,CAAA;AAC5C;AAWA,eAAe,eAAA,GAA2C;AACxD,EAAA,IAAI;AAGF,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,mBAAmB,CAAA;AAC5C,IAAA,OAAO,GAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,OAAA,GACJ,mJAEmB,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA;AAC3E,IAAA,MAAM,IAAI,KAAA,CAAM,OAAA,EAAS,EAAE,KAAA,EAAO,OAAO,CAAA;AAAA,EAC3C;AACF;AAEA,SAAS,eAAe,KAAA,EAAuB;AAC7C,EAAA,IAAI,OAAO,gBAAgB,WAAA,EAAa,OAAO,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,MAAA;AAE/E,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,UAAA,CAAW,CAAC,CAAA;AAC/B,IAAA,IAAI,IAAA,GAAO,KAAM,KAAA,IAAS,CAAA;AAAA,SAAA,IACjB,IAAA,GAAO,MAAO,KAAA,IAAS,CAAA;AAAA,SAAA,IACvB,IAAA,IAAQ,KAAA,IAAU,IAAA,IAAQ,KAAA,EAAQ;AAEzC,MAAA,KAAA,IAAS,CAAA;AACT,MAAA,CAAA,EAAA;AAAA,IACF,OAAO,KAAA,IAAS,CAAA;AAAA,EAClB;AACA,EAAA,OAAO,KAAA;AACT;AAQA,eAAsB,wBAAA,CAAyB,OAAA,GAAqC,EAAC,EAAqB;AACxG,EAAA,MAAM,WAAA,GAAc,MAAM,eAAA,EAAgB;AAE1C,EAAA,MAAM,aACJ,OAAA,CAAQ,kBAAA,KAAuB,MAAA,GAAY,OAAA,CAAQ,qBAAqB,WAAA,CAAY,8BAAA;AAEtF,EAAA,OAAO;AAAA,IACL,MAAM,IAAI,GAAA,EAAK;AACb,MAAA,OAAO,WAAA,CAAY,YAAA,CAAa,WAAA,CAAY,GAAG,CAAC,CAAA;AAAA,IAClD,CAAA;AAAA,IACA,MAAM,GAAA,CAAI,GAAA,EAAK,KAAA,EAAO;AACpB,MAAA,MAAM,IAAA,GAAO,eAAe,KAAK,CAAA;AACjC,MAAA,IAAI,OAAO,4BAAA,EAA8B;AACvC,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,kCAAA,EAAqC,GAAG,CAAA,KAAA,EAAQ,IAAI,qCAAqC,4BAA4B,CAAA;AAAA,SACvH;AAAA,MACF;AACA,MAAA,MAAM,WAAA,CAAY,YAAA;AAAA,QAChB,YAAY,GAAG,CAAA;AAAA,QACf,KAAA;AAAA,QACA,UAAA,KAAe,MAAA,GAAY,EAAE,kBAAA,EAAoB,YAAW,GAAI;AAAA,OAClE;AAAA,IACF,CAAA;AAAA,IACA,MAAM,OAAO,GAAA,EAAK;AAChB,MAAA,MAAM,WAAA,CAAY,eAAA,CAAgB,WAAA,CAAY,GAAG,CAAC,CAAA;AAAA,IACpD;AAAA,GACF;AACF","file":"expo-secure-store.mjs","sourcesContent":["import type { Storage } from '../storage/types';\n\n/**\n * Adapter that persists session and key material in the iOS Keychain / Android\n * Keystore via [`expo-secure-store`](https://docs.expo.dev/versions/latest/sdk/securestore/).\n *\n * `expo-secure-store` is an optional peer dependency; install it in your Expo\n * project with `npx expo install expo-secure-store`.\n *\n * The module is loaded lazily via dynamic `import('expo-secure-store')` so web\n * bundlers strip the dependency from web builds entirely.\n */\n\n/**\n * Minimal structural type for the parts of `expo-secure-store` we use. We\n * type the surface here instead of importing the package's types because the\n * package is an optional peer dependency and may not be installed when this\n * SDK is type-checked (e.g. web-only consumers).\n */\ntype SecureStoreApi = {\n getItemAsync: (key: string) => Promise<string | null>;\n setItemAsync: (key: string, value: string, options?: { keychainAccessible?: number }) => Promise<void>;\n deleteItemAsync: (key: string) => Promise<void>;\n /**\n * Default we use: requires the device to be unlocked and disables iCloud\n * Keychain backup of the value (so a stolen iCloud backup cannot exfiltrate\n * the SDK's private key material to another device).\n */\n WHEN_UNLOCKED_THIS_DEVICE_ONLY?: number;\n};\n\n/**\n * Hard cap per stored value. Generously above what the SDK actually writes\n * (sessions ≈ 600–800 bytes, private scalars ≈ 43 chars), and well within\n * iOS Keychain's practical limit. Refuses oversized writes loudly rather\n * than letting the platform truncate or silently fail.\n */\nexport const SECURE_STORE_MAX_VALUE_BYTES = 4096;\n\n/**\n * Map a logical storage key onto one Expo SecureStore accepts. SecureStore keys\n * must match `[A-Za-z0-9._-]`, but the SDK namespaces its keys with `:`\n * (`pollar:<apiKeyHash>:session`, `pollar:dpop-key:<apiKeyHash>`, …), which\n * SecureStore rejects with an `Invalid key` error. Replace every disallowed\n * character with `_`. The transform is deterministic and collision-free for the\n * SDK's fixed key templates — their only variable segment is a hex hash — so a\n * given logical key always resolves to the same SecureStore key.\n */\nexport function sanitizeKey(key: string): string {\n return key.replace(/[^A-Za-z0-9._-]/g, '_');\n}\n\nexport interface SecureStoreAdapterOptions {\n /**\n * Override the iOS Keychain accessibility class. Defaults to\n * `WHEN_UNLOCKED_THIS_DEVICE_ONLY` when available on the loaded module.\n * On Android this is a no-op (the platform manages access via Keystore).\n */\n keychainAccessible?: number;\n}\n\nasync function loadSecureStore(): Promise<SecureStoreApi> {\n try {\n // @ts-expect-error -- optional peer dep; not present when the SDK is built or\n // when the SDK runs on web. Resolved at runtime in Expo / RN apps.\n const mod = await import('expo-secure-store');\n return mod as unknown as SecureStoreApi;\n } catch (error) {\n const message =\n `[PollarClient:storage] Failed to load 'expo-secure-store'. ` +\n `Install it in your Expo app: \\`npx expo install expo-secure-store\\`. ` +\n `Original error: ${error instanceof Error ? error.message : String(error)}`;\n throw new Error(message, { cause: error });\n }\n}\n\nfunction utf8ByteLength(value: string): number {\n if (typeof TextEncoder !== 'undefined') return new TextEncoder().encode(value).length;\n // Fallback: count UTF-8 bytes manually for environments without TextEncoder.\n let bytes = 0;\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i);\n if (code < 0x80) bytes += 1;\n else if (code < 0x800) bytes += 2;\n else if (code >= 0xd800 && code <= 0xdbff) {\n // Surrogate pair → 4 bytes; advance the index.\n bytes += 4;\n i++;\n } else bytes += 3;\n }\n return bytes;\n}\n\n/**\n * Create a `Storage` adapter backed by Expo SecureStore.\n *\n * Throws synchronously (via the returned Promise) at construction time if\n * `expo-secure-store` cannot be loaded.\n */\nexport async function createSecureStoreAdapter(options: SecureStoreAdapterOptions = {}): Promise<Storage> {\n const SecureStore = await loadSecureStore();\n\n const accessible =\n options.keychainAccessible !== undefined ? options.keychainAccessible : SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY;\n\n return {\n async get(key) {\n return SecureStore.getItemAsync(sanitizeKey(key));\n },\n async set(key, value) {\n const size = utf8ByteLength(value);\n if (size > SECURE_STORE_MAX_VALUE_BYTES) {\n throw new Error(\n `[PollarClient:storage] Value for \"${key}\" is ${size} bytes, exceeds SecureStore limit ${SECURE_STORE_MAX_VALUE_BYTES}`,\n );\n }\n await SecureStore.setItemAsync(\n sanitizeKey(key),\n value,\n accessible !== undefined ? { keychainAccessible: accessible } : undefined,\n );\n },\n async remove(key) {\n await SecureStore.deleteItemAsync(sanitizeKey(key));\n },\n };\n}\n"]}
|
|
@@ -11,7 +11,7 @@ async function loadAppState() {
|
|
|
11
11
|
return AppState;
|
|
12
12
|
} catch (error) {
|
|
13
13
|
const message = `[PollarClient:visibility] Failed to load 'react-native' AppState. This adapter only runs inside a React Native app. Original error: ${error instanceof Error ? error.message : String(error)}`;
|
|
14
|
-
throw new Error(message);
|
|
14
|
+
throw new Error(message, { cause: error });
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
async function createAppStateVisibilityProvider() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/react-native-appstate.ts"],"names":[],"mappings":";;;AA8BA,eAAe,YAAA,GAAqC;AAClD,EAAA,IAAI;AAGF,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,cAAc,CAAA;AACvC,IAAA,MAAM,QAAA,
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/react-native-appstate.ts"],"names":[],"mappings":";;;AA8BA,eAAe,YAAA,GAAqC;AAClD,EAAA,IAAI;AAGF,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,cAAc,CAAA;AACvC,IAAA,MAAM,QAAA,GACH,GAAA,CAAmC,QAAA,IAAa,GAAA,CAAiD,OAAA,EAAS,QAAA;AAC7G,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAAA,IACxE;AACA,IAAA,OAAO,QAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,OAAA,GACJ,uIAEmB,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA;AAC3E,IAAA,MAAM,IAAI,KAAA,CAAM,OAAA,EAAS,EAAE,KAAA,EAAO,OAAO,CAAA;AAAA,EAC3C;AACF;AAOA,eAAsB,gCAAA,GAAgE;AACpF,EAAA,MAAM,QAAA,GAAW,MAAM,YAAA,EAAa;AAEpC,EAAA,MAAM,QAAA,GAAW,CAAC,KAAA,KAA2B,KAAA,KAAU,QAAA;AAEvD,EAAA,OAAO;AAAA,IACL,SAAA,EAAW,MAAM,QAAA,CAAS,QAAA,CAAS,YAAY,CAAA;AAAA,IAC/C,QAAA,EAAU,CAAC,EAAA,KAAO;AAIhB,MAAA,IAAI,IAAA,GAAO,QAAA,CAAS,QAAA,CAAS,YAAY,CAAA;AACzC,MAAA,MAAM,YAAA,GAAe,QAAA,CAAS,gBAAA,CAAiB,QAAA,EAAU,CAAC,KAAA,KAAU;AAClE,QAAA,MAAM,IAAA,GAAO,SAAS,KAAK,CAAA;AAC3B,QAAA,IAAI,SAAS,IAAA,EAAM;AACjB,UAAA,IAAA,GAAO,IAAA;AACP,UAAA,EAAA,CAAG,IAAI,CAAA;AAAA,QACT;AAAA,MACF,CAAC,CAAA;AACD,MAAA,OAAO,MAAM,aAAa,MAAA,EAAO;AAAA,IACnC;AAAA,GACF;AACF","file":"react-native-appstate.js","sourcesContent":["import type { VisibilityProvider } from '../visibility/types';\n\n/**\n * `AppState`-backed {@link VisibilityProvider} for React Native.\n *\n * Wire it into the silent-refresh scheduler so proactive token renewals are\n * skipped while the app is backgrounded and run the moment it returns to the\n * foreground — matching the web `visibilitychange` behavior and sidestepping\n * RN's aggressive background timer throttling.\n *\n * `react-native` is the consumer's framework (not a dependency of this SDK),\n * so the module is loaded lazily via dynamic `import('react-native')`. That\n * keeps web/Node bundles from ever resolving it. Because loading is async, the\n * factory is async too — mirror the `createSecureStoreAdapter` usage:\n *\n * import { createAppStateVisibilityProvider } from '@pollar/core/adapters/react-native-appstate';\n * const visibilityProvider = await createAppStateVisibilityProvider();\n * new PollarClient({ apiKey, storage, visibilityProvider });\n */\n\n/**\n * Minimal structural type for the slice of `react-native`'s `AppState` we use.\n * Typed here instead of importing the package's types because `react-native`\n * is an optional peer the SDK is not type-checked against.\n */\ntype AppStateApi = {\n currentState: string;\n addEventListener: (type: 'change', handler: (state: string) => void) => { remove: () => void };\n};\n\nasync function loadAppState(): Promise<AppStateApi> {\n try {\n // @ts-expect-error -- optional peer dep; resolved at runtime in RN apps,\n // absent when the SDK is built or run on web/Node.\n const mod = await import('react-native');\n const AppState =\n (mod as { AppState?: AppStateApi }).AppState ?? (mod as { default?: { AppState?: AppStateApi } }).default?.AppState;\n if (!AppState) {\n throw new Error(\"'react-native' loaded but exposes no AppState export\");\n }\n return AppState;\n } catch (error) {\n const message =\n `[PollarClient:visibility] Failed to load 'react-native' AppState. ` +\n `This adapter only runs inside a React Native app. ` +\n `Original error: ${error instanceof Error ? error.message : String(error)}`;\n throw new Error(message, { cause: error });\n }\n}\n\n/**\n * Create a `VisibilityProvider` backed by React Native's `AppState`.\n *\n * Throws (via the returned Promise) if `react-native` cannot be loaded.\n */\nexport async function createAppStateVisibilityProvider(): Promise<VisibilityProvider> {\n const AppState = await loadAppState();\n\n const isActive = (state: string): boolean => state === 'active';\n\n return {\n isVisible: () => isActive(AppState.currentState),\n onChange: (cb) => {\n // Filter duplicate notifications — listeners only see real transitions,\n // matching the web provider's contract. RN also emits 'inactive'\n // (iOS transition state) which we collapse into \"not visible\".\n let last = isActive(AppState.currentState);\n const subscription = AppState.addEventListener('change', (state) => {\n const next = isActive(state);\n if (next !== last) {\n last = next;\n cb(next);\n }\n });\n return () => subscription.remove();\n },\n };\n}\n"]}
|
|
@@ -9,7 +9,7 @@ async function loadAppState() {
|
|
|
9
9
|
return AppState;
|
|
10
10
|
} catch (error) {
|
|
11
11
|
const message = `[PollarClient:visibility] Failed to load 'react-native' AppState. This adapter only runs inside a React Native app. Original error: ${error instanceof Error ? error.message : String(error)}`;
|
|
12
|
-
throw new Error(message);
|
|
12
|
+
throw new Error(message, { cause: error });
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
async function createAppStateVisibilityProvider() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/react-native-appstate.ts"],"names":[],"mappings":";AA8BA,eAAe,YAAA,GAAqC;AAClD,EAAA,IAAI;AAGF,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,cAAc,CAAA;AACvC,IAAA,MAAM,QAAA,
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/react-native-appstate.ts"],"names":[],"mappings":";AA8BA,eAAe,YAAA,GAAqC;AAClD,EAAA,IAAI;AAGF,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,cAAc,CAAA;AACvC,IAAA,MAAM,QAAA,GACH,GAAA,CAAmC,QAAA,IAAa,GAAA,CAAiD,OAAA,EAAS,QAAA;AAC7G,IAAA,IAAI,CAAC,QAAA,EAAU;AACb,MAAA,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAAA,IACxE;AACA,IAAA,OAAO,QAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,OAAA,GACJ,uIAEmB,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA;AAC3E,IAAA,MAAM,IAAI,KAAA,CAAM,OAAA,EAAS,EAAE,KAAA,EAAO,OAAO,CAAA;AAAA,EAC3C;AACF;AAOA,eAAsB,gCAAA,GAAgE;AACpF,EAAA,MAAM,QAAA,GAAW,MAAM,YAAA,EAAa;AAEpC,EAAA,MAAM,QAAA,GAAW,CAAC,KAAA,KAA2B,KAAA,KAAU,QAAA;AAEvD,EAAA,OAAO;AAAA,IACL,SAAA,EAAW,MAAM,QAAA,CAAS,QAAA,CAAS,YAAY,CAAA;AAAA,IAC/C,QAAA,EAAU,CAAC,EAAA,KAAO;AAIhB,MAAA,IAAI,IAAA,GAAO,QAAA,CAAS,QAAA,CAAS,YAAY,CAAA;AACzC,MAAA,MAAM,YAAA,GAAe,QAAA,CAAS,gBAAA,CAAiB,QAAA,EAAU,CAAC,KAAA,KAAU;AAClE,QAAA,MAAM,IAAA,GAAO,SAAS,KAAK,CAAA;AAC3B,QAAA,IAAI,SAAS,IAAA,EAAM;AACjB,UAAA,IAAA,GAAO,IAAA;AACP,UAAA,EAAA,CAAG,IAAI,CAAA;AAAA,QACT;AAAA,MACF,CAAC,CAAA;AACD,MAAA,OAAO,MAAM,aAAa,MAAA,EAAO;AAAA,IACnC;AAAA,GACF;AACF","file":"react-native-appstate.mjs","sourcesContent":["import type { VisibilityProvider } from '../visibility/types';\n\n/**\n * `AppState`-backed {@link VisibilityProvider} for React Native.\n *\n * Wire it into the silent-refresh scheduler so proactive token renewals are\n * skipped while the app is backgrounded and run the moment it returns to the\n * foreground — matching the web `visibilitychange` behavior and sidestepping\n * RN's aggressive background timer throttling.\n *\n * `react-native` is the consumer's framework (not a dependency of this SDK),\n * so the module is loaded lazily via dynamic `import('react-native')`. That\n * keeps web/Node bundles from ever resolving it. Because loading is async, the\n * factory is async too — mirror the `createSecureStoreAdapter` usage:\n *\n * import { createAppStateVisibilityProvider } from '@pollar/core/adapters/react-native-appstate';\n * const visibilityProvider = await createAppStateVisibilityProvider();\n * new PollarClient({ apiKey, storage, visibilityProvider });\n */\n\n/**\n * Minimal structural type for the slice of `react-native`'s `AppState` we use.\n * Typed here instead of importing the package's types because `react-native`\n * is an optional peer the SDK is not type-checked against.\n */\ntype AppStateApi = {\n currentState: string;\n addEventListener: (type: 'change', handler: (state: string) => void) => { remove: () => void };\n};\n\nasync function loadAppState(): Promise<AppStateApi> {\n try {\n // @ts-expect-error -- optional peer dep; resolved at runtime in RN apps,\n // absent when the SDK is built or run on web/Node.\n const mod = await import('react-native');\n const AppState =\n (mod as { AppState?: AppStateApi }).AppState ?? (mod as { default?: { AppState?: AppStateApi } }).default?.AppState;\n if (!AppState) {\n throw new Error(\"'react-native' loaded but exposes no AppState export\");\n }\n return AppState;\n } catch (error) {\n const message =\n `[PollarClient:visibility] Failed to load 'react-native' AppState. ` +\n `This adapter only runs inside a React Native app. ` +\n `Original error: ${error instanceof Error ? error.message : String(error)}`;\n throw new Error(message, { cause: error });\n }\n}\n\n/**\n * Create a `VisibilityProvider` backed by React Native's `AppState`.\n *\n * Throws (via the returned Promise) if `react-native` cannot be loaded.\n */\nexport async function createAppStateVisibilityProvider(): Promise<VisibilityProvider> {\n const AppState = await loadAppState();\n\n const isActive = (state: string): boolean => state === 'active';\n\n return {\n isVisible: () => isActive(AppState.currentState),\n onChange: (cb) => {\n // Filter duplicate notifications — listeners only see real transitions,\n // matching the web provider's contract. RN also emits 'inactive'\n // (iOS transition state) which we collapse into \"not visible\".\n let last = isActive(AppState.currentState);\n const subscription = AppState.addEventListener('change', (state) => {\n const next = isActive(state);\n if (next !== last) {\n last = next;\n cb(next);\n }\n });\n return () => subscription.remove();\n },\n };\n}\n"]}
|
|
@@ -8,7 +8,7 @@ async function loadKeychain() {
|
|
|
8
8
|
return mod;
|
|
9
9
|
} catch (error) {
|
|
10
10
|
const message = `[PollarClient:storage] Failed to load 'react-native-keychain'. Install it in your React Native app: \`npm i react-native-keychain\` (plus iOS pod install). Original error: ${error instanceof Error ? error.message : String(error)}`;
|
|
11
|
-
throw new Error(message);
|
|
11
|
+
throw new Error(message, { cause: error });
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
function utf8ByteLength(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/react-native-keychain.ts"],"names":[],"mappings":";;;AA8CO,IAAM,wBAAA,GAA2B;AAYxC,eAAe,YAAA,GAAqC;AAClD,EAAA,IAAI;AAGF,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,uBAAuB,CAAA;AAChD,IAAA,OAAO,GAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,OAAA,GACJ,+KAE2C,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA;AACnG,IAAA,MAAM,IAAI,
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/react-native-keychain.ts"],"names":[],"mappings":";;;AA8CO,IAAM,wBAAA,GAA2B;AAYxC,eAAe,YAAA,GAAqC;AAClD,EAAA,IAAI;AAGF,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,uBAAuB,CAAA;AAChD,IAAA,OAAO,GAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,OAAA,GACJ,+KAE2C,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA;AACnG,IAAA,MAAM,IAAI,KAAA,CAAM,OAAA,EAAS,EAAE,KAAA,EAAO,OAAO,CAAA;AAAA,EAC3C;AACF;AAEA,SAAS,eAAe,KAAA,EAAuB;AAC7C,EAAA,IAAI,OAAO,gBAAgB,WAAA,EAAa,OAAO,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,MAAA;AAC/E,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,UAAA,CAAW,CAAC,CAAA;AAC/B,IAAA,IAAI,IAAA,GAAO,KAAM,KAAA,IAAS,CAAA;AAAA,SAAA,IACjB,IAAA,GAAO,MAAO,KAAA,IAAS,CAAA;AAAA,SAAA,IACvB,IAAA,IAAQ,KAAA,IAAU,IAAA,IAAQ,KAAA,EAAQ;AACzC,MAAA,KAAA,IAAS,CAAA;AACT,MAAA,CAAA,EAAA;AAAA,IACF,OAAO,KAAA,IAAS,CAAA;AAAA,EAClB;AACA,EAAA,OAAO,KAAA;AACT;AAQA,eAAsB,qBAAA,CAAsB,OAAA,GAAkC,EAAC,EAAqB;AAClG,EAAA,MAAM,QAAA,GAAW,MAAM,YAAA,EAAa;AAEpC,EAAA,MAAM,UAAA,GACJ,QAAQ,UAAA,KAAe,MAAA,GAAY,QAAQ,UAAA,GAAa,QAAA,CAAS,aAAa,gCAAgC,CAAA;AAEhH,EAAA,SAAS,aAAa,GAAA,EAA8B;AAClD,IAAA,MAAM,IAAA,GAAwB,EAAE,OAAA,EAAS,GAAA,EAAI;AAC7C,IAAA,IAAI,UAAA,KAAe,MAAA,EAAW,IAAA,CAAK,UAAA,GAAa,UAAA;AAChD,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,IAAI,GAAA,EAAK;AACb,MAAA,MAAM,SAAS,MAAM,QAAA,CAAS,mBAAmB,EAAE,OAAA,EAAS,KAAK,CAAA;AACjE,MAAA,IAAI,MAAA,KAAW,OAAO,OAAO,IAAA;AAC7B,MAAA,OAAO,MAAA,CAAO,QAAA;AAAA,IAChB,CAAA;AAAA,IACA,MAAM,GAAA,CAAI,GAAA,EAAK,KAAA,EAAO;AACpB,MAAA,MAAM,IAAA,GAAO,eAAe,KAAK,CAAA;AACjC,MAAA,IAAI,OAAO,wBAAA,EAA0B;AACnC,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,kCAAA,EAAqC,GAAG,CAAA,KAAA,EAAQ,IAAI,kCAAkC,wBAAwB,CAAA;AAAA,SAChH;AAAA,MACF;AAGA,MAAA,MAAM,SAAS,kBAAA,CAAmB,GAAA,EAAK,KAAA,EAAO,YAAA,CAAa,GAAG,CAAC,CAAA;AAAA,IACjE,CAAA;AAAA,IACA,MAAM,OAAO,GAAA,EAAK;AAChB,MAAA,MAAM,QAAA,CAAS,oBAAA,CAAqB,EAAE,OAAA,EAAS,KAAK,CAAA;AAAA,IACtD;AAAA,GACF;AACF","file":"react-native-keychain.js","sourcesContent":["import type { Storage } from '../storage/types';\n\n/**\n * Adapter that persists session and key material in the iOS Keychain / Android\n * Keystore via [`react-native-keychain`](https://github.com/oblador/react-native-keychain).\n *\n * `react-native-keychain` is an optional peer dependency; install it in your\n * React Native project with `npm i react-native-keychain` (and follow its\n * iOS pod-install / Android linking instructions).\n *\n * The module is loaded lazily via dynamic `import('react-native-keychain')`\n * so web bundlers strip the dependency from web builds entirely.\n *\n * Storage model: one Keychain `service` per logical key. Each `Storage.set(k, v)`\n * call writes a separate Keychain entry under `service = k`; this keeps the\n * adapter simple but means the number of distinct keys you write should stay\n * bounded (the SDK uses 2–3 keys per `apiKeyHash`).\n */\n\ntype KeychainOptions = {\n service?: string;\n accessible?: string;\n};\n\ntype KeychainCredentials = {\n username: string;\n password: string;\n service: string;\n storage?: string;\n};\n\ntype KeychainApi = {\n setGenericPassword: (\n username: string,\n password: string,\n options?: KeychainOptions,\n ) => Promise<false | { service: string; storage?: string }>;\n getGenericPassword: (options?: KeychainOptions) => Promise<false | KeychainCredentials>;\n resetGenericPassword: (options?: KeychainOptions) => Promise<boolean>;\n ACCESSIBLE?: Record<string, string | undefined>;\n};\n\n/**\n * Hard cap per stored value. iOS Keychain has no formal byte limit but\n * practical limits sit a few KB; we refuse oversized writes loudly.\n */\nexport const KEYCHAIN_MAX_VALUE_BYTES = 4096;\n\nexport interface KeychainAdapterOptions {\n /**\n * Override the iOS Keychain accessibility class. Defaults to\n * `ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY` when available on the loaded\n * module — that prevents iCloud Keychain backup from carrying the SDK's\n * private key material to another device.\n */\n accessible?: string;\n}\n\nasync function loadKeychain(): Promise<KeychainApi> {\n try {\n // @ts-expect-error -- optional peer dep; not present when the SDK is built or\n // when the SDK runs on web. Resolved at runtime in React Native apps.\n const mod = await import('react-native-keychain');\n return mod as unknown as KeychainApi;\n } catch (error) {\n const message =\n `[PollarClient:storage] Failed to load 'react-native-keychain'. ` +\n `Install it in your React Native app: \\`npm i react-native-keychain\\` ` +\n `(plus iOS pod install). Original error: ${error instanceof Error ? error.message : String(error)}`;\n throw new Error(message, { cause: error });\n }\n}\n\nfunction utf8ByteLength(value: string): number {\n if (typeof TextEncoder !== 'undefined') return new TextEncoder().encode(value).length;\n let bytes = 0;\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i);\n if (code < 0x80) bytes += 1;\n else if (code < 0x800) bytes += 2;\n else if (code >= 0xd800 && code <= 0xdbff) {\n bytes += 4;\n i++;\n } else bytes += 3;\n }\n return bytes;\n}\n\n/**\n * Create a `Storage` adapter backed by `react-native-keychain`.\n *\n * Throws (via the returned Promise) at construction time if the package\n * cannot be loaded.\n */\nexport async function createKeychainAdapter(options: KeychainAdapterOptions = {}): Promise<Storage> {\n const Keychain = await loadKeychain();\n\n const accessible: string | undefined =\n options.accessible !== undefined ? options.accessible : Keychain.ACCESSIBLE?.['WHEN_UNLOCKED_THIS_DEVICE_ONLY'];\n\n function buildOptions(key: string): KeychainOptions {\n const opts: KeychainOptions = { service: key };\n if (accessible !== undefined) opts.accessible = accessible;\n return opts;\n }\n\n return {\n async get(key) {\n const result = await Keychain.getGenericPassword({ service: key });\n if (result === false) return null;\n return result.password;\n },\n async set(key, value) {\n const size = utf8ByteLength(value);\n if (size > KEYCHAIN_MAX_VALUE_BYTES) {\n throw new Error(\n `[PollarClient:storage] Value for \"${key}\" is ${size} bytes, exceeds Keychain limit ${KEYCHAIN_MAX_VALUE_BYTES}`,\n );\n }\n // Use the storage key as both the username and the service so a\n // (service, account) lookup is unambiguous on both platforms.\n await Keychain.setGenericPassword(key, value, buildOptions(key));\n },\n async remove(key) {\n await Keychain.resetGenericPassword({ service: key });\n },\n };\n}\n"]}
|
|
@@ -6,7 +6,7 @@ async function loadKeychain() {
|
|
|
6
6
|
return mod;
|
|
7
7
|
} catch (error) {
|
|
8
8
|
const message = `[PollarClient:storage] Failed to load 'react-native-keychain'. Install it in your React Native app: \`npm i react-native-keychain\` (plus iOS pod install). Original error: ${error instanceof Error ? error.message : String(error)}`;
|
|
9
|
-
throw new Error(message);
|
|
9
|
+
throw new Error(message, { cause: error });
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
function utf8ByteLength(value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/react-native-keychain.ts"],"names":[],"mappings":";AA8CO,IAAM,wBAAA,GAA2B;AAYxC,eAAe,YAAA,GAAqC;AAClD,EAAA,IAAI;AAGF,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,uBAAuB,CAAA;AAChD,IAAA,OAAO,GAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,OAAA,GACJ,+KAE2C,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA;AACnG,IAAA,MAAM,IAAI,
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/react-native-keychain.ts"],"names":[],"mappings":";AA8CO,IAAM,wBAAA,GAA2B;AAYxC,eAAe,YAAA,GAAqC;AAClD,EAAA,IAAI;AAGF,IAAA,MAAM,GAAA,GAAM,MAAM,OAAO,uBAAuB,CAAA;AAChD,IAAA,OAAO,GAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,OAAA,GACJ,+KAE2C,KAAA,YAAiB,KAAA,GAAQ,MAAM,OAAA,GAAU,MAAA,CAAO,KAAK,CAAC,CAAA,CAAA;AACnG,IAAA,MAAM,IAAI,KAAA,CAAM,OAAA,EAAS,EAAE,KAAA,EAAO,OAAO,CAAA;AAAA,EAC3C;AACF;AAEA,SAAS,eAAe,KAAA,EAAuB;AAC7C,EAAA,IAAI,OAAO,gBAAgB,WAAA,EAAa,OAAO,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,KAAK,CAAA,CAAE,MAAA;AAC/E,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,IAAA,MAAM,IAAA,GAAO,KAAA,CAAM,UAAA,CAAW,CAAC,CAAA;AAC/B,IAAA,IAAI,IAAA,GAAO,KAAM,KAAA,IAAS,CAAA;AAAA,SAAA,IACjB,IAAA,GAAO,MAAO,KAAA,IAAS,CAAA;AAAA,SAAA,IACvB,IAAA,IAAQ,KAAA,IAAU,IAAA,IAAQ,KAAA,EAAQ;AACzC,MAAA,KAAA,IAAS,CAAA;AACT,MAAA,CAAA,EAAA;AAAA,IACF,OAAO,KAAA,IAAS,CAAA;AAAA,EAClB;AACA,EAAA,OAAO,KAAA;AACT;AAQA,eAAsB,qBAAA,CAAsB,OAAA,GAAkC,EAAC,EAAqB;AAClG,EAAA,MAAM,QAAA,GAAW,MAAM,YAAA,EAAa;AAEpC,EAAA,MAAM,UAAA,GACJ,QAAQ,UAAA,KAAe,MAAA,GAAY,QAAQ,UAAA,GAAa,QAAA,CAAS,aAAa,gCAAgC,CAAA;AAEhH,EAAA,SAAS,aAAa,GAAA,EAA8B;AAClD,IAAA,MAAM,IAAA,GAAwB,EAAE,OAAA,EAAS,GAAA,EAAI;AAC7C,IAAA,IAAI,UAAA,KAAe,MAAA,EAAW,IAAA,CAAK,UAAA,GAAa,UAAA;AAChD,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,IAAI,GAAA,EAAK;AACb,MAAA,MAAM,SAAS,MAAM,QAAA,CAAS,mBAAmB,EAAE,OAAA,EAAS,KAAK,CAAA;AACjE,MAAA,IAAI,MAAA,KAAW,OAAO,OAAO,IAAA;AAC7B,MAAA,OAAO,MAAA,CAAO,QAAA;AAAA,IAChB,CAAA;AAAA,IACA,MAAM,GAAA,CAAI,GAAA,EAAK,KAAA,EAAO;AACpB,MAAA,MAAM,IAAA,GAAO,eAAe,KAAK,CAAA;AACjC,MAAA,IAAI,OAAO,wBAAA,EAA0B;AACnC,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,kCAAA,EAAqC,GAAG,CAAA,KAAA,EAAQ,IAAI,kCAAkC,wBAAwB,CAAA;AAAA,SAChH;AAAA,MACF;AAGA,MAAA,MAAM,SAAS,kBAAA,CAAmB,GAAA,EAAK,KAAA,EAAO,YAAA,CAAa,GAAG,CAAC,CAAA;AAAA,IACjE,CAAA;AAAA,IACA,MAAM,OAAO,GAAA,EAAK;AAChB,MAAA,MAAM,QAAA,CAAS,oBAAA,CAAqB,EAAE,OAAA,EAAS,KAAK,CAAA;AAAA,IACtD;AAAA,GACF;AACF","file":"react-native-keychain.mjs","sourcesContent":["import type { Storage } from '../storage/types';\n\n/**\n * Adapter that persists session and key material in the iOS Keychain / Android\n * Keystore via [`react-native-keychain`](https://github.com/oblador/react-native-keychain).\n *\n * `react-native-keychain` is an optional peer dependency; install it in your\n * React Native project with `npm i react-native-keychain` (and follow its\n * iOS pod-install / Android linking instructions).\n *\n * The module is loaded lazily via dynamic `import('react-native-keychain')`\n * so web bundlers strip the dependency from web builds entirely.\n *\n * Storage model: one Keychain `service` per logical key. Each `Storage.set(k, v)`\n * call writes a separate Keychain entry under `service = k`; this keeps the\n * adapter simple but means the number of distinct keys you write should stay\n * bounded (the SDK uses 2–3 keys per `apiKeyHash`).\n */\n\ntype KeychainOptions = {\n service?: string;\n accessible?: string;\n};\n\ntype KeychainCredentials = {\n username: string;\n password: string;\n service: string;\n storage?: string;\n};\n\ntype KeychainApi = {\n setGenericPassword: (\n username: string,\n password: string,\n options?: KeychainOptions,\n ) => Promise<false | { service: string; storage?: string }>;\n getGenericPassword: (options?: KeychainOptions) => Promise<false | KeychainCredentials>;\n resetGenericPassword: (options?: KeychainOptions) => Promise<boolean>;\n ACCESSIBLE?: Record<string, string | undefined>;\n};\n\n/**\n * Hard cap per stored value. iOS Keychain has no formal byte limit but\n * practical limits sit a few KB; we refuse oversized writes loudly.\n */\nexport const KEYCHAIN_MAX_VALUE_BYTES = 4096;\n\nexport interface KeychainAdapterOptions {\n /**\n * Override the iOS Keychain accessibility class. Defaults to\n * `ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY` when available on the loaded\n * module — that prevents iCloud Keychain backup from carrying the SDK's\n * private key material to another device.\n */\n accessible?: string;\n}\n\nasync function loadKeychain(): Promise<KeychainApi> {\n try {\n // @ts-expect-error -- optional peer dep; not present when the SDK is built or\n // when the SDK runs on web. Resolved at runtime in React Native apps.\n const mod = await import('react-native-keychain');\n return mod as unknown as KeychainApi;\n } catch (error) {\n const message =\n `[PollarClient:storage] Failed to load 'react-native-keychain'. ` +\n `Install it in your React Native app: \\`npm i react-native-keychain\\` ` +\n `(plus iOS pod install). Original error: ${error instanceof Error ? error.message : String(error)}`;\n throw new Error(message, { cause: error });\n }\n}\n\nfunction utf8ByteLength(value: string): number {\n if (typeof TextEncoder !== 'undefined') return new TextEncoder().encode(value).length;\n let bytes = 0;\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i);\n if (code < 0x80) bytes += 1;\n else if (code < 0x800) bytes += 2;\n else if (code >= 0xd800 && code <= 0xdbff) {\n bytes += 4;\n i++;\n } else bytes += 3;\n }\n return bytes;\n}\n\n/**\n * Create a `Storage` adapter backed by `react-native-keychain`.\n *\n * Throws (via the returned Promise) at construction time if the package\n * cannot be loaded.\n */\nexport async function createKeychainAdapter(options: KeychainAdapterOptions = {}): Promise<Storage> {\n const Keychain = await loadKeychain();\n\n const accessible: string | undefined =\n options.accessible !== undefined ? options.accessible : Keychain.ACCESSIBLE?.['WHEN_UNLOCKED_THIS_DEVICE_ONLY'];\n\n function buildOptions(key: string): KeychainOptions {\n const opts: KeychainOptions = { service: key };\n if (accessible !== undefined) opts.accessible = accessible;\n return opts;\n }\n\n return {\n async get(key) {\n const result = await Keychain.getGenericPassword({ service: key });\n if (result === false) return null;\n return result.password;\n },\n async set(key, value) {\n const size = utf8ByteLength(value);\n if (size > KEYCHAIN_MAX_VALUE_BYTES) {\n throw new Error(\n `[PollarClient:storage] Value for \"${key}\" is ${size} bytes, exceeds Keychain limit ${KEYCHAIN_MAX_VALUE_BYTES}`,\n );\n }\n // Use the storage key as both the username and the service so a\n // (service, account) lookup is unambiguous on both platforms.\n await Keychain.setGenericPassword(key, value, buildOptions(key));\n },\n async remove(key) {\n await Keychain.resetGenericPassword({ service: key });\n },\n };\n}\n"]}
|