@agentshouse/mdruntime 0.1.0-cd26b960b7262 → 0.1.0-cd30853a1b15e
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -19
- package/dist/acceptance.d.ts +0 -3
- package/dist/acceptance.d.ts.map +1 -1
- package/dist/acceptance.js +72 -158
- package/dist/act-lanes.d.ts +30 -0
- package/dist/act-lanes.d.ts.map +1 -0
- package/dist/act-lanes.js +225 -0
- package/dist/actions.d.ts +8 -2
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +118 -67
- package/dist/adapter.d.ts +81 -56
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +0 -1
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +0 -5
- package/dist/authored.d.ts +88 -0
- package/dist/authored.d.ts.map +1 -0
- package/dist/authored.js +345 -0
- package/dist/bootstrap.d.ts +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +6 -19
- 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 +4 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +3 -0
- package/dist/chat-types.d.ts +0 -2
- package/dist/chat-types.d.ts.map +1 -1
- package/dist/chat-types.js +0 -2
- package/dist/client.d.ts +10 -19
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +30 -18
- package/dist/collection.d.ts +25 -4
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +19 -2
- package/dist/column.d.ts +5 -0
- package/dist/column.d.ts.map +1 -0
- package/dist/column.js +9 -0
- package/dist/compile.d.ts +2 -0
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +17 -5
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +31 -24
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -1
- package/dist/conversation-live.d.ts +26 -12
- package/dist/conversation-live.d.ts.map +1 -1
- package/dist/conversation-live.js +23 -12
- package/dist/conversation-row.d.ts +8 -1
- package/dist/conversation-row.d.ts.map +1 -1
- package/dist/conversation-row.js +20 -7
- package/dist/conversation.d.ts +32 -10
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +317 -232
- 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 +19 -44
- package/dist/durability.d.ts +4 -0
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +4 -0
- package/dist/entry.js +1 -1
- package/dist/expectation.d.ts +25 -14
- package/dist/expectation.d.ts.map +1 -1
- package/dist/expectation.js +51 -20
- 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.map +1 -1
- package/dist/figures.js +4 -2
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +2 -1
- package/dist/host.d.ts +18 -2
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +55 -15
- package/dist/index.d.ts +15 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -7
- package/dist/install.d.ts +10 -6
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +92 -109
- package/dist/lifecycle.d.ts +1 -1
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +54 -40
- package/dist/listing.d.ts +11 -12
- package/dist/listing.d.ts.map +1 -1
- package/dist/listing.js +48 -72
- package/dist/live.d.ts +5 -3
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +74 -31
- package/dist/loaded.d.ts +5 -0
- package/dist/loaded.d.ts.map +1 -0
- package/dist/loaded.js +40 -0
- package/dist/locks.d.ts +2 -4
- package/dist/locks.d.ts.map +1 -1
- package/dist/locks.js +10 -39
- package/dist/lost.d.ts.map +1 -1
- package/dist/lost.js +11 -23
- package/dist/media.d.ts +5 -0
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +65 -14
- package/dist/memory.d.ts +7 -3
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +56 -26
- package/dist/messages.d.ts +12 -6
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +25 -27
- 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 -139
- package/dist/observation.d.ts +27 -12
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +173 -69
- package/dist/opened.d.ts +1 -1
- package/dist/opened.d.ts.map +1 -1
- package/dist/opened.js +2 -2
- package/dist/opening.d.ts.map +1 -1
- package/dist/opening.js +5 -9
- package/dist/outcome.d.ts +7 -0
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +23 -2
- package/dist/overlay.d.ts +6 -2
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +37 -24
- 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 -59
- package/dist/parts.d.ts.map +1 -1
- package/dist/parts.js +10 -123
- package/dist/preparation.d.ts +27 -6
- package/dist/preparation.d.ts.map +1 -1
- package/dist/preparation.js +154 -66
- package/dist/presence.d.ts +58 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +231 -0
- package/dist/queries.d.ts +2 -2
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +66 -30
- package/dist/questions.d.ts +1 -5
- package/dist/questions.d.ts.map +1 -1
- package/dist/questions.js +1 -7
- package/dist/queue.d.ts +40 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +480 -0
- package/dist/reach.d.ts +2 -3
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +83 -25
- package/dist/react.d.ts +8 -5
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +28 -5
- package/dist/readiness.d.ts +9 -3
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +3 -5
- package/dist/reads.d.ts +4 -2
- package/dist/reads.d.ts.map +1 -1
- package/dist/reads.js +11 -4
- package/dist/recovery.d.ts +3 -1
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +29 -7
- package/dist/refresh.js +2 -2
- package/dist/refusal.d.ts.map +1 -1
- package/dist/refusal.js +5 -5
- package/dist/replica.d.ts +5 -11
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +14 -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 +30 -5
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +270 -63
- package/dist/room-drafts.d.ts.map +1 -1
- package/dist/room-drafts.js +8 -5
- package/dist/room-list.d.ts +10 -0
- package/dist/room-list.d.ts.map +1 -0
- package/dist/room-list.js +45 -0
- package/dist/rows.d.ts +2 -1
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +9 -10
- package/dist/runtime.d.ts +9 -18
- package/dist/runtime.d.ts.map +1 -1
- package/dist/save.d.ts +42 -90
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +314 -223
- package/dist/saves.d.ts +2 -40
- package/dist/saves.d.ts.map +1 -1
- package/dist/saves.js +124 -703
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +4 -4
- package/dist/shown.d.ts +7 -5
- package/dist/shown.d.ts.map +1 -1
- package/dist/shown.js +75 -58
- package/dist/socket/lanes.d.ts +12 -4
- package/dist/socket/lanes.d.ts.map +1 -1
- package/dist/socket/lanes.js +34 -8
- package/dist/socket.d.ts +3 -6
- package/dist/socket.d.ts.map +1 -1
- package/dist/socket.js +3 -14
- package/dist/source.d.ts +5 -7
- package/dist/source.d.ts.map +1 -1
- package/dist/source.js +13 -2
- package/dist/store/installs.d.ts +2 -2
- package/dist/store/installs.d.ts.map +1 -1
- package/dist/store/installs.js +10 -4
- package/dist/store/readiness.d.ts +5 -4
- package/dist/store/readiness.d.ts.map +1 -1
- package/dist/store/readiness.js +28 -12
- package/dist/store/records.d.ts +17 -6
- package/dist/store/records.d.ts.map +1 -1
- package/dist/store/records.js +56 -22
- package/dist/store/rooms-shown.d.ts +8 -0
- package/dist/store/rooms-shown.d.ts.map +1 -0
- package/dist/store/rooms-shown.js +13 -0
- package/dist/store.d.ts +23 -14
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +46 -17
- package/dist/stored.d.ts +24 -13
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +80 -29
- package/dist/surface.d.ts +5 -3
- package/dist/surface.d.ts.map +1 -1
- package/dist/surface.js +23 -21
- package/dist/sync.d.ts +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +89 -85
- package/dist/tables.d.ts +16 -56
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +86 -327
- package/dist/tallies.d.ts.map +1 -1
- package/dist/tallies.js +1 -2
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +4 -10
- package/dist/wire.d.ts +45 -7
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +15 -4
- 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/log.d.ts +0 -6
- package/dist/log.d.ts.map +0 -1
- package/dist/log.js +0 -14
- package/dist/socket/rooms.d.ts +0 -44
- 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/dist/saves.js
CHANGED
|
@@ -1,403 +1,102 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isImageType, parse } from '@agentshouse/mdmodel';
|
|
2
2
|
import { binaryRecord } from './binary.js';
|
|
3
|
-
import { changeSet
|
|
4
|
-
import {
|
|
5
|
-
import { available,
|
|
3
|
+
import { changeSet } from './change-set.js';
|
|
4
|
+
import { queuedFile } from './change.js';
|
|
5
|
+
import { available, textType } from './collection.js';
|
|
6
6
|
import { runtimeError } from './failure.js';
|
|
7
7
|
import { attachedFile } from './file.js';
|
|
8
8
|
import { roomDraftRevision } from './identity.js';
|
|
9
|
-
import { confirmedAgain } from './install.js';
|
|
10
9
|
import { compositionFor } from './listing.js';
|
|
10
|
+
import { scoped } from './models.js';
|
|
11
11
|
import { absentBinary, attachAnswer, binaryAnswer, binaryLinkAnswer, failure, OFFLINE, UNCERTAIN, } from './outcome.js';
|
|
12
12
|
import { sameBinary } from './overlay.js';
|
|
13
13
|
import { ancestors } from './paths.js';
|
|
14
|
-
import { connectionNow
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
14
|
+
import { connectionNow } from './preparation.js';
|
|
15
|
+
import { append, sliceAt, withdrawable } from './queue.js';
|
|
16
|
+
import { entryAt, replaying, slicing, } from './replica.js';
|
|
17
|
+
import { binaryBase, changesOf, held, heldFile, joinedOver, moveTarget, readSavedParts, } from './room-draft.js';
|
|
17
18
|
import { refuse } from './refusal.js';
|
|
18
19
|
import { visibleOf } from './room-drafts.js';
|
|
19
|
-
import {
|
|
20
|
-
import { buildBatch, buildMove, joined, limiting, queuedDocuments, requeuing, } from './save.js';
|
|
20
|
+
import { destination, planSave, } from './save.js';
|
|
21
21
|
import { pendingShown } from './shown.js';
|
|
22
22
|
import { paced, ROW_WEIGHT, stepped } from './turn.js';
|
|
23
23
|
import { validatedNeighbourhood } from './validation.js';
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
export async function sliceAt(store, record, view, queue, extra = [], moved = []) {
|
|
31
|
-
const local = await store.open();
|
|
32
|
-
const needed = new Set([...touchedBy(queue), ...extra]);
|
|
33
|
-
const movedPaths = [...new Set([...movedBy(queue, record.locator), ...moved])];
|
|
34
|
-
if (movedPaths.length > 0) {
|
|
35
|
-
for (const { path } of await local.referrers(record.key, view, movedPaths)) {
|
|
36
|
-
needed.add(path);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
const paths = [...needed];
|
|
40
|
-
return stepped(slicing(await local.entries(record.key, view, paths), paths));
|
|
41
|
-
}
|
|
42
|
-
export async function landedWhole(store, locator, batch, confirmedLayer, current) {
|
|
43
|
-
if (!landed(batch, confirmedLayer, current))
|
|
44
|
-
return false;
|
|
45
|
-
const partner = partnerOf(batch, locator);
|
|
46
|
-
if (partner === null)
|
|
47
|
-
return true;
|
|
48
|
-
const other = await store.replica(partner);
|
|
49
|
-
const half = other?.queue.find((one) => one.id === batch.id);
|
|
50
|
-
if (other === undefined || half === undefined)
|
|
51
|
-
return false;
|
|
52
|
-
return landed(half, await sliceAt(store, other.record, 'confirmed', [half]));
|
|
53
|
-
}
|
|
54
|
-
async function carriedBack(store, locator, unlanded, written) {
|
|
55
|
-
const back = new Map();
|
|
56
|
-
const taken = [];
|
|
57
|
-
for (const batch of unlanded) {
|
|
58
|
-
const between = batch.between;
|
|
59
|
-
if (between === undefined)
|
|
60
|
-
continue;
|
|
61
|
-
const source = between.source.room;
|
|
62
|
-
const destination = between.destination.room;
|
|
63
|
-
const local = await store.open();
|
|
64
|
-
const arrived = (await local.pending(destination, batch.id)).state?.drafts ?? [];
|
|
65
|
-
for (const { path: to } of arrived) {
|
|
66
|
-
const record = await store.draft(destination, to);
|
|
67
|
-
const path = between.moves.find((move) => move.to === to)?.path;
|
|
68
|
-
if (record === undefined || path === undefined)
|
|
69
|
-
continue;
|
|
70
|
-
const [into, key] = source === locator ? [written, path] : [back, documentKey(source, path)];
|
|
71
|
-
const existing = into.get(key) ?? (await store.draft(source, path));
|
|
72
|
-
taken.push({ locator: destination, path: to });
|
|
73
|
-
into.set(key, {
|
|
74
|
-
locator: source,
|
|
75
|
-
path,
|
|
76
|
-
revision: roomDraftRevision(),
|
|
77
|
-
parts: returnedInto(existing?.parts, record.parts),
|
|
78
|
-
returned: existing?.returned ?? null,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
24
|
+
const FULL = { outcome: 'refused', code: 'durable-write', retryable: true };
|
|
25
|
+
async function savedRoom({ store, presence }, locator) {
|
|
26
|
+
const replica = await store.replica(locator);
|
|
27
|
+
if (replica === undefined || presence.lost(locator)) {
|
|
28
|
+
throw runtimeError('unknown-room', `Unknown Room ${locator}`);
|
|
81
29
|
}
|
|
82
|
-
|
|
83
|
-
const elsewhere = taken.filter((one) => one.locator !== locator);
|
|
84
|
-
const rooms = new Set([...records, ...elsewhere].map((one) => one.locator));
|
|
85
|
-
return {
|
|
86
|
-
taken: taken.filter((one) => one.locator === locator).map(({ path }) => path),
|
|
87
|
-
writes: [...rooms].map((room) => ({
|
|
88
|
-
locator: room,
|
|
89
|
-
roomDrafts: records.filter((record) => record.locator === room),
|
|
90
|
-
consumedRoomDrafts: elsewhere.filter((one) => one.locator === room).map(({ path }) => path),
|
|
91
|
-
})),
|
|
92
|
-
changes: [...rooms].map((room) => changeSet(room, {
|
|
93
|
-
drafts: [...records, ...elsewhere]
|
|
94
|
-
.filter((one) => one.locator === room)
|
|
95
|
-
.map(({ path }) => path),
|
|
96
|
-
})),
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
export function preparedAfter(runtime, locator, answer) {
|
|
100
|
-
if (answer.indexed[locator] === false)
|
|
101
|
-
prepare(runtime, [locator]);
|
|
30
|
+
return replica;
|
|
102
31
|
}
|
|
103
|
-
export function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
export async function settle(runtime, local, locator, work, record, queue, settling = {}) {
|
|
117
|
-
return together(runtime, local, [
|
|
118
|
-
await settlement(runtime, local, locator, work, record, queue, settling),
|
|
119
|
-
]);
|
|
120
|
-
}
|
|
121
|
-
async function unpairedIn(store, locator, batch) {
|
|
122
|
-
const { state } = await (await store.open()).pending(locator, batch.id);
|
|
123
|
-
if (state === undefined)
|
|
124
|
-
return false;
|
|
125
|
-
return !leavingIn(batch, locator) || withdrawable(new Map([[batch.id, state]]))(batch);
|
|
126
|
-
}
|
|
127
|
-
export async function together(runtime, local, settlements) {
|
|
128
|
-
const settled = new Set(settlements.map(({ locator }) => locator));
|
|
129
|
-
const all = [...settlements];
|
|
130
|
-
for (let wave = settlements; wave.length > 0;) {
|
|
131
|
-
const partnered = new Map();
|
|
132
|
-
for (const { locator, left } of wave) {
|
|
133
|
-
for (const batch of left) {
|
|
134
|
-
const partner = partnerOf(batch, locator);
|
|
135
|
-
if (partner === null ||
|
|
136
|
-
settled.has(partner) ||
|
|
137
|
-
!(await unpairedIn(runtime.store, partner, batch))) {
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
partnered.set(partner, [...(partnered.get(partner) ?? []), batch]);
|
|
32
|
+
export function saveRoomDrafts(runtime, locator, named, writtenBy) {
|
|
33
|
+
const { store } = runtime;
|
|
34
|
+
return store.exclusive(async () => {
|
|
35
|
+
if (store.space.full())
|
|
36
|
+
return FULL;
|
|
37
|
+
const replica = await savedRoom(runtime, locator);
|
|
38
|
+
const drafts = new Map((await store.drafts(locator, named.map(({ path }) => path))).map((record) => [record.path, record]));
|
|
39
|
+
const saved = [];
|
|
40
|
+
for (const { path, revision } of named) {
|
|
41
|
+
const record = drafts.get(path);
|
|
42
|
+
if (record === undefined || record.revision !== revision) {
|
|
43
|
+
return { outcome: 'refused', code: 'stale', retryable: false, file: path };
|
|
141
44
|
}
|
|
45
|
+
if (!saved.some((draft) => draft.path === path))
|
|
46
|
+
saved.push({ path, parts: record.parts });
|
|
142
47
|
}
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
all.push(...partners);
|
|
149
|
-
wave = partners;
|
|
150
|
-
}
|
|
151
|
-
const answer = await local.commit({
|
|
152
|
-
rooms: all.flatMap(({ commit }) => commit.rooms),
|
|
153
|
-
writes: all.flatMap(({ commit }) => commit.writes),
|
|
48
|
+
const result = await loaded(runtime, locator, replica, saved, new Set(saved.map(({ path }) => path)), [], writtenBy);
|
|
49
|
+
if (result.outcome !== 'refused' || result.code !== 'changed-underneath')
|
|
50
|
+
return result;
|
|
51
|
+
return joinedUnderneath(runtime, locator, saved.map(({ path }) => drafts.get(path)), result);
|
|
154
52
|
});
|
|
155
|
-
return mergeChangeSets(all.map((one) => one.apply(answer)));
|
|
156
53
|
}
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const overrides = settling.changes;
|
|
163
|
-
const read = await sliceAt(store, record, view, queue, [
|
|
164
|
-
...(before !== undefined && before.record.key === record.key ? before.overlaid : []),
|
|
165
|
-
...(overrides?.set.map(({ path }) => path) ?? []),
|
|
166
|
-
...(overrides?.removed ?? []),
|
|
167
|
-
]);
|
|
168
|
-
const confirmedLayer = overrides === undefined
|
|
169
|
-
? read
|
|
170
|
-
: await stepped(slicingWith(read, overrides.set, overrides.removed));
|
|
171
|
-
const composition = store.composed(record);
|
|
172
|
-
const shown = queue.filter((batch) => {
|
|
173
|
-
const held = store.landed.of(locator, batch.id);
|
|
174
|
-
return (held !== undefined &&
|
|
175
|
-
Object.entries(held).some(([path, revision]) => confirmedLayer.revisions[path] !== revision));
|
|
54
|
+
async function joinedUnderneath({ store }, locator, records, refused) {
|
|
55
|
+
const visible = (await visibleOf(store, locator, records.map(({ path }) => path)));
|
|
56
|
+
const joined = records.flatMap((record) => {
|
|
57
|
+
const join = joinedOver(record.parts, record.marks, record.path, visible);
|
|
58
|
+
return join === null ? [] : [{ ...record, ...join, revision: roomDraftRevision() }];
|
|
176
59
|
});
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
for (const batch of withdrawn) {
|
|
183
|
-
unqueued.push({ batch, landed: await landedWhole(store, locator, batch, confirmedLayer) });
|
|
184
|
-
}
|
|
185
|
-
const returning = [
|
|
186
|
-
...[...unqueued].reverse().map(({ batch, landed: held }) => ({
|
|
187
|
-
batch,
|
|
188
|
-
returned: localSaves || held ? null : returnedBy(),
|
|
189
|
-
})),
|
|
190
|
-
...(settling.leaving ?? []),
|
|
191
|
-
];
|
|
192
|
-
const written = new Map();
|
|
193
|
-
for (const { batch, returned: reason } of returning) {
|
|
194
|
-
if (reason === null || batch.between?.destination.room === locator)
|
|
195
|
-
continue;
|
|
196
|
-
for (const saved of work.states.get(batch.id)?.drafts ?? []) {
|
|
197
|
-
const existing = written.get(saved.path) ?? (await store.draft(locator, saved.path));
|
|
198
|
-
written.set(saved.path, {
|
|
199
|
-
locator,
|
|
200
|
-
path: saved.path,
|
|
201
|
-
revision: roomDraftRevision(),
|
|
202
|
-
parts: returnedInto(existing?.parts, saved.parts),
|
|
203
|
-
returned: reason,
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
const carried = await carriedBack(store, locator, [
|
|
208
|
-
...[...unqueued].reverse().flatMap(({ batch, landed: held }) => (held ? [] : [batch])),
|
|
209
|
-
...(settling.leaving ?? []).flatMap(({ batch, returned }) => returned === null ? [] : [batch]),
|
|
210
|
-
], written);
|
|
211
|
-
const replayedLayer = await replayed(store, record, confirmedLayer, kept);
|
|
212
|
-
const { overlay } = await overlayDiff(record, before, replayedLayer.visible, replayedLayer.touched);
|
|
213
|
-
const commit = {
|
|
214
|
-
rooms: [
|
|
215
|
-
{
|
|
216
|
-
locator,
|
|
217
|
-
key: record.key,
|
|
218
|
-
record: storedRecord(record),
|
|
219
|
-
roots: record.typedSelection?.roots ?? [],
|
|
220
|
-
composed: composedOf(composition),
|
|
221
|
-
...settling.commit,
|
|
222
|
-
...(overrides === undefined
|
|
223
|
-
? {}
|
|
224
|
-
: { confirmed: { set: overrides.set, removed: overrides.removed } }),
|
|
225
|
-
overlay,
|
|
226
|
-
},
|
|
227
|
-
],
|
|
228
|
-
writes: [
|
|
229
|
-
{
|
|
230
|
-
locator,
|
|
231
|
-
queue: kept,
|
|
232
|
-
stored: previousQueue,
|
|
233
|
-
founded,
|
|
234
|
-
roomDrafts: [...written.values()],
|
|
235
|
-
consumedRoomDrafts: carried.taken,
|
|
236
|
-
},
|
|
237
|
-
...carried.writes,
|
|
238
|
-
],
|
|
239
|
-
};
|
|
240
|
-
const staying = new Set(kept.map((batch) => batch.id));
|
|
241
|
-
return {
|
|
242
|
-
locator,
|
|
243
|
-
left: previousQueue.filter((batch) => !staying.has(batch.id)),
|
|
244
|
-
commit,
|
|
245
|
-
apply: (answer) => {
|
|
246
|
-
for (const batch of previousQueue) {
|
|
247
|
-
if (!staying.has(batch.id))
|
|
248
|
-
store.landed.drop(locator, batch.id);
|
|
249
|
-
}
|
|
250
|
-
const drafts = [...written.keys(), ...carried.taken];
|
|
251
|
-
for (const { batch, landed: held } of unqueued) {
|
|
252
|
-
const outcome = localSaves ? 'dropped' : held ? 'landed' : 'withdrawn';
|
|
253
|
-
journal.record(`outcome ${locator} ${batch.id} ${outcome}`);
|
|
254
|
-
}
|
|
255
|
-
const changed = answer.changed[locator];
|
|
256
|
-
if (settling.commit?.published !== undefined ||
|
|
257
|
-
settling.commit?.refreshed === true ||
|
|
258
|
-
overrides !== undefined) {
|
|
259
|
-
confirmedAgain(store, locator);
|
|
260
|
-
}
|
|
261
|
-
preparedAfter(runtime, locator, answer);
|
|
262
|
-
return mergeChangeSets([
|
|
263
|
-
changeSet(locator, {
|
|
264
|
-
documents: changed === undefined ? [] : changed,
|
|
265
|
-
room: settling.room,
|
|
266
|
-
queue: queueChanged,
|
|
267
|
-
drafts,
|
|
268
|
-
}),
|
|
269
|
-
...carried.changes,
|
|
270
|
-
]);
|
|
271
|
-
},
|
|
272
|
-
};
|
|
60
|
+
if (joined.length === 0)
|
|
61
|
+
return refused;
|
|
62
|
+
await (await store.open()).commit({ rooms: [], writes: [{ locator, roomDrafts: joined }] });
|
|
63
|
+
await store.changed(changeSet(locator, { drafts: joined.map(({ path }) => path) }));
|
|
64
|
+
return refused;
|
|
273
65
|
}
|
|
274
|
-
export
|
|
275
|
-
const { store, journal, localSaves, submitLater } = runtime;
|
|
276
|
-
const ids = new Set(batches.map(({ id }) => id));
|
|
277
|
-
const leaving = batches.map((batch) => ({
|
|
278
|
-
batch,
|
|
279
|
-
returned: !localSaves && leavingIn(batch, locator) ? returnedBy() : null,
|
|
280
|
-
}));
|
|
281
|
-
const outcome = localSaves ? 'dropped' : 'unpaired';
|
|
282
|
-
const work = await store.work(locator);
|
|
283
|
-
const current = work.replica;
|
|
284
|
-
if (current !== undefined) {
|
|
285
|
-
const settled = await settlement(runtime, local, locator, work, current.record, current.queue.filter((one) => !ids.has(one.id)), { leaving });
|
|
286
|
-
return {
|
|
287
|
-
...settled,
|
|
288
|
-
apply(answer) {
|
|
289
|
-
for (const batch of batches)
|
|
290
|
-
journal.record(`outcome ${locator} ${batch.id} ${outcome}`);
|
|
291
|
-
const set = settled.apply(answer);
|
|
292
|
-
submitLater(locator);
|
|
293
|
-
return set;
|
|
294
|
-
},
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
const queue = work.queue.filter((one) => !ids.has(one.id));
|
|
298
|
-
const written = new Map();
|
|
299
|
-
for (const { batch, returned } of leaving) {
|
|
300
|
-
if (returned === null)
|
|
301
|
-
continue;
|
|
302
|
-
for (const saved of work.states.get(batch.id)?.drafts ?? []) {
|
|
303
|
-
written.set(saved.path, {
|
|
304
|
-
locator,
|
|
305
|
-
path: saved.path,
|
|
306
|
-
revision: roomDraftRevision(),
|
|
307
|
-
parts: returnedInto((written.get(saved.path) ?? (await store.draft(locator, saved.path)))?.parts, saved.parts),
|
|
308
|
-
returned,
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
const carried = await carriedBack(store, locator, leaving.flatMap(({ batch, returned }) => (returned === null ? [] : [batch])), written);
|
|
313
|
-
return {
|
|
314
|
-
locator,
|
|
315
|
-
left: batches,
|
|
316
|
-
commit: {
|
|
317
|
-
rooms: [],
|
|
318
|
-
writes: [
|
|
319
|
-
{
|
|
320
|
-
locator,
|
|
321
|
-
queue,
|
|
322
|
-
stored: work.queue,
|
|
323
|
-
roomDrafts: [...written.values()],
|
|
324
|
-
consumedRoomDrafts: carried.taken,
|
|
325
|
-
},
|
|
326
|
-
...carried.writes,
|
|
327
|
-
],
|
|
328
|
-
},
|
|
329
|
-
apply() {
|
|
330
|
-
for (const batch of batches)
|
|
331
|
-
journal.record(`outcome ${locator} ${batch.id} ${outcome}`);
|
|
332
|
-
submitLater(locator);
|
|
333
|
-
return mergeChangeSets([
|
|
334
|
-
changeSet(locator, {
|
|
335
|
-
queue: true,
|
|
336
|
-
drafts: [...written.keys(), ...carried.taken],
|
|
337
|
-
}),
|
|
338
|
-
...carried.changes,
|
|
339
|
-
]);
|
|
340
|
-
},
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
async function savedRoom(store, locator) {
|
|
344
|
-
const local = await store.open();
|
|
345
|
-
const replica = await store.replica(locator);
|
|
346
|
-
if (replica === undefined || store.fences.has(locator)) {
|
|
347
|
-
throw runtimeError('unknown-room', `Unknown Room ${locator}`);
|
|
348
|
-
}
|
|
349
|
-
return { local, replica };
|
|
350
|
-
}
|
|
351
|
-
export async function saveRoomDrafts(runtime, locator, named, writtenBy) {
|
|
352
|
-
const { store } = runtime;
|
|
353
|
-
const target = await savedRoom(store, locator);
|
|
354
|
-
const drafts = new Map((await store.drafts(locator, named.map(({ path }) => path))).map((record) => [record.path, record]));
|
|
355
|
-
const saved = [];
|
|
356
|
-
for (const { path, revision } of named) {
|
|
357
|
-
const record = drafts.get(path);
|
|
358
|
-
if (record === undefined || record.revision !== revision) {
|
|
359
|
-
return { outcome: 'refused', code: 'stale', retryable: false, file: path };
|
|
360
|
-
}
|
|
361
|
-
if (!saved.some((draft) => draft.path === path))
|
|
362
|
-
saved.push({ path, parts: record.parts });
|
|
363
|
-
}
|
|
364
|
-
return committed(runtime, locator, target, saved, new Set(saved.map(({ path }) => path)), writtenBy);
|
|
365
|
-
}
|
|
366
|
-
export async function saveParts(runtime, locator, named, writtenBy) {
|
|
66
|
+
export function saveParts(runtime, locator, named, writtenBy) {
|
|
367
67
|
const { store } = runtime;
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
68
|
+
return store.exclusive(async () => {
|
|
69
|
+
const read = readSavedParts(named);
|
|
70
|
+
if (store.space.full())
|
|
71
|
+
return FULL;
|
|
72
|
+
const replica = await savedRoom(runtime, locator);
|
|
73
|
+
const saved = [];
|
|
74
|
+
const files = [];
|
|
75
|
+
const filed = read.filter(({ parts }) => parts.file !== undefined).map(({ path }) => path);
|
|
76
|
+
const visible = filed.length === 0 ? undefined : await visibleOf(store, locator, filed);
|
|
77
|
+
for (const { path, parts } of read) {
|
|
78
|
+
const file = parts.file === undefined
|
|
79
|
+
? undefined
|
|
80
|
+
: await heldFile(parts.file.blob, parts.file.base === undefined ? binaryBase(visible, path) : parts.file.base);
|
|
81
|
+
if (parts.file !== undefined && file !== undefined) {
|
|
82
|
+
files.push({ key: file.localId, blob: parts.file.blob });
|
|
83
|
+
}
|
|
84
|
+
saved.push({ path, parts: held(undefined, parts, file) });
|
|
380
85
|
}
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
return committed(runtime, locator, target, saved, new Set(), writtenBy, files);
|
|
86
|
+
return loaded(runtime, locator, replica, saved, new Set(), files, writtenBy);
|
|
87
|
+
});
|
|
384
88
|
}
|
|
385
|
-
async function
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
return [];
|
|
393
|
-
const to = moveTarget(parts.move);
|
|
394
|
-
if (to === path || holding.has(to))
|
|
395
|
-
return [];
|
|
396
|
-
return [[path, { ...record, path: to, revision: roomDraftRevision() }]];
|
|
397
|
-
}));
|
|
89
|
+
async function unmodeledIn(runtime, record) {
|
|
90
|
+
const composition = compositionFor(runtime, record);
|
|
91
|
+
if (available(composition))
|
|
92
|
+
return false;
|
|
93
|
+
const local = await runtime.store.open();
|
|
94
|
+
const roots = record.declaration.typedSelection?.roots ?? [];
|
|
95
|
+
return local.typedWithin(record.key, 'visible', roots, false);
|
|
398
96
|
}
|
|
399
|
-
async function
|
|
400
|
-
const
|
|
97
|
+
async function savingRoom(runtime, replica, changes, extra, drafted) {
|
|
98
|
+
const { store } = runtime;
|
|
99
|
+
const { record, queue, states } = replica;
|
|
401
100
|
const local = await store.open();
|
|
402
101
|
const paths = [
|
|
403
102
|
...extra,
|
|
@@ -408,26 +107,55 @@ async function savingIn(store, replica, changes, extra) {
|
|
|
408
107
|
const referring = removed.length === 0
|
|
409
108
|
? []
|
|
410
109
|
: (await local.referrers(record.key, 'confirmed', removed)).map(({ path }) => path);
|
|
411
|
-
const
|
|
412
|
-
const { visible } = await replayed(store, record, confirmedLayer, replica.queue);
|
|
110
|
+
const confirmed = await sliceAt(store, record, 'confirmed', queue, [...paths, ...paths.flatMap(ancestors), ...referring], moved);
|
|
413
111
|
const composition = store.composed(record);
|
|
112
|
+
const changeable = withdrawable(states);
|
|
414
113
|
return {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
114
|
+
held: { replica, confirmed },
|
|
115
|
+
room: {
|
|
116
|
+
record,
|
|
117
|
+
rule: runtime.writeRule(record.declaration),
|
|
118
|
+
composition: compositionFor(runtime, record),
|
|
119
|
+
queue,
|
|
120
|
+
changeable: new Set(queue.filter(changeable).map(({ id }) => id)),
|
|
121
|
+
saved: new Map([...states].map(([id, state]) => [id, state.drafts])),
|
|
122
|
+
*replay(over) {
|
|
123
|
+
return (yield* replaying(confirmed, over, record.locator, composition)).visible;
|
|
124
|
+
},
|
|
125
|
+
folders: new Set(await local.folders(record.key, paths)),
|
|
126
|
+
modelled: !(await unmodeledIn(runtime, record)),
|
|
127
|
+
drafts: new Map(drafted.length === 0
|
|
128
|
+
? []
|
|
129
|
+
: (await store.drafts(record.locator, drafted)).map((held) => [held.path, held])),
|
|
130
|
+
validate: validatorOf(runtime, record, confirmed),
|
|
421
131
|
},
|
|
422
132
|
};
|
|
423
133
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
const
|
|
430
|
-
|
|
134
|
+
function targetsOf(saved) {
|
|
135
|
+
return saved.flatMap(({ parts }) => (parts.move === undefined ? [] : [moveTarget(parts.move)]));
|
|
136
|
+
}
|
|
137
|
+
async function loaded(runtime, locator, replica, saved, consumed, files, writtenBy) {
|
|
138
|
+
const { store, localSaves } = runtime;
|
|
139
|
+
const into = destination(locator, saved);
|
|
140
|
+
const target = into === null ? undefined : await savedRoom(runtime, into);
|
|
141
|
+
const paths = saved.map(({ path }) => path);
|
|
142
|
+
const targets = targetsOf(saved);
|
|
143
|
+
const parts = saved.map(({ path, parts: held }) => changesOf(path, held));
|
|
144
|
+
const source = target === undefined
|
|
145
|
+
? await savingRoom(runtime, replica, parts.flatMap(({ changes }) => changes), [...paths, ...parts.flatMap(({ bases }) => bases.map(({ path }) => path))], targets.length === 0 ? [] : [...paths, ...targets])
|
|
146
|
+
: await savingRoom(runtime, replica, paths.map((path) => ({ kind: 'remove', path })), paths, paths);
|
|
147
|
+
const arriving = target === undefined ? undefined : await savingRoom(runtime, target, [], targets, targets);
|
|
148
|
+
const { providerLimits } = await (await store.open()).facts();
|
|
149
|
+
const plan = await planSave({ saved, consumed, writtenBy, local: localSaves, providerLimits }, source.room, arriving?.room);
|
|
150
|
+
return append(runtime, {
|
|
151
|
+
plan,
|
|
152
|
+
files,
|
|
153
|
+
held: new Map([source, ...(arriving === undefined ? [] : [arriving])].map(({ room, held }) => [
|
|
154
|
+
room.record.locator,
|
|
155
|
+
held,
|
|
156
|
+
])),
|
|
157
|
+
after: () => pendingShown(runtime),
|
|
158
|
+
});
|
|
431
159
|
}
|
|
432
160
|
function textDiffers(left, right) {
|
|
433
161
|
return ((left?.content ?? undefined) !== (right?.content ?? undefined) ||
|
|
@@ -451,8 +179,7 @@ function lookupOf(local, key, view) {
|
|
|
451
179
|
function declaredOf(entry) {
|
|
452
180
|
if (entry.binary !== null)
|
|
453
181
|
return { type: undefined, binary: true };
|
|
454
|
-
|
|
455
|
-
return { type: read.ok ? (read.frontmatter.type ?? 'any') : undefined, binary: false };
|
|
182
|
+
return { type: textType(parse(entry.content ?? '')), binary: false };
|
|
456
183
|
}
|
|
457
184
|
function overriddenLookup(lookup, overrides) {
|
|
458
185
|
return async (paths) => {
|
|
@@ -503,309 +230,6 @@ function referrersOf(local, key, view) {
|
|
|
503
230
|
...new Set((await local.referrers(key, view, targets)).map(({ path }) => path)),
|
|
504
231
|
];
|
|
505
232
|
}
|
|
506
|
-
async function sideOf(runtime, locator, replica, saving) {
|
|
507
|
-
const record = replica.record;
|
|
508
|
-
return {
|
|
509
|
-
room: locator,
|
|
510
|
-
handle: record.handle,
|
|
511
|
-
baseRoomVersion: record.roomVersion,
|
|
512
|
-
composition: compositionFor(runtime, record),
|
|
513
|
-
operations: record.operations,
|
|
514
|
-
providerOwnedPaths: record.providerOwnedPaths,
|
|
515
|
-
authoredPaths: record.authoredPaths,
|
|
516
|
-
visible: saving.visible,
|
|
517
|
-
folders: saving.folders,
|
|
518
|
-
queued: queuedDocuments(replica.queue),
|
|
519
|
-
unmodeled: await unmodeledIn(runtime, record),
|
|
520
|
-
validate: validatorOf(runtime, record, saving.confirmed),
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
function movedInto(locator, saved) {
|
|
524
|
-
const into = saved.find(({ parts }) => parts.move?.room !== undefined && parts.move.room !== locator);
|
|
525
|
-
return into?.parts.move?.room ?? null;
|
|
526
|
-
}
|
|
527
|
-
async function saveCommit(store, local, files, rooms) {
|
|
528
|
-
const commits = [];
|
|
529
|
-
for (const { locator, saving, before, queue } of rooms) {
|
|
530
|
-
const replayedLayer = await replayed(store, saving.record, saving.confirmed, queue);
|
|
531
|
-
const diff = await overlayDiff(saving.record, before, replayedLayer.visible, replayedLayer.touched);
|
|
532
|
-
commits.push({
|
|
533
|
-
locator,
|
|
534
|
-
key: saving.record.key,
|
|
535
|
-
roots: saving.record.typedSelection?.roots ?? [],
|
|
536
|
-
composed: composedOf(store.composed(saving.record)),
|
|
537
|
-
overlay: diff.overlay,
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
try {
|
|
541
|
-
return await local.commit({
|
|
542
|
-
rooms: commits,
|
|
543
|
-
writes: rooms.map(({ write }) => write),
|
|
544
|
-
files,
|
|
545
|
-
whole: true,
|
|
546
|
-
});
|
|
547
|
-
}
|
|
548
|
-
catch {
|
|
549
|
-
return null;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
async function committedMove(runtime, locator, destination, { local, replica }, saved, consumed, writtenBy, files) {
|
|
553
|
-
const { store, localSaves } = runtime;
|
|
554
|
-
const mixed = saved.find(({ parts }) => Object.keys(parts).length !== 1 || parts.move?.room !== destination);
|
|
555
|
-
if (mixed !== undefined) {
|
|
556
|
-
return { outcome: 'refused', code: 'mixed-room-move', retryable: false, file: mixed.path };
|
|
557
|
-
}
|
|
558
|
-
const moves = saved.map(({ path, parts }) => ({ path, to: moveTarget(parts.move) }));
|
|
559
|
-
const sourceSaving = await savingIn(store, replica, moves.map(({ path }) => ({ kind: 'remove', path })), saved.map(({ path }) => path));
|
|
560
|
-
const changed = saved.flatMap(({ path, parts }) => changedParts(path, parts, sourceSaving.visible));
|
|
561
|
-
if (changed.length > 0)
|
|
562
|
-
return { outcome: 'refused', code: 'changed-underneath', retryable: false, parts: changed };
|
|
563
|
-
const target = await store.replica(destination);
|
|
564
|
-
if (target === undefined || store.fences.has(destination)) {
|
|
565
|
-
throw runtimeError('unknown-room', `Unknown Room ${destination}`);
|
|
566
|
-
}
|
|
567
|
-
const arriving = await savingIn(store, target, [], moves.map(({ to }) => to));
|
|
568
|
-
const built = await buildMove({
|
|
569
|
-
source: await sideOf(runtime, locator, replica, sourceSaving),
|
|
570
|
-
destination: await sideOf(runtime, destination, target, arriving),
|
|
571
|
-
moves,
|
|
572
|
-
providerLimits: (await local.facts()).providerLimits,
|
|
573
|
-
pathRule: runtime.pathRule,
|
|
574
|
-
local: localSaves,
|
|
575
|
-
...(writtenBy === undefined ? {} : { writtenBy }),
|
|
576
|
-
});
|
|
577
|
-
if (built.kind === 'refused')
|
|
578
|
-
return { outcome: 'refused', ...built.refusal };
|
|
579
|
-
const id = built.leaving.id;
|
|
580
|
-
const leavingDrafts = new Map((await store.drafts(locator, saved.map(({ path }) => path))).map((record) => [record.path, record]));
|
|
581
|
-
const arrivingDrafts = new Set((await store.drafts(destination, moves.map(({ to }) => to))).map(({ path }) => path));
|
|
582
|
-
const carrying = saved.flatMap(({ path, parts }) => {
|
|
583
|
-
const record = leavingDrafts.get(path);
|
|
584
|
-
const to = moveTarget(parts.move);
|
|
585
|
-
if (record === undefined || consumed.has(path) || arrivingDrafts.has(to)) {
|
|
586
|
-
return [];
|
|
587
|
-
}
|
|
588
|
-
const moved = {
|
|
589
|
-
...record,
|
|
590
|
-
locator: destination,
|
|
591
|
-
path: to,
|
|
592
|
-
revision: roomDraftRevision(),
|
|
593
|
-
};
|
|
594
|
-
return [{ from: path, record: moved }];
|
|
595
|
-
});
|
|
596
|
-
const carried = carrying.map(({ record }) => record);
|
|
597
|
-
const marked = carried.map(({ path }) => ({ path, parts: {} }));
|
|
598
|
-
const left = [...consumed, ...carrying.map(({ from }) => from)];
|
|
599
|
-
const leavingQueue = [...replica.queue, built.leaving];
|
|
600
|
-
const arrivingQueue = [...target.queue, built.arriving];
|
|
601
|
-
const answer = await saveCommit(store, local, files, [
|
|
602
|
-
{
|
|
603
|
-
locator,
|
|
604
|
-
saving: sourceSaving,
|
|
605
|
-
before: replica,
|
|
606
|
-
queue: leavingQueue,
|
|
607
|
-
write: {
|
|
608
|
-
locator,
|
|
609
|
-
queue: leavingQueue,
|
|
610
|
-
stored: replica.queue,
|
|
611
|
-
saved: new Map([[id, saved]]),
|
|
612
|
-
consumedRoomDrafts: left,
|
|
613
|
-
},
|
|
614
|
-
},
|
|
615
|
-
{
|
|
616
|
-
locator: destination,
|
|
617
|
-
saving: arriving,
|
|
618
|
-
before: target,
|
|
619
|
-
queue: arrivingQueue,
|
|
620
|
-
write: {
|
|
621
|
-
locator: destination,
|
|
622
|
-
queue: arrivingQueue,
|
|
623
|
-
stored: target.queue,
|
|
624
|
-
saved: new Map([[id, marked]]),
|
|
625
|
-
roomDrafts: carried,
|
|
626
|
-
},
|
|
627
|
-
},
|
|
628
|
-
]);
|
|
629
|
-
if (answer === null)
|
|
630
|
-
return { outcome: 'refused', code: 'durable-write', retryable: true };
|
|
631
|
-
if (answer.fenced !== null) {
|
|
632
|
-
throw runtimeError('unknown-room', `Unknown Room ${answer.fenced}`);
|
|
633
|
-
}
|
|
634
|
-
if (store.fences.has(locator) || store.fences.has(destination)) {
|
|
635
|
-
throw runtimeError('unknown-room', `Unknown Room ${locator}`);
|
|
636
|
-
}
|
|
637
|
-
preparedAfter(runtime, locator, answer);
|
|
638
|
-
preparedAfter(runtime, destination, answer);
|
|
639
|
-
const shownNow = store.changed(mergeChangeSets([
|
|
640
|
-
changeSet(locator, {
|
|
641
|
-
documents: answer.changed[locator] ?? [],
|
|
642
|
-
queue: true,
|
|
643
|
-
drafts: left,
|
|
644
|
-
}),
|
|
645
|
-
changeSet(destination, {
|
|
646
|
-
documents: answer.changed[destination] ?? [],
|
|
647
|
-
queue: true,
|
|
648
|
-
drafts: carried.map(({ path }) => path),
|
|
649
|
-
}),
|
|
650
|
-
]));
|
|
651
|
-
await Promise.all([shownNow, queuedSave(runtime, locator)]);
|
|
652
|
-
return { outcome: 'saved', identifier: id };
|
|
653
|
-
}
|
|
654
|
-
function queuedSave(runtime, locator) {
|
|
655
|
-
const shownNow = pendingShown(runtime);
|
|
656
|
-
runtime.submitLater(locator);
|
|
657
|
-
return shownNow;
|
|
658
|
-
}
|
|
659
|
-
async function committed(runtime, locator, target, saved, consumed, writtenBy, files = []) {
|
|
660
|
-
const { store, localSaves } = runtime;
|
|
661
|
-
if (store.space.full())
|
|
662
|
-
return { outcome: 'refused', code: 'durable-write', retryable: true };
|
|
663
|
-
const into = movedInto(locator, saved);
|
|
664
|
-
if (into !== null) {
|
|
665
|
-
return committedMove(runtime, locator, into, target, saved, consumed, writtenBy, files);
|
|
666
|
-
}
|
|
667
|
-
const { local, replica } = target;
|
|
668
|
-
const { providerLimits } = await local.facts();
|
|
669
|
-
const parts = saved.map(({ path, parts: held }) => changesOf(path, held));
|
|
670
|
-
const changes = parts.flatMap(({ changes: made }) => made);
|
|
671
|
-
const saving = await savingIn(store, replica, changes, [
|
|
672
|
-
...saved.map(({ path }) => path),
|
|
673
|
-
...parts.flatMap(({ bases }) => bases.map(({ path }) => path)),
|
|
674
|
-
]);
|
|
675
|
-
const changed = saved.flatMap(({ path, parts: held }) => changedParts(path, held, saving.visible));
|
|
676
|
-
if (changed.length > 0)
|
|
677
|
-
return { outcome: 'refused', code: 'changed-underneath', retryable: false, parts: changed };
|
|
678
|
-
const requeue = await stepped(requeuing(replica.queue, changes, saving.visible, withdrawable(replica.states), saving.visibleOver));
|
|
679
|
-
if (requeue.kind === 'refused')
|
|
680
|
-
return { outcome: 'refused', ...requeue.refusal };
|
|
681
|
-
const foreign = [...requeue.withdrawn.keys()].some((origin) => {
|
|
682
|
-
const writer = replica.queue.find((batch) => batch.id === origin)?.writtenBy;
|
|
683
|
-
return writer?.app !== writtenBy?.app || writer?.release !== writtenBy?.release;
|
|
684
|
-
});
|
|
685
|
-
if (foreign) {
|
|
686
|
-
return {
|
|
687
|
-
outcome: 'refused',
|
|
688
|
-
code: 'unknown-revision',
|
|
689
|
-
retryable: false,
|
|
690
|
-
file: [...requeue.queued].at(-1),
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
const withdrawing = requeue.withdrawn.size > 0;
|
|
694
|
-
const input = {
|
|
695
|
-
local: localSaves,
|
|
696
|
-
baseRoomVersion: replica.record.roomVersion,
|
|
697
|
-
composition: compositionFor(runtime, replica.record),
|
|
698
|
-
operations: replica.record.operations,
|
|
699
|
-
providerOwnedPaths: replica.record.providerOwnedPaths,
|
|
700
|
-
authoredPaths: replica.record.authoredPaths,
|
|
701
|
-
providerLimits,
|
|
702
|
-
pathRule: runtime.pathRule,
|
|
703
|
-
handle: replica.record.handle,
|
|
704
|
-
visible: saving.visible,
|
|
705
|
-
folders: saving.folders,
|
|
706
|
-
changes,
|
|
707
|
-
bases: parts.flatMap(({ bases }) => bases),
|
|
708
|
-
queued: queuedDocuments(replica.queue),
|
|
709
|
-
writtenBy,
|
|
710
|
-
unmodeled: await unmodeledIn(runtime, replica.record),
|
|
711
|
-
validate: validatorOf(runtime, replica.record, saving.confirmed),
|
|
712
|
-
};
|
|
713
|
-
const checked = withdrawing
|
|
714
|
-
? await buildBatch({ ...input, queued: new Set([...input.queued, ...requeue.queued]) })
|
|
715
|
-
: null;
|
|
716
|
-
if (checked?.kind === 'refused')
|
|
717
|
-
return { outcome: 'refused', ...checked.refusal };
|
|
718
|
-
const owners = [...requeue.owners].flatMap((origin) => {
|
|
719
|
-
const batch = requeue.withdrawn.get(origin);
|
|
720
|
-
return batch === undefined || batch === null ? [] : [{ origin, batch }];
|
|
721
|
-
});
|
|
722
|
-
if (owners.length > 1) {
|
|
723
|
-
return {
|
|
724
|
-
outcome: 'refused',
|
|
725
|
-
code: 'unknown-revision',
|
|
726
|
-
retryable: false,
|
|
727
|
-
file: [...requeue.queued].at(-1),
|
|
728
|
-
};
|
|
729
|
-
}
|
|
730
|
-
const built = !withdrawing
|
|
731
|
-
? await buildBatch(input)
|
|
732
|
-
: requeue.changes.length === 0
|
|
733
|
-
? null
|
|
734
|
-
: await buildBatch({
|
|
735
|
-
...input,
|
|
736
|
-
visible: await stepped(saving.visibleOver(requeue.queue)),
|
|
737
|
-
changes: requeue.changes,
|
|
738
|
-
queued: queuedDocuments(requeue.queue),
|
|
739
|
-
});
|
|
740
|
-
if (built?.kind === 'refused')
|
|
741
|
-
return { outcome: 'refused', ...built.refusal };
|
|
742
|
-
const [owner] = owners;
|
|
743
|
-
const join = built === null || owner === undefined ? null : joined(requeue.queue, owner.batch, built.batch);
|
|
744
|
-
if (join?.kind === 'refused')
|
|
745
|
-
return { outcome: 'refused', ...join.refusal };
|
|
746
|
-
const withdrawn = new Map(requeue.withdrawn);
|
|
747
|
-
if (join !== null && owner !== undefined)
|
|
748
|
-
withdrawn.set(owner.origin, join.batch);
|
|
749
|
-
const drafted = new Map();
|
|
750
|
-
for (const [origin, replaced] of withdrawn) {
|
|
751
|
-
if (replaced === null)
|
|
752
|
-
continue;
|
|
753
|
-
const carried = withdrawnDrafts(replica.states.get(origin)?.drafts ?? [], changes);
|
|
754
|
-
drafted.set(replaced.id, replaced === join?.batch
|
|
755
|
-
? [
|
|
756
|
-
...carried.map((held) => joinedDraft(held, saved)),
|
|
757
|
-
...saved.filter((draft) => !carried.some((held) => held.path === draft.path)),
|
|
758
|
-
]
|
|
759
|
-
: carried);
|
|
760
|
-
}
|
|
761
|
-
if (built !== null && join === null)
|
|
762
|
-
drafted.set(built.batch.id, saved);
|
|
763
|
-
const batches = join !== null ? join.queue : built === null ? requeue.queue : [...requeue.queue, built.batch];
|
|
764
|
-
for (const replaced of withdrawn.values()) {
|
|
765
|
-
if (replaced === null || !batches.includes(replaced))
|
|
766
|
-
continue;
|
|
767
|
-
const limited = await stepped(limiting(batches, replaced, providerLimits, saving.visibleOver));
|
|
768
|
-
if (limited !== null)
|
|
769
|
-
return { outcome: 'refused', ...limited };
|
|
770
|
-
}
|
|
771
|
-
const carried = await carriedDrafts(store, locator, saved, consumed);
|
|
772
|
-
const left = [...consumed, ...carried.keys()];
|
|
773
|
-
const arrived = [...carried.values()];
|
|
774
|
-
const answer = await saveCommit(store, local, files, [
|
|
775
|
-
{
|
|
776
|
-
locator,
|
|
777
|
-
saving,
|
|
778
|
-
before: replica,
|
|
779
|
-
queue: batches,
|
|
780
|
-
write: {
|
|
781
|
-
locator,
|
|
782
|
-
queue: batches,
|
|
783
|
-
stored: replica.queue,
|
|
784
|
-
saved: drafted,
|
|
785
|
-
consumedRoomDrafts: left,
|
|
786
|
-
roomDrafts: arrived,
|
|
787
|
-
},
|
|
788
|
-
},
|
|
789
|
-
]);
|
|
790
|
-
if (answer === null)
|
|
791
|
-
return { outcome: 'refused', code: 'durable-write', retryable: true };
|
|
792
|
-
if (answer.fenced !== null) {
|
|
793
|
-
throw runtimeError('unknown-room', `Unknown Room ${answer.fenced}`);
|
|
794
|
-
}
|
|
795
|
-
if (store.fences.has(locator))
|
|
796
|
-
throw runtimeError('unknown-room', `Unknown Room ${locator}`);
|
|
797
|
-
preparedAfter(runtime, locator, answer);
|
|
798
|
-
const shownNow = store.changed(changeSet(locator, {
|
|
799
|
-
documents: answer.changed[locator] ?? [],
|
|
800
|
-
queue: true,
|
|
801
|
-
drafts: [...left, ...arrived.map(({ path }) => path)],
|
|
802
|
-
}));
|
|
803
|
-
await Promise.all([shownNow, queuedSave(runtime, locator)]);
|
|
804
|
-
const identifier = join?.batch.id ??
|
|
805
|
-
built?.batch.id ??
|
|
806
|
-
[...requeue.withdrawn].map(([origin, replaced]) => replaced?.id ?? origin).at(-1);
|
|
807
|
-
return { outcome: 'saved', identifier };
|
|
808
|
-
}
|
|
809
233
|
async function queuedFacts(store, locator, path, localId) {
|
|
810
234
|
for (const batch of (await store.replica(locator))?.queue ?? []) {
|
|
811
235
|
const change = batch.changes[queuedFile(batch, path, localId)];
|
|
@@ -822,7 +246,7 @@ export function batchFile(store, batch) {
|
|
|
822
246
|
};
|
|
823
247
|
}
|
|
824
248
|
async function transferBinary(runtime, request, transfer, answer) {
|
|
825
|
-
const { store, socket,
|
|
249
|
+
const { store, socket, presence } = runtime;
|
|
826
250
|
const { session } = socket;
|
|
827
251
|
if (socket.closed() || session.now().refusal !== null)
|
|
828
252
|
return UNCERTAIN;
|
|
@@ -838,25 +262,22 @@ async function transferBinary(runtime, request, transfer, answer) {
|
|
|
838
262
|
const { closed, authorization, refusal } = session.now();
|
|
839
263
|
if (closed || authorization === undefined || refusal !== null)
|
|
840
264
|
return UNCERTAIN;
|
|
841
|
-
const since =
|
|
842
|
-
journal.record(`transfer binary ${request.locator} ${request.path}`);
|
|
265
|
+
const since = presence.losses();
|
|
843
266
|
try {
|
|
844
267
|
const result = await transfer(authorization);
|
|
845
268
|
const settled = result;
|
|
846
|
-
|
|
847
|
-
if (store.fences.since(request.locator, since))
|
|
269
|
+
if (presence.lostSince(request.locator, since))
|
|
848
270
|
return failure({ kind: 'room-not-readable', locator: request.locator });
|
|
849
271
|
if (settled.kind === 'scoped-refusal') {
|
|
850
272
|
const room = await store.record(settled.locator);
|
|
851
273
|
if (room !== undefined)
|
|
852
|
-
await
|
|
274
|
+
await scoped(runtime, settled, room.locator, room.declaration);
|
|
853
275
|
}
|
|
854
276
|
if (settled.kind === 'revoked-authorization')
|
|
855
277
|
await refuse(runtime, 'revoked-authorization');
|
|
856
278
|
return answer(result);
|
|
857
279
|
}
|
|
858
280
|
catch {
|
|
859
|
-
journal.record('outcome binary transport-uncertainty');
|
|
860
281
|
return UNCERTAIN;
|
|
861
282
|
}
|
|
862
283
|
}
|
|
@@ -969,7 +390,7 @@ async function showBinaryPath(runtime, locator, path, revision) {
|
|
|
969
390
|
if (kept !== null)
|
|
970
391
|
return kept;
|
|
971
392
|
const mediaType = await heldMediaType(store, locator, path);
|
|
972
|
-
if (mediaType
|
|
393
|
+
if (mediaType !== undefined && isImageType(mediaType))
|
|
973
394
|
return fetchBinary(runtime, asked, name);
|
|
974
395
|
return linkOf(runtime, asked, revision === undefined ? mediaType : undefined);
|
|
975
396
|
}
|