@fbltd/async 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.
package/README.md ADDED
@@ -0,0 +1,73 @@
1
+ ### R&D async tools ###
2
+
3
+ First of all, attention paid to creation of reactive model
4
+ leveraging native JavaScript async features like Promises, (async) iterators
5
+ and generators.
6
+
7
+ The version is 0.0.0 so keep it in mind
8
+
9
+ ```typescript
10
+ const counter = new DependencyStream<number>(0);
11
+
12
+ async function onCounterChange() {
13
+ for await (let value of counter.stream()) {
14
+ // do whatever you want
15
+ }
16
+
17
+ // here we going in case of disposing counter (counter.dispose)
18
+ // or stream itself (counter.stream().dispose)
19
+ }
20
+
21
+ onCounterChange();
22
+
23
+ let i = 0;
24
+ setInterval(() => {
25
+ for (let k = 0; k < 10; k++) {
26
+ // The counter detects changes when the first new value
27
+ // differs from the previous one.
28
+ // Subsequent value updates are collected and processed together
29
+ // during the next microtask execution phase of the event loop.
30
+ counter.set(i++);
31
+ }
32
+ }, 1000)
33
+ ```
34
+
35
+ Of course, there is a React integration via useStream hook:
36
+
37
+ ```typescript jsx
38
+ type IController = {
39
+ controller: SomeController,
40
+ }
41
+ export const Counter: React.FC<ITest> = React.memo(({
42
+ controller,
43
+ }) => {
44
+ // num is an instance of DependencyStream
45
+ const num = controller.num;
46
+ const {value, dispose} = useStream(num);
47
+
48
+ // resolving promises on component unmounting
49
+ // This is won't cause of rerender
50
+ useEffect(() => dispose, [])
51
+
52
+ return (
53
+ <div className={"container"}>
54
+ <button className={"incrementer"}
55
+ // Now result is array of streams values
56
+ // It should be object
57
+ onClick={() => num.set(value![0] + 1)}>
58
+ +
59
+ </button>
60
+
61
+ <div className={cn("display")}>
62
+ {
63
+ // Now result is array of streams values
64
+ // It should be object
65
+ }
66
+ {value![0]}
67
+ </div>
68
+ </div>
69
+ )
70
+ })
71
+ ```
72
+
73
+ Now this package is CommonJS module but should be ESM.
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DependencyStream = void 0;
4
+ const utils_1 = require("../utils");
5
+ class DependencyStream {
6
+ _value;
7
+ promise;
8
+ abortPromise = (0, utils_1.getPromise)();
9
+ constructor(_value) {
10
+ this._value = _value;
11
+ }
12
+ _set(v) {
13
+ if (v === this._value)
14
+ return;
15
+ this._value = v;
16
+ this.promise && this.promise.resolve(v);
17
+ }
18
+ set = (v) => {
19
+ this._set(v);
20
+ };
21
+ get() {
22
+ return this._value;
23
+ }
24
+ [Symbol.asyncIterator]() {
25
+ const totalDispose = this.abortPromise;
26
+ const selfDispose = (0, utils_1.getPromise)();
27
+ return {
28
+ owner: this,
29
+ dispose: () => selfDispose.resolve(undefined),
30
+ next: async () => {
31
+ if (!this.promise) {
32
+ this.promise = (0, utils_1.getPromise)();
33
+ this._set(this._value);
34
+ }
35
+ await Promise.race([
36
+ totalDispose.promise,
37
+ selfDispose.promise,
38
+ this.promise.promise,
39
+ ]);
40
+ this.promise = undefined;
41
+ if (totalDispose.isFulfilled || selfDispose.isFulfilled) {
42
+ return { done: true };
43
+ }
44
+ const value = this.get();
45
+ return {
46
+ done: false,
47
+ get value() {
48
+ return value;
49
+ }
50
+ };
51
+ }
52
+ };
53
+ }
54
+ dispose() {
55
+ this.abortPromise.resolve(undefined);
56
+ this.abortPromise = (0, utils_1.getPromise)();
57
+ }
58
+ }
59
+ exports.DependencyStream = DependencyStream;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./dependency-stream"), exports);
18
+ __exportStar(require("./utils"), exports);
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stream = stream;
4
+ exports.once = once;
5
+ exports.anyStream = anyStream;
6
+ function stream(dep) {
7
+ return dep[Symbol.asyncIterator]();
8
+ }
9
+ function once(dep) {
10
+ let isIterationWas = false;
11
+ const iterator = dep[Symbol.asyncIterator]();
12
+ return {
13
+ [Symbol.asyncIterator]() {
14
+ return {
15
+ next: async () => {
16
+ if (isIterationWas)
17
+ return { done: true };
18
+ return iterator.next();
19
+ }
20
+ };
21
+ },
22
+ dispose: iterator.dispose,
23
+ };
24
+ }
25
+ function anyStream(...deps) {
26
+ const streams = deps.map((dep) => stream(dep));
27
+ let disposed = false;
28
+ return {
29
+ dispose: () => {
30
+ streams.map(s => s.dispose());
31
+ disposed = true;
32
+ },
33
+ [Symbol.asyncIterator]() {
34
+ return {
35
+ next: async () => {
36
+ await Promise.race(streams.map(s => s.next()));
37
+ if (disposed) {
38
+ return { done: true };
39
+ }
40
+ return { done: false, value: streams.map(s => s.owner.get()) };
41
+ }
42
+ };
43
+ }
44
+ };
45
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./core"), exports);
18
+ __exportStar(require("./react"), exports);
19
+ __exportStar(require("./utils"), exports);
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./src"), exports);
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./use-stream"), exports);
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useStream = useStream;
4
+ const core_1 = require("../../core");
5
+ const react_1 = require("react");
6
+ class StreamController {
7
+ setValue;
8
+ streams;
9
+ iterator;
10
+ constructor(...streams) {
11
+ this.streams = streams;
12
+ this.init();
13
+ }
14
+ async init() {
15
+ this.iterator = (0, core_1.anyStream)(...this.streams);
16
+ for await (let chunk of this.iterator) {
17
+ console.log(chunk);
18
+ this.setValue?.(chunk);
19
+ }
20
+ }
21
+ dispose = () => {
22
+ this.iterator?.dispose();
23
+ };
24
+ }
25
+ function useStream(...streams) {
26
+ const [value, setValue] = (0, react_1.useState)(streams.map(s => s.get()));
27
+ const [obj] = (0, react_1.useState)(() => ({ controller: new StreamController(...streams) }));
28
+ obj.controller.setValue = setValue;
29
+ (0, react_1.useEffect)(() => {
30
+ return () => {
31
+ obj.controller.dispose();
32
+ obj.controller = undefined;
33
+ };
34
+ }, []);
35
+ return {
36
+ value,
37
+ dispose: obj.controller.dispose,
38
+ };
39
+ }
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getPromise = getPromise;
4
+ function getPromise() {
5
+ let resolve;
6
+ let reject;
7
+ let isPending = true;
8
+ let isFulfilled = false;
9
+ const promise = new Promise((res, rej) => {
10
+ resolve = (value) => {
11
+ res(value);
12
+ isPending = false;
13
+ isFulfilled = true;
14
+ };
15
+ reject = (error) => {
16
+ rej(error);
17
+ isPending = false;
18
+ isFulfilled = true;
19
+ };
20
+ });
21
+ return {
22
+ resolve,
23
+ reject,
24
+ get isPending() {
25
+ return isPending;
26
+ },
27
+ get isFulfilled() {
28
+ return isFulfilled;
29
+ },
30
+ promise
31
+ };
32
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./get-promise"), exports);
@@ -0,0 +1,21 @@
1
+ export declare class DependencyStream<T = any> {
2
+ private _value;
3
+ private promise;
4
+ private abortPromise;
5
+ constructor(_value: T);
6
+ private _set;
7
+ set: (v: T) => void;
8
+ get(): T;
9
+ [Symbol.asyncIterator](this: DependencyStream<T>): {
10
+ owner: DependencyStream<T>;
11
+ dispose: () => void;
12
+ next: () => Promise<{
13
+ done: boolean;
14
+ readonly value?: undefined;
15
+ } | {
16
+ done: boolean;
17
+ readonly value: T;
18
+ }>;
19
+ };
20
+ dispose(this: DependencyStream<T>): void;
21
+ }
@@ -0,0 +1,2 @@
1
+ export * from './dependency-stream';
2
+ export * from './utils';
@@ -0,0 +1,35 @@
1
+ import { DependencyStream } from "./dependency-stream";
2
+ export declare function stream(dep: DependencyStream): {
3
+ owner: DependencyStream<any>;
4
+ dispose: () => void;
5
+ next: () => Promise<{
6
+ done: boolean;
7
+ readonly value?: undefined;
8
+ } | {
9
+ done: boolean;
10
+ readonly value: any;
11
+ }>;
12
+ };
13
+ export declare function once(dep: DependencyStream): {
14
+ [Symbol.asyncIterator](): {
15
+ next: () => Promise<{
16
+ done: boolean;
17
+ readonly value: any;
18
+ } | {
19
+ done: boolean;
20
+ }>;
21
+ };
22
+ dispose: () => void;
23
+ };
24
+ export declare function anyStream(...deps: DependencyStream[]): {
25
+ dispose: () => void;
26
+ [Symbol.asyncIterator](): {
27
+ next: () => Promise<{
28
+ done: boolean;
29
+ readonly value?: undefined;
30
+ } | {
31
+ done: boolean;
32
+ value: any[];
33
+ }>;
34
+ };
35
+ };
@@ -0,0 +1,3 @@
1
+ export * from './core';
2
+ export * from './react';
3
+ export * from './utils';
@@ -0,0 +1 @@
1
+ export * from './src';
@@ -0,0 +1 @@
1
+ export * from './use-stream';
@@ -0,0 +1,5 @@
1
+ import { DependencyStream } from "../../core";
2
+ export declare function useStream(...streams: DependencyStream[]): {
3
+ value: Array<any> | undefined;
4
+ dispose: () => void;
5
+ };
@@ -0,0 +1,7 @@
1
+ export declare function getPromise<TReturn>(): {
2
+ resolve: (value: TReturn) => void;
3
+ reject: (value: Error) => void;
4
+ readonly isPending: boolean;
5
+ readonly isFulfilled: boolean;
6
+ promise: Promise<TReturn>;
7
+ };
@@ -0,0 +1 @@
1
+ export * from './get-promise';
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@fbltd/async",
3
+ "version": "1.0.1",
4
+ "description": "R&D async tools",
5
+ "exports": {
6
+ "require": "./dist/bin/index.js",
7
+ "import": "./dist/bin/index.js",
8
+ "types": "./dist/types/index.d.ts"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "package.json",
13
+ "tsconfig.base.json",
14
+ "tsconfig.json"
15
+ ],
16
+ "scripts": {
17
+ "clearDist": "rm dist -rf || true",
18
+ "clearModules": "rm node_modules -rf || true",
19
+ "clearAll": "npm run clearDist && npm run clearModules",
20
+ "build": "cd src/react && npm run build && cd ../../ && npm run clearAll && npm i && mkdir dist && tsc",
21
+ "test": "jest --config=./__tests__/jest.config.js",
22
+ "patch": "npm version patch && git commit -a -m version patch && git push",
23
+ "deploy": "npm run build && npm run test && npm run patch && npm run publish"
24
+ },
25
+ "author": "",
26
+ "license": "ISC",
27
+ "devDependencies": {
28
+ "@types/jest": "^30.0.0",
29
+ "jest": "^30.0.4",
30
+ "ts-jest": "^29.4.0",
31
+ "typescript": "^5.x.x"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ }
36
+ }
@@ -0,0 +1,36 @@
1
+ // @formatter:off
2
+ {
3
+ "compilerOptions": {
4
+
5
+ // syntax
6
+ "esModuleInterop": true,
7
+ "allowSyntheticDefaultImports": true,
8
+ "noImplicitThis": true,
9
+ "noImplicitReturns": true,
10
+ "noImplicitOverride": true,
11
+ "noImplicitAny": true,
12
+ "strictNullChecks": true,
13
+ "strictBindCallApply": true,
14
+ "strictBuiltinIteratorReturn": true,
15
+ "strictFunctionTypes": true,
16
+ "strictPropertyInitialization": true,
17
+ "allowJs": false,
18
+
19
+ // emit
20
+ "target": "ESNext",
21
+ "noEmitOnError": true,
22
+ "removeComments": false,
23
+ "outDir": "dist/bin",
24
+ "declaration": true,
25
+ "declarationDir": "dist/types",
26
+ "declarationMap": false, // Typically, source files are not emits, so there is no reason to emit source maps
27
+ "sourceMap": false // Typically, source files are not emits, so there is no reason to emit source maps
28
+ },
29
+
30
+ "exclude": [
31
+ "./node_modules/",
32
+ "./dist/",
33
+ "./__tests__",
34
+ ]
35
+ }
36
+ // @formatter:on
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ // @formatter:off
2
+ {
3
+ "extends": "./tsconfig.base.json",
4
+ "compilerOptions": {
5
+ "module": "CommonJS",
6
+ "moduleResolution": "node",
7
+ },
8
+ }
9
+ // @formatter:on