resonare 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -1,15 +1,14 @@
1
1
  # Resonare [![Version](https://img.shields.io/npm/v/resonare.svg?labelColor=black&color=blue)](https://www.npmjs.com/package/resonare)
2
2
 
3
- A configuration-based store for restoring user preferences without flash of inaccurate styles.
3
+ A state store for multi-dimensional themes and user preferences.
4
4
 
5
5
  ## Features
6
6
 
7
- - Define and manage user preferences, e.g.:
7
+ - Define and manage themes and user preferences, e.g.:
8
8
  - Color scheme: system, light, dark
9
9
  - Contrast preference: standard, high
10
10
  - Spacing: compact, comfortable, spacious
11
- - Showing/hiding sections
12
- - Sidebar width
11
+ - Showing, hiding, or collapsing sidebar/sections
13
12
  - etc.
14
13
  - Framework-agnostic
15
14
  - Prevent flicker on page load
@@ -72,7 +71,7 @@ export const themeScript = createInlineThemeScript([
72
71
 
73
72
  ### 2. Inject inline script
74
73
 
75
- Inject the script as below into the `<head>` of your HTML document. This differs by framework. See the examples below.
74
+ Inject the script into the `<head>` of your HTML document. The exact pattern differs by framework. Here are some examples.
76
75
 
77
76
  ```tsx
78
77
  // React.js
@@ -221,16 +220,15 @@ const script = createInlineThemeScript([
221
220
  #### Client-side persistence
222
221
 
223
222
  ```tsx
224
- import * as React from 'react'
225
223
  import {
226
224
  createInlineThemeScript,
227
225
  createThemeStore,
228
226
  getThemesAndOptions,
229
- type ThemeStoreConfig,
227
+ type ThemeScriptParameter,
230
228
  } from 'resonare'
231
229
  import { useResonare } from 'resonare/react'
232
230
 
233
- const STORE = {
231
+ const PARAM = {
234
232
  key: 'demo',
235
233
  config: {
236
234
  colorScheme: {
@@ -263,31 +261,31 @@ const STORE = {
263
261
  document.documentElement.dataset[key] = String(value)
264
262
  })
265
263
  },
266
- } as const satisfies Parameters<typeof createInlineThemeScript>[0][number]
264
+ } as const satisfies ThemeScriptParameter
265
+
266
+ export const themeScript = createInlineThemeScript([PARAM])
267
267
 
268
- export const themeScript = createInlineThemeScript([STORE])
268
+ const themeStore = createThemeStore(PARAM.config)
269
269
 
270
- const themeStore = createThemeStore(STORE.config)
271
270
 
272
271
  function ThemeSelect() {
273
- const { themes, setThemes, restore, subscribe, sync } =
274
- useResonare(themeStore)
272
+ const { themes, setThemes } = useResonare(themeStore)
275
273
 
276
274
  React.useEffect(() => {
277
- restore()
278
-
279
- const unsubscribe = subscribe(STORE.handler)
275
+ const unsubscribe = subscribe(PARAM.handler)
280
276
 
281
277
  const stopSync = sync()
282
278
 
283
- return () => {
284
- unsubscribe()
279
+ restore()
285
280
 
281
+ return () => {
286
282
  stopSync?.()
283
+
284
+ unsubscribe()
287
285
  }
288
- }, [restore, subscribe, sync])
286
+ }, [subscribe, restore, sync])
289
287
 
290
- return getThemesAndOptions(STORE.config).map(([theme, options]) => (
288
+ return getThemesAndOptions(PARAM.config).map(([theme, options]) => (
291
289
  <div key={theme}>
292
290
  <label htmlFor={theme}>{theme}</label>
293
291
  <select
@@ -341,12 +339,9 @@ export function ThemeSelect({ persistedStateFromDb }) {
341
339
  }),
342
340
  )
343
341
 
344
- const { themes, setThemes, restore, subscribe, sync } =
345
- useResonare(themeStore)
342
+ const { themes, setThemes, subscribe, sync } = useResonare(themeStore)
346
343
 
347
344
  React.useEffect(() => {
348
- restore()
349
-
350
345
  const unsubscribe = subscribe(({ resolvedThemes }) => {
351
346
  Object.entries(resolvedThemes).forEach(([key, value]) => {
352
347
  document.documentElement.dataset[key] = String(value)
@@ -356,11 +351,11 @@ export function ThemeSelect({ persistedStateFromDb }) {
356
351
  const stopSync = sync()
357
352
 
358
353
  return () => {
359
- unsubscribe()
360
-
361
354
  stopSync?.()
355
+
356
+ unsubscribe()
362
357
  }
363
- }, [restore, subscribe, sync])
358
+ }, [subscribe, sync])
364
359
 
365
360
  return getThemesAndOptions(CONFIG).map(([theme, options]) => (
366
361
  <div key={theme}>
package/dist/index.d.ts CHANGED
@@ -1,8 +1,11 @@
1
1
  //#region src/storage.d.ts
2
+ /**
3
+ * Pluggable persistence for `createThemeStore`.
4
+ */
2
5
  type StorageAdapter = {
3
6
  get: () => object | null;
4
- set: (value: object) => void;
5
- broadcast?: (value: object) => void;
7
+ set: (value: object) => void; /** Optional: notify other tabs/contexts after local `set` operation. */
8
+ broadcast?: (value: object) => void; /** Optional: subscribes to other tabs/contexts' updates, returns `unsubscribe` function. */
6
9
  watch?: (cb: (value: object) => void) => () => void;
7
10
  };
8
11
  type StorageAdapterCreate = ({
@@ -11,10 +14,35 @@ type StorageAdapterCreate = ({
11
14
  abortController: AbortController;
12
15
  }) => StorageAdapter;
13
16
  type StorageAdapterCreator<Options> = (options: Options) => StorageAdapterCreate;
17
+ /**
18
+ * Persists theme store in `localStorage` or `sessionStorage`.
19
+ * @example
20
+ * ```ts
21
+ * import { createThemeStore, localStorageAdapter } from 'resonare'
22
+ *
23
+ * const store = createThemeStore(
24
+ * { colorMode: { options: ['light', 'dark'] },
25
+ * { storage: localStorageAdapter({ key: 'app', type: 'localStorage' }) },
26
+ * )
27
+ * ```
28
+ */
14
29
  declare const localStorageAdapter: StorageAdapterCreator<{
15
30
  key: string;
16
31
  type?: 'localStorage' | 'sessionStorage';
17
32
  }>;
33
+ /**
34
+ * In-memory persistence and sync via `BroadcastChannel`.
35
+ * Useful with server-side persistence.
36
+ * @example
37
+ * ```ts
38
+ * import { createThemeStore, memoryStorageAdapter } from 'resonare'
39
+ *
40
+ * const store = createThemeStore(
41
+ * { colorMode: { options: ['light', 'dark'] },
42
+ * { storage: memoryStorageAdapter({ key: 'app' }) },
43
+ * )
44
+ * ```
45
+ */
18
46
  declare const memoryStorageAdapter: StorageAdapterCreator<{
19
47
  key: string;
20
48
  }>;
@@ -80,14 +108,39 @@ declare class ThemeStore<T extends ThemeStoreConfig> {
80
108
  restore: () => void;
81
109
  subscribe: (callback: Listener<T>) => (() => void);
82
110
  sync: () => (() => void) | undefined;
111
+ /** Clears subscribers and aborts media-query listeners tied to this store instance. */
83
112
  destroy: () => void;
84
113
  }
85
114
  declare function createThemeStore<T extends ThemeStoreConfig>(config: T, options?: ThemeStoreOptions<T>): ThemeStore<T>;
86
- declare function createInlineThemeScript(configs: Array<{
87
- key?: string;
115
+ type ThemeScriptParameter = {
116
+ /** `localStorage` key; defaults to the 'resonare'. */key?: string;
88
117
  config: ThemeStoreConfig;
89
118
  handler: Listener<ThemeStoreConfig>;
90
- }>): string;
119
+ };
120
+ /**
121
+ * Creates an IIFE script string that reads persisted themes from `localStorage` and runs your handlers immediately.
122
+ *
123
+ * Useful for avoiding flash of incorrect styles.
124
+ * @example
125
+ * ```tsx
126
+ * import { createInlineThemeScript } from 'resonare'
127
+ *
128
+ * const inlineScript = createInlineThemeScript([
129
+ * {
130
+ * key: 'my-app',
131
+ * config: {
132
+ * colorMode: { options: ['light', 'dark'] },
133
+ * },
134
+ * handler: ({ resolvedThemes }) => {
135
+ * document.documentElement.dataset.colorMode = String(
136
+ * resolvedThemes.colorMode,
137
+ * )
138
+ * },
139
+ * },
140
+ * ])
141
+ * ```
142
+ */
143
+ declare function createInlineThemeScript(themeScriptParameters: Array<ThemeScriptParameter>): string;
91
144
  //#endregion
92
- export { StorageAdapter, StorageAdapterCreate, StorageAdapterCreator, ThemeAndOptions, type ThemeStore, ThemeStoreConfig, Themes, createInlineThemeScript, createThemeStore, getDefaultThemes, getThemesAndOptions, localStorageAdapter, memoryStorageAdapter };
145
+ export { StorageAdapter, StorageAdapterCreate, StorageAdapterCreator, ThemeScriptParameter, type ThemeStore, ThemeStoreConfig, Themes, createInlineThemeScript, createThemeStore, getDefaultThemes, getThemesAndOptions, localStorageAdapter, memoryStorageAdapter };
93
146
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/storage.ts","../src/index.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/storage.ts","../src/index.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var e=`resonare-monorepo`;const t=({key:e,type:t=`localStorage`})=>({abortController:n})=>({get:()=>JSON.parse(window[t].getItem(e)||`null`),set:n=>{window[t].setItem(e,JSON.stringify(n))},watch:r=>{let i=new AbortController;return window.addEventListener(`storage`,n=>{n.storageArea===window[t]&&n.key===e&&r(JSON.parse(n.newValue))},{signal:AbortSignal.any([n.signal,i.signal])}),()=>{i.abort()}}}),n=({key:t})=>({abortController:n})=>{let r=new Map,i=new BroadcastChannel(e);return{get:()=>r.get(t)||null,set:e=>{r.set(t,e)},broadcast:e=>{i.postMessage({key:t,value:e})},watch:e=>{let r=new AbortController;return i.addEventListener(`message`,n=>{n.data.key===t&&e(n.data.value)},{signal:AbortSignal.any([n.signal,r.signal])}),()=>{r.abort()}}}};function r(e){return Object.entries(e).map(([e,t])=>[e,(t.options||[]).map(e=>typeof e==`object`?e.value:e)])}function i(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t.initialValue??(typeof t.options[0]==`object`?t.options[0].value:t.options[0])]))}var a=class{#e;#t;#n;#r;#i;#a=new Set;#o;#s=new AbortController;constructor(n,{initialState:r={},storage:a=t({key:e})}={}){let o={...r.systemOptions};this.#n=Object.fromEntries(Object.entries(n).map(([e,t])=>{let n=(t.options||[]).map(t=>typeof t==`object`?(t.media&&!Object.hasOwn(o,e)&&(o[e]=[t.media[1],t.media[2]]),[String(t.value),t]):[String(t),{value:t}]);return[e,Object.fromEntries(n)]})),this.#r=o,this.#e=i(n),this.#t={...this.#e,...r.themes},this.#i=a?.({abortController:this.#s})??null,this.#o={}}getThemes=()=>this.#t;getResolvedThemes=()=>this.#l();setThemes=e=>{let t=typeof e==`function`?e(this.#t):e;this.#c({...this.#t,...t});let n=this.toPersist();this.#i&&(this.#i.set(n),this.#i.broadcast?.(n))};updateSystemOption=(e,[t,n])=>{this.#r[e]=[t,n],this.setThemes({...this.#t})};toPersist=()=>({themes:this.#t,systemOptions:this.#r});restore=()=>{let e=this.#i?.get();if(!e){this.#c({...this.#e});return}this.#r={...this.#r,...e.systemOptions},this.#c({...this.#e,...e.themes})};subscribe=e=>(this.#a.add(e),()=>{this.#a.delete(e)});sync=()=>{if(this.#i?.watch)return this.#i.watch(e=>{this.#r=e.systemOptions,this.#c(e.themes)})};destroy=()=>{this.#a.clear(),this.#s.abort()};#c=e=>{this.#t=e,this.#d()};#l=()=>Object.fromEntries(Object.entries(this.#t).map(([e,t])=>{let n=this.#n[e]?.[t];return[e,n?this.#u({themeKey:e,option:n}):t]}));#u=({themeKey:t,option:n})=>{if(!n.media)return n.value;if(!(typeof window<`u`&&window.document!==void 0&&window.document.createElement!==void 0))return console.warn(`[${e}] Option with key "media" cannot be resolved in server environment.`),n.value;let[r]=n.media;if(!this.#o[r]){let e=window.matchMedia(r);this.#o[r]=e,e.addEventListener(`change`,()=>{this.#t[t]===n.value&&this.#c({...this.#t})},{signal:this.#s.signal})}let[i,a]=this.#r[t];return this.#o[r].matches?i:a};#d=()=>{for(let e of this.#a)e({themes:this.#t,resolvedThemes:this.#l()})}};function o(e,t={}){return new a(e,t)}const s=(({key:e,config:t})=>{let n=JSON.parse(localStorage.getItem(e)||`{"themes":{}}`).themes;return Object.entries(t).reduce((e,[t,r])=>{let i=r.options,a=i?.[0],o=n[t]??r.initialValue??(typeof a==`object`?a.value:a);if(e.themes[t]=o,i){let n=i.find(e=>typeof e==`object`&&!!e.media&&e.value===o);if(n){let[r,i,a]=n.media;e.resolvedThemes[t]=matchMedia(r).matches?i:a}else e.resolvedThemes[t]=o}else e.resolvedThemes[t]=o;return e},{themes:{},resolvedThemes:{}})}).toString();function c(t){return`(()=>{var r=${s};[${t.map(({key:t=e,config:n,handler:r})=>`{key:${JSON.stringify(t)},config:${JSON.stringify(n)},h:${r.toString()}}`).join(`,`)}].forEach((c)=>c.h(r(c)))})()`}export{c as createInlineThemeScript,o as createThemeStore,i as getDefaultThemes,r as getThemesAndOptions,t as localStorageAdapter,n as memoryStorageAdapter};
1
+ var e=`resonare`;const t=({key:e,type:t=`localStorage`})=>({abortController:n})=>({get:()=>JSON.parse(window[t].getItem(e)||`null`),set:n=>{window[t].setItem(e,JSON.stringify(n))},watch:r=>{let i=new AbortController;return window.addEventListener(`storage`,n=>{n.storageArea===window[t]&&n.key===e&&r(JSON.parse(n.newValue))},{signal:AbortSignal.any([n.signal,i.signal])}),()=>{i.abort()}}}),n=({key:t})=>({abortController:n})=>{let r=new Map,i=new BroadcastChannel(e);return{get:()=>r.get(t)||null,set:e=>{r.set(t,e)},broadcast:e=>{i.postMessage({key:t,value:e})},watch:e=>{let r=new AbortController;return i.addEventListener(`message`,n=>{n.data.key===t&&e(n.data.value)},{signal:AbortSignal.any([n.signal,r.signal])}),()=>{r.abort()}}}};function r(e){return Object.entries(e).map(([e,t])=>[e,(t.options||[]).map(e=>typeof e==`object`?e.value:e)])}function i(e){return Object.fromEntries(Object.entries(e).map(([e,t])=>[e,t.initialValue??(typeof t.options[0]==`object`?t.options[0].value:t.options[0])]))}var a=class{#e;#t;#n;#r;#i;#a=new Set;#o;#s=new AbortController;constructor(n,{initialState:r={},storage:a=t({key:e})}={}){let o={...r.systemOptions},s=Object.fromEntries(Object.entries(n).map(([e,t])=>{let n=(t.options||[]).map(t=>typeof t==`object`?(t.media&&!Object.hasOwn(o,e)&&(o[e]=[t.media[1],t.media[2]]),[String(t.value),t]):[String(t),{value:t}]);return[e,Object.fromEntries(n)]}));this.#n=s,this.#r=o,this.#e=i(n),this.#t={...this.#e,...r.themes},this.#i=a?.({abortController:this.#s})??null,this.#o={}}getThemes=()=>this.#t;getResolvedThemes=()=>this.#l();setThemes=e=>{let t=typeof e==`function`?e(this.#t):e;this.#c({...this.#t,...t});let n=this.toPersist();this.#i&&(this.#i.set(n),this.#i.broadcast?.(n))};updateSystemOption=(e,[t,n])=>{this.#r[e]=[t,n],this.setThemes({...this.#t})};toPersist=()=>({themes:this.#t,systemOptions:this.#r});restore=()=>{let e=this.#i?.get();if(!e){this.#c({...this.#e});return}this.#r={...this.#r,...e.systemOptions},this.#c({...this.#e,...e.themes})};subscribe=e=>(this.#a.add(e),()=>{this.#a.delete(e)});sync=()=>{if(this.#i?.watch)return this.#i.watch(e=>{this.#r=e.systemOptions,this.#c(e.themes)})};destroy=()=>{this.#a.clear(),this.#s.abort()};#c=e=>{this.#t=e,this.#d()};#l=()=>Object.fromEntries(Object.entries(this.#t).map(([e,t])=>{let n=this.#n[e]?.[t];return[e,n?this.#u({themeKey:e,option:n}):t]}));#u=({themeKey:t,option:n})=>{if(!n.media)return n.value;if(!(typeof window<`u`&&window.document!==void 0&&window.document.createElement!==void 0))return process.env.NODE_ENV!==`production`&&console.warn(`[${e}] Option with key "media" cannot be resolved in server environment.`),n.value;let[r]=n.media;if(!this.#o[r]){let e=window.matchMedia(r);this.#o[r]=e,e.addEventListener(`change`,()=>{this.#t[t]===n.value&&this.#c({...this.#t})},{signal:this.#s.signal})}let[i,a]=this.#r[t];return this.#o[r].matches?i:a};#d=()=>{for(let e of this.#a)e({themes:this.#t,resolvedThemes:this.#l()})}};function o(e,t={}){return new a(e,t)}const s=e=>{e.forEach(([e,t,n])=>{let r=JSON.parse(localStorage.getItem(e)||`{}`).themes??{};n(Object.entries(t).reduce((e,[t,n])=>{let i=n.options,a=i?.[0],o=r[t]??n.initialValue??(typeof a==`object`?a.value:a);e.resolvedThemes[t]=e.themes[t]=o;let s=i?.find(e=>typeof e==`object`&&!!e.media&&e.value===o);if(s){let[n,r,i]=s.media;e.resolvedThemes[t]=matchMedia(n).matches?r:i}return e},{themes:{},resolvedThemes:{}}))})};function c(t){return`(${s})([${t.map(({key:t=e,config:n,handler:r})=>`[${JSON.stringify(t)},${JSON.stringify(n)},${r}]`).join(`,`)}])`}export{c as createInlineThemeScript,o as createThemeStore,i as getDefaultThemes,r as getThemesAndOptions,t as localStorageAdapter,n as memoryStorageAdapter};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["name","PACKAGE_NAME","type","StorageAdapter","get","set","value","broadcast","watch","cb","StorageAdapterCreate","abortController","AbortController","StorageAdapterCreator","options","Options","localStorageAdapter","key","JSON","parse","window","getItem","setItem","stringify","controller","addEventListener","e","storageArea","newValue","signal","AbortSignal","any","abort","memoryStorageAdapter","storage","Map","channel","BroadcastChannel","postMessage","data","name","PACKAGE_NAME","type","localStorageAdapter","StorageAdapter","StorageAdapterCreate","ThemeValue","ThemeOption","value","T","media","ThemeConfig","options","ReadonlyArray","initialValue","ThemeStoreConfig","Record","KeyedThemeStoreConfig","Themes","K","U","Listener","themes","resolvedThemes","ThemeKeysWithSystemOption","NonSystemOptionValues","SystemOptions","PersistedState","Partial","systemOptions","ThemeStoreOptions","initialState","storage","ThemeAndOptions","Array","getThemesAndOptions","config","Object","entries","map","themeKey","themeConfig","option","getDefaultThemes","fromEntries","ThemeStore","defaultThemes","currentThemes","keyedConfig","listeners","Set","mediaQueryCache","MediaQueryList","abortController","AbortController","constructor","key","hasOwn","String","getThemes","getResolvedThemes","resolveThemes","setThemes","updatedThemes","setThemesAndNotify","stateToPersist","toPersist","set","broadcast","updateSystemOption","ifMatch","ifNotMatch","restore","persistedState","get","subscribe","callback","add","delete","sync","watch","destroy","clear","abort","#setThemesAndNotify","notify","#resolveThemes","optionKey","resolveThemeOption","#resolveThemeOption","window","document","createElement","console","warn","mediaQuery","mediaQueryList","matchMedia","addEventListener","signal","matches","#notify","listener","createThemeStore","restoreThemesString","persistedThemes","JSON","parse","localStorage","getItem","reduce","acc","firstOption","mediaOption","find","Required","toString","createInlineThemeScript","configs","handler","serializedConfigs","stringify","join"],"sources":["../../package.json","../src/storage.ts","../src/index.ts"],"sourcesContent":["","import { name as PACKAGE_NAME } from '../../package.json' with { type: 'json' }\n\nexport type StorageAdapter = {\n\tget: () => object | null\n\tset: (value: object) => void\n\t// del: () => void\n\tbroadcast?: (value: object) => void\n\twatch?: (cb: (value: object) => void) => () => void\n}\n\nexport type StorageAdapterCreate = ({\n\tabortController,\n}: {\n\tabortController: AbortController\n}) => StorageAdapter\n\nexport type StorageAdapterCreator<Options> = (\n\toptions: Options,\n) => StorageAdapterCreate\n\nexport const localStorageAdapter: StorageAdapterCreator<{\n\tkey: string\n\ttype?: 'localStorage' | 'sessionStorage'\n}> = ({ key, type = 'localStorage' }) => {\n\treturn ({ abortController }) => {\n\t\treturn {\n\t\t\tget: () => {\n\t\t\t\treturn JSON.parse(window[type].getItem(key) || 'null')\n\t\t\t},\n\n\t\t\tset: (value: object) => {\n\t\t\t\twindow[type].setItem(key, JSON.stringify(value))\n\t\t\t},\n\n\t\t\t// del: () => {\n\t\t\t// \twindow[type].removeItem(key)\n\t\t\t// },\n\n\t\t\twatch: (cb) => {\n\t\t\t\tconst controller = new AbortController()\n\n\t\t\t\twindow.addEventListener(\n\t\t\t\t\t'storage',\n\t\t\t\t\t(e) => {\n\t\t\t\t\t\tif (e.storageArea !== window[type]) return\n\n\t\t\t\t\t\tif (e.key !== key) return\n\n\t\t\t\t\t\tcb(JSON.parse(e.newValue!))\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tsignal: AbortSignal.any([\n\t\t\t\t\t\t\tabortController.signal,\n\t\t\t\t\t\t\tcontroller.signal,\n\t\t\t\t\t\t]),\n\t\t\t\t\t},\n\t\t\t\t)\n\n\t\t\t\treturn () => {\n\t\t\t\t\tcontroller.abort()\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t}\n}\n\nexport const memoryStorageAdapter: StorageAdapterCreator<{\n\tkey: string\n}> = ({ key }) => {\n\treturn ({ abortController }) => {\n\t\tconst storage = new Map<string, object>()\n\t\tconst channel = new BroadcastChannel(PACKAGE_NAME)\n\n\t\treturn {\n\t\t\tget: () => {\n\t\t\t\treturn storage.get(key) || null\n\t\t\t},\n\n\t\t\tset: (value: object) => {\n\t\t\t\tstorage.set(key, value)\n\t\t\t},\n\n\t\t\t// del: () => {\n\t\t\t// \tstorage.delete(key)\n\t\t\t// },\n\n\t\t\tbroadcast: (value: object) => {\n\t\t\t\tchannel.postMessage({ key, value })\n\t\t\t},\n\n\t\t\twatch: (cb) => {\n\t\t\t\tconst controller = new AbortController()\n\n\t\t\t\tchannel.addEventListener(\n\t\t\t\t\t'message',\n\t\t\t\t\t(e) => {\n\t\t\t\t\t\tif (e.data.key !== key) return\n\n\t\t\t\t\t\tcb(e.data.value)\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tsignal: AbortSignal.any([\n\t\t\t\t\t\t\tabortController.signal,\n\t\t\t\t\t\t\tcontroller.signal,\n\t\t\t\t\t\t]),\n\t\t\t\t\t},\n\t\t\t\t)\n\n\t\t\t\treturn () => {\n\t\t\t\t\tcontroller.abort()\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t}\n}\n","import { name as PACKAGE_NAME } from '../../package.json' with { type: 'json' }\nimport {\n\tlocalStorageAdapter,\n\ttype StorageAdapter,\n\ttype StorageAdapterCreate,\n} from './storage'\n\nexport * from './storage'\n\ntype ThemeValue = string | number | boolean\n\ntype ThemeOption<T extends ThemeValue = string> = {\n\tvalue: T\n\tmedia?: [string, T, T]\n}\n\ntype ThemeConfig<T extends ThemeValue = string> =\n\t| {\n\t\t\toptions: ReadonlyArray<T | ThemeOption<T>>\n\t\t\tinitialValue?: T\n\t }\n\t| { initialValue: T; options?: never }\n\nexport type ThemeStoreConfig = Record<\n\tstring,\n\tThemeConfig<string> | ThemeConfig<number> | ThemeConfig<boolean>\n>\n\n// { [themeKey]: { [optionKey]: ThemeOption } }\ntype KeyedThemeStoreConfig<T extends ThemeStoreConfig> = {\n\t[K in keyof T]: Record<string, ThemeOption>\n}\n\nexport type Themes<T extends ThemeStoreConfig> = {\n\t[K in keyof T]: T[K] extends { options: ReadonlyArray<infer U> }\n\t\t? U extends ThemeOption\n\t\t\t? U['value']\n\t\t\t: U\n\t\t: T[K] extends { initialValue: infer U }\n\t\t\t? U extends string\n\t\t\t\t? string\n\t\t\t\t: U extends number\n\t\t\t\t\t? number\n\t\t\t\t\t: boolean\n\t\t\t: never\n}\n\ntype Listener<T extends ThemeStoreConfig> = (value: {\n\tthemes: Themes<T>\n\tresolvedThemes: Themes<T>\n}) => void\n\ntype ThemeKeysWithSystemOption<T extends ThemeStoreConfig> = {\n\t[K in keyof T]: T[K] extends { options: ReadonlyArray<infer U> }\n\t\t? U extends { media: ReadonlyArray<unknown> }\n\t\t\t? K\n\t\t\t: never\n\t\t: never\n}[keyof T]\n\ntype NonSystemOptionValues<\n\tT extends ThemeStoreConfig,\n\tK extends keyof T,\n> = T[K] extends { options: ReadonlyArray<infer U> }\n\t? U extends ThemeValue\n\t\t? U\n\t\t: U extends ThemeOption\n\t\t\t? U extends { media: [string, string, string] }\n\t\t\t\t? never\n\t\t\t\t: U['value']\n\t\t\t: never\n\t: never\n\ntype SystemOptions<T extends ThemeStoreConfig> = {\n\t[K in ThemeKeysWithSystemOption<T>]?: [\n\t\tNonSystemOptionValues<T, K>,\n\t\tNonSystemOptionValues<T, K>,\n\t]\n}\n\ntype PersistedState<T extends ThemeStoreConfig> = {\n\tthemes: Partial<Themes<T>>\n\tsystemOptions: SystemOptions<T>\n}\n\ntype ThemeStoreOptions<T extends ThemeStoreConfig> = {\n\tinitialState?: Partial<PersistedState<T>>\n\tstorage?: StorageAdapterCreate | null\n}\n\nexport type ThemeAndOptions<T extends ThemeStoreConfig> = Array<\n\t{\n\t\t[K in keyof T]: [\n\t\t\tK,\n\t\t\tArray<\n\t\t\t\tT[K] extends { options: ReadonlyArray<infer U> }\n\t\t\t\t\t? U extends ThemeOption\n\t\t\t\t\t\t? U['value']\n\t\t\t\t\t\t: U\n\t\t\t\t\t: never\n\t\t\t>,\n\t\t]\n\t}[keyof T]\n>\n\nexport function getThemesAndOptions<T extends ThemeStoreConfig>(config: T) {\n\treturn Object.entries(config).map(([themeKey, themeConfig]) => {\n\t\treturn [\n\t\t\tthemeKey,\n\t\t\t(themeConfig.options || []).map((option) =>\n\t\t\t\ttypeof option === 'object' ? option.value : option,\n\t\t\t),\n\t\t]\n\t}) as ThemeAndOptions<T>\n}\n\nexport function getDefaultThemes<T extends ThemeStoreConfig>(config: T) {\n\treturn Object.fromEntries(\n\t\tObject.entries(config).map(([themeKey, themeConfig]) => {\n\t\t\treturn [\n\t\t\t\tthemeKey,\n\t\t\t\tthemeConfig.initialValue ??\n\t\t\t\t\t(typeof themeConfig.options[0] === 'object'\n\t\t\t\t\t\t? themeConfig.options[0].value\n\t\t\t\t\t\t: themeConfig.options[0]),\n\t\t\t]\n\t\t}),\n\t) as Themes<T>\n}\n\nclass ThemeStore<T extends ThemeStoreConfig> {\n\t#defaultThemes: Themes<T>\n\t#currentThemes: Themes<T>\n\n\t#keyedConfig: KeyedThemeStoreConfig<T>\n\n\t#systemOptions: SystemOptions<T>\n\n\t#storage: StorageAdapter | null\n\n\t#listeners: Set<Listener<T>> = new Set<Listener<T>>()\n\n\t#mediaQueryCache: Record<string, MediaQueryList>\n\n\t#abortController = new AbortController()\n\n\tconstructor(\n\t\tconfig: T,\n\t\t{\n\t\t\tinitialState = {},\n\t\t\tstorage = localStorageAdapter({ key: PACKAGE_NAME }),\n\t\t}: ThemeStoreOptions<T> = {},\n\t) {\n\t\tconst systemOptions: Record<string, [ThemeValue, ThemeValue]> = {\n\t\t\t...initialState.systemOptions,\n\t\t}\n\n\t\tconst keyedConfig = Object.fromEntries(\n\t\t\tObject.entries(config).map(([themeKey, themeConfig]) => {\n\t\t\t\tconst entries = (themeConfig.options || []).map((option) => {\n\t\t\t\t\tif (typeof option === 'object') {\n\t\t\t\t\t\tif (option.media && !Object.hasOwn(systemOptions, themeKey)) {\n\t\t\t\t\t\t\tsystemOptions[themeKey] = [option.media[1], option.media[2]]\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn [String(option.value), option]\n\t\t\t\t\t}\n\n\t\t\t\t\treturn [String(option), { value: option }]\n\t\t\t\t})\n\t\t\t\treturn [themeKey, Object.fromEntries(entries)]\n\t\t\t}),\n\t\t) as KeyedThemeStoreConfig<T>\n\n\t\tthis.#keyedConfig = keyedConfig\n\n\t\tthis.#systemOptions = systemOptions as SystemOptions<T>\n\n\t\tthis.#defaultThemes = getDefaultThemes(config)\n\n\t\tthis.#currentThemes = { ...this.#defaultThemes, ...initialState.themes }\n\n\t\tthis.#storage =\n\t\t\tstorage?.({\n\t\t\t\tabortController: this.#abortController,\n\t\t\t}) ?? null\n\n\t\tthis.#mediaQueryCache = {}\n\t}\n\n\tgetThemes = (): Themes<T> => {\n\t\treturn this.#currentThemes\n\t}\n\n\tgetResolvedThemes = (): Themes<T> => {\n\t\treturn this.#resolveThemes()\n\t}\n\n\tsetThemes = (\n\t\tthemes:\n\t\t\t| Partial<Themes<T>>\n\t\t\t| ((currentThemes: Themes<T>) => Partial<Themes<T>>),\n\t): void => {\n\t\tconst updatedThemes =\n\t\t\ttypeof themes === 'function' ? themes(this.#currentThemes) : themes\n\n\t\tthis.#setThemesAndNotify({ ...this.#currentThemes, ...updatedThemes })\n\n\t\tconst stateToPersist = this.toPersist()\n\n\t\tif (this.#storage) {\n\t\t\tthis.#storage.set(stateToPersist)\n\t\t\tthis.#storage.broadcast?.(stateToPersist)\n\t\t}\n\t}\n\n\tupdateSystemOption = <K extends ThemeKeysWithSystemOption<T>>(\n\t\tthemeKey: K,\n\t\t[ifMatch, ifNotMatch]: [\n\t\t\tNonSystemOptionValues<T, K>,\n\t\t\tNonSystemOptionValues<T, K>,\n\t\t],\n\t): void => {\n\t\tthis.#systemOptions[themeKey] = [ifMatch, ifNotMatch]\n\n\t\tthis.setThemes({ ...this.#currentThemes })\n\t}\n\n\ttoPersist = (): PersistedState<T> => {\n\t\treturn {\n\t\t\tthemes: this.#currentThemes,\n\t\t\tsystemOptions: this.#systemOptions,\n\t\t}\n\t}\n\n\trestore = (): void => {\n\t\tconst persistedState = this.#storage?.get()\n\n\t\tif (!persistedState) {\n\t\t\tthis.#setThemesAndNotify({ ...this.#defaultThemes })\n\t\t\treturn\n\t\t}\n\n\t\tthis.#systemOptions = {\n\t\t\t...this.#systemOptions,\n\t\t\t...persistedState.systemOptions,\n\t\t}\n\n\t\tthis.#setThemesAndNotify({\n\t\t\t...this.#defaultThemes,\n\t\t\t...persistedState.themes,\n\t\t})\n\t}\n\n\tsubscribe = (callback: Listener<T>): (() => void) => {\n\t\tthis.#listeners.add(callback)\n\n\t\treturn () => {\n\t\t\tthis.#listeners.delete(callback)\n\t\t}\n\t}\n\n\tsync = (): (() => void) | undefined => {\n\t\tif (!this.#storage?.watch) return\n\n\t\treturn this.#storage.watch((persistedState) => {\n\t\t\tthis.#systemOptions = (persistedState as PersistedState<T>).systemOptions\n\n\t\t\tthis.#setThemesAndNotify((persistedState as PersistedState<T>).themes)\n\t\t})\n\t}\n\n\tdestroy = (): void => {\n\t\tthis.#listeners.clear()\n\t\tthis.#abortController.abort()\n\t}\n\n\t#setThemesAndNotify = (themes: Themes<T>): void => {\n\t\tthis.#currentThemes = themes\n\t\tthis.#notify()\n\t}\n\n\t#resolveThemes = (): Themes<T> => {\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(this.#currentThemes).map(([themeKey, optionKey]) => {\n\t\t\t\tconst option = this.#keyedConfig[themeKey]?.[optionKey]\n\n\t\t\t\treturn [\n\t\t\t\t\tthemeKey,\n\t\t\t\t\toption ? this.#resolveThemeOption({ themeKey, option }) : optionKey,\n\t\t\t\t]\n\t\t\t}),\n\t\t) as Themes<T>\n\t}\n\n\t#resolveThemeOption = ({\n\t\tthemeKey,\n\t\toption,\n\t}: {\n\t\tthemeKey: string\n\t\toption: ThemeOption\n\t}): string => {\n\t\tif (!option.media) return option.value\n\n\t\tif (\n\t\t\t!(\n\t\t\t\ttypeof window !== 'undefined' &&\n\t\t\t\ttypeof window.document !== 'undefined' &&\n\t\t\t\ttypeof window.document.createElement !== 'undefined'\n\t\t\t)\n\t\t) {\n\t\t\tconsole.warn(\n\t\t\t\t`[${PACKAGE_NAME}] Option with key \"media\" cannot be resolved in server environment.`,\n\t\t\t)\n\n\t\t\treturn option.value\n\t\t}\n\n\t\tconst [mediaQuery] = option.media\n\n\t\tif (!this.#mediaQueryCache[mediaQuery]) {\n\t\t\tconst mediaQueryList = window.matchMedia(mediaQuery)\n\n\t\t\tthis.#mediaQueryCache[mediaQuery] = mediaQueryList\n\n\t\t\tmediaQueryList.addEventListener(\n\t\t\t\t'change',\n\t\t\t\t() => {\n\t\t\t\t\tif (this.#currentThemes[themeKey] === option.value) {\n\t\t\t\t\t\tthis.#setThemesAndNotify({ ...this.#currentThemes })\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{ signal: this.#abortController.signal },\n\t\t\t)\n\t\t}\n\n\t\tconst [ifMatch, ifNotMatch] = this.#systemOptions[themeKey]!\n\n\t\treturn this.#mediaQueryCache[mediaQuery].matches ? ifMatch : ifNotMatch\n\t}\n\n\t#notify = (): void => {\n\t\tfor (const listener of this.#listeners) {\n\t\t\tlistener({\n\t\t\t\tthemes: this.#currentThemes,\n\t\t\t\tresolvedThemes: this.#resolveThemes(),\n\t\t\t})\n\t\t}\n\t}\n}\n\nexport type { ThemeStore }\n\nexport function createThemeStore<T extends ThemeStoreConfig>(\n\tconfig: T,\n\toptions: ThemeStoreOptions<T> = {},\n): ThemeStore<T> {\n\treturn new ThemeStore<T>(config, options)\n}\n\nconst restoreThemesString = (({\n\tkey,\n\tconfig,\n}: {\n\tkey: string\n\tconfig: ThemeStoreConfig\n}) => {\n\tconst persistedThemes = JSON.parse(\n\t\tlocalStorage.getItem(key) || '{\"themes\":{}}',\n\t).themes\n\n\treturn Object.entries(config).reduce<{\n\t\tthemes: Record<string, ThemeValue>\n\t\tresolvedThemes: Record<string, ThemeValue>\n\t}>(\n\t\t(acc, [themeKey, themeConfig]) => {\n\t\t\tconst options = themeConfig.options\n\n\t\t\tconst firstOption = options?.[0]\n\n\t\t\tconst initialValue =\n\t\t\t\tpersistedThemes[themeKey] ??\n\t\t\t\tthemeConfig.initialValue ??\n\t\t\t\t(typeof firstOption === 'object' ? firstOption.value : firstOption!)\n\n\t\t\tacc.themes[themeKey] = initialValue\n\n\t\t\tif (options) {\n\t\t\t\tconst mediaOption = options.find(\n\t\t\t\t\t(option): option is Required<ThemeOption> =>\n\t\t\t\t\t\ttypeof option === 'object' &&\n\t\t\t\t\t\t!!option.media &&\n\t\t\t\t\t\toption.value === initialValue,\n\t\t\t\t)\n\n\t\t\t\tif (mediaOption) {\n\t\t\t\t\tconst [mediaQuery, ifMatch, ifNotMatch] = mediaOption.media\n\n\t\t\t\t\tacc.resolvedThemes[themeKey] = matchMedia(mediaQuery).matches\n\t\t\t\t\t\t? ifMatch\n\t\t\t\t\t\t: ifNotMatch\n\t\t\t\t} else {\n\t\t\t\t\tacc.resolvedThemes[themeKey] = initialValue\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tacc.resolvedThemes[themeKey] = initialValue\n\t\t\t}\n\n\t\t\treturn acc\n\t\t},\n\t\t{\n\t\t\tthemes: {},\n\t\t\tresolvedThemes: {},\n\t\t},\n\t)\n}).toString()\n\nexport function createInlineThemeScript(\n\tconfigs: Array<{\n\t\tkey?: string\n\t\tconfig: ThemeStoreConfig\n\t\thandler: Listener<ThemeStoreConfig>\n\t}>,\n) {\n\tconst serializedConfigs = configs.map(\n\t\t({ key = PACKAGE_NAME, config, handler }) =>\n\t\t\t`{key:${JSON.stringify(key)},config:${JSON.stringify(config)},h:${handler.toString()}}`,\n\t)\n\n\treturn `(()=>{var r=${restoreThemesString};[${serializedConfigs.join(',')}].forEach((c)=>c.h(r(c)))})()`\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["name","PACKAGE_NAME","type","StorageAdapter","get","set","value","broadcast","watch","cb","StorageAdapterCreate","abortController","AbortController","StorageAdapterCreator","options","Options","localStorageAdapter","key","JSON","parse","window","getItem","setItem","stringify","controller","addEventListener","e","storageArea","newValue","signal","AbortSignal","any","abort","memoryStorageAdapter","storage","Map","channel","BroadcastChannel","postMessage","data","name","PACKAGE_NAME","type","localStorageAdapter","StorageAdapter","StorageAdapterCreate","ThemeValue","ThemeOption","value","T","media","ThemeConfig","options","ReadonlyArray","initialValue","ThemeStoreConfig","Record","KeyedThemeStoreConfig","Themes","K","U","Listener","themes","resolvedThemes","ThemeKeysWithSystemOption","NonSystemOptionValues","SystemOptions","PersistedState","Partial","systemOptions","ThemeStoreOptions","initialState","storage","ThemeAndOptions","Array","getThemesAndOptions","config","Object","entries","map","themeKey","themeConfig","option","getDefaultThemes","fromEntries","ThemeStore","defaultThemes","currentThemes","keyedConfig","listeners","Set","mediaQueryCache","MediaQueryList","abortController","AbortController","constructor","key","hasOwn","String","getThemes","getResolvedThemes","resolveThemes","setThemes","updatedThemes","setThemesAndNotify","stateToPersist","toPersist","set","broadcast","updateSystemOption","ifMatch","ifNotMatch","restore","persistedState","get","subscribe","callback","add","delete","sync","watch","destroy","clear","abort","#setThemesAndNotify","notify","#resolveThemes","optionKey","resolveThemeOption","#resolveThemeOption","window","document","createElement","PROD","console","warn","mediaQuery","mediaQueryList","matchMedia","addEventListener","signal","matches","#notify","listener","createThemeStore","restoreScript","params","forEach","handler","persistedThemes","JSON","parse","localStorage","getItem","reduce","acc","firstOption","currentValue","maybeSystemOption","find","Required","ThemeScriptParameter","createInlineThemeScript","themeScriptParameters","serializedThemeScriptParameters","stringify","join"],"sources":["../package.json","../src/storage.ts","../src/index.ts"],"sourcesContent":["","import { name as PACKAGE_NAME } from '../package.json' with { type: 'json' }\n\n/**\n * Pluggable persistence for `createThemeStore`.\n */\nexport type StorageAdapter = {\n\tget: () => object | null\n\tset: (value: object) => void\n\t/** Optional: notify other tabs/contexts after local `set` operation. */\n\tbroadcast?: (value: object) => void\n\t/** Optional: subscribes to other tabs/contexts' updates, returns `unsubscribe` function. */\n\twatch?: (cb: (value: object) => void) => () => void\n}\n\nexport type StorageAdapterCreate = ({\n\tabortController,\n}: {\n\tabortController: AbortController\n}) => StorageAdapter\n\nexport type StorageAdapterCreator<Options> = (\n\toptions: Options,\n) => StorageAdapterCreate\n\n/**\n * Persists theme store in `localStorage` or `sessionStorage`.\n * @example\n * ```ts\n * import { createThemeStore, localStorageAdapter } from 'resonare'\n *\n * const store = createThemeStore(\n * { colorMode: { options: ['light', 'dark'] },\n * { storage: localStorageAdapter({ key: 'app', type: 'localStorage' }) },\n * )\n * ```\n */\nexport const localStorageAdapter: StorageAdapterCreator<{\n\tkey: string\n\ttype?: 'localStorage' | 'sessionStorage'\n}> = ({ key, type = 'localStorage' }) => {\n\treturn ({ abortController }) => {\n\t\treturn {\n\t\t\tget: () => {\n\t\t\t\treturn JSON.parse(window[type].getItem(key) || 'null')\n\t\t\t},\n\n\t\t\tset: (value: object) => {\n\t\t\t\twindow[type].setItem(key, JSON.stringify(value))\n\t\t\t},\n\n\t\t\twatch: (cb) => {\n\t\t\t\tconst controller = new AbortController()\n\n\t\t\t\twindow.addEventListener(\n\t\t\t\t\t'storage',\n\t\t\t\t\t(e) => {\n\t\t\t\t\t\tif (e.storageArea !== window[type]) return\n\n\t\t\t\t\t\tif (e.key !== key) return\n\n\t\t\t\t\t\tcb(JSON.parse(e.newValue!))\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tsignal: AbortSignal.any([\n\t\t\t\t\t\t\tabortController.signal,\n\t\t\t\t\t\t\tcontroller.signal,\n\t\t\t\t\t\t]),\n\t\t\t\t\t},\n\t\t\t\t)\n\n\t\t\t\treturn () => {\n\t\t\t\t\tcontroller.abort()\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t}\n}\n\n/**\n * In-memory persistence and sync via `BroadcastChannel`.\n * Useful with server-side persistence.\n * @example\n * ```ts\n * import { createThemeStore, memoryStorageAdapter } from 'resonare'\n *\n * const store = createThemeStore(\n * { colorMode: { options: ['light', 'dark'] },\n * { storage: memoryStorageAdapter({ key: 'app' }) },\n * )\n * ```\n */\nexport const memoryStorageAdapter: StorageAdapterCreator<{\n\tkey: string\n}> = ({ key }) => {\n\treturn ({ abortController }) => {\n\t\tconst storage = new Map<string, object>()\n\t\tconst channel = new BroadcastChannel(PACKAGE_NAME)\n\n\t\treturn {\n\t\t\tget: () => {\n\t\t\t\treturn storage.get(key) || null\n\t\t\t},\n\n\t\t\tset: (value: object) => {\n\t\t\t\tstorage.set(key, value)\n\t\t\t},\n\n\t\t\tbroadcast: (value: object) => {\n\t\t\t\tchannel.postMessage({ key, value })\n\t\t\t},\n\n\t\t\twatch: (cb) => {\n\t\t\t\tconst controller = new AbortController()\n\n\t\t\t\tchannel.addEventListener(\n\t\t\t\t\t'message',\n\t\t\t\t\t(e) => {\n\t\t\t\t\t\tif (e.data.key !== key) return\n\n\t\t\t\t\t\tcb(e.data.value)\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tsignal: AbortSignal.any([\n\t\t\t\t\t\t\tabortController.signal,\n\t\t\t\t\t\t\tcontroller.signal,\n\t\t\t\t\t\t]),\n\t\t\t\t\t},\n\t\t\t\t)\n\n\t\t\t\treturn () => {\n\t\t\t\t\tcontroller.abort()\n\t\t\t\t}\n\t\t\t},\n\t\t}\n\t}\n}\n","import { name as PACKAGE_NAME } from '../package.json' with { type: 'json' }\nimport {\n\tlocalStorageAdapter,\n\ttype StorageAdapter,\n\ttype StorageAdapterCreate,\n} from './storage'\n\nexport * from './storage'\n\ntype ThemeValue = string | number | boolean\n\ntype ThemeOption<T extends ThemeValue = string> = {\n\tvalue: T\n\tmedia?: [string, T, T]\n}\n\ntype ThemeConfig<T extends ThemeValue = string> =\n\t| {\n\t\t\toptions: ReadonlyArray<T | ThemeOption<T>>\n\t\t\tinitialValue?: T\n\t }\n\t| { initialValue: T; options?: never }\n\nexport type ThemeStoreConfig = Record<\n\tstring,\n\tThemeConfig<string> | ThemeConfig<number> | ThemeConfig<boolean>\n>\n\n// { [themeKey]: { [optionKey]: ThemeOption } }\ntype KeyedThemeStoreConfig<T extends ThemeStoreConfig> = {\n\t[K in keyof T]: Record<string, ThemeOption>\n}\n\nexport type Themes<T extends ThemeStoreConfig> = {\n\t[K in keyof T]: T[K] extends { options: ReadonlyArray<infer U> }\n\t\t? U extends ThemeOption\n\t\t\t? U['value']\n\t\t\t: U\n\t\t: T[K] extends { initialValue: infer U }\n\t\t\t? U extends string\n\t\t\t\t? string\n\t\t\t\t: U extends number\n\t\t\t\t\t? number\n\t\t\t\t\t: boolean\n\t\t\t: never\n}\n\ntype Listener<T extends ThemeStoreConfig> = (value: {\n\tthemes: Themes<T>\n\tresolvedThemes: Themes<T>\n}) => void\n\ntype ThemeKeysWithSystemOption<T extends ThemeStoreConfig> = {\n\t[K in keyof T]: T[K] extends { options: ReadonlyArray<infer U> }\n\t\t? U extends { media: ReadonlyArray<unknown> }\n\t\t\t? K\n\t\t\t: never\n\t\t: never\n}[keyof T]\n\ntype NonSystemOptionValues<\n\tT extends ThemeStoreConfig,\n\tK extends keyof T,\n> = T[K] extends { options: ReadonlyArray<infer U> }\n\t? U extends ThemeValue\n\t\t? U\n\t\t: U extends ThemeOption\n\t\t\t? U extends { media: [string, string, string] }\n\t\t\t\t? never\n\t\t\t\t: U['value']\n\t\t\t: never\n\t: never\n\ntype SystemOptions<T extends ThemeStoreConfig> = {\n\t[K in ThemeKeysWithSystemOption<T>]?: [\n\t\tNonSystemOptionValues<T, K>,\n\t\tNonSystemOptionValues<T, K>,\n\t]\n}\n\ntype PersistedState<T extends ThemeStoreConfig> = {\n\tthemes: Partial<Themes<T>>\n\tsystemOptions: SystemOptions<T>\n}\n\ntype ThemeStoreOptions<T extends ThemeStoreConfig> = {\n\tinitialState?: Partial<PersistedState<T>>\n\tstorage?: StorageAdapterCreate | null\n}\n\ntype ThemeAndOptions<T extends ThemeStoreConfig> = Array<\n\t{\n\t\t[K in keyof T]: [\n\t\t\tK,\n\t\t\tArray<\n\t\t\t\tT[K] extends { options: ReadonlyArray<infer U> }\n\t\t\t\t\t? U extends ThemeOption\n\t\t\t\t\t\t? U['value']\n\t\t\t\t\t\t: U\n\t\t\t\t\t: never\n\t\t\t>,\n\t\t]\n\t}[keyof T]\n>\n\nexport function getThemesAndOptions<T extends ThemeStoreConfig>(config: T) {\n\treturn Object.entries(config).map(([themeKey, themeConfig]) => {\n\t\treturn [\n\t\t\tthemeKey,\n\t\t\t(themeConfig.options || []).map((option) =>\n\t\t\t\ttypeof option === 'object' ? option.value : option,\n\t\t\t),\n\t\t]\n\t}) as ThemeAndOptions<T>\n}\n\nexport function getDefaultThemes<T extends ThemeStoreConfig>(config: T) {\n\treturn Object.fromEntries(\n\t\tObject.entries(config).map(([themeKey, themeConfig]) => {\n\t\t\treturn [\n\t\t\t\tthemeKey,\n\t\t\t\tthemeConfig.initialValue ??\n\t\t\t\t\t(typeof themeConfig.options[0] === 'object'\n\t\t\t\t\t\t? themeConfig.options[0].value\n\t\t\t\t\t\t: themeConfig.options[0]),\n\t\t\t]\n\t\t}),\n\t) as Themes<T>\n}\n\nclass ThemeStore<T extends ThemeStoreConfig> {\n\t#defaultThemes: Themes<T>\n\t#currentThemes: Themes<T>\n\n\t#keyedConfig: KeyedThemeStoreConfig<T>\n\n\t#systemOptions: SystemOptions<T>\n\n\t#storage: StorageAdapter | null\n\n\t#listeners: Set<Listener<T>> = new Set<Listener<T>>()\n\n\t#mediaQueryCache: Record<string, MediaQueryList>\n\n\t#abortController = new AbortController()\n\n\tconstructor(\n\t\tconfig: T,\n\t\t{\n\t\t\tinitialState = {},\n\t\t\tstorage = localStorageAdapter({ key: PACKAGE_NAME }),\n\t\t}: ThemeStoreOptions<T> = {},\n\t) {\n\t\tconst systemOptions: Record<string, [ThemeValue, ThemeValue]> = {\n\t\t\t...initialState.systemOptions,\n\t\t}\n\n\t\tconst keyedConfig = Object.fromEntries(\n\t\t\tObject.entries(config).map(([themeKey, themeConfig]) => {\n\t\t\t\tconst entries = (themeConfig.options || []).map((option) => {\n\t\t\t\t\tif (typeof option === 'object') {\n\t\t\t\t\t\tif (option.media && !Object.hasOwn(systemOptions, themeKey)) {\n\t\t\t\t\t\t\tsystemOptions[themeKey] = [option.media[1], option.media[2]]\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn [String(option.value), option]\n\t\t\t\t\t}\n\n\t\t\t\t\treturn [String(option), { value: option }]\n\t\t\t\t})\n\t\t\t\treturn [themeKey, Object.fromEntries(entries)]\n\t\t\t}),\n\t\t) as KeyedThemeStoreConfig<T>\n\n\t\tthis.#keyedConfig = keyedConfig\n\n\t\tthis.#systemOptions = systemOptions as SystemOptions<T>\n\n\t\tthis.#defaultThemes = getDefaultThemes(config)\n\n\t\tthis.#currentThemes = { ...this.#defaultThemes, ...initialState.themes }\n\n\t\tthis.#storage =\n\t\t\tstorage?.({\n\t\t\t\tabortController: this.#abortController,\n\t\t\t}) ?? null\n\n\t\tthis.#mediaQueryCache = {}\n\t}\n\n\tgetThemes = (): Themes<T> => {\n\t\treturn this.#currentThemes\n\t}\n\n\tgetResolvedThemes = (): Themes<T> => {\n\t\treturn this.#resolveThemes()\n\t}\n\n\tsetThemes = (\n\t\tthemes:\n\t\t\t| Partial<Themes<T>>\n\t\t\t| ((currentThemes: Themes<T>) => Partial<Themes<T>>),\n\t): void => {\n\t\tconst updatedThemes =\n\t\t\ttypeof themes === 'function' ? themes(this.#currentThemes) : themes\n\n\t\tthis.#setThemesAndNotify({ ...this.#currentThemes, ...updatedThemes })\n\n\t\tconst stateToPersist = this.toPersist()\n\n\t\tif (this.#storage) {\n\t\t\tthis.#storage.set(stateToPersist)\n\t\t\tthis.#storage.broadcast?.(stateToPersist)\n\t\t}\n\t}\n\n\tupdateSystemOption = <K extends ThemeKeysWithSystemOption<T>>(\n\t\tthemeKey: K,\n\t\t[ifMatch, ifNotMatch]: [\n\t\t\tNonSystemOptionValues<T, K>,\n\t\t\tNonSystemOptionValues<T, K>,\n\t\t],\n\t): void => {\n\t\tthis.#systemOptions[themeKey] = [ifMatch, ifNotMatch]\n\n\t\tthis.setThemes({ ...this.#currentThemes })\n\t}\n\n\ttoPersist = (): PersistedState<T> => {\n\t\treturn {\n\t\t\tthemes: this.#currentThemes,\n\t\t\tsystemOptions: this.#systemOptions,\n\t\t}\n\t}\n\n\trestore = (): void => {\n\t\tconst persistedState = this.#storage?.get()\n\n\t\tif (!persistedState) {\n\t\t\tthis.#setThemesAndNotify({ ...this.#defaultThemes })\n\t\t\treturn\n\t\t}\n\n\t\tthis.#systemOptions = {\n\t\t\t...this.#systemOptions,\n\t\t\t...persistedState.systemOptions,\n\t\t}\n\n\t\tthis.#setThemesAndNotify({\n\t\t\t...this.#defaultThemes,\n\t\t\t...persistedState.themes,\n\t\t})\n\t}\n\n\tsubscribe = (callback: Listener<T>): (() => void) => {\n\t\tthis.#listeners.add(callback)\n\n\t\treturn () => {\n\t\t\tthis.#listeners.delete(callback)\n\t\t}\n\t}\n\n\tsync = (): (() => void) | undefined => {\n\t\tif (!this.#storage?.watch) return\n\n\t\treturn this.#storage.watch((persistedState) => {\n\t\t\tthis.#systemOptions = (persistedState as PersistedState<T>).systemOptions\n\n\t\t\tthis.#setThemesAndNotify((persistedState as PersistedState<T>).themes)\n\t\t})\n\t}\n\n\t/** Clears subscribers and aborts media-query listeners tied to this store instance. */\n\tdestroy = (): void => {\n\t\tthis.#listeners.clear()\n\t\tthis.#abortController.abort()\n\t}\n\n\t#setThemesAndNotify = (themes: Themes<T>): void => {\n\t\tthis.#currentThemes = themes\n\t\tthis.#notify()\n\t}\n\n\t#resolveThemes = (): Themes<T> => {\n\t\treturn Object.fromEntries(\n\t\t\tObject.entries(this.#currentThemes).map(([themeKey, optionKey]) => {\n\t\t\t\tconst option = this.#keyedConfig[themeKey]?.[optionKey]\n\n\t\t\t\treturn [\n\t\t\t\t\tthemeKey,\n\t\t\t\t\toption ? this.#resolveThemeOption({ themeKey, option }) : optionKey,\n\t\t\t\t]\n\t\t\t}),\n\t\t) as Themes<T>\n\t}\n\n\t#resolveThemeOption = ({\n\t\tthemeKey,\n\t\toption,\n\t}: {\n\t\tthemeKey: string\n\t\toption: ThemeOption\n\t}): string => {\n\t\tif (!option.media) return option.value\n\n\t\tif (\n\t\t\t!(\n\t\t\t\ttypeof window !== 'undefined' &&\n\t\t\t\ttypeof window.document !== 'undefined' &&\n\t\t\t\ttypeof window.document.createElement !== 'undefined'\n\t\t\t)\n\t\t) {\n\t\t\tif (!PROD) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`[${PACKAGE_NAME}] Option with key \"media\" cannot be resolved in server environment.`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\treturn option.value\n\t\t}\n\n\t\tconst [mediaQuery] = option.media\n\n\t\tif (!this.#mediaQueryCache[mediaQuery]) {\n\t\t\tconst mediaQueryList = window.matchMedia(mediaQuery)\n\n\t\t\tthis.#mediaQueryCache[mediaQuery] = mediaQueryList\n\n\t\t\tmediaQueryList.addEventListener(\n\t\t\t\t'change',\n\t\t\t\t() => {\n\t\t\t\t\tif (this.#currentThemes[themeKey] === option.value) {\n\t\t\t\t\t\tthis.#setThemesAndNotify({ ...this.#currentThemes })\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{ signal: this.#abortController.signal },\n\t\t\t)\n\t\t}\n\n\t\tconst [ifMatch, ifNotMatch] = this.#systemOptions[themeKey]!\n\n\t\treturn this.#mediaQueryCache[mediaQuery].matches ? ifMatch : ifNotMatch\n\t}\n\n\t#notify = (): void => {\n\t\tfor (const listener of this.#listeners) {\n\t\t\tlistener({\n\t\t\t\tthemes: this.#currentThemes,\n\t\t\t\tresolvedThemes: this.#resolveThemes(),\n\t\t\t})\n\t\t}\n\t}\n}\n\nexport type { ThemeStore }\n\nexport function createThemeStore<T extends ThemeStoreConfig>(\n\tconfig: T,\n\toptions: ThemeStoreOptions<T> = {},\n): ThemeStore<T> {\n\treturn new ThemeStore<T>(config, options)\n}\n\nconst restoreScript = (\n\tparams: Array<[string, ThemeStoreConfig, Listener<any>]>,\n) => {\n\tparams.forEach(([key, config, handler]) => {\n\t\tconst persistedThemes =\n\t\t\tJSON.parse(localStorage.getItem(key) || '{}').themes ?? {}\n\n\t\thandler(\n\t\t\tObject.entries(config).reduce<{\n\t\t\t\tthemes: Record<string, ThemeValue>\n\t\t\t\tresolvedThemes: Record<string, ThemeValue>\n\t\t\t}>(\n\t\t\t\t(acc, [themeKey, themeConfig]) => {\n\t\t\t\t\tconst options = themeConfig.options\n\n\t\t\t\t\tconst firstOption = options?.[0]\n\n\t\t\t\t\tconst currentValue =\n\t\t\t\t\t\tpersistedThemes[themeKey] ??\n\t\t\t\t\t\tthemeConfig.initialValue ??\n\t\t\t\t\t\t(typeof firstOption === 'object' ? firstOption.value : firstOption!)\n\n\t\t\t\t\tacc.resolvedThemes[themeKey] = acc.themes[themeKey] = currentValue\n\n\t\t\t\t\tconst maybeSystemOption = options?.find(\n\t\t\t\t\t\t(option): option is Required<ThemeOption> =>\n\t\t\t\t\t\t\ttypeof option === 'object' &&\n\t\t\t\t\t\t\t!!option.media &&\n\t\t\t\t\t\t\toption.value === currentValue,\n\t\t\t\t\t)\n\n\t\t\t\t\tif (maybeSystemOption) {\n\t\t\t\t\t\tconst [mediaQuery, ifMatch, ifNotMatch] = maybeSystemOption.media\n\n\t\t\t\t\t\tacc.resolvedThemes[themeKey] = matchMedia(mediaQuery).matches\n\t\t\t\t\t\t\t? ifMatch\n\t\t\t\t\t\t\t: ifNotMatch\n\t\t\t\t\t}\n\n\t\t\t\t\treturn acc\n\t\t\t\t},\n\t\t\t\t{ themes: {}, resolvedThemes: {} },\n\t\t\t),\n\t\t)\n\t})\n}\n\nexport type ThemeScriptParameter = {\n\t/** `localStorage` key; defaults to the 'resonare'. */\n\tkey?: string\n\tconfig: ThemeStoreConfig\n\thandler: Listener<ThemeStoreConfig>\n}\n\n/**\n * Creates an IIFE script string that reads persisted themes from `localStorage` and runs your handlers immediately.\n *\n * Useful for avoiding flash of incorrect styles.\n * @example\n * ```tsx\n * import { createInlineThemeScript } from 'resonare'\n *\n * const inlineScript = createInlineThemeScript([\n * {\n * key: 'my-app',\n * config: {\n * colorMode: { options: ['light', 'dark'] },\n * },\n * handler: ({ resolvedThemes }) => {\n * document.documentElement.dataset.colorMode = String(\n * resolvedThemes.colorMode,\n * )\n * },\n * },\n * ])\n * ```\n */\nexport function createInlineThemeScript(\n\tthemeScriptParameters: Array<ThemeScriptParameter>,\n) {\n\tconst serializedThemeScriptParameters = themeScriptParameters.map(\n\t\t({ key = PACKAGE_NAME, config, handler }) =>\n\t\t\t`[${JSON.stringify(key)},${JSON.stringify(config)},${handler}]`,\n\t)\n\n\treturn `(${restoreScript})([${serializedThemeScriptParameters.join(',')}])`\n}\n"],"mappings":"iBCoCA,MAAagB,GAGP,CAAEC,MAAKf,OAAO,mBACX,CAAES,sBACF,CACNP,QACQc,KAAKC,MAAMC,OAAOlB,GAAMmB,QAAQJ,EAAI,EAAI,OAAO,CAGvDZ,IAAMC,GAAkB,CACvBc,OAAOlB,GAAMoB,QAAQL,EAAKC,KAAKK,UAAUjB,EAAM,CAAC,EAGjDE,MAAQC,GAAO,CACd,IAAMe,EAAa,IAAIZ,gBAmBvB,OAjBAQ,OAAOK,iBACN,UACCC,GAAM,CACFA,EAAEC,cAAgBP,OAAOlB,IAEzBwB,EAAET,MAAQA,GAEdR,EAAGS,KAAKC,MAAMO,EAAEE,SAAU,CAAC,EAE5B,CACCC,OAAQC,YAAYC,IAAI,CACvBpB,EAAgBkB,OAChBL,EAAWK,OACX,CAAA,CAEH,CAAC,KAEY,CACZL,EAAWQ,OAAO,GAGpB,EAiBUC,GAEP,CAAEhB,UACC,CAAEN,qBAAsB,CAC/B,IAAMuB,EAAU,IAAIC,IACdC,EAAU,IAAIC,iBAAiBpC,EAAa,CAElD,MAAO,CACNG,QACQ8B,EAAQ9B,IAAIa,EAAI,EAAI,KAG5BZ,IAAMC,GAAkB,CACvB4B,EAAQ7B,IAAIY,EAAKX,EAAM,EAGxBC,UAAYD,GAAkB,CAC7B8B,EAAQE,YAAY,CAAErB,MAAKX,QAAO,CAAC,EAGpCE,MAAQC,GAAO,CACd,IAAMe,EAAa,IAAIZ,gBAiBvB,OAfAwB,EAAQX,iBACP,UACCC,GAAM,CACFA,EAAEa,KAAKtB,MAAQA,GAEnBR,EAAGiB,EAAEa,KAAKjC,MAAM,EAEjB,CACCuB,OAAQC,YAAYC,IAAI,CACvBpB,EAAgBkB,OAChBL,EAAWK,OACX,CAAA,CAEH,CAAC,KAEY,CACZL,EAAWQ,OAAO,GAGpB,EC5BH,SAAgB2C,EAAgDC,EAAW,CAC1E,OAAOC,OAAOC,QAAQF,EAAO,CAACG,KAAK,CAACC,EAAUC,KACtC,CACND,GACCC,EAAY7B,SAAW,EAAE,EAAE2B,IAAKG,GAChC,OAAOA,GAAW,SAAWA,EAAOlC,MAAQkC,EAC5C,CACD,CACA,CAGH,SAAgBC,EAA6CP,EAAW,CACvE,OAAOC,OAAOO,YACbP,OAAOC,QAAQF,EAAO,CAACG,KAAK,CAACC,EAAUC,KAC/B,CACND,EACAC,EAAY3B,eACV,OAAO2B,EAAY7B,QAAQ,IAAO,SAChC6B,EAAY7B,QAAQ,GAAGJ,MACvBiC,EAAY7B,QAAQ,IACxB,CAEH,CAAC,CAGF,IAAMiC,EAAN,KAA6C,CAC5C,GACA,GAEA,GAEA,GAEA,GAEA,GAA+B,IAAIK,IAEnC,GAEA,GAAmB,IAAII,gBAEvBC,YACCnB,EACA,CACCL,eAAe,EAAE,CACjBC,UAAU7B,EAAoB,CAAEqD,IAAKvD,EAAc,CAAA,EAC1B,EAAE,CAC3B,CACD,IAAM4B,EAA0D,CAC/D,GAAGE,EAAaF,cAChB,CAEKmB,EAAcX,OAAOO,YAC1BP,OAAOC,QAAQF,EAAO,CAACG,KAAK,CAACC,EAAUC,KAAiB,CACvD,IAAMH,GAAWG,EAAY7B,SAAW,EAAE,EAAE2B,IAAKG,GAC5C,OAAOA,GAAW,UACjBA,EAAOhC,OAAS,CAAC2B,OAAOoB,OAAO5B,EAAeW,EAAS,GAC1DX,EAAcW,GAAY,CAACE,EAAOhC,MAAM,GAAIgC,EAAOhC,MAAM,GAAG,EAGtD,CAACgD,OAAOhB,EAAOlC,MAAM,CAAEkC,EAAO,EAG/B,CAACgB,OAAOhB,EAAO,CAAE,CAAElC,MAAOkC,EAAQ,CAAC,CACzC,CACF,MAAO,CAACF,EAAUH,OAAOO,YAAYN,EAAQ,CAAC,EAEhD,CAAC,CAED,MAAA,EAAoBU,EAEpB,MAAA,EAAsBnB,EAEtB,MAAA,EAAsBc,EAAiBP,EAAO,CAE9C,MAAA,EAAsB,CAAE,GAAG,MAAA,EAAqB,GAAGL,EAAaT,OAAQ,CAExE,MAAA,EACCU,IAAU,CACTqB,gBAAiB,MAAA,EACjB,CAAC,EAAI,KAEP,MAAA,EAAwB,EAAE,CAG3BM,cACQ,MAAA,EAGRC,sBACQ,MAAA,GAAqB,CAG7BE,UACCxC,GAGU,CACV,IAAMyC,EACL,OAAOzC,GAAW,WAAaA,EAAO,MAAA,EAAoB,CAAGA,EAE9D,MAAA,EAAyB,CAAE,GAAG,MAAA,EAAqB,GAAGyC,EAAe,CAAC,CAEtE,IAAME,EAAiB,KAAKC,WAAW,CAEnC,MAAA,IACH,MAAA,EAAcC,IAAIF,EAAe,CACjC,MAAA,EAAcG,YAAYH,EAAe,GAI3CI,oBACC7B,EACA,CAAC8B,EAASC,KAIA,CACV,MAAA,EAAoB/B,GAAY,CAAC8B,EAASC,EAAW,CAErD,KAAKT,UAAU,CAAE,GAAG,MAAA,EAAqB,CAAC,EAG3CI,eACQ,CACN5C,OAAQ,MAAA,EACRO,cAAe,MAAA,EACf,EAGF2C,YAAsB,CACrB,IAAMC,EAAiB,MAAA,GAAeC,KAAK,CAE3C,GAAI,CAACD,EAAgB,CACpB,MAAA,EAAyB,CAAE,GAAG,MAAA,EAAqB,CAAC,CACpD,OAGD,MAAA,EAAsB,CACrB,GAAG,MAAA,EACH,GAAGA,EAAe5C,cAClB,CAED,MAAA,EAAyB,CACxB,GAAG,MAAA,EACH,GAAG4C,EAAenD,OAClB,CAAC,EAGHqD,UAAaC,IACZ,MAAA,EAAgBC,IAAID,EAAS,KAEhB,CACZ,MAAA,EAAgBE,OAAOF,EAAS,GAIlCG,SAAuC,CACjC,SAAA,GAAeC,MAEpB,OAAO,MAAA,EAAcA,MAAOP,GAAmB,CAC9C,MAAA,EAAuBA,EAAqC5C,cAE5D,MAAA,EAA0B4C,EAAqCnD,OAAO,EACrE,EAIH2D,YAAsB,CACrB,MAAA,EAAgBC,OAAO,CACvB,MAAA,EAAsBC,OAAO,EAG9B,GAAuB7D,GAA4B,CAClD,MAAA,EAAsBA,EACtB,MAAA,GAAc,EAGf,OACQe,OAAOO,YACbP,OAAOC,QAAQ,MAAA,EAAoB,CAACC,KAAK,CAACC,EAAU+C,KAAe,CAClE,IAAM7C,EAAS,MAAA,EAAkBF,KAAY+C,GAE7C,MAAO,CACN/C,EACAE,EAAS,MAAA,EAAyB,CAAEF,WAAUE,SAAQ,CAAC,CAAG6C,EAC1D,EAEH,CAAC,CAGF,IAAuB,CACtB/C,WACAE,YAIa,CACb,GAAI,CAACA,EAAOhC,MAAO,OAAOgC,EAAOlC,MAEjC,GACC,EACC,OAAOkF,OAAW,KACXA,OAAOC,WAAa,QACpBD,OAAOC,SAASC,gBAAkB,QAS1C,OANI,QAAA,IAAA,WAAA,cACHE,QAAQC,KACP,IAAI9F,EAAY,qEAChB,CAGKyC,EAAOlC,MAGf,GAAM,CAACwF,GAActD,EAAOhC,MAE5B,GAAI,CAAC,MAAA,EAAsBsF,GAAa,CACvC,IAAMC,EAAiBP,OAAOQ,WAAWF,EAAW,CAEpD,MAAA,EAAsBA,GAAcC,EAEpCA,EAAeE,iBACd,aACM,CACD,MAAA,EAAoB3D,KAAcE,EAAOlC,OAC5C,MAAA,EAAyB,CAAE,GAAG,MAAA,EAAqB,CAAC,EAGtD,CAAE4F,OAAQ,MAAA,EAAsBA,OACjC,CAAC,CAGF,GAAM,CAAC9B,EAASC,GAAc,MAAA,EAAoB/B,GAElD,OAAO,MAAA,EAAsBwD,GAAYK,QAAU/B,EAAUC,GAG9D,OAAsB,CACrB,IAAK,IAAMgC,KAAY,MAAA,EACtBA,EAAS,CACRjF,OAAQ,MAAA,EACRC,eAAgB,MAAA,GAAoB,CACpC,CAAC,GAOL,SAAgBiF,EACfpE,EACAxB,EAAgC,EAAE,CAClB,CAChB,OAAO,IAAIiC,EAAcT,EAAQxB,EAAQ,CAG1C,MAAM6F,EACLC,GACI,CACJA,EAAOC,SAAS,CAACnD,EAAKpB,EAAQwE,KAAa,CAC1C,IAAMC,EACLC,KAAKC,MAAMC,aAAaC,QAAQzD,EAAI,EAAI,KAAK,CAAClC,QAAU,EAAE,CAE3DsF,EACCvE,OAAOC,QAAQF,EAAO,CAAC8E,QAIrBC,EAAK,CAAC3E,EAAUC,KAAiB,CACjC,IAAM7B,EAAU6B,EAAY7B,QAEtBwG,EAAcxG,IAAU,GAExByG,EACLR,EAAgBrE,IAChBC,EAAY3B,eACX,OAAOsG,GAAgB,SAAWA,EAAY5G,MAAQ4G,GAExDD,EAAI5F,eAAeiB,GAAY2E,EAAI7F,OAAOkB,GAAY6E,EAEtD,IAAMC,EAAoB1G,GAAS2G,KACjC7E,GACA,OAAOA,GAAW,UAClB,CAAC,CAACA,EAAOhC,OACTgC,EAAOlC,QAAU6G,EAClB,CAED,GAAIC,EAAmB,CACtB,GAAM,CAACtB,EAAY1B,EAASC,GAAc+C,EAAkB5G,MAE5DyG,EAAI5F,eAAeiB,GAAY0D,WAAWF,EAAW,CAACK,QACnD/B,EACAC,EAGJ,OAAO4C,GAER,CAAE7F,OAAQ,EAAE,CAAEC,eAAgB,EAAC,CAChC,CACD,CAAC,EACA,EAiCH,SAAgBmG,EACfC,EACC,CAMD,MAAO,IAAIlB,EAAa,KALgBkB,EAAsBpF,KAC5D,CAAEiB,MAAMvD,EAAcmC,SAAQwE,aAC9B,IAAIE,KAAKe,UAAUrE,EAAI,CAAA,GAAIsD,KAAKe,UAAUzF,EAAO,CAAA,GAAIwE,EAAO,GAC7D,CAE6DkB,KAAK,IAAI,CAAA"}
package/dist/react.d.ts CHANGED
@@ -1,29 +1,28 @@
1
1
  import { ThemeStore, ThemeStoreConfig, Themes } from "./index.js";
2
2
 
3
3
  //#region src/react.d.ts
4
+ /**
5
+ * Subscribes a React component to a theme store.
6
+ * @example
7
+ * ```tsx
8
+ * export function ThemeToggle() {
9
+ * const { themes, resolvedThemes, setThemes } = useResonare(store)
10
+ *
11
+ * // ...
12
+ * }
13
+ * ```
14
+ */
4
15
  declare function useResonare<T extends ThemeStoreConfig>(store: ThemeStore<T>): {
5
16
  themes: Themes<T>;
6
17
  resolvedThemes: Themes<T>;
18
+ destroy: () => void;
19
+ restore: () => void;
7
20
  setThemes: (themes: Partial<Themes<T>> | ((currentThemes: Themes<T>) => Partial<Themes<T>>)) => void;
8
- updateSystemOption: <K extends { [K_1 in keyof T]: T[K_1] extends {
9
- options: ReadonlyArray<infer U>;
10
- } ? U extends {
11
- media: ReadonlyArray<unknown>;
12
- } ? K_1 : never : never }[keyof T]>(themeKey: K, [ifMatch, ifNotMatch]: [T[K] extends {
13
- options: ReadonlyArray<infer U>;
14
- } ? U extends string | number | boolean ? U : U extends {
15
- value: string;
16
- media?: [string, string, string];
17
- } ? U extends {
18
- media: [string, string, string];
19
- } ? never : U["value"] : never : never, T[K] extends {
20
- options: ReadonlyArray<infer U>;
21
- } ? U extends string | number | boolean ? U : U extends {
22
- value: string;
23
- media?: [string, string, string];
24
- } ? U extends {
25
- media: [string, string, string];
26
- } ? never : U["value"] : never : never]) => void;
21
+ subscribe: (callback: (value: {
22
+ themes: Themes<T>;
23
+ resolvedThemes: Themes<T>;
24
+ }) => void) => (() => void);
25
+ sync: () => (() => void) | undefined;
27
26
  toPersist: () => {
28
27
  themes: Partial<Themes<T>>;
29
28
  systemOptions: { [K_1 in { [K in keyof T]: T[K] extends {
@@ -46,13 +45,25 @@ declare function useResonare<T extends ThemeStoreConfig>(store: ThemeStore<T>):
46
45
  media: [string, string, string];
47
46
  } ? never : U["value"] : never : never] };
48
47
  };
49
- restore: () => void;
50
- sync: () => (() => void) | undefined;
51
- subscribe: (callback: (value: {
52
- themes: Themes<T>;
53
- resolvedThemes: Themes<T>;
54
- }) => void) => (() => void);
55
- destroy: () => void;
48
+ updateSystemOption: <K extends { [K_1 in keyof T]: T[K_1] extends {
49
+ options: ReadonlyArray<infer U>;
50
+ } ? U extends {
51
+ media: ReadonlyArray<unknown>;
52
+ } ? K_1 : never : never }[keyof T]>(themeKey: K, [ifMatch, ifNotMatch]: [T[K] extends {
53
+ options: ReadonlyArray<infer U>;
54
+ } ? U extends string | number | boolean ? U : U extends {
55
+ value: string;
56
+ media?: [string, string, string];
57
+ } ? U extends {
58
+ media: [string, string, string];
59
+ } ? never : U["value"] : never : never, T[K] extends {
60
+ options: ReadonlyArray<infer U>;
61
+ } ? U extends string | number | boolean ? U : U extends {
62
+ value: string;
63
+ media?: [string, string, string];
64
+ } ? U extends {
65
+ media: [string, string, string];
66
+ } ? never : U["value"] : never : never]) => void;
56
67
  };
57
68
  //#endregion
58
69
  export { useResonare };
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","names":[],"sources":["../src/react.ts"],"mappings":";;;iBAGgB,WAAA,WAAsB,gBAAA,CAAA,CAAkB,KAAA,EAAO,UAAA,CAAW,CAAA"}
1
+ {"version":3,"file":"react.d.ts","names":[],"sources":["../src/react.ts"],"mappings":";;;;;AAcA;;;;;;;;;iBAAgB,WAAA,WAAsB,gBAAA,CAAA,CAAkB,KAAA,EAAO,UAAA,CAAW,CAAA"}
package/dist/react.js CHANGED
@@ -1,2 +1,2 @@
1
- import{c as e}from"react-compiler-runtime";import*as t from"react";function n(n){let r=e(12),{getThemes:i,getResolvedThemes:a,setThemes:o,updateSystemOption:s,toPersist:c,restore:l,sync:u,subscribe:d,destroy:f}=n,p=t.useSyncExternalStore(d,i,i),m;r[0]===a?m=r[1]:(m=a(),r[0]=a,r[1]=m);let h;return r[2]!==f||r[3]!==l||r[4]!==o||r[5]!==d||r[6]!==u||r[7]!==m||r[8]!==p||r[9]!==c||r[10]!==s?(h={themes:p,resolvedThemes:m,setThemes:o,updateSystemOption:s,toPersist:c,restore:l,sync:u,subscribe:d,destroy:f},r[2]=f,r[3]=l,r[4]=o,r[5]=d,r[6]=u,r[7]=m,r[8]=p,r[9]=c,r[10]=s,r[11]=h):h=r[11],h}export{n as useResonare};
1
+ import{c as e}from"react-compiler-runtime";import*as t from"react";function n(n){let r=e(12),{destroy:i,getResolvedThemes:a,getThemes:o,restore:s,setThemes:c,subscribe:l,sync:u,toPersist:d,updateSystemOption:f}=n,p=t.useSyncExternalStore(l,o,o),m;r[0]===a?m=r[1]:(m=a(),r[0]=a,r[1]=m);let h;return r[2]!==i||r[3]!==s||r[4]!==c||r[5]!==l||r[6]!==u||r[7]!==m||r[8]!==p||r[9]!==d||r[10]!==f?(h={themes:p,resolvedThemes:m,destroy:i,restore:s,setThemes:c,subscribe:l,sync:u,toPersist:d,updateSystemOption:f},r[2]=i,r[3]=s,r[4]=c,r[5]=l,r[6]=u,r[7]=m,r[8]=p,r[9]=d,r[10]=f,r[11]=h):h=r[11],h}export{n as useResonare};
2
2
  //# sourceMappingURL=react.js.map
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","names":["React","ThemeStore","ThemeStoreConfig","useResonare","store","$","_c","getThemes","getResolvedThemes","setThemes","updateSystemOption","toPersist","restore","sync","subscribe","destroy","themes","useSyncExternalStore","t0","t1","resolvedThemes"],"sources":["../src/react.ts"],"sourcesContent":["import * as React from 'react'\nimport type { ThemeStore, ThemeStoreConfig } from '.'\n\nexport function useResonare<T extends ThemeStoreConfig>(store: ThemeStore<T>) {\n\tconst {\n\t\tgetThemes,\n\t\tgetResolvedThemes,\n\t\tsetThemes,\n\t\tupdateSystemOption,\n\t\ttoPersist,\n\t\trestore,\n\t\tsync,\n\t\tsubscribe,\n\t\tdestroy,\n\t} = store\n\n\tconst themes = React.useSyncExternalStore(subscribe, getThemes, getThemes)\n\n\treturn {\n\t\tthemes,\n\t\tresolvedThemes: getResolvedThemes(),\n\t\tsetThemes,\n\t\tupdateSystemOption,\n\t\ttoPersist,\n\t\trestore,\n\t\tsync,\n\t\tsubscribe,\n\t\tdestroy,\n\t}\n}\n"],"mappings":"mEAGA,SAAOG,EAAAC,EAAA,CAAA,IAAAC,EAAAC,EAAA,GAAA,CACN,CAAAC,YAAAC,oBAAAC,YAAAC,qBAAAC,YAAAC,UAAAC,OAAAC,YAAAC,WAUIX,EAEJY,EAAehB,EAAKiB,qBAAsBH,EAAWP,EAAWA,EAAU,CAAAW,EAAAb,EAAA,KAAAG,EAItCU,EAAAb,EAAA,IAAnBa,EAAAV,GAAmB,CAAAH,EAAA,GAAAG,EAAAH,EAAA,GAAAa,GAAA,IAAAC,EAQnC,OARmCd,EAAA,KAAAU,GAAAV,EAAA,KAAAO,GAAAP,EAAA,KAAAI,GAAAJ,EAAA,KAAAS,GAAAT,EAAA,KAAAQ,GAAAR,EAAA,KAAAa,GAAAb,EAAA,KAAAW,GAAAX,EAAA,KAAAM,GAAAN,EAAA,MAAAK,GAF7BS,EAAA,CAAAH,SAAAI,eAEUF,EAAmBT,YAAAC,qBAAAC,YAAAC,UAAAC,OAAAC,YAAAC,UAQnC,CAAAV,EAAA,GAAAU,EAAAV,EAAA,GAAAO,EAAAP,EAAA,GAAAI,EAAAJ,EAAA,GAAAS,EAAAT,EAAA,GAAAQ,EAAAR,EAAA,GAAAa,EAAAb,EAAA,GAAAW,EAAAX,EAAA,GAAAM,EAAAN,EAAA,IAAAK,EAAAL,EAAA,IAAAc,GAAAA,EAAAd,EAAA,IAVMc"}
1
+ {"version":3,"file":"react.js","names":["React","ThemeStore","ThemeStoreConfig","useResonare","store","$","_c","destroy","getResolvedThemes","getThemes","restore","setThemes","subscribe","sync","toPersist","updateSystemOption","themes","useSyncExternalStore","t0","t1","resolvedThemes"],"sources":["../src/react.ts"],"sourcesContent":["import * as React from 'react'\nimport type { ThemeStore, ThemeStoreConfig } from '.'\n\n/**\n * Subscribes a React component to a theme store.\n * @example\n * ```tsx\n * export function ThemeToggle() {\n * const { themes, resolvedThemes, setThemes } = useResonare(store)\n *\n * // ...\n * }\n * ```\n */\nexport function useResonare<T extends ThemeStoreConfig>(store: ThemeStore<T>) {\n\tconst {\n\t\tdestroy,\n\t\tgetResolvedThemes,\n\t\tgetThemes,\n\t\trestore,\n\t\tsetThemes,\n\t\tsubscribe,\n\t\tsync,\n\t\ttoPersist,\n\t\tupdateSystemOption,\n\t} = store\n\n\tconst themes = React.useSyncExternalStore(subscribe, getThemes, getThemes)\n\n\treturn {\n\t\tthemes,\n\t\tresolvedThemes: getResolvedThemes(),\n\t\tdestroy,\n\t\trestore,\n\t\tsetThemes,\n\t\tsubscribe,\n\t\tsync,\n\t\ttoPersist,\n\t\tupdateSystemOption,\n\t}\n}\n"],"mappings":"mEAcA,SAAOG,EAAAC,EAAA,CAAA,IAAAC,EAAAC,EAAA,GAAA,CACN,CAAAC,UAAAC,oBAAAC,YAAAC,UAAAC,YAAAC,YAAAC,OAAAC,YAAAC,sBAUIX,EAEJY,EAAehB,EAAKiB,qBAAsBL,EAAWH,EAAWA,EAAU,CAAAS,EAAAb,EAAA,KAAAG,EAItCU,EAAAb,EAAA,IAAnBa,EAAAV,GAAmB,CAAAH,EAAA,GAAAG,EAAAH,EAAA,GAAAa,GAAA,IAAAC,EAQnC,OARmCd,EAAA,KAAAE,GAAAF,EAAA,KAAAK,GAAAL,EAAA,KAAAM,GAAAN,EAAA,KAAAO,GAAAP,EAAA,KAAAQ,GAAAR,EAAA,KAAAa,GAAAb,EAAA,KAAAW,GAAAX,EAAA,KAAAS,GAAAT,EAAA,MAAAU,GAF7BI,EAAA,CAAAH,SAAAI,eAEUF,EAAmBX,UAAAG,UAAAC,YAAAC,YAAAC,OAAAC,YAAAC,qBAQnC,CAAAV,EAAA,GAAAE,EAAAF,EAAA,GAAAK,EAAAL,EAAA,GAAAM,EAAAN,EAAA,GAAAO,EAAAP,EAAA,GAAAQ,EAAAR,EAAA,GAAAa,EAAAb,EAAA,GAAAW,EAAAX,EAAA,GAAAS,EAAAT,EAAA,IAAAU,EAAAV,EAAA,IAAAc,GAAAA,EAAAd,EAAA,IAVMc"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "resonare",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Resonare",
5
5
  "keywords": [
6
6
  "theming",
@@ -34,21 +34,21 @@
34
34
  "react-compiler-runtime": "1.0.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@rolldown/plugin-babel": "0.2.2",
38
- "@size-limit/preset-small-lib": "12.0.1",
39
- "@types/node": "25.5.2",
37
+ "@rolldown/plugin-babel": "0.2.3",
38
+ "@size-limit/preset-small-lib": "12.1.0",
39
+ "@types/node": "25.6.0",
40
40
  "@types/react": "19.2.14",
41
41
  "@vitejs/plugin-react": "6.0.1",
42
42
  "babel-plugin-react-compiler": "1.0.0",
43
43
  "cross-env": "10.1.0",
44
- "jsdom": "29.0.1",
45
- "react": "19.2.4",
46
- "react-dom": "19.2.4",
47
- "size-limit": "12.0.1",
48
- "tsdown": "0.21.7",
49
- "typescript": "6.0.2",
50
- "vite": "8.0.3",
51
- "vitest": "4.1.2"
44
+ "jsdom": "29.0.2",
45
+ "react": "19.2.5",
46
+ "react-dom": "19.2.5",
47
+ "size-limit": "12.1.0",
48
+ "tsdown": "0.21.9",
49
+ "typescript": "6.0.3",
50
+ "vite": "8.0.9",
51
+ "vitest": "4.1.5"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "react": "^18.0.0 || ^19.0.0"