@agentshouse/app-bridge 0.1.0-ce3c71da46f00 → 0.1.0-cf2e2ca943fbc

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.
@@ -1,5 +1,6 @@
1
1
  import type { MdRuntimeClient } from '@agentshouse/mdruntime/client';
2
- import type { HouseAppMessage, HouseExternalAnswer, HouseExternalRequest, HouseBridgePort, HouseGaChoice, HouseRefusal, HouseSourceDownload, HouseSourceTop, HouseStatus, HouseTab } from './wire.js';
2
+ import type { HouseAppMessage, HouseExternalAnswer, HouseExternalRequest, HouseBridgePort, HouseGaChoice, HouseImageCopy, HouseInstallation, HouseNotifications, HouseRefusal, HouseSourceDownload, HouseSourceTop, HouseStatus, HouseTab } from './wire.js';
3
+ export type { HouseImageCopy } from './wire.js';
3
4
  export declare class HouseCeremonyRefused extends Error {
4
5
  readonly path: string | null;
5
6
  constructor(path: string | null);
@@ -8,14 +9,6 @@ export declare class HouseNavigationRefused extends Error {
8
9
  readonly address: string | null;
9
10
  constructor(address: string | null);
10
11
  }
11
- export interface HouseImageCopy {
12
- readonly attachment: string;
13
- readonly copy: string;
14
- readonly name: string;
15
- readonly media_type: string;
16
- }
17
- type HouseInstallation = 'accepted' | 'dismissed' | 'manual';
18
- type HouseNotifications = 'on' | 'off' | 'blocked' | 'unavailable';
19
12
  export interface HouseBridgeSource {
20
13
  readonly port: Promise<HouseBridgePort>;
21
14
  readonly runtime: Promise<MdRuntimeClient>;
@@ -43,4 +36,3 @@ export interface HouseBridge {
43
36
  gaConsent(choice: HouseGaChoice): void;
44
37
  }
45
38
  export declare function connectHouseBridge(source?: HouseBridgeSource): HouseBridge;
46
- export {};
@@ -14,19 +14,45 @@ export class HouseNavigationRefused extends Error {
14
14
  this.address = address;
15
15
  }
16
16
  }
17
- function answered(pending, answer, value) {
18
- if (typeof answer.id !== 'number')
19
- return;
17
+ function pendingAnswer(pending, answer) {
20
18
  const asked = pending.get(answer.id);
21
19
  if (asked === undefined)
22
- return;
20
+ return null;
23
21
  pending.delete(answer.id);
24
- if (answer.refused === true)
22
+ if (answer.refused === true) {
25
23
  asked.reject(new HouseCeremonyRefused(null));
26
- else if (answer.refusal !== undefined)
24
+ return null;
25
+ }
26
+ if (answer.refusal !== undefined) {
27
27
  asked.reject(new DOMException(answer.refusal.message, answer.refusal.name));
28
- else
29
- asked.resolve(value);
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;
30
56
  }
31
57
  function settled() {
32
58
  let settle = null;
@@ -72,8 +98,8 @@ export function connectHouseBridge(source = heldBridge()) {
72
98
  held.postMessage(message);
73
99
  }
74
100
  function receive(event) {
75
- const data = event.data;
76
- if (data === null || typeof data !== 'object')
101
+ const data = frameMessage(event.data);
102
+ if (data === null)
77
103
  return;
78
104
  if (data.house === 'status') {
79
105
  status = data.status;
@@ -104,61 +130,59 @@ export function connectHouseBridge(source = heldBridge()) {
104
130
  return;
105
131
  }
106
132
  if (data.house === 'attachment') {
107
- const answer = data;
108
- if (typeof answer.id !== 'number')
133
+ if (typeof data.id !== 'number')
109
134
  return;
110
- const pending = attachments.get(answer.id);
135
+ const pending = attachments.get(data.id);
111
136
  if (pending === undefined)
112
137
  return;
113
- attachments.delete(answer.id);
114
- pending.resolve(answer.image ?? null);
138
+ attachments.delete(data.id);
139
+ pending.resolve(data.image ?? null);
115
140
  return;
116
141
  }
117
142
  if (data.house === 'install') {
118
- const answer = data;
119
- answered(installations, answer, answer.outcome);
143
+ const pending = pendingAnswer(installations, data);
144
+ if (pending !== null && 'outcome' in data)
145
+ pending.resolve(data.outcome);
120
146
  return;
121
147
  }
122
148
  if (data.house === 'notifications') {
123
- const answer = data;
124
- answered(notifications, answer, answer.state);
149
+ const pending = pendingAnswer(notifications, data);
150
+ if (pending !== null && 'state' in data)
151
+ pending.resolve(data.state);
125
152
  return;
126
153
  }
127
154
  if (data.house === 'badge') {
128
- answered(badges, data, undefined);
155
+ pendingAnswer(badges, data)?.resolve();
129
156
  return;
130
157
  }
131
158
  if (data.house === 'source') {
132
- const answer = data;
133
- if (typeof answer.id !== 'number')
159
+ if (typeof data.id !== 'number')
134
160
  return;
135
- const pending = sources.get(answer.id);
161
+ const pending = sources.get(data.id);
136
162
  if (pending === undefined)
137
163
  return;
138
- sources.delete(answer.id);
139
- pending.resolve(answer.answer ?? null);
164
+ sources.delete(data.id);
165
+ pending.resolve(data.answer ?? null);
140
166
  return;
141
167
  }
142
168
  if (data.house === 'external-call') {
143
- const answer = data;
144
- if (typeof answer.id !== 'number')
169
+ if (typeof data.id !== 'number')
145
170
  return;
146
- const pending = externals.get(answer.id);
171
+ const pending = externals.get(data.id);
147
172
  if (pending === undefined)
148
173
  return;
149
- externals.delete(answer.id);
150
- pending.resolve(answer.answer ?? null);
174
+ externals.delete(data.id);
175
+ pending.resolve(data.answer ?? null);
151
176
  return;
152
177
  }
153
178
  if (data.house === 'screenshot') {
154
- const answer = data;
155
- if (typeof answer.id !== 'number')
179
+ if (typeof data.id !== 'number')
156
180
  return;
157
- const pending = screenshots.get(answer.id);
181
+ const pending = screenshots.get(data.id);
158
182
  if (pending === undefined)
159
183
  return;
160
- screenshots.delete(answer.id);
161
- pending.resolve(answer.image ?? null);
184
+ screenshots.delete(data.id);
185
+ pending.resolve(data.image ?? null);
162
186
  return;
163
187
  }
164
188
  if (data.house === 'ceremony-refused') {
@@ -189,7 +213,10 @@ export function connectHouseBridge(source = heldBridge()) {
189
213
  const pending = settled();
190
214
  const id = ++nextScreenshot;
191
215
  screenshots.set(id, pending.settle);
192
- send({ house, id, ...asked });
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 });
193
220
  return pending.promise;
194
221
  }
195
222
  function navigate(mode, asked) {
@@ -3,7 +3,7 @@ export interface HouseBridgeEvent {
3
3
  readonly data: unknown;
4
4
  }
5
5
  export interface HouseBridgePort {
6
- postMessage(message: unknown): void;
6
+ postMessage(message: HouseFrameRequest): void;
7
7
  addEventListener(type: 'message', listener: (event: HouseBridgeEvent) => void): void;
8
8
  start(): void;
9
9
  }
@@ -21,7 +21,11 @@ export interface HouseGa {
21
21
  readonly choice: HouseGaChoice | null;
22
22
  }
23
23
  export interface HouseStatus {
24
- readonly app: string;
24
+ readonly app: string | null;
25
+ readonly preferences: {
26
+ readonly room_ref: string;
27
+ readonly path: string;
28
+ } | null;
25
29
  readonly runtime: string;
26
30
  readonly credential: HouseCredentialState;
27
31
  readonly sync: HouseSyncState | null;
@@ -31,6 +35,7 @@ export interface HouseStatus {
31
35
  readonly loss: string | null;
32
36
  readonly ga: HouseGa;
33
37
  readonly service_mode: 'open' | 'write-halted' | null;
38
+ readonly offline: boolean;
34
39
  }
35
40
  export type HouseNavigationMode = 'push' | 'replace';
36
41
  interface HouseRelease {
@@ -83,5 +88,128 @@ export interface HouseAppMessage {
83
88
  readonly from: string;
84
89
  readonly body: unknown;
85
90
  }
91
+ export type HouseInstallation = 'accepted' | 'dismissed' | 'manual';
92
+ export type HouseNotifications = 'on' | 'off' | 'blocked' | 'unavailable';
93
+ export interface HouseImageCopy {
94
+ readonly attachment: string;
95
+ readonly copy: string;
96
+ readonly name: string;
97
+ readonly media_type: string;
98
+ }
99
+ export interface HouseFrameRefusal {
100
+ readonly name: string;
101
+ readonly message: string;
102
+ }
103
+ export type HouseFrameRequest = {
104
+ readonly house: 'navigate';
105
+ readonly mode: HouseNavigationMode;
106
+ readonly address: string;
107
+ } | ({
108
+ readonly house: 'tab';
109
+ } & HouseTab) | {
110
+ readonly house: 'ceremony';
111
+ readonly path: string;
112
+ } | {
113
+ readonly house: 'install';
114
+ readonly id: number;
115
+ } | {
116
+ readonly house: 'notifications';
117
+ readonly id: number;
118
+ readonly on?: boolean;
119
+ } | {
120
+ readonly house: 'badge';
121
+ readonly id: number;
122
+ readonly unread: readonly string[];
123
+ } | {
124
+ readonly house: 'attachment-read';
125
+ readonly id: number;
126
+ readonly room: string;
127
+ readonly attachment: string;
128
+ } | {
129
+ readonly house: 'source-top' | 'source-download';
130
+ readonly id: number;
131
+ readonly app: string;
132
+ readonly release?: string;
133
+ } | {
134
+ readonly house: 'external-call';
135
+ readonly id: number;
136
+ readonly request: HouseExternalRequest;
137
+ } | {
138
+ readonly house: 'screenshot-read';
139
+ readonly id: number;
140
+ readonly app: string;
141
+ } | {
142
+ readonly house: 'site-screenshot-read';
143
+ readonly id: number;
144
+ readonly site: string;
145
+ } | {
146
+ readonly house: 'ga/consent';
147
+ readonly choice: HouseGaChoice;
148
+ };
149
+ export type HouseInstallAnswer = {
150
+ readonly outcome: HouseInstallation;
151
+ } | {
152
+ readonly refusal: HouseFrameRefusal;
153
+ };
154
+ export type HouseNotificationAnswer = {
155
+ readonly refused: true;
156
+ } | {
157
+ readonly refusal: HouseFrameRefusal;
158
+ } | {
159
+ readonly state: HouseNotifications;
160
+ };
161
+ export type HouseBadgeAnswer = {
162
+ readonly refused: true;
163
+ } | {
164
+ readonly refusal: HouseFrameRefusal;
165
+ } | {
166
+ readonly refused?: never;
167
+ readonly refusal?: never;
168
+ };
169
+ export type HouseFrameMessage = {
170
+ readonly house: 'runtime';
171
+ } | {
172
+ readonly house: 'status';
173
+ readonly status: HouseStatus;
174
+ } | {
175
+ readonly house: 'screenshot';
176
+ readonly id: number;
177
+ readonly image: Blob | null;
178
+ } | {
179
+ readonly house: 'address';
180
+ readonly address: string;
181
+ readonly navigated: boolean;
182
+ } | {
183
+ readonly house: 'ceremony-refused';
184
+ readonly path: string | null;
185
+ } | {
186
+ readonly house: 'navigate-refused';
187
+ readonly address: string | null;
188
+ } | {
189
+ readonly house: 'attachment';
190
+ readonly id: number;
191
+ readonly image: HouseImageCopy | null;
192
+ } | {
193
+ readonly house: 'source';
194
+ readonly id: number;
195
+ readonly answer: HouseSourceTop | HouseSourceDownload | HouseRefusal | null;
196
+ } | {
197
+ readonly house: 'external-call';
198
+ readonly id: number;
199
+ readonly answer: HouseExternalAnswer | HouseRefusal | null;
200
+ } | ({
201
+ readonly house: 'install';
202
+ readonly id: number;
203
+ } & HouseInstallAnswer) | ({
204
+ readonly house: 'notifications';
205
+ readonly id: number;
206
+ } & HouseNotificationAnswer) | ({
207
+ readonly house: 'badge';
208
+ readonly id: number;
209
+ } & HouseBadgeAnswer) | {
210
+ readonly house: 'app-message';
211
+ readonly from: string;
212
+ readonly body: unknown;
213
+ };
86
214
  export declare const HOUSE_ADDRESS_MAX_CHARS = 1000;
87
215
  export {};
package/docs/families.md CHANGED
@@ -32,7 +32,9 @@
32
32
  click. `setBadge(unread)` takes the identifiers of the Human Notifications
33
33
  unread in this storage and sets the icon's badge to their number, or clears
34
34
  it at none, where the browser shows one.
35
- - **Status** - the App reference the runtime client is attached under, sync
35
+ - **Status** - the App reference the runtime client is attached under,
36
+ `preferences`: the `room_ref` and Path of the App's own preferences document,
37
+ or `null` until the runtime holds that Room, sync
36
38
  state, credential state and observed failure, `ga`:
37
39
  whether the page measures and the visitor's stored analytics choice, and
38
40
  `service_mode`: `open`, `write-halted` while House refuses writes, so the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentshouse/app-bridge",
3
- "version": "0.1.0-ce3c71da46f00",
3
+ "version": "0.1.0-cf2e2ca943fbc",
4
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, App and Site screenshot reads, and App messages, with an in-page test host.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -19,7 +19,7 @@
19
19
  }
20
20
  },
21
21
  "dependencies": {
22
- "@agentshouse/mdruntime": "0.1.0-c30ed2b8e04d5"
22
+ "@agentshouse/mdruntime": "0.1.0-cacb4c3f6734e"
23
23
  },
24
24
  "files": [
25
25
  "dist",