patron-oop 1.1.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/CHANGELOG.md +11 -0
- package/LICENSE.md +7 -0
- package/README.md +9 -0
- package/commitizen.cjs +50 -0
- package/dist/patron.d.ts +173 -0
- package/dist/patron.js +363 -0
- package/dist/patron.js.map +1 -0
- package/dist/patron.mjs +346 -0
- package/dist/patron.mjs.map +1 -0
- package/eslint.config.mjs +36 -0
- package/package.json +48 -0
- package/rollup.config.js +35 -0
- package/src/Cache.test.ts +20 -0
- package/src/Cache.ts +31 -0
- package/src/CacheType.ts +4 -0
- package/src/Chain.test.ts +72 -0
- package/src/Chain.ts +79 -0
- package/src/ChainType.ts +7 -0
- package/src/Factory.test.ts +16 -0
- package/src/Factory.ts +21 -0
- package/src/FactoryDynamic.ts +11 -0
- package/src/FactoryType.ts +3 -0
- package/src/FactoryWithFactories.ts +25 -0
- package/src/Guest.test.ts +13 -0
- package/src/Guest.ts +11 -0
- package/src/GuestAware.ts +11 -0
- package/src/GuestAwareType.ts +5 -0
- package/src/GuestCast.ts +20 -0
- package/src/GuestExecutorType.ts +3 -0
- package/src/GuestInTheMiddle.test.ts +71 -0
- package/src/GuestInTheMiddle.ts +20 -0
- package/src/GuestPool.test.ts +41 -0
- package/src/GuestPool.ts +46 -0
- package/src/GuestSync.test.ts +11 -0
- package/src/GuestSync.ts +14 -0
- package/src/GuestType.ts +10 -0
- package/src/GuestValueType.ts +5 -0
- package/src/Patron.test.ts +20 -0
- package/src/Patron.ts +17 -0
- package/src/PatronOnce.test.ts +18 -0
- package/src/PatronOnce.ts +30 -0
- package/src/PatronPool.test.ts +26 -0
- package/src/PatronPool.ts +76 -0
- package/src/PoolType.ts +7 -0
- package/src/Source.test.ts +13 -0
- package/src/Source.ts +20 -0
- package/src/SourceType.ts +4 -0
- package/src/index.ts +16 -0
- package/tsconfig.json +26 -0
package/src/Chain.ts
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
import { Cache } from "./Cache";
|
2
|
+
import { Guest } from "./Guest";
|
3
|
+
import { GuestPool } from "./GuestPool";
|
4
|
+
import { ChainType } from "./ChainType";
|
5
|
+
import { GuestInTheMiddle } from "./GuestInTheMiddle";
|
6
|
+
import { GuestType } from "./GuestType";
|
7
|
+
|
8
|
+
export class Chain<T> implements ChainType<T> {
|
9
|
+
private theChain: Cache<Record<string, unknown>>;
|
10
|
+
|
11
|
+
private keysKnown = new Set();
|
12
|
+
|
13
|
+
private keysFilled = new Set();
|
14
|
+
|
15
|
+
private filledChainPool = new GuestPool(this);
|
16
|
+
|
17
|
+
public constructor() {
|
18
|
+
this.theChain = new Cache<Record<string, unknown>>(this, {});
|
19
|
+
}
|
20
|
+
|
21
|
+
public resultArray(guest: GuestType<T>) {
|
22
|
+
this.filledChainPool.add(
|
23
|
+
new GuestInTheMiddle(guest, (value: Record<string, unknown>) =>
|
24
|
+
Object.values(value),
|
25
|
+
),
|
26
|
+
);
|
27
|
+
if (this.isChainFilled()) {
|
28
|
+
this.theChain.receiving(
|
29
|
+
new Guest((chain) => {
|
30
|
+
this.filledChainPool.receive(Object.values(chain));
|
31
|
+
}),
|
32
|
+
);
|
33
|
+
}
|
34
|
+
|
35
|
+
return this;
|
36
|
+
}
|
37
|
+
|
38
|
+
public result(guest: GuestType<T>) {
|
39
|
+
if (this.isChainFilled()) {
|
40
|
+
this.filledChainPool.add(guest);
|
41
|
+
this.theChain.receiving(
|
42
|
+
new Guest((chain) => {
|
43
|
+
this.filledChainPool.receive(chain);
|
44
|
+
}),
|
45
|
+
);
|
46
|
+
} else {
|
47
|
+
this.filledChainPool.add(guest);
|
48
|
+
}
|
49
|
+
return this;
|
50
|
+
}
|
51
|
+
|
52
|
+
public receiveKey<R>(key: string): GuestType<R> {
|
53
|
+
this.keysKnown.add(key);
|
54
|
+
return new Guest((value) => {
|
55
|
+
// Обернул в очередь чтобы можно было синхронно наполнить очередь известных ключей
|
56
|
+
queueMicrotask(() => {
|
57
|
+
this.theChain.receiving(
|
58
|
+
new Guest((chain) => {
|
59
|
+
this.keysFilled.add(key);
|
60
|
+
const lastChain = {
|
61
|
+
...chain,
|
62
|
+
[key]: value,
|
63
|
+
};
|
64
|
+
this.theChain.receive(lastChain);
|
65
|
+
if (this.isChainFilled()) {
|
66
|
+
this.filledChainPool.receive(lastChain);
|
67
|
+
}
|
68
|
+
}),
|
69
|
+
);
|
70
|
+
});
|
71
|
+
});
|
72
|
+
}
|
73
|
+
|
74
|
+
private isChainFilled() {
|
75
|
+
return (
|
76
|
+
this.keysFilled.size > 0 && this.keysFilled.size === this.keysKnown.size
|
77
|
+
);
|
78
|
+
}
|
79
|
+
}
|
package/src/ChainType.ts
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
import { expect, test } from "vitest";
|
2
|
+
import { Factory } from "./Factory";
|
3
|
+
import { Guest } from "./Guest";
|
4
|
+
import { Source } from "./Source";
|
5
|
+
|
6
|
+
test("factory", () => {
|
7
|
+
const sourceFactory = new Factory(Source);
|
8
|
+
|
9
|
+
const source = sourceFactory.create(42);
|
10
|
+
|
11
|
+
source.receiving(
|
12
|
+
new Guest((value) => {
|
13
|
+
expect(value).toBe(42);
|
14
|
+
}),
|
15
|
+
);
|
16
|
+
});
|
package/src/Factory.ts
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
import { FactoryType } from "./FactoryType";
|
2
|
+
|
3
|
+
interface Constructable<T> {
|
4
|
+
new (...args: unknown[]): T;
|
5
|
+
}
|
6
|
+
|
7
|
+
interface Prototyped<T> {
|
8
|
+
prototype: T;
|
9
|
+
}
|
10
|
+
|
11
|
+
export class Factory<T> implements FactoryType<T> {
|
12
|
+
public constructor(private constructorFn: Prototyped<T>) {}
|
13
|
+
|
14
|
+
public create<R extends unknown[], CT = null>(
|
15
|
+
...args: R
|
16
|
+
): CT extends null ? T : CT {
|
17
|
+
return new (this.constructorFn as Constructable<T>)(
|
18
|
+
...args,
|
19
|
+
) as CT extends null ? T : CT;
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import { FactoryType } from "./FactoryType";
|
2
|
+
|
3
|
+
export class FactoryDynamic<T> implements FactoryType<T> {
|
4
|
+
public constructor(private creationFn: (...args: unknown[]) => T) {}
|
5
|
+
|
6
|
+
public create<R extends unknown[], CT = null>(
|
7
|
+
...args: R
|
8
|
+
): CT extends null ? T : CT {
|
9
|
+
return this.creationFn(...args) as CT extends null ? T : CT;
|
10
|
+
}
|
11
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { FactoryType } from "./FactoryType";
|
2
|
+
|
3
|
+
interface Constructable<T> {
|
4
|
+
new (...args: unknown[]): T;
|
5
|
+
}
|
6
|
+
|
7
|
+
interface Prototyped<T> {
|
8
|
+
prototype: T;
|
9
|
+
}
|
10
|
+
|
11
|
+
export class FactoryWithFactories<T> implements FactoryType<T> {
|
12
|
+
public constructor(
|
13
|
+
private constructorFn: Prototyped<T>,
|
14
|
+
private factories: Record<string, unknown> = {},
|
15
|
+
) {}
|
16
|
+
|
17
|
+
public create<R extends unknown[], CT = null>(
|
18
|
+
...args: R
|
19
|
+
): CT extends null ? T : CT {
|
20
|
+
return new (this.constructorFn as Constructable<T>)(
|
21
|
+
...args,
|
22
|
+
this.factories,
|
23
|
+
) as CT extends null ? T : CT;
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,13 @@
|
|
1
|
+
import { expect, test } from "vitest";
|
2
|
+
import { Guest } from "./Guest";
|
3
|
+
import { Source } from "./Source";
|
4
|
+
|
5
|
+
test("guest dynamic", () => {
|
6
|
+
const one = new Source(1);
|
7
|
+
|
8
|
+
one.receiving(
|
9
|
+
new Guest((value) => {
|
10
|
+
expect(value).toBe(1);
|
11
|
+
}),
|
12
|
+
);
|
13
|
+
});
|
package/src/Guest.ts
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
import { GuestExecutorType } from "./GuestExecutorType";
|
2
|
+
import { GuestType, ReceiveOptions } from "./GuestType";
|
3
|
+
|
4
|
+
export class Guest<T> implements GuestType<T> {
|
5
|
+
public constructor(private receiver: GuestExecutorType<T>) {}
|
6
|
+
|
7
|
+
public receive(value: T, options?: ReceiveOptions) {
|
8
|
+
this.receiver(value, options);
|
9
|
+
return this;
|
10
|
+
}
|
11
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import { GuestType } from "./GuestType";
|
2
|
+
import { GuestAwareType } from "./GuestAwareType";
|
3
|
+
|
4
|
+
export class GuestAware<T = unknown> implements GuestAwareType<T> {
|
5
|
+
public constructor(private guestReceiver: (guest: GuestType<T>) => void) {}
|
6
|
+
|
7
|
+
public receiving(guest: GuestType<T>): GuestType<T> {
|
8
|
+
this.guestReceiver(guest);
|
9
|
+
return guest;
|
10
|
+
}
|
11
|
+
}
|
package/src/GuestCast.ts
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
import { GuestType, ReceiveOptions } from "./GuestType";
|
2
|
+
|
3
|
+
export class GuestCast<T> implements GuestType<T> {
|
4
|
+
public constructor(
|
5
|
+
private sourceGuest: GuestType<unknown>,
|
6
|
+
private targetGuest: GuestType<T>,
|
7
|
+
) {}
|
8
|
+
|
9
|
+
introduction() {
|
10
|
+
if (!this.sourceGuest.introduction) {
|
11
|
+
return "guest";
|
12
|
+
}
|
13
|
+
return this.sourceGuest.introduction();
|
14
|
+
}
|
15
|
+
|
16
|
+
receive(value: T, options?: ReceiveOptions): this {
|
17
|
+
this.targetGuest.receive(value, options);
|
18
|
+
return this;
|
19
|
+
}
|
20
|
+
}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
import { expect, test } from "vitest";
|
2
|
+
import { Guest } from "./Guest";
|
3
|
+
import { GuestInTheMiddle } from "./GuestInTheMiddle";
|
4
|
+
import { Patron } from "./Patron";
|
5
|
+
import { Chain } from "./Chain";
|
6
|
+
import { Source } from "./Source";
|
7
|
+
|
8
|
+
test("test guest in the middle", () => {
|
9
|
+
const one = new Source(1);
|
10
|
+
|
11
|
+
let accumValue = 0;
|
12
|
+
const guest = new Guest((value: number) => {
|
13
|
+
accumValue += value;
|
14
|
+
});
|
15
|
+
one.receiving(
|
16
|
+
new GuestInTheMiddle(guest, (value) => {
|
17
|
+
guest.receive(value + 3);
|
18
|
+
}),
|
19
|
+
);
|
20
|
+
|
21
|
+
expect(accumValue).toBe(4);
|
22
|
+
});
|
23
|
+
|
24
|
+
test("test patron in the middle", () => {
|
25
|
+
const one = new Source(1);
|
26
|
+
|
27
|
+
let accumValue = 0;
|
28
|
+
const guest = new Patron(
|
29
|
+
new Guest((value: number) => {
|
30
|
+
accumValue += value;
|
31
|
+
}),
|
32
|
+
);
|
33
|
+
one.receiving(
|
34
|
+
new GuestInTheMiddle(guest, (value) => {
|
35
|
+
guest.receive(value + 3);
|
36
|
+
}),
|
37
|
+
);
|
38
|
+
one.receive(3);
|
39
|
+
|
40
|
+
setTimeout(() => {
|
41
|
+
one.receive(3);
|
42
|
+
});
|
43
|
+
|
44
|
+
setTimeout(() => {
|
45
|
+
expect(accumValue).toBe(16);
|
46
|
+
});
|
47
|
+
});
|
48
|
+
|
49
|
+
test("test chain in the middle", () => {
|
50
|
+
const one = new Source(1);
|
51
|
+
const two = new Source(2);
|
52
|
+
const chain = new Chain<{ one: number; two: number }>();
|
53
|
+
|
54
|
+
one.receiving(new Patron(chain.receiveKey("one")));
|
55
|
+
two.receiving(new Patron(chain.receiveKey("two")));
|
56
|
+
|
57
|
+
one.receive(3);
|
58
|
+
one.receive(4);
|
59
|
+
|
60
|
+
const guest = new Patron(
|
61
|
+
new Guest((value: { one: number; two: number; three: number }) => {
|
62
|
+
expect(Object.values(value).length).toBe(3);
|
63
|
+
}),
|
64
|
+
);
|
65
|
+
|
66
|
+
chain.result(
|
67
|
+
new GuestInTheMiddle(guest, (value) => {
|
68
|
+
guest.receive({ ...value, three: 99 });
|
69
|
+
}),
|
70
|
+
);
|
71
|
+
});
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { GuestType, ReceiveOptions } from "./GuestType";
|
2
|
+
|
3
|
+
export class GuestInTheMiddle<T> implements GuestType<T> {
|
4
|
+
public constructor(
|
5
|
+
private baseGuest: GuestType<unknown>,
|
6
|
+
private middleFn: (value: T, options?: ReceiveOptions) => void,
|
7
|
+
) {}
|
8
|
+
|
9
|
+
introduction() {
|
10
|
+
if (!this.baseGuest.introduction) {
|
11
|
+
return "guest";
|
12
|
+
}
|
13
|
+
return this.baseGuest.introduction();
|
14
|
+
}
|
15
|
+
|
16
|
+
receive(value: T, options?: ReceiveOptions): this {
|
17
|
+
this.middleFn(value, options);
|
18
|
+
return this;
|
19
|
+
}
|
20
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import { expect, test } from "vitest";
|
2
|
+
import { Guest } from "./Guest";
|
3
|
+
import { Patron } from "./Patron";
|
4
|
+
import { GuestPool } from "./GuestPool";
|
5
|
+
|
6
|
+
test("patron pool with guests", () => {
|
7
|
+
const pool = new GuestPool(null);
|
8
|
+
let receivedCount = 0;
|
9
|
+
|
10
|
+
// 2 + 2
|
11
|
+
pool.add(
|
12
|
+
new Patron(
|
13
|
+
new Guest<number>((value) => {
|
14
|
+
receivedCount += value;
|
15
|
+
}),
|
16
|
+
),
|
17
|
+
);
|
18
|
+
// 2 + 2
|
19
|
+
pool.add(
|
20
|
+
new Patron(
|
21
|
+
new Guest<number>((value) => {
|
22
|
+
receivedCount += value;
|
23
|
+
}),
|
24
|
+
),
|
25
|
+
);
|
26
|
+
// 2
|
27
|
+
pool.add(
|
28
|
+
new Guest<number>((value) => {
|
29
|
+
receivedCount += value;
|
30
|
+
}),
|
31
|
+
);
|
32
|
+
pool.receive(2);
|
33
|
+
|
34
|
+
setTimeout(() => {
|
35
|
+
pool.receive(2);
|
36
|
+
});
|
37
|
+
|
38
|
+
setTimeout(() => {
|
39
|
+
expect(receivedCount).toBe(10);
|
40
|
+
}, 10);
|
41
|
+
});
|
package/src/GuestPool.ts
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
import { PatronPool } from "./PatronPool";
|
2
|
+
import { PoolType } from "./PoolType";
|
3
|
+
import { GuestType, ReceiveOptions } from "./GuestType";
|
4
|
+
|
5
|
+
export class GuestPool<T> implements GuestType<T>, PoolType<T> {
|
6
|
+
private guests = new Set<GuestType<T>>();
|
7
|
+
|
8
|
+
private patronPool: PatronPool<T>;
|
9
|
+
|
10
|
+
public constructor(initiator: unknown) {
|
11
|
+
this.patronPool = new PatronPool(initiator);
|
12
|
+
}
|
13
|
+
|
14
|
+
public receive(value: T, options?: ReceiveOptions): this {
|
15
|
+
this.deliverToGuests(value, options);
|
16
|
+
this.patronPool.receive(value, options);
|
17
|
+
return this;
|
18
|
+
}
|
19
|
+
|
20
|
+
public add(guest: GuestType<T>): this {
|
21
|
+
if (!guest.introduction || guest.introduction() === "guest") {
|
22
|
+
this.guests.add(guest);
|
23
|
+
}
|
24
|
+
this.patronPool.add(guest);
|
25
|
+
return this;
|
26
|
+
}
|
27
|
+
|
28
|
+
public remove(patron: GuestType<T>): this {
|
29
|
+
this.guests.delete(patron);
|
30
|
+
this.patronPool.remove(patron);
|
31
|
+
return this;
|
32
|
+
}
|
33
|
+
|
34
|
+
public distribute(receiving: T, possiblePatron: GuestType<T>): this {
|
35
|
+
this.add(possiblePatron);
|
36
|
+
this.receive(receiving);
|
37
|
+
return this;
|
38
|
+
}
|
39
|
+
|
40
|
+
private deliverToGuests(value: T, options?: ReceiveOptions) {
|
41
|
+
this.guests.forEach((target) => {
|
42
|
+
target.receive(value, options);
|
43
|
+
});
|
44
|
+
this.guests.clear();
|
45
|
+
}
|
46
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import { expect, test } from "vitest";
|
2
|
+
import { Source } from "./Source";
|
3
|
+
import { GuestSync } from "./GuestSync";
|
4
|
+
|
5
|
+
test("guest sync", () => {
|
6
|
+
const source = new Source(123);
|
7
|
+
const syncGuest = new GuestSync(222);
|
8
|
+
expect(syncGuest.value()).toBe(222);
|
9
|
+
source.receiving(syncGuest);
|
10
|
+
expect(syncGuest.value()).toBe(123);
|
11
|
+
});
|
package/src/GuestSync.ts
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
import { GuestValueType } from "./GuestValueType";
|
2
|
+
|
3
|
+
export class GuestSync<T> implements GuestValueType<T> {
|
4
|
+
public constructor(private theValue: T) {}
|
5
|
+
|
6
|
+
public receive(value: T): this {
|
7
|
+
this.theValue = value;
|
8
|
+
return this;
|
9
|
+
}
|
10
|
+
|
11
|
+
public value() {
|
12
|
+
return this.theValue;
|
13
|
+
}
|
14
|
+
}
|
package/src/GuestType.ts
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
import { expect, test } from "vitest";
|
2
|
+
import { Patron } from "./Patron";
|
3
|
+
import { Guest } from "./Guest";
|
4
|
+
import { Source } from "./Source";
|
5
|
+
|
6
|
+
test("patron always guest", () => {
|
7
|
+
const one = new Source(1);
|
8
|
+
let patronCalledTimes = 0;
|
9
|
+
const patron = new Patron(
|
10
|
+
new Guest(() => {
|
11
|
+
patronCalledTimes += 1;
|
12
|
+
}),
|
13
|
+
);
|
14
|
+
one.receiving(patron);
|
15
|
+
one.receive(2);
|
16
|
+
|
17
|
+
queueMicrotask(() => {
|
18
|
+
expect(patronCalledTimes).toBe(2);
|
19
|
+
});
|
20
|
+
});
|
package/src/Patron.ts
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
import { GuestType, ReceiveOptions } from "./GuestType";
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Патрон - это постоянный посетитель
|
5
|
+
*/
|
6
|
+
export class Patron<T> implements GuestType<T> {
|
7
|
+
public constructor(private willBePatron: GuestType<T>) {}
|
8
|
+
|
9
|
+
public introduction() {
|
10
|
+
return "patron" as const;
|
11
|
+
}
|
12
|
+
|
13
|
+
public receive(value: T, options?: ReceiveOptions): this {
|
14
|
+
this.willBePatron.receive(value, options);
|
15
|
+
return this;
|
16
|
+
}
|
17
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { expect, test } from "vitest";
|
2
|
+
import { Source } from "./Source";
|
3
|
+
import { PatronOnce } from "./PatronOnce";
|
4
|
+
import { Guest } from "./Guest";
|
5
|
+
|
6
|
+
test("patron once", () => {
|
7
|
+
const source = new Source(42);
|
8
|
+
let calls = 0;
|
9
|
+
const patron = new PatronOnce(
|
10
|
+
new Guest(() => {
|
11
|
+
calls += 1;
|
12
|
+
}),
|
13
|
+
);
|
14
|
+
source.receiving(patron);
|
15
|
+
source.receive(22);
|
16
|
+
|
17
|
+
expect(calls).toBe(1);
|
18
|
+
});
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { GuestType, ReceiveOptions } from "./GuestType";
|
2
|
+
import { PoolType } from "./PoolType";
|
3
|
+
|
4
|
+
type PoolAware = {
|
5
|
+
pool?: PoolType;
|
6
|
+
};
|
7
|
+
|
8
|
+
export class PatronOnce<T> implements GuestType<T> {
|
9
|
+
private received = false;
|
10
|
+
|
11
|
+
public constructor(private baseGuest: GuestType<T>) {}
|
12
|
+
|
13
|
+
public introduction() {
|
14
|
+
return "patron" as const;
|
15
|
+
}
|
16
|
+
|
17
|
+
public receive(value: T, options?: ReceiveOptions): this {
|
18
|
+
if (!this.received) {
|
19
|
+
this.baseGuest.receive(value, options);
|
20
|
+
}
|
21
|
+
|
22
|
+
const data = options?.data as PoolAware;
|
23
|
+
// Если есть пул, то удаляем себя из пула
|
24
|
+
if (data?.pool) {
|
25
|
+
data.pool.remove(this);
|
26
|
+
}
|
27
|
+
|
28
|
+
return this;
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { expect, test } from "vitest";
|
2
|
+
import { Guest } from "./Guest";
|
3
|
+
import { PatronPool } from "./PatronPool";
|
4
|
+
import { Patron } from "./Patron";
|
5
|
+
|
6
|
+
test("patron pool", () => {
|
7
|
+
const pool = new PatronPool(null);
|
8
|
+
let receivedCount = 0;
|
9
|
+
|
10
|
+
pool.add(
|
11
|
+
new Patron(
|
12
|
+
new Guest<number>((value) => {
|
13
|
+
receivedCount += value;
|
14
|
+
}),
|
15
|
+
),
|
16
|
+
);
|
17
|
+
pool.add(
|
18
|
+
new Patron(
|
19
|
+
new Guest<number>((value) => {
|
20
|
+
receivedCount += value;
|
21
|
+
expect(receivedCount).toBe(4);
|
22
|
+
}),
|
23
|
+
),
|
24
|
+
);
|
25
|
+
pool.receive(2);
|
26
|
+
});
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import { PoolType } from "./PoolType";
|
2
|
+
import { GuestType, ReceiveOptions } from "./GuestType";
|
3
|
+
|
4
|
+
const poolSets = new Map<PoolType, Set<GuestType>>();
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Удалить патрон из всех пулов
|
8
|
+
*/
|
9
|
+
export const removePatronFromPools = (patron: GuestType) => {
|
10
|
+
poolSets.forEach((pool) => {
|
11
|
+
pool.delete(patron);
|
12
|
+
});
|
13
|
+
};
|
14
|
+
|
15
|
+
export class PatronPool<T> implements PoolType<T> {
|
16
|
+
private patrons = new Set<GuestType<T>>();
|
17
|
+
|
18
|
+
public receive: (value: T, options?: ReceiveOptions) => this;
|
19
|
+
|
20
|
+
public constructor(private initiator: unknown) {
|
21
|
+
poolSets.set(this, this.patrons);
|
22
|
+
|
23
|
+
let lastMicrotask: (() => void) | null = null;
|
24
|
+
const doReceive = (value: T, options?: ReceiveOptions) => {
|
25
|
+
this.patrons.forEach((target) => {
|
26
|
+
this.sendValueToGuest(value, target, options);
|
27
|
+
});
|
28
|
+
};
|
29
|
+
this.receive = (value: T, options?: ReceiveOptions) => {
|
30
|
+
const currentMicroTask = () => {
|
31
|
+
if (currentMicroTask === lastMicrotask) {
|
32
|
+
doReceive(value, options);
|
33
|
+
}
|
34
|
+
};
|
35
|
+
lastMicrotask = currentMicroTask;
|
36
|
+
queueMicrotask(currentMicroTask);
|
37
|
+
return this;
|
38
|
+
};
|
39
|
+
}
|
40
|
+
|
41
|
+
public add(shouldBePatron: GuestType<T>) {
|
42
|
+
if (
|
43
|
+
shouldBePatron.introduction &&
|
44
|
+
shouldBePatron.introduction() === "patron"
|
45
|
+
) {
|
46
|
+
this.patrons.add(shouldBePatron);
|
47
|
+
}
|
48
|
+
return this;
|
49
|
+
}
|
50
|
+
|
51
|
+
public remove(patron: GuestType<T>) {
|
52
|
+
this.patrons.delete(patron);
|
53
|
+
return this;
|
54
|
+
}
|
55
|
+
|
56
|
+
public distribute(receiving: T, possiblePatron: GuestType<T>): this {
|
57
|
+
this.add(possiblePatron);
|
58
|
+
this.sendValueToGuest(receiving, possiblePatron, {});
|
59
|
+
return this;
|
60
|
+
}
|
61
|
+
|
62
|
+
private sendValueToGuest(
|
63
|
+
value: T,
|
64
|
+
guest: GuestType<T>,
|
65
|
+
options?: ReceiveOptions,
|
66
|
+
) {
|
67
|
+
guest.receive(value, {
|
68
|
+
...options,
|
69
|
+
data: {
|
70
|
+
...((options?.data as Record<string, unknown>) ?? {}),
|
71
|
+
initiator: this.initiator,
|
72
|
+
pool: this,
|
73
|
+
},
|
74
|
+
});
|
75
|
+
}
|
76
|
+
}
|
package/src/PoolType.ts
ADDED