@chromahq/store 0.0.3 → 0.0.4

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.
@@ -0,0 +1,405 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var vanilla = require('zustand/vanilla');
5
+
6
+ function _interopNamespaceDefault(e) {
7
+ var n = Object.create(null);
8
+ if (e) {
9
+ Object.keys(e).forEach(function (k) {
10
+ if (k !== 'default') {
11
+ var d = Object.getOwnPropertyDescriptor(e, k);
12
+ Object.defineProperty(n, k, d.get ? d : {
13
+ enumerable: true,
14
+ get: function () { return e[k]; }
15
+ });
16
+ }
17
+ });
18
+ }
19
+ n.default = e;
20
+ return Object.freeze(n);
21
+ }
22
+
23
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
24
+
25
+ function chromeStoragePersist(options) {
26
+ return (config) => (set, get, store) => {
27
+ const key = options.name;
28
+ let isInitialized = false;
29
+ let persistenceSetup = false;
30
+ const initialState = config(set, get, store);
31
+ const loadPersistedState = async () => {
32
+ try {
33
+ if (!chrome?.storage?.local) {
34
+ console.warn(`Chrome storage not available for "${key}", using memory only`);
35
+ isInitialized = true;
36
+ setupPersistence();
37
+ return;
38
+ }
39
+ const result = await new Promise((resolve, reject) => {
40
+ chrome.storage.local.get([key], (result2) => {
41
+ if (chrome.runtime.lastError) {
42
+ reject(chrome.runtime.lastError);
43
+ } else {
44
+ resolve(result2);
45
+ }
46
+ });
47
+ });
48
+ if (result[key]) {
49
+ const mergedState = { ...initialState, ...result[key] };
50
+ set(mergedState);
51
+ } else {
52
+ await persistState(initialState);
53
+ }
54
+ } catch (error) {
55
+ console.error(`Failed to load persisted state for "${key}":`, error);
56
+ } finally {
57
+ isInitialized = true;
58
+ setupPersistence();
59
+ }
60
+ };
61
+ const persistState = async (state) => {
62
+ if (!chrome?.storage?.local) {
63
+ return;
64
+ }
65
+ return new Promise((resolve) => {
66
+ chrome.storage.local.set({ [key]: state }, () => {
67
+ if (chrome.runtime.lastError) {
68
+ console.error(`Failed to persist state for "${key}":`, chrome.runtime.lastError);
69
+ }
70
+ resolve();
71
+ });
72
+ });
73
+ };
74
+ const setupPersistence = () => {
75
+ if (persistenceSetup) return;
76
+ persistenceSetup = true;
77
+ store.subscribe((state) => {
78
+ if (!isInitialized) return;
79
+ persistState(state);
80
+ });
81
+ };
82
+ loadPersistedState();
83
+ return initialState;
84
+ };
85
+ }
86
+
87
+ function useCentralStore(store, selector) {
88
+ return React.useSyncExternalStore(
89
+ store.subscribe,
90
+ () => selector(store.getState()),
91
+ () => selector(store.getState())
92
+ );
93
+ }
94
+ function useCentralDispatch(store) {
95
+ return store.setState;
96
+ }
97
+
98
+ class BridgeStore {
99
+ constructor(bridge, initialState, storeName = "default") {
100
+ this.listeners = /* @__PURE__ */ new Set();
101
+ this.currentState = null;
102
+ this.previousState = null;
103
+ this.ready = false;
104
+ this.initialize = async () => {
105
+ try {
106
+ console.log("Initializing bridge store:", this.storeName, this.bridge);
107
+ const state = await this.bridge.send(`store:${this.storeName}:getState`);
108
+ this.previousState = this.currentState;
109
+ this.currentState = state;
110
+ this.notifyListeners();
111
+ this.ready = true;
112
+ } catch (error) {
113
+ console.warn("Failed to initialize bridge store:", error);
114
+ }
115
+ };
116
+ this.notifyListeners = () => {
117
+ if (!this.listeners) {
118
+ console.warn("BridgeStore: listeners not initialized");
119
+ return;
120
+ }
121
+ if (this.currentState && this.previousState) {
122
+ this.listeners.forEach((listener) => listener(this.currentState, this.previousState));
123
+ }
124
+ };
125
+ this.getState = () => {
126
+ return this.currentState;
127
+ };
128
+ this.subscribe = (listener) => {
129
+ if (!this.listeners) {
130
+ console.error("BridgeStore: Cannot subscribe, listeners not initialized");
131
+ return () => {
132
+ };
133
+ }
134
+ this.listeners.add(listener);
135
+ if (this.currentState && this.previousState) {
136
+ listener(this.currentState, this.previousState);
137
+ }
138
+ return () => {
139
+ if (this.listeners) {
140
+ this.listeners.delete(listener);
141
+ }
142
+ };
143
+ };
144
+ // Additional StoreApi methods
145
+ this.destroy = () => {
146
+ if (this.listeners) {
147
+ this.listeners.clear();
148
+ }
149
+ };
150
+ this.getInitialState = () => {
151
+ return this.getState();
152
+ };
153
+ this.bridge = bridge;
154
+ this.currentState = initialState || null;
155
+ this.previousState = initialState || null;
156
+ this.storeName = storeName;
157
+ this.setupStateSync();
158
+ this.initialize();
159
+ }
160
+ setupStateSync() {
161
+ if (this.bridge.on) {
162
+ this.bridge.on(`store:${this.storeName}:stateChanged`, (newState) => {
163
+ this.previousState = this.currentState;
164
+ this.currentState = newState;
165
+ this.notifyListeners();
166
+ });
167
+ }
168
+ }
169
+ setState(partial, replace) {
170
+ let actualUpdate;
171
+ if (typeof partial === "function") {
172
+ if (this.currentState === null) {
173
+ console.warn("BridgeStore: Cannot execute function update, state not initialized");
174
+ return;
175
+ }
176
+ actualUpdate = partial(this.currentState);
177
+ } else {
178
+ actualUpdate = partial;
179
+ }
180
+ const payload = { partial: actualUpdate, replace };
181
+ this.bridge.send(`store:${this.storeName}:setState`, payload).catch((error) => {
182
+ console.error("Failed to update state via bridge:", error);
183
+ });
184
+ if (this.currentState) {
185
+ this.previousState = this.currentState;
186
+ if (replace) {
187
+ this.currentState = actualUpdate;
188
+ } else {
189
+ this.currentState = { ...this.currentState, ...actualUpdate };
190
+ }
191
+ this.notifyListeners();
192
+ }
193
+ }
194
+ }
195
+ function createBridgeStore(bridge, initialState, storeName = "default") {
196
+ return new BridgeStore(bridge, initialState, storeName);
197
+ }
198
+
199
+ class StoreBuilder {
200
+ constructor(name = "default") {
201
+ this.config = {
202
+ name,
203
+ slices: []
204
+ };
205
+ }
206
+ /**
207
+ * Add state slices to the store
208
+ */
209
+ withSlices(...slices) {
210
+ this.config.slices = [...this.config.slices, ...slices];
211
+ return this;
212
+ }
213
+ /**
214
+ * Attach a bridge for cross-context communication
215
+ */
216
+ withBridge(bridge) {
217
+ this.config.bridge = bridge;
218
+ return this;
219
+ }
220
+ /**
221
+ * Create the store
222
+ */
223
+ async create() {
224
+ if (this.config.slices.length === 0) {
225
+ throw new Error("Store must have at least one slice. Use withSlices() to add state.");
226
+ }
227
+ return await this.createBaseStore();
228
+ }
229
+ async createBaseStore() {
230
+ const bridge = this.config.bridge || globalThis.bridge;
231
+ if (bridge) {
232
+ createBridgeStore(bridge, void 0, this.config.name);
233
+ }
234
+ return this.createServiceWorkerStore();
235
+ }
236
+ createServiceWorkerStore() {
237
+ const creator = (set, get, store2) => {
238
+ let state = {};
239
+ for (const slice of this.config.slices) {
240
+ const sliceState = slice(set, get, store2);
241
+ state = { ...state, ...sliceState };
242
+ }
243
+ return state;
244
+ };
245
+ const persistOptions = { name: this.config.name };
246
+ const persistedCreator = chromeStoragePersist(persistOptions)(creator);
247
+ const store = vanilla.createStore(persistedCreator);
248
+ const centralStore = store;
249
+ return centralStore;
250
+ }
251
+ }
252
+ function createStore(name) {
253
+ return new StoreBuilder(name);
254
+ }
255
+
256
+ function useStoreActions(store) {
257
+ return React.useMemo(
258
+ () => ({
259
+ // Update state with partial data
260
+ update: (partial) => {
261
+ store.setState((state) => ({ ...state, ...partial }));
262
+ },
263
+ // Update state with a function
264
+ updateWith: (updater) => {
265
+ store.setState((state) => ({ ...state, ...updater(state) }));
266
+ },
267
+ // Replace entire state
268
+ replace: (newState) => {
269
+ store.setState(newState, true);
270
+ },
271
+ // Direct access to setState
272
+ setState: store.setState.bind(store)
273
+ }),
274
+ [store]
275
+ );
276
+ }
277
+ function createStoreHooks() {
278
+ const StoreContext = React.createContext(null);
279
+ function StoreProvider({ store, children }) {
280
+ const storeRef = React.useRef(store);
281
+ return React__namespace.createElement(StoreContext.Provider, { value: storeRef.current }, children);
282
+ }
283
+ function useStore(selector) {
284
+ const store = React.useContext(StoreContext);
285
+ if (!store) throw new Error("useStore must be used within a StoreProvider");
286
+ return useCentralStore(store, selector);
287
+ }
288
+ function useStoreInstance() {
289
+ const store = React.useContext(StoreContext);
290
+ if (!store) throw new Error("useStoreInstance must be used within a StoreProvider");
291
+ return store;
292
+ }
293
+ function useActions() {
294
+ const store = useStoreInstance();
295
+ return useStoreActions(store);
296
+ }
297
+ function createActionHook(actionsFactory) {
298
+ return function useCustomActions() {
299
+ const store = useStoreInstance();
300
+ const baseActions = useStoreActions(store);
301
+ return React.useMemo(() => actionsFactory(baseActions), [baseActions]);
302
+ };
303
+ }
304
+ return {
305
+ StoreProvider,
306
+ useStore,
307
+ useStoreInstance,
308
+ useActions,
309
+ createActionHook
310
+ };
311
+ }
312
+
313
+ function autoRegisterStoreHandlers(store) {
314
+ if (!store) {
315
+ throw new Error("autoRegisterStoreHandlers: store parameter is required");
316
+ }
317
+ class AutoGetStoreStateMessage {
318
+ handle() {
319
+ if (!store) {
320
+ throw new Error("Store instance not available");
321
+ }
322
+ return store.getState();
323
+ }
324
+ }
325
+ class AutoSetStoreStateMessage {
326
+ handle(...args) {
327
+ if (!store) {
328
+ throw new Error("Store instance not available");
329
+ }
330
+ let partial;
331
+ let replace = false;
332
+ if (args.length === 1 && args[0] && typeof args[0] === "object") {
333
+ const payload = args[0];
334
+ if ("partial" in payload && "replace" in payload) {
335
+ ({ partial, replace } = payload);
336
+ } else {
337
+ partial = payload;
338
+ replace = false;
339
+ }
340
+ } else if (args.length >= 2) {
341
+ partial = args[0];
342
+ replace = args[1] || false;
343
+ } else if (args.length === 1) {
344
+ partial = args[0];
345
+ replace = false;
346
+ } else {
347
+ return store.getState();
348
+ }
349
+ if (partial === void 0) {
350
+ return store.getState();
351
+ }
352
+ if (replace) {
353
+ store.setState(partial, true);
354
+ } else {
355
+ store.setState(partial);
356
+ }
357
+ return store.getState();
358
+ }
359
+ }
360
+ class AutoSubscribeToStoreMessage {
361
+ handle() {
362
+ if (!store) {
363
+ throw new Error("Store instance not available");
364
+ }
365
+ store.subscribe((state, prevState) => {
366
+ });
367
+ }
368
+ }
369
+ return {
370
+ GetStoreStateMessage: AutoGetStoreStateMessage,
371
+ SetStoreStateMessage: AutoSetStoreStateMessage,
372
+ SubscribeToStoreMessage: AutoSubscribeToStoreMessage
373
+ };
374
+ }
375
+
376
+ async function init(storeDefinition) {
377
+ try {
378
+ let builder = createStore(storeDefinition.name);
379
+ if (storeDefinition.slices) {
380
+ builder = builder.withSlices(...storeDefinition.slices);
381
+ }
382
+ const store = await builder.create();
383
+ return {
384
+ store,
385
+ classes: autoRegisterStoreHandlers(store)
386
+ };
387
+ } catch (error) {
388
+ console.error(`Failed to initialize store "${storeDefinition.name}":`, error);
389
+ throw error;
390
+ }
391
+ }
392
+ if (typeof globalThis !== "undefined") {
393
+ globalThis.__CHROMA__ = globalThis.__CHROMA__ || {};
394
+ globalThis.__CHROMA__.initStores = init;
395
+ globalThis.initStores = init;
396
+ }
397
+
398
+ exports.BridgeStore = BridgeStore;
399
+ exports.StoreBuilder = StoreBuilder;
400
+ exports.chromeStoragePersist = chromeStoragePersist;
401
+ exports.createBridgeStore = createBridgeStore;
402
+ exports.createStore = createStore;
403
+ exports.createStoreHooks = createStoreHooks;
404
+ exports.useCentralDispatch = useCentralDispatch;
405
+ exports.useCentralStore = useCentralStore;
@@ -0,0 +1,129 @@
1
+ import { StoreApi, StateCreator } from 'zustand';
2
+ import { StateCreator as StateCreator$1 } from 'zustand/vanilla';
3
+ import * as React from 'react';
4
+ import { ReactNode } from 'react';
5
+
6
+ type PersistOptions = {
7
+ name: string;
8
+ version?: number;
9
+ migrate?: (state: any, version: number) => any;
10
+ };
11
+ interface StoreDefinition {
12
+ name: string;
13
+ slices?: StateCreator<any, [], [], any>[];
14
+ persistence?: PersistOptions;
15
+ config?: Record<string, any>;
16
+ }
17
+ type ExtractSliceState<T> = T extends StateCreator<infer State, any, any, any> ? State : never;
18
+ type SliceCreator<T> = StateCreator<T, [], [], T>;
19
+ interface StoreConfig<T> {
20
+ slices: readonly StateCreator<any, [], [], any>[];
21
+ persist?: PersistOptions;
22
+ }
23
+ type MergeSlices<Slices extends readonly StateCreator<any, [], [], any>[]> = Slices extends readonly [infer First, ...infer Rest] ? First extends StateCreator<any, [], [], infer FirstState> ? Rest extends readonly StateCreator<any, [], [], any>[] ? FirstState & MergeSlices<Rest> : FirstState : {} : {};
24
+ interface CentralStore<T> extends StoreApi<T> {
25
+ }
26
+
27
+ declare function chromeStoragePersist<S>(options: PersistOptions): (config: StateCreator<S>) => StateCreator<S>;
28
+
29
+ declare function useCentralStore<T, U = T>(store: CentralStore<T>, selector: (state: T) => U): U;
30
+ declare function useCentralDispatch<T>(store: CentralStore<T>): {
31
+ (partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: false): void;
32
+ (state: T | ((state: T) => T), replace: true): void;
33
+ };
34
+
35
+ interface Bridge {
36
+ send: <Req = unknown, Res = unknown>(key: string, payload?: Req) => Promise<Res>;
37
+ isConnected: boolean;
38
+ }
39
+ interface BridgeWithEvents extends Bridge {
40
+ on?: (key: string, handler: (payload: any) => void) => void;
41
+ }
42
+ interface BridgeWithHandlers extends Bridge {
43
+ register: (key: string, handler: (payload?: any) => any) => void;
44
+ broadcast: (key: string, payload: any) => void;
45
+ on?: (key: string, handler: (payload: any) => void) => void;
46
+ }
47
+ declare class BridgeStore<T> implements CentralStore<T> {
48
+ private bridge;
49
+ private listeners;
50
+ private currentState;
51
+ private previousState;
52
+ private storeName;
53
+ private ready;
54
+ constructor(bridge: BridgeWithEvents, initialState?: T, storeName?: string);
55
+ initialize: () => Promise<void>;
56
+ private setupStateSync;
57
+ private notifyListeners;
58
+ getState: () => T;
59
+ setState(partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: false): void;
60
+ setState(state: T | ((state: T) => T), replace: true): void;
61
+ subscribe: (listener: (state: T, prevState: T) => void) => (() => void);
62
+ destroy: () => void;
63
+ getInitialState: () => T;
64
+ }
65
+ declare function createBridgeStore<T>(bridge: BridgeWithEvents, initialState?: T, storeName?: string): CentralStore<T>;
66
+
67
+ /**
68
+ * Core store builder with fluent API
69
+ */
70
+ declare class StoreBuilder<T = any> {
71
+ private config;
72
+ constructor(name?: string);
73
+ /**
74
+ * Add state slices to the store
75
+ */
76
+ withSlices(...slices: StateCreator$1<any, [], [], any>[]): this;
77
+ /**
78
+ * Attach a bridge for cross-context communication
79
+ */
80
+ withBridge(bridge?: BridgeWithEvents): this;
81
+ /**
82
+ * Create the store
83
+ */
84
+ create(): Promise<CentralStore<T>>;
85
+ private createBaseStore;
86
+ private createServiceWorkerStore;
87
+ }
88
+ /**
89
+ * Create a new store builder
90
+ */
91
+ declare function createStore<T = any>(name?: string): StoreBuilder<T>;
92
+
93
+ /**
94
+ * Store actions helper (inlined from actions.ts)
95
+ */
96
+ declare function useStoreActions<T extends Record<string, any>>(store: CentralStore<T>): {
97
+ update: (partial: Partial<T>) => void;
98
+ updateWith: (updater: (state: T) => Partial<T>) => void;
99
+ replace: (newState: T) => void;
100
+ setState: {
101
+ (partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: false): void;
102
+ (state: T | ((state: T) => T), replace: true): void;
103
+ };
104
+ };
105
+ /**
106
+ * Create a complete store hook factory with typed providers and action creators
107
+ * This prevents confusion by providing only the hooks you need, not global ones
108
+ */
109
+ declare function createStoreHooks<T extends Record<string, any>>(): {
110
+ StoreProvider: ({ store, children }: {
111
+ store: CentralStore<T>;
112
+ children: ReactNode;
113
+ }) => React.FunctionComponentElement<React.ProviderProps<CentralStore<T> | null>>;
114
+ useStore: <U>(selector: (state: T) => U) => U;
115
+ useStoreInstance: () => CentralStore<T>;
116
+ useActions: () => {
117
+ update: (partial: Partial<T>) => void;
118
+ updateWith: (updater: (state: T) => Partial<T>) => void;
119
+ replace: (newState: T) => void;
120
+ setState: {
121
+ (partial: T | Partial<T> | ((state: T) => T | Partial<T>), replace?: false): void;
122
+ (state: T | ((state: T) => T), replace: true): void;
123
+ };
124
+ };
125
+ createActionHook: <ActionMap extends Record<string, (...args: any[]) => void>>(actionsFactory: (actions: ReturnType<typeof useStoreActions<T>>) => ActionMap) => () => ActionMap;
126
+ };
127
+
128
+ export { BridgeStore, StoreBuilder, chromeStoragePersist, createBridgeStore, createStore, createStoreHooks, useCentralDispatch, useCentralStore };
129
+ export type { Bridge, BridgeWithEvents, BridgeWithHandlers, CentralStore, ExtractSliceState, MergeSlices, PersistOptions, SliceCreator, StoreConfig, StoreDefinition };
@@ -0,0 +1,378 @@
1
+ import * as React from 'react';
2
+ import { useSyncExternalStore, createContext, useMemo, useContext, useRef } from 'react';
3
+ import { createStore as createStore$1 } from 'zustand/vanilla';
4
+
5
+ function chromeStoragePersist(options) {
6
+ return (config) => (set, get, store) => {
7
+ const key = options.name;
8
+ let isInitialized = false;
9
+ let persistenceSetup = false;
10
+ const initialState = config(set, get, store);
11
+ const loadPersistedState = async () => {
12
+ try {
13
+ if (!chrome?.storage?.local) {
14
+ console.warn(`Chrome storage not available for "${key}", using memory only`);
15
+ isInitialized = true;
16
+ setupPersistence();
17
+ return;
18
+ }
19
+ const result = await new Promise((resolve, reject) => {
20
+ chrome.storage.local.get([key], (result2) => {
21
+ if (chrome.runtime.lastError) {
22
+ reject(chrome.runtime.lastError);
23
+ } else {
24
+ resolve(result2);
25
+ }
26
+ });
27
+ });
28
+ if (result[key]) {
29
+ const mergedState = { ...initialState, ...result[key] };
30
+ set(mergedState);
31
+ } else {
32
+ await persistState(initialState);
33
+ }
34
+ } catch (error) {
35
+ console.error(`Failed to load persisted state for "${key}":`, error);
36
+ } finally {
37
+ isInitialized = true;
38
+ setupPersistence();
39
+ }
40
+ };
41
+ const persistState = async (state) => {
42
+ if (!chrome?.storage?.local) {
43
+ return;
44
+ }
45
+ return new Promise((resolve) => {
46
+ chrome.storage.local.set({ [key]: state }, () => {
47
+ if (chrome.runtime.lastError) {
48
+ console.error(`Failed to persist state for "${key}":`, chrome.runtime.lastError);
49
+ }
50
+ resolve();
51
+ });
52
+ });
53
+ };
54
+ const setupPersistence = () => {
55
+ if (persistenceSetup) return;
56
+ persistenceSetup = true;
57
+ store.subscribe((state) => {
58
+ if (!isInitialized) return;
59
+ persistState(state);
60
+ });
61
+ };
62
+ loadPersistedState();
63
+ return initialState;
64
+ };
65
+ }
66
+
67
+ function useCentralStore(store, selector) {
68
+ return useSyncExternalStore(
69
+ store.subscribe,
70
+ () => selector(store.getState()),
71
+ () => selector(store.getState())
72
+ );
73
+ }
74
+ function useCentralDispatch(store) {
75
+ return store.setState;
76
+ }
77
+
78
+ class BridgeStore {
79
+ constructor(bridge, initialState, storeName = "default") {
80
+ this.listeners = /* @__PURE__ */ new Set();
81
+ this.currentState = null;
82
+ this.previousState = null;
83
+ this.ready = false;
84
+ this.initialize = async () => {
85
+ try {
86
+ console.log("Initializing bridge store:", this.storeName, this.bridge);
87
+ const state = await this.bridge.send(`store:${this.storeName}:getState`);
88
+ this.previousState = this.currentState;
89
+ this.currentState = state;
90
+ this.notifyListeners();
91
+ this.ready = true;
92
+ } catch (error) {
93
+ console.warn("Failed to initialize bridge store:", error);
94
+ }
95
+ };
96
+ this.notifyListeners = () => {
97
+ if (!this.listeners) {
98
+ console.warn("BridgeStore: listeners not initialized");
99
+ return;
100
+ }
101
+ if (this.currentState && this.previousState) {
102
+ this.listeners.forEach((listener) => listener(this.currentState, this.previousState));
103
+ }
104
+ };
105
+ this.getState = () => {
106
+ return this.currentState;
107
+ };
108
+ this.subscribe = (listener) => {
109
+ if (!this.listeners) {
110
+ console.error("BridgeStore: Cannot subscribe, listeners not initialized");
111
+ return () => {
112
+ };
113
+ }
114
+ this.listeners.add(listener);
115
+ if (this.currentState && this.previousState) {
116
+ listener(this.currentState, this.previousState);
117
+ }
118
+ return () => {
119
+ if (this.listeners) {
120
+ this.listeners.delete(listener);
121
+ }
122
+ };
123
+ };
124
+ // Additional StoreApi methods
125
+ this.destroy = () => {
126
+ if (this.listeners) {
127
+ this.listeners.clear();
128
+ }
129
+ };
130
+ this.getInitialState = () => {
131
+ return this.getState();
132
+ };
133
+ this.bridge = bridge;
134
+ this.currentState = initialState || null;
135
+ this.previousState = initialState || null;
136
+ this.storeName = storeName;
137
+ this.setupStateSync();
138
+ this.initialize();
139
+ }
140
+ setupStateSync() {
141
+ if (this.bridge.on) {
142
+ this.bridge.on(`store:${this.storeName}:stateChanged`, (newState) => {
143
+ this.previousState = this.currentState;
144
+ this.currentState = newState;
145
+ this.notifyListeners();
146
+ });
147
+ }
148
+ }
149
+ setState(partial, replace) {
150
+ let actualUpdate;
151
+ if (typeof partial === "function") {
152
+ if (this.currentState === null) {
153
+ console.warn("BridgeStore: Cannot execute function update, state not initialized");
154
+ return;
155
+ }
156
+ actualUpdate = partial(this.currentState);
157
+ } else {
158
+ actualUpdate = partial;
159
+ }
160
+ const payload = { partial: actualUpdate, replace };
161
+ this.bridge.send(`store:${this.storeName}:setState`, payload).catch((error) => {
162
+ console.error("Failed to update state via bridge:", error);
163
+ });
164
+ if (this.currentState) {
165
+ this.previousState = this.currentState;
166
+ if (replace) {
167
+ this.currentState = actualUpdate;
168
+ } else {
169
+ this.currentState = { ...this.currentState, ...actualUpdate };
170
+ }
171
+ this.notifyListeners();
172
+ }
173
+ }
174
+ }
175
+ function createBridgeStore(bridge, initialState, storeName = "default") {
176
+ return new BridgeStore(bridge, initialState, storeName);
177
+ }
178
+
179
+ class StoreBuilder {
180
+ constructor(name = "default") {
181
+ this.config = {
182
+ name,
183
+ slices: []
184
+ };
185
+ }
186
+ /**
187
+ * Add state slices to the store
188
+ */
189
+ withSlices(...slices) {
190
+ this.config.slices = [...this.config.slices, ...slices];
191
+ return this;
192
+ }
193
+ /**
194
+ * Attach a bridge for cross-context communication
195
+ */
196
+ withBridge(bridge) {
197
+ this.config.bridge = bridge;
198
+ return this;
199
+ }
200
+ /**
201
+ * Create the store
202
+ */
203
+ async create() {
204
+ if (this.config.slices.length === 0) {
205
+ throw new Error("Store must have at least one slice. Use withSlices() to add state.");
206
+ }
207
+ return await this.createBaseStore();
208
+ }
209
+ async createBaseStore() {
210
+ const bridge = this.config.bridge || globalThis.bridge;
211
+ if (bridge) {
212
+ createBridgeStore(bridge, void 0, this.config.name);
213
+ }
214
+ return this.createServiceWorkerStore();
215
+ }
216
+ createServiceWorkerStore() {
217
+ const creator = (set, get, store2) => {
218
+ let state = {};
219
+ for (const slice of this.config.slices) {
220
+ const sliceState = slice(set, get, store2);
221
+ state = { ...state, ...sliceState };
222
+ }
223
+ return state;
224
+ };
225
+ const persistOptions = { name: this.config.name };
226
+ const persistedCreator = chromeStoragePersist(persistOptions)(creator);
227
+ const store = createStore$1(persistedCreator);
228
+ const centralStore = store;
229
+ return centralStore;
230
+ }
231
+ }
232
+ function createStore(name) {
233
+ return new StoreBuilder(name);
234
+ }
235
+
236
+ function useStoreActions(store) {
237
+ return useMemo(
238
+ () => ({
239
+ // Update state with partial data
240
+ update: (partial) => {
241
+ store.setState((state) => ({ ...state, ...partial }));
242
+ },
243
+ // Update state with a function
244
+ updateWith: (updater) => {
245
+ store.setState((state) => ({ ...state, ...updater(state) }));
246
+ },
247
+ // Replace entire state
248
+ replace: (newState) => {
249
+ store.setState(newState, true);
250
+ },
251
+ // Direct access to setState
252
+ setState: store.setState.bind(store)
253
+ }),
254
+ [store]
255
+ );
256
+ }
257
+ function createStoreHooks() {
258
+ const StoreContext = createContext(null);
259
+ function StoreProvider({ store, children }) {
260
+ const storeRef = useRef(store);
261
+ return React.createElement(StoreContext.Provider, { value: storeRef.current }, children);
262
+ }
263
+ function useStore(selector) {
264
+ const store = useContext(StoreContext);
265
+ if (!store) throw new Error("useStore must be used within a StoreProvider");
266
+ return useCentralStore(store, selector);
267
+ }
268
+ function useStoreInstance() {
269
+ const store = useContext(StoreContext);
270
+ if (!store) throw new Error("useStoreInstance must be used within a StoreProvider");
271
+ return store;
272
+ }
273
+ function useActions() {
274
+ const store = useStoreInstance();
275
+ return useStoreActions(store);
276
+ }
277
+ function createActionHook(actionsFactory) {
278
+ return function useCustomActions() {
279
+ const store = useStoreInstance();
280
+ const baseActions = useStoreActions(store);
281
+ return useMemo(() => actionsFactory(baseActions), [baseActions]);
282
+ };
283
+ }
284
+ return {
285
+ StoreProvider,
286
+ useStore,
287
+ useStoreInstance,
288
+ useActions,
289
+ createActionHook
290
+ };
291
+ }
292
+
293
+ function autoRegisterStoreHandlers(store) {
294
+ if (!store) {
295
+ throw new Error("autoRegisterStoreHandlers: store parameter is required");
296
+ }
297
+ class AutoGetStoreStateMessage {
298
+ handle() {
299
+ if (!store) {
300
+ throw new Error("Store instance not available");
301
+ }
302
+ return store.getState();
303
+ }
304
+ }
305
+ class AutoSetStoreStateMessage {
306
+ handle(...args) {
307
+ if (!store) {
308
+ throw new Error("Store instance not available");
309
+ }
310
+ let partial;
311
+ let replace = false;
312
+ if (args.length === 1 && args[0] && typeof args[0] === "object") {
313
+ const payload = args[0];
314
+ if ("partial" in payload && "replace" in payload) {
315
+ ({ partial, replace } = payload);
316
+ } else {
317
+ partial = payload;
318
+ replace = false;
319
+ }
320
+ } else if (args.length >= 2) {
321
+ partial = args[0];
322
+ replace = args[1] || false;
323
+ } else if (args.length === 1) {
324
+ partial = args[0];
325
+ replace = false;
326
+ } else {
327
+ return store.getState();
328
+ }
329
+ if (partial === void 0) {
330
+ return store.getState();
331
+ }
332
+ if (replace) {
333
+ store.setState(partial, true);
334
+ } else {
335
+ store.setState(partial);
336
+ }
337
+ return store.getState();
338
+ }
339
+ }
340
+ class AutoSubscribeToStoreMessage {
341
+ handle() {
342
+ if (!store) {
343
+ throw new Error("Store instance not available");
344
+ }
345
+ store.subscribe((state, prevState) => {
346
+ });
347
+ }
348
+ }
349
+ return {
350
+ GetStoreStateMessage: AutoGetStoreStateMessage,
351
+ SetStoreStateMessage: AutoSetStoreStateMessage,
352
+ SubscribeToStoreMessage: AutoSubscribeToStoreMessage
353
+ };
354
+ }
355
+
356
+ async function init(storeDefinition) {
357
+ try {
358
+ let builder = createStore(storeDefinition.name);
359
+ if (storeDefinition.slices) {
360
+ builder = builder.withSlices(...storeDefinition.slices);
361
+ }
362
+ const store = await builder.create();
363
+ return {
364
+ store,
365
+ classes: autoRegisterStoreHandlers(store)
366
+ };
367
+ } catch (error) {
368
+ console.error(`Failed to initialize store "${storeDefinition.name}":`, error);
369
+ throw error;
370
+ }
371
+ }
372
+ if (typeof globalThis !== "undefined") {
373
+ globalThis.__CHROMA__ = globalThis.__CHROMA__ || {};
374
+ globalThis.__CHROMA__.initStores = init;
375
+ globalThis.initStores = init;
376
+ }
377
+
378
+ export { BridgeStore, StoreBuilder, chromeStoragePersist, createBridgeStore, createStore, createStoreHooks, useCentralDispatch, useCentralStore };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chromahq/store",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
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",