@thi.ng/rstream 8.2.12 → 8.2.14

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/object.js CHANGED
@@ -1,103 +1,39 @@
1
1
  import { dedupe } from "@thi.ng/transducers/dedupe";
2
2
  import { __nextID } from "./idgen.js";
3
3
  import { subscription } from "./subscription.js";
4
- /**
5
- * Takes an arbitrary object `src` and object of options (see
6
- * {@link StreamObjOpts}). Creates a new object and for each selected
7
- * key creates a new stream, optionally seeded with the key's value in
8
- * `src`. Returns new object of streams.
9
- *
10
- * @remarks
11
- * The structure of the returned object is
12
- * {@link StreamObj | as follows}:
13
- *
14
- * ```ts
15
- * {
16
- * streams: { ... },
17
- * next(x): void;
18
- * done(): void;
19
- * }
20
- * ```
21
- *
22
- * All streams will be stored under `streams`. The `next()` and `done()`
23
- * functions/methods allow the object itself to be used as subscriber
24
- * for an upstream subscribable (see 2nd example below):
25
- *
26
- * - `next()` - takes a object of same type as `src` and feeds each
27
- * key's new value into its respective stream. If the `defaults`
28
- * option is given, `undefined` key values are replaced with their
29
- * specified default. If `dedupe` is enabled (default) only changed
30
- * values (as per `equiv` predicate option) will be propagated
31
- * downstream.
32
- * - `done()` - calls {@link ISubscriber.done} on all streams
33
- *
34
- * The optional `opts` arg is used to customize overall behavior of
35
- * `fromObject` and specify shared options for *all* created streams.
36
- *
37
- * @example
38
- * ```ts
39
- * type Foo = { a?: number; b: string; };
40
- *
41
- * const obj = fromObject(<Foo>{ a: 1, b: "foo" })
42
- *
43
- * obj.streams.a.subscribe(trace("a"))
44
- * // a 1
45
- * obj.streams.b.subscribe(trace("b"))
46
- * // b foo
47
- *
48
- * obj.next({ b: "bar" })
49
- * // a undefined
50
- * // b bar
51
- * ```
52
- *
53
- * @example
54
- * ```ts
55
- * const obj = fromObject(<Foo>{}, ["a", "b"], { initial: false });
56
- * obj.streams.a.subscribe(trace("a"));
57
- * obj.streams.b.subscribe(trace("b"));
58
- *
59
- * const src = subscription<Foo, Foo>();
60
- * // use as subscriber
61
- * src.subscribe(obj);
62
- *
63
- * src.next({ a: 1, b: "foo" });
64
- * // a 1
65
- * // b foo
66
- * ```
67
- *
68
- * @param src -
69
- * @param opts -
70
- */
71
- export const fromObject = (src, opts = {}) => {
72
- const id = opts.id || `obj${__nextID()}`;
73
- const keys = opts.keys || Object.keys(src);
74
- const _opts = opts.dedupe !== false
75
- ? {
76
- xform: dedupe(opts.equiv || ((a, b) => a === b)),
77
- ...opts,
78
- }
79
- : opts;
80
- const streams = {};
81
- for (let k of keys) {
82
- streams[k] = subscription(undefined, {
83
- ..._opts,
84
- id: `${id}-${String(k)}`,
85
- });
4
+ const fromObject = (src, opts = {}) => {
5
+ const id = opts.id || `obj${__nextID()}`;
6
+ const keys = opts.keys || Object.keys(src);
7
+ const _opts = opts.dedupe !== false ? {
8
+ xform: dedupe(opts.equiv || ((a, b) => a === b)),
9
+ ...opts
10
+ } : opts;
11
+ const streams = {};
12
+ for (let k of keys) {
13
+ streams[k] = subscription(void 0, {
14
+ ..._opts,
15
+ id: `${id}-${String(k)}`
16
+ });
17
+ }
18
+ const res = {
19
+ streams,
20
+ next(state) {
21
+ for (let k of keys) {
22
+ const val = state[k];
23
+ streams[k].next(
24
+ opts.defaults && val === void 0 ? opts.defaults[k] : val
25
+ );
26
+ }
27
+ },
28
+ done() {
29
+ for (let k of keys) {
30
+ streams[k].done();
31
+ }
86
32
  }
87
- const res = {
88
- streams,
89
- next(state) {
90
- for (let k of keys) {
91
- const val = state[k];
92
- streams[k].next(opts.defaults && val === undefined ? opts.defaults[k] : val);
93
- }
94
- },
95
- done() {
96
- for (let k of keys) {
97
- streams[k].done();
98
- }
99
- },
100
- };
101
- opts.initial !== false && res.next(src);
102
- return res;
33
+ };
34
+ opts.initial !== false && res.next(src);
35
+ return res;
36
+ };
37
+ export {
38
+ fromObject
103
39
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/rstream",
3
- "version": "8.2.12",
3
+ "version": "8.2.14",
4
4
  "description": "Reactive streams & subscription primitives for constructing dataflow graphs / pipelines",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -28,7 +28,9 @@
28
28
  ],
29
29
  "license": "Apache-2.0",
30
30
  "scripts": {
31
- "build": "yarn clean && tsc --declaration",
31
+ "build": "yarn build:esbuild && yarn build:decl",
32
+ "build:decl": "tsc --declaration --emitDeclarationOnly",
33
+ "build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
32
34
  "clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc internal",
33
35
  "doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
34
36
  "doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
@@ -37,18 +39,18 @@
37
39
  "test": "bun test"
38
40
  },
39
41
  "dependencies": {
40
- "@thi.ng/api": "^8.9.10",
41
- "@thi.ng/arrays": "^2.7.6",
42
- "@thi.ng/associative": "^6.3.22",
43
- "@thi.ng/atom": "^5.2.16",
44
- "@thi.ng/checks": "^3.4.10",
45
- "@thi.ng/errors": "^2.4.4",
46
- "@thi.ng/logger": "^2.0.1",
47
- "@thi.ng/transducers": "^8.8.13"
42
+ "@thi.ng/api": "^8.9.12",
43
+ "@thi.ng/arrays": "^2.7.8",
44
+ "@thi.ng/associative": "^6.3.24",
45
+ "@thi.ng/atom": "^5.2.18",
46
+ "@thi.ng/checks": "^3.4.12",
47
+ "@thi.ng/errors": "^2.4.6",
48
+ "@thi.ng/logger": "^2.0.2",
49
+ "@thi.ng/transducers": "^8.8.15"
48
50
  },
49
51
  "devDependencies": {
50
52
  "@microsoft/api-extractor": "^7.38.3",
51
- "@thi.ng/testament": "^0.4.3",
53
+ "esbuild": "^0.19.8",
52
54
  "rimraf": "^5.0.5",
53
55
  "tools": "^0.0.1",
54
56
  "typedoc": "^0.25.4",
@@ -213,5 +215,5 @@
213
215
  ],
214
216
  "year": 2017
215
217
  },
216
- "gitHead": "04d1de79f256d7a53c6b5fd157b37f49bc88e11d\n"
218
+ "gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
217
219
  }
package/post-worker.js CHANGED
@@ -2,64 +2,33 @@ import { isTransferable } from "@thi.ng/checks/is-transferable";
2
2
  import { isTypedArray } from "@thi.ng/checks/is-typedarray";
3
3
  import { LOGGER } from "./logger.js";
4
4
  import { defWorker } from "./defworker.js";
5
- /**
6
- * Creates a {@link ISubscriber | subscriber} which forwards received
7
- * values to given worker.
8
- *
9
- * @remarks
10
- * The `worker` can be an existing `Worker` instance, a JS source code
11
- * `Blob` or an URL string. In the latter two cases, a worker is created
12
- * automatically. If `transfer` is true, the received values will be
13
- * marked as *transferrable* and the host app loses all access
14
- * permissions to these marked values. See `Worker.postMessage()` for
15
- * details.
16
- *
17
- * If `terminate` is set to a positive number, then the worker will be
18
- * automatically terminated after the stated number of milliseconds
19
- * since the parent subscription is {@link ISubscriber.done}.
20
- *
21
- * @example
22
- * ```ts
23
- * // worker source code
24
- * src = `self.onmessage = (e) => console.log("worker", e.data);`;
25
- *
26
- * a = stream();
27
- * a.subscribe(
28
- * postWorker(src, { type: "application/javascript" }))
29
- * );
30
- *
31
- * a.next(42)
32
- * // worker 42
33
- * ```
34
- *
35
- * @param worker -
36
- * @param transfer -
37
- * @param terminate - worker termination delay (ms)
38
- */
39
- export const postWorker = (worker, transfer = false, terminate = 0) => {
40
- const _worker = defWorker(worker);
41
- return {
42
- next(x) {
43
- if (x instanceof Promise) {
44
- x.then((y) => this.next(y));
45
- return;
46
- }
47
- let tx;
48
- if (transfer) {
49
- const ta = isTypedArray(x);
50
- if (ta || isTransferable(x)) {
51
- tx = [ta ? x.buffer : x];
52
- }
53
- }
54
- _worker.postMessage(x, tx || []);
55
- },
56
- done() {
57
- if (terminate > 0) {
58
- setTimeout(() => {
59
- LOGGER.info("terminating worker...");
60
- _worker.terminate();
61
- }, terminate);
62
- }
63
- },
64
- };
5
+ const postWorker = (worker, transfer = false, terminate = 0) => {
6
+ const _worker = defWorker(worker);
7
+ return {
8
+ next(x) {
9
+ if (x instanceof Promise) {
10
+ x.then((y) => this.next(y));
11
+ return;
12
+ }
13
+ let tx;
14
+ if (transfer) {
15
+ const ta = isTypedArray(x);
16
+ if (ta || isTransferable(x)) {
17
+ tx = [ta ? x.buffer : x];
18
+ }
19
+ }
20
+ _worker.postMessage(x, tx || []);
21
+ },
22
+ done() {
23
+ if (terminate > 0) {
24
+ setTimeout(() => {
25
+ LOGGER.info("terminating worker...");
26
+ _worker.terminate();
27
+ }, terminate);
28
+ }
29
+ }
30
+ };
31
+ };
32
+ export {
33
+ postWorker
65
34
  };
package/promise.js CHANGED
@@ -1,40 +1,34 @@
1
1
  import { CloseMode, State } from "./api.js";
2
2
  import { __optsWithID } from "./idgen.js";
3
3
  import { stream } from "./stream.js";
4
- /**
5
- * Yields a single-value {@link Stream} of the resolved promise and then
6
- * automatically marks itself done.
7
- *
8
- * @remarks
9
- * It doesn't matter if the promise resolves before the first subscriber
10
- * has attached.
11
- *
12
- * @param src -
13
- * @param opts -
14
- */
15
- export const fromPromise = (src, opts) => {
16
- let canceled = false;
17
- let isError = false;
18
- let err = {};
19
- src.catch((e) => {
20
- err = e;
21
- isError = true;
22
- });
23
- return stream((stream) => {
24
- src.then((x) => {
25
- if (!canceled && stream.getState() < State.DONE) {
26
- if (isError) {
27
- stream.error(err);
28
- err = null;
29
- }
30
- else {
31
- stream.next(x);
32
- stream.closeIn !== CloseMode.NEVER && stream.done();
33
- }
34
- }
35
- }, (e) => stream.error(e));
36
- return () => {
37
- canceled = true;
38
- };
39
- }, __optsWithID("promise", opts));
4
+ const fromPromise = (src, opts) => {
5
+ let canceled = false;
6
+ let isError = false;
7
+ let err = {};
8
+ src.catch((e) => {
9
+ err = e;
10
+ isError = true;
11
+ });
12
+ return stream((stream2) => {
13
+ src.then(
14
+ (x) => {
15
+ if (!canceled && stream2.getState() < State.DONE) {
16
+ if (isError) {
17
+ stream2.error(err);
18
+ err = null;
19
+ } else {
20
+ stream2.next(x);
21
+ stream2.closeIn !== CloseMode.NEVER && stream2.done();
22
+ }
23
+ }
24
+ },
25
+ (e) => stream2.error(e)
26
+ );
27
+ return () => {
28
+ canceled = true;
29
+ };
30
+ }, __optsWithID("promise", opts));
31
+ };
32
+ export {
33
+ fromPromise
40
34
  };
package/promises.js CHANGED
@@ -1,44 +1,10 @@
1
1
  import { mapcat } from "@thi.ng/transducers/mapcat";
2
2
  import { __optsWithID } from "./idgen.js";
3
3
  import { fromPromise } from "./promise.js";
4
- /**
5
- * Wraps given iterable in `Promise.all()` to yield {@link Stream} of results in
6
- * same order as arguments, then closes.
7
- *
8
- * @remarks
9
- * If any of the promises rejects, all others will do so too. In this case the
10
- * stream calls {@link ISubscriber.error} in all of its subscribers.
11
- *
12
- * @remarks
13
- * Type signature updated to use `Awaited<T>`, a new core type introduced in
14
- * TypeScript 4.5.
15
- *
16
- * @example
17
- * ```ts
18
- * fromPromises([
19
- * Promise.resolve(1),
20
- * Promise.resolve(2),
21
- * Promise.resolve(3)
22
- * ]).subscribe(trace())
23
- * // 1
24
- * // 2
25
- * // 3
26
- * // done
27
- * ```
28
- *
29
- * @example
30
- * If individual error handling is required, an alternative is below
31
- * (however this approach provides no ordering guarantees):
32
- *
33
- * ```ts
34
- * fromIterable([
35
- * Promise.resolve(1),
36
- * new Promise(() => setTimeout(() => { throw new Error("eeek"); }, 10)),
37
- * Promise.resolve(3)
38
- * ]).subscribe(resolve()).subscribe(trace())
39
- * ```
40
- *
41
- * @param promises -
42
- * @param opts -
43
- */
44
- export const fromPromises = (promises, opts) => fromPromise(Promise.all(promises), __optsWithID("promises", opts)).transform(mapcat((x) => x));
4
+ const fromPromises = (promises, opts) => fromPromise(
5
+ Promise.all(promises),
6
+ __optsWithID("promises", opts)
7
+ ).transform(mapcat((x) => x));
8
+ export {
9
+ fromPromises
10
+ };
package/pubsub.js CHANGED
@@ -1,115 +1,101 @@
1
1
  import { EquivMap } from "@thi.ng/associative/equiv-map";
2
2
  import { unsupported } from "@thi.ng/errors/unsupported";
3
- import { CloseMode, } from "./api.js";
3
+ import {
4
+ CloseMode
5
+ } from "./api.js";
4
6
  import { __optsWithID } from "./idgen.js";
5
7
  import { LOGGER } from "./logger.js";
6
8
  import { Subscription, subscription } from "./subscription.js";
7
- /**
8
- * Topic based stream splitter. Applies `topic` function to each received value
9
- * and only forwards it to the child subscriptions of the returned topic.
10
- *
11
- * @remarks
12
- * The actual topic (return value from `topic` fn) can be of any type `T`, or
13
- * `undefined`. If the latter is returned, the incoming value will not be
14
- * processed further. Complex topics (e.g objects / arrays) are allowed and
15
- * they're matched against registered topics using
16
- * [`equiv()`](https://docs.thi.ng/umbrella/equiv/functions/equiv.html) by
17
- * default (but customizable via `equiv` option). Each topic can have any number
18
- * of subscribers.
19
- *
20
- * If a `xform` transducer is given, it is always applied prior to passing the
21
- * input to the topic function. I.e. in this case the topic function will
22
- * receive the transformed inputs.
23
- *
24
- * {@link PubSub} supports dynamic topic subscriptions and unsubscriptions via
25
- * {@link PubSub.(subscribeTopic:1)} and {@link PubSub.unsubscribeTopic}.
26
- * However, the standard {@link ISubscribable.(subscribe:1)} /
27
- * {@link ISubscribable.unsubscribe} methods are NOT supported (since
28
- * meaningless) and will throw an error! `unsubscribe()` can only be called
29
- * WITHOUT argument to unsubscribe the entire `PubSub` instance (incl. all topic
30
- * subscriptions) from the parent stream.
31
- *
32
- * @param opts -
33
- */
34
- export const pubsub = (opts) => new PubSub(opts);
35
- /**
36
- * @see {@link pubsub} for reference & examples.
37
- */
38
- export class PubSub extends Subscription {
39
- topicfn;
40
- topics;
41
- constructor(opts) {
42
- super(undefined, __optsWithID("pubsub", {
43
- xform: opts.xform,
44
- }));
45
- this.topicfn = opts.topic;
46
- this.topics = new EquivMap(undefined, {
47
- equiv: opts.equiv,
48
- });
9
+ const pubsub = (opts) => new PubSub(opts);
10
+ class PubSub extends Subscription {
11
+ topicfn;
12
+ topics;
13
+ constructor(opts) {
14
+ super(
15
+ void 0,
16
+ __optsWithID("pubsub", {
17
+ xform: opts.xform
18
+ })
19
+ );
20
+ this.topicfn = opts.topic;
21
+ this.topics = new EquivMap(void 0, {
22
+ equiv: opts.equiv
23
+ });
24
+ }
25
+ /**
26
+ * Unsupported. Use {@link PubSub.(subscribeTopic:1)} instead.
27
+ */
28
+ subscribe() {
29
+ return unsupported(`use subscribeTopic() instead`);
30
+ }
31
+ /**
32
+ * Unsupported. Use {@link PubSub.(subscribeTopic:1)} instead.
33
+ */
34
+ transform() {
35
+ return unsupported(`use subscribeTopic() instead`);
36
+ }
37
+ subscribeTopic(topicID, sub, opts) {
38
+ let t = this.topics.get(topicID);
39
+ !t && this.topics.set(
40
+ topicID,
41
+ t = subscription(
42
+ void 0,
43
+ __optsWithID("topic", {
44
+ closeOut: CloseMode.NEVER
45
+ })
46
+ )
47
+ );
48
+ return t.subscribe(sub, opts);
49
+ }
50
+ transformTopic(topicID, xform, opts = {}) {
51
+ return this.subscribeTopic(
52
+ topicID,
53
+ { error: opts.error },
54
+ {
55
+ ...opts,
56
+ xform
57
+ }
58
+ );
59
+ }
60
+ unsubscribeTopic(topicID, sub) {
61
+ const t = this.topics.get(topicID);
62
+ return t ? t.unsubscribe(sub) : false;
63
+ }
64
+ unsubscribe(sub) {
65
+ if (!sub) {
66
+ for (let t of this.topics.values()) {
67
+ t.unsubscribe();
68
+ }
69
+ this.topics.clear();
70
+ return super.unsubscribe();
49
71
  }
50
- /**
51
- * Unsupported. Use {@link PubSub.(subscribeTopic:1)} instead.
52
- */
53
- subscribe() {
54
- return unsupported(`use subscribeTopic() instead`);
72
+ return unsupported();
73
+ }
74
+ done() {
75
+ for (let t of this.topics.values()) {
76
+ t.done();
55
77
  }
56
- /**
57
- * Unsupported. Use {@link PubSub.(subscribeTopic:1)} instead.
58
- */
59
- transform() {
60
- return unsupported(`use subscribeTopic() instead`);
61
- }
62
- subscribeTopic(topicID, sub, opts) {
63
- let t = this.topics.get(topicID);
64
- !t &&
65
- this.topics.set(topicID, (t = subscription(undefined, __optsWithID("topic", {
66
- closeOut: CloseMode.NEVER,
67
- }))));
68
- return t.subscribe(sub, opts);
69
- }
70
- transformTopic(topicID, xform, opts = {}) {
71
- return this.subscribeTopic(topicID, { error: opts.error }, {
72
- ...opts,
73
- xform,
74
- });
75
- }
76
- unsubscribeTopic(topicID, sub) {
77
- const t = this.topics.get(topicID);
78
- return t ? t.unsubscribe(sub) : false;
79
- }
80
- unsubscribe(sub) {
81
- if (!sub) {
82
- for (let t of this.topics.values()) {
83
- t.unsubscribe();
84
- }
85
- this.topics.clear();
86
- return super.unsubscribe();
87
- }
88
- // only the PubSub itself can be unsubscribed
89
- return unsupported();
90
- }
91
- done() {
92
- for (let t of this.topics.values()) {
93
- t.done();
94
- }
95
- super.done();
96
- }
97
- dispatch(x) {
98
- LOGGER.debug(this.id, "dispatch", x);
99
- this.cacheLast && (this.last = x);
100
- const t = this.topicfn(x);
101
- if (t !== undefined) {
102
- const sub = this.topics.get(t);
103
- if (sub) {
104
- try {
105
- sub.next && sub.next(x);
106
- }
107
- catch (e) {
108
- if (!sub.error || !sub.error(e)) {
109
- return this.unhandledError(e);
110
- }
111
- }
112
- }
78
+ super.done();
79
+ }
80
+ dispatch(x) {
81
+ LOGGER.debug(this.id, "dispatch", x);
82
+ this.cacheLast && (this.last = x);
83
+ const t = this.topicfn(x);
84
+ if (t !== void 0) {
85
+ const sub = this.topics.get(t);
86
+ if (sub) {
87
+ try {
88
+ sub.next && sub.next(x);
89
+ } catch (e) {
90
+ if (!sub.error || !sub.error(e)) {
91
+ return this.unhandledError(e);
92
+ }
113
93
  }
94
+ }
114
95
  }
96
+ }
115
97
  }
98
+ export {
99
+ PubSub,
100
+ pubsub
101
+ };
package/raf.js CHANGED
@@ -2,31 +2,19 @@ import { isNode } from "@thi.ng/checks/is-node";
2
2
  import { __optsWithID } from "./idgen.js";
3
3
  import { fromInterval } from "./interval.js";
4
4
  import { stream } from "./stream.js";
5
- /**
6
- * Yields {@link Stream} of a monotonically increasing counter (or timestamps),
7
- * triggered by a `requestAnimationFrame()` loop (only available in browser
8
- * environments).
9
- *
10
- * @remarks
11
- * In NodeJS, this function falls back to {@link fromInterval}, yielding a
12
- * similar (approx. 60Hz) stream (the {@link FromRAFOpts.timestamp} option will
13
- * be ignored).
14
- *
15
- * All subscribers to this stream will be processed during that same RAF loop
16
- * iteration.
17
- */
18
- export const fromRAF = (opts = {}) => isNode()
19
- ? fromInterval(16, opts)
20
- : stream((stream) => {
21
- let i = 0;
22
- let isActive = true;
23
- const loop = (time) => {
24
- isActive && stream.next(opts.timestamp ? time : i++);
25
- isActive && (id = requestAnimationFrame(loop));
26
- };
27
- let id = requestAnimationFrame(loop);
28
- return () => {
29
- isActive = false;
30
- cancelAnimationFrame(id);
31
- };
32
- }, __optsWithID("raf", opts));
5
+ const fromRAF = (opts = {}) => isNode() ? fromInterval(16, opts) : stream((stream2) => {
6
+ let i = 0;
7
+ let isActive = true;
8
+ const loop = (time) => {
9
+ isActive && stream2.next(opts.timestamp ? time : i++);
10
+ isActive && (id = requestAnimationFrame(loop));
11
+ };
12
+ let id = requestAnimationFrame(loop);
13
+ return () => {
14
+ isActive = false;
15
+ cancelAnimationFrame(id);
16
+ };
17
+ }, __optsWithID("raf", opts));
18
+ export {
19
+ fromRAF
20
+ };