@safercity/sdk-react-native 0.0.1 → 0.1.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/index.cjs CHANGED
@@ -50,9 +50,185 @@ function isStreamingSupported() {
50
50
  return supportsStreaming();
51
51
  }
52
52
 
53
+ // src/secure-storage.ts
54
+ var STORAGE_KEY = "@safercity:tokens";
55
+ function hasSecureStore() {
56
+ try {
57
+ __require("expo-secure-store");
58
+ return true;
59
+ } catch {
60
+ return false;
61
+ }
62
+ }
63
+ function hasAsyncStorage() {
64
+ try {
65
+ __require("@react-native-async-storage/async-storage");
66
+ return true;
67
+ } catch {
68
+ return false;
69
+ }
70
+ }
71
+ var SecureStoreTokenStorage = class {
72
+ tokens = null;
73
+ initialized = false;
74
+ initPromise = null;
75
+ constructor() {
76
+ this.initPromise = this.loadFromStore();
77
+ }
78
+ async loadFromStore() {
79
+ if (this.initialized) return;
80
+ try {
81
+ const SecureStore = __require("expo-secure-store");
82
+ const value = await SecureStore.getItemAsync(STORAGE_KEY);
83
+ if (value) {
84
+ this.tokens = JSON.parse(value);
85
+ }
86
+ } catch (error) {
87
+ console.warn("[SaferCity] Failed to load tokens from SecureStore:", error);
88
+ }
89
+ this.initialized = true;
90
+ }
91
+ get() {
92
+ return this.tokens;
93
+ }
94
+ set(tokens) {
95
+ this.tokens = tokens;
96
+ this.saveToStore(tokens).catch((error) => {
97
+ console.warn("[SaferCity] Failed to save tokens to SecureStore:", error);
98
+ });
99
+ }
100
+ clear() {
101
+ this.tokens = null;
102
+ this.clearFromStore().catch((error) => {
103
+ console.warn("[SaferCity] Failed to clear tokens from SecureStore:", error);
104
+ });
105
+ }
106
+ async saveToStore(tokens) {
107
+ try {
108
+ const SecureStore = __require("expo-secure-store");
109
+ await SecureStore.setItemAsync(STORAGE_KEY, JSON.stringify(tokens));
110
+ } catch (error) {
111
+ console.warn("[SaferCity] Failed to save tokens to SecureStore:", error);
112
+ }
113
+ }
114
+ async clearFromStore() {
115
+ try {
116
+ const SecureStore = __require("expo-secure-store");
117
+ await SecureStore.deleteItemAsync(STORAGE_KEY);
118
+ } catch (error) {
119
+ console.warn("[SaferCity] Failed to clear tokens from SecureStore:", error);
120
+ }
121
+ }
122
+ /**
123
+ * Wait for initial load to complete
124
+ */
125
+ async waitForInit() {
126
+ if (this.initPromise) {
127
+ await this.initPromise;
128
+ }
129
+ }
130
+ };
131
+ var AsyncStorageTokenStorage = class {
132
+ tokens = null;
133
+ initialized = false;
134
+ initPromise = null;
135
+ constructor() {
136
+ this.initPromise = this.loadFromStorage();
137
+ }
138
+ async loadFromStorage() {
139
+ if (this.initialized) return;
140
+ try {
141
+ const AsyncStorage = __require("@react-native-async-storage/async-storage").default;
142
+ const value = await AsyncStorage.getItem(STORAGE_KEY);
143
+ if (value) {
144
+ this.tokens = JSON.parse(value);
145
+ }
146
+ } catch (error) {
147
+ console.warn("[SaferCity] Failed to load tokens from AsyncStorage:", error);
148
+ }
149
+ this.initialized = true;
150
+ }
151
+ get() {
152
+ return this.tokens;
153
+ }
154
+ set(tokens) {
155
+ this.tokens = tokens;
156
+ this.saveToStorage(tokens).catch((error) => {
157
+ console.warn("[SaferCity] Failed to save tokens to AsyncStorage:", error);
158
+ });
159
+ }
160
+ clear() {
161
+ this.tokens = null;
162
+ this.clearFromStorage().catch((error) => {
163
+ console.warn("[SaferCity] Failed to clear tokens from AsyncStorage:", error);
164
+ });
165
+ }
166
+ async saveToStorage(tokens) {
167
+ try {
168
+ const AsyncStorage = __require("@react-native-async-storage/async-storage").default;
169
+ await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(tokens));
170
+ } catch (error) {
171
+ console.warn("[SaferCity] Failed to save tokens to AsyncStorage:", error);
172
+ }
173
+ }
174
+ async clearFromStorage() {
175
+ try {
176
+ const AsyncStorage = __require("@react-native-async-storage/async-storage").default;
177
+ await AsyncStorage.removeItem(STORAGE_KEY);
178
+ } catch (error) {
179
+ console.warn("[SaferCity] Failed to clear tokens from AsyncStorage:", error);
180
+ }
181
+ }
182
+ /**
183
+ * Wait for initial load to complete
184
+ */
185
+ async waitForInit() {
186
+ if (this.initPromise) {
187
+ await this.initPromise;
188
+ }
189
+ }
190
+ };
191
+ var InMemoryTokenStorage = class {
192
+ tokens = null;
193
+ get() {
194
+ return this.tokens;
195
+ }
196
+ set(tokens) {
197
+ this.tokens = tokens;
198
+ }
199
+ clear() {
200
+ this.tokens = null;
201
+ }
202
+ };
203
+ function createSecureTokenStorage() {
204
+ if (hasSecureStore()) {
205
+ return new SecureStoreTokenStorage();
206
+ }
207
+ if (hasAsyncStorage()) {
208
+ console.warn(
209
+ "[SaferCity] expo-secure-store not available. Using AsyncStorage which is less secure. Consider installing expo-secure-store for better security."
210
+ );
211
+ return new AsyncStorageTokenStorage();
212
+ }
213
+ console.warn(
214
+ "[SaferCity] No persistent storage available. Using in-memory storage. Tokens will be lost when the app closes."
215
+ );
216
+ return new InMemoryTokenStorage();
217
+ }
218
+ function getStorageType() {
219
+ if (hasSecureStore()) return "secure-store";
220
+ if (hasAsyncStorage()) return "async-storage";
221
+ return "memory";
222
+ }
223
+
224
+ exports.AsyncStorageTokenStorage = AsyncStorageTokenStorage;
225
+ exports.InMemoryTokenStorage = InMemoryTokenStorage;
226
+ exports.SecureStoreTokenStorage = SecureStoreTokenStorage;
53
227
  exports.createReactNativeClient = createReactNativeClient;
54
228
  exports.createReactNativeStreamAdapter = createReactNativeStreamAdapter;
229
+ exports.createSecureTokenStorage = createSecureTokenStorage;
55
230
  exports.getReactNativeFetch = getReactNativeFetch;
231
+ exports.getStorageType = getStorageType;
56
232
  exports.isStreamingSupported = isStreamingSupported;
57
233
  exports.supportsStreaming = supportsStreaming;
