@electrojs/runtime 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 +386 -0
- package/dist/client-CD3ueTol.mjs +1 -0
- package/dist/client-CVXQ55OB.d.mts +62 -0
- package/dist/client.d.mts +2 -0
- package/dist/client.mjs +1 -0
- package/dist/index.d.mts +1476 -0
- package/dist/index.mjs +1 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anton Ryuben
|
|
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,386 @@
|
|
|
1
|
+
# @electrojs/runtime
|
|
2
|
+
|
|
3
|
+
The main-process runtime for Electro applications.
|
|
4
|
+
|
|
5
|
+
`@electrojs/runtime` manages the full lifecycle of an Electron main process: scanning decorator metadata, building the module graph, wiring dependency injection, running lifecycle hooks, exposing a typed IPC bridge, dispatching signals, and scheduling background jobs. It is the layer between your application code and the Electron APIs.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @electrojs/runtime
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
> Peer dependency: `@electrojs/common` must be installed alongside.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { Module, Injectable, command, query } from "@electrojs/common";
|
|
23
|
+
import { AppKernel, createConsoleLogger } from "@electrojs/runtime";
|
|
24
|
+
|
|
25
|
+
@Injectable()
|
|
26
|
+
class GreetingService {
|
|
27
|
+
@query()
|
|
28
|
+
hello() {
|
|
29
|
+
return "world";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@command()
|
|
33
|
+
setLocale(locale: string) {
|
|
34
|
+
// ...
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Module({ providers: [GreetingService] })
|
|
39
|
+
class AppModule {}
|
|
40
|
+
|
|
41
|
+
const kernel = AppKernel.create(AppModule, {
|
|
42
|
+
logger: createConsoleLogger(),
|
|
43
|
+
});
|
|
44
|
+
await kernel.start();
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`AppKernel.create` scans `AppModule`, validates the module graph, creates the DI container, instantiates module declarations, and runs startup lifecycle hooks. That single call is enough to bootstrap a working application.
|
|
48
|
+
|
|
49
|
+
Modules, providers, windows, and views also receive `this.logger` through the authoring API, so app-level logs can use the same runtime logger contract and be redirected to a file, Sentry, or any other sink by passing a custom logger into `AppKernel.create(...)`.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Modules
|
|
54
|
+
|
|
55
|
+
Every feature lives inside a module. A module declares what it owns and what it shares:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
@Module({
|
|
59
|
+
imports: [DatabaseModule],
|
|
60
|
+
providers: [UserService],
|
|
61
|
+
views: [UserView],
|
|
62
|
+
windows: [UserWindow],
|
|
63
|
+
exports: [UserService, UserWindow],
|
|
64
|
+
})
|
|
65
|
+
class UserModule {}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- **imports** -- modules this module depends on. Their exported declarations become available for injection.
|
|
69
|
+
- **providers** -- service and infrastructure classes managed by this module.
|
|
70
|
+
- **views** -- renderer view classes owned by this module.
|
|
71
|
+
- **windows** -- window host classes owned by this module.
|
|
72
|
+
- **exports** -- declarations shared with modules that import this one.
|
|
73
|
+
|
|
74
|
+
The framework builds a directed acyclic graph from imports, detects cycles at startup, and instantiates modules in dependency-first (topological) order.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Providers and dependency injection
|
|
79
|
+
|
|
80
|
+
Providers are classes decorated with `@Injectable()`. They hold business logic, repositories, and infrastructure. Views and windows are declared separately in `@Module({ views, windows })`.
|
|
81
|
+
|
|
82
|
+
Inject dependencies with the `inject()` function:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { inject, Injector } from "@electrojs/runtime";
|
|
86
|
+
|
|
87
|
+
@Injectable()
|
|
88
|
+
class AuthService {
|
|
89
|
+
private readonly db = inject(DatabaseService);
|
|
90
|
+
private readonly config = inject(ConfigService);
|
|
91
|
+
|
|
92
|
+
@query()
|
|
93
|
+
getMe() {
|
|
94
|
+
return this.db.findUser(this.config.get("userId"));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`inject()` works inside:
|
|
100
|
+
|
|
101
|
+
- **Property initializers** -- during construction of framework-managed classes
|
|
102
|
+
- **Lifecycle hooks** -- `onInit`, `onReady`, `onShutdown`, `onDispose`
|
|
103
|
+
- **Capability handlers** -- methods decorated with `@command`, `@query`, `@signal`, `@job`
|
|
104
|
+
|
|
105
|
+
Calling it outside these scopes throws a `DIError`.
|
|
106
|
+
|
|
107
|
+
### Scopes
|
|
108
|
+
|
|
109
|
+
- `singleton` (default) -- one instance per module injector
|
|
110
|
+
- `transient` -- a new instance every time the token is resolved
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
@Injectable({ scope: "transient" })
|
|
114
|
+
class RequestContext {}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Lifecycle hooks
|
|
120
|
+
|
|
121
|
+
Modules and providers can implement lifecycle hooks to participate in startup and shutdown:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
@Injectable()
|
|
125
|
+
class DatabaseService {
|
|
126
|
+
async onInit() {
|
|
127
|
+
await this.pool.connect();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async onReady() {
|
|
131
|
+
await this.runMigrations();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async onShutdown() {
|
|
135
|
+
await this.pool.end();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
onDispose() {
|
|
139
|
+
this.logger.flush();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
| Hook | Phase | Purpose |
|
|
145
|
+
| ------------ | -------- | ---------------------------------------------------- |
|
|
146
|
+
| `onInit` | Startup | Set up resources (connections, state) |
|
|
147
|
+
| `onReady` | Startup | Cross-module coordination, everything is initialized |
|
|
148
|
+
| `onShutdown` | Shutdown | Release resources gracefully |
|
|
149
|
+
| `onDispose` | Shutdown | Final cleanup (file handles, timers) |
|
|
150
|
+
|
|
151
|
+
**Ordering:** Startup hooks run per-module in dependency-first order. Within each module, provider hooks run before the module's own hook. Shutdown runs in the reverse order -- module first, then its providers.
|
|
152
|
+
|
|
153
|
+
If a startup hook throws, the framework automatically rolls back already-initialized modules by calling their shutdown hooks.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Bridge (IPC)
|
|
158
|
+
|
|
159
|
+
The bridge connects the main process to renderer views with typed channels. Decorate methods with `@command()` or `@query()` to expose them to the renderer:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
@Injectable()
|
|
163
|
+
class FileService {
|
|
164
|
+
@query()
|
|
165
|
+
async listFiles(directory: string): Promise<string[]> {
|
|
166
|
+
return fs.readdir(directory);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@command()
|
|
170
|
+
async deleteFile(path: string): Promise<void> {
|
|
171
|
+
await fs.unlink(path);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
- **Queries** are read-only operations -- they return data without side effects.
|
|
177
|
+
- **Commands** perform mutations.
|
|
178
|
+
|
|
179
|
+
Channel names are derived as `moduleId:methodName` (e.g. `file:listFiles`). Views declare which channels they can access:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
@View({
|
|
183
|
+
source: "view:main",
|
|
184
|
+
access: ["file:listFiles", "file:deleteFile"],
|
|
185
|
+
})
|
|
186
|
+
class MainView extends ViewProvider {}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Calls from a view to channels not listed in `access` are rejected by the `BridgeAccessGuard`.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Signals
|
|
194
|
+
|
|
195
|
+
Signals provide fire-and-forget cross-module communication. Any provider can publish; any provider can subscribe.
|
|
196
|
+
|
|
197
|
+
### Declarative handlers
|
|
198
|
+
|
|
199
|
+
Use the `@signal()` decorator to subscribe a method at bootstrap time:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
@Injectable()
|
|
203
|
+
class NotificationService {
|
|
204
|
+
@signal({ id: "user-logged-in" })
|
|
205
|
+
onUserLogin(payload: { userId: string }) {
|
|
206
|
+
this.showWelcome(payload.userId);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Programmatic API
|
|
212
|
+
|
|
213
|
+
Use `SignalBus` directly for runtime subscriptions:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
@Injectable()
|
|
217
|
+
class AuditService {
|
|
218
|
+
private readonly bus = inject(SignalBus);
|
|
219
|
+
|
|
220
|
+
onInit() {
|
|
221
|
+
this.bus.subscribe("user-logged-in", (payload) => {
|
|
222
|
+
this.log("login", payload);
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Publishing a signal:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
this.bus.publish("user-logged-in", { userId: "42" });
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Handlers run asynchronously via microtasks. Each handler retains the injection context from the point where it was subscribed, so `inject()` works correctly inside handlers.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Jobs
|
|
239
|
+
|
|
240
|
+
Jobs are background tasks that can run on a cron schedule or be triggered manually. Decorate a method with `@job()`:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
@Injectable()
|
|
244
|
+
class SyncService {
|
|
245
|
+
@job({ cron: "0 * * * *" })
|
|
246
|
+
async syncUsers(context: JobContext) {
|
|
247
|
+
const users = await this.fetchRemoteUsers();
|
|
248
|
+
|
|
249
|
+
for (const [i, user] of users.entries()) {
|
|
250
|
+
if (context.isCancelled) break;
|
|
251
|
+
context.reportProgress(((i + 1) / users.length) * 100);
|
|
252
|
+
await this.upsert(user);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The first argument is always a `JobContext`, which provides:
|
|
259
|
+
|
|
260
|
+
- `isCancelled` -- check whether the job was cancelled
|
|
261
|
+
- `reportProgress(percent)` -- report 0-100 progress
|
|
262
|
+
|
|
263
|
+
Jobs without a `cron` option are manual-only. Use `JobRegistry` to control them at runtime:
|
|
264
|
+
|
|
265
|
+
```ts
|
|
266
|
+
const jobs = inject(JobRegistry);
|
|
267
|
+
await jobs.run("sync:syncUsers"); // trigger manually
|
|
268
|
+
jobs.cancel("sync:syncUsers"); // cancel a running job
|
|
269
|
+
const state = jobs.getState("sync:syncUsers"); // { status, progress, lastRunAt }
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Desktop
|
|
275
|
+
|
|
276
|
+
Windows and views are first-class providers. Extend the base classes `WindowProvider` and `ViewProvider` to manage Electron windows and their content.
|
|
277
|
+
|
|
278
|
+
### Windows
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
@Window({ id: "main", configuration: { width: 1280, height: 800 } })
|
|
282
|
+
class MainWindow extends WindowProvider {
|
|
283
|
+
private readonly mainView = inject(MainView);
|
|
284
|
+
|
|
285
|
+
onReady() {
|
|
286
|
+
this.create();
|
|
287
|
+
this.mount(this.mainView);
|
|
288
|
+
this.show();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
`WindowProvider` gives you: `create()`, `mount(view)`, `show()`, `hide()`, `focus()`, `close()`, `getBounds()`.
|
|
294
|
+
|
|
295
|
+
### Views
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
@View({
|
|
299
|
+
source: "view:main",
|
|
300
|
+
access: ["file:listFiles", "file:deleteFile"],
|
|
301
|
+
signals: ["user-logged-in"],
|
|
302
|
+
})
|
|
303
|
+
class MainView extends ViewProvider {
|
|
304
|
+
async onReady() {
|
|
305
|
+
await this.load();
|
|
306
|
+
this.setBounds({ x: 0, y: 0, width: 1280, height: 800 });
|
|
307
|
+
this.setBackgroundColor("#00000000");
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
`ViewProvider` gives you: `load()`, `setBounds(rect)`, `setBackgroundColor(color)`, `focus()`. Views are created with strict security defaults: `sandbox: true`, `contextIsolation: true`, `nodeIntegration: false`.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Exports
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
// App
|
|
320
|
+
import { AppKernel } from "@electrojs/runtime";
|
|
321
|
+
|
|
322
|
+
// Container
|
|
323
|
+
import { inject, InjectionContext, Injector } from "@electrojs/runtime";
|
|
324
|
+
|
|
325
|
+
// Modules
|
|
326
|
+
import { ModuleRef, ProviderRef, ModuleRegistry } from "@electrojs/runtime";
|
|
327
|
+
import { scanModules, validateAppDefinition } from "@electrojs/runtime";
|
|
328
|
+
|
|
329
|
+
// Signals
|
|
330
|
+
import { SignalBus, SignalContext } from "@electrojs/runtime";
|
|
331
|
+
|
|
332
|
+
// Jobs
|
|
333
|
+
import { JobContext, JobRegistry } from "@electrojs/runtime";
|
|
334
|
+
|
|
335
|
+
// Desktop
|
|
336
|
+
import { WindowProvider, ViewProvider, WindowManager, ViewManager, RendererRegistry, RendererSession } from "@electrojs/runtime";
|
|
337
|
+
|
|
338
|
+
// Bridge
|
|
339
|
+
import { BridgeAccessGuard, BridgeDispatcher, BridgeHandler, serializeBridgeError } from "@electrojs/runtime";
|
|
340
|
+
|
|
341
|
+
// Errors
|
|
342
|
+
import { RuntimeError, BootstrapError, DIError, LifecycleError, BridgeError, SignalError, JobError } from "@electrojs/runtime";
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Type-only imports:
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
import type {
|
|
349
|
+
AppDefinition,
|
|
350
|
+
ModuleDefinition,
|
|
351
|
+
ProviderDefinition,
|
|
352
|
+
ViewDefinition,
|
|
353
|
+
WindowDefinition,
|
|
354
|
+
BridgeMethodDefinition,
|
|
355
|
+
SignalHandlerDefinition,
|
|
356
|
+
JobDefinition,
|
|
357
|
+
KernelState,
|
|
358
|
+
ModuleStatus,
|
|
359
|
+
LifecycleTarget,
|
|
360
|
+
ModuleSnapshot,
|
|
361
|
+
ProviderSnapshot,
|
|
362
|
+
JobStatus,
|
|
363
|
+
JobRuntimeState,
|
|
364
|
+
BridgeRequest,
|
|
365
|
+
BridgeResponse,
|
|
366
|
+
SignalHandler,
|
|
367
|
+
SignalListener,
|
|
368
|
+
ContextualSignalHandler,
|
|
369
|
+
ProviderKind,
|
|
370
|
+
BridgeMethodKind,
|
|
371
|
+
} from "@electrojs/runtime";
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## Package layering
|
|
377
|
+
|
|
378
|
+
```txt
|
|
379
|
+
@electrojs/common ← decorators, metadata, DI primitives
|
|
380
|
+
↑
|
|
381
|
+
@electrojs/runtime ← this package: DI container, lifecycle, bridge, signals, jobs, desktop
|
|
382
|
+
↑
|
|
383
|
+
@electrojs/renderer ← renderer-side bridge client, signal subscriptions
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
`@electrojs/common` defines the shared vocabulary. `@electrojs/runtime` implements the main-process engine. `@electrojs/renderer` provides the renderer-side counterpart.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`electro:bridge`,t=`electro:register`;function n(e){return new Promise(t=>setTimeout(t,e))}function r(r){let{viewId:a,ipcRenderer:o}=r,s=o.invoke(t,{viewId:a});async function c(t,n){let r=crypto.randomUUID();return await o.invoke(e,{callId:r,channel:t,args:n})}async function l(e,t){await s;for(let r=0;r<80;r+=1){let i=await c(e,t);if(i.error?.code!==`ELECTRO_BRIDGE_KERNEL_NOT_READY`||r===79)return i;await n(25)}return c(e,t)}return{async invoke(e,t){let n=await l(e,t);if(n.error){let e=Error(n.error.message);throw n.error.code&&(e.code=n.error.code),e}return n.result},subscribe(e,t){let n=(n,...r)=>{let i=r[0];i.signalId===e&&t(i.payload)};return o.on(i,n),()=>{o.removeListener(i,n)}},once(e,t){let n=!1,r=(a,...s)=>{let c=s[0];c.signalId===e&&!n&&(n=!0,o.removeListener(i,r),t(c.payload))};return o.on(i,r),()=>{n||(n=!0,o.removeListener(i,r))}}}}const i=`electro:signal`,a={bridge:e,register:t,signal:i};export{r as n,a as t};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
//#region src/bridge/client.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Creates the preload-side bridge client that connects renderer processes
|
|
4
|
+
* to the main process via Electron IPC.
|
|
5
|
+
*
|
|
6
|
+
* This function is called inside generated preload scripts. It returns an object
|
|
7
|
+
* matching the {@link RendererPreloadApi} contract expected by `@electrojs/renderer`:
|
|
8
|
+
* - `invoke(channel, payload)` — sends a bridge request to the main process
|
|
9
|
+
* - `subscribe(signalKey, listener)` — listens for signals forwarded from the main process
|
|
10
|
+
* - `once(signalKey, listener)` — like `subscribe`, but auto-removes after the first match
|
|
11
|
+
*
|
|
12
|
+
* On creation the client sends an `electro:register` message so the main process
|
|
13
|
+
* can create a {@link RendererSession} for this renderer's `webContentsId`.
|
|
14
|
+
*
|
|
15
|
+
* @example Generated preload script (produced by `@electrojs/codegen`):
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { contextBridge, ipcRenderer } from "electron";
|
|
18
|
+
* import { createBridgeClient } from "@electrojs/runtime";
|
|
19
|
+
*
|
|
20
|
+
* contextBridge.exposeInMainWorld("__ELECTRO_RENDERER__", createBridgeClient({
|
|
21
|
+
* viewId: "main",
|
|
22
|
+
* ipcRenderer,
|
|
23
|
+
* }));
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Minimal subset of Electron's `IpcRenderer` used by the bridge client.
|
|
28
|
+
* Avoids importing the full Electron types at the package level.
|
|
29
|
+
*/
|
|
30
|
+
interface ElectroIpcRenderer {
|
|
31
|
+
invoke(channel: string, ...args: unknown[]): Promise<unknown>;
|
|
32
|
+
on(channel: string, listener: (event: unknown, ...args: unknown[]) => void): void;
|
|
33
|
+
removeListener(channel: string, listener: (event: unknown, ...args: unknown[]) => void): void;
|
|
34
|
+
}
|
|
35
|
+
/** Configuration accepted by {@link createBridgeClient}. */
|
|
36
|
+
interface BridgeClientConfig {
|
|
37
|
+
/** The view ID this renderer is associated with (from `@View({ id })` or derived from source). */
|
|
38
|
+
readonly viewId: string;
|
|
39
|
+
/** Electron's `ipcRenderer` instance, available in preload scripts. */
|
|
40
|
+
readonly ipcRenderer: ElectroIpcRenderer;
|
|
41
|
+
}
|
|
42
|
+
/** The object exposed on `window.__ELECTRO_RENDERER__` for the renderer package to consume. */
|
|
43
|
+
interface PreloadBridgeApi {
|
|
44
|
+
invoke(channel: string, payload: readonly unknown[]): Promise<unknown>;
|
|
45
|
+
subscribe(signalKey: string, listener: (payload: unknown) => void): () => void;
|
|
46
|
+
once(signalKey: string, listener: (payload: unknown) => void): () => void;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Create a bridge client for use in Electron preload scripts.
|
|
50
|
+
*
|
|
51
|
+
* The returned object is intended to be passed to
|
|
52
|
+
* `contextBridge.exposeInMainWorld("__ELECTRO_RENDERER__", ...)`.
|
|
53
|
+
*/
|
|
54
|
+
declare function createBridgeClient(config: BridgeClientConfig): PreloadBridgeApi;
|
|
55
|
+
/** Well-known IPC channel names used by the Electro bridge protocol. */
|
|
56
|
+
declare const IPC_CHANNELS: {
|
|
57
|
+
readonly bridge: "electro:bridge";
|
|
58
|
+
readonly register: "electro:register";
|
|
59
|
+
readonly signal: "electro:signal";
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
export { createBridgeClient as a, PreloadBridgeApi as i, ElectroIpcRenderer as n, IPC_CHANNELS as r, BridgeClientConfig as t };
|
package/dist/client.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{n as e,t}from"./client-CD3ueTol.mjs";export{t as IPC_CHANNELS,e as createBridgeClient};
|