@pollar/core 0.6.0 → 0.7.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/README.md +276 -42
- package/dist/adapters/expo-secure-store.d.mts +26 -0
- package/dist/adapters/expo-secure-store.d.ts +26 -0
- package/dist/adapters/expo-secure-store.js +53 -0
- package/dist/adapters/expo-secure-store.js.map +1 -0
- package/dist/adapters/expo-secure-store.mjs +50 -0
- package/dist/adapters/expo-secure-store.mjs.map +1 -0
- package/dist/adapters/react-native-keychain.d.mts +25 -0
- package/dist/adapters/react-native-keychain.d.ts +25 -0
- package/dist/adapters/react-native-keychain.js +60 -0
- package/dist/adapters/react-native-keychain.js.map +1 -0
- package/dist/adapters/react-native-keychain.mjs +57 -0
- package/dist/adapters/react-native-keychain.mjs.map +1 -0
- package/dist/index.d.mts +743 -36
- package/dist/index.d.ts +743 -36
- package/dist/index.js +819 -124
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +811 -125
- package/dist/index.mjs.map +1 -1
- package/dist/index.rn.d.mts +30 -0
- package/dist/index.rn.d.ts +30 -0
- package/dist/index.rn.js +2627 -0
- package/dist/index.rn.js.map +1 -0
- package/dist/index.rn.mjs +2599 -0
- package/dist/index.rn.mjs.map +1 -0
- package/dist/types-DqgJIJBl.d.mts +19 -0
- package/dist/types-DqgJIJBl.d.ts +19 -0
- package/package.json +54 -2
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/adapters/react-native-keychain.ts
|
|
4
|
+
var KEYCHAIN_MAX_VALUE_BYTES = 4096;
|
|
5
|
+
async function loadKeychain() {
|
|
6
|
+
try {
|
|
7
|
+
const mod = await import('react-native-keychain');
|
|
8
|
+
return mod;
|
|
9
|
+
} catch (error) {
|
|
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);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function utf8ByteLength(value) {
|
|
15
|
+
if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(value).length;
|
|
16
|
+
let bytes = 0;
|
|
17
|
+
for (let i = 0; i < value.length; i++) {
|
|
18
|
+
const code = value.charCodeAt(i);
|
|
19
|
+
if (code < 128) bytes += 1;
|
|
20
|
+
else if (code < 2048) bytes += 2;
|
|
21
|
+
else if (code >= 55296 && code <= 56319) {
|
|
22
|
+
bytes += 4;
|
|
23
|
+
i++;
|
|
24
|
+
} else bytes += 3;
|
|
25
|
+
}
|
|
26
|
+
return bytes;
|
|
27
|
+
}
|
|
28
|
+
async function createKeychainAdapter(options = {}) {
|
|
29
|
+
const Keychain = await loadKeychain();
|
|
30
|
+
const accessible = options.accessible !== void 0 ? options.accessible : Keychain.ACCESSIBLE?.["WHEN_UNLOCKED_THIS_DEVICE_ONLY"];
|
|
31
|
+
function buildOptions(key) {
|
|
32
|
+
const opts = { service: key };
|
|
33
|
+
if (accessible !== void 0) opts.accessible = accessible;
|
|
34
|
+
return opts;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
async get(key) {
|
|
38
|
+
const result = await Keychain.getGenericPassword({ service: key });
|
|
39
|
+
if (result === false) return null;
|
|
40
|
+
return result.password;
|
|
41
|
+
},
|
|
42
|
+
async set(key, value) {
|
|
43
|
+
const size = utf8ByteLength(value);
|
|
44
|
+
if (size > KEYCHAIN_MAX_VALUE_BYTES) {
|
|
45
|
+
throw new Error(
|
|
46
|
+
`[PollarClient:storage] Value for "${key}" is ${size} bytes, exceeds Keychain limit ${KEYCHAIN_MAX_VALUE_BYTES}`
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
await Keychain.setGenericPassword(key, value, buildOptions(key));
|
|
50
|
+
},
|
|
51
|
+
async remove(key) {
|
|
52
|
+
await Keychain.resetGenericPassword({ service: key });
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
exports.KEYCHAIN_MAX_VALUE_BYTES = KEYCHAIN_MAX_VALUE_BYTES;
|
|
58
|
+
exports.createKeychainAdapter = createKeychainAdapter;
|
|
59
|
+
//# sourceMappingURL=react-native-keychain.js.map
|
|
60
|
+
//# sourceMappingURL=react-native-keychain.js.map
|
|
@@ -0,0 +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,MAAM,OAAO,CAAA;AAAA,EACzB;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-ignore -- 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);\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"]}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/adapters/react-native-keychain.ts
|
|
2
|
+
var KEYCHAIN_MAX_VALUE_BYTES = 4096;
|
|
3
|
+
async function loadKeychain() {
|
|
4
|
+
try {
|
|
5
|
+
const mod = await import('react-native-keychain');
|
|
6
|
+
return mod;
|
|
7
|
+
} catch (error) {
|
|
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);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function utf8ByteLength(value) {
|
|
13
|
+
if (typeof TextEncoder !== "undefined") return new TextEncoder().encode(value).length;
|
|
14
|
+
let bytes = 0;
|
|
15
|
+
for (let i = 0; i < value.length; i++) {
|
|
16
|
+
const code = value.charCodeAt(i);
|
|
17
|
+
if (code < 128) bytes += 1;
|
|
18
|
+
else if (code < 2048) bytes += 2;
|
|
19
|
+
else if (code >= 55296 && code <= 56319) {
|
|
20
|
+
bytes += 4;
|
|
21
|
+
i++;
|
|
22
|
+
} else bytes += 3;
|
|
23
|
+
}
|
|
24
|
+
return bytes;
|
|
25
|
+
}
|
|
26
|
+
async function createKeychainAdapter(options = {}) {
|
|
27
|
+
const Keychain = await loadKeychain();
|
|
28
|
+
const accessible = options.accessible !== void 0 ? options.accessible : Keychain.ACCESSIBLE?.["WHEN_UNLOCKED_THIS_DEVICE_ONLY"];
|
|
29
|
+
function buildOptions(key) {
|
|
30
|
+
const opts = { service: key };
|
|
31
|
+
if (accessible !== void 0) opts.accessible = accessible;
|
|
32
|
+
return opts;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
async get(key) {
|
|
36
|
+
const result = await Keychain.getGenericPassword({ service: key });
|
|
37
|
+
if (result === false) return null;
|
|
38
|
+
return result.password;
|
|
39
|
+
},
|
|
40
|
+
async set(key, value) {
|
|
41
|
+
const size = utf8ByteLength(value);
|
|
42
|
+
if (size > KEYCHAIN_MAX_VALUE_BYTES) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`[PollarClient:storage] Value for "${key}" is ${size} bytes, exceeds Keychain limit ${KEYCHAIN_MAX_VALUE_BYTES}`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
await Keychain.setGenericPassword(key, value, buildOptions(key));
|
|
48
|
+
},
|
|
49
|
+
async remove(key) {
|
|
50
|
+
await Keychain.resetGenericPassword({ service: key });
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export { KEYCHAIN_MAX_VALUE_BYTES, createKeychainAdapter };
|
|
56
|
+
//# sourceMappingURL=react-native-keychain.mjs.map
|
|
57
|
+
//# sourceMappingURL=react-native-keychain.mjs.map
|
|
@@ -0,0 +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,MAAM,OAAO,CAAA;AAAA,EACzB;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-ignore -- 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);\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"]}
|