@openfort/react-native 0.1.17 → 0.1.18

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.
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const SDK_INFO = {
5
5
  name: '@openfort/react-native',
6
- version: '0.1.15',
6
+ version: '0.1.18',
7
7
  description: 'React Native client for Openfort',
8
8
  homepage: 'https://openfort.io/docs',
9
9
  };
@@ -49,7 +49,7 @@ export function createOpenfortClient({ baseConfiguration, overrides, shieldConfi
49
49
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
50
  digest: digest,
51
51
  },
52
- storage: createNormalizedStorage(SecureStorageAdapter),
52
+ storage: createNormalizedStorage(baseConfiguration.publishableKey, SecureStorageAdapter),
53
53
  },
54
54
  shieldConfiguration,
55
55
  });
@@ -2,6 +2,25 @@
2
2
  import * as SecureStore from 'expo-secure-store';
3
3
  import { logger } from '../lib/logger';
4
4
  import { NativeStorageUtils } from '../native';
5
+ /**
6
+ * Tracks pending write operations per key.
7
+ * This ensures `get` waits for any in-flight `save` to complete,
8
+ * providing read-after-write consistency while keeping `save` synchronous.
9
+ */
10
+ const pendingWrites = new Map();
11
+ /**
12
+ * Creates a scope prefix from the publishable key.
13
+ * Extracts the unique project identifier after the "pk_test_" or "pk_live_" prefix.
14
+ * Uses the first 8 characters of that unique part to keep keys readable.
15
+ *
16
+ * e.g., "pk_test_abc123xyz789" -> "abc123xy"
17
+ */
18
+ function createScope(publishableKey) {
19
+ // Remove the "pk_test_" or "pk_live_" prefix (8 characters)
20
+ const uniquePart = publishableKey.substring(8);
21
+ // Use first 8 characters of the unique part as scope
22
+ return uniquePart.substring(0, 8);
23
+ }
5
24
  // Define the StorageKeys enum values that match the Openfort SDK
6
25
  var StorageKeys;
