@agentshouse/mdruntime 0.1.0-cd26b960b7262 → 0.1.0-cd30853a1b15e

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 (257) hide show
  1. package/README.md +21 -19
  2. package/dist/acceptance.d.ts +0 -3
  3. package/dist/acceptance.d.ts.map +1 -1
  4. package/dist/acceptance.js +72 -158
  5. package/dist/act-lanes.d.ts +30 -0
  6. package/dist/act-lanes.d.ts.map +1 -0
  7. package/dist/act-lanes.js +225 -0
  8. package/dist/actions.d.ts +8 -2
  9. package/dist/actions.d.ts.map +1 -1
  10. package/dist/actions.js +118 -67
  11. package/dist/adapter.d.ts +81 -56
  12. package/dist/adapter.d.ts.map +1 -1
  13. package/dist/attached-model.d.ts +0 -1
  14. package/dist/attached-model.d.ts.map +1 -1
  15. package/dist/attached-model.js +0 -5
  16. package/dist/authored.d.ts +88 -0
  17. package/dist/authored.d.ts.map +1 -0
  18. package/dist/authored.js +345 -0
  19. package/dist/bootstrap.d.ts +1 -1
  20. package/dist/bootstrap.d.ts.map +1 -1
  21. package/dist/bootstrap.js +6 -19
  22. package/dist/change-set.d.ts +4 -0
  23. package/dist/change-set.d.ts.map +1 -1
  24. package/dist/change-set.js +6 -0
  25. package/dist/change.d.ts +4 -0
  26. package/dist/change.d.ts.map +1 -1
  27. package/dist/change.js +3 -0
  28. package/dist/chat-types.d.ts +0 -2
  29. package/dist/chat-types.d.ts.map +1 -1
  30. package/dist/chat-types.js +0 -2
  31. package/dist/client.d.ts +10 -19
  32. package/dist/client.d.ts.map +1 -1
  33. package/dist/client.js +30 -18
  34. package/dist/collection.d.ts +25 -4
  35. package/dist/collection.d.ts.map +1 -1
  36. package/dist/collection.js +19 -2
  37. package/dist/column.d.ts +5 -0
  38. package/dist/column.d.ts.map +1 -0
  39. package/dist/column.js +9 -0
  40. package/dist/compile.d.ts +2 -0
  41. package/dist/compile.d.ts.map +1 -1
  42. package/dist/compile.js +17 -5
  43. package/dist/connection.d.ts.map +1 -1
  44. package/dist/connection.js +31 -24
  45. package/dist/context.d.ts.map +1 -1
  46. package/dist/context.js +1 -1
  47. package/dist/conversation-live.d.ts +26 -12
  48. package/dist/conversation-live.d.ts.map +1 -1
  49. package/dist/conversation-live.js +23 -12
  50. package/dist/conversation-row.d.ts +8 -1
  51. package/dist/conversation-row.d.ts.map +1 -1
  52. package/dist/conversation-row.js +20 -7
  53. package/dist/conversation.d.ts +32 -10
  54. package/dist/conversation.d.ts.map +1 -1
  55. package/dist/conversation.js +317 -232
  56. package/dist/declaration.d.ts +10 -0
  57. package/dist/declaration.d.ts.map +1 -0
  58. package/dist/declaration.js +24 -0
  59. package/dist/discovery.d.ts.map +1 -1
  60. package/dist/discovery.js +19 -44
  61. package/dist/durability.d.ts +4 -0
  62. package/dist/durability.d.ts.map +1 -1
  63. package/dist/durability.js +4 -0
  64. package/dist/entry.js +1 -1
  65. package/dist/expectation.d.ts +25 -14
  66. package/dist/expectation.d.ts.map +1 -1
  67. package/dist/expectation.js +51 -20
  68. package/dist/failure.d.ts +3 -0
  69. package/dist/failure.d.ts.map +1 -1
  70. package/dist/failure.js +6 -0
  71. package/dist/figures.d.ts.map +1 -1
  72. package/dist/figures.js +4 -2
  73. package/dist/file.d.ts.map +1 -1
  74. package/dist/file.js +2 -1
  75. package/dist/host.d.ts +18 -2
  76. package/dist/host.d.ts.map +1 -1
  77. package/dist/host.js +55 -15
  78. package/dist/index.d.ts +15 -10
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +12 -7
  81. package/dist/install.d.ts +10 -6
  82. package/dist/install.d.ts.map +1 -1
  83. package/dist/install.js +92 -109
  84. package/dist/lifecycle.d.ts +1 -1
  85. package/dist/lifecycle.d.ts.map +1 -1
  86. package/dist/lifecycle.js +54 -40
  87. package/dist/listing.d.ts +11 -12
  88. package/dist/listing.d.ts.map +1 -1
  89. package/dist/listing.js +48 -72
  90. package/dist/live.d.ts +5 -3
  91. package/dist/live.d.ts.map +1 -1
  92. package/dist/live.js +74 -31
  93. package/dist/loaded.d.ts +5 -0
  94. package/dist/loaded.d.ts.map +1 -0
  95. package/dist/loaded.js +40 -0
  96. package/dist/locks.d.ts +2 -4
  97. package/dist/locks.d.ts.map +1 -1
  98. package/dist/locks.js +10 -39
  99. package/dist/lost.d.ts.map +1 -1
  100. package/dist/lost.js +11 -23
  101. package/dist/media.d.ts +5 -0
  102. package/dist/media.d.ts.map +1 -1
  103. package/dist/media.js +65 -14
  104. package/dist/memory.d.ts +7 -3
  105. package/dist/memory.d.ts.map +1 -1
  106. package/dist/memory.js +56 -26
  107. package/dist/messages.d.ts +12 -6
  108. package/dist/messages.d.ts.map +1 -1
  109. package/dist/messages.js +25 -27
  110. package/dist/model.d.ts +0 -1
  111. package/dist/model.d.ts.map +1 -1
  112. package/dist/model.js +0 -5
  113. package/dist/models.d.ts +4 -8
  114. package/dist/models.d.ts.map +1 -1
  115. package/dist/models.js +97 -139
  116. package/dist/observation.d.ts +27 -12
  117. package/dist/observation.d.ts.map +1 -1
  118. package/dist/observation.js +173 -69
  119. package/dist/opened.d.ts +1 -1
  120. package/dist/opened.d.ts.map +1 -1
  121. package/dist/opened.js +2 -2
  122. package/dist/opening.d.ts.map +1 -1
  123. package/dist/opening.js +5 -9
  124. package/dist/outcome.d.ts +7 -0
  125. package/dist/outcome.d.ts.map +1 -1
  126. package/dist/outcome.js +23 -2
  127. package/dist/overlay.d.ts +6 -2
  128. package/dist/overlay.d.ts.map +1 -1
  129. package/dist/overlay.js +37 -24
  130. package/dist/page-metadata.d.ts +8 -0
  131. package/dist/page-metadata.d.ts.map +1 -0
  132. package/dist/page-metadata.js +19 -0
  133. package/dist/parts.d.ts +3 -59
  134. package/dist/parts.d.ts.map +1 -1
  135. package/dist/parts.js +10 -123
  136. package/dist/preparation.d.ts +27 -6
  137. package/dist/preparation.d.ts.map +1 -1
  138. package/dist/preparation.js +154 -66
  139. package/dist/presence.d.ts +58 -0
  140. package/dist/presence.d.ts.map +1 -0
  141. package/dist/presence.js +231 -0
  142. package/dist/queries.d.ts +2 -2
  143. package/dist/queries.d.ts.map +1 -1
  144. package/dist/queries.js +66 -30
  145. package/dist/questions.d.ts +1 -5
  146. package/dist/questions.d.ts.map +1 -1
  147. package/dist/questions.js +1 -7
  148. package/dist/queue.d.ts +40 -0
  149. package/dist/queue.d.ts.map +1 -0
  150. package/dist/queue.js +480 -0
  151. package/dist/reach.d.ts +2 -3
  152. package/dist/reach.d.ts.map +1 -1
  153. package/dist/reach.js +83 -25
  154. package/dist/react.d.ts +8 -5
  155. package/dist/react.d.ts.map +1 -1
  156. package/dist/react.js +28 -5
  157. package/dist/readiness.d.ts +9 -3
  158. package/dist/readiness.d.ts.map +1 -1
  159. package/dist/readiness.js +3 -5
  160. package/dist/reads.d.ts +4 -2
  161. package/dist/reads.d.ts.map +1 -1
  162. package/dist/reads.js +11 -4
  163. package/dist/recovery.d.ts +3 -1
  164. package/dist/recovery.d.ts.map +1 -1
  165. package/dist/recovery.js +29 -7
  166. package/dist/refresh.js +2 -2
  167. package/dist/refusal.d.ts.map +1 -1
  168. package/dist/refusal.js +5 -5
  169. package/dist/replica.d.ts +5 -11
  170. package/dist/replica.d.ts.map +1 -1
  171. package/dist/replica.js +14 -16
  172. package/dist/retried.d.ts +30 -0
  173. package/dist/retried.d.ts.map +1 -0
  174. package/dist/retried.js +72 -0
  175. package/dist/room-draft.d.ts +30 -5
  176. package/dist/room-draft.d.ts.map +1 -1
  177. package/dist/room-draft.js +270 -63
  178. package/dist/room-drafts.d.ts.map +1 -1
  179. package/dist/room-drafts.js +8 -5
  180. package/dist/room-list.d.ts +10 -0
  181. package/dist/room-list.d.ts.map +1 -0
  182. package/dist/room-list.js +45 -0
  183. package/dist/rows.d.ts +2 -1
  184. package/dist/rows.d.ts.map +1 -1
  185. package/dist/rows.js +9 -10
  186. package/dist/runtime.d.ts +9 -18
  187. package/dist/runtime.d.ts.map +1 -1
  188. package/dist/save.d.ts +42 -90
  189. package/dist/save.d.ts.map +1 -1
  190. package/dist/save.js +314 -223
  191. package/dist/saves.d.ts +2 -40
  192. package/dist/saves.d.ts.map +1 -1
  193. package/dist/saves.js +124 -703
  194. package/dist/seed.d.ts.map +1 -1
  195. package/dist/seed.js +4 -4
  196. package/dist/shown.d.ts +7 -5
  197. package/dist/shown.d.ts.map +1 -1
  198. package/dist/shown.js +75 -58
  199. package/dist/socket/lanes.d.ts +12 -4
  200. package/dist/socket/lanes.d.ts.map +1 -1
  201. package/dist/socket/lanes.js +34 -8
  202. package/dist/socket.d.ts +3 -6
  203. package/dist/socket.d.ts.map +1 -1
  204. package/dist/socket.js +3 -14
  205. package/dist/source.d.ts +5 -7
  206. package/dist/source.d.ts.map +1 -1
  207. package/dist/source.js +13 -2
  208. package/dist/store/installs.d.ts +2 -2
  209. package/dist/store/installs.d.ts.map +1 -1
  210. package/dist/store/installs.js +10 -4
  211. package/dist/store/readiness.d.ts +5 -4
  212. package/dist/store/readiness.d.ts.map +1 -1
  213. package/dist/store/readiness.js +28 -12
  214. package/dist/store/records.d.ts +17 -6
  215. package/dist/store/records.d.ts.map +1 -1
  216. package/dist/store/records.js +56 -22
  217. package/dist/store/rooms-shown.d.ts +8 -0
  218. package/dist/store/rooms-shown.d.ts.map +1 -0
  219. package/dist/store/rooms-shown.js +13 -0
  220. package/dist/store.d.ts +23 -14
  221. package/dist/store.d.ts.map +1 -1
  222. package/dist/store.js +46 -17
  223. package/dist/stored.d.ts +24 -13
  224. package/dist/stored.d.ts.map +1 -1
  225. package/dist/stored.js +80 -29
  226. package/dist/surface.d.ts +5 -3
  227. package/dist/surface.d.ts.map +1 -1
  228. package/dist/surface.js +23 -21
  229. package/dist/sync.d.ts +1 -1
  230. package/dist/sync.d.ts.map +1 -1
  231. package/dist/sync.js +89 -85
  232. package/dist/tables.d.ts +16 -56
  233. package/dist/tables.d.ts.map +1 -1
  234. package/dist/tables.js +86 -327
  235. package/dist/tallies.d.ts.map +1 -1
  236. package/dist/tallies.js +1 -2
  237. package/dist/validation.d.ts.map +1 -1
  238. package/dist/validation.js +4 -10
  239. package/dist/wire.d.ts +45 -7
  240. package/dist/wire.d.ts.map +1 -1
  241. package/dist/wire.js +15 -4
  242. package/package.json +3 -2
  243. package/dist/attempt.d.ts +0 -16
  244. package/dist/attempt.d.ts.map +0 -1
  245. package/dist/attempt.js +0 -70
  246. package/dist/job.d.ts +0 -33
  247. package/dist/job.d.ts.map +0 -1
  248. package/dist/job.js +0 -54
  249. package/dist/log.d.ts +0 -6
  250. package/dist/log.d.ts.map +0 -1
  251. package/dist/log.js +0 -14
  252. package/dist/socket/rooms.d.ts +0 -44
  253. package/dist/socket/rooms.d.ts.map +0 -1
  254. package/dist/socket/rooms.js +0 -90
  255. package/dist/store/fences.d.ts +0 -16
  256. package/dist/store/fences.d.ts.map +0 -1
  257. package/dist/store/fences.js +0 -26
