@agentshouse/mdruntime 0.1.0-cd0f0e6405606 → 0.1.0-cd26b960b7262

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 (377) hide show
  1. package/README.md +19 -16
  2. package/dist/acceptance.d.ts +6 -0
  3. package/dist/acceptance.d.ts.map +1 -0
  4. package/dist/acceptance.js +385 -0
  5. package/dist/actions.d.ts +20 -0
  6. package/dist/actions.d.ts.map +1 -0
  7. package/dist/actions.js +251 -0
  8. package/dist/adapter.d.ts +143 -85
  9. package/dist/adapter.d.ts.map +1 -1
  10. package/dist/attached-model.d.ts +7 -10
  11. package/dist/attached-model.d.ts.map +1 -1
  12. package/dist/attached-model.js +40 -41
  13. package/dist/binary.d.ts +0 -5
  14. package/dist/binary.d.ts.map +1 -1
  15. package/dist/binary.js +1 -1
  16. package/dist/bootstrap.d.ts +30 -11
  17. package/dist/bootstrap.d.ts.map +1 -1
  18. package/dist/bootstrap.js +320 -111
  19. package/dist/cell.d.ts +6 -0
  20. package/dist/cell.d.ts.map +1 -0
  21. package/dist/cell.js +9 -0
  22. package/dist/chain.d.ts +12 -0
  23. package/dist/chain.d.ts.map +1 -0
  24. package/dist/chain.js +64 -0
  25. package/dist/change-set.d.ts +9 -16
  26. package/dist/change-set.d.ts.map +1 -1
  27. package/dist/change-set.js +11 -89
  28. package/dist/change.d.ts +32 -1
  29. package/dist/change.d.ts.map +1 -1
  30. package/dist/change.js +14 -3
  31. package/dist/chat-types.d.ts +5 -0
  32. package/dist/chat-types.d.ts.map +1 -0
  33. package/dist/chat-types.js +13 -0
  34. package/dist/client.d.ts +33 -12
  35. package/dist/client.d.ts.map +1 -1
  36. package/dist/client.js +87 -9
  37. package/dist/collection.d.ts +11 -22
  38. package/dist/collection.d.ts.map +1 -1
  39. package/dist/collection.js +39 -356
  40. package/dist/compile.d.ts +16 -0
  41. package/dist/compile.d.ts.map +1 -0
  42. package/dist/compile.js +711 -0
  43. package/dist/connection.d.ts +7 -137
  44. package/dist/connection.d.ts.map +1 -1
  45. package/dist/connection.js +101 -3611
  46. package/dist/context.d.ts +2 -0
  47. package/dist/context.d.ts.map +1 -0
  48. package/dist/{chat/context.js → context.js} +1 -4
  49. package/dist/conversation-live.d.ts +136 -0
  50. package/dist/conversation-live.d.ts.map +1 -0
  51. package/dist/conversation-live.js +248 -0
  52. package/dist/conversation-row.d.ts +33 -0
  53. package/dist/conversation-row.d.ts.map +1 -0
  54. package/dist/conversation-row.js +53 -0
  55. package/dist/conversation.d.ts +48 -44
  56. package/dist/conversation.d.ts.map +1 -1
  57. package/dist/conversation.js +892 -754
  58. package/dist/digest.d.ts +7 -0
  59. package/dist/digest.d.ts.map +1 -0
  60. package/dist/digest.js +113 -0
  61. package/dist/discovery.d.ts +10 -0
  62. package/dist/discovery.d.ts.map +1 -0
  63. package/dist/discovery.js +175 -0
  64. package/dist/durability.d.ts +16 -4
  65. package/dist/durability.d.ts.map +1 -1
  66. package/dist/durability.js +5 -0
  67. package/dist/entry.d.ts +5 -2
  68. package/dist/entry.d.ts.map +1 -1
  69. package/dist/entry.js +33 -29
  70. package/dist/expectation.d.ts +4 -2
  71. package/dist/expectation.d.ts.map +1 -1
  72. package/dist/expectation.js +33 -11
  73. package/dist/figures.d.ts +49 -0
  74. package/dist/figures.d.ts.map +1 -0
  75. package/dist/figures.js +197 -0
  76. package/dist/file.d.ts +2 -19
  77. package/dist/file.d.ts.map +1 -1
  78. package/dist/file.js +7 -122
  79. package/dist/holdings.d.ts +29 -0
  80. package/dist/holdings.d.ts.map +1 -0
  81. package/dist/holdings.js +95 -0
  82. package/dist/host.d.ts +4 -7
  83. package/dist/host.d.ts.map +1 -1
  84. package/dist/host.js +67 -39
  85. package/dist/identity.d.ts +0 -2
  86. package/dist/identity.d.ts.map +1 -1
  87. package/dist/identity.js +0 -6
  88. package/dist/index.d.ts +33 -13
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +12 -2
  91. package/dist/install.d.ts +56 -0
  92. package/dist/install.d.ts.map +1 -0
  93. package/dist/install.js +554 -0
  94. package/dist/job.d.ts +33 -0
  95. package/dist/job.d.ts.map +1 -0
  96. package/dist/job.js +54 -0
  97. package/dist/lifecycle.d.ts +7 -0
  98. package/dist/lifecycle.d.ts.map +1 -0
  99. package/dist/lifecycle.js +268 -0
  100. package/dist/listing.d.ts +53 -0
  101. package/dist/listing.d.ts.map +1 -0
  102. package/dist/listing.js +176 -0
  103. package/dist/live.d.ts +23 -0
  104. package/dist/live.d.ts.map +1 -0
  105. package/dist/live.js +163 -0
  106. package/dist/locks.d.ts +9 -0
  107. package/dist/locks.d.ts.map +1 -0
  108. package/dist/locks.js +61 -0
  109. package/dist/log.d.ts +1 -0
  110. package/dist/log.d.ts.map +1 -1
  111. package/dist/lost.d.ts +5 -0
  112. package/dist/lost.d.ts.map +1 -0
  113. package/dist/lost.js +61 -0
  114. package/dist/media.d.ts +8 -0
  115. package/dist/media.d.ts.map +1 -0
  116. package/dist/media.js +218 -0
  117. package/dist/memory.d.ts +14 -6
  118. package/dist/memory.d.ts.map +1 -1
  119. package/dist/memory.js +239 -106
  120. package/dist/messages.d.ts +52 -0
  121. package/dist/messages.d.ts.map +1 -0
  122. package/dist/messages.js +162 -0
  123. package/dist/model.d.ts +8 -10
  124. package/dist/model.d.ts.map +1 -1
  125. package/dist/model.js +6 -18
  126. package/dist/models.d.ts +14 -0
  127. package/dist/models.d.ts.map +1 -0
  128. package/dist/models.js +195 -0
  129. package/dist/name.d.ts +4 -0
  130. package/dist/name.d.ts.map +1 -0
  131. package/dist/name.js +122 -0
  132. package/dist/observation.d.ts +15 -2
  133. package/dist/observation.d.ts.map +1 -1
  134. package/dist/observation.js +98 -14
  135. package/dist/opened.d.ts +22 -0
  136. package/dist/opened.d.ts.map +1 -0
  137. package/dist/opened.js +107 -0
  138. package/dist/opening.d.ts +4 -0
  139. package/dist/opening.d.ts.map +1 -0
  140. package/dist/opening.js +57 -0
  141. package/dist/outcome.d.ts +44 -9
  142. package/dist/outcome.d.ts.map +1 -1
  143. package/dist/outcome.js +41 -2
  144. package/dist/overlay.d.ts +3 -6
  145. package/dist/overlay.d.ts.map +1 -1
  146. package/dist/overlay.js +46 -136
  147. package/dist/{chat/parts.d.ts → parts.d.ts} +12 -9
  148. package/dist/parts.d.ts.map +1 -0
  149. package/dist/{chat/parts.js → parts.js} +53 -46
  150. package/dist/paths.d.ts +2 -0
  151. package/dist/paths.d.ts.map +1 -1
  152. package/dist/paths.js +26 -0
  153. package/dist/preparation.d.ts +20 -0
  154. package/dist/preparation.d.ts.map +1 -0
  155. package/dist/preparation.js +226 -0
  156. package/dist/queries.d.ts +16 -0
  157. package/dist/queries.d.ts.map +1 -0
  158. package/dist/queries.js +272 -0
  159. package/dist/query.d.ts +21 -207
  160. package/dist/query.d.ts.map +1 -1
  161. package/dist/query.js +3 -493
  162. package/dist/{chat/questions.d.ts → questions.d.ts} +5 -2
  163. package/dist/questions.d.ts.map +1 -0
  164. package/dist/{chat/questions.js → questions.js} +13 -2
  165. package/dist/reach.d.ts +20 -9
  166. package/dist/reach.d.ts.map +1 -1
  167. package/dist/reach.js +278 -103
  168. package/dist/react.d.ts +14 -2
  169. package/dist/react.d.ts.map +1 -1
  170. package/dist/react.js +10 -1
  171. package/dist/{chat/read.d.ts → read.d.ts} +2 -0
  172. package/dist/read.d.ts.map +1 -0
  173. package/dist/{chat/read.js → read.js} +4 -0
  174. package/dist/readiness.d.ts +13 -17
  175. package/dist/readiness.d.ts.map +1 -1
  176. package/dist/readiness.js +2 -8
  177. package/dist/reads.d.ts +36 -10
  178. package/dist/reads.d.ts.map +1 -1
  179. package/dist/reads.js +157 -84
  180. package/dist/recovery.d.ts +3 -2
  181. package/dist/recovery.d.ts.map +1 -1
  182. package/dist/recovery.js +43 -8
  183. package/dist/refresh.d.ts +18 -15
  184. package/dist/refresh.d.ts.map +1 -1
  185. package/dist/refresh.js +143 -58
  186. package/dist/refusal.d.ts +5 -0
  187. package/dist/refusal.d.ts.map +1 -0
  188. package/dist/refusal.js +55 -0
  189. package/dist/replica.d.ts +56 -38
  190. package/dist/replica.d.ts.map +1 -1
  191. package/dist/replica.js +313 -197
  192. package/dist/room-draft.d.ts +25 -8
  193. package/dist/room-draft.d.ts.map +1 -1
  194. package/dist/room-draft.js +153 -65
  195. package/dist/room-drafts.d.ts +24 -0
  196. package/dist/room-drafts.d.ts.map +1 -0
  197. package/dist/room-drafts.js +217 -0
  198. package/dist/rows.d.ts +31 -31
  199. package/dist/rows.d.ts.map +1 -1
  200. package/dist/rows.js +59 -123
  201. package/dist/runtime.d.ts +43 -0
  202. package/dist/runtime.d.ts.map +1 -0
  203. package/dist/runtime.js +1 -0
  204. package/dist/save.d.ts +56 -15
  205. package/dist/save.d.ts.map +1 -1
  206. package/dist/save.js +407 -121
  207. package/dist/saves.d.ts +70 -0
  208. package/dist/saves.d.ts.map +1 -0
  209. package/dist/saves.js +1005 -0
  210. package/dist/search.d.ts +12 -95
  211. package/dist/search.d.ts.map +1 -1
  212. package/dist/search.js +10 -266
  213. package/dist/seed.d.ts +11 -2
  214. package/dist/seed.d.ts.map +1 -1
  215. package/dist/seed.js +71 -1
  216. package/dist/shared.d.ts +5 -9
  217. package/dist/shared.d.ts.map +1 -1
  218. package/dist/shared.js +66 -121
  219. package/dist/shown.d.ts +31 -0
  220. package/dist/shown.d.ts.map +1 -0
  221. package/dist/shown.js +292 -0
  222. package/dist/socket/admission.d.ts +7 -0
  223. package/dist/socket/admission.d.ts.map +1 -0
  224. package/dist/socket/admission.js +39 -0
  225. package/dist/socket/answers.d.ts +6 -0
  226. package/dist/socket/answers.d.ts.map +1 -0
  227. package/dist/socket/answers.js +20 -0
  228. package/dist/socket/lanes.d.ts +20 -0
  229. package/dist/socket/lanes.d.ts.map +1 -0
  230. package/dist/socket/lanes.js +77 -0
  231. package/dist/socket/reads.d.ts +10 -0
  232. package/dist/socket/reads.d.ts.map +1 -0
  233. package/dist/socket/reads.js +31 -0
  234. package/dist/socket/rooms.d.ts +44 -0
  235. package/dist/socket/rooms.d.ts.map +1 -0
  236. package/dist/socket/rooms.js +90 -0
  237. package/dist/socket/session.d.ts +38 -0
  238. package/dist/socket/session.d.ts.map +1 -0
  239. package/dist/socket/session.js +23 -0
  240. package/dist/socket.d.ts +70 -0
  241. package/dist/socket.d.ts.map +1 -0
  242. package/dist/socket.js +175 -0
  243. package/dist/source.d.ts +61 -0
  244. package/dist/source.d.ts.map +1 -0
  245. package/dist/source.js +82 -0
  246. package/dist/status.d.ts +4 -5
  247. package/dist/status.d.ts.map +1 -1
  248. package/dist/status.js +3 -7
  249. package/dist/store/fences.d.ts +16 -0
  250. package/dist/store/fences.d.ts.map +1 -0
  251. package/dist/store/fences.js +26 -0
  252. package/dist/store/installs.d.ts +30 -0
  253. package/dist/store/installs.d.ts.map +1 -0
  254. package/dist/store/installs.js +34 -0
  255. package/dist/store/readiness.d.ts +10 -0
  256. package/dist/store/readiness.d.ts.map +1 -0
  257. package/dist/store/readiness.js +52 -0
  258. package/dist/store/records.d.ts +212 -0
  259. package/dist/store/records.d.ts.map +1 -0
  260. package/dist/store/records.js +431 -0
  261. package/dist/store/room-held.d.ts +8 -0
  262. package/dist/store/room-held.d.ts.map +1 -0
  263. package/dist/store/room-held.js +17 -0
  264. package/dist/store/space.d.ts +11 -0
  265. package/dist/store/space.d.ts.map +1 -0
  266. package/dist/store/space.js +41 -0
  267. package/dist/store/watchers.d.ts +17 -0
  268. package/dist/store/watchers.d.ts.map +1 -0
  269. package/dist/store/watchers.js +56 -0
  270. package/dist/store/writes.d.ts +24 -0
  271. package/dist/store/writes.d.ts.map +1 -0
  272. package/dist/store/writes.js +125 -0
  273. package/dist/store.d.ts +106 -164
  274. package/dist/store.d.ts.map +1 -1
  275. package/dist/store.js +136 -752
  276. package/dist/stored.d.ts +156 -0
  277. package/dist/stored.d.ts.map +1 -0
  278. package/dist/stored.js +649 -0
  279. package/dist/surface.d.ts +88 -0
  280. package/dist/surface.d.ts.map +1 -0
  281. package/dist/surface.js +223 -0
  282. package/dist/sync.d.ts +19 -0
  283. package/dist/sync.d.ts.map +1 -0
  284. package/dist/sync.js +532 -0
  285. package/dist/tables.d.ts +416 -0
  286. package/dist/tables.d.ts.map +1 -0
  287. package/dist/tables.js +740 -0
  288. package/dist/tallies.d.ts +18 -0
  289. package/dist/tallies.d.ts.map +1 -0
  290. package/dist/tallies.js +129 -0
  291. package/dist/transfer.d.ts +0 -5
  292. package/dist/transfer.d.ts.map +1 -1
  293. package/dist/turn.d.ts +2 -2
  294. package/dist/turn.d.ts.map +1 -1
  295. package/dist/turn.js +34 -23
  296. package/dist/validation.d.ts +29 -0
  297. package/dist/validation.d.ts.map +1 -0
  298. package/dist/validation.js +104 -0
  299. package/dist/windows.d.ts +1 -16
  300. package/dist/windows.d.ts.map +1 -1
  301. package/dist/windows.js +2 -79
  302. package/dist/wire.d.ts +34 -6
  303. package/dist/wire.d.ts.map +1 -1
  304. package/dist/wire.js +12 -2
  305. package/package.json +18 -19
  306. package/AGENTS.md +0 -299
  307. package/dist/chat/acts.d.ts +0 -33
  308. package/dist/chat/acts.d.ts.map +0 -1
  309. package/dist/chat/acts.js +0 -36
  310. package/dist/chat/context.d.ts +0 -3
  311. package/dist/chat/context.d.ts.map +0 -1
  312. package/dist/chat/earlier.d.ts +0 -10
  313. package/dist/chat/earlier.d.ts.map +0 -1
  314. package/dist/chat/earlier.js +0 -54
  315. package/dist/chat/input.d.ts +0 -9
  316. package/dist/chat/input.d.ts.map +0 -1
  317. package/dist/chat/input.js +0 -99
  318. package/dist/chat/kit.d.ts +0 -61
  319. package/dist/chat/kit.d.ts.map +0 -1
  320. package/dist/chat/kit.js +0 -123
  321. package/dist/chat/lasting.d.ts +0 -6
  322. package/dist/chat/lasting.d.ts.map +0 -1
  323. package/dist/chat/lasting.js +0 -19
  324. package/dist/chat/list.d.ts +0 -27
  325. package/dist/chat/list.d.ts.map +0 -1
  326. package/dist/chat/list.js +0 -99
  327. package/dist/chat/markdown.d.ts +0 -21
  328. package/dist/chat/markdown.d.ts.map +0 -1
  329. package/dist/chat/markdown.js +0 -246
  330. package/dist/chat/message.d.ts +0 -6
  331. package/dist/chat/message.d.ts.map +0 -1
  332. package/dist/chat/message.js +0 -192
  333. package/dist/chat/messages.d.ts +0 -22
  334. package/dist/chat/messages.d.ts.map +0 -1
  335. package/dist/chat/messages.js +0 -171
  336. package/dist/chat/name.d.ts +0 -6
  337. package/dist/chat/name.d.ts.map +0 -1
  338. package/dist/chat/name.js +0 -37
  339. package/dist/chat/opened.d.ts +0 -11
  340. package/dist/chat/opened.d.ts.map +0 -1
  341. package/dist/chat/opened.js +0 -17
  342. package/dist/chat/overlay.d.ts +0 -31
  343. package/dist/chat/overlay.d.ts.map +0 -1
  344. package/dist/chat/overlay.js +0 -97
  345. package/dist/chat/parts.d.ts.map +0 -1
  346. package/dist/chat/pieces.d.ts +0 -62
  347. package/dist/chat/pieces.d.ts.map +0 -1
  348. package/dist/chat/pieces.js +0 -1110
  349. package/dist/chat/process.d.ts +0 -20
  350. package/dist/chat/process.d.ts.map +0 -1
  351. package/dist/chat/process.js +0 -32
  352. package/dist/chat/provider.d.ts +0 -63
  353. package/dist/chat/provider.d.ts.map +0 -1
  354. package/dist/chat/provider.js +0 -630
  355. package/dist/chat/question.d.ts +0 -9
  356. package/dist/chat/question.d.ts.map +0 -1
  357. package/dist/chat/question.js +0 -69
  358. package/dist/chat/questions.d.ts.map +0 -1
  359. package/dist/chat/read.d.ts.map +0 -1
  360. package/dist/chat/ready.d.ts +0 -34
  361. package/dist/chat/ready.d.ts.map +0 -1
  362. package/dist/chat/ready.js +0 -91
  363. package/dist/chat/steady.d.ts +0 -3
  364. package/dist/chat/steady.d.ts.map +0 -1
  365. package/dist/chat/steady.js +0 -26
  366. package/dist/chat/style.d.ts +0 -4
  367. package/dist/chat/style.d.ts.map +0 -1
  368. package/dist/chat/style.js +0 -302
  369. package/dist/chat.d.ts +0 -14
  370. package/dist/chat.d.ts.map +0 -1
  371. package/dist/chat.js +0 -8
  372. package/dist/fence.d.ts +0 -6
  373. package/dist/fence.d.ts.map +0 -1
  374. package/dist/fence.js +0 -10
  375. package/dist/read-side.d.ts +0 -27
  376. package/dist/read-side.d.ts.map +0 -1
  377. package/dist/read-side.js +0 -824
