coakka-v2-connector-electron 2.5.2 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CONSUMING.md CHANGED
@@ -13,3 +13,51 @@ Use the package only from the Electron main process and preload boundary:
13
13
  - renderer: call `window.coakka.intent(...)`
14
14
 
15
15
  Do not import the runtime connector directly from renderer code.
16
+
17
+ Use Electron 42 or later and the exact Node connector dependency declared by
18
+ the package. The Node package includes the native runtime for its supported
19
+ platforms; applications do not separately install private native dependencies.
20
+ Use a released package/platform combination from the compatibility matrix.
21
+
22
+ Keep `contextIsolation: true`, `nodeIntegration: false` and `sandbox: true` on
23
+ the application window. Compile your preload source into one CommonJS file
24
+ with a JavaScript bundler: include `coakka-v2-connector-electron/preload` in the
25
+ bundle and leave only `electron` external. Neither a runtime owner nor native
26
+ library loading belongs in the preload. The renderer receives only the
27
+ intent methods exposed by the context bridge, not general-purpose IPC access.
28
+
29
+ Supply `authorize` when registering main-process IPC handlers. Check the
30
+ window, main frame and trusted document URL using Electron's main-process
31
+ event; return literal `true` only for an accepted sender. Do not trust an
32
+ intent's `source` string as sender identity. Reject untrusted navigation and
33
+ window creation according to the application's policy.
34
+
35
+ Remove registered IPC handlers and await runtime close on every exit path.
36
+ A close does not cancel or join an application handler already executing; cancel
37
+ and join business tasks separately before releasing their application resources.
38
+ Late handler results cannot reply through the closing native owner.
39
+ A failed duplicate handler registration preserves the existing handler.
40
+ Startup snapshots report the runtime's accepted values; omitted CPU and
41
+ connection preferences use `AUTO` and `MULTIPLEXED`. Renderer intent deadlines
42
+ remain bounded (2 seconds when omitted, at most 30 seconds), and each IPC
43
+ batch defaults to the accepted runtime queue capacity capped at 64 values.
44
+ The inherited ask path requires no host worker-pool tuning; one lazy bounded
45
+ main-process scheduler advances Core-owned tickets without allocating one
46
+ worker for every ask.
47
+ An explicit batch maximum may not exceed that capacity or 256. Batches run in
48
+ input order and are not atomic: completed items are not rolled back on failure.
49
+ These intent limits belong to the Electron application boundary; they are
50
+ not a second native capability or configuration table.
51
+
52
+ The qualified Electron 42 package passes its installed main-process,
53
+ sandboxed renderer, File, Stream and shutdown gates on macOS ARM64, Linux
54
+ ARM64/x64 and Windows ARM64/x64. It consumes the exact Node archive selected
55
+ for the same version; registry availability is recorded in the current package ledger.
56
+
57
+ ## Shutdown timing
58
+
59
+ Closing retires native work before releasing its resources. Host scheduling
60
+ and storage affect that latency: macOS qualification observed occasional
61
+ Runtime/File shutdowns taking about 2.4 seconds. That observation is not a
62
+ hard maximum. Release application resources only after close has returned
63
+ or its Promise has settled.
@@ -28,4 +28,4 @@ substantially equivalent infrastructure service.
28
28
  It does not replace either license.
29
29
 
30
30
  Public guidance and stable links are available in the
