@agentshouse/app-bridge 0.1.0-alpha.7 → 0.1.0-alpha.9
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 +17 -2
- package/client/src/bridge.d.ts +4 -0
- package/client/src/bridge.js +43 -0
- package/client/src/test-host.d.ts +1 -0
- package/client/src/test-host.js +8 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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 six 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
|
|
@@ -17,6 +17,14 @@ port's one listener. It carries five families:
|
|
|
17
17
|
absolute path, carries its current address as the return target, and the page
|
|
18
18
|
returns there when it completes. An App names an address House gave it in a
|
|
19
19
|
reading and composes none.
|
|
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.
|
|
20
28
|
- **Status** - the App reference the Runtime port is attached under, sync
|
|
21
29
|
state, credential state, release availability and observed failure, `ga`:
|
|
22
30
|
whether the page measures and the visitor's stored analytics choice, and
|
|
@@ -39,6 +47,11 @@ port's one listener. It carries five families:
|
|
|
39
47
|
refusal is House's `{ error: { code } }`, `app_release_not_found` for an
|
|
40
48
|
App with no release; a read House does not answer, or one that outlived
|
|
41
49
|
the browser's signed-in User, is `null`.
|
|
50
|
+
- **App screenshot read** - given an App reference, `readScreenshot(app)`
|
|
51
|
+
answers the image this browser captured of that App's screen the first time
|
|
52
|
+
it ran its current release, as a PNG `Blob` at most 1200 pixels wide, or
|
|
53
|
+
`null` when this browser holds none. The image stays in the browser; a Site
|
|
54
|
+
holds none.
|
|
42
55
|
|
|
43
56
|
The package carries no credential handling and no Runtime family. It surfaces
|
|
44
57
|
the dedicated Runtime port the worker transfers over the bridge as a plain
|
|
@@ -99,4 +112,6 @@ records in `consents` each
|
|
|
99
112
|
`gaConsent` it takes while that status measures and publishes the status the
|
|
100
113
|
choice leaves, records in `tabs` each `setTab` the shell would take and ignores
|
|
101
114
|
any other, answers a release code read from the `sources` it was given, or
|
|
102
|
-
`null`,
|
|
115
|
+
`null`, answers `readScreenshot` from the `screenshots` it was given by App
|
|
116
|
+
reference, answers every `install()` `'manual'`, as a browser with no install
|
|
117
|
+
prompt does, and attaches a Runtime port.
|
package/client/src/bridge.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface HouseImageCopy {
|
|
|
13
13
|
readonly name: string;
|
|
14
14
|
readonly media_type: string;
|
|
15
15
|
}
|
|
16
|
+
type HouseInstallation = 'accepted' | 'dismissed' | 'manual';
|
|
16
17
|
export interface HouseBridge {
|
|
17
18
|
readonly runtime: Promise<HouseMessagePort>;
|
|
18
19
|
address(): string | null;
|
|
@@ -20,12 +21,15 @@ export interface HouseBridge {
|
|
|
20
21
|
push(address: string): Promise<string>;
|
|
21
22
|
replace(address: string): Promise<string>;
|
|
22
23
|
ceremony(path: string): Promise<never>;
|
|
24
|
+
install(): Promise<HouseInstallation>;
|
|
23
25
|
setTab(tab: HouseTab): void;
|
|
24
26
|
readAttachment(room: string, attachment: string): Promise<HouseImageCopy | null>;
|
|
25
27
|
sourceTop(app: string, release?: string): Promise<HouseSourceTop | HouseSourceRefusal | null>;
|
|
26
28
|
sourceDownload(app: string, release?: string): Promise<HouseSourceDownload | HouseSourceRefusal | null>;
|
|
29
|
+
readScreenshot(app: string): Promise<Blob | null>;
|
|
27
30
|
status(): HouseStatus | null;
|
|
28
31
|
onStatus(listener: (status: HouseStatus) => void): () => void;
|
|
29
32
|
gaConsent(choice: HouseGaChoice): void;
|
|
30
33
|
}
|
|
31
34
|
export declare function connectHouseBridge(source?: Promise<HouseBridgePort>): HouseBridge;
|
|
35
|
+
export {};
|
package/client/src/bridge.js
CHANGED
|
@@ -35,6 +35,10 @@ export function connectHouseBridge(source = heldBridge()) {
|
|
|
35
35
|
let nextAttachment = 0;
|
|
36
36
|
const sources = new Map();
|
|
37
37
|
let nextSource = 0;
|
|
38
|
+
const installations = new Map();
|
|
39
|
+
let nextInstallation = 0;
|
|
40
|
+
const screenshots = new Map();
|
|
41
|
+
let nextScreenshot = 0;
|
|
38
42
|
const addressListeners = new Set();
|
|
39
43
|
const statusListeners = new Set();
|
|
40
44
|
let outbox = [];
|
|
@@ -90,6 +94,20 @@ export function connectHouseBridge(source = heldBridge()) {
|
|
|
90
94
|
pending.resolve(answer.image ?? null);
|
|
91
95
|
return;
|
|
92
96
|
}
|
|
97
|
+
if (data.house === 'install') {
|
|
98
|
+
const answer = data;
|
|
99
|
+
if (typeof answer.id !== 'number')
|
|
100
|
+
return;
|
|
101
|
+
const pending = installations.get(answer.id);
|
|
102
|
+
if (pending === undefined)
|
|
103
|
+
return;
|
|
104
|
+
installations.delete(answer.id);
|
|
105
|
+
if (answer.outcome !== undefined)
|
|
106
|
+
pending.resolve(answer.outcome);
|
|
107
|
+
else
|
|
108
|
+
pending.reject(new DOMException(answer.refusal?.message, answer.refusal?.name));
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
93
111
|
if (data.house === 'source') {
|
|
94
112
|
const answer = data;
|
|
95
113
|
if (typeof answer.id !== 'number')
|
|
@@ -101,6 +119,17 @@ export function connectHouseBridge(source = heldBridge()) {
|
|
|
101
119
|
pending.resolve(answer.answer ?? null);
|
|
102
120
|
return;
|
|
103
121
|
}
|
|
122
|
+
if (data.house === 'screenshot') {
|
|
123
|
+
const answer = data;
|
|
124
|
+
if (typeof answer.id !== 'number')
|
|
125
|
+
return;
|
|
126
|
+
const pending = screenshots.get(answer.id);
|
|
127
|
+
if (pending === undefined)
|
|
128
|
+
return;
|
|
129
|
+
screenshots.delete(answer.id);
|
|
130
|
+
pending.resolve(answer.image ?? null);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
104
133
|
if (data.house === 'ceremony-refused') {
|
|
105
134
|
const refused = data.path;
|
|
106
135
|
const named = typeof refused === 'string' ? refused : null;
|
|
@@ -154,6 +183,13 @@ export function connectHouseBridge(source = heldBridge()) {
|
|
|
154
183
|
send({ house: 'ceremony', path });
|
|
155
184
|
return pending.promise;
|
|
156
185
|
},
|
|
186
|
+
install() {
|
|
187
|
+
const pending = settled();
|
|
188
|
+
const id = ++nextInstallation;
|
|
189
|
+
installations.set(id, pending.settle);
|
|
190
|
+
send({ house: 'install', id });
|
|
191
|
+
return pending.promise;
|
|
192
|
+
},
|
|
157
193
|
setTab(tab) {
|
|
158
194
|
send({ house: 'tab', title: tab.title, icon: tab.icon });
|
|
159
195
|
},
|
|
@@ -170,6 +206,13 @@ export function connectHouseBridge(source = heldBridge()) {
|
|
|
170
206
|
sourceDownload(app, release) {
|
|
171
207
|
return readSource('source-download', app, release);
|
|
172
208
|
},
|
|
209
|
+
readScreenshot(app) {
|
|
210
|
+
const pending = settled();
|
|
211
|
+
const id = ++nextScreenshot;
|
|
212
|
+
screenshots.set(id, pending.settle);
|
|
213
|
+
send({ house: 'screenshot-read', id, app });
|
|
214
|
+
return pending.promise;
|
|
215
|
+
},
|
|
173
216
|
status() {
|
|
174
217
|
return status;
|
|
175
218
|
},
|
|
@@ -25,5 +25,6 @@ export interface HouseBridgeTestHostOptions {
|
|
|
25
25
|
readonly top: HouseSourceTop;
|
|
26
26
|
readonly download: HouseSourceDownload;
|
|
27
27
|
}[];
|
|
28
|
+
readonly screenshots?: Readonly<Record<string, Blob>>;
|
|
28
29
|
}
|
|
29
30
|
export declare function createHouseBridgeTestHost(options?: HouseBridgeTestHostOptions): HouseBridgeTestHost;
|
package/client/src/test-host.js
CHANGED
|
@@ -79,6 +79,9 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
79
79
|
else if (data.house === 'ceremony') {
|
|
80
80
|
ceremony(data.path);
|
|
81
81
|
}
|
|
82
|
+
else if (data.house === 'install') {
|
|
83
|
+
deliver({ house: 'install', id: data.id, outcome: 'manual' });
|
|
84
|
+
}
|
|
82
85
|
else if (data.house === 'tab') {
|
|
83
86
|
const change = tabChange(data);
|
|
84
87
|
if (change !== null)
|
|
@@ -109,6 +112,11 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
109
112
|
},
|
|
110
113
|
});
|
|
111
114
|
}
|
|
115
|
+
else if (data.house === 'screenshot-read') {
|
|
116
|
+
const asked = data;
|
|
117
|
+
const held = typeof asked.app === 'string' ? options.screenshots?.[asked.app] : undefined;
|
|
118
|
+
deliver({ house: 'screenshot', id: asked.id, image: held ?? null });
|
|
119
|
+
}
|
|
112
120
|
}
|
|
113
121
|
const port = {
|
|
114
122
|
postMessage(message) {
|
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, the tab title and icon, ceremonies, status, Site image reads and App
|
|
3
|
+
"version": "0.1.0-alpha.9",
|
|
4
|
+
"description": "Renderer-side client of the agents.house App bridge: navigation, the tab title and icon, ceremonies, installation, 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,
|