fractostate 1.0.2 → 2.0.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.
@@ -4,33 +4,93 @@ var react = require('react');
4
4
  var store = require('./store.js');
5
5
  var proxy = require('./proxy.js');
6
6
 
7
+ /***************************************************************************
8
+ * FractoState - Fast And Secure
9
+ *
10
+ * @author Nehonix
11
+ * @license NOSL
12
+ *
13
+ * Copyright (c) 2025 Nehonix. All rights reserved.
14
+ *
15
+ * This License governs the use, modification, and distribution of software
16
+ * provided by NEHONIX under its open source projects.
17
+ * NEHONIX is committed to fostering collaborative innovation while strictly
18
+ * protecting its intellectual property rights.
19
+ * Violation of any term of this License will result in immediate termination of all granted rights
20
+ * and may subject the violator to legal action.
21
+ *
22
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
23
+ * INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
24
+ * AND NON-INFRINGEMENT.
25
+ * IN NO EVENT SHALL NEHONIX BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
26
+ * OR CONSEQUENTIAL DAMAGES ARISING FROM THE USE OR INABILITY TO USE THE SOFTWARE,
27
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
28
+ *
29
+ ***************************************************************************** */
30
+ /**
31
+ * Defines a derived flow that automatically updates based on a source flow.
32
+ * No store registration happens here; it's purely a definition for the hook.
33
+ */
34
+ function defineDerived(source, selector, derivedKey) {
35
+ return {
36
+ key: derivedKey || `${source.key}_derived_${Date.now()}`,
37
+ source,
38
+ selector,
39
+ };
40
+ }
7
41
  /**
8
42
  * Defines a flow in a centralized manner.
9
43
  * Allows defining the key, initial state, and options in a single reusable object.
10
44
  */
11
45
  function defineFlow(key, initial, options) {
46
+ // If generic T is provided manually but A is not, we want A to be inferred from options
47
+ // However, explicit generics override inference.
48
+ // The user should ideally use `defineFlow('key', { ...initial }, { actions: ... })`
49
+ // without explicit generics to get full inference.
50
+ // Or provide both. This signature supports both but relies on inference for best DX.
12
51
  return { key, initial, options };
13
52
  }
