flexium
Version:
A lightweight, signals-based UI framework with cross-platform renderers
1 lines • 8.2 kB
Source Map (JSON)
{"version":3,"sources":["../src/core/state.ts"],"names":["globalStateRegistry","state","initialValueOrFetcher","options","s","actions","key","fn","res","resActions","createResource","val","signal","getter","setter","newValue","v","clearGlobalState"],"mappings":"uCAIA,IAAMA,CAAAA,CAAsB,IAAI,GAAA,CAmCzB,SAASC,EACdC,CAAAA,CACAC,CAAAA,CACkC,CAClC,IAAIC,CAAAA,CACAC,CAAAA,CAAe,EAAC,CAEdC,CAAAA,CAAMH,CAAAA,EAAS,GAAA,CAGrB,GAAIG,CAAAA,EAAO,OAAOA,CAAAA,EAAQ,QAAA,EAAYN,CAAAA,CAAoB,GAAA,CAAIM,CAAG,CAAA,CAO/DF,EANeJ,CAAAA,CAAoB,GAAA,CAAIM,CAAG,CAAA,CAAA,KAgBvC,CACH,GAAI,OAAOJ,CAAAA,EAA0B,UAAA,CAAY,CAO/C,IAAMK,CAAAA,CAAKL,CAAAA,CAEL,CAACM,CAAAA,CAAKC,CAAU,CAAA,CAAIC,CAAAA,CACxBH,CAAAA,CACA,MAAOI,GAAQA,CACjB,CAAA,CAEAP,CAAAA,CAAII,CAAAA,CACJH,CAAAA,CAAUI,EACZ,MAEEL,CAAAA,CAAIQ,CAAAA,CAAUV,CAAqB,CAAA,CAIjCI,CAAAA,EAAO,OAAOA,CAAAA,EAAQ,QAAA,GAMvBF,CAAAA,CAAU,aAAA,CAAgBC,CAAAA,CAC3BL,CAAAA,CAAoB,IAAIM,CAAAA,CAAKF,CAAC,CAAA,EAElC,CAGI,CAACC,CAAAA,CAAQ,SAAYD,CAAAA,CAAU,aAAA,GACjCC,CAAAA,CAAWD,CAAAA,CAAU,aAAA,CAAA,CAMvB,IAAMS,GAAU,IAAMT,CAAAA,CAAE,KAAA,CAAA,CAIxB,MAAA,CAAO,gBAAA,CAAiBS,CAAAA,CAAQ,CAC9B,OAAA,CAAS,CAAE,GAAA,CAAK,IAAOT,CAAAA,CAAU,OAAA,EAAW,KAAM,CAAA,CAClD,KAAA,CAAO,CAAE,GAAA,CAAK,IAAOA,CAAAA,CAAU,KAAM,CAAA,CACrC,KAAA,CAAO,CAAE,GAAA,CAAK,IAAOA,CAAAA,CAAU,OAAS,OAAQ,CAAA,CAChD,OAAQ,CAAE,GAAA,CAAK,IAAOA,CAAAA,CAAU,MAAA,EAAUA,CAAAA,CAAE,IAAA,EAAO,CAAA,CACnD,KAAM,CAAE,KAAA,CAAQA,CAAAA,CAAU,IAAA,GAAS,IAAMA,CAAAA,CAAE,MAAO,CACpD,CAAC,CAAA,CAGD,IAAMU,CAAAA,EAAWC,CAAAA,EAAmC,CAElD,GAAIV,CAAAA,CAAQ,MAAA,CAGR,GAAI,OAAOU,CAAAA,EAAa,WAAY,CAChC,IAAMR,CAAAA,CAAKQ,CAAAA,CACXV,CAAAA,CAAQ,MAAA,CAAOE,EAAGH,CAAAA,CAAE,IAAA,EAAM,CAAC,EAC/B,CAAA,KACIC,EAAQ,MAAA,CAAOU,CAAQ,CAAA,CAAA,KAAA,GAIvB,OAAOA,CAAAA,EAAa,UAAA,CAAY,CAChC,IAAMR,CAAAA,CAAKQ,CAAAA,CACXX,CAAAA,CAAE,KAAA,CAAQG,CAAAA,CAAGH,EAAE,IAAA,EAAM,EACzB,CAAA,KACIA,CAAAA,CAAE,KAAA,CAAQW,EAGpB,CAAA,CAAA,CAGA,OAAAD,CAAAA,CAAO,MAAA,CAAST,CAAAA,CAAQ,MAAA,GAAYW,GAAS,CACrC,OAAOA,CAAAA,EAAM,UAAA,CAAYZ,CAAAA,CAAE,KAAA,CAASY,EAAUZ,CAAAA,CAAE,IAAA,EAAM,CAAA,CACrDA,CAAAA,CAAE,KAAA,CAAQY,EACnB,CAAA,CAAA,CACAF,CAAAA,CAAO,OAAA,CAAUT,CAAAA,CAAQ,OAAA,GAAY,IAAM,CAA+B,CAAA,CAAA,CAEnE,CAACQ,CAAAA,CAAQC,CAAM,CACxB,CAKO,SAASG,CAAAA,EAAmB,CACjCjB,CAAAA,CAAoB,KAAA,GACtB","file":"chunk-ZK6FOZDP.mjs","sourcesContent":["import { signal, createResource } from './signal';\n\n// Global registry for keyed states\n// We store either a Signal, Resource, or Computed\nconst globalStateRegistry = new Map<string, any>();\n\n// Enhanced Getter Type: acts as accessor but carries Resource properties if applicable\nexport type StateGetter<T> = {\n (): T;\n loading: boolean;\n error: any;\n state: 'unresolved' | 'pending' | 'ready' | 'refreshing' | 'errored';\n latest: T | undefined;\n read: () => T | undefined;\n map: T extends (infer U)[] \n ? (fn: (item: U, index: () => number) => any) => any \n : never;\n};\n\n// Enhanced Setter Type: acts as setter but carries Resource actions\nexport type StateSetter<T> = {\n (newValue: T | ((prev: T) => T)): void;\n mutate: (v: T | undefined) => void;\n refetch: () => void;\n};\n\n/**\n * Unified State API\n * \n * Usage:\n * 1. Local Value: const [count, setCount] = state(0);\n * 2. Local Resource: const [data, actions] = state(async () => fetch('/api').then(r => r.json()));\n * 3. Global Value: const [theme, setTheme] = state('light', { key: 'theme' });\n * 4. Global Resource: const [user, actions] = state(fetchUser, { key: 'user' });\n * 5. Computed (Derived): const [double] = state(() => count() * 2);\n * \n * @param initialValueOrFetcher - Initial value, or a function to derive/fetch state.\n * @param options - Optional settings (e.g., global key).\n */\nexport function state<T>(\n initialValueOrFetcher: T | ((...args: any[]) => T | Promise<T>), \n options?: { key?: string }\n): [StateGetter<T>, StateSetter<T>] {\n let s: any;\n let actions: any = {};\n\n const key = options?.key;\n \n // 1. Check Global Registry\n if (key && typeof key === 'string' && globalStateRegistry.has(key)) {\n const cached = globalStateRegistry.get(key)!;\n // We return the cached signal/resource directly\n // However, we need to reconstruct the getter/setter wrapper to match the return type\n // But wait, we need to know if the cached one is a Resource or a Signal to attach correct actions?\n // Actually, we can store the *result tuple* or the *base signal object* and re-wrap.\n // Storing the base object (Signal or Resource) is better.\n s = cached;\n \n // Re-derive actions based on whether it's a resource or signal\n // Ideally, we should store the *actions* too if it's a resource.\n // Let's simplify: Signal/Resource objects already contain necessary methods.\n // For Resource: it has internal mechanics. But `createResource` returns [resource, actions].\n // We need to store the PAIR in the registry if we want to fully restore it.\n } \n \n // 2. Create New (if not in registry)\n else {\n if (typeof initialValueOrFetcher === 'function') {\n // It's a function -> Treat as Resource source\n // Pass the function as 'source' to createResource.\n // createResource tracks dependencies in 'source' execution.\n // The fetcher simply receives the result of source() and returns it.\n // This supports both sync computed values (fn returns value) and async resources (fn returns Promise).\n \n const fn = initialValueOrFetcher as () => any;\n \n const [res, resActions] = createResource(\n fn, \n async (val) => val // The value computed by fn is passed here\n );\n \n s = res;\n actions = resActions;\n } else {\n // It's a value -> Create Signal\n s = signal<T>(initialValueOrFetcher);\n }\n\n // Save to registry if key provided\n if (key && typeof key === 'string') {\n // We need to store both signal and actions to restore them later?\n // For Signal, actions is empty (just set).\n // For Resource, actions has refetch.\n // Let's store the object `s` and attach `actions` to it or store a wrapper?\n // Let's store the object `s` and attach `_actions` property to it for retrieval.\n (s as any)._stateActions = actions;\n globalStateRegistry.set(key, s);\n }\n }\n\n // If we retrieved from cache, retrieve actions\n if (!actions.refetch && (s as any)._stateActions) {\n actions = (s as any)._stateActions;\n }\n\n // 3. Construct Return Tuple\n \n // Getter Wrapper\n const getter = (() => s.value) as StateGetter<T>;\n \n // Attach Resource properties to getter (if available)\n // This allows access like: data.loading, data.error\n Object.defineProperties(getter, {\n loading: { get: () => (s as any).loading || false },\n error: { get: () => (s as any).error },\n state: { get: () => (s as any).state || 'ready' },\n latest: { get: () => (s as any).latest ?? s.peek() },\n read: { value: (s as any).read || (() => s.value) }\n });\n\n // Setter Wrapper\n const setter = ((newValue: T | ((prev: T) => T)) => {\n // Use mutate if available (Resource), otherwise set (Signal)\n if (actions.mutate) {\n // Resource mutate usually takes value directly. \n // If function, we need to handle it manually.\n if (typeof newValue === 'function') {\n const fn = newValue as (prev: T) => T;\n actions.mutate(fn(s.peek()));\n } else {\n actions.mutate(newValue);\n }\n } else {\n // Signal set\n if (typeof newValue === 'function') {\n const fn = newValue as (prev: T) => T;\n s.value = fn(s.peek());\n } else {\n s.value = newValue;\n }\n }\n }) as StateSetter<T>;\n\n // Attach Actions to setter\n setter.mutate = actions.mutate || ((v: T) => { \n if (typeof v === 'function') s.value = (v as any)(s.peek());\n else s.value = v; \n });\n setter.refetch = actions.refetch || (() => { /* no-op for simple state */ });\n\n return [getter, setter];\n}\n\n/**\n * Clear all global states (useful for testing or resetting app)\n */\nexport function clearGlobalState() {\n globalStateRegistry.clear();\n}\n"]}