tempo-plugin-sdk 1.0.0 → 1.1.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/README.md +13 -0
- package/dist/dev-assets/bridge-client.js +415 -0
- package/dist/dev-assets/structured-clone.js +297 -0
- package/dist/globals.d.ts +16 -1
- package/dist/globals.d.ts.map +1 -1
- package/dist/globals.js +31 -1
- package/dist/globals.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +2 -5
- package/dist/runtime.js.map +1 -1
- package/dist/ui.d.ts.map +1 -1
- package/dist/ui.js +2 -3
- package/dist/ui.js.map +1 -1
- package/dist/vite.d.ts +24 -0
- package/dist/vite.d.ts.map +1 -0
- package/dist/vite.js +47 -0
- package/dist/vite.js.map +1 -0
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -38,3 +38,16 @@ type PluginIpc = {
|
|
|
38
38
|
Pass it to `connect<PluginIpc>()` and `defineRuntime<PluginIpc>()` to type channel names, arguments, and invoke results on both sides. Omitting the contract keeps dynamic string channels available.
|
|
39
39
|
|
|
40
40
|
The SDK uses APIs injected by Tempo. It does not request or bypass plugin permissions.
|
|
41
|
+
|
|
42
|
+
Vite projects can use the SDK-owned development transport and manifest helper:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { defineConfig } from "vite";
|
|
46
|
+
import { tempoPlugin } from "tempo-plugin-sdk/vite";
|
|
47
|
+
|
|
48
|
+
export default defineConfig({
|
|
49
|
+
plugins: [tempoPlugin()],
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`tempoPlugin()` injects the SDK-owned transport during development and copies `manifest.json` during builds. Production plugin pages receive the versioned transport from Tempo, so generated projects do not need `.tempo` bridge files.
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/** Host-injected globals for Tempo plugin UI pages. */
|
|
2
|
+
(() => {
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
if (globalThis.__tempoPluginUi || window.tempo || window.ipcRenderer) return;
|
|
6
|
+
|
|
7
|
+
const sca = globalThis.__tempoSca;
|
|
8
|
+
const pending = new Map();
|
|
9
|
+
const runtimeListeners = new Map();
|
|
10
|
+
const platformListeners = new Map();
|
|
11
|
+
const tempoEventListeners = new Map();
|
|
12
|
+
const contextWaiters = [];
|
|
13
|
+
const ORIGINAL_LISTENER = Symbol("tempo.originalListener");
|
|
14
|
+
const SPECIALIZED_PLATFORM_EVENTS = new Set(["settings.changed", "theme.changed"]);
|
|
15
|
+
let requestSeq = 0;
|
|
16
|
+
let context = null;
|
|
17
|
+
|
|
18
|
+
const HOST_METHODS = new Set([
|
|
19
|
+
"mainPanel.hide",
|
|
20
|
+
"mainPanel.back",
|
|
21
|
+
"mainPanel.setSize",
|
|
22
|
+
"window.setRect",
|
|
23
|
+
"window.close",
|
|
24
|
+
"theme.get",
|
|
25
|
+
"theme.onChange",
|
|
26
|
+
"notify.show",
|
|
27
|
+
"session.push",
|
|
28
|
+
"subscription.release",
|
|
29
|
+
"storage.plugin.get",
|
|
30
|
+
"storage.plugin.set",
|
|
31
|
+
"storage.plugin.delete",
|
|
32
|
+
"storage.plugin.list",
|
|
33
|
+
"files.readText",
|
|
34
|
+
"files.writeText",
|
|
35
|
+
"files.readBytes",
|
|
36
|
+
"files.writeBytes",
|
|
37
|
+
"files.list",
|
|
38
|
+
"files.stat",
|
|
39
|
+
"files.mkdir",
|
|
40
|
+
"files.remove",
|
|
41
|
+
"files.rename",
|
|
42
|
+
"app.open",
|
|
43
|
+
"external.open",
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
function nextId() {
|
|
47
|
+
requestSeq += 1;
|
|
48
|
+
return `plugin-${requestSeq}-${Date.now()}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function call(method, params) {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const id = nextId();
|
|
54
|
+
pending.set(id, { resolve, reject });
|
|
55
|
+
window.parent.postMessage(
|
|
56
|
+
{ type: "tempo-plugin-rpc", id, method, params: params ?? {} },
|
|
57
|
+
"*",
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function eventName(event) {
|
|
63
|
+
if (typeof event !== "string" || !event.trim()) {
|
|
64
|
+
throw new TypeError("event must be a non-empty string");
|
|
65
|
+
}
|
|
66
|
+
return event.trim();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function assertEventHandler(handler) {
|
|
70
|
+
if (typeof handler !== "function") {
|
|
71
|
+
throw new TypeError("event handler must be a function");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function removeListenerEntry(map, name, handler) {
|
|
76
|
+
const listeners = map.get(name);
|
|
77
|
+
if (!listeners) return false;
|
|
78
|
+
const removed = listeners.delete(handler);
|
|
79
|
+
if (listeners.size === 0) map.delete(name);
|
|
80
|
+
return removed;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function unlisten(map, event, handler) {
|
|
84
|
+
const name = eventName(event);
|
|
85
|
+
assertEventHandler(handler);
|
|
86
|
+
const listeners = map.get(name);
|
|
87
|
+
if (!listeners) return false;
|
|
88
|
+
let removed = false;
|
|
89
|
+
for (const listener of listeners) {
|
|
90
|
+
if (listener === handler || listener[ORIGINAL_LISTENER] === handler) {
|
|
91
|
+
listeners.delete(listener);
|
|
92
|
+
removed = true;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (listeners.size === 0) map.delete(name);
|
|
96
|
+
return removed;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function listen(map, event, handler) {
|
|
100
|
+
const name = eventName(event);
|
|
101
|
+
assertEventHandler(handler);
|
|
102
|
+
if (!map.has(name)) map.set(name, new Set());
|
|
103
|
+
map.get(name).add(handler);
|
|
104
|
+
return () => {
|
|
105
|
+
removeListenerEntry(map, name, handler);
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function listenOnce(map, event, handler) {
|
|
110
|
+
const name = eventName(event);
|
|
111
|
+
assertEventHandler(handler);
|
|
112
|
+
const wrapped = (payload) => {
|
|
113
|
+
removeListenerEntry(map, name, wrapped);
|
|
114
|
+
handler(payload);
|
|
115
|
+
};
|
|
116
|
+
wrapped[ORIGINAL_LISTENER] = handler;
|
|
117
|
+
return listen(map, name, wrapped);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function clearListeners(map, event) {
|
|
121
|
+
if (event === undefined) {
|
|
122
|
+
map.clear();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
map.delete(eventName(event));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function countListeners(map, event) {
|
|
129
|
+
return map.get(eventName(event))?.size ?? 0;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function emit(map, event, payload) {
|
|
133
|
+
for (const handler of [...(map.get(event) ?? [])]) {
|
|
134
|
+
try {
|
|
135
|
+
handler(payload);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
console.error(`[tempo] event handler failed for ${event}`, error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function decodeResult(result) {
|
|
143
|
+
return sca?.isScaEnvelope(result) ? sca.scaDecode(result) : result;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const ipcRenderer = {
|
|
147
|
+
invoke(channel, ...args) {
|
|
148
|
+
const name = String(channel ?? "").trim();
|
|
149
|
+
if (!name) return Promise.reject(new Error("ipcRenderer channel is required"));
|
|
150
|
+
if (!sca) return Promise.reject(new Error("structured clone codec unavailable"));
|
|
151
|
+
try {
|
|
152
|
+
return call(`ipc.invoke.${name}`, sca.scaEncodeArgs(args)).then(decodeResult);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
return Promise.reject(error);
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
send(channel, ...args) {
|
|
158
|
+
const name = String(channel ?? "").trim();
|
|
159
|
+
if (!name) throw new Error("ipcRenderer channel is required");
|
|
160
|
+
if (!sca) throw new Error("structured clone codec unavailable");
|
|
161
|
+
void call(`ipc.send.${name}`, sca.scaEncodeArgs(args)).catch((error) => {
|
|
162
|
+
console.error(`[tempo] ipcRenderer.send failed for ${name}`, error);
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
on(channel, listener) {
|
|
166
|
+
const name = String(channel ?? "").trim();
|
|
167
|
+
return listen(runtimeListeners, name, (payload) => {
|
|
168
|
+
const event = { sender: "runtime" };
|
|
169
|
+
try {
|
|
170
|
+
if (sca?.isScaEnvelope(payload)) {
|
|
171
|
+
listener(event, ...sca.scaDecodeArgs(payload));
|
|
172
|
+
} else if (payload?.$ipcArgs && Array.isArray(payload.$ipcArgs)) {
|
|
173
|
+
listener(event, ...payload.$ipcArgs);
|
|
174
|
+
} else {
|
|
175
|
+
listener(event, payload);
|
|
176
|
+
}
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error(`[tempo] ipcRenderer.on failed for ${name}`, error);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const storage = {
|
|
185
|
+
async get(key) {
|
|
186
|
+
const result = await call("storage.plugin.get", { key });
|
|
187
|
+
return result?.value ?? null;
|
|
188
|
+
},
|
|
189
|
+
async set(key, value) {
|
|
190
|
+
await call("storage.plugin.set", { key, value });
|
|
191
|
+
},
|
|
192
|
+
async delete(key) {
|
|
193
|
+
await call("storage.plugin.delete", { key });
|
|
194
|
+
},
|
|
195
|
+
async list() {
|
|
196
|
+
const result = await call("storage.plugin.list", {});
|
|
197
|
+
return Array.isArray(result?.keys) ? result.keys : [];
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
function bytesToBase64(bytes) {
|
|
202
|
+
if (Object.prototype.toString.call(bytes) !== "[object Uint8Array]") {
|
|
203
|
+
throw new TypeError("tempo.files.writeBytes requires a Uint8Array");
|
|
204
|
+
}
|
|
205
|
+
let binary = "";
|
|
206
|
+
for (let offset = 0; offset < bytes.length; offset += 32768) {
|
|
207
|
+
binary += String.fromCharCode(...bytes.subarray(offset, offset + 32768));
|
|
208
|
+
}
|
|
209
|
+
return btoa(binary);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function base64ToBytes(encoded) {
|
|
213
|
+
const binary = atob(String(encoded ?? ""));
|
|
214
|
+
const bytes = new Uint8Array(binary.length);
|
|
215
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
216
|
+
bytes[index] = binary.charCodeAt(index);
|
|
217
|
+
}
|
|
218
|
+
return bytes;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const files = {
|
|
222
|
+
async readText(path) {
|
|
223
|
+
const result = await call("files.readText", { path });
|
|
224
|
+
return new TextDecoder().decode(base64ToBytes(result?.base64));
|
|
225
|
+
},
|
|
226
|
+
writeText: (path, content) =>
|
|
227
|
+
call("files.writeText", {
|
|
228
|
+
path,
|
|
229
|
+
base64: bytesToBase64(new TextEncoder().encode(String(content))),
|
|
230
|
+
}).then(() => undefined),
|
|
231
|
+
async readBytes(path) {
|
|
232
|
+
const result = await call("files.readBytes", { path });
|
|
233
|
+
return base64ToBytes(result?.base64);
|
|
234
|
+
},
|
|
235
|
+
writeBytes: (path, bytes) =>
|
|
236
|
+
call("files.writeBytes", { path, base64: bytesToBase64(bytes) }).then(
|
|
237
|
+
() => undefined,
|
|
238
|
+
),
|
|
239
|
+
async list(path = "") {
|
|
240
|
+
const result = await call("files.list", { path });
|
|
241
|
+
return Array.isArray(result?.entries) ? result.entries : [];
|
|
242
|
+
},
|
|
243
|
+
async stat(path) {
|
|
244
|
+
const result = await call("files.stat", { path });
|
|
245
|
+
return result?.stat ?? null;
|
|
246
|
+
},
|
|
247
|
+
mkdir: (path, options = {}) =>
|
|
248
|
+
call("files.mkdir", { path, recursive: options.recursive === true }).then(
|
|
249
|
+
() => undefined,
|
|
250
|
+
),
|
|
251
|
+
remove: (path, options = {}) =>
|
|
252
|
+
call("files.remove", { path, recursive: options.recursive === true }).then(
|
|
253
|
+
() => undefined,
|
|
254
|
+
),
|
|
255
|
+
rename: (from, to) =>
|
|
256
|
+
call("files.rename", { from, to }).then(() => undefined),
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const settings = {
|
|
260
|
+
async getAll() {
|
|
261
|
+
return { ...((await storage.get("__tempo/settings")) ?? {}) };
|
|
262
|
+
},
|
|
263
|
+
async get(id, fallback) {
|
|
264
|
+
const values = await settings.getAll();
|
|
265
|
+
return Object.prototype.hasOwnProperty.call(values, id) ? values[id] : fallback;
|
|
266
|
+
},
|
|
267
|
+
subscribe(handler) {
|
|
268
|
+
return listen(platformListeners, "settings.changed", (payload) => {
|
|
269
|
+
handler(payload?.values && typeof payload.values === "object" ? payload.values : {});
|
|
270
|
+
});
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const tempo = {
|
|
275
|
+
get context() {
|
|
276
|
+
return context;
|
|
277
|
+
},
|
|
278
|
+
ready() {
|
|
279
|
+
return context
|
|
280
|
+
? Promise.resolve(context)
|
|
281
|
+
: new Promise((resolve) => contextWaiters.push(resolve));
|
|
282
|
+
},
|
|
283
|
+
events: {
|
|
284
|
+
on(event, handler) {
|
|
285
|
+
return listen(tempoEventListeners, event, handler);
|
|
286
|
+
},
|
|
287
|
+
once(event, handler) {
|
|
288
|
+
return listenOnce(tempoEventListeners, event, handler);
|
|
289
|
+
},
|
|
290
|
+
off(event, handler) {
|
|
291
|
+
return unlisten(tempoEventListeners, event, handler);
|
|
292
|
+
},
|
|
293
|
+
removeAllListeners(event) {
|
|
294
|
+
clearListeners(tempoEventListeners, event);
|
|
295
|
+
},
|
|
296
|
+
listenerCount(event) {
|
|
297
|
+
return countListeners(tempoEventListeners, event);
|
|
298
|
+
},
|
|
299
|
+
eventNames() {
|
|
300
|
+
return [...tempoEventListeners.keys()];
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
storage,
|
|
304
|
+
files,
|
|
305
|
+
settings,
|
|
306
|
+
notify: {
|
|
307
|
+
async show(options = {}) {
|
|
308
|
+
await call("notify.show", options);
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
theme: {
|
|
312
|
+
async get() {
|
|
313
|
+
const result = await call("theme.get", {});
|
|
314
|
+
return typeof result === "string" ? result : (result?.theme ?? "system");
|
|
315
|
+
},
|
|
316
|
+
async subscribe(handler) {
|
|
317
|
+
const result = await call("theme.onChange", {});
|
|
318
|
+
const subscriptionId = result?.subscriptionId;
|
|
319
|
+
if (!subscriptionId) throw new Error("theme subscription was not created");
|
|
320
|
+
const off = listen(platformListeners, "theme.changed", (payload) => {
|
|
321
|
+
handler(String(payload?.theme ?? "system"));
|
|
322
|
+
});
|
|
323
|
+
return () => {
|
|
324
|
+
off();
|
|
325
|
+
void call("subscription.release", { subscriptionId });
|
|
326
|
+
};
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
mainPanel: {
|
|
330
|
+
hide: () => call("mainPanel.hide", {}).then(() => undefined),
|
|
331
|
+
back: () => call("mainPanel.back", {}).then(() => undefined),
|
|
332
|
+
setSize: (height) => call("mainPanel.setSize", { height }).then(() => undefined),
|
|
333
|
+
},
|
|
334
|
+
window: {
|
|
335
|
+
setRect: (rect) => call("window.setRect", rect).then(() => undefined),
|
|
336
|
+
close: () => call("window.close", {}).then(() => undefined),
|
|
337
|
+
},
|
|
338
|
+
app: {
|
|
339
|
+
open: (appId, params) =>
|
|
340
|
+
call("app.open", { appId, params: params ?? null }).then(() => undefined),
|
|
341
|
+
},
|
|
342
|
+
external: {
|
|
343
|
+
open: (url) => call("external.open", { url }).then(() => undefined),
|
|
344
|
+
},
|
|
345
|
+
session: {
|
|
346
|
+
push: (payload) => call("session.push", { payload }).then(() => undefined),
|
|
347
|
+
},
|
|
348
|
+
host(method, params) {
|
|
349
|
+
const name = String(method ?? "").trim().replace(/^host\./, "");
|
|
350
|
+
if (!HOST_METHODS.has(name) && !name.startsWith("storage.plugin.")) {
|
|
351
|
+
return Promise.reject(new Error(`unknown host method: ${name}`));
|
|
352
|
+
}
|
|
353
|
+
return call(name, params);
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
window.addEventListener("message", (event) => {
|
|
358
|
+
if (event.source !== window.parent) return;
|
|
359
|
+
const data = event.data;
|
|
360
|
+
if (!data || typeof data !== "object") return;
|
|
361
|
+
|
|
362
|
+
if (data.type === "tempo-plugin-rpc-response") {
|
|
363
|
+
const entry = pending.get(data.id);
|
|
364
|
+
if (!entry) return;
|
|
365
|
+
pending.delete(data.id);
|
|
366
|
+
if (data.ok) entry.resolve(data.result);
|
|
367
|
+
else entry.reject(Object.assign(new Error(data.error?.message ?? "plugin call failed"), data.error ?? {}));
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (data.type === "tempo-plugin-context") {
|
|
372
|
+
context = data;
|
|
373
|
+
while (contextWaiters.length) contextWaiters.shift()(data);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (data.type === "tempo-plugin-event") {
|
|
378
|
+
if (data.source === "runtime") {
|
|
379
|
+
emit(runtimeListeners, data.event, data.payload);
|
|
380
|
+
} else {
|
|
381
|
+
emit(platformListeners, data.event, data.payload);
|
|
382
|
+
if (!SPECIALIZED_PLATFORM_EVENTS.has(data.event)) {
|
|
383
|
+
emit(tempoEventListeners, data.event, data.payload);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (data.type === "tempo-plugin-dev-log") {
|
|
390
|
+
const label = `[runtime:${data.source}]`;
|
|
391
|
+
if (data.source === "stderr") console.error(label, data.message);
|
|
392
|
+
else console.log(label, data.message);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
window.addEventListener(
|
|
397
|
+
"keydown",
|
|
398
|
+
(event) => {
|
|
399
|
+
if (event.key !== "Escape") return;
|
|
400
|
+
event.preventDefault();
|
|
401
|
+
void tempo.mainPanel.back();
|
|
402
|
+
},
|
|
403
|
+
true,
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
Object.defineProperty(globalThis, "__tempoPluginUi", {
|
|
407
|
+
value: Object.freeze({ protocolVersion: 1, tempo, ipcRenderer }),
|
|
408
|
+
configurable: false,
|
|
409
|
+
enumerable: false,
|
|
410
|
+
writable: false,
|
|
411
|
+
});
|
|
412
|
+
window.tempo = tempo;
|
|
413
|
+
window.ipcRenderer = ipcRenderer;
|
|
414
|
+
window.parent.postMessage({ type: "tempo-plugin-ready" }, "*");
|
|
415
|
+
})();
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portable HTML Structured Clone–style codec for the injected UI bridge.
|
|
3
|
+
* Envelope: { "$sca": "<base64>" }
|
|
4
|
+
*/
|
|
5
|
+
(function (global) {
|
|
6
|
+
"use strict";
|
|
7
|
+
|
|
8
|
+
const TYPED_ARRAY_CTORS = {
|
|
9
|
+
Int8Array,
|
|
10
|
+
Uint8Array,
|
|
11
|
+
Uint8ClampedArray,
|
|
12
|
+
Int16Array,
|
|
13
|
+
Uint16Array,
|
|
14
|
+
Int32Array,
|
|
15
|
+
Uint32Array,
|
|
16
|
+
Float32Array,
|
|
17
|
+
Float64Array,
|
|
18
|
+
BigInt64Array,
|
|
19
|
+
BigUint64Array,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function bytesToBase64(bytes) {
|
|
23
|
+
let binary = "";
|
|
24
|
+
for (let i = 0; i < bytes.length; i += 1) binary += String.fromCharCode(bytes[i]);
|
|
25
|
+
return btoa(binary);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function base64ToBytes(b64) {
|
|
29
|
+
const binary = atob(b64);
|
|
30
|
+
const out = new Uint8Array(binary.length);
|
|
31
|
+
for (let i = 0; i < binary.length; i += 1) out[i] = binary.charCodeAt(i);
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function utf8Encode(text) {
|
|
36
|
+
return new TextEncoder().encode(text);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function utf8Decode(bytes) {
|
|
40
|
+
return new TextDecoder().decode(bytes);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isPlainObject(value) {
|
|
44
|
+
const proto = Object.getPrototypeOf(value);
|
|
45
|
+
return proto === Object.prototype || proto === null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function reject(value) {
|
|
49
|
+
const kind =
|
|
50
|
+
value === null
|
|
51
|
+
? "null"
|
|
52
|
+
: typeof value === "object"
|
|
53
|
+
? value.constructor?.name ?? "Object"
|
|
54
|
+
: typeof value;
|
|
55
|
+
throw new Error(`Failed to serialize arguments: ${kind} is not structured-cloneable`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function encodeValue(value, seen, out) {
|
|
59
|
+
if (value === undefined) return { t: "u" };
|
|
60
|
+
if (value === null) return null;
|
|
61
|
+
const ty = typeof value;
|
|
62
|
+
if (ty === "boolean" || ty === "string") return value;
|
|
63
|
+
if (ty === "number") {
|
|
64
|
+
if (Number.isNaN(value) || !Number.isFinite(value)) return { t: "num", v: value };
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
if (ty === "bigint") return { t: "bi", v: value.toString() };
|
|
68
|
+
if (ty === "symbol" || ty === "function") reject(value);
|
|
69
|
+
if (ty !== "object") reject(value);
|
|
70
|
+
|
|
71
|
+
if (seen.has(value)) return { t: "r", i: seen.get(value) };
|
|
72
|
+
if (value instanceof Promise || value instanceof WeakMap || value instanceof WeakSet) reject(value);
|
|
73
|
+
if (typeof Element !== "undefined" && value instanceof Element) reject(value);
|
|
74
|
+
if (typeof Node !== "undefined" && value instanceof Node) reject(value);
|
|
75
|
+
|
|
76
|
+
if (value instanceof Date) {
|
|
77
|
+
const i = out.length;
|
|
78
|
+
seen.set(value, i);
|
|
79
|
+
out.push({ t: "date", v: value.getTime() });
|
|
80
|
+
return { t: "r", i };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (value instanceof ArrayBuffer) {
|
|
84
|
+
const i = out.length;
|
|
85
|
+
seen.set(value, i);
|
|
86
|
+
out.push({ t: "ab", v: bytesToBase64(new Uint8Array(value)) });
|
|
87
|
+
return { t: "r", i };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (ArrayBuffer.isView(value)) {
|
|
91
|
+
const i = out.length;
|
|
92
|
+
seen.set(value, i);
|
|
93
|
+
const bytes = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
94
|
+
if (value instanceof DataView) {
|
|
95
|
+
out.push({ t: "dv", v: bytesToBase64(bytes), o: 0, l: value.byteLength });
|
|
96
|
+
return { t: "r", i };
|
|
97
|
+
}
|
|
98
|
+
const name = value.constructor.name;
|
|
99
|
+
if (!TYPED_ARRAY_CTORS[name]) reject(value);
|
|
100
|
+
out.push({ t: "ta", n: name, v: bytesToBase64(bytes) });
|
|
101
|
+
return { t: "r", i };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (value instanceof Map) {
|
|
105
|
+
const i = out.length;
|
|
106
|
+
seen.set(value, i);
|
|
107
|
+
const ir = { t: "m", v: [] };
|
|
108
|
+
out.push(ir);
|
|
109
|
+
for (const [k, v] of value) ir.v.push([encodeValue(k, seen, out), encodeValue(v, seen, out)]);
|
|
110
|
+
return { t: "r", i };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (value instanceof Set) {
|
|
114
|
+
const i = out.length;
|
|
115
|
+
seen.set(value, i);
|
|
116
|
+
const ir = { t: "s", v: [] };
|
|
117
|
+
out.push(ir);
|
|
118
|
+
for (const item of value) ir.v.push(encodeValue(item, seen, out));
|
|
119
|
+
return { t: "r", i };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (value instanceof Error) {
|
|
123
|
+
const i = out.length;
|
|
124
|
+
seen.set(value, i);
|
|
125
|
+
out.push({
|
|
126
|
+
t: "e",
|
|
127
|
+
n: value.name || "Error",
|
|
128
|
+
m: value.message || "",
|
|
129
|
+
c: value.cause !== undefined ? encodeValue(value.cause, seen, out) : undefined,
|
|
130
|
+
});
|
|
131
|
+
return { t: "r", i };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (Array.isArray(value)) {
|
|
135
|
+
const i = out.length;
|
|
136
|
+
seen.set(value, i);
|
|
137
|
+
const ir = { t: "a", v: [] };
|
|
138
|
+
out.push(ir);
|
|
139
|
+
for (const item of value) ir.v.push(encodeValue(item, seen, out));
|
|
140
|
+
return { t: "r", i };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!isPlainObject(value)) reject(value);
|
|
144
|
+
|
|
145
|
+
const i = out.length;
|
|
146
|
+
seen.set(value, i);
|
|
147
|
+
const ir = { t: "o", v: [] };
|
|
148
|
+
out.push(ir);
|
|
149
|
+
for (const key of Object.keys(value)) {
|
|
150
|
+
ir.v.push([key, encodeValue(value[key], seen, out)]);
|
|
151
|
+
}
|
|
152
|
+
return { t: "r", i };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function decodeIr(ir, table) {
|
|
156
|
+
if (ir === null || typeof ir === "boolean" || typeof ir === "number" || typeof ir === "string") {
|
|
157
|
+
return ir;
|
|
158
|
+
}
|
|
159
|
+
if (typeof ir !== "object" || !("t" in ir)) reject(ir);
|
|
160
|
+
switch (ir.t) {
|
|
161
|
+
case "u":
|
|
162
|
+
return undefined;
|
|
163
|
+
case "bi":
|
|
164
|
+
return BigInt(ir.v);
|
|
165
|
+
case "num":
|
|
166
|
+
return Number(ir.v);
|
|
167
|
+
case "r":
|
|
168
|
+
if (ir.i < 0 || ir.i >= table.length) throw new Error("Failed to deserialize: invalid ref");
|
|
169
|
+
return table[ir.i];
|
|
170
|
+
default:
|
|
171
|
+
throw new Error(`Failed to deserialize: unexpected tag ${ir.t}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function materializeSlot(slot, table, index) {
|
|
176
|
+
if (slot === null || typeof slot !== "object" || !("t" in slot)) {
|
|
177
|
+
table[index] = slot;
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
switch (slot.t) {
|
|
181
|
+
case "date":
|
|
182
|
+
table[index] = new Date(slot.v);
|
|
183
|
+
return;
|
|
184
|
+
case "ab":
|
|
185
|
+
table[index] = base64ToBytes(slot.v).buffer;
|
|
186
|
+
return;
|
|
187
|
+
case "ta": {
|
|
188
|
+
const Ctor = TYPED_ARRAY_CTORS[slot.n];
|
|
189
|
+
if (!Ctor) throw new Error(`Failed to deserialize: unknown TypedArray ${slot.n}`);
|
|
190
|
+
const bytes = base64ToBytes(slot.v);
|
|
191
|
+
table[index] = new Ctor(
|
|
192
|
+
bytes.buffer,
|
|
193
|
+
bytes.byteOffset,
|
|
194
|
+
bytes.byteLength / Ctor.BYTES_PER_ELEMENT,
|
|
195
|
+
);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
case "dv": {
|
|
199
|
+
const bytes = base64ToBytes(slot.v);
|
|
200
|
+
table[index] = new DataView(bytes.buffer, bytes.byteOffset, slot.l);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
case "a":
|
|
204
|
+
table[index] = new Array(slot.v.length);
|
|
205
|
+
return;
|
|
206
|
+
case "o":
|
|
207
|
+
table[index] = {};
|
|
208
|
+
return;
|
|
209
|
+
case "m":
|
|
210
|
+
table[index] = new Map();
|
|
211
|
+
return;
|
|
212
|
+
case "s":
|
|
213
|
+
table[index] = new Set();
|
|
214
|
+
return;
|
|
215
|
+
case "e": {
|
|
216
|
+
const err = new Error(slot.m);
|
|
217
|
+
err.name = slot.n;
|
|
218
|
+
table[index] = err;
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
default:
|
|
222
|
+
throw new Error("Failed to deserialize: bad slot tag");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function fillSlot(slot, table, index) {
|
|
227
|
+
if (slot === null || typeof slot !== "object" || !("t" in slot)) return;
|
|
228
|
+
switch (slot.t) {
|
|
229
|
+
case "a": {
|
|
230
|
+
const arr = table[index];
|
|
231
|
+
for (let i = 0; i < slot.v.length; i += 1) arr[i] = decodeIr(slot.v[i], table);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
case "o": {
|
|
235
|
+
const obj = table[index];
|
|
236
|
+
for (const [k, v] of slot.v) obj[k] = decodeIr(v, table);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
case "m": {
|
|
240
|
+
const map = table[index];
|
|
241
|
+
for (const [k, v] of slot.v) map.set(decodeIr(k, table), decodeIr(v, table));
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
case "s": {
|
|
245
|
+
const set = table[index];
|
|
246
|
+
for (const item of slot.v) set.add(decodeIr(item, table));
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
case "e": {
|
|
250
|
+
if (slot.c !== undefined) table[index].cause = decodeIr(slot.c, table);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
default:
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function scaEncode(value) {
|
|
259
|
+
const seen = new Map();
|
|
260
|
+
const table = [];
|
|
261
|
+
const root = encodeValue(value, seen, table);
|
|
262
|
+
const bytes = utf8Encode(JSON.stringify({ root, table }));
|
|
263
|
+
return { $sca: bytesToBase64(bytes) };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function scaDecode(envelope) {
|
|
267
|
+
if (!envelope || typeof envelope.$sca !== "string") {
|
|
268
|
+
throw new Error("Failed to deserialize: missing $sca envelope");
|
|
269
|
+
}
|
|
270
|
+
const payload = JSON.parse(utf8Decode(base64ToBytes(envelope.$sca)));
|
|
271
|
+
const table = new Array(payload.table.length);
|
|
272
|
+
for (let i = 0; i < payload.table.length; i += 1) materializeSlot(payload.table[i], table, i);
|
|
273
|
+
for (let i = 0; i < payload.table.length; i += 1) fillSlot(payload.table[i], table, i);
|
|
274
|
+
return decodeIr(payload.root, table);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function isScaEnvelope(value) {
|
|
278
|
+
return (
|
|
279
|
+
!!value &&
|
|
280
|
+
typeof value === "object" &&
|
|
281
|
+
!Array.isArray(value) &&
|
|
282
|
+
typeof value.$sca === "string" &&
|
|
283
|
+
Object.keys(value).length === 1
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
global.__tempoSca = {
|
|
288
|
+
scaEncode,
|
|
289
|
+
scaDecode,
|
|
290
|
+
isScaEnvelope,
|
|
291
|
+
scaEncodeArgs: (args) => scaEncode(args),
|
|
292
|
+
scaDecodeArgs: (envelope) => {
|
|
293
|
+
const value = scaDecode(envelope);
|
|
294
|
+
return Array.isArray(value) ? value : [value];
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
})(typeof globalThis !== "undefined" ? globalThis : window);
|
package/dist/globals.d.ts
CHANGED
|
@@ -1,2 +1,17 @@
|
|
|
1
|
-
|
|
1
|
+
import type { IpcMainApi, IpcRendererApi, TempoLifecycleRegistrar, TempoRuntimeApi, TempoUiApi } from "./types.js";
|
|
2
|
+
type TempoUiTransport = {
|
|
3
|
+
protocolVersion: number;
|
|
4
|
+
tempo: TempoUiApi;
|
|
5
|
+
ipcRenderer: IpcRendererApi;
|
|
6
|
+
};
|
|
7
|
+
type TempoRuntimeTransport = {
|
|
8
|
+
protocolVersion: number;
|
|
9
|
+
tempo: TempoRuntimeApi;
|
|
10
|
+
ipcMain: IpcMainApi;
|
|
11
|
+
onMounted: TempoLifecycleRegistrar;
|
|
12
|
+
onUnmounted: TempoLifecycleRegistrar;
|
|
13
|
+
};
|
|
14
|
+
export declare function getUiTransport(): TempoUiTransport;
|
|
15
|
+
export declare function getRuntimeTransport(): TempoRuntimeTransport;
|
|
16
|
+
export {};
|
|
2
17
|
//# sourceMappingURL=globals.d.ts.map
|
package/dist/globals.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"globals.d.ts","sourceRoot":"","sources":["../src/globals.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"globals.d.ts","sourceRoot":"","sources":["../src/globals.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,uBAAuB,EACvB,eAAe,EACf,UAAU,EACX,MAAM,YAAY,CAAC;AAIpB,KAAK,gBAAgB,GAAG;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,UAAU,CAAC;IAClB,WAAW,EAAE,cAAc,CAAC;CAC7B,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,eAAe,CAAC;IACvB,OAAO,EAAE,UAAU,CAAC;IACpB,SAAS,EAAE,uBAAuB,CAAC;IACnC,WAAW,EAAE,uBAAuB,CAAC;CACtC,CAAC;AA2BF,wBAAgB,cAAc,IAAI,gBAAgB,CAQjD;AAED,wBAAgB,mBAAmB,IAAI,qBAAqB,CAU3D"}
|
package/dist/globals.js
CHANGED
|
@@ -1,8 +1,38 @@
|
|
|
1
|
-
|
|
1
|
+
const TRANSPORT_PROTOCOL_VERSION = 1;
|
|
2
|
+
function getHostGlobal(name) {
|
|
2
3
|
const value = globalThis[name];
|
|
3
4
|
if (value === undefined || value === null) {
|
|
4
5
|
throw new Error(`Tempo SDK could not connect: host global \"${name}\" is unavailable`);
|
|
5
6
|
}
|
|
6
7
|
return value;
|
|
7
8
|
}
|
|
9
|
+
function getInternalTransport(name) {
|
|
10
|
+
const value = globalThis[name];
|
|
11
|
+
if (value === undefined || value === null)
|
|
12
|
+
return undefined;
|
|
13
|
+
if (typeof value !== "object") {
|
|
14
|
+
throw new Error(`Tempo SDK could not connect: host transport "${name}" is invalid`);
|
|
15
|
+
}
|
|
16
|
+
const protocolVersion = value.protocolVersion;
|
|
17
|
+
if (protocolVersion !== TRANSPORT_PROTOCOL_VERSION) {
|
|
18
|
+
throw new Error(`Tempo SDK could not connect: host transport "${name}" uses unsupported protocol ${String(protocolVersion)} (expected ${TRANSPORT_PROTOCOL_VERSION})`);
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
export function getUiTransport() {
|
|
23
|
+
return (getInternalTransport("__tempoPluginUi") ?? {
|
|
24
|
+
protocolVersion: TRANSPORT_PROTOCOL_VERSION,
|
|
25
|
+
tempo: getHostGlobal("tempo"),
|
|
26
|
+
ipcRenderer: getHostGlobal("ipcRenderer"),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export function getRuntimeTransport() {
|
|
30
|
+
return (getInternalTransport("__tempoPluginRuntime") ?? {
|
|
31
|
+
protocolVersion: TRANSPORT_PROTOCOL_VERSION,
|
|
32
|
+
tempo: getHostGlobal("tempo"),
|
|
33
|
+
ipcMain: getHostGlobal("ipcMain"),
|
|
34
|
+
onMounted: getHostGlobal("onMounted"),
|
|
35
|
+
onUnmounted: getHostGlobal("onUnmounted"),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
8
38
|
//# sourceMappingURL=globals.js.map
|
package/dist/globals.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"globals.js","sourceRoot":"","sources":["../src/globals.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"globals.js","sourceRoot":"","sources":["../src/globals.ts"],"names":[],"mappings":"AAQA,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAgBrC,SAAS,aAAa,CAAI,IAAY;IACpC,MAAM,KAAK,GAAI,UAAsC,CAAC,IAAI,CAAC,CAAC;IAC5D,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CACb,8CAA8C,IAAI,mBAAmB,CACtE,CAAC;IACJ,CAAC;IACD,OAAO,KAAU,CAAC;AACpB,CAAC;AAED,SAAS,oBAAoB,CAAI,IAAY;IAC3C,MAAM,KAAK,GAAI,UAAsC,CAAC,IAAI,CAAC,CAAC;IAC5D,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,gDAAgD,IAAI,cAAc,CAAC,CAAC;IACtF,CAAC;IACD,MAAM,eAAe,GAAI,KAAuC,CAAC,eAAe,CAAC;IACjF,IAAI,eAAe,KAAK,0BAA0B,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CACb,gDAAgD,IAAI,+BAA+B,MAAM,CAAC,eAAe,CAAC,cAAc,0BAA0B,GAAG,CACtJ,CAAC;IACJ,CAAC;IACD,OAAO,KAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,CACL,oBAAoB,CAAmB,iBAAiB,CAAC,IAAI;QAC3D,eAAe,EAAE,0BAA0B;QAC3C,KAAK,EAAE,aAAa,CAAa,OAAO,CAAC;QACzC,WAAW,EAAE,aAAa,CAAiB,aAAa,CAAC;KAC1D,CACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,OAAO,CACL,oBAAoB,CAAwB,sBAAsB,CAAC,IAAI;QACrE,eAAe,EAAE,0BAA0B;QAC3C,KAAK,EAAE,aAAa,CAAkB,OAAO,CAAC;QAC9C,OAAO,EAAE,aAAa,CAAa,SAAS,CAAC;QAC7C,SAAS,EAAE,aAAa,CAA0B,WAAW,CAAC;QAC9D,WAAW,EAAE,aAAa,CAA0B,aAAa,CAAC;KACnE,CACF,CAAC;AACJ,CAAC"}
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EAEpB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EAEpB,iBAAiB,EAIjB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,mBAAmB,YAAY,CAAC;AAchC,wBAAgB,aAAa,CAC3B,SAAS,SAAS,iBAAiB,GAAG,oBAAoB,EAC1D,KAAK,EAAE,iBAAiB,CAAC,SAAS,CAAC,GAAG,IAAI,CA2E3C"}
|
package/dist/runtime.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getRuntimeTransport } from "./globals.js";
|
|
2
2
|
function createIpc(ipcMain) {
|
|
3
3
|
return {
|
|
4
4
|
handle: (channel, handler) => ipcMain.handle(channel, handler),
|
|
@@ -10,10 +10,7 @@ export function defineRuntime(setup) {
|
|
|
10
10
|
if (typeof setup !== "function") {
|
|
11
11
|
throw new TypeError("defineRuntime requires a setup function");
|
|
12
12
|
}
|
|
13
|
-
const host =
|
|
14
|
-
const ipcMain = getHostGlobal("ipcMain");
|
|
15
|
-
const onMounted = getHostGlobal("onMounted");
|
|
16
|
-
const onUnmounted = getHostGlobal("onUnmounted");
|
|
13
|
+
const { tempo: host, ipcMain, onMounted, onUnmounted } = getRuntimeTransport();
|
|
17
14
|
const disposers = [];
|
|
18
15
|
let disposing = false;
|
|
19
16
|
async function runDisposers() {
|
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAanD,SAAS,SAAS,CAChB,OAAmB;IAEnB,OAAO;QACL,MAAM,EAAE,CAAC,OAAe,EAAE,OAAgC,EAAE,EAAE,CAC5D,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,OAAgB,CAAC;QAC3C,EAAE,EAAE,CAAC,OAAe,EAAE,QAAkC,EAAE,EAAE,CAC1D,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,QAAiB,CAAC;QACxC,IAAI,EAAE,CAAC,OAAe,EAAE,GAAG,IAAW,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;KAC5C,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,aAAa,CAE3B,KAAmC;IACnC,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,mBAAmB,EAAE,CAAC;IAC/E,MAAM,SAAS,GAAoB,EAAE,CAAC;IACtC,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,KAAK,UAAU,YAAY;QACzB,SAAS,GAAG,IAAI,CAAC;QACjB,MAAM,MAAM,GAAc,EAAE,CAAC;QAC7B,OAAO,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC;gBACH,MAAM,SAAS,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC;YAC5B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,OAAO,GAAmC,MAAM,CAAC,MAAM,CAAC;QAC5D,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,IAAI,EAAE,IAAI,CAAC,GAAG;QACd,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,GAAG,EAAE,SAAS,CAAY,OAAO,CAAC;QAClC,SAAS,CAAC,QAAuB;YAC/B,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;gBACnC,MAAM,IAAI,SAAS,CAAC,+BAA+B,CAAC,CAAC;YACvD,CAAC;YACD,IAAI,SAAS,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;YAC1E,CAAC;YACD,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;KACF,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;YACtC,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;gBACnC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC3B,CAAC;iBAAM,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAClC,MAAM,IAAI,SAAS,CAAC,iDAAiD,CAAC,CAAC;YACzE,CAAC;QACH,CAAC;QAAC,OAAO,UAAU,EAAE,CAAC;YACpB,MAAM,aAAa,GAAG,MAAM,YAAY,EAAE,CAAC;YAC3C,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,IAAI,cAAc,CACtB,CAAC,UAAU,EAAE,GAAG,aAAa,CAAC,EAC9B,wCAAwC,CACzC,CAAC;YACJ,CAAC;YACD,MAAM,UAAU,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,WAAW,CAAC,KAAK,IAAI,EAAE;QACrB,MAAM,MAAM,GAAG,MAAM,YAAY,EAAE,CAAC;QACpC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,cAAc,CAAC,MAAM,EAAE,8BAA8B,CAAC,CAAC;QACnE,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/ui.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EAEpB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EAEpB,iBAAiB,EACjB,aAAa,EAEd,MAAM,YAAY,CAAC;AAEpB,mBAAmB,YAAY,CAAC;AAehC,wBAAsB,OAAO,CAC3B,SAAS,SAAS,iBAAiB,GAAG,oBAAoB,KACvD,OAAO,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAsBrC"}
|
package/dist/ui.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getUiTransport } from "./globals.js";
|
|
2
2
|
function createIpc(ipcRenderer) {
|
|
3
3
|
return {
|
|
4
4
|
invoke: (channel, ...args) => ipcRenderer.invoke(channel, ...args),
|
|
@@ -7,8 +7,7 @@ function createIpc(ipcRenderer) {
|
|
|
7
7
|
};
|
|
8
8
|
}
|
|
9
9
|
export async function connect() {
|
|
10
|
-
const host =
|
|
11
|
-
const ipcRenderer = getHostGlobal("ipcRenderer");
|
|
10
|
+
const { tempo: host, ipcRenderer } = getUiTransport();
|
|
12
11
|
const context = await host.ready();
|
|
13
12
|
if (!context || typeof context !== "object") {
|
|
14
13
|
throw new Error("Tempo SDK could not connect: host returned an invalid UI context");
|
package/dist/ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAW9C,SAAS,SAAS,CAChB,WAA2B;IAE3B,OAAO;QACL,MAAM,EAAE,CAAC,OAAe,EAAE,GAAG,IAAW,EAAE,EAAE,CAC1C,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;QACtC,IAAI,EAAE,CAAC,OAAe,EAAE,GAAG,IAAW,EAAE,EAAE,CACxC,WAAW,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC;QACpC,EAAE,EAAE,CAAC,OAAe,EAAE,QAAkC,EAAE,EAAE,CAC1D,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,QAAiB,CAAC;KACpB,CAAC;AAC7B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO;IAG3B,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,cAAc,EAAE,CAAC;IACtD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;IACnC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC;IACtF,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,OAAO;QACP,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,IAAI,EAAE,IAAI,CAAC,GAAG;QACd,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,GAAG,EAAE,SAAS,CAAY,WAAW,CAAC;KACvC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/vite.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type TempoHtmlTag = {
|
|
2
|
+
tag: string;
|
|
3
|
+
children: string;
|
|
4
|
+
injectTo: "head-prepend" | "head" | "body-prepend" | "body";
|
|
5
|
+
};
|
|
6
|
+
export type TempoViteConfig = {
|
|
7
|
+
command: "serve" | "build";
|
|
8
|
+
root: string;
|
|
9
|
+
build: {
|
|
10
|
+
outDir: string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export type TempoVitePlugin = {
|
|
14
|
+
name: string;
|
|
15
|
+
apply?: "serve" | "build";
|
|
16
|
+
enforce?: "pre" | "post";
|
|
17
|
+
transformIndexHtml?: () => TempoHtmlTag[];
|
|
18
|
+
configResolved?: (config: TempoViteConfig) => void;
|
|
19
|
+
closeBundle?: () => void;
|
|
20
|
+
};
|
|
21
|
+
export declare function tempoPlugin(options?: {
|
|
22
|
+
outDir?: string;
|
|
23
|
+
}): TempoVitePlugin;
|
|
24
|
+
//# sourceMappingURL=vite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.d.ts","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,cAAc,GAAG,MAAM,GAAG,cAAc,GAAG,MAAM,CAAC;CAC7D,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,YAAY,EAAE,CAAC;IAC1C,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B,CAAC;AAQF,wBAAgB,WAAW,CAAC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,eAAe,CAyC9E"}
|
package/dist/vite.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
const devAssetsRoot = fileURLToPath(new URL("./dev-assets/", import.meta.url));
|
|
5
|
+
function readDevAsset(fileName) {
|
|
6
|
+
return fs.readFileSync(path.join(devAssetsRoot, fileName), "utf8");
|
|
7
|
+
}
|
|
8
|
+
export function tempoPlugin(options = {}) {
|
|
9
|
+
let command = "build";
|
|
10
|
+
let source = "";
|
|
11
|
+
let target = "";
|
|
12
|
+
return {
|
|
13
|
+
name: "tempo-plugin",
|
|
14
|
+
enforce: "pre",
|
|
15
|
+
configResolved(config) {
|
|
16
|
+
command = config.command;
|
|
17
|
+
source = path.join(config.root, "manifest.json");
|
|
18
|
+
target = path.resolve(config.root, options.outDir ?? config.build.outDir, "manifest.json");
|
|
19
|
+
},
|
|
20
|
+
transformIndexHtml() {
|
|
21
|
+
if (command !== "serve")
|
|
22
|
+
return [];
|
|
23
|
+
return [
|
|
24
|
+
{
|
|
25
|
+
tag: "script",
|
|
26
|
+
children: readDevAsset("structured-clone.js"),
|
|
27
|
+
injectTo: "head-prepend",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
tag: "script",
|
|
31
|
+
children: readDevAsset("bridge-client.js"),
|
|
32
|
+
injectTo: "head-prepend",
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
},
|
|
36
|
+
closeBundle() {
|
|
37
|
+
if (command !== "build")
|
|
38
|
+
return;
|
|
39
|
+
if (!source || !target) {
|
|
40
|
+
throw new Error("tempo-plugin was not initialized by Vite");
|
|
41
|
+
}
|
|
42
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
43
|
+
fs.copyFileSync(source, target);
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=vite.js.map
|
package/dist/vite.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vite.js","sourceRoot":"","sources":["../src/vite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAyBzC,MAAM,aAAa,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE/E,SAAS,YAAY,CAAC,QAAgB;IACpC,OAAO,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAA+B,EAAE;IAC3D,IAAI,OAAO,GAAsB,OAAO,CAAC;IACzC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,MAAM,GAAG,EAAE,CAAC;IAEhB,OAAO;QACL,IAAI,EAAE,cAAc;QACpB,OAAO,EAAE,KAAK;QACd,cAAc,CAAC,MAAuB;YACpC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;YACzB,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;YACjD,MAAM,GAAG,IAAI,CAAC,OAAO,CACnB,MAAM,CAAC,IAAI,EACX,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,EACrC,eAAe,CAChB,CAAC;QACJ,CAAC;QACD,kBAAkB;YAChB,IAAI,OAAO,KAAK,OAAO;gBAAE,OAAO,EAAE,CAAC;YACnC,OAAO;gBACL;oBACE,GAAG,EAAE,QAAQ;oBACb,QAAQ,EAAE,YAAY,CAAC,qBAAqB,CAAC;oBAC7C,QAAQ,EAAE,cAAc;iBACzB;gBACD;oBACE,GAAG,EAAE,QAAQ;oBACb,QAAQ,EAAE,YAAY,CAAC,kBAAkB,CAAC;oBAC1C,QAAQ,EAAE,cAAc;iBACzB;aACF,CAAC;QACJ,CAAC;QACD,WAAW;YACT,IAAI,OAAO,KAAK,OAAO;gBAAE,OAAO;YAChC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAC9D,CAAC;YACD,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACxD,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tempo-plugin-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Application model and typed host APIs for Tempo plugins",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,10 +24,14 @@
|
|
|
24
24
|
"./runtime": {
|
|
25
25
|
"types": "./dist/runtime.d.ts",
|
|
26
26
|
"import": "./dist/runtime.js"
|
|
27
|
+
},
|
|
28
|
+
"./vite": {
|
|
29
|
+
"types": "./dist/vite.d.ts",
|
|
30
|
+
"import": "./dist/vite.js"
|
|
27
31
|
}
|
|
28
32
|
},
|
|
29
33
|
"scripts": {
|
|
30
|
-
"build": "
|
|
34
|
+
"build": "node ./scripts/build.mjs",
|
|
31
35
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
32
36
|
"test": "pnpm build && node --test"
|
|
33
37
|
},
|
|
@@ -35,6 +39,7 @@
|
|
|
35
39
|
"access": "public"
|
|
36
40
|
},
|
|
37
41
|
"devDependencies": {
|
|
42
|
+
"@types/node": "^26.1.0",
|
|
38
43
|
"typescript": "~5.8.3"
|
|
39
44
|
}
|
|
40
45
|
}
|