package/README.md CHANGED
@@ -12,14 +12,14 @@ npm install @agentshouse/mdruntime
12
12
 
13
13
  ## Entries
14
14
 
15
- | Entry | For |
16
- | ------------------------------- | ---------------------------------------------------------------------------------------------- |
17
- | `@agentshouse/mdruntime/client` | The host's client: `connectMdRuntime(port)` |
18
- | `@agentshouse/mdruntime/host` | The host that serves a Runtime: `serveMdRuntime` |
19
- | `@agentshouse/mdruntime` | Shared types |
20
- | `@agentshouse/mdruntime/memory` | Tests: `createMemoryAdapter`, an in-memory Adapter |
21
- | `@agentshouse/mdruntime/tables` | The host's database: the Authored store's and opened files' tables, `answerRuntime` |
22
- | `@agentshouse/mdruntime/react` | React over the client: `useObserve`, `useSave`; `useReading`, a reader of an expectation store |
15
+ | Entry | For |
16
+ | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17
+ | `@agentshouse/mdruntime/client` | The host's client: `connectMdRuntime(port)` |
18
+ | `@agentshouse/mdruntime/host` | The host that serves a Runtime: `serveMdRuntime` |
19
+ | `@agentshouse/mdruntime` | Shared types |
20
+ | `@agentshouse/mdruntime/memory` | Tests: `createMemoryAdapter`, an in-memory Adapter |
21
+ | `@agentshouse/mdruntime/tables` | The host's database: `formatted` lays it out for a device format, keeping the Authored store's tables and creating any it lacks, and `answerRuntime` answers it |
22
+ | `@agentshouse/mdruntime/react` | React over the client: `useObserve`, `useSave`; `useExpected`, a reader of an expectation store; `useLane`, a reader of an act lane |
23
23
 