package/dist/save.js CHANGED
@@ -1,12 +1,13 @@
1
1
  import { definitionOf } from './model.js';
2
- import { apply, ATTACHMENT, links, parse, pointerSegments, references, relative, relativize, retarget, serialize, target, } from '@agentshouse/mdmodel';
3
- import { immutable, queuedFile, departure, } from './change.js';
4
- import { available, changedCleanly, collectionOf, documentsOf, governing, referring, validateCollection, } from './collection.js';
2
+ import { apply, ARRAY_INDEX, ATTACHMENT, links, parse, pointerSegments, references, relative, relativize, retarget, roomReferences, serialize, target, } from '@agentshouse/mdmodel';
3
+ import { immutable, leaving, queuedFile, departure, } from './change.js';
4
+ import { collectionOf, documentsOf, governing, } from './collection.js';
5
5
  import { changeBatchIdentity } from './identity.js';
6
- import { overlay } from './overlay.js';
7
- import { covered, normalize } from './paths.js';
8
- import { differences } from './shared.js';
9
- import { ROW_WEIGHT, stepped } from './turn.js';
6
+ import { overlaying } from './overlay.js';
7
+ import { ancestors, authoredAt, covered, normalize } from './paths.js';
8
+ import { entriesIn, sorting } from './shared.js';
9
+ import { paced, ROW_WEIGHT, stepped } from './turn.js';
10
+ import { validatedCollection } from './validation.js';
10
11
  function refuse(code, file, location) {
11
12
  return {
12
13
  kind: 'refused',
@@ -19,9 +20,6 @@ function refuse(code, file, location) {
19
20
  };
20
21
  }
21
22
  function repointed(document, definition, from, to, moves, settled) {
22
- const parsed = parse(document.content, from);
23
- if (!parsed.ok)
24
- return [];
25
23
  const changes = [];
26
24
  const names = new Set();
27
25
  for (const { pointer, value } of definition === undefined
@@ -44,7 +42,7 @@ function repointed(document, definition, from, to, moves, settled) {
44
42
  }
45
43
  if (changes.length === 0)
46
44
  return [];
47
- const applied = apply(parsed.document, changes);
45
+ const applied = apply(document, changes);
48
46
  if (applied.outcome !== 'applied')
49
47
  return [];
50
48
  const next = applied.document;
@@ -116,7 +114,16 @@ function relinks(path, moved, before, moves) {
116
114
  function partKey(change) {
117
115
  return change.kind === 'section' ? `section ${change.name}` : change.kind;
118
116
  }
119
- function rewrites(changes, visible) {
117
+ const NAMES_A_SKIP_TESTS = 64;
118
+ const ENCODED = /[%&\\]/u;
119
+ function movedNames(moves) {
120
+ const names = new Set([...moves.keys()].map((path) => path.slice(path.lastIndexOf('/') + 1)));
121
+ return names.size > NAMES_A_SKIP_TESTS ? null : [...names];
122
+ }
123
+ function cannotLink(text, names) {
124
+ return names !== null && !ENCODED.test(text) && !names.some((name) => text.includes(name));
125
+ }
126
+ function* rewrites(changes, visible) {
120
127
  const moves = new Map();
121
128
  const settled = new Set();
122
129
  const parts = new Map();
@@ -141,16 +148,20 @@ function rewrites(changes, visible) {
141
148
  }
142
149
  if (moves.size === 0)
143
150
  return { kind: 'changes', changes };
151
+ const names = movedNames(moves);
144
152
  const merged = [...changes];
145
153
  const added = [];
146
- for (const [path, document] of documentsOf(visible.collection)) {
154
+ for (const [path, document] of yield* documentsOf(visible.collection)) {
147
155
  if (settled.has(path))
148
156
  continue;
149
157
  const moved = moves.get(path) ?? path;
150
158
  const definition = definitionOf(governing(visible.collection.composition, path), document.type);
151
- added.push(...repointed(document, definition, path, moved, moves, settled));
159
+ const repointing = repointed(document, definition, path, moved, moves, settled);
160
+ added.push(...repointing);
152
161
  const own = parts.get(path) ?? [];
153
- const relinked = relinks(path, moved, edited(document, own.map((index) => merged[index])), moves);
162
+ const relinked = moved === path && own.length === 0 && cannotLink(document.content, names)
163
+ ? { kind: 'changes', changes: [] }
164
+ : relinks(path, moved, edited(document, own.map((index) => merged[index])), moves);
154
165
  if (relinked.kind === 'refused')
155
166
  return relinked;
156
167
  const keyed = new Map();
@@ -175,29 +186,97 @@ function rewrites(changes, visible) {
175
186
  else
176
187
  merged[index] = change;
177
188
  }
189
+ const text = visible.collection.text[path].length;
190
+ const rewritten = repointing.length > 0 || relinked.changes.length > 0;
191
+ yield ROW_WEIGHT + text + (rewritten ? text : 0);
178
192
  }
179
193
  return {
180
194
  kind: 'changes',
181
195
  changes: [...added, ...merged.filter((change) => change !== null)],
182
196
  };
183
197
  }
184
- function anticipated(changes, preconditions, rewritten, visible) {
198
+ function* anticipated(changes, preconditions, rewritten, visible) {
185
199
  const given = new Set(changes);
186
200
  const derived = rewritten.filter((change) => !given.has(change));
187
201
  return {
188
202
  changes: rewritten,
189
- preconditions: [...preconditionsOf(derived, visible, []), ...preconditions],
203
+ preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...preconditions],
190
204
  };
191
205
  }
192
- export function providerRewritten(batch, visible) {
193
- const rewritten = rewrites(batch.changes, visible);
206
+ export function* providerRewritten(batch, visible) {
207
+ const rewritten = yield* rewrites(batch.changes, visible);
194
208
  if (rewritten.kind === 'refused')
195
209
  return batch;
196
210
  return {
197
211
  ...batch,
198
- ...anticipated(batch.changes, batch.preconditions, rewritten.changes, visible),
212
+ ...(yield* anticipated(batch.changes, batch.preconditions, rewritten.changes, visible)),
199
213
  };
200
214
  }
215
+ function crossed(document, definition, path, to) {
216
+ const changes = [];
217
+ for (const link of links(document, path)) {
218
+ const written = to(link.target, link.href);
219
+ if (written !== undefined)
220
+ changes.push({ kind: 'link', href: link.href, to: written });
221
+ }
222
+ for (const { pointer, value } of definition === undefined
223
+ ? []
224
+ : references(document, definition)) {
225
+ const named = target(path, value);
226
+ const written = named === null ? undefined : to(named, value);
227
+ if (written !== undefined) {
228
+ changes.push({ kind: 'reference', pointer, value: retarget(value, written), prior: value });
229
+ }
230
+ }
231
+ return changes;
232
+ }
233
+ function rewritten(path, document, changes) {
234
+ if (changes.length === 0)
235
+ return [];
236
+ const applied = apply(document, changes);
237
+ if (applied.outcome !== 'applied')
238
+ return [];
239
+ const after = applied.document;
240
+ if (document.type === undefined)
241
+ return [{ kind: 'replace', path, content: serialize(after) }];
242
+ const names = new Set(changes.flatMap((change) => change.kind === 'reference' ? [pointerSegments(change.pointer)[0] ?? ''] : []));
243
+ return [
244
+ ...[...names].map((name) => ({ kind: 'field', path, name, value: after.fields[name] })),
245
+ ...bodies(path, document, after),
246
+ ];
247
+ }
248
+ function roomPathOf(handle, path) {
249
+ return `/rooms/${handle}/${path}`;
250
+ }
251
+ export function* leavingRewritten(batch, visible) {
252
+ const between = batch.between;
253
+ if (between === undefined)
254
+ return batch;
255
+ const moved = new Map(between.moves.map(({ path, to }) => [path, roomPathOf(between.destination.handle, to)]));
256
+ const derived = [];
257
+ for (const [path, document] of yield* documentsOf(visible.collection)) {
258
+ if (moved.has(path))
259
+ continue;
260
+ const definition = definitionOf(governing(visible.collection.composition, path), document.type);
261
+ derived.push(...rewritten(path, document, crossed(document, definition, path, (named) => moved.get(named))));
262
+ yield ROW_WEIGHT + visible.collection.text[path].length;
263
+ }
264
+ if (derived.length === 0)
265
+ return batch;
266
+ return {
267
+ ...batch,
268
+ changes: [...derived, ...batch.changes],
269
+ preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...batch.preconditions],
270
+ };
271
+ }
272
+ export function rewriting(batch, locator) {
273
+ return leaving(batch, locator) || batch.changes.some((change) => change.kind === 'move');
274
+ }
275
+ export function* effectiveIn(batch, locator, visible) {
276
+ return leaving(batch, locator)
277
+ ? yield* leavingRewritten(batch, visible)
278
+ : yield* providerRewritten(batch, visible);
279
+ }
201
280
  function* incoming(collection, removed, candidates) {
202
281
  const found = new Map();
203
282
  const note = (target, file) => {
@@ -208,13 +287,19 @@ function* incoming(collection, removed, candidates) {
208
287
  found.set(target, referrers);
209
288
  };
210
289
  const read = function* (paths) {
211
- for (const path of [...new Set(paths)].sort()) {
290
+ const seen = new Set();
291
+ for (const path of paths) {
292
+ if (seen.has(path))
293
+ continue;
294
+ seen.add(path);
212
295
  const document = collection.document(path);
213
296
  if (document !== undefined)
214
297
  yield [path, document];
215
298
  }
216
299
  };
217
- for (const [path, document] of candidates === null ? documentsOf(collection) : read(candidates)) {
300
+ for (const [path, document] of candidates === null
301
+ ? yield* documentsOf(collection)
302
+ : read(candidates)) {
218
303
  const definition = definitionOf(governing(collection.composition, path), document.type);
219
304
  for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
220
305
  if (to.includes(ATTACHMENT))
@@ -225,7 +310,7 @@ function* incoming(collection, removed, candidates) {
225
310
  }
226
311
  yield ROW_WEIGHT + collection.text[path].length;
227
312
  }
228
- const [first] = [...found.keys()].sort();
313
+ const [first] = yield* sorting(found.keys());
229
314
  if (first === undefined)
230
315
  return null;
231
316
  const every = new Set();
@@ -238,14 +323,16 @@ function* incoming(collection, removed, candidates) {
238
323
  code: 'referenced-missing-file',
239
324
  retryable: false,
240
325
  file: first,
241
- referrers: [...every].sort(),
326
+ referrers: yield* sorting(every),
242
327
  },
243
328
  };
244
329
  }
245
330
  function affected(change) {
246
331
  return change.kind === 'move' ? [change.path, change.to] : [change.path];
247
332
  }
248
- function guard(changes, input) {
333
+ const WRITE = ['write'];
334
+ const MOVE_OUT = ['write', 'erase'];
335
+ function guard(changes, input, removing) {
249
336
  const overridden = new Map();
250
337
  const existing = {
251
338
  has: (path) => overridden.get(path) ??
@@ -253,21 +340,26 @@ function guard(changes, input) {
253
340
  add: (path) => void overridden.set(path, true),
254
341
  delete: (path) => void overridden.set(path, false),
255
342
  };
343
+ const occupied = (path) => input.folders.has(path) ? path : (ancestors(path).find((one) => existing.has(one)) ?? null);
256
344
  for (const [index, change] of changes.entries()) {
257
345
  for (const path of affected(change)) {
258
346
  const normalized = normalize(path);
259
347
  if (normalized === null || normalized === '')
260
348
  return refuse('room-root', path);
261
- if (covered(input.providerOwnedPaths, normalized)) {
349
+ if (covered(input.providerOwnedPaths, normalized) ||
350
+ (input.authoredPaths !== undefined && !authoredAt(input.authoredPaths, normalized))) {
262
351
  return refuse('provider-owned-path', normalized);
263
352
  }
264
- const operation = change.kind === 'remove' ? 'delete' : 'write';
265
- if (!input.local && !covered(input.operations[operation], normalized))
266
- return refuse(`${operation}-grant`, normalized);
353
+ if (!input.pathRule(normalized))
354
+ return refuse('invalid-path', normalized);
355
+ for (const operation of change.kind === 'remove' ? removing : WRITE)
356
+ if (!input.local && !covered(input.operations[operation], normalized))
357
+ return refuse(`${operation}-grant`, normalized);
267
358
  }
268
- if (change.kind === 'create') {
269
- if (existing.has(change.path))
270
- return refuse('path-collision', change.path);
359
+ if (change.kind === 'create' || change.kind === 'arrival') {
360
+ const taken = existing.has(change.path) ? change.path : occupied(change.path);
361
+ if (taken !== null)
362
+ return refuse('path-collision', taken);
271
363
  existing.add(change.path);
272
364
  continue;
273
365
  }
@@ -277,6 +369,9 @@ function guard(changes, input) {
277
369
  input.bases.some((base) => base.kind === 'absent' && base.path === change.path))) {
278
370
  return refuse('path-collision', change.path);
279
371
  }
372
+ const taken = occupied(change.path);
373
+ if (taken !== null)
374
+ return refuse('path-collision', taken);
280
375
  if (!input.local &&
281
376
  existing.has(change.path) &&
282
377
  input.visible.revisions[departure(changes, index)] === undefined) {
@@ -288,8 +383,9 @@ function guard(changes, input) {
288
383
  if (change.kind === 'move') {
289
384
  if (!existing.has(change.path))
290
385
  return refuse('path-absent', change.path);
291
- if (existing.has(change.to))
292
- return refuse('path-collision', change.to);
386
+ const taken = existing.has(change.to) ? change.to : occupied(change.to);
387
+ if (taken !== null)
388
+ return refuse('path-collision', taken);
293
389
  if (unrevised(input, change.path)) {
294
390
  return refuse('unknown-revision', change.path);
295
391
  }
@@ -325,7 +421,7 @@ export function queuedDocuments(queue) {
325
421
  const paths = new Set();
326
422
  for (const batch of queue) {
327
423
  for (const change of batch.changes) {
328
- if (change.kind === 'binary')
424
+ if (change.kind === 'binary' || (change.kind === 'arrival' && !('content' in change)))
329
425
  continue;
330
426
  if (change.kind === 'remove')
331
427
  paths.delete(change.path);
@@ -354,7 +450,7 @@ function sameTarget(precondition, change) {
354
450
  return ((precondition.kind !== 'field' && precondition.kind !== 'section') ||
355
451
  ((change.kind === 'field' || change.kind === 'section') && precondition.name === change.name));
356
452
  }
357
- function preconditionsOf(changes, visible, bases) {
453
+ function* preconditionsOf(changes, visible, bases) {
358
454
  const preconditions = [];
359
455
  const seen = new Set();
360
456
  const add = (precondition) => {
@@ -365,6 +461,7 @@ function preconditionsOf(changes, visible, bases) {
365
461
  preconditions.push(precondition);
366
462
  };
367
463
  for (const change of changes) {
464
+ yield ROW_WEIGHT;
368
465
  if (change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble') {
369
466
  const based = bases.find((base) => sameTarget(base, change));
370
467
  if (based !== undefined) {
@@ -412,10 +509,13 @@ function preconditionsOf(changes, visible, bases) {
412
509
  function depth(path) {
413
510
  return path.split('/').length;
414
511
  }
415
- function exceeded(limits, changes, effective, resulting) {
512
+ function* exceeding(limits, changes, effective, resulting) {
416
513
  const written = effective.map((change) => (change.kind === 'move' ? change.to : change.path));
417
514
  const created = changes
418
- .filter((change) => change.kind === 'create' || change.kind === 'move' || change.kind === 'binary')
515
+ .filter((change) => change.kind === 'create' ||
516
+ change.kind === 'move' ||
517
+ change.kind === 'binary' ||
518
+ change.kind === 'arrival')
419
519
  .map((change) => (change.kind === 'move' ? change.to : change.path));
420
520
  for (const limit of limits) {
421
521
  const value = limit.value;
@@ -429,9 +529,13 @@ function exceeded(limits, changes, effective, resulting) {
429
529
  return refuse('provider-limit', undefined, limit.name);
430
530
  }
431
531
  if (limit.name === 'document-size') {
432
- const large = written.find((path) => new TextEncoder().encode(resulting.documents[path] ?? '').byteLength > value);
433
- if (large !== undefined)
434
- return refuse('provider-limit', large, limit.name);
532
+ for (const path of written) {
533
+ const text = resulting.documents[path] ?? '';
534
+ if (new TextEncoder().encode(text).byteLength > value) {
535
+ return refuse('provider-limit', path, limit.name);
536
+ }
537
+ yield ROW_WEIGHT + text.length;
538
+ }
435
539
  }
436
540
  if (limit.name === 'path-length') {
437
541
  const long = created.find((path) => path.length > value);
@@ -497,18 +601,6 @@ function relativeChanges(input) {
497
601
  return { ...change, fields, sections };
498
602
  });
499
603
  }
500
- function* unmodeledType(collection, composition) {
501
- if (available(composition))
502
- return false;
503
- for (const [path, document] of documentsOf(collection)) {
504
- if (document.type !== undefined &&
505
- document.type !== 'any' &&
506
- governing(composition, path).status === 'unavailable')
507
- return true;
508
- yield ROW_WEIGHT + collection.text[path].length;
509
- }
510
- return false;
511
- }
512
604
  function unqueueable(file) {
513
605
  return { kind: 'refused', refusal: { code: 'unknown-revision', retryable: false, file } };
514
606
  }
@@ -611,8 +703,8 @@ function folded(changes, change, appending, deferred) {
611
703
  changes.push(change);
612
704
  return 'appended';
613
705
  }
614
- function relinked(batch, move, before, after, appending) {
615
- const derived = rewrites([move], after);
706
+ function* relinked(batch, move, before, after, appending) {
707
+ const derived = yield* rewrites([move], after);
616
708
  if (derived.kind === 'refused')
617
709
  return derived;
618
710
  const changes = [...batch.changes];
@@ -639,6 +731,7 @@ function relinked(batch, move, before, after, appending) {
639
731
  if (!changed)
640
732
  return { kind: 'relinked', batch };
641
733
  const held = new Set(batch.preconditions.map(identity));
734
+ const added = yield* preconditionsOf(appended, before, []);
642
735
  return {
643
736
  kind: 'relinked',
644
737
  batch: immutable({
@@ -647,7 +740,7 @@ function relinked(batch, move, before, after, appending) {
647
740
  changes,
648
741
  preconditions: [
649
742
  ...batch.preconditions,
650
- ...preconditionsOf(appended, before, []).filter((precondition) => !held.has(identity(precondition))),
743
+ ...added.filter((precondition) => !held.has(identity(precondition))),
651
744
  ],
652
745
  }),
653
746
  };
@@ -693,7 +786,7 @@ function withdrawnFile(batch, change, at) {
693
786
  ];
694
787
  return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
695
788
  }
696
- export function requeued(queue, changes, visible, changeable, state) {
789
+ export function* requeuing(queue, changes, visible, changeable, state) {
697
790
  const next = [...queue];
698
791
  const origins = new Map();
699
792
  const withdrawn = new Map();
@@ -716,19 +809,19 @@ export function requeued(queue, changes, visible, changeable, state) {
716
809
  return unqueueable(change.path);
717
810
  const following = next.slice(index);
718
811
  const rebuilt = [];
719
- let before = change.kind === 'move' ? state(next.slice(0, index)) : visible;
812
+ let before = change.kind === 'move' ? yield* state(next.slice(0, index)) : visible;
720
813
  for (const [offset, batch] of following.entries()) {
721
814
  let current = batch;
722
815
  if (change.kind === 'move') {
723
- const after = state(next.slice(0, index + offset + 1));
816
+ const after = yield* state(next.slice(0, index + offset + 1));
724
817
  const appending = offset === 0 && created(batch, queuedFile(batch, change.path, held.localId));
725
- const relinking = relinked(batch, change, before, after, appending);
818
+ const relinking = yield* relinked(batch, change, before, after, appending);
726
819
  if (relinking.kind === 'refused')
727
820
  return relinking;
728
821
  current = relinking.batch;
729
822
  before = after;
730
823
  }
731
- if (current !== batch && !open(batch))
824
+ if (current !== batch && (!open(batch) || batch.between !== undefined))
732
825
  return unqueueable(change.path);
733
826
  rebuilt.push(offset === 0
734
827
  ? withdrawnFile(current, change, queuedFile(current, change.path, held.localId))
@@ -798,60 +891,66 @@ export function joined(queue, owner, extra) {
798
891
  });
799
892
  return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
800
893
  }
801
- export function limitedIn(queue, batch, limits, state) {
894
+ export function* limiting(queue, batch, limits, state) {
802
895
  const at = queue.indexOf(batch);
803
- const limited = exceeded(limits, batch.changes, providerRewritten(batch, state(queue.slice(0, at))).changes, state(queue.slice(0, at + 1)));
804
- return limited?.kind === 'refused' ? limited.refusal : null;
896
+ const rewritten = yield* providerRewritten(batch, yield* state(queue.slice(0, at)));
897
+ const limited = yield* exceeding(limits, batch.changes, rewritten.changes, yield* state(queue.slice(0, at + 1)));
898
+ return limited?.refusal ?? null;
805
899
  }
806
- function* batching(input) {
807
- const changes = relativeChanges(input);
808
- const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
809
- if (structural !== undefined &&
810
- (yield* unmodeledType(input.visible.collection, input.composition)))
811
- return refuse('model-unavailable', structural.path);
812
- if (changes.length === 0)
813
- return refuse('no-change');
814
- const rewritten = rewrites(changes, input.visible);
815
- if (rewritten.kind === 'refused')
816
- return rewritten;
817
- const refusal = guard(changes, input);
818
- if (refusal !== null)
819
- return refusal;
820
- const preconditions = preconditionsOf(changes, input.visible, input.bases);
821
- const predicted = anticipated(changes, preconditions, rewritten.changes, input.visible);
822
- const resulting = overlay(input.visible, [
823
- { id: '', baseRoomVersion: input.baseRoomVersion, ...predicted },
824
- ]);
825
- const clean = input.clean !== undefined && available(input.composition) ? input.clean : null;
826
- const removed = new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path));
900
+ function typedText(content) {
901
+ if (content === undefined)
902
+ return false;
903
+ const parsed = parse(content);
904
+ return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
905
+ }
906
+ function touchedLibrary(resulting, touched, composition) {
907
+ const entries = touched
908
+ .flatMap((path) => {
909
+ const content = resulting.documents[path];
910
+ const binary = resulting.binaries[path] !== undefined;
911
+ return content === undefined && !binary ? [] : [{ path, content: content ?? null }];
912
+ })
913
+ .sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
914
+ const contents = new Map(entries.map(({ path, content }) => [path, content]));
915
+ const binaries = new Set(Array.from(entriesIn(resulting.binaries), ([path]) => path));
916
+ return validatedCollection(() => Promise.resolve({ entries, next: null }), async (paths) => {
917
+ const known = new Map();
918
+ const pace = paced();
919
+ for (const path of paths) {
920
+ const content = contents.get(path);
921
+ if (binaries.has(path))
922
+ known.set(path, { type: undefined, binary: true });
923
+ else if (content !== undefined && content !== null) {
924
+ const read = parse(content, path);
925
+ if (read.ok)
926
+ known.set(path, { type: read.document.type ?? 'any', binary: false });
927
+ await pace(content.length + ROW_WEIGHT);
928
+ }
929
+ }
930
+ return known;
931
+ }, composition);
932
+ }
933
+ async function judging(visible, resulting, composition, validate, removed, touched) {
827
934
  if (removed.size > 0) {
828
- const referrers = clean === null ? null : referring(clean, input.composition, removed);
829
- const referenced = yield* incoming(collectionOf(resulting.documents, input.composition), removed, clean === null || referrers === null
830
- ? null
831
- : [...referrers, ...(yield* differences(clean.text, resulting.documents))]);
935
+ const referenced = await stepped(incoming(collectionOf(resulting.documents, composition), removed, null));
832
936
  if (referenced !== null)
833
937
  return referenced;
834
938
  }
835
- const typed = (content) => {
836
- if (content === undefined)
837
- return false;
838
- const parsed = parse(content);
839
- return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
840
- };
841
- const touched = [...new Set(predicted.changes.flatMap(affected))];
842
- const typedPaths = touched.filter((path) => typed(input.visible.documents[path]) || typed(resulting.documents[path]));
843
- const needsModel = typedPaths.length > 0;
844
- const withoutModel = typedPaths.find((path) => governing(input.composition, path).status === 'unavailable');
939
+ const typedPaths = [];
940
+ const pace = paced();
941
+ for (const path of touched) {
942
+ const before = visible.documents[path];
943
+ const after = resulting.documents[path];
944
+ if (typedText(before) || typedText(after))
945
+ typedPaths.push(path);
946
+ await pace((before?.length ?? 0) + (after?.length ?? 0) + ROW_WEIGHT);
947
+ }
948
+ const withoutModel = typedPaths.find((path) => governing(composition, path).status === 'unavailable');
845
949
  if (withoutModel !== undefined)
846
950
  return refuse('model-unavailable', withoutModel);
847
- const verdict = clean !== null &&
848
- (yield* changedCleanly({ text: resulting.documents, binaries: resulting.binaries }, clean, input.composition))
849
- ? { verdict: 'accept' }
850
- : yield* validateCollection(needsModel
851
- ? resulting.documents
852
- : Object.fromEntries(touched.flatMap((path) => resulting.documents[path] === undefined
853
- ? []
854
- : [[path, resulting.documents[path]]])), Object.keys(resulting.binaries), input.composition);
951
+ const verdict = typedPaths.length > 0
952
+ ? await validate(resulting, touched)
953
+ : await touchedLibrary(resulting, touched, composition);
855
954
  if (verdict.verdict === 'refuse') {
856
955
  return {
857
956
  kind: 'refused',
@@ -864,12 +963,92 @@ function* batching(input) {
864
963
  },
865
964
  };
866
965
  }
867
- const limited = exceeded(input.providerLimits, changes, predicted.changes, resulting);
966
+ return {
967
+ kind: 'judged',
968
+ validated: typedPaths
969
+ .map((path) => governing(composition, path))
970
+ .find((model) => model.status !== 'unavailable'),
971
+ };
972
+ }
973
+ function placed(reference) {
974
+ const key = roomPathOf(reference.handle, reference.path);
975
+ if (reference.kind === 'link')
976
+ return `link ${key}`;
977
+ if (reference.kind === 'item')
978
+ return `section ${reference.section} ${key}`;
979
+ const pointer = reference.pointer
980
+ .split('/')
981
+ .map((segment) => (ARRAY_INDEX.test(segment) ? '-' : segment))
982
+ .join('/');
983
+ return `field ${pointer} ${key}`;
984
+ }
985
+ function roomReferencesIn(path, content, composition) {
986
+ if (content === undefined)
987
+ return [];
988
+ const parsed = parse(content, path);
989
+ if (!parsed.ok)
990
+ return [];
991
+ return roomReferences(parsed.document, definitionOf(governing(composition, path), parsed.document.type));
992
+ }
993
+ function* admittedRooms(changes, visible, resulting, composition) {
994
+ const from = new Map();
995
+ for (const [index, change] of changes.entries()) {
996
+ if (change.kind === 'move')
997
+ from.set(change.to, departure(changes, index));
998
+ }
999
+ for (const path of [...new Set(changes.flatMap(affected))].sort()) {
1000
+ const now = resulting.documents[path];
1001
+ if (now === undefined)
1002
+ continue;
1003
+ const held = new Set(roomReferencesIn(path, visible.documents[from.get(path) ?? path], composition).map(placed));
1004
+ for (const reference of roomReferencesIn(path, now, composition)) {
1005
+ if (reference.kind === 'link' || reference.crossRoom === true)
1006
+ continue;
1007
+ if (held.has(placed(reference)))
1008
+ continue;
1009
+ return refuse('unresolved-reference', path, reference.kind === 'field' ? reference.pointer : reference.section);
1010
+ }
1011
+ yield ROW_WEIGHT + now.length;
1012
+ }
1013
+ return null;
1014
+ }
1015
+ function validationOf(validated) {
1016
+ return validated === undefined
1017
+ ? {}
1018
+ : {
1019
+ validation: {
1020
+ identity: validated.snapshot.identity,
1021
+ version: validated.snapshot.version,
1022
+ source: validated.status,
1023
+ },
1024
+ };
1025
+ }
1026
+ async function batching(input) {
1027
+ const changes = relativeChanges(input);
1028
+ const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
1029
+ if (structural !== undefined && input.unmodeled) {
1030
+ return refuse('model-unavailable', structural.path);
1031
+ }
1032
+ if (changes.length === 0)
1033
+ return refuse('no-change');
1034
+ const rewritten = await stepped(rewrites(changes, input.visible));
1035
+ if (rewritten.kind === 'refused')
1036
+ return rewritten;
1037
+ const refusal = guard(changes, input, WRITE);
1038
+ if (refusal !== null)
1039
+ return refusal;
1040
+ const preconditions = await stepped(preconditionsOf(changes, input.visible, input.bases));
1041
+ const predicted = await stepped(anticipated(changes, preconditions, rewritten.changes, input.visible));
1042
+ const resulting = await stepped(overlaying(input.visible, [{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted }]));
1043
+ const judged = await judging(input.visible, resulting, input.composition, input.validate, new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path)), [...new Set(predicted.changes.flatMap(affected))]);
1044
+ if (judged.kind === 'refused')
1045
+ return judged;
1046
+ const crossing = await stepped(admittedRooms(changes, input.visible, resulting, input.composition));
1047
+ if (crossing !== null)
1048
+ return crossing;
1049
+ const limited = await stepped(exceeding(input.providerLimits, changes, predicted.changes, resulting));
868
1050
  if (limited !== null)
869
1051
  return limited;
870
- const validated = typedPaths
871
- .map((path) => governing(input.composition, path))
872
- .find((model) => model.status !== 'unavailable');
873
1052
  return {
874
1053
  kind: 'batch',
875
1054
  batch: immutable({
@@ -878,18 +1057,125 @@ function* batching(input) {
878
1057
  changes,
879
1058
  preconditions,
880
1059
  ...(input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy }),
881
- ...(validated === undefined
882
- ? {}
883
- : {
884
- validation: {
885
- identity: validated.snapshot.identity,
886
- version: validated.snapshot.version,
887
- source: validated.status,
888
- },
889
- }),
1060
+ ...validationOf(judged.validated),
890
1061
  }),
891
1062
  };
892
1063
  }
1064
+ export const MOVE_DOCUMENTS = 'move-documents';
1065
+ function heldAt(visible, path) {
1066
+ return visible.documents[path] !== undefined || visible.binaries[path] !== undefined;
1067
+ }
1068
+ function arrivingText(path, to, source, moved) {
1069
+ const content = source.visible.documents[path];
1070
+ const document = source.visible.collection.document(path);
1071
+ if (document === undefined)
1072
+ return content;
1073
+ const definition = definitionOf(governing(source.composition, path), document.type);
1074
+ const changes = crossed(document, definition, path, (named, href) => {
1075
+ const at = moved.get(named);
1076
+ if (at !== undefined)
1077
+ return target(to, href) === at ? undefined : relative(to, at);
1078
+ return heldAt(source.visible, named) ? roomPathOf(source.handle, named) : undefined;
1079
+ });
1080
+ if (changes.length === 0)
1081
+ return content;
1082
+ const applied = apply(document, changes);
1083
+ return applied.outcome === 'applied' ? serialize(applied.document) : content;
1084
+ }
1085
+ function arrivalOf({ path, to }, source, moved) {
1086
+ const { documents, revisions, fileIds, created, changes, binaries } = source.visible;
1087
+ const kept = {
1088
+ ...(fileIds[path] === undefined ? {} : { fileId: fileIds[path] }),
1089
+ ...(created[path] === undefined ? {} : { created: created[path] }),
1090
+ ...(changes[path] === undefined ? {} : { change: changes[path] }),
1091
+ };
1092
+ const heads = {
1093
+ ...(revisions[path] === undefined ? {} : { revision: revisions[path] }),
1094
+ ...kept,
1095
+ };
1096
+ const binary = binaries[path];
1097
+ if (binary?.state === 'accepted') {
1098
+ const { byteLength, mediaType, sha256 } = binary;
1099
+ return {
1100
+ kind: 'arrival',
1101
+ path: to,
1102
+ binary: { byteLength, mediaType, sha256 },
1103
+ ...heads,
1104
+ };
1105
+ }
1106
+ const content = arrivingText(path, to, source, moved);
1107
+ return {
1108
+ kind: 'arrival',
1109
+ path: to,
1110
+ content,
1111
+ ...(content === documents[path] ? heads : kept),
1112
+ };
1113
+ }
1114
+ async function moving(input) {
1115
+ const { source, destination, moves, pathRule } = input;
1116
+ const removals = moves.map(({ path }) => ({ kind: 'remove', path }));
1117
+ const leaves = guard(removals, { ...source, local: input.local, bases: [], pathRule }, MOVE_OUT);
1118
+ if (leaves !== null)
1119
+ return leaves;
1120
+ const uploading = moves.find(({ path }) => source.visible.binaries[path]?.state === 'pending');
1121
+ if (uploading !== undefined)
1122
+ return refuse('unknown-revision', uploading.path);
1123
+ const moved = new Map(moves.map(({ path, to }) => [path, to]));
1124
+ const arrivals = moves.map((move) => arrivalOf(move, source, moved));
1125
+ const arrives = guard(arrivals, { ...destination, local: input.local, bases: [], pathRule }, WRITE);
1126
+ if (arrives !== null)
1127
+ return arrives;
1128
+ const bound = input.providerLimits.find((limit) => limit.name === MOVE_DOCUMENTS)?.value;
1129
+ if (typeof bound === 'number' && moves.length > bound) {
1130
+ return refuse('provider-limit', undefined, MOVE_DOCUMENTS);
1131
+ }
1132
+ if (source.unmodeled)
1133
+ return refuse('model-unavailable', moves[0].path);
1134
+ const id = changeBatchIdentity();
1135
+ const between = {
1136
+ source: { room: source.room, baseRoomVersion: source.baseRoomVersion },
1137
+ destination: {
1138
+ room: destination.room,
1139
+ baseRoomVersion: destination.baseRoomVersion,
1140
+ handle: destination.handle,
1141
+ },
1142
+ moves,
1143
+ };
1144
+ const written = input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy };
1145
+ const leavingBatch = {
1146
+ id,
1147
+ baseRoomVersion: source.baseRoomVersion,
1148
+ changes: removals,
1149
+ preconditions: moves.map(({ path }) => existence(source.visible, path)),
1150
+ ...written,
1151
+ between,
1152
+ };
1153
+ const effective = await stepped(leavingRewritten(leavingBatch, source.visible));
1154
+ const left = await stepped(overlaying(source.visible, [effective]));
1155
+ const sourceJudged = await judging(source.visible, left, source.composition, source.validate, new Set(moves.map(({ path }) => path)), [...new Set(effective.changes.map((change) => change.path))]);
1156
+ if (sourceJudged.kind === 'refused')
1157
+ return sourceJudged;
1158
+ const arrivingBatch = {
1159
+ id,
1160
+ baseRoomVersion: destination.baseRoomVersion,
1161
+ changes: arrivals,
1162
+ preconditions: moves.map(({ to }) => ({ kind: 'absent', path: to })),
1163
+ ...written,
1164
+ between,
1165
+ };
1166
+ const arrived = await stepped(overlaying(destination.visible, [arrivingBatch]));
1167
+ const destinationJudged = await judging(destination.visible, arrived, destination.composition, destination.validate, new Set(), moves.map(({ to }) => to));
1168
+ if (destinationJudged.kind === 'refused')
1169
+ return destinationJudged;
1170
+ const limited = (await stepped(exceeding(input.providerLimits, removals, effective.changes, left))) ??
1171
+ (await stepped(exceeding(input.providerLimits, arrivals, arrivals, arrived)));
1172
+ if (limited !== null)
1173
+ return limited;
1174
+ return { kind: 'moved', leaving: immutable(leavingBatch), arriving: immutable(arrivingBatch) };
1175
+ }
1176
+ export function buildMove(input) {
1177
+ return moving(input);
1178
+ }
893
1179
  export function buildBatch(input) {
894
- return stepped(batching(input));
1180
+ return batching(input);
895
1181
  }