7
26
  (function (StorageKeys) {
@@ -24,6 +43,11 @@ export const SecureStorageAdapter = {
24
43
  async get(key) {
25
44
  try {
26
45
  const normalizedKey = normalizeKey(key);
46
+ // Wait for any pending write to complete before reading
47
+ const pendingWrite = pendingWrites.get(normalizedKey);
48
+ if (pendingWrite) {
49
+ await pendingWrite;
50
+ }
27
51
  const result = await SecureStore.getItemAsync(normalizedKey, NativeStorageUtils.getStorageOptions());
28
52
  // If result is a string (as expected), return it
29
53
  if (typeof result === 'string' || result === null) {
@@ -45,14 +69,21 @@ export const SecureStorageAdapter = {
45
69
  }
46
70
  },
47
71
  async save(key, value) {
48
- try {
49
- const normalizedKey = normalizeKey(key);
50
- await SecureStore.setItemAsync(normalizedKey, value, NativeStorageUtils.getStorageOptions());
51
- }
52
- catch (error) {
53
- logger.warn('Failed to set item in secure store', error);
54
- throw error;
55
- }
72
+ const normalizedKey = normalizeKey(key);
73
+ const writePromise = (async () => {
74
+ try {
75
+ await SecureStore.setItemAsync(normalizedKey, value, NativeStorageUtils.getStorageOptions());
76
+ }
77
+ catch (error) {
78
+ logger.warn('Failed to set item in secure store', error);
79
+ throw error;
80
+ }
81
+ finally {
82
+ pendingWrites.delete(normalizedKey);
83
+ }
84
+ })();
85
+ pendingWrites.set(normalizedKey, writePromise);
86
+ return writePromise;
56
87
  },
57
88
  async remove(key) {
58
89
  try {
@@ -78,7 +109,7 @@ export const SecureStorageAdapter = {
78
109
  },
79
110
  };
80
111
  /**
81
- * Normalises an Openfort storage key for use with Expo Secure Store.
112
+ * Normalizes an Openfort storage key for use with Expo Secure Store.
82
113
  *
83
114
  * @param key - The key provided by the Openfort SDK.
84
115
  * @returns A key that is safe to use with Expo Secure Store.
@@ -88,38 +119,57 @@ function normalizeKey(key) {
88
119
  }
89
120
  /**
90
121
  * Creates a type-safe storage adapter that bridges the Openfort SDK storage API with the React Native implementation.
122
+ * Storage keys are scoped by publishable key to isolate data between different projects.
91
123
  *
124
+ * @param publishableKey - The publishable key used to scope storage keys.
92
125
  * @param customStorage - Optional custom storage implementation. When omitted the {@link SecureStorageAdapter} is used.
93
126
  * @returns An object that satisfies the {@link Storage} interface expected by `@openfort/openfort-js`.
94
127
  */
95
- export function createNormalizedStorage(customStorage) {
128
+ export function createNormalizedStorage(publishableKey, customStorage) {
96
129
  const baseStorage = customStorage || SecureStorageAdapter;
130
+ const scope = createScope(publishableKey);
131
+ /**
132
+ * Prefixes a storage key with the scope.
133
+ * e.g., "openfort.authentication" -> "abc123xy.openfort.authentication"
134
+ */
135
+ function scopeKey(key) {
136
+ return `${scope}.${key}`;
137
+ }
97
138
  return {
98
139
  async get(key) {
99
140
  // Convert the unknown key to our StorageKeys enum
100
141
  const storageKey = keyToStorageKeys(key);
101
- const result = await baseStorage.get(storageKey);
142
+ const scopedKey = scopeKey(storageKey);
143
+ const result = await baseStorage.get(scopedKey);
102
144
  return result;
103
145
  },
104
146
  save(key, value) {
105
147
  logger.info(`Saving to storage key: ${key}, value: ${value}`);
106
148
  const storageKey = keyToStorageKeys(key);
149
+ const scopedKey = scopeKey(storageKey);
107
150
  // Fire and forget - don't await as the SDK expects synchronous behavior
108
- baseStorage.save(storageKey, value).catch((error) => {
151
+ baseStorage.save(scopedKey, value).catch((error) => {
109
152
  logger.error('Failed to save to storage', error);
110
153
  });
111
154
  },
112
155
  remove(key) {
113
156
  logger.info(`Removing from storage key: ${key}`);
114
157
  const storageKey = keyToStorageKeys(key);
158
+ const scopedKey = scopeKey(storageKey);
115
159
  // Fire and forget - don't await as the SDK expects synchronous behavior
116
- baseStorage.remove(storageKey).catch((error) => {
160
+ baseStorage.remove(scopedKey).catch((error) => {
117
161
  logger.error('Failed to remove from storage', error);
118
162
  });
119
163
  },
120
164
  flush() {
121
165
  logger.info('Flushing storage');
122
- baseStorage.flush();
166
+ // Remove all scoped keys for this project
167
+ for (const key of Object.values(StorageKeys)) {
168
+ const scopedKey = scopeKey(key);
169
+ baseStorage.remove(scopedKey).catch((error) => {
170
+ logger.error('Failed to remove from storage during flush', error);
171
+ });
172
+ }
123
173
  },
124
174
  };
125
175
  }
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export declare const SDK_INFO: {
5
5
  readonly name: "@openfort/react-native";
6
- readonly version: "0.1.15";
6
+ readonly version: "0.1.18";
7
7
  readonly description: "React Native client for Openfort";
8
8
  readonly homepage: "https://openfort.io/docs";
9
9
  };
@@ -1,21 +1,10 @@
1
1
  import type { Storage } from '@openfort/openfort-js';
2
- declare enum StorageKeys {
3
- AUTHENTICATION = "openfort.authentication",
4
- SIGNER = "openfort.signer",
5
- CONFIGURATION = "openfort.configuration",
6
- ACCOUNT = "openfort.account",
7
- TEST = "openfort.test",
8
- RECOVERY = "openfort.recovery",
9
- SESSION = "openfort.session",
10
- PKCE_STATE = "openfort.pkce_state",
11
- PKCE_VERIFIER = "openfort.pkce_verifier"
12
- }
13
2
  interface OpenfortStorage {
14
- get(key: StorageKeys): Promise<string | null>;
15
- save(key: StorageKeys, value: string): Promise<void>;
16
- remove(key: StorageKeys): Promise<void>;
3
+ get(key: string): Promise<string | null>;
4
+ save(key: string, value: string): Promise<void>;
5
+ remove(key: string): Promise<void>;
17
6
  flush(): void;
18
- keyExists(key: StorageKeys): Promise<boolean>;
7
+ keyExists(key: string): Promise<boolean>;
19
8
  getStorageInfo(): Promise<{
20
9
  isAvailable: boolean;
21
10
  platform: string;
@@ -30,9 +19,11 @@ interface OpenfortStorage {
30
19
  export declare const SecureStorageAdapter: OpenfortStorage;
31
20
  /**
32
21
  * Creates a type-safe storage adapter that bridges the Openfort SDK storage API with the React Native implementation.
22
+ * Storage keys are scoped by publishable key to isolate data between different projects.
33
23
  *
24
+ * @param publishableKey - The publishable key used to scope storage keys.
34
25
  * @param customStorage - Optional custom storage implementation. When omitted the {@link SecureStorageAdapter} is used.
35
26
  * @returns An object that satisfies the {@link Storage} interface expected by `@openfort/openfort-js`.
36
27
  */
37
- export declare function createNormalizedStorage(customStorage?: OpenfortStorage): Storage;
28
+ export declare function createNormalizedStorage(publishableKey: string, customStorage?: OpenfortStorage): Storage;
38
29
  export {};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openfort/react-native",
3
3
  "main": "dist/index.js",
4
- "version": "0.1.17",
4
+ "version": "0.1.18",
5
5
  "license": "MIT",
6
6
  "description": "React Native SDK for Openfort platform integration",
7
7
  "types": "dist/types/index.d.ts",
@@ -16,7 +16,7 @@
16
16
  }
17
17
  },
18
18
  "dependencies": {
19
- "@openfort/openfort-js": "^0.10.38"
19
+ "@openfort/openfort-js": "^0.10.39"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "expo-apple-authentication": "*",