14
- /**
15
- * Unique "All-in-One" hook to create or consume a FractoState flow.
16
- * - If called with a FlowDefinition or an initial value: initializes the flow if it doesn't exist.
17
- * - If called with just a key: connects to the existing flow.
18
- *
19
- * Returns a 2-element array: [state, toolbox]
20
- * where toolbox contains { ops, set, undo, redo, history, ... }
21
- */
53
+ // --- Implementation ---
22
54
  function useFlow(keyOrDef, maybeInitialValue, options = {}) {
23
- // Argument analysis to unify initialization and consumption
55
+ // 1. Identify if we are dealing with a Derived Flow
56
+ const isDerived = typeof keyOrDef === "object" &&
57
+ "selector" in keyOrDef &&
58
+ "source" in keyOrDef;
59
+ // --- Logic for DERIVED Flows (Read-Only Reactive) ---
60
+ if (isDerived) {
61
+ const derivedDef = keyOrDef;
62
+ const sourceKey = derivedDef.source.key;
63
+ const selector = derivedDef.selector;
64
+ const getSourceState = () => store.store.get(sourceKey, derivedDef.source.initial);
65
+ // Initial derived state
66
+ const [derivedState, setDerivedState] = react.useState(() => selector(getSourceState()));
67
+ react.useEffect(() => {
68
+ const unsub = store.store.subscribe(sourceKey, () => {
69
+ const nextSource = getSourceState();
70
+ const nextDerived = selector(nextSource);
71
+ setDerivedState((prev) => {
72
+ if (prev === nextDerived)
73
+ return prev;
74
+ return nextDerived;
75
+ });
76
+ });
77
+ return unsub;
78
+ }, [sourceKey, selector]);
79
+ return [derivedState, {}];
80
+ }
81
+ // --- Logic for STANDARD Flows (Read-Write) ---
24
82
  const isDef = typeof keyOrDef !== "string";
25
- const key = isDef ? keyOrDef.key : keyOrDef;
26
- // Initial data comes either from the definition or the direct argument
27
- const initialValue = isDef ? keyOrDef.initial : maybeInitialValue;
28
- // Merge options
29
- const opt = isDef ? { ...keyOrDef.options, ...options } : options;
30
- // Store access: store.get handles initialization if initialValue is present
83
+ const key = isDef
84
+ ? keyOrDef.key
85
+ : keyOrDef;
86
+ const initialValue = isDef
87
+ ? keyOrDef.initial
88
+ : maybeInitialValue;
89
+ const opt = isDef
90
+ ? { ...keyOrDef.options, ...options }
91
+ : options;
31
92
  const [state, setState] = react.useState(() => store.store.get(key, initialValue));
32
93
  react.useEffect(() => {
33
- // Subscribe to store changes
34
94
  const unsub = store.store.subscribe(key, () => {
35
95
  setState(store.store.get(key, initialValue));
36
96
  });
@@ -42,12 +102,20 @@ function useFlow(keyOrDef, maybeInitialValue, options = {}) {
42
102
  store.store.set(key, next, opt);
43
103
  }, [key, initialValue, opt]);
44
104
  const toolbox = react.useMemo(() => {
45
- return {
46
- ops: {
47
- get self() {
48
- return proxy.createDeepProxy(key, [], store.store.get(key, initialValue), opt);
49
- },
105
+ const defaultOps = {
106
+ get self() {
107
+ return proxy.createDeepProxy(key, [], store.store.get(key, initialValue), opt);
50
108
  },
109
+ };
110
+ const boundActions = {};
111
+ if (opt.actions) {
112
+ for (const [actionName, actionFn] of Object.entries(opt.actions)) {
113
+ boundActions[actionName] = (...args) => actionFn(defaultOps, ...args);
114
+ }
115
+ }
116
+ return {
117
+ ops: defaultOps,
118
+ actions: boundActions,
51
119
  set: setManual,
52
120
  undo: () => store.store.undo(key),
53
121
  redo: () => store.store.redo(key),
@@ -61,6 +129,7 @@ function useFlow(keyOrDef, maybeInitialValue, options = {}) {
61
129
  return [state, toolbox];
62
130
  }
63
131
 
132
+ exports.defineDerived = defineDerived;
64
133
  exports.defineFlow = defineFlow;
65
134
  exports.useFlow = useFlow;
66
135
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/index.ts"],"sourcesContent":["import { useState, useEffect, useCallback, useMemo } from \"react\";\nimport type { FlowOptions, FlowOperations, FlowDefinition } from \"./types\";\nimport { store } from \"./store\";\nimport { createDeepProxy } from \"./proxy\";\n\nexport * from \"./types\";\n\n/**\n * Defines a flow in a centralized manner.\n * Allows defining the key, initial state, and options in a single reusable object.\n */\nexport function defineFlow<T>(\n key: string,\n initial: T,\n options?: FlowOptions<T>,\n): FlowDefinition<T> {\n return { key, initial, options };\n}\n\n/**\n * Unique \"All-in-One\" hook to create or consume a FractoState flow.\n * - If called with a FlowDefinition or an initial value: initializes the flow if it doesn't exist.\n * - If called with just a key: connects to the existing flow.\n *\n * Returns a 2-element array: [state, toolbox]\n * where toolbox contains { ops, set, undo, redo, history, ... }\n */\nexport function useFlow<T>(\n keyOrDef: string | FlowDefinition<T>,\n maybeInitialValue?: T,\n options: FlowOptions<T> = {},\n): [T, FlowOperations<T>] {\n // Argument analysis to unify initialization and consumption\n const isDef = typeof keyOrDef !== \"string\";\n const key = isDef ? keyOrDef.key : keyOrDef;\n\n // Initial data comes either from the definition or the direct argument\n const initialValue = isDef ? keyOrDef.initial : maybeInitialValue;\n\n // Merge options\n const opt = isDef ? { ...keyOrDef.options, ...options } : options;\n\n // Store access: store.get handles initialization if initialValue is present\n const [state, setState] = useState(() => store.get(key, initialValue));\n\n useEffect(() => {\n // Subscribe to store changes\n const unsub = store.subscribe(key, () => {\n setState(store.get(key, initialValue));\n });\n return unsub;\n }, [key, initialValue]);\n\n const setManual = useCallback(\n (val: T | ((prev: T) => T)) => {\n const current = store.get(key, initialValue);\n const next = typeof val === \"function\" ? (val as any)(current) : val;\n store.set(key, next, opt);\n },\n [key, initialValue, opt],\n );\n\n const toolbox = useMemo((): FlowOperations<T> => {\n return {\n ops: {\n get self() {\n return createDeepProxy<T>(key, [], store.get(key, initialValue), opt);\n },\n },\n set: setManual,\n undo: () => store.undo(key),\n redo: () => store.redo(key),\n history: store.getHistory(key),\n canUndo: store.getHistory(key).length > 1,\n canRedo: store.getRedoStack(key).length > 0,\n reset: () => store.reset(key),\n cf: opt,\n };\n }, [key, opt, state, initialValue, setManual]);\n\n return [state, toolbox];\n}\n"],"names":["useState","store","useEffect","useCallback","useMemo","createDeepProxy"],"mappings":";;;;;;AAOA;;;AAGG;SACa,UAAU,CACxB,GAAW,EACX,OAAU,EACV,OAAwB,EAAA;AAExB,IAAA,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE;AAClC;AAEA;;;;;;;AAOG;AACG,SAAU,OAAO,CACrB,QAAoC,EACpC,iBAAqB,EACrB,UAA0B,EAAE,EAAA;;AAG5B,IAAA,MAAM,KAAK,GAAG,OAAO,QAAQ,KAAK,QAAQ;AAC1C,IAAA,MAAM,GAAG,GAAG,KAAK,GAAG,QAAQ,CAAC,GAAG,GAAG,QAAQ;;AAG3C,IAAA,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,CAAC,OAAO,GAAG,iBAAiB;;AAGjE,IAAA,MAAM,GAAG,GAAG,KAAK,GAAG,EAAE,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO;;IAGjE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAGA,cAAQ,CAAC,MAAMC,WAAK,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAEtEC,eAAS,CAAC,MAAK;;QAEb,MAAM,KAAK,GAAGD,WAAK,CAAC,SAAS,CAAC,GAAG,EAAE,MAAK;YACtC,QAAQ,CAACA,WAAK,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;AACxC,QAAA,CAAC,CAAC;AACF,QAAA,OAAO,KAAK;AACd,IAAA,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;AAEvB,IAAA,MAAM,SAAS,GAAGE,iBAAW,CAC3B,CAAC,GAAyB,KAAI;QAC5B,MAAM,OAAO,GAAGF,WAAK,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC;AAC5C,QAAA,MAAM,IAAI,GAAG,OAAO,GAAG,KAAK,UAAU,GAAI,GAAW,CAAC,OAAO,CAAC,GAAG,GAAG;QACpEA,WAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC;IAC3B,CAAC,EACD,CAAC,GAAG,EAAE,YAAY,EAAE,GAAG,CAAC,CACzB;AAED,IAAA,MAAM,OAAO,GAAGG,aAAO,CAAC,MAAwB;QAC9C,OAAO;AACL,YAAA,GAAG,EAAE;AACH,gBAAA,IAAI,IAAI,GAAA;AACN,oBAAA,OAAOC,qBAAe,CAAI,GAAG,EAAE,EAAE,EAAEJ,WAAK,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,GAAG,CAAC;gBACvE,CAAC;AACF,aAAA;AACD,YAAA,GAAG,EAAE,SAAS;YACd,IAAI,EAAE,MAAMA,WAAK,CAAC,IAAI,CAAC,GAAG,CAAC;YAC3B,IAAI,EAAE,MAAMA,WAAK,CAAC,IAAI,CAAC,GAAG,CAAC;AAC3B,YAAA,OAAO,EAAEA,WAAK,CAAC,UAAU,CAAC,GAAG,CAAC;YAC9B,OAAO,EAAEA,WAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;YACzC,OAAO,EAAEA,WAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;YAC3C,KAAK,EAAE,MAAMA,WAAK,CAAC,KAAK,CAAC,GAAG,CAAC;AAC7B,YAAA,EAAE,EAAE,GAAG;SACR;AACH,IAAA,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AAE9C,IAAA,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC;AACzB;;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/index.ts"],"sourcesContent":["/***************************************************************************\n * FractoState - Fast And Secure\n *\n * @author Nehonix\n * @license NOSL\n *\n * Copyright (c) 2025 Nehonix. All rights reserved.\n *\n * This License governs the use, modification, and distribution of software\n * provided by NEHONIX under its open source projects.\n * NEHONIX is committed to fostering collaborative innovation while strictly\n * protecting its intellectual property rights.\n * Violation of any term of this License will result in immediate termination of all granted rights\n * and may subject the violator to legal action.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n * INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,\n * AND NON-INFRINGEMENT.\n * IN NO EVENT SHALL NEHONIX BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,\n * OR CONSEQUENTIAL DAMAGES ARISING FROM THE USE OR INABILITY TO USE THE SOFTWARE,\n * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n *\n ***************************************************************************** */\n\nimport { useState, useEffect, useCallback, useMemo } from \"react\";\nimport type {\n FlowOptions,\n FlowOperations,\n FlowDefinition,\n FlowAction,\n} from \"./types\";\nimport { store } from \"./store\";\nimport { createDeepProxy } from \"./proxy\";\n\nexport * from \"./types\";\n\n// --- New feature: Derived Flows ---\n\nexport interface DerivedFlowDefinition<T, R> {\n key: string;\n source: FlowDefinition<T, any>; // The source flow\n selector: (state: T) => R; // The computation logic\n options?: any;\n}\n\n/**\n * Defines a derived flow that automatically updates based on a source flow.\n * No store registration happens here; it's purely a definition for the hook.\n */\nexport function defineDerived<T, R>(\n source: FlowDefinition<T, any>,\n selector: (state: T) => R,\n derivedKey?: string,\n): DerivedFlowDefinition<T, R> {\n return {\n key: derivedKey || `${source.key}_derived_${Date.now()}`,\n source,\n selector,\n };\n}\n\n/**\n * Defines a flow in a centralized manner.\n * Allows defining the key, initial state, and options in a single reusable object.\n */\nexport function defineFlow<T, A extends Record<string, FlowAction<T>> = {}>(\n key: string,\n initial: T,\n options?: FlowOptions<T, A>,\n): FlowDefinition<T, A> {\n // If generic T is provided manually but A is not, we want A to be inferred from options\n // However, explicit generics override inference.\n // The user should ideally use `defineFlow('key', { ...initial }, { actions: ... })`\n // without explicit generics to get full inference.\n // Or provide both. This signature supports both but relies on inference for best DX.\n return { key, initial, options };\n}\n\n// --- 1. Overload for DERIVED Flows (Read-Only) ---\nexport function useFlow<T, R>(derivedDef: DerivedFlowDefinition<T, R>): [R, {}];\n\n// --- 2. Overload for STANDARD Flows (Read-Write) ---\nexport function useFlow<\n T,\n A extends Record<string, FlowAction<T>> = Record<string, FlowAction<T>>,\n>(\n keyOrDef: string | FlowDefinition<T, A>,\n maybeInitialValue?: T,\n options?: FlowOptions<T, A>,\n): [T, FlowOperations<T, A>];\n\n// --- Implementation ---\nexport function useFlow<\n T,\n A extends Record<string, FlowAction<T>> = Record<string, FlowAction<T>>,\n R = T,\n>(\n keyOrDef: string | FlowDefinition<T, A> | DerivedFlowDefinition<any, R>,\n maybeInitialValue?: T,\n options: FlowOptions<T, A> = {},\n): [R extends T ? T : R, Partial<FlowOperations<T, A>>] {\n // 1. Identify if we are dealing with a Derived Flow\n const isDerived =\n typeof keyOrDef === \"object\" &&\n \"selector\" in keyOrDef &&\n \"source\" in keyOrDef;\n\n // --- Logic for DERIVED Flows (Read-Only Reactive) ---\n if (isDerived) {\n const derivedDef = keyOrDef as DerivedFlowDefinition<any, R>;\n const sourceKey = derivedDef.source.key;\n const selector = derivedDef.selector;\n\n const getSourceState = () =>\n store.get(sourceKey, derivedDef.source.initial);\n\n // Initial derived state\n const [derivedState, setDerivedState] = useState(() =>\n selector(getSourceState()),\n );\n\n useEffect(() => {\n const unsub = store.subscribe(sourceKey, () => {\n const nextSource = getSourceState();\n const nextDerived = selector(nextSource);\n setDerivedState((prev) => {\n if (prev === nextDerived) return prev;\n return nextDerived;\n });\n });\n return unsub;\n }, [sourceKey, selector]);\n\n return [derivedState as any, {}];\n }\n\n // --- Logic for STANDARD Flows (Read-Write) ---\n const isDef = typeof keyOrDef !== \"string\";\n const key = isDef\n ? (keyOrDef as FlowDefinition<T, A>).key\n : (keyOrDef as string);\n\n const initialValue = isDef\n ? (keyOrDef as FlowDefinition<T, A>).initial\n : maybeInitialValue;\n\n const opt: FlowOptions<T, A> = isDef\n ? { ...(keyOrDef as FlowDefinition<T, A>).options, ...options }\n : options;\n\n const [state, setState] = useState(() => store.get(key, initialValue));\n\n useEffect(() => {\n const unsub = store.subscribe(key, () => {\n setState(store.get(key, initialValue));\n });\n return unsub;\n }, [key, initialValue]);\n\n const setManual = useCallback(\n (val: T | ((prev: T) => T)) => {\n const current = store.get(key, initialValue);\n const next = typeof val === \"function\" ? (val as any)(current) : val;\n store.set(key, next, opt);\n },\n [key, initialValue, opt],\n );\n\n const toolbox = useMemo((): FlowOperations<T, A> => {\n const defaultOps = {\n get self() {\n return createDeepProxy<T>(key, [], store.get(key, initialValue), opt);\n },\n };\n\n const boundActions = {} as any;\n if (opt.actions) {\n for (const [actionName, actionFn] of Object.entries(opt.actions)) {\n boundActions[actionName] = (...args: any[]) =>\n (actionFn as Function)(defaultOps as any, ...args);\n }\n }\n\n return {\n ops: defaultOps,\n actions: boundActions,\n set: setManual,\n undo: () => store.undo(key),\n redo: () => store.redo(key),\n history: store.getHistory(key),\n canUndo: store.getHistory(key).length > 1,\n canRedo: store.getRedoStack(key).length > 0,\n reset: () => store.reset(key),\n cf: opt,\n };\n }, [key, opt, state, initialValue, setManual]);\n\n return [state as any, toolbox];\n}\n"],"names":["store","useState","useEffect","useCallback","useMemo","createDeepProxy"],"mappings":"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"}
@@ -2,33 +2,93 @@ import { useState, useEffect, useCallback, useMemo } from 'react';
2
2
  import { store } from './store.js';
3
3
  import { createDeepProxy } from './proxy.js';
4
4
 
5
+ /***************************************************************************
6
+ * FractoState - Fast And Secure
7
+ *
8
+ * @author Nehonix
9
+ * @license NOSL
10
+ *
11
+ * Copyright (c) 2025 Nehonix. All rights reserved.
12
+ *
13
+ * This License governs the use, modification, and distribution of software
14
+ * provided by NEHONIX under its open source projects.
15
+ * NEHONIX is committed to fostering collaborative innovation while strictly
16
+ * protecting its intellectual property rights.
17
+ * Violation of any term of this License will result in immediate termination of all granted rights
18
+ * and may subject the violator to legal action.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
21
+ * INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
22
+ * AND NON-INFRINGEMENT.
23
+ * IN NO EVENT SHALL NEHONIX BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
24
+ * OR CONSEQUENTIAL DAMAGES ARISING FROM THE USE OR INABILITY TO USE THE SOFTWARE,
25
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
26
+ *
27
+ ***************************************************************************** */
28
+ /**
29
+ * Defines a derived flow that automatically updates based on a source flow.
30
+ * No store registration happens here; it's purely a definition for the hook.
31
+ */
32
+ function defineDerived(source, selector, derivedKey) {
33
+ return {
34
+ key: derivedKey || `${source.key}_derived_${Date.now()}`,
35
+ source,
36
+ selector,
37
+ };
38
+ }
5
39
  /**
6
40
  * Defines a flow in a centralized manner.
7
41
  * Allows defining the key, initial state, and options in a single reusable object.
8
42
  */
9
43
  function defineFlow(key, initial, options) {
44
+ // If generic T is provided manually but A is not, we want A to be inferred from options
45
+ // However, explicit generics override inference.
46
+ // The user should ideally use `defineFlow('key', { ...initial }, { actions: ... })`
47
+ // without explicit generics to get full inference.
48
+ // Or provide both. This signature supports both but relies on inference for best DX.
10
49
  return { key, initial, options };
11
50
  }
12
- /**
13
- * Unique "All-in-One" hook to create or consume a FractoState flow.
14
- * - If called with a FlowDefinition or an initial value: initializes the flow if it doesn't exist.
15
- * - If called with just a key: connects to the existing flow.
16
- *
17
- * Returns a 2-element array: [state, toolbox]
18
- * where toolbox contains { ops, set, undo, redo, history, ... }
19
- */
51
+ // --- Implementation ---
20
52
  function useFlow(keyOrDef, maybeInitialValue, options = {}) {
21
- // Argument analysis to unify initialization and consumption
53
+ // 1. Identify if we are dealing with a Derived Flow
54
+ const isDerived = typeof keyOrDef === "object" &&
55
+ "selector" in keyOrDef &&
56
+ "source" in keyOrDef;
57
+ // --- Logic for DERIVED Flows (Read-Only Reactive) ---
58
+ if (isDerived) {
59
+ const derivedDef = keyOrDef;
60
+ const sourceKey = derivedDef.source.key;
61
+ const selector = derivedDef.selector;
62
+ const getSourceState = () => store.get(sourceKey, derivedDef.source.initial);
63
+ // Initial derived state
64
+ const [derivedState, setDerivedState] = useState(() => selector(getSourceState()));
65
+ useEffect(() => {
66
+ const unsub = store.subscribe(sourceKey, () => {
67
+ const nextSource = getSourceState();
68
+ const nextDerived = selector(nextSource);
69
+ setDerivedState((prev) => {
70
+ if (prev === nextDerived)
71
+ return prev;
72
+ return nextDerived;
73
+ });
74
+ });
75
+ return unsub;
76
+ }, [sourceKey, selector]);
77
+ return [derivedState, {}];
78
+ }
79
+ // --- Logic for STANDARD Flows (Read-Write) ---
22
80
  const isDef = typeof keyOrDef !== "string";
23
- const key = isDef ? keyOrDef.key : keyOrDef;
24
- // Initial data comes either from the definition or the direct argument
25
- const initialValue = isDef ? keyOrDef.initial : maybeInitialValue;
26
- // Merge options
27
- const opt = isDef ? { ...keyOrDef.options, ...options } : options;
28
- // Store access: store.get handles initialization if initialValue is present
81
+ const key = isDef
82
+ ? keyOrDef.key
83
+ : keyOrDef;
84
+ const initialValue = isDef
85
+ ? keyOrDef.initial
86
+ : maybeInitialValue;
87
+ const opt = isDef
88
+ ? { ...keyOrDef.options, ...options }
89
+ : options;
29
90
  const [state, setState] = useState(() => store.get(key, initialValue));
30
91
  useEffect(() => {
31
- // Subscribe to store changes
32
92
  const unsub = store.subscribe(key, () => {
33
93
  setState(store.get(key, initialValue));
34
94
  });
@@ -40,12 +100,20 @@ function useFlow(keyOrDef, maybeInitialValue, options = {}) {
40
100
  store.set(key, next, opt);
41
101
  }, [key, initialValue, opt]);
42
102
  const toolbox = useMemo(() => {
43
- return {
44
- ops: {
45
- get self() {
46
- return createDeepProxy(key, [], store.get(key, initialValue), opt);
47
- },
103
+ const defaultOps = {
104
+ get self() {
105
+ return createDeepProxy(key, [], store.get(key, initialValue), opt);
48
106
  },
107
+ };
108
+ const boundActions = {};
109
+ if (opt.actions) {
110
+ for (const [actionName, actionFn] of Object.entries(opt.actions)) {
111
+ boundActions[actionName] = (...args) => actionFn(defaultOps, ...args);
112
+ }
113
+ }
114
+ return {
115
+ ops: defaultOps,
116
+ actions: boundActions,
49
117
  set: setManual,
50
118
  undo: () => store.undo(key),
51
119
  redo: () => store.redo(key),
@@ -59,5 +127,5 @@ function useFlow(keyOrDef, maybeInitialValue, options = {}) {
59
127
  return [state, toolbox];
60
128
  }
61
129
 
62
- export { defineFlow, useFlow };
130
+ export { defineDerived, defineFlow, useFlow };
63
131
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/index.ts"],"sourcesContent":["import { useState, useEffect, useCallback, useMemo } from \"react\";\nimport type { FlowOptions, FlowOperations, FlowDefinition } from \"./types\";\nimport { store } from \"./store\";\nimport { createDeepProxy } from \"./proxy\";\n\nexport * from \"./types\";\n\n/**\n * Defines a flow in a centralized manner.\n * Allows defining the key, initial state, and options in a single reusable object.\n */\nexport function defineFlow<T>(\n key: string,\n initial: T,\n options?: FlowOptions<T>,\n): FlowDefinition<T> {\n return { key, initial, options };\n}\n\n/**\n * Unique \"All-in-One\" hook to create or consume a FractoState flow.\n * - If called with a FlowDefinition or an initial value: initializes the flow if it doesn't exist.\n * - If called with just a key: connects to the existing flow.\n *\n * Returns a 2-element array: [state, toolbox]\n * where toolbox contains { ops, set, undo, redo, history, ... }\n */\nexport function useFlow<T>(\n keyOrDef: string | FlowDefinition<T>,\n maybeInitialValue?: T,\n options: FlowOptions<T> = {},\n): [T, FlowOperations<T>] {\n // Argument analysis to unify initialization and consumption\n const isDef = typeof keyOrDef !== \"string\";\n const key = isDef ? keyOrDef.key : keyOrDef;\n\n // Initial data comes either from the definition or the direct argument\n const initialValue = isDef ? keyOrDef.initial : maybeInitialValue;\n\n // Merge options\n const opt = isDef ? { ...keyOrDef.options, ...options } : options;\n\n // Store access: store.get handles initialization if initialValue is present\n const [state, setState] = useState(() => store.get(key, initialValue));\n\n useEffect(() => {\n // Subscribe to store changes\n const unsub = store.subscribe(key, () => {\n setState(store.get(key, initialValue));\n });\n return unsub;\n }, [key, initialValue]);\n\n const setManual = useCallback(\n (val: T | ((prev: T) => T)) => {\n const current = store.get(key, initialValue);\n const next = typeof val === \"function\" ? (val as any)(current) : val;\n store.set(key, next, opt);\n },\n [key, initialValue, opt],\n );\n\n const toolbox = useMemo((): FlowOperations<T> => {\n return {\n ops: {\n get self() {\n return createDeepProxy<T>(key, [], store.get(key, initialValue), opt);\n },\n },\n set: setManual,\n undo: () => store.undo(key),\n redo: () => store.redo(key),\n history: store.getHistory(key),\n canUndo: store.getHistory(key).length > 1,\n canRedo: store.getRedoStack(key).length > 0,\n reset: () => store.reset(key),\n cf: opt,\n };\n }, [key, opt, state, initialValue, setManual]);\n\n return [state, toolbox];\n}\n"],"names":[],"mappings":";;;;AAOA;;;AAGG;SACa,UAAU,CACxB,GAAW,EACX,OAAU,EACV,OAAwB,EAAA;AAExB,IAAA,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE;AAClC;AAEA;;;;;;;AAOG;AACG,SAAU,OAAO,CACrB,QAAoC,EACpC,iBAAqB,EACrB,UAA0B,EAAE,EAAA;;AAG5B,IAAA,MAAM,KAAK,GAAG,OAAO,QAAQ,KAAK,QAAQ;AAC1C,IAAA,MAAM,GAAG,GAAG,KAAK,GAAG,QAAQ,CAAC,GAAG,GAAG,QAAQ;;AAG3C,IAAA,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,CAAC,OAAO,GAAG,iBAAiB;;AAGjE,IAAA,MAAM,GAAG,GAAG,KAAK,GAAG,EAAE,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,OAAO,EAAE,GAAG,OAAO;;IAGjE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAEtE,SAAS,CAAC,MAAK;;QAEb,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,MAAK;YACtC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;AACxC,QAAA,CAAC,CAAC;AACF,QAAA,OAAO,KAAK;AACd,IAAA,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;AAEvB,IAAA,MAAM,SAAS,GAAG,WAAW,CAC3B,CAAC,GAAyB,KAAI;QAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC;AAC5C,QAAA,MAAM,IAAI,GAAG,OAAO,GAAG,KAAK,UAAU,GAAI,GAAW,CAAC,OAAO,CAAC,GAAG,GAAG;QACpE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC;IAC3B,CAAC,EACD,CAAC,GAAG,EAAE,YAAY,EAAE,GAAG,CAAC,CACzB;AAED,IAAA,MAAM,OAAO,GAAG,OAAO,CAAC,MAAwB;QAC9C,OAAO;AACL,YAAA,GAAG,EAAE;AACH,gBAAA,IAAI,IAAI,GAAA;AACN,oBAAA,OAAO,eAAe,CAAI,GAAG,EAAE,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,GAAG,CAAC;gBACvE,CAAC;AACF,aAAA;AACD,YAAA,GAAG,EAAE,SAAS;YACd,IAAI,EAAE,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;YAC3B,IAAI,EAAE,MAAM,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;AAC3B,YAAA,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;YAC9B,OAAO,EAAE,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;YACzC,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC;YAC3C,KAAK,EAAE,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;AAC7B,YAAA,EAAE,EAAE,GAAG;SACR;AACH,IAAA,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;AAE9C,IAAA,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC;AACzB;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../src/index.ts"],"sourcesContent":["/***************************************************************************\n * FractoState - Fast And Secure\n *\n * @author Nehonix\n * @license NOSL\n *\n * Copyright (c) 2025 Nehonix. All rights reserved.\n *\n * This License governs the use, modification, and distribution of software\n * provided by NEHONIX under its open source projects.\n * NEHONIX is committed to fostering collaborative innovation while strictly\n * protecting its intellectual property rights.\n * Violation of any term of this License will result in immediate termination of all granted rights\n * and may subject the violator to legal action.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,\n * INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,\n * AND NON-INFRINGEMENT.\n * IN NO EVENT SHALL NEHONIX BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,\n * OR CONSEQUENTIAL DAMAGES ARISING FROM THE USE OR INABILITY TO USE THE SOFTWARE,\n * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n *\n ***************************************************************************** */\n\nimport { useState, useEffect, useCallback, useMemo } from \"react\";\nimport type {\n FlowOptions,\n FlowOperations,\n FlowDefinition,\n FlowAction,\n} from \"./types\";\nimport { store } from \"./store\";\nimport { createDeepProxy } from \"./proxy\";\n\nexport * from \"./types\";\n\n// --- New feature: Derived Flows ---\n\nexport interface DerivedFlowDefinition<T, R> {\n key: string;\n source: FlowDefinition<T, any>; // The source flow\n selector: (state: T) => R; // The computation logic\n options?: any;\n}\n\n/**\n * Defines a derived flow that automatically updates based on a source flow.\n * No store registration happens here; it's purely a definition for the hook.\n */\nexport function defineDerived<T, R>(\n source: FlowDefinition<T, any>,\n selector: (state: T) => R,\n derivedKey?: string,\n): DerivedFlowDefinition<T, R> {\n return {\n key: derivedKey || `${source.key}_derived_${Date.now()}`,\n source,\n selector,\n };\n}\n\n/**\n * Defines a flow in a centralized manner.\n * Allows defining the key, initial state, and options in a single reusable object.\n */\nexport function defineFlow<T, A extends Record<string, FlowAction<T>> = {}>(\n key: string,\n initial: T,\n options?: FlowOptions<T, A>,\n): FlowDefinition<T, A> {\n // If generic T is provided manually but A is not, we want A to be inferred from options\n // However, explicit generics override inference.\n // The user should ideally use `defineFlow('key', { ...initial }, { actions: ... })`\n // without explicit generics to get full inference.\n // Or provide both. This signature supports both but relies on inference for best DX.\n return { key, initial, options };\n}\n\n// --- 1. Overload for DERIVED Flows (Read-Only) ---\nexport function useFlow<T, R>(derivedDef: DerivedFlowDefinition<T, R>): [R, {}];\n\n// --- 2. Overload for STANDARD Flows (Read-Write) ---\nexport function useFlow<\n T,\n A extends Record<string, FlowAction<T>> = Record<string, FlowAction<T>>,\n>(\n keyOrDef: string | FlowDefinition<T, A>,\n maybeInitialValue?: T,\n options?: FlowOptions<T, A>,\n): [T, FlowOperations<T, A>];\n\n// --- Implementation ---\nexport function useFlow<\n T,\n A extends Record<string, FlowAction<T>> = Record<string, FlowAction<T>>,\n R = T,\n>(\n keyOrDef: string | FlowDefinition<T, A> | DerivedFlowDefinition<any, R>,\n maybeInitialValue?: T,\n options: FlowOptions<T, A> = {},\n): [R extends T ? T : R, Partial<FlowOperations<T, A>>] {\n // 1. Identify if we are dealing with a Derived Flow\n const isDerived =\n typeof keyOrDef === \"object\" &&\n \"selector\" in keyOrDef &&\n \"source\" in keyOrDef;\n\n // --- Logic for DERIVED Flows (Read-Only Reactive) ---\n if (isDerived) {\n const derivedDef = keyOrDef as DerivedFlowDefinition<any, R>;\n const sourceKey = derivedDef.source.key;\n const selector = derivedDef.selector;\n\n const getSourceState = () =>\n store.get(sourceKey, derivedDef.source.initial);\n\n // Initial derived state\n const [derivedState, setDerivedState] = useState(() =>\n selector(getSourceState()),\n );\n\n useEffect(() => {\n const unsub = store.subscribe(sourceKey, () => {\n const nextSource = getSourceState();\n const nextDerived = selector(nextSource);\n setDerivedState((prev) => {\n if (prev === nextDerived) return prev;\n return nextDerived;\n });\n });\n return unsub;\n }, [sourceKey, selector]);\n\n return [derivedState as any, {}];\n }\n\n // --- Logic for STANDARD Flows (Read-Write) ---\n const isDef = typeof keyOrDef !== \"string\";\n const key = isDef\n ? (keyOrDef as FlowDefinition<T, A>).key\n : (keyOrDef as string);\n\n const initialValue = isDef\n ? (keyOrDef as FlowDefinition<T, A>).initial\n : maybeInitialValue;\n\n const opt: FlowOptions<T, A> = isDef\n ? { ...(keyOrDef as FlowDefinition<T, A>).options, ...options }\n : options;\n\n const [state, setState] = useState(() => store.get(key, initialValue));\n\n useEffect(() => {\n const unsub = store.subscribe(key, () => {\n setState(store.get(key, initialValue));\n });\n return unsub;\n }, [key, initialValue]);\n\n const setManual = useCallback(\n (val: T | ((prev: T) => T)) => {\n const current = store.get(key, initialValue);\n const next = typeof val === \"function\" ? (val as any)(current) : val;\n store.set(key, next, opt);\n },\n [key, initialValue, opt],\n );\n\n const toolbox = useMemo((): FlowOperations<T, A> => {\n const defaultOps = {\n get self() {\n return createDeepProxy<T>(key, [], store.get(key, initialValue), opt);\n },\n };\n\n const boundActions = {} as any;\n if (opt.actions) {\n for (const [actionName, actionFn] of Object.entries(opt.actions)) {\n boundActions[actionName] = (...args: any[]) =>\n (actionFn as Function)(defaultOps as any, ...args);\n }\n }\n\n return {\n ops: defaultOps,\n actions: boundActions,\n set: setManual,\n undo: () => store.undo(key),\n redo: () => store.redo(key),\n history: store.getHistory(key),\n canUndo: store.getHistory(key).length > 1,\n canRedo: store.getRedoStack(key).length > 0,\n reset: () => store.reset(key),\n cf: opt,\n };\n }, [key, opt, state, initialValue, setManual]);\n\n return [state as any, toolbox];\n}\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,14 @@
1
- interface FlowOptions<T = any> {
1
+ type FlowAction<T> = (ops: {
2
+ self: TypeAwareOps<T>;
3
+ }, ...args: any[]) => any;
4
+ /**
5
+ * Utility type to extract the public signature of actions.
6
+ * It removes the first argument (ops) from each action function.
7
+ */
8
+ type BoundActions<A> = {
9
+ [K in keyof A]: A[K] extends (ops: any, ...args: infer P) => infer R ? (...args: P) => R : never;
10
+ };
11
+ interface FlowOptions<T = any, A extends Record<string, FlowAction<T>> = {}> {
2
12
  /** Enable history tracking for undo/redo functionality */
3
13
  timeTravel?: boolean;
4
14
  /** Maximum number of states to keep in history (default: 100) */
@@ -7,6 +17,8 @@ interface FlowOptions<T = any> {
7
17
  debounce?: number;
8
18
  /** Array of functions to transform state before it is saved */
9
19
  middleware?: Array<(state: T) => T>;
20
+ /** Custom async actions attached to the flow */
21
+ actions?: A;
10
22
  }
11
23
  type BaseOps<T> = {
12
24
  /** Replaces the entire value at this path */
@@ -73,20 +85,26 @@ type BooleanOps = BaseOps<boolean> & {
73
85
  toggle: () => void;
74
86
  };
75
87
  type TypeAwareOps<T> = T extends number ? NumberOps : T extends string ? StringOps : T extends boolean ? BooleanOps : T extends Array<infer U> ? ArrayOps<U> : T extends object ? ObjectOps<T> : BaseOps<T>;
76
- interface FlowDefinition<T> {
88
+ type FlowOpsObject<T> = {
89
+ /**
90
+ * Recursive, type-aware proxy that provides surgical atomic operations
91
+ * tailored to the shape of your state. It allows direct-like manipulation
92
+ * that is internally processed as immutable updates.
93
+ */
94
+ self: TypeAwareOps<T>;
95
+ };
96
+ interface FlowDefinition<T, A extends Record<string, FlowAction<T>> = {}> {
77
97
  key: string;
78
98
  initial: T;
79
- options?: FlowOptions<T>;
99
+ options?: FlowOptions<T, A>;
80
100
  }
81
- interface FlowOperations<T> {
82
- ops: {
83
- /**
84
- * Recursive, type-aware proxy that provides surgical atomic operations
85
- * tailored to the shape of your state. It allows direct-like manipulation
86
- * that is internally processed as immutable updates.
87
- */
88
- self: TypeAwareOps<T>;
89
- };
101
+ interface FlowOperations<T, A extends Record<string, FlowAction<T>> = {}> {
102
+ ops: FlowOpsObject<T>;
103
+ /**
104
+ * Custom actions bound to the flow.
105
+ * The 'ops' argument is automatically injected.
106
+ */
107
+ actions: BoundActions<A>;
90
108
  /** Reverts to the previous state in history */
91
109
  undo: () => void;
92
110
  /** Restores the previously undone state */
@@ -102,23 +120,51 @@ interface FlowOperations<T> {
102
120
  /** Restores the state to its initial value */
103
121
  reset: () => void;
104
122
  /** Current flow configuration options */
105
- cf: FlowOptions<T>;
123
+ cf: FlowOptions<T, A>;
106
124
  }
107
125
 
126
+ /***************************************************************************
127
+ * FractoState - Fast And Secure
128
+ *
129
+ * @author Nehonix
130
+ * @license NOSL
131
+ *
132
+ * Copyright (c) 2025 Nehonix. All rights reserved.
133
+ *
134
+ * This License governs the use, modification, and distribution of software
135
+ * provided by NEHONIX under its open source projects.
136
+ * NEHONIX is committed to fostering collaborative innovation while strictly
137
+ * protecting its intellectual property rights.
138
+ * Violation of any term of this License will result in immediate termination of all granted rights
139
+ * and may subject the violator to legal action.
140
+ *
141
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
142
+ * INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
143
+ * AND NON-INFRINGEMENT.
144
+ * IN NO EVENT SHALL NEHONIX BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
145
+ * OR CONSEQUENTIAL DAMAGES ARISING FROM THE USE OR INABILITY TO USE THE SOFTWARE,
146
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
147
+ *
148
+ ***************************************************************************** */
149
+
150
+ interface DerivedFlowDefinition<T, R> {
151
+ key: string;
152
+ source: FlowDefinition<T, any>;
153
+ selector: (state: T) => R;
154
+ options?: any;
155
+ }
108
156
  /**
109
- * Defines a flow in a centralized manner.
110
- * Allows defining the key, initial state, and options in a single reusable object.
157
+ * Defines a derived flow that automatically updates based on a source flow.
158
+ * No store registration happens here; it's purely a definition for the hook.
111
159
  */
112
- declare function defineFlow<T>(key: string, initial: T, options?: FlowOptions<T>): FlowDefinition<T>;
160
+ declare function defineDerived<T, R>(source: FlowDefinition<T, any>, selector: (state: T) => R, derivedKey?: string): DerivedFlowDefinition<T, R>;
113
161
  /**
114
- * Unique "All-in-One" hook to create or consume a FractoState flow.
115
- * - If called with a FlowDefinition or an initial value: initializes the flow if it doesn't exist.
116
- * - If called with just a key: connects to the existing flow.
117
- *
118
- * Returns a 2-element array: [state, toolbox]
119
- * where toolbox contains { ops, set, undo, redo, history, ... }
162
+ * Defines a flow in a centralized manner.
163
+ * Allows defining the key, initial state, and options in a single reusable object.
120
164
  */
121
- declare function useFlow<T>(keyOrDef: string | FlowDefinition<T>, maybeInitialValue?: T, options?: FlowOptions<T>): [T, FlowOperations<T>];
165
+ declare function defineFlow<T, A extends Record<string, FlowAction<T>> = {}>(key: string, initial: T, options?: FlowOptions<T, A>): FlowDefinition<T, A>;
166
+ declare function useFlow<T, R>(derivedDef: DerivedFlowDefinition<T, R>): [R, {}];
167
+ declare function useFlow<T, A extends Record<string, FlowAction<T>> = Record<string, FlowAction<T>>>(keyOrDef: string | FlowDefinition<T, A>, maybeInitialValue?: T, options?: FlowOptions<T, A>): [T, FlowOperations<T, A>];
122
168
 
123
- export { defineFlow, useFlow };
124
- export type { ArrayOps, BaseOps, BooleanOps, FlowDefinition, FlowOperations, FlowOptions, NumberOps, ObjectOps, StringOps, TypeAwareOps };
169
+ export { defineDerived, defineFlow, useFlow };
170
+ export type { ArrayOps, BaseOps, BooleanOps, BoundActions, DerivedFlowDefinition, FlowAction, FlowDefinition, FlowOperations, FlowOpsObject, FlowOptions, NumberOps, ObjectOps, StringOps, TypeAwareOps };
package/package.json CHANGED
@@ -1,4 +1,5 @@
1
1
  {
2
+ "version": "2.0.0",
2
3
  "author": {
3
4
  "name": "Nehonix",
4
5
  "url": "https://nehonix.com",
@@ -43,18 +44,8 @@
43
44
  "exports": {
44
45
  ".": {
45
46
  "types": "./dist/index.d.ts",
46
- "import": {
47
- "types": "./dist/index.d.ts",
48
- "default": "./dist/esm/index.js"
49
- },
50
- "require": {
51
- "types": "./dist/index.d.ts",
52
- "default": "./dist/cjs/index.js"
53
- },
54
- "node": {
55
- "import": "./dist/esm/index.js",
56
- "require": "./dist/cjs/index.js"
57
- },
47
+ "import": "./dist/esm/index.js",
48
+ "require": "./dist/cjs/index.js",
58
49
  "default": "./dist/cjs/index.js"
59
50
  },
60
51
  "./package.json": "./package.json"
@@ -71,7 +62,7 @@
71
62
  "build": "rollup -c",
72
63
  "dev": "tsup src/index.ts --watch --dts --format esm,cjs",
73
64
  "lint": "eslint src",
74
- "test": "vitest"
75
- },
76
- "version": "1.0.2"
65
+ "test": "vitest",
66
+ "prepublishOnly": "npm run build"
67
+ }
77
68
  }