24
24
  ## Usage
25
25
 
@@ -33,24 +33,26 @@ const rooms = await runtime.rooms();
33
33
  const notes = await runtime.query({ types: ['note'], select: 'all', limit: 20 });
34
34
  ```
35
35
 
36
- `observe('storage-loss')` reports `null` or the stable code
37
- `storage-loss` when unsynchronized Room Drafts and Pending Changes were lost.
38
- The host supplies the user-facing wording.
36
+ `observe('storage-loss')` reports `null`, the stable code `storage-loss` when
37
+ unsynchronized Room Drafts and Pending Changes were lost, or `visit` while the host holds
38
+ the database in memory, which ends with the visit; the readiness `persistence` answer is then
39
+ `memory`. The host supplies the user-facing wording.
39
40
 
40
41
  In House the bridge object the page holds as `window.houseBridge` places the Chat, which
41
- House serves with the page: install and bundle nothing for it. Mount it in an element,
42
- themed by CSS custom properties on that element, docked at a third of the window:
42
+ House serves with the page: install and bundle nothing for it. Mount it in an element it
43
+ fills, where it lays out the User's chats by the element's width, themed by CSS custom
44
+ properties on that element, or as the Chat button, House's dot that opens it in a sheet:
43
45
 
44
46
  ```ts
