@vailix/mask 0.2.2 → 0.2.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # @vailix/mask
2
2
 
3
+ ## 0.2.3
4
+
5
+ ### Patch Changes
6
+
7
+ - c1ca263: fix: prevent race condition in SDK initialization
8
+ - Implement singleton pattern for `VailixSDK.create()` to prevent concurrent database connections
9
+ - Ensure database is properly closed before deletion on key mismatch errors
10
+ - Add missing internal BLE types (`InternalNearbyUser`, `PendingPairRequest`, `BleServiceConfig`)
11
+ - Add `VailixSDK.destroy()` for cleanup and `VailixSDK.isInitialized()` for status checking
12
+
3
13
  ## 0.2.2
4
14
 
5
15
  ### Patch Changes
package/README.md CHANGED
@@ -62,6 +62,16 @@ const unsubscribe = sdk.onNearbyUsersChanged((users) => {
62
62
  });
63
63
  ```
64
64
 
65
+ > **Note:** `VailixSDK.create()` returns a singleton instance. Multiple calls return the same instance, and the config is only used on the first call. Use `VailixSDK.destroy()` to reset the SDK if needed.
66
+
67
+ ### Static Methods
68
+
69
+ | Method | Description |
70
+ |--------|-------------|
71
+ | `VailixSDK.create(config)` | Create or return the singleton SDK instance |
72
+ | `VailixSDK.destroy()` | Release resources and reset the singleton |
73
+ | `VailixSDK.isInitialized()` | Check if the SDK has been initialized |
74
+
65
75
  ## Compatibility
66
76
 
67
77
  - **Expo**: SDK 52+ (Development Build required, **Expo Go not supported**)
package/dist/index.d.mts CHANGED
@@ -227,6 +227,8 @@ interface ParsedQR {
227
227
  }
228
228
 
229
229
  declare class VailixSDK {
230
+ private static instance;
231
+ private static initPromise;
230
232
  identity: IdentityManager;
231
233
  storage: StorageService;
232
234
  matcher: MatcherService;
@@ -237,11 +239,27 @@ declare class VailixSDK {
237
239
  private rpiDurationMs;
238
240
  private constructor();
239
241
  /**
240
- * Create and initialize the VailixSDK.
242
+ * Create or return the singleton SDK instance.
241
243
  *
242
- * @param config - Unified configuration object
244
+ * Thread-safe: concurrent calls will wait for the first initialization to complete
245
+ * and return the same instance. Config is only used on first initialization.
246
+ *
247
+ * @param config - Unified configuration object (used only on first call)
243
248
  */
244
249
  static create(config: VailixConfig): Promise<VailixSDK>;
250
+ /**
251
+ * Internal initialization logic (extracted for singleton pattern).
252
+ */
253
+ private static doCreate;
254
+ /**
255
+ * Destroy the singleton instance and release all resources.
256
+ * Use for testing or when the app needs to fully reset the SDK.
257
+ */
258
+ static destroy(): Promise<void>;
259
+ /**
260
+ * Check if the SDK has been initialized.
261
+ */
262
+ static isInitialized(): boolean;
245
263
  /** Get current QR code data */
246
264
  getQRCode(): string;
247
265
  /**
package/dist/index.d.ts CHANGED
@@ -227,6 +227,8 @@ interface ParsedQR {
227
227
  }
228
228
 
229
229
  declare class VailixSDK {
230
+ private static instance;
231
+ private static initPromise;
230
232
  identity: IdentityManager;
231
233
  storage: StorageService;
232
234
  matcher: MatcherService;
@@ -237,11 +239,27 @@ declare class VailixSDK {
237
239
  private rpiDurationMs;
238
240
  private constructor();
239
241
  /**
240
- * Create and initialize the VailixSDK.
242
+ * Create or return the singleton SDK instance.
241
243
  *
242
- * @param config - Unified configuration object
244
+ * Thread-safe: concurrent calls will wait for the first initialization to complete
245
+ * and return the same instance. Config is only used on first initialization.
246
+ *
247
+ * @param config - Unified configuration object (used only on first call)
243
248
  */
244
249
  static create(config: VailixConfig): Promise<VailixSDK>;
250
+ /**
251
+ * Internal initialization logic (extracted for singleton pattern).
252
+ */
253
+ private static doCreate;
254
+ /**
255
+ * Destroy the singleton instance and release all resources.
256
+ * Use for testing or when the app needs to fully reset the SDK.
257
+ */
258
+ static destroy(): Promise<void>;
259
+ /**
260
+ * Check if the SDK has been initialized.
261
+ */
262
+ static isInitialized(): boolean;
245
263
  /** Get current QR code data */
246
264
  getQRCode(): string;
247
265
  /**
package/dist/index.js CHANGED
@@ -826,6 +826,9 @@ async function tryOpenEncryptedDatabase(masterKey) {
826
826
 
827
827
  // src/index.ts
828
828
  var VailixSDK = class _VailixSDK {
829
+ // Singleton instance and initialization promise for thread-safety
830
+ static instance = null;
831
+ static initPromise = null;
829
832
  identity;
830
833
  storage;
831
834
  matcher;
@@ -845,11 +848,33 @@ var VailixSDK = class _VailixSDK {
845
848
  this.rpiDurationMs = rpiDurationMs;
846
849
  }
847
850
  /**
848
- * Create and initialize the VailixSDK.
851
+ * Create or return the singleton SDK instance.
849
852
  *
850
- * @param config - Unified configuration object
853
+ * Thread-safe: concurrent calls will wait for the first initialization to complete
854
+ * and return the same instance. Config is only used on first initialization.
855
+ *
856
+ * @param config - Unified configuration object (used only on first call)
851
857
  */
852
858
  static async create(config) {
859
+ if (_VailixSDK.instance) {
860
+ return _VailixSDK.instance;
861
+ }
862
+ if (_VailixSDK.initPromise) {
863
+ return _VailixSDK.initPromise;
864
+ }
865
+ _VailixSDK.initPromise = _VailixSDK.doCreate(config);
866
+ try {
867
+ _VailixSDK.instance = await _VailixSDK.initPromise;
868
+ return _VailixSDK.instance;
869
+ } catch (error) {
870
+ _VailixSDK.initPromise = null;
871
+ throw error;
872
+ }
873
+ }
874
+ /**
875
+ * Internal initialization logic (extracted for singleton pattern).
876
+ */
877
+ static async doCreate(config) {
853
878
  const rpiDuration = config.rpiDurationMs ?? 15 * 60 * 1e3;
854
879
  if (config.rescanIntervalMs && config.rescanIntervalMs > rpiDuration) {
855
880
  throw new Error(`rescanIntervalMs (${config.rescanIntervalMs}) cannot exceed rpiDurationMs (${rpiDuration})`);
@@ -885,6 +910,23 @@ var VailixSDK = class _VailixSDK {
885
910
  rpiDuration
886
911
  );
887
912
  }
913
+ /**
914
+ * Destroy the singleton instance and release all resources.
915
+ * Use for testing or when the app needs to fully reset the SDK.
916
+ */
917
+ static async destroy() {
918
+ if (_VailixSDK.instance) {
919
+ _VailixSDK.instance.ble.destroy();
920
+ _VailixSDK.instance = null;
921
+ }
922
+ _VailixSDK.initPromise = null;
923
+ }
924
+ /**
925
+ * Check if the SDK has been initialized.
926
+ */
927
+ static isInitialized() {
928
+ return _VailixSDK.instance !== null;
929
+ }
888
930
  // ========================================================================
889
931
  // QR Code Methods
890
932
  // ========================================================================
package/dist/index.mjs CHANGED
@@ -790,6 +790,9 @@ async function tryOpenEncryptedDatabase(masterKey) {
790
790
 
791
791
  // src/index.ts
792
792
  var VailixSDK = class _VailixSDK {
793
+ // Singleton instance and initialization promise for thread-safety
794
+ static instance = null;
795
+ static initPromise = null;
793
796
  identity;
794
797
  storage;
795
798
  matcher;
@@ -809,11 +812,33 @@ var VailixSDK = class _VailixSDK {
809
812
  this.rpiDurationMs = rpiDurationMs;
810
813
  }
811
814
  /**
812
- * Create and initialize the VailixSDK.
815
+ * Create or return the singleton SDK instance.
813
816
  *
814
- * @param config - Unified configuration object
817
+ * Thread-safe: concurrent calls will wait for the first initialization to complete
818
+ * and return the same instance. Config is only used on first initialization.
819
+ *
820
+ * @param config - Unified configuration object (used only on first call)
815
821
  */
816
822
  static async create(config) {
823
+ if (_VailixSDK.instance) {
824
+ return _VailixSDK.instance;
825
+ }
826
+ if (_VailixSDK.initPromise) {
827
+ return _VailixSDK.initPromise;
828
+ }
829
+ _VailixSDK.initPromise = _VailixSDK.doCreate(config);
830
+ try {
831
+ _VailixSDK.instance = await _VailixSDK.initPromise;
832
+ return _VailixSDK.instance;
833
+ } catch (error) {
834
+ _VailixSDK.initPromise = null;
835
+ throw error;
836
+ }
837
+ }
838
+ /**
839
+ * Internal initialization logic (extracted for singleton pattern).
840
+ */
841
+ static async doCreate(config) {
817
842
  const rpiDuration = config.rpiDurationMs ?? 15 * 60 * 1e3;
818
843
  if (config.rescanIntervalMs && config.rescanIntervalMs > rpiDuration) {
819
844
  throw new Error(`rescanIntervalMs (${config.rescanIntervalMs}) cannot exceed rpiDurationMs (${rpiDuration})`);
@@ -849,6 +874,23 @@ var VailixSDK = class _VailixSDK {
849
874
  rpiDuration
850
875
  );
851
876
  }
877
+ /**
878
+ * Destroy the singleton instance and release all resources.
879
+ * Use for testing or when the app needs to fully reset the SDK.
880
+ */
881
+ static async destroy() {
882
+ if (_VailixSDK.instance) {
883
+ _VailixSDK.instance.ble.destroy();
884
+ _VailixSDK.instance = null;
885
+ }
886
+ _VailixSDK.initPromise = null;
887
+ }
888
+ /**
889
+ * Check if the SDK has been initialized.
890
+ */
891
+ static isInitialized() {
892
+ return _VailixSDK.instance !== null;
893
+ }
852
894
  // ========================================================================
853
895
  // QR Code Methods
854
896
  // ========================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vailix/mask",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Privacy-preserving proximity tracing SDK for React Native & Expo",
5
5
  "author": "Gil Eyni",
6
6
  "keywords": [
package/src/index.ts CHANGED
@@ -16,6 +16,10 @@ import type {
16
16
  } from './types';
17
17
 
18
18
  export class VailixSDK {
19
+ // Singleton instance and initialization promise for thread-safety
20
+ private static instance: VailixSDK | null = null;
21
+ private static initPromise: Promise<VailixSDK> | null = null;
22
+
19
23
  public identity: IdentityManager;
20
24
  public storage: StorageService;
21
25
  public matcher: MatcherService;
@@ -46,11 +50,41 @@ export class VailixSDK {
46
50
  }
47
51
 
48
52
  /**
49
- * Create and initialize the VailixSDK.
53
+ * Create or return the singleton SDK instance.
54
+ *
55
+ * Thread-safe: concurrent calls will wait for the first initialization to complete
56
+ * and return the same instance. Config is only used on first initialization.
50
57
  *
51
- * @param config - Unified configuration object
58
+ * @param config - Unified configuration object (used only on first call)
52
59
  */
53
60
  static async create(config: VailixConfig): Promise<VailixSDK> {
61
+ // Already initialized - return existing instance
62
+ if (VailixSDK.instance) {
63
+ return VailixSDK.instance;
64
+ }
65
+
66
+ // Initialization in progress - wait for it (prevents race condition)
67
+ if (VailixSDK.initPromise) {
68
+ return VailixSDK.initPromise;
69
+ }
70
+
71
+ // First call - start initialization
72
+ VailixSDK.initPromise = VailixSDK.doCreate(config);
73
+
74
+ try {
75
+ VailixSDK.instance = await VailixSDK.initPromise;
76
+ return VailixSDK.instance;
77
+ } catch (error) {
78
+ // Clear promise on failure so retry is possible
79
+ VailixSDK.initPromise = null;
80
+ throw error;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Internal initialization logic (extracted for singleton pattern).
86
+ */
87
+ private static async doCreate(config: VailixConfig): Promise<VailixSDK> {
54
88
  // Validate: rescanInterval cannot exceed rpiDuration
55
89
  const rpiDuration = config.rpiDurationMs ?? 15 * 60 * 1000; // Default 15 min
56
90
  if (config.rescanIntervalMs && config.rescanIntervalMs > rpiDuration) {
@@ -99,6 +133,27 @@ export class VailixSDK {
99
133
  );
100
134
  }
101
135
 
136
+ /**
137
+ * Destroy the singleton instance and release all resources.
138
+ * Use for testing or when the app needs to fully reset the SDK.
139
+ */
140
+ static async destroy(): Promise<void> {
141
+ if (VailixSDK.instance) {
142
+ // Cleanup BLE resources
143
+ VailixSDK.instance.ble.destroy();
144
+ // Note: Database connection cleanup is handled by expo-sqlite
145
+ VailixSDK.instance = null;
146
+ }
147
+ VailixSDK.initPromise = null;
148
+ }
149
+
150
+ /**
151
+ * Check if the SDK has been initialized.
152
+ */
153
+ static isInitialized(): boolean {
154
+ return VailixSDK.instance !== null;
155
+ }
156
+
102
157
  // ========================================================================
103
158
  // QR Code Methods
104
159
  // ========================================================================