@zappdev/runtime 0.1.0 → 0.6.0-alpha.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/app.ts +28 -81
- package/bridge.ts +23 -0
- package/context-menu.ts +84 -0
- package/dialog.ts +29 -103
- package/dock.ts +76 -0
- package/events.ts +104 -226
- package/index.ts +17 -25
- package/menu.ts +59 -68
- package/notification.ts +199 -0
- package/package.json +7 -5
- package/services.ts +46 -81
- package/sync.ts +60 -44
- package/window.ts +125 -0
- package/worker-globals.ts +47 -0
- package/worker.ts +104 -248
- package/README.md +0 -51
- package/bindings-contract.ts +0 -31
- package/protocol.ts +0 -94
- package/windows.ts +0 -222
package/worker.ts
CHANGED
|
@@ -1,287 +1,143 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Worker — Zapp Workers with postMessage + named channels.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { Worker } from "@zappdev/runtime";
|
|
7
|
+
*
|
|
8
|
+
* const w = new Worker("./worker.ts");
|
|
9
|
+
*
|
|
10
|
+
* // Standard postMessage
|
|
11
|
+
* w.postMessage({ task: "compute" });
|
|
12
|
+
* w.onmessage = (e) => console.log(e.data);
|
|
13
|
+
*
|
|
14
|
+
* // Named channels (typed routing)
|
|
15
|
+
* w.send("compute", { n: 42 });
|
|
16
|
+
* w.receive("result", (data) => console.log(data));
|
|
17
|
+
*
|
|
18
|
+
* w.terminate();
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { getBridge, type ZappBridge } from "./bridge";
|
|
23
|
+
|
|
24
|
+
// Channel message envelope
|
|
25
|
+
const CHANNEL_KEY = "__zc";
|
|
26
|
+
const DATA_KEY = "d";
|
|
27
|
+
|
|
28
|
+
export interface WorkerMessageEvent {
|
|
29
|
+
data: unknown;
|
|
7
30
|
}
|
|
8
31
|
|
|
9
|
-
type ZappBridge = {
|
|
10
|
-
createWorker: (scriptUrl: string, options?: { shared?: boolean }) => string;
|
|
11
|
-
postToWorker: (workerId: string, data: unknown) => void;
|
|
12
|
-
terminateWorker: (workerId: string) => void;
|
|
13
|
-
subscribeWorker: (
|
|
14
|
-
workerId: string,
|
|
15
|
-
onMessage?: (data: unknown) => void,
|
|
16
|
-
onError?: (data: unknown) => void,
|
|
17
|
-
onClose?: (data: unknown) => void,
|
|
18
|
-
) => () => void;
|
|
19
|
-
resolveWorkerScriptURL?: (scriptURL: string | URL) => string;
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
function getBridge(): ZappBridge {
|
|
23
|
-
const b = (globalThis as unknown as Record<symbol, unknown>)[
|
|
24
|
-
Symbol.for("zapp.bridge")
|
|
25
|
-
] as ZappBridge | undefined;
|
|
26
|
-
if (!b?.createWorker) {
|
|
27
|
-
throw new Error(
|
|
28
|
-
"Zapp worker bridge unavailable. " +
|
|
29
|
-
"Make sure the webview bootstrap has loaded before importing @zapp/runtime.",
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
return b;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function rewriteToBundledWorker(url: URL): string {
|
|
36
|
-
const ext = url.pathname.split(".").pop()?.toLowerCase();
|
|
37
|
-
if (ext !== "ts" && ext !== "tsx") return url.toString();
|
|
38
|
-
const toBundledUrl = (mappedPath: string): string => {
|
|
39
|
-
const normalized = mappedPath.startsWith("/") ? mappedPath : `/${mappedPath}`;
|
|
40
|
-
if (url.protocol === "zapp:") {
|
|
41
|
-
return `zapp://app${normalized}`;
|
|
42
|
-
}
|
|
43
|
-
return new URL(normalized, url).toString();
|
|
44
|
-
};
|
|
45
|
-
const manifest = (globalThis as unknown as Record<symbol, unknown>)[
|
|
46
|
-
Symbol.for("zapp.workerManifest")
|
|
47
|
-
] as Record<string, string> | undefined;
|
|
48
|
-
const fileName = url.pathname.split("/").pop() ?? "worker.ts";
|
|
49
|
-
const spec = `./${fileName}`;
|
|
50
|
-
if (manifest) {
|
|
51
|
-
const mapped = manifest[spec] ?? manifest[fileName];
|
|
52
|
-
if (typeof mapped === "string" && mapped.length > 0) {
|
|
53
|
-
return toBundledUrl(mapped);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
const pathParts = url.pathname.split("/");
|
|
57
|
-
const fallbackName = pathParts[pathParts.length - 1] ?? "worker.ts";
|
|
58
|
-
const stem = fallbackName.replace(/\.[^.]+$/, "");
|
|
59
|
-
const bundledPath = `/zapp-workers/${stem}.mjs`;
|
|
60
|
-
return toBundledUrl(bundledPath);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function resolveWorkerScriptURL(
|
|
64
|
-
scriptURL: string | URL,
|
|
65
|
-
importMetaUrl?: string | URL,
|
|
66
|
-
): string {
|
|
67
|
-
const bridge = getBridge();
|
|
68
|
-
if (bridge.resolveWorkerScriptURL) {
|
|
69
|
-
return bridge.resolveWorkerScriptURL(scriptURL);
|
|
70
|
-
}
|
|
71
|
-
if (scriptURL instanceof URL) {
|
|
72
|
-
return rewriteToBundledWorker(scriptURL);
|
|
73
|
-
}
|
|
74
|
-
if (importMetaUrl) {
|
|
75
|
-
return rewriteToBundledWorker(new URL(scriptURL, String(importMetaUrl)));
|
|
76
|
-
}
|
|
77
|
-
return scriptURL;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
type ListenerEntry = { listener: EventListenerOrEventListenerObject; once: boolean };
|
|
81
|
-
|
|
82
|
-
/** A Zapp worker that runs a script in a separate native thread. */
|
|
83
32
|
export class Worker {
|
|
84
33
|
readonly id: string;
|
|
85
|
-
|
|
34
|
+
private _bridge: ZappBridge;
|
|
35
|
+
onmessage: ((event: WorkerMessageEvent) => void) | null = null;
|
|
36
|
+
onerror: ((error: unknown) => void) | null = null;
|
|
86
37
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
onclose: ((event: Event) => void) | null = null;
|
|
38
|
+
/** @internal */
|
|
39
|
+
_messageHandlers: Array<(event: WorkerMessageEvent) => void> = [];
|
|
90
40
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
close: [],
|
|
95
|
-
};
|
|
96
|
-
#pendingErrors: ErrorEvent[] = [];
|
|
97
|
-
#unsubscribe: (() => void) | null = null;
|
|
41
|
+
constructor(scriptUrl: string) {
|
|
42
|
+
this._bridge = getBridge();
|
|
43
|
+
this.id = (this._bridge as any).createWorker(scriptUrl);
|
|
98
44
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const resolved = resolveWorkerScriptURL(scriptURL, options?.importMetaUrl);
|
|
102
|
-
this.scriptURL = resolved;
|
|
103
|
-
this.id = bridge.createWorker(resolved, {
|
|
104
|
-
shared: options?.shared === true,
|
|
105
|
-
});
|
|
106
|
-
this.#unsubscribe = bridge.subscribeWorker(
|
|
107
|
-
this.id,
|
|
108
|
-
(payload) => {
|
|
109
|
-
if (payload && typeof payload === "object" && "__zapp_channel" in (payload as Record<string, unknown>)) {
|
|
110
|
-
const event = new MessageEvent("message", { data: payload });
|
|
111
|
-
this.#emit("message", event);
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
const event = new MessageEvent("message", { data: payload });
|
|
115
|
-
this.onmessage?.(event);
|
|
116
|
-
this.#emit("message", event);
|
|
117
|
-
},
|
|
118
|
-
(payload) => {
|
|
119
|
-
const errorPayload = payload as {
|
|
120
|
-
message?: string;
|
|
121
|
-
filename?: string;
|
|
122
|
-
lineno?: number;
|
|
123
|
-
colno?: number;
|
|
124
|
-
};
|
|
125
|
-
const event = new ErrorEvent("error", {
|
|
126
|
-
message: errorPayload?.message ?? "Worker error",
|
|
127
|
-
filename: errorPayload?.filename ?? "",
|
|
128
|
-
lineno: errorPayload?.lineno ?? 0,
|
|
129
|
-
colno: errorPayload?.colno ?? 0,
|
|
130
|
-
});
|
|
131
|
-
this.#dispatchError(event);
|
|
132
|
-
},
|
|
133
|
-
() => {
|
|
134
|
-
const event = new Event("close");
|
|
135
|
-
this.onclose?.(event);
|
|
136
|
-
this.#emit("close", event);
|
|
137
|
-
},
|
|
138
|
-
);
|
|
45
|
+
// Register this instance for message dispatch
|
|
46
|
+
(this._bridge as any)._workers[this.id] = this;
|
|
139
47
|
}
|
|
140
48
|
|
|
49
|
+
/** Send a raw message to the worker. */
|
|
141
50
|
postMessage(data: unknown): void {
|
|
142
|
-
|
|
51
|
+
(this._bridge as any).postToWorker(this.id, data);
|
|
143
52
|
}
|
|
144
53
|
|
|
54
|
+
/** Send a message on a named channel. */
|
|
145
55
|
send(channel: string, data: unknown): void {
|
|
146
|
-
this.postMessage({
|
|
56
|
+
this.postMessage({ [CHANNEL_KEY]: channel, [DATA_KEY]: data });
|
|
147
57
|
}
|
|
148
58
|
|
|
59
|
+
/** Listen for messages on a named channel. Returns unsubscribe function. */
|
|
149
60
|
receive(channel: string, handler: (data: unknown) => void): () => void {
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
if (
|
|
153
|
-
handler(
|
|
61
|
+
const listener = (event: WorkerMessageEvent) => {
|
|
62
|
+
const msg = event.data as Record<string, unknown>;
|
|
63
|
+
if (msg && msg[CHANNEL_KEY] === channel) {
|
|
64
|
+
handler(msg[DATA_KEY]);
|
|
154
65
|
}
|
|
155
66
|
};
|
|
156
|
-
this.
|
|
157
|
-
return () =>
|
|
67
|
+
this._messageHandlers.push(listener);
|
|
68
|
+
return () => {
|
|
69
|
+
this._messageHandlers = this._messageHandlers.filter(h => h !== listener);
|
|
70
|
+
};
|
|
158
71
|
}
|
|
159
72
|
|
|
73
|
+
/** Terminate the worker. */
|
|
160
74
|
terminate(): void {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
this.#unsubscribe = null;
|
|
164
|
-
getBridge().terminateWorker(this.id);
|
|
165
|
-
queueMicrotask(() => unsubscribe?.());
|
|
75
|
+
(this._bridge as any).terminateWorker(this.id);
|
|
76
|
+
delete (this._bridge as any)._workers[this.id];
|
|
166
77
|
}
|
|
78
|
+
}
|
|
167
79
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (!listener) return;
|
|
174
|
-
if (type !== "message" && type !== "error" && type !== "close") return;
|
|
175
|
-
const list = this.#listeners[type as "message" | "error" | "close"];
|
|
176
|
-
for (const entry of list) {
|
|
177
|
-
if (entry.listener === listener) return;
|
|
178
|
-
}
|
|
179
|
-
const once = typeof options === "object" && options?.once === true;
|
|
180
|
-
const signal = typeof options === "object" ? options.signal : undefined;
|
|
181
|
-
if (signal?.aborted) return;
|
|
182
|
-
list.push({ listener, once });
|
|
183
|
-
if (type === "error") this.#flushPendingErrors();
|
|
184
|
-
if (signal) {
|
|
185
|
-
const onAbort = () => this.removeEventListener(type, listener);
|
|
186
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
187
|
-
}
|
|
188
|
-
}
|
|
80
|
+
/** Port for SharedWorker communication — mirrors Worker API. */
|
|
81
|
+
export class SharedWorkerPort {
|
|
82
|
+
readonly id: string;
|
|
83
|
+
private _bridge: ZappBridge;
|
|
84
|
+
onmessage: ((event: WorkerMessageEvent) => void) | null = null;
|
|
189
85
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
listener: EventListenerOrEventListenerObject | null,
|
|
193
|
-
): void {
|
|
194
|
-
if (!listener) return;
|
|
195
|
-
if (type !== "message" && type !== "error" && type !== "close") return;
|
|
196
|
-
const list = this.#listeners[type as "message" | "error" | "close"];
|
|
197
|
-
for (let i = list.length - 1; i >= 0; i -= 1) {
|
|
198
|
-
if (list[i]?.listener === listener) list.splice(i, 1);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
86
|
+
/** @internal */
|
|
87
|
+
_messageHandlers: Array<(event: WorkerMessageEvent) => void> = [];
|
|
201
88
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
this
|
|
205
|
-
|
|
89
|
+
/** @internal */
|
|
90
|
+
constructor(workerId: string, bridge: ZappBridge) {
|
|
91
|
+
this.id = workerId;
|
|
92
|
+
this._bridge = bridge;
|
|
206
93
|
}
|
|
207
94
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
if (typeof entry.listener === "function") {
|
|
212
|
-
(entry.listener as (e: Event) => void)(event);
|
|
213
|
-
} else {
|
|
214
|
-
entry.listener.handleEvent(event);
|
|
215
|
-
}
|
|
216
|
-
if (entry.once) this.removeEventListener(type, entry.listener);
|
|
217
|
-
}
|
|
95
|
+
/** Send a raw message to the shared worker. */
|
|
96
|
+
postMessage(data: unknown): void {
|
|
97
|
+
(this._bridge as any).postToWorker(this.id, data);
|
|
218
98
|
}
|
|
219
99
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
this.onerror?.(event);
|
|
226
|
-
this.#emit("error", event);
|
|
100
|
+
/** Send a message on a named channel. */
|
|
101
|
+
send(channel: string, data: unknown): void {
|
|
102
|
+
this.postMessage({ [CHANNEL_KEY]: channel, [DATA_KEY]: data });
|
|
227
103
|
}
|
|
228
104
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
105
|
+
/** Listen for messages on a named channel. Returns unsubscribe function. */
|
|
106
|
+
receive(channel: string, handler: (data: unknown) => void): () => void {
|
|
107
|
+
const listener = (event: WorkerMessageEvent) => {
|
|
108
|
+
const msg = event.data as Record<string, unknown>;
|
|
109
|
+
if (msg && msg[CHANNEL_KEY] === channel) {
|
|
110
|
+
handler(msg[DATA_KEY]);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
this._messageHandlers.push(listener);
|
|
114
|
+
return () => {
|
|
115
|
+
this._messageHandlers = this._messageHandlers.filter(h => h !== listener);
|
|
116
|
+
};
|
|
237
117
|
}
|
|
238
118
|
}
|
|
239
119
|
|
|
240
|
-
/**
|
|
120
|
+
/**
|
|
121
|
+
* SharedWorker — persists as long as any window holds a reference.
|
|
122
|
+
* Multiple windows creating SharedWorker with the same URL connect to the same native worker.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* const sw = new SharedWorker("./shared-worker.ts");
|
|
127
|
+
* sw.port.postMessage({ task: "sync" });
|
|
128
|
+
* sw.port.onmessage = (e) => console.log(e.data);
|
|
129
|
+
* sw.port.send("channel", data);
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
241
132
|
export class SharedWorker {
|
|
242
|
-
readonly port:
|
|
243
|
-
postMessage: (data: unknown) => void;
|
|
244
|
-
onmessage: ((event: MessageEvent) => void) | null;
|
|
245
|
-
start: () => void;
|
|
246
|
-
close: () => void;
|
|
247
|
-
addEventListener: (type: string, listener: EventListenerOrEventListenerObject) => void;
|
|
248
|
-
removeEventListener: (type: string, listener: EventListenerOrEventListenerObject) => void;
|
|
249
|
-
};
|
|
250
|
-
onerror: ((event: ErrorEvent) => void) | null = null;
|
|
251
|
-
|
|
252
|
-
readonly #inner: Worker;
|
|
253
|
-
|
|
254
|
-
constructor(scriptURL: string | URL, options?: WorkerOptions) {
|
|
255
|
-
this.#inner = new Worker(scriptURL, {
|
|
256
|
-
...options,
|
|
257
|
-
shared: true,
|
|
258
|
-
});
|
|
133
|
+
readonly port: SharedWorkerPort;
|
|
259
134
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
close: () => this.#inner.terminate(),
|
|
265
|
-
addEventListener: (type: string, listener: EventListenerOrEventListenerObject) =>
|
|
266
|
-
this.#inner.addEventListener(type, listener),
|
|
267
|
-
removeEventListener: (type: string, listener: EventListenerOrEventListenerObject) =>
|
|
268
|
-
this.#inner.removeEventListener(type, listener),
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
this.#inner.addEventListener("message", ((e: MessageEvent) => {
|
|
272
|
-
this.port.onmessage?.(e);
|
|
273
|
-
}) as EventListener);
|
|
274
|
-
|
|
275
|
-
this.#inner.addEventListener("error", ((e: ErrorEvent) => {
|
|
276
|
-
this.onerror?.(e);
|
|
277
|
-
}) as EventListener);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
send(channel: string, data: unknown): void {
|
|
281
|
-
this.#inner.send(channel, data);
|
|
282
|
-
}
|
|
135
|
+
constructor(scriptUrl: string) {
|
|
136
|
+
const bridge = getBridge();
|
|
137
|
+
const workerId = (bridge as any).createSharedWorker(scriptUrl);
|
|
138
|
+
this.port = new SharedWorkerPort(workerId, bridge);
|
|
283
139
|
|
|
284
|
-
|
|
285
|
-
|
|
140
|
+
// Register port for message dispatch
|
|
141
|
+
(bridge as any)._workers[workerId] = this.port;
|
|
286
142
|
}
|
|
287
143
|
}
|
package/README.md
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
# @zapp/runtime
|
|
2
|
-
|
|
3
|
-
Frontend runtime API for Zapp desktop apps. Provides type-safe access to native window management, events, dialogs, menus, services, and more from your web frontend.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
|
-
```sh
|
|
8
|
-
bun add @zapp/runtime
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
```sh
|
|
12
|
-
npm install @zapp/runtime
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Usage
|
|
16
|
-
|
|
17
|
-
```ts
|
|
18
|
-
import { App, Window, Events, Dialog, Menu } from "@zapp/runtime";
|
|
19
|
-
|
|
20
|
-
// Listen for window events
|
|
21
|
-
Events.on("window:resize", (payload) => {
|
|
22
|
-
console.log("Window resized:", payload.width, payload.height);
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
// Open a file dialog
|
|
26
|
-
const result = await Dialog.openFile({
|
|
27
|
-
title: "Select a file",
|
|
28
|
-
filters: [{ name: "Images", extensions: ["png", "jpg"] }],
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// Create a new window
|
|
32
|
-
await Window.open({ title: "My Window", width: 800, height: 600 });
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Features
|
|
36
|
-
|
|
37
|
-
- **Window management** -- create, resize, move, and close native windows
|
|
38
|
-
- **Event system** -- subscribe to window and app lifecycle events
|
|
39
|
-
- **Dialogs** -- native open/save file dialogs and message boxes
|
|
40
|
-
- **Menus** -- build native application and context menus
|
|
41
|
-
- **Services** -- communicate with backend services
|
|
42
|
-
- **Workers** -- spawn Web Workers and Shared Workers with native integration
|
|
43
|
-
- **Sync primitives** -- cross-thread synchronization utilities
|
|
44
|
-
|
|
45
|
-
## Docs
|
|
46
|
-
|
|
47
|
-
See the full documentation in [`../../docs/`](../../docs/).
|
|
48
|
-
|
|
49
|
-
## License
|
|
50
|
-
|
|
51
|
-
MIT
|
package/bindings-contract.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/** Current version of the bindings schema format. */
|
|
2
|
-
export const ZAPP_BINDINGS_SCHEMA_VERSION = 1;
|
|
3
|
-
|
|
4
|
-
/** Describes a single method exposed by a service binding. */
|
|
5
|
-
export type ZappServiceBindingMethod = {
|
|
6
|
-
name: string;
|
|
7
|
-
requestType?: string;
|
|
8
|
-
responseType?: string;
|
|
9
|
-
capability?: string;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
/** Describes a service and the methods it exposes. */
|
|
13
|
-
export type ZappServiceBinding = {
|
|
14
|
-
name: string;
|
|
15
|
-
namespace?: string;
|
|
16
|
-
methods: ZappServiceBindingMethod[];
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/** Top-level manifest listing all available service bindings. */
|
|
20
|
-
export type ZappBindingsManifest = {
|
|
21
|
-
v: typeof ZAPP_BINDINGS_SCHEMA_VERSION;
|
|
22
|
-
generatedAt: string;
|
|
23
|
-
services: ZappServiceBinding[];
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/** Create an empty bindings manifest with the current schema version. */
|
|
27
|
-
export const emptyBindingsManifest = (): ZappBindingsManifest => ({
|
|
28
|
-
v: ZAPP_BINDINGS_SCHEMA_VERSION,
|
|
29
|
-
generatedAt: new Date().toISOString(),
|
|
30
|
-
services: [],
|
|
31
|
-
});
|
package/protocol.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/** Current version of the worker wire protocol. */
|
|
2
|
-
export const ZAPP_WORKER_PROTOCOL_VERSION = 1;
|
|
3
|
-
/** Current version of the service invocation protocol. */
|
|
4
|
-
export const ZAPP_SERVICE_PROTOCOL_VERSION = 1;
|
|
5
|
-
|
|
6
|
-
/** Discriminator strings for worker control messages. */
|
|
7
|
-
export type ZappWorkerControlType =
|
|
8
|
-
| "zapp:worker:init"
|
|
9
|
-
| "zapp:worker:post"
|
|
10
|
-
| "zapp:worker:message"
|
|
11
|
-
| "zapp:worker:error"
|
|
12
|
-
| "zapp:worker:terminate";
|
|
13
|
-
|
|
14
|
-
/** Envelope wrapping all worker control messages on the wire. */
|
|
15
|
-
export interface ZappWorkerEnvelope<T = unknown> {
|
|
16
|
-
v: typeof ZAPP_WORKER_PROTOCOL_VERSION;
|
|
17
|
-
t: ZappWorkerControlType;
|
|
18
|
-
workerId: string;
|
|
19
|
-
payload?: T;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Payload for the worker init control message. */
|
|
23
|
-
export interface ZappWorkerInitPayload {
|
|
24
|
-
scriptUrl: string;
|
|
25
|
-
shared?: boolean;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Payload for posting data to a worker. */
|
|
29
|
-
export interface ZappWorkerPostPayload {
|
|
30
|
-
data: unknown;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** Payload for a message received from a worker. */
|
|
34
|
-
export interface ZappWorkerMessagePayload {
|
|
35
|
-
data: unknown;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Payload describing a worker error. */
|
|
39
|
-
export interface ZappWorkerErrorPayload {
|
|
40
|
-
message: string;
|
|
41
|
-
stack?: string;
|
|
42
|
-
filename?: string;
|
|
43
|
-
lineno?: number;
|
|
44
|
-
colno?: number;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** Host-side bridge interface for managing workers from the native layer. */
|
|
48
|
-
export type ZappWorkerHostBridge = {
|
|
49
|
-
createWorker(scriptUrl: string, options?: { shared?: boolean }): string;
|
|
50
|
-
postToWorker(workerId: string, data: unknown): void;
|
|
51
|
-
terminateWorker(workerId: string): void;
|
|
52
|
-
subscribe(
|
|
53
|
-
workerId: string,
|
|
54
|
-
onMessage: (data: unknown) => void,
|
|
55
|
-
onError: (error: ZappWorkerErrorPayload) => void,
|
|
56
|
-
): () => void;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
/** Standard error codes returned by service invocations. */
|
|
60
|
-
export type ZappServiceErrorCode =
|
|
61
|
-
| "BAD_REQUEST"
|
|
62
|
-
| "INVALID_METHOD"
|
|
63
|
-
| "UNAUTHORIZED"
|
|
64
|
-
| "NOT_FOUND"
|
|
65
|
-
| "INTERNAL_ERROR"
|
|
66
|
-
| "TIMEOUT";
|
|
67
|
-
|
|
68
|
-
/** A request to invoke a named service method. */
|
|
69
|
-
export interface ZappServiceInvokeRequest {
|
|
70
|
-
v: typeof ZAPP_SERVICE_PROTOCOL_VERSION;
|
|
71
|
-
id: string;
|
|
72
|
-
method: string;
|
|
73
|
-
args: unknown;
|
|
74
|
-
meta: {
|
|
75
|
-
sourceCtxId: string;
|
|
76
|
-
capability?: string;
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/** Structured error returned from a failed service invocation. */
|
|
81
|
-
export interface ZappServiceInvokeError {
|
|
82
|
-
code: ZappServiceErrorCode;
|
|
83
|
-
message: string;
|
|
84
|
-
details?: unknown;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/** Response envelope from a service invocation. */
|
|
88
|
-
export interface ZappServiceInvokeResponse {
|
|
89
|
-
v: typeof ZAPP_SERVICE_PROTOCOL_VERSION;
|
|
90
|
-
id: string;
|
|
91
|
-
ok: boolean;
|
|
92
|
-
result?: unknown;
|
|
93
|
-
error?: ZappServiceInvokeError;
|
|
94
|
-
}
|