@agentshouse/mdruntime 0.1.0-ce531b51ff088 → 0.1.0-ce5b3c1e6ea87

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 (322) hide show
  1. package/README.md +25 -12
  2. package/dist/acceptance.d.ts +6 -0
  3. package/dist/acceptance.d.ts.map +1 -0
  4. package/dist/acceptance.js +389 -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 +221 -62
  9. package/dist/adapter.d.ts.map +1 -1
  10. package/dist/adapter.js +1 -9
  11. package/dist/attached-model.d.ts +8 -0
  12. package/dist/attached-model.d.ts.map +1 -0
  13. package/dist/attached-model.js +46 -0
  14. package/dist/attempt.d.ts +16 -0
  15. package/dist/attempt.d.ts.map +1 -0
  16. package/dist/attempt.js +70 -0
  17. package/dist/binary.d.ts +18 -0
  18. package/dist/binary.d.ts.map +1 -0
  19. package/dist/binary.js +8 -0
  20. package/dist/bootstrap.d.ts +40 -6
  21. package/dist/bootstrap.d.ts.map +1 -1
  22. package/dist/bootstrap.js +336 -51
  23. package/dist/cell.d.ts +6 -0
  24. package/dist/cell.d.ts.map +1 -0
  25. package/dist/cell.js +9 -0
  26. package/dist/chain.d.ts +12 -0
  27. package/dist/chain.d.ts.map +1 -0
  28. package/dist/chain.js +64 -0
  29. package/dist/change-set.d.ts +9 -14
  30. package/dist/change-set.d.ts.map +1 -1
  31. package/dist/change-set.js +11 -56
  32. package/dist/change.d.ts +46 -0
  33. package/dist/change.d.ts.map +1 -1
  34. package/dist/change.js +34 -0
  35. package/dist/chat-types.d.ts +5 -0
  36. package/dist/chat-types.d.ts.map +1 -0
  37. package/dist/chat-types.js +13 -0
  38. package/dist/client.d.ts +55 -18
  39. package/dist/client.d.ts.map +1 -1
  40. package/dist/client.js +162 -30
  41. package/dist/collection.d.ts +13 -14
  42. package/dist/collection.d.ts.map +1 -1
  43. package/dist/collection.js +52 -56
  44. package/dist/compile.d.ts +16 -0
  45. package/dist/compile.d.ts.map +1 -0
  46. package/dist/compile.js +717 -0
  47. package/dist/connection.d.ts +8 -106
  48. package/dist/connection.d.ts.map +1 -1
  49. package/dist/connection.js +105 -2094
  50. package/dist/context.d.ts +2 -0
  51. package/dist/context.d.ts.map +1 -0
  52. package/dist/context.js +7 -0
  53. package/dist/conversation-live.d.ts +136 -0
  54. package/dist/conversation-live.d.ts.map +1 -0
  55. package/dist/conversation-live.js +248 -0
  56. package/dist/conversation-row.d.ts +33 -0
  57. package/dist/conversation-row.d.ts.map +1 -0
  58. package/dist/conversation-row.js +53 -0
  59. package/dist/conversation.d.ts +77 -40
  60. package/dist/conversation.d.ts.map +1 -1
  61. package/dist/conversation.js +1028 -438
  62. package/dist/digest.d.ts +7 -0
  63. package/dist/digest.d.ts.map +1 -0
  64. package/dist/digest.js +113 -0
  65. package/dist/discovery.d.ts +10 -0
  66. package/dist/discovery.d.ts.map +1 -0
  67. package/dist/discovery.js +175 -0
  68. package/dist/durability.d.ts +19 -1
  69. package/dist/durability.d.ts.map +1 -1
  70. package/dist/durability.js +5 -0
  71. package/dist/entry.d.ts +20 -0
  72. package/dist/entry.d.ts.map +1 -0
  73. package/dist/entry.js +161 -0
  74. package/dist/expectation.d.ts +21 -0
  75. package/dist/expectation.d.ts.map +1 -0
  76. package/dist/expectation.js +70 -0
  77. package/dist/failure.d.ts +13 -0
  78. package/dist/failure.d.ts.map +1 -0
  79. package/dist/failure.js +19 -0
  80. package/dist/figures.d.ts +48 -0
  81. package/dist/figures.d.ts.map +1 -0
  82. package/dist/figures.js +197 -0
  83. package/dist/file.d.ts +2 -19
  84. package/dist/file.d.ts.map +1 -1
  85. package/dist/file.js +7 -122
  86. package/dist/holdings.d.ts +29 -0
  87. package/dist/holdings.d.ts.map +1 -0
  88. package/dist/holdings.js +95 -0
  89. package/dist/host.d.ts +18 -2
  90. package/dist/host.d.ts.map +1 -1
  91. package/dist/host.js +165 -51
  92. package/dist/identity.d.ts +3 -0
  93. package/dist/identity.d.ts.map +1 -1
  94. package/dist/identity.js +13 -1
  95. package/dist/index.d.ts +36 -10
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +13 -0
  98. package/dist/install.d.ts +56 -0
  99. package/dist/install.d.ts.map +1 -0
  100. package/dist/install.js +560 -0
  101. package/dist/job.d.ts +33 -0
  102. package/dist/job.d.ts.map +1 -0
  103. package/dist/job.js +54 -0
  104. package/dist/lifecycle.d.ts +7 -0
  105. package/dist/lifecycle.d.ts.map +1 -0
  106. package/dist/lifecycle.js +268 -0
  107. package/dist/listing.d.ts +53 -0
  108. package/dist/listing.d.ts.map +1 -0
  109. package/dist/listing.js +175 -0
  110. package/dist/live.d.ts +23 -0
  111. package/dist/live.d.ts.map +1 -0
  112. package/dist/live.js +165 -0
  113. package/dist/locks.d.ts +9 -0
  114. package/dist/locks.d.ts.map +1 -0
  115. package/dist/locks.js +61 -0
  116. package/dist/log.d.ts +1 -0
  117. package/dist/log.d.ts.map +1 -1
  118. package/dist/lost.d.ts +5 -0
  119. package/dist/lost.d.ts.map +1 -0
  120. package/dist/lost.js +61 -0
  121. package/dist/media.d.ts +8 -0
  122. package/dist/media.d.ts.map +1 -0
  123. package/dist/media.js +218 -0
  124. package/dist/memory.d.ts +30 -9
  125. package/dist/memory.d.ts.map +1 -1
  126. package/dist/memory.js +337 -86
  127. package/dist/messages.d.ts +52 -0
  128. package/dist/messages.d.ts.map +1 -0
  129. package/dist/messages.js +168 -0
  130. package/dist/model.d.ts +17 -16
  131. package/dist/model.d.ts.map +1 -1
  132. package/dist/model.js +9 -9
  133. package/dist/models.d.ts +14 -0
  134. package/dist/models.d.ts.map +1 -0
  135. package/dist/models.js +193 -0
  136. package/dist/name.d.ts +4 -0
  137. package/dist/name.d.ts.map +1 -0
  138. package/dist/name.js +122 -0
  139. package/dist/observation.d.ts +22 -2
  140. package/dist/observation.d.ts.map +1 -1
  141. package/dist/observation.js +133 -17
  142. package/dist/open.d.ts +2 -0
  143. package/dist/open.d.ts.map +1 -0
  144. package/dist/open.js +1 -0
  145. package/dist/opened.d.ts +22 -0
  146. package/dist/opened.d.ts.map +1 -0
  147. package/dist/opened.js +107 -0
  148. package/dist/opening.d.ts +4 -0
  149. package/dist/opening.d.ts.map +1 -0
  150. package/dist/opening.js +57 -0
  151. package/dist/outcome.d.ts +75 -5
  152. package/dist/outcome.d.ts.map +1 -1
  153. package/dist/outcome.js +84 -1
  154. package/dist/overlay.d.ts +16 -5
  155. package/dist/overlay.d.ts.map +1 -1
  156. package/dist/overlay.js +147 -180
  157. package/dist/parts.d.ts +59 -0
  158. package/dist/parts.d.ts.map +1 -0
  159. package/dist/parts.js +118 -0
  160. package/dist/paths.d.ts +1 -1
  161. package/dist/paths.d.ts.map +1 -1
  162. package/dist/paths.js +4 -16
  163. package/dist/preparation.d.ts +20 -0
  164. package/dist/preparation.d.ts.map +1 -0
  165. package/dist/preparation.js +226 -0
  166. package/dist/queries.d.ts +16 -0
  167. package/dist/queries.d.ts.map +1 -0
  168. package/dist/queries.js +272 -0
  169. package/dist/query.d.ts +56 -180
  170. package/dist/query.d.ts.map +1 -1
  171. package/dist/query.js +3 -422
  172. package/dist/questions.d.ts +34 -0
  173. package/dist/questions.d.ts.map +1 -0
  174. package/dist/questions.js +122 -0
  175. package/dist/reach.d.ts +56 -0
  176. package/dist/reach.d.ts.map +1 -0
  177. package/dist/reach.js +494 -0
  178. package/dist/react.d.ts +51 -0
  179. package/dist/react.d.ts.map +1 -0
  180. package/dist/react.js +54 -0
  181. package/dist/read.d.ts +7 -0
  182. package/dist/read.d.ts.map +1 -0
  183. package/dist/read.js +17 -0
  184. package/dist/readiness.d.ts +50 -0
  185. package/dist/readiness.d.ts.map +1 -0
  186. package/dist/readiness.js +27 -0
  187. package/dist/reads.d.ts +37 -0
  188. package/dist/reads.d.ts.map +1 -0
  189. package/dist/reads.js +171 -0
  190. package/dist/recovery.d.ts +26 -0
  191. package/dist/recovery.d.ts.map +1 -0
  192. package/dist/recovery.js +232 -0
  193. package/dist/refresh.d.ts +21 -7
  194. package/dist/refresh.d.ts.map +1 -1
  195. package/dist/refresh.js +155 -55
  196. package/dist/refusal.d.ts +5 -0
  197. package/dist/refusal.d.ts.map +1 -0
  198. package/dist/refusal.js +56 -0
  199. package/dist/replica.d.ts +58 -33
  200. package/dist/replica.d.ts.map +1 -1
  201. package/dist/replica.js +356 -158
  202. package/dist/room-draft.d.ts +62 -11
  203. package/dist/room-draft.d.ts.map +1 -1
  204. package/dist/room-draft.js +281 -83
  205. package/dist/room-drafts.d.ts +24 -0
  206. package/dist/room-drafts.d.ts.map +1 -0
  207. package/dist/room-drafts.js +216 -0
  208. package/dist/rows.d.ts +32 -34
  209. package/dist/rows.d.ts.map +1 -1
  210. package/dist/rows.js +65 -87
  211. package/dist/runtime.d.ts +43 -0
  212. package/dist/runtime.d.ts.map +1 -0
  213. package/dist/runtime.js +1 -0
  214. package/dist/save.d.ts +76 -6
  215. package/dist/save.d.ts.map +1 -1
  216. package/dist/save.js +904 -182
  217. package/dist/saves.d.ts +70 -0
  218. package/dist/saves.d.ts.map +1 -0
  219. package/dist/saves.js +1006 -0
  220. package/dist/search.d.ts +15 -54
  221. package/dist/search.d.ts.map +1 -1
  222. package/dist/search.js +10 -121
  223. package/dist/seed.d.ts +25 -0
  224. package/dist/seed.d.ts.map +1 -0
  225. package/dist/seed.js +146 -0
  226. package/dist/shared.d.ts +15 -0
  227. package/dist/shared.d.ts.map +1 -0
  228. package/dist/shared.js +425 -0
  229. package/dist/shown.d.ts +31 -0
  230. package/dist/shown.d.ts.map +1 -0
  231. package/dist/shown.js +295 -0
  232. package/dist/socket/admission.d.ts +7 -0
  233. package/dist/socket/admission.d.ts.map +1 -0
  234. package/dist/socket/admission.js +39 -0
  235. package/dist/socket/answers.d.ts +6 -0
  236. package/dist/socket/answers.d.ts.map +1 -0
  237. package/dist/socket/answers.js +20 -0
  238. package/dist/socket/lanes.d.ts +20 -0
  239. package/dist/socket/lanes.d.ts.map +1 -0
  240. package/dist/socket/lanes.js +77 -0
  241. package/dist/socket/reads.d.ts +10 -0
  242. package/dist/socket/reads.d.ts.map +1 -0
  243. package/dist/socket/reads.js +31 -0
  244. package/dist/socket/rooms.d.ts +43 -0
  245. package/dist/socket/rooms.d.ts.map +1 -0
  246. package/dist/socket/rooms.js +90 -0
  247. package/dist/socket/session.d.ts +39 -0
  248. package/dist/socket/session.d.ts.map +1 -0
  249. package/dist/socket/session.js +23 -0
  250. package/dist/socket.d.ts +70 -0
  251. package/dist/socket.d.ts.map +1 -0
  252. package/dist/socket.js +175 -0
  253. package/dist/source.d.ts +61 -0
  254. package/dist/source.d.ts.map +1 -0
  255. package/dist/source.js +81 -0
  256. package/dist/status.d.ts +7 -4
  257. package/dist/status.d.ts.map +1 -1
  258. package/dist/status.js +3 -15
  259. package/dist/store/fences.d.ts +16 -0
  260. package/dist/store/fences.d.ts.map +1 -0
  261. package/dist/store/fences.js +26 -0
  262. package/dist/store/installs.d.ts +30 -0
  263. package/dist/store/installs.d.ts.map +1 -0
  264. package/dist/store/installs.js +34 -0
  265. package/dist/store/readiness.d.ts +10 -0
  266. package/dist/store/readiness.d.ts.map +1 -0
  267. package/dist/store/readiness.js +52 -0
  268. package/dist/store/records.d.ts +212 -0
  269. package/dist/store/records.d.ts.map +1 -0
  270. package/dist/store/records.js +408 -0
  271. package/dist/store/room-held.d.ts +8 -0
  272. package/dist/store/room-held.d.ts.map +1 -0
  273. package/dist/store/room-held.js +17 -0
  274. package/dist/store/space.d.ts +11 -0
  275. package/dist/store/space.d.ts.map +1 -0
  276. package/dist/store/space.js +43 -0
  277. package/dist/store/watchers.d.ts +17 -0
  278. package/dist/store/watchers.d.ts.map +1 -0
  279. package/dist/store/watchers.js +57 -0
  280. package/dist/store/writes.d.ts +24 -0
  281. package/dist/store/writes.d.ts.map +1 -0
  282. package/dist/store/writes.js +125 -0
  283. package/dist/store.d.ts +106 -93
  284. package/dist/store.d.ts.map +1 -1
  285. package/dist/store.js +142 -385
  286. package/dist/stored.d.ts +156 -0
  287. package/dist/stored.d.ts.map +1 -0
  288. package/dist/stored.js +637 -0
  289. package/dist/surface.d.ts +88 -0
  290. package/dist/surface.d.ts.map +1 -0
  291. package/dist/surface.js +223 -0
  292. package/dist/sync.d.ts +20 -0
  293. package/dist/sync.d.ts.map +1 -0
  294. package/dist/sync.js +532 -0
  295. package/dist/tables.d.ts +415 -0
  296. package/dist/tables.d.ts.map +1 -0
  297. package/dist/tables.js +740 -0
  298. package/dist/tallies.d.ts +18 -0
  299. package/dist/tallies.d.ts.map +1 -0
  300. package/dist/tallies.js +129 -0
  301. package/dist/transfer.d.ts +0 -5
  302. package/dist/transfer.d.ts.map +1 -1
  303. package/dist/turn.d.ts +9 -0
  304. package/dist/turn.d.ts.map +1 -0
  305. package/dist/turn.js +75 -0
  306. package/dist/validation.d.ts +29 -0
  307. package/dist/validation.d.ts.map +1 -0
  308. package/dist/validation.js +104 -0
  309. package/dist/windows.d.ts +11 -0
  310. package/dist/windows.d.ts.map +1 -0
  311. package/dist/windows.js +16 -0
  312. package/dist/wire.d.ts +207 -20
  313. package/dist/wire.d.ts.map +1 -1
  314. package/dist/wire.js +96 -61
  315. package/package.json +33 -10
  316. package/AGENTS.md +0 -207
  317. package/dist/attachment.d.ts +0 -3
  318. package/dist/attachment.d.ts.map +0 -1
  319. package/dist/attachment.js +0 -8
  320. package/dist/read-side.d.ts +0 -14
  321. package/dist/read-side.d.ts.map +0 -1
  322. package/dist/read-side.js +0 -371
