@agentshouse/mdruntime 0.1.0-ce131cb435f8b → 0.1.0-ce41fec66d91b
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 +18 -15
- package/dist/adapter.d.ts +110 -72
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +5 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +38 -4
- package/dist/binary.d.ts +0 -5
- package/dist/binary.d.ts.map +1 -1
- package/dist/binary.js +1 -1
- package/dist/bootstrap.d.ts +25 -10
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +180 -103
- package/dist/chain.d.ts +11 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +64 -0
- package/dist/change-set.d.ts +9 -16
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +11 -89
- package/dist/change.d.ts +31 -1
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +10 -1
- package/dist/client.d.ts +11 -8
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +80 -7
- package/dist/collection.d.ts +10 -22
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +39 -356
- package/dist/compile.d.ts +16 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +786 -0
- package/dist/connection.d.ts +17 -11
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +2779 -1329
- package/dist/conversation.d.ts +19 -11
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +298 -166
- package/dist/digest.d.ts +7 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +113 -0
- package/dist/durability.d.ts +2 -5
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +5 -0
- package/dist/entry.d.ts +4 -1
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +18 -24
- package/dist/file.d.ts +2 -19
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +7 -122
- package/dist/fulltext.d.ts +27 -0
- package/dist/fulltext.d.ts.map +1 -0
- package/dist/fulltext.js +370 -0
- package/dist/host.d.ts +3 -5
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +48 -21
- package/dist/identity.d.ts +0 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -3
- package/dist/index.d.ts +14 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/live.d.ts +22 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +123 -0
- package/dist/media.d.ts +8 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/media.js +218 -0
- package/dist/memory.d.ts +10 -6
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +195 -102
- package/dist/messages.d.ts +28 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +90 -0
- package/dist/model.d.ts +6 -1
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +7 -10
- package/dist/name.d.ts +4 -0
- package/dist/name.d.ts.map +1 -0
- package/dist/name.js +122 -0
- package/dist/observation.d.ts +13 -0
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +84 -0
- package/dist/opened.d.ts +22 -0
- package/dist/opened.d.ts.map +1 -0
- package/dist/opened.js +107 -0
- package/dist/outcome.d.ts +40 -8
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +38 -2
- package/dist/overlay.d.ts +2 -6
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +87 -176
- package/dist/paths.d.ts +1 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +4 -0
- package/dist/query.d.ts +11 -1
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +13 -1
- package/dist/reach.d.ts +13 -4
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +200 -78
- package/dist/readiness.d.ts +14 -15
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +2 -8
- package/dist/recovery.d.ts +3 -2
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +34 -7
- package/dist/refresh.d.ts +18 -15
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +136 -58
- package/dist/replica.d.ts +55 -38
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +278 -198
- package/dist/room-draft.d.ts +21 -3
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +102 -28
- package/dist/rows.d.ts +38 -31
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +173 -101
- package/dist/save.d.ts +52 -15
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +504 -182
- package/dist/search.d.ts +11 -43
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +0 -213
- package/dist/shared.d.ts +4 -3
- package/dist/shared.d.ts.map +1 -1
- package/dist/shared.js +62 -35
- package/dist/status.d.ts +4 -3
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +3 -1
- package/dist/store.d.ts +81 -101
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +300 -693
- package/dist/stored.d.ts +130 -0
- package/dist/stored.d.ts.map +1 -0
- package/dist/stored.js +560 -0
- package/dist/tables.d.ts +325 -0
- package/dist/tables.d.ts.map +1 -0
- package/dist/tables.js +578 -0
- package/dist/transfer.d.ts +0 -5
- package/dist/transfer.d.ts.map +1 -1
- package/dist/turn.d.ts +2 -2
- package/dist/turn.d.ts.map +1 -1
- package/dist/turn.js +34 -23
- package/dist/validation.d.ts +29 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +101 -0
- package/dist/windows.d.ts +2 -6
- package/dist/windows.d.ts.map +1 -1
- package/dist/windows.js +30 -34
- package/dist/wire.d.ts +17 -5
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +5 -2
- package/dist/words.d.ts +19 -0
- package/dist/words.d.ts.map +1 -0
- package/dist/words.js +156 -0
- package/package.json +18 -19
- package/AGENTS.md +0 -299
- package/dist/chat/acts.d.ts +0 -33
- package/dist/chat/acts.d.ts.map +0 -1
- package/dist/chat/acts.js +0 -36
- package/dist/chat/context.d.ts +0 -3
- package/dist/chat/context.d.ts.map +0 -1
- package/dist/chat/context.js +0 -10
- package/dist/chat/earlier.d.ts +0 -10
- package/dist/chat/earlier.d.ts.map +0 -1
- package/dist/chat/earlier.js +0 -54
- package/dist/chat/input.d.ts +0 -9
- package/dist/chat/input.d.ts.map +0 -1
- package/dist/chat/input.js +0 -99
- package/dist/chat/kit.d.ts +0 -61
- package/dist/chat/kit.d.ts.map +0 -1
- package/dist/chat/kit.js +0 -123
- package/dist/chat/lasting.d.ts +0 -6
- package/dist/chat/lasting.d.ts.map +0 -1
- package/dist/chat/lasting.js +0 -19
- package/dist/chat/list.d.ts +0 -27
- package/dist/chat/list.d.ts.map +0 -1
- package/dist/chat/list.js +0 -99
- package/dist/chat/markdown.d.ts +0 -20
- package/dist/chat/markdown.d.ts.map +0 -1
- package/dist/chat/markdown.js +0 -188
- package/dist/chat/message.d.ts +0 -6
- package/dist/chat/message.d.ts.map +0 -1
- package/dist/chat/message.js +0 -192
- package/dist/chat/messages.d.ts +0 -22
- package/dist/chat/messages.d.ts.map +0 -1
- package/dist/chat/messages.js +0 -155
- package/dist/chat/name.d.ts +0 -6
- package/dist/chat/name.d.ts.map +0 -1
- package/dist/chat/name.js +0 -37
- package/dist/chat/opened.d.ts +0 -11
- package/dist/chat/opened.d.ts.map +0 -1
- package/dist/chat/opened.js +0 -17
- package/dist/chat/overlay.d.ts +0 -31
- package/dist/chat/overlay.d.ts.map +0 -1
- package/dist/chat/overlay.js +0 -97
- package/dist/chat/parts.d.ts +0 -57
- package/dist/chat/parts.d.ts.map +0 -1
- package/dist/chat/parts.js +0 -119
- package/dist/chat/process.d.ts +0 -20
- package/dist/chat/process.d.ts.map +0 -1
- package/dist/chat/process.js +0 -32
- package/dist/chat/provider.d.ts +0 -63
- package/dist/chat/provider.d.ts.map +0 -1
- package/dist/chat/provider.js +0 -628
- package/dist/chat/question.d.ts +0 -9
- package/dist/chat/question.d.ts.map +0 -1
- package/dist/chat/question.js +0 -69
- package/dist/chat/questions.d.ts +0 -31
- package/dist/chat/questions.d.ts.map +0 -1
- package/dist/chat/questions.js +0 -111
- package/dist/chat/read.d.ts +0 -5
- package/dist/chat/read.d.ts.map +0 -1
- package/dist/chat/read.js +0 -13
- package/dist/chat/ready.d.ts +0 -34
- package/dist/chat/ready.d.ts.map +0 -1
- package/dist/chat/ready.js +0 -91
- package/dist/chat/style.d.ts +0 -4
- package/dist/chat/style.d.ts.map +0 -1
- package/dist/chat/style.js +0 -302
- package/dist/chat.d.ts +0 -14
- package/dist/chat.d.ts.map +0 -1
- package/dist/chat.js +0 -8
- package/dist/read-side.d.ts +0 -27
- package/dist/read-side.d.ts.map +0 -1
- package/dist/read-side.js +0 -824
- package/dist/reads.d.ts +0 -11
- package/dist/reads.d.ts.map +0 -1
- package/dist/reads.js +0 -98
package/dist/reach.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { unheldView } from './conversation.js';
|
|
2
2
|
import { modelTypes, NO_MODEL } from './model.js';
|
|
3
|
-
import {
|
|
3
|
+
import { changeOf, digestApplied, digestOf, observing, } from './observation.js';
|
|
4
4
|
import { runtimeError } from './failure.js';
|
|
5
|
-
import { failure, undeclared, } from './outcome.js';
|
|
5
|
+
import { failure, lockedRoom, ROOM_LOCKED, undeclared, UNREADABLE, } from './outcome.js';
|
|
6
6
|
import { beneath } from './paths.js';
|
|
7
|
-
import { reconciled } from './read-side.js';
|
|
8
7
|
import { moveTarget, readParts, staleWrite, } from './room-draft.js';
|
|
9
8
|
import { documentKey } from './rows.js';
|
|
10
9
|
import { undeclaredSearch } from './search.js';
|
|
@@ -15,19 +14,26 @@ function unconversed(name) {
|
|
|
15
14
|
const NO_CONVERSATION = {
|
|
16
15
|
list: () => Promise.resolve([]),
|
|
17
16
|
read: (conversation) => Promise.resolve(unheldView(conversation)),
|
|
18
|
-
create: unconversed('
|
|
19
|
-
send: unconversed('
|
|
20
|
-
answer: unconversed('
|
|
21
|
-
interrupt: unconversed('
|
|
22
|
-
kill: unconversed('
|
|
23
|
-
|
|
24
|
-
delete: unconversed('conversation.delete'),
|
|
17
|
+
create: unconversed('conversations/create'),
|
|
18
|
+
send: unconversed('conversations/send'),
|
|
19
|
+
answer: unconversed('conversations/interaction/answer'),
|
|
20
|
+
interrupt: unconversed('conversations/interrupt'),
|
|
21
|
+
kill: unconversed('conversations/kill'),
|
|
22
|
+
delete: unconversed('conversations/delete'),
|
|
25
23
|
};
|
|
26
24
|
const NO_CHAT_DRAFT = {
|
|
27
25
|
write: () => Promise.resolve(),
|
|
28
26
|
prepend: () => Promise.resolve(),
|
|
29
27
|
discard: () => Promise.resolve(),
|
|
30
28
|
};
|
|
29
|
+
function areasOf(reach) {
|
|
30
|
+
return reach.areas ?? [];
|
|
31
|
+
}
|
|
32
|
+
function fenced(reach, room, path) {
|
|
33
|
+
return areasOf(reach).some((area) => area.room === room &&
|
|
34
|
+
beneath(area.path, path) &&
|
|
35
|
+
!area.open.some((open) => beneath(open, path)));
|
|
36
|
+
}
|
|
31
37
|
export function holds(reach, room) {
|
|
32
38
|
return (reach === null ||
|
|
33
39
|
reach.rooms.includes(room) ||
|
|
@@ -48,26 +54,13 @@ export function governs(reach, room) {
|
|
|
48
54
|
}
|
|
49
55
|
function covers(reach, room, path) {
|
|
50
56
|
return (reach === null ||
|
|
51
|
-
reach
|
|
52
|
-
|
|
57
|
+
(!fenced(reach, room, path) &&
|
|
58
|
+
(reach.rooms.includes(room) ||
|
|
59
|
+
reach.documents.some((document) => document.room === room && document.path === path))));
|
|
53
60
|
}
|
|
54
61
|
function conversing(reach) {
|
|
55
62
|
return reach === null || reach.conversations;
|
|
56
63
|
}
|
|
57
|
-
function refers(reach, scope) {
|
|
58
|
-
if (reach === null)
|
|
59
|
-
return true;
|
|
60
|
-
if (scope === null)
|
|
61
|
-
return false;
|
|
62
|
-
if (scope.kind === 'conversations')
|
|
63
|
-
return reach.conversations;
|
|
64
|
-
return scope.path === undefined
|
|
65
|
-
? wholly(reach, scope.locator)
|
|
66
|
-
: covers(reach, scope.locator, scope.path);
|
|
67
|
-
}
|
|
68
|
-
function unreachedReference(fileRef) {
|
|
69
|
-
return { kind: 'absent', code: 'binary-file-absent', fields: { fileRef }, retryable: false };
|
|
70
|
-
}
|
|
71
64
|
export function planReach(reach) {
|
|
72
65
|
return reach === null
|
|
73
66
|
? null
|
|
@@ -76,6 +69,9 @@ export function planReach(reach) {
|
|
|
76
69
|
documents: [
|
|
77
70
|
...new Set(reach.documents.map(({ room, path }) => documentKey(room, path))),
|
|
78
71
|
].sort(),
|
|
72
|
+
areas: areasOf(reach)
|
|
73
|
+
.map(({ room, path, open }) => ({ room, path, open: [...new Set(open)].sort() }))
|
|
74
|
+
.sort((left, right) => documentKey(left.room, left.path) < documentKey(right.room, right.path) ? -1 : 1),
|
|
79
75
|
};
|
|
80
76
|
}
|
|
81
77
|
function roomsWithin(reach, rooms) {
|
|
@@ -91,7 +87,7 @@ function typesWithin(reach, room, types) {
|
|
|
91
87
|
};
|
|
92
88
|
}
|
|
93
89
|
function moves(reach, room, move) {
|
|
94
|
-
return move === undefined || covers(reach, room, moveTarget(move));
|
|
90
|
+
return move === undefined || covers(reach, move.room ?? room, moveTarget(move));
|
|
95
91
|
}
|
|
96
92
|
function draftWithin(reach, draft) {
|
|
97
93
|
return covers(reach, draft.room, draft.path) && moves(reach, draft.room, draft.parts.move);
|
|
@@ -124,33 +120,66 @@ function savedWithin(reach, room, result) {
|
|
|
124
120
|
function statusWithin(reach, status) {
|
|
125
121
|
return {
|
|
126
122
|
...status,
|
|
127
|
-
attentionRequired: status.attentionRequired.filter((entry) =>
|
|
123
|
+
attentionRequired: status.attentionRequired.filter((entry) => covers(reach, entry.room, entry.path)),
|
|
124
|
+
unsent: status.unsent.filter((change) => covers(reach, change.room, change.path)),
|
|
128
125
|
};
|
|
129
126
|
}
|
|
130
127
|
function queueWithin(reach, room, entries) {
|
|
131
|
-
return entries
|
|
132
|
-
(
|
|
128
|
+
return entries
|
|
129
|
+
.filter(({ batch }) => batch.changes.every((change) => covers(reach, room, change.path) &&
|
|
130
|
+
(change.kind !== 'move' || covers(reach, room, change.to))))
|
|
131
|
+
.map((entry) => {
|
|
132
|
+
const { between, ...batch } = entry.batch;
|
|
133
|
+
if (between === undefined)
|
|
134
|
+
return entry;
|
|
135
|
+
return wholly(reach, between.source.room) && wholly(reach, between.destination.room)
|
|
136
|
+
? entry
|
|
137
|
+
: { ...entry, batch };
|
|
138
|
+
});
|
|
133
139
|
}
|
|
134
140
|
function unreached(room) {
|
|
135
|
-
return runtimeError('room
|
|
141
|
+
return runtimeError('unknown-room', `Unknown Room ${room}`);
|
|
142
|
+
}
|
|
143
|
+
function lockedError(room) {
|
|
144
|
+
return runtimeError(ROOM_LOCKED, `${room} is locked until House confirms its access`, true);
|
|
145
|
+
}
|
|
146
|
+
function deviceUnreached() {
|
|
147
|
+
return runtimeError('device-unreached', 'Only the panel cleans up this device');
|
|
148
|
+
}
|
|
149
|
+
const ATTACH_REQUIRED = 'attach-required';
|
|
150
|
+
function attachRequired() {
|
|
151
|
+
return runtimeError(ATTACH_REQUIRED, 'App code takes a file in with binaryFile.attach');
|
|
152
|
+
}
|
|
153
|
+
function carriesFile(parts) {
|
|
154
|
+
return parts !== null && typeof parts === 'object' && Object.hasOwn(parts, 'file');
|
|
136
155
|
}
|
|
137
156
|
export function narrowed(runtime, given, told) {
|
|
138
157
|
const { md, watch } = runtime;
|
|
139
158
|
let reach = given;
|
|
140
159
|
let port = told;
|
|
141
160
|
const open = new Set();
|
|
142
|
-
function following(begin) {
|
|
161
|
+
function following(begin, locking = false) {
|
|
143
162
|
let stop = begin();
|
|
144
163
|
const restart = () => {
|
|
145
164
|
stop();
|
|
146
165
|
stop = begin();
|
|
147
166
|
};
|
|
148
167
|
open.add(restart);
|
|
168
|
+
const unwatch = locking ? runtime.watchLocks(restart) : () => { };
|
|
149
169
|
return () => {
|
|
150
170
|
open.delete(restart);
|
|
171
|
+
unwatch();
|
|
151
172
|
stop();
|
|
152
173
|
};
|
|
153
174
|
}
|
|
175
|
+
const unlocking = (rooms) => {
|
|
176
|
+
const locked = (rooms ?? []).filter((room) => runtime.locked(room));
|
|
177
|
+
return locked.length === 0
|
|
178
|
+
? null
|
|
179
|
+
: { pending: locked.map((room) => ({ room, awaits: 'unlock' })) };
|
|
180
|
+
};
|
|
181
|
+
const unlocked = (entries) => entries.filter((entry) => !runtime.locked(entry.room));
|
|
182
|
+
const intoLocked = (held) => held.some((move) => move?.room !== undefined && runtime.locked(move.room));
|
|
154
183
|
const within = (topic, input, deliver) => {
|
|
155
184
|
if (topic === 'rooms') {
|
|
156
185
|
return following(() => watch(topic, input, (rooms) => deliver(roomsWithin(reach, rooms))));
|
|
@@ -159,45 +188,60 @@ export function narrowed(runtime, given, told) {
|
|
|
159
188
|
return following(() => runtime.watchReadiness(port, reach, deliver));
|
|
160
189
|
}
|
|
161
190
|
if (topic === 'sync-status') {
|
|
162
|
-
return following(() => watch(topic, input, (status) =>
|
|
191
|
+
return following(() => watch(topic, input, (status) => {
|
|
192
|
+
const within = statusWithin(reach, status);
|
|
193
|
+
deliver({ ...within, attentionRequired: unlocked(within.attentionRequired) });
|
|
194
|
+
}), true);
|
|
163
195
|
}
|
|
164
196
|
if (topic === 'room-drafts') {
|
|
165
|
-
return following(() => watch(topic, input, (drafts) => deliver(draftsWithin(reach, drafts))));
|
|
197
|
+
return following(() => watch(topic, input, (drafts) => deliver(unlocked(draftsWithin(reach, drafts)))), true);
|
|
166
198
|
}
|
|
167
199
|
if (topic === 'queue') {
|
|
168
200
|
const room = input[0];
|
|
169
201
|
return following(() => {
|
|
170
|
-
if (!holds(reach, room)) {
|
|
202
|
+
if (!holds(reach, room) || runtime.locked(room)) {
|
|
171
203
|
deliver([]);
|
|
172
204
|
return () => { };
|
|
173
205
|
}
|
|
174
206
|
return watch(topic, input, (entries) => deliver(queueWithin(reach, room, entries)));
|
|
175
|
-
});
|
|
207
|
+
}, true);
|
|
176
208
|
}
|
|
177
209
|
if (topic === 'query') {
|
|
178
210
|
const plan = runtime.plan(input[0], reach);
|
|
179
211
|
let held = null;
|
|
180
212
|
return following(() => {
|
|
213
|
+
const locked = unlocking(plan.rooms);
|
|
214
|
+
if (locked !== null) {
|
|
215
|
+
const answer = { result: locked, keys: [] };
|
|
216
|
+
if (held === null || changeOf(held, answer) !== null)
|
|
217
|
+
deliver(answer);
|
|
218
|
+
held = digestOf(answer);
|
|
219
|
+
return () => { };
|
|
220
|
+
}
|
|
181
221
|
let first = true;
|
|
182
|
-
|
|
222
|
+
let open = true;
|
|
223
|
+
const heard = (message) => {
|
|
183
224
|
if (!first) {
|
|
184
|
-
held =
|
|
225
|
+
held = digestApplied(held, message);
|
|
185
226
|
deliver(message);
|
|
186
227
|
return;
|
|
187
228
|
}
|
|
188
229
|
first = false;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
deliver(next.message);
|
|
230
|
+
const change = held === null ? message : changeOf(held, message);
|
|
231
|
+
held = digestOf(message);
|
|
232
|
+
if (change !== null)
|
|
233
|
+
deliver(change);
|
|
234
|
+
};
|
|
235
|
+
const current = { ...plan, reach: planReach(reach), locked: runtime.lockedRooms() };
|
|
236
|
+
const stop = runtime.watchPlan(current, (message) => {
|
|
237
|
+
if (open)
|
|
238
|
+
heard(message);
|
|
199
239
|
});
|
|
200
|
-
|
|
240
|
+
return () => {
|
|
241
|
+
open = false;
|
|
242
|
+
stop();
|
|
243
|
+
};
|
|
244
|
+
}, true);
|
|
201
245
|
}
|
|
202
246
|
if (topic === 'conversation') {
|
|
203
247
|
return following(() => {
|
|
@@ -217,70 +261,149 @@ export function narrowed(runtime, given, told) {
|
|
|
217
261
|
}
|
|
218
262
|
return watch(topic, input, deliver);
|
|
219
263
|
};
|
|
264
|
+
const files = new Map();
|
|
265
|
+
let handed = 0;
|
|
266
|
+
async function hand(reading) {
|
|
267
|
+
const answer = await reading;
|
|
268
|
+
if (answer.kind !== 'binary')
|
|
269
|
+
return answer;
|
|
270
|
+
handed += 1;
|
|
271
|
+
const file = String(handed);
|
|
272
|
+
files.set(file, answer.file);
|
|
273
|
+
return { ...answer, file };
|
|
274
|
+
}
|
|
220
275
|
const conversation = () => (conversing(reach) ? md.conversation : NO_CONVERSATION);
|
|
221
276
|
const chatDraft = () => (conversing(reach) ? md.chatDraft : NO_CHAT_DRAFT);
|
|
222
277
|
const view = {
|
|
223
278
|
...md,
|
|
224
|
-
rooms: () => roomsWithin(reach,
|
|
225
|
-
query: ((input) =>
|
|
279
|
+
rooms: () => md.rooms().then((rooms) => roomsWithin(reach, rooms)),
|
|
280
|
+
query: ((input) => {
|
|
281
|
+
const plan = runtime.plan(input, reach);
|
|
282
|
+
return Promise.resolve(unlocking(plan.rooms) ?? runtime.query({ ...plan, locked: runtime.lockedRooms() }));
|
|
283
|
+
}),
|
|
226
284
|
search: (input) => port.search === 'required'
|
|
227
|
-
? runtime.search(input, reach)
|
|
285
|
+
? runtime.search(input, reach, runtime.lockedRooms())
|
|
228
286
|
: Promise.resolve(undeclaredSearch()),
|
|
229
287
|
types(room) {
|
|
288
|
+
if (holds(reach, room) && runtime.locked(room))
|
|
289
|
+
throw lockedError(room);
|
|
230
290
|
if (wholly(reach, room))
|
|
231
291
|
return md.types(room);
|
|
232
292
|
if (!holds(reach, room))
|
|
233
293
|
return structuredClone(UNHELD_TYPES);
|
|
234
|
-
|
|
235
|
-
return typesWithin(reach, listed, md.types(room));
|
|
294
|
+
return typesWithin(reach, runtime.room(room), md.types(room));
|
|
236
295
|
},
|
|
237
296
|
async save(room, roomDrafts, writtenBy) {
|
|
238
297
|
if (!holds(reach, room))
|
|
239
298
|
throw unreached(room);
|
|
240
|
-
|
|
241
|
-
|
|
299
|
+
if (runtime.locked(room))
|
|
300
|
+
return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
|
|
301
|
+
const held = (await Promise.all(roomDrafts.map(({ path }) => md.roomDraft.read(room, path)))).map((draft) => draft?.parts.move);
|
|
302
|
+
const outside = roomDrafts.find(({ path }, at) => !covers(reach, room, path) || !moves(reach, room, held[at]));
|
|
242
303
|
if (outside !== undefined) {
|
|
243
304
|
return { outcome: 'refused', code: 'stale', retryable: false, file: outside.path };
|
|
244
305
|
}
|
|
306
|
+
if (intoLocked(held))
|
|
307
|
+
return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
|
|
245
308
|
return savedWithin(reach, room, await md.save(room, roomDrafts, writtenBy));
|
|
246
309
|
},
|
|
247
310
|
async saveParts(room, parts, writtenBy) {
|
|
248
311
|
if (!holds(reach, room))
|
|
249
312
|
throw unreached(room);
|
|
250
|
-
|
|
313
|
+
const filed = reach === null ? undefined : parts.find(({ parts: one }) => carriesFile(one));
|
|
314
|
+
if (filed !== undefined) {
|
|
315
|
+
return { outcome: 'refused', code: ATTACH_REQUIRED, retryable: false, file: filed.path };
|
|
316
|
+
}
|
|
317
|
+
if (runtime.locked(room))
|
|
318
|
+
return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
|
|
319
|
+
const held = parts.map(({ parts: one }) => readParts(one).move);
|
|
320
|
+
if (parts.some(({ path }, at) => !covers(reach, room, path) || !moves(reach, room, held[at]))) {
|
|
251
321
|
throw unreached(room);
|
|
252
322
|
}
|
|
323
|
+
if (intoLocked(held))
|
|
324
|
+
return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
|
|
253
325
|
return savedWithin(reach, room, await md.saveParts(room, parts, writtenBy));
|
|
254
326
|
},
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
327
|
+
resolve(room, path, destination) {
|
|
328
|
+
const at = runtime.located(room, path, destination);
|
|
329
|
+
return at !== null && !covers(reach, at.room, at.path)
|
|
330
|
+
? Promise.resolve(UNREADABLE)
|
|
331
|
+
: md.resolve(room, path, destination);
|
|
332
|
+
},
|
|
333
|
+
history: (room, path, query) => !covers(reach, room, path)
|
|
334
|
+
? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
|
|
335
|
+
: runtime.locked(room)
|
|
336
|
+
? Promise.resolve(lockedRoom(room))
|
|
337
|
+
: md.history(room, path, query),
|
|
258
338
|
binaryFile: {
|
|
259
|
-
read: (room, path, revision) => covers(reach, room, path)
|
|
260
|
-
?
|
|
261
|
-
:
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
339
|
+
read: (room, path, revision) => !covers(reach, room, path)
|
|
340
|
+
? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
|
|
341
|
+
: runtime.locked(room)
|
|
342
|
+
? Promise.resolve(lockedRoom(room))
|
|
343
|
+
: hand(md.binaryFile.read(room, path, revision)),
|
|
344
|
+
link: (room, path, revision) => !covers(reach, room, path)
|
|
345
|
+
? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
|
|
346
|
+
: runtime.locked(room)
|
|
347
|
+
? Promise.resolve(lockedRoom(room))
|
|
348
|
+
: md.binaryFile.link(room, path, revision),
|
|
349
|
+
show: (room, path, revision) => !covers(reach, room, path)
|
|
350
|
+
? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
|
|
351
|
+
: runtime.locked(room)
|
|
352
|
+
? Promise.resolve(lockedRoom(room))
|
|
353
|
+
: hand(md.binaryFile.show(room, path, revision)),
|
|
354
|
+
attach: (room, file) => !wholly(reach, room)
|
|
355
|
+
? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
|
|
356
|
+
: runtime.locked(room)
|
|
357
|
+
? Promise.resolve(lockedRoom(room))
|
|
358
|
+
: md.binaryFile.attach(room, file),
|
|
359
|
+
async part(file, part) {
|
|
360
|
+
const key = files.get(file);
|
|
361
|
+
if (key === undefined)
|
|
362
|
+
return null;
|
|
363
|
+
const bytes = await md.binaryFile.part(key, part);
|
|
364
|
+
if (bytes === null)
|
|
365
|
+
files.delete(file);
|
|
366
|
+
return bytes;
|
|
367
|
+
},
|
|
268
368
|
},
|
|
269
|
-
reached: (room, roomVersion, path) => (path === undefined ? wholly(reach, room) : covers(reach, room, path)) &&
|
|
270
|
-
|
|
369
|
+
reached: async (room, roomVersion, path) => (path === undefined ? wholly(reach, room) : covers(reach, room, path)) &&
|
|
370
|
+
!runtime.locked(room) &&
|
|
371
|
+
(await md.reached(room, roomVersion, path)),
|
|
271
372
|
log: () => (reach === null ? md.log() : ''),
|
|
373
|
+
cleanUp: () => (reach === null ? md.cleanUp() : Promise.reject(deviceUnreached())),
|
|
272
374
|
roomDraft: {
|
|
273
|
-
list: () => md.roomDraft.list().then((drafts) => draftsWithin(reach, drafts)),
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
375
|
+
list: () => md.roomDraft.list().then((drafts) => unlocked(draftsWithin(reach, drafts))),
|
|
376
|
+
async read(room, path) {
|
|
377
|
+
if (!covers(reach, room, path))
|
|
378
|
+
return null;
|
|
379
|
+
if (runtime.locked(room))
|
|
380
|
+
throw lockedError(room);
|
|
381
|
+
return shownDraft(reach, await md.roomDraft.read(room, path));
|
|
382
|
+
},
|
|
383
|
+
write(room, path, revision, parts, next) {
|
|
384
|
+
try {
|
|
385
|
+
if (reach !== null && carriesFile(parts))
|
|
386
|
+
throw attachRequired();
|
|
387
|
+
const move = readParts(parts).move;
|
|
388
|
+
if (!covers(reach, room, path) || !moves(reach, room, move))
|
|
389
|
+
throw unreached(room);
|
|
390
|
+
if (runtime.locked(room))
|
|
391
|
+
throw lockedError(room);
|
|
392
|
+
if (intoLocked([move]))
|
|
393
|
+
throw lockedError(move.room);
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
return Promise.reject(error);
|
|
278
397
|
}
|
|
279
|
-
return
|
|
398
|
+
return md.roomDraft
|
|
399
|
+
.write(room, path, revision, parts, next)
|
|
400
|
+
.then((written) => writeWithin(reach, written));
|
|
280
401
|
},
|
|
281
402
|
async discard(room, path, revision) {
|
|
282
403
|
if (!covers(reach, room, path))
|
|
283
404
|
return staleWrite(null);
|
|
405
|
+
if (runtime.locked(room))
|
|
406
|
+
throw lockedError(room);
|
|
284
407
|
return writeWithin(reach, await md.roomDraft.discard(room, path, revision));
|
|
285
408
|
},
|
|
286
409
|
},
|
|
@@ -292,7 +415,6 @@ export function narrowed(runtime, given, told) {
|
|
|
292
415
|
answer: (id, answer) => conversation().answer(id, answer),
|
|
293
416
|
interrupt: (id) => conversation().interrupt(id),
|
|
294
417
|
kill: (id) => conversation().kill(id),
|
|
295
|
-
cancel: (id, turn) => conversation().cancel(id, turn),
|
|
296
418
|
delete: (id) => conversation().delete(id),
|
|
297
419
|
},
|
|
298
420
|
chatDraft: {
|
package/dist/readiness.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { Open } from './open.js';
|
|
2
2
|
export type CodeAvailability = Open<'stored' | 'absent'>;
|
|
3
|
-
export type RoomPreparation = Open<'complete' | 'acquiring' | 'preparing' | '
|
|
3
|
+
export type RoomPreparation = Open<'complete' | 'acquiring' | 'preparing' | 'unadmitted'>;
|
|
4
4
|
export type RoomCoverage = {
|
|
5
5
|
readonly room: string;
|
|
6
6
|
readonly state: RoomPreparation;
|
|
7
7
|
};
|
|
8
|
-
export type ConversationCoverage = Open<'complete' | 'acquiring' | '
|
|
8
|
+
export type ConversationCoverage = Open<'complete' | 'acquiring' | 'undeclared'>;
|
|
9
9
|
export type SearchObligation = Open<'required' | 'unused'>;
|
|
10
10
|
export type TargetCoverage = {
|
|
11
11
|
readonly complete: boolean;
|
|
@@ -18,18 +18,18 @@ export type Persistence = Open<'granted' | 'denied' | 'unavailable'>;
|
|
|
18
18
|
export type Durability = Open<'persistent' | 'best-effort'>;
|
|
19
19
|
export type ConnectionAvailability = Open<'connected' | 'connecting' | 'unreachable'>;
|
|
20
20
|
export type HouseOutcome = Open<'open' | 'write-halted' | 'refused'>;
|
|
21
|
-
export type
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
readonly
|
|
21
|
+
export type Space = Open<'full'>;
|
|
22
|
+
export type InstallationStep = Open<'downloading' | 'writing' | 'replaying'>;
|
|
23
|
+
export type InstallationAmount = {
|
|
24
|
+
readonly done: number;
|
|
25
|
+
readonly total: number;
|
|
25
26
|
};
|
|
26
|
-
export type
|
|
27
|
-
readonly
|
|
28
|
-
readonly
|
|
29
|
-
readonly
|
|
30
|
-
readonly location: string | null;
|
|
27
|
+
export type Installation = {
|
|
28
|
+
readonly step: InstallationStep | null;
|
|
29
|
+
readonly rooms: InstallationAmount;
|
|
30
|
+
readonly bytes: InstallationAmount;
|
|
31
31
|
};
|
|
32
|
-
export type ReadinessStatus = Open<'preparing' | 'paused' | '
|
|
32
|
+
export type ReadinessStatus = Open<'preparing' | 'paused' | 'ready' | 'lost'>;
|
|
33
33
|
export type Readiness = {
|
|
34
34
|
readonly status: ReadinessStatus;
|
|
35
35
|
readonly ready: boolean;
|
|
@@ -40,14 +40,13 @@ export type Readiness = {
|
|
|
40
40
|
readonly persistence: Persistence;
|
|
41
41
|
readonly connection: ConnectionAvailability;
|
|
42
42
|
readonly house: HouseOutcome;
|
|
43
|
-
readonly
|
|
44
|
-
readonly
|
|
43
|
+
readonly space: Space | null;
|
|
44
|
+
readonly installation: Installation;
|
|
45
45
|
readonly lost: boolean;
|
|
46
46
|
};
|
|
47
47
|
export type ReadinessFacts = Omit<Readiness, 'status' | 'ready' | 'usable' | 'durability' | 'coverage'> & {
|
|
48
48
|
readonly coverage: Omit<TargetCoverage, 'complete'>;
|
|
49
49
|
readonly established: boolean;
|
|
50
50
|
};
|
|
51
|
-
export declare const SETTLED_ROOMS: ReadonlySet<string>;
|
|
52
51
|
export declare function readinessOf(facts: ReadinessFacts): Readiness;
|
|
53
52
|
//# sourceMappingURL=readiness.d.ts.map
|
package/dist/readiness.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,MAAM,gBAAgB,GAAG,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAEzD,MAAM,MAAM,eAAe,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"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"}
|
package/dist/readiness.js
CHANGED
|
@@ -1,27 +1,21 @@
|
|
|
1
1
|
const COMPLETE_ROOMS = new Set(['complete', 'unadmitted']);
|
|
2
|
-
export const SETTLED_ROOMS = new Set([...COMPLETE_ROOMS, 'refused']);
|
|
3
2
|
export function readinessOf(facts) {
|
|
4
3
|
const { established, coverage: held, ...reported } = facts;
|
|
5
4
|
const settledConversations = held.conversations === 'complete' || held.conversations === 'undeclared';
|
|
6
5
|
const complete = held.listed &&
|
|
7
6
|
settledConversations &&
|
|
8
7
|
held.rooms.every((room) => COMPLETE_ROOMS.has(room.state));
|
|
9
|
-
const settled = held.listed &&
|
|
10
|
-
settledConversations &&
|
|
11
|
-
held.rooms.every((room) => SETTLED_ROOMS.has(room.state));
|
|
12
8
|
const coverage = { ...held, complete };
|
|
13
9
|
const stored = facts.code === 'stored';
|
|
14
10
|
const ready = stored && complete;
|
|
15
11
|
const usable = stored && (ready || established);
|
|
16
12
|
const status = ready
|
|
17
13
|
? 'ready'
|
|
18
|
-
: facts.
|
|
14
|
+
: facts.space !== null
|
|
19
15
|
? 'paused'
|
|
20
16
|
: facts.lost && !established
|
|
21
17
|
? 'lost'
|
|
22
|
-
:
|
|
23
|
-
? 'refused'
|
|
24
|
-
: 'preparing';
|
|
18
|
+
: 'preparing';
|
|
25
19
|
return {
|
|
26
20
|
...reported,
|
|
27
21
|
coverage,
|
package/dist/recovery.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export type AttemptOptions = {
|
|
|
8
8
|
};
|
|
9
9
|
export type RecoveryOptions = {
|
|
10
10
|
readonly firstMs?: number;
|
|
11
|
-
readonly
|
|
11
|
+
readonly maxMs?: number;
|
|
12
12
|
readonly attempts?: number;
|
|
13
13
|
readonly inFlight?: number;
|
|
14
14
|
};
|
|
@@ -20,6 +20,7 @@ export type Recovery = {
|
|
|
20
20
|
scope(inFlight?: number): Recovery;
|
|
21
21
|
retire(): Promise<void>;
|
|
22
22
|
};
|
|
23
|
-
export declare function retryDelay(tries: number, firstMs?: number,
|
|
23
|
+
export declare function retryDelay(tries: number, firstMs?: number, maxMs?: number): number;
|
|
24
|
+
export declare function untilAnswered<T>(ask: () => Promise<T>, signal: AbortSignal, answered?: (answer: T) => boolean): Promise<T>;
|
|
24
25
|
export declare function createRecovery(options?: RecoveryOptions): Recovery;
|
|
25
26
|
//# sourceMappingURL=recovery.d.ts.map
|
package/dist/recovery.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;AAElE,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEhF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;AAElE,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEhF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClF,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,MAAM,IAAI,IAAI,CAAC;IACf,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACnC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB,CAAC;AAsBF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,SAAW,EAAE,KAAK,SAAS,GAAG,MAAM,CAGpF;AAED,wBAAsB,aAAa,CAAC,CAAC,EACnC,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACrB,MAAM,EAAE,WAAW,EACnB,QAAQ,GAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAoB,GAC5C,OAAO,CAAC,CAAC,CAAC,CAsBZ;AAwKD,wBAAgB,cAAc,CAAC,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAStE"}
|
package/dist/recovery.js
CHANGED
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
// A first retry waits about a second, so a passing failure clears before the next attempt.
|
|
2
2
|
const FIRST_MS = 1_000;
|
|
3
3
|
// Waits stop doubling at half a minute, so work resumes soon after a long failure ends.
|
|
4
|
-
const
|
|
4
|
+
const MAX_MS = 30_000;
|
|
5
5
|
const ATTEMPTS = 8;
|
|
6
|
-
export function retryDelay(tries, firstMs = FIRST_MS,
|
|
7
|
-
const
|
|
8
|
-
return
|
|
6
|
+
export function retryDelay(tries, firstMs = FIRST_MS, maxMs = MAX_MS) {
|
|
7
|
+
const longest = Math.min(maxMs, firstMs * 2 ** tries);
|
|
8
|
+
return longest / 2 + (Math.random() * longest) / 2;
|
|
9
|
+
}
|
|
10
|
+
export async function untilAnswered(ask, signal, answered = () => true) {
|
|
11
|
+
for (let tries = 0;; tries += 1) {
|
|
12
|
+
try {
|
|
13
|
+
const answer = await ask();
|
|
14
|
+
if (answered(answer) || signal.aborted)
|
|
15
|
+
return answer;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (signal.aborted)
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
await new Promise((resume) => {
|
|
22
|
+
const stop = () => {
|
|
23
|
+
clearTimeout(again);
|
|
24
|
+
resume();
|
|
25
|
+
};
|
|
26
|
+
// A read that failed is asked again on the doubling backoff, so its asker is answered once the failure clears.
|
|
27
|
+
const again = setTimeout(() => {
|
|
28
|
+
signal.removeEventListener('abort', stop);
|
|
29
|
+
resume();
|
|
30
|
+
}, retryDelay(tries));
|
|
31
|
+
signal.addEventListener('abort', stop, { once: true });
|
|
32
|
+
});
|
|
33
|
+
signal.throwIfAborted();
|
|
34
|
+
}
|
|
9
35
|
}
|
|
10
36
|
function owner(backoff, inFlight, release = () => { }) {
|
|
11
37
|
const intents = new Map();
|
|
@@ -84,7 +110,8 @@ function owner(backoff, inFlight, release = () => { }) {
|
|
|
84
110
|
return;
|
|
85
111
|
}
|
|
86
112
|
if (result === 'again') {
|
|
87
|
-
|
|
113
|
+
// The next attempt waits the doubling backoff, up to its maximum.
|
|
114
|
+
const delay = retryDelay(intent.tries - 1, backoff.firstMs, backoff.maxMs);
|
|
88
115
|
wait(key, intent, Date.now() + delay, false);
|
|
89
116
|
}
|
|
90
117
|
else
|
|
@@ -135,7 +162,7 @@ function owner(backoff, inFlight, release = () => { }) {
|
|
|
135
162
|
wait(key, intent, options.at, true);
|
|
136
163
|
else if (options.again === true) {
|
|
137
164
|
intent.tries = 1;
|
|
138
|
-
wait(key, intent, Date.now() + retryDelay(0, backoff.firstMs, backoff.
|
|
165
|
+
wait(key, intent, Date.now() + retryDelay(0, backoff.firstMs, backoff.maxMs), false);
|
|
139
166
|
}
|
|
140
167
|
else
|
|
141
168
|
launch(key, intent);
|
|
@@ -191,7 +218,7 @@ function owner(backoff, inFlight, release = () => { }) {
|
|
|
191
218
|
export function createRecovery(options = {}) {
|
|
192
219
|
return owner({
|
|
193
220
|
firstMs: options.firstMs ?? FIRST_MS,
|
|
194
|
-
|
|
221
|
+
maxMs: options.maxMs ?? MAX_MS,
|
|
195
222
|
attempts: options.attempts ?? ATTEMPTS,
|
|
196
223
|
}, options.inFlight ?? Infinity);
|
|
197
224
|
}
|