@agentshouse/app-bridge 0.1.0-alpha.1 → 0.1.0-alpha.10
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 +68 -7
- package/client/src/bridge.d.ts +38 -0
- package/client/src/bridge.js +261 -0
- package/client/src/index.d.ts +3 -0
- package/{test-host.d.ts → client/src/test-host.d.ts} +12 -1
- package/{test-host.js → client/src/test-host.js} +68 -9
- package/client/src/wire.d.ts +69 -0
- package/package.json +12 -8
- package/src/apps/remainder.d.ts +2 -0
- package/src/apps/remainder.js +16 -0
- package/src/apps/tab.d.ts +6 -0
- package/src/apps/tab.js +27 -0
- package/bridge.d.ts +0 -20
- package/bridge.js +0 -134
- package/index.d.ts +0 -3
- package/wire.d.ts +0 -30
- /package/{index.js → client/src/index.js} +0 -0
- /package/{wire.js → client/src/wire.js} +0 -0
package/README.md
CHANGED
|
@@ -2,18 +2,67 @@
|
|
|
2
2
|
|
|
3
3
|
The renderer-side client of the agents.house App bridge. An App served by House
|
|
4
4
|
receives one message port before its own scripts run; this package is that
|
|
5
|
-
port's one listener. It carries
|
|
5
|
+
port's one listener. It carries eight families:
|
|
6
6
|
|
|
7
7
|
- **Navigation** - the part of the shell's URL below the App's path, with its
|
|
8
8
|
query and fragment. The shell delivers it when the App starts and whenever the
|
|
9
|
-
User navigates; the App asks the shell to push or replace it.
|
|
9
|
+
User navigates; the App asks the shell to push or replace it. The App also
|
|
10
|
+
sets its tab's title and icon with `setTab({ title, icon })`, either or both:
|
|
11
|
+
a title of at most 200 characters and an icon given as a base64 `data:` URL
|
|
12
|
+
of an SVG, PNG, GIF or WebP image of at most 1,048,576 characters. The shell
|
|
13
|
+
shows them over the App's display name, or a Site's title, and its tab icon
|
|
14
|
+
until the next load, and ignores a request outside those bounds.
|
|
10
15
|
- **Ceremony** - the App asks the shell to leave for one of House's own
|
|
11
16
|
purpose-bound authenticated pages. The shell navigates only to a same-origin
|
|
12
17
|
absolute path, carries its current address as the return target, and the page
|
|
13
18
|
returns there when it completes. An App names an address House gave it in a
|
|
14
19
|
reading and composes none.
|
|
15
|
-
- **
|
|
16
|
-
|
|
20
|
+
- **Installation** - `install()` offers the page as a Home-screen app. Where the
|
|
21
|
+
browser has an install prompt ready the shell shows it and the call resolves
|
|
22
|
+
`'accepted'` or `'dismissed'` with the User's choice, spending that prompt;
|
|
23
|
+
otherwise, as on every iPhone and iPad, it resolves `'manual'` and the App
|
|
24
|
+
shows its own instructions. A prompt the browser refuses to show, as for a
|
|
25
|
+
call made without a click, rejects with the browser's own `DOMException`
|
|
26
|
+
and stays ready. Call it from a click. The standard
|
|
27
|
+
`display-mode: standalone` media query tells the App it runs installed.
|
|
28
|
+
- **Notifications** - House's own panel alone; an App's or a Site's call
|
|
29
|
+
rejects with `HouseCeremonyRefused`. `notifications()` answers this browser
|
|
30
|
+
storage's push state: `'on'`, `'off'`, `'blocked'` where the browser refuses
|
|
31
|
+
notifications, or `'unavailable'` where it has no push, as on an iPhone or
|
|
32
|
+
iPad outside a Home-screen app. `notifications(true)` asks the browser's
|
|
33
|
+
permission and subscribes, `notifications(false)` unsubscribes, and each
|
|
34
|
+
answers the state it leaves; a failure of the browser's push service rejects
|
|
35
|
+
with the browser's own `DOMException`. Call `notifications(true)` from a
|
|
36
|
+
click. `setBadge(unread)` takes the identifiers of the Human Notifications
|
|
37
|
+
unread in this storage and sets the icon's badge to their number, or clears
|
|
38
|
+
it at none, where the browser shows one.
|
|
39
|
+
- **Status** - the App reference the Runtime port is attached under, sync
|
|
40
|
+
state, credential state, release availability and observed failure, `ga`:
|
|
41
|
+
whether the page measures and the visitor's stored analytics choice, and
|
|
42
|
+
`service_mode`: `open`, `write-halted` while House refuses writes, so the
|
|
43
|
+
App can stop retrying them, or `null` while the runtime holds no House
|
|
44
|
+
connection.
|
|
45
|
+
The App reports the visitor's choice with `gaConsent('accept')` or
|
|
46
|
+
`gaConsent('decline')`; the shell hands it to House's analytics loader, and
|
|
47
|
+
the choice never reaches House.
|
|
48
|
+
- **Site image read** - given a Published Room and an original attachment
|
|
49
|
+
reference, it answers the web copy's path and metadata, or `null` when no
|
|
50
|
+
copy exists. The original bytes never cross this bridge at a Site.
|
|
51
|
+
- **App release code** - `sourceTop(app, release)` answers the top level of
|
|
52
|
+
the latest successful release of an App the User may use: its folders, its
|
|
53
|
+
files with their sizes and its root README's text, or `null` for none;
|
|
54
|
+
`sourceDownload(app, release)` answers every file of that release with its
|
|
55
|
+
path, its size and its `text`, or its bytes as `base64`. The browser keeps
|
|
56
|
+
each answer per App and release: a read naming a release it holds answers
|
|
57
|
+
without asking House, and keeping a newer release drops the earlier one. A
|
|
58
|
+
refusal is House's `{ error: { code } }`, `app_release_not_found` for an
|
|
59
|
+
App with no release; a read House does not answer, or one that outlived
|
|
60
|
+
the browser's signed-in User, is `null`.
|
|
61
|
+
- **App screenshot read** - given an App reference, `readScreenshot(app)`
|
|
62
|
+
answers the image this browser captured of that App's screen the first time
|
|
63
|
+
it ran its current release, as a PNG `Blob` at most 1200 pixels wide, or
|
|
64
|
+
`null` when this browser holds none. The image stays in the browser; a Site
|
|
65
|
+
holds none.
|
|
17
66
|
|
|
18
67
|
The package carries no credential handling and no Runtime family. It surfaces
|
|
19
68
|
the dedicated Runtime port the worker transfers over the bridge as a plain
|
|
@@ -30,6 +79,7 @@ const md = connectMdRuntime(await bridge.runtime);
|
|
|
30
79
|
|
|
31
80
|
bridge.onAddress((address) => render(address));
|
|
32
81
|
bridge.onStatus((status) => showSync(status.sync));
|
|
82
|
+
bridge.setTab({ title: 'Atlas (3)' });
|
|
33
83
|
|
|
34
84
|
open('/rooms/atlas');
|
|
35
85
|
function open(address) {
|
|
@@ -63,6 +113,17 @@ host.open('/rooms/atlas');
|
|
|
63
113
|
```
|
|
64
114
|
|
|
65
115
|
The host plays the shell and the worker: it delivers the address the App starts
|
|
66
|
-
at
|
|
67
|
-
|
|
68
|
-
|
|
116
|
+
at and each address `open` plays as Back or Forward through the shell's own
|
|
117
|
+
slice below the App's path, admitting nothing, so it delivers `//notes` exactly
|
|
118
|
+
as a browser at `/apps/<app>//notes` does. It records every push and replace
|
|
119
|
+
and answers each with the address the shell would then show, records an
|
|
120
|
+
admitted ceremony, refuses a push, replace or ceremony that is not a
|
|
121
|
+
same-origin absolute path, publishes a status, its `service_mode` included,
|
|
122
|
+
records in `consents` each
|
|
123
|
+
`gaConsent` it takes while that status measures and publishes the status the
|
|
124
|
+
choice leaves, records in `tabs` each `setTab` the shell would take and ignores
|
|
125
|
+
any other, answers a release code read from the `sources` it was given, or
|
|
126
|
+
`null`, answers `readScreenshot` from the `screenshots` it was given by App
|
|
127
|
+
reference, answers every `install()` `'manual'`, as a browser with no install
|
|
128
|
+
prompt does, answers every `notifications()` `'unavailable'`, as a browser
|
|
129
|
+
without push does, settles every `setBadge`, and attaches a Runtime port.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { HouseBridgePort, HouseGaChoice, HouseMessagePort, HouseSourceDownload, HouseSourceRefusal, HouseSourceTop, HouseStatus, HouseTab } from './wire.js';
|
|
2
|
+
export declare class HouseCeremonyRefused extends Error {
|
|
3
|
+
readonly path: string | null;
|
|
4
|
+
constructor(path: string | null);
|
|
5
|
+
}
|
|
6
|
+
export declare class HouseNavigationRefused extends Error {
|
|
7
|
+
readonly address: string | null;
|
|
8
|
+
constructor(address: string | null);
|
|
9
|
+
}
|
|
10
|
+
export interface HouseImageCopy {
|
|
11
|
+
readonly attachment: string;
|
|
12
|
+
readonly copy: string;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly media_type: string;
|
|
15
|
+
}
|
|
16
|
+
type HouseInstallation = 'accepted' | 'dismissed' | 'manual';
|
|
17
|
+
type HouseNotifications = 'on' | 'off' | 'blocked' | 'unavailable';
|
|
18
|
+
export interface HouseBridge {
|
|
19
|
+
readonly runtime: Promise<HouseMessagePort>;
|
|
20
|
+
address(): string | null;
|
|
21
|
+
onAddress(listener: (address: string) => void): () => void;
|
|
22
|
+
push(address: string): Promise<string>;
|
|
23
|
+
replace(address: string): Promise<string>;
|
|
24
|
+
ceremony(path: string): Promise<never>;
|
|
25
|
+
install(): Promise<HouseInstallation>;
|
|
26
|
+
notifications(on?: boolean): Promise<HouseNotifications>;
|
|
27
|
+
setBadge(unread: readonly string[]): Promise<void>;
|
|
28
|
+
setTab(tab: HouseTab): void;
|
|
29
|
+
readAttachment(room: string, attachment: string): Promise<HouseImageCopy | null>;
|
|
30
|
+
sourceTop(app: string, release?: string): Promise<HouseSourceTop | HouseSourceRefusal | null>;
|
|
31
|
+
sourceDownload(app: string, release?: string): Promise<HouseSourceDownload | HouseSourceRefusal | null>;
|
|
32
|
+
readScreenshot(app: string): Promise<Blob | null>;
|
|
33
|
+
status(): HouseStatus | null;
|
|
34
|
+
onStatus(listener: (status: HouseStatus) => void): () => void;
|
|
35
|
+
gaConsent(choice: HouseGaChoice): void;
|
|
36
|
+
}
|
|
37
|
+
export declare function connectHouseBridge(source?: Promise<HouseBridgePort>): HouseBridge;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,261 @@
|
|
|
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 answered(pending, answer, value) {
|
|
18
|
+
if (typeof answer.id !== 'number')
|
|
19
|
+
return;
|
|
20
|
+
const asked = pending.get(answer.id);
|
|
21
|
+
if (asked === undefined)
|
|
22
|
+
return;
|
|
23
|
+
pending.delete(answer.id);
|
|
24
|
+
if (answer.refused === true)
|
|
25
|
+
asked.reject(new HouseCeremonyRefused(null));
|
|
26
|
+
else if (answer.refusal !== undefined)
|
|
27
|
+
asked.reject(new DOMException(answer.refusal.message, answer.refusal.name));
|
|
28
|
+
else
|
|
29
|
+
asked.resolve(value);
|
|
30
|
+
}
|
|
31
|
+
function settled() {
|
|
32
|
+
let settle = null;
|
|
33
|
+
const promise = new Promise((resolve, reject) => {
|
|
34
|
+
settle = { resolve, reject };
|
|
35
|
+
});
|
|
36
|
+
return { promise, settle: settle };
|
|
37
|
+
}
|
|
38
|
+
function heldBridge() {
|
|
39
|
+
const held = globalThis.houseBridge;
|
|
40
|
+
if (held === undefined)
|
|
41
|
+
throw new Error('house_bridge_absent');
|
|
42
|
+
return held;
|
|
43
|
+
}
|
|
44
|
+
export function connectHouseBridge(source = heldBridge()) {
|
|
45
|
+
const runtime = settled();
|
|
46
|
+
const ceremonies = [];
|
|
47
|
+
const navigations = [];
|
|
48
|
+
const attachments = new Map();
|
|
49
|
+
let nextAttachment = 0;
|
|
50
|
+
const sources = new Map();
|
|
51
|
+
let nextSource = 0;
|
|
52
|
+
const installations = new Map();
|
|
53
|
+
let nextInstallation = 0;
|
|
54
|
+
const notifications = new Map();
|
|
55
|
+
let nextNotifications = 0;
|
|
56
|
+
const badges = new Map();
|
|
57
|
+
let nextBadge = 0;
|
|
58
|
+
const screenshots = new Map();
|
|
59
|
+
let nextScreenshot = 0;
|
|
60
|
+
const addressListeners = new Set();
|
|
61
|
+
const statusListeners = new Set();
|
|
62
|
+
let outbox = [];
|
|
63
|
+
let held = null;
|
|
64
|
+
let address = null;
|
|
65
|
+
let status = null;
|
|
66
|
+
function send(message) {
|
|
67
|
+
if (held === null)
|
|
68
|
+
outbox?.push(message);
|
|
69
|
+
else
|
|
70
|
+
held.postMessage(message);
|
|
71
|
+
}
|
|
72
|
+
function receive(event) {
|
|
73
|
+
const data = event.data;
|
|
74
|
+
if (data === null || typeof data !== 'object')
|
|
75
|
+
return;
|
|
76
|
+
if (data.house === 'runtime') {
|
|
77
|
+
const [port] = event.ports;
|
|
78
|
+
if (port !== undefined)
|
|
79
|
+
runtime.settle.resolve(port);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (data.house === 'status') {
|
|
83
|
+
status = data.status;
|
|
84
|
+
for (const listener of statusListeners)
|
|
85
|
+
listener(status);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (data.house === 'address') {
|
|
89
|
+
address = String(data.address);
|
|
90
|
+
if (data.navigated === true)
|
|
91
|
+
navigations.shift()?.settle.resolve(address);
|
|
92
|
+
for (const listener of addressListeners)
|
|
93
|
+
listener(address);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (data.house === 'navigate-refused') {
|
|
97
|
+
const refused = data.address;
|
|
98
|
+
const named = typeof refused === 'string' ? refused : null;
|
|
99
|
+
const index = named === null ? 0 : navigations.findIndex((one) => one.address === named);
|
|
100
|
+
const [pending] = navigations.splice(index < 0 ? 0 : index, 1);
|
|
101
|
+
pending?.settle.reject(new HouseNavigationRefused(named));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (data.house === 'attachment') {
|
|
105
|
+
const answer = data;
|
|
106
|
+
if (typeof answer.id !== 'number')
|
|
107
|
+
return;
|
|
108
|
+
const pending = attachments.get(answer.id);
|
|
109
|
+
if (pending === undefined)
|
|
110
|
+
return;
|
|
111
|
+
attachments.delete(answer.id);
|
|
112
|
+
pending.resolve(answer.image ?? null);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (data.house === 'install') {
|
|
116
|
+
const answer = data;
|
|
117
|
+
answered(installations, answer, answer.outcome);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (data.house === 'notifications') {
|
|
121
|
+
const answer = data;
|
|
122
|
+
answered(notifications, answer, answer.state);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (data.house === 'badge') {
|
|
126
|
+
answered(badges, data, undefined);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (data.house === 'source') {
|
|
130
|
+
const answer = data;
|
|
131
|
+
if (typeof answer.id !== 'number')
|
|
132
|
+
return;
|
|
133
|
+
const pending = sources.get(answer.id);
|
|
134
|
+
if (pending === undefined)
|
|
135
|
+
return;
|
|
136
|
+
sources.delete(answer.id);
|
|
137
|
+
pending.resolve(answer.answer ?? null);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (data.house === 'screenshot') {
|
|
141
|
+
const answer = data;
|
|
142
|
+
if (typeof answer.id !== 'number')
|
|
143
|
+
return;
|
|
144
|
+
const pending = screenshots.get(answer.id);
|
|
145
|
+
if (pending === undefined)
|
|
146
|
+
return;
|
|
147
|
+
screenshots.delete(answer.id);
|
|
148
|
+
pending.resolve(answer.image ?? null);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (data.house === 'ceremony-refused') {
|
|
152
|
+
const refused = data.path;
|
|
153
|
+
const named = typeof refused === 'string' ? refused : null;
|
|
154
|
+
const index = named === null ? 0 : ceremonies.findIndex((one) => one.path === named);
|
|
155
|
+
const [pending] = ceremonies.splice(index < 0 ? 0 : index, 1);
|
|
156
|
+
pending?.settle.reject(new HouseCeremonyRefused(named));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
void source.then((port) => {
|
|
160
|
+
held = port;
|
|
161
|
+
port.addEventListener('message', receive);
|
|
162
|
+
port.start();
|
|
163
|
+
const waiting = outbox ?? [];
|
|
164
|
+
outbox = null;
|
|
165
|
+
for (const message of waiting)
|
|
166
|
+
port.postMessage(message);
|
|
167
|
+
});
|
|
168
|
+
function readSource(house, app, release) {
|
|
169
|
+
const pending = settled();
|
|
170
|
+
const id = ++nextSource;
|
|
171
|
+
sources.set(id, pending.settle);
|
|
172
|
+
send({ house, id, app, release });
|
|
173
|
+
return pending.promise;
|
|
174
|
+
}
|
|
175
|
+
function navigate(mode, asked) {
|
|
176
|
+
const pending = settled();
|
|
177
|
+
navigations.push({ address: asked, settle: pending.settle });
|
|
178
|
+
send({ house: 'navigate', mode, address: asked });
|
|
179
|
+
return pending.promise;
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
runtime: runtime.promise,
|
|
183
|
+
address() {
|
|
184
|
+
return address;
|
|
185
|
+
},
|
|
186
|
+
onAddress(listener) {
|
|
187
|
+
addressListeners.add(listener);
|
|
188
|
+
return () => {
|
|
189
|
+
addressListeners.delete(listener);
|
|
190
|
+
};
|
|
191
|
+
},
|
|
192
|
+
push(asked) {
|
|
193
|
+
return navigate('push', asked);
|
|
194
|
+
},
|
|
195
|
+
replace(asked) {
|
|
196
|
+
return navigate('replace', asked);
|
|
197
|
+
},
|
|
198
|
+
ceremony(path) {
|
|
199
|
+
const pending = settled();
|
|
200
|
+
ceremonies.push({ path, settle: pending.settle });
|
|
201
|
+
send({ house: 'ceremony', path });
|
|
202
|
+
return pending.promise;
|
|
203
|
+
},
|
|
204
|
+
install() {
|
|
205
|
+
const pending = settled();
|
|
206
|
+
const id = ++nextInstallation;
|
|
207
|
+
installations.set(id, pending.settle);
|
|
208
|
+
send({ house: 'install', id });
|
|
209
|
+
return pending.promise;
|
|
210
|
+
},
|
|
211
|
+
notifications(on) {
|
|
212
|
+
const pending = settled();
|
|
213
|
+
const id = ++nextNotifications;
|
|
214
|
+
notifications.set(id, pending.settle);
|
|
215
|
+
send({ house: 'notifications', id, on });
|
|
216
|
+
return pending.promise;
|
|
217
|
+
},
|
|
218
|
+
setBadge(unread) {
|
|
219
|
+
const pending = settled();
|
|
220
|
+
const id = ++nextBadge;
|
|
221
|
+
badges.set(id, pending.settle);
|
|
222
|
+
send({ house: 'badge', id, unread });
|
|
223
|
+
return pending.promise;
|
|
224
|
+
},
|
|
225
|
+
setTab(tab) {
|
|
226
|
+
send({ house: 'tab', title: tab.title, icon: tab.icon });
|
|
227
|
+
},
|
|
228
|
+
readAttachment(room, attachment) {
|
|
229
|
+
const pending = settled();
|
|
230
|
+
const id = ++nextAttachment;
|
|
231
|
+
attachments.set(id, pending.settle);
|
|
232
|
+
send({ house: 'attachment-read', id, room, attachment });
|
|
233
|
+
return pending.promise;
|
|
234
|
+
},
|
|
235
|
+
sourceTop(app, release) {
|
|
236
|
+
return readSource('source-top', app, release);
|
|
237
|
+
},
|
|
238
|
+
sourceDownload(app, release) {
|
|
239
|
+
return readSource('source-download', app, release);
|
|
240
|
+
},
|
|
241
|
+
readScreenshot(app) {
|
|
242
|
+
const pending = settled();
|
|
243
|
+
const id = ++nextScreenshot;
|
|
244
|
+
screenshots.set(id, pending.settle);
|
|
245
|
+
send({ house: 'screenshot-read', id, app });
|
|
246
|
+
return pending.promise;
|
|
247
|
+
},
|
|
248
|
+
status() {
|
|
249
|
+
return status;
|
|
250
|
+
},
|
|
251
|
+
onStatus(listener) {
|
|
252
|
+
statusListeners.add(listener);
|
|
253
|
+
return () => {
|
|
254
|
+
statusListeners.delete(listener);
|
|
255
|
+
};
|
|
256
|
+
},
|
|
257
|
+
gaConsent(choice) {
|
|
258
|
+
send({ house: 'ga/consent', choice });
|
|
259
|
+
},
|
|
260
|
+
};
|
|
261
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { connectHouseBridge, HouseCeremonyRefused, HouseNavigationRefused, type HouseBridge, type HouseImageCopy, } from './bridge.js';
|
|
2
|
+
export { HOUSE_ADDRESS_MAX_CHARS } from './wire.js';
|
|
3
|
+
export type { HouseBridgeEvent, HouseBridgePort, HouseCredentialState, HouseGa, HouseGaChoice, HouseMessagePort, HouseNavigationMode, HouseSourceDownload, HouseSourceFile, HouseSourceRefusal, HouseSourceTop, HouseStatus, HouseSyncState, } from './wire.js';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HouseImageCopy } from './bridge.js';
|
|
2
|
+
import type { HouseBridgePort, HouseGaChoice, HouseMessagePort, HouseNavigationMode, HouseSourceDownload, HouseSourceTop, HouseStatus, HouseTab } from './wire.js';
|
|
2
3
|
export interface HouseBridgeNavigation {
|
|
3
4
|
readonly mode: HouseNavigationMode;
|
|
4
5
|
readonly address: string;
|
|
@@ -7,6 +8,8 @@ export interface HouseBridgeTestHost {
|
|
|
7
8
|
readonly port: Promise<HouseBridgePort>;
|
|
8
9
|
readonly navigations: readonly HouseBridgeNavigation[];
|
|
9
10
|
readonly ceremonies: readonly string[];
|
|
11
|
+
readonly consents: readonly HouseGaChoice[];
|
|
12
|
+
readonly tabs: readonly HouseTab[];
|
|
10
13
|
address(): string;
|
|
11
14
|
open(address: string): void;
|
|
12
15
|
publish(status: HouseStatus): void;
|
|
@@ -15,5 +18,13 @@ export interface HouseBridgeTestHost {
|
|
|
15
18
|
export interface HouseBridgeTestHostOptions {
|
|
16
19
|
readonly address?: string;
|
|
17
20
|
readonly status?: HouseStatus;
|
|
21
|
+
readonly images?: readonly (HouseImageCopy & {
|
|
22
|
+
readonly room: string;
|
|
23
|
+
})[];
|
|
24
|
+
readonly sources?: readonly {
|
|
25
|
+
readonly top: HouseSourceTop;
|
|
26
|
+
readonly download: HouseSourceDownload;
|
|
27
|
+
}[];
|
|
28
|
+
readonly screenshots?: Readonly<Record<string, Blob>>;
|
|
18
29
|
}
|
|
19
30
|
export declare function createHouseBridgeTestHost(options?: HouseBridgeTestHostOptions): HouseBridgeTestHost;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { appAddressOf } from '../../src/apps/remainder.js';
|
|
2
|
+
import { tabChange } from '../../src/apps/tab.js';
|
|
1
3
|
import { HOUSE_ADDRESS_MAX_CHARS } from './wire.js';
|
|
2
4
|
const HOST_ORIGIN = 'https://house.invalid';
|
|
3
5
|
function sameOriginPath(value) {
|
|
@@ -13,10 +15,13 @@ function sameOriginPath(value) {
|
|
|
13
15
|
export function createHouseBridgeTestHost(options = {}) {
|
|
14
16
|
const navigations = [];
|
|
15
17
|
const ceremonies = [];
|
|
18
|
+
const consents = [];
|
|
19
|
+
const tabs = [];
|
|
16
20
|
const listeners = new Set();
|
|
17
21
|
const queued = [];
|
|
18
22
|
let started = false;
|
|
19
|
-
let address =
|
|
23
|
+
let address = appAddressOf('/', options.address ?? '/');
|
|
24
|
+
let status = options.status ?? null;
|
|
20
25
|
function deliver(data, ports = []) {
|
|
21
26
|
const event = { data, ports };
|
|
22
27
|
if (!started) {
|
|
@@ -52,6 +57,18 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
52
57
|
}
|
|
53
58
|
ceremonies.push(target);
|
|
54
59
|
}
|
|
60
|
+
function publish(next) {
|
|
61
|
+
status = next;
|
|
62
|
+
deliver({ house: 'status', status });
|
|
63
|
+
}
|
|
64
|
+
function consent(choice) {
|
|
65
|
+
if (status === null || !status.ga.measures)
|
|
66
|
+
return;
|
|
67
|
+
if (choice !== 'accept' && choice !== 'decline')
|
|
68
|
+
return;
|
|
69
|
+
consents.push(choice);
|
|
70
|
+
publish({ ...status, ga: { measures: true, choice } });
|
|
71
|
+
}
|
|
55
72
|
function receive(message) {
|
|
56
73
|
const data = message;
|
|
57
74
|
if (data === null || typeof data !== 'object')
|
|
@@ -62,6 +79,50 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
62
79
|
else if (data.house === 'ceremony') {
|
|
63
80
|
ceremony(data.path);
|
|
64
81
|
}
|
|
82
|
+
else if (data.house === 'install') {
|
|
83
|
+
deliver({ house: 'install', id: data.id, outcome: 'manual' });
|
|
84
|
+
}
|
|
85
|
+
else if (data.house === 'notifications') {
|
|
86
|
+
deliver({ house: 'notifications', id: data.id, state: 'unavailable' });
|
|
87
|
+
}
|
|
88
|
+
else if (data.house === 'badge') {
|
|
89
|
+
deliver({ house: 'badge', id: data.id });
|
|
90
|
+
}
|
|
91
|
+
else if (data.house === 'tab') {
|
|
92
|
+
const change = tabChange(data);
|
|
93
|
+
if (change !== null)
|
|
94
|
+
tabs.push(change);
|
|
95
|
+
}
|
|
96
|
+
else if (data.house === 'ga/consent') {
|
|
97
|
+
consent(data.choice);
|
|
98
|
+
}
|
|
99
|
+
else if (data.house === 'source-top' || data.house === 'source-download') {
|
|
100
|
+
const asked = data;
|
|
101
|
+
const source = options.sources?.find((entry) => entry.top.app_ref === asked.app);
|
|
102
|
+
const answer = source === undefined ? null : data.house === 'source-top' ? source.top : source.download;
|
|
103
|
+
deliver({ house: 'source', id: asked.id, answer });
|
|
104
|
+
}
|
|
105
|
+
else if (data.house === 'attachment-read') {
|
|
106
|
+
const asked = data;
|
|
107
|
+
const image = options.images?.find((entry) => entry.room === asked.room && entry.attachment === asked.attachment);
|
|
108
|
+
deliver({
|
|
109
|
+
house: 'attachment',
|
|
110
|
+
id: asked.id,
|
|
111
|
+
image: image === undefined
|
|
112
|
+
? null
|
|
113
|
+
: {
|
|
114
|
+
attachment: image.attachment,
|
|
115
|
+
copy: image.copy,
|
|
116
|
+
name: image.name,
|
|
117
|
+
media_type: image.media_type,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
else if (data.house === 'screenshot-read') {
|
|
122
|
+
const asked = data;
|
|
123
|
+
const held = typeof asked.app === 'string' ? options.screenshots?.[asked.app] : undefined;
|
|
124
|
+
deliver({ house: 'screenshot', id: asked.id, image: held ?? null });
|
|
125
|
+
}
|
|
65
126
|
}
|
|
66
127
|
const port = {
|
|
67
128
|
postMessage(message) {
|
|
@@ -81,24 +142,22 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
81
142
|
});
|
|
82
143
|
},
|
|
83
144
|
};
|
|
84
|
-
if (
|
|
85
|
-
deliver({ house: 'status', status
|
|
145
|
+
if (status !== null)
|
|
146
|
+
deliver({ house: 'status', status });
|
|
86
147
|
deliver({ house: 'address', address, navigated: false });
|
|
87
148
|
return {
|
|
88
149
|
port: Promise.resolve(port),
|
|
89
150
|
navigations,
|
|
90
151
|
ceremonies,
|
|
152
|
+
consents,
|
|
153
|
+
tabs,
|
|
91
154
|
address() {
|
|
92
155
|
return address;
|
|
93
156
|
},
|
|
94
157
|
open(asked) {
|
|
95
|
-
|
|
96
|
-
if (target !== null)
|
|
97
|
-
arrive(target, false);
|
|
98
|
-
},
|
|
99
|
-
publish(status) {
|
|
100
|
-
deliver({ house: 'status', status });
|
|
158
|
+
arrive(appAddressOf('/', asked), false);
|
|
101
159
|
},
|
|
160
|
+
publish,
|
|
102
161
|
attachRuntime(runtime) {
|
|
103
162
|
deliver({ house: 'runtime' }, [runtime]);
|
|
104
163
|
},
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { TabChange } from '../../src/apps/tab.js';
|
|
2
|
+
export interface HouseBridgeEvent {
|
|
3
|
+
readonly data: unknown;
|
|
4
|
+
readonly ports: readonly HouseMessagePort[];
|
|
5
|
+
}
|
|
6
|
+
export interface HouseMessagePort {
|
|
7
|
+
postMessage(message: unknown): void;
|
|
8
|
+
addEventListener(type: 'message', listener: (event: HouseBridgeEvent) => void): void;
|
|
9
|
+
start(): void;
|
|
10
|
+
}
|
|
11
|
+
export type HouseBridgePort = HouseMessagePort;
|
|
12
|
+
export interface HouseSyncState {
|
|
13
|
+
readonly state: string;
|
|
14
|
+
readonly providerUnreachable: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface HouseCredentialState {
|
|
17
|
+
readonly state: 'none' | 'held' | 'rejected';
|
|
18
|
+
readonly expires_at: number | null;
|
|
19
|
+
}
|
|
20
|
+
export type HouseGaChoice = 'accept' | 'decline';
|
|
21
|
+
export interface HouseGa {
|
|
22
|
+
readonly measures: boolean;
|
|
23
|
+
readonly choice: HouseGaChoice | null;
|
|
24
|
+
}
|
|
25
|
+
export interface HouseStatus {
|
|
26
|
+
readonly app: string;
|
|
27
|
+
readonly runtime: string;
|
|
28
|
+
readonly credential: HouseCredentialState;
|
|
29
|
+
readonly sync: HouseSyncState | null;
|
|
30
|
+
readonly refusal: {
|
|
31
|
+
readonly reason: string;
|
|
32
|
+
} | null;
|
|
33
|
+
readonly loss: string | null;
|
|
34
|
+
readonly release: unknown;
|
|
35
|
+
readonly ga: HouseGa;
|
|
36
|
+
readonly service_mode: 'open' | 'write-halted' | null;
|
|
37
|
+
}
|
|
38
|
+
export type HouseNavigationMode = 'push' | 'replace';
|
|
39
|
+
interface HouseRelease {
|
|
40
|
+
readonly app_ref: string;
|
|
41
|
+
readonly release: string;
|
|
42
|
+
}
|
|
43
|
+
export interface HouseSourceTop extends HouseRelease {
|
|
44
|
+
readonly folders: readonly string[];
|
|
45
|
+
readonly files: readonly {
|
|
46
|
+
readonly name: string;
|
|
47
|
+
readonly size: number;
|
|
48
|
+
}[];
|
|
49
|
+
readonly readme: string | null;
|
|
50
|
+
}
|
|
51
|
+
export type HouseSourceFile = {
|
|
52
|
+
readonly path: string;
|
|
53
|
+
readonly size: number;
|
|
54
|
+
} & ({
|
|
55
|
+
readonly text: string;
|
|
56
|
+
} | {
|
|
57
|
+
readonly base64: string;
|
|
58
|
+
});
|
|
59
|
+
export interface HouseSourceDownload extends HouseRelease {
|
|
60
|
+
readonly files: readonly HouseSourceFile[];
|
|
61
|
+
}
|
|
62
|
+
export interface HouseSourceRefusal {
|
|
63
|
+
readonly error: {
|
|
64
|
+
readonly code: string;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export type HouseTab = TabChange;
|
|
68
|
+
export declare const HOUSE_ADDRESS_MAX_CHARS = 1000;
|
|
69
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentshouse/app-bridge",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
4
|
-
"description": "Renderer-side client of the agents.house App bridge: navigation, ceremonies and status, with an in-page test host.",
|
|
3
|
+
"version": "0.1.0-alpha.10",
|
|
4
|
+
"description": "Renderer-side client of the agents.house App bridge: navigation, the tab title and icon, ceremonies, installation, notifications and the icon badge, status, Site image reads, App release code and App screenshot reads, with an in-page test host.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -10,17 +10,21 @@
|
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
13
|
-
"types": "./index.d.ts",
|
|
14
|
-
"default": "./index.js"
|
|
13
|
+
"types": "./client/src/index.d.ts",
|
|
14
|
+
"default": "./client/src/index.js"
|
|
15
15
|
},
|
|
16
16
|
"./test-host": {
|
|
17
|
-
"types": "./test-host.d.ts",
|
|
18
|
-
"default": "./test-host.js"
|
|
17
|
+
"types": "./client/src/test-host.d.ts",
|
|
18
|
+
"default": "./client/src/test-host.js"
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"files": [
|
|
22
|
-
"
|
|
23
|
-
"
|
|
22
|
+
"client/src/*.js",
|
|
23
|
+
"client/src/*.d.ts",
|
|
24
|
+
"src/apps/remainder.js",
|
|
25
|
+
"src/apps/remainder.d.ts",
|
|
26
|
+
"src/apps/tab.js",
|
|
27
|
+
"src/apps/tab.d.ts",
|
|
24
28
|
"README.md"
|
|
25
29
|
]
|
|
26
30
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function split(address) {
|
|
2
|
+
const mark = address.search(/[?#]/u);
|
|
3
|
+
return mark < 0
|
|
4
|
+
? { path: address, rest: '' }
|
|
5
|
+
: { path: address.slice(0, mark), rest: address.slice(mark) };
|
|
6
|
+
}
|
|
7
|
+
export function appAddressOf(root, address) {
|
|
8
|
+
const { path, rest } = split(address);
|
|
9
|
+
const below = root === '/' ? path : path.slice(root.length);
|
|
10
|
+
return `${below.startsWith('/') ? below : `/${below}`}${rest}`;
|
|
11
|
+
}
|
|
12
|
+
export function shellAddressOf(root, appAddress) {
|
|
13
|
+
const { path, rest } = split(appAddress);
|
|
14
|
+
const below = path === '/' ? '' : path;
|
|
15
|
+
return `${root === '/' ? '' : root}${below}${rest}` || '/';
|
|
16
|
+
}
|
package/src/apps/tab.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const TAB_TITLE_MAX_CHARS = 200;
|
|
2
|
+
const TAB_ICON_MAX_CHARS = 1_048_576;
|
|
3
|
+
function caseless(text) {
|
|
4
|
+
return text.replace(/[a-z]/gu, (letter) => `[${letter}${letter.toUpperCase()}]`);
|
|
5
|
+
}
|
|
6
|
+
export const TAB_IMAGE = new RegExp(`^${caseless('data:image/')}(?:svg\\+xml|${['png', 'gif', 'webp'].map(caseless).join('|')});${caseless('base64')},[A-Za-z0-9+/]+={0,2}$`, 'u');
|
|
7
|
+
function admittedTitle(title) {
|
|
8
|
+
return typeof title === 'string' && title.length <= TAB_TITLE_MAX_CHARS;
|
|
9
|
+
}
|
|
10
|
+
function admittedIcon(icon) {
|
|
11
|
+
return typeof icon === 'string' && icon.length <= TAB_ICON_MAX_CHARS && TAB_IMAGE.test(icon);
|
|
12
|
+
}
|
|
13
|
+
export function tabChange(asked) {
|
|
14
|
+
if (asked === null || typeof asked !== 'object')
|
|
15
|
+
return null;
|
|
16
|
+
const { title, icon } = asked;
|
|
17
|
+
if (title === undefined && icon === undefined)
|
|
18
|
+
return null;
|
|
19
|
+
if (title !== undefined && !admittedTitle(title))
|
|
20
|
+
return null;
|
|
21
|
+
if (icon !== undefined && !admittedIcon(icon))
|
|
22
|
+
return null;
|
|
23
|
+
return {
|
|
24
|
+
...(title === undefined ? {} : { title: title }),
|
|
25
|
+
...(icon === undefined ? {} : { icon: icon }),
|
|
26
|
+
};
|
|
27
|
+
}
|
package/bridge.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import type { HouseBridgePort, HouseMessagePort, HouseStatus } from './wire.js';
|
|
2
|
-
export declare class HouseCeremonyRefused extends Error {
|
|
3
|
-
readonly path: string | null;
|
|
4
|
-
constructor(path: string | null);
|
|
5
|
-
}
|
|
6
|
-
export declare class HouseNavigationRefused extends Error {
|
|
7
|
-
readonly address: string | null;
|
|
8
|
-
constructor(address: string | null);
|
|
9
|
-
}
|
|
10
|
-
export interface HouseBridge {
|
|
11
|
-
readonly runtime: Promise<HouseMessagePort>;
|
|
12
|
-
address(): string | null;
|
|
13
|
-
onAddress(listener: (address: string) => void): () => void;
|
|
14
|
-
push(address: string): Promise<string>;
|
|
15
|
-
replace(address: string): Promise<string>;
|
|
16
|
-
ceremony(path: string): Promise<never>;
|
|
17
|
-
status(): HouseStatus | null;
|
|
18
|
-
onStatus(listener: (status: HouseStatus) => void): () => void;
|
|
19
|
-
}
|
|
20
|
-
export declare function connectHouseBridge(source?: Promise<HouseBridgePort>): HouseBridge;
|
package/bridge.js
DELETED
|
@@ -1,134 +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 settled() {
|
|
18
|
-
let settle = null;
|
|
19
|
-
const promise = new Promise((resolve, reject) => {
|
|
20
|
-
settle = { resolve, reject };
|
|
21
|
-
});
|
|
22
|
-
return { promise, settle: settle };
|
|
23
|
-
}
|
|
24
|
-
function heldBridge() {
|
|
25
|
-
const held = globalThis.houseBridge;
|
|
26
|
-
if (held === undefined)
|
|
27
|
-
throw new Error('house_bridge_absent');
|
|
28
|
-
return held;
|
|
29
|
-
}
|
|
30
|
-
export function connectHouseBridge(source = heldBridge()) {
|
|
31
|
-
const runtime = settled();
|
|
32
|
-
const ceremonies = [];
|
|
33
|
-
const navigations = [];
|
|
34
|
-
const addressListeners = new Set();
|
|
35
|
-
const statusListeners = new Set();
|
|
36
|
-
let outbox = [];
|
|
37
|
-
let held = null;
|
|
38
|
-
let address = null;
|
|
39
|
-
let status = null;
|
|
40
|
-
function send(message) {
|
|
41
|
-
if (held === null)
|
|
42
|
-
outbox?.push(message);
|
|
43
|
-
else
|
|
44
|
-
held.postMessage(message);
|
|
45
|
-
}
|
|
46
|
-
function receive(event) {
|
|
47
|
-
const data = event.data;
|
|
48
|
-
if (data === null || typeof data !== 'object')
|
|
49
|
-
return;
|
|
50
|
-
if (data.house === 'runtime') {
|
|
51
|
-
const [port] = event.ports;
|
|
52
|
-
if (port !== undefined)
|
|
53
|
-
runtime.settle.resolve(port);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
if (data.house === 'status') {
|
|
57
|
-
status = data.status;
|
|
58
|
-
for (const listener of statusListeners)
|
|
59
|
-
listener(status);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (data.house === 'address') {
|
|
63
|
-
address = String(data.address);
|
|
64
|
-
if (data.navigated === true)
|
|
65
|
-
navigations.shift()?.settle.resolve(address);
|
|
66
|
-
for (const listener of addressListeners)
|
|
67
|
-
listener(address);
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
if (data.house === 'navigate-refused') {
|
|
71
|
-
const refused = data.address;
|
|
72
|
-
const named = typeof refused === 'string' ? refused : null;
|
|
73
|
-
const index = named === null ? 0 : navigations.findIndex((one) => one.address === named);
|
|
74
|
-
const [pending] = navigations.splice(index < 0 ? 0 : index, 1);
|
|
75
|
-
pending?.settle.reject(new HouseNavigationRefused(named));
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
if (data.house === 'ceremony-refused') {
|
|
79
|
-
const refused = data.path;
|
|
80
|
-
const named = typeof refused === 'string' ? refused : null;
|
|
81
|
-
const index = named === null ? 0 : ceremonies.findIndex((one) => one.path === named);
|
|
82
|
-
const [pending] = ceremonies.splice(index < 0 ? 0 : index, 1);
|
|
83
|
-
pending?.settle.reject(new HouseCeremonyRefused(named));
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
void source.then((port) => {
|
|
87
|
-
held = port;
|
|
88
|
-
port.addEventListener('message', receive);
|
|
89
|
-
port.start();
|
|
90
|
-
const waiting = outbox ?? [];
|
|
91
|
-
outbox = null;
|
|
92
|
-
for (const message of waiting)
|
|
93
|
-
port.postMessage(message);
|
|
94
|
-
});
|
|
95
|
-
function navigate(mode, asked) {
|
|
96
|
-
const pending = settled();
|
|
97
|
-
navigations.push({ address: asked, settle: pending.settle });
|
|
98
|
-
send({ house: 'navigate', mode, address: asked });
|
|
99
|
-
return pending.promise;
|
|
100
|
-
}
|
|
101
|
-
return {
|
|
102
|
-
runtime: runtime.promise,
|
|
103
|
-
address() {
|
|
104
|
-
return address;
|
|
105
|
-
},
|
|
106
|
-
onAddress(listener) {
|
|
107
|
-
addressListeners.add(listener);
|
|
108
|
-
return () => {
|
|
109
|
-
addressListeners.delete(listener);
|
|
110
|
-
};
|
|
111
|
-
},
|
|
112
|
-
push(asked) {
|
|
113
|
-
return navigate('push', asked);
|
|
114
|
-
},
|
|
115
|
-
replace(asked) {
|
|
116
|
-
return navigate('replace', asked);
|
|
117
|
-
},
|
|
118
|
-
ceremony(path) {
|
|
119
|
-
const pending = settled();
|
|
120
|
-
ceremonies.push({ path, settle: pending.settle });
|
|
121
|
-
send({ house: 'ceremony', path });
|
|
122
|
-
return pending.promise;
|
|
123
|
-
},
|
|
124
|
-
status() {
|
|
125
|
-
return status;
|
|
126
|
-
},
|
|
127
|
-
onStatus(listener) {
|
|
128
|
-
statusListeners.add(listener);
|
|
129
|
-
return () => {
|
|
130
|
-
statusListeners.delete(listener);
|
|
131
|
-
};
|
|
132
|
-
},
|
|
133
|
-
};
|
|
134
|
-
}
|
package/index.d.ts
DELETED
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
export { connectHouseBridge, HouseCeremonyRefused, HouseNavigationRefused, type HouseBridge, } from './bridge.js';
|
|
2
|
-
export { HOUSE_ADDRESS_MAX_CHARS } from './wire.js';
|
|
3
|
-
export type { HouseBridgeEvent, HouseBridgePort, HouseCredentialState, HouseMessagePort, HouseNavigationMode, HouseStatus, HouseSyncState, } from './wire.js';
|
package/wire.d.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export interface HouseBridgeEvent {
|
|
2
|
-
readonly data: unknown;
|
|
3
|
-
readonly ports: readonly HouseMessagePort[];
|
|
4
|
-
}
|
|
5
|
-
export interface HouseMessagePort {
|
|
6
|
-
postMessage(message: unknown): void;
|
|
7
|
-
addEventListener(type: 'message', listener: (event: HouseBridgeEvent) => void): void;
|
|
8
|
-
start(): void;
|
|
9
|
-
}
|
|
10
|
-
export type HouseBridgePort = HouseMessagePort;
|
|
11
|
-
export interface HouseSyncState {
|
|
12
|
-
readonly state: string;
|
|
13
|
-
readonly providerUnreachable: boolean;
|
|
14
|
-
}
|
|
15
|
-
export interface HouseCredentialState {
|
|
16
|
-
readonly state: 'none' | 'held' | 'rejected';
|
|
17
|
-
readonly expires_at: number | null;
|
|
18
|
-
}
|
|
19
|
-
export interface HouseStatus {
|
|
20
|
-
readonly runtime: string;
|
|
21
|
-
readonly credential: HouseCredentialState;
|
|
22
|
-
readonly sync: HouseSyncState | null;
|
|
23
|
-
readonly refusal: {
|
|
24
|
-
readonly reason: string;
|
|
25
|
-
} | null;
|
|
26
|
-
readonly loss: string | null;
|
|
27
|
-
readonly release: unknown;
|
|
28
|
-
}
|
|
29
|
-
export type HouseNavigationMode = 'push' | 'replace';
|
|
30
|
-
export declare const HOUSE_ADDRESS_MAX_CHARS = 1000;
|
|
File without changes
|
|
File without changes
|