@agentshouse/app-bridge 0.1.0-ca4ec913df08d → 0.1.0-ca559eade7c19
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 +3 -1
- package/dist/calls.d.ts +118 -0
- package/dist/calls.js +1 -0
- package/dist/index.d.ts +43 -16
- package/dist/mdmodel/blocks.d.ts +10 -0
- package/dist/mdmodel/condition.d.ts +149 -0
- package/dist/mdmodel/definition.d.ts +79 -0
- package/dist/mdmodel/divide.d.ts +2 -0
- package/dist/mdmodel/document.d.ts +15 -0
- package/dist/mdmodel/fields.d.ts +5 -0
- package/dist/mdmodel/index.d.ts +18 -0
- package/dist/mdmodel/paths.d.ts +17 -0
- package/dist/mdmodel/profile.d.ts +5 -0
- package/dist/mdmodel/project.d.ts +20 -0
- package/dist/mdmodel/references.d.ts +3 -0
- package/dist/mdmodel/types.d.ts +143 -0
- package/dist/mdmodel/validate.d.ts +7 -0
- package/dist/mdmodel/version.d.ts +1 -0
- package/dist/mdmodel/yaml.d.ts +55 -0
- package/dist/mdruntime/acquisition.d.ts +13 -0
- package/dist/mdruntime/adapter.d.ts +479 -0
- package/dist/mdruntime/binary.d.ts +22 -0
- package/dist/mdruntime/change-set.d.ts +34 -0
- package/dist/mdruntime/change.d.ts +88 -0
- package/dist/mdruntime/client.d.ts +82 -0
- package/dist/mdruntime/collection.d.ts +38 -0
- package/dist/mdruntime/connection.d.ts +141 -0
- package/dist/mdruntime/conversation.d.ts +105 -0
- package/dist/mdruntime/durability.d.ts +14 -0
- package/dist/mdruntime/failure.d.ts +12 -0
- package/dist/mdruntime/file.d.ts +19 -0
- package/dist/mdruntime/model.d.ts +57 -0
- package/dist/mdruntime/observation.d.ts +29 -0
- package/dist/mdruntime/open.d.ts +1 -0
- package/dist/mdruntime/outcome.d.ts +46 -0
- package/dist/mdruntime/overlay.d.ts +27 -0
- package/dist/mdruntime/query.d.ts +253 -0
- package/dist/mdruntime/reach.d.ts +44 -0
- package/dist/mdruntime/readiness.d.ts +52 -0
- package/dist/mdruntime/recovery.d.ts +24 -0
- package/dist/mdruntime/replica.d.ts +45 -0
- package/dist/mdruntime/room-draft.d.ts +155 -0
- package/dist/mdruntime/rows.d.ts +37 -0
- package/dist/mdruntime/save.d.ts +63 -0
- package/dist/mdruntime/search.d.ts +91 -0
- package/dist/mdruntime/seed.d.ts +15 -0
- package/dist/mdruntime/shared.d.ts +10 -0
- package/dist/mdruntime/status.d.ts +26 -0
- package/dist/mdruntime/store.d.ts +160 -0
- package/dist/mdruntime/transfer.d.ts +9 -0
- package/dist/mdruntime/turn.d.ts +6 -0
- package/dist/mdruntime/wire.d.ts +206 -0
- package/dist/test-host.d.ts +5 -5
- package/dist/test-host.js +60 -21
- package/docs/families.md +57 -15
- package/package.json +1 -4
package/dist/test-host.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const HOST_ORIGIN = 'https://house.invalid';
|
|
2
|
-
const ADDRESS_MAX_CHARS =
|
|
2
|
+
const ADDRESS_MAX_CHARS = 64 * 1024;
|
|
3
3
|
function sameOriginPath(value) {
|
|
4
4
|
if (typeof value !== 'string')
|
|
5
5
|
return null;
|
|
@@ -16,15 +16,34 @@ function appAddress(address) {
|
|
|
16
16
|
function ceremonyRefused(path) {
|
|
17
17
|
return Object.assign(new Error('house_ceremony_refused'), {
|
|
18
18
|
name: 'HouseCeremonyRefused',
|
|
19
|
+
code: 'house_ceremony_refused',
|
|
20
|
+
retryable: false,
|
|
19
21
|
path,
|
|
20
22
|
});
|
|
21
23
|
}
|
|
22
24
|
function navigationRefused(address) {
|
|
23
25
|
return Object.assign(new Error('house_navigation_refused'), {
|
|
24
26
|
name: 'HouseNavigationRefused',
|
|
27
|
+
code: 'house_navigation_refused',
|
|
28
|
+
retryable: false,
|
|
25
29
|
address,
|
|
26
30
|
});
|
|
27
31
|
}
|
|
32
|
+
function followHeld(listeners, listener, held) {
|
|
33
|
+
let following = true;
|
|
34
|
+
queueMicrotask(() => {
|
|
35
|
+
if (!following)
|
|
36
|
+
return;
|
|
37
|
+
listeners.add(listener);
|
|
38
|
+
const value = held();
|
|
39
|
+
if (value !== null)
|
|
40
|
+
listener(value);
|
|
41
|
+
});
|
|
42
|
+
return () => {
|
|
43
|
+
following = false;
|
|
44
|
+
listeners.delete(listener);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
28
47
|
export function createHouseBridgeTestHost(options = {}) {
|
|
29
48
|
const navigations = [];
|
|
30
49
|
const ceremonies = [];
|
|
@@ -38,6 +57,7 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
38
57
|
let published = options.status ?? null;
|
|
39
58
|
let shown = null;
|
|
40
59
|
let seen = null;
|
|
60
|
+
let cooperation = null;
|
|
41
61
|
let attached = () => { };
|
|
42
62
|
const runtime = new Promise((resolve) => {
|
|
43
63
|
attached = resolve;
|
|
@@ -59,7 +79,17 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
59
79
|
});
|
|
60
80
|
}
|
|
61
81
|
function navigate(mode, asked) {
|
|
62
|
-
|
|
82
|
+
let path = asked;
|
|
83
|
+
if (/^https?:\/\//i.test(asked)) {
|
|
84
|
+
try {
|
|
85
|
+
const target = new URL(asked);
|
|
86
|
+
path = target.origin === HOST_ORIGIN ? target.pathname + target.search + target.hash : '';
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
path = '';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const target = sameOriginPath(path);
|
|
63
93
|
if (target === null)
|
|
64
94
|
return Promise.reject(navigationRefused(asked));
|
|
65
95
|
navigations.push({ mode, address: target });
|
|
@@ -72,10 +102,7 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
72
102
|
runtime,
|
|
73
103
|
address: () => shown,
|
|
74
104
|
onAddress(listener) {
|
|
75
|
-
addressListeners
|
|
76
|
-
return () => {
|
|
77
|
-
addressListeners.delete(listener);
|
|
78
|
-
};
|
|
105
|
+
return followHeld(addressListeners, listener, () => shown);
|
|
79
106
|
},
|
|
80
107
|
push: (asked) => navigate('push', asked),
|
|
81
108
|
replace: (asked) => navigate('replace', asked),
|
|
@@ -92,17 +119,6 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
92
119
|
setTab(tab) {
|
|
93
120
|
tabs.push(tab);
|
|
94
121
|
},
|
|
95
|
-
readAttachment(room, attachment) {
|
|
96
|
-
const image = options.images?.find((entry) => entry.room === room && entry.attachment === attachment);
|
|
97
|
-
return Promise.resolve(image === undefined
|
|
98
|
-
? null
|
|
99
|
-
: {
|
|
100
|
-
attachment: image.attachment,
|
|
101
|
-
copy: image.copy,
|
|
102
|
-
name: image.name,
|
|
103
|
-
media_type: image.media_type,
|
|
104
|
-
});
|
|
105
|
-
},
|
|
106
122
|
sourceTop: (app) => Promise.resolve(options.sources?.find((entry) => entry.top.app_ref === app)?.top ?? null),
|
|
107
123
|
sourceDownload: (app) => Promise.resolve(options.sources?.find((entry) => entry.top.app_ref === app)?.download ?? null),
|
|
108
124
|
callExternal(request) {
|
|
@@ -113,10 +129,7 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
113
129
|
readSiteScreenshot: (site) => Promise.resolve(options.siteScreenshots?.[site] ?? null),
|
|
114
130
|
status: () => seen,
|
|
115
131
|
onStatus(listener) {
|
|
116
|
-
statusListeners
|
|
117
|
-
return () => {
|
|
118
|
-
statusListeners.delete(listener);
|
|
119
|
-
};
|
|
132
|
+
return followHeld(statusListeners, listener, () => seen);
|
|
120
133
|
},
|
|
121
134
|
messages(listener) {
|
|
122
135
|
messageListeners.add(listener);
|
|
@@ -132,7 +145,19 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
132
145
|
consents.push(choice);
|
|
133
146
|
publish({ ...published, analytics: { measures: true, choice } });
|
|
134
147
|
},
|
|
148
|
+
cooperate(given) {
|
|
149
|
+
cooperation = given;
|
|
150
|
+
return () => {
|
|
151
|
+
if (cooperation === given)
|
|
152
|
+
cooperation = null;
|
|
153
|
+
};
|
|
154
|
+
},
|
|
135
155
|
};
|
|
156
|
+
function cooperating() {
|
|
157
|
+
if (cooperation === null)
|
|
158
|
+
throw new Error('the App registered no cooperation');
|
|
159
|
+
return cooperation;
|
|
160
|
+
}
|
|
136
161
|
Object.defineProperty(globalThis, 'houseBridge', { value: bridge, configurable: true });
|
|
137
162
|
if (published !== null)
|
|
138
163
|
publish(published);
|
|
@@ -157,5 +182,19 @@ export function createHouseBridgeTestHost(options = {}) {
|
|
|
157
182
|
attachRuntime(client) {
|
|
158
183
|
attached(client);
|
|
159
184
|
},
|
|
185
|
+
async checkpoint() {
|
|
186
|
+
const held = cooperating();
|
|
187
|
+
await held.checkpoint();
|
|
188
|
+
await held.persist();
|
|
189
|
+
return JSON.parse(JSON.stringify((await held.capture()) ?? null));
|
|
190
|
+
},
|
|
191
|
+
async restore(task) {
|
|
192
|
+
const held = cooperating();
|
|
193
|
+
await held.restore(task);
|
|
194
|
+
return (await held.ready()) === true;
|
|
195
|
+
},
|
|
196
|
+
settle(activation) {
|
|
197
|
+
cooperating().settle(activation);
|
|
198
|
+
},
|
|
160
199
|
};
|
|
161
200
|
}
|
package/docs/families.md
CHANGED
|
@@ -12,23 +12,25 @@
|
|
|
12
12
|
purpose-bound authenticated pages. The shell navigates only to a same-origin
|
|
13
13
|
absolute path, carries its current address as the return target, and the page
|
|
14
14
|
returns there when it completes. An App names an address House gave it in a
|
|
15
|
-
reading and composes
|
|
15
|
+
reading and composes one only for `/bind/<app_ref>`, where the User binds
|
|
16
|
+
its Rooms and connects its account slots, with `?slot=<name>` to open at one
|
|
17
|
+
slot.
|
|
16
18
|
- **Installation** - `install()` offers the page as a Home-screen app. Where the
|
|
17
19
|
browser has an install prompt ready the shell shows it and the call resolves
|
|
18
20
|
`'accepted'` or `'dismissed'` with the User's choice, spending that prompt;
|
|
19
21
|
otherwise, as on every iPhone and iPad, it resolves `'manual'` and the App
|
|
20
22
|
shows its own instructions. A prompt the browser refuses to show, as for a
|
|
21
|
-
call made without a click, rejects with
|
|
22
|
-
and stays ready. Call it from a click. The standard
|
|
23
|
+
call made without a click, rejects with an error named and coded by the
|
|
24
|
+
browser's own error name, with `retryable` false, and stays ready. Call it from a click. The standard
|
|
23
25
|
`display-mode: standalone` media query tells the App it runs installed.
|
|
24
26
|
- **Notifications** - House's own panel alone; an App's or a Site's call
|
|
25
|
-
rejects with an error named `HouseCeremonyRefused`. `notifications()` answers this browser
|
|
27
|
+
rejects with an error named `HouseCeremonyRefused`, carrying `code` and `retryable`. `notifications()` answers this browser
|
|
26
28
|
storage's push state: `'on'`, `'off'`, `'blocked'` where the browser refuses
|
|
27
29
|
notifications, or `'unavailable'` where it has no push, as on an iPhone or
|
|
28
30
|
iPad outside a Home-screen app. `notifications(true)` asks the browser's
|
|
29
31
|
permission and subscribes, `notifications(false)` unsubscribes, and each
|
|
30
|
-
answers the state it leaves; a failure of the browser's push service
|
|
31
|
-
|
|
32
|
+
answers the state it leaves; a failure of the browser's push service, and
|
|
33
|
+
of `setBadge`, rejects the same way. Call `notifications(true)` from a
|
|
32
34
|
click. `setBadge(unread)` takes the identifiers of the Human Notifications
|
|
33
35
|
unread in this storage and sets the icon's badge to their number, or clears
|
|
34
36
|
it at none, where the browser shows one.
|
|
@@ -39,10 +41,21 @@
|
|
|
39
41
|
whether the page measures and the visitor's stored analytics choice, and
|
|
40
42
|
`serviceMode`: `open`, `write-halted` while House refuses writes, so the
|
|
41
43
|
App can stop retrying them, or `null` while the runtime holds no House
|
|
42
|
-
connection. Every status field is camelCase.
|
|
44
|
+
connection. Every status field is camelCase. `onStatus(listener)` and
|
|
45
|
+
`onAddress(listener)` hand a listener the status and the address the bridge
|
|
46
|
+
already holds, then each change.
|
|
43
47
|
The App reports the visitor's choice with `analyticsConsent('accept')` or
|
|
44
48
|
`analyticsConsent('decline')`; the shell hands it to House's analytics loader, and
|
|
45
49
|
the choice never reaches House.
|
|
50
|
+
- **Handoff** - when House hands its runtime over to a newer build, the App keeps
|
|
51
|
+
running, its `runtime` client and observations included: a call it makes
|
|
52
|
+
meanwhile waits for the newer runtime, however long that takes, and is
|
|
53
|
+
answered once, by it; each observation goes on with the newer runtime's
|
|
54
|
+
values; and the status and the address arrive again. A runtime that ended
|
|
55
|
+
before answering has its reads answered by the next one; since it may have
|
|
56
|
+
applied any other call, an act on House is answered the outcome
|
|
57
|
+
`transport-uncertainty`, and every other call, a Save or a draft write among
|
|
58
|
+
them, fails with the retryable code `transport-uncertainty`.
|
|
46
59
|
- **Site image read** - given a Published Room and an original attachment
|
|
47
60
|
reference, it answers the web copy's path and metadata, or `null` when no
|
|
48
61
|
copy exists. The original bytes never cross this bridge at a Site.
|
|
@@ -53,23 +66,24 @@
|
|
|
53
66
|
path, its size and its `text`, or its bytes as `base64`. The browser keeps
|
|
54
67
|
each answer per App and release: a read naming a release it holds answers
|
|
55
68
|
without asking House, and keeping a newer release drops the earlier one. A
|
|
56
|
-
refusal is House's `{ error: { code } }`, `app_release_not_found` for an
|
|
69
|
+
refusal is House's `{ error: { code, retryable } }`, `app_release_not_found` for an
|
|
57
70
|
App with no release; a read House does not answer, or one that outlived
|
|
58
71
|
the browser's signed-in User, is `null`.
|
|
59
|
-
- **External calls** - `callExternal({ url, method, headers, body, slot })`
|
|
72
|
+
- **External calls** - `callExternal({ url, method, headers, body, slot, room_ref })`
|
|
60
73
|
has House's server send an HTTPS request to one of the release's declared
|
|
61
74
|
external origins and resolves with the service's `status`, `headers` and its
|
|
62
75
|
body as `body` text or `body_base64`, or a refusal as House's
|
|
63
|
-
`{ error: { code } }`, or `null` when House does not answer. House fills in the
|
|
64
|
-
App and its running release; naming an account slot
|
|
65
|
-
|
|
66
|
-
|
|
76
|
+
`{ error: { code, retryable } }`, or `null` when House does not answer. House fills in the
|
|
77
|
+
App and its running release; naming an account slot and one of the App's
|
|
78
|
+
bound Rooms places the key of the account that Room connects to the slot,
|
|
79
|
+
when the User holds one of the account's roles there, which the App never
|
|
80
|
+
sees, and naming neither sends no key. A Site's call names no slot, and the House panel names an
|
|
67
81
|
`account` instead. `body_base64` replaces `body` for bytes.
|
|
68
82
|
- **App messages** - `messages(listener)` hands the listener each message
|
|
69
83
|
another holder of this App sends to this User's running copies, as `from`,
|
|
70
84
|
the sender's User id, and `body`, the JSON value they sent, until the
|
|
71
85
|
function it returns is called. App code sends one through the Runtime's
|
|
72
|
-
`call('
|
|
86
|
+
`call('message.send', { to, body })` with the recipient's User id and
|
|
73
87
|
a body of at most 32 KiB of JSON; House fills in the App and relays it only
|
|
74
88
|
to copies running now, storing nothing. The panel sends none.
|
|
75
89
|
- **App screenshot read** - given an App reference, `readScreenshot(app)`
|
|
@@ -82,6 +96,31 @@
|
|
|
82
96
|
after its latest release, as a JPEG `Blob`, or `null` when House holds none
|
|
83
97
|
or does not answer. A Site's own renderer reads none.
|
|
84
98
|
|
|
99
|
+
- **Cooperation** - `cooperate({ checkpoint, persist, capture, restore,
|
|
100
|
+
ready, settle })` lets House activate a newer release of the App while it
|
|
101
|
+
runs, keeping the User's task; the function it returns withdraws it.
|
|
102
|
+
Register it in the entry script, before the first draw, or House treats the
|
|
103
|
+
page as not cooperating and the newer release waits for the App's next
|
|
104
|
+
open. When a newer release is stored, House calls `checkpoint()`, which
|
|
105
|
+
resolves at the App's next safe checkpoint and may never resolve, then
|
|
106
|
+
`persist()`, which makes authored work durable, then `capture()`, which
|
|
107
|
+
answers the meaningful task the App owns, such as the selected record,
|
|
108
|
+
filters, cursor, selection and scroll position, as JSON of at most 65,536
|
|
109
|
+
characters; House keeps the address itself. In the newer release House
|
|
110
|
+
calls `restore(task)` once its document has drawn, so the scroll position
|
|
111
|
+
and focus it sets hold, or `restore(null)` when it opens the release without
|
|
112
|
+
a task, then `ready()`, the App's own check that its local data and
|
|
113
|
+
capabilities work, and finally `settle('committed')` once the release is
|
|
114
|
+
active or `settle('abandoned')` when House returns to the release before
|
|
115
|
+
it, restoring the same task there. A release House opens already active is
|
|
116
|
+
settled `'committed'` at once, after `restore(task)` when House hands it a
|
|
117
|
+
task, and a cooperation registered after House settled is settled at its
|
|
118
|
+
registration. Until `settle('committed')` House fails the release's outside
|
|
119
|
+
calls at once, as offline, those of the workers it starts included, and
|
|
120
|
+
refuses its Saves, write acts and external calls with the retryable code
|
|
121
|
+
`house_trial_withheld`, so a start-up effect is not repeated; run start-up
|
|
122
|
+
effects after `settle('committed')`.
|
|
123
|
+
|
|
85
124
|
## The test host
|
|
86
125
|
|
|
87
126
|
`createHouseBridgeTestHost(options)` plays House in one page: it installs a
|
|
@@ -104,4 +143,7 @@ given by Site identifier, answers every `install()` `'manual'`, as a browser
|
|
|
104
143
|
with no install prompt does, answers every `notifications()` `'unavailable'`,
|
|
105
144
|
as a browser without push does, settles every `setBadge`, delivers each App
|
|
106
145
|
message `message({ from, body })` plays, and hands `bridge.runtime` the MD
|
|
107
|
-
Runtime client `attachRuntime(client)` gives it.
|
|
146
|
+
Runtime client `attachRuntime(client)` gives it. `checkpoint()` runs the
|
|
147
|
+
registered cooperation's checkpoint, persist and capture and answers the task,
|
|
148
|
+
`restore(task)` runs its restore and answers its readiness check, and
|
|
149
|
+
`settle(activation)` hands it the outcome.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentshouse/app-bridge",
|
|
3
|
-
"version": "0.1.0-
|
|
3
|
+
"version": "0.1.0-ca559eade7c19",
|
|
4
4
|
"description": "Types of the agents.house App bridge House hands every App: 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 accessor and an in-page test host.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -18,9 +18,6 @@
|
|
|
18
18
|
"default": "./dist/test-host.js"
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
|
-
"dependencies": {
|
|
22
|
-
"@agentshouse/mdruntime": "0.1.0-cf6d5f0a5c9c8"
|
|
23
|
-
},
|
|
24
21
|
"files": [
|
|
25
22
|
"dist",
|
|
26
23
|
"docs",
|