vivth 0.11.1 → 1.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.
- package/.vivth/dist/init.mjs +24 -0
- package/README.md +2157 -69
- package/README.src.md +35 -0
- package/bun.lock +57 -3
- package/dev/index.mjs +24 -25
- package/index.mjs +51 -29
- package/package.json +11 -7
- package/src/bundler/CompileMJS.mjs +110 -0
- package/src/bundler/EsBundler.mjs +79 -0
- package/src/class/Console.mjs +62 -0
- package/src/class/Derived.mjs +37 -21
- package/src/class/Effect.mjs +106 -0
- package/src/class/EnvSignal.mjs +88 -0
- package/src/class/EventSignal.mjs +200 -0
- package/src/class/ListDerived.mjs +39 -0
- package/src/class/ListSignal.mjs +256 -0
- package/src/class/Paths.mjs +70 -0
- package/src/class/QChannel.mjs +184 -0
- package/src/class/SafeExit.mjs +131 -0
- package/src/class/Setup.mjs +73 -0
- package/src/class/Signal.mjs +152 -54
- package/src/class/WorkerMainThread.mjs +328 -0
- package/src/class/WorkerResult.mjs +30 -0
- package/src/class/WorkerThread.mjs +151 -0
- package/src/common/Base64URL.mjs +26 -0
- package/src/common/EventNameSpace.mjs +8 -0
- package/src/common/eventObjects.mjs +5 -0
- package/src/common/lazie.mjs +3 -0
- package/src/doc/JSautoDOC.mjs +386 -0
- package/src/doc/parsedFile.mjs +537 -0
- package/src/function/CreateImmutable.mjs +64 -0
- package/src/function/EventCheck.mjs +27 -0
- package/src/function/EventObject.mjs +21 -0
- package/src/function/IsAsync.mjs +23 -0
- package/src/function/LazyFactory.mjs +71 -0
- package/src/function/Timeout.mjs +23 -0
- package/src/function/Try.mjs +64 -0
- package/src/function/TryAsync.mjs +15 -4
- package/src/function/TrySync.mjs +9 -4
- package/src/function/TsToMjs.mjs +67 -0
- package/src/function/WriteFileSafe.mjs +37 -0
- package/src/types/{AnyButUndefined.type.mjs → AnyButUndefined.mjs} +1 -0
- package/src/types/ExtnameType.mjs +6 -0
- package/src/types/IsListSignal.mjs +6 -0
- package/src/types/ListArg.mjs +6 -0
- package/src/types/MutationType.mjs +8 -0
- package/src/types/QCBFIFOReturn.mjs +6 -0
- package/src/types/QCBReturn.mjs +6 -0
- package/tsconfig.json +3 -3
- package/types/dev/index.d.mts +1 -0
- package/types/index.d.mts +34 -8
- package/types/src/bundler/A.d.mts +1 -0
- package/types/src/bundler/CompileMJS.d.mts +8 -0
- package/types/src/bundler/EsBundler.d.mts +7 -0
- package/types/src/class/Console.d.mts +40 -0
- package/types/src/class/Derived.d.mts +21 -7
- package/types/src/class/Effect.d.mts +77 -0
- package/types/src/class/EnvSignal.d.mts +47 -0
- package/types/src/class/EventSignal.d.mts +145 -0
- package/types/src/class/ListDerived.d.mts +35 -0
- package/types/src/class/ListSignal.d.mts +150 -0
- package/types/src/class/Paths.d.mts +50 -0
- package/types/src/class/QChannel.d.mts +115 -0
- package/types/src/class/SafeExit.d.mts +76 -0
- package/types/src/class/Setup.d.mts +76 -0
- package/types/src/class/Signal.d.mts +105 -26
- package/types/src/class/WorkerMainThread.d.mts +149 -0
- package/types/src/class/WorkerResult.d.mts +25 -0
- package/types/src/class/WorkerThread.d.mts +70 -0
- package/types/src/common/Base64URL.d.mts +1 -0
- package/types/src/common/EventNameSpace.d.mts +6 -0
- package/types/src/common/eventObjects.d.mts +3 -0
- package/types/src/common/lazie.d.mts +1 -0
- package/types/src/doc/JSautoDOC.d.mts +76 -0
- package/types/src/doc/parsedFile.d.mts +154 -0
- package/types/src/function/CreateImmutable.d.mts +3 -0
- package/types/src/function/EventCheck.d.mts +2 -0
- package/types/src/function/EventObject.d.mts +4 -0
- package/types/src/function/IsAsync.d.mts +1 -0
- package/types/src/function/LazyFactory.d.mts +4 -0
- package/types/src/function/Timeout.d.mts +1 -0
- package/types/src/function/Try.d.mts +1 -0
- package/types/src/function/TsToMjs.d.mts +4 -0
- package/types/src/function/WriteFileSafe.d.mts +2 -0
- package/types/src/types/{AnyButUndefined.type.d.mts → AnyButUndefined.d.mts} +3 -0
- package/types/src/types/ExtnameType.d.mts +4 -0
- package/types/src/types/IsListSignal.d.mts +4 -0
- package/types/src/types/ListArg.d.mts +4 -0
- package/types/src/types/MutationType.d.mts +5 -0
- package/types/src/types/QCBFIFOReturn.d.mts +4 -0
- package/types/src/types/QCBReturn.d.mts +7 -0
- package/src/class/$.mjs +0 -68
- package/src/class/PingFIFO.mjs +0 -78
- package/src/class/PingUnique.mjs +0 -84
- package/src/class/Q.mjs +0 -98
- package/src/class/QFIFO.mjs +0 -66
- package/src/class/QUnique.mjs +0 -75
- package/src/common.mjs +0 -16
- package/src/function/NewQBlock.mjs +0 -39
- package/types/src/class/$.d.mts +0 -38
- package/types/src/class/PingFIFO.d.mts +0 -57
- package/types/src/class/PingUnique.d.mts +0 -48
- package/types/src/class/Q.d.mts +0 -63
- package/types/src/class/QFIFO.d.mts +0 -47
- package/types/src/class/QUnique.d.mts +0 -46
- package/types/src/common.d.mts +0 -2
- 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
|
+
}
|
package/src/class/Signal.mjs
CHANGED
|
@@ -1,99 +1,197 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
-
import {
|
|
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
|
-
* @
|
|
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
|
-
* @
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
*
|
|
30
|
-
* @return {void}
|
|
70
|
+
* @param {Set<Effect>} setOfSubscribers
|
|
31
71
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* @return {void}
|
|
92
|
+
* @description
|
|
93
|
+
* - collection of remove methods
|
|
41
94
|
*/
|
|
42
|
-
remove
|
|
43
|
-
|
|
44
|
-
|
|
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.#
|
|
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
|
-
|
|
150
|
+
get prev() {
|
|
151
|
+
return this.#prev;
|
|
152
|
+
}
|
|
64
153
|
/**
|
|
65
154
|
* @type {Value}
|
|
66
155
|
*/
|
|
67
|
-
|
|
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
|
-
|
|
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.#
|
|
190
|
+
if (this.#value === newValue) {
|
|
84
191
|
return;
|
|
85
192
|
}
|
|
86
|
-
this.#prev = this.#
|
|
87
|
-
this.#
|
|
88
|
-
this.
|
|
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());
|
|
98
|
-
};
|
|
99
197
|
}
|