45
- const chat = await window.houseBridge.chat.mount(element, { width: '1/3', showChats: true });
47
+ const chat = await window.houseBridge.chat.mount(element, { conversation });
46
48
  element.style.setProperty('--chat-accent', '#2a6cf0');
47
- chat.update({ width: 'full', showChats: true });
48
- chat.unmount();
49
+ chat.update({ conversation: null, agent });
50
+ const button = await window.houseBridge.chat.mountButton({});
51
+ button.open({ agent });
49
52
  ```
50
53
 
51
- The rules an App's agent follows, including Local Query, search, Room
52
- Draft, Save and the Chat, are the `apps/` pages of House's Guide, which House's
53
- `guide/open` opens.
54
+ The rules an App's agent follows, including Local Query, search, Room Draft, Save and the
55
+ Chat, are the `apps/` pages of House's Guide, which House's `guide/open` opens.
54
56
 
55
57
  ## License
56
58
 
@@ -1,6 +1,3 @@
1
1
  import type { Runtime } from './runtime.js';
2
- import type { RuntimeStore } from './store.js';
3
- export declare function unpair(runtime: Runtime, local: RuntimeStore): Promise<void>;
4
2
  export declare function submitLater(runtime: Runtime, locator: string): void;
5
- export declare function submitAll(runtime: Runtime): Promise<void>;
6
3
  //# sourceMappingURL=acceptance.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"acceptance.d.ts","sourceRoot":"","sources":["../src/acceptance.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAG5C,OAAO,KAAK,EAAE,YAAY,EAAe,MAAM,YAAY,CAAC;AAkO5D,wBAAsB,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBjF;AA+ND,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,QAK5D;AA4BD,wBAAsB,SAAS,CAAC,OAAO,EAAE,OAAO,iBAM/C"}
1
+ {"version":3,"file":"acceptance.d.ts","sourceRoot":"","sources":["../src/acceptance.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AA0V5C,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,QAK5D"}
@@ -1,18 +1,19 @@
1
+ import { refusalError } from './failure.js';
1
2
  import { changeSet } from './change-set.js';
3
+ import { declared } from './declaration.js';
2
4
  import { leaving as leavingIn, partnerOf } from './change.js';
3
5
  import { olderThan, showing } from './install.js';
4
- import { readable } from './listing.js';
6
+ import { scoped } from './models.js';
7
+ import { answered, settle } from './queue.js';
5
8
  import { refuse } from './refusal.js';
6
- import { landed, returnedPaths, returning, slicing, touchedBy } from './replica.js';
7
- import { batchFile, landedWhole, settle, settlement, sliceAt, together, unpairing, withdrawable, } from './saves.js';
9
+ import { returnedPaths, returning, slicing } from './replica.js';
10
+ import { batchFile } from './saves.js';
8
11
  import { rest, roomUnreadable, statusShown } from './shown.js';
12
+ import { advanced, withhold } from './sync.js';
9
13
  import { unconfirmed } from './transfer.js';
10
14
  import { stepped } from './turn.js';
11
- function pendingKey(locator, id) {
12
- return `${locator}\u0000${id}`;
13
- }
14
15
  async function mark(runtime, local, locator, batch, submission, lost) {
15
- const { store, socket } = runtime;
16
+ const { store, socket, presence } = runtime;
16
17
  const partner = partnerOf(batch, locator);
17
18
  for (const held of partner === null ? [locator] : [locator, partner]) {
18
19
  const { state, room } = await local.pending(held, batch.id);
@@ -22,116 +23,86 @@ async function mark(runtime, local, locator, batch, submission, lost) {
22
23
  continue;
23
24
  }
24
25
  await local.submit(held, batch.id, submission, lost);
25
- if (store.fences.has(held))
26
+ if (presence.lost(held))
26
27
  continue;
27
28
  if (room)
28
29
  store.changed(changeSet(held, { queue: true }));
29
30
  }
30
31
  statusShown(runtime);
31
32
  }
32
- async function keepUpload({ store, socket }, local, locator, identifier, upload) {
33
+ async function keepUpload({ socket, presence }, local, locator, identifier, upload) {
33
34
  if (!socket.continuing())
34
35
  throw new Error();
35
36
  await local.keepUpload(locator, identifier, upload);
36
- if (store.fences.has(locator))
37
+ if (presence.lost(locator))
37
38
  throw new Error();
38
39
  if (!socket.continuing())
39
40
  throw new Error();
40
41
  }
41
- function leaving(batch, result) {
42
- return {
43
- batch,
44
- returned: {
45
- kind: result.kind,
46
- code: result.code,
47
- fields: result.fields,
48
- retryable: result.retryable,
49
- },
50
- };
51
- }
52
- function logAccepted({ journal }, locator, submitted, accepted) {
53
- journal.record(`outcome ${locator} ${submitted.id} accepted ${accepted.roomVersion}` +
54
- (accepted.author === undefined ? '' : ` author ${accepted.author}`) +
55
- (accepted.time === undefined ? '' : ` time ${accepted.time}`));
56
- }
57
- function settling(acceptance) {
58
- return acceptance !== null && 'apply' in acceptance;
59
- }
60
- function failureOf(acceptance) {
61
- return settling(acceptance) ? null : acceptance;
42
+ function restands(adoptions) {
43
+ return adoptions.flatMap((one) => (one === null || !('record' in one) ? [] : [one]));
62
44
  }
63
- async function acceptance(runtime, room, local, submitted, accepted) {
45
+ async function adoption(runtime, room, current, local, submitted, accepted) {
64
46
  const { store, socket } = runtime;
65
47
  if (!socket.continuing())
66
48
  return unconfirmed;
67
- const work = await store.work(room.locator);
68
- const current = work.replica;
69
49
  if (current === undefined || !current.queue.some((batch) => batch.id === submitted.id)) {
70
50
  return null;
71
51
  }
72
52
  const held = current.record.roomVersion;
73
- logAccepted(runtime, room.locator, submitted, accepted);
74
53
  let record = current.record;
75
- let changes;
54
+ let adopted;
76
55
  if (!socket.reads.reached(room.locator, held, accepted.roomVersion) &&
77
56
  !olderThan(runtime, room.locator, accepted.roomVersion, held)) {
78
57
  const paths = [...returnedPaths(submitted, accepted)];
79
58
  const confirmedLayer = await stepped(slicing(await local.entries(record.key, 'confirmed', paths), paths));
80
- const back = await stepped(returning(confirmedLayer, submitted, accepted, accepted.roomVersion, store.composed(record)));
59
+ adopted = await stepped(returning(confirmedLayer, submitted, accepted, accepted.roomVersion, store.composed(record)));
81
60
  record = {
82
61
  ...record,
83
62
  roomVersion: socket.follows(room.locator, held, accepted.roomVersion)
84
63
  ? accepted.roomVersion
85
64
  : held,
86
65
  };
87
- changes = back;
88
66
  }
89
- const settled = await settlement(runtime, local, room.locator, work, record, current.queue.filter((batch) => batch.id !== submitted.id), { room: true, ...(changes === undefined ? {} : { changes }) });
90
67
  const reached = record.roomVersion;
91
68
  return {
92
- ...settled,
93
- apply(answer) {
94
- const set = settled.apply(answer);
69
+ locator: room.locator,
70
+ record,
71
+ accepted: submitted.id,
72
+ ...(adopted === undefined ? {} : { adopted }),
73
+ after() {
95
74
  showing(runtime, room.locator, accepted.roomVersion, reached);
96
- readable(runtime, room);
97
- return set;
75
+ runtime.presence.readable(room.locator);
98
76
  },
99
77
  };
100
78
  }
101
- async function adoptedTogether(runtime, local, acceptances) {
102
- const settlements = acceptances.filter(settling);
103
- if (settlements.length > 0) {
104
- const set = await together(runtime, local, settlements);
105
- void statusShown(runtime);
106
- void runtime.store.changed(set);
107
- }
108
- await unpair(runtime, local);
109
- }
110
79
  async function adopted(runtime, room, local, submitted, accepted) {
111
- const failed = await runtime.store.exclusive(async () => {
112
- const adopting = await acceptance(runtime, room, local, submitted, accepted);
113
- await adoptedTogether(runtime, local, [adopting]);
114
- return failureOf(adopting);
80
+ let kept = true;
81
+ await settle(runtime, [room.locator], async (held) => {
82
+ const adopting = await adoption(runtime, room, held.get(room.locator)?.replica, local, submitted, accepted);
83
+ kept = adopting !== unconfirmed;
84
+ return restands([adopting]);
115
85
  });
116
- return failed === null;
86
+ return kept;
117
87
  }
118
88
  async function adoptedMove(runtime, room, partner, local, submitted, accepted) {
119
- const { store, socket } = runtime;
120
- const other = socket.rooms.discovered(partner);
121
- const failed = await store.exclusive(async () => {
122
- const left = await acceptance(runtime, room, local, submitted, accepted);
123
- const arriving = (await store.replica(partner))?.queue.find((batch) => batch.id === submitted.id);
89
+ const other = runtime.presence.room(partner);
90
+ let kept = true;
91
+ await settle(runtime, [room.locator, partner], async (held) => {
92
+ const left = await adoption(runtime, room, held.get(room.locator)?.replica, local, submitted, accepted);
93
+ const there = held.get(partner)?.replica;
94
+ const arriving = there?.queue.find((batch) => batch.id === submitted.id);
124
95
  const arrived = other === undefined || arriving === undefined || accepted.destination === undefined
125
96
  ? null
126
- : await acceptance(runtime, other, local, arriving, {
97
+ : await adoption(runtime, other, there, local, arriving, {
127
98
  kind: 'accepted',
128
99
  ...accepted.destination,
129
100
  });
130
- await adoptedTogether(runtime, local, [left, arrived]);
131
- return failureOf(left);
101
+ kept = left !== unconfirmed;
102
+ return restands([left, arrived]);
132
103
  });
133
104
  submitLater(runtime, partner);
134
- return failed === null;
105
+ return kept;
135
106
  }
136
107
  async function behindIn({ store }, locator, batch) {
137
108
  const local = await store.open();
@@ -141,38 +112,12 @@ async function behindIn({ store }, locator, batch) {
141
112
  ]);
142
113
  return room ? order.indexOf(batch.id) > 0 : order.length > 0;
143
114
  }
144
- async function unpaired(local, skipped) {
145
- const { queued, moving } = await local.moving();
146
- for (const { locator, batch, state } of moving) {
147
- const partner = partnerOf(batch, locator);
148
- if (partner === null || skipped.has(pendingKey(locator, batch.id)))
149
- continue;
150
- if (queued.get(partner)?.includes(batch.id) === true)
151
- continue;
152
- if (!leavingIn(batch, locator) || withdrawable(new Map([[batch.id, state]]))(batch)) {
153
- return { locator, batch };
154
- }
155
- }
156
- return null;
157
- }
158
- export async function unpair(runtime, local) {
159
- const { store, socket } = runtime;
160
- const handled = new Set();
161
- for (let held = await unpaired(local, handled); held !== null && socket.continuing(); held = await unpaired(local, handled)) {
162
- const { locator, batch } = held;
163
- handled.add(pendingKey(locator, batch.id));
164
- store.changed(await together(runtime, local, [await unpairing(runtime, local, locator, [batch])]));
165
- }
166
- if (handled.size === 0)
167
- return;
168
- statusShown(runtime);
169
- }
170
115
  async function submitRoom(runtime, room, local, authorized, progress, signal) {
171
- const { store, socket, journal, advanced, withhold } = runtime;
116
+ const { store, socket, presence } = runtime;
172
117
  while (socket.continuing() &&
173
118
  socket.session.now().authorization !== undefined &&
174
119
  !signal.aborted) {
175
- const headId = (await local.order(room.locator)).find((id) => store.landed.of(room.locator, id) === undefined);
120
+ const headId = (await local.order(room.locator)).find((id) => store.conflicted.of(room.locator, id) === undefined);
176
121
  if (headId === undefined)
177
122
  return 'done';
178
123
  const [head, pending] = await Promise.all([
@@ -201,27 +146,24 @@ async function submitRoom(runtime, room, local, authorized, progress, signal) {
201
146
  }
202
147
  if (socket.writesHeld()) {
203
148
  progress.complete = false;
204
- journal.record(`submission ${room.locator} ${head.id} held`);
205
149
  return 'done';
206
150
  }
207
151
  const lost = earlier || submission.state === 'submitted';
208
152
  let result;
209
- journal.record(`submission ${room.locator} ${head.id}`);
210
153
  await store.exclusive(() => mark(runtime, local, room.locator, head, { state: 'submitted' }, lost));
211
- if (socket.session.now().paused || store.fences.has(room.locator))
154
+ if (socket.session.now().paused || presence.lost(room.locator))
212
155
  return 'done';
213
156
  if (signal.aborted || socket.writesHeld()) {
214
157
  await store.exclusive(() => mark(runtime, local, room.locator, head, submission, earlier));
215
158
  if (signal.aborted)
216
159
  return 'done';
217
160
  progress.complete = false;
218
- journal.record(`submission ${room.locator} ${head.id} held`);
219
161
  return 'done';
220
162
  }
221
163
  const opensBefore = socket.session.now().openSignals;
222
164
  const answeredHere = socket.awaitingAnswer(room.locator);
223
165
  const answeredThere = partner === null ? () => { } : socket.awaitingAnswer(partner);
224
- const answered = () => {
166
+ const heard = () => {
225
167
  answeredHere();
226
168
  answeredThere();
227
169
  };
@@ -233,29 +175,33 @@ async function submitRoom(runtime, room, local, authorized, progress, signal) {
233
175
  ...(state?.upload === undefined ? {} : { upload: state.upload }),
234
176
  }, (upload) => store.exclusive(() => keepUpload(runtime, local, room.locator, head.id, upload)));
235
177
  }
236
- catch {
237
- answered();
178
+ catch (error) {
179
+ runtime.retries.failed('sending', submitting(room.locator), error);
180
+ heard();
238
181
  progress.complete = false;
239
182
  if (socket.continuing())
240
183
  progress.reachable = false;
241
184
  return 'again';
242
185
  }
243
186
  if (signal.aborted) {
244
- answered();
187
+ heard();
245
188
  return 'done';
246
189
  }
247
190
  if (result.kind !== 'accepted' || !socket.continuing())
248
- answered();
191
+ heard();
249
192
  if (!socket.continuing())
250
193
  return 'done';
194
+ if (result.kind === 'accepted')
195
+ runtime.retries.succeeded('sending', submitting(room.locator));
251
196
  if (result.kind === 'transport-uncertainty') {
197
+ runtime.retries.uncertain('sending', submitting(room.locator), result);
252
198
  progress.complete = false;
253
199
  progress.reachable = false;
254
200
  return 'again';
255
201
  }
256
202
  if (result.kind === 'room-not-readable') {
257
203
  progress.complete = false;
258
- roomUnreadable(runtime, socket.rooms.discovered(result.locator) ?? room);
204
+ roomUnreadable(runtime, (presence.room(result.locator) ?? room).locator);
259
205
  return 'done';
260
206
  }
261
207
  if (result.kind === 'revoked-authorization') {
@@ -263,81 +209,54 @@ async function submitRoom(runtime, room, local, authorized, progress, signal) {
263
209
  return 'done';
264
210
  }
265
211
  if (result.kind === 'scoped-refusal') {
266
- await runtime.scoped(result, room, local);
212
+ await scoped(runtime, result, room.locator, declared(room));
267
213
  progress.complete = false;
268
214
  return 'done';
269
215
  }
270
216
  if (result.kind === 'write-halted') {
271
- journal.record(`outcome ${room.locator} ${head.id} write-halted`);
272
217
  await store.exclusive(() => mark(runtime, local, room.locator, head, { state: 'waiting' }, lost));
273
218
  if (!socket.haltedSince(opensBefore))
274
219
  continue;
275
220
  progress.complete = false;
276
221
  return 'done';
277
222
  }
278
- const other = partner === null ? undefined : socket.rooms.discovered(partner);
279
- if (result.kind === 'conflicted' ||
280
- result.kind === 'rejected' ||
281
- result.kind === 'retryable-refusal') {
282
- journal.record(`outcome ${room.locator} ${head.id} ${result.kind}` +
283
- (result.kind === 'conflicted' && result.roomVersion !== undefined
284
- ? ` ${result.roomVersion}`
285
- : ''));
286
- journal.record(`refusal ${result.kind} ${result.code}`);
287
- }
223
+ const other = partner === null ? undefined : presence.room(partner);
288
224
  if (result.kind === 'retryable-refusal') {
225
+ runtime.retries.failed('sending', submitting(room.locator), refusalError(result));
289
226
  const reset = result.reset;
290
227
  progress.complete = false;
291
228
  await store.exclusive(() => mark(runtime, local, room.locator, head, reset === undefined ? { state: 'waiting' } : { state: 'reset', reset }, lost));
292
229
  return reset === undefined ? 'again' : { at: Date.parse(reset) };
293
230
  }
294
231
  if (result.kind === 'conflicted' || result.kind === 'rejected') {
295
- const left = leaving(head, result);
296
232
  const refreshed = async () => ({
297
- there: other === undefined ? null : await advanced(other, local, authorized),
298
- here: await advanced(room, local, authorized),
233
+ there: other === undefined ? null : await advanced(runtime, other, local, authorized),
234
+ here: await advanced(runtime, room, local, authorized),
299
235
  });
300
236
  const judged = result.kind === 'conflicted' && other !== undefined ? await refreshed() : null;
301
237
  if (judged !== null && (judged.there !== null || judged.here !== null)) {
302
238
  await store.exclusive(() => mark(runtime, local, room.locator, head, { state: 'waiting' }, lost));
303
239
  if (other !== undefined && judged.there !== null) {
304
- await withhold(judged.there, other, local, progress);
240
+ await withhold(runtime, judged.there, other, local, progress);
305
241
  }
306
242
  if (judged.here !== null)
307
- await withhold(judged.here, room, local, progress);
243
+ await withhold(runtime, judged.here, room, local, progress);
308
244
  return 'done';
309
245
  }
310
- await store.exclusive(async () => {
311
- if (!socket.continuing())
312
- return;
313
- const work = await store.work(room.locator);
314
- const current = work.replica;
315
- if (current === undefined || !current.queue.some((batch) => batch.id === head.id)) {
316
- return;
317
- }
318
- const confirmedLayer = await sliceAt(store, current.record, 'confirmed', [head]);
319
- const held = result.kind === 'conflicted' &&
320
- (await landedWhole(store, room.locator, head, confirmedLayer, result.affected));
321
- if (held && partner === null && !landed(head, confirmedLayer)) {
322
- store.landed.hold(room.locator, head.id, Object.fromEntries([...touchedBy([head])].map((path) => [path, confirmedLayer.revisions[path]])));
323
- return;
324
- }
325
- store.changed(await settle(runtime, local, room.locator, work, current.record, current.queue.filter((batch) => batch.id !== head.id), { leaving: [held ? { ...left, returned: null } : left] }));
326
- await unpair(runtime, local);
327
- statusShown(runtime);
328
- });
246
+ await answered(runtime, room.locator, head, result);
329
247
  const { there, here } = judged ?? (await refreshed());
330
- if (other !== undefined && there !== null)
331
- await withhold(there, other, local, progress);
248
+ if (other !== undefined && there !== null) {
249
+ await withhold(runtime, there, other, local, progress);
250
+ }
332
251
  if (here !== null) {
333
- await withhold(here, room, local, progress);
252
+ await withhold(runtime, here, room, local, progress);
334
253
  return 'done';
335
254
  }
336
255
  continue;
337
256
  }
338
257
  const kept = await (partner === null
339
258
  ? adopted(runtime, room, local, head, result)
340
- : adoptedMove(runtime, room, partner, local, head, result)).finally(answered);
259
+ : adoptedMove(runtime, room, partner, local, head, result)).finally(heard);
341
260
  if (!kept) {
342
261
  progress.complete = false;
343
262
  return 'done';
@@ -345,14 +264,17 @@ async function submitRoom(runtime, room, local, authorized, progress, signal) {
345
264
  }
346
265
  return 'done';
347
266
  }
267
+ function submitting(locator) {
268
+ return `submit ${locator}`;
269
+ }
348
270
  export function submitLater(runtime, locator) {
349
271
  if (runtime.localSaves)
350
272
  return;
351
- void runtime.recovery.attempt(`submit ${locator}`, (signal) => submitPass(runtime, locator, signal));
273
+ void runtime.recovery.attempt(submitting(locator), (signal) => submitPass(runtime, locator, signal));
352
274
  }
353
275
  async function submitPass(runtime, locator, signal) {
354
- const { store, socket } = runtime;
355
- const room = socket.rooms.discovered(locator);
276
+ const { store, socket, presence } = runtime;
277
+ const room = presence.room(locator);
356
278
  if (!socket.continuing() || socket.stopping() || room === undefined) {
357
279
  return 'done';
358
280
  }
@@ -365,7 +287,8 @@ async function submitPass(runtime, locator, signal) {
365
287
  return 'done';
366
288
  next = await submitRoom(runtime, room, await store.open(), authorization, progress, signal);
367
289
  }
368
- catch {
290
+ catch (error) {
291
+ runtime.retries.failed('sending', submitting(locator), error);
369
292
  progress.reachable = false;
370
293
  next = 'again';
371
294
  }
@@ -374,12 +297,3 @@ async function submitPass(runtime, locator, signal) {
374
297
  await rest(runtime, progress);
375
298
  return next === 'again' && !socket.isOpen() ? 'done' : next;
376
299
  }
377
- export async function submitAll(runtime) {
378
- const { store } = runtime;
379
- if (runtime.localSaves)
380
- return;
381
- for (const locator of await (await store.open()).queued()) {
382
- if (!store.fences.has(locator))
383
- submitLater(runtime, locator);
384
- }
385
- }
@@ -0,0 +1,30 @@
1
+ import { type Expected, type Expecting } from './expectation.js';
2
+ export type Ended = 'accepted' | 'refused' | 'unanswered' | 'unsent';
3
+ export type Send<O> = {
4
+ (name: string, input: unknown, sending: (operation: string) => Promise<O>): Promise<O>;
5
+ expect(expected: Expected): void;
6
+ };
7
+ export type LaneShown<O> = {
8
+ readonly busy: boolean;
9
+ readonly drawn: boolean;
10
+ readonly ended: O | null;
11
+ };
12
+ export type Lane<O> = {
13
+ run(steps: (send: Send<O>) => Promise<O>, expected?: Expected): Promise<O>;
14
+ shown(): LaneShown<O>;
15
+ subscribe(listener: () => void): () => void;
16
+ forget(): void;
17
+ };
18
+ export type Acting<O> = {
19
+ open(): Lane<O>;
20
+ };
21
+ export type ActingRules<O> = {
22
+ readonly store: Pick<Expecting, 'begin' | 'expect' | 'drop'>;
23
+ readonly uncertain: O;
24
+ ending(outcome: O): Ended;
25
+ acted?(): void;
26
+ mint?(): string;
27
+ settles?(operation: string): Promise<boolean>;
28
+ };
29
+ export declare function acting<O>(rules: ActingRules<O>): Acting<O>;
30
+ //# sourceMappingURL=act-lanes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"act-lanes.d.ts","sourceRoot":"","sources":["../src/act-lanes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAG/E,MAAM,MAAM,KAAK,GAAG,UAAU,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;AAErE,MAAM,MAAM,IAAI,CAAC,CAAC,IAAI;IACpB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACvF,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACzB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,IAAI,CAAC,CAAC,IAAI;IACpB,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3E,KAAK,IAAI,SAAS,CAAC,CAAC,CAAC,CAAC;IACtB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;IAC5C,MAAM,IAAI,IAAI,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI;IACtB,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI;IAC3B,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC,CAAC;IAC7D,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;IACtB,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,KAAK,CAAC;IAC1B,KAAK,CAAC,IAAI,IAAI,CAAC;IACf,IAAI,CAAC,IAAI,MAAM,CAAC;IAChB,OAAO,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/C,CAAC;AAUF,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CA6N1D"}