package/dist/save.js CHANGED
@@ -1,55 +1,73 @@
1
1
  import { definitionOf } from './model.js';
2
- import { apply, links, parse, retarget, serialize, target, } from '@agentshouse/mdmodel';
3
- import { immutable, } from './change.js';
4
- import { governing, parseCollection, 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, relative } from './paths.js';
8
- function refuse(rule, file, location) {
6
+ import { overlaying } from './overlay.js';
7
+ import { ancestors, 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';
11
+ function refuse(code, file, location) {
9
12
  return {
10
13
  kind: 'refused',
11
14
  refusal: {
12
- rule,
15
+ code,
16
+ retryable: false,
13
17
  ...(file === undefined ? {} : { file }),
14
18
  ...(location === undefined ? {} : { location }),
15
19
  },
16
20
  };
17
21
  }
18
- function referenceField(spec) {
19
- return spec.kind === 'reference' || (spec.kind === 'list' && spec.of.kind === 'reference');
20
- }
21
- function repointed(value, from, to, moves) {
22
- if (Array.isArray(value)) {
23
- const next = value.map((item) => repointed(item, from, to, moves) ?? item);
24
- return next.some((item, index) => item !== value[index]) ? next : undefined;
22
+ function repointed(document, definition, from, to, moves, settled) {
23
+ const parsed = parse(document.content, from);
24
+ if (!parsed.ok)
25
+ return [];
26
+ const changes = [];
27
+ const names = new Set();
28
+ for (const { pointer, value } of definition === undefined
29
+ ? []
30
+ : references(document, definition)) {
31
+ const [name = ''] = pointerSegments(pointer);
32
+ const named = target(from, value);
33
+ if (named === null || settled.has(`${from} ${name}`))
34
+ continue;
35
+ const destination = moves.get(named) ?? named;
36
+ if (target(to, value) === destination)
37
+ continue;
38
+ changes.push({
39
+ kind: 'reference',
40
+ pointer,
41
+ value: retarget(value, relative(to, destination)),
42
+ prior: value,
43
+ });
44
+ names.add(name);
25
45
  }
26
- if (typeof value !== 'string')
27
- return undefined;
28
- const named = target(from, value);
29
- if (named === null)
30
- return undefined;
31
- const destination = moves.get(named) ?? named;
32
- return target(to, value) === destination ? undefined : retarget(value, relative(to, destination));
46
+ if (changes.length === 0)
47
+ return [];
48
+ const applied = apply(parsed.document, changes);
49
+ if (applied.outcome !== 'applied')
50
+ return [];
51
+ const next = applied.document;
52
+ return [...names].map((name) => ({ kind: 'field', path: from, name, value: next.fields[name] }));
33
53
  }
34
- function edited(document, path, changes) {
35
- let next = document;
54
+ function edited(document, changes) {
55
+ const parts = new Map();
36
56
  for (const change of changes) {
37
- if (change.path !== path)
38
- continue;
39
- if (change.kind !== 'section' && change.kind !== 'preamble')
40
- continue;
41
- const applied = apply(next, change.kind === 'preamble'
42
- ? { kind: 'preamble', value: change.value, prior: next.preamble }
43
- : {
57
+ if (change.kind === 'preamble')
58
+ parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
59
+ if (change.kind === 'section')
60
+ parts.set(`section ${change.name}`, {
44
61
  kind: 'section',
45
62
  name: change.name,
46
63
  value: change.value,
47
- prior: next.sections[change.name],
64
+ prior: document.sections[change.name] ?? '',
48
65
  });
49
- if (applied.outcome === 'applied')
50
- next = applied.document;
51
66
  }
52
- return next;
67
+ if (parts.size === 0)
68
+ return document;
69
+ const applied = apply(document, [...parts.values()]);
70
+ return applied.outcome === 'applied' ? applied.document : document;
53
71
  }
54
72
  function bodies(path, before, after) {
55
73
  const changes = [];
@@ -64,7 +82,7 @@ function bodies(path, before, after) {
64
82
  return changes;
65
83
  }
66
84
  function relinks(path, moved, before, moves) {
67
- let after = before;
85
+ const changes = [];
68
86
  for (const link of links(before, path)) {
69
87
  const destination = moves.get(link.target) ?? link.target;
70
88
  if (destination === link.target && moved === path)
@@ -72,19 +90,23 @@ function relinks(path, moved, before, moves) {
72
90
  const to = relative(moved, destination);
73
91
  if (to === relative(path, link.target))
74
92
  continue;
75
- const applied = apply(after, { kind: 'link', href: link.href, to });
76
- if (applied.outcome === 'refuse') {
77
- return {
78
- kind: 'refused',
79
- refusal: {
80
- rule: applied.refusal.rule,
81
- file: path,
82
- ...(applied.refusal.location === undefined ? {} : { location: applied.refusal.location }),
83
- },
84
- };
85
- }
86
- after = applied.document;
93
+ changes.push({ kind: 'link', href: link.href, to });
94
+ }
95
+ if (changes.length === 0)
96
+ return { kind: 'changes', changes: [] };
97
+ const applied = apply(before, changes);
98
+ if (applied.outcome === 'refuse') {
99
+ return {
100
+ kind: 'refused',
101
+ refusal: {
102
+ code: applied.refusal.rule,
103
+ retryable: false,
104
+ file: path,
105
+ ...(applied.refusal.location === undefined ? {} : { location: applied.refusal.location }),
106
+ },
107
+ };
87
108
  }
109
+ const after = applied.document;
88
110
  if (after === before)
89
111
  return { kind: 'changes', changes: [] };
90
112
  if (before.type === undefined) {
@@ -92,93 +114,161 @@ function relinks(path, moved, before, moves) {
92
114
  }
93
115
  return { kind: 'changes', changes: bodies(path, before, after) };
94
116
  }
95
- function subsumed(merged, path) {
96
- for (let index = merged.length - 1; index >= 0; index -= 1) {
97
- const item = merged[index];
98
- if (item.path !== path)
99
- continue;
100
- if (item.kind === 'section' || item.kind === 'preamble')
101
- merged.splice(index, 1);
102
- }
117
+ function partKey(change) {
118
+ return change.kind === 'section' ? `section ${change.name}` : change.kind;
103
119
  }
104
- function rewrites(changes, visible) {
120
+ function* rewrites(changes, visible) {
105
121
  const moves = new Map();
106
122
  const settled = new Set();
107
- for (const change of changes) {
123
+ const parts = new Map();
124
+ for (const [index, change] of changes.entries()) {
108
125
  if (change.kind === 'move')
109
126
  moves.set(change.path, change.to);
110
- if (change.kind === 'remove' || change.kind === 'replace' || change.kind === 'create') {
127
+ if (change.kind === 'remove' ||
128
+ change.kind === 'replace' ||
129
+ change.kind === 'create' ||
130
+ change.kind === 'binary') {
111
131
  settled.add(change.path);
112
132
  }
113
133
  if (change.kind === 'field')
114
134
  settled.add(`${change.path} ${change.name}`);
135
+ if (isPart(change)) {
136
+ const held = parts.get(change.path);
137
+ if (held === undefined)
138
+ parts.set(change.path, [index]);
139
+ else
140
+ held.push(index);
141
+ }
115
142
  }
116
143
  if (moves.size === 0)
117
144
  return { kind: 'changes', changes };
118
145
  const merged = [...changes];
119
146
  const added = [];
120
- for (const [path, document] of Object.entries(visible.collection.documents)) {
147
+ for (const [path, document] of yield* documentsOf(visible.collection)) {
121
148
  if (settled.has(path))
122
149
  continue;
123
150
  const moved = moves.get(path) ?? path;
124
151
  const definition = definitionOf(governing(visible.collection.composition, path), document.type);
125
- for (const [name, spec] of Object.entries(definition?.fields ?? {})) {
126
- if (!referenceField(spec) || settled.has(`${path} ${name}`))
127
- continue;
128
- const value = repointed(document.fields[name], path, moved, moves);
129
- if (value === undefined)
130
- continue;
131
- added.push({ kind: 'field', path, name, value });
132
- }
133
- const relinked = relinks(path, moved, edited(document, path, merged), moves);
152
+ const repointing = repointed(document, definition, path, moved, moves, settled);
153
+ added.push(...repointing);
154
+ const own = parts.get(path) ?? [];
155
+ const relinked = relinks(path, moved, edited(document, own.map((index) => merged[index])), moves);
134
156
  if (relinked.kind === 'refused')
135
157
  return relinked;
158
+ const keyed = new Map();
159
+ for (const index of own) {
160
+ const key = partKey(merged[index]);
161
+ if (!keyed.has(key))
162
+ keyed.set(key, index);
163
+ }
136
164
  for (const change of relinked.changes) {
137
165
  if (change.kind === 'replace') {
138
- subsumed(merged, path);
166
+ for (const index of own) {
167
+ const kind = merged[index]?.kind;
168
+ if (kind === 'section' || kind === 'preamble')
169
+ merged[index] = null;
170
+ }
139
171
  added.push(change);
140
172
  continue;
141
173
  }
142
- const index = merged.findIndex((item) => item.kind === change.kind &&
143
- item.path === path &&
144
- (change.kind !== 'section' || (item.kind === 'section' && item.name === change.name)));
145
- if (index === -1)
174
+ const index = keyed.get(partKey(change));
175
+ if (index === undefined)
146
176
  added.push(change);
147
177
  else
148
178
  merged[index] = change;
149
179
  }
180
+ const text = visible.collection.text[path].length;
181
+ const rewritten = repointing.length > 0 || relinked.changes.length > 0;
182
+ yield ROW_WEIGHT + text + (rewritten ? text : 0);
150
183
  }
151
- return { kind: 'changes', changes: [...added, ...merged] };
184
+ return {
185
+ kind: 'changes',
186
+ changes: [...added, ...merged.filter((change) => change !== null)],
187
+ };
152
188
  }
153
- function anticipated(changes, preconditions, rewritten, visible) {
154
- const derived = rewritten.filter((change) => !changes.includes(change));
189
+ function* anticipated(changes, preconditions, rewritten, visible) {
190
+ const given = new Set(changes);
191
+ const derived = rewritten.filter((change) => !given.has(change));
155
192
  return {
156
193
  changes: rewritten,
157
- preconditions: [...preconditionsOf(derived, visible, []), ...preconditions],
194
+ preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...preconditions],
158
195
  };
159
196
  }
160
- export function providerRewritten(batch, visible) {
161
- const rewritten = rewrites(batch.changes, visible);
197
+ export function* providerRewritten(batch, visible) {
198
+ const rewritten = yield* rewrites(batch.changes, visible);
162
199
  if (rewritten.kind === 'refused')
163
200
  return batch;
164
201
  return {
165
202
  ...batch,
166
- ...anticipated(batch.changes, batch.preconditions, rewritten.changes, visible),
203
+ ...(yield* anticipated(batch.changes, batch.preconditions, rewritten.changes, visible)),
167
204
  };
168
205
  }
169
- function targets(value, from) {
170
- const values = Array.isArray(value) ? value : [value];
171
- const resolved = [];
172
- for (const item of values) {
173
- if (typeof item !== 'string')
206
+ function crossed(document, definition, path, to) {
207
+ const changes = [];
208
+ for (const link of links(document, path)) {
209
+ const written = to(link.target, link.href);
210
+ if (written !== undefined)
211
+ changes.push({ kind: 'link', href: link.href, to: written });
212
+ }
213
+ for (const { pointer, value } of definition === undefined
214
+ ? []
215
+ : references(document, definition)) {
216
+ const named = target(path, value);
217
+ const written = named === null ? undefined : to(named, value);
218
+ if (written !== undefined) {
219
+ changes.push({ kind: 'reference', pointer, value: retarget(value, written), prior: value });
220
+ }
221
+ }
222
+ return changes;
223
+ }
224
+ function rewritten(path, document, changes) {
225
+ if (changes.length === 0)
226
+ return [];
227
+ const applied = apply(document, changes);
228
+ if (applied.outcome !== 'applied')
229
+ return [];
230
+ const after = applied.document;
231
+ if (document.type === undefined)
232
+ return [{ kind: 'replace', path, content: serialize(after) }];
233
+ const names = new Set(changes.flatMap((change) => change.kind === 'reference' ? [pointerSegments(change.pointer)[0] ?? ''] : []));
234
+ return [
235
+ ...[...names].map((name) => ({ kind: 'field', path, name, value: after.fields[name] })),
236
+ ...bodies(path, document, after),
237
+ ];
238
+ }
239
+ function roomPathOf(handle, path) {
240
+ return `/rooms/${handle}/${path}`;
241
+ }
242
+ export function* leavingRewritten(batch, visible) {
243
+ const between = batch.between;
244
+ if (between === undefined)
245
+ return batch;
246
+ const moved = new Map(between.moves.map(({ path, to }) => [path, roomPathOf(between.destination.handle, to)]));
247
+ const derived = [];
248
+ for (const [path, document] of yield* documentsOf(visible.collection)) {
249
+ if (moved.has(path))
174
250
  continue;
175
- const named = target(from, item);
176
- if (named !== null)
177
- resolved.push(named);
251
+ const definition = definitionOf(governing(visible.collection.composition, path), document.type);
252
+ derived.push(...rewritten(path, document, crossed(document, definition, path, (named) => moved.get(named))));
253
+ yield ROW_WEIGHT + visible.collection.text[path].length;
178
254
  }
179
- return resolved;
255
+ if (derived.length === 0)
256
+ return batch;
257
+ return {
258
+ ...batch,
259
+ changes: [...derived, ...batch.changes],
260
+ preconditions: [...(yield* preconditionsOf(derived, visible, [])), ...batch.preconditions],
261
+ };
180
262
  }
181
- function incoming(collection, removed) {
263
+ export function rewriting(batch, locator) {
264
+ return leaving(batch, locator) || batch.changes.some((change) => change.kind === 'move');
265
+ }
266
+ export function* effectiveIn(batch, locator, visible) {
267
+ return leaving(batch, locator)
268
+ ? yield* leavingRewritten(batch, visible)
269
+ : yield* providerRewritten(batch, visible);
270
+ }
271
+ function* incoming(collection, removed, candidates) {
182
272
  const found = new Map();
183
273
  const note = (target, file) => {
184
274
  if (!removed.has(target))
@@ -187,18 +277,31 @@ function incoming(collection, removed) {
187
277
  referrers.add(file);
188
278
  found.set(target, referrers);
189
279
  };
190
- for (const [path, document] of Object.entries(collection.documents)) {
280
+ const read = function* (paths) {
281
+ const seen = new Set();
282
+ for (const path of paths) {
283
+ if (seen.has(path))
284
+ continue;
285
+ seen.add(path);
286
+ const document = collection.document(path);
287
+ if (document !== undefined)
288
+ yield [path, document];
289
+ }
290
+ };
291
+ for (const [path, document] of candidates === null
292
+ ? yield* documentsOf(collection)
293
+ : read(candidates)) {
191
294
  const definition = definitionOf(governing(collection.composition, path), document.type);
192
- for (const [name, spec] of Object.entries(definition?.fields ?? {})) {
193
- if (!referenceField(spec))
295
+ for (const { value, to } of definition === undefined ? [] : references(document, definition)) {
296
+ if (to.includes(ATTACHMENT))
194
297
  continue;
195
- for (const named of targets(document.fields[name], path))
298
+ const named = target(path, value);
299
+ if (named !== null)
196
300
  note(named, path);
197
301
  }
198
- for (const link of links(document, path))
199
- note(link.target, path);
302
+ yield ROW_WEIGHT + collection.text[path].length;
200
303
  }
201
- const [first] = [...found.keys()].sort();
304
+ const [first] = yield* sorting(found.keys());
202
305
  if (first === undefined)
203
306
  return null;
204
307
  const every = new Set();
@@ -207,18 +310,29 @@ function incoming(collection, removed) {
207
310
  every.add(file);
208
311
  return {
209
312
  kind: 'refused',
210
- refusal: { rule: 'referenced-missing-file', file: first, referrers: [...every].sort() },
313
+ refusal: {
314
+ code: 'referenced-missing-file',
315
+ retryable: false,
316
+ file: first,
317
+ referrers: yield* sorting(every),
318
+ },
211
319
  };
212
320
  }
213
321
  function affected(change) {
214
322
  return change.kind === 'move' ? [change.path, change.to] : [change.path];
215
323
  }
216
- function guard(changes, input) {
217
- const existing = new Set([
218
- ...Object.keys(input.visible.documents),
219
- ...input.visible.binaryTargets,
220
- ]);
221
- for (const change of changes) {
324
+ const WRITE = ['write'];
325
+ const MOVE_OUT = ['write', 'erase'];
326
+ function guard(changes, input, removing) {
327
+ const overridden = new Map();
328
+ const existing = {
329
+ has: (path) => overridden.get(path) ??
330
+ (Object.hasOwn(input.visible.documents, path) || Object.hasOwn(input.visible.binaries, path)),
331
+ add: (path) => void overridden.set(path, true),
332
+ delete: (path) => void overridden.set(path, false),
333
+ };
334
+ const occupied = (path) => input.folders.has(path) ? path : (ancestors(path).find((one) => existing.has(one)) ?? null);
335
+ for (const [index, change] of changes.entries()) {
222
336
  for (const path of affected(change)) {
223
337
  const normalized = normalize(path);
224
338
  if (normalized === null || normalized === '')
@@ -226,22 +340,43 @@ function guard(changes, input) {
226
340
  if (covered(input.providerOwnedPaths, normalized)) {
227
341
  return refuse('provider-owned-path', normalized);
228
342
  }
229
- const operation = change.kind === 'remove' ? 'delete' : 'write';
230
- if (!covered(input.operations[operation], normalized))
231
- return refuse(`${operation}-grant`, normalized);
343
+ if (!input.pathRule(normalized))
344
+ return refuse('invalid-path', normalized);
345
+ for (const operation of change.kind === 'remove' ? removing : WRITE)
346
+ if (!input.local && !covered(input.operations[operation], normalized))
347
+ return refuse(`${operation}-grant`, normalized);
232
348
  }
233
- if (change.kind === 'create') {
234
- if (existing.has(change.path))
349
+ if (change.kind === 'create' || change.kind === 'arrival') {
350
+ const taken = existing.has(change.path) ? change.path : occupied(change.path);
351
+ if (taken !== null)
352
+ return refuse('path-collision', taken);
353
+ existing.add(change.path);
354
+ continue;
355
+ }
356
+ if (change.kind === 'binary') {
357
+ if (input.visible.documents[change.path] !== undefined ||
358
+ (existing.has(change.path) &&
359
+ input.bases.some((base) => base.kind === 'absent' && base.path === change.path))) {
235
360
  return refuse('path-collision', change.path);
361
+ }
362
+ const taken = occupied(change.path);
363
+ if (taken !== null)
364
+ return refuse('path-collision', taken);
365
+ if (!input.local &&
366
+ existing.has(change.path) &&
367
+ input.visible.revisions[departure(changes, index)] === undefined) {
368
+ return refuse('unknown-revision', change.path);
369
+ }
236
370
  existing.add(change.path);
237
371
  continue;
238
372
  }
239
373
  if (change.kind === 'move') {
240
374
  if (!existing.has(change.path))
241
375
  return refuse('path-absent', change.path);
242
- if (existing.has(change.to))
243
- return refuse('path-collision', change.to);
244
- if (unrevisionedDocument(input.visible, change.path)) {
376
+ const taken = existing.has(change.to) ? change.to : occupied(change.to);
377
+ if (taken !== null)
378
+ return refuse('path-collision', taken);
379
+ if (unrevised(input, change.path)) {
245
380
  return refuse('unknown-revision', change.path);
246
381
  }
247
382
  existing.delete(change.path);
@@ -251,30 +386,53 @@ function guard(changes, input) {
251
386
  if (!existing.has(change.path))
252
387
  return refuse('path-absent', change.path);
253
388
  if (change.kind === 'remove') {
254
- if (unrevisionedDocument(input.visible, change.path)) {
389
+ if (unrevised(input, change.path)) {
255
390
  return refuse('unknown-revision', change.path);
256
391
  }
257
392
  existing.delete(change.path);
258
393
  continue;
259
394
  }
260
395
  if (change.kind === 'replace') {
261
- if (input.visible.revisions[change.path] === undefined) {
396
+ if (unrevised(input, change.path)) {
262
397
  return refuse('unknown-revision', change.path);
263
398
  }
264
399
  continue;
265
400
  }
266
- if (input.visible.collection.documents[change.path] === undefined) {
401
+ if (input.visible.collection.document(change.path) === undefined) {
267
402
  return refuse('path-absent', change.path);
268
403
  }
269
404
  }
270
405
  return null;
271
406
  }
272
- function unrevisionedDocument(visible, path) {
273
- return visible.revisions[path] === undefined && !visible.binaryTargets.includes(path);
407
+ function unrevised(input, path) {
408
+ return (!input.local && input.queued?.has(path) !== true && input.visible.revisions[path] === undefined);
409
+ }
410
+ export function queuedDocuments(queue) {
411
+ const paths = new Set();
412
+ for (const batch of queue) {
413
+ for (const change of batch.changes) {
414
+ if (change.kind === 'binary' || (change.kind === 'arrival' && !('content' in change)))
415
+ continue;
416
+ if (change.kind === 'remove')
417
+ paths.delete(change.path);
418
+ else if (change.kind !== 'move')
419
+ paths.add(change.path);
420
+ else if (paths.delete(change.path))
421
+ paths.add(change.to);
422
+ }
423
+ }
424
+ return paths;
425
+ }
426
+ function identity(precondition) {
427
+ const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
428
+ return `${precondition.kind} ${precondition.path} ${part}`;
274
429
  }
275
430
  function existence(visible, path) {
276
431
  const revision = visible.revisions[path];
277
- return revision === undefined ? { kind: 'present', path } : { kind: 'revision', path, revision };
432
+ if (revision !== undefined)
433
+ return { kind: 'revision', path, revision };
434
+ const content = visible.documents[path];
435
+ return content === undefined ? { kind: 'present', path } : { kind: 'content', path, content };
278
436
  }
279
437
  function sameTarget(precondition, change) {
280
438
  if (precondition.kind !== change.kind || precondition.path !== change.path)
@@ -282,25 +440,25 @@ function sameTarget(precondition, change) {
282
440
  return ((precondition.kind !== 'field' && precondition.kind !== 'section') ||
283
441
  ((change.kind === 'field' || change.kind === 'section') && precondition.name === change.name));
284
442
  }
285
- function preconditionsOf(changes, visible, bases) {
443
+ function* preconditionsOf(changes, visible, bases) {
286
444
  const preconditions = [];
287
445
  const seen = new Set();
288
446
  const add = (precondition) => {
289
- const part = precondition.kind === 'field' || precondition.kind === 'section' ? precondition.name : '';
290
- const identity = `${precondition.kind} ${precondition.path} ${part}`;
291
- if (seen.has(identity))
447
+ const held = identity(precondition);
448
+ if (seen.has(held))
292
449
  return;
293
- seen.add(identity);
450
+ seen.add(held);
294
451
  preconditions.push(precondition);
295
452
  };
296
453
  for (const change of changes) {
454
+ yield ROW_WEIGHT;
297
455
  if (change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble') {
298
456
  const based = bases.find((base) => sameTarget(base, change));
299
457
  if (based !== undefined) {
300
458
  add(based);
301
459
  continue;
302
460
  }
303
- const document = visible.collection.documents[change.path];
461
+ const document = visible.collection.document(change.path);
304
462
  if (change.kind === 'field') {
305
463
  add({
306
464
  kind: 'field',
@@ -325,6 +483,13 @@ function preconditionsOf(changes, visible, bases) {
325
483
  add({ kind: 'absent', path: change.path });
326
484
  continue;
327
485
  }
486
+ if (change.kind === 'binary') {
487
+ const revision = visible.revisions[change.path];
488
+ add(visible.binaries[change.path] === undefined || revision === undefined
489
+ ? { kind: 'absent', path: change.path }
490
+ : { kind: 'revision', path: change.path, revision });
491
+ continue;
492
+ }
328
493
  add(existence(visible, change.path));
329
494
  if (change.kind === 'move')
330
495
  add({ kind: 'absent', path: change.to });
@@ -334,10 +499,13 @@ function preconditionsOf(changes, visible, bases) {
334
499
  function depth(path) {
335
500
  return path.split('/').length;
336
501
  }
337
- function exceeded(limits, changes, resulting) {
338
- const written = changes.map((change) => (change.kind === 'move' ? change.to : change.path));
502
+ function* exceeding(limits, changes, effective, resulting) {
503
+ const written = effective.map((change) => (change.kind === 'move' ? change.to : change.path));
339
504
  const created = changes
340
- .filter((change) => change.kind === 'create' || change.kind === 'move')
505
+ .filter((change) => change.kind === 'create' ||
506
+ change.kind === 'move' ||
507
+ change.kind === 'binary' ||
508
+ change.kind === 'arrival')
341
509
  .map((change) => (change.kind === 'move' ? change.to : change.path));
342
510
  for (const limit of limits) {
343
511
  const value = limit.value;
@@ -351,9 +519,13 @@ function exceeded(limits, changes, resulting) {
351
519
  return refuse('provider-limit', undefined, limit.name);
352
520
  }
353
521
  if (limit.name === 'document-size') {
354
- const large = written.find((path) => (resulting.documents[path] ?? '').length > value);
355
- if (large !== undefined)
356
- return refuse('provider-limit', large, limit.name);
522
+ for (const path of written) {
523
+ const text = resulting.documents[path] ?? '';
524
+ if (new TextEncoder().encode(text).byteLength > value) {
525
+ return refuse('provider-limit', path, limit.name);
526
+ }
527
+ yield ROW_WEIGHT + text.length;
528
+ }
357
529
  }
358
530
  if (limit.name === 'path-length') {
359
531
  const long = created.find((path) => path.length > value);
@@ -368,67 +540,505 @@ function exceeded(limits, changes, resulting) {
368
540
  }
369
541
  return null;
370
542
  }
371
- export function buildBatch(input) {
372
- const structural = input.changes.find((change) => change.kind === 'move' || change.kind === 'remove');
373
- if (structural !== undefined &&
374
- Object.entries(input.visible.collection.documents).some(([path, document]) => document.type !== undefined &&
375
- document.type !== 'any' &&
376
- governing(input.composition, path).status === 'unavailable'))
377
- return refuse('model-unavailable', structural.path);
378
- if (input.changes.length === 0)
379
- return refuse('no-change');
380
- const rewritten = rewrites(input.changes, input.visible);
381
- if (rewritten.kind === 'refused')
382
- return rewritten;
383
- const changes = input.rewriting === 'provider' ? input.changes : rewritten.changes;
384
- const refusal = guard(changes, input);
385
- if (refusal !== null)
386
- return refusal;
387
- const preconditions = preconditionsOf(changes, input.visible, input.bases);
388
- const predicted = input.rewriting === 'provider'
389
- ? anticipated(changes, preconditions, rewritten.changes, input.visible)
390
- : { changes, preconditions };
391
- const resulting = overlay(input.visible, [
392
- { id: '', baseRoomVersion: input.baseRoomVersion, ...predicted },
393
- ]);
394
- const removed = new Set(changes.filter((change) => change.kind === 'remove').map((change) => change.path));
543
+ function shellRelative(text, path, handle, definition) {
544
+ if (!text.includes(`/rooms/${handle}/`))
545
+ return null;
546
+ const parsed = parse(text, path);
547
+ if (!parsed.ok)
548
+ return null;
549
+ const result = relativize(parsed.document, path, handle, definition(parsed.document.type));
550
+ return result.outcome === 'applied' && result.document.content !== text ? result.document : null;
551
+ }
552
+ function relativeField(path, name, value, handle, definition) {
553
+ const opened = parse('---\n---\n');
554
+ if (definition === undefined ||
555
+ !opened.ok ||
556
+ !JSON.stringify(value ?? null).includes(`/rooms/${handle}/`))
557
+ return value;
558
+ const held = apply(opened.document, [{ kind: 'field', name, value, prior: undefined }]);
559
+ if (held.outcome !== 'applied')
560
+ return value;
561
+ const result = relativize(held.document, path, handle, definition);
562
+ return result.outcome === 'applied' ? result.document.fields[name] : value;
563
+ }
564
+ function relativeChanges(input) {
565
+ const { handle, composition, visible } = input;
566
+ const typed = (path) => (type) => definitionOf(governing(composition, path), type);
567
+ const body = (path, text) => shellRelative(text, path, handle, () => undefined)?.content ?? text;
568
+ return input.changes.map((change) => {
569
+ const definition = typed(change.path);
570
+ if (change.kind === 'field') {
571
+ const type = visible.collection.document(change.path)?.type;
572
+ const value = relativeField(change.path, change.name, change.value, handle, definition(type));
573
+ return value === change.value ? change : { ...change, value };
574
+ }
575
+ if (change.kind === 'section' || change.kind === 'preamble') {
576
+ const value = body(change.path, change.value);
577
+ return value === change.value ? change : { ...change, value };
578
+ }
579
+ if (change.kind === 'replace' || (change.kind === 'create' && 'content' in change)) {
580
+ const content = shellRelative(change.content, change.path, handle, definition)?.content ?? change.content;
581
+ return content === change.content ? change : { ...change, content };
582
+ }
583
+ if (change.kind !== 'create')
584
+ return change;
585
+ const type = typeof change.fields.type === 'string' ? change.fields.type : undefined;
586
+ const fields = Object.fromEntries(Object.entries(change.fields).map(([name, value]) => [
587
+ name,
588
+ relativeField(change.path, name, value, handle, definition(type)),
589
+ ]));
590
+ const sections = Object.fromEntries(Object.entries(change.sections).map(([name, value]) => [name, body(change.path, value)]));
591
+ return { ...change, fields, sections };
592
+ });
593
+ }
594
+ function unqueueable(file) {
595
+ return { kind: 'refused', refusal: { code: 'unknown-revision', retryable: false, file } };
596
+ }
597
+ function isPart(change) {
598
+ return change.kind === 'field' || change.kind === 'section' || change.kind === 'preamble';
599
+ }
600
+ function touched(change) {
601
+ return change.kind === 'move' ? [change.path, change.to] : [change.path];
602
+ }
603
+ function partsApplied(content, path, changes) {
604
+ const parsed = parse(content, path);
605
+ if (!parsed.ok)
606
+ return null;
607
+ const document = parsed.document;
608
+ const parts = new Map();
609
+ for (const change of changes) {
610
+ if (change.kind === 'field')
611
+ parts.set(`field ${change.name}`, {
612
+ kind: 'field',
613
+ name: change.name,
614
+ value: change.value,
615
+ prior: document.fields[change.name],
616
+ });
617
+ else if (change.kind === 'section')
618
+ parts.set(`section ${change.name}`, {
619
+ kind: 'section',
620
+ name: change.name,
621
+ value: change.value,
622
+ prior: document.sections[change.name] ?? '',
623
+ });
624
+ else
625
+ parts.set('preamble', { kind: 'preamble', value: change.value, prior: document.preamble });
626
+ }
627
+ const applied = apply(document, [...parts.values()]);
628
+ return applied.outcome === 'applied' ? serialize(applied.document) : null;
629
+ }
630
+ function settledParts(changes, deferred) {
631
+ for (const [held, parts] of deferred) {
632
+ const content = partsApplied(held.content, held.path, parts);
633
+ if (content === null)
634
+ return held.path;
635
+ changes[changes.indexOf(held)] = { ...held, content };
636
+ }
637
+ deferred.clear();
638
+ return null;
639
+ }
640
+ function folded(changes, change, appending, deferred) {
641
+ const held = changes.find((item) => item.path === change.path && (item.kind === 'create' || item.kind === 'replace'));
642
+ const whole = held === undefined ? -1 : changes.indexOf(held);
643
+ const occupied = changes.some((item) => !isPart(item) && item !== held && touched(item).includes(change.path));
644
+ if (isPart(change)) {
645
+ const same = changes.findIndex((item) => item.kind === change.kind &&
646
+ item.path === change.path &&
647
+ (change.kind === 'preamble' || ('name' in item && item.name === change.name)));
648
+ if (same !== -1) {
649
+ changes[same] = change;
650
+ return 'folded';
651
+ }
652
+ if (held === undefined) {
653
+ if (!appending)
654
+ return 'skipped';
655
+ if (occupied)
656
+ return 'refused';
657
+ changes.push(change);
658
+ return 'appended';
659
+ }
660
+ if ('content' in held) {
661
+ const parts = deferred.get(held);
662
+ if (parts === undefined)
663
+ deferred.set(held, [change]);
664
+ else
665
+ parts.push(change);
666
+ return 'folded';
667
+ }
668
+ if (change.kind === 'preamble')
669
+ return 'refused';
670
+ changes[whole] =
671
+ change.kind === 'field'
672
+ ? { ...held, fields: { ...held.fields, [change.name]: change.value } }
673
+ : { ...held, sections: { ...held.sections, [change.name]: change.value } };
674
+ return 'folded';
675
+ }
676
+ if (change.kind === 'replace') {
677
+ if (held !== undefined) {
678
+ if (!('content' in held))
679
+ return 'refused';
680
+ changes[whole] = { ...held, content: change.content };
681
+ return 'folded';
682
+ }
683
+ const parts = changes.filter((item) => isPart(item) && item.path === change.path);
684
+ if (parts.length === 0 && !appending)
685
+ return 'skipped';
686
+ if (occupied)
687
+ return 'refused';
688
+ changes.splice(0, changes.length, ...changes.filter((item) => !parts.includes(item)), change);
689
+ return 'appended';
690
+ }
691
+ if (changes.some((item) => touched(change).some((path) => touched(item).includes(path))))
692
+ return 'refused';
693
+ changes.push(change);
694
+ return 'appended';
695
+ }
696
+ function* relinked(batch, move, before, after, appending) {
697
+ const derived = yield* rewrites([move], after);
698
+ if (derived.kind === 'refused')
699
+ return derived;
700
+ const changes = [...batch.changes];
701
+ const appended = [];
702
+ let changed = false;
703
+ const deferred = new Map();
704
+ for (const change of derived.changes) {
705
+ if (change === move)
706
+ continue;
707
+ const unsettled = isPart(change) ? null : settledParts(changes, deferred);
708
+ if (unsettled !== null)
709
+ return unqueueable(unsettled);
710
+ const outcome = folded(changes, change, appending, deferred);
711
+ if (outcome === 'refused')
712
+ return unqueueable(change.path);
713
+ if (outcome === 'appended')
714
+ appended.push(change);
715
+ if (outcome !== 'skipped')
716
+ changed = true;
717
+ }
718
+ const unsettled = settledParts(changes, deferred);
719
+ if (unsettled !== null)
720
+ return unqueueable(unsettled);
721
+ if (!changed)
722
+ return { kind: 'relinked', batch };
723
+ const held = new Set(batch.preconditions.map(identity));
724
+ const added = yield* preconditionsOf(appended, before, []);
725
+ return {
726
+ kind: 'relinked',
727
+ batch: immutable({
728
+ ...batch,
729
+ id: changeBatchIdentity(),
730
+ changes,
731
+ preconditions: [
732
+ ...batch.preconditions,
733
+ ...added.filter((precondition) => !held.has(identity(precondition))),
734
+ ],
735
+ }),
736
+ };
737
+ }
738
+ function leading(changes, at) {
739
+ const from = departure(changes, at);
740
+ return new Set(changes.filter((change, index) => index < at && change.kind === 'move' && departure(changes, index) === from));
741
+ }
742
+ function created(batch, at) {
743
+ const file = batch.changes[at];
744
+ return (leading(batch.changes, at).size === 0 &&
745
+ batch.preconditions.some((precondition) => precondition.kind === 'absent' && precondition.path === file.path));
746
+ }
747
+ function withdrawnFile(batch, change, at) {
748
+ const file = batch.changes[at];
749
+ const from = departure(batch.changes, at);
750
+ const moves = leading(batch.changes, at);
751
+ const fresh = created(batch, at);
752
+ const vacated = new Set([...moves].map((move) => move.to));
753
+ if (fresh)
754
+ vacated.add(file.path);
755
+ const replaced = fresh
756
+ ? change.kind === 'remove'
757
+ ? []
758
+ : [{ ...file, path: change.to }]
759
+ : change.kind === 'remove'
760
+ ? [{ kind: 'remove', path: from }]
761
+ : change.to === from
762
+ ? [{ ...file, path: from }]
763
+ : [
764
+ { kind: 'move', path: from, to: change.to },
765
+ { ...file, path: change.to },
766
+ ];
767
+ const added = change.kind === 'move' && (fresh || change.to !== from)
768
+ ? [{ kind: 'absent', path: change.to }]
769
+ : [];
770
+ const changes = batch.changes.flatMap((item) => item === file ? replaced : item.kind === 'move' && moves.has(item) ? [] : [item]);
771
+ if (changes.length === 0)
772
+ return null;
773
+ const preconditions = [
774
+ ...added,
775
+ ...batch.preconditions.filter((precondition) => precondition.kind !== 'absent' || !vacated.has(precondition.path)),
776
+ ];
777
+ return immutable({ ...batch, id: changeBatchIdentity(), changes, preconditions });
778
+ }
779
+ export function* requeuing(queue, changes, visible, changeable, state) {
780
+ const next = [...queue];
781
+ const origins = new Map();
782
+ const withdrawn = new Map();
783
+ const owners = new Set();
784
+ const queued = new Set();
785
+ const kept = [];
786
+ const originOf = (batch) => origins.get(batch) ?? batch.id;
787
+ const open = (batch) => origins.has(batch) || changeable(batch);
788
+ for (const change of changes) {
789
+ const held = visible.binaries[change.path];
790
+ if ((change.kind !== 'move' && change.kind !== 'remove') || held?.state !== 'pending') {
791
+ kept.push(change);
792
+ continue;
793
+ }
794
+ let index = next.length - 1;
795
+ while (index >= 0 && queuedFile(next[index], change.path, held.localId) === -1)
796
+ index -= 1;
797
+ const owner = next[index];
798
+ if (owner === undefined || !open(owner))
799
+ return unqueueable(change.path);
800
+ const following = next.slice(index);
801
+ const rebuilt = [];
802
+ let before = change.kind === 'move' ? yield* state(next.slice(0, index)) : visible;
803
+ for (const [offset, batch] of following.entries()) {
804
+ let current = batch;
805
+ if (change.kind === 'move') {
806
+ const after = yield* state(next.slice(0, index + offset + 1));
807
+ const appending = offset === 0 && created(batch, queuedFile(batch, change.path, held.localId));
808
+ const relinking = yield* relinked(batch, change, before, after, appending);
809
+ if (relinking.kind === 'refused')
810
+ return relinking;
811
+ current = relinking.batch;
812
+ before = after;
813
+ }
814
+ if (current !== batch && (!open(batch) || batch.between !== undefined))
815
+ return unqueueable(change.path);
816
+ rebuilt.push(offset === 0
817
+ ? withdrawnFile(current, change, queuedFile(current, change.path, held.localId))
818
+ : current);
819
+ }
820
+ for (const [offset, batch] of following.entries()) {
821
+ const replaced = rebuilt[offset];
822
+ if (replaced === batch)
823
+ continue;
824
+ const origin = originOf(batch);
825
+ withdrawn.set(origin, replaced);
826
+ if (replaced !== null)
827
+ origins.set(replaced, origin);
828
+ }
829
+ owners.add(originOf(owner));
830
+ next.splice(index, following.length, ...rebuilt.filter((batch) => batch !== null));
831
+ queued.add(change.path);
832
+ }
833
+ return {
834
+ kind: 'requeued',
835
+ queue: withdrawn.size === 0 ? queue : next,
836
+ changes: kept,
837
+ queued,
838
+ withdrawn,
839
+ owners,
840
+ };
841
+ }
842
+ function covering(precondition) {
843
+ return precondition.kind === 'field' ||
844
+ precondition.kind === 'section' ||
845
+ precondition.kind === 'preamble'
846
+ ? identity(precondition)
847
+ : `path ${precondition.path}`;
848
+ }
849
+ export function joined(queue, owner, extra) {
850
+ const later = new Set(queue.slice(queue.indexOf(owner) + 1).flatMap((batch) => batch.changes.flatMap(touched)));
851
+ const clash = extra.changes.flatMap(touched).find((path) => later.has(path));
852
+ if (clash !== undefined)
853
+ return unqueueable(clash);
854
+ const changes = [...owner.changes];
855
+ const deferred = new Map();
856
+ for (const change of extra.changes) {
857
+ const unsettled = isPart(change) ? null : settledParts(changes, deferred);
858
+ if (unsettled !== null)
859
+ return unqueueable(unsettled);
860
+ if (folded(changes, change, true, deferred) === 'refused')
861
+ return unqueueable(change.path);
862
+ }
863
+ const unsettled = settledParts(changes, deferred);
864
+ if (unsettled !== null)
865
+ return unqueueable(unsettled);
866
+ const held = new Set(owner.preconditions.map(covering));
867
+ const written = new Set(owner.changes.flatMap((change) => change.kind === 'create' || change.kind === 'replace' ? [change.path] : []));
868
+ const preconditions = [
869
+ ...owner.preconditions,
870
+ ...extra.preconditions.filter((precondition) => {
871
+ const key = covering(precondition);
872
+ return (!held.has(key) && (key === `path ${precondition.path}` || !written.has(precondition.path)));
873
+ }),
874
+ ];
875
+ const batch = immutable({
876
+ ...owner,
877
+ ...(extra.validation === undefined ? {} : { validation: extra.validation }),
878
+ id: changeBatchIdentity(),
879
+ changes,
880
+ preconditions,
881
+ });
882
+ return { kind: 'joined', batch, queue: queue.map((item) => (item === owner ? batch : item)) };
883
+ }
884
+ export function* limiting(queue, batch, limits, state) {
885
+ const at = queue.indexOf(batch);
886
+ const rewritten = yield* providerRewritten(batch, yield* state(queue.slice(0, at)));
887
+ const limited = yield* exceeding(limits, batch.changes, rewritten.changes, yield* state(queue.slice(0, at + 1)));
888
+ return limited?.kind === 'refused' ? limited.refusal : null;
889
+ }
890
+ function typedText(content) {
891
+ if (content === undefined)
892
+ return false;
893
+ const parsed = parse(content);
894
+ return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
895
+ }
896
+ function touchedLibrary(resulting, touched, composition) {
897
+ const entries = touched
898
+ .flatMap((path) => {
899
+ const content = resulting.documents[path];
900
+ const binary = resulting.binaries[path] !== undefined;
901
+ return content === undefined && !binary ? [] : [{ path, content: content ?? null }];
902
+ })
903
+ .sort((left, right) => (left.path < right.path ? -1 : left.path > right.path ? 1 : 0));
904
+ const contents = new Map(entries.map(({ path, content }) => [path, content]));
905
+ const binaries = new Set(Array.from(entriesIn(resulting.binaries), ([path]) => path));
906
+ return validatedCollection(() => Promise.resolve({ entries, next: null }), async (paths) => {
907
+ const known = new Map();
908
+ const pace = paced();
909
+ for (const path of paths) {
910
+ const content = contents.get(path);
911
+ if (binaries.has(path))
912
+ known.set(path, { type: undefined, binary: true });
913
+ else if (content !== undefined && content !== null) {
914
+ const read = parse(content, path);
915
+ if (read.ok)
916
+ known.set(path, { type: read.document.type ?? 'any', binary: false });
917
+ await pace(content.length + ROW_WEIGHT);
918
+ }
919
+ }
920
+ return known;
921
+ }, composition);
922
+ }
923
+ async function judging(visible, resulting, composition, validate, removed, touched) {
395
924
  if (removed.size > 0) {
396
- const referenced = incoming(parseCollection(resulting.documents, input.composition), removed);
925
+ const referenced = await stepped(incoming(collectionOf(resulting.documents, composition), removed, null));
397
926
  if (referenced !== null)
398
927
  return referenced;
399
928
  }
400
- const typed = (content) => {
401
- if (content === undefined)
402
- return false;
403
- const parsed = parse(content);
404
- return parsed.ok && parsed.document.type !== undefined && parsed.document.type !== 'any';
405
- };
406
- const typedPaths = [...new Set(predicted.changes.flatMap(affected))].filter((path) => typed(input.visible.documents[path]) || typed(resulting.documents[path]));
407
- const needsModel = typedPaths.length > 0;
408
- const withoutModel = typedPaths.find((path) => governing(input.composition, path).status === 'unavailable');
929
+ const typedPaths = [];
930
+ const pace = paced();
931
+ for (const path of touched) {
932
+ const before = visible.documents[path];
933
+ const after = resulting.documents[path];
934
+ if (typedText(before) || typedText(after))
935
+ typedPaths.push(path);
936
+ await pace((before?.length ?? 0) + (after?.length ?? 0) + ROW_WEIGHT);
937
+ }
938
+ const withoutModel = typedPaths.find((path) => governing(composition, path).status === 'unavailable');
409
939
  if (withoutModel !== undefined)
410
940
  return refuse('model-unavailable', withoutModel);
411
- const validationText = needsModel
412
- ? resulting.documents
413
- : Object.fromEntries(Object.entries(resulting.documents).filter(([path]) => predicted.changes.some((change) => affected(change).includes(path))));
414
- const verdict = validateCollection(validationText, resulting.binaryTargets, input.composition);
941
+ const verdict = typedPaths.length > 0
942
+ ? await validate(resulting, touched)
943
+ : await touchedLibrary(resulting, touched, composition);
415
944
  if (verdict.verdict === 'refuse') {
416
945
  return {
417
946
  kind: 'refused',
418
947
  refusal: {
419
- rule: verdict.rule,
948
+ code: verdict.rule,
949
+ retryable: false,
420
950
  file: verdict.file,
421
951
  ...(verdict.location === undefined ? {} : { location: verdict.location }),
422
952
  ...(verdict.referrers === undefined ? {} : { referrers: verdict.referrers }),
423
953
  },
424
954
  };
425
955
  }
426
- const limited = exceeded(input.providerLimits, changes, resulting);
956
+ return {
957
+ kind: 'judged',
958
+ validated: typedPaths
959
+ .map((path) => governing(composition, path))
960
+ .find((model) => model.status !== 'unavailable'),
961
+ };
962
+ }
963
+ function placed(reference) {
964
+ const key = roomPathOf(reference.handle, reference.path);
965
+ if (reference.kind === 'link')
966
+ return `link ${key}`;
967
+ if (reference.kind === 'item')
968
+ return `section ${reference.section} ${key}`;
969
+ const pointer = reference.pointer
970
+ .split('/')
971
+ .map((segment) => (ARRAY_INDEX.test(segment) ? '-' : segment))
972
+ .join('/');
973
+ return `field ${pointer} ${key}`;
974
+ }
975
+ function roomReferencesIn(path, content, composition) {
976
+ if (content === undefined)
977
+ return [];
978
+ const parsed = parse(content, path);
979
+ if (!parsed.ok)
980
+ return [];
981
+ return roomReferences(parsed.document, definitionOf(governing(composition, path), parsed.document.type));
982
+ }
983
+ function* admittedRooms(changes, visible, resulting, composition) {
984
+ const from = new Map();
985
+ for (const [index, change] of changes.entries()) {
986
+ if (change.kind === 'move')
987
+ from.set(change.to, departure(changes, index));
988
+ }
989
+ for (const path of [...new Set(changes.flatMap(affected))].sort()) {
990
+ const now = resulting.documents[path];
991
+ if (now === undefined)
992
+ continue;
993
+ const held = new Set(roomReferencesIn(path, visible.documents[from.get(path) ?? path], composition).map(placed));
994
+ for (const reference of roomReferencesIn(path, now, composition)) {
995
+ if (reference.kind === 'link' || reference.crossRoom === true)
996
+ continue;
997
+ if (held.has(placed(reference)))
998
+ continue;
999
+ return refuse('unresolved-reference', path, reference.kind === 'field' ? reference.pointer : reference.section);
1000
+ }
1001
+ yield ROW_WEIGHT + now.length;
1002
+ }
1003
+ return null;
1004
+ }
1005
+ function validationOf(validated) {
1006
+ return validated === undefined
1007
+ ? {}
1008
+ : {
1009
+ validation: {
1010
+ identity: validated.snapshot.identity,
1011
+ version: validated.snapshot.version,
1012
+ source: validated.status,
1013
+ },
1014
+ };
1015
+ }
1016
+ async function batching(input) {
1017
+ const changes = relativeChanges(input);
1018
+ const structural = changes.find((change) => change.kind === 'move' || change.kind === 'remove');
1019
+ if (structural !== undefined && input.unmodeled) {
1020
+ return refuse('model-unavailable', structural.path);
1021
+ }
1022
+ if (changes.length === 0)
1023
+ return refuse('no-change');
1024
+ const rewritten = await stepped(rewrites(changes, input.visible));
1025
+ if (rewritten.kind === 'refused')
1026
+ return rewritten;
1027
+ const refusal = guard(changes, input, WRITE);
1028
+ if (refusal !== null)
1029
+ return refusal;
1030
+ const preconditions = await stepped(preconditionsOf(changes, input.visible, input.bases));
1031
+ const predicted = await stepped(anticipated(changes, preconditions, rewritten.changes, input.visible));
1032
+ const resulting = await stepped(overlaying(input.visible, [{ id: '', baseRoomVersion: input.baseRoomVersion, ...predicted }]));
1033
+ 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))]);
1034
+ if (judged.kind === 'refused')
1035
+ return judged;
1036
+ const crossing = await stepped(admittedRooms(changes, input.visible, resulting, input.composition));
1037
+ if (crossing !== null)
1038
+ return crossing;
1039
+ const limited = await stepped(exceeding(input.providerLimits, changes, predicted.changes, resulting));
427
1040
  if (limited !== null)
428
1041
  return limited;
429
- const validated = typedPaths
430
- .map((path) => governing(input.composition, path))
431
- .find((model) => model.status !== 'unavailable');
432
1042
  return {
433
1043
  kind: 'batch',
434
1044
  batch: immutable({
@@ -437,15 +1047,127 @@ export function buildBatch(input) {
437
1047
  changes,
438
1048
  preconditions,
439
1049
  ...(input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy }),
440
- ...(validated === undefined
441
- ? {}
442
- : {
443
- validation: {
444
- identity: validated.snapshot.identity,
445
- version: validated.snapshot.version,
446
- source: validated.status,
447
- },
448
- }),
1050
+ ...validationOf(judged.validated),
449
1051
  }),
450
1052
  };
451
1053
  }
1054
+ export const MOVE_DOCUMENTS = 'move-documents';
1055
+ function heldAt(visible, path) {
1056
+ return visible.documents[path] !== undefined || visible.binaries[path] !== undefined;
1057
+ }
1058
+ function arrivingText(path, to, source, moved) {
1059
+ const content = source.visible.documents[path];
1060
+ const document = source.visible.collection.document(path);
1061
+ if (document === undefined)
1062
+ return content;
1063
+ const definition = definitionOf(governing(source.composition, path), document.type);
1064
+ const changes = crossed(document, definition, path, (named, href) => {
1065
+ const at = moved.get(named);
1066
+ if (at !== undefined)
1067
+ return target(to, href) === at ? undefined : relative(to, at);
1068
+ return heldAt(source.visible, named) ? roomPathOf(source.handle, named) : undefined;
1069
+ });
1070
+ if (changes.length === 0)
1071
+ return content;
1072
+ const applied = apply(document, changes);
1073
+ return applied.outcome === 'applied' ? serialize(applied.document) : content;
1074
+ }
1075
+ function arrivalOf({ path, to }, source, moved) {
1076
+ const { documents, revisions, fileIds, created, changes, binaries } = source.visible;
1077
+ const kept = {
1078
+ ...(fileIds[path] === undefined ? {} : { fileId: fileIds[path] }),
1079
+ ...(created[path] === undefined ? {} : { created: created[path] }),
1080
+ ...(changes[path] === undefined ? {} : { change: changes[path] }),
1081
+ };
1082
+ const heads = {
1083
+ ...(revisions[path] === undefined ? {} : { revision: revisions[path] }),
1084
+ ...kept,
1085
+ };
1086
+ const binary = binaries[path];
1087
+ if (binary?.state === 'accepted') {
1088
+ const { byteLength, mediaType, sha256 } = binary;
1089
+ return {
1090
+ kind: 'arrival',
1091
+ path: to,
1092
+ binary: { byteLength, mediaType, sha256 },
1093
+ ...heads,
1094
+ };
1095
+ }
1096
+ const content = arrivingText(path, to, source, moved);
1097
+ return {
1098
+ kind: 'arrival',
1099
+ path: to,
1100
+ content,
1101
+ ...(content === documents[path] ? heads : kept),
1102
+ };
1103
+ }
1104
+ async function moving(input) {
1105
+ const { source, destination, moves, pathRule } = input;
1106
+ if (moves.length === 0)
1107
+ return refuse('no-change');
1108
+ const removals = moves.map(({ path }) => ({ kind: 'remove', path }));
1109
+ const leaves = guard(removals, { ...source, local: input.local, bases: [], pathRule }, MOVE_OUT);
1110
+ if (leaves !== null)
1111
+ return leaves;
1112
+ const uploading = moves.find(({ path }) => source.visible.binaries[path]?.state === 'pending');
1113
+ if (uploading !== undefined)
1114
+ return refuse('unknown-revision', uploading.path);
1115
+ const moved = new Map(moves.map(({ path, to }) => [path, to]));
1116
+ const arrivals = moves.map((move) => arrivalOf(move, source, moved));
1117
+ const arrives = guard(arrivals, { ...destination, local: input.local, bases: [], pathRule }, WRITE);
1118
+ if (arrives !== null)
1119
+ return arrives;
1120
+ const bound = input.providerLimits.find((limit) => limit.name === MOVE_DOCUMENTS)?.value;
1121
+ if (typeof bound === 'number' && moves.length > bound) {
1122
+ return refuse('provider-limit', undefined, MOVE_DOCUMENTS);
1123
+ }
1124
+ if (source.unmodeled)
1125
+ return refuse('model-unavailable', moves[0].path);
1126
+ const id = changeBatchIdentity();
1127
+ const between = {
1128
+ source: { room: source.room, baseRoomVersion: source.baseRoomVersion },
1129
+ destination: {
1130
+ room: destination.room,
1131
+ baseRoomVersion: destination.baseRoomVersion,
1132
+ handle: destination.handle,
1133
+ },
1134
+ moves,
1135
+ };
1136
+ const written = input.writtenBy === undefined ? {} : { writtenBy: input.writtenBy };
1137
+ const leavingBatch = {
1138
+ id,
1139
+ baseRoomVersion: source.baseRoomVersion,
1140
+ changes: removals,
1141
+ preconditions: moves.map(({ path }) => existence(source.visible, path)),
1142
+ ...written,
1143
+ between,
1144
+ };
1145
+ const effective = await stepped(leavingRewritten(leavingBatch, source.visible));
1146
+ const left = await stepped(overlaying(source.visible, [effective]));
1147
+ 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))]);
1148
+ if (sourceJudged.kind === 'refused')
1149
+ return sourceJudged;
1150
+ const arrivingBatch = {
1151
+ id,
1152
+ baseRoomVersion: destination.baseRoomVersion,
1153
+ changes: arrivals,
1154
+ preconditions: moves.map(({ to }) => ({ kind: 'absent', path: to })),
1155
+ ...written,
1156
+ between,
1157
+ };
1158
+ const arrived = await stepped(overlaying(destination.visible, [arrivingBatch]));
1159
+ const destinationJudged = await judging(destination.visible, arrived, destination.composition, destination.validate, new Set(), moves.map(({ to }) => to));
1160
+ if (destinationJudged.kind === 'refused')
1161
+ return destinationJudged;
1162
+ const limited = (await stepped(exceeding(input.providerLimits, removals, effective.changes, left))) ??
1163
+ (await stepped(exceeding(input.providerLimits, arrivals, arrivals, arrived)));
1164
+ if (limited !== null)
1165
+ return limited;
1166
+ return { kind: 'moved', leaving: immutable(leavingBatch), arriving: immutable(arrivingBatch) };
1167
+ }
1168
+ export function buildMove(input) {
1169
+ return moving(input);
1170
+ }
1171
+ export function buildBatch(input) {
1172
+ return batching(input);
1173
+ }