@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.
- package/README.md +8 -8
- package/dist/acceptance.d.ts +0 -3
- package/dist/acceptance.d.ts.map +1 -1
- package/dist/acceptance.js +74 -145
- package/dist/act-lanes.d.ts +33 -0
- package/dist/act-lanes.d.ts.map +1 -0
- package/dist/act-lanes.js +177 -0
- package/dist/actions.d.ts +7 -2
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +44 -31
- package/dist/adapter.d.ts +30 -44
- package/dist/adapter.d.ts.map +1 -1
- package/dist/bootstrap.d.ts +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +6 -18
- package/dist/change-set.d.ts +4 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +6 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +4 -2
- package/dist/chat-types.d.ts +2 -0
- package/dist/chat-types.d.ts.map +1 -1
- package/dist/chat-types.js +11 -0
- package/dist/client.d.ts +9 -12
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +29 -14
- package/dist/collection.d.ts +22 -4
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +12 -2
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +15 -21
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +30 -17
- package/dist/conversation-live.d.ts +138 -0
- package/dist/conversation-live.d.ts.map +1 -0
- package/dist/conversation-live.js +249 -0
- package/dist/conversation-row.d.ts.map +1 -1
- package/dist/conversation-row.js +7 -1
- package/dist/conversation.d.ts +44 -44
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +904 -1024
- package/dist/declaration.d.ts +10 -0
- package/dist/declaration.d.ts.map +1 -0
- package/dist/declaration.js +24 -0
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +18 -40
- package/dist/durability.d.ts +12 -0
- package/dist/durability.d.ts.map +1 -1
- package/dist/expectation.d.ts +4 -2
- package/dist/expectation.d.ts.map +1 -1
- package/dist/expectation.js +33 -11
- package/dist/failure.d.ts +3 -0
- package/dist/failure.d.ts.map +1 -1
- package/dist/failure.js +6 -0
- package/dist/figures.d.ts +1 -0
- package/dist/figures.d.ts.map +1 -1
- package/dist/figures.js +2 -2
- package/dist/holdings.d.ts +29 -0
- package/dist/holdings.d.ts.map +1 -0
- package/dist/holdings.js +95 -0
- package/dist/host.d.ts +17 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +38 -8
- package/dist/index.d.ts +13 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -3
- package/dist/install.d.ts +1 -3
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +49 -70
- package/dist/lifecycle.d.ts +1 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +42 -30
- package/dist/listing.d.ts +6 -11
- package/dist/listing.d.ts.map +1 -1
- package/dist/listing.js +25 -61
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +3 -3
- package/dist/locks.d.ts +2 -4
- package/dist/locks.d.ts.map +1 -1
- package/dist/locks.js +10 -38
- package/dist/lost.d.ts.map +1 -1
- package/dist/lost.js +10 -21
- package/dist/media.d.ts +1 -1
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +4 -4
- package/dist/memory.d.ts +4 -2
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +38 -20
- package/dist/messages.d.ts +1 -4
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +14 -32
- package/dist/model.d.ts +0 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -5
- package/dist/models.d.ts +4 -8
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +97 -134
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +12 -17
- package/dist/opened.d.ts +1 -1
- package/dist/opened.d.ts.map +1 -1
- package/dist/opened.js +5 -5
- package/dist/opening.d.ts.map +1 -1
- package/dist/opening.js +8 -5
- package/dist/outcome.d.ts +6 -1
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +15 -0
- package/dist/overlay.d.ts +2 -1
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +32 -21
- package/dist/page-metadata.d.ts +8 -0
- package/dist/page-metadata.d.ts.map +1 -0
- package/dist/page-metadata.js +19 -0
- package/dist/parts.d.ts +3 -51
- package/dist/parts.d.ts.map +1 -1
- package/dist/parts.js +14 -111
- package/dist/paths.d.ts +1 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +22 -0
- package/dist/preparation.d.ts.map +1 -1
- package/dist/preparation.js +22 -15
- package/dist/presence.d.ts +52 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +220 -0
- package/dist/queries.d.ts +1 -1
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +5 -7
- package/dist/query.d.ts +0 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/questions.d.ts +2 -0
- package/dist/questions.d.ts.map +1 -1
- package/dist/questions.js +5 -1
- package/dist/queue.d.ts +40 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +455 -0
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +28 -9
- package/dist/react.d.ts +15 -0
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +15 -1
- package/dist/readiness.d.ts +0 -1
- package/dist/readiness.d.ts.map +1 -1
- package/dist/reads.d.ts +4 -1
- package/dist/reads.d.ts.map +1 -1
- package/dist/reads.js +47 -5
- package/dist/refresh.js +2 -2
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +5 -4
- package/dist/replica.d.ts +5 -10
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +15 -16
- package/dist/retried.d.ts +30 -0
- package/dist/retried.d.ts.map +1 -0
- package/dist/retried.js +72 -0
- package/dist/room-draft.d.ts +4 -6
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +36 -52
- package/dist/room-drafts.d.ts.map +1 -1
- package/dist/room-drafts.js +17 -16
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +3 -4
- package/dist/runtime.d.ts +11 -17
- package/dist/runtime.d.ts.map +1 -1
- package/dist/save.d.ts +41 -86
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +232 -112
- package/dist/saves.d.ts +2 -40
- package/dist/saves.d.ts.map +1 -1
- package/dist/saves.js +107 -698
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +2 -3
- package/dist/shared.d.ts +1 -6
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +13 -95
- package/dist/shown.d.ts +4 -3
- package/dist/shown.d.ts.map +1 -1
- package/dist/shown.js +29 -33
- package/dist/socket/lanes.d.ts +2 -1
- package/dist/socket/lanes.d.ts.map +1 -1
- package/dist/socket/lanes.js +5 -2
- package/dist/socket/session.d.ts +0 -1
- package/dist/socket/session.d.ts.map +1 -1
- package/dist/socket.d.ts +2 -4
- package/dist/socket.d.ts.map +1 -1
- package/dist/socket.js +4 -8
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +5 -4
- package/dist/store/records.d.ts +4 -4
- package/dist/store/records.d.ts.map +1 -1
- package/dist/store/records.js +33 -11
- package/dist/store/space.d.ts +1 -1
- package/dist/store/space.d.ts.map +1 -1
- package/dist/store/space.js +0 -2
- package/dist/store/watchers.js +1 -2
- package/dist/store.d.ts +11 -6
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +38 -13
- package/dist/stored.d.ts +1 -1
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +22 -10
- package/dist/surface.d.ts +4 -2
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +20 -14
- package/dist/sync.d.ts +0 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +91 -82
- package/dist/tables.d.ts +13 -8
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +25 -12
- package/dist/tallies.js +5 -5
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +2 -2
- package/dist/wire.d.ts +41 -4
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +13 -3
- package/package.json +3 -2
- package/dist/attempt.d.ts +0 -16
- package/dist/attempt.d.ts.map +0 -1
- package/dist/attempt.js +0 -70
- package/dist/job.d.ts +0 -33
- package/dist/job.d.ts.map +0 -1
- package/dist/job.js +0 -54
- package/dist/socket/rooms.d.ts +0 -43
- package/dist/socket/rooms.d.ts.map +0 -1
- package/dist/socket/rooms.js +0 -90
- package/dist/store/fences.d.ts +0 -16
- package/dist/store/fences.d.ts.map +0 -1
- 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
|
|
package/dist/acceptance.d.ts
CHANGED
|
@@ -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
|
package/dist/acceptance.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acceptance.d.ts","sourceRoot":"","sources":["../src/acceptance.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/acceptance.js
CHANGED
|
@@ -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 {
|
|
6
|
+
import { scoped } from './models.js';
|
|
7
|
+
import { answered, settle } from './queue.js';
|
|
6
8
|
import { refuse } from './refusal.js';
|
|
7
|
-
import {
|
|
8
|
-
import { batchFile
|
|
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 (
|
|
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({
|
|
33
|
+
async function keepUpload({ socket, presence }, local, locator, identifier, upload) {
|
|
34
34
|
if (!socket.continuing())
|
|
35
|
-
throw new Error(
|
|
35
|
+
throw new Error();
|
|
36
36
|
await local.keepUpload(locator, identifier, upload);
|
|
37
|
-
if (
|
|
38
|
-
throw
|
|
37
|
+
if (presence.lost(locator))
|
|
38
|
+
throw new Error();
|
|
39
39
|
if (!socket.continuing())
|
|
40
|
-
throw new Error(
|
|
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
|
|
59
|
-
return
|
|
47
|
+
function restands(adoptions) {
|
|
48
|
+
return adoptions.flatMap((one) => (one === null || !('record' in one) ? [] : [one]));
|
|
60
49
|
}
|
|
61
|
-
function
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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(
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
await
|
|
115
|
-
|
|
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
|
|
92
|
+
return kept;
|
|
118
93
|
}
|
|
119
94
|
async function adoptedMove(runtime, room, partner, local, submitted, accepted) {
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const left = await
|
|
124
|
-
const
|
|
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
|
|
103
|
+
: await adoption(runtime, other, there, local, arriving, {
|
|
128
104
|
kind: 'accepted',
|
|
129
105
|
...accepted.destination,
|
|
130
106
|
});
|
|
131
|
-
|
|
132
|
-
return
|
|
107
|
+
kept = left !== unconfirmed;
|
|
108
|
+
return restands([left, arrived]);
|
|
133
109
|
});
|
|
134
110
|
submitLater(runtime, partner);
|
|
135
|
-
return
|
|
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,
|
|
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 ||
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
196
|
+
heard();
|
|
246
197
|
return 'done';
|
|
247
198
|
}
|
|
248
199
|
if (result.kind !== 'accepted' || !socket.continuing())
|
|
249
|
-
|
|
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,
|
|
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
|
|
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 :
|
|
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
|
|
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(
|
|
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(
|
|
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 =
|
|
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
|
+
}
|