@agentshouse/app-bridge 0.1.0-c415579fa0c3a → 0.1.0-c58a055eeaa71
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 +14 -11
- package/dist/index.d.ts +139 -0
- package/dist/index.js +1 -0
- package/dist/{client/src/test-host.d.ts → test-host.d.ts} +3 -4
- package/dist/test-host.js +165 -0
- package/docs/families.md +35 -34
- package/package.json +7 -7
- package/dist/client/src/bridge.d.ts +0 -38
- package/dist/client/src/bridge.js +0 -320
- package/dist/client/src/index.d.ts +0 -3
- package/dist/client/src/index.js +0 -2
- package/dist/client/src/test-host.js +0 -185
- package/dist/client/src/wire.d.ts +0 -215
- package/dist/client/src/wire.js +0 -1
- package/dist/src/apps/remainder.d.ts +0 -2
- package/dist/src/apps/remainder.js +0 -16
- package/dist/src/apps/tab.d.ts +0 -6
- package/dist/src/apps/tab.js +0 -27
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
export class HouseCeremonyRefused extends Error {
|
|
2
|
-
path;
|
|
3
|
-
constructor(path) {
|
|
4
|
-
super('house_ceremony_refused');
|
|
5
|
-
this.name = 'HouseCeremonyRefused';
|
|
6
|
-
this.path = path;
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
export class HouseNavigationRefused extends Error {
|
|
10
|
-
address;
|
|
11
|
-
constructor(address) {
|
|
12
|
-
super('house_navigation_refused');
|
|
13
|
-
this.name = 'HouseNavigationRefused';
|
|
14
|
-
this.address = address;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function pendingAnswer(pending, answer) {
|
|
18
|
-
const asked = pending.get(answer.id);
|
|
19
|
-
if (asked === undefined)
|
|
20
|
-
return null;
|
|
21
|
-
pending.delete(answer.id);
|
|
22
|
-
if (answer.refused === true) {
|
|
23
|
-
asked.reject(new HouseCeremonyRefused(null));
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
if (answer.refusal !== undefined) {
|
|
27
|
-
asked.reject(new DOMException(answer.refusal.message, answer.refusal.name));
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
return asked;
|
|
31
|
-
}
|
|
32
|
-
const FRAME_HOUSES = [
|
|
33
|
-
'runtime',
|
|
34
|
-
'status',
|
|
35
|
-
'screenshot',
|
|
36
|
-
'address',
|
|
37
|
-
'ceremony-refused',
|
|
38
|
-
'navigate-refused',
|
|
39
|
-
'attachment',
|
|
40
|
-
'source',
|
|
41
|
-
'external-call',
|
|
42
|
-
'install',
|
|
43
|
-
'notifications',
|
|
44
|
-
'badge',
|
|
45
|
-
'app-message',
|
|
46
|
-
];
|
|
47
|
-
function frameMessage(value) {
|
|
48
|
-
if (value === null || typeof value !== 'object')
|
|
49
|
-
return null;
|
|
50
|
-
const message = value;
|
|
51
|
-
if (typeof message.house !== 'string')
|
|
52
|
-
return null;
|
|
53
|
-
if (!FRAME_HOUSES.includes(message.house))
|
|
54
|
-
return null;
|
|
55
|
-
return value;
|
|
56
|
-
}
|
|
57
|
-
function settled() {
|
|
58
|
-
let settle = null;
|
|
59
|
-
const promise = new Promise((resolve, reject) => {
|
|
60
|
-
settle = { resolve, reject };
|
|
61
|
-
});
|
|
62
|
-
return { promise, settle: settle };
|
|
63
|
-
}
|
|
64
|
-
function heldBridge() {
|
|
65
|
-
const held = globalThis;
|
|
66
|
-
if (held.houseBridge === undefined || held.houseRuntime === undefined)
|
|
67
|
-
throw new Error('house_bridge_absent');
|
|
68
|
-
return { port: held.houseBridge, runtime: held.houseRuntime };
|
|
69
|
-
}
|
|
70
|
-
export function connectHouseBridge(source = heldBridge()) {
|
|
71
|
-
const ceremonies = [];
|
|
72
|
-
const navigations = [];
|
|
73
|
-
const attachments = new Map();
|
|
74
|
-
let nextAttachment = 0;
|
|
75
|
-
const sources = new Map();
|
|
76
|
-
let nextSource = 0;
|
|
77
|
-
const installations = new Map();
|
|
78
|
-
let nextInstallation = 0;
|
|
79
|
-
const notifications = new Map();
|
|
80
|
-
let nextNotifications = 0;
|
|
81
|
-
const badges = new Map();
|
|
82
|
-
let nextBadge = 0;
|
|
83
|
-
const screenshots = new Map();
|
|
84
|
-
let nextScreenshot = 0;
|
|
85
|
-
const externals = new Map();
|
|
86
|
-
let nextExternal = 0;
|
|
87
|
-
const addressListeners = new Set();
|
|
88
|
-
const statusListeners = new Set();
|
|
89
|
-
const messageListeners = new Set();
|
|
90
|
-
let outbox = [];
|
|
91
|
-
let held = null;
|
|
92
|
-
let address = null;
|
|
93
|
-
let status = null;
|
|
94
|
-
function send(message) {
|
|
95
|
-
if (held === null)
|
|
96
|
-
outbox?.push(message);
|
|
97
|
-
else
|
|
98
|
-
held.postMessage(message);
|
|
99
|
-
}
|
|
100
|
-
function receive(event) {
|
|
101
|
-
const data = frameMessage(event.data);
|
|
102
|
-
if (data === null)
|
|
103
|
-
return;
|
|
104
|
-
if (data.house === 'status') {
|
|
105
|
-
status = data.status;
|
|
106
|
-
for (const listener of statusListeners)
|
|
107
|
-
listener(status);
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (data.house === 'app-message') {
|
|
111
|
-
const { from, body } = data;
|
|
112
|
-
for (const listener of messageListeners)
|
|
113
|
-
listener({ from, body });
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
if (data.house === 'address') {
|
|
117
|
-
address = String(data.address);
|
|
118
|
-
if (data.navigated === true)
|
|
119
|
-
navigations.shift()?.settle.resolve(address);
|
|
120
|
-
for (const listener of addressListeners)
|
|
121
|
-
listener(address);
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
if (data.house === 'navigate-refused') {
|
|
125
|
-
const refused = data.address;
|
|
126
|
-
const named = typeof refused === 'string' ? refused : null;
|
|
127
|
-
const index = named === null ? 0 : navigations.findIndex((one) => one.address === named);
|
|
128
|
-
const [pending] = navigations.splice(index < 0 ? 0 : index, 1);
|
|
129
|
-
pending?.settle.reject(new HouseNavigationRefused(named));
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
if (data.house === 'attachment') {
|
|
133
|
-
if (typeof data.id !== 'number')
|
|
134
|
-
return;
|
|
135
|
-
const pending = attachments.get(data.id);
|
|
136
|
-
if (pending === undefined)
|
|
137
|
-
return;
|
|
138
|
-
attachments.delete(data.id);
|
|
139
|
-
pending.resolve(data.image ?? null);
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
if (data.house === 'install') {
|
|
143
|
-
const pending = pendingAnswer(installations, data);
|
|
144
|
-
if (pending !== null && 'outcome' in data)
|
|
145
|
-
pending.resolve(data.outcome);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
if (data.house === 'notifications') {
|
|
149
|
-
const pending = pendingAnswer(notifications, data);
|
|
150
|
-
if (pending !== null && 'state' in data)
|
|
151
|
-
pending.resolve(data.state);
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
if (data.house === 'badge') {
|
|
155
|
-
pendingAnswer(badges, data)?.resolve();
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
if (data.house === 'source') {
|
|
159
|
-
if (typeof data.id !== 'number')
|
|
160
|
-
return;
|
|
161
|
-
const pending = sources.get(data.id);
|
|
162
|
-
if (pending === undefined)
|
|
163
|
-
return;
|
|
164
|
-
sources.delete(data.id);
|
|
165
|
-
pending.resolve(data.answer ?? null);
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
if (data.house === 'external-call') {
|
|
169
|
-
if (typeof data.id !== 'number')
|
|
170
|
-
return;
|
|
171
|
-
const pending = externals.get(data.id);
|
|
172
|
-
if (pending === undefined)
|
|
173
|
-
return;
|
|
174
|
-
externals.delete(data.id);
|
|
175
|
-
pending.resolve(data.answer ?? null);
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
if (data.house === 'screenshot') {
|
|
179
|
-
if (typeof data.id !== 'number')
|
|
180
|
-
return;
|
|
181
|
-
const pending = screenshots.get(data.id);
|
|
182
|
-
if (pending === undefined)
|
|
183
|
-
return;
|
|
184
|
-
screenshots.delete(data.id);
|
|
185
|
-
pending.resolve(data.image ?? null);
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
if (data.house === 'ceremony-refused') {
|
|
189
|
-
const refused = data.path;
|
|
190
|
-
const named = typeof refused === 'string' ? refused : null;
|
|
191
|
-
const index = named === null ? 0 : ceremonies.findIndex((one) => one.path === named);
|
|
192
|
-
const [pending] = ceremonies.splice(index < 0 ? 0 : index, 1);
|
|
193
|
-
pending?.settle.reject(new HouseCeremonyRefused(named));
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
void source.port.then((port) => {
|
|
197
|
-
held = port;
|
|
198
|
-
port.addEventListener('message', receive);
|
|
199
|
-
port.start();
|
|
200
|
-
const waiting = outbox ?? [];
|
|
201
|
-
outbox = null;
|
|
202
|
-
for (const message of waiting)
|
|
203
|
-
port.postMessage(message);
|
|
204
|
-
});
|
|
205
|
-
function readSource(house, app, release) {
|
|
206
|
-
const pending = settled();
|
|
207
|
-
const id = ++nextSource;
|
|
208
|
-
sources.set(id, pending.settle);
|
|
209
|
-
send({ house, id, app, release });
|
|
210
|
-
return pending.promise;
|
|
211
|
-
}
|
|
212
|
-
function screenshot(house, asked) {
|
|
213
|
-
const pending = settled();
|
|
214
|
-
const id = ++nextScreenshot;
|
|
215
|
-
screenshots.set(id, pending.settle);
|
|
216
|
-
if (house === 'screenshot-read' && 'app' in asked)
|
|
217
|
-
send({ house, id, app: asked.app });
|
|
218
|
-
if (house === 'site-screenshot-read' && 'site' in asked)
|
|
219
|
-
send({ house, id, site: asked.site });
|
|
220
|
-
return pending.promise;
|
|
221
|
-
}
|
|
222
|
-
function navigate(mode, asked) {
|
|
223
|
-
const pending = settled();
|
|
224
|
-
navigations.push({ address: asked, settle: pending.settle });
|
|
225
|
-
send({ house: 'navigate', mode, address: asked });
|
|
226
|
-
return pending.promise;
|
|
227
|
-
}
|
|
228
|
-
return {
|
|
229
|
-
runtime: source.runtime,
|
|
230
|
-
address() {
|
|
231
|
-
return address;
|
|
232
|
-
},
|
|
233
|
-
onAddress(listener) {
|
|
234
|
-
addressListeners.add(listener);
|
|
235
|
-
return () => {
|
|
236
|
-
addressListeners.delete(listener);
|
|
237
|
-
};
|
|
238
|
-
},
|
|
239
|
-
push(asked) {
|
|
240
|
-
return navigate('push', asked);
|
|
241
|
-
},
|
|
242
|
-
replace(asked) {
|
|
243
|
-
return navigate('replace', asked);
|
|
244
|
-
},
|
|
245
|
-
ceremony(path) {
|
|
246
|
-
const pending = settled();
|
|
247
|
-
ceremonies.push({ path, settle: pending.settle });
|
|
248
|
-
send({ house: 'ceremony', path });
|
|
249
|
-
return pending.promise;
|
|
250
|
-
},
|
|
251
|
-
install() {
|
|
252
|
-
const pending = settled();
|
|
253
|
-
const id = ++nextInstallation;
|
|
254
|
-
installations.set(id, pending.settle);
|
|
255
|
-
send({ house: 'install', id });
|
|
256
|
-
return pending.promise;
|
|
257
|
-
},
|
|
258
|
-
notifications(on) {
|
|
259
|
-
const pending = settled();
|
|
260
|
-
const id = ++nextNotifications;
|
|
261
|
-
notifications.set(id, pending.settle);
|
|
262
|
-
send({ house: 'notifications', id, on });
|
|
263
|
-
return pending.promise;
|
|
264
|
-
},
|
|
265
|
-
setBadge(unread) {
|
|
266
|
-
const pending = settled();
|
|
267
|
-
const id = ++nextBadge;
|
|
268
|
-
badges.set(id, pending.settle);
|
|
269
|
-
send({ house: 'badge', id, unread });
|
|
270
|
-
return pending.promise;
|
|
271
|
-
},
|
|
272
|
-
setTab(tab) {
|
|
273
|
-
send({ house: 'tab', title: tab.title, icon: tab.icon });
|
|
274
|
-
},
|
|
275
|
-
readAttachment(room, attachment) {
|
|
276
|
-
const pending = settled();
|
|
277
|
-
const id = ++nextAttachment;
|
|
278
|
-
attachments.set(id, pending.settle);
|
|
279
|
-
send({ house: 'attachment-read', id, room, attachment });
|
|
280
|
-
return pending.promise;
|
|
281
|
-
},
|
|
282
|
-
sourceTop(app, release) {
|
|
283
|
-
return readSource('source-top', app, release);
|
|
284
|
-
},
|
|
285
|
-
sourceDownload(app, release) {
|
|
286
|
-
return readSource('source-download', app, release);
|
|
287
|
-
},
|
|
288
|
-
callExternal(request) {
|
|
289
|
-
const pending = settled();
|
|
290
|
-
const id = ++nextExternal;
|
|
291
|
-
externals.set(id, pending.settle);
|
|
292
|
-
send({ house: 'external-call', id, request });
|
|
293
|
-
return pending.promise;
|
|
294
|
-
},
|
|
295
|
-
readScreenshot(app) {
|
|
296
|
-
return screenshot('screenshot-read', { app });
|
|
297
|
-
},
|
|
298
|
-
readSiteScreenshot(site) {
|
|
299
|
-
return screenshot('site-screenshot-read', { site });
|
|
300
|
-
},
|
|
301
|
-
status() {
|
|
302
|
-
return status;
|
|
303
|
-
},
|
|
304
|
-
onStatus(listener) {
|
|
305
|
-
statusListeners.add(listener);
|
|
306
|
-
return () => {
|
|
307
|
-
statusListeners.delete(listener);
|
|
308
|
-
};
|
|
309
|
-
},
|
|
310
|
-
messages(listener) {
|
|
311
|
-
messageListeners.add(listener);
|
|
312
|
-
return () => {
|
|
313
|
-
messageListeners.delete(listener);
|
|
314
|
-
};
|
|
315
|
-
},
|
|
316
|
-
gaConsent(choice) {
|
|
317
|
-
send({ house: 'ga/consent', choice });
|
|
318
|
-
},
|
|
319
|
-
};
|
|
320
|
-
}
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
export { connectHouseBridge, HouseCeremonyRefused, HouseNavigationRefused, type HouseBridge, type HouseBridgeSource, type HouseImageCopy, } from './bridge.js';
|
|
2
|
-
export { HOUSE_ADDRESS_MAX_CHARS } from './wire.js';
|
|
3
|
-
export type { HouseAppMessage, HouseBridgeEvent, HouseBridgePort, HouseCredentialState, HouseExternalAnswer, HouseExternalRequest, HouseGa, HouseGaChoice, HouseNavigationMode, HouseRefusal, HouseSourceDownload, HouseSourceFile, HouseSourceTop, HouseStatus, HouseSyncState, } from './wire.js';
|
package/dist/client/src/index.js
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import { appAddressOf } from '../../src/apps/remainder.js';
|
|
2
|
-
import { tabChange } from '../../src/apps/tab.js';
|
|
3
|
-
import { HOUSE_ADDRESS_MAX_CHARS } from './wire.js';
|
|
4
|
-
const HOST_ORIGIN = 'https://house.invalid';
|
|
5
|
-
function sameOriginPath(value) {
|
|
6
|
-
if (typeof value !== 'string')
|
|
7
|
-
return null;
|
|
8
|
-
if (value.charAt(0) !== '/' || value.slice(0, 2) === '//')
|
|
9
|
-
return null;
|
|
10
|
-
if (value.length > HOUSE_ADDRESS_MAX_CHARS)
|
|
11
|
-
return null;
|
|
12
|
-
const target = new URL(value, HOST_ORIGIN);
|
|
13
|
-
return target.origin === HOST_ORIGIN ? `${target.pathname}${target.search}${target.hash}` : null;
|
|
14
|
-
}
|
|
15
|
-
export function createHouseBridgeTestHost(options = {}) {
|
|
16
|
-
const navigations = [];
|
|
17
|
-
const ceremonies = [];
|
|
18
|
-
const consents = [];
|
|
19
|
-
const tabs = [];
|
|
20
|
-
const externalCalls = [];
|
|
21
|
-
const listeners = new Set();
|
|
22
|
-
const queued = [];
|
|
23
|
-
let started = false;
|
|
24
|
-
let address = appAddressOf('/', options.address ?? '/');
|
|
25
|
-
let status = options.status ?? null;
|
|
26
|
-
let attached = () => { };
|
|
27
|
-
const runtime = new Promise((resolve) => {
|
|
28
|
-
attached = resolve;
|
|
29
|
-
});
|
|
30
|
-
function deliver(data) {
|
|
31
|
-
const event = { data };
|
|
32
|
-
if (!started) {
|
|
33
|
-
queued.push(event);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
queueMicrotask(() => {
|
|
37
|
-
for (const listener of listeners)
|
|
38
|
-
listener(event);
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
function arrive(target, navigated) {
|
|
42
|
-
address = target;
|
|
43
|
-
deliver({ house: 'address', address, navigated });
|
|
44
|
-
}
|
|
45
|
-
function navigate(mode, asked) {
|
|
46
|
-
const target = sameOriginPath(asked);
|
|
47
|
-
if (target === null) {
|
|
48
|
-
deliver({
|
|
49
|
-
house: 'navigate-refused',
|
|
50
|
-
address: typeof asked === 'string' ? asked : null,
|
|
51
|
-
});
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
navigations.push({ mode: mode === 'replace' ? 'replace' : 'push', address: target });
|
|
55
|
-
arrive(target, true);
|
|
56
|
-
}
|
|
57
|
-
function ceremony(path) {
|
|
58
|
-
const target = sameOriginPath(path);
|
|
59
|
-
if (target === null) {
|
|
60
|
-
deliver({ house: 'ceremony-refused', path: typeof path === 'string' ? path : null });
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
ceremonies.push(target);
|
|
64
|
-
}
|
|
65
|
-
function publish(next) {
|
|
66
|
-
status = next;
|
|
67
|
-
deliver({ house: 'status', status });
|
|
68
|
-
}
|
|
69
|
-
function consent(choice) {
|
|
70
|
-
if (status === null || !status.ga.measures)
|
|
71
|
-
return;
|
|
72
|
-
if (choice !== 'accept' && choice !== 'decline')
|
|
73
|
-
return;
|
|
74
|
-
consents.push(choice);
|
|
75
|
-
publish({ ...status, ga: { measures: true, choice } });
|
|
76
|
-
}
|
|
77
|
-
function receive(message) {
|
|
78
|
-
const data = message;
|
|
79
|
-
if (data === null || typeof data !== 'object')
|
|
80
|
-
return;
|
|
81
|
-
if (data.house === 'navigate') {
|
|
82
|
-
navigate(data.mode, data.address);
|
|
83
|
-
}
|
|
84
|
-
else if (data.house === 'ceremony') {
|
|
85
|
-
ceremony(data.path);
|
|
86
|
-
}
|
|
87
|
-
else if (data.house === 'install') {
|
|
88
|
-
deliver({ house: 'install', id: data.id, outcome: 'manual' });
|
|
89
|
-
}
|
|
90
|
-
else if (data.house === 'notifications') {
|
|
91
|
-
deliver({ house: 'notifications', id: data.id, state: 'unavailable' });
|
|
92
|
-
}
|
|
93
|
-
else if (data.house === 'badge') {
|
|
94
|
-
deliver({ house: 'badge', id: data.id });
|
|
95
|
-
}
|
|
96
|
-
else if (data.house === 'tab') {
|
|
97
|
-
const change = tabChange(data);
|
|
98
|
-
if (change !== null)
|
|
99
|
-
tabs.push(change);
|
|
100
|
-
}
|
|
101
|
-
else if (data.house === 'ga/consent') {
|
|
102
|
-
consent(data.choice);
|
|
103
|
-
}
|
|
104
|
-
else if (data.house === 'source-top' || data.house === 'source-download') {
|
|
105
|
-
const asked = data;
|
|
106
|
-
const source = options.sources?.find((entry) => entry.top.app_ref === asked.app);
|
|
107
|
-
const answer = source === undefined ? null : data.house === 'source-top' ? source.top : source.download;
|
|
108
|
-
deliver({ house: 'source', id: asked.id, answer });
|
|
109
|
-
}
|
|
110
|
-
else if (data.house === 'attachment-read') {
|
|
111
|
-
const asked = data;
|
|
112
|
-
const image = options.images?.find((entry) => entry.room === asked.room && entry.attachment === asked.attachment);
|
|
113
|
-
deliver({
|
|
114
|
-
house: 'attachment',
|
|
115
|
-
id: asked.id,
|
|
116
|
-
image: image === undefined
|
|
117
|
-
? null
|
|
118
|
-
: {
|
|
119
|
-
attachment: image.attachment,
|
|
120
|
-
copy: image.copy,
|
|
121
|
-
name: image.name,
|
|
122
|
-
media_type: image.media_type,
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
else if (data.house === 'external-call') {
|
|
127
|
-
const asked = data;
|
|
128
|
-
externalCalls.push(asked.request);
|
|
129
|
-
void Promise.resolve(options.external?.(asked.request) ?? null).then((answer) => deliver({ house: 'external-call', id: asked.id, answer }));
|
|
130
|
-
}
|
|
131
|
-
else if (data.house === 'screenshot-read') {
|
|
132
|
-
const asked = data;
|
|
133
|
-
const held = typeof asked.app === 'string' ? options.screenshots?.[asked.app] : undefined;
|
|
134
|
-
deliver({ house: 'screenshot', id: asked.id, image: held ?? null });
|
|
135
|
-
}
|
|
136
|
-
else if (data.house === 'site-screenshot-read') {
|
|
137
|
-
const asked = data;
|
|
138
|
-
const held = typeof asked.site === 'string' ? options.siteScreenshots?.[asked.site] : undefined;
|
|
139
|
-
deliver({ house: 'screenshot', id: asked.id, image: held ?? null });
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
const port = {
|
|
143
|
-
postMessage(message) {
|
|
144
|
-
receive(message);
|
|
145
|
-
},
|
|
146
|
-
addEventListener(_type, listener) {
|
|
147
|
-
listeners.add(listener);
|
|
148
|
-
},
|
|
149
|
-
start() {
|
|
150
|
-
started = true;
|
|
151
|
-
const held = queued.splice(0);
|
|
152
|
-
queueMicrotask(() => {
|
|
153
|
-
for (const event of held) {
|
|
154
|
-
for (const listener of listeners)
|
|
155
|
-
listener(event);
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
},
|
|
159
|
-
};
|
|
160
|
-
if (status !== null)
|
|
161
|
-
deliver({ house: 'status', status });
|
|
162
|
-
deliver({ house: 'address', address, navigated: false });
|
|
163
|
-
return {
|
|
164
|
-
port: Promise.resolve(port),
|
|
165
|
-
runtime,
|
|
166
|
-
navigations,
|
|
167
|
-
ceremonies,
|
|
168
|
-
consents,
|
|
169
|
-
tabs,
|
|
170
|
-
externalCalls,
|
|
171
|
-
address() {
|
|
172
|
-
return address;
|
|
173
|
-
},
|
|
174
|
-
open(asked) {
|
|
175
|
-
arrive(appAddressOf('/', asked), false);
|
|
176
|
-
},
|
|
177
|
-
publish,
|
|
178
|
-
message(message) {
|
|
179
|
-
deliver({ house: 'app-message', from: message.from, body: message.body });
|
|
180
|
-
},
|
|
181
|
-
attachRuntime(client) {
|
|
182
|
-
attached(client);
|
|
183
|
-
},
|
|
184
|
-
};
|
|
185
|
-
}
|