@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.
Files changed (56) hide show
  1. package/README.md +3 -1
  2. package/dist/calls.d.ts +118 -0
  3. package/dist/calls.js +1 -0
  4. package/dist/index.d.ts +43 -16
  5. package/dist/mdmodel/blocks.d.ts +10 -0
  6. package/dist/mdmodel/condition.d.ts +149 -0
  7. package/dist/mdmodel/definition.d.ts +79 -0
  8. package/dist/mdmodel/divide.d.ts +2 -0
  9. package/dist/mdmodel/document.d.ts +15 -0
  10. package/dist/mdmodel/fields.d.ts +5 -0
  11. package/dist/mdmodel/index.d.ts +18 -0
  12. package/dist/mdmodel/paths.d.ts +17 -0
  13. package/dist/mdmodel/profile.d.ts +5 -0
  14. package/dist/mdmodel/project.d.ts +20 -0
  15. package/dist/mdmodel/references.d.ts +3 -0
  16. package/dist/mdmodel/types.d.ts +143 -0
  17. package/dist/mdmodel/validate.d.ts +7 -0
  18. package/dist/mdmodel/version.d.ts +1 -0
  19. package/dist/mdmodel/yaml.d.ts +55 -0
  20. package/dist/mdruntime/acquisition.d.ts +13 -0
  21. package/dist/mdruntime/adapter.d.ts +479 -0
  22. package/dist/mdruntime/binary.d.ts +22 -0
  23. package/dist/mdruntime/change-set.d.ts +34 -0
  24. package/dist/mdruntime/change.d.ts +88 -0
  25. package/dist/mdruntime/client.d.ts +82 -0
  26. package/dist/mdruntime/collection.d.ts +38 -0
  27. package/dist/mdruntime/connection.d.ts +141 -0
  28. package/dist/mdruntime/conversation.d.ts +105 -0
  29. package/dist/mdruntime/durability.d.ts +14 -0
  30. package/dist/mdruntime/failure.d.ts +12 -0
  31. package/dist/mdruntime/file.d.ts +19 -0
  32. package/dist/mdruntime/model.d.ts +57 -0
  33. package/dist/mdruntime/observation.d.ts +29 -0
  34. package/dist/mdruntime/open.d.ts +1 -0
  35. package/dist/mdruntime/outcome.d.ts +46 -0
  36. package/dist/mdruntime/overlay.d.ts +27 -0
  37. package/dist/mdruntime/query.d.ts +253 -0
  38. package/dist/mdruntime/reach.d.ts +44 -0
  39. package/dist/mdruntime/readiness.d.ts +52 -0
  40. package/dist/mdruntime/recovery.d.ts +24 -0
  41. package/dist/mdruntime/replica.d.ts +45 -0
  42. package/dist/mdruntime/room-draft.d.ts +155 -0
  43. package/dist/mdruntime/rows.d.ts +37 -0
  44. package/dist/mdruntime/save.d.ts +63 -0
  45. package/dist/mdruntime/search.d.ts +91 -0
  46. package/dist/mdruntime/seed.d.ts +15 -0
  47. package/dist/mdruntime/shared.d.ts +10 -0
  48. package/dist/mdruntime/status.d.ts +26 -0
  49. package/dist/mdruntime/store.d.ts +160 -0
  50. package/dist/mdruntime/transfer.d.ts +9 -0
  51. package/dist/mdruntime/turn.d.ts +6 -0
  52. package/dist/mdruntime/wire.d.ts +206 -0
  53. package/dist/test-host.d.ts +5 -5
  54. package/dist/test-host.js +60 -21
  55. package/docs/families.md +57 -15
  56. 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 = 1000;
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
- const target = sameOriginPath(asked);
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.add(listener);
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.add(listener);
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 none.
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 the browser's own `DOMException`
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 rejects
31
- with the browser's own `DOMException`. Call `notifications(true)` from a
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 places the key of the
65
- account the User connected to it, which the App never sees, and naming none
66
- sends no key. A Site's call names no slot, and the House panel names an
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('apps/messages/send', { to, body })` with the recipient's User id and
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-ca4ec913df08d",
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",