@chromahq/store 1.0.13 → 1.0.15

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.js CHANGED
@@ -118,20 +118,35 @@ class BridgeStore {
118
118
  this.readyCallbacks = /* @__PURE__ */ new Set();
119
119
  this.initializationAttempts = 0;
120
120
  this.maxInitializationAttempts = 10;
121
+ this.initializationTimer = null;
122
+ this.isInitializing = false;
121
123
  this.initialize = async () => {
124
+ if (this.isInitializing) {
125
+ return;
126
+ }
127
+ if (this.initializationTimer) {
128
+ clearTimeout(this.initializationTimer);
129
+ this.initializationTimer = null;
130
+ }
122
131
  this.initializationAttempts++;
132
+ this.isInitializing = true;
123
133
  try {
124
134
  if (this.initializationAttempts > this.maxInitializationAttempts) {
125
135
  console.error(
126
136
  `BridgeStore[${this.storeName}]: Max initialization attempts (${this.maxInitializationAttempts}) reached, giving up`
127
137
  );
138
+ this.isInitializing = false;
128
139
  return;
129
140
  }
130
141
  if (!this.bridge.isConnected) {
131
- console.warn(
132
- `BridgeStore[${this.storeName}]: Bridge not connected (attempt ${this.initializationAttempts}), retrying in 1s...`
133
- );
134
- setTimeout(() => this.initialize(), 500);
142
+ if (this.initializationAttempts === 1 || this.initializationAttempts % 3 === 0) {
143
+ console.log(
144
+ `BridgeStore[${this.storeName}]: Waiting for bridge connection (attempt ${this.initializationAttempts}/${this.maxInitializationAttempts})...`
145
+ );
146
+ }
147
+ const delay = Math.min(500 * Math.pow(2, this.initializationAttempts - 1), 5e3);
148
+ this.isInitializing = false;
149
+ this.initializationTimer = setTimeout(() => this.initialize(), delay);
135
150
  return;
136
151
  }
137
152
  const state = await this.bridge.send(`store:${this.storeName}:getState`);
@@ -142,20 +157,21 @@ class BridgeStore {
142
157
  }
143
158
  this.notifyListeners();
144
159
  this.ready = true;
160
+ this.isInitializing = false;
161
+ console.log(`BridgeStore[${this.storeName}]: Initialized successfully`);
145
162
  this.notifyReady();
146
163
  } catch (error) {
164
+ this.isInitializing = false;
147
165
  console.error(
148
166
  `BridgeStore[${this.storeName}]: Failed to initialize (attempt ${this.initializationAttempts}):`,
149
167
  error
150
168
  );
151
- if (this.bridge.isConnected && this.initializationAttempts < this.maxInitializationAttempts) {
152
- const delay = Math.min(2e3 * this.initializationAttempts, 1e4);
153
- console.warn(`BridgeStore[${this.storeName}]: Retrying initialization in ${delay}ms...`);
154
- setTimeout(() => this.initialize(), delay);
169
+ if (this.initializationAttempts < this.maxInitializationAttempts) {
170
+ const delay = Math.min(1e3 * Math.pow(2, this.initializationAttempts - 1), 1e4);
171
+ console.log(`BridgeStore[${this.storeName}]: Retrying initialization in ${delay}ms...`);
172
+ this.initializationTimer = setTimeout(() => this.initialize(), delay);
155
173
  } else {
156
- console.error(
157
- `BridgeStore[${this.storeName}]: Bridge disconnected or max attempts reached, cannot retry`
158
- );
174
+ console.error(`BridgeStore[${this.storeName}]: Max attempts reached, cannot retry`);
159
175
  }
160
176
  }
161
177
  };
@@ -191,9 +207,14 @@ class BridgeStore {
191
207
  };
192
208
  // Additional StoreApi methods
193
209
  this.destroy = () => {
210
+ if (this.initializationTimer) {
211
+ clearTimeout(this.initializationTimer);
212
+ this.initializationTimer = null;
213
+ }
194
214
  if (this.listeners) {
195
215
  this.listeners.clear();
196
216
  }
217
+ this.readyCallbacks.clear();
197
218
  };
198
219
  this.getInitialState = () => {
199
220
  return this.getState();
@@ -244,11 +265,16 @@ class BridgeStore {
244
265
  this.readyCallbacks.clear();
245
266
  };
246
267
  /**
247
- * Force re-initialization of the store (useful for debugging)
268
+ * Force re-initialization of the store (useful for debugging or after reconnection)
248
269
  */
249
270
  this.forceInitialize = async () => {
250
271
  console.debug(`BridgeStore[${this.storeName}]: Force re-initialization requested`);
272
+ if (this.initializationTimer) {
273
+ clearTimeout(this.initializationTimer);
274
+ this.initializationTimer = null;
275
+ }
251
276
  this.ready = false;
277
+ this.isInitializing = false;
252
278
  this.initializationAttempts = 0;
253
279
  await this.initialize();
254
280
  };
@@ -259,6 +285,7 @@ class BridgeStore {
259
285
  return {
260
286
  storeName: this.storeName,
261
287
  ready: this.ready,
288
+ isInitializing: this.isInitializing,
262
289
  bridgeConnected: this.bridge.isConnected,
263
290
  hasCurrentState: this.currentState !== null,
264
291
  hasInitialState: this.initialState !== null,
package/dist/index.d.ts CHANGED
@@ -51,11 +51,13 @@ interface Bridge {
51
51
  }
52
52
  interface BridgeWithEvents extends Bridge {
53
53
  on?: (key: string, handler: (payload: any) => void) => void;
54
+ off?: (key: string, handler: (payload: any) => void) => void;
54
55
  }
55
56
  interface BridgeWithHandlers extends Bridge {
56
57
  register: (key: string, handler: (payload?: any) => any) => void;
57
58
  broadcast: (key: string, payload: any) => void;
58
59
  on?: (key: string, handler: (payload: any) => void) => void;
60
+ off?: (key: string, handler: (payload: any) => void) => void;
59
61
  }
60
62
  declare class BridgeStore<T> implements CentralStore<T> {
61
63
  private bridge;
@@ -68,6 +70,8 @@ declare class BridgeStore<T> implements CentralStore<T> {
68
70
  private readyCallbacks;
69
71
  private initializationAttempts;
70
72
  private readonly maxInitializationAttempts;
73
+ private initializationTimer;
74
+ private isInitializing;
71
75
  constructor(bridge: BridgeWithEvents, initialState?: T, storeName?: string, readyCallbacks?: Set<() => void>);
72
76
  initialize: () => Promise<void>;
73
77
  private stateSyncSequence;
@@ -86,7 +90,7 @@ declare class BridgeStore<T> implements CentralStore<T> {
86
90
  reset: () => void;
87
91
  private notifyReady;
88
92
  /**
89
- * Force re-initialization of the store (useful for debugging)
93
+ * Force re-initialization of the store (useful for debugging or after reconnection)
90
94
  */
91
95
  forceInitialize: () => Promise<void>;
92
96
  /**
@@ -95,6 +99,7 @@ declare class BridgeStore<T> implements CentralStore<T> {
95
99
  getDebugInfo: () => {
96
100
  storeName: string;
97
101
  ready: boolean;
102
+ isInitializing: boolean;
98
103
  bridgeConnected: boolean;
99
104
  hasCurrentState: boolean;
100
105
  hasInitialState: boolean;
package/dist/index.es.js CHANGED
@@ -98,20 +98,35 @@ class BridgeStore {
98
98
  this.readyCallbacks = /* @__PURE__ */ new Set();
99
99
  this.initializationAttempts = 0;
100
100
  this.maxInitializationAttempts = 10;
101
+ this.initializationTimer = null;
102
+ this.isInitializing = false;
101
103
  this.initialize = async () => {
104
+ if (this.isInitializing) {
105
+ return;
106
+ }
107
+ if (this.initializationTimer) {
108
+ clearTimeout(this.initializationTimer);
109
+ this.initializationTimer = null;
110
+ }
102
111
  this.initializationAttempts++;
112
+ this.isInitializing = true;
103
113
  try {
104
114
  if (this.initializationAttempts > this.maxInitializationAttempts) {
105
115
  console.error(
106
116
  `BridgeStore[${this.storeName}]: Max initialization attempts (${this.maxInitializationAttempts}) reached, giving up`
107
117
  );
118
+ this.isInitializing = false;
108
119
  return;
109
120
  }
110
121
  if (!this.bridge.isConnected) {
111
- console.warn(
112
- `BridgeStore[${this.storeName}]: Bridge not connected (attempt ${this.initializationAttempts}), retrying in 1s...`
113
- );
114
- setTimeout(() => this.initialize(), 500);
122
+ if (this.initializationAttempts === 1 || this.initializationAttempts % 3 === 0) {
123
+ console.log(
124
+ `BridgeStore[${this.storeName}]: Waiting for bridge connection (attempt ${this.initializationAttempts}/${this.maxInitializationAttempts})...`
125
+ );
126
+ }
127
+ const delay = Math.min(500 * Math.pow(2, this.initializationAttempts - 1), 5e3);
128
+ this.isInitializing = false;
129
+ this.initializationTimer = setTimeout(() => this.initialize(), delay);
115
130
  return;
116
131
  }
117
132
  const state = await this.bridge.send(`store:${this.storeName}:getState`);
@@ -122,20 +137,21 @@ class BridgeStore {
122
137
  }
123
138
  this.notifyListeners();
124
139
  this.ready = true;
140
+ this.isInitializing = false;
141
+ console.log(`BridgeStore[${this.storeName}]: Initialized successfully`);
125
142
  this.notifyReady();
126
143
  } catch (error) {
144
+ this.isInitializing = false;
127
145
  console.error(
128
146
  `BridgeStore[${this.storeName}]: Failed to initialize (attempt ${this.initializationAttempts}):`,
129
147
  error
130
148
  );
131
- if (this.bridge.isConnected && this.initializationAttempts < this.maxInitializationAttempts) {
132
- const delay = Math.min(2e3 * this.initializationAttempts, 1e4);
133
- console.warn(`BridgeStore[${this.storeName}]: Retrying initialization in ${delay}ms...`);
134
- setTimeout(() => this.initialize(), delay);
149
+ if (this.initializationAttempts < this.maxInitializationAttempts) {
150
+ const delay = Math.min(1e3 * Math.pow(2, this.initializationAttempts - 1), 1e4);
151
+ console.log(`BridgeStore[${this.storeName}]: Retrying initialization in ${delay}ms...`);
152
+ this.initializationTimer = setTimeout(() => this.initialize(), delay);
135
153
  } else {
136
- console.error(
137
- `BridgeStore[${this.storeName}]: Bridge disconnected or max attempts reached, cannot retry`
138
- );
154
+ console.error(`BridgeStore[${this.storeName}]: Max attempts reached, cannot retry`);
139
155
  }
140
156
  }
141
157
  };
@@ -171,9 +187,14 @@ class BridgeStore {
171
187
  };
172
188
  // Additional StoreApi methods
173
189
  this.destroy = () => {
190
+ if (this.initializationTimer) {
191
+ clearTimeout(this.initializationTimer);
192
+ this.initializationTimer = null;
193
+ }
174
194
  if (this.listeners) {
175
195
  this.listeners.clear();
176
196
  }
197
+ this.readyCallbacks.clear();
177
198
  };
178
199
  this.getInitialState = () => {
179
200
  return this.getState();
@@ -224,11 +245,16 @@ class BridgeStore {
224
245
  this.readyCallbacks.clear();
225
246
  };
226
247
  /**
227
- * Force re-initialization of the store (useful for debugging)
248
+ * Force re-initialization of the store (useful for debugging or after reconnection)
228
249
  */
229
250
  this.forceInitialize = async () => {
230
251
  console.debug(`BridgeStore[${this.storeName}]: Force re-initialization requested`);
252
+ if (this.initializationTimer) {
253
+ clearTimeout(this.initializationTimer);
254
+ this.initializationTimer = null;
255
+ }
231
256
  this.ready = false;
257
+ this.isInitializing = false;
232
258
  this.initializationAttempts = 0;
233
259
  await this.initialize();
234
260
  };
@@ -239,6 +265,7 @@ class BridgeStore {
239
265
  return {
240
266
  storeName: this.storeName,
241
267
  ready: this.ready,
268
+ isInitializing: this.isInitializing,
242
269
  bridgeConnected: this.bridge.isConnected,
243
270
  hasCurrentState: this.currentState !== null,
244
271
  hasInitialState: this.initialState !== null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chromahq/store",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Centralized, persistent store for Chrome extensions using zustand, accessible from service workers and React, with chrome.storage.local persistence.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs.js",