@agentshouse/mdruntime 0.1.0-c069136dcb4e8 → 0.1.0-c076b00ebf42a
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 +3 -0
- package/dist/acceptance.d.ts.map +1 -0
- package/dist/acceptance.js +299 -0
- 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 +26 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +302 -0
- package/dist/adapter.d.ts +108 -85
- package/dist/adapter.d.ts.map +1 -1
- package/dist/attached-model.d.ts +3 -11
- package/dist/attached-model.d.ts.map +1 -1
- package/dist/attached-model.js +20 -60
- package/dist/bootstrap.d.ts +9 -5
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +204 -85
- package/dist/cell.d.ts +6 -0
- package/dist/cell.d.ts.map +1 -0
- package/dist/cell.js +9 -0
- package/dist/chain.d.ts +2 -1
- package/dist/chain.d.ts.map +1 -1
- 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 +1 -0
- package/dist/change.d.ts.map +1 -1
- package/dist/change.js +4 -2
- package/dist/chat-types.d.ts +3 -0
- package/dist/chat-types.d.ts.map +1 -0
- package/dist/chat-types.js +11 -0
- package/dist/client.d.ts +29 -22
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +36 -20
- package/dist/collection.d.ts +26 -4
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +19 -2
- package/dist/compile.d.ts +8 -6
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +286 -336
- package/dist/connection.d.ts +5 -142
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +110 -4970
- package/dist/context.d.ts +2 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +7 -0
- package/dist/conversation-live.d.ts +150 -0
- package/dist/conversation-live.d.ts.map +1 -0
- package/dist/conversation-live.js +259 -0
- package/dist/conversation-row.d.ts +35 -0
- package/dist/conversation-row.d.ts.map +1 -0
- package/dist/conversation-row.js +55 -0
- package/dist/conversation.d.ts +65 -44
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +999 -881
- 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 +10 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +150 -0
- package/dist/durability.d.ts +19 -0
- package/dist/durability.d.ts.map +1 -1
- package/dist/durability.js +4 -0
- package/dist/entry.d.ts +2 -2
- package/dist/entry.d.ts.map +1 -1
- package/dist/entry.js +18 -8
- package/dist/expectation.d.ts +26 -13
- package/dist/expectation.d.ts.map +1 -1
- package/dist/expectation.js +72 -19
- 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 +49 -0
- package/dist/figures.d.ts.map +1 -0
- package/dist/figures.js +199 -0
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +2 -1
- 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 +21 -7
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +77 -41
- 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 +34 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -5
- package/dist/install.d.ts +60 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +537 -0
- package/dist/lifecycle.d.ts +7 -0
- package/dist/lifecycle.d.ts.map +1 -0
- package/dist/lifecycle.js +278 -0
- package/dist/listing.d.ts +52 -0
- package/dist/listing.d.ts.map +1 -0
- package/dist/listing.js +152 -0
- package/dist/live.d.ts +9 -6
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +118 -36
- package/dist/locks.d.ts +7 -0
- package/dist/locks.d.ts.map +1 -0
- package/dist/locks.js +32 -0
- package/dist/lost.d.ts +5 -0
- package/dist/lost.d.ts.map +1 -0
- package/dist/lost.js +49 -0
- package/dist/media.d.ts +1 -1
- package/dist/media.d.ts.map +1 -1
- package/dist/media.js +10 -13
- package/dist/memory.d.ts +15 -6
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +178 -89
- package/dist/messages.d.ts +35 -5
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +108 -36
- package/dist/model.d.ts +2 -10
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +0 -14
- package/dist/models.d.ts +10 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +153 -0
- package/dist/observation.d.ts +28 -13
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +185 -81
- 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 +4 -0
- package/dist/opening.d.ts.map +1 -0
- package/dist/opening.js +53 -0
- package/dist/outcome.d.ts +11 -1
- package/dist/outcome.d.ts.map +1 -1
- package/dist/outcome.js +26 -2
- package/dist/overlay.d.ts +6 -1
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +47 -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 +4 -0
- package/dist/parts.d.ts.map +1 -0
- package/dist/parts.js +13 -0
- 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 +41 -0
- package/dist/preparation.d.ts.map +1 -0
- package/dist/preparation.js +314 -0
- package/dist/presence.d.ts +58 -0
- package/dist/presence.d.ts.map +1 -0
- package/dist/presence.js +233 -0
- package/dist/queries.d.ts +16 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +308 -0
- package/dist/query.d.ts +14 -210
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +3 -505
- package/dist/questions.d.ts +29 -0
- package/dist/questions.d.ts.map +1 -0
- package/dist/questions.js +115 -0
- package/dist/queue.d.ts +40 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +450 -0
- package/dist/reach.d.ts +16 -15
- package/dist/reach.d.ts.map +1 -1
- package/dist/reach.js +248 -147
- package/dist/react.d.ts +17 -3
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +33 -1
- package/dist/read.d.ts +7 -0
- package/dist/read.d.ts.map +1 -0
- package/dist/read.js +17 -0
- package/dist/readiness.d.ts +9 -6
- package/dist/readiness.d.ts.map +1 -1
- package/dist/readiness.js +3 -5
- package/dist/reads.d.ts +39 -0
- package/dist/reads.d.ts.map +1 -0
- package/dist/reads.js +178 -0
- package/dist/recovery.d.ts +3 -1
- package/dist/recovery.d.ts.map +1 -1
- package/dist/recovery.js +38 -8
- package/dist/refresh.d.ts +2 -1
- package/dist/refresh.d.ts.map +1 -1
- package/dist/refresh.js +31 -3
- package/dist/refusal.d.ts +5 -0
- package/dist/refusal.d.ts.map +1 -0
- package/dist/refusal.js +55 -0
- package/dist/replica.d.ts +11 -16
- package/dist/replica.d.ts.map +1 -1
- package/dist/replica.js +64 -30
- 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 +7 -8
- package/dist/room-draft.d.ts.map +1 -1
- package/dist/room-draft.js +113 -111
- package/dist/room-drafts.d.ts +24 -0
- package/dist/room-drafts.d.ts.map +1 -0
- package/dist/room-drafts.js +217 -0
- package/dist/rows.d.ts +16 -22
- package/dist/rows.d.ts.map +1 -1
- package/dist/rows.js +49 -186
- package/dist/runtime.d.ts +34 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +1 -0
- package/dist/save.d.ts +42 -86
- package/dist/save.d.ts.map +1 -1
- package/dist/save.js +344 -174
- package/dist/saves.d.ts +32 -0
- package/dist/saves.d.ts.map +1 -0
- package/dist/saves.js +409 -0
- package/dist/search.d.ts +12 -63
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +9 -52
- package/dist/seed.d.ts +11 -2
- package/dist/seed.d.ts.map +1 -1
- package/dist/seed.js +71 -1
- 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 +33 -0
- package/dist/shown.d.ts.map +1 -0
- package/dist/shown.js +309 -0
- package/dist/socket/admission.d.ts +7 -0
- package/dist/socket/admission.d.ts.map +1 -0
- package/dist/socket/admission.js +39 -0
- package/dist/socket/answers.d.ts +6 -0
- package/dist/socket/answers.d.ts.map +1 -0
- package/dist/socket/answers.js +20 -0
- package/dist/socket/lanes.d.ts +28 -0
- package/dist/socket/lanes.d.ts.map +1 -0
- package/dist/socket/lanes.js +103 -0
- package/dist/socket/reads.d.ts +10 -0
- package/dist/socket/reads.d.ts.map +1 -0
- package/dist/socket/reads.js +31 -0
- package/dist/socket/session.d.ts +38 -0
- package/dist/socket/session.d.ts.map +1 -0
- package/dist/socket/session.js +23 -0
- package/dist/socket.d.ts +67 -0
- package/dist/socket.d.ts.map +1 -0
- package/dist/socket.js +164 -0
- package/dist/source.d.ts +54 -0
- package/dist/source.d.ts.map +1 -0
- package/dist/source.js +82 -0
- package/dist/status.d.ts +0 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +0 -6
- package/dist/store/installs.d.ts +30 -0
- package/dist/store/installs.d.ts.map +1 -0
- package/dist/store/installs.js +40 -0
- package/dist/store/readiness.d.ts +11 -0
- package/dist/store/readiness.d.ts.map +1 -0
- package/dist/store/readiness.js +68 -0
- package/dist/store/records.d.ts +217 -0
- package/dist/store/records.d.ts.map +1 -0
- package/dist/store/records.js +464 -0
- package/dist/store/room-held.d.ts +8 -0
- package/dist/store/room-held.d.ts.map +1 -0
- package/dist/store/room-held.js +17 -0
- 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/space.d.ts +11 -0
- package/dist/store/space.d.ts.map +1 -0
- package/dist/store/space.js +41 -0
- package/dist/store/watchers.d.ts +17 -0
- package/dist/store/watchers.d.ts.map +1 -0
- package/dist/store/watchers.js +56 -0
- package/dist/store/writes.d.ts +24 -0
- package/dist/store/writes.d.ts.map +1 -0
- package/dist/store/writes.js +125 -0
- package/dist/store.d.ts +113 -144
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +171 -364
- package/dist/stored.d.ts +72 -35
- package/dist/stored.d.ts.map +1 -1
- package/dist/stored.js +340 -197
- package/dist/surface.d.ts +89 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +222 -0
- package/dist/sync.d.ts +19 -0
- package/dist/sync.d.ts.map +1 -0
- package/dist/sync.js +535 -0
- package/dist/tables.d.ts +124 -30
- package/dist/tables.d.ts.map +1 -1
- package/dist/tables.js +242 -62
- package/dist/tallies.d.ts +18 -0
- package/dist/tallies.d.ts.map +1 -0
- package/dist/tallies.js +128 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +9 -12
- package/dist/windows.d.ts +1 -12
- package/dist/windows.d.ts.map +1 -1
- package/dist/windows.js +2 -75
- package/dist/wire.d.ts +58 -9
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js +21 -5
- 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/fence.d.ts +0 -6
- package/dist/fence.d.ts.map +0 -1
- package/dist/fence.js +0 -10
- package/dist/fulltext.d.ts +0 -27
- package/dist/fulltext.d.ts.map +0 -1
- package/dist/fulltext.js +0 -370
- package/dist/log.d.ts +0 -5
- package/dist/log.d.ts.map +0 -1
- package/dist/log.js +0 -14
- package/dist/words.d.ts +0 -19
- package/dist/words.d.ts.map +0 -1
- package/dist/words.js +0 -156
package/dist/room-draft.js
CHANGED
|
@@ -1,60 +1,70 @@
|
|
|
1
|
-
import { apply,
|
|
1
|
+
import { apply, create, documentValue, equalValue, parse, serialize, } from '@agentshouse/mdmodel';
|
|
2
|
+
import { governing } from './collection.js';
|
|
2
3
|
import { runtimeError } from './failure.js';
|
|
3
4
|
import { sha256 } from './digest.js';
|
|
5
|
+
import { declaredType } from './outcome.js';
|
|
4
6
|
import { binaryLocalId } from './identity.js';
|
|
5
7
|
import { normalize } from './paths.js';
|
|
6
|
-
export function returnedBy(
|
|
7
|
-
return { kind, code:
|
|
8
|
+
export function returnedBy() {
|
|
9
|
+
return { kind: 'withdrawn', code: 'withdrawn', fields: {}, retryable: false };
|
|
8
10
|
}
|
|
9
11
|
export function staleWrite(current) {
|
|
10
|
-
return { outcome: 'stale',
|
|
12
|
+
return { outcome: 'stale', current };
|
|
11
13
|
}
|
|
12
|
-
function malformed() {
|
|
13
|
-
throw runtimeError('room-draft-malformed');
|
|
14
|
+
function malformed(at, needs) {
|
|
15
|
+
throw runtimeError('room-draft-malformed', at === undefined ? 'room-draft-malformed' : `room-draft-malformed at ${at}: ${needs}`);
|
|
14
16
|
}
|
|
15
17
|
function plain(value) {
|
|
16
18
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
17
19
|
}
|
|
18
|
-
function text(value) {
|
|
19
|
-
return typeof value === 'string' ? value : malformed();
|
|
20
|
+
function text(value, at) {
|
|
21
|
+
return typeof value === 'string' ? value : malformed(at, 'text');
|
|
20
22
|
}
|
|
21
|
-
function optionalText(value) {
|
|
22
|
-
return value === undefined ? undefined : text(value);
|
|
23
|
+
function optionalText(value, at) {
|
|
24
|
+
return value === undefined ? undefined : text(value, at);
|
|
23
25
|
}
|
|
24
|
-
function markdownBase(value) {
|
|
25
|
-
return value === null ? null : optionalText(value);
|
|
26
|
+
function markdownBase(value, at) {
|
|
27
|
+
return value === null ? null : optionalText(value, at);
|
|
26
28
|
}
|
|
27
|
-
function entries(value, each) {
|
|
29
|
+
function entries(value, at, each) {
|
|
28
30
|
if (!plain(value))
|
|
29
|
-
malformed();
|
|
31
|
+
malformed(at, 'an object of named values');
|
|
30
32
|
const read = {};
|
|
31
33
|
for (const [name, item] of Object.entries(value))
|
|
32
|
-
read[name] = each(item);
|
|
34
|
+
read[name] = each(item, `${at}.${name}`);
|
|
33
35
|
return read;
|
|
34
36
|
}
|
|
35
|
-
function
|
|
37
|
+
function documentValueAt(item, at) {
|
|
38
|
+
return documentValue(item) ? item : malformed(at, 'a document value');
|
|
39
|
+
}
|
|
40
|
+
function fieldValue(value, at) {
|
|
36
41
|
if (value === null)
|
|
37
42
|
return null;
|
|
38
|
-
if (!plain(value) || !Object.hasOwn(value, 'value') || !
|
|
39
|
-
malformed();
|
|
43
|
+
if (!plain(value) || !Object.hasOwn(value, 'value') || !documentValue(value.value)) {
|
|
44
|
+
malformed(at, '{ value } with a document value, or null where the document lacks it');
|
|
40
45
|
}
|
|
41
46
|
return { value: value.value };
|
|
42
47
|
}
|
|
43
|
-
function textPart(value) {
|
|
48
|
+
function textPart(value, at) {
|
|
44
49
|
if (!plain(value))
|
|
45
|
-
malformed();
|
|
46
|
-
const base = optionalText(value.base);
|
|
47
|
-
return { value: text(value.value), ...(base === undefined ? {} : { base }) };
|
|
50
|
+
malformed(at, '{ value, base }');
|
|
51
|
+
const base = optionalText(value.base, `${at}.base`);
|
|
52
|
+
return { value: text(value.value, `${at}.value`), ...(base === undefined ? {} : { base }) };
|
|
48
53
|
}
|
|
49
|
-
function creationOf(value) {
|
|
54
|
+
function creationOf(value, at) {
|
|
50
55
|
if (!plain(value))
|
|
51
|
-
malformed();
|
|
56
|
+
malformed(at, '{ type, fields, sections }, or { markdown }');
|
|
52
57
|
if (Object.hasOwn(value, 'markdown'))
|
|
53
|
-
return { markdown: text(value.markdown) };
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
return { markdown: text(value.markdown, `${at}.markdown`) };
|
|
59
|
+
if (typeof value.type !== 'string')
|
|
60
|
+
malformed(`${at}.type`, 'a type name, or markdown in place of type, fields and sections');
|
|
61
|
+
return {
|
|
62
|
+
type: value.type,
|
|
63
|
+
fields: entries(value.fields, `${at}.fields`, documentValueAt),
|
|
64
|
+
sections: entries(value.sections, `${at}.sections`, text),
|
|
65
|
+
};
|
|
56
66
|
}
|
|
57
|
-
const PART_NAMES =
|
|
67
|
+
const PART_NAMES = [
|
|
58
68
|
'fields',
|
|
59
69
|
'sections',
|
|
60
70
|
'preamble',
|
|
@@ -63,52 +73,56 @@ const PART_NAMES = new Set([
|
|
|
63
73
|
'move',
|
|
64
74
|
'removal',
|
|
65
75
|
'file',
|
|
66
|
-
]
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
];
|
|
77
|
+
const NAMED_PARTS = `a part named ${PART_NAMES.slice(0, -1).join(', ')} or ${PART_NAMES.slice(-1).join('')}`;
|
|
78
|
+
export function readParts(value, path) {
|
|
79
|
+
if (!plain(value))
|
|
80
|
+
malformed(path, 'an object of parts');
|
|
81
|
+
const unknown = Object.keys(value).find((name) => !PART_NAMES.includes(name));
|
|
82
|
+
if (unknown !== undefined)
|
|
83
|
+
malformed(`${path} ${unknown}`, NAMED_PARTS);
|
|
70
84
|
const parts = {};
|
|
71
85
|
if (value.fields !== undefined) {
|
|
72
|
-
parts.fields = entries(value.fields, (item) => {
|
|
73
|
-
if (!plain(item) || !Object.hasOwn(item, 'value') || !
|
|
74
|
-
malformed();
|
|
86
|
+
parts.fields = entries(value.fields, `${path} fields`, (item, at) => {
|
|
87
|
+
if (!plain(item) || !Object.hasOwn(item, 'value') || !documentValue(item.value)) {
|
|
88
|
+
malformed(at, '{ value, base } with a document value');
|
|
75
89
|
}
|
|
76
90
|
return item.base === undefined
|
|
77
91
|
? { value: item.value }
|
|
78
|
-
: { value: item.value, base: fieldValue(item.base) };
|
|
92
|
+
: { value: item.value, base: fieldValue(item.base, `${at}.base`) };
|
|
79
93
|
});
|
|
80
94
|
}
|
|
81
95
|
if (value.sections !== undefined)
|
|
82
|
-
parts.sections = entries(value.sections, textPart);
|
|
96
|
+
parts.sections = entries(value.sections, `${path} sections`, textPart);
|
|
83
97
|
if (value.preamble !== undefined)
|
|
84
|
-
parts.preamble = textPart(value.preamble);
|
|
98
|
+
parts.preamble = textPart(value.preamble, `${path} preamble`);
|
|
85
99
|
if (value.markdown !== undefined)
|
|
86
|
-
parts.markdown = textPart(value.markdown);
|
|
100
|
+
parts.markdown = textPart(value.markdown, `${path} markdown`);
|
|
87
101
|
if (value.creation !== undefined)
|
|
88
|
-
parts.creation = creationOf(value.creation);
|
|
102
|
+
parts.creation = creationOf(value.creation, `${path} creation`);
|
|
89
103
|
if (value.move !== undefined) {
|
|
90
104
|
if (!plain(value.move))
|
|
91
|
-
malformed();
|
|
92
|
-
const base = markdownBase(value.move.base);
|
|
93
|
-
const room = optionalText(value.move.room);
|
|
105
|
+
malformed(`${path} move`, '{ to, room, base }');
|
|
106
|
+
const base = markdownBase(value.move.base, `${path} move.base`);
|
|
107
|
+
const room = optionalText(value.move.room, `${path} move.room`);
|
|
94
108
|
parts.move = {
|
|
95
|
-
to: text(value.move.to),
|
|
109
|
+
to: text(value.move.to, `${path} move.to`),
|
|
96
110
|
...(room === undefined ? {} : { room }),
|
|
97
111
|
...(base === undefined ? {} : { base }),
|
|
98
112
|
};
|
|
99
113
|
}
|
|
100
114
|
if (value.removal !== undefined) {
|
|
101
115
|
if (!plain(value.removal))
|
|
102
|
-
malformed();
|
|
103
|
-
const base = markdownBase(value.removal.base);
|
|
116
|
+
malformed(`${path} removal`, '{ base }');
|
|
117
|
+
const base = markdownBase(value.removal.base, `${path} removal.base`);
|
|
104
118
|
parts.removal = base === undefined ? {} : { base };
|
|
105
119
|
}
|
|
106
120
|
if (value.file !== undefined) {
|
|
107
121
|
if (!plain(value.file) || !(value.file.blob instanceof Blob))
|
|
108
|
-
malformed();
|
|
122
|
+
malformed(`${path} file`, '{ blob, base } with a Blob');
|
|
109
123
|
if (Object.keys(value).length > 1)
|
|
110
124
|
throw runtimeError('room-draft-file-exclusive');
|
|
111
|
-
const base = markdownBase(value.file.base);
|
|
125
|
+
const base = markdownBase(value.file.base, `${path} file.base`);
|
|
112
126
|
parts.file = base === undefined ? { blob: value.file.blob } : { blob: value.file.blob, base };
|
|
113
127
|
}
|
|
114
128
|
return parts;
|
|
@@ -243,8 +257,10 @@ export function underneath(parts, path, visible) {
|
|
|
243
257
|
const markdown = visible?.documents[path] ?? null;
|
|
244
258
|
const document = visible?.collection.document(path);
|
|
245
259
|
const report = {};
|
|
260
|
+
const unread = document?.type !== undefined &&
|
|
261
|
+
governing(visible.collection.composition, path).status === 'unavailable';
|
|
246
262
|
const fields = {};
|
|
247
|
-
for (const [name, part] of named(parts.fields)) {
|
|
263
|
+
for (const [name, part] of unread ? [] : named(parts.fields)) {
|
|
248
264
|
const current = document !== undefined && Object.hasOwn(document.fields, name)
|
|
249
265
|
? { value: document.fields[name] }
|
|
250
266
|
: null;
|
|
@@ -254,7 +270,7 @@ export function underneath(parts, path, visible) {
|
|
|
254
270
|
if (Object.keys(fields).length > 0)
|
|
255
271
|
report.fields = fields;
|
|
256
272
|
const sections = {};
|
|
257
|
-
for (const [name, part] of named(parts.sections)) {
|
|
273
|
+
for (const [name, part] of unread ? [] : named(parts.sections)) {
|
|
258
274
|
const current = document !== undefined && Object.hasOwn(document.sections, name)
|
|
259
275
|
? document.sections[name]
|
|
260
276
|
: null;
|
|
@@ -264,8 +280,9 @@ export function underneath(parts, path, visible) {
|
|
|
264
280
|
if (Object.keys(sections).length > 0)
|
|
265
281
|
report.sections = sections;
|
|
266
282
|
const preamble = document?.preamble ?? null;
|
|
267
|
-
if (parts.preamble !== undefined && preamble !== parts.preamble.base)
|
|
283
|
+
if (!unread && parts.preamble !== undefined && preamble !== parts.preamble.base) {
|
|
268
284
|
report.preamble = preamble;
|
|
285
|
+
}
|
|
269
286
|
if (parts.markdown !== undefined && markdown !== parts.markdown.base)
|
|
270
287
|
report.markdown = markdown;
|
|
271
288
|
if (parts.creation !== undefined && markdown !== null)
|
|
@@ -281,13 +298,12 @@ export function underneath(parts, path, visible) {
|
|
|
281
298
|
}
|
|
282
299
|
return report;
|
|
283
300
|
}
|
|
284
|
-
const UNTYPED_MEDIA_TYPE = 'application/octet-stream';
|
|
285
301
|
export async function heldFile(blob, base) {
|
|
286
302
|
return {
|
|
287
303
|
localId: binaryLocalId(),
|
|
288
304
|
...(blob instanceof File ? { name: blob.name } : {}),
|
|
289
305
|
byteLength: blob.size,
|
|
290
|
-
mediaType: blob
|
|
306
|
+
mediaType: declaredType(blob),
|
|
291
307
|
sha256: await sha256(blob),
|
|
292
308
|
base,
|
|
293
309
|
};
|
|
@@ -318,33 +334,45 @@ function joined(existing, returned) {
|
|
|
318
334
|
merged[name] = part;
|
|
319
335
|
return Object.keys(merged).length === 0 ? undefined : merged;
|
|
320
336
|
}
|
|
321
|
-
function
|
|
337
|
+
export function lineEnded(text) {
|
|
322
338
|
return text === '' || text.endsWith('\n') ? text : `${text}\n`;
|
|
323
339
|
}
|
|
324
|
-
function
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
value: part.value,
|
|
332
|
-
prior: undefined,
|
|
333
|
-
})));
|
|
334
|
-
return next.outcome === 'applied' ? serialize(next.document) : malformed();
|
|
340
|
+
function written(start, changes, definition) {
|
|
341
|
+
const next = apply(start, changes, definition);
|
|
342
|
+
return next.outcome === 'applied' ? next.document : malformed();
|
|
343
|
+
}
|
|
344
|
+
function empty(definition) {
|
|
345
|
+
const parsed = parse('', '', definition);
|
|
346
|
+
return parsed.ok ? parsed.document : malformed();
|
|
335
347
|
}
|
|
336
|
-
export function partsAfter(markdown, parts) {
|
|
348
|
+
export function partsAfter(markdown, parts, definitions) {
|
|
337
349
|
const fields = named(parts.fields);
|
|
350
|
+
const sections = named(parts.sections);
|
|
351
|
+
if (fields.length === 0 && sections.length === 0 && parts.preamble === undefined)
|
|
352
|
+
return markdown;
|
|
353
|
+
const whole = parse(markdown, '', definitions);
|
|
354
|
+
const type = whole.ok ? whole.document.type : undefined;
|
|
355
|
+
const definition = definitions?.(type);
|
|
356
|
+
if (type !== undefined && definition === undefined)
|
|
357
|
+
return null;
|
|
358
|
+
const valued = fields.length === 0
|
|
359
|
+
? empty(definition)
|
|
360
|
+
: written(create(type, definition), fields.map(([name, part]) => ({
|
|
361
|
+
kind: 'field',
|
|
362
|
+
name,
|
|
363
|
+
value: part.value,
|
|
364
|
+
prior: undefined,
|
|
365
|
+
})), definition);
|
|
366
|
+
const head = parts.preamble === undefined
|
|
367
|
+
? valued
|
|
368
|
+
: written(valued, [{ kind: 'preamble', value: lineEnded(parts.preamble.value), prior: valued.preamble }], definition);
|
|
338
369
|
const pieces = [
|
|
339
|
-
...(fields.length === 0 ? [] : [
|
|
340
|
-
...(
|
|
341
|
-
...named(parts.sections).map(([name, part]) => `## ${name}\n${part.value}`),
|
|
370
|
+
...(fields.length === 0 && parts.preamble === undefined ? [] : [serialize(head)]),
|
|
371
|
+
...sections.map(([name, part]) => serialize(written(empty(definition), [{ kind: 'section', name, value: lineEnded(part.value), prior: undefined }], definition))),
|
|
342
372
|
];
|
|
343
|
-
|
|
344
|
-
return markdown;
|
|
345
|
-
return `${ended(markdown)}\n${pieces.map(ended).join('')}`;
|
|
373
|
+
return `${lineEnded(markdown)}\n${pieces.join('')}`;
|
|
346
374
|
}
|
|
347
|
-
export function returnedInto(existing, returned) {
|
|
375
|
+
export function returnedInto(existing, returned, definitions) {
|
|
348
376
|
if (existing === undefined)
|
|
349
377
|
return returned;
|
|
350
378
|
const next = { ...existing };
|
|
@@ -364,7 +392,9 @@ export function returnedInto(existing, returned) {
|
|
|
364
392
|
delete next.fields;
|
|
365
393
|
delete next.sections;
|
|
366
394
|
delete next.preamble;
|
|
367
|
-
const value = partsAfter(whole.value, existing.markdown === undefined ? existing : returned);
|
|
395
|
+
const value = partsAfter(whole.value, existing.markdown === undefined ? existing : returned, definitions);
|
|
396
|
+
if (value === null)
|
|
397
|
+
return null;
|
|
368
398
|
next.markdown = { value, base: whole.base };
|
|
369
399
|
}
|
|
370
400
|
const creation = existing.creation ?? returned.creation;
|
|
@@ -386,12 +416,13 @@ export function moveTarget(move) {
|
|
|
386
416
|
return to === null || to === '' ? move.to : to;
|
|
387
417
|
}
|
|
388
418
|
export function readSavedParts(named) {
|
|
389
|
-
const read = named.map(({ path, parts }) => ({ path, parts: readParts(parts) }));
|
|
390
|
-
|
|
391
|
-
normalize(path) !== path
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
419
|
+
const read = named.map(({ path, parts }) => ({ path, parts: readParts(parts, path) }));
|
|
420
|
+
read.forEach(({ path }, at) => {
|
|
421
|
+
if (path === '' || normalize(path) !== path)
|
|
422
|
+
malformed(path, 'a path inside the Room, such as notes/a.md');
|
|
423
|
+
if (read.findIndex((one) => one.path === path) !== at)
|
|
424
|
+
malformed(path, 'each path once in one save');
|
|
425
|
+
});
|
|
395
426
|
return read;
|
|
396
427
|
}
|
|
397
428
|
export function partsChanges(named) {
|
|
@@ -433,11 +464,11 @@ export function changesOf(path, parts) {
|
|
|
433
464
|
bases.push({ kind: 'field', path, name, value: part.base?.value });
|
|
434
465
|
}
|
|
435
466
|
for (const [name, part] of named(parts.sections)) {
|
|
436
|
-
changes.push({ kind: 'section', path, name, value: part.value });
|
|
467
|
+
changes.push({ kind: 'section', path, name, value: lineEnded(part.value) });
|
|
437
468
|
bases.push({ kind: 'section', path, name, value: part.base });
|
|
438
469
|
}
|
|
439
470
|
if (parts.preamble !== undefined) {
|
|
440
|
-
changes.push({ kind: 'preamble', path, value: parts.preamble.value });
|
|
471
|
+
changes.push({ kind: 'preamble', path, value: lineEnded(parts.preamble.value) });
|
|
441
472
|
bases.push({ kind: 'preamble', path, value: parts.preamble.base });
|
|
442
473
|
}
|
|
443
474
|
if (parts.markdown !== undefined) {
|
|
@@ -461,35 +492,6 @@ export function changesOf(path, parts) {
|
|
|
461
492
|
changes.push({ kind: 'remove', path });
|
|
462
493
|
return { changes, bases };
|
|
463
494
|
}
|
|
464
|
-
function rebased(before, after) {
|
|
465
|
-
return before === undefined ? after : { ...after, base: before.base };
|
|
466
|
-
}
|
|
467
|
-
function rebasedAll(before, after) {
|
|
468
|
-
return {
|
|
469
|
-
...before,
|
|
470
|
-
...Object.fromEntries(Object.entries(after).map(([name, part]) => [name, rebased(before?.[name], part)])),
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
export function joinedDraft(carried, saved) {
|
|
474
|
-
const draft = saved.find((item) => item.path === carried.path);
|
|
475
|
-
if (draft === undefined)
|
|
476
|
-
return carried;
|
|
477
|
-
const held = carried.parts;
|
|
478
|
-
const next = draft.parts;
|
|
479
|
-
return {
|
|
480
|
-
path: carried.path,
|
|
481
|
-
parts: {
|
|
482
|
-
...held,
|
|
483
|
-
...next,
|
|
484
|
-
...(next.fields === undefined ? {} : { fields: rebasedAll(held.fields, next.fields) }),
|
|
485
|
-
...(next.sections === undefined
|
|
486
|
-
? {}
|
|
487
|
-
: { sections: rebasedAll(held.sections, next.sections) }),
|
|
488
|
-
...(next.preamble === undefined ? {} : { preamble: rebased(held.preamble, next.preamble) }),
|
|
489
|
-
...(next.markdown === undefined ? {} : { markdown: rebased(held.markdown, next.markdown) }),
|
|
490
|
-
},
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
495
|
export function withdrawnDrafts(drafts, changes) {
|
|
494
496
|
return drafts.flatMap((draft) => {
|
|
495
497
|
const file = draft.parts.file;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type ChangeSet } from './change-set.js';
|
|
2
|
+
import { type VisibleLayer } from './replica.js';
|
|
3
|
+
import { type RoomDraft, type RoomDraftWrite } from './room-draft.js';
|
|
4
|
+
import type { Runtime } from './runtime.js';
|
|
5
|
+
import type { Store } from './store.js';
|
|
6
|
+
export type RoomDraftSurface = {
|
|
7
|
+
list(): Promise<readonly RoomDraft[]>;
|
|
8
|
+
read(room: string, path: string): Promise<RoomDraft | null>;
|
|
9
|
+
write(room: string, path: string, revision: string | null, parts: unknown, next?: string): Promise<RoomDraftWrite>;
|
|
10
|
+
discard(room: string, path: string, revision: string): Promise<RoomDraftWrite>;
|
|
11
|
+
};
|
|
12
|
+
export type PresentationSurface = {
|
|
13
|
+
read(app: string): Promise<unknown>;
|
|
14
|
+
write(app: string, value: unknown): Promise<void>;
|
|
15
|
+
edit(app: string, members: Readonly<Record<string, unknown>>): Promise<void>;
|
|
16
|
+
};
|
|
17
|
+
export declare function visibleOf(store: Store, locator: string, paths: readonly string[]): Promise<VisibleLayer | undefined>;
|
|
18
|
+
export declare function publishRoomDrafts({ store, socket }: Runtime, waitingOnly: boolean, touched?: ChangeSet['documents'] | null): Promise<void>;
|
|
19
|
+
export declare function emitPresentation({ store, socket }: Runtime, app: string): Promise<void>;
|
|
20
|
+
export declare function roomDraftSurface(runtime: Runtime): RoomDraftSurface;
|
|
21
|
+
export declare function presentationSurface(runtime: Runtime): PresentationSurface;
|
|
22
|
+
export declare function watchRoomDrafts({ store, socket }: Runtime, deliver: (value: unknown) => void): () => void;
|
|
23
|
+
export declare function watchPresentation({ store, socket }: Runtime, app: string, deliver: (value: unknown) => void): () => void;
|
|
24
|
+
//# sourceMappingURL=room-drafts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"room-drafts.d.ts","sourceRoot":"","sources":["../src/room-drafts.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,KAAK,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5D,OAAO,EAAW,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAQL,KAAK,SAAS,EAEd,KAAK,cAAc,EACpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,IAAI,OAAO,CAAC,SAAS,SAAS,EAAE,CAAC,CAAC;IACtC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAC5D,KAAK,CACH,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,KAAK,EAAE,OAAO,EACd,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,cAAc,CAAC,CAAC;IAC3B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAChF,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9E,CAAC;AAMF,wBAAsB,SAAS,CAC7B,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAMnC;AA0CD,wBAAgB,iBAAiB,CAC/B,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,EAC1B,WAAW,EAAE,OAAO,EACpB,OAAO,GAAE,SAAS,CAAC,WAAW,CAAC,GAAG,IAAW,GAC5C,OAAO,CAAC,IAAI,CAAC,CAuBf;AAOD,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUvF;AAoDD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,CAkDnE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,mBAAmB,CA8BzE;AAED,wBAAgB,eAAe,CAC7B,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,EAC1B,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAChC,MAAM,IAAI,CAiBZ;AAED,wBAAgB,iBAAiB,CAC/B,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,EAC1B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAChC,MAAM,IAAI,CASZ"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { plain } from '@agentshouse/mdmodel';
|
|
2
|
+
import { changeSet } from './change-set.js';
|
|
3
|
+
import { collectionOf } from './collection.js';
|
|
4
|
+
import { runtimeError, uncaught } from './failure.js';
|
|
5
|
+
import { roomDraftRevision } from './identity.js';
|
|
6
|
+
import { normalize } from './paths.js';
|
|
7
|
+
import { slicing } from './replica.js';
|
|
8
|
+
import { binaryBase, gatheredParts, held, heldFile, readParts, staleWrite, underneath, } from './room-draft.js';
|
|
9
|
+
import { stepped } from './turn.js';
|
|
10
|
+
const ROOM_DRAFTS = 'room-drafts';
|
|
11
|
+
const presentationTopic = (app) => `presentation\u0000${app}`;
|
|
12
|
+
export async function visibleOf(store, locator, paths) {
|
|
13
|
+
const kept = await store.record(locator);
|
|
14
|
+
if (kept === undefined)
|
|
15
|
+
return undefined;
|
|
16
|
+
const local = await store.open();
|
|
17
|
+
const slice = await stepped(slicing(await local.entries(kept.key, 'visible', paths), paths));
|
|
18
|
+
return { ...slice, collection: collectionOf(slice.documents, store.composed(kept)) };
|
|
19
|
+
}
|
|
20
|
+
function roomDraftOf(record, visible) {
|
|
21
|
+
return structuredClone({
|
|
22
|
+
room: record.locator,
|
|
23
|
+
path: record.path,
|
|
24
|
+
revision: record.revision,
|
|
25
|
+
parts: record.parts,
|
|
26
|
+
changedUnderneath: underneath(record.parts, record.path, visible),
|
|
27
|
+
returned: record.returned,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
async function roomDraftsOf(store, records) {
|
|
31
|
+
const rooms = new Map();
|
|
32
|
+
for (const record of records) {
|
|
33
|
+
const paths = rooms.get(record.locator) ?? [];
|
|
34
|
+
paths.push(record.path);
|
|
35
|
+
rooms.set(record.locator, paths);
|
|
36
|
+
}
|
|
37
|
+
const layers = new Map();
|
|
38
|
+
for (const [locator, paths] of rooms) {
|
|
39
|
+
layers.set(locator, await visibleOf(store, locator, paths));
|
|
40
|
+
}
|
|
41
|
+
return records.map((record) => roomDraftOf(record, layers.get(record.locator)));
|
|
42
|
+
}
|
|
43
|
+
function inOrder(records) {
|
|
44
|
+
return [...records].sort((left, right) => {
|
|
45
|
+
if (left.locator !== right.locator)
|
|
46
|
+
return left.locator < right.locator ? -1 : 1;
|
|
47
|
+
if (left.path !== right.path)
|
|
48
|
+
return left.path < right.path ? -1 : 1;
|
|
49
|
+
return 0;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async function snapshotRoomDrafts(store) {
|
|
53
|
+
return roomDraftsOf(store, inOrder(await store.drafts()));
|
|
54
|
+
}
|
|
55
|
+
export function publishRoomDrafts({ store, socket }, waitingOnly, touched = null) {
|
|
56
|
+
const unwatched = () => socket.closed() || store.watching(ROOM_DRAFTS).length === 0;
|
|
57
|
+
if (unwatched())
|
|
58
|
+
return Promise.resolve();
|
|
59
|
+
return store.publication(async () => {
|
|
60
|
+
if (unwatched())
|
|
61
|
+
return;
|
|
62
|
+
const records = await store.drafts();
|
|
63
|
+
if (socket.closed())
|
|
64
|
+
return;
|
|
65
|
+
if (touched !== null &&
|
|
66
|
+
!records.some((record) => touched.some(({ locator, path }) => locator === record.locator && (path === null || path === record.path)))) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const value = await roomDraftsOf(store, inOrder(records));
|
|
70
|
+
if (socket.closed())
|
|
71
|
+
return;
|
|
72
|
+
const answered = store.watching(ROOM_DRAFTS, waitingOnly ? 'waiting' : 'all');
|
|
73
|
+
for (const watcher of answered)
|
|
74
|
+
watcher.answer(value);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async function presentationOf(store, app) {
|
|
78
|
+
const value = await (await store.open()).presentation(app);
|
|
79
|
+
return value === undefined ? null : structuredClone(value);
|
|
80
|
+
}
|
|
81
|
+
export function emitPresentation({ store, socket }, app) {
|
|
82
|
+
const topic = presentationTopic(app);
|
|
83
|
+
const listening = () => store.watching(topic, 'ready');
|
|
84
|
+
if (socket.closed() || listening().length === 0)
|
|
85
|
+
return Promise.resolve();
|
|
86
|
+
return store.publication(async () => {
|
|
87
|
+
if (socket.closed() || listening().length === 0)
|
|
88
|
+
return;
|
|
89
|
+
const value = await presentationOf(store, app);
|
|
90
|
+
if (socket.closed())
|
|
91
|
+
return;
|
|
92
|
+
for (const watcher of listening())
|
|
93
|
+
watcher.answer(value);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async function writtenRoomDraft({ store }, room, path, revision, input, next) {
|
|
97
|
+
const parts = readParts(input, path);
|
|
98
|
+
if (path === '' || normalize(path) !== path)
|
|
99
|
+
throw runtimeError('room-draft-malformed', `room-draft-malformed at ${path}: a path inside the Room, such as notes/a.md`);
|
|
100
|
+
const local = await store.open();
|
|
101
|
+
const current = await store.draft(room, path);
|
|
102
|
+
if ((current?.revision ?? null) !== revision) {
|
|
103
|
+
return staleWrite(current === undefined ? null : (await roomDraftsOf(store, [current]))[0]);
|
|
104
|
+
}
|
|
105
|
+
const file = parts.file === undefined
|
|
106
|
+
? undefined
|
|
107
|
+
: await heldFile(parts.file.blob, parts.file.base === undefined
|
|
108
|
+
? binaryBase(await visibleOf(store, room, [path]), path)
|
|
109
|
+
: parts.file.base);
|
|
110
|
+
const record = {
|
|
111
|
+
locator: room,
|
|
112
|
+
path,
|
|
113
|
+
revision: next,
|
|
114
|
+
parts: held(current?.parts, parts, file),
|
|
115
|
+
returned: current?.returned ?? null,
|
|
116
|
+
};
|
|
117
|
+
await local.keepRoomDraft(record, parts.file === undefined || file === undefined
|
|
118
|
+
? []
|
|
119
|
+
: [{ key: file.localId, blob: parts.file.blob }]);
|
|
120
|
+
await store.changed(changeSet(room, { drafts: [path] }));
|
|
121
|
+
return { outcome: 'written', roomDraft: (await roomDraftsOf(store, [record]))[0] };
|
|
122
|
+
}
|
|
123
|
+
async function keptPresentation({ store }, app, value) {
|
|
124
|
+
await (await store.open()).present({ app, value });
|
|
125
|
+
await store.changed(changeSet('', { presentation: app }));
|
|
126
|
+
}
|
|
127
|
+
export function roomDraftSurface(runtime) {
|
|
128
|
+
const { store } = runtime;
|
|
129
|
+
return {
|
|
130
|
+
list() {
|
|
131
|
+
return store
|
|
132
|
+
.exclusive(async () => {
|
|
133
|
+
await store.open();
|
|
134
|
+
return snapshotRoomDrafts(store);
|
|
135
|
+
})
|
|
136
|
+
.then((listed) => {
|
|
137
|
+
publishRoomDrafts(runtime, true);
|
|
138
|
+
return listed;
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
async read(room, path) {
|
|
142
|
+
const record = await store.draft(room, path);
|
|
143
|
+
return record === undefined ? null : (await roomDraftsOf(store, [record]))[0];
|
|
144
|
+
},
|
|
145
|
+
write(room, path, revision, input, next = roomDraftRevision()) {
|
|
146
|
+
if (typeof next !== 'string' || next === '')
|
|
147
|
+
return Promise.reject(runtimeError('room-draft-malformed'));
|
|
148
|
+
return store.gathered(`room-draft\u0000${room}\u0000${path}`, { revision, input, next }, (earlier, later) => {
|
|
149
|
+
if (later.revision !== earlier.next)
|
|
150
|
+
return null;
|
|
151
|
+
const parts = gatheredParts(earlier.input, later.input);
|
|
152
|
+
return parts === null
|
|
153
|
+
? null
|
|
154
|
+
: { revision: earlier.revision, input: parts, next: later.next };
|
|
155
|
+
}, (written) => writtenRoomDraft(runtime, room, path, written.revision, written.input, written.next));
|
|
156
|
+
},
|
|
157
|
+
discard(room, path, revision) {
|
|
158
|
+
return store.exclusive(async () => {
|
|
159
|
+
const local = await store.open();
|
|
160
|
+
const current = await store.draft(room, path);
|
|
161
|
+
if (current === undefined || current.revision !== revision) {
|
|
162
|
+
return staleWrite(current === undefined ? null : (await roomDraftsOf(store, [current]))[0]);
|
|
163
|
+
}
|
|
164
|
+
await local.discardRoomDraft(room, path);
|
|
165
|
+
await store.changed(changeSet(room, { drafts: [path] }));
|
|
166
|
+
return { outcome: 'written', roomDraft: null };
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
export function presentationSurface(runtime) {
|
|
172
|
+
const { store } = runtime;
|
|
173
|
+
return {
|
|
174
|
+
read(app) {
|
|
175
|
+
return store.exclusive(() => presentationOf(store, app));
|
|
176
|
+
},
|
|
177
|
+
write(app, value) {
|
|
178
|
+
return store.exclusive(() => keptPresentation(runtime, app, structuredClone(value)));
|
|
179
|
+
},
|
|
180
|
+
edit(app, members) {
|
|
181
|
+
return store.gathered(`presentation\u0000${app}`, structuredClone(members), (earlier, later) => ({ ...earlier, ...later }), async (edited) => {
|
|
182
|
+
const kept = await (await store.open()).presentation(app);
|
|
183
|
+
await keptPresentation(runtime, app, Object.fromEntries([
|
|
184
|
+
...Object.entries(plain(kept) ? kept : {}).filter(([member]) => !Object.hasOwn(edited, member)),
|
|
185
|
+
...Object.entries(edited).filter(([, value]) => value !== null),
|
|
186
|
+
]));
|
|
187
|
+
});
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
export function watchRoomDrafts({ store, socket }, deliver) {
|
|
192
|
+
const watcher = store.watch(ROOM_DRAFTS, deliver);
|
|
193
|
+
const unwatched = () => socket.closed() || !watcher.held();
|
|
194
|
+
void store
|
|
195
|
+
.exclusive(async () => {
|
|
196
|
+
await store.open();
|
|
197
|
+
})
|
|
198
|
+
.then(() => store.publication(async () => {
|
|
199
|
+
if (unwatched())
|
|
200
|
+
return;
|
|
201
|
+
const value = await snapshotRoomDrafts(store);
|
|
202
|
+
if (!unwatched())
|
|
203
|
+
watcher.answer(value);
|
|
204
|
+
}), uncaught);
|
|
205
|
+
return watcher.stop;
|
|
206
|
+
}
|
|
207
|
+
export function watchPresentation({ store, socket }, app, deliver) {
|
|
208
|
+
const watcher = store.watch(presentationTopic(app), deliver);
|
|
209
|
+
void store
|
|
210
|
+
.exclusive(async () => {
|
|
211
|
+
const value = await presentationOf(store, app);
|
|
212
|
+
if (!socket.closed() && watcher.held())
|
|
213
|
+
watcher.answer(value);
|
|
214
|
+
})
|
|
215
|
+
.catch(uncaught);
|
|
216
|
+
return watcher.stop;
|
|
217
|
+
}
|