@moeru/eventa 0.3.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 +286 -0
- package/dist/adapters/electron/main.d.mts +26 -0
- package/dist/adapters/electron/main.mjs +67 -0
- package/dist/adapters/electron/main.mjs.map +1 -0
- package/dist/adapters/electron/renderer.d.mts +24 -0
- package/dist/adapters/electron/renderer.mjs +53 -0
- package/dist/adapters/electron/renderer.mjs.map +1 -0
- package/dist/adapters/event-emitter/index.d.mts +35 -0
- package/dist/adapters/event-emitter/index.mjs +62 -0
- package/dist/adapters/event-emitter/index.mjs.map +1 -0
- package/dist/adapters/event-target/index.d.mts +35 -0
- package/dist/adapters/event-target/index.mjs +67 -0
- package/dist/adapters/event-target/index.mjs.map +1 -0
- package/dist/adapters/websocket/h3/index.d.mts +57 -0
- package/dist/adapters/websocket/h3/index.mjs +100 -0
- package/dist/adapters/websocket/h3/index.mjs.map +1 -0
- package/dist/adapters/websocket/index.d.mts +12 -0
- package/dist/adapters/websocket/index.mjs +1 -0
- package/dist/adapters/websocket/native/index.d.mts +26 -0
- package/dist/adapters/websocket/native/index.mjs +40 -0
- package/dist/adapters/websocket/native/index.mjs.map +1 -0
- package/dist/adapters/webworkers/index.d.mts +63 -0
- package/dist/adapters/webworkers/index.mjs +42 -0
- package/dist/adapters/webworkers/index.mjs.map +1 -0
- package/dist/adapters/webworkers/worker/index.d.mts +24 -0
- package/dist/adapters/webworkers/worker/index.mjs +40 -0
- package/dist/adapters/webworkers/worker/index.mjs.map +1 -0
- package/dist/context-C35Qku9U.mjs +191 -0
- package/dist/context-C35Qku9U.mjs.map +1 -0
- package/dist/context-C_-MRU46.d.mts +41 -0
- package/dist/eventa-B2HPBK4S.d.mts +51 -0
- package/dist/index-BgaYNgPh.d.mts +24 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +4 -0
- package/dist/internal-DjiAQtsa.mjs +18 -0
- package/dist/internal-DjiAQtsa.mjs.map +1 -0
- package/dist/invoke-LTUFMmHi.d.mts +137 -0
- package/dist/shared-Da41l-vp.mjs +21 -0
- package/dist/shared-Da41l-vp.mjs.map +1 -0
- package/dist/shared-DbO1rU2W.mjs +71 -0
- package/dist/shared-DbO1rU2W.mjs.map +1 -0
- package/dist/shared-DllZ3RPS.d.mts +18 -0
- package/dist/src-Bb-vxm5k.mjs +289 -0
- package/dist/src-Bb-vxm5k.mjs.map +1 -0
- package/package.json +121 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 RainbowBird
|
|
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,286 @@
|
|
|
1
|
+
# `eventa`
|
|
2
|
+
|
|
3
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
4
|
+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
5
|
+
[![bundle][bundle-src]][bundle-href]
|
|
6
|
+
[![JSDocs][jsdocs-src]][jsdocs-href]
|
|
7
|
+
[![License][license-src]][license-href]
|
|
8
|
+
|
|
9
|
+
Transport-aware events powering ergonomic RPC and streaming flows.
|
|
10
|
+
|
|
11
|
+
> Heavily inspired by pragmatic RPC flows, but centred on pure events so transports stay swappable.
|
|
12
|
+
|
|
13
|
+
> [!WARNING]
|
|
14
|
+
> Eventa forwards whatever payload you emit. Validate data at the edges before sending it to untrusted peers.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npm install @moeru/eventa
|
|
20
|
+
pnpm i @moeru/eventa
|
|
21
|
+
bun i @moeru/eventa
|
|
22
|
+
ni @moeru/eventa
|
|
23
|
+
yarn add @moeru/eventa
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Getting Started
|
|
27
|
+
|
|
28
|
+
### Event
|
|
29
|
+
|
|
30
|
+
It's very simple:
|
|
31
|
+
|
|
32
|
+
- `defineEventa`: all event should be defined with this util, it produces type safe constraints
|
|
33
|
+
- `context`: a channel bridges to peers (Electron, Worker, WebSocket Peer, you name it)
|
|
34
|
+
- `createContext`: to wrap any compatible event listener
|
|
35
|
+
|
|
36
|
+
If you need only events without RPC mechanism, then use with `context.emit(...)` and `context.on(...)`
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { createContext, defineEventa } from '@moeru/eventa'
|
|
40
|
+
|
|
41
|
+
const move = defineEventa<{ x: number, y: number }>()
|
|
42
|
+
const ctx = createContext()
|
|
43
|
+
|
|
44
|
+
ctx.emit(move, { x: 100, y: 200 })
|
|
45
|
+
ctx.on(move, ({ body }) => console.log(body.x, body.y))
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### RPC/Stream RPC
|
|
49
|
+
|
|
50
|
+
Events can be seen as packets transferring in networks, so we can use pure event to form a RPC/Stream RPC like how gRPC and tRPC works.
|
|
51
|
+
|
|
52
|
+
- `defineInvokeEventa`: define types of RPC/Stream RPC
|
|
53
|
+
- `defineInvoke`: this produce a `function` returns `Promise` for your RPC call to be used later, you can store and use it everywhere you want
|
|
54
|
+
- `defineInvokeHandler`: similar to how Nuxt, h3 defines their handler, we use `defineInvokeHandler` to hook a auto
|
|
55
|
+
- `defineStreamInvokeHandler`: similar to gRPC, when one RPC invocation produces not only one response, but multiple intermediate events, you may want to use it
|
|
56
|
+
|
|
57
|
+
#### Simple Example
|
|
58
|
+
|
|
59
|
+
The most simple way to show how it works:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { createContext, defineInvoke, defineInvokeEventa, defineInvokeHandler } from '@moeru/eventa'
|
|
63
|
+
|
|
64
|
+
const ctx = createContext()
|
|
65
|
+
const someMethodDefine = defineInvokeEventa<{ output: string }, { input: number }>('random name')
|
|
66
|
+
defineInvokeHandler(ctx, someMethodDefine, ({ input }) => ({ output: String(input) }))
|
|
67
|
+
|
|
68
|
+
const someMethod = defineInvoke(ctx, someMethodDefine)
|
|
69
|
+
console.log(await someMethod(42)) // => { output: '42' }
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Adapters
|
|
73
|
+
|
|
74
|
+
Eventa comes with various adapters for common use scenarios across browsers and Node.js, escalating the event orchestration in Electron, Web Workers, and WebSockets, etc.
|
|
75
|
+
|
|
76
|
+
<details>
|
|
77
|
+
<summary>Electron</summary>
|
|
78
|
+
|
|
79
|
+
1. Create a shared events module:
|
|
80
|
+
```ts
|
|
81
|
+
import { defineInvokeEventa } from '@moeru/eventa'
|
|
82
|
+
|
|
83
|
+
export const readdir = defineInvokeEventa<{ directories: string[] }, { cwd: string, target: string }>('rpc:node:fs/promise:readdir')
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
2. In the main process, bridge the adapter to `ipcMain` and your `BrowserWindow` instance:
|
|
87
|
+
```ts
|
|
88
|
+
import { createContext as createMainContext } from '@moeru/eventa/adapters/electron/main'
|
|
89
|
+
import { app, BrowserWindow, ipcMain } from 'electron'
|
|
90
|
+
|
|
91
|
+
import { readdir } from './events/readdir'
|
|
92
|
+
|
|
93
|
+
app.on('ready', () => {
|
|
94
|
+
// ... other code
|
|
95
|
+
const { context: mainCtx } = createMainContext(ipcMain, mainWindow.webContents)
|
|
96
|
+
defineInvokeHandler(mainCtx, readdir, async ({ cwd, target }) => {
|
|
97
|
+
const fs = await import('node:fs/promises')
|
|
98
|
+
const path = await import('node:path')
|
|
99
|
+
const fullPath = path.resolve(cwd, target)
|
|
100
|
+
const directories = await fs.readdir(fullPath, { withFileTypes: true })
|
|
101
|
+
return { directories: directories.filter(dirent => dirent.isDirectory()).map(dirent => dirent.name) }
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
3. In the renderer (not restricted to preload scripts, but recommended), bridge to `ipcRenderer` and expose a safe API:
|
|
106
|
+
```ts
|
|
107
|
+
import { createContext as createRendererContext } from '@moeru/eventa/adapters/electron/renderer'
|
|
108
|
+
import { contextBridge, ipcRenderer } from 'electron'
|
|
109
|
+
|
|
110
|
+
import { defineInvoke, readdir } from './events/readdir'
|
|
111
|
+
|
|
112
|
+
const { context: rendererCtx } = createRendererContext(ipcRenderer)
|
|
113
|
+
const invokeReaddir = defineInvoke(rendererCtx, readdir)
|
|
114
|
+
|
|
115
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
116
|
+
invokeReaddir({ cwd: '/', target: 'usr' }).then((result) => {
|
|
117
|
+
console.log('directories', result.directories)
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
```
|
|
121
|
+
4. The main and renderer contexts now share the invoke pipeline used throughout the examples in `src/adapters/electron/*.test.ts`.
|
|
122
|
+
|
|
123
|
+
</details>
|
|
124
|
+
|
|
125
|
+
<details>
|
|
126
|
+
<summary>Web Worker</summary>
|
|
127
|
+
|
|
128
|
+
1. Spawn the worker and wrap it with the main-thread adapter:
|
|
129
|
+
```ts
|
|
130
|
+
import Worker from 'web-worker'
|
|
131
|
+
|
|
132
|
+
import { createContext, defineInvoke, defineInvokeEventa } from '@moeru/eventa/adapters/webworkers'
|
|
133
|
+
|
|
134
|
+
const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' })
|
|
135
|
+
const { context: mainCtx } = createContext(worker)
|
|
136
|
+
|
|
137
|
+
export const syncEvents = defineInvokeEventa<{ status: string }, { jobId: string }>('worker:sync')
|
|
138
|
+
export const invokeSync = defineInvoke(mainCtx, syncEvents)
|
|
139
|
+
```
|
|
140
|
+
2. Inside the worker entry, create the worker context and register handlers:
|
|
141
|
+
```ts
|
|
142
|
+
import { defineInvokeHandler } from '@moeru/eventa'
|
|
143
|
+
import { createContext } from '@moeru/eventa/adapters/webworkers/worker'
|
|
144
|
+
|
|
145
|
+
import { syncEvents } from '../sync'
|
|
146
|
+
|
|
147
|
+
const { context: workerCtx } = createContext()
|
|
148
|
+
defineInvokeHandler(workerCtx, syncEvents, ({ jobId }) => ({ status: `synced ${jobId}` }))
|
|
149
|
+
```
|
|
150
|
+
3. The same pattern works for streaming handlers and for sending transferrable(s) by switching to `defineStreamInvoke` or `defineOutboundWorkerEventa` as shown in `src/adapters/webworkers/index.spec.ts`.
|
|
151
|
+
|
|
152
|
+
</details>
|
|
153
|
+
|
|
154
|
+
<details>
|
|
155
|
+
<summary>WebSocket (Client)</summary>
|
|
156
|
+
|
|
157
|
+
1. Open a `WebSocket` and wrap it with the native adapter:
|
|
158
|
+
```ts
|
|
159
|
+
import { defineInvoke, defineInvokeEventa } from '@moeru/eventa'
|
|
160
|
+
import { createContext as createWsContext } from '@moeru/eventa/adapters/websocket/native'
|
|
161
|
+
|
|
162
|
+
const socket = new WebSocket('wss://example.com/ws')
|
|
163
|
+
const { context: wsCtx } = createWsContext(socket)
|
|
164
|
+
|
|
165
|
+
const chatEvents = defineInvokeEventa<{ message: string }, { text: string }>('chat:send')
|
|
166
|
+
export const sendChat = defineInvoke(wsCtx, chatEvents)
|
|
167
|
+
```
|
|
168
|
+
2. Listen for connection lifecycle events to update UI state or retry logic:
|
|
169
|
+
```ts
|
|
170
|
+
import { wsConnectedEvent, wsDisconnectedEvent } from '@moeru/eventa/adapters/websocket/native'
|
|
171
|
+
|
|
172
|
+
wsCtx.on(wsConnectedEvent, () => console.log('connected'))
|
|
173
|
+
wsCtx.on(wsDisconnectedEvent, () => console.log('disconnected'))
|
|
174
|
+
```
|
|
175
|
+
3. Pair the client with either the H3 global or peer adapter on the server for a full RPC channel (`src/adapters/websocket/h3/*.test.ts`).
|
|
176
|
+
|
|
177
|
+
</details>
|
|
178
|
+
|
|
179
|
+
<details>
|
|
180
|
+
<summary>WebSocket (Server with H3)</summary>
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import { defineInvoke, defineInvokeHandler } from '@moeru/eventa'
|
|
184
|
+
// we support h3 by default, you can implement whatever you want, it's simple
|
|
185
|
+
import { createContext } from '@moeru/eventa/adapters/websocket/h3'
|
|
186
|
+
|
|
187
|
+
const chatEvents = defineInvokeEventa<{ message: string }, { text: string }>('chat:send')
|
|
188
|
+
|
|
189
|
+
const app = new H3()
|
|
190
|
+
const { untilLeastOneConnected, hooks } = createPeerHooks()
|
|
191
|
+
app.get('/ws', defineWebSocketHandler(hooks))
|
|
192
|
+
|
|
193
|
+
const { context } = await untilLeastOneConnected
|
|
194
|
+
defineInvokeHandler(context, chatEvents, ({ text: string }) => {
|
|
195
|
+
// you can safely throw any error you want, you can even make the error type safe when using `defineInvoke`
|
|
196
|
+
return { message: `Echo: ${text}` }
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
</details>
|
|
200
|
+
|
|
201
|
+
### Advanced Usage
|
|
202
|
+
|
|
203
|
+
#### Streaming RPC
|
|
204
|
+
|
|
205
|
+
`defineInvokeHandler` is complemented by `defineStreamInvokeHandler` for long-running operations that need to report progress or intermediate results.
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
import { createContext, defineInvokeEventa, defineStreamInvoke, defineStreamInvokeHandler, toStreamHandler } from '@moeru/eventa'
|
|
209
|
+
|
|
210
|
+
const ctx = createContext()
|
|
211
|
+
const syncEvents = defineInvokeEventa<
|
|
212
|
+
{ type: 'progress' | 'result', value: number },
|
|
213
|
+
{ jobId: string }
|
|
214
|
+
>('rpc:sync')
|
|
215
|
+
|
|
216
|
+
// toStreamHelper converts an async function into an async generator
|
|
217
|
+
// so you can use imperative code instead of a generator function.
|
|
218
|
+
defineStreamInvokeHandler(ctx, syncEvents, toStreamHandler(async ({ payload, emit }) => {
|
|
219
|
+
emit({ type: 'progress', value: 0 })
|
|
220
|
+
for (let i = 1; i <= 5; i++) {
|
|
221
|
+
emit({ type: 'progress', value: i * 20 })
|
|
222
|
+
}
|
|
223
|
+
emit({ type: 'result', value: 100 })
|
|
224
|
+
}))
|
|
225
|
+
|
|
226
|
+
const sync = defineStreamInvoke(ctx, syncEvents)
|
|
227
|
+
for await (const update of sync({ jobId: 'import' })) {
|
|
228
|
+
console.log(update.type, update.value)
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Both generator-style and imperative handlers are exercised in `src/stream.spec.ts:7`.
|
|
233
|
+
|
|
234
|
+
#### Shorthands for `defineInvokeHandler` and `defineInvoke`
|
|
235
|
+
|
|
236
|
+
When you have multiple invoke events to register handlers for, or to create invoke functions for, you can use `defineInvokeHandlers` and `defineInvokes` to do so in bulk.
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
const events = {
|
|
240
|
+
double: defineInvokeEventa<number, number>(),
|
|
241
|
+
append: defineInvokeEventa<string, string>(),
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
defineInvokeHandlers(ctx, events, {
|
|
245
|
+
double: input => input * 2,
|
|
246
|
+
append: input => `${input}!`,
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
const {
|
|
250
|
+
double: invokeDouble,
|
|
251
|
+
append: invokeAppend,
|
|
252
|
+
} = defineInvokes(ctx, events)
|
|
253
|
+
|
|
254
|
+
await invokeDouble(5) // 10
|
|
255
|
+
await invokeAppend('test') // 'test!'
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Development
|
|
259
|
+
|
|
260
|
+
```sh
|
|
261
|
+
pnpm i
|
|
262
|
+
pnpm test
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
> [!NOTE]
|
|
266
|
+
> `pnpm test` runs Vitest interactively. Use `pnpm test:run` for a single pass.
|
|
267
|
+
|
|
268
|
+
## Similar projects
|
|
269
|
+
|
|
270
|
+
- [`birpc`](https://github.com/antfu-collective/birpc): We dislike the way the API designs, we want fully free sharable invok-able functions
|
|
271
|
+
- [`async-call-rpc`](https://github.com/Jack-Works/async-call-rpc): it only works with JSON-RPC, but the DX is similar
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
MIT
|
|
276
|
+
|
|
277
|
+
[npm-version-src]: https://img.shields.io/npm/v/@moeru/eventa?style=flat&colorA=080f12&colorB=1fa669
|
|
278
|
+
[npm-version-href]: https://npmjs.com/package/@moeru/eventa
|
|
279
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/@moeru/eventa?style=flat&colorA=080f12&colorB=1fa669
|
|
280
|
+
[npm-downloads-href]: https://npmjs.com/package/@moeru/eventa
|
|
281
|
+
[bundle-src]: https://img.shields.io/bundlephobia/minzip/@moeru/eventa?style=flat&colorA=080f12&colorB=1fa669&label=minzip
|
|
282
|
+
[bundle-href]: https://bundlephobia.com/result?p=@moeru/eventa
|
|
283
|
+
[license-src]: https://img.shields.io/github/license/moeru-ai/eventa.svg?style=flat&colorA=080f12&colorB=1fa669
|
|
284
|
+
[license-href]: https://github.com/moeru-ai/eventa/blob/main/LICENSE
|
|
285
|
+
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669
|
|
286
|
+
[jsdocs-href]: https://www.jsdocs.io/package/@moeru/eventa
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import "../../eventa-B2HPBK4S.mjs";
|
|
2
|
+
import { EventContext } from "../../context-C_-MRU46.mjs";
|
|
3
|
+
import "../../invoke-LTUFMmHi.mjs";
|
|
4
|
+
import "../../index-BgaYNgPh.mjs";
|
|
5
|
+
import { Payload, errorEvent } from "../../shared-DllZ3RPS.mjs";
|
|
6
|
+
import { BrowserWindow, IpcMain, IpcMainEvent } from "electron";
|
|
7
|
+
|
|
8
|
+
//#region src/adapters/electron/main.d.ts
|
|
9
|
+
declare function createContext(ipcMain: IpcMain, window?: BrowserWindow, options?: {
|
|
10
|
+
onlySameWindow?: boolean;
|
|
11
|
+
messageEventName?: string | false;
|
|
12
|
+
errorEventName?: string | false;
|
|
13
|
+
extraListeners?: Record<string, (_: any, event: Event) => void | Promise<void>>;
|
|
14
|
+
throwIfFailedToSend?: boolean;
|
|
15
|
+
}): {
|
|
16
|
+
context: EventContext<any, {
|
|
17
|
+
raw: {
|
|
18
|
+
ipcMainEvent: IpcMainEvent;
|
|
19
|
+
event: Event | unknown;
|
|
20
|
+
};
|
|
21
|
+
}>;
|
|
22
|
+
dispose: () => void;
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { Payload, createContext, errorEvent };
|
|
26
|
+
//# sourceMappingURL=main.d.mts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { EventaFlowDirection, and, createContext as createContext$1, defineInboundEventa, defineOutboundEventa, matchBy } from "../../context-C35Qku9U.mjs";
|
|
2
|
+
import "../../src-Bb-vxm5k.mjs";
|
|
3
|
+
import { errorEvent, generatePayload, parsePayload } from "../../shared-Da41l-vp.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/adapters/electron/main.ts
|
|
6
|
+
function withRemoval(ipcMain, type, listener) {
|
|
7
|
+
ipcMain.on(type, listener);
|
|
8
|
+
return { remove: () => {
|
|
9
|
+
ipcMain.off(type, listener);
|
|
10
|
+
} };
|
|
11
|
+
}
|
|
12
|
+
function createContext(ipcMain, window, options) {
|
|
13
|
+
const ctx = createContext$1();
|
|
14
|
+
const { messageEventName = "eventa-message", errorEventName = "eventa-error", extraListeners = {}, onlySameWindow = false } = options || {};
|
|
15
|
+
const cleanupRemoval = [];
|
|
16
|
+
ctx.on(and(matchBy("*"), matchBy((e) => e._flowDirection === EventaFlowDirection.Outbound || !e._flowDirection)), (event, options$1) => {
|
|
17
|
+
const eventBody = generatePayload(event.id, {
|
|
18
|
+
...defineOutboundEventa(event.type),
|
|
19
|
+
...event
|
|
20
|
+
});
|
|
21
|
+
if (messageEventName !== false) try {
|
|
22
|
+
if (window != null) {
|
|
23
|
+
if (window.isDestroyed()) return;
|
|
24
|
+
if (onlySameWindow) {
|
|
25
|
+
if (window.webContents.id === options$1?.raw.ipcMainEvent.sender.id) window?.webContents?.send(messageEventName, eventBody);
|
|
26
|
+
} else window?.webContents?.send(messageEventName, eventBody);
|
|
27
|
+
} else {
|
|
28
|
+
if (options$1?.raw.ipcMainEvent.sender.isDestroyed()) return;
|
|
29
|
+
options$1?.raw.ipcMainEvent.sender.send(messageEventName, eventBody);
|
|
30
|
+
}
|
|
31
|
+
} catch (error) {
|
|
32
|
+
if (!(error instanceof Error) || error?.message !== "Object has been destroyed") throw error;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
if (messageEventName) cleanupRemoval.push(withRemoval(ipcMain, messageEventName, (ipcMainEvent, event) => {
|
|
36
|
+
try {
|
|
37
|
+
const { type, payload } = parsePayload(event);
|
|
38
|
+
ctx.emit(defineInboundEventa(type), payload.body, { raw: {
|
|
39
|
+
ipcMainEvent,
|
|
40
|
+
event
|
|
41
|
+
} });
|
|
42
|
+
} catch (error) {
|
|
43
|
+
console.error("Failed to parse IpcMain message:", error);
|
|
44
|
+
ctx.emit(errorEvent, { error }, { raw: {
|
|
45
|
+
ipcMainEvent,
|
|
46
|
+
event
|
|
47
|
+
} });
|
|
48
|
+
}
|
|
49
|
+
}));
|
|
50
|
+
if (errorEventName) cleanupRemoval.push(withRemoval(ipcMain, errorEventName, (ipcMainEvent, error) => {
|
|
51
|
+
ctx.emit(errorEvent, { error }, { raw: {
|
|
52
|
+
ipcMainEvent,
|
|
53
|
+
event: error
|
|
54
|
+
} });
|
|
55
|
+
}));
|
|
56
|
+
for (const [eventName, listener] of Object.entries(extraListeners)) cleanupRemoval.push(withRemoval(ipcMain, eventName, listener));
|
|
57
|
+
return {
|
|
58
|
+
context: ctx,
|
|
59
|
+
dispose: () => {
|
|
60
|
+
cleanupRemoval.forEach((removal) => removal.remove());
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
export { createContext };
|
|
67
|
+
//# sourceMappingURL=main.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.mjs","names":["createBaseContext","cleanupRemoval: Array<{ remove: () => void }>","options"],"sources":["../../../src/adapters/electron/main.ts"],"sourcesContent":["import type { BrowserWindow, IpcMain, IpcMainEvent } from 'electron'\n\nimport type { EventContext } from '../../context'\nimport type { DirectionalEventa, Eventa } from '../../eventa'\n\nimport { createContext as createBaseContext } from '../../context'\nimport { and, defineInboundEventa, defineOutboundEventa, EventaFlowDirection, matchBy } from '../../eventa'\nimport { generatePayload, parsePayload } from './internal'\nimport { errorEvent } from './shared'\n\nfunction withRemoval(ipcMain: IpcMain, type: string, listener: Parameters<IpcMain['on']>[1]) {\n ipcMain.on(type, listener)\n\n return {\n remove: () => {\n ipcMain.off(type, listener)\n },\n }\n}\n\nexport function createContext(ipcMain: IpcMain, window?: BrowserWindow, options?: {\n onlySameWindow?: boolean\n messageEventName?: string | false\n errorEventName?: string | false\n extraListeners?: Record<string, (_, event: Event) => void | Promise<void>>\n throwIfFailedToSend?: boolean\n}) {\n const ctx = createBaseContext() as EventContext<any, { raw: { ipcMainEvent: IpcMainEvent, event: Event | unknown } }>\n\n const {\n messageEventName = 'eventa-message',\n errorEventName = 'eventa-error',\n extraListeners = {},\n onlySameWindow = false,\n } = options || {}\n\n const cleanupRemoval: Array<{ remove: () => void }> = []\n\n ctx.on(and(\n matchBy('*'),\n matchBy((e: DirectionalEventa<any>) => e._flowDirection === EventaFlowDirection.Outbound || !e._flowDirection),\n ), (event, options) => {\n const eventBody = generatePayload(event.id, { ...defineOutboundEventa(event.type), ...event })\n if (messageEventName !== false) {\n try {\n if (window != null) {\n if (window.isDestroyed()) {\n return\n }\n\n if (onlySameWindow) {\n if (window.webContents.id === options?.raw.ipcMainEvent.sender.id) {\n window?.webContents?.send(messageEventName, eventBody)\n }\n }\n else {\n window?.webContents?.send(messageEventName, eventBody)\n }\n }\n else {\n if (options?.raw.ipcMainEvent.sender.isDestroyed()) {\n return\n }\n\n options?.raw.ipcMainEvent.sender.send(messageEventName, eventBody)\n }\n }\n catch (error) {\n // NOTICE: Electron may throw if the window is closed before sending\n // ignore the error if it's about destroyed object\n if (!(error instanceof Error) || error?.message !== 'Object has been destroyed') {\n throw error\n }\n }\n }\n })\n\n if (messageEventName) {\n cleanupRemoval.push(withRemoval(ipcMain, messageEventName, (ipcMainEvent, event: Event | unknown) => {\n try {\n const { type, payload } = parsePayload<Eventa<any>>(event)\n ctx.emit(defineInboundEventa(type), payload.body, { raw: { ipcMainEvent, event } })\n }\n catch (error) {\n console.error('Failed to parse IpcMain message:', error)\n ctx.emit(errorEvent, { error }, { raw: { ipcMainEvent, event } })\n }\n }))\n }\n\n if (errorEventName) {\n cleanupRemoval.push(withRemoval(ipcMain, errorEventName, (ipcMainEvent, error: Event | unknown) => {\n ctx.emit(errorEvent, { error }, { raw: { ipcMainEvent, event: error } })\n }))\n }\n\n for (const [eventName, listener] of Object.entries(extraListeners)) {\n cleanupRemoval.push(withRemoval(ipcMain, eventName, listener))\n }\n\n return {\n context: ctx,\n dispose: () => {\n cleanupRemoval.forEach(removal => removal.remove())\n },\n }\n}\n\nexport type * from './shared'\n"],"mappings":";;;;;AAUA,SAAS,YAAY,SAAkB,MAAc,UAAwC;AAC3F,SAAQ,GAAG,MAAM,SAAS;AAE1B,QAAO,EACL,cAAc;AACZ,UAAQ,IAAI,MAAM,SAAS;IAE9B;;AAGH,SAAgB,cAAc,SAAkB,QAAwB,SAMrE;CACD,MAAM,MAAMA,iBAAmB;CAE/B,MAAM,EACJ,mBAAmB,kBACnB,iBAAiB,gBACjB,iBAAiB,EAAE,EACnB,iBAAiB,UACf,WAAW,EAAE;CAEjB,MAAMC,iBAAgD,EAAE;AAExD,KAAI,GAAG,IACL,QAAQ,IAAI,EACZ,SAAS,MAA8B,EAAE,mBAAmB,oBAAoB,YAAY,CAAC,EAAE,eAAe,CAC/G,GAAG,OAAO,cAAY;EACrB,MAAM,YAAY,gBAAgB,MAAM,IAAI;GAAE,GAAG,qBAAqB,MAAM,KAAK;GAAE,GAAG;GAAO,CAAC;AAC9F,MAAI,qBAAqB,MACvB,KAAI;AACF,OAAI,UAAU,MAAM;AAClB,QAAI,OAAO,aAAa,CACtB;AAGF,QAAI,gBACF;SAAI,OAAO,YAAY,OAAOC,WAAS,IAAI,aAAa,OAAO,GAC7D,SAAQ,aAAa,KAAK,kBAAkB,UAAU;UAIxD,SAAQ,aAAa,KAAK,kBAAkB,UAAU;UAGrD;AACH,QAAIA,WAAS,IAAI,aAAa,OAAO,aAAa,CAChD;AAGF,eAAS,IAAI,aAAa,OAAO,KAAK,kBAAkB,UAAU;;WAG/D,OAAO;AAGZ,OAAI,EAAE,iBAAiB,UAAU,OAAO,YAAY,4BAClD,OAAM;;GAIZ;AAEF,KAAI,iBACF,gBAAe,KAAK,YAAY,SAAS,mBAAmB,cAAc,UAA2B;AACnG,MAAI;GACF,MAAM,EAAE,MAAM,YAAY,aAA0B,MAAM;AAC1D,OAAI,KAAK,oBAAoB,KAAK,EAAE,QAAQ,MAAM,EAAE,KAAK;IAAE;IAAc;IAAO,EAAE,CAAC;WAE9E,OAAO;AACZ,WAAQ,MAAM,oCAAoC,MAAM;AACxD,OAAI,KAAK,YAAY,EAAE,OAAO,EAAE,EAAE,KAAK;IAAE;IAAc;IAAO,EAAE,CAAC;;GAEnE,CAAC;AAGL,KAAI,eACF,gBAAe,KAAK,YAAY,SAAS,iBAAiB,cAAc,UAA2B;AACjG,MAAI,KAAK,YAAY,EAAE,OAAO,EAAE,EAAE,KAAK;GAAE;GAAc,OAAO;GAAO,EAAE,CAAC;GACxE,CAAC;AAGL,MAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,eAAe,CAChE,gBAAe,KAAK,YAAY,SAAS,WAAW,SAAS,CAAC;AAGhE,QAAO;EACL,SAAS;EACT,eAAe;AACb,kBAAe,SAAQ,YAAW,QAAQ,QAAQ,CAAC;;EAEtD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import "../../eventa-B2HPBK4S.mjs";
|
|
2
|
+
import { EventContext } from "../../context-C_-MRU46.mjs";
|
|
3
|
+
import "../../invoke-LTUFMmHi.mjs";
|
|
4
|
+
import "../../index-BgaYNgPh.mjs";
|
|
5
|
+
import { Payload, errorEvent } from "../../shared-DllZ3RPS.mjs";
|
|
6
|
+
import { IpcRenderer, IpcRendererListener } from "@electron-toolkit/preload";
|
|
7
|
+
|
|
8
|
+
//#region src/adapters/electron/renderer.d.ts
|
|
9
|
+
declare function createContext(ipcRenderer: IpcRenderer, options?: {
|
|
10
|
+
messageEventName?: string | false;
|
|
11
|
+
errorEventName?: string | false;
|
|
12
|
+
extraListeners?: Record<string, IpcRendererListener>;
|
|
13
|
+
}): {
|
|
14
|
+
context: EventContext<any, {
|
|
15
|
+
raw: {
|
|
16
|
+
ipcRendererEvent: Electron.IpcRendererEvent;
|
|
17
|
+
event: Event | unknown;
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
20
|
+
dispose: () => void;
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { Payload, createContext, errorEvent };
|
|
24
|
+
//# sourceMappingURL=renderer.d.mts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { EventaFlowDirection, and, createContext as createContext$1, defineInboundEventa, defineOutboundEventa, matchBy } from "../../context-C35Qku9U.mjs";
|
|
2
|
+
import "../../src-Bb-vxm5k.mjs";
|
|
3
|
+
import { errorEvent, generatePayload, parsePayload } from "../../shared-Da41l-vp.mjs";
|
|
4
|
+
|
|
5
|
+
//#region src/adapters/electron/renderer.ts
|
|
6
|
+
function createContext(ipcRenderer, options) {
|
|
7
|
+
const ctx = createContext$1();
|
|
8
|
+
const { messageEventName = "eventa-message", errorEventName = "eventa-error", extraListeners = {} } = options || {};
|
|
9
|
+
const cleanupRemoval = [];
|
|
10
|
+
ctx.on(and(matchBy((e) => e._flowDirection === EventaFlowDirection.Outbound || !e._flowDirection), matchBy("*")), (event) => {
|
|
11
|
+
const eventBody = generatePayload(event.id, {
|
|
12
|
+
...defineOutboundEventa(event.type),
|
|
13
|
+
...event
|
|
14
|
+
});
|
|
15
|
+
if (messageEventName !== false) try {
|
|
16
|
+
ipcRenderer.send(messageEventName, eventBody);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (!(error instanceof Error) || error?.message !== "Object has been destroyed") throw error;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
if (messageEventName) ipcRenderer.on(messageEventName, (ipcRendererEvent, event) => {
|
|
22
|
+
try {
|
|
23
|
+
const { type, payload } = parsePayload(event);
|
|
24
|
+
ctx.emit(defineInboundEventa(type), payload.body, { raw: {
|
|
25
|
+
ipcRendererEvent,
|
|
26
|
+
event
|
|
27
|
+
} });
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error("Failed to parse IpcRenderer message:", error);
|
|
30
|
+
ctx.emit(errorEvent, { error }, { raw: {
|
|
31
|
+
ipcRendererEvent,
|
|
32
|
+
event
|
|
33
|
+
} });
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
if (errorEventName) ipcRenderer.on(errorEventName, (ipcRendererEvent, error) => {
|
|
37
|
+
ctx.emit(errorEvent, { error }, { raw: {
|
|
38
|
+
ipcRendererEvent,
|
|
39
|
+
event: error
|
|
40
|
+
} });
|
|
41
|
+
});
|
|
42
|
+
for (const [eventName, listener] of Object.entries(extraListeners)) ipcRenderer.on(eventName, listener);
|
|
43
|
+
return {
|
|
44
|
+
context: ctx,
|
|
45
|
+
dispose: () => {
|
|
46
|
+
cleanupRemoval.forEach((removal) => removal.remove());
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
//#endregion
|
|
52
|
+
export { createContext };
|
|
53
|
+
//# sourceMappingURL=renderer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"renderer.mjs","names":["createBaseContext","cleanupRemoval: Array<{ remove: () => void }>"],"sources":["../../../src/adapters/electron/renderer.ts"],"sourcesContent":["import type { IpcRenderer, IpcRendererListener } from '@electron-toolkit/preload'\n\nimport type { EventContext } from '../../context'\nimport type { DirectionalEventa, Eventa } from '../../eventa'\n\nimport { createContext as createBaseContext } from '../../context'\nimport { and, defineInboundEventa, defineOutboundEventa, EventaFlowDirection, matchBy } from '../../eventa'\nimport { generatePayload, parsePayload } from './internal'\nimport { errorEvent } from './shared'\n\nexport function createContext(ipcRenderer: IpcRenderer, options?: {\n messageEventName?: string | false\n errorEventName?: string | false\n extraListeners?: Record<string, IpcRendererListener>\n}) {\n const ctx = createBaseContext() as EventContext<any, { raw: { ipcRendererEvent: Electron.IpcRendererEvent, event: Event | unknown } }>\n\n const {\n messageEventName = 'eventa-message',\n errorEventName = 'eventa-error',\n extraListeners = {},\n } = options || {}\n\n const cleanupRemoval: Array<{ remove: () => void }> = []\n\n ctx.on(and(\n matchBy((e: DirectionalEventa<any>) => e._flowDirection === EventaFlowDirection.Outbound || !e._flowDirection),\n matchBy('*'),\n ), (event) => {\n const eventBody = generatePayload(event.id, { ...defineOutboundEventa(event.type), ...event })\n if (messageEventName !== false) {\n try {\n ipcRenderer.send(messageEventName, eventBody)\n }\n catch (error) {\n if (!(error instanceof Error) || error?.message !== 'Object has been destroyed') {\n throw error\n }\n }\n }\n })\n\n if (messageEventName) {\n ipcRenderer.on(messageEventName, (ipcRendererEvent, event) => {\n try {\n const { type, payload } = parsePayload<Eventa<any>>(event)\n ctx.emit(defineInboundEventa(type), payload.body, { raw: { ipcRendererEvent, event } })\n }\n catch (error) {\n console.error('Failed to parse IpcRenderer message:', error)\n ctx.emit(errorEvent, { error }, { raw: { ipcRendererEvent, event } })\n }\n })\n }\n\n if (errorEventName) {\n ipcRenderer.on(errorEventName, (ipcRendererEvent, error) => {\n ctx.emit(errorEvent, { error }, { raw: { ipcRendererEvent, event: error } })\n })\n }\n\n for (const [eventName, listener] of Object.entries(extraListeners)) {\n ipcRenderer.on(eventName, listener)\n }\n\n return {\n context: ctx,\n dispose: () => {\n cleanupRemoval.forEach(removal => removal.remove())\n },\n }\n}\n\nexport type * from './shared'\n"],"mappings":";;;;;AAUA,SAAgB,cAAc,aAA0B,SAIrD;CACD,MAAM,MAAMA,iBAAmB;CAE/B,MAAM,EACJ,mBAAmB,kBACnB,iBAAiB,gBACjB,iBAAiB,EAAE,KACjB,WAAW,EAAE;CAEjB,MAAMC,iBAAgD,EAAE;AAExD,KAAI,GAAG,IACL,SAAS,MAA8B,EAAE,mBAAmB,oBAAoB,YAAY,CAAC,EAAE,eAAe,EAC9G,QAAQ,IAAI,CACb,GAAG,UAAU;EACZ,MAAM,YAAY,gBAAgB,MAAM,IAAI;GAAE,GAAG,qBAAqB,MAAM,KAAK;GAAE,GAAG;GAAO,CAAC;AAC9F,MAAI,qBAAqB,MACvB,KAAI;AACF,eAAY,KAAK,kBAAkB,UAAU;WAExC,OAAO;AACZ,OAAI,EAAE,iBAAiB,UAAU,OAAO,YAAY,4BAClD,OAAM;;GAIZ;AAEF,KAAI,iBACF,aAAY,GAAG,mBAAmB,kBAAkB,UAAU;AAC5D,MAAI;GACF,MAAM,EAAE,MAAM,YAAY,aAA0B,MAAM;AAC1D,OAAI,KAAK,oBAAoB,KAAK,EAAE,QAAQ,MAAM,EAAE,KAAK;IAAE;IAAkB;IAAO,EAAE,CAAC;WAElF,OAAO;AACZ,WAAQ,MAAM,wCAAwC,MAAM;AAC5D,OAAI,KAAK,YAAY,EAAE,OAAO,EAAE,EAAE,KAAK;IAAE;IAAkB;IAAO,EAAE,CAAC;;GAEvE;AAGJ,KAAI,eACF,aAAY,GAAG,iBAAiB,kBAAkB,UAAU;AAC1D,MAAI,KAAK,YAAY,EAAE,OAAO,EAAE,EAAE,KAAK;GAAE;GAAkB,OAAO;GAAO,EAAE,CAAC;GAC5E;AAGJ,MAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,eAAe,CAChE,aAAY,GAAG,WAAW,SAAS;AAGrC,QAAO;EACL,SAAS;EACT,eAAe;AACb,kBAAe,SAAQ,YAAW,QAAQ,QAAQ,CAAC;;EAEtD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { EventTag, EventaType } from "../../eventa-B2HPBK4S.mjs";
|
|
2
|
+
import { EventContext } from "../../context-C_-MRU46.mjs";
|
|
3
|
+
import "../../invoke-LTUFMmHi.mjs";
|
|
4
|
+
import "../../index-BgaYNgPh.mjs";
|
|
5
|
+
|
|
6
|
+
//#region src/adapters/event-emitter/shared.d.ts
|
|
7
|
+
interface Payload<T> {
|
|
8
|
+
id: string;
|
|
9
|
+
type: EventTag<any, any>;
|
|
10
|
+
payload: T;
|
|
11
|
+
}
|
|
12
|
+
declare const errorEvent: {
|
|
13
|
+
body?: {
|
|
14
|
+
error: unknown;
|
|
15
|
+
} | undefined;
|
|
16
|
+
id: string;
|
|
17
|
+
type?: EventaType.Event | undefined;
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/adapters/event-emitter/index.d.ts
|
|
21
|
+
declare function createContext(eventTarget: NodeJS.EventEmitter, options?: {
|
|
22
|
+
messageEventName?: string | false;
|
|
23
|
+
errorEventName?: string | false;
|
|
24
|
+
extraListeners?: Record<string, (event: Event) => void | Promise<void>>;
|
|
25
|
+
}): {
|
|
26
|
+
context: EventContext<any, {
|
|
27
|
+
raw: {
|
|
28
|
+
event: CustomEvent | Event | unknown;
|
|
29
|
+
};
|
|
30
|
+
}>;
|
|
31
|
+
dispose: () => void;
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
export { Payload, createContext, errorEvent };
|
|
35
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { EventaFlowDirection, and, createContext as createContext$1, defineEventa, defineInboundEventa, defineOutboundEventa, matchBy, nanoid } from "../../context-C35Qku9U.mjs";
|
|
2
|
+
import "../../src-Bb-vxm5k.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/adapters/event-emitter/internal.ts
|
|
5
|
+
function generatePayload(type, payload) {
|
|
6
|
+
return {
|
|
7
|
+
id: nanoid(),
|
|
8
|
+
type,
|
|
9
|
+
payload
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function parsePayload(data) {
|
|
13
|
+
return data;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/adapters/event-emitter/shared.ts
|
|
18
|
+
const errorEvent = { ...defineEventa() };
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/adapters/event-emitter/index.ts
|
|
22
|
+
function withRemoval(eventTarget, type, listener) {
|
|
23
|
+
eventTarget.on(type, listener);
|
|
24
|
+
return { remove: () => {
|
|
25
|
+
eventTarget.off(type, listener);
|
|
26
|
+
} };
|
|
27
|
+
}
|
|
28
|
+
function createContext(eventTarget, options) {
|
|
29
|
+
const ctx = createContext$1();
|
|
30
|
+
const { messageEventName = "message", errorEventName = "error", extraListeners = {} } = options || {};
|
|
31
|
+
const cleanupRemoval = [];
|
|
32
|
+
ctx.on(and(matchBy((e) => e._flowDirection === EventaFlowDirection.Outbound || !e._flowDirection), matchBy("*")), (event) => {
|
|
33
|
+
const detail = generatePayload(event.id, {
|
|
34
|
+
...defineOutboundEventa(event.type),
|
|
35
|
+
...event
|
|
36
|
+
});
|
|
37
|
+
eventTarget.emit(event.id, detail);
|
|
38
|
+
});
|
|
39
|
+
if (messageEventName) cleanupRemoval.push(withRemoval(eventTarget, messageEventName, (event) => {
|
|
40
|
+
try {
|
|
41
|
+
const { type, payload } = parsePayload(event.detail);
|
|
42
|
+
ctx.emit(defineInboundEventa(type), payload.body, { raw: { event } });
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error("Failed to parse EventEmitter message:", error);
|
|
45
|
+
ctx.emit(errorEvent, { error }, { raw: { event } });
|
|
46
|
+
}
|
|
47
|
+
}));
|
|
48
|
+
if (errorEventName) cleanupRemoval.push(withRemoval(eventTarget, errorEventName, (error) => {
|
|
49
|
+
ctx.emit(errorEvent, { error }, { raw: { event: error } });
|
|
50
|
+
}));
|
|
51
|
+
for (const [eventName, listener] of Object.entries(extraListeners)) cleanupRemoval.push(withRemoval(eventTarget, eventName, listener));
|
|
52
|
+
return {
|
|
53
|
+
context: ctx,
|
|
54
|
+
dispose: () => {
|
|
55
|
+
cleanupRemoval.forEach((removal) => removal.remove());
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
export { createContext };
|
|
62
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["createBaseContext","cleanupRemoval: Array<{ remove: () => void }>"],"sources":["../../../src/adapters/event-emitter/internal.ts","../../../src/adapters/event-emitter/shared.ts","../../../src/adapters/event-emitter/index.ts"],"sourcesContent":["import type { EventTag } from '../..'\nimport type { Payload as CustomEventDetailDetail } from './shared'\n\nimport { nanoid } from '../..'\n\nexport function generatePayload<T>(type: EventTag<any, any>, payload: T): CustomEventDetailDetail<T> {\n return {\n id: nanoid(),\n type,\n payload,\n }\n}\n\nexport function parsePayload<T>(data: unknown): CustomEventDetailDetail<T> {\n return data as CustomEventDetailDetail<T>\n}\n","import type { EventTag } from '../../eventa'\n\nimport { defineEventa } from '../../eventa'\n\nexport interface Payload<T> {\n id: string\n type: EventTag<any, any>\n payload: T\n}\n\nexport const errorEvent = { ...defineEventa<{ error: unknown }>() }\n","import type { EventContext } from '../../context'\nimport type { DirectionalEventa, Eventa } from '../../eventa'\n\nimport { createContext as createBaseContext } from '../../context'\nimport { and, defineInboundEventa, defineOutboundEventa, EventaFlowDirection, matchBy } from '../../eventa'\nimport { generatePayload, parsePayload } from './internal'\nimport { errorEvent } from './shared'\n\nfunction withRemoval(eventTarget: NodeJS.EventEmitter, type: string, listener: Parameters<NodeJS.EventEmitter['on']>[1]) {\n eventTarget.on(type, listener)\n\n return {\n remove: () => {\n eventTarget.off(type, listener)\n },\n }\n}\n\nexport function createContext(eventTarget: NodeJS.EventEmitter, options?: {\n messageEventName?: string | false\n errorEventName?: string | false\n extraListeners?: Record<string, (event: Event) => void | Promise<void>>\n}) {\n const ctx = createBaseContext() as EventContext<any, { raw: { event: CustomEvent | Event | unknown } }>\n\n const {\n messageEventName = 'message',\n errorEventName = 'error',\n extraListeners = {},\n } = options || {}\n\n const cleanupRemoval: Array<{ remove: () => void }> = []\n\n ctx.on(and(\n matchBy((e: DirectionalEventa<any>) => e._flowDirection === EventaFlowDirection.Outbound || !e._flowDirection),\n matchBy('*'),\n ), (event) => {\n const detail = generatePayload(event.id, { ...defineOutboundEventa(event.type), ...event })\n eventTarget.emit(event.id, detail)\n })\n\n if (messageEventName) {\n cleanupRemoval.push(withRemoval(eventTarget, messageEventName, (event) => {\n try {\n const { type, payload } = parsePayload<Eventa<any>>((event as CustomEvent).detail)\n ctx.emit(defineInboundEventa(type), payload.body, { raw: { event } })\n }\n catch (error) {\n console.error('Failed to parse EventEmitter message:', error)\n ctx.emit(errorEvent, { error }, { raw: { event } })\n }\n }))\n }\n\n if (errorEventName) {\n cleanupRemoval.push(withRemoval(eventTarget, errorEventName, (error) => {\n ctx.emit(errorEvent, { error }, { raw: { event: error } })\n }))\n }\n\n for (const [eventName, listener] of Object.entries(extraListeners)) {\n cleanupRemoval.push(withRemoval(eventTarget, eventName, listener))\n }\n\n return {\n context: ctx,\n dispose: () => {\n cleanupRemoval.forEach(removal => removal.remove())\n },\n }\n}\n\nexport type * from './shared'\n"],"mappings":";;;;AAKA,SAAgB,gBAAmB,MAA0B,SAAwC;AACnG,QAAO;EACL,IAAI,QAAQ;EACZ;EACA;EACD;;AAGH,SAAgB,aAAgB,MAA2C;AACzE,QAAO;;;;;ACJT,MAAa,aAAa,EAAE,GAAG,cAAkC,EAAE;;;;ACFnE,SAAS,YAAY,aAAkC,MAAc,UAAoD;AACvH,aAAY,GAAG,MAAM,SAAS;AAE9B,QAAO,EACL,cAAc;AACZ,cAAY,IAAI,MAAM,SAAS;IAElC;;AAGH,SAAgB,cAAc,aAAkC,SAI7D;CACD,MAAM,MAAMA,iBAAmB;CAE/B,MAAM,EACJ,mBAAmB,WACnB,iBAAiB,SACjB,iBAAiB,EAAE,KACjB,WAAW,EAAE;CAEjB,MAAMC,iBAAgD,EAAE;AAExD,KAAI,GAAG,IACL,SAAS,MAA8B,EAAE,mBAAmB,oBAAoB,YAAY,CAAC,EAAE,eAAe,EAC9G,QAAQ,IAAI,CACb,GAAG,UAAU;EACZ,MAAM,SAAS,gBAAgB,MAAM,IAAI;GAAE,GAAG,qBAAqB,MAAM,KAAK;GAAE,GAAG;GAAO,CAAC;AAC3F,cAAY,KAAK,MAAM,IAAI,OAAO;GAClC;AAEF,KAAI,iBACF,gBAAe,KAAK,YAAY,aAAa,mBAAmB,UAAU;AACxE,MAAI;GACF,MAAM,EAAE,MAAM,YAAY,aAA2B,MAAsB,OAAO;AAClF,OAAI,KAAK,oBAAoB,KAAK,EAAE,QAAQ,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;WAEhE,OAAO;AACZ,WAAQ,MAAM,yCAAyC,MAAM;AAC7D,OAAI,KAAK,YAAY,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;;GAErD,CAAC;AAGL,KAAI,eACF,gBAAe,KAAK,YAAY,aAAa,iBAAiB,UAAU;AACtE,MAAI,KAAK,YAAY,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,OAAO,EAAE,CAAC;GAC1D,CAAC;AAGL,MAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,eAAe,CAChE,gBAAe,KAAK,YAAY,aAAa,WAAW,SAAS,CAAC;AAGpE,QAAO;EACL,SAAS;EACT,eAAe;AACb,kBAAe,SAAQ,YAAW,QAAQ,QAAQ,CAAC;;EAEtD"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { EventTag, EventaType } from "../../eventa-B2HPBK4S.mjs";
|
|
2
|
+
import { EventContext } from "../../context-C_-MRU46.mjs";
|
|
3
|
+
import "../../invoke-LTUFMmHi.mjs";
|
|
4
|
+
import "../../index-BgaYNgPh.mjs";
|
|
5
|
+
|
|
6
|
+
//#region src/adapters/event-target/shared.d.ts
|
|
7
|
+
interface CustomEventDetail<T> {
|
|
8
|
+
id: string;
|
|
9
|
+
type: EventTag<any, any>;
|
|
10
|
+
payload: T;
|
|
11
|
+
}
|
|
12
|
+
declare const workerErrorEvent: {
|
|
13
|
+
body?: {
|
|
14
|
+
error: unknown;
|
|
15
|
+
} | undefined;
|
|
16
|
+
id: string;
|
|
17
|
+
type?: EventaType.Event | undefined;
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/adapters/event-target/index.d.ts
|
|
21
|
+
declare function createContext(eventTarget: EventTarget, options?: {
|
|
22
|
+
messageEventName?: string | false;
|
|
23
|
+
errorEventName?: string | false;
|
|
24
|
+
extraListeners?: Record<string, (event: Event) => void | Promise<void>>;
|
|
25
|
+
}): {
|
|
26
|
+
context: EventContext<any, {
|
|
27
|
+
raw: {
|
|
28
|
+
event: CustomEvent | Event | unknown;
|
|
29
|
+
};
|
|
30
|
+
}>;
|
|
31
|
+
dispose: () => void;
|
|
32
|
+
};
|
|
33
|
+
//#endregion
|
|
34
|
+
export { CustomEventDetail, createContext, workerErrorEvent };
|
|
35
|
+
//# sourceMappingURL=index.d.mts.map
|