@agentshouse/mdruntime 0.1.0-ce531b51ff088 → 0.1.0-ce6bc9239a3ee

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/README.md +25 -12
  2. package/dist/acceptance.d.ts +26 -0
  3. package/dist/acceptance.d.ts.map +1 -0
  4. package/dist/acceptance.js +384 -0
  5. package/dist/adapter.d.ts +218 -62
  6. package/dist/adapter.d.ts.map +1 -1
  7. package/dist/adapter.js +1 -9
  8. package/dist/attached-model.d.ts +8 -0
  9. package/dist/attached-model.d.ts.map +1 -0
  10. package/dist/attached-model.js +46 -0
  11. package/dist/attempt.d.ts +16 -0
  12. package/dist/attempt.d.ts.map +1 -0
  13. package/dist/attempt.js +70 -0
  14. package/dist/binary.d.ts +18 -0
  15. package/dist/binary.d.ts.map +1 -0
  16. package/dist/binary.js +8 -0
  17. package/dist/bootstrap.d.ts +40 -6
  18. package/dist/bootstrap.d.ts.map +1 -1
  19. package/dist/bootstrap.js +336 -51
  20. package/dist/chain.d.ts +12 -0
  21. package/dist/chain.d.ts.map +1 -0
  22. package/dist/chain.js +64 -0
  23. package/dist/change-set.d.ts +9 -14
  24. package/dist/change-set.d.ts.map +1 -1
  25. package/dist/change-set.js +11 -56
  26. package/dist/change.d.ts +46 -0
  27. package/dist/change.d.ts.map +1 -1
  28. package/dist/change.js +34 -0
  29. package/dist/chat-types.d.ts +3 -0
  30. package/dist/chat-types.d.ts.map +1 -0
  31. package/dist/chat-types.js +2 -0
  32. package/dist/client.d.ts +50 -18
  33. package/dist/client.d.ts.map +1 -1
  34. package/dist/client.js +162 -30
  35. package/dist/collection.d.ts +13 -14
  36. package/dist/collection.d.ts.map +1 -1
  37. package/dist/collection.js +52 -56
  38. package/dist/compile.d.ts +16 -0
  39. package/dist/compile.d.ts.map +1 -0
  40. package/dist/compile.js +717 -0
  41. package/dist/connection.d.ts +63 -48
  42. package/dist/connection.d.ts.map +1 -1
  43. package/dist/connection.js +2487 -1575
  44. package/dist/context.d.ts +2 -0
  45. package/dist/context.d.ts.map +1 -0
  46. package/dist/context.js +7 -0
  47. package/dist/conversation-row.d.ts +33 -0
  48. package/dist/conversation-row.d.ts.map +1 -0
  49. package/dist/conversation-row.js +47 -0
  50. package/dist/conversation.d.ts +72 -22
  51. package/dist/conversation.d.ts.map +1 -1
  52. package/dist/conversation.js +1008 -273
  53. package/dist/digest.d.ts +7 -0
  54. package/dist/digest.d.ts.map +1 -0
  55. package/dist/digest.js +113 -0
  56. package/dist/durability.d.ts +10 -1
  57. package/dist/durability.d.ts.map +1 -1
  58. package/dist/durability.js +5 -0
  59. package/dist/entry.d.ts +20 -0
  60. package/dist/entry.d.ts.map +1 -0
  61. package/dist/entry.js +161 -0
  62. package/dist/expectation.d.ts +21 -0
  63. package/dist/expectation.d.ts.map +1 -0
  64. package/dist/expectation.js +70 -0
  65. package/dist/failure.d.ts +13 -0
  66. package/dist/failure.d.ts.map +1 -0
  67. package/dist/failure.js +19 -0
  68. package/dist/figures.d.ts +48 -0
  69. package/dist/figures.d.ts.map +1 -0
  70. package/dist/figures.js +197 -0
  71. package/dist/file.d.ts +2 -19
  72. package/dist/file.d.ts.map +1 -1
  73. package/dist/file.js +7 -122
  74. package/dist/host.d.ts +18 -2
  75. package/dist/host.d.ts.map +1 -1
  76. package/dist/host.js +164 -50
  77. package/dist/identity.d.ts +3 -0
  78. package/dist/identity.d.ts.map +1 -1
  79. package/dist/identity.js +13 -1
  80. package/dist/index.d.ts +31 -10
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +13 -0
  83. package/dist/install.d.ts +86 -0
  84. package/dist/install.d.ts.map +1 -0
  85. package/dist/install.js +537 -0
  86. package/dist/job.d.ts +33 -0
  87. package/dist/job.d.ts.map +1 -0
  88. package/dist/job.js +54 -0
  89. package/dist/live.d.ts +23 -0
  90. package/dist/live.d.ts.map +1 -0
  91. package/dist/live.js +165 -0
  92. package/dist/log.d.ts +1 -0
  93. package/dist/log.d.ts.map +1 -1
  94. package/dist/media.d.ts +8 -0
  95. package/dist/media.d.ts.map +1 -0
  96. package/dist/media.js +218 -0
  97. package/dist/memory.d.ts +30 -9
  98. package/dist/memory.d.ts.map +1 -1
  99. package/dist/memory.js +337 -86
  100. package/dist/messages.d.ts +52 -0
  101. package/dist/messages.d.ts.map +1 -0
  102. package/dist/messages.js +168 -0
  103. package/dist/model.d.ts +17 -16
  104. package/dist/model.d.ts.map +1 -1
  105. package/dist/model.js +9 -9
  106. package/dist/name.d.ts +4 -0
  107. package/dist/name.d.ts.map +1 -0
  108. package/dist/name.js +122 -0
  109. package/dist/observation.d.ts +22 -2
  110. package/dist/observation.d.ts.map +1 -1
  111. package/dist/observation.js +133 -17
  112. package/dist/open.d.ts +2 -0
  113. package/dist/open.d.ts.map +1 -0
  114. package/dist/open.js +1 -0
  115. package/dist/opened.d.ts +22 -0
  116. package/dist/opened.d.ts.map +1 -0
  117. package/dist/opened.js +107 -0
  118. package/dist/outcome.d.ts +73 -5
  119. package/dist/outcome.d.ts.map +1 -1
  120. package/dist/outcome.js +84 -1
  121. package/dist/overlay.d.ts +16 -5
  122. package/dist/overlay.d.ts.map +1 -1
  123. package/dist/overlay.js +147 -180
  124. package/dist/parts.d.ts +59 -0
  125. package/dist/parts.d.ts.map +1 -0
  126. package/dist/parts.js +118 -0
  127. package/dist/paths.d.ts +1 -1
  128. package/dist/paths.d.ts.map +1 -1
  129. package/dist/paths.js +4 -16
  130. package/dist/query.d.ts +56 -180
  131. package/dist/query.d.ts.map +1 -1
  132. package/dist/query.js +3 -422
  133. package/dist/questions.d.ts +32 -0
  134. package/dist/questions.d.ts.map +1 -0
  135. package/dist/questions.js +118 -0
  136. package/dist/reach.d.ts +54 -0
  137. package/dist/reach.d.ts.map +1 -0
  138. package/dist/reach.js +494 -0
  139. package/dist/react.d.ts +46 -0
  140. package/dist/react.d.ts.map +1 -0
  141. package/dist/react.js +54 -0
  142. package/dist/read.d.ts +7 -0
  143. package/dist/read.d.ts.map +1 -0
  144. package/dist/read.js +17 -0
  145. package/dist/readiness.d.ts +50 -0
  146. package/dist/readiness.d.ts.map +1 -0
  147. package/dist/readiness.js +27 -0
  148. package/dist/reads.d.ts +36 -0
  149. package/dist/reads.d.ts.map +1 -0
  150. package/dist/reads.js +136 -0
  151. package/dist/recovery.d.ts +26 -0
  152. package/dist/recovery.d.ts.map +1 -0
  153. package/dist/recovery.js +232 -0
  154. package/dist/refresh.d.ts +21 -7
  155. package/dist/refresh.d.ts.map +1 -1
  156. package/dist/refresh.js +155 -55
  157. package/dist/replica.d.ts +58 -33
  158. package/dist/replica.d.ts.map +1 -1
  159. package/dist/replica.js +355 -158
  160. package/dist/room-draft.d.ts +62 -11
  161. package/dist/room-draft.d.ts.map +1 -1
  162. package/dist/room-draft.js +281 -83
  163. package/dist/room-drafts.d.ts +29 -0
  164. package/dist/room-drafts.d.ts.map +1 -0
  165. package/dist/room-drafts.js +216 -0
  166. package/dist/rows.d.ts +32 -34
  167. package/dist/rows.d.ts.map +1 -1
  168. package/dist/rows.js +65 -87
  169. package/dist/save.d.ts +74 -6
  170. package/dist/save.d.ts.map +1 -1
  171. package/dist/save.js +902 -182
  172. package/dist/saves.d.ts +96 -0
  173. package/dist/saves.d.ts.map +1 -0
  174. package/dist/saves.js +999 -0
  175. package/dist/search.d.ts +15 -54
  176. package/dist/search.d.ts.map +1 -1
  177. package/dist/search.js +10 -121
  178. package/dist/seed.d.ts +30 -0
  179. package/dist/seed.d.ts.map +1 -0
  180. package/dist/seed.js +141 -0
  181. package/dist/shared.d.ts +15 -0
  182. package/dist/shared.d.ts.map +1 -0
  183. package/dist/shared.js +421 -0
  184. package/dist/socket/admission.d.ts +7 -0
  185. package/dist/socket/admission.d.ts.map +1 -0
  186. package/dist/socket/admission.js +39 -0
  187. package/dist/socket/answers.d.ts +6 -0
  188. package/dist/socket/answers.d.ts.map +1 -0
  189. package/dist/socket/answers.js +20 -0
  190. package/dist/socket/reads.d.ts +10 -0
  191. package/dist/socket/reads.d.ts.map +1 -0
  192. package/dist/socket/reads.js +31 -0
  193. package/dist/socket.d.ts +43 -0
  194. package/dist/socket.d.ts.map +1 -0
  195. package/dist/socket.js +96 -0
  196. package/dist/source.d.ts +61 -0
  197. package/dist/source.d.ts.map +1 -0
  198. package/dist/source.js +81 -0
  199. package/dist/status.d.ts +7 -4
  200. package/dist/status.d.ts.map +1 -1
  201. package/dist/status.js +3 -15
  202. package/dist/store/fences.d.ts +16 -0
  203. package/dist/store/fences.d.ts.map +1 -0
  204. package/dist/store/fences.js +26 -0
  205. package/dist/store/installs.d.ts +30 -0
  206. package/dist/store/installs.d.ts.map +1 -0
  207. package/dist/store/installs.js +34 -0
  208. package/dist/store/records.d.ts +212 -0
  209. package/dist/store/records.d.ts.map +1 -0
  210. package/dist/store/records.js +408 -0
  211. package/dist/store/room-held.d.ts +8 -0
  212. package/dist/store/room-held.d.ts.map +1 -0
  213. package/dist/store/room-held.js +17 -0
  214. package/dist/store/watchers.d.ts +15 -0
  215. package/dist/store/watchers.d.ts.map +1 -0
  216. package/dist/store/watchers.js +36 -0
  217. package/dist/store/writes.d.ts +24 -0
  218. package/dist/store/writes.d.ts.map +1 -0
  219. package/dist/store/writes.js +125 -0
  220. package/dist/store.d.ts +77 -94
  221. package/dist/store.d.ts.map +1 -1
  222. package/dist/store.js +118 -389
  223. package/dist/stored.d.ts +156 -0
  224. package/dist/stored.d.ts.map +1 -0
  225. package/dist/stored.js +637 -0
  226. package/dist/tables.d.ts +407 -0
  227. package/dist/tables.d.ts.map +1 -0
  228. package/dist/tables.js +724 -0
  229. package/dist/tallies.d.ts +18 -0
  230. package/dist/tallies.d.ts.map +1 -0
  231. package/dist/tallies.js +129 -0
  232. package/dist/transfer.d.ts +0 -5
  233. package/dist/transfer.d.ts.map +1 -1
  234. package/dist/turn.d.ts +9 -0
  235. package/dist/turn.d.ts.map +1 -0
  236. package/dist/turn.js +75 -0
  237. package/dist/validation.d.ts +29 -0
  238. package/dist/validation.d.ts.map +1 -0
  239. package/dist/validation.js +104 -0
  240. package/dist/windows.d.ts +11 -0
  241. package/dist/windows.d.ts.map +1 -0
  242. package/dist/windows.js +16 -0
  243. package/dist/wire.d.ts +205 -19
  244. package/dist/wire.d.ts.map +1 -1
  245. package/dist/wire.js +95 -61
  246. package/package.json +33 -10
  247. package/AGENTS.md +0 -207
  248. package/dist/attachment.d.ts +0 -3
  249. package/dist/attachment.d.ts.map +0 -1
  250. package/dist/attachment.js +0 -8
  251. package/dist/read-side.d.ts +0 -14
  252. package/dist/read-side.d.ts.map +0 -1
  253. package/dist/read-side.js +0 -371