58
234
  Object.keys(sdk).forEach(function (k) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/fetch-adapter.ts","../src/client.ts"],"names":["FetchStreamAdapter","createSaferCityClient"],"mappings":";;;;;;;;;;;AAYA,SAAS,YAAA,GAAoC;AAC3C,EAAA,IAAI;AAGF,IAAA,MAAM,SAAA,GAAY,UAAQ,YAAY,CAAA;AACtC,IAAA,OAAO,WAAW,KAAA,IAAS,IAAA;AAAA,EAC7B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAUO,SAAS,oBAAoB,WAAA,EAA0C;AAC5E,EAAA,MAAM,YAAY,YAAA,EAAa;AAE/B,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,OAAO,SAAA;AAAA,EACT;AAEA,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,OAAO,WAAA;AAAA,EACT;AAIA,EAAA,OAAO,UAAA,CAAW,KAAA;AACpB;AAKO,SAAS,iBAAA,GAA6B;AAC3C,EAAA,MAAM,YAAY,YAAA,EAAa;AAC/B,EAAA,OAAO,SAAA,KAAc,IAAA;AACvB;AAMO,SAAS,+BAA+B,WAAA,EAAgD;AAC7F,EAAA,MAAM,OAAA,GAAU,oBAAoB,WAAW,CAAA;AAC/C,EAAA,OAAO,IAAIA,2BAAmB,OAAO,CAAA;AACvC;;;ACrBO,SAAS,wBAAwB,OAAA,EAAoD;AAC1F,EAAA,MAAM,OAAA,GAAU,mBAAA,CAAoB,OAAA,CAAQ,WAAW,CAAA;AACvD,EAAA,MAAM,aAAA,GAAgB,8BAAA,CAA+B,OAAA,CAAQ,WAAW,CAAA;AAExE,EAAA,OAAOC,yBAAA,CAAsB;AAAA,IAC3B,GAAG,OAAA;AAAA,IACH,KAAA,EAAO,OAAA;AAAA,IACP;AAAA,GACD,CAAA;AACH;AAgBO,SAAS,oBAAA,GAAgC;AAC9C,EAAA,OAAO,iBAAA,EAAkB;AAC3B","file":"index.cjs","sourcesContent":["/**\n * React Native fetch adapter with expo-fetch support for streaming\n * \n * Expo SDK 52+ includes expo-fetch which supports ReadableStream\n * for SSE and streaming responses.\n */\n\nimport { FetchStreamAdapter } from '@safercity/sdk-core';\n\n/**\n * Detect if expo-fetch is available (Expo SDK 52+)\n */\nfunction getExpoFetch(): typeof fetch | null {\n try {\n // Dynamic import to avoid bundling issues\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const expoFetch = require('expo/fetch');\n return expoFetch?.fetch ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the best available fetch implementation for React Native\n * \n * Priority:\n * 1. expo-fetch (if available) - supports streaming\n * 2. Custom fetch provided by user\n * 3. Global fetch (limited streaming support)\n */\nexport function getReactNativeFetch(customFetch?: typeof fetch): typeof fetch {\n const expoFetch = getExpoFetch();\n \n if (expoFetch) {\n return expoFetch;\n }\n \n if (customFetch) {\n return customFetch;\n }\n \n // Fall back to global fetch\n // Note: Streaming may not work properly without expo-fetch\n return globalThis.fetch;\n}\n\n/**\n * Check if the current environment supports streaming\n */\nexport function supportsStreaming(): boolean {\n const expoFetch = getExpoFetch();\n return expoFetch !== null;\n}\n\n/**\n * Create a stream adapter for React Native\n * Uses expo-fetch if available, otherwise falls back to standard fetch\n */\nexport function createReactNativeStreamAdapter(customFetch?: typeof fetch): FetchStreamAdapter {\n const fetchFn = getReactNativeFetch(customFetch);\n return new FetchStreamAdapter(fetchFn);\n}\n","/**\n * React Native SaferCity client\n * \n * Pre-configured client with React Native optimizations:\n * - Uses expo-fetch for streaming support (if available)\n * - Falls back to standard fetch for non-Expo apps\n */\n\nimport { createSaferCityClient, type SaferCityClientOptions, type SaferCityClient } from '@safercity/sdk';\nimport { createReactNativeStreamAdapter, getReactNativeFetch, supportsStreaming } from './fetch-adapter';\n\nexport interface ReactNativeClientOptions extends Omit<SaferCityClientOptions, 'fetch' | 'streamAdapter'> {\n /**\n * Custom fetch implementation (optional)\n * If not provided, will use expo-fetch (if available) or global fetch\n */\n customFetch?: typeof fetch;\n}\n\n/**\n * Create a SaferCity client optimized for React Native\n * \n * @example\n * ```tsx\n * import { createReactNativeClient } from '@safercity/sdk-react-native';\n * \n * const client = createReactNativeClient({\n * baseUrl: 'https://api.safercity.com',\n * token: userToken,\n * tenantId: 'tenant-123',\n * });\n * \n * // Use just like the regular client\n * const { data } = await client.health.check();\n * \n * // Streaming works with expo-fetch\n * for await (const event of client.panics.streamUpdates('panic-123')) {\n * console.log('Update:', event.data);\n * }\n * ```\n */\nexport function createReactNativeClient(options: ReactNativeClientOptions): SaferCityClient {\n const fetchFn = getReactNativeFetch(options.customFetch);\n const streamAdapter = createReactNativeStreamAdapter(options.customFetch);\n \n return createSaferCityClient({\n ...options,\n fetch: fetchFn,\n streamAdapter,\n });\n}\n\n/**\n * Check if streaming is supported in the current React Native environment\n * \n * @example\n * ```tsx\n * import { isStreamingSupported } from '@safercity/sdk-react-native';\n * \n * if (isStreamingSupported()) {\n * // Use streaming features\n * } else {\n * // Fall back to polling\n * }\n * ```\n */\nexport function isStreamingSupported(): boolean {\n return supportsStreaming();\n}\n"]}
1
+ {"version":3,"sources":["../src/fetch-adapter.ts","../src/client.ts","../src/secure-storage.ts"],"names":["FetchStreamAdapter","createSaferCityClient"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * React Native fetch adapter with expo-fetch support for streaming\n * \n * Expo SDK 52+ includes expo-fetch which supports ReadableStream\n * for SSE and streaming responses.\n */\n\nimport { FetchStreamAdapter } from '@safercity/sdk-core';\n\n/**\n * Detect if expo-fetch is available (Expo SDK 52+)\n */\nfunction getExpoFetch(): typeof fetch | null {\n try {\n // Dynamic import to avoid bundling issues\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const expoFetch = require('expo/fetch');\n return expoFetch?.fetch ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the best available fetch implementation for React Native\n * \n * Priority:\n * 1. expo-fetch (if available) - supports streaming\n * 2. Custom fetch provided by user\n * 3. Global fetch (limited streaming support)\n */\nexport function getReactNativeFetch(customFetch?: typeof fetch): typeof fetch {\n const expoFetch = getExpoFetch();\n \n if (expoFetch) {\n return expoFetch;\n }\n \n if (customFetch) {\n return customFetch;\n }\n \n // Fall back to global fetch\n // Note: Streaming may not work properly without expo-fetch\n return globalThis.fetch;\n}\n\n/**\n * Check if the current environment supports streaming\n */\nexport function supportsStreaming(): boolean {\n const expoFetch = getExpoFetch();\n return expoFetch !== null;\n}\n\n/**\n * Create a stream adapter for React Native\n * Uses expo-fetch if available, otherwise falls back to standard fetch\n */\nexport function createReactNativeStreamAdapter(customFetch?: typeof fetch): FetchStreamAdapter {\n const fetchFn = getReactNativeFetch(customFetch);\n return new FetchStreamAdapter(fetchFn);\n}\n","/**\n * React Native SaferCity client\n * \n * Pre-configured client with React Native optimizations:\n * - Uses expo-fetch for streaming support (if available)\n * - Falls back to standard fetch for non-Expo apps\n */\n\nimport { createSaferCityClient, type SaferCityClientOptions, type SaferCityClient } from '@safercity/sdk';\nimport { createReactNativeStreamAdapter, getReactNativeFetch, supportsStreaming } from './fetch-adapter';\n\nexport interface ReactNativeClientOptions extends Omit<SaferCityClientOptions, 'fetch' | 'streamAdapter'> {\n /**\n * Custom fetch implementation (optional)\n * If not provided, will use expo-fetch (if available) or global fetch\n */\n customFetch?: typeof fetch;\n}\n\n/**\n * Create a SaferCity client optimized for React Native\n * \n * @example\n * ```tsx\n * import { createReactNativeClient } from '@safercity/sdk-react-native';\n * \n * const client = createReactNativeClient({\n * baseUrl: 'https://api.safercity.com',\n * token: userToken,\n * tenantId: 'tenant-123',\n * });\n * \n * // Use just like the regular client\n * const { data } = await client.health.check();\n * \n * // Streaming works with expo-fetch\n * for await (const event of client.panics.streamUpdates('panic-123')) {\n * console.log('Update:', event.data);\n * }\n * ```\n */\nexport function createReactNativeClient(options: ReactNativeClientOptions): SaferCityClient {\n const fetchFn = getReactNativeFetch(options.customFetch);\n const streamAdapter = createReactNativeStreamAdapter(options.customFetch);\n \n return createSaferCityClient({\n ...options,\n fetch: fetchFn,\n streamAdapter,\n });\n}\n\n/**\n * Check if streaming is supported in the current React Native environment\n * \n * @example\n * ```tsx\n * import { isStreamingSupported } from '@safercity/sdk-react-native';\n * \n * if (isStreamingSupported()) {\n * // Use streaming features\n * } else {\n * // Fall back to polling\n * }\n * ```\n */\nexport function isStreamingSupported(): boolean {\n return supportsStreaming();\n}\n","/**\n * Secure Token Storage for React Native\n * \n * Provides secure storage for OAuth tokens using:\n * - expo-secure-store (preferred, if available)\n * - AsyncStorage fallback (less secure, but widely available)\n */\n\nimport type { TokenStorage, AuthTokens } from '@safercity/sdk-core';\n\n// Storage key prefix\nconst STORAGE_KEY = '@safercity:tokens';\n\n/**\n * Check if expo-secure-store is available\n */\nfunction hasSecureStore(): boolean {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require('expo-secure-store');\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if @react-native-async-storage/async-storage is available\n */\nfunction hasAsyncStorage(): boolean {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require('@react-native-async-storage/async-storage');\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Secure token storage using expo-secure-store\n * \n * This is the most secure option for React Native apps.\n * Data is encrypted using the device's secure enclave (iOS) or keystore (Android).\n */\nexport class SecureStoreTokenStorage implements TokenStorage {\n private tokens: AuthTokens | null = null;\n private initialized = false;\n private initPromise: Promise<void> | null = null;\n\n constructor() {\n // Start loading tokens immediately\n this.initPromise = this.loadFromStore();\n }\n\n private async loadFromStore(): Promise<void> {\n if (this.initialized) return;\n \n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const SecureStore = require('expo-secure-store');\n const value = await SecureStore.getItemAsync(STORAGE_KEY);\n \n if (value) {\n this.tokens = JSON.parse(value);\n }\n } catch (error) {\n console.warn('[SaferCity] Failed to load tokens from SecureStore:', error);\n }\n \n this.initialized = true;\n }\n\n get(): AuthTokens | null {\n // Return cached value synchronously\n return this.tokens;\n }\n\n set(tokens: AuthTokens): void {\n this.tokens = tokens;\n \n // Persist asynchronously\n this.saveToStore(tokens).catch((error) => {\n console.warn('[SaferCity] Failed to save tokens to SecureStore:', error);\n });\n }\n\n clear(): void {\n this.tokens = null;\n \n // Clear from storage asynchronously\n this.clearFromStore().catch((error) => {\n console.warn('[SaferCity] Failed to clear tokens from SecureStore:', error);\n });\n }\n\n private async saveToStore(tokens: AuthTokens): Promise<void> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const SecureStore = require('expo-secure-store');\n await SecureStore.setItemAsync(STORAGE_KEY, JSON.stringify(tokens));\n } catch (error) {\n console.warn('[SaferCity] Failed to save tokens to SecureStore:', error);\n }\n }\n\n private async clearFromStore(): Promise<void> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const SecureStore = require('expo-secure-store');\n await SecureStore.deleteItemAsync(STORAGE_KEY);\n } catch (error) {\n console.warn('[SaferCity] Failed to clear tokens from SecureStore:', error);\n }\n }\n\n /**\n * Wait for initial load to complete\n */\n async waitForInit(): Promise<void> {\n if (this.initPromise) {\n await this.initPromise;\n }\n }\n}\n\n/**\n * Token storage using AsyncStorage (fallback)\n * \n * Less secure than SecureStore but works without Expo.\n * Data is stored unencrypted on the device.\n */\nexport class AsyncStorageTokenStorage implements TokenStorage {\n private tokens: AuthTokens | null = null;\n private initialized = false;\n private initPromise: Promise<void> | null = null;\n\n constructor() {\n // Start loading tokens immediately\n this.initPromise = this.loadFromStorage();\n }\n\n private async loadFromStorage(): Promise<void> {\n if (this.initialized) return;\n \n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const AsyncStorage = require('@react-native-async-storage/async-storage').default;\n const value = await AsyncStorage.getItem(STORAGE_KEY);\n \n if (value) {\n this.tokens = JSON.parse(value);\n }\n } catch (error) {\n console.warn('[SaferCity] Failed to load tokens from AsyncStorage:', error);\n }\n \n this.initialized = true;\n }\n\n get(): AuthTokens | null {\n return this.tokens;\n }\n\n set(tokens: AuthTokens): void {\n this.tokens = tokens;\n \n this.saveToStorage(tokens).catch((error) => {\n console.warn('[SaferCity] Failed to save tokens to AsyncStorage:', error);\n });\n }\n\n clear(): void {\n this.tokens = null;\n \n this.clearFromStorage().catch((error) => {\n console.warn('[SaferCity] Failed to clear tokens from AsyncStorage:', error);\n });\n }\n\n private async saveToStorage(tokens: AuthTokens): Promise<void> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const AsyncStorage = require('@react-native-async-storage/async-storage').default;\n await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(tokens));\n } catch (error) {\n console.warn('[SaferCity] Failed to save tokens to AsyncStorage:', error);\n }\n }\n\n private async clearFromStorage(): Promise<void> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const AsyncStorage = require('@react-native-async-storage/async-storage').default;\n await AsyncStorage.removeItem(STORAGE_KEY);\n } catch (error) {\n console.warn('[SaferCity] Failed to clear tokens from AsyncStorage:', error);\n }\n }\n\n /**\n * Wait for initial load to complete\n */\n async waitForInit(): Promise<void> {\n if (this.initPromise) {\n await this.initPromise;\n }\n }\n}\n\n/**\n * In-memory token storage (no persistence)\n * \n * Use this when you don't want to persist tokens.\n * Tokens will be lost when the app is closed.\n */\nexport class InMemoryTokenStorage implements TokenStorage {\n private tokens: AuthTokens | null = null;\n\n get(): AuthTokens | null {\n return this.tokens;\n }\n\n set(tokens: AuthTokens): void {\n this.tokens = tokens;\n }\n\n clear(): void {\n this.tokens = null;\n }\n}\n\n/**\n * Create the best available token storage for the current environment\n * \n * Priority:\n * 1. expo-secure-store (most secure)\n * 2. @react-native-async-storage/async-storage (less secure)\n * 3. In-memory (no persistence)\n * \n * @example\n * ```tsx\n * import { createSecureTokenStorage } from '@safercity/sdk-react-native';\n * import { TokenManager } from '@safercity/sdk';\n * \n * const tokenManager = new TokenManager({\n * credentials: {\n * clientId: 'my-client',\n * clientSecret: 'my-secret',\n * },\n * storage: createSecureTokenStorage(),\n * });\n * ```\n */\nexport function createSecureTokenStorage(): TokenStorage {\n if (hasSecureStore()) {\n return new SecureStoreTokenStorage();\n }\n \n if (hasAsyncStorage()) {\n console.warn(\n '[SaferCity] expo-secure-store not available. ' +\n 'Using AsyncStorage which is less secure. ' +\n 'Consider installing expo-secure-store for better security.'\n );\n return new AsyncStorageTokenStorage();\n }\n \n console.warn(\n '[SaferCity] No persistent storage available. ' +\n 'Using in-memory storage. Tokens will be lost when the app closes.'\n );\n return new InMemoryTokenStorage();\n}\n\n/**\n * Check what type of storage is available\n */\nexport function getStorageType(): 'secure-store' | 'async-storage' | 'memory' {\n if (hasSecureStore()) return 'secure-store';\n if (hasAsyncStorage()) return 'async-storage';\n return 'memory';\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { SaferCityClientOptions, SaferCityClient } from '@safercity/sdk';
2
2
  export * from '@safercity/sdk';
3
- import { FetchStreamAdapter } from '@safercity/sdk-core';
3
+ import { FetchStreamAdapter, TokenStorage, AuthTokens } from '@safercity/sdk-core';
4
4
 
5
5
  /**
6
6
  * React Native SaferCity client
@@ -82,4 +82,96 @@ declare function supportsStreaming(): boolean;
82
82
  */
83
83
  declare function createReactNativeStreamAdapter(customFetch?: typeof fetch): FetchStreamAdapter;
84
84
 
85
- export { type ReactNativeClientOptions, createReactNativeClient, createReactNativeStreamAdapter, getReactNativeFetch, isStreamingSupported, supportsStreaming };
85
+ /**
86
+ * Secure Token Storage for React Native
87
+ *
88
+ * Provides secure storage for OAuth tokens using:
89
+ * - expo-secure-store (preferred, if available)
90
+ * - AsyncStorage fallback (less secure, but widely available)
91
+ */
92
+
93
+ /**
94
+ * Secure token storage using expo-secure-store
95
+ *
96
+ * This is the most secure option for React Native apps.
97
+ * Data is encrypted using the device's secure enclave (iOS) or keystore (Android).
98
+ */
99
+ declare class SecureStoreTokenStorage implements TokenStorage {
100
+ private tokens;
101
+ private initialized;
102
+ private initPromise;
103
+ constructor();
104
+ private loadFromStore;
105
+ get(): AuthTokens | null;
106
+ set(tokens: AuthTokens): void;
107
+ clear(): void;
108
+ private saveToStore;
109
+ private clearFromStore;
110
+ /**
111
+ * Wait for initial load to complete
112
+ */
113
+ waitForInit(): Promise<void>;
114
+ }
115
+ /**
116
+ * Token storage using AsyncStorage (fallback)
117
+ *
118
+ * Less secure than SecureStore but works without Expo.
119
+ * Data is stored unencrypted on the device.
120
+ */
121
+ declare class AsyncStorageTokenStorage implements TokenStorage {
122
+ private tokens;
123
+ private initialized;
124
+ private initPromise;
125
+ constructor();
126
+ private loadFromStorage;
127
+ get(): AuthTokens | null;
128
+ set(tokens: AuthTokens): void;
129
+ clear(): void;
130
+ private saveToStorage;
131
+ private clearFromStorage;
132
+ /**
133
+ * Wait for initial load to complete
134
+ */
135
+ waitForInit(): Promise<void>;
136
+ }
137
+ /**
138
+ * In-memory token storage (no persistence)
139
+ *
140
+ * Use this when you don't want to persist tokens.
141
+ * Tokens will be lost when the app is closed.
142
+ */
143
+ declare class InMemoryTokenStorage implements TokenStorage {
144
+ private tokens;
145
+ get(): AuthTokens | null;
146
+ set(tokens: AuthTokens): void;
147
+ clear(): void;
148
+ }
149
+ /**
150
+ * Create the best available token storage for the current environment
151
+ *
152
+ * Priority:
153
+ * 1. expo-secure-store (most secure)
154
+ * 2. @react-native-async-storage/async-storage (less secure)
155
+ * 3. In-memory (no persistence)
156
+ *
157
+ * @example
158
+ * ```tsx
159
+ * import { createSecureTokenStorage } from '@safercity/sdk-react-native';
160
+ * import { TokenManager } from '@safercity/sdk';
161
+ *
162
+ * const tokenManager = new TokenManager({
163
+ * credentials: {
164
+ * clientId: 'my-client',
165
+ * clientSecret: 'my-secret',
166
+ * },
167
+ * storage: createSecureTokenStorage(),
168
+ * });
169
+ * ```
170
+ */
171
+ declare function createSecureTokenStorage(): TokenStorage;
172
+ /**
173
+ * Check what type of storage is available
174
+ */
175
+ declare function getStorageType(): 'secure-store' | 'async-storage' | 'memory';
176
+
177
+ export { AsyncStorageTokenStorage, InMemoryTokenStorage, type ReactNativeClientOptions, SecureStoreTokenStorage, createReactNativeClient, createReactNativeStreamAdapter, createSecureTokenStorage, getReactNativeFetch, getStorageType, isStreamingSupported, supportsStreaming };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { SaferCityClientOptions, SaferCityClient } from '@safercity/sdk';
2
2
  export * from '@safercity/sdk';
3
- import { FetchStreamAdapter } from '@safercity/sdk-core';
3
+ import { FetchStreamAdapter, TokenStorage, AuthTokens } from '@safercity/sdk-core';
4
4
 
5
5
  /**
6
6
  * React Native SaferCity client
@@ -82,4 +82,96 @@ declare function supportsStreaming(): boolean;
82
82
  */
83
83
  declare function createReactNativeStreamAdapter(customFetch?: typeof fetch): FetchStreamAdapter;
84
84
 
85
- export { type ReactNativeClientOptions, createReactNativeClient, createReactNativeStreamAdapter, getReactNativeFetch, isStreamingSupported, supportsStreaming };
85
+ /**
86
+ * Secure Token Storage for React Native
87
+ *
88
+ * Provides secure storage for OAuth tokens using:
89
+ * - expo-secure-store (preferred, if available)
90
+ * - AsyncStorage fallback (less secure, but widely available)
91
+ */
92
+
93
+ /**
94
+ * Secure token storage using expo-secure-store
95
+ *
96
+ * This is the most secure option for React Native apps.
97
+ * Data is encrypted using the device's secure enclave (iOS) or keystore (Android).
98
+ */
99
+ declare class SecureStoreTokenStorage implements TokenStorage {
100
+ private tokens;
101
+ private initialized;
102
+ private initPromise;
103
+ constructor();
104
+ private loadFromStore;
105
+ get(): AuthTokens | null;
106
+ set(tokens: AuthTokens): void;
107
+ clear(): void;
108
+ private saveToStore;
109
+ private clearFromStore;
110
+ /**
111
+ * Wait for initial load to complete
112
+ */
113
+ waitForInit(): Promise<void>;
114
+ }
115
+ /**
116
+ * Token storage using AsyncStorage (fallback)
117
+ *
118
+ * Less secure than SecureStore but works without Expo.
119
+ * Data is stored unencrypted on the device.
120
+ */
121
+ declare class AsyncStorageTokenStorage implements TokenStorage {
122
+ private tokens;
123
+ private initialized;
124
+ private initPromise;
125
+ constructor();
126
+ private loadFromStorage;
127
+ get(): AuthTokens | null;
128
+ set(tokens: AuthTokens): void;
129
+ clear(): void;
130
+ private saveToStorage;
131
+ private clearFromStorage;
132
+ /**
133
+ * Wait for initial load to complete
134
+ */
135
+ waitForInit(): Promise<void>;
136
+ }
137
+ /**
138
+ * In-memory token storage (no persistence)
139
+ *
140
+ * Use this when you don't want to persist tokens.
141
+ * Tokens will be lost when the app is closed.
142
+ */
143
+ declare class InMemoryTokenStorage implements TokenStorage {
144
+ private tokens;
145
+ get(): AuthTokens | null;
146
+ set(tokens: AuthTokens): void;
147
+ clear(): void;
148
+ }
149
+ /**
150
+ * Create the best available token storage for the current environment
151
+ *
152
+ * Priority:
153
+ * 1. expo-secure-store (most secure)
154
+ * 2. @react-native-async-storage/async-storage (less secure)
155
+ * 3. In-memory (no persistence)
156
+ *
157
+ * @example
158
+ * ```tsx
159
+ * import { createSecureTokenStorage } from '@safercity/sdk-react-native';
160
+ * import { TokenManager } from '@safercity/sdk';
161
+ *
162
+ * const tokenManager = new TokenManager({
163
+ * credentials: {
164
+ * clientId: 'my-client',
165
+ * clientSecret: 'my-secret',
166
+ * },
167
+ * storage: createSecureTokenStorage(),
168
+ * });
169
+ * ```
170
+ */
171
+ declare function createSecureTokenStorage(): TokenStorage;
172
+ /**
173
+ * Check what type of storage is available
174
+ */
175
+ declare function getStorageType(): 'secure-store' | 'async-storage' | 'memory';
176
+
177
+ export { AsyncStorageTokenStorage, InMemoryTokenStorage, type ReactNativeClientOptions, SecureStoreTokenStorage, createReactNativeClient, createReactNativeStreamAdapter, createSecureTokenStorage, getReactNativeFetch, getStorageType, isStreamingSupported, supportsStreaming };
package/dist/index.js CHANGED
@@ -49,6 +49,177 @@ function isStreamingSupported() {
49
49
  return supportsStreaming();
50
50
  }
51
51
 
52
- export { createReactNativeClient, createReactNativeStreamAdapter, getReactNativeFetch, isStreamingSupported, supportsStreaming };
52
+ // src/secure-storage.ts
53
+ var STORAGE_KEY = "@safercity:tokens";
54
+ function hasSecureStore() {
55
+ try {
56
+ __require("expo-secure-store");
57
+ return true;
58
+ } catch {
59
+ return false;
60
+ }
61
+ }
62
+ function hasAsyncStorage() {
63
+ try {
64
+ __require("@react-native-async-storage/async-storage");
65
+ return true;
66
+ } catch {
67
+ return false;
68
+ }
69
+ }
70
+ var SecureStoreTokenStorage = class {
71
+ tokens = null;
72
+ initialized = false;
73
+ initPromise = null;
74
+ constructor() {
75
+ this.initPromise = this.loadFromStore();
76
+ }
77
+ async loadFromStore() {
78
+ if (this.initialized) return;
79
+ try {
80
+ const SecureStore = __require("expo-secure-store");
81
+ const value = await SecureStore.getItemAsync(STORAGE_KEY);
82
+ if (value) {
83
+ this.tokens = JSON.parse(value);
84
+ }
85
+ } catch (error) {
86
+ console.warn("[SaferCity] Failed to load tokens from SecureStore:", error);
87
+ }
88
+ this.initialized = true;
89
+ }
90
+ get() {
91
+ return this.tokens;
92
+ }
93
+ set(tokens) {
94
+ this.tokens = tokens;
95
+ this.saveToStore(tokens).catch((error) => {
96
+ console.warn("[SaferCity] Failed to save tokens to SecureStore:", error);
97
+ });
98
+ }
99
+ clear() {
100
+ this.tokens = null;
101
+ this.clearFromStore().catch((error) => {
102
+ console.warn("[SaferCity] Failed to clear tokens from SecureStore:", error);
103
+ });
104
+ }
105
+ async saveToStore(tokens) {
106
+ try {
107
+ const SecureStore = __require("expo-secure-store");
108
+ await SecureStore.setItemAsync(STORAGE_KEY, JSON.stringify(tokens));
109
+ } catch (error) {
110
+ console.warn("[SaferCity] Failed to save tokens to SecureStore:", error);
111
+ }
112
+ }
113
+ async clearFromStore() {
114
+ try {
115
+ const SecureStore = __require("expo-secure-store");
116
+ await SecureStore.deleteItemAsync(STORAGE_KEY);
117
+ } catch (error) {
118
+ console.warn("[SaferCity] Failed to clear tokens from SecureStore:", error);
119
+ }
120
+ }
121
+ /**
122
+ * Wait for initial load to complete
123
+ */
124
+ async waitForInit() {
125
+ if (this.initPromise) {
126
+ await this.initPromise;
127
+ }
128
+ }
129
+ };
130
+ var AsyncStorageTokenStorage = class {
131
+ tokens = null;
132
+ initialized = false;
133
+ initPromise = null;
134
+ constructor() {
135
+ this.initPromise = this.loadFromStorage();
136
+ }
137
+ async loadFromStorage() {
138
+ if (this.initialized) return;
139
+ try {
140
+ const AsyncStorage = __require("@react-native-async-storage/async-storage").default;
141
+ const value = await AsyncStorage.getItem(STORAGE_KEY);
142
+ if (value) {
143
+ this.tokens = JSON.parse(value);
144
+ }
145
+ } catch (error) {
146
+ console.warn("[SaferCity] Failed to load tokens from AsyncStorage:", error);
147
+ }
148
+ this.initialized = true;
149
+ }
150
+ get() {
151
+ return this.tokens;
152
+ }
153
+ set(tokens) {
154
+ this.tokens = tokens;
155
+ this.saveToStorage(tokens).catch((error) => {
156
+ console.warn("[SaferCity] Failed to save tokens to AsyncStorage:", error);
157
+ });
158
+ }
159
+ clear() {
160
+ this.tokens = null;
161
+ this.clearFromStorage().catch((error) => {
162
+ console.warn("[SaferCity] Failed to clear tokens from AsyncStorage:", error);
163
+ });
164
+ }
165
+ async saveToStorage(tokens) {
166
+ try {
167
+ const AsyncStorage = __require("@react-native-async-storage/async-storage").default;
168
+ await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(tokens));
169
+ } catch (error) {
170
+ console.warn("[SaferCity] Failed to save tokens to AsyncStorage:", error);
171
+ }
172
+ }
173
+ async clearFromStorage() {
174
+ try {
175
+ const AsyncStorage = __require("@react-native-async-storage/async-storage").default;
176
+ await AsyncStorage.removeItem(STORAGE_KEY);
177
+ } catch (error) {
178
+ console.warn("[SaferCity] Failed to clear tokens from AsyncStorage:", error);
179
+ }
180
+ }
181
+ /**
182
+ * Wait for initial load to complete
183
+ */
184
+ async waitForInit() {
185
+ if (this.initPromise) {
186
+ await this.initPromise;
187
+ }
188
+ }
189
+ };
190
+ var InMemoryTokenStorage = class {
191
+ tokens = null;
192
+ get() {
193
+ return this.tokens;
194
+ }
195
+ set(tokens) {
196
+ this.tokens = tokens;
197
+ }
198
+ clear() {
199
+ this.tokens = null;
200
+ }
201
+ };
202
+ function createSecureTokenStorage() {
203
+ if (hasSecureStore()) {
204
+ return new SecureStoreTokenStorage();
205
+ }
206
+ if (hasAsyncStorage()) {
207
+ console.warn(
208
+ "[SaferCity] expo-secure-store not available. Using AsyncStorage which is less secure. Consider installing expo-secure-store for better security."
209
+ );
210
+ return new AsyncStorageTokenStorage();
211
+ }
212
+ console.warn(
213
+ "[SaferCity] No persistent storage available. Using in-memory storage. Tokens will be lost when the app closes."
214
+ );
215
+ return new InMemoryTokenStorage();
216
+ }
217
+ function getStorageType() {
218
+ if (hasSecureStore()) return "secure-store";
219
+ if (hasAsyncStorage()) return "async-storage";
220
+ return "memory";
221
+ }
222
+
223
+ export { AsyncStorageTokenStorage, InMemoryTokenStorage, SecureStoreTokenStorage, createReactNativeClient, createReactNativeStreamAdapter, createSecureTokenStorage, getReactNativeFetch, getStorageType, isStreamingSupported, supportsStreaming };
53
224
  //# sourceMappingURL=index.js.map
54
225
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/fetch-adapter.ts","../src/client.ts"],"names":[],"mappings":";;;;;;;;;;AAYA,SAAS,YAAA,GAAoC;AAC3C,EAAA,IAAI;AAGF,IAAA,MAAM,SAAA,GAAY,UAAQ,YAAY,CAAA;AACtC,IAAA,OAAO,WAAW,KAAA,IAAS,IAAA;AAAA,EAC7B,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAUO,SAAS,oBAAoB,WAAA,EAA0C;AAC5E,EAAA,MAAM,YAAY,YAAA,EAAa;AAE/B,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,OAAO,SAAA;AAAA,EACT;AAEA,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,OAAO,WAAA;AAAA,EACT;AAIA,EAAA,OAAO,UAAA,CAAW,KAAA;AACpB;AAKO,SAAS,iBAAA,GAA6B;AAC3C,EAAA,MAAM,YAAY,YAAA,EAAa;AAC/B,EAAA,OAAO,SAAA,KAAc,IAAA;AACvB;AAMO,SAAS,+BAA+B,WAAA,EAAgD;AAC7F,EAAA,MAAM,OAAA,GAAU,oBAAoB,WAAW,CAAA;AAC/C,EAAA,OAAO,IAAI,mBAAmB,OAAO,CAAA;AACvC;;;ACrBO,SAAS,wBAAwB,OAAA,EAAoD;AAC1F,EAAA,MAAM,OAAA,GAAU,mBAAA,CAAoB,OAAA,CAAQ,WAAW,CAAA;AACvD,EAAA,MAAM,aAAA,GAAgB,8BAAA,CAA+B,OAAA,CAAQ,WAAW,CAAA;AAExE,EAAA,OAAO,qBAAA,CAAsB;AAAA,IAC3B,GAAG,OAAA;AAAA,IACH,KAAA,EAAO,OAAA;AAAA,IACP;AAAA,GACD,CAAA;AACH;AAgBO,SAAS,oBAAA,GAAgC;AAC9C,EAAA,OAAO,iBAAA,EAAkB;AAC3B","file":"index.js","sourcesContent":["/**\n * React Native fetch adapter with expo-fetch support for streaming\n * \n * Expo SDK 52+ includes expo-fetch which supports ReadableStream\n * for SSE and streaming responses.\n */\n\nimport { FetchStreamAdapter } from '@safercity/sdk-core';\n\n/**\n * Detect if expo-fetch is available (Expo SDK 52+)\n */\nfunction getExpoFetch(): typeof fetch | null {\n try {\n // Dynamic import to avoid bundling issues\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const expoFetch = require('expo/fetch');\n return expoFetch?.fetch ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the best available fetch implementation for React Native\n * \n * Priority:\n * 1. expo-fetch (if available) - supports streaming\n * 2. Custom fetch provided by user\n * 3. Global fetch (limited streaming support)\n */\nexport function getReactNativeFetch(customFetch?: typeof fetch): typeof fetch {\n const expoFetch = getExpoFetch();\n \n if (expoFetch) {\n return expoFetch;\n }\n \n if (customFetch) {\n return customFetch;\n }\n \n // Fall back to global fetch\n // Note: Streaming may not work properly without expo-fetch\n return globalThis.fetch;\n}\n\n/**\n * Check if the current environment supports streaming\n */\nexport function supportsStreaming(): boolean {\n const expoFetch = getExpoFetch();\n return expoFetch !== null;\n}\n\n/**\n * Create a stream adapter for React Native\n * Uses expo-fetch if available, otherwise falls back to standard fetch\n */\nexport function createReactNativeStreamAdapter(customFetch?: typeof fetch): FetchStreamAdapter {\n const fetchFn = getReactNativeFetch(customFetch);\n return new FetchStreamAdapter(fetchFn);\n}\n","/**\n * React Native SaferCity client\n * \n * Pre-configured client with React Native optimizations:\n * - Uses expo-fetch for streaming support (if available)\n * - Falls back to standard fetch for non-Expo apps\n */\n\nimport { createSaferCityClient, type SaferCityClientOptions, type SaferCityClient } from '@safercity/sdk';\nimport { createReactNativeStreamAdapter, getReactNativeFetch, supportsStreaming } from './fetch-adapter';\n\nexport interface ReactNativeClientOptions extends Omit<SaferCityClientOptions, 'fetch' | 'streamAdapter'> {\n /**\n * Custom fetch implementation (optional)\n * If not provided, will use expo-fetch (if available) or global fetch\n */\n customFetch?: typeof fetch;\n}\n\n/**\n * Create a SaferCity client optimized for React Native\n * \n * @example\n * ```tsx\n * import { createReactNativeClient } from '@safercity/sdk-react-native';\n * \n * const client = createReactNativeClient({\n * baseUrl: 'https://api.safercity.com',\n * token: userToken,\n * tenantId: 'tenant-123',\n * });\n * \n * // Use just like the regular client\n * const { data } = await client.health.check();\n * \n * // Streaming works with expo-fetch\n * for await (const event of client.panics.streamUpdates('panic-123')) {\n * console.log('Update:', event.data);\n * }\n * ```\n */\nexport function createReactNativeClient(options: ReactNativeClientOptions): SaferCityClient {\n const fetchFn = getReactNativeFetch(options.customFetch);\n const streamAdapter = createReactNativeStreamAdapter(options.customFetch);\n \n return createSaferCityClient({\n ...options,\n fetch: fetchFn,\n streamAdapter,\n });\n}\n\n/**\n * Check if streaming is supported in the current React Native environment\n * \n * @example\n * ```tsx\n * import { isStreamingSupported } from '@safercity/sdk-react-native';\n * \n * if (isStreamingSupported()) {\n * // Use streaming features\n * } else {\n * // Fall back to polling\n * }\n * ```\n */\nexport function isStreamingSupported(): boolean {\n return supportsStreaming();\n}\n"]}
1
+ {"version":3,"sources":["../src/fetch-adapter.ts","../src/client.ts","../src/secure-storage.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * React Native fetch adapter with expo-fetch support for streaming\n * \n * Expo SDK 52+ includes expo-fetch which supports ReadableStream\n * for SSE and streaming responses.\n */\n\nimport { FetchStreamAdapter } from '@safercity/sdk-core';\n\n/**\n * Detect if expo-fetch is available (Expo SDK 52+)\n */\nfunction getExpoFetch(): typeof fetch | null {\n try {\n // Dynamic import to avoid bundling issues\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const expoFetch = require('expo/fetch');\n return expoFetch?.fetch ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the best available fetch implementation for React Native\n * \n * Priority:\n * 1. expo-fetch (if available) - supports streaming\n * 2. Custom fetch provided by user\n * 3. Global fetch (limited streaming support)\n */\nexport function getReactNativeFetch(customFetch?: typeof fetch): typeof fetch {\n const expoFetch = getExpoFetch();\n \n if (expoFetch) {\n return expoFetch;\n }\n \n if (customFetch) {\n return customFetch;\n }\n \n // Fall back to global fetch\n // Note: Streaming may not work properly without expo-fetch\n return globalThis.fetch;\n}\n\n/**\n * Check if the current environment supports streaming\n */\nexport function supportsStreaming(): boolean {\n const expoFetch = getExpoFetch();\n return expoFetch !== null;\n}\n\n/**\n * Create a stream adapter for React Native\n * Uses expo-fetch if available, otherwise falls back to standard fetch\n */\nexport function createReactNativeStreamAdapter(customFetch?: typeof fetch): FetchStreamAdapter {\n const fetchFn = getReactNativeFetch(customFetch);\n return new FetchStreamAdapter(fetchFn);\n}\n","/**\n * React Native SaferCity client\n * \n * Pre-configured client with React Native optimizations:\n * - Uses expo-fetch for streaming support (if available)\n * - Falls back to standard fetch for non-Expo apps\n */\n\nimport { createSaferCityClient, type SaferCityClientOptions, type SaferCityClient } from '@safercity/sdk';\nimport { createReactNativeStreamAdapter, getReactNativeFetch, supportsStreaming } from './fetch-adapter';\n\nexport interface ReactNativeClientOptions extends Omit<SaferCityClientOptions, 'fetch' | 'streamAdapter'> {\n /**\n * Custom fetch implementation (optional)\n * If not provided, will use expo-fetch (if available) or global fetch\n */\n customFetch?: typeof fetch;\n}\n\n/**\n * Create a SaferCity client optimized for React Native\n * \n * @example\n * ```tsx\n * import { createReactNativeClient } from '@safercity/sdk-react-native';\n * \n * const client = createReactNativeClient({\n * baseUrl: 'https://api.safercity.com',\n * token: userToken,\n * tenantId: 'tenant-123',\n * });\n * \n * // Use just like the regular client\n * const { data } = await client.health.check();\n * \n * // Streaming works with expo-fetch\n * for await (const event of client.panics.streamUpdates('panic-123')) {\n * console.log('Update:', event.data);\n * }\n * ```\n */\nexport function createReactNativeClient(options: ReactNativeClientOptions): SaferCityClient {\n const fetchFn = getReactNativeFetch(options.customFetch);\n const streamAdapter = createReactNativeStreamAdapter(options.customFetch);\n \n return createSaferCityClient({\n ...options,\n fetch: fetchFn,\n streamAdapter,\n });\n}\n\n/**\n * Check if streaming is supported in the current React Native environment\n * \n * @example\n * ```tsx\n * import { isStreamingSupported } from '@safercity/sdk-react-native';\n * \n * if (isStreamingSupported()) {\n * // Use streaming features\n * } else {\n * // Fall back to polling\n * }\n * ```\n */\nexport function isStreamingSupported(): boolean {\n return supportsStreaming();\n}\n","/**\n * Secure Token Storage for React Native\n * \n * Provides secure storage for OAuth tokens using:\n * - expo-secure-store (preferred, if available)\n * - AsyncStorage fallback (less secure, but widely available)\n */\n\nimport type { TokenStorage, AuthTokens } from '@safercity/sdk-core';\n\n// Storage key prefix\nconst STORAGE_KEY = '@safercity:tokens';\n\n/**\n * Check if expo-secure-store is available\n */\nfunction hasSecureStore(): boolean {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require('expo-secure-store');\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Check if @react-native-async-storage/async-storage is available\n */\nfunction hasAsyncStorage(): boolean {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require('@react-native-async-storage/async-storage');\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Secure token storage using expo-secure-store\n * \n * This is the most secure option for React Native apps.\n * Data is encrypted using the device's secure enclave (iOS) or keystore (Android).\n */\nexport class SecureStoreTokenStorage implements TokenStorage {\n private tokens: AuthTokens | null = null;\n private initialized = false;\n private initPromise: Promise<void> | null = null;\n\n constructor() {\n // Start loading tokens immediately\n this.initPromise = this.loadFromStore();\n }\n\n private async loadFromStore(): Promise<void> {\n if (this.initialized) return;\n \n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const SecureStore = require('expo-secure-store');\n const value = await SecureStore.getItemAsync(STORAGE_KEY);\n \n if (value) {\n this.tokens = JSON.parse(value);\n }\n } catch (error) {\n console.warn('[SaferCity] Failed to load tokens from SecureStore:', error);\n }\n \n this.initialized = true;\n }\n\n get(): AuthTokens | null {\n // Return cached value synchronously\n return this.tokens;\n }\n\n set(tokens: AuthTokens): void {\n this.tokens = tokens;\n \n // Persist asynchronously\n this.saveToStore(tokens).catch((error) => {\n console.warn('[SaferCity] Failed to save tokens to SecureStore:', error);\n });\n }\n\n clear(): void {\n this.tokens = null;\n \n // Clear from storage asynchronously\n this.clearFromStore().catch((error) => {\n console.warn('[SaferCity] Failed to clear tokens from SecureStore:', error);\n });\n }\n\n private async saveToStore(tokens: AuthTokens): Promise<void> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const SecureStore = require('expo-secure-store');\n await SecureStore.setItemAsync(STORAGE_KEY, JSON.stringify(tokens));\n } catch (error) {\n console.warn('[SaferCity] Failed to save tokens to SecureStore:', error);\n }\n }\n\n private async clearFromStore(): Promise<void> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const SecureStore = require('expo-secure-store');\n await SecureStore.deleteItemAsync(STORAGE_KEY);\n } catch (error) {\n console.warn('[SaferCity] Failed to clear tokens from SecureStore:', error);\n }\n }\n\n /**\n * Wait for initial load to complete\n */\n async waitForInit(): Promise<void> {\n if (this.initPromise) {\n await this.initPromise;\n }\n }\n}\n\n/**\n * Token storage using AsyncStorage (fallback)\n * \n * Less secure than SecureStore but works without Expo.\n * Data is stored unencrypted on the device.\n */\nexport class AsyncStorageTokenStorage implements TokenStorage {\n private tokens: AuthTokens | null = null;\n private initialized = false;\n private initPromise: Promise<void> | null = null;\n\n constructor() {\n // Start loading tokens immediately\n this.initPromise = this.loadFromStorage();\n }\n\n private async loadFromStorage(): Promise<void> {\n if (this.initialized) return;\n \n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const AsyncStorage = require('@react-native-async-storage/async-storage').default;\n const value = await AsyncStorage.getItem(STORAGE_KEY);\n \n if (value) {\n this.tokens = JSON.parse(value);\n }\n } catch (error) {\n console.warn('[SaferCity] Failed to load tokens from AsyncStorage:', error);\n }\n \n this.initialized = true;\n }\n\n get(): AuthTokens | null {\n return this.tokens;\n }\n\n set(tokens: AuthTokens): void {\n this.tokens = tokens;\n \n this.saveToStorage(tokens).catch((error) => {\n console.warn('[SaferCity] Failed to save tokens to AsyncStorage:', error);\n });\n }\n\n clear(): void {\n this.tokens = null;\n \n this.clearFromStorage().catch((error) => {\n console.warn('[SaferCity] Failed to clear tokens from AsyncStorage:', error);\n });\n }\n\n private async saveToStorage(tokens: AuthTokens): Promise<void> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const AsyncStorage = require('@react-native-async-storage/async-storage').default;\n await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(tokens));\n } catch (error) {\n console.warn('[SaferCity] Failed to save tokens to AsyncStorage:', error);\n }\n }\n\n private async clearFromStorage(): Promise<void> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const AsyncStorage = require('@react-native-async-storage/async-storage').default;\n await AsyncStorage.removeItem(STORAGE_KEY);\n } catch (error) {\n console.warn('[SaferCity] Failed to clear tokens from AsyncStorage:', error);\n }\n }\n\n /**\n * Wait for initial load to complete\n */\n async waitForInit(): Promise<void> {\n if (this.initPromise) {\n await this.initPromise;\n }\n }\n}\n\n/**\n * In-memory token storage (no persistence)\n * \n * Use this when you don't want to persist tokens.\n * Tokens will be lost when the app is closed.\n */\nexport class InMemoryTokenStorage implements TokenStorage {\n private tokens: AuthTokens | null = null;\n\n get(): AuthTokens | null {\n return this.tokens;\n }\n\n set(tokens: AuthTokens): void {\n this.tokens = tokens;\n }\n\n clear(): void {\n this.tokens = null;\n }\n}\n\n/**\n * Create the best available token storage for the current environment\n * \n * Priority:\n * 1. expo-secure-store (most secure)\n * 2. @react-native-async-storage/async-storage (less secure)\n * 3. In-memory (no persistence)\n * \n * @example\n * ```tsx\n * import { createSecureTokenStorage } from '@safercity/sdk-react-native';\n * import { TokenManager } from '@safercity/sdk';\n * \n * const tokenManager = new TokenManager({\n * credentials: {\n * clientId: 'my-client',\n * clientSecret: 'my-secret',\n * },\n * storage: createSecureTokenStorage(),\n * });\n * ```\n */\nexport function createSecureTokenStorage(): TokenStorage {\n if (hasSecureStore()) {\n return new SecureStoreTokenStorage();\n }\n \n if (hasAsyncStorage()) {\n console.warn(\n '[SaferCity] expo-secure-store not available. ' +\n 'Using AsyncStorage which is less secure. ' +\n 'Consider installing expo-secure-store for better security.'\n );\n return new AsyncStorageTokenStorage();\n }\n \n console.warn(\n '[SaferCity] No persistent storage available. ' +\n 'Using in-memory storage. Tokens will be lost when the app closes.'\n );\n return new InMemoryTokenStorage();\n}\n\n/**\n * Check what type of storage is available\n */\nexport function getStorageType(): 'secure-store' | 'async-storage' | 'memory' {\n if (hasSecureStore()) return 'secure-store';\n if (hasAsyncStorage()) return 'async-storage';\n return 'memory';\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@safercity/sdk-react-native",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "React Native adapter for SaferCity SDK with expo-fetch streaming support",
5
5
  "license": "MIT",
6
6
  "author": {