@pawells/rxjs-events 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/LICENSE +21 -0
- package/README.md +210 -0
- package/build/async-generator-esn.d.ts +27 -0
- package/build/async-generator-esn.d.ts.map +1 -0
- package/build/async-generator-esn.js +2 -0
- package/build/async-generator-esn.js.map +1 -0
- package/build/async-observable.d.ts +72 -0
- package/build/async-observable.d.ts.map +1 -0
- package/build/async-observable.js +201 -0
- package/build/async-observable.js.map +1 -0
- package/build/event-data.d.ts +17 -0
- package/build/event-data.d.ts.map +1 -0
- package/build/event-data.js +2 -0
- package/build/event-data.js.map +1 -0
- package/build/event-filter.d.ts +44 -0
- package/build/event-filter.d.ts.map +1 -0
- package/build/event-filter.js +66 -0
- package/build/event-filter.js.map +1 -0
- package/build/event-function.d.ts +23 -0
- package/build/event-function.d.ts.map +1 -0
- package/build/event-function.js +2 -0
- package/build/event-function.js.map +1 -0
- package/build/extract-event-payload.d.ts +7 -0
- package/build/extract-event-payload.d.ts.map +1 -0
- package/build/extract-event-payload.js +2 -0
- package/build/extract-event-payload.js.map +1 -0
- package/build/filter-criteria.d.ts +8 -0
- package/build/filter-criteria.d.ts.map +1 -0
- package/build/filter-criteria.js +2 -0
- package/build/filter-criteria.js.map +1 -0
- package/build/handler.d.ts +225 -0
- package/build/handler.d.ts.map +1 -0
- package/build/handler.js +341 -0
- package/build/handler.js.map +1 -0
- package/build/index.d.ts +9 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +7 -0
- package/build/index.js.map +1 -0
- package/build/types.d.ts +20 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/package.json +80 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Phillip Aaron Wells
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# RxJS Events
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@pawells/rxjs-events)
|
|
4
|
+
[](https://github.com/PhillipAWells/rxjs-events/releases)
|
|
5
|
+
[](https://github.com/PhillipAWells/rxjs-events/actions/workflows/ci.yml)
|
|
6
|
+
[](https://nodejs.org)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
[](https://github.com/sponsors/PhillipAWells)
|
|
9
|
+
|
|
10
|
+
RxJS-based event handling library with reactive observables and async event streams. Supports subscription management, backpressure-aware async iteration, and TC39 Explicit Resource Management (`await using`).
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @pawells/rxjs-events
|
|
16
|
+
# or
|
|
17
|
+
yarn add @pawells/rxjs-events
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
### Defining an event shape
|
|
23
|
+
|
|
24
|
+
Events must have **exactly one top-level key** whose name is the event type and whose value is the payload:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import type { TEventData } from '@pawells/rxjs-events';
|
|
28
|
+
|
|
29
|
+
interface UserCreatedEvent extends TEventData {
|
|
30
|
+
UserCreated: { userId: string; username: string };
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### EventHandler — subscribe, trigger, and unsubscribe
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { EventHandler } from '@pawells/rxjs-events';
|
|
38
|
+
|
|
39
|
+
const handler = new EventHandler<{ UserCreated: { userId: string; username: string } }, 'UserCreated'>('UserCreated');
|
|
40
|
+
|
|
41
|
+
// Subscribe — returns a numeric ID
|
|
42
|
+
const id = handler.Subscribe((payload) => {
|
|
43
|
+
console.log('New user:', payload.userId);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Trigger the event
|
|
47
|
+
handler.Trigger({ userId: '42', username: 'alice' });
|
|
48
|
+
|
|
49
|
+
// Unsubscribe by ID
|
|
50
|
+
handler.Unsubscribe(id);
|
|
51
|
+
|
|
52
|
+
// Destroy — completes the underlying Subject and removes all subscriptions
|
|
53
|
+
handler.Destroy();
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Async iteration with `GetAsyncIterableIterator`
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const handler = new EventHandler<{ MessageReceived: { text: string } }, 'MessageReceived'>('MessageReceived');
|
|
60
|
+
|
|
61
|
+
async function processMessages() {
|
|
62
|
+
for await (const payload of handler.GetAsyncIterableIterator()) {
|
|
63
|
+
console.log('Message:', payload.text);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Explicit Resource Management (`await using`)
|
|
69
|
+
|
|
70
|
+
`EventHandler` exposes `GetAsyncIterator()`, which returns an `IAsyncGeneratorESN` implementing `Symbol.asyncDispose`:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
async function processOnce() {
|
|
74
|
+
await using iter = handler.GetAsyncIterator();
|
|
75
|
+
const { value, done } = await iter.next();
|
|
76
|
+
if (!done) console.log(value);
|
|
77
|
+
// iter is automatically disposed at block exit
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### AsyncObservable with backpressure
|
|
82
|
+
|
|
83
|
+
`AsyncObservable` is an `Observable` subclass with a push buffer and configurable overflow handling:
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
import { AsyncObservable, BackpressureStrategy } from '@pawells/rxjs-events';
|
|
87
|
+
|
|
88
|
+
const obs = new AsyncObservable<string>({
|
|
89
|
+
bufferSize: 100,
|
|
90
|
+
strategy: BackpressureStrategy.DropOldest,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
for await (const item of obs) {
|
|
94
|
+
console.log(item);
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Filtering events
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import { EventFilter } from '@pawells/rxjs-events';
|
|
102
|
+
import type { IFilterCriteria } from '@pawells/rxjs-events';
|
|
103
|
+
|
|
104
|
+
const criteria: IFilterCriteria = { userId: '42' };
|
|
105
|
+
const event = { UserCreated: { userId: '42', username: 'alice' } };
|
|
106
|
+
|
|
107
|
+
if (EventFilter(event, criteria)) {
|
|
108
|
+
// event matches all criteria
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## API
|
|
113
|
+
|
|
114
|
+
### `EventHandler<TObject, TEvent>`
|
|
115
|
+
|
|
116
|
+
Main event handler class wrapping an RxJS `Subject`.
|
|
117
|
+
|
|
118
|
+
| Method | Signature | Description |
|
|
119
|
+
|--------|-----------|-------------|
|
|
120
|
+
| `constructor` | `(name: TEvent)` | Creates a handler with the given event name |
|
|
121
|
+
| `Name` | `string` (getter) | Returns the event name |
|
|
122
|
+
| `Subscribe` | `(handler: TEventFunction<TObject[TEvent]>, options?: ISubscriptionOptions) => number` | Subscribes and returns a numeric ID |
|
|
123
|
+
| `Unsubscribe` | `(id: number) => void` | Removes subscription by ID |
|
|
124
|
+
| `Trigger` | `(data: TObject[TEvent]) => void` | Emits the event with the given payload |
|
|
125
|
+
| `Destroy` | `() => void` | Completes the Subject and cleans up all subscriptions |
|
|
126
|
+
| `GetAsyncIterableIterator` | `() => AsyncIterableIterator<TObject[TEvent]>` | Returns an async iterable iterator |
|
|
127
|
+
| `GetAsyncIterator` | `() => IAsyncGeneratorESN<TObject[TEvent]>` | Returns a disposable async generator |
|
|
128
|
+
|
|
129
|
+
Subscription IDs are recycled internally — allocation is O(1).
|
|
130
|
+
|
|
131
|
+
### `AsyncObservable<T>`
|
|
132
|
+
|
|
133
|
+
An `Observable` subclass with a push buffer and configurable backpressure. Implements `Symbol.asyncIterator`.
|
|
134
|
+
|
|
135
|
+
| Option | Type | Description |
|
|
136
|
+
|--------|------|-------------|
|
|
137
|
+
| `bufferSize` | `number` | Maximum number of buffered items |
|
|
138
|
+
| `strategy` | `BackpressureStrategy` | `DropOldest`, `DropNewest`, or `Error` |
|
|
139
|
+
|
|
140
|
+
Throws `BufferOverflowError` when strategy is `Error` and the buffer is full.
|
|
141
|
+
|
|
142
|
+
### `EventFilter(event, criteria)`
|
|
143
|
+
|
|
144
|
+
Filters a single-key event object against an `IFilterCriteria` map using strict equality. Returns `true` if all criteria match.
|
|
145
|
+
|
|
146
|
+
### Types
|
|
147
|
+
|
|
148
|
+
| Export | Description |
|
|
149
|
+
|--------|-------------|
|
|
150
|
+
| `TEventData` | Base type alias (`Record<string, unknown>`) — all event shapes extend this |
|
|
151
|
+
| `TEventFunction<TEvent>` | Handler callback: `(data: TEvent) => Promise<void> \| void` |
|
|
152
|
+
| `TEventHandler` | Union type for handler references |
|
|
153
|
+
| `TEventFilter` | Filter function type |
|
|
154
|
+
| `TAsyncObserver` | Async observer callback type |
|
|
155
|
+
| `TUnsubscribe` | Unsubscribe function type |
|
|
156
|
+
| `IFilterCriteria` | Index-signature interface `{ [key: string]: unknown }` |
|
|
157
|
+
| `ISubscriptionOptions` | Options passed to `Subscribe` |
|
|
158
|
+
| `IBackpressureConfig` | Configuration object for `AsyncObservable` |
|
|
159
|
+
| `IAsyncGeneratorESN<T, TReturn, TNext>` | `AsyncGenerator` extended with `Symbol.asyncDispose` |
|
|
160
|
+
| `TExtractEventPayload<TEvent>` | Utility type — extracts payload type from a `TEventData` object |
|
|
161
|
+
| `BackpressureStrategy` | Enum: `DropOldest`, `DropNewest`, `Error` |
|
|
162
|
+
| `BufferOverflowError` | Error thrown on buffer overflow when strategy is `Error` |
|
|
163
|
+
|
|
164
|
+
### Mocks (test helpers)
|
|
165
|
+
|
|
166
|
+
Import from `@pawells/rxjs-events/src/mocks/index.js` in tests (not re-exported from the main entry point):
|
|
167
|
+
|
|
168
|
+
| Export | Description |
|
|
169
|
+
|--------|-------------|
|
|
170
|
+
| `MockEventHandler` | Controllable mock with spy capabilities for asserting triggers and subscriptions |
|
|
171
|
+
| `MockAsyncObservable` | Controllable mock exposing push/complete/error helpers |
|
|
172
|
+
| `SetupMatchers` | Registers all custom Vitest matchers — call from `vitest.setup.ts` |
|
|
173
|
+
| `ToHaveSubscribers` | Custom matcher: asserts a handler has N active subscribers |
|
|
174
|
+
| `ToHaveTriggeredEvent` | Custom matcher: asserts a mock handler triggered a specific event |
|
|
175
|
+
| `ToMatchEventFilter` | Custom matcher: asserts an event matches a filter criteria map |
|
|
176
|
+
| `GenerateUserEvents` | Factory for typed user-event objects |
|
|
177
|
+
| `GenerateMessageEvents` | Factory for message-event objects |
|
|
178
|
+
| `GenerateEventData` | Generic event-data factory |
|
|
179
|
+
| `GenerateFilterCriteria` | Produces `IFilterCriteria` maps for filter tests |
|
|
180
|
+
| `GenerateSubscriptionScenarios` | Generates multi-subscription test scenarios |
|
|
181
|
+
|
|
182
|
+
## Development
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
yarn build # Compile TypeScript → ./build/
|
|
186
|
+
yarn dev # Build and run
|
|
187
|
+
yarn watch # TypeScript watch mode
|
|
188
|
+
yarn typecheck # Type check without emitting
|
|
189
|
+
yarn lint # ESLint src/
|
|
190
|
+
yarn lint:fix # ESLint with auto-fix
|
|
191
|
+
yarn test # Run Vitest tests
|
|
192
|
+
yarn test:ui # Open interactive Vitest UI in a browser
|
|
193
|
+
yarn test:coverage # Run tests with coverage report
|
|
194
|
+
yarn start # Run built output
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
To run a single test file:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
yarn vitest run src/path/to/file.test.ts
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Requirements
|
|
204
|
+
|
|
205
|
+
- Node.js >= 24.0.0
|
|
206
|
+
- ESM-only (`"type": "module"`) — use ESM imports throughout
|
|
207
|
+
|
|
208
|
+
## License
|
|
209
|
+
|
|
210
|
+
MIT — See [LICENSE](./LICENSE) for details.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extended AsyncGenerator interface with async disposal support.
|
|
3
|
+
*
|
|
4
|
+
* ESN = Explicit Symbol Notation — extends AsyncGenerator with `Symbol.asyncDispose` support
|
|
5
|
+
* for the TC39 Explicit Resource Management proposal (`using` keyword). `Symbol.asyncDispose`
|
|
6
|
+
* is called when the generator is used in a `await using` block.
|
|
7
|
+
*
|
|
8
|
+
* @template T The type of the values yielded by the generator.
|
|
9
|
+
* @template TReturn The type of the value returned by the generator.
|
|
10
|
+
* @template TNext The type of the value that can be passed to the generator's next() method.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* async function* myGenerator(): IAsyncGeneratorESN<string, void, void> {
|
|
15
|
+
* yield 'value1';
|
|
16
|
+
* yield 'value2';
|
|
17
|
+
* }
|
|
18
|
+
*
|
|
19
|
+
* // With await using (requires TypeScript 5.2+)
|
|
20
|
+
* await using gen = myGenerator() as unknown as AsyncDisposable;
|
|
21
|
+
* // gen is automatically disposed when it goes out of scope
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export interface IAsyncGeneratorESN<T = unknown, TReturn = unknown, TNext = unknown> extends AsyncGenerator<T, TReturn, TNext> {
|
|
25
|
+
[Symbol.asyncDispose](): PromiseLike<void>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=async-generator-esn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-generator-esn.d.ts","sourceRoot":"","sources":["../src/async-generator-esn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,KAAK,GAAG,OAAO,CAAE,SAAQ,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC;IAC7H,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,CAAA;CAC1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-generator-esn.js","sourceRoot":"","sources":["../src/async-generator-esn.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when the observable buffer overflows.
|
|
4
|
+
*/
|
|
5
|
+
export declare class BufferOverflowError extends Error {
|
|
6
|
+
constructor(maxSize: number);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Backpressure overflow strategy
|
|
10
|
+
*/
|
|
11
|
+
export declare enum BackpressureStrategy {
|
|
12
|
+
/** Drop oldest events when buffer is full */
|
|
13
|
+
DropOldest = "DropOldest",
|
|
14
|
+
/** Drop newest events when buffer is full */
|
|
15
|
+
DropNewest = "DropNewest",
|
|
16
|
+
/** Throw error when buffer is full */
|
|
17
|
+
Error = "Error"
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Configuration for AsyncObservable backpressure
|
|
21
|
+
*/
|
|
22
|
+
export interface IBackpressureConfig {
|
|
23
|
+
/** Maximum buffer size (default: 1000) */
|
|
24
|
+
maxBufferSize?: number;
|
|
25
|
+
/** Strategy when buffer overflows (default: DropOldest) */
|
|
26
|
+
overflowStrategy?: BackpressureStrategy;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* An Observable that can be used as an async iterator with backpressure support.
|
|
30
|
+
* @template T The type of the values emitted by the observable.
|
|
31
|
+
*/
|
|
32
|
+
export declare class AsyncObservable<T> extends Observable<T> {
|
|
33
|
+
private readonly _Subject;
|
|
34
|
+
private readonly _Buffer;
|
|
35
|
+
private readonly _MaxBufferSize;
|
|
36
|
+
private readonly _OverflowStrategy;
|
|
37
|
+
/**
|
|
38
|
+
* @remarks
|
|
39
|
+
* **Buffer replay on subscription:** every new subscriber receives all buffered items
|
|
40
|
+
* (those added via `Push()` before the subscriber connected), followed by live items.
|
|
41
|
+
* This is intentional "replay" behaviour — late subscribers see historical data.
|
|
42
|
+
* If you only want live items, subscribe before pushing any data.
|
|
43
|
+
*/
|
|
44
|
+
constructor(config?: IBackpressureConfig);
|
|
45
|
+
/**
|
|
46
|
+
* Adds an event to the buffer with backpressure handling
|
|
47
|
+
*/
|
|
48
|
+
Push(value: T): void;
|
|
49
|
+
private _HandleOverflow;
|
|
50
|
+
/**
|
|
51
|
+
* Destroys the observable and completes the internal subject, cleaning up all resources.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* const observable = new AsyncObservable<number>();
|
|
56
|
+
* observable.Push(1);
|
|
57
|
+
* observable.Destroy(); // Completes the subject; subscribers stop receiving events
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
Destroy(): void;
|
|
61
|
+
/**
|
|
62
|
+
* Returns an async generator that can be used to iterate over the values emitted by the observable.
|
|
63
|
+
*
|
|
64
|
+
* Each call creates an independent iterator with its own local queue, so multiple concurrent
|
|
65
|
+
* iterators on the same AsyncObservable are safe — they do not interfere with each other or
|
|
66
|
+
* with the shared replay buffer.
|
|
67
|
+
*
|
|
68
|
+
* @returns An async generator.
|
|
69
|
+
*/
|
|
70
|
+
[Symbol.asyncIterator](): AsyncGenerator<T, void, void>;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=async-observable.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-observable.d.ts","sourceRoot":"","sources":["../src/async-observable.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAyB,MAAM,MAAM,CAAC;AAGzD;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBACjC,OAAO,EAAE,MAAM;CAI3B;AAED;;GAEG;AACH,oBAAY,oBAAoB;IAC/B,6CAA6C;IAC7C,UAAU,eAAe;IACzB,6CAA6C;IAC7C,UAAU,eAAe;IACzB,sCAAsC;IACtC,KAAK,UAAU;CACf;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IACnC,0CAA0C;IAC1C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;CACxC;AAKD;;;GAGG;AACH,qBAAa,eAAe,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;IACpD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAE7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAW;IAEnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAuB;IAEzD;;;;;;OAMG;gBACS,MAAM,CAAC,EAAE,mBAAmB;IAqBxC;;OAEG;IACI,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI;IAS3B,OAAO,CAAC,eAAe;IAevB;;;;;;;;;OASG;IACI,OAAO,IAAI,IAAI;IAKtB;;;;;;;;OAQG;IACI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC;CAwG9D"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { Observable, Subject } from 'rxjs';
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when the observable buffer overflows.
|
|
4
|
+
*/
|
|
5
|
+
export class BufferOverflowError extends Error {
|
|
6
|
+
constructor(maxSize) {
|
|
7
|
+
super(`Buffer overflow: maximum buffer size of ${maxSize} exceeded`);
|
|
8
|
+
this.name = 'BufferOverflowError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Backpressure overflow strategy
|
|
13
|
+
*/
|
|
14
|
+
export var BackpressureStrategy;
|
|
15
|
+
(function (BackpressureStrategy) {
|
|
16
|
+
/** Drop oldest events when buffer is full */
|
|
17
|
+
BackpressureStrategy["DropOldest"] = "DropOldest";
|
|
18
|
+
/** Drop newest events when buffer is full */
|
|
19
|
+
BackpressureStrategy["DropNewest"] = "DropNewest";
|
|
20
|
+
/** Throw error when buffer is full */
|
|
21
|
+
BackpressureStrategy["Error"] = "Error";
|
|
22
|
+
})(BackpressureStrategy || (BackpressureStrategy = {}));
|
|
23
|
+
/** Default maximum buffer size for AsyncObservable instances */
|
|
24
|
+
const DEFAULT_MAX_BUFFER_SIZE = 1000;
|
|
25
|
+
/**
|
|
26
|
+
* An Observable that can be used as an async iterator with backpressure support.
|
|
27
|
+
* @template T The type of the values emitted by the observable.
|
|
28
|
+
*/
|
|
29
|
+
export class AsyncObservable extends Observable {
|
|
30
|
+
_Subject = new Subject();
|
|
31
|
+
_Buffer = [];
|
|
32
|
+
_MaxBufferSize;
|
|
33
|
+
_OverflowStrategy;
|
|
34
|
+
/**
|
|
35
|
+
* @remarks
|
|
36
|
+
* **Buffer replay on subscription:** every new subscriber receives all buffered items
|
|
37
|
+
* (those added via `Push()` before the subscriber connected), followed by live items.
|
|
38
|
+
* This is intentional "replay" behaviour — late subscribers see historical data.
|
|
39
|
+
* If you only want live items, subscribe before pushing any data.
|
|
40
|
+
*/
|
|
41
|
+
constructor(config) {
|
|
42
|
+
super(observer => {
|
|
43
|
+
// Connect the observer to the live subject first so it will receive future pushes,
|
|
44
|
+
// then replay already-buffered items in order. Because JavaScript is single-threaded,
|
|
45
|
+
// no new Push() can interleave between these two steps, so replay order is guaranteed.
|
|
46
|
+
const innerSub = this._Subject.subscribe(observer);
|
|
47
|
+
for (const item of this._Buffer) {
|
|
48
|
+
observer.next(item);
|
|
49
|
+
}
|
|
50
|
+
return () => innerSub.unsubscribe();
|
|
51
|
+
});
|
|
52
|
+
this._MaxBufferSize = config?.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;
|
|
53
|
+
this._OverflowStrategy = config?.overflowStrategy ?? BackpressureStrategy.DropOldest;
|
|
54
|
+
// Validate buffer size
|
|
55
|
+
if (this._MaxBufferSize <= 0) {
|
|
56
|
+
throw new RangeError('maxBufferSize must be a positive integer');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Adds an event to the buffer with backpressure handling
|
|
61
|
+
*/
|
|
62
|
+
Push(value) {
|
|
63
|
+
if (this._Buffer.length >= this._MaxBufferSize) {
|
|
64
|
+
this._HandleOverflow(value);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this._Buffer.push(value);
|
|
68
|
+
this._Subject.next(value);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
_HandleOverflow(value) {
|
|
72
|
+
switch (this._OverflowStrategy) {
|
|
73
|
+
case BackpressureStrategy.DropOldest:
|
|
74
|
+
this._Buffer.shift();
|
|
75
|
+
this._Buffer.push(value);
|
|
76
|
+
this._Subject.next(value);
|
|
77
|
+
break;
|
|
78
|
+
case BackpressureStrategy.DropNewest:
|
|
79
|
+
// Simply don't add the new value
|
|
80
|
+
break;
|
|
81
|
+
case BackpressureStrategy.Error:
|
|
82
|
+
throw new BufferOverflowError(this._MaxBufferSize);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Destroys the observable and completes the internal subject, cleaning up all resources.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* const observable = new AsyncObservable<number>();
|
|
91
|
+
* observable.Push(1);
|
|
92
|
+
* observable.Destroy(); // Completes the subject; subscribers stop receiving events
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
Destroy() {
|
|
96
|
+
this._Subject.complete();
|
|
97
|
+
this._Buffer.length = 0;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Returns an async generator that can be used to iterate over the values emitted by the observable.
|
|
101
|
+
*
|
|
102
|
+
* Each call creates an independent iterator with its own local queue, so multiple concurrent
|
|
103
|
+
* iterators on the same AsyncObservable are safe — they do not interfere with each other or
|
|
104
|
+
* with the shared replay buffer.
|
|
105
|
+
*
|
|
106
|
+
* @returns An async generator.
|
|
107
|
+
*/
|
|
108
|
+
[Symbol.asyncIterator]() {
|
|
109
|
+
let subscription;
|
|
110
|
+
let hasError = false;
|
|
111
|
+
let error;
|
|
112
|
+
let completed = false;
|
|
113
|
+
// Per-iterator queue — never mutates the shared _Buffer.
|
|
114
|
+
const localQueue = [];
|
|
115
|
+
const deferreds = [];
|
|
116
|
+
const handleError = (err) => {
|
|
117
|
+
hasError = true;
|
|
118
|
+
error = err;
|
|
119
|
+
while (deferreds.length) {
|
|
120
|
+
const deferred = deferreds.shift();
|
|
121
|
+
if (deferred) {
|
|
122
|
+
const [, reject] = deferred;
|
|
123
|
+
reject(err);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
const handleComplete = () => {
|
|
128
|
+
completed = true;
|
|
129
|
+
while (deferreds.length) {
|
|
130
|
+
const deferred = deferreds.shift();
|
|
131
|
+
if (deferred) {
|
|
132
|
+
const [resolve] = deferred;
|
|
133
|
+
resolve({ done: true, value: undefined });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const generator = {
|
|
138
|
+
[Symbol.asyncDispose]: () => {
|
|
139
|
+
return new Promise((resolve) => {
|
|
140
|
+
subscription?.unsubscribe();
|
|
141
|
+
resolve();
|
|
142
|
+
});
|
|
143
|
+
},
|
|
144
|
+
next: () => {
|
|
145
|
+
// Lazily create the subscription on first next() call.
|
|
146
|
+
// Because JS is single-threaded, the Observable's subscriber function runs
|
|
147
|
+
// synchronously: it first connects to _Subject (for live items), then replays
|
|
148
|
+
// all buffered items through the `next` handler below. At that point `deferreds`
|
|
149
|
+
// is always empty, so every replayed item lands in `localQueue`. This means the
|
|
150
|
+
// shared _Buffer is never mutated here and multiple concurrent iterators are safe.
|
|
151
|
+
subscription ??= this.subscribe({
|
|
152
|
+
complete: handleComplete,
|
|
153
|
+
error: handleError,
|
|
154
|
+
next: (value) => {
|
|
155
|
+
if (deferreds.length) {
|
|
156
|
+
// A consumer is already waiting; deliver directly.
|
|
157
|
+
const deferred = deferreds.shift();
|
|
158
|
+
if (deferred) {
|
|
159
|
+
const [resolve] = deferred;
|
|
160
|
+
resolve({ done: false, value });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
localQueue.push(value);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
if (localQueue.length) {
|
|
169
|
+
const value = localQueue.shift();
|
|
170
|
+
return Promise.resolve({ done: false, value });
|
|
171
|
+
}
|
|
172
|
+
if (completed) {
|
|
173
|
+
return Promise.resolve({ done: true, value: undefined });
|
|
174
|
+
}
|
|
175
|
+
if (hasError) {
|
|
176
|
+
return Promise.reject(error);
|
|
177
|
+
}
|
|
178
|
+
return new Promise((resolve, reject) => {
|
|
179
|
+
deferreds.push([resolve, reject]);
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
return: () => {
|
|
183
|
+
subscription?.unsubscribe();
|
|
184
|
+
// Resolve all pending next() promises so callers are not left hanging.
|
|
185
|
+
handleComplete();
|
|
186
|
+
return Promise.resolve({ done: true, value: null });
|
|
187
|
+
},
|
|
188
|
+
throw: (err) => {
|
|
189
|
+
subscription?.unsubscribe();
|
|
190
|
+
// Reject all pending next() promises so callers are not left hanging.
|
|
191
|
+
handleError(err);
|
|
192
|
+
return Promise.reject(err);
|
|
193
|
+
},
|
|
194
|
+
[Symbol.asyncIterator]() {
|
|
195
|
+
return this;
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
return generator;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=async-observable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-observable.js","sourceRoot":"","sources":["../src/async-observable.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base interface for all event data structures.
|
|
3
|
+
* Events should have a single key representing the event type with associated payload data.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* interface UserCreatedEvent extends TEventData {
|
|
8
|
+
* UserCreated: {
|
|
9
|
+
* userId: string;
|
|
10
|
+
* username: string;
|
|
11
|
+
* email: string;
|
|
12
|
+
* };
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export type TEventData = Record<string, unknown>;
|
|
17
|
+
//# sourceMappingURL=event-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-data.d.ts","sourceRoot":"","sources":["../src/event-data.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-data.js","sourceRoot":"","sources":["../src/event-data.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TEventData } from './event-data.js';
|
|
2
|
+
import { IFilterCriteria } from './filter-criteria.js';
|
|
3
|
+
/**
|
|
4
|
+
* Filters events based on payload property matching criteria.
|
|
5
|
+
* Performs strict equality comparison between event payload properties and filter arguments.
|
|
6
|
+
*
|
|
7
|
+
* @template TEvent - The event data type extending TEventData
|
|
8
|
+
* @param event - The event to filter, must have exactly one property representing the event type
|
|
9
|
+
* @param args - Filter criteria object with property-value pairs to match against the event payload
|
|
10
|
+
* @returns true if the event matches all filter criteria or if no filter is provided, false otherwise
|
|
11
|
+
*
|
|
12
|
+
* @throws {Error} 'No Event' - When event is null or undefined
|
|
13
|
+
* @throws {Error} 'More than one payload structure.' - When event has more than one top-level property
|
|
14
|
+
* @throws {Error} 'No Payload' - When the event's payload is null or undefined
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* interface UserEvent extends TEventData {
|
|
19
|
+
* UserCreated: {
|
|
20
|
+
* userId: string;
|
|
21
|
+
* username: string;
|
|
22
|
+
* role: string;
|
|
23
|
+
* };
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* const event: UserEvent = {
|
|
27
|
+
* UserCreated: { userId: '123', username: 'john', role: 'admin' }
|
|
28
|
+
* };
|
|
29
|
+
*
|
|
30
|
+
* // Match by single property
|
|
31
|
+
* EventFilter(event, { role: 'admin' }); // true
|
|
32
|
+
* EventFilter(event, { role: 'user' }); // false
|
|
33
|
+
*
|
|
34
|
+
* // Match by multiple properties
|
|
35
|
+
* EventFilter(event, { username: 'john', role: 'admin' }); // true
|
|
36
|
+
* EventFilter(event, { username: 'john', role: 'user' }); // false
|
|
37
|
+
*
|
|
38
|
+
* // No filter (always passes)
|
|
39
|
+
* EventFilter(event, null); // true
|
|
40
|
+
* EventFilter(event, undefined); // true
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function EventFilter<TEvent extends TEventData = TEventData>(event: TEvent, args: IFilterCriteria | null | undefined): boolean;
|
|
44
|
+
//# sourceMappingURL=event-filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-filter.d.ts","sourceRoot":"","sources":["../src/event-filter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,WAAW,CAAC,MAAM,SAAS,UAAU,GAAG,UAAU,EACjE,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,eAAe,GAAG,IAAI,GAAG,SAAS,GACtC,OAAO,CA0BT"}
|