@@ -1,59 +1,68 @@
1
- import { apply, parse, serialize } from '@agentshouse/mdmodel';
1
+ import { apply, equalValue, frontmatterValue, parse, serialize, } from '@agentshouse/mdmodel';
2
+ import { runtimeError } from './failure.js';
3
+ import { sha256 } from './digest.js';
4
+ import { binaryLocalId } from './identity.js';
2
5
  import { normalize } from './paths.js';
3
- function malformed() {
4
- throw new Error('room-draft-malformed');
6
+ export function returnedBy(kind) {
7
+ return { kind, code: kind, fields: {}, retryable: false };
8
+ }
9
+ export function staleWrite(current) {
10
+ return { outcome: 'stale', code: 'stale', retryable: false, current };
11
+ }
12
+ function malformed(at, needs) {
13
+ throw runtimeError('room-draft-malformed', at === undefined ? 'room-draft-malformed' : `room-draft-malformed at ${at}: ${needs}`);
5
14
  }
6
15
  function plain(value) {
7
16
  return typeof value === 'object' && value !== null && !Array.isArray(value);
8
17
  }
9
- function frontmatterValue(value) {
10
- if (Array.isArray(value))
11
- return value.every(frontmatterValue);
12
- return (value === null ||
13
- typeof value === 'string' ||
14
- typeof value === 'boolean' ||
15
- (typeof value === 'number' && Number.isFinite(value)));
16
- }
17
- function text(value) {
18
- return typeof value === 'string' ? value : malformed();
18
+ function text(value, at) {
19
+ return typeof value === 'string' ? value : malformed(at, 'text');
19
20
  }
20
- function optionalText(value) {
21
- return value === undefined ? undefined : text(value);
21
+ function optionalText(value, at) {
22
+ return value === undefined ? undefined : text(value, at);
22
23
  }
23
- function markdownBase(value) {
24
- return value === null ? null : optionalText(value);
24
+ function markdownBase(value, at) {
25
+ return value === null ? null : optionalText(value, at);
25
26
  }
26
- function entries(value, each) {
27
+ function entries(value, at, each) {
27
28
  if (!plain(value))
28
- malformed();
29
+ malformed(at, 'an object of named values');
29
30
  const read = {};
30
31
  for (const [name, item] of Object.entries(value))
31
- read[name] = each(item);
32
+ read[name] = each(item, `${at}.${name}`);
32
33
  return read;
33
34
  }
34
- function fieldValue(value) {
35
+ function frontmatter(item, at) {
36
+ return frontmatterValue(item) ? item : malformed(at, 'a frontmatter value');
37
+ }
38
+ function fieldValue(value, at) {
35
39
  if (value === null)
36
40
  return null;
37
41
  if (!plain(value) || !Object.hasOwn(value, 'value') || !frontmatterValue(value.value)) {
38
- malformed();
42
+ malformed(at, '{ value } with a frontmatter value, or null where the document lacks it');
39
43
  }
40
44
  return { value: value.value };
41
45
  }
42
- function textPart(value) {
46
+ function textPart(value, at) {
43
47
  if (!plain(value))
44
- malformed();
45
- const base = optionalText(value.base);
46
- return { value: text(value.value), ...(base === undefined ? {} : { base }) };
48
+ malformed(at, '{ value, base }');
49
+ const base = optionalText(value.base, `${at}.base`);
50
+ return { value: text(value.value, `${at}.value`), ...(base === undefined ? {} : { base }) };
47
51
  }
48
- function creationOf(value) {
52
+ function creationOf(value, at) {
49
53
  if (!plain(value))
50
- malformed();
54
+ malformed(at, '{ type, fields, sections }, or { markdown }');
51
55
  if (Object.hasOwn(value, 'markdown'))
52
- return { markdown: text(value.markdown) };
53
- const fields = entries(value.fields, (item) => (frontmatterValue(item) ? item : malformed()));
54
- return { type: text(value.type), fields, sections: entries(value.sections, text) };
56
+ return { markdown: text(value.markdown, `${at}.markdown`) };
57
+ if (typeof value.type !== 'string')
58
+ malformed(`${at}.type`, 'a type name, or markdown in place of type, fields and sections');
59
+ return {
60
+ type: value.type,
61
+ fields: entries(value.fields, `${at}.fields`, frontmatter),
62
+ sections: entries(value.sections, `${at}.sections`, text),
63
+ };
55
64
  }
56
- const PART_NAMES = new Set([
65
+ const PART_NAMES = [
57
66
  'fields',
58
67
  'sections',
59
68
  'preamble',
@@ -61,41 +70,59 @@ const PART_NAMES = new Set([
61
70
  'creation',
62
71
  'move',
63
72
  'removal',
64
- ]);
65
- export function readParts(value) {
66
- if (!plain(value) || Object.keys(value).some((name) => !PART_NAMES.has(name)))
67
- malformed();
73
+ 'file',
74
+ ];
75
+ const NAMED_PARTS = `a part named ${PART_NAMES.slice(0, -1).join(', ')} or ${PART_NAMES.at(-1)}`;
76
+ export function readParts(value, path) {
77
+ if (!plain(value))
78
+ malformed(path, 'an object of parts');
79
+ const unknown = Object.keys(value).find((name) => !PART_NAMES.includes(name));
80
+ if (unknown !== undefined)
81
+ malformed(`${path} ${unknown}`, NAMED_PARTS);
68
82
  const parts = {};
69
83
  if (value.fields !== undefined) {
70
- parts.fields = entries(value.fields, (item) => {
84
+ parts.fields = entries(value.fields, `${path} fields`, (item, at) => {
71
85
  if (!plain(item) || !Object.hasOwn(item, 'value') || !frontmatterValue(item.value)) {
72
- malformed();
86
+ malformed(at, '{ value, base } with a frontmatter value');
73
87
  }
74
88
  return item.base === undefined
75
89
  ? { value: item.value }
76
- : { value: item.value, base: fieldValue(item.base) };
90
+ : { value: item.value, base: fieldValue(item.base, `${at}.base`) };
77
91
  });
78
92
  }
79
93
  if (value.sections !== undefined)
80
- parts.sections = entries(value.sections, textPart);
94
+ parts.sections = entries(value.sections, `${path} sections`, textPart);
81
95
  if (value.preamble !== undefined)
82
- parts.preamble = textPart(value.preamble);
96
+ parts.preamble = textPart(value.preamble, `${path} preamble`);
83
97
  if (value.markdown !== undefined)
84
- parts.markdown = textPart(value.markdown);
98
+ parts.markdown = textPart(value.markdown, `${path} markdown`);
85
99
  if (value.creation !== undefined)
86
- parts.creation = creationOf(value.creation);
100
+ parts.creation = creationOf(value.creation, `${path} creation`);
87
101
  if (value.move !== undefined) {
88
102
  if (!plain(value.move))
89
- malformed();
90
- const base = markdownBase(value.move.base);
91
- parts.move = { to: text(value.move.to), ...(base === undefined ? {} : { base }) };
103
+ malformed(`${path} move`, '{ to, room, base }');
104
+ const base = markdownBase(value.move.base, `${path} move.base`);
105
+ const room = optionalText(value.move.room, `${path} move.room`);
106
+ parts.move = {
107
+ to: text(value.move.to, `${path} move.to`),
108
+ ...(room === undefined ? {} : { room }),
109
+ ...(base === undefined ? {} : { base }),
110
+ };
92
111
  }
93
112
  if (value.removal !== undefined) {
94
113
  if (!plain(value.removal))
95
- malformed();
96
- const base = markdownBase(value.removal.base);
114
+ malformed(`${path} removal`, '{ base }');
115
+ const base = markdownBase(value.removal.base, `${path} removal.base`);
97
116
  parts.removal = base === undefined ? {} : { base };
98
117
  }
118
+ if (value.file !== undefined) {
119
+ if (!plain(value.file) || !(value.file.blob instanceof Blob))
120
+ malformed(`${path} file`, '{ blob, base } with a Blob');
121
+ if (Object.keys(value).length > 1)
122
+ throw runtimeError('room-draft-file-exclusive');
123
+ const base = markdownBase(value.file.base, `${path} file.base`);
124
+ parts.file = base === undefined ? { blob: value.file.blob } : { blob: value.file.blob, base };
125
+ }
99
126
  return parts;
100
127
  }
101
128
  function named(record) {
@@ -108,17 +135,78 @@ function kept(prior, base) {
108
135
  if (prior !== undefined)
109
136
  return prior.base;
110
137
  if (base === undefined)
111
- throw new Error('room-draft-base-missing');
138
+ throw runtimeError('room-draft-base-missing');
112
139
  return base;
113
140
  }
141
+ function movePart(current, move) {
142
+ return {
143
+ to: move.to,
144
+ ...(move.room === undefined ? {} : { room: move.room }),
145
+ base: kept(current, move.base),
146
+ };
147
+ }
114
148
  function editsText(parts) {
115
149
  return (parts.preamble !== undefined ||
116
150
  named(parts.fields).length > 0 ||
117
151
  named(parts.sections).length > 0);
118
152
  }
119
- export function held(current, parts) {
153
+ function partNames(parts) {
154
+ const names = [];
155
+ for (const [name, part] of Object.entries(parts)) {
156
+ if (name === 'preamble' || name === 'markdown')
157
+ names.push(name);
158
+ else if ((name === 'fields' || name === 'sections') && plain(part))
159
+ names.push(...Object.keys(part).map((one) => `${name}/${one}`));
160
+ else
161
+ return null;
162
+ }
163
+ return names.sort().join('\u0000');
164
+ }
165
+ function withBaseOf(prior, part) {
166
+ return plain(part) && plain(prior) && Object.hasOwn(prior, 'base')
167
+ ? { ...part, base: prior.base }
168
+ : part;
169
+ }
170
+ export function gatheredParts(earlier, later) {
171
+ if (!plain(earlier) || !plain(later))
172
+ return null;
173
+ const named = partNames(earlier);
174
+ if (named === null || named !== partNames(later))
175
+ return null;
176
+ return Object.fromEntries(Object.entries(later).map(([name, part]) => [
177
+ name,
178
+ name === 'fields' || name === 'sections'
179
+ ? Object.fromEntries(Object.entries(part).map(([one, value]) => [
180
+ one,
181
+ withBaseOf(earlier[name][one], value),
182
+ ]))
183
+ : withBaseOf(earlier[name], part),
184
+ ]));
185
+ }
186
+ export function held(current, parts, file) {
120
187
  if (parts.markdown !== undefined && editsText(parts)) {
121
- throw new Error('room-draft-markdown-exclusive');
188
+ throw runtimeError('room-draft-markdown-exclusive');
189
+ }
190
+ if (file !== undefined) {
191
+ if (current !== undefined && current.file === undefined) {
192
+ throw runtimeError('room-draft-file-exclusive');
193
+ }
194
+ return { file: { ...file, base: current?.file === undefined ? file.base : current.file.base } };
195
+ }
196
+ if (current?.file !== undefined) {
197
+ if (editsText(parts) ||
198
+ parts.markdown !== undefined ||
199
+ parts.creation !== undefined ||
200
+ (parts.move === undefined && parts.removal === undefined)) {
201
+ throw runtimeError('room-draft-file-exclusive');
202
+ }
203
+ return {
204
+ file: current.file,
205
+ ...(parts.move === undefined ? {} : { move: movePart(current.move, parts.move) }),
206
+ ...(parts.removal === undefined
207
+ ? {}
208
+ : { removal: { base: kept(current.removal, parts.removal.base) } }),
209
+ };
122
210
  }
123
211
  const next = {};
124
212
  const fields = named(parts.fields);
@@ -149,24 +237,15 @@ export function held(current, parts) {
149
237
  }
150
238
  if (parts.creation !== undefined)
151
239
  next.creation = parts.creation;
152
- if (parts.move !== undefined) {
153
- next.move = { to: parts.move.to, base: kept(current?.move, parts.move.base) };
154
- }
240
+ if (parts.move !== undefined)
241
+ next.move = movePart(current?.move, parts.move);
155
242
  if (parts.removal !== undefined) {
156
243
  next.removal = { base: kept(current?.removal, parts.removal.base) };
157
244
  }
158
245
  if (Object.keys(next).length === 0)
159
- throw new Error('room-draft-empty');
246
+ throw runtimeError('room-draft-empty');
160
247
  return next;
161
248
  }
162
- export function equalValue(left, right) {
163
- if (left === right)
164
- return true;
165
- return (Array.isArray(left) &&
166
- Array.isArray(right) &&
167
- left.length === right.length &&
168
- left.every((item, index) => equalValue(item, right[index])));
169
- }
170
249
  function sameField(left, right) {
171
250
  if (left === null || right === null)
172
251
  return left === right;
@@ -174,7 +253,7 @@ function sameField(left, right) {
174
253
  }
175
254
  export function underneath(parts, path, visible) {
176
255
  const markdown = visible?.documents[path] ?? null;
177
- const document = visible?.collection.documents[path];
256
+ const document = visible?.collection.document(path);
178
257
  const report = {};
179
258
  const fields = {};
180
259
  for (const [name, part] of named(parts.fields)) {
@@ -207,17 +286,41 @@ export function underneath(parts, path, visible) {
207
286
  report.move = markdown;
208
287
  if (parts.removal !== undefined && markdown !== parts.removal.base)
209
288
  report.removal = markdown;
289
+ if (parts.file !== undefined) {
290
+ const file = binaryBase(visible, path);
291
+ if (file !== parts.file.base || markdown !== null)
292
+ report.file = file;
293
+ }
210
294
  return report;
211
295
  }
212
- export function changedParts(path, report) {
296
+ const UNTYPED_MEDIA_TYPE = 'application/octet-stream';
297
+ export async function heldFile(blob, base) {
298
+ return {
299
+ localId: binaryLocalId(),
300
+ ...(blob instanceof File ? { name: blob.name } : {}),
301
+ byteLength: blob.size,
302
+ mediaType: blob.type === '' ? UNTYPED_MEDIA_TYPE : blob.type,
303
+ sha256: await sha256(blob),
304
+ base,
305
+ };
306
+ }
307
+ export function binaryBase(visible, path) {
308
+ const binary = visible?.binaries[path];
309
+ if (binary === undefined)
310
+ return null;
311
+ return visible?.revisions[path] ?? (binary.state === 'pending' ? binary.localId : null);
312
+ }
313
+ export function changedParts(path, parts, visible) {
314
+ const report = underneath(parts, path, visible);
315
+ const made = parts.file?.base === null;
213
316
  return [
214
- ...(report.creation === undefined ? [] : [{ path, part: 'creation' }]),
215
317
  ...named(report.fields).map(([name]) => ({ path, part: 'field', name })),
216
318
  ...named(report.sections).map(([name]) => ({ path, part: 'section', name })),
217
319
  ...(report.preamble === undefined ? [] : [{ path, part: 'preamble' }]),
218
320
  ...(report.markdown === undefined ? [] : [{ path, part: 'markdown' }]),
219
321
  ...(report.move === undefined ? [] : [{ path, part: 'move' }]),
220
322
  ...(report.removal === undefined ? [] : [{ path, part: 'removal' }]),
323
+ ...(report.file === undefined || made ? [] : [{ path, part: 'file' }]),
221
324
  ];
222
325
  }
223
326
  function joined(existing, returned) {
@@ -227,23 +330,22 @@ function joined(existing, returned) {
227
330
  merged[name] = part;
228
331
  return Object.keys(merged).length === 0 ? undefined : merged;
229
332
  }
230
- function ended(text) {
333
+ export function lineEnded(text) {
231
334
  return text === '' || text.endsWith('\n') ? text : `${text}\n`;
232
335
  }
233
336
  function frontmatterOf(fields) {
234
337
  const opened = parse('---\n---\n');
235
338
  if (!opened.ok)
236
339
  return malformed();
237
- let document = opened.document;
238
- for (const [name, part] of fields) {
239
- const next = apply(document, { kind: 'field', name, value: part.value, prior: undefined });
240
- if (next.outcome !== 'applied')
241
- return malformed();
242
- document = next.document;
243
- }
244
- return serialize(document);
340
+ const next = apply(opened.document, fields.map(([name, part]) => ({
341
+ kind: 'field',
342
+ name,
343
+ value: part.value,
344
+ prior: undefined,
345
+ })));
346
+ return next.outcome === 'applied' ? serialize(next.document) : malformed();
245
347
  }
246
- function concatenated(markdown, parts) {
348
+ export function partsAfter(markdown, parts) {
247
349
  const fields = named(parts.fields);
248
350
  const pieces = [
249
351
  ...(fields.length === 0 ? [] : [frontmatterOf(fields)]),
@@ -252,7 +354,7 @@ function concatenated(markdown, parts) {
252
354
  ];
253
355
  if (pieces.length === 0)
254
356
  return markdown;
255
- return `${ended(markdown)}\n${pieces.map(ended).join('')}`;
357
+ return `${lineEnded(markdown)}\n${pieces.map(lineEnded).join('')}`;
256
358
  }
257
359
  export function returnedInto(existing, returned) {
258
360
  if (existing === undefined)
@@ -274,20 +376,57 @@ export function returnedInto(existing, returned) {
274
376
  delete next.fields;
275
377
  delete next.sections;
276
378
  delete next.preamble;
277
- const value = concatenated(whole.value, existing.markdown === undefined ? existing : returned);
379
+ const value = partsAfter(whole.value, existing.markdown === undefined ? existing : returned);
278
380
  next.markdown = { value, base: whole.base };
279
381
  }
280
382
  const creation = existing.creation ?? returned.creation;
281
383
  const move = existing.move ?? returned.move;
282
384
  const removal = existing.removal ?? returned.removal;
385
+ const file = existing.file ?? returned.file;
283
386
  if (creation !== undefined)
284
387
  next.creation = creation;
285
388
  if (move !== undefined)
286
389
  next.move = move;
287
390
  if (removal !== undefined)
288
391
  next.removal = removal;
392
+ if (file !== undefined)
393
+ next.file = file;
289
394
  return next;
290
395
  }
396
+ export function moveTarget(move) {
397
+ const to = normalize(move.to);
398
+ return to === null || to === '' ? move.to : to;
399
+ }
400
+ export function readSavedParts(named) {
401
+ const read = named.map(({ path, parts }) => ({ path, parts: readParts(parts, path) }));
402
+ read.forEach(({ path }, at) => {
403
+ if (path === '' || normalize(path) !== path)
404
+ malformed(path, 'a path inside the Room, such as notes/a.md');
405
+ if (read.findIndex((one) => one.path === path) !== at)
406
+ malformed(path, 'each path once in one save');
407
+ });
408
+ return read;
409
+ }
410
+ export function partsChanges(named) {
411
+ const changes = [];
412
+ const preconditions = [];
413
+ for (const { path, parts } of readSavedParts(named)) {
414
+ const parted = held(undefined, parts);
415
+ const made = changesOf(path, parted);
416
+ changes.push(...made.changes);
417
+ preconditions.push(...made.bases);
418
+ for (const change of made.changes) {
419
+ if (change.kind === 'create')
420
+ preconditions.push({ kind: 'absent', path });
421
+ if (change.kind === 'move')
422
+ preconditions.push({ kind: 'absent', path: change.to });
423
+ }
424
+ const revision = parted.markdown?.base ?? parted.move?.base ?? parted.removal?.base;
425
+ if (typeof revision === 'string')
426
+ preconditions.push({ kind: 'revision', path, revision });
427
+ }
428
+ return { changes, preconditions };
429
+ }
291
430
  export function changesOf(path, parts) {
292
431
  const changes = [];
293
432
  const bases = [];
@@ -307,21 +446,80 @@ export function changesOf(path, parts) {
307
446
  bases.push({ kind: 'field', path, name, value: part.base?.value });
308
447
  }
309
448
  for (const [name, part] of named(parts.sections)) {
310
- changes.push({ kind: 'section', path, name, value: part.value });
449
+ changes.push({ kind: 'section', path, name, value: lineEnded(part.value) });
311
450
  bases.push({ kind: 'section', path, name, value: part.base });
312
451
  }
313
452
  if (parts.preamble !== undefined) {
314
- changes.push({ kind: 'preamble', path, value: parts.preamble.value });
453
+ changes.push({ kind: 'preamble', path, value: lineEnded(parts.preamble.value) });
315
454
  bases.push({ kind: 'preamble', path, value: parts.preamble.base });
316
455
  }
317
456
  if (parts.markdown !== undefined) {
318
457
  changes.push({ kind: 'replace', path, content: parts.markdown.value });
319
458
  }
320
- if (parts.move !== undefined) {
321
- const to = normalize(parts.move.to);
322
- changes.push({ kind: 'move', path, to: to === null || to === '' ? parts.move.to : to });
459
+ if (parts.file !== undefined && parts.removal === undefined) {
460
+ const { base, name: _name, ...file } = parts.file;
461
+ const at = parts.move === undefined ? path : moveTarget(parts.move);
462
+ if (base !== null && at !== path)
463
+ changes.push({ kind: 'move', path, to: at });
464
+ changes.push({ kind: 'binary', path: at, ...file });
465
+ if (base === null)
466
+ bases.push({ kind: 'absent', path: at });
467
+ return { changes, bases };
323
468
  }
469
+ if (parts.file?.base === null)
470
+ return { changes, bases };
471
+ if (parts.move !== undefined)
472
+ changes.push({ kind: 'move', path, to: moveTarget(parts.move) });
324
473
  if (parts.removal !== undefined)
325
474
  changes.push({ kind: 'remove', path });
326
475
  return { changes, bases };
327
476
  }
477
+ function rebased(before, after) {
478
+ return before === undefined ? after : { ...after, base: before.base };
479
+ }
480
+ function rebasedAll(before, after) {
481
+ return {
482
+ ...before,
483
+ ...Object.fromEntries(Object.entries(after).map(([name, part]) => [name, rebased(before?.[name], part)])),
484
+ };
485
+ }
486
+ export function joinedDraft(carried, saved) {
487
+ const draft = saved.find((item) => item.path === carried.path);
488
+ if (draft === undefined)
489
+ return carried;
490
+ const held = carried.parts;
491
+ const next = draft.parts;
492
+ return {
493
+ path: carried.path,
494
+ parts: {
495
+ ...held,
496
+ ...next,
497
+ ...(next.fields === undefined ? {} : { fields: rebasedAll(held.fields, next.fields) }),
498
+ ...(next.sections === undefined
499
+ ? {}
500
+ : { sections: rebasedAll(held.sections, next.sections) }),
501
+ ...(next.preamble === undefined ? {} : { preamble: rebased(held.preamble, next.preamble) }),
502
+ ...(next.markdown === undefined ? {} : { markdown: rebased(held.markdown, next.markdown) }),
503
+ },
504
+ };
505
+ }
506
+ export function withdrawnDrafts(drafts, changes) {
507
+ return drafts.flatMap((draft) => {
508
+ const file = draft.parts.file;
509
+ const at = draft.parts.move?.to ?? draft.path;
510
+ const change = changes.find((item) => (item.kind === 'move' || item.kind === 'remove') && item.path === at);
511
+ if (file === undefined || change === undefined)
512
+ return [draft];
513
+ if (change.kind === 'move') {
514
+ return [
515
+ {
516
+ path: draft.path,
517
+ parts: change.to === draft.path ? { file } : { file, move: { to: change.to, base: null } },
518
+ },
519
+ ];
520
+ }
521
+ return file.base === null
522
+ ? []
523
+ : [{ path: draft.path, parts: { file, removal: { base: null } } }];
524
+ });
525
+ }
@@ -0,0 +1,29 @@
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 { Store } from './store.js';
5
+ export type RoomDrafting = {
6
+ readonly store: Store;
7
+ closed(): boolean;
8
+ publishChangeSet(set: ChangeSet): Promise<void>;
9
+ statusShown(): Promise<void>;
10
+ };
11
+ export type RoomDraftSurface = {
12
+ list(): Promise<readonly RoomDraft[]>;
13
+ read(room: string, path: string): Promise<RoomDraft | null>;
14
+ write(room: string, path: string, revision: string | null, parts: unknown, next?: string): Promise<RoomDraftWrite>;
15
+ discard(room: string, path: string, revision: string): Promise<RoomDraftWrite>;
16
+ };
17
+ export type PresentationSurface = {
18
+ read(app: string): Promise<unknown>;
19
+ write(app: string, value: unknown): Promise<void>;
20
+ edit(app: string, members: Readonly<Record<string, unknown>>): Promise<void>;
21
+ };
22
+ export declare function visibleOf(store: Store, locator: string, paths: readonly string[]): Promise<VisibleLayer | undefined>;
23
+ export declare function publishRoomDrafts({ store, closed }: RoomDrafting, waitingOnly: boolean, touched?: ChangeSet['documents'] | null): Promise<void>;
24
+ export declare function emitPresentation({ store, closed }: RoomDrafting, app: string): Promise<void>;
25
+ export declare function roomDraftSurface(drafting: RoomDrafting): RoomDraftSurface;
26
+ export declare function presentationSurface(drafting: RoomDrafting): PresentationSurface;
27
+ export declare function watchRoomDrafts({ store, closed }: RoomDrafting, deliver: (value: unknown) => void): () => void;
28
+ export declare function watchPresentation({ store, closed }: RoomDrafting, app: string, deliver: (value: unknown) => void): () => void;
29
+ //# 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,KAAK,EAAE,MAAM,YAAY,CAAC;AAGxC,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,MAAM,IAAI,OAAO,CAAC;IAClB,gBAAgB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAEF,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,YAAY,EAC/B,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,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU5F;AA4DD,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,GAAG,gBAAgB,CAkDzE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,YAAY,GAAG,mBAAmB,CA0B/E;AAED,wBAAgB,eAAe,CAC7B,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,YAAY,EAC/B,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAChC,MAAM,IAAI,CAiBZ;AAED,wBAAgB,iBAAiB,CAC/B,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,YAAY,EAC/B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAChC,MAAM,IAAI,CASZ"}