@agentshouse/mdruntime 0.1.0-c0136de3edfbe → 0.1.0-c014edb0e9d9b

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 (228) hide show
  1. package/README.md +8 -8
  2. package/dist/acceptance.d.ts +0 -3
  3. package/dist/acceptance.d.ts.map +1 -1
  4. package/dist/acceptance.js +74 -145
  5. package/dist/act-lanes.d.ts +33 -0
  6. package/dist/act-lanes.d.ts.map +1 -0
  7. package/dist/act-lanes.js +177 -0
  8. package/dist/actions.d.ts +7 -2
  9. package/dist/actions.d.ts.map +1 -1
  10. package/dist/actions.js +44 -31
  11. package/dist/adapter.d.ts +30 -44
  12. package/dist/adapter.d.ts.map +1 -1
  13. package/dist/bootstrap.d.ts +1 -1
  14. package/dist/bootstrap.d.ts.map +1 -1
  15. package/dist/bootstrap.js +6 -18
  16. package/dist/change-set.d.ts +4 -0
  17. package/dist/change-set.d.ts.map +1 -1
  18. package/dist/change-set.js +6 -0
  19. package/dist/change.d.ts.map +1 -1
  20. package/dist/change.js +4 -2
  21. package/dist/chat-types.d.ts +2 -0
  22. package/dist/chat-types.d.ts.map +1 -1
  23. package/dist/chat-types.js +11 -0
  24. package/dist/client.d.ts +9 -12
  25. package/dist/client.d.ts.map +1 -1
  26. package/dist/client.js +29 -14
  27. package/dist/collection.d.ts +22 -4
  28. package/dist/collection.d.ts.map +1 -1
  29. package/dist/collection.js +12 -2
  30. package/dist/compile.d.ts.map +1 -1
  31. package/dist/compile.js +15 -21
  32. package/dist/connection.d.ts.map +1 -1
  33. package/dist/connection.js +30 -17
  34. package/dist/conversation-live.d.ts +138 -0
  35. package/dist/conversation-live.d.ts.map +1 -0
  36. package/dist/conversation-live.js +249 -0
  37. package/dist/conversation-row.d.ts.map +1 -1
  38. package/dist/conversation-row.js +7 -1
  39. package/dist/conversation.d.ts +44 -44
  40. package/dist/conversation.d.ts.map +1 -1
  41. package/dist/conversation.js +904 -1024
  42. package/dist/declaration.d.ts +10 -0
  43. package/dist/declaration.d.ts.map +1 -0
  44. package/dist/declaration.js +24 -0
  45. package/dist/discovery.d.ts.map +1 -1
  46. package/dist/discovery.js +18 -40
  47. package/dist/durability.d.ts +12 -0
  48. package/dist/durability.d.ts.map +1 -1
  49. package/dist/expectation.d.ts +4 -2
  50. package/dist/expectation.d.ts.map +1 -1
  51. package/dist/expectation.js +33 -11
  52. package/dist/failure.d.ts +3 -0
  53. package/dist/failure.d.ts.map +1 -1
  54. package/dist/failure.js +6 -0
  55. package/dist/figures.d.ts +1 -0
  56. package/dist/figures.d.ts.map +1 -1
  57. package/dist/figures.js +2 -2
  58. package/dist/holdings.d.ts +29 -0
  59. package/dist/holdings.d.ts.map +1 -0
  60. package/dist/holdings.js +95 -0
  61. package/dist/host.d.ts +17 -2
  62. package/dist/host.d.ts.map +1 -1
  63. package/dist/host.js +38 -8
  64. package/dist/index.d.ts +13 -6
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +9 -3
  67. package/dist/install.d.ts +1 -3
  68. package/dist/install.d.ts.map +1 -1
  69. package/dist/install.js +49 -70
  70. package/dist/lifecycle.d.ts +1 -1
  71. package/dist/lifecycle.d.ts.map +1 -1
  72. package/dist/lifecycle.js +42 -30
  73. package/dist/listing.d.ts +6 -11
  74. package/dist/listing.d.ts.map +1 -1
  75. package/dist/listing.js +25 -61
  76. package/dist/live.d.ts.map +1 -1
  77. package/dist/live.js +3 -3
  78. package/dist/locks.d.ts +2 -4
  79. package/dist/locks.d.ts.map +1 -1
  80. package/dist/locks.js +10 -38
  81. package/dist/lost.d.ts.map +1 -1
  82. package/dist/lost.js +10 -21
  83. package/dist/media.d.ts +1 -1
  84. package/dist/media.d.ts.map +1 -1
  85. package/dist/media.js +4 -4
  86. package/dist/memory.d.ts +4 -2
  87. package/dist/memory.d.ts.map +1 -1
  88. package/dist/memory.js +38 -20
  89. package/dist/messages.d.ts +1 -4
  90. package/dist/messages.d.ts.map +1 -1
  91. package/dist/messages.js +14 -32
  92. package/dist/model.d.ts +0 -1
  93. package/dist/model.d.ts.map +1 -1
  94. package/dist/model.js +0 -5
  95. package/dist/models.d.ts +4 -8
  96. package/dist/models.d.ts.map +1 -1
  97. package/dist/models.js +97 -134
  98. package/dist/observation.d.ts.map +1 -1
  99. package/dist/observation.js +12 -17
  100. package/dist/opened.d.ts +1 -1
  101. package/dist/opened.d.ts.map +1 -1
  102. package/dist/opened.js +5 -5
  103. package/dist/opening.d.ts.map +1 -1
  104. package/dist/opening.js +8 -5
  105. package/dist/outcome.d.ts +6 -1
  106. package/dist/outcome.d.ts.map +1 -1
  107. package/dist/outcome.js +15 -0
  108. package/dist/overlay.d.ts +2 -1
  109. package/dist/overlay.d.ts.map +1 -1
  110. package/dist/overlay.js +32 -21
  111. package/dist/page-metadata.d.ts +8 -0
  112. package/dist/page-metadata.d.ts.map +1 -0
  113. package/dist/page-metadata.js +19 -0
  114. package/dist/parts.d.ts +3 -51
  115. package/dist/parts.d.ts.map +1 -1
  116. package/dist/parts.js +14 -111
  117. package/dist/paths.d.ts +1 -0
  118. package/dist/paths.d.ts.map +1 -1
  119. package/dist/paths.js +22 -0
  120. package/dist/preparation.d.ts.map +1 -1
  121. package/dist/preparation.js +22 -15
  122. package/dist/presence.d.ts +52 -0
  123. package/dist/presence.d.ts.map +1 -0
  124. package/dist/presence.js +220 -0
  125. package/dist/queries.d.ts +1 -1
  126. package/dist/queries.d.ts.map +1 -1
  127. package/dist/queries.js +5 -7
  128. package/dist/query.d.ts +0 -1
  129. package/dist/query.d.ts.map +1 -1
  130. package/dist/questions.d.ts +2 -0
  131. package/dist/questions.d.ts.map +1 -1
  132. package/dist/questions.js +5 -1
  133. package/dist/queue.d.ts +40 -0
  134. package/dist/queue.d.ts.map +1 -0
  135. package/dist/queue.js +455 -0
  136. package/dist/reach.d.ts.map +1 -1
  137. package/dist/reach.js +28 -9
  138. package/dist/react.d.ts +15 -0
  139. package/dist/react.d.ts.map +1 -1
  140. package/dist/react.js +15 -1
  141. package/dist/readiness.d.ts +0 -1
  142. package/dist/readiness.d.ts.map +1 -1
  143. package/dist/reads.d.ts +4 -1
  144. package/dist/reads.d.ts.map +1 -1
  145. package/dist/reads.js +47 -5
  146. package/dist/refresh.js +2 -2
  147. package/dist/refusal.d.ts.map +1 -1
  148. package/dist/refusal.js +5 -4
  149. package/dist/replica.d.ts +5 -10
  150. package/dist/replica.d.ts.map +1 -1
  151. package/dist/replica.js +15 -16
  152. package/dist/retried.d.ts +30 -0
  153. package/dist/retried.d.ts.map +1 -0
  154. package/dist/retried.js +72 -0
  155. package/dist/room-draft.d.ts +4 -6
  156. package/dist/room-draft.d.ts.map +1 -1
  157. package/dist/room-draft.js +36 -52
  158. package/dist/room-drafts.d.ts.map +1 -1
  159. package/dist/room-drafts.js +17 -16
  160. package/dist/rows.d.ts.map +1 -1
  161. package/dist/rows.js +3 -4
  162. package/dist/runtime.d.ts +11 -17
  163. package/dist/runtime.d.ts.map +1 -1
  164. package/dist/save.d.ts +41 -86
  165. package/dist/save.d.ts.map +1 -1
  166. package/dist/save.js +232 -112
  167. package/dist/saves.d.ts +2 -40
  168. package/dist/saves.d.ts.map +1 -1
  169. package/dist/saves.js +107 -698
  170. package/dist/seed.d.ts.map +1 -1
  171. package/dist/seed.js +2 -3
  172. package/dist/shared.d.ts +1 -6
  173. package/dist/shared.d.ts.map +1 -1
  174. package/dist/shared.js +13 -95
  175. package/dist/shown.d.ts +4 -3
  176. package/dist/shown.d.ts.map +1 -1
  177. package/dist/shown.js +29 -33
  178. package/dist/socket/lanes.d.ts +2 -1
  179. package/dist/socket/lanes.d.ts.map +1 -1
  180. package/dist/socket/lanes.js +5 -2
  181. package/dist/socket/session.d.ts +0 -1
  182. package/dist/socket/session.d.ts.map +1 -1
  183. package/dist/socket.d.ts +2 -4
  184. package/dist/socket.d.ts.map +1 -1
  185. package/dist/socket.js +4 -8
  186. package/dist/source.d.ts.map +1 -1
  187. package/dist/source.js +5 -4
  188. package/dist/store/records.d.ts +4 -4
  189. package/dist/store/records.d.ts.map +1 -1
  190. package/dist/store/records.js +33 -11
  191. package/dist/store/space.d.ts +1 -1
  192. package/dist/store/space.d.ts.map +1 -1
  193. package/dist/store/space.js +0 -2
  194. package/dist/store/watchers.js +1 -2
  195. package/dist/store.d.ts +11 -6
  196. package/dist/store.d.ts.map +1 -1
  197. package/dist/store.js +38 -13
  198. package/dist/stored.d.ts +1 -1
  199. package/dist/stored.d.ts.map +1 -1
  200. package/dist/stored.js +22 -10
  201. package/dist/surface.d.ts +4 -2
  202. package/dist/surface.d.ts.map +1 -1
  203. package/dist/surface.js +20 -14
  204. package/dist/sync.d.ts +0 -1
  205. package/dist/sync.d.ts.map +1 -1
  206. package/dist/sync.js +91 -82
  207. package/dist/tables.d.ts +13 -8
  208. package/dist/tables.d.ts.map +1 -1
  209. package/dist/tables.js +25 -12
  210. package/dist/tallies.js +5 -5
  211. package/dist/validation.d.ts.map +1 -1
  212. package/dist/validation.js +2 -2
  213. package/dist/wire.d.ts +41 -4
  214. package/dist/wire.d.ts.map +1 -1
  215. package/dist/wire.js +13 -3
  216. package/package.json +3 -2
  217. package/dist/attempt.d.ts +0 -16
  218. package/dist/attempt.d.ts.map +0 -1
  219. package/dist/attempt.js +0 -70
  220. package/dist/job.d.ts +0 -33
  221. package/dist/job.d.ts.map +0 -1
  222. package/dist/job.js +0 -54
  223. package/dist/socket/rooms.d.ts +0 -43
  224. package/dist/socket/rooms.d.ts.map +0 -1
  225. package/dist/socket/rooms.js +0 -90
  226. package/dist/store/fences.d.ts +0 -16
  227. package/dist/store/fences.d.ts.map +0 -1
  228. 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` |
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; `useLane`, a reader of an act lane |
23
23
 
24
24
  ## Usage
25
25
 
@@ -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":"AAsBA,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;AAoOD,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;AAyX5C,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,QAK5D"}
@@ -1,19 +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
- import { runtimeError } from './failure.js';
4
5
  import { olderThan, showing } from './install.js';
5
- import { readable } from './listing.js';
6
+ import { scoped } from './models.js';
7
+ import { answered, settle } from './queue.js';
6
8
  import { refuse } from './refusal.js';
7
- import { landed, returnedPaths, returning, slicing, touchedBy } from './replica.js';
8
- 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';
9
11
  import { rest, roomUnreadable, statusShown } from './shown.js';
12
+ import { advanced, withhold } from './sync.js';
10
13
  import { unconfirmed } from './transfer.js';
11
14
  import { stepped } from './turn.js';
12
- function pendingKey(locator, id) {
13
- return `${locator}\u0000${id}`;
14
- }
15
15
  async function mark(runtime, local, locator, batch, submission, lost) {
16
- const { store, socket } = runtime;
16
+ const { store, socket, presence } = runtime;
17
17
  const partner = partnerOf(batch, locator);
18
18
  for (const held of partner === null ? [locator] : [locator, partner]) {
19
19
  const { state, room } = await local.pending(held, batch.id);
@@ -23,116 +23,92 @@ async function mark(runtime, local, locator, batch, submission, lost) {
23
23
  continue;
24
24
  }
25
25
  await local.submit(held, batch.id, submission, lost);
26
- if (store.fences.has(held))
26
+ if (presence.lost(held))
27
27
  continue;
28
28
  if (room)
29
29
  store.changed(changeSet(held, { queue: true }));
30
30
  }
31
31
  statusShown(runtime);
32
32
  }
33
- async function keepUpload({ store, socket }, local, locator, identifier, upload) {
33
+ async function keepUpload({ socket, presence }, local, locator, identifier, upload) {
34
34
  if (!socket.continuing())
35
- throw new Error('connection refused');
35
+ throw new Error();
36
36
  await local.keepUpload(locator, identifier, upload);
37
- if (store.fences.has(locator))
38
- throw runtimeError('unknown-room', `Unknown Room ${locator}`);
37
+ if (presence.lost(locator))
38
+ throw new Error();
39
39
  if (!socket.continuing())
40
- throw new Error('connection refused');
41
- }
42
- function leaving(batch, result) {
43
- return {
44
- batch,
45
- returned: {
46
- kind: result.kind,
47
- code: result.code,
48
- fields: result.fields,
49
- retryable: result.retryable,
50
- },
51
- };
40
+ throw new Error();
52
41
  }
53
42
  function logAccepted({ journal }, locator, submitted, accepted) {
54
43
  journal.record(`outcome ${locator} ${submitted.id} accepted ${accepted.roomVersion}` +
55
44
  (accepted.author === undefined ? '' : ` author ${accepted.author}`) +
56
45
  (accepted.time === undefined ? '' : ` time ${accepted.time}`));
57
46
  }
58
- function settling(acceptance) {
59
- return acceptance !== null && 'apply' in acceptance;
47
+ function restands(adoptions) {
48
+ return adoptions.flatMap((one) => (one === null || !('record' in one) ? [] : [one]));
60
49
  }
61
- function failureOf(acceptance) {
62
- return settling(acceptance) ? null : acceptance;
63
- }
64
- async function acceptance(runtime, room, local, submitted, accepted) {
50
+ async function adoption(runtime, room, current, local, submitted, accepted) {
65
51
  const { store, socket } = runtime;
66
52
  if (!socket.continuing())
67
53
  return unconfirmed;
68
- const work = await store.work(room.locator);
69
- const current = work.replica;
70
54
  if (current === undefined || !current.queue.some((batch) => batch.id === submitted.id)) {
71
55
  return null;
72
56
  }
73
57
  const held = current.record.roomVersion;
74
58
  logAccepted(runtime, room.locator, submitted, accepted);
75
59
  let record = current.record;
76
- let changes;
60
+ let adopted;
77
61
  if (!socket.reads.reached(room.locator, held, accepted.roomVersion) &&
78
62
  !olderThan(runtime, room.locator, accepted.roomVersion, held)) {
79
63
  const paths = [...returnedPaths(submitted, accepted)];
80
64
  const confirmedLayer = await stepped(slicing(await local.entries(record.key, 'confirmed', paths), paths));
81
- const back = await stepped(returning(confirmedLayer, submitted, accepted, accepted.roomVersion, store.composed(record)));
65
+ adopted = await stepped(returning(confirmedLayer, submitted, accepted, accepted.roomVersion, store.composed(record)));
82
66
  record = {
83
67
  ...record,
84
68
  roomVersion: socket.follows(room.locator, held, accepted.roomVersion)
85
69
  ? accepted.roomVersion
86
70
  : held,
87
71
  };
88
- changes = back;
89
72
  }
90
- const settled = await settlement(runtime, local, room.locator, work, record, current.queue.filter((batch) => batch.id !== submitted.id), { room: true, ...(changes === undefined ? {} : { changes }) });
91
73
  const reached = record.roomVersion;
92
74
  return {
93
- ...settled,
94
- apply(answer) {
95
- const set = settled.apply(answer);
75
+ locator: room.locator,
76
+ record,
77
+ accepted: submitted.id,
78
+ ...(adopted === undefined ? {} : { adopted }),
79
+ after() {
96
80
  showing(runtime, room.locator, accepted.roomVersion, reached);
97
- readable(runtime, room);
98
- return set;
81
+ runtime.presence.readable(room.locator);
99
82
  },
100
83
  };
101
84
  }
102
- async function adoptedTogether(runtime, local, acceptances) {
103
- const settlements = acceptances.filter(settling);
104
- if (settlements.length > 0) {
105
- const set = await together(runtime, local, settlements);
106
- void statusShown(runtime);
107
- void runtime.store.changed(set);
108
- }
109
- await unpair(runtime, local);
110
- }
111
85
  async function adopted(runtime, room, local, submitted, accepted) {
112
- const failed = await runtime.store.exclusive(async () => {
113
- const adopting = await acceptance(runtime, room, local, submitted, accepted);
114
- await adoptedTogether(runtime, local, [adopting]);
115
- return failureOf(adopting);
86
+ let kept = true;
87
+ await settle(runtime, [room.locator], async (held) => {
88
+ const adopting = await adoption(runtime, room, held.get(room.locator)?.replica, local, submitted, accepted);
89
+ kept = adopting !== unconfirmed;
90
+ return restands([adopting]);
116
91
  });
117
- return failed === null;
92
+ return kept;
118
93
  }
119
94
  async function adoptedMove(runtime, room, partner, local, submitted, accepted) {
120
- const { store, socket } = runtime;
121
- const other = socket.rooms.discovered(partner);
122
- const failed = await store.exclusive(async () => {
123
- const left = await acceptance(runtime, room, local, submitted, accepted);
124
- const arriving = (await store.replica(partner))?.queue.find((batch) => batch.id === submitted.id);
95
+ const other = runtime.presence.room(partner);
96
+ let kept = true;
97
+ await settle(runtime, [room.locator, partner], async (held) => {
98
+ const left = await adoption(runtime, room, held.get(room.locator)?.replica, local, submitted, accepted);
99
+ const there = held.get(partner)?.replica;
100
+ const arriving = there?.queue.find((batch) => batch.id === submitted.id);
125
101
  const arrived = other === undefined || arriving === undefined || accepted.destination === undefined
126
102
  ? null
127
- : await acceptance(runtime, other, local, arriving, {
103
+ : await adoption(runtime, other, there, local, arriving, {
128
104
  kind: 'accepted',
129
105
  ...accepted.destination,
130
106
  });
131
- await adoptedTogether(runtime, local, [left, arrived]);
132
- return failureOf(left);
107
+ kept = left !== unconfirmed;
108
+ return restands([left, arrived]);
133
109
  });
134
110
  submitLater(runtime, partner);
135
- return failed === null;
111
+ return kept;
136
112
  }
137
113
  async function behindIn({ store }, locator, batch) {
138
114
  const local = await store.open();
@@ -142,34 +118,8 @@ async function behindIn({ store }, locator, batch) {
142
118
  ]);
143
119
  return room ? order.indexOf(batch.id) > 0 : order.length > 0;
144
120
  }
145
- async function unpaired(local, skipped) {
146
- const { queued, moving } = await local.moving();
147
- for (const { locator, batch, state } of moving) {
148
- const partner = partnerOf(batch, locator);
149
- if (partner === null || skipped.has(pendingKey(locator, batch.id)))
150
- continue;
151
- if (queued.get(partner)?.includes(batch.id) === true)
152
- continue;
153
- if (!leavingIn(batch, locator) || withdrawable(new Map([[batch.id, state]]))(batch)) {
154
- return { locator, batch };
155
- }
156
- }
157
- return null;
158
- }
159
- export async function unpair(runtime, local) {
160
- const { store, socket } = runtime;
161
- const handled = new Set();
162
- for (let held = await unpaired(local, handled); held !== null && socket.continuing(); held = await unpaired(local, handled)) {
163
- const { locator, batch } = held;
164
- handled.add(pendingKey(locator, batch.id));
165
- store.changed(await together(runtime, local, [await unpairing(runtime, local, locator, [batch])]));
166
- }
167
- if (handled.size === 0)
168
- return;
169
- statusShown(runtime);
170
- }
171
121
  async function submitRoom(runtime, room, local, authorized, progress, signal) {
172
- const { store, socket, journal, advanced, withhold } = runtime;
122
+ const { store, socket, presence, journal } = runtime;
173
123
  while (socket.continuing() &&
174
124
  socket.session.now().authorization !== undefined &&
175
125
  !signal.aborted) {
@@ -209,7 +159,7 @@ async function submitRoom(runtime, room, local, authorized, progress, signal) {
209
159
  let result;
210
160
  journal.record(`submission ${room.locator} ${head.id}`);
211
161
  await store.exclusive(() => mark(runtime, local, room.locator, head, { state: 'submitted' }, lost));
212
- if (socket.session.now().paused || store.fences.has(room.locator))
162
+ if (socket.session.now().paused || presence.lost(room.locator))
213
163
  return 'done';
214
164
  if (signal.aborted || socket.writesHeld()) {
215
165
  await store.exclusive(() => mark(runtime, local, room.locator, head, submission, earlier));
@@ -222,7 +172,7 @@ async function submitRoom(runtime, room, local, authorized, progress, signal) {
222
172
  const opensBefore = socket.session.now().openSignals;
223
173
  const answeredHere = socket.awaitingAnswer(room.locator);
224
174
  const answeredThere = partner === null ? () => { } : socket.awaitingAnswer(partner);
225
- const answered = () => {
175
+ const heard = () => {
226
176
  answeredHere();
227
177
  answeredThere();
228
178
  };
@@ -234,29 +184,33 @@ async function submitRoom(runtime, room, local, authorized, progress, signal) {
234
184
  ...(state?.upload === undefined ? {} : { upload: state.upload }),
235
185
  }, (upload) => store.exclusive(() => keepUpload(runtime, local, room.locator, head.id, upload)));
236
186
  }
237
- catch {
238
- answered();
187
+ catch (error) {
188
+ runtime.retries.failed('sending', submitting(room.locator), error);
189
+ heard();
239
190
  progress.complete = false;
240
191
  if (socket.continuing())
241
192
  progress.reachable = false;
242
193
  return 'again';
243
194
  }
244
195
  if (signal.aborted) {
245
- answered();
196
+ heard();
246
197
  return 'done';
247
198
  }
248
199
  if (result.kind !== 'accepted' || !socket.continuing())
249
- answered();
200
+ heard();
250
201
  if (!socket.continuing())
251
202
  return 'done';
203
+ if (result.kind === 'accepted')
204
+ runtime.retries.succeeded('sending', submitting(room.locator));
252
205
  if (result.kind === 'transport-uncertainty') {
206
+ runtime.retries.uncertain('sending', submitting(room.locator), result);
253
207
  progress.complete = false;
254
208
  progress.reachable = false;
255
209
  return 'again';
256
210
  }
257
211
  if (result.kind === 'room-not-readable') {
258
212
  progress.complete = false;
259
- roomUnreadable(runtime, socket.rooms.discovered(result.locator) ?? room);
213
+ roomUnreadable(runtime, (presence.room(result.locator) ?? room).locator);
260
214
  return 'done';
261
215
  }
262
216
  if (result.kind === 'revoked-authorization') {
@@ -264,7 +218,7 @@ async function submitRoom(runtime, room, local, authorized, progress, signal) {
264
218
  return 'done';
265
219
  }
266
220
  if (result.kind === 'scoped-refusal') {
267
- await runtime.scoped(result, room, local);
221
+ await scoped(runtime, result, room.locator, declared(room));
268
222
  progress.complete = false;
269
223
  return 'done';
270
224
  }
@@ -276,7 +230,7 @@ async function submitRoom(runtime, room, local, authorized, progress, signal) {
276
230
  progress.complete = false;
277
231
  return 'done';
278
232
  }
279
- const other = partner === null ? undefined : socket.rooms.discovered(partner);
233
+ const other = partner === null ? undefined : presence.room(partner);
280
234
  if (result.kind === 'conflicted' ||
281
235
  result.kind === 'rejected' ||
282
236
  result.kind === 'retryable-refusal') {
@@ -287,61 +241,41 @@ async function submitRoom(runtime, room, local, authorized, progress, signal) {
287
241
  journal.record(`refusal ${result.kind} ${result.code}`);
288
242
  }
289
243
  if (result.kind === 'retryable-refusal') {
244
+ runtime.retries.failed('sending', submitting(room.locator), refusalError(result));
290
245
  const reset = result.reset;
291
246
  progress.complete = false;
292
247
  await store.exclusive(() => mark(runtime, local, room.locator, head, reset === undefined ? { state: 'waiting' } : { state: 'reset', reset }, lost));
293
248
  return reset === undefined ? 'again' : { at: Date.parse(reset) };
294
249
  }
295
250
  if (result.kind === 'conflicted' || result.kind === 'rejected') {
296
- const left = leaving(head, result);
297
251
  const refreshed = async () => ({
298
- there: other === undefined ? null : await advanced(other, local, authorized),
299
- here: await advanced(room, local, authorized),
252
+ there: other === undefined ? null : await advanced(runtime, other, local, authorized),
253
+ here: await advanced(runtime, room, local, authorized),
300
254
  });
301
255
  const judged = result.kind === 'conflicted' && other !== undefined ? await refreshed() : null;
302
256
  if (judged !== null && (judged.there !== null || judged.here !== null)) {
303
257
  await store.exclusive(() => mark(runtime, local, room.locator, head, { state: 'waiting' }, lost));
304
258
  if (other !== undefined && judged.there !== null) {
305
- await withhold(judged.there, other, local, progress);
259
+ await withhold(runtime, judged.there, other, local, progress);
306
260
  }
307
261
  if (judged.here !== null)
308
- await withhold(judged.here, room, local, progress);
262
+ await withhold(runtime, judged.here, room, local, progress);
309
263
  return 'done';
310
264
  }
311
- await store.exclusive(async () => {
312
- if (!socket.continuing())
313
- return;
314
- const work = await store.work(room.locator);
315
- const current = work.replica;
316
- if (current === undefined || !current.queue.some((batch) => batch.id === head.id)) {
317
- return;
318
- }
319
- const confirmedLayer = await sliceAt(store, current.record, 'confirmed', [head]);
320
- const held = result.kind === 'conflicted' &&
321
- (await landedWhole(store, room.locator, head, confirmedLayer, result.affected));
322
- if (held &&
323
- result.kind === 'conflicted' &&
324
- partner === null &&
325
- !landed(head, confirmedLayer)) {
326
- store.landed.hold(room.locator, head.id, Object.fromEntries([...touchedBy([head])].map((path) => [path, confirmedLayer.revisions[path]])));
327
- return;
328
- }
329
- 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] }));
330
- await unpair(runtime, local);
331
- statusShown(runtime);
332
- });
265
+ await answered(runtime, room.locator, head, result);
333
266
  const { there, here } = judged ?? (await refreshed());
334
- if (other !== undefined && there !== null)
335
- await withhold(there, other, local, progress);
267
+ if (other !== undefined && there !== null) {
268
+ await withhold(runtime, there, other, local, progress);
269
+ }
336
270
  if (here !== null) {
337
- await withhold(here, room, local, progress);
271
+ await withhold(runtime, here, room, local, progress);
338
272
  return 'done';
339
273
  }
340
274
  continue;
341
275
  }
342
276
  const kept = await (partner === null
343
277
  ? adopted(runtime, room, local, head, result)
344
- : adoptedMove(runtime, room, partner, local, head, result)).finally(answered);
278
+ : adoptedMove(runtime, room, partner, local, head, result)).finally(heard);
345
279
  if (!kept) {
346
280
  progress.complete = false;
347
281
  return 'done';
@@ -349,14 +283,17 @@ async function submitRoom(runtime, room, local, authorized, progress, signal) {
349
283
  }
350
284
  return 'done';
351
285
  }
286
+ function submitting(locator) {
287
+ return `submit ${locator}`;
288
+ }
352
289
  export function submitLater(runtime, locator) {
353
290
  if (runtime.localSaves)
354
291
  return;
355
- void runtime.recovery.attempt(`submit ${locator}`, (signal) => submitPass(runtime, locator, signal));
292
+ void runtime.recovery.attempt(submitting(locator), (signal) => submitPass(runtime, locator, signal));
356
293
  }
357
294
  async function submitPass(runtime, locator, signal) {
358
- const { store, socket } = runtime;
359
- const room = socket.rooms.discovered(locator);
295
+ const { store, socket, presence } = runtime;
296
+ const room = presence.room(locator);
360
297
  if (!socket.continuing() || socket.stopping() || room === undefined) {
361
298
  return 'done';
362
299
  }
@@ -369,7 +306,8 @@ async function submitPass(runtime, locator, signal) {
369
306
  return 'done';
370
307
  next = await submitRoom(runtime, room, await store.open(), authorization, progress, signal);
371
308
  }
372
- catch {
309
+ catch (error) {
310
+ runtime.retries.failed('sending', submitting(locator), error);
373
311
  progress.reachable = false;
374
312
  next = 'again';
375
313
  }
@@ -378,12 +316,3 @@ async function submitPass(runtime, locator, signal) {
378
316
  await rest(runtime, progress);
379
317
  return next === 'again' && !socket.isOpen() ? 'done' : next;
380
318
  }
381
- export async function submitAll(runtime) {
382
- const { store } = runtime;
383
- if (runtime.localSaves)
384
- return;
385
- for (const locator of await (await store.open()).queued()) {
386
- if (!store.fences.has(locator))
387
- submitLater(runtime, locator);
388
- }
389
- }
@@ -0,0 +1,33 @@
1
+ import { type ExpectedOf, type Expecting } from './expectation.js';
2
+ export type Ended = 'accepted' | 'refused' | 'unanswered' | 'unsent';
3
+ type Keyed = {
4
+ readonly key: string;
5
+ };
6
+ export type Send<O, E extends Keyed> = {
7
+ (name: string, input: unknown, sending: (operation: string) => Promise<O>): Promise<O>;
8
+ expect(expected: ExpectedOf<E>): void;
9
+ };
10
+ export type LaneShown<O> = {
11
+ readonly busy: boolean;
12
+ readonly drawn: boolean;
13
+ readonly ended: O | null;
14
+ };
15
+ export type Lane<O, E extends Keyed> = {
16
+ run(steps: (send: Send<O, E>) => Promise<O>, expected?: ExpectedOf<E>): Promise<O>;
17
+ shown(): LaneShown<O>;
18
+ subscribe(listener: () => void): () => void;
19
+ forget(): void;
20
+ };
21
+ export type Acting<O, E extends Keyed> = {
22
+ open(): Lane<O, E>;
23
+ };
24
+ export type ActingRules<O, E extends Keyed> = {
25
+ readonly store: Pick<Expecting<E>, 'begin' | 'expect' | 'drop'>;
26
+ readonly uncertain: O;
27
+ ending(outcome: O): Ended;
28
+ acted?(): void;
29
+ mint?(): string;
30
+ };
31
+ export declare function acting<O, E extends Keyed>(rules: ActingRules<O, E>): Acting<O, E>;
32
+ export {};
33
+ //# 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,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGjF,MAAM,MAAM,KAAK,GAAG,UAAU,GAAG,SAAS,GAAG,YAAY,GAAG,QAAQ,CAAC;AAErE,KAAK,KAAK,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtC,MAAM,MAAM,IAAI,CAAC,CAAC,EAAE,CAAC,SAAS,KAAK,IAAI;IACrC,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,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;CACvC,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,EAAE,CAAC,SAAS,KAAK,IAAI;IACrC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnF,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,EAAE,CAAC,SAAS,KAAK,IAAI;IACvC,IAAI,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,CAAC,EAAE,CAAC,SAAS,KAAK,IAAI;IAC5C,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC,CAAC;IAChE,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;CACjB,CAAC;AAQF,wBAAgB,MAAM,CAAC,CAAC,EAAE,CAAC,SAAS,KAAK,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAmKjF"}
@@ -0,0 +1,177 @@
1
+ import { expectedList } from './expectation.js';
2
+ import { changeBatchIdentity } from './identity.js';
3
+ const IDLE = { busy: false, drawn: false, ended: null };
4
+ export function acting(rules) {
5
+ const { store, uncertain, ending } = rules;
6
+ const mint = rules.mint ?? (() => changeBatchIdentity());
7
+ const identities = new Map();
8
+ const waiting = new Map();
9
+ const blobs = new WeakMap();
10
+ let counted = 0;
11
+ let touches = 0;
12
+ const requestOf = (name, input) => {
13
+ const carried = [];
14
+ const json = JSON.stringify(input, (_key, value) => {
15
+ if (!(value instanceof Blob))
16
+ return value;
17
+ let one = blobs.get(value);
18
+ if (one === undefined) {
19
+ counted += 1;
20
+ one = counted;
21
+ blobs.set(value, one);
22
+ }
23
+ carried.push(one);
24
+ return null;
25
+ });
26
+ return `${name}\u0000${json}\u0000${carried.join(',')}`;
27
+ };
28
+ const identityOf = (request) => {
29
+ const held = identities.get(request);
30
+ if (held !== undefined)
31
+ return held;
32
+ const fresh = { operation: mint(), touched: 0, flying: 0 };
33
+ identities.set(request, fresh);
34
+ return fresh;
35
+ };
36
+ const settled = (act, ended, calls) => {
37
+ const released = new Map();
38
+ for (const { request, identity, ended: answered } of calls) {
39
+ const heard = answered === 'accepted' || answered === 'refused';
40
+ if (ended === 'accepted' && heard)
41
+ released.set(request, identity.touched);
42
+ else {
43
+ touches += 1;
44
+ identity.touched = touches;
45
+ identities.set(request, identity);
46
+ }
47
+ }
48
+ if (ended === 'refused' || ended === 'unsent')
49
+ store.drop(act);
50
+ for (const { request, ended: answered } of calls) {
51
+ const earlier = waiting.get(request);
52
+ if (answered === 'unsent' && earlier !== undefined)
53
+ continue;
54
+ if (earlier !== undefined && earlier !== act)
55
+ store.drop(earlier);
56
+ if (ended === 'unanswered')
57
+ waiting.set(request, act);
58
+ else
59
+ waiting.delete(request);
60
+ }
61
+ if (ended === 'accepted' || calls.some((one) => one.ended === 'accepted'))
62
+ rules.acted?.();
63
+ return released;
64
+ };
65
+ const release = (released) => {
66
+ for (const [request, touched] of released) {
67
+ const held = identities.get(request);
68
+ if (held !== undefined && held.touched === touched && held.flying === 0) {
69
+ identities.delete(request);
70
+ }
71
+ }
72
+ };
73
+ return {
74
+ open() {
75
+ const listeners = new Set();
76
+ const releasing = new Map();
77
+ let tail = Promise.resolve();
78
+ let pressed = 0;
79
+ let drawing = 0;
80
+ let ended = null;
81
+ let shown = IDLE;
82
+ const show = () => {
83
+ const busy = pressed > 0;
84
+ const drawn = drawing > 0;
85
+ if (shown.busy === busy && shown.drawn === drawn && shown.ended === ended)
86
+ return;
87
+ shown = { busy, drawn, ended };
88
+ for (const listener of listeners)
89
+ listener();
90
+ };
91
+ const run = (steps, expected = null) => {
92
+ const act = store.begin();
93
+ let drew = false;
94
+ const expect = (more) => {
95
+ if (expectedList(more).length === 0)
96
+ return;
97
+ store.expect(act, more);
98
+ if (drew)
99
+ return;
100
+ drew = true;
101
+ drawing += 1;
102
+ show();
103
+ };
104
+ pressed += 1;
105
+ expect(expected);
106
+ show();
107
+ const calls = [];
108
+ const send = Object.assign(async (name, input, sending) => {
109
+ const request = requestOf(name, input);
110
+ const identity = identityOf(request);
111
+ const called = { request, identity, ended: null };
112
+ calls.push(called);
113
+ touches += 1;
114
+ identity.touched = touches;
115
+ identity.flying += 1;
116
+ let outcome;
117
+ try {
118
+ outcome = await sending(identity.operation);
119
+ }
120
+ catch {
121
+ outcome = uncertain;
122
+ }
123
+ finally {
124
+ identity.flying -= 1;
125
+ }
126
+ called.ended = ending(outcome);
127
+ return outcome;
128
+ }, { expect });
129
+ const end = (outcome, how) => {
130
+ for (const [request, touched] of settled(act, how, calls))
131
+ releasing.set(request, touched);
132
+ pressed -= 1;
133
+ if (drew)
134
+ drawing -= 1;
135
+ ended = outcome;
136
+ if (pressed === 0) {
137
+ release(releasing);
138
+ releasing.clear();
139
+ }
140
+ show();
141
+ };
142
+ const turn = tail.then(async () => {
143
+ ended = null;
144
+ show();
145
+ let outcome;
146
+ try {
147
+ outcome = await steps(send);
148
+ }
149
+ catch (error) {
150
+ end(uncertain, 'unanswered');
151
+ throw error;
152
+ }
153
+ end(outcome, ending(outcome));
154
+ return outcome;
155
+ });
156
+ tail = turn.catch(() => undefined);
157
+ return turn;
158
+ };
159
+ return {
160
+ run,
161
+ shown: () => shown,
162
+ subscribe(listener) {
163
+ listeners.add(listener);
164
+ return () => {
165
+ listeners.delete(listener);
166
+ };
167
+ },
168
+ forget() {
169
+ if (pressed > 0 || ended === null)
170
+ return;
171
+ ended = null;
172
+ show();
173
+ },
174
+ };
175
+ },
176
+ };
177
+ }