vivth 0.11.2 → 1.0.1

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.
Files changed (106) hide show
  1. package/README.md +2157 -69
  2. package/README.src.md +35 -0
  3. package/bun.lock +57 -3
  4. package/dev/index.mjs +24 -25
  5. package/index.mjs +51 -29
  6. package/package.json +11 -7
  7. package/src/bundler/CompileMJS.mjs +110 -0
  8. package/src/bundler/EsBundler.mjs +79 -0
  9. package/src/class/Console.mjs +62 -0
  10. package/src/class/Derived.mjs +36 -25
  11. package/src/class/Effect.mjs +106 -0
  12. package/src/class/EnvSignal.mjs +88 -0
  13. package/src/class/EventSignal.mjs +200 -0
  14. package/src/class/ListDerived.mjs +39 -0
  15. package/src/class/ListSignal.mjs +256 -0
  16. package/src/class/Paths.mjs +70 -0
  17. package/src/class/QChannel.mjs +184 -0
  18. package/src/class/SafeExit.mjs +131 -0
  19. package/src/class/Setup.mjs +73 -0
  20. package/src/class/Signal.mjs +152 -54
  21. package/src/class/WorkerMainThread.mjs +328 -0
  22. package/src/class/WorkerResult.mjs +30 -0
  23. package/src/class/WorkerThread.mjs +151 -0
  24. package/src/common/Base64URL.mjs +26 -0
  25. package/src/common/EventNameSpace.mjs +8 -0
  26. package/src/common/eventObjects.mjs +5 -0
  27. package/src/common/lazie.mjs +3 -0
  28. package/src/doc/JSautoDOC.mjs +386 -0
  29. package/src/doc/parsedFile.mjs +537 -0
  30. package/src/function/CreateImmutable.mjs +64 -0
  31. package/src/function/EventCheck.mjs +27 -0
  32. package/src/function/EventObject.mjs +21 -0
  33. package/src/function/IsAsync.mjs +23 -0
  34. package/src/function/LazyFactory.mjs +71 -0
  35. package/src/function/Timeout.mjs +23 -0
  36. package/src/function/Try.mjs +64 -0
  37. package/src/function/TryAsync.mjs +15 -4
  38. package/src/function/TrySync.mjs +9 -4
  39. package/src/function/TsToMjs.mjs +67 -0
  40. package/src/function/WriteFileSafe.mjs +37 -0
  41. package/src/types/{AnyButUndefined.type.mjs → AnyButUndefined.mjs} +1 -0
  42. package/src/types/ExtnameType.mjs +6 -0
  43. package/src/types/IsListSignal.mjs +6 -0
  44. package/src/types/ListArg.mjs +6 -0
  45. package/src/types/MutationType.mjs +8 -0
  46. package/src/types/QCBFIFOReturn.mjs +6 -0
  47. package/src/types/QCBReturn.mjs +6 -0
  48. package/tsconfig.json +3 -3
  49. package/types/dev/index.d.mts +1 -0
  50. package/types/index.d.mts +34 -8
  51. package/types/src/bundler/A.d.mts +1 -0
  52. package/types/src/bundler/CompileMJS.d.mts +8 -0
  53. package/types/src/bundler/EsBundler.d.mts +7 -0
  54. package/types/src/class/Console.d.mts +40 -0
  55. package/types/src/class/Derived.d.mts +21 -9
  56. package/types/src/class/Effect.d.mts +77 -0
  57. package/types/src/class/EnvSignal.d.mts +47 -0
  58. package/types/src/class/EventSignal.d.mts +145 -0
  59. package/types/src/class/ListDerived.d.mts +35 -0
  60. package/types/src/class/ListSignal.d.mts +150 -0
  61. package/types/src/class/Paths.d.mts +50 -0
  62. package/types/src/class/QChannel.d.mts +115 -0
  63. package/types/src/class/SafeExit.d.mts +76 -0
  64. package/types/src/class/Setup.d.mts +76 -0
  65. package/types/src/class/Signal.d.mts +105 -26
  66. package/types/src/class/WorkerMainThread.d.mts +149 -0
  67. package/types/src/class/WorkerResult.d.mts +25 -0
  68. package/types/src/class/WorkerThread.d.mts +70 -0
  69. package/types/src/common/Base64URL.d.mts +1 -0
  70. package/types/src/common/EventNameSpace.d.mts +6 -0
  71. package/types/src/common/eventObjects.d.mts +3 -0
  72. package/types/src/common/lazie.d.mts +1 -0
  73. package/types/src/doc/JSautoDOC.d.mts +76 -0
  74. package/types/src/doc/parsedFile.d.mts +154 -0
  75. package/types/src/function/CreateImmutable.d.mts +3 -0
  76. package/types/src/function/EventCheck.d.mts +2 -0
  77. package/types/src/function/EventObject.d.mts +4 -0
  78. package/types/src/function/IsAsync.d.mts +1 -0
  79. package/types/src/function/LazyFactory.d.mts +4 -0
  80. package/types/src/function/Timeout.d.mts +1 -0
  81. package/types/src/function/Try.d.mts +1 -0
  82. package/types/src/function/TsToMjs.d.mts +4 -0
  83. package/types/src/function/WriteFileSafe.d.mts +2 -0
  84. package/types/src/types/{AnyButUndefined.type.d.mts → AnyButUndefined.d.mts} +3 -0
  85. package/types/src/types/ExtnameType.d.mts +4 -0
  86. package/types/src/types/IsListSignal.d.mts +4 -0
  87. package/types/src/types/ListArg.d.mts +4 -0
  88. package/types/src/types/MutationType.d.mts +5 -0
  89. package/types/src/types/QCBFIFOReturn.d.mts +4 -0
  90. package/types/src/types/QCBReturn.d.mts +7 -0
  91. package/src/class/$.mjs +0 -68
  92. package/src/class/PingFIFO.mjs +0 -78
  93. package/src/class/PingUnique.mjs +0 -84
  94. package/src/class/Q.mjs +0 -98
  95. package/src/class/QFIFO.mjs +0 -66
  96. package/src/class/QUnique.mjs +0 -75
  97. package/src/common.mjs +0 -16
  98. package/src/function/NewQBlock.mjs +0 -39
  99. package/types/src/class/$.d.mts +0 -40
  100. package/types/src/class/PingFIFO.d.mts +0 -57
  101. package/types/src/class/PingUnique.d.mts +0 -48
  102. package/types/src/class/Q.d.mts +0 -63
  103. package/types/src/class/QFIFO.d.mts +0 -47
  104. package/types/src/class/QUnique.d.mts +0 -46
  105. package/types/src/common.d.mts +0 -2
  106. package/types/src/function/NewQBlock.d.mts +0 -1
@@ -0,0 +1,70 @@
1
+ // @ts-check
2
+
3
+ import { Console } from './Console.mjs';
4
+
5
+ /**
6
+ * @description
7
+ * - class helpers to define pathReference;
8
+ * - is a singleton;
9
+ */
10
+ export class Paths {
11
+ /**
12
+ * @type {Paths}
13
+ */
14
+ static #instance = undefined;
15
+ /**
16
+ * @description
17
+ * @param {Object} options
18
+ * @param {string} options.root
19
+ * - browser: location.origin
20
+ * - node/bun compatible: process?.env?.INIT_CWD ?? process?.cwd();
21
+ * - deno: Deno.env.get("INIT_CWD") ?? Deno.cwd(); need for `deno run --allow-env --allow-read your_script.ts`
22
+ * - other: you need to check your JSRuntime for the rootPath reference;
23
+ * @example
24
+ * import { Paths } from 'vivth';
25
+ *
26
+ * new Paths({
27
+ * root: location.origin,
28
+ * })
29
+ */
30
+ constructor({ root }) {
31
+ if (Paths.#instance) {
32
+ return Paths.#instance;
33
+ }
34
+ Paths.#instance = this;
35
+ this.#root = root;
36
+ }
37
+ /**
38
+ * @type {string}
39
+ */
40
+ #root = undefined;
41
+ /**
42
+ * @description
43
+ * - reference for rootPath
44
+ * - `Paths` needed to be instantiated via:
45
+ * >- `Paths` constructor;
46
+ * >- `Setup.paths` constructor;
47
+ * @type {string}
48
+ */
49
+ static get root() {
50
+ if (!Paths.#instance.#root) {
51
+ Console.error({
52
+ error: 'Paths.instance.#root is undefined',
53
+ solutions: 'instantiate `Paths` or instantiate `Setup`',
54
+ });
55
+ return undefined;
56
+ }
57
+ return Paths.#instance.#root;
58
+ }
59
+ /**
60
+ * @description
61
+ * - normalize path separator to forward slash `/`;
62
+ * @param {string} path_
63
+ * @returns {string}
64
+ * @example
65
+ * import { Paths } from 'vivth';
66
+ *
67
+ * Paths.normalize('file:\\D:\\myFile.mjs'); // return 'file://D://myFile.mjs'
68
+ */
69
+ static normalize = (path_) => path_.replace(/\\/g, '/');
70
+ }
@@ -0,0 +1,184 @@
1
+ // @ts-check
2
+
3
+ import { LazyFactory } from '../function/LazyFactory.mjs';
4
+ import { TryAsync } from '../function/TryAsync.mjs';
5
+
6
+ /**
7
+ * @description
8
+ * - class for `Queue` handling;
9
+ * @template {AnyButUndefinedType} T
10
+ */
11
+ export class QChannel {
12
+ /**
13
+ * @typedef {import('../types/AnyButUndefined.mjs').AnyButUndefined} AnyButUndefinedType
14
+ * @typedef {import('../types/QCBReturn.mjs').QCBReturn} QCBReturn
15
+ * @typedef {import('../types/QCBFIFOReturn.mjs').QCBFIFOReturn} QCBFIFOReturn
16
+ */
17
+ /**
18
+ * @description
19
+ * - to modify `MapReference`
20
+ * @param {Map<AnyButUndefinedType, [Promise<any>, {}]>} uniqueMap
21
+ * @returns {typeof QChannel}
22
+ * - usefull for Queue primitive on multiple library but single reference, like the Web by making the `Map` on `window` object;
23
+ */
24
+ static makeQClass = (uniqueMap) => {
25
+ QChannel.#uniquePromiser = uniqueMap;
26
+ return QChannel;
27
+ };
28
+ /**
29
+ * @type {Map<AnyButUndefinedType, [Promise<any>, {}]>}
30
+ */
31
+ static #uniquePromiser = new Map();
32
+ /**
33
+ * - ensures that each id has only one task running at a time.
34
+ * - calls with the same id will wait for the previous call to finish.
35
+ * @param {AnyButUndefinedType} id
36
+ * @returns {Promise<QCBReturn>} Resolves when it's safe to proceed for the given id, returning a cleanup function
37
+ */
38
+ static #uniqueCB = async (id) => {
39
+ const existing = QChannel.#uniquePromiser.get(id);
40
+ let resolveFn;
41
+ const nextPromise = new Promise((resolve) => {
42
+ resolveFn = resolve;
43
+ });
44
+ const context = {};
45
+ if (!existing) {
46
+ QChannel.#uniquePromiser.set(id, [nextPromise, context]);
47
+ await Promise.resolve();
48
+ } else {
49
+ const [prevPromise] = existing;
50
+ await prevPromise;
51
+ QChannel.#uniquePromiser.set(id, [nextPromise, context]);
52
+ }
53
+ const resume = () => {
54
+ resolveFn();
55
+ QChannel.#uniquePromiser.delete(id);
56
+ };
57
+ return {
58
+ resume,
59
+ get isLastOnQ() {
60
+ const [_, lastContext] = QChannel.#uniquePromiser.get(id);
61
+ return lastContext === context;
62
+ },
63
+ };
64
+ };
65
+ /**
66
+ * @description
67
+ * - first in first out handler
68
+ */
69
+ static fifo = LazyFactory(() => {
70
+ const qfifo = new QChannel();
71
+ return {
72
+ /**
73
+ * @static fifo
74
+ * @description
75
+ * - blocks execution for subsequent calls until the current one finishes.
76
+ * @returns {Promise<QCBFIFOReturn>} Resolves when it's safe to proceed, returning a cleanup function
77
+ * @example
78
+ * const { resume } = await QChannel.fifo.key();
79
+ * // blocks all `FIFO` called using this method and QChannel.fifoCallback;
80
+ * resume();
81
+ */
82
+ key: async () => {
83
+ return await qfifo.key(
84
+ /**
85
+ * uses locally declared object to make it unique from other QChannel instances;
86
+ */
87
+ qfifo
88
+ );
89
+ },
90
+ /**
91
+ * @static fifo
92
+ * @description
93
+ * - blocks execution for subsequent calls until the current one finishes.
94
+ * @template ResultType
95
+ * @param {()=>Promise<ResultType>} asyncCallback
96
+ * @returns {Promise<[ResultType|undefined, Error|undefined]>}
97
+ * @example
98
+ * const [result, error] = await QChannel.fifo.callback(async () = > {
99
+ * // code
100
+ * })
101
+ */
102
+ callback: async (asyncCallback) => {
103
+ return await TryAsync(async () => {
104
+ const { resume } = await this.fifo.key();
105
+ const result = await asyncCallback();
106
+ resume();
107
+ return result;
108
+ });
109
+ },
110
+ };
111
+ });
112
+ /**
113
+ * @type {Map<T, WeakKey>}
114
+ */
115
+ #mapped = new Map();
116
+ /**
117
+ * @description
118
+ * - clear up all queued on the instance;
119
+ * - only clear up the reference, the already called will not be stoped;
120
+ * @returns {void}
121
+ */
122
+ clear = () => {
123
+ this.#mapped.clear();
124
+ };
125
+ /**
126
+ * @description
127
+ * - each `QChannelInstance` are managing it's own `queue`, and will not `await` for other `QChannelInstance`;
128
+ * - caveat:
129
+ * >- need to manually call resume();
130
+ * >- slightly more performant than `callback`;
131
+ * @param {T} keyID
132
+ * @returns {Promise<QCBReturn>}
133
+ * @example
134
+ * const q = new QChannel();
135
+ * const handler = async () => {
136
+ * const { resume, isLastOnQ } = await q.key(keyID);
137
+ * // if (!isLastOnQ) { // imperative debounce if needed
138
+ * // resume();
139
+ * // return;
140
+ * // }
141
+ * // don't forget to call resume before any returns;
142
+ * // blocks only if keyID is the same, until resume is called;
143
+ * resume(); // don't forget to call resume before any returns;
144
+ * return 'something';
145
+ * }
146
+ * handler();
147
+ */
148
+ key = async (keyID) => {
149
+ const { resume } = await QChannel.#uniqueCB(this);
150
+ const mapped = this.#mapped;
151
+ if (!mapped.has(keyID)) {
152
+ mapped.set(keyID, {});
153
+ }
154
+ resume();
155
+ return await QChannel.#uniqueCB(mapped.get(keyID));
156
+ };
157
+ /**
158
+ * @description
159
+ * - `callbackBlock` with error as value:
160
+ * - caveat:
161
+ * >- no need to manually call resume();
162
+ * >- slightly less performant than `callback`;
163
+ * @template ResultType
164
+ * @param {T} keyID
165
+ * @param {(options:Omit<QCBReturn, "resume">)=>Promise<ResultType>} asyncCallback
166
+ * @returns {Promise<[ResultType|undefined, Error|undefined]>}
167
+ * @example
168
+ * const q = new QChannel();
169
+ * const [result, error] = await q.callback(keyID, async ({ isLastOnQ }) = > {
170
+ * // if (!isLastOnQ) { // imperative debounce if needed
171
+ * // return;
172
+ * // }
173
+ * // code
174
+ * })
175
+ */
176
+ callback = async (keyID, asyncCallback) => {
177
+ return await TryAsync(async () => {
178
+ const { resume, isLastOnQ } = await this.key(keyID);
179
+ const result = await asyncCallback({ isLastOnQ });
180
+ resume();
181
+ return result;
182
+ });
183
+ };
184
+ }
@@ -0,0 +1,131 @@
1
+ // @ts-check
2
+
3
+ import { TryAsync } from '../function/TryAsync.mjs';
4
+ import { Console } from './Console.mjs';
5
+ import { Effect, setOfEffects } from './Effect.mjs';
6
+ import { EnvSignal } from './EnvSignal.mjs';
7
+ import { setOFSignals } from './Signal.mjs';
8
+
9
+ /**
10
+ * @type {Set<()=>Promise<void>>}
11
+ */
12
+ export const safeCleanUpCBs = new Set();
13
+
14
+ /**
15
+ * @description
16
+ * - class helper for describing how to Safely Response on exit events
17
+ * - singleton;
18
+ * @template {[string, ...string[]]} ExitEventNames
19
+ */
20
+ export class SafeExit {
21
+ /**
22
+ * @description
23
+ * - only accessible after instantiation;
24
+ * @type {SafeExit}
25
+ */
26
+ static instance = undefined;
27
+ /**
28
+ * @description
29
+ * @param {Object} options
30
+ * @param {ExitEventNames} options.exitEventNames
31
+ * @param {(eventName:string)=>void} [options.exitCallbackListeners]
32
+ * - default value
33
+ * ```js
34
+ * (eventName) => {
35
+ * process.once(eventName, function () {
36
+ * SafeExit.instance.exiting.correction(true);
37
+ * Console.log(`safe exit via "${eventName}"`);
38
+ * });
39
+ * };
40
+ * ```
41
+ * - if your exit callback doesn't uses `process` global object you need to input on the SafeExit instantiation
42
+ * @example
43
+ * import { SafeExit, Console } from 'vivth';
44
+ *
45
+ * new SafeExit({
46
+ * // exitEventNames are blank by default, you need to manually name them all;
47
+ * exitEventNames: ['SIGINT', 'SIGTERM', ...otherExitEventNames],
48
+ * // optional deno example
49
+ * exitCallbackListeners = (eventName) => {
50
+ * const sig = Deno.signal(eventName);
51
+ * for await (const _ of sig) {
52
+ * exiting.correction(true);
53
+ * sig.dispose();
54
+ * Console.log(`safe exit via "${eventName}"`);
55
+ * }
56
+ * }
57
+ * });
58
+ */
59
+ constructor({ exitEventNames, exitCallbackListeners = undefined }) {
60
+ if (SafeExit.instance) {
61
+ return SafeExit.instance;
62
+ }
63
+ SafeExit.instance = this;
64
+ if (exitCallbackListeners) {
65
+ this.#exitCallbackListeners = exitCallbackListeners;
66
+ }
67
+ this.#register(exitEventNames);
68
+ }
69
+ /**
70
+ * @description
71
+ * - optional exit event registration, by listening to it inside an `Effect`;
72
+ * - when the value is `true`, meaning program is exitting;
73
+ * @type {EnvSignal<boolean>}
74
+ */
75
+ exiting = new EnvSignal(false);
76
+ /**
77
+ * @param {ExitEventNames} exitEventNames
78
+ * @returns {void}
79
+ */
80
+ #register = (exitEventNames) => {
81
+ exitEventNames.forEach((eventName) => {
82
+ this.#exitCallbackListeners(eventName);
83
+ });
84
+ };
85
+ /**
86
+ * @type {(eventName:string)=>void}
87
+ */
88
+ #exitCallbackListeners = (eventName) => {
89
+ SafeExit.instance.exiting.env.value;
90
+ process.once(eventName, function () {
91
+ Console.log(`safe exit via "${eventName}"`);
92
+ SafeExit.instance.exiting.correction(true);
93
+ });
94
+ };
95
+ /**
96
+ * @description
97
+ * - optional exit event registration;
98
+ * - the callbacks will be called when exiting;
99
+ * @param {()=>(Promise<void>)} cb
100
+ * @example
101
+ * import { SafeExit } from 'vivth';
102
+ *
103
+ * SafeExit.instance.addCallback(()=>{
104
+ * // code
105
+ * })
106
+ */
107
+ addCallback = (cb) => {
108
+ safeCleanUpCBs.add(cb);
109
+ };
110
+ #autoCleanUp = new Effect(async ({ subscribe }) => {
111
+ if (!subscribe(this.exiting.env).value) {
112
+ return;
113
+ }
114
+ setOFSignals.forEach((signal) => {
115
+ signal.remove.ref();
116
+ });
117
+ setOfEffects.forEach((effect) => {
118
+ effect.options.removeEffect();
119
+ });
120
+ safeCleanUpCBs.forEach((cleanup) => {
121
+ TryAsync(async () => {
122
+ await cleanup();
123
+ }).then(([_, error]) => {
124
+ if (!error) {
125
+ return;
126
+ }
127
+ Console.error(error);
128
+ });
129
+ });
130
+ });
131
+ }
@@ -0,0 +1,73 @@
1
+ // @ts-check
2
+
3
+ import { SafeExit } from './SafeExit.mjs';
4
+ import { Paths } from './Paths.mjs';
5
+ import { WorkerMainThread } from './WorkerMainThread.mjs';
6
+ import { WorkerThread } from './WorkerThread.mjs';
7
+
8
+ /**
9
+ * @description
10
+ * - class with collections of methods/constructors for setting up necessary variables for vivth internal functionalities;
11
+ */
12
+ export class Setup {
13
+ /**
14
+ * @description
15
+ * - proxy `SafeExit_instance` for Setup
16
+ * @example
17
+ * import { Setup, Console } from 'vivth';
18
+ *
19
+ * new Setup.safeExit({
20
+ * // exitEventNames are blank by default, you need to manually name them all;
21
+ * exitEventNames: ['SIGINT', 'SIGTERM', ...otherExitEventNames],
22
+ * // optional deno example
23
+ * exitCallbackListeners = (eventName) => {
24
+ * const sig = Deno.signal(eventName);
25
+ * for await (const _ of sig) {
26
+ * SafeExit.instance.exiting.correction(true);
27
+ * sig.dispose();
28
+ * Console.info(`safe exit via "${eventName}"`);
29
+ * }
30
+ * }
31
+ * });
32
+ */
33
+ static safeExit = SafeExit;
34
+ /**
35
+ * @description
36
+ * - proxy `Paths_instance` for Setup;
37
+ * @example
38
+ * import { Setup } from 'vivth';
39
+ *
40
+ * new Setup.paths({
41
+ * root: location.origin, // browser example
42
+ * })
43
+ */
44
+ static paths = Paths;
45
+ /**
46
+ * @description
47
+ * - proxy `WorkerMainThread_instance` for Setup;
48
+ * @example
49
+ * import { Setup } from 'vivth';
50
+ *
51
+ * Setup.workerMain({
52
+ * workerClass: async () => await (import('worker_threads')).Worker,
53
+ * basePath: 'public/assets/js/workers',
54
+ * pathValidator: async (workerPath, root, base) => {
55
+ * const res = await fetch(`${root}/${base}/${workerPath}`);
56
+ * // might also check wheter it need base or not
57
+ * return await res.ok;
58
+ * },
59
+ * });
60
+ */
61
+ static workerMain = WorkerMainThread.setup;
62
+ /**
63
+ * @description
64
+ * - correct `parentPort` reference when needed;
65
+ * @example
66
+ * import { Setup } from 'vivth';
67
+ *
68
+ * Setup.workerThread({ parentPort: async () => await import('node:worker_threads') });
69
+ * // that is the default value, if your parentPort/equivalent API is not that;
70
+ * // you need to call this method;
71
+ */
72
+ static workerThread = WorkerThread.setup;
73
+ }
@@ -1,99 +1,197 @@
1
1
  // @ts-check
2
2
 
3
- import { $ } from './$.mjs';
3
+ import { LazyFactory } from '../function/LazyFactory.mjs';
4
+ import { TryAsync } from '../function/TryAsync.mjs';
5
+ import { TrySync } from '../function/TrySync.mjs';
6
+ import { Console } from './Console.mjs';
7
+ import { Effect, setOfEffects } from './Effect.mjs';
4
8
 
5
9
  /**
6
- * @description
7
- * - a class for creating signal;
8
- * - can be subscribed by using [New$](#new$) or [NewDerived](#newderived);
9
- * - for minimal total bundle size use `function` [NewSignal](#newSignal) instead;
10
+ * @type {Set<Signal>}
10
11
  */
12
+ export const setOFSignals = new Set();
13
+
11
14
  /**
15
+ * @description
16
+ * - a class for creating effect to signals;
12
17
  * @template Value
13
18
  */
14
19
  export class Signal {
15
20
  /**
16
- * @protected
17
- */
18
- get subscribed() {
19
- return $.mappedSignals.get(this);
20
- }
21
- /**
22
- * destroy all props
21
+ * @description
22
+ * - subsrcibers reference of this instance;
23
23
  */
24
- unRef = () => {
25
- this.removeAll$();
26
- this.#Value = null;
27
- };
24
+ subscribers = LazyFactory(() => ({
25
+ /**
26
+ * @instance subscribers
27
+ * @description
28
+ * - subscribedEffects
29
+ * @type {Set<Effect>}
30
+ */
31
+ setOf: new Set(),
32
+ /**
33
+ * @instance subscribers
34
+ * @description
35
+ * - manually notify on non primitive value or value that have depths;
36
+ * @param {(options:{signalInstance:Signal<Value>})=>Promise<void>} [callback]
37
+ * @returns {void}
38
+ * @example
39
+ * // for deep signal like array or object you can:
40
+ *
41
+ * const arraySignal = new Signal([1,2]);
42
+ * arraySignal.value.push(3);
43
+ * arraySignal.subscribers.notify();
44
+ *
45
+ * // OR for more complex mutation:
46
+ *
47
+ * const objectSignal = new Signal({a:'test', b:'test'});
48
+ * objectSignal.subscribers.notify(async ({ signalInstance }) => {
49
+ * signalInstance.value['c'] = 'testc';
50
+ * signalInstance.value['d'] = 'testd';
51
+ * });
52
+ */
53
+ notify: (callback = undefined) => {
54
+ if (!callback) {
55
+ Signal.#notify(this.subscribers.setOf);
56
+ return;
57
+ }
58
+ TryAsync(async () => {
59
+ await callback({ signalInstance: this });
60
+ }).then(([_, error]) => {
61
+ if (error) {
62
+ Console.error({ message: 'unable to run callback', callback, error });
63
+ return;
64
+ }
65
+ Signal.#notify(this.subscribers.setOf);
66
+ });
67
+ },
68
+ }));
28
69
  /**
29
- * remove all effects
30
- * @return {void}
70
+ * @param {Set<Effect>} setOfSubscribers
31
71
  */
32
- removeAll$ = () => {
33
- this.subscribed?.forEach(($_) => {
34
- $_.remove$();
72
+ static #notify = (setOfSubscribers) => {
73
+ const [_, error] = TrySync(() => {
74
+ const effects = setOfSubscribers;
75
+ effects.forEach((effect) => {
76
+ if (!setOfEffects.has(effect)) {
77
+ effects.delete(effect);
78
+ return;
79
+ }
80
+ /**
81
+ * effect.run is already TryAsync
82
+ */
83
+ effect.run();
84
+ });
35
85
  });
86
+ if (!error) {
87
+ return;
88
+ }
89
+ Console.error(error);
36
90
  };
37
91
  /**
38
- * remove effect
39
- * @param {$} $_
40
- * @return {void}
92
+ * @description
93
+ * - collection of remove methods
41
94
  */
42
- remove$ = ($_) => {
43
- if ($.effects.get($_)?.has(this)) {
44
- $_.remove$();
45
- }
46
- };
95
+ remove = LazyFactory(() => ({
96
+ /**
97
+ * @instance remove
98
+ * @description
99
+ * - remove effect subscriber to react from this instance value changes;
100
+ * @param {Effect} effectInstance
101
+ * @returns {void}
102
+ */
103
+ subscriber: (effectInstance) => {
104
+ effectInstance.options.removeEffect();
105
+ this.subscribers.setOf.delete(effectInstance);
106
+ },
107
+ /**
108
+ * @instance remove
109
+ * @description
110
+ * - remove all effect subscribers to react from this instance value changes;
111
+ * @type {()=>void}
112
+ */
113
+ allSubscribers: () => {
114
+ const $ = this.subscribers.setOf;
115
+ $.forEach(this.remove.subscriber);
116
+ },
117
+ /**
118
+ * @instance remove
119
+ * @description
120
+ * - remove this instance from `vivth` reactivity engine, and nullify it's own value;
121
+ * @type {()=>void}
122
+ */
123
+ ref: () => {
124
+ this.remove.allSubscribers();
125
+ this.#value = null;
126
+ setOFSignals.delete(this);
127
+ },
128
+ }));
47
129
  /**
130
+ * @description
48
131
  * @param {Value} value
132
+ * @example
133
+ * import { Signal, Effect } from 'vivth';
134
+ *
135
+ * const count = new Signal(0);
49
136
  */
50
137
  constructor(value) {
51
- this.#Value = value;
138
+ this.#value = value;
139
+ setOFSignals.add(this);
52
140
  }
53
141
  /**
54
142
  * @type {Value}
55
143
  */
56
144
  #prev = undefined;
57
- get prev() {
58
- return this.#prev;
59
- }
60
145
  /**
146
+ * @description
147
+ * - value before change;
61
148
  * @type {Value}
62
149
  */
63
- #Value;
150
+ get prev() {
151
+ return this.#prev;
152
+ }
64
153
  /**
65
154
  * @type {Value}
66
155
  */
67
- get nonReactiveValue() {
68
- return this.#Value;
69
- }
156
+ #value;
70
157
  /**
158
+ * @description
159
+ * - value after change;
71
160
  * @type {Value}
161
+ * @example
162
+ * import { Signal, Effect, Derived } from 'vivth';
163
+ *
164
+ * const count = new Signal(0);
165
+ * count.value; // not reactive
166
+ *
167
+ * new Effect(async ({ subscribe }) =>{
168
+ * const countValue = subscribe(count).value; // reactive
169
+ * })
170
+ * const oneMoreThanCount = new Effect(async ({ subscribe }) =>{
171
+ * return subscribe(count).value + 1; // reactive
172
+ * })
72
173
  */
73
174
  get value() {
74
- if ($.isRegistering) {
75
- $.activeSignal.add(this);
76
- }
77
- return this.#Value;
175
+ return this.#value;
78
176
  }
79
177
  /**
178
+ * @description
179
+ * - assign new value then automatically notify all subscribers;
80
180
  * @type {Value}
181
+ * @example
182
+ * import { Signal } from 'vivth';
183
+ *
184
+ * const count = new Signal(0);
185
+ * count.value++;
186
+ * // OR
187
+ * count.value = 9;
81
188
  */
82
189
  set value(newValue) {
83
- if (this.#Value === newValue) {
190
+ if (this.#value === newValue) {
84
191
  return;
85
192
  }
86
- this.#prev = this.#Value;
87
- this.#Value = newValue;
88
- this.call$();
193
+ this.#prev = this.#value;
194
+ this.#value = newValue;
195
+ this.subscribers.notify();
89
196
  }
90
- /**
91
- * @returns {void}
92
- */
93
- call$ = () => {
94
- if (!this.subscribed) {
95
- return;
96
- }
97
- this.subscribed.forEach(($_) => $_.effect({ remove$: $_.remove$ }));
98
- };
99
197
  }