31
- [CoAkka package licensing guide](https://github.com/phuong-tran/coakka-samples/blob/main/docs/package-licensing.md).
31
+ [CoAkka package licensing guide](https://github.com/phuong-tran/coakka-samples/blob/83c4dd7de7290df064e79fcbbca1f470458142a0/docs/common/package-licensing.md).
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # CoAkka Runtime Connector For Electron
2
2
 
3
3
  <p align="center">
4
- <img src="https://raw.githubusercontent.com/phuong-tran/coakka-samples/main/docs/assets/brand/coakka-logo.png" alt="CoAkka" width="480">
4
+ <img src="https://raw.githubusercontent.com/phuong-tran/coakka-samples/main/docs/common/assets/brand/coakka-logo.png" alt="CoAkka" width="480">
5
5
  </p>
6
6
 
7
7
  **This is the Electron connector in the polyglot, multi-language,
@@ -12,8 +12,8 @@ contract used by Node.js, the JVM, Python, Go, C#, Rust, Swift, and other
12
12
  connector lanes.
13
13
 
14
14
  Kubernetes is supported but not required. Use the public
15
- [Ecosystem Overview](https://github.com/phuong-tran/coakka-samples/blob/main/docs/ecosystem-overview.md)
16
- and [Compatibility Matrix](https://github.com/phuong-tran/coakka-publish/blob/main/docs/compatibility-matrix.md)
15
+ [Ecosystem Overview](https://github.com/phuong-tran/coakka-samples/blob/main/docs/common/ecosystem-overview.md)
16
+ and [Compatibility Matrix](https://github.com/phuong-tran/coakka-publish/blob/main/docs/coakka-runtime/reference/compatibility-matrix.md)
17
17
  to select an exact package and native platform tuple.
18
18
  Start with the [CoAkka Documentation](https://github.com/phuong-tran/coakka-samples/blob/main/docs/README.md)
19
19
  for concepts, integration paths, operations, and runnable samples.
@@ -36,17 +36,15 @@ Use these public repositories to orient first:
36
36
  | `coakka-samples` | Runnable examples and code you can inspect first. | https://github.com/phuong-tran/coakka-samples |
37
37
  | `coakka-publish` | Released packages, native archives, manifests, checksums, compatibility matrix, and release notes. | https://github.com/phuong-tran/coakka-publish |
38
38
 
39
- Run the matching sample:
39
+ Run the [3.0.0 first-run sample](https://github.com/phuong-tran/coakka-samples/tree/2b2f0bd61f23c399323fc2a70436331819c3ef92/coakka-runtime/electron/registry-basic):
40
40
 
41
41
  ```sh
42
42
  git clone https://github.com/phuong-tran/coakka-samples.git
43
- cd coakka-samples
44
- bash run.sh runtime electron basic
43
+ cd coakka-samples/coakka-runtime/electron/registry-basic
44
+ npm install
45
+ npm start
45
46
  ```
46
47
 
47
- No-checkout npm smoke:
48
- https://github.com/phuong-tran/coakka-samples/blob/main/docs/first-npm-smoke.md
49
-
50
48
  Samples docs directory:
51
49
  https://github.com/phuong-tran/coakka-samples/tree/main/docs
52
50
 
@@ -69,16 +67,18 @@ The main-process options can forward the shared `connectionStrategy` and
69
67
  `security` startup specs. Capability/config/security getters and atomic apply
70
68
  results are exposed on `ElectronRuntimeIntentBridge`; renderer/preload code
71
69
  still never receives credential material. See
72
- [Connection Strategies](https://github.com/phuong-tran/coakka-samples/blob/main/docs/connection-strategies.md),
73
- [TLS and mTLS](https://github.com/phuong-tran/coakka-samples/blob/main/docs/tls-and-mtls.md),
74
- and [Troubleshooting](https://github.com/phuong-tran/coakka-samples/blob/main/docs/troubleshooting.md).
70
+ [Connection Strategies](https://github.com/phuong-tran/coakka-samples/blob/main/docs/coakka-runtime/reference/connection-strategies.md),
71
+ [TLS and mTLS](https://github.com/phuong-tran/coakka-samples/blob/main/docs/coakka-runtime/reference/tls-and-mtls.md),
72
+ and [Troubleshooting](https://github.com/phuong-tran/coakka-samples/blob/main/docs/coakka-runtime/operations/troubleshooting.md).
75
73
 
76
74
  ## Main Process
77
75
 
78
76
  ```js
79
- import { ipcMain } from "electron";
77
+ import { BrowserWindow, ipcMain } from "electron";
78
+ import { pathToFileURL } from "node:url";
80
79
  import {
81
80
  ElectronRuntimeIntentBridge,
81
+ localRoute,
82
82
  registerElectronIntentIpcHandler,
83
83
  } from "coakka-v2-connector-electron";
84
84
 
@@ -86,7 +86,7 @@ const target = "samples.electron.intent.echo";
86
86
  const bridge = ElectronRuntimeIntentBridge.start({
87
87
  systemName: "electron-app",
88
88
  nodeId: "electron-app-main",
89
- defaultTarget: target,
89
+ routes: [localRoute(target)],
90
90
  });
91
91
 
92
92
  bridge.registerJsonIntentHandler(target, async (intent) => ({
@@ -94,11 +94,32 @@ bridge.registerJsonIntentHandler(target, async (intent) => ({
94
94
  echo: intent.payload,
95
95
  }));
96
96
 
97
- registerElectronIntentIpcHandler(ipcMain, bridge);
97
+ // app.whenReady() has resolved. These are your application's absolute paths.
98
+ const window = new BrowserWindow({
99
+ webPreferences: {
100
+ preload: bundledPreloadPath,
101
+ contextIsolation: true,
102
+ nodeIntegration: false,
103
+ sandbox: true,
104
+ },
105
+ });
106
+ const trustedUrl = pathToFileURL(rendererHtmlPath).href;
107
+ const unregister = registerElectronIntentIpcHandler(ipcMain, bridge, {
108
+ authorize: (event) => event.sender === window.webContents &&
109
+ event.senderFrame === window.webContents.mainFrame &&
110
+ event.senderFrame.url === trustedUrl,
111
+ });
112
+ await window.loadFile(rendererHtmlPath);
113
+ // At application shutdown: unregister(), then await bridge.close().
98
114
  ```
99
115
 
100
116
  ## Preload
101
117
 
118
+ Bundle this application-owned source into one CommonJS preload file, leaving
119
+ only `electron` external. A sandboxed preload cannot load arbitrary package
120
+ files with `require`; do not disable the sandbox to make package imports work.
121
+ Use that bundled file as `bundledPreloadPath` above.
122
+
102
123
  ```js
103
124
  const { contextBridge, ipcRenderer } = require("electron");
104
125
  const { createCoAkkaPreloadApi } = require("coakka-v2-connector-electron/preload");
@@ -124,17 +145,44 @@ const result = await window.coakka.intent({
124
145
  });
125
146
  ```
126
147
 
127
- ## Smoke
128
-
129
- ```sh
130
- npm run build
131
- bash scripts/smoke-packaged-package.sh
132
- ```
133
-
134
- The packaged smoke installs the generated package into a disposable Electron
135
- app, loads a hidden renderer window, sends one renderer intent through preload
136
- and IPC, and verifies that the main process handles it through the runtime host
137
- exposed by its packaged Node connector dependency.
148
+ ## Lifecycle And Admission
149
+
150
+ For sustained renderer traffic, `intentBatch([...])` amortizes a structured-clone
151
+ IPC round trip. Items execute sequentially through the same runtime ask path
152
+ and preserve order. The default maximum is the accepted queue capacity capped
153
+ at 64 intents; an explicit maximum cannot exceed that capacity or 256. A batch
154
+ is not atomic: earlier items remain completed if a later item fails, and later
155
+ items are not started. Prefer single intents for individual completion latency.
156
+
157
+ The application must authorize every single and batch IPC invocation. Only an
158
+ explicit `true` permits delivery; exceptions and every other value reject the
159
+ invocation. Match the expected window, main frame and trusted document, not
160
+ just a renderer-supplied source name. Remove IPC handlers when their owner
161
+ closes. Do not expose `ipcRenderer` itself through the context bridge.
162
+
163
+ Startup options are submitted to the native runtime; use
164
+ `runtimeStartupConfig()` for accepted configuration rather than reconstructing
165
+ it from input. Omitted policy uses `AUTO` and `MULTIPLEXED`. Main owns handlers
166
+ and runtime lifetime; a refused duplicate registration leaves the accepted
167
+ handler intact. Await `close()` to retire native ownership and registered
168
+ callbacks; a reader waiting for handler capacity is also awakened. Close does
169
+ not cancel or join already-running application handlers. Their late results
170
+ cannot submit native replies after close begins. Cancel/join application tasks
171
+ separately before releasing resources those tasks still use.
172
+ File and Stream Lane owners must also be explicitly closed by their owners.
173
+
174
+ Electron inherits the Node package's lazy bounded ask scheduler in the main
175
+ process. It requires no host worker-pool tuning and does not allocate one worker
176
+ per ask. The Electron wrapper adds no second scheduler or response matcher;
177
+ Core still owns ticket matching, timeout/cancellation and completion notifications.
178
+
179
+ The qualified Electron 42 archive passes installed default-path execution
180
+ on macOS ARM64, Linux ARM64/x64 and Windows ARM64/x64, including sandboxed
181
+ renderer and main-process File/Stream/shutdown gates. Registry availability is recorded in the [current package ledger](https://github.com/phuong-tran/coakka-samples/blob/main/docs/common/current-packages.md).
182
+
183
+ See [Consuming](CONSUMING.md) for package and renderer setup. Installed-package
184
+ tests exercise the minified main and bundled preload with the renderer sandbox
185
+ enabled; they do not substitute a source checkout for the packaged dependency.
138
186
 
139
187
  Package metadata records the exact native generation, release channel, and
140
188
  supported platform tuple. Contact: `gabrielgun1983@gmail.com`.
@@ -143,23 +191,23 @@ supported platform tuple. Contact: `gabrielgun1983@gmail.com`.
143
191
 
144
192
  The Node dependency exposes `FileLane` to trusted Electron main-process code.
145
193
  Do not forward raw file paths, authorization tokens, or lane methods through
146
- preload/renderer IPC. See the shared [file-lane contract](https://github.com/phuong-tran/coakka-samples/blob/main/docs/runtime-file-transfer.md).
194
+ preload/renderer IPC. See the shared [file-lane contract](https://github.com/phuong-tran/coakka-samples/blob/main/docs/coakka-runtime/guides/runtime-file-transfer.md).
147
195
 
148
196
  ## Stream Lane
149
197
 
150
- Version `2.5.2` contains owner-aware File and Stream Lane connector surfaces for Electron main
151
- over exact native generation `2.5.0+4b65d0b2256037bf7fc180bfa6df8c41efc1dd6a`. Keep runtime ownership in the
152
- main process and follow the public
153
- [streaming contract](https://github.com/phuong-tran/coakka-samples/blob/main/docs/runtime-streaming.md).
198
+ The matching Node dependency provides owner-aware File and Stream Lane surfaces
199
+ for trusted Electron main-process code. Keep runtime ownership in the main
200
+ process and follow the public
201
+ [streaming contract](https://github.com/phuong-tran/coakka-samples/blob/main/docs/coakka-runtime/guides/runtime-streaming.md).
154
202
  Electron re-exports Node's `FileReceiveGrant`, `StreamPublishGrant`, and
155
203
  `openOwned`/`prepare*Grant` methods only to main-process code. Follow the
156
- [owner-grant ONE/ALL sample](https://github.com/phuong-tran/coakka-samples/blob/main/docs/runtime-lane-owner-grants.md)
204
+ [owner-grant ONE/ALL sample](https://github.com/phuong-tran/coakka-samples/blob/main/docs/coakka-runtime/reference/runtime-lane-owner-grants.md)
157
205
  and keep serialized grants out of preload and renderer IPC.
158
206
 
159
207
  ## AI-Assisted Integration
160
208
 
161
209
  Before generating Electron integration code, read the public
162
- [AI-assisted integration guide](https://github.com/phuong-tran/coakka-samples/blob/main/docs/ai-assisted-integration.md)
210
+ [AI-assisted integration guide](https://github.com/phuong-tran/coakka-samples/blob/main/docs/coakka-runtime/guides/ai-assisted-integration.md)
163
211
  and the runnable Electron sample. Connection policy and native lane ownership
164
212
  belong to Electron main; renderer code sends validated intent only.
165
213
 
@@ -175,6 +223,6 @@ Those native terms permit ordinary application and SaaS use but require a
175
223
  separate agreement to sell or offer CoAkka itself as managed runtime or
176
224
  infrastructure.
177
225
 
178
- See [CoAkka Package Licensing](https://github.com/phuong-tran/coakka-samples/blob/main/docs/package-licensing.md)
226
+ See [CoAkka Package Licensing](https://github.com/phuong-tran/coakka-samples/blob/main/docs/common/package-licensing.md)
179
227
  for the file-scope map. The package also carries offline `LICENSE`,
180
228
  `NATIVE-LICENSE.md`, `PACKAGE-LICENSE.md`, and `NOTICE` copies.
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
1
- import { type RuntimeCapabilitiesSnapshot, type RuntimeTcpConnectionApplyResult, type RuntimeTcpConnectionConfigSnapshot, type RuntimeTcpConnectionStrategySpec, type RuntimeTcpSecurityApplyResult, type RuntimeTcpSecurityInfoSnapshot, type RuntimeTcpSecuritySpec } from "coakka-v2-connector-node";
2
- export { RuntimeCapability, RuntimeTcpConnectionMode, RuntimeTcpSecurityMode, RuntimeTlsReloadMode, supportsRuntimeCapabilities, } from "coakka-v2-connector-node";
1
+ import { type ConnectorStartSpec, type RuntimeClientStats, type RuntimeCapabilitiesSnapshot, type RuntimeDeliverySnapshot, type RuntimeInfoSnapshot, type RuntimeStartupSnapshot, type RuntimeTcpConnectionApplyResult, type RuntimeTcpConnectionConfigSnapshot, type RuntimeTcpConnectionStrategySpec, type RuntimeTcpSecurityApplyResult, type RuntimeTcpSecurityInfoSnapshot, type RuntimeTcpSecuritySpec } from "coakka-v2-connector-node";
2
+ export { EndpointFlag, RuntimeCapability, RuntimeNetworkMode, RuntimeTcpConnectionMode, RuntimeTcpSecurityMode, RuntimeTlsReloadMode, RouteStrategy, embeddedNetwork, localRoute, networkNode, outboundOnlyNetwork, supportsRuntimeCapabilities, type EndpointSpec, type RouteSpec, type RuntimeNetworkConfig, } from "coakka-v2-connector-node";
3
3
  /**
4
4
  * File and Stream Lanes are available only to trusted Electron main-process
5
5
  * code. They are intentionally absent from preload and renderer intents so
6
6
  * file paths and bearer grants never cross that boundary implicitly.
7
7
  */
8
8
  export { FILE_LANE_DETAIL_MAX_BYTES, FILE_LANE_SHA256_BYTES, LANE_OWNER_GRANTS_FEATURE, FileLane, FileLaneError, FileLaneFlag, FileLaneSecurityMode, FileReceiveGrant, FileTransferDirection, FileTransferResult, FileTransferState, LaneOwnerGrantUnavailableError, STREAM_LANE_DETAIL_MAX_BYTES, STREAM_LANE_MAX_FRAME_BYTES, STREAM_LANE_MAX_WINDOW_BYTES, StreamConsumerDecision, StreamDirection, StreamFrameFlag, StreamLane, StreamLaneError, StreamLaneFlag, StreamLaneSecurityMode, StreamPublishGrant, StreamPressureReason, StreamPressureState, StreamResult, StreamSourceSignal, StreamState, type FileDigest, type FileLaneConfig, type FileLaneSecurityConfig, type FileLaneStats, type FileReceiveSpec, type FileSendSpec, type FileTransferSnapshot, type LaneOwnerConfig, type LaneOwnerEndpoint, type StreamConsumer, type StreamFrameMetadata, type StreamLaneConfig, type StreamLaneSecurityConfig, type StreamLaneStats, type StreamPressureSnapshot, type StreamPublishSpec, type StreamSessionSnapshot, type StreamSource, type StreamSourceFrame, type StreamSubscribeSpec, } from "coakka-v2-connector-node";
9
- export declare const COAKKA_ELECTRON_CONNECTOR_VERSION = "2.5.2";
9
+ export declare const COAKKA_ELECTRON_CONNECTOR_VERSION = "3.0.0";
10
10
  export declare const COAKKA_ELECTRON_INTENT_CHANNEL = "coakka:intent";
11
+ export declare const COAKKA_ELECTRON_INTENT_BATCH_CHANNEL = "coakka:intent-batch";
11
12
  export interface ElectronIntentPayloadIdentity {
12
13
  messageType: string;
13
14
  payloadSchemaVersion?: number;
@@ -29,47 +30,50 @@ export interface ElectronIntentResult {
29
30
  target: string;
30
31
  operation: string;
31
32
  payload: Record<string, unknown>;
32
- runtime: {
33
- generation: number;
34
- delivered: number;
35
- matchedResponses: number;
36
- matchedDeadletters: number;
33
+ payloadIdentity: {
34
+ messageType: string;
35
+ payloadSchemaVersion: number;
36
+ payloadFormat: "json";
37
37
  };
38
38
  }
39
- export interface ElectronRuntimeIntentBridgeOptions {
40
- systemName: string;
41
- nodeId: string;
42
- defaultTarget: string;
43
- routePort?: number;
44
- queueCapacity?: number;
45
- strictNoDrop?: boolean;
39
+ export interface ElectronRuntimeIntentBridgeOptions extends ConnectorStartSpec {
46
40
  runtimeLibPath?: string;
47
- connectionStrategy?: RuntimeTcpConnectionStrategySpec;
48
- security?: RuntimeTcpSecuritySpec;
49
41
  }
50
42
  export type ElectronJsonIntentHandler = (intent: ElectronIntentEnvelope) => Record<string, unknown> | Promise<Record<string, unknown>>;
51
43
  export interface IpcMainLike {
52
- handle(channel: string, listener: (event: unknown, intent: ElectronIntentEnvelope) => Promise<ElectronIntentResult>): void;
44
+ handle(channel: string, listener: (event: unknown, payload: unknown) => Promise<unknown>): void;
53
45
  removeHandler(channel: string): void;
54
46
  }
47
+ export interface ElectronIntentIpcRegistrationOptions {
48
+ channel?: string;
49
+ batchChannel?: string;
50
+ /** Defaults to Core's accepted queue capacity, capped at 64 IPC values. */
51
+ maximumBatchSize?: number;
52
+ /** App-owned sender/frame trust decision; only literal true permits dispatch. */
53
+ authorize(event: unknown): boolean | Promise<boolean>;
54
+ }
55
55
  export declare class ElectronRuntimeIntentBridge {
56
56
  private readonly runtimeHost;
57
- readonly defaultTarget: string;
58
57
  static start(options: ElectronRuntimeIntentBridgeOptions): ElectronRuntimeIntentBridge;
59
58
  private readonly handlers;
60
59
  private closed;
60
+ private closePromise;
61
61
  private constructor();
62
62
  registerJsonIntentHandler(target: string, handler: ElectronJsonIntentHandler): void;
63
63
  handleIntent(intent: ElectronIntentEnvelope): Promise<ElectronIntentResult>;
64
+ runtimeInfo(): RuntimeInfoSnapshot;
65
+ /** Connector-owned counters for trusted main-process diagnostics only. */
66
+ connectorStats(): RuntimeClientStats;
67
+ runtimeStartupConfig(): RuntimeStartupSnapshot & {
68
+ delivery: RuntimeDeliverySnapshot;
69
+ };
64
70
  runtimeCapabilities(): RuntimeCapabilitiesSnapshot;
65
71
  tcpConnectionConfig(): RuntimeTcpConnectionConfigSnapshot;
66
72
  tcpSecurityInfo(): RuntimeTcpSecurityInfoSnapshot;
67
- startupConnectionResult(): RuntimeTcpConnectionApplyResult | null;
68
- startupSecurityResult(): RuntimeTcpSecurityApplyResult | null;
69
73
  applyTcpConnectionStrategy(spec: RuntimeTcpConnectionStrategySpec): RuntimeTcpConnectionApplyResult;
70
74
  applyTcpSecurity(spec: RuntimeTcpSecuritySpec): RuntimeTcpSecurityApplyResult;
71
- close(): void;
75
+ close(): Promise<void>;
72
76
  private createRuntimeHandler;
73
77
  private requireOpen;
74
78
  }
75
- export declare function registerElectronIntentIpcHandler(ipcMain: IpcMainLike, bridge: ElectronRuntimeIntentBridge, channel?: string): () => void;
79
+ export declare function registerElectronIntentIpcHandler(ipcMain: IpcMainLike, bridge: ElectronRuntimeIntentBridge, options: ElectronIntentIpcRegistrationOptions): () => void;
package/dist/index.js CHANGED
@@ -1,191 +1 @@
1
- import { DeliveryHint, EndpointFlag, NodeRuntimeClient, PayloadFormat, PayloadIdentity, RuntimeHost, } from "coakka-v2-connector-node";
2
- export { RuntimeCapability, RuntimeTcpConnectionMode, RuntimeTcpSecurityMode, RuntimeTlsReloadMode, supportsRuntimeCapabilities, } from "coakka-v2-connector-node";
3
- /**
4
- * File and Stream Lanes are available only to trusted Electron main-process
5
- * code. They are intentionally absent from preload and renderer intents so
6
- * file paths and bearer grants never cross that boundary implicitly.
7
- */
8
- export { FILE_LANE_DETAIL_MAX_BYTES, FILE_LANE_SHA256_BYTES, LANE_OWNER_GRANTS_FEATURE, FileLane, FileLaneError, FileLaneFlag, FileLaneSecurityMode, FileReceiveGrant, FileTransferDirection, FileTransferResult, FileTransferState, LaneOwnerGrantUnavailableError, STREAM_LANE_DETAIL_MAX_BYTES, STREAM_LANE_MAX_FRAME_BYTES, STREAM_LANE_MAX_WINDOW_BYTES, StreamConsumerDecision, StreamDirection, StreamFrameFlag, StreamLane, StreamLaneError, StreamLaneFlag, StreamLaneSecurityMode, StreamPublishGrant, StreamPressureReason, StreamPressureState, StreamResult, StreamSourceSignal, StreamState, } from "coakka-v2-connector-node";
9
- export const COAKKA_ELECTRON_CONNECTOR_VERSION = "2.5.2";
10
- export const COAKKA_ELECTRON_INTENT_CHANNEL = "coakka:intent";
11
- const DEFAULT_ROUTE_PORT = 19481;
12
- export class ElectronRuntimeIntentBridge {
13
- runtimeHost;
14
- defaultTarget;
15
- static start(options) {
16
- const target = requireNonBlank(options.defaultTarget, "defaultTarget");
17
- const startSpec = {
18
- systemName: requireNonBlank(options.systemName, "systemName"),
19
- nodeId: requireNonBlank(options.nodeId, "nodeId"),
20
- queueCapacity: options.queueCapacity ?? 64,
21
- strictNoDrop: options.strictNoDrop ?? true,
22
- generation: 1,
23
- connectionStrategy: options.connectionStrategy,
24
- security: options.security,
25
- routes: [
26
- {
27
- target,
28
- endpoints: [
29
- {
30
- host: "127.0.0.1",
31
- port: options.routePort ?? DEFAULT_ROUTE_PORT,
32
- flags: EndpointFlag.LOCAL,
33
- },
34
- ],
35
- },
36
- ],
37
- };
38
- return new ElectronRuntimeIntentBridge(RuntimeHost.start(startSpec, options.runtimeLibPath), target);
39
- }
40
- handlers = new Map();
41
- closed = false;
42
- constructor(runtimeHost, defaultTarget) {
43
- this.runtimeHost = runtimeHost;
44
- this.defaultTarget = defaultTarget;
45
- }
46
- registerJsonIntentHandler(target, handler) {
47
- const normalizedTarget = requireNonBlank(target, "target");
48
- this.handlers.set(normalizedTarget, handler);
49
- this.runtimeHost.registerHandler(normalizedTarget, this.createRuntimeHandler(normalizedTarget), true);
50
- }
51
- async handleIntent(intent) {
52
- this.requireOpen();
53
- const normalized = normalizeIntent(intent);
54
- const identity = toPayloadIdentity(normalized.payloadIdentity);
55
- const response = await this.runtimeHost.askJson(normalized.source, normalized.target, normalized.payload, identity, normalized.timeoutMs ?? 2_000, normalized.operation, DeliveryHint.ROUTER_DEFAULT, normalized.headers ?? {});
56
- const clientStats = this.runtimeHost.clientStats();
57
- const runtimeStats = this.runtimeHost.stats();
58
- return {
59
- intentId: normalized.intentId,
60
- source: normalized.source,
61
- target: normalized.target,
62
- operation: normalized.operation,
63
- payload: response,
64
- runtime: {
65
- generation: runtimeStats.appliedGeneration,
66
- delivered: clientStats.deliveredRequests,
67
- matchedResponses: clientStats.matchedResponses,
68
- matchedDeadletters: clientStats.matchedDeadletters,
69
- },
70
- };
71
- }
72
- runtimeCapabilities() {
73
- return this.runtimeHost.runtimeCapabilities();
74
- }
75
- tcpConnectionConfig() {
76
- return this.runtimeHost.tcpConnectionConfig();
77
- }
78
- tcpSecurityInfo() {
79
- return this.runtimeHost.tcpSecurityInfo();
80
- }
81
- startupConnectionResult() {
82
- return this.runtimeHost.startupConnectionResult();
83
- }
84
- startupSecurityResult() {
85
- return this.runtimeHost.startupSecurityResult();
86
- }
87
- applyTcpConnectionStrategy(spec) {
88
- return this.runtimeHost.applyTcpConnectionStrategy(spec);
89
- }
90
- applyTcpSecurity(spec) {
91
- return this.runtimeHost.applyTcpSecurity(spec);
92
- }
93
- close() {
94
- if (this.closed) {
95
- return;
96
- }
97
- this.closed = true;
98
- this.runtimeHost.close();
99
- }
100
- createRuntimeHandler(target) {
101
- return async (request) => {
102
- const handler = this.handlers.get(target);
103
- if (handler == null) {
104
- throw new Error(`no Electron intent handler registered for target=${target}`);
105
- }
106
- const payload = JSON.parse(Buffer.from(request.payload).toString("utf8"));
107
- const resultPayload = await handler({
108
- intentId: request.message_id,
109
- source: request.source,
110
- target,
111
- operation: "intent",
112
- payload,
113
- payloadIdentity: {
114
- messageType: request.message_type,
115
- payloadSchemaVersion: request.payload_schema_version,
116
- payloadFormat: "json",
117
- },
118
- headers: request.headers,
119
- });
120
- return NodeRuntimeClient.makeJsonReplyFromRequestIdentity(request, target, resultPayload);
121
- };
122
- }
123
- requireOpen() {
124
- if (this.closed) {
125
- throw new Error("Electron runtime intent bridge is closed");
126
- }
127
- }
128
- }
129
- export function registerElectronIntentIpcHandler(ipcMain, bridge, channel = COAKKA_ELECTRON_INTENT_CHANNEL) {
130
- ipcMain.handle(channel, async (_event, intent) => bridge.handleIntent(intent));
131
- return () => ipcMain.removeHandler(channel);
132
- }
133
- function normalizeIntent(intent) {
134
- if (intent == null || typeof intent !== "object") {
135
- throw new Error("intent envelope is required");
136
- }
137
- return {
138
- intentId: requireNonBlank(intent.intentId, "intentId"),
139
- source: requireNonBlank(intent.source, "source"),
140
- target: requireNonBlank(intent.target, "target"),
141
- operation: requireNonBlank(intent.operation, "operation"),
142
- payload: requireJsonObject(intent.payload, "payload"),
143
- payloadIdentity: requirePayloadIdentity(intent.payloadIdentity),
144
- timeoutMs: normalizeTimeout(intent.timeoutMs),
145
- headers: normalizeHeaders(intent.headers),
146
- };
147
- }
148
- function toPayloadIdentity(identity) {
149
- return new PayloadIdentity(identity.messageType, identity.payloadSchemaVersion ?? 1, PayloadFormat.JSON).requireTyped("Electron intent payloadIdentity");
150
- }
151
- function requirePayloadIdentity(identity) {
152
- if (identity == null || typeof identity !== "object") {
153
- throw new Error("payloadIdentity is required");
154
- }
155
- return {
156
- messageType: requireNonBlank(identity.messageType, "payloadIdentity.messageType"),
157
- payloadSchemaVersion: identity.payloadSchemaVersion ?? 1,
158
- payloadFormat: "json",
159
- };
160
- }
161
- function requireJsonObject(value, what) {
162
- if (value == null || typeof value !== "object" || Array.isArray(value)) {
163
- throw new Error(`${what} must be a JSON object`);
164
- }
165
- return value;
166
- }
167
- function normalizeHeaders(headers) {
168
- if (headers == null) {
169
- return {};
170
- }
171
- const normalized = {};
172
- for (const [key, value] of Object.entries(headers)) {
173
- normalized[requireNonBlank(key, "header key")] = String(value);
174
- }
175
- return normalized;
176
- }
177
- function normalizeTimeout(timeoutMs) {
178
- if (timeoutMs == null) {
179
- return undefined;
180
- }
181
- if (!Number.isInteger(timeoutMs) || timeoutMs < 1) {
182
- throw new Error("timeoutMs must be a positive integer");
183
- }
184
- return timeoutMs;
185
- }
186
- function requireNonBlank(value, what) {
187
- if (typeof value !== "string" || value.trim() === "") {
188
- throw new Error(`${what} is required`);
189
- }
190
- return value;
191
- }
1
+ var S=Object.defineProperty;var a=(e,t)=>S(e,"name",{value:t,configurable:!0});import{DeliveryHint as w,NodeRuntimeClient as T,PayloadFormat as y,PayloadIdentity as f,RuntimeHost as I}from"coakka-v2-connector-node";import{EndpointFlag as z,RuntimeCapability as j,RuntimeNetworkMode as k,RuntimeTcpConnectionMode as B,RuntimeTcpSecurityMode as x,RuntimeTlsReloadMode as D,RouteStrategy as J,embeddedNetwork as V,localRoute as $,networkNode as v,outboundOnlyNetwork as K,supportsRuntimeCapabilities as U}from"coakka-v2-connector-node";import{FILE_LANE_DETAIL_MAX_BYTES as Y,FILE_LANE_SHA256_BYTES as G,LANE_OWNER_GRANTS_FEATURE as W,FileLane as Q,FileLaneError as Z,FileLaneFlag as ee,FileLaneSecurityMode as te,FileReceiveGrant as re,FileTransferDirection as ne,FileTransferResult as oe,FileTransferState as ae,LaneOwnerGrantUnavailableError as ie,STREAM_LANE_DETAIL_MAX_BYTES as se,STREAM_LANE_MAX_FRAME_BYTES as ue,STREAM_LANE_MAX_WINDOW_BYTES as le,StreamConsumerDecision as ce,StreamDirection as me,StreamFrameFlag as de,StreamLane as pe,StreamLaneError as he,StreamLaneFlag as ye,StreamLaneSecurityMode as fe,StreamPublishGrant as Ee,StreamPressureReason as Se,StreamPressureState as we,StreamResult as Te,StreamSourceSignal as Ie,StreamState as ge}from"coakka-v2-connector-node";var L="3.0.0",g="coakka:intent",_="coakka:intent-batch",C=2e3,p=3e4,h=class e{static{a(this,"ElectronRuntimeIntentBridge")}runtimeHost;static start(t){let{runtimeLibPath:r,...n}=t;return new e(I.start(n,r))}handlers=new Map;closed=!1;closePromise=null;constructor(t){this.runtimeHost=t}registerJsonIntentHandler(t,r){this.requireOpen();let n=i(t,"target");if(typeof r!="function")throw new TypeError("Electron intent handler must be a function");this.runtimeHost.registerHandler(n,this.createRuntimeHandler(n),!0),this.handlers.set(n,r)}async handleIntent(t){this.requireOpen();let r=N(t),n=O(r.payloadIdentity),o=await this.runtimeHost.askTyped(r.source,r.target,Buffer.from(JSON.stringify(r.payload)),n,r.timeoutMs??C,r.operation,w.ROUTER_DEFAULT,r.headers??{}),s=new f(o.message_type,o.payload_schema_version,o.payload_format).requireTyped("Electron intent response");if(s.payloadFormat!==y.JSON)throw new Error("Electron intent response payloadFormat must be json");let u=m(JSON.parse(Buffer.from(o.payload).toString("utf8")),"response payload");return{intentId:r.intentId,source:r.source,target:r.target,operation:r.operation,payload:u,payloadIdentity:{messageType:s.messageType,payloadSchemaVersion:s.payloadSchemaVersion,payloadFormat:"json"}}}runtimeInfo(){return this.requireOpen(),this.runtimeHost.runtimeInfo()}connectorStats(){return this.requireOpen(),this.runtimeHost.clientStats()}runtimeStartupConfig(){return this.requireOpen(),this.runtimeHost.runtimeStartupConfig()}runtimeCapabilities(){return this.requireOpen(),this.runtimeHost.runtimeCapabilities()}tcpConnectionConfig(){return this.requireOpen(),this.runtimeHost.tcpConnectionConfig()}tcpSecurityInfo(){return this.requireOpen(),this.runtimeHost.tcpSecurityInfo()}applyTcpConnectionStrategy(t){return this.requireOpen(),this.runtimeHost.applyTcpConnectionStrategy(t)}applyTcpSecurity(t){return this.requireOpen(),this.runtimeHost.applyTcpSecurity(t)}close(){return this.closePromise!=null?this.closePromise:(this.closed=!0,this.handlers.clear(),this.closePromise=this.runtimeHost.close(),this.closePromise)}createRuntimeHandler(t){return async r=>{let n=this.handlers.get(t);if(n==null)throw new Error(`no Electron intent handler registered for target=${t}`);let o=m(JSON.parse(Buffer.from(r.payload).toString("utf8")),"request payload"),s=await n({intentId:r.message_id,source:r.source,target:t,operation:i(r.headers.operation,"request operation"),payload:o,payloadIdentity:{messageType:r.message_type,payloadSchemaVersion:r.payload_schema_version,payloadFormat:"json"},headers:r.headers});return T.makeJsonReplyFromRequestIdentity(r,t,m(s,"handler response payload"))}}requireOpen(){if(this.closed)throw new Error("Electron runtime intent bridge is closed")}};function P(e,t,r){let n=i(r.channel??g,"IPC channel"),o=i(r.batchChannel??_,"IPC batch channel");if(n===o)throw new Error("Electron intent and batch IPC channels must be distinct");if(typeof r.authorize!="function")throw new Error("Electron intent IPC registration requires an authorize policy");let s=t.runtimeStartupConfig().queueCapacity,u=r.maximumBatchSize??Math.min(s,64);if(!Number.isInteger(u)||u<1||u>s||u>256)throw new Error(`maximumBatchSize must be an integer in 1..${Math.min(s,256)}`);e.handle(n,async(c,l)=>{if(await r.authorize(c)!==!0)throw new Error("Electron intent IPC sender is not authorized");return t.handleIntent(l)});try{e.handle(o,async(c,l)=>{if(await r.authorize(c)!==!0)throw new Error("Electron intent IPC sender is not authorized");if(!Array.isArray(l)||l.length<1||l.length>u)throw new Error(`Electron intent batch must contain 1..${u} intents`);let d=[];for(let E of l)d.push(await t.handleIntent(E));return d})}catch(c){throw e.removeHandler(n),c}return()=>{e.removeHandler(n),e.removeHandler(o)}}a(P,"registerElectronIntentIpcHandler");function N(e){if(e==null||typeof e!="object")throw new Error("intent envelope is required");return{intentId:i(e.intentId,"intentId"),source:i(e.source,"source"),target:i(e.target,"target"),operation:i(e.operation,"operation"),payload:m(e.payload,"payload"),payloadIdentity:A(e.payloadIdentity),timeoutMs:R(e.timeoutMs),headers:b(e.headers)}}a(N,"normalizeIntent");function O(e){return new f(e.messageType,e.payloadSchemaVersion??1,y.JSON).requireTyped("Electron intent payloadIdentity")}a(O,"toPayloadIdentity");function A(e){if(e==null||typeof e!="object")throw new Error("payloadIdentity is required");if(e.payloadFormat!=null&&e.payloadFormat!=="json")throw new Error("payloadIdentity.payloadFormat must be json");let t=e.payloadSchemaVersion??1;if(!Number.isInteger(t)||t<1||t>4294967295)throw new Error("payloadIdentity.payloadSchemaVersion must be an integer in 1..4294967295");return{messageType:i(e.messageType,"payloadIdentity.messageType"),payloadSchemaVersion:t,payloadFormat:"json"}}a(A,"requirePayloadIdentity");function m(e,t){if(e==null||typeof e!="object"||Array.isArray(e))throw new Error(`${t} must be a JSON object`);return e}a(m,"requireJsonObject");function b(e){if(e==null)return{};let t=Object.create(null);for(let[r,n]of Object.entries(e)){let o=i(r,"header key");if(["method","operation"].includes(o.toLowerCase()))throw new Error(`headers.${o} is reserved by the intent envelope`);if(typeof n!="string")throw new Error(`headers.${o} must be a string`);t[o]=n}return t}a(b,"normalizeHeaders");function R(e){if(e!=null){if(!Number.isInteger(e)||e<1||e>p)throw new Error(`timeoutMs must be an integer in 1..${p}`);return e}}a(R,"normalizeTimeout");function i(e,t){if(typeof e!="string"||e.trim()==="")throw new Error(`${t} is required`);return e}a(i,"requireNonBlank");export{L as COAKKA_ELECTRON_CONNECTOR_VERSION,_ as COAKKA_ELECTRON_INTENT_BATCH_CHANNEL,g as COAKKA_ELECTRON_INTENT_CHANNEL,h as ElectronRuntimeIntentBridge,z as EndpointFlag,Y as FILE_LANE_DETAIL_MAX_BYTES,G as FILE_LANE_SHA256_BYTES,Q as FileLane,Z as FileLaneError,ee as FileLaneFlag,te as FileLaneSecurityMode,re as FileReceiveGrant,ne as FileTransferDirection,oe as FileTransferResult,ae as FileTransferState,W as LANE_OWNER_GRANTS_FEATURE,ie as LaneOwnerGrantUnavailableError,J as RouteStrategy,j as RuntimeCapability,k as RuntimeNetworkMode,B as RuntimeTcpConnectionMode,x as RuntimeTcpSecurityMode,D as RuntimeTlsReloadMode,se as STREAM_LANE_DETAIL_MAX_BYTES,ue as STREAM_LANE_MAX_FRAME_BYTES,le as STREAM_LANE_MAX_WINDOW_BYTES,ce as StreamConsumerDecision,me as StreamDirection,de as StreamFrameFlag,pe as StreamLane,he as StreamLaneError,ye as StreamLaneFlag,fe as StreamLaneSecurityMode,Se as StreamPressureReason,we as StreamPressureState,Ee as StreamPublishGrant,Te as StreamResult,Ie as StreamSourceSignal,ge as StreamState,V as embeddedNetwork,$ as localRoute,v as networkNode,K as outboundOnlyNetwork,P as registerElectronIntentIpcHandler,U as supportsRuntimeCapabilities};
package/dist/preload.cjs CHANGED
@@ -1,16 +1 @@
1
- "use strict";
2
-
3
- const COAKKA_ELECTRON_INTENT_CHANNEL = "coakka:intent";
4
-
5
- function createCoAkkaPreloadApi(ipcRenderer, channel = COAKKA_ELECTRON_INTENT_CHANNEL) {
6
- return {
7
- intent(intent) {
8
- return ipcRenderer.invoke(channel, intent);
9
- },
10
- };
11
- }
12
-
13
- module.exports = {
14
- COAKKA_ELECTRON_INTENT_CHANNEL,
15
- createCoAkkaPreloadApi,
16
- };
1
+ "use strict";var o=Object.defineProperty;var s=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var E=(r,t)=>o(r,"name",{value:t,configurable:!0});var C=(r,t)=>{for(var n in t)o(r,n,{get:t[n],enumerable:!0})},u=(r,t,n,e)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of A(t))!k.call(r,i)&&i!==n&&o(r,i,{get:()=>t[i],enumerable:!(e=s(t,i))||e.enumerable});return r};var T=r=>u(o({},"__esModule",{value:!0}),r);var a={};C(a,{COAKKA_ELECTRON_INTENT_BATCH_CHANNEL:()=>N,COAKKA_ELECTRON_INTENT_CHANNEL:()=>c,createCoAkkaPreloadApi:()=>_});module.exports=T(a);var c="coakka:intent",N="coakka:intent-batch";function _(r,t=c,n=N){if(typeof t!="string"||t.trim()==="")throw new Error("Electron intent IPC channel is required");if(typeof n!="string"||n.trim()==="")throw new Error("Electron intent batch IPC channel is required");if(t===n)throw new Error("Electron intent and batch IPC channels must be distinct");return{intent(e){return r.invoke(t,e)},intentBatch(e){return r.invoke(n,e)}}}E(_,"createCoAkkaPreloadApi");0&&(module.exports={COAKKA_ELECTRON_INTENT_BATCH_CHANNEL,COAKKA_ELECTRON_INTENT_CHANNEL,createCoAkkaPreloadApi});
package/dist/preload.d.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  export declare const COAKKA_ELECTRON_INTENT_CHANNEL = "coakka:intent";
2
+ export declare const COAKKA_ELECTRON_INTENT_BATCH_CHANNEL = "coakka:intent-batch";
2
3
  export interface IpcRendererLike {
3
4
  invoke(channel: string, payload: unknown): Promise<unknown>;
4
5
  }
5
6
  export interface CoAkkaElectronPreloadApi {
6
7
  intent(intent: unknown): Promise<unknown>;
8
+ intentBatch(intents: readonly unknown[]): Promise<unknown>;
7
9
  }
8
- export declare function createCoAkkaPreloadApi(ipcRenderer: IpcRendererLike, channel?: string): CoAkkaElectronPreloadApi;
10
+ export declare function createCoAkkaPreloadApi(ipcRenderer: IpcRendererLike, channel?: string, batchChannel?: string): CoAkkaElectronPreloadApi;
package/dist/preload.js CHANGED
@@ -1,8 +1 @@
1
- export const COAKKA_ELECTRON_INTENT_CHANNEL = "coakka:intent";
2
- export function createCoAkkaPreloadApi(ipcRenderer, channel = COAKKA_ELECTRON_INTENT_CHANNEL) {
3
- return {
4
- intent(intent) {
5
- return ipcRenderer.invoke(channel, intent);
6
- },
7
- };
8
- }
1
+ var o=Object.defineProperty;var i=(r,t)=>o(r,"name",{value:t,configurable:!0});var E="coakka:intent",c="coakka:intent-batch";function s(r,t=E,n=c){if(typeof t!="string"||t.trim()==="")throw new Error("Electron intent IPC channel is required");if(typeof n!="string"||n.trim()==="")throw new Error("Electron intent batch IPC channel is required");if(t===n)throw new Error("Electron intent and batch IPC channels must be distinct");return{intent(e){return r.invoke(t,e)},intentBatch(e){return r.invoke(n,e)}}}i(s,"createCoAkkaPreloadApi");export{c as COAKKA_ELECTRON_INTENT_BATCH_CHANNEL,E as COAKKA_ELECTRON_INTENT_CHANNEL,s as createCoAkkaPreloadApi};
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "coakka-v2-connector-electron",
3
- "version": "2.5.2",
4
- "description": "Electron connector for the polyglot, multi-language, multi-platform CoAkka Runtime ecosystem",
3
+ "version": "3.0.0",
4
+ "description": "Electron connector for CoAkka Runtime, a native-backed polyglot distributed runtime for application-owned work",
5
5
  "license": "SEE LICENSE IN PACKAGE-LICENSE.md",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/phuong-tran/coakka-publish.git"
9
9
  },
10
- "homepage": "https://github.com/phuong-tran/coakka-samples/tree/main/runtime/electron",
10
+ "homepage": "https://github.com/phuong-tran/coakka-samples/tree/2b2f0bd61f23c399323fc2a70436331819c3ef92/coakka-runtime/electron/registry-basic",
11
11
  "bugs": {
12
12
  "url": "https://github.com/phuong-tran/coakka-samples/issues"
13
13
  },
@@ -20,11 +20,13 @@
20
20
  "polyglot",
21
21
  "multi-language",
22
22
  "multi-platform",
23
- "ipc"
23
+ "ipc",
24
+ "distributed-runtime",
25
+ "application-owned-work"
24
26
  ],
25
27
  "type": "module",
26
28
  "engines": {
27
- "node": ">=22"
29
+ "node": ">=22.0.0"
28
30
  },
29
31
  "main": "./dist/index.js",
30
32
  "types": "./dist/index.d.ts",
@@ -48,20 +50,10 @@
48
50
  "NOTICE",
49
51
  "README.md"
50
52
  ],
51
- "scripts": {
52
- "build": "node scripts/build-package.mjs",
53
- "verify": "npm run build",
54
- "pack:release": "npm run build && node scripts/pack-release.mjs",
55
- "smoke:packaged": "bash scripts/smoke-packaged-package.sh"
56
- },
57
53
  "dependencies": {
58
- "coakka-v2-connector-node": "2.5.2"
54
+ "coakka-v2-connector-node": "3.0.0"
59
55
  },
60
56
  "peerDependencies": {
61
57
  "electron": ">=42"
62
- },
63
- "devDependencies": {
64
- "@types/node": "^22.0.0",
65
- "typescript": "^5.8.3"
66
58
  }
67
59
  }