@deijose/nix-js 2.2.1 → 2.3.0

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.
@@ -1,2 +1,2 @@
1
- export { Signal, signal, effect, computed, batch, watch, untrack, nextTick, html, repeat, ref, showWhen, portal, createPortalOutlet, portalOutlet, provideOutlet, injectOutlet, createErrorBoundary, transition, mount, NixComponent, createStore, createRouter, RouterView, Link, nixRouter, RouterKey, suspend, lazy, provide, inject, createInjectionKey, nixField, nixFieldArray, createForm, required, minLength, maxLength, email, pattern, min, max, createValidator, validators, extendValidators, } from "./nix";
2
- export type { WatchOptions, NixTemplate, NixMountHandle, MountOptions, KeyedList, NixRef, PortalOutlet, ErrorFallback, TransitionOptions, TransitionContent, Store, StoreSignals, Router, NamedRouteLocation, RouteLocation, RouteRecord, RouterOptions, NavigationGuard, NavigationGuardResult, AfterEachHook, ResolvedRoute, ScrollPosition, ScrollBehavior, RouterMode, SuspenseOptions, InjectionKey, Validator, ValidateOn, FieldState, FieldArrayState, FieldErrors, FormState, FormOptions, ValidatorsBase, NixChildren, } from "./nix";
1
+ export { Signal, signal, effect, computed, batch, watch, untrack, nextTick, html, repeat, ref, showWhen, portal, createPortalOutlet, portalOutlet, provideOutlet, injectOutlet, createErrorBoundary, transition, mount, NixComponent, createStore, persistPlugin, loggerPlugin, guardPlugin, bridgePlugin, createRouter, RouterView, Link, nixRouter, RouterKey, suspend, lazy, provide, inject, createInjectionKey, nixField, nixFieldArray, createForm, required, minLength, maxLength, email, pattern, min, max, createValidator, validators, extendValidators, } from "./nix";
2
+ export type { WatchOptions, NixTemplate, NixMountHandle, MountOptions, KeyedList, NixRef, PortalOutlet, ErrorFallback, TransitionOptions, TransitionContent, Store, StoreSignals, NixPlugin, Router, NamedRouteLocation, RouteLocation, RouteRecord, RouterOptions, NavigationGuard, NavigationGuardResult, AfterEachHook, ResolvedRoute, ScrollPosition, ScrollBehavior, RouterMode, SuspenseOptions, InjectionKey, Validator, ValidateOn, FieldState, FieldArrayState, FieldErrors, FormState, FormOptions, ValidatorsBase, NixChildren, } from "./nix";
@@ -7,7 +7,8 @@ export type { MountOptions } from "./component";
7
7
  export { NixComponent } from "./lifecycle";
8
8
  export type { NixChildren } from "./lifecycle";
9
9
  export { createStore } from "./store";
10
- export type { Store, StoreSignals } from "./store";
10
+ export type { Store, StoreSignals, NixPlugin } from "./store";
11
+ export { persistPlugin, loggerPlugin, guardPlugin, bridgePlugin } from "./plugins";
11
12
  export { createRouter, RouterView, Link, nixRouter, RouterKey } from "./router";
12
13
  export type { Router, NamedRouteLocation, RouteLocation, RouteRecord, RouterOptions, NavigationGuard, NavigationGuardResult, AfterEachHook, ResolvedRoute, ScrollPosition, ScrollBehavior, RouterMode, } from "./router";
13
14
  export { suspend, lazy } from "./async";
@@ -1,16 +1,52 @@
1
1
  import { type NixPlugin, type Store } from "./store";
2
+ /**
3
+ * Minimal interface that any storage adapter must implement.
4
+ * Compatible with localStorage, sessionStorage, AsyncStorage, IndexedDB, etc.
5
+ */
6
+ interface StorageAdapter {
7
+ /** Retrieves an item from storage. Returns null if not found. */
8
+ getItem(key: string): string | null | Promise<string | null>;
9
+ /** Stores an item in storage. */
10
+ setItem(key: string, value: string): void | Promise<void>;
11
+ /** Optional: Removes an item from storage. */
12
+ removeItem?(key: string): void | Promise<void>;
13
+ }
14
+ /**
15
+ * Persists the store state to a storage medium (defaults to localStorage).
16
+ * Automatically hydrates on initialization and saves changes on every reactive flush.
17
+ */
2
18
  export declare function persistPlugin<T extends Record<string, unknown>>(storageKey: string, opts?: {
3
- /** Keys a excluir del storage (ej: tokens de sesión efímeros). */
19
+ /** Storage adapter to use. Defaults to localStorage. */
20
+ storage?: StorageAdapter;
21
+ /** Properties to exclude from persistence. */
4
22
  exclude?: Array<keyof T>;
5
- /** Serialización custom. Por defecto JSON.stringify. */
23
+ /** Custom serialization function. Defaults to JSON.stringify. */
6
24
  serialize?: (state: T) => string;
25
+ /** Custom deserialization function. Defaults to JSON.parse. */
7
26
  deserialize?: (raw: string) => Partial<T>;
27
+ /** Debounce interval in milliseconds for batching writes. Defaults to 0 (immediate). */
28
+ debounce?: number;
8
29
  }): NixPlugin<T>;
30
+ /**
31
+ * Logs state transitions to the console.
32
+ * Calculates property-level diffs and groups output by store ID.
33
+ */
9
34
  export declare function loggerPlugin<T extends Record<string, unknown>>(opts?: {
35
+ /** Whether the console group should be collapsed by default. */
10
36
  collapsed?: boolean;
37
+ /** Optional filter to skip logging for specific changes. */
11
38
  filter?: (diff: Partial<T>) => boolean;
12
39
  }): NixPlugin<T>;
40
+ /**
41
+ * Function type for mutation guards.
42
+ */
13
43
  type GuardFn<T extends Record<string, unknown>> = (next: Partial<T>, current: T) => Partial<T> | void;
44
+ /**
45
+ * Intercepts $patch and $reset calls to validate or transform state before it is applied.
46
+ */
14
47
  export declare function guardPlugin<T extends Record<string, unknown>>(guards: GuardFn<T>[]): NixPlugin<T>;
48
+ /**
49
+ * Synchronizes data between two stores by watching one and patching the other.
50
+ */
15
51
  export declare function bridgePlugin<TA extends Record<string, unknown>, TB extends Record<string, unknown>>(sourceStore: Store<TB>, sync: (sourceState: TB, targetStore: Store<TA>) => void): NixPlugin<TA>;
16
52
  export {};
@@ -1,60 +1,125 @@
1
1
  import { Signal, type WatchOptions } from "./reactivity";
2
- export type StoreSignals<T extends Record<string, unknown>> = {
2
+ /**
3
+ * Maps a plain state object T into a record of Signals — one per key.
4
+ * This is what the actions/getters factories receive as their argument.
5
+ *
6
+ * Given: { count: number, name: string }
7
+ * Becomes: { count: Signal<number>, name: Signal<string> }
8
+ */
9
+ export type StoreSignals<T extends object> = {
3
10
  readonly [K in keyof T]: Signal<T[K]>;
4
11
  };
12
+ /**
13
+ * A read-only Signal — extends Signal so it satisfies `instanceof Signal`
14
+ * checks (used by `watch()`), but throws on any mutation attempt.
15
+ */
5
16
  export declare class ReadonlySignal<T> extends Signal<T> {
6
17
  private readonly label;
7
18
  constructor(source: Signal<T>, label?: string);
8
19
  }
20
+ /**
21
+ * Maps a getters factory result (a record of Signals) into a record of
22
+ * read-only Signals exposed on the store.
23
+ *
24
+ * The Omit pattern enforces `readonly value` at the type level — TypeScript
25
+ * blocks `getter.value = x` at compile time. The runtime ReadonlySignal class
26
+ * also throws on mutation as defense in depth.
27
+ */
9
28
  export type StoreGetters<G extends Record<string, Signal<unknown>>> = {
10
- readonly [K in keyof G]: ReadonlySignal<G[K] extends Signal<infer V> ? V : never>;
29
+ readonly [K in keyof G]: Omit<ReadonlySignal<G[K] extends Signal<infer V> ? V : never>, "value"> & {
30
+ readonly value: G[K] extends Signal<infer V> ? V : never;
31
+ };
11
32
  };
12
- export type Store<T extends Record<string, unknown>, A extends Record<string, unknown> = Record<never, never>, G extends Record<string, Signal<unknown>> = Record<never, never>> = StoreSignals<T> & A & StoreGetters<G> & {
33
+ /**
34
+ * The full store type — combines reactive state signals, action methods,
35
+ * computed getters (as ReadonlySignals), and the framework-level $-prefixed API.
36
+ */
37
+ export type Store<T extends object, A extends object = Record<never, never>, G extends Record<string, Signal<unknown>> = Record<never, never>> = StoreSignals<T> & A & StoreGetters<G> & {
13
38
  readonly $id: string;
14
- /** Current snapshot — reading inside effect/computed creates subscription. */
39
+ /** Reactive snapshot — reading inside effect/computed creates a subscription to the whole state. */
15
40
  readonly $state: T;
16
41
  /**
17
- * The computed Signal that backs $state.
18
- * Plugins receive this to compose new reactive nodes on top.
19
- * This is what makes the plugin system reactive-native:
20
- * no hooks, just signals all the way down.
42
+ * Passive snapshot returns the current state values WITHOUT creating
43
+ * a reactive subscription. Use this in plugins, loggers, persistence,
44
+ * or anywhere you need a one-shot read.
45
+ */
46
+ $snapshot(): T;
47
+ /**
48
+ * The computed Signal that backs $state. Plugins receive this to
49
+ * compose new reactive nodes on top.
21
50
  */
22
51
  readonly $stateSignal: ReadonlySignal<T>;
23
52
  /** Reset to initial values (batched). */
24
53
  $reset(): void;
25
54
  /** Partial update (batched). */
26
55
  $patch(partial: Partial<T>): void;
27
- /**
28
- * Watches state changes. This is exactly watch() from reactivity.ts —
29
- * no new primitive to learn.
30
- */
56
+ /** Watches state changes. Equivalent to `watch(store.$stateSignal, cb, opts)`. */
31
57
  $watch(cb: (next: T, prev: T | undefined) => void, options?: WatchOptions): () => void;
32
58
  /** Disposes the store and runs all plugin cleanups. */
33
59
  $dispose(): void;
34
60
  };
61
+ export type ActionsFactory<T extends object, A extends object> = (signals: StoreSignals<T>) => A;
62
+ export type GettersFactory<T extends object, G extends Record<string, Signal<unknown>>> = (signals: StoreSignals<T>) => G;
35
63
  /**
36
64
  * A NixPlugin is a function that receives the assembled store and
37
- * optionally returns a cleanup function.
65
+ * optionally returns a cleanup function called on $dispose().
38
66
  *
39
- * There are NO lifecycle hooks. The plugin extends the signal graph directly:
67
+ * There are NO lifecycle hooks. Plugins extend the signal graph directly
68
+ * using the framework primitives:
40
69
  *
41
70
  * watch(store.$stateSignal, ...) — react to any state change
42
71
  * computed(() => store.someSignal.value) — derive new nodes
43
- * store.mySignal = signal(...) attach new reactive state
44
- * wrapMethod(store, '$patch', ...) — intercept mutations
45
- *
46
- * Because plugins only use Nix.js primitives, they compose with each other
47
- * naturally one plugin can observe a signal that another plugin added.
72
+ * store.$snapshot() passive read for logging/persistence
73
+ */
74
+ export type NixPlugin<T extends object, A extends object = Record<never, never>, G extends Record<string, Signal<unknown>> = Record<never, never>> = (store: Store<T, A, G>) => (() => void) | void;
75
+ /**
76
+ * Options object for createStore. NoInfer<T> on factory parameters ensures
77
+ * T is inferred ONLY from initialState, never from the factories — this is
78
+ * what restores the type inference that broke in v2.2.1.
48
79
  */
49
- export type NixPlugin<T extends Record<string, unknown>, A extends Record<string, unknown> = Record<never, never>, G extends Record<string, Signal<unknown>> = Record<never, never>> = (store: Store<T, A, G>) => (() => void) | void;
50
- export interface CreateStoreOptions<T extends Record<string, unknown>, A extends Record<string, unknown> = Record<never, never>, G extends Record<string, Signal<unknown>> = Record<never, never>> {
51
- /** Display name for the store. Used in error messages and devtools. */
80
+ export type CreateStoreOptions<T extends object, A extends object, G extends Record<string, Signal<unknown>>> = {
81
+ /** Display name. Used in error messages, devtools, and $id. */
52
82
  name?: string;
53
- /** Factory that receives the raw signals and returns action methods. */
54
- actions?: (signals: StoreSignals<T>) => A;
55
- /** Factory that receives the raw signals and returns computed getters. */
56
- getters?: (signals: StoreSignals<T>) => G;
83
+ /** Action factory receives raw signals, returns methods exposed on the store. */
84
+ actions?: (signals: StoreSignals<NoInfer<T>>) => A;
85
+ /** Getter factory receives raw signals, returns computed Signals exposed as ReadonlySignals. */
86
+ getters?: (signals: StoreSignals<NoInfer<T>>) => G;
57
87
  /** Plugins to extend the store. Each receives the assembled store. */
58
- plugins?: NixPlugin<T, A, G>[];
59
- }
60
- export declare function createStore<T extends Record<string, unknown>, A extends Record<string, unknown> = Record<never, never>, G extends Record<string, Signal<unknown>> = Record<never, never>>(initialState: T, options?: CreateStoreOptions<T, A, G>): Store<T, A, G>;
88
+ plugins?: NixPlugin<NoInfer<T>, A, G>[];
89
+ };
90
+ /**
91
+ * Creates a reactive store with optional actions, getters, and plugins.
92
+ *
93
+ * @example Just state
94
+ * ```ts
95
+ * const counter = createStore({ count: 0 });
96
+ * counter.count.value++;
97
+ * ```
98
+ *
99
+ * @example State + actions
100
+ * ```ts
101
+ * const counter = createStore({ count: 0 }, {
102
+ * actions: (s) => ({ increment: () => s.count.value++ }),
103
+ * });
104
+ * counter.increment();
105
+ * ```
106
+ *
107
+ * @example State + getters (no actions)
108
+ * ```ts
109
+ * const inventory = createStore({ items: [] as Item[] }, {
110
+ * getters: (s) => ({ count: computed(() => s.items.value.length) }),
111
+ * });
112
+ * inventory.count.value;
113
+ * ```
114
+ *
115
+ * @example Full store
116
+ * ```ts
117
+ * const cart = createStore({ items: [] as Item[], discount: 0 }, {
118
+ * name: "cart",
119
+ * actions: (s) => ({ addItem: (i: Item) => { s.items.value = [...s.items.value, i] } }),
120
+ * getters: (s) => ({ total: computed(() => s.items.value.reduce((sum, i) => sum + i.price, 0) * (1 - s.discount.value)) }),
121
+ * plugins: [persistPlugin({ key: "cart" })],
122
+ * });
123
+ * ```
124
+ */
125
+ export declare function createStore<T extends object, A extends object = Record<never, never>, G extends Record<string, Signal<unknown>> = Record<never, never>>(initialState: T, options?: CreateStoreOptions<T, A, G>): Store<T, A, G>;
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./signals.cjs"),t=require("./template2.cjs"),n=require("./lifecycle.cjs"),r=require("./context.cjs"),i=require("./router.cjs"),a=require("./component.cjs"),o=require("./store.cjs"),s=require("./async.cjs"),c=require("./form.cjs");exports.Link=i.Link,exports.NixComponent=n.NixComponent,exports.RouterKey=i.RouterKey,exports.RouterView=i.RouterView,exports.Signal=e.Signal,exports.batch=e.batch,exports.computed=e.computed,exports.createErrorBoundary=t.t,exports.createForm=c.createForm,exports.createInjectionKey=r.createInjectionKey,exports.createPortalOutlet=t.n,exports.createRouter=i.createRouter,exports.createStore=o.createStore,exports.createValidator=c.createValidator,exports.effect=e.effect,exports.email=c.email,exports.extendValidators=c.extendValidators,exports.html=t.l,exports.inject=r.inject,exports.injectOutlet=t.r,exports.lazy=s.lazy,exports.max=c.max,exports.maxLength=c.maxLength,exports.min=c.min,exports.minLength=c.minLength,exports.mount=a.mount,exports.nextTick=e.nextTick,exports.nixField=c.nixField,exports.nixFieldArray=c.nixFieldArray,exports.nixRouter=i.nixRouter,exports.pattern=c.pattern,exports.portal=t.i,exports.portalOutlet=t.a,exports.provide=r.provide,exports.provideOutlet=t.o,exports.ref=t.h,exports.repeat=t.d,exports.required=c.required,exports.showWhen=t.u,exports.signal=e.signal,exports.suspend=s.suspend,exports.transition=t.s,exports.untrack=e.untrack,exports.validators=c.validators,exports.watch=e.watch;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./signals.cjs"),t=require("./template2.cjs"),n=require("./lifecycle.cjs"),r=require("./context.cjs"),i=require("./router.cjs"),a=require("./component.cjs"),o=require("./store.cjs"),s=require("./plugins.cjs"),c=require("./async.cjs"),l=require("./form.cjs");exports.Link=i.Link,exports.NixComponent=n.NixComponent,exports.RouterKey=i.RouterKey,exports.RouterView=i.RouterView,exports.Signal=e.Signal,exports.batch=e.batch,exports.bridgePlugin=s.bridgePlugin,exports.computed=e.computed,exports.createErrorBoundary=t.t,exports.createForm=l.createForm,exports.createInjectionKey=r.createInjectionKey,exports.createPortalOutlet=t.n,exports.createRouter=i.createRouter,exports.createStore=o.createStore,exports.createValidator=l.createValidator,exports.effect=e.effect,exports.email=l.email,exports.extendValidators=l.extendValidators,exports.guardPlugin=s.guardPlugin,exports.html=t.l,exports.inject=r.inject,exports.injectOutlet=t.r,exports.lazy=c.lazy,exports.loggerPlugin=s.loggerPlugin,exports.max=l.max,exports.maxLength=l.maxLength,exports.min=l.min,exports.minLength=l.minLength,exports.mount=a.mount,exports.nextTick=e.nextTick,exports.nixField=l.nixField,exports.nixFieldArray=l.nixFieldArray,exports.nixRouter=i.nixRouter,exports.pattern=l.pattern,exports.persistPlugin=s.persistPlugin,exports.portal=t.i,exports.portalOutlet=t.a,exports.provide=r.provide,exports.provideOutlet=t.o,exports.ref=t.h,exports.repeat=t.d,exports.required=l.required,exports.showWhen=t.u,exports.signal=e.signal,exports.suspend=c.suspend,exports.transition=t.s,exports.untrack=e.untrack,exports.validators=l.validators,exports.watch=e.watch;
@@ -1 +1 @@
1
- import{Signal as e,batch as t,computed as n,effect as r,nextTick as i,signal as a,untrack as o,watch as s}from"./signals.js";import{a as c,d as l,h as u,i as d,l as f,n as p,o as m,r as h,s as g,t as _,u as v}from"./template2.js";import{NixComponent as y}from"./lifecycle.js";import{createInjectionKey as b,inject as x,provide as S}from"./context.js";import{Link as C,RouterKey as w,RouterView as T,createRouter as E,nixRouter as D}from"./router.js";import{mount as O}from"./component.js";import{createStore as k}from"./store.js";import{lazy as A,suspend as j}from"./async.js";import{createForm as M,createValidator as N,email as P,extendValidators as F,max as I,maxLength as L,min as R,minLength as z,nixField as B,nixFieldArray as V,pattern as H,required as U,validators as W}from"./form.js";export{C as Link,y as NixComponent,w as RouterKey,T as RouterView,e as Signal,t as batch,n as computed,_ as createErrorBoundary,M as createForm,b as createInjectionKey,p as createPortalOutlet,E as createRouter,k as createStore,N as createValidator,r as effect,P as email,F as extendValidators,f as html,x as inject,h as injectOutlet,A as lazy,I as max,L as maxLength,R as min,z as minLength,O as mount,i as nextTick,B as nixField,V as nixFieldArray,D as nixRouter,H as pattern,d as portal,c as portalOutlet,S as provide,m as provideOutlet,u as ref,l as repeat,U as required,v as showWhen,a as signal,j as suspend,g as transition,o as untrack,W as validators,s as watch};
1
+ import{Signal as e,batch as t,computed as n,effect as r,nextTick as i,signal as a,untrack as o,watch as s}from"./signals.js";import{a as c,d as l,h as u,i as d,l as f,n as p,o as m,r as h,s as g,t as _,u as v}from"./template2.js";import{NixComponent as y}from"./lifecycle.js";import{createInjectionKey as b,inject as x,provide as S}from"./context.js";import{Link as C,RouterKey as w,RouterView as T,createRouter as E,nixRouter as D}from"./router.js";import{mount as O}from"./component.js";import{createStore as k}from"./store.js";import{bridgePlugin as A,guardPlugin as j,loggerPlugin as M,persistPlugin as N}from"./plugins.js";import{lazy as P,suspend as F}from"./async.js";import{createForm as I,createValidator as L,email as R,extendValidators as z,max as B,maxLength as V,min as H,minLength as U,nixField as W,nixFieldArray as G,pattern as K,required as q,validators as J}from"./form.js";export{C as Link,y as NixComponent,w as RouterKey,T as RouterView,e as Signal,t as batch,A as bridgePlugin,n as computed,_ as createErrorBoundary,I as createForm,b as createInjectionKey,p as createPortalOutlet,E as createRouter,k as createStore,L as createValidator,r as effect,R as email,z as extendValidators,j as guardPlugin,f as html,x as inject,h as injectOutlet,P as lazy,M as loggerPlugin,B as max,V as maxLength,H as min,U as minLength,O as mount,i as nextTick,W as nixField,G as nixFieldArray,D as nixRouter,K as pattern,N as persistPlugin,d as portal,c as portalOutlet,S as provide,m as provideOutlet,u as ref,l as repeat,q as required,v as showWhen,a as signal,F as suspend,g as transition,o as untrack,J as validators,s as watch};
@@ -0,0 +1 @@
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./signals.cjs");function t(t,r={}){let{storage:n=localStorage,exclude:o=[],serialize:l=JSON.stringify,deserialize:i=JSON.parse,debounce:c=0}=r;return r=>{let s;return e.untrack(async()=>{try{let e=await n.getItem(t);if(!e)return;let l=i(e),c={};for(let e of Object.keys(l))e in r.$state&&!o.includes(e)&&(c[e]=l[e]);Object.keys(c).length>0&&r.$patch(c)}catch{}}),e.watch(r.$stateSignal,e=>{let r=()=>{try{let r=0===o.length?e:Object.fromEntries(Object.entries(e).filter(([e])=>!o.includes(e)));n.setItem(t,l(r))}catch{}};c>0?(clearTimeout(s),s=setTimeout(r,c)):r()})}}function n(t={}){let{collapsed:r=!0,filter:n}=t;return t=>{let o=r?console.groupCollapsed:console.group;return e.watch(t.$stateSignal,(e,r)=>{if(!r)return;let l={};for(let t of Object.keys(e))Object.is(e[t],r[t])||(l[t]=e[t]);0!==Object.keys(l).length&&(n&&!n(l)||(o(`%c[nix:${t.$id}]%c ${Object.keys(l).join(", ")}`,"color:#7F77DD;font-weight:500","color:inherit;font-weight:400"),console.log("prev →",r),console.log("next →",e),console.log("diff →",l),console.groupEnd()))},{immediate:!0})}}function r(t){return r=>{let n=r.$patch.bind(r),o=r.$reset.bind(r);return r.$patch=function(o){let l=o,i=e.untrack(()=>r.$state);for(let e of t){let t=e(l,i);void 0!==t&&(l=t)}n(l)},r.$reset=function(){let n=e.untrack(()=>r.$state);for(let e of t)e(n,n);o()},()=>{r.$patch=n,r.$reset=o}}}function i(t,r){return n=>e.watch(t.$stateSignal,e=>{r(e,n)})}exports.bridgePlugin=i,exports.guardPlugin=r,exports.loggerPlugin=n,exports.persistPlugin=t;
@@ -0,0 +1 @@
1
+ import{untrack as e,watch as t}from"./signals.js";function n(n,r={}){let{storage:l=localStorage,exclude:o=[],serialize:i=JSON.stringify,deserialize:s=JSON.parse,debounce:c=0}=r;return r=>{let a;return e(async()=>{try{let e=await l.getItem(n);if(!e)return;let t=s(e),i={};for(let e of Object.keys(t))e in r.$state&&!o.includes(e)&&(i[e]=t[e]);Object.keys(i).length>0&&r.$patch(i)}catch{}}),t(r.$stateSignal,e=>{let t=()=>{try{let t=0===o.length?e:Object.fromEntries(Object.entries(e).filter(([e])=>!o.includes(e)));l.setItem(n,i(t))}catch{}};c>0?(clearTimeout(a),a=setTimeout(t,c)):t()})}}function r(e={}){let{collapsed:n=!0,filter:r}=e;return e=>{let l=n?console.groupCollapsed:console.group;return t(e.$stateSignal,(t,n)=>{if(!n)return;let o={};for(let e of Object.keys(t))Object.is(t[e],n[e])||(o[e]=t[e]);0!==Object.keys(o).length&&(r&&!r(o)||(l(`%c[nix:${e.$id}]%c ${Object.keys(o).join(", ")}`,"color:#7F77DD;font-weight:500","color:inherit;font-weight:400"),console.log("prev →",n),console.log("next →",t),console.log("diff →",o),console.groupEnd()))},{immediate:!0})}}function i(t){return n=>{let r=n.$patch.bind(n),l=n.$reset.bind(n);return n.$patch=function(l){let o=l,i=e(()=>n.$state);for(let e of t){let t=e(o,i);void 0!==t&&(o=t)}r(o)},n.$reset=function(){let r=e(()=>n.$state);for(let e of t)e(r,r);l()},()=>{n.$patch=r,n.$reset=l}}}function a(e,n){return r=>t(e.$stateSignal,e=>{n(e,r)})}export{a as bridgePlugin,i as guardPlugin,r as loggerPlugin,n as persistPlugin};
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./signals.cjs");var t=class extends e.Signal{label;constructor(e,t="ReadonlySignal"){super(e.peek()),this.label=t,Object.defineProperty(this,"value",{get:()=>e.value,set:()=>{throw Error(`[Nix] "${this.label}" is read-only.`)},configurable:!1}),this.update=()=>{throw Error(`[Nix] "${this.label}" is read-only.`)},this.dispose=()=>{throw Error(`[Nix] Cannot dispose "${this.label}" directly.`)}}},n=new Set(["$id","$state","$stateSignal","$reset","$patch","$watch","$dispose"]);function r(e){if("__proto__"===e||"constructor"===e||"prototype"===e)throw Error(`[Nix] Store key "${e}" is not allowed for security reasons.`);if(n.has(e))throw Error(`[Nix] Store key "${e}" is reserved.`)}function i(e,t){return!n.has(e)||(console.warn(`[Nix] Store ${t} "${e}" is reserved and will be ignored.`),!1)}function a(e,r){return new t(e,r)}function o(t,o={}){let{name:l="store",actions:s,getters:c,plugins:f=[]}=o,u=Object.keys(t),d={};for(let o of u)r(o),d[o]=e.signal(t[o]);let g,b=d,$=e.computed(()=>{let e={};for(let t of u)e[t]=d[t].value;return e}),h=a($,`store "${l}".$stateSignal`);try{g=structuredClone(t)}catch(e){throw Error(`[Nix] Store "${l}" initialState contains non-serializable data (functions, DOM nodes, Symbols, or WeakRefs). Remove these before creating the store. Original error: ${e}`)}let p=Object.assign(Object.create(null),b,{$reset:function(){e.batch(()=>{for(let e of u)d[e].value=g[e]})},$patch:function(t){e.batch(()=>{for(let e of Object.keys(t))Object.prototype.hasOwnProperty.call(d,e)&&(d[e].value=t[e])})},$watch:function(t,r){return e.watch($,t,r)}});Object.defineProperty(p,"$id",{value:l,writable:!1,enumerable:!1,configurable:!1}),Object.defineProperty(p,"$state",{get:()=>$.value,enumerable:!0,configurable:!1}),Object.defineProperty(p,"$stateSignal",{value:h,writable:!1,enumerable:!1,configurable:!1});let y=new Set([...u,...Array.from(n)]);if(s){let e=s(b);for(let t of Object.keys(e))if(i(t,"action")){if(y.has(t)){console.warn(`[Nix] Store "${l}": action "${t}" collides with an existing signal or getter and will be ignored.`);continue}y.add(t),p[t]=e[t]}}if(c){let t=c(b);for(let r of Object.keys(t)){if(!i(r,"getter"))continue;if(y.has(r)){console.warn(`[Nix] Store "${l}": getter "${r}" collides with an existing signal or action and will be ignored.`);continue}let o=t[r];if(!(o instanceof e.Signal))throw TypeError(`[Nix] Store "${l}": getter "${r}" must return a Signal (wrap it with computed()). Got: ${typeof o}`);y.add(r),p[r]=a(o,`getter "${r}" in store "${l}"`)}}let w=[()=>$.dispose()];for(let e of f)try{let t=e(p);"function"==typeof t&&w.push(t)}catch(e){console.error(`[Nix] Plugin initialization failed for store "${l}":`,e)}return p.$dispose=()=>{for(let e of w)e()},p}exports.ReadonlySignal=t,exports.createStore=o;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./signals.cjs");var t=class extends e.Signal{label;constructor(e,t="ReadonlySignal"){super(e.peek()),this.label=t,Object.defineProperty(this,"value",{get:()=>e.value,set:()=>{throw Error(`[Nix] "${this.label}" is read-only.`)},configurable:!1}),this.update=()=>{throw Error(`[Nix] "${this.label}" is read-only.`)},this.dispose=()=>{throw Error(`[Nix] Cannot dispose "${this.label}" directly.`)}}},n=new Set(["$id","$state","$stateSignal","$snapshot","$reset","$patch","$watch","$dispose"]);function r(e){if("__proto__"===e||"constructor"===e||"prototype"===e)throw Error(`[Nix] Store key "${e}" is not allowed for security reasons.`);if(n.has(e))throw Error(`[Nix] Store key "${e}" is reserved.`)}function i(e,t){return!n.has(e)||(console.warn(`[Nix] Store ${t} "${e}" is reserved and will be ignored.`),!1)}function a(e,r){return new t(e,r)}function o(t,o){let{name:l="store",actions:s,getters:c,plugins:f=[]}=o??{},u=Object.keys(t),d={};for(let o of u)r(o),d[o]=e.signal(t[o]);let $,g=d,h=e.computed(()=>{let e={};for(let t of u)e[t]=d[t].value;return e}),b=a(h,`store "${l}".$stateSignal`);try{$=structuredClone(t)}catch(e){throw Error(`[Nix] Store "${l}" initialState contains non-serializable data (functions, DOM nodes, Symbols, or WeakRefs). Remove these before creating the store. Original error: ${e}`)}let p=Object.assign(Object.create(null),g,{$reset:function(){e.batch(()=>{for(let e of u)d[e].value=$[e]})},$patch:function(t){e.batch(()=>{for(let e of Object.keys(t))Object.prototype.hasOwnProperty.call(d,e)&&(d[e].value=t[e])})},$watch:function(t,r){return e.watch(h,t,r)},$snapshot:function(){let e={};for(let t of u)e[t]=d[t].peek();return e}});Object.defineProperty(p,"$id",{value:l,writable:!1,enumerable:!1,configurable:!1}),Object.defineProperty(p,"$state",{get:()=>h.value,enumerable:!0,configurable:!1}),Object.defineProperty(p,"$stateSignal",{value:b,writable:!1,enumerable:!1,configurable:!1});let y=new Set([...u,...Array.from(n)]);if(s){let e=s(g);for(let t of Object.keys(e))if(i(t,"action")){if(y.has(t)){console.warn(`[Nix] Store "${l}": action "${t}" collides with an existing signal or getter and will be ignored.`);continue}y.add(t),p[t]=e[t]}}if(c){let t=c(g);for(let r of Object.keys(t)){if(!i(r,"getter"))continue;if(y.has(r)){console.warn(`[Nix] Store "${l}": getter "${r}" collides with an existing signal or action and will be ignored.`);continue}let o=t[r];if(!(o instanceof e.Signal))throw TypeError(`[Nix] Store "${l}": getter "${r}" must return a Signal (wrap it with computed()). Got: ${typeof o}`);y.add(r),p[r]=a(o,`getter "${r}" in store "${l}"`)}}let w=[()=>h.dispose()];for(let e of f)try{let t=e(p);"function"==typeof t&&w.push(t)}catch(e){console.error(`[Nix] Plugin initialization failed for store "${l}":`,e)}return p.$dispose=()=>{for(let e of w)e()},p}exports.ReadonlySignal=t,exports.createStore=o;
package/dist/lib/store.js CHANGED
@@ -1 +1 @@
1
- import{Signal as e,batch as t,computed as n,signal as r,watch as i}from"./signals.js";var a=class extends e{label;constructor(e,t="ReadonlySignal"){super(e.peek()),this.label=t,Object.defineProperty(this,"value",{get:()=>e.value,set:()=>{throw Error(`[Nix] "${this.label}" is read-only.`)},configurable:!1}),this.update=()=>{throw Error(`[Nix] "${this.label}" is read-only.`)},this.dispose=()=>{throw Error(`[Nix] Cannot dispose "${this.label}" directly.`)}}},o=new Set(["$id","$state","$stateSignal","$reset","$patch","$watch","$dispose"]);function s(e){if("__proto__"===e||"constructor"===e||"prototype"===e)throw Error(`[Nix] Store key "${e}" is not allowed for security reasons.`);if(o.has(e))throw Error(`[Nix] Store key "${e}" is reserved.`)}function c(e,t){return!o.has(e)||(console.warn(`[Nix] Store ${t} "${e}" is reserved and will be ignored.`),!1)}function l(e,t){return new a(e,t)}function u(a,f={}){let{name:u="store",actions:d,getters:$,plugins:g=[]}=f,h=Object.keys(a),b={};for(let e of h)s(e),b[e]=r(a[e]);let p,w=b,y=n(()=>{let e={};for(let t of h)e[t]=b[t].value;return e}),S=l(y,`store "${u}".$stateSignal`);try{p=structuredClone(a)}catch(e){throw Error(`[Nix] Store "${u}" initialState contains non-serializable data (functions, DOM nodes, Symbols, or WeakRefs). Remove these before creating the store. Original error: ${e}`)}let x=Object.assign(Object.create(null),w,{$reset:function(){t(()=>{for(let e of h)b[e].value=p[e]})},$patch:function(e){t(()=>{for(let t of Object.keys(e))Object.prototype.hasOwnProperty.call(b,t)&&(b[t].value=e[t])})},$watch:function(e,t){return i(y,e,t)}});Object.defineProperty(x,"$id",{value:u,writable:!1,enumerable:!1,configurable:!1}),Object.defineProperty(x,"$state",{get:()=>y.value,enumerable:!0,configurable:!1}),Object.defineProperty(x,"$stateSignal",{value:S,writable:!1,enumerable:!1,configurable:!1});let O=new Set([...h,...Array.from(o)]);if(d){let e=d(w);for(let t of Object.keys(e))if(c(t,"action")){if(O.has(t)){console.warn(`[Nix] Store "${u}": action "${t}" collides with an existing signal or getter and will be ignored.`);continue}O.add(t),x[t]=e[t]}}if($){let t=$(w);for(let r of Object.keys(t)){if(!c(r,"getter"))continue;if(O.has(r)){console.warn(`[Nix] Store "${u}": getter "${r}" collides with an existing signal or action and will be ignored.`);continue}let o=t[r];if(!(o instanceof e))throw TypeError(`[Nix] Store "${u}": getter "${r}" must return a Signal (wrap it with computed()). Got: ${typeof o}`);O.add(r),x[r]=l(o,`getter "${r}" in store "${u}"`)}}let j=[()=>y.dispose()];for(let e of g)try{let t=e(x);"function"==typeof t&&j.push(t)}catch(e){console.error(`[Nix] Plugin initialization failed for store "${u}":`,e)}return x.$dispose=()=>{for(let e of j)e()},x}export{a as ReadonlySignal,u as createStore};
1
+ import{Signal as e,batch as t,computed as n,signal as r,watch as i}from"./signals.js";var a=class extends e{label;constructor(e,t="ReadonlySignal"){super(e.peek()),this.label=t,Object.defineProperty(this,"value",{get:()=>e.value,set:()=>{throw Error(`[Nix] "${this.label}" is read-only.`)},configurable:!1}),this.update=()=>{throw Error(`[Nix] "${this.label}" is read-only.`)},this.dispose=()=>{throw Error(`[Nix] Cannot dispose "${this.label}" directly.`)}}},o=new Set(["$id","$state","$stateSignal","$snapshot","$reset","$patch","$watch","$dispose"]);function s(e){if("__proto__"===e||"constructor"===e||"prototype"===e)throw Error(`[Nix] Store key "${e}" is not allowed for security reasons.`);if(o.has(e))throw Error(`[Nix] Store key "${e}" is reserved.`)}function c(e,t){return!o.has(e)||(console.warn(`[Nix] Store ${t} "${e}" is reserved and will be ignored.`),!1)}function l(e,t){return new a(e,t)}function u(a,f){let{name:u="store",actions:d,getters:$,plugins:h=[]}=f??{},g=Object.keys(a),p={};for(let e of g)s(e),p[e]=r(a[e]);let b,w=p,y=n(()=>{let e={};for(let t of g)e[t]=p[t].value;return e}),S=l(y,`store "${u}".$stateSignal`);try{b=structuredClone(a)}catch(e){throw Error(`[Nix] Store "${u}" initialState contains non-serializable data (functions, DOM nodes, Symbols, or WeakRefs). Remove these before creating the store. Original error: ${e}`)}let x=Object.assign(Object.create(null),w,{$reset:function(){t(()=>{for(let e of g)p[e].value=b[e]})},$patch:function(e){t(()=>{for(let t of Object.keys(e))Object.prototype.hasOwnProperty.call(p,t)&&(p[t].value=e[t])})},$watch:function(e,t){return i(y,e,t)},$snapshot:function(){let e={};for(let t of g)e[t]=p[t].peek();return e}});Object.defineProperty(x,"$id",{value:u,writable:!1,enumerable:!1,configurable:!1}),Object.defineProperty(x,"$state",{get:()=>y.value,enumerable:!0,configurable:!1}),Object.defineProperty(x,"$stateSignal",{value:S,writable:!1,enumerable:!1,configurable:!1});let O=new Set([...g,...Array.from(o)]);if(d){let e=d(w);for(let t of Object.keys(e))if(c(t,"action")){if(O.has(t)){console.warn(`[Nix] Store "${u}": action "${t}" collides with an existing signal or getter and will be ignored.`);continue}O.add(t),x[t]=e[t]}}if($){let t=$(w);for(let r of Object.keys(t)){if(!c(r,"getter"))continue;if(O.has(r)){console.warn(`[Nix] Store "${u}": getter "${r}" collides with an existing signal or action and will be ignored.`);continue}let o=t[r];if(!(o instanceof e))throw TypeError(`[Nix] Store "${u}": getter "${r}" must return a Signal (wrap it with computed()). Got: ${typeof o}`);O.add(r),x[r]=l(o,`getter "${r}" in store "${u}"`)}}let j=[()=>y.dispose()];for(let e of h)try{let t=e(x);"function"==typeof t&&j.push(t)}catch(e){console.error(`[Nix] Plugin initialization failed for store "${u}":`,e)}return x.$dispose=()=>{for(let e of j)e()},x}export{a as ReadonlySignal,u as createStore};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deijose/nix-js",
3
- "version": "2.2.1",
3
+ "version": "2.3.0",
4
4
  "description": "A lightweight, fully reactive micro-framework — no virtual DOM, no compiler, just signals and tagged templates.",
5
5
  "license": "MIT",
6
6
  "author": "Deiver Vasquez",