@thi.ng/csp 2.1.115 → 3.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/mult.d.ts CHANGED
@@ -1,25 +1,17 @@
1
- import { DCons } from "@thi.ng/dcons/dcons";
2
- import type { Transducer } from "@thi.ng/transducers";
3
- import type { IWriteableChannel } from "./api.js";
1
+ import type { IClosable, IWriteable } from "./api.js";
4
2
  import { Channel } from "./channel.js";
5
- export declare class Mult<T> implements IWriteableChannel<T> {
6
- protected static nextID: number;
3
+ export declare const mult: <T>(arg?: string | Channel<T>) => Mult<T>;
4
+ export declare class Mult<T> implements IWriteable<T>, IClosable {
7
5
  protected src: Channel<any>;
8
- protected taps: DCons<Channel<any>>;
9
- protected tapID: number;
10
- constructor();
11
- constructor(id: string);
12
- constructor(src: Channel<T>);
13
- constructor(tx: Transducer<any, T>);
14
- constructor(id: string, tx: Transducer<any, T>);
15
- get id(): string;
16
- set id(id: string);
17
- channel(): Channel<any>;
18
- write(val: any): Promise<boolean>;
19
- close(flush?: boolean): Promise<void> | undefined;
20
- tap<R>(ch?: Channel<R> | Transducer<T, R>): Channel<R> | undefined;
21
- untap(ch: Channel<any>): boolean;
22
- untapAll(close?: boolean): boolean;
6
+ protected taps: Channel<any>[];
7
+ constructor(arg?: string | Channel<T>);
8
+ writable(): boolean;
9
+ write(val: T): Promise<boolean>;
10
+ close(): void;
11
+ closed(): boolean;
12
+ subscribe(ch?: Channel<T>): Channel<T>;
13
+ unsubscribe(ch: Channel<T>): boolean;
14
+ unsubscribeAll(close?: boolean): void;
23
15
  protected process(): Promise<void>;
24
16
  }
25
17
  //# sourceMappingURL=mult.d.ts.map
package/mult.js CHANGED
@@ -1,109 +1,71 @@
1
- import { DCons } from "@thi.ng/dcons/dcons";
2
- import { illegalArity } from "@thi.ng/errors/illegal-arity";
3
1
  import { Channel } from "./channel.js";
2
+ import { __nextID } from "./idgen.js";
3
+ const mult = (arg) => new Mult(arg);
4
4
  class Mult {
5
- static nextID = 0;
6
5
  src;
7
- taps;
8
- tapID = 0;
9
- constructor(...args) {
6
+ taps = [];
7
+ constructor(arg) {
10
8
  let id, src;
11
- switch (args.length) {
12
- case 2:
13
- id = args[0];
14
- src = args[1];
15
- break;
16
- case 1:
17
- if (typeof args[0] === "string") {
18
- id = args[0];
19
- } else {
20
- src = args[0];
21
- }
22
- break;
23
- case 0:
24
- id = "mult" + Mult.nextID++;
25
- break;
26
- default:
27
- illegalArity(args.length);
28
- }
29
- if (src instanceof Channel) {
30
- this.src = src;
9
+ if (typeof arg === "string") {
10
+ id = arg;
31
11
  } else {
32
- this.src = new Channel(id, src);
12
+ src = arg;
33
13
  }
34
- this.taps = new DCons();
14
+ this.src = src instanceof Channel ? src : new Channel({ id: id ?? `mult${__nextID()}` });
35
15
  this.process();
36
16
  }
37
- get id() {
38
- return this.src && this.src.id;
39
- }
40
- set id(id) {
41
- this.src && (this.src.id = id);
42
- }
43
- channel() {
44
- return this.src;
17
+ writable() {
18
+ return this.src.writable();
45
19
  }
46
20
  write(val) {
47
- if (this.src) {
48
- return this.src.write(val);
49
- }
50
- return Promise.resolve(false);
21
+ return this.src.write(val);
51
22
  }
52
- close(flush = false) {
53
- return this.src ? this.src.close(flush) : void 0;
23
+ close() {
24
+ return this.src.close();
54
25
  }
55
- tap(ch) {
56
- if (this.taps) {
57
- if (!(ch instanceof Channel)) {
58
- ch = new Channel(this.src.id + "-tap" + this.tapID++, ch);
59
- } else if (this.taps.find(ch)) {
60
- return ch;
61
- }
62
- this.taps.push(ch);
26
+ closed() {
27
+ return this.src.closed();
28
+ }
29
+ subscribe(ch) {
30
+ if (!ch) {
31
+ ch = new Channel({
32
+ id: `${this.src.id}-tap${__nextID()}`
33
+ });
34
+ } else if (this.taps.includes(ch)) {
63
35
  return ch;
64
36
  }
37
+ this.taps.push(ch);
38
+ return ch;
65
39
  }
66
- untap(ch) {
67
- if (this.taps) {
68
- const t = this.taps.find(ch);
69
- if (t) {
70
- this.taps.remove(t);
71
- return true;
72
- }
40
+ unsubscribe(ch) {
41
+ const idx = this.taps.indexOf(ch);
42
+ if (idx >= 0) {
43
+ this.taps.splice(idx, 1);
44
+ return true;
73
45
  }
74
46
  return false;
75
47
  }
76
- untapAll(close = true) {
77
- if (this.taps) {
78
- let tap = this.taps.head;
79
- while (tap) {
80
- close && tap.value.close();
81
- this.taps.remove(tap);
82
- tap = tap.next;
83
- }
84
- return true;
48
+ unsubscribeAll(close = true) {
49
+ if (close) {
50
+ for (let t of this.taps)
51
+ t.close();
85
52
  }
86
- return false;
53
+ this.taps.length = 0;
87
54
  }
88
55
  async process() {
89
56
  let x;
90
- while ((x = null, x = await this.src.read()) !== void 0) {
91
- let t = this.taps.head;
92
- while (t) {
93
- if (!await t.value.write(x)) {
94
- this.taps.remove(t);
57
+ while ((x = await this.src.read()) !== void 0) {
58
+ for (let t of this.taps) {
59
+ if (!await t.write(x)) {
60
+ this.unsubscribe(t);
95
61
  }
96
- t = t.next;
97
62
  }
63
+ x = null;
98
64
  }
99
- for (let t of this.taps) {
100
- await t.close();
101
- }
102
- delete this.src;
103
- delete this.taps;
104
- delete this.tapID;
65
+ this.unsubscribeAll();
105
66
  }
106
67
  }
107
68
  export {
108
- Mult
69
+ Mult,
70
+ mult
109
71
  };
package/ops.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import type { Fn2, Maybe } from "@thi.ng/api";
2
+ import type { IClosable, IWriteable } from "./api.js";
3
+ import { Channel } from "./channel.js";
4
+ export declare const broadcast: <T>(src: Channel<T>, dest: Channel<T>[], close?: boolean) => Promise<void>;
5
+ export declare const concat: <T>(dest: IWriteable<T> & IClosable, chans: Iterable<Channel<T>>, close?: boolean) => Promise<void>;
6
+ /**
7
+ * Consumes & collects all future values written to channel `chan` until
8
+ * closed or the max. number of values has been collected (whatever comes
9
+ * first). Returns a promise of the result array.
10
+ *
11
+ * @param chan
12
+ * @param res
13
+ * @param num
14
+ */
15
+ export declare const consume: <T>(chan: Channel<T>, res?: T[], num?: number) => Promise<T[]>;
16
+ export declare const consumeWith: <T>(chan: Channel<T>, fn: Fn2<T, Channel<T>, void>, num?: number) => Promise<void>;
17
+ export declare const drain: <T>(chan: Channel<T>) => Promise<Awaited<T>[]>;
18
+ export declare const fromAsyncIterable: <T>(src: AsyncIterable<T>, close?: boolean) => Channel<T>;
19
+ export declare const merge: <T>(src: Channel<T>[], dest?: Channel<T>, close?: boolean) => Channel<T>;
20
+ export declare const pipe: <T, DEST extends IWriteable<T> & IClosable>(src: Channel<T>, dest: DEST, close?: boolean) => DEST;
21
+ export declare const select: <T>(input: Channel<T>, ...xs: Channel<T>[]) => Promise<[Maybe<T>, Channel<T>]>;
22
+ export declare const timeout: (delay: number) => Channel<any>;
23
+ //# sourceMappingURL=ops.d.ts.map
package/ops.js ADDED
@@ -0,0 +1,117 @@
1
+ import { assert } from "@thi.ng/errors/assert";
2
+ import { Channel } from "./channel.js";
3
+ const broadcast = async (src, dest, close = true) => {
4
+ for await (let x of src) {
5
+ for (let chan of dest)
6
+ chan.write(x);
7
+ }
8
+ if (close) {
9
+ for (let chan of dest)
10
+ chan.close();
11
+ }
12
+ };
13
+ const concat = async (dest, chans, close = true) => {
14
+ return (async () => {
15
+ for (let c of chans) {
16
+ await consumeWith(c, (x) => dest.write(x));
17
+ }
18
+ close && dest.close();
19
+ })();
20
+ };
21
+ const consume = async (chan, res = [], num = Infinity) => {
22
+ for (let n = 0; !chan.closed() && n < num; n++) {
23
+ const x = await chan.read();
24
+ if (x == void 0)
25
+ break;
26
+ res.push(x);
27
+ }
28
+ return res;
29
+ };
30
+ const consumeWith = async (chan, fn, num = Infinity) => {
31
+ for (let n = 0; !chan.closed() && n < num; n++) {
32
+ const x = await chan.read();
33
+ if (x == void 0)
34
+ break;
35
+ fn(x, chan);
36
+ }
37
+ };
38
+ const drain = async (chan) => await (async () => {
39
+ const ops = [];
40
+ for (let i = 0, n = chan.writes.length + chan.queue.length; i < n; i++) {
41
+ ops.push(chan.read());
42
+ }
43
+ return Promise.all(ops);
44
+ })();
45
+ const fromAsyncIterable = (src, close = true) => {
46
+ const chan = new Channel();
47
+ (async () => {
48
+ for await (let x of src) {
49
+ await chan.write(x);
50
+ if (!chan.writable())
51
+ break;
52
+ }
53
+ close && chan.close();
54
+ })();
55
+ return chan;
56
+ };
57
+ const merge = (src, dest, close = true) => {
58
+ assert(src.length > 0, "no inputs given");
59
+ dest = dest || new Channel();
60
+ (async () => {
61
+ while (true) {
62
+ const [x, ch] = await select(...src);
63
+ if (x === void 0) {
64
+ src.splice(src.indexOf(ch), 1);
65
+ if (!src.length) {
66
+ close && dest.close();
67
+ break;
68
+ }
69
+ } else {
70
+ await dest.write(x);
71
+ }
72
+ }
73
+ })();
74
+ return dest;
75
+ };
76
+ const pipe = (src, dest, close = true) => {
77
+ (async () => {
78
+ for await (let x of src) {
79
+ await dest.write(x);
80
+ if (!dest.writable())
81
+ break;
82
+ }
83
+ close && dest.close();
84
+ })();
85
+ return dest;
86
+ };
87
+ const select = async (input, ...xs) => {
88
+ const inputs = [input, ...xs];
89
+ const sel = await Promise.race(inputs.map((x) => x.race()));
90
+ for (let chan of inputs) {
91
+ if (chan !== sel)
92
+ chan.races.shift();
93
+ }
94
+ if (sel.writes.readable()) {
95
+ const [msg, write] = sel.writes.read();
96
+ write(true);
97
+ return [msg, sel];
98
+ }
99
+ return [void 0, sel];
100
+ };
101
+ const timeout = (delay) => {
102
+ const ch = new Channel();
103
+ setTimeout(() => ch.close(), delay);
104
+ return ch;
105
+ };
106
+ export {
107
+ broadcast,
108
+ concat,
109
+ consume,
110
+ consumeWith,
111
+ drain,
112
+ fromAsyncIterable,
113
+ merge,
114
+ pipe,
115
+ select,
116
+ timeout
117
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@thi.ng/csp",
3
- "version": "2.1.115",
4
- "description": "ES6 promise based CSP primitives & operations",
3
+ "version": "3.0.0",
4
+ "description": "Primitives & operators for Communicating Sequential Processes based on async/await and async iterables",
5
5
  "type": "module",
6
6
  "module": "./index.js",
7
7
  "typings": "./index.d.ts",
@@ -27,7 +27,7 @@
27
27
  "build": "yarn build:esbuild && yarn build:decl",
28
28
  "build:decl": "tsc --declaration --emitDeclarationOnly",
29
29
  "build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
30
- "clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
30
+ "clean": "bun ../../tools/src/clean-package.ts",
31
31
  "doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
32
32
  "doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
33
33
  "doc:readme": "bun ../../tools/src/module-stats.ts && bun ../../tools/src/readme.ts",
@@ -40,31 +40,31 @@
40
40
  "tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
41
41
  },
42
42
  "dependencies": {
43
- "@thi.ng/api": "^8.11.0",
44
- "@thi.ng/arrays": "^2.9.4",
45
- "@thi.ng/checks": "^3.6.2",
46
- "@thi.ng/dcons": "^3.2.110",
47
- "@thi.ng/errors": "^2.5.5",
48
- "@thi.ng/transducers": "^9.0.2"
43
+ "@thi.ng/api": "^8.11.1",
44
+ "@thi.ng/buffers": "^0.1.2",
45
+ "@thi.ng/checks": "^3.6.3",
46
+ "@thi.ng/errors": "^2.5.6"
49
47
  },
50
48
  "devDependencies": {
51
49
  "@microsoft/api-extractor": "^7.43.0",
52
50
  "esbuild": "^0.20.2",
53
- "rimraf": "^5.0.5",
54
51
  "typedoc": "^0.25.12",
55
52
  "typescript": "^5.4.3"
56
53
  },
57
54
  "keywords": [
58
55
  "async",
56
+ "blocking",
59
57
  "channel",
60
58
  "communication",
61
59
  "csp",
62
60
  "datastructure",
61
+ "iterator",
62
+ "merge",
63
63
  "multiplex",
64
+ "multitasking",
64
65
  "pipeline",
65
66
  "promise",
66
67
  "pubsub",
67
- "transducer",
68
68
  "typescript"
69
69
  ],
70
70
  "publishConfig": {
@@ -84,25 +84,27 @@
84
84
  "./api": {
85
85
  "default": "./api.js"
86
86
  },
87
- "./buffer": {
88
- "default": "./buffer.js"
89
- },
90
87
  "./channel": {
91
88
  "default": "./channel.js"
92
89
  },
93
90
  "./mult": {
94
91
  "default": "./mult.js"
95
92
  },
93
+ "./ops": {
94
+ "default": "./ops.js"
95
+ },
96
96
  "./pubsub": {
97
97
  "default": "./pubsub.js"
98
98
  }
99
99
  },
100
100
  "thi.ng": {
101
101
  "related": [
102
- "rstream"
102
+ "fibers",
103
+ "rstream",
104
+ "transducers-async"
103
105
  ],
104
- "status": "deprecated",
106
+ "status": "beta",
105
107
  "year": 2016
106
108
  },
107
- "gitHead": "8339d05ecc857e529c7325a9839c0063b89e728d\n"
109
+ "gitHead": "aed3421c21044c005fbcb7cc37965ccf85a870d2\n"
108
110
  }
package/pubsub.d.ts CHANGED
@@ -1,35 +1,29 @@
1
1
  import type { IObjectOf } from "@thi.ng/api";
2
- import type { Transducer } from "@thi.ng/transducers";
3
- import type { IWriteableChannel, TopicFn } from "./api.js";
2
+ import type { IClosable, IWriteable, TopicFn } from "./api.js";
4
3
  import { Channel } from "./channel.js";
5
4
  import { Mult } from "./mult.js";
6
- export declare class PubSub<T> implements IWriteableChannel<T> {
7
- protected static NEXT_ID: number;
5
+ export declare function pubsub<T>(fn: TopicFn<T>): PubSub<T>;
6
+ export declare function pubsub<T>(src: Channel<T>, fn: TopicFn<T>): PubSub<T>;
7
+ export declare class PubSub<T> implements IWriteable<T>, IClosable {
8
8
  protected src: Channel<T>;
9
9
  protected fn: TopicFn<T>;
10
10
  protected topics: IObjectOf<Mult<T>>;
11
11
  constructor(fn: TopicFn<T>);
12
12
  constructor(src: Channel<T>, fn: TopicFn<T>);
13
- get id(): string;
14
- set id(id: string);
15
- channel(): Channel<T>;
16
- write(val: any): Promise<boolean>;
17
- close(flush?: boolean): Promise<void> | undefined;
13
+ writable(): boolean;
14
+ write(val: T): Promise<boolean>;
15
+ close(): void;
16
+ closed(): boolean;
18
17
  /**
19
- * Creates a new topic subscription channel and returns it.
20
- * Each topic is managed by its own {@link Mult} and can have arbitrary
21
- * number of subscribers. If the optional transducer is given, it will
22
- * only be applied to the new subscription channel.
23
- *
24
- * The special "*" topic can be used to subscribe to all messages and
25
- * acts as multiplexed pass-through of the source channel.
18
+ * Creates a new topic subscription channel and returns it. Each topic is
19
+ * managed by its own {@link Mult} and can have arbitrary number of
20
+ * subscribers.
26
21
  *
27
22
  * @param id - topic id
28
- * @param tx - transducer for new subscription
29
23
  */
30
- sub(id: string, tx?: Transducer<T, any>): Channel<any> | undefined;
31
- unsub(id: string, ch: Channel<T>): boolean;
32
- unsubAll(id: string, close?: boolean): boolean;
24
+ subscribeTopic(id: string): Channel<T>;
25
+ unsubscribeTopic(id: string, ch: Channel<T>): boolean;
26
+ unsubscribeAll(id: string, close?: boolean): void;
33
27
  protected process(): Promise<void>;
34
28
  }
35
29
  //# sourceMappingURL=pubsub.d.ts.map
package/pubsub.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { illegalArity } from "@thi.ng/errors/illegal-arity";
2
2
  import { Channel } from "./channel.js";
3
3
  import { Mult } from "./mult.js";
4
+ function pubsub(...args) {
5
+ return new PubSub(...args);
6
+ }
4
7
  class PubSub {
5
- static NEXT_ID = 0;
6
8
  src;
7
9
  fn;
8
10
  topics;
@@ -13,7 +15,7 @@ class PubSub {
13
15
  this.fn = args[1];
14
16
  break;
15
17
  case 1:
16
- this.src = new Channel("pubsub" + PubSub.NEXT_ID++);
18
+ this.src = new Channel();
17
19
  this.fn = args[0];
18
20
  break;
19
21
  default:
@@ -22,74 +24,55 @@ class PubSub {
22
24
  this.topics = {};
23
25
  this.process();
24
26
  }
25
- get id() {
26
- return this.src && this.src.id;
27
- }
28
- set id(id) {
29
- this.src && (this.src.id = id);
30
- }
31
- channel() {
32
- return this.src;
27
+ writable() {
28
+ return this.src.writable();
33
29
  }
34
30
  write(val) {
35
- if (this.src) {
36
- return this.src.write(val);
37
- }
38
- return Promise.resolve(false);
31
+ return this.src.write(val);
32
+ }
33
+ close() {
34
+ return this.src.close();
39
35
  }
40
- close(flush = false) {
41
- return this.src ? this.src.close(flush) : void 0;
36
+ closed() {
37
+ return this.src.closed();
42
38
  }
43
39
  /**
44
- * Creates a new topic subscription channel and returns it.
45
- * Each topic is managed by its own {@link Mult} and can have arbitrary
46
- * number of subscribers. If the optional transducer is given, it will
47
- * only be applied to the new subscription channel.
48
- *
49
- * The special "*" topic can be used to subscribe to all messages and
50
- * acts as multiplexed pass-through of the source channel.
40
+ * Creates a new topic subscription channel and returns it. Each topic is
41
+ * managed by its own {@link Mult} and can have arbitrary number of
42
+ * subscribers.
51
43
  *
52
44
  * @param id - topic id
53
- * @param tx - transducer for new subscription
54
45
  */
55
- sub(id, tx) {
46
+ subscribeTopic(id) {
56
47
  let topic = this.topics[id];
57
48
  if (!topic) {
58
- this.topics[id] = topic = new Mult(this.src.id + "-" + id);
49
+ this.topics[id] = topic = new Mult(`${this.src.id}-${id}`);
59
50
  }
60
- return topic.tap(tx);
51
+ return topic.subscribe();
61
52
  }
62
- unsub(id, ch) {
63
- let topic = this.topics[id];
64
- if (topic) {
65
- return topic.untap(ch);
66
- }
67
- return false;
53
+ unsubscribeTopic(id, ch) {
54
+ const topic = this.topics[id];
55
+ return topic?.unsubscribe(ch) ?? false;
68
56
  }
69
- unsubAll(id, close = true) {
70
- let topic = this.topics[id];
71
- if (topic) {
72
- return topic.untapAll(close);
73
- }
74
- return false;
57
+ unsubscribeAll(id, close = true) {
58
+ const topic = this.topics[id];
59
+ topic?.unsubscribeAll(close);
75
60
  }
76
61
  async process() {
77
62
  let x;
78
- while ((x = null, x = await this.src.read()) !== void 0) {
79
- const id = await this.fn(x);
63
+ while ((x = await this.src.read()) !== void 0) {
64
+ const id = this.fn(x);
80
65
  let topic = this.topics[id];
81
66
  topic && await topic.write(x);
82
- topic = this.topics["*"];
83
- topic && await topic.write(x);
67
+ x = null;
84
68
  }
85
- for (let id of Object.keys(this.topics)) {
86
- this.topics[id].close();
69
+ for (let t of Object.values(this.topics)) {
70
+ t.close();
87
71
  }
88
- delete this.src;
89
- delete this.topics;
90
- delete this.fn;
72
+ this.topics = {};
91
73
  }
92
74
  }
93
75
  export {
94
- PubSub
76
+ PubSub,
77
+ pubsub
95
78
  };
package/buffer.d.ts DELETED
@@ -1,24 +0,0 @@
1
- import { DCons } from "@thi.ng/dcons/dcons";
2
- import type { ChannelItem, IBuffer } from "./api.js";
3
- export declare class FixedBuffer<T> implements IBuffer<T> {
4
- buf: DCons<ChannelItem<T>>;
5
- limit: number;
6
- constructor(limit?: number);
7
- get length(): number;
8
- isEmpty(): boolean;
9
- isFull(): boolean;
10
- release(): boolean;
11
- push(x: ChannelItem<T>): boolean;
12
- drop(): ChannelItem<T> | undefined;
13
- }
14
- export declare class DroppingBuffer<T> extends FixedBuffer<T> {
15
- constructor(limit?: number);
16
- isFull(): boolean;
17
- push(x: ChannelItem<T>): boolean;
18
- }
19
- export declare class SlidingBuffer<T> extends FixedBuffer<T> {
20
- constructor(limit?: number);
21
- isFull(): boolean;
22
- push(x: ChannelItem<T>): boolean;
23
- }
24
- //# sourceMappingURL=buffer.d.ts.map