@agentshouse/mdruntime 0.1.0-cb2e390ee7b89 → 0.1.0-cb8a03985803b

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 (392) hide show
  1. package/README.md +23 -19
  2. package/dist/acceptance.d.ts +3 -0
  3. package/dist/acceptance.d.ts.map +1 -0
  4. package/dist/acceptance.js +299 -0
  5. package/dist/act-lanes.d.ts +30 -0
  6. package/dist/act-lanes.d.ts.map +1 -0
  7. package/dist/act-lanes.js +225 -0
  8. package/dist/actions.d.ts +26 -0
  9. package/dist/actions.d.ts.map +1 -0
  10. package/dist/actions.js +302 -0
  11. package/dist/adapter.d.ts +163 -125
  12. package/dist/adapter.d.ts.map +1 -1
  13. package/dist/attached-model.d.ts +7 -10
  14. package/dist/attached-model.d.ts.map +1 -1
  15. package/dist/attached-model.js +40 -41
  16. package/dist/binary.d.ts +0 -5
  17. package/dist/binary.d.ts.map +1 -1
  18. package/dist/binary.js +1 -1
  19. package/dist/bootstrap.d.ts +30 -11
  20. package/dist/bootstrap.d.ts.map +1 -1
  21. package/dist/bootstrap.js +309 -113
  22. package/dist/cell.d.ts +6 -0
  23. package/dist/cell.d.ts.map +1 -0
  24. package/dist/cell.js +9 -0
  25. package/dist/chain.d.ts +12 -0
  26. package/dist/chain.d.ts.map +1 -0
  27. package/dist/chain.js +64 -0
  28. package/dist/change-set.d.ts +13 -16
  29. package/dist/change-set.d.ts.map +1 -1
  30. package/dist/change-set.js +17 -89
  31. package/dist/change.d.ts +32 -1
  32. package/dist/change.d.ts.map +1 -1
  33. package/dist/change.js +14 -3
  34. package/dist/chat-types.d.ts +5 -0
  35. package/dist/chat-types.d.ts.map +1 -0
  36. package/dist/chat-types.js +13 -0
  37. package/dist/client.d.ts +36 -24
  38. package/dist/client.d.ts.map +1 -1
  39. package/dist/client.js +118 -23
  40. package/dist/collection.d.ts +29 -21
  41. package/dist/collection.d.ts.map +1 -1
  42. package/dist/collection.js +48 -290
  43. package/dist/compile.d.ts +18 -0
  44. package/dist/compile.d.ts.map +1 -0
  45. package/dist/compile.js +724 -0
  46. package/dist/connection.d.ts +7 -134
  47. package/dist/connection.d.ts.map +1 -1
  48. package/dist/connection.js +107 -3606
  49. package/dist/context.d.ts +2 -0
  50. package/dist/context.d.ts.map +1 -0
  51. package/dist/{chat/context.js → context.js} +1 -4
  52. package/dist/conversation-live.d.ts +140 -0
  53. package/dist/conversation-live.d.ts.map +1 -0
  54. package/dist/conversation-live.js +250 -0
  55. package/dist/conversation-row.d.ts +33 -0
  56. package/dist/conversation-row.d.ts.map +1 -0
  57. package/dist/conversation-row.js +53 -0
  58. package/dist/conversation.d.ts +72 -49
  59. package/dist/conversation.d.ts.map +1 -1
  60. package/dist/conversation.js +1005 -794
  61. package/dist/declaration.d.ts +10 -0
  62. package/dist/declaration.d.ts.map +1 -0
  63. package/dist/declaration.js +24 -0
  64. package/dist/digest.d.ts +7 -0
  65. package/dist/digest.d.ts.map +1 -0
  66. package/dist/digest.js +113 -0
  67. package/dist/discovery.d.ts +10 -0
  68. package/dist/discovery.d.ts.map +1 -0
  69. package/dist/discovery.js +150 -0
  70. package/dist/durability.d.ts +20 -4
  71. package/dist/durability.d.ts.map +1 -1
  72. package/dist/durability.js +9 -0
  73. package/dist/entry.d.ts +5 -2
  74. package/dist/entry.d.ts.map +1 -1
  75. package/dist/entry.js +33 -28
  76. package/dist/expectation.d.ts +26 -13
  77. package/dist/expectation.d.ts.map +1 -1
  78. package/dist/expectation.js +72 -19
  79. package/dist/failure.d.ts +3 -0
  80. package/dist/failure.d.ts.map +1 -1
  81. package/dist/failure.js +6 -0
  82. package/dist/figures.d.ts +49 -0
  83. package/dist/figures.d.ts.map +1 -0
  84. package/dist/figures.js +197 -0
  85. package/dist/file.d.ts +2 -19
  86. package/dist/file.d.ts.map +1 -1
  87. package/dist/file.js +7 -122
  88. package/dist/holdings.d.ts +29 -0
  89. package/dist/holdings.d.ts.map +1 -0
  90. package/dist/holdings.js +95 -0
  91. package/dist/host.d.ts +21 -11
  92. package/dist/host.d.ts.map +1 -1
  93. package/dist/host.js +113 -49
  94. package/dist/identity.d.ts +0 -2
  95. package/dist/identity.d.ts.map +1 -1
  96. package/dist/identity.js +0 -6
  97. package/dist/index.d.ts +39 -15
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +18 -4
  100. package/dist/install.d.ts +54 -0
  101. package/dist/install.d.ts.map +1 -0
  102. package/dist/install.js +530 -0
  103. package/dist/lifecycle.d.ts +7 -0
  104. package/dist/lifecycle.d.ts.map +1 -0
  105. package/dist/lifecycle.js +277 -0
  106. package/dist/listing.d.ts +48 -0
  107. package/dist/listing.d.ts.map +1 -0
  108. package/dist/listing.js +139 -0
  109. package/dist/live.d.ts +23 -0
  110. package/dist/live.d.ts.map +1 -0
  111. package/dist/live.js +165 -0
  112. package/dist/locks.d.ts +7 -0
  113. package/dist/locks.d.ts.map +1 -0
  114. package/dist/locks.js +32 -0
  115. package/dist/lost.d.ts +5 -0
  116. package/dist/lost.d.ts.map +1 -0
  117. package/dist/lost.js +49 -0
  118. package/dist/media.d.ts +8 -0
  119. package/dist/media.d.ts.map +1 -0
  120. package/dist/media.js +218 -0
  121. package/dist/memory.d.ts +17 -8
  122. package/dist/memory.d.ts.map +1 -1
  123. package/dist/memory.js +273 -121
  124. package/dist/messages.d.ts +56 -0
  125. package/dist/messages.d.ts.map +1 -0
  126. package/dist/messages.js +158 -0
  127. package/dist/model.d.ts +8 -11
  128. package/dist/model.d.ts.map +1 -1
  129. package/dist/model.js +6 -23
  130. package/dist/models.d.ts +10 -0
  131. package/dist/models.d.ts.map +1 -0
  132. package/dist/models.js +153 -0
  133. package/dist/name.d.ts +4 -0
  134. package/dist/name.d.ts.map +1 -0
  135. package/dist/name.js +122 -0
  136. package/dist/observation.d.ts +17 -3
  137. package/dist/observation.d.ts.map +1 -1
  138. package/dist/observation.js +107 -17
  139. package/dist/opened.d.ts +22 -0
  140. package/dist/opened.d.ts.map +1 -0
  141. package/dist/opened.js +107 -0
  142. package/dist/opening.d.ts +4 -0
  143. package/dist/opening.d.ts.map +1 -0
  144. package/dist/opening.js +53 -0
  145. package/dist/outcome.d.ts +47 -9
  146. package/dist/outcome.d.ts.map +1 -1
  147. package/dist/outcome.js +56 -2
  148. package/dist/overlay.d.ts +7 -5
  149. package/dist/overlay.d.ts.map +1 -1
  150. package/dist/overlay.js +123 -182
  151. package/dist/page-metadata.d.ts +8 -0
  152. package/dist/page-metadata.d.ts.map +1 -0
  153. package/dist/page-metadata.js +19 -0
  154. package/dist/parts.d.ts +4 -0
  155. package/dist/parts.d.ts.map +1 -0
  156. package/dist/parts.js +13 -0
  157. package/dist/paths.d.ts +2 -0
  158. package/dist/paths.d.ts.map +1 -1
  159. package/dist/paths.js +26 -0
  160. package/dist/preparation.d.ts +20 -0
  161. package/dist/preparation.d.ts.map +1 -0
  162. package/dist/preparation.js +227 -0
  163. package/dist/presence.d.ts +52 -0
  164. package/dist/presence.d.ts.map +1 -0
  165. package/dist/presence.js +220 -0
  166. package/dist/queries.d.ts +16 -0
  167. package/dist/queries.d.ts.map +1 -0
  168. package/dist/queries.js +282 -0
  169. package/dist/query.d.ts +21 -203
  170. package/dist/query.d.ts.map +1 -1
  171. package/dist/query.js +3 -491
  172. package/dist/{chat/questions.d.ts → questions.d.ts} +5 -7
  173. package/dist/questions.d.ts.map +1 -0
  174. package/dist/{chat/questions.js → questions.js} +11 -7
  175. package/dist/queue.d.ts +40 -0
  176. package/dist/queue.d.ts.map +1 -0
  177. package/dist/queue.js +450 -0
  178. package/dist/reach.d.ts +20 -9
  179. package/dist/reach.d.ts.map +1 -1
  180. package/dist/reach.js +341 -111
  181. package/dist/react.d.ts +16 -4
  182. package/dist/react.d.ts.map +1 -1
  183. package/dist/react.js +35 -15
  184. package/dist/{chat/read.d.ts → read.d.ts} +2 -0
  185. package/dist/read.d.ts.map +1 -0
  186. package/dist/{chat/read.js → read.js} +4 -0
  187. package/dist/readiness.d.ts +14 -18
  188. package/dist/readiness.d.ts.map +1 -1
  189. package/dist/readiness.js +2 -8
  190. package/dist/reads.d.ts +38 -10
  191. package/dist/reads.d.ts.map +1 -1
  192. package/dist/reads.js +163 -83
  193. package/dist/recovery.d.ts +5 -2
  194. package/dist/recovery.d.ts.map +1 -1
  195. package/dist/recovery.js +65 -8
  196. package/dist/refresh.d.ts +18 -15
  197. package/dist/refresh.d.ts.map +1 -1
  198. package/dist/refresh.js +145 -60
  199. package/dist/refusal.d.ts +5 -0
  200. package/dist/refusal.d.ts.map +1 -0
  201. package/dist/refusal.js +55 -0
  202. package/dist/replica.d.ts +49 -36
  203. package/dist/replica.d.ts.map +1 -1
  204. package/dist/replica.js +320 -196
  205. package/dist/retried.d.ts +30 -0
  206. package/dist/retried.d.ts.map +1 -0
  207. package/dist/retried.js +72 -0
  208. package/dist/room-draft.d.ts +28 -11
  209. package/dist/room-draft.d.ts.map +1 -1
  210. package/dist/room-draft.js +194 -118
  211. package/dist/room-drafts.d.ts +24 -0
  212. package/dist/room-drafts.d.ts.map +1 -0
  213. package/dist/room-drafts.js +217 -0
  214. package/dist/rows.d.ts +31 -30
  215. package/dist/rows.d.ts.map +1 -1
  216. package/dist/rows.js +60 -120
  217. package/dist/runtime.d.ts +34 -0
  218. package/dist/runtime.d.ts.map +1 -0
  219. package/dist/runtime.js +1 -0
  220. package/dist/save.d.ts +43 -49
  221. package/dist/save.d.ts.map +1 -1
  222. package/dist/save.js +776 -275
  223. package/dist/saves.d.ts +32 -0
  224. package/dist/saves.d.ts.map +1 -0
  225. package/dist/saves.js +410 -0
  226. package/dist/search.d.ts +12 -95
  227. package/dist/search.d.ts.map +1 -1
  228. package/dist/search.js +10 -266
  229. package/dist/seed.d.ts +11 -2
  230. package/dist/seed.d.ts.map +1 -1
  231. package/dist/seed.js +71 -1
  232. package/dist/shared.d.ts +6 -7
  233. package/dist/shared.d.ts.map +1 -1
  234. package/dist/shared.js +75 -104
  235. package/dist/shown.d.ts +33 -0
  236. package/dist/shown.d.ts.map +1 -0
  237. package/dist/shown.js +295 -0
  238. package/dist/socket/admission.d.ts +7 -0
  239. package/dist/socket/admission.d.ts.map +1 -0
  240. package/dist/socket/admission.js +39 -0
  241. package/dist/socket/answers.d.ts +6 -0
  242. package/dist/socket/answers.d.ts.map +1 -0
  243. package/dist/socket/answers.js +20 -0
  244. package/dist/socket/lanes.d.ts +28 -0
  245. package/dist/socket/lanes.d.ts.map +1 -0
  246. package/dist/socket/lanes.js +103 -0
  247. package/dist/socket/reads.d.ts +10 -0
  248. package/dist/socket/reads.d.ts.map +1 -0
  249. package/dist/socket/reads.js +31 -0
  250. package/dist/socket/session.d.ts +38 -0
  251. package/dist/socket/session.d.ts.map +1 -0
  252. package/dist/socket/session.js +23 -0
  253. package/dist/socket.d.ts +67 -0
  254. package/dist/socket.d.ts.map +1 -0
  255. package/dist/socket.js +164 -0
  256. package/dist/source.d.ts +54 -0
  257. package/dist/source.d.ts.map +1 -0
  258. package/dist/source.js +82 -0
  259. package/dist/status.d.ts +4 -5
  260. package/dist/status.d.ts.map +1 -1
  261. package/dist/status.js +3 -7
  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 +214 -0
  269. package/dist/store/records.d.ts.map +1 -0
  270. package/dist/store/records.js +438 -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 +41 -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 +56 -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 +111 -164
  284. package/dist/store.d.ts.map +1 -1
  285. package/dist/store.js +159 -754
  286. package/dist/stored.d.ts +157 -0
  287. package/dist/stored.d.ts.map +1 -0
  288. package/dist/stored.js +649 -0
  289. package/dist/surface.d.ts +88 -0
  290. package/dist/surface.d.ts.map +1 -0
  291. package/dist/surface.js +220 -0
  292. package/dist/sync.d.ts +19 -0
  293. package/dist/sync.d.ts.map +1 -0
  294. package/dist/sync.js +535 -0
  295. package/dist/tables.d.ts +415 -0
  296. package/dist/tables.d.ts.map +1 -0
  297. package/dist/tables.js +761 -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 +5 -3
  304. package/dist/turn.d.ts.map +1 -1
  305. package/dist/turn.js +51 -21
  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 +9 -16
  310. package/dist/windows.d.ts.map +1 -1
  311. package/dist/windows.js +11 -75
  312. package/dist/wire.d.ts +89 -9
  313. package/dist/wire.d.ts.map +1 -1
  314. package/dist/wire.js +29 -6
  315. package/package.json +19 -19
  316. package/AGENTS.md +0 -299
  317. package/dist/acquisition.d.ts +0 -14
  318. package/dist/acquisition.d.ts.map +0 -1
  319. package/dist/acquisition.js +0 -24
  320. package/dist/attempt.d.ts +0 -16
  321. package/dist/attempt.d.ts.map +0 -1
  322. package/dist/attempt.js +0 -70
  323. package/dist/chat/acts.d.ts +0 -33
  324. package/dist/chat/acts.d.ts.map +0 -1
  325. package/dist/chat/acts.js +0 -36
  326. package/dist/chat/context.d.ts +0 -3
  327. package/dist/chat/context.d.ts.map +0 -1
  328. package/dist/chat/earlier.d.ts +0 -10
  329. package/dist/chat/earlier.d.ts.map +0 -1
  330. package/dist/chat/earlier.js +0 -54
  331. package/dist/chat/input.d.ts +0 -9
  332. package/dist/chat/input.d.ts.map +0 -1
  333. package/dist/chat/input.js +0 -99
  334. package/dist/chat/kit.d.ts +0 -61
  335. package/dist/chat/kit.d.ts.map +0 -1
  336. package/dist/chat/kit.js +0 -123
  337. package/dist/chat/lasting.d.ts +0 -6
  338. package/dist/chat/lasting.d.ts.map +0 -1
  339. package/dist/chat/lasting.js +0 -19
  340. package/dist/chat/list.d.ts +0 -27
  341. package/dist/chat/list.d.ts.map +0 -1
  342. package/dist/chat/list.js +0 -99
  343. package/dist/chat/markdown.d.ts +0 -20
  344. package/dist/chat/markdown.d.ts.map +0 -1
  345. package/dist/chat/markdown.js +0 -188
  346. package/dist/chat/message.d.ts +0 -6
  347. package/dist/chat/message.d.ts.map +0 -1
  348. package/dist/chat/message.js +0 -192
  349. package/dist/chat/messages.d.ts +0 -22
  350. package/dist/chat/messages.d.ts.map +0 -1
  351. package/dist/chat/messages.js +0 -155
  352. package/dist/chat/name.d.ts +0 -6
  353. package/dist/chat/name.d.ts.map +0 -1
  354. package/dist/chat/name.js +0 -37
  355. package/dist/chat/opened.d.ts +0 -11
  356. package/dist/chat/opened.d.ts.map +0 -1
  357. package/dist/chat/opened.js +0 -17
  358. package/dist/chat/overlay.d.ts +0 -31
  359. package/dist/chat/overlay.d.ts.map +0 -1
  360. package/dist/chat/overlay.js +0 -97
  361. package/dist/chat/parts.d.ts +0 -57
  362. package/dist/chat/parts.d.ts.map +0 -1
  363. package/dist/chat/parts.js +0 -119
  364. package/dist/chat/process.d.ts +0 -20
  365. package/dist/chat/process.d.ts.map +0 -1
  366. package/dist/chat/process.js +0 -32
  367. package/dist/chat/provider.d.ts +0 -63
  368. package/dist/chat/provider.d.ts.map +0 -1
  369. package/dist/chat/provider.js +0 -635
  370. package/dist/chat/question.d.ts +0 -9
  371. package/dist/chat/question.d.ts.map +0 -1
  372. package/dist/chat/question.js +0 -69
  373. package/dist/chat/questions.d.ts.map +0 -1
  374. package/dist/chat/read.d.ts.map +0 -1
  375. package/dist/chat/ready.d.ts +0 -34
  376. package/dist/chat/ready.d.ts.map +0 -1
  377. package/dist/chat/ready.js +0 -91
  378. package/dist/chat/style.d.ts +0 -4
  379. package/dist/chat/style.d.ts.map +0 -1
  380. package/dist/chat/style.js +0 -302
  381. package/dist/chat.d.ts +0 -14
  382. package/dist/chat.d.ts.map +0 -1
  383. package/dist/chat.js +0 -8
  384. package/dist/fence.d.ts +0 -6
  385. package/dist/fence.d.ts.map +0 -1
  386. package/dist/fence.js +0 -10
  387. package/dist/log.d.ts +0 -5
  388. package/dist/log.d.ts.map +0 -1
  389. package/dist/log.js +0 -14
  390. package/dist/read-side.d.ts +0 -25
  391. package/dist/read-side.d.ts.map +0 -1
  392. package/dist/read-side.js +0 -761
@@ -0,0 +1,724 @@
1
+ import { ARRAY_INDEX, bodied, byCodePoint, compared, cursorOf, heldKind, parse, pointerSegments, sql, } from '@agentshouse/mdmodel';
2
+ import { binaryRecord } from './binary.js';
3
+ import { definitionsAt } from './collection.js';
4
+ import { documentKey, pathOfKey, roomOfKey } from './rows.js';
5
+ import { FROM_START, MAXIMUM_WINDOW } from './windows.js';
6
+ const UNTYPED = 'any';
7
+ const { all, any, exists, fragment, grouped, joined, listed } = sql;
8
+ export const VISIBLE = (alias) => `${alias}.visible = 1 AND ${alias}.present = 1 AND ${alias}.queried = 1`;
9
+ function located(alias) {
10
+ return fragment(`(SELECT k.locator FROM replica_keys k WHERE k.key = ${alias}.key)`);
11
+ }
12
+ export function within(alias, path) {
13
+ return fragment(`(${alias}.path = ? OR (${alias}.path >= ? AND ${alias}.path < ?))`, [
14
+ path,
15
+ `${path}/`,
16
+ `${path}0`,
17
+ ]);
18
+ }
19
+ function ancestors(area) {
20
+ const depth = area.path.split('/').length;
21
+ return area.open.flatMap((open) => {
22
+ const segments = open.split('/');
23
+ return segments.slice(depth - 1, -1).map((_, at) => segments.slice(0, depth + at).join('/'));
24
+ });
25
+ }
26
+ function fenced(alias, key, area, folders) {
27
+ const opened = any([
28
+ ...area.open.map((open) => within(alias, open)),
29
+ ...(folders ? ancestors(area).map((path) => fragment(`${alias}.path = ?`, [path])) : []),
30
+ ]);
31
+ return all([
32
+ fragment(`${alias}.key = ?`, [key]),
33
+ within(alias, area.path),
34
+ fragment(`NOT ${grouped(opened).sql}`, opened.bind),
35
+ ]);
36
+ }
37
+ function reached(bound, alias) {
38
+ const reach = bound.reach;
39
+ if (reach === null)
40
+ return fragment('1');
41
+ const rooms = reach.rooms.flatMap((locator) => {
42
+ const key = bound.keys.get(locator);
43
+ return key === undefined ? [] : [key];
44
+ });
45
+ const documents = reach.documents.flatMap((held) => {
46
+ const key = bound.keys.get(roomOfKey(held));
47
+ return key === undefined ? [] : [[key, pathOfKey(held)]];
48
+ });
49
+ const outside = any(reach.areas.flatMap((area) => {
50
+ const key = bound.keys.get(area.room);
51
+ return key === undefined ? [] : [fenced(alias, key, area, false)];
52
+ }));
53
+ return all([
54
+ any([
55
+ ...(rooms.length === 0 ? [] : [fragment(`${alias}.key IN ${listed(rooms).sql}`, rooms)]),
56
+ ...(documents.length === 0
57
+ ? []
58
+ : [
59
+ fragment(`EXISTS (SELECT 1 FROM json_each(?) j WHERE j.value ->> 0 = ${alias}.key AND j.value ->> 1 = ${alias}.path)`, [JSON.stringify(documents)]),
60
+ ]),
61
+ ]),
62
+ ...(reach.areas.length === 0 ? [] : [fragment(`NOT ${grouped(outside).sql}`, outside.bind)]),
63
+ ]);
64
+ }
65
+ function related(bound, plan) {
66
+ const parts = [];
67
+ if (plan.hop !== null) {
68
+ parts.push(exists(joined([
69
+ fragment(`SELECT 1 FROM replica_links l JOIN replica_entries m ON m.key = d.key AND m.path = l.target AND ${VISIBLE('m')} WHERE l.doc = d.id AND l.kind = 'field' AND`),
70
+ grouped(sql.placed('l', plan.hop.field, 'link')),
71
+ fragment('AND'),
72
+ grouped(all([sql.scoped('m', plan.hop), reached(bound, 'm')])),
73
+ ], ' ')));
74
+ }
75
+ if (plan.backlinks !== null) {
76
+ const field = plan.backlinks.field;
77
+ parts.push(exists(joined([
78
+ fragment(`SELECT 1 FROM replica_links l JOIN replica_entries s ON s.id = l.doc AND ${VISIBLE('s')} WHERE l.key = d.key AND l.target = d.path AND l.kind = 'field' AND`),
79
+ ...(field === null ? [] : [grouped(sql.placed('l', field, 'link')), fragment('AND')]),
80
+ grouped(all([sql.scoped('s', plan.backlinks), reached(bound, 's')])),
81
+ ], ' ')));
82
+ }
83
+ return parts;
84
+ }
85
+ function filtered(bound, plan) {
86
+ const keys = bound.rooms.map(({ key }) => key);
87
+ return all([
88
+ fragment(`d.key IN ${listed(keys).sql} AND ${VISIBLE('d')}`, keys),
89
+ reached(bound, 'd'),
90
+ sql.tests('d', plan),
91
+ ...related(bound, plan),
92
+ ]);
93
+ }
94
+ function orderingOf(options) {
95
+ const stringSort = options.stringSort ?? 'locale';
96
+ const collated = stringSort === 'locale';
97
+ return {
98
+ direction: options.direction,
99
+ nulls: options.nulls ?? 'first',
100
+ stringSort,
101
+ locale: collated ? options.locale : undefined,
102
+ localeOptions: collated
103
+ ? options.localeOptions
104
+ : undefined,
105
+ };
106
+ }
107
+ const GROUP_ORDER = {
108
+ direction: 'asc',
109
+ nulls: 'first',
110
+ stringSort: 'locale',
111
+ locale: undefined,
112
+ localeOptions: undefined,
113
+ };
114
+ const GROUP_TIES = {
115
+ direction: 'asc',
116
+ nulls: 'last',
117
+ stringSort: 'lexical',
118
+ locale: undefined,
119
+ localeOptions: undefined,
120
+ };
121
+ const GREATEST = { ...GROUP_ORDER, direction: 'desc', nulls: 'last' };
122
+ const TIMES = {
123
+ created: { value: 'd.created', instant: 'd.created_instant' },
124
+ updated: { value: "(d.change ->> '$.time')", instant: 'd.changed_instant' },
125
+ };
126
+ function subfolderOf(column, prefix) {
127
+ const root = prefix ?? '';
128
+ const base = root === '' ? '' : `${root}/`;
129
+ const rest = `substr(${column}, length(?) + 1)`;
130
+ return fragment(`(CASE WHEN ${column} = ? THEN NULL WHEN instr(${rest}, '/') = 0 THEN ${column} ELSE ? || substr(${rest}, 1, instr(${rest}, '/') - 1) END)`, [root, base, base, base, base]);
131
+ }
132
+ function keyTerm(key, ordering, index, prefix) {
133
+ if (key.of === 'field') {
134
+ const alias = `o${index}`;
135
+ return {
136
+ term: {
137
+ ordering,
138
+ kind: fragment(`${alias}.kind`),
139
+ value: fragment(`${alias}.value`),
140
+ instant: fragment(`${alias}.instant`),
141
+ },
142
+ join: joined([
143
+ fragment(`LEFT JOIN replica_values ${alias} ON ${alias}.doc = d.id AND `),
144
+ grouped(sql.placed(alias, key.field, 'exact')),
145
+ fragment(` AND ${alias}.item = -1 AND ${alias}.kind NOT IN ('object', 'undefined')`),
146
+ ], ''),
147
+ };
148
+ }
149
+ if (key.of === 'size') {
150
+ return {
151
+ term: {
152
+ ordering,
153
+ kind: fragment("(CASE WHEN d.bytes IS NULL THEN 'null' ELSE 'number' END)"),
154
+ value: fragment('d.bytes'),
155
+ },
156
+ join: null,
157
+ };
158
+ }
159
+ if (key.of === 'created' || key.of === 'updated') {
160
+ const { value, instant } = TIMES[key.of];
161
+ return {
162
+ term: {
163
+ ordering,
164
+ kind: fragment(`(CASE WHEN ${value} IS NULL THEN 'null' ELSE 'string' END)`),
165
+ value: fragment(value),
166
+ instant: fragment(instant),
167
+ },
168
+ join: null,
169
+ };
170
+ }
171
+ const expression = key.of === 'room'
172
+ ? located('d')
173
+ : key.of === 'path'
174
+ ? fragment('d.path')
175
+ : key.of === 'type'
176
+ ? fragment('d.type')
177
+ : key.of === 'subfolder'
178
+ ? subfolderOf('d.folder', prefix)
179
+ : fragment('d.folder');
180
+ return {
181
+ term: {
182
+ ordering,
183
+ kind: fragment(`(CASE WHEN ${expression.sql} IS NULL THEN 'null' ELSE 'string' END)`, expression.bind),
184
+ value: expression,
185
+ },
186
+ join: null,
187
+ };
188
+ }
189
+ function decoded(kind, value) {
190
+ if (kind === 'string')
191
+ return value;
192
+ if (kind === 'number')
193
+ return typeof value === 'bigint' ? Number(value) : value;
194
+ if (kind === 'boolean')
195
+ return Number(value) === 1;
196
+ if (kind === 'array')
197
+ return JSON.parse(value);
198
+ return null;
199
+ }
200
+ function sortedOf(kind, value, instant) {
201
+ return sql.sortValue(kind, decoded(kind, value), instant);
202
+ }
203
+ function sortedValue(row, index) {
204
+ return sortedOf(row[`k${index}`], row[`v${index}`], row[`i${index}`]);
205
+ }
206
+ function decodedKey(key, row, index) {
207
+ const kind = row[`k${index}`];
208
+ if (key.of === 'field' && (kind === null || kind === undefined))
209
+ return undefined;
210
+ return sortedValue(row, index);
211
+ }
212
+ function instantOf(term) {
213
+ return term.instant ?? fragment('NULL');
214
+ }
215
+ const registered = new WeakMap();
216
+ function hashed(text) {
217
+ let hash = 0x811c9dc5;
218
+ for (let index = 0; index < text.length; index += 1) {
219
+ hash ^= text.charCodeAt(index);
220
+ hash = Math.imul(hash, 0x01000193);
221
+ }
222
+ return (hash >>> 0).toString(36);
223
+ }
224
+ function collating(connection) {
225
+ let held = registered.get(connection);
226
+ if (held === undefined) {
227
+ held = new Set();
228
+ registered.set(connection, held);
229
+ }
230
+ const names = held;
231
+ return (ordering, lists, total) => {
232
+ if (!lists && ordering.stringSort !== 'locale')
233
+ return null;
234
+ const inner = lists
235
+ ? {
236
+ ...ordering,
237
+ direction: 'asc',
238
+ nulls: ordering.direction === 'desc'
239
+ ? ordering.nulls === 'first'
240
+ ? 'last'
241
+ : 'first'
242
+ : ordering.nulls,
243
+ }
244
+ : ordering;
245
+ const spec = JSON.stringify([
246
+ lists,
247
+ total,
248
+ inner.nulls,
249
+ inner.stringSort,
250
+ inner.locale ?? null,
251
+ inner.localeOptions ?? null,
252
+ ]);
253
+ const name = `mdr_${lists ? 'l' : 's'}${hashed(spec)}`;
254
+ if (!names.has(name)) {
255
+ const collator = new Intl.Collator(inner.locale, inner.localeOptions);
256
+ connection.collate(name, lists
257
+ ? (left, right) => Math.sign(compared(JSON.parse(left), JSON.parse(right), inner)) || (total ? Math.sign(byCodePoint(left, right)) : 0)
258
+ : (left, right) => Math.sign(collator.compare(left, right)));
259
+ names.add(name);
260
+ }
261
+ return name;
262
+ };
263
+ }
264
+ function masked(pointers) {
265
+ const root = { whole: false, members: new Map() };
266
+ for (const pointer of pointers) {
267
+ let mask = root;
268
+ for (const token of pointerSegments(pointer)) {
269
+ const next = mask.members.get(token) ?? { whole: false, members: new Map() };
270
+ mask.members.set(token, next);
271
+ mask = next;
272
+ }
273
+ mask.whole = true;
274
+ }
275
+ return root;
276
+ }
277
+ function merged(left, right) {
278
+ if (left === undefined)
279
+ return right;
280
+ const members = new Map(left.members);
281
+ for (const [token, mask] of right.members)
282
+ members.set(token, merged(members.get(token), mask));
283
+ return { whole: left.whole || right.whole, members };
284
+ }
285
+ function cut(value, mask) {
286
+ if (mask.whole)
287
+ return value;
288
+ if (Array.isArray(value)) {
289
+ const each = {
290
+ whole: false,
291
+ members: new Map([...mask.members].filter(([token]) => !ARRAY_INDEX.test(token))),
292
+ };
293
+ const kept = [];
294
+ value.forEach((item, index) => {
295
+ const part = cut(item, merged(mask.members.get(String(index)), each));
296
+ if (part !== undefined)
297
+ kept[index] = part;
298
+ });
299
+ return kept.length === 0 ? undefined : kept;
300
+ }
301
+ if (typeof value !== 'object' || value === null)
302
+ return undefined;
303
+ const kept = Object.entries(value).flatMap(([key, held]) => {
304
+ const member = mask.members.get(key);
305
+ const part = member === undefined ? undefined : cut(held, member);
306
+ return part === undefined ? [] : [[key, part]];
307
+ });
308
+ return kept.length === 0 ? undefined : Object.fromEntries(kept);
309
+ }
310
+ function sectionsOf(sections, names) {
311
+ if (names === 'all')
312
+ return sections;
313
+ return Object.fromEntries(names.flatMap((name) => (Object.hasOwn(sections, name) ? [[name, sections[name]]] : [])));
314
+ }
315
+ function freeParts(room, path, type, content) {
316
+ const definitions = room.composition === undefined ? undefined : definitionsAt(room.composition, path);
317
+ const definition = definitions?.(type === UNTYPED ? undefined : type);
318
+ if (definition === undefined && type !== UNTYPED)
319
+ return null;
320
+ const read = parse(content, path, definition);
321
+ return read.ok ? read.document : null;
322
+ }
323
+ function documentOf(shape, queried, row, linked) {
324
+ const room = queried.locator;
325
+ const path = row.path;
326
+ const type = row.type;
327
+ const fileId = row.file_id;
328
+ const revision = row.revision;
329
+ if (type === null) {
330
+ const binary = row.binary === null ? null : JSON.parse(row.binary);
331
+ const content = row.content ?? null;
332
+ return {
333
+ room,
334
+ path,
335
+ ...(binary === null
336
+ ? {}
337
+ : {
338
+ binaryFile: binaryRecord(path, binary, revision ?? undefined),
339
+ }),
340
+ ...(shape.markdown && content !== null ? { markdown: content } : {}),
341
+ ...(shape.change ? { change: changeOf(row) } : {}),
342
+ ...(shape.fileId ? { fileId } : {}),
343
+ ...(shape.revision ? { revision } : {}),
344
+ ...heldFacts(shape, row),
345
+ };
346
+ }
347
+ const body = shape.sections !== null || shape.preamble
348
+ ? freeParts(queried, path, type, row.content)
349
+ : null;
350
+ const fields = JSON.parse(row.fields ?? '{}');
351
+ return {
352
+ room,
353
+ path,
354
+ type,
355
+ ...(shape.fields === null
356
+ ? {}
357
+ : {
358
+ fields: shape.fields === 'all'
359
+ ? fields
360
+ : (cut(fields, masked(shape.fields)) ?? {}),
361
+ }),
362
+ ...(shape.sections === null || body === null
363
+ ? {}
364
+ : { sections: sectionsOf(body.sections, shape.sections) }),
365
+ ...(shape.preamble && body !== null ? { preamble: body.preamble } : {}),
366
+ ...(shape.markdown ? { markdown: row.content } : {}),
367
+ ...(shape.backlinks ? { backlinks: linked.get(documentKey(room, path)) ?? [] } : {}),
368
+ ...(shape.change ? { change: changeOf(row) } : {}),
369
+ ...(shape.fileId ? { fileId } : {}),
370
+ ...(shape.revision ? { revision } : {}),
371
+ ...heldFacts(shape, row),
372
+ };
373
+ }
374
+ function changeOf(row) {
375
+ return row.change === null ? null : JSON.parse(row.change);
376
+ }
377
+ function heldFacts(shape, row) {
378
+ return {
379
+ ...(shape.size ? { size: row.bytes === null ? null : Number(row.bytes) } : {}),
380
+ ...(shape.created ? { created: row.created } : {}),
381
+ };
382
+ }
383
+ function backlinksOf(connection, bound, rows) {
384
+ const linked = new Map();
385
+ const byKey = new Map();
386
+ for (const row of rows) {
387
+ const key = Number(row.key);
388
+ const held = byKey.get(key) ?? [];
389
+ held.push(row.path);
390
+ byKey.set(key, held);
391
+ }
392
+ const reach = reached(bound, 's');
393
+ for (const [key, targets] of byKey) {
394
+ const room = bound.rooms.find((one) => one.key === key).locator;
395
+ const found = connection.rows(`SELECT target, path, type, field FROM (SELECT l.target, s.path, s.type, l.field, ROW_NUMBER() OVER (PARTITION BY l.target ORDER BY s.path, l.field) AS rank FROM (SELECT DISTINCT doc, target, field FROM replica_links WHERE key = ? AND kind = 'field' AND target IN (SELECT value FROM json_each(?))) l JOIN replica_entries s ON s.id = l.doc AND ${VISIBLE('s')} WHERE ${reach.sql}) WHERE rank <= ? ORDER BY target, path, field`, [key, JSON.stringify(targets), ...reach.bind, MAXIMUM_WINDOW]);
396
+ for (const row of found) {
397
+ const at = documentKey(room, row.target);
398
+ const held = linked.get(at) ?? [];
399
+ held.push({ path: row.path, type: row.type, field: row.field });
400
+ linked.set(at, held);
401
+ }
402
+ }
403
+ return linked;
404
+ }
405
+ function documents(connection, bound, plan, shape, budget) {
406
+ const terms = shape.orderBy.map(({ key, options }, index) => keyTerm(key, orderingOf(options), index, plan.root.pathPrefix));
407
+ const filter = filtered(bound, plan);
408
+ const cursor = plan.after?.values ?? null;
409
+ const room = located('d');
410
+ const after = cursor === null
411
+ ? null
412
+ : (() => {
413
+ const key = cursor[shape.orderBy.length];
414
+ return sql.following(terms.map(({ term }, index) => sql.beyond(bound.collate, term, cursor[index])), any([
415
+ fragment(`${room.sql} > ?`, [...room.bind, roomOfKey(key)]),
416
+ fragment(`${room.sql} = ? AND d.path > ?`, [
417
+ ...room.bind,
418
+ roomOfKey(key),
419
+ pathOfKey(key),
420
+ ]),
421
+ ]));
422
+ })();
423
+ const selected = joined([
424
+ fragment('SELECT d.id, d.key, d.path, d.type, d.binary, d.revision, d.fields, d.change, d.file_id, d.bytes, d.created'),
425
+ ...terms.map(({ term }, index) => joined([
426
+ fragment(`, ${term.kind.sql} AS k${index}`, term.kind.bind),
427
+ fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
428
+ fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
429
+ ], '')),
430
+ fragment(' FROM replica_entries d'),
431
+ ...terms.flatMap(({ join }) => (join === null ? [] : [fragment(' '), join])),
432
+ fragment(' WHERE '),
433
+ after === null ? filter : all([filter, after]),
434
+ fragment(' ORDER BY '),
435
+ joined([...terms.map(({ term }) => sql.sorted(bound.collate, term)), room, fragment('d.path')], ', '),
436
+ ...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
437
+ ], '');
438
+ const rows = connection.rows(selected.sql, selected.bind);
439
+ const windowed = plan.limit === null ? rows : rows.slice(0, plan.limit);
440
+ const kept = bodied(shape) ? withContent(connection, windowed, budget) : windowed;
441
+ const cut = kept.length;
442
+ const roomOf = new Map(bound.rooms.map(({ key, locator }) => [key, locator]));
443
+ const queried = new Map(bound.rooms.map((one) => [one.key, one]));
444
+ const linked = shape.backlinks ? backlinksOf(connection, bound, kept) : new Map();
445
+ const found = kept.map((row) => documentOf(shape, queried.get(Number(row.key)), row, linked));
446
+ const last = kept.at(-1);
447
+ const cursorAt = (row) => cursorOf([
448
+ ...shape.orderBy.map(({ key }, index) => decodedKey(key, row, index)),
449
+ documentKey(roomOf.get(Number(row.key)), row.path),
450
+ ]);
451
+ const more = cut < windowed.length && last !== undefined ? cursorAt(last) : undefined;
452
+ const next = more !== undefined || plan.limit === null || rows.length <= plan.limit
453
+ ? undefined
454
+ : last === undefined
455
+ ? (plan.after?.text ?? FROM_START)
456
+ : cursorAt(last);
457
+ const total = plan.total
458
+ ? (() => {
459
+ const counted = joined([fragment('SELECT count(*) AS held FROM replica_entries d WHERE '), filter], '');
460
+ return Number(connection.rows(counted.sql, counted.bind)[0].held);
461
+ })()
462
+ : undefined;
463
+ return {
464
+ documents: found,
465
+ ...(next === undefined ? {} : { next }),
466
+ ...(more === undefined ? {} : { more }),
467
+ ...(total === undefined ? {} : { total }),
468
+ };
469
+ }
470
+ function withContent(connection, rows, budget) {
471
+ if (rows.length === 0)
472
+ return rows;
473
+ const ids = rows.map((row) => row.id);
474
+ const sizes = new Map(connection
475
+ .rows(`SELECT id, octet_length(content) AS size FROM replica_entries WHERE id IN ${listed(ids).sql}`, ids)
476
+ .map((row) => [row.id, Number(row.size ?? 0)]));
477
+ let cut = rows.length;
478
+ let spent = 0;
479
+ for (let at = 0; budget !== null && at < rows.length; at += 1) {
480
+ const size = sizes.get(rows[at].id) ?? 0;
481
+ if (at > 0 && spent + size > budget) {
482
+ cut = at;
483
+ break;
484
+ }
485
+ spent += size;
486
+ }
487
+ const kept = rows.slice(0, cut);
488
+ const read = kept.map((row) => row.id);
489
+ const held = new Map(connection
490
+ .rows(`SELECT id, content FROM replica_entries WHERE id IN ${listed(read).sql}`, read)
491
+ .map((row) => [row.id, row.content]));
492
+ return kept.map((row) => ({ ...row, content: held.get(row.id) ?? null }));
493
+ }
494
+ function storedFolders(bound, plan, subfolder) {
495
+ if (plan.paths !== null || plan.fileIds !== null)
496
+ return null;
497
+ if (plan.hop !== null || plan.backlinks !== null)
498
+ return null;
499
+ if (subfolder && plan.folder !== null)
500
+ return null;
501
+ const whole = bound.rooms.filter(({ locator }) => bound.reach === null || bound.reach.rooms.includes(locator));
502
+ if (whole.length === 0)
503
+ return null;
504
+ const keys = whole.map(({ key }) => key);
505
+ const prefix = plan.root.pathPrefix ?? '';
506
+ const outside = (bound.reach?.areas ?? []).flatMap((area) => {
507
+ const key = bound.keys.get(area.room);
508
+ if (key === undefined)
509
+ return [];
510
+ const fence = fenced('f', key, area, true);
511
+ return [fragment(`NOT ${grouped(fence).sql}`, fence.bind)];
512
+ });
513
+ const under = prefix === ''
514
+ ? subfolder
515
+ ? [fragment("f.path <> ''")]
516
+ : []
517
+ : [
518
+ subfolder
519
+ ? fragment('(f.path >= ? AND f.path < ?)', [`${prefix}/`, `${prefix}0`])
520
+ : within('f', prefix),
521
+ ];
522
+ return all([
523
+ fragment(`f.key IN ${listed(keys).sql} AND f.layer = 0 AND f.present = 1`, keys),
524
+ ...outside,
525
+ ...under,
526
+ ...(plan.outside ?? []).map((folder) => fragment(`NOT ${within('f', folder).sql}`, within('f', folder).bind)),
527
+ ...(plan.folder === null ? [] : [fragment('f.path = ?', [plan.folder])]),
528
+ ]);
529
+ }
530
+ const SIZES = [
531
+ 'CASE WHEN d.binary IS NULL THEN 1 ELSE 0 END AS dn',
532
+ 'CASE WHEN d.binary IS NULL THEN d.bytes ELSE 0 END AS ds',
533
+ 'CASE WHEN d.binary IS NULL THEN 0 ELSE 1 END AS fn',
534
+ 'CASE WHEN d.binary IS NULL THEN 0 ELSE d.bytes END AS fs',
535
+ ];
536
+ const LATEST = "d.changed_instant || ' ' || (d.change ->> '$.time') AS u";
537
+ function greatestTerm(ordering) {
538
+ return { ordering, kind: fragment('gk'), value: fragment('gv'), instant: fragment('gi') };
539
+ }
540
+ function latestOf(held) {
541
+ if (typeof held !== 'string')
542
+ return null;
543
+ return held.slice(held.indexOf(' ') + 1);
544
+ }
545
+ function groups(connection, bound, plan, shape) {
546
+ const prefix = plan.root.pathPrefix;
547
+ const keyed = shape.groupBy.map((key, index) => {
548
+ const { term, join } = keyTerm(key, GROUP_ORDER, index, prefix);
549
+ const kind = fragment(`COALESCE(${term.kind.sql}, 'null')`, term.kind.bind);
550
+ return { term: { ...term, kind }, join };
551
+ });
552
+ const ordering = shape.orderBy.map(({ by }) => by);
553
+ const sized = shape.size || ordering.includes('size');
554
+ const dated = shape.updated || ordering.includes('updated');
555
+ const greatest = shape.greatest === null
556
+ ? null
557
+ : keyTerm({ of: 'field', field: shape.greatest }, GREATEST, keyed.length, prefix);
558
+ const filter = filtered(bound, plan);
559
+ const inner = joined([
560
+ fragment('SELECT '),
561
+ joined(keyed.map(({ term }, index) => joined([
562
+ fragment(`${term.kind.sql} AS k${index}`, term.kind.bind),
563
+ fragment(`, ${term.value.sql} AS v${index}`, term.value.bind),
564
+ fragment(`, ${instantOf(term).sql} AS i${index}`, instantOf(term).bind),
565
+ ], '')), ', '),
566
+ fragment(['', '1 AS n', ...(sized ? SIZES : []), ...(dated ? [LATEST] : [])].join(', ')),
567
+ ...(greatest === null
568
+ ? []
569
+ : [
570
+ fragment(`, ${greatest.term.kind.sql} AS gk`, greatest.term.kind.bind),
571
+ fragment(`, ${greatest.term.value.sql} AS gv`, greatest.term.value.bind),
572
+ fragment(`, ${instantOf(greatest.term).sql} AS gi`, instantOf(greatest.term).bind),
573
+ ]),
574
+ fragment(' FROM replica_entries d'),
575
+ ...[...keyed, ...(greatest === null ? [] : [greatest])].flatMap(({ join }) => join === null ? [] : [fragment(' '), join]),
576
+ fragment(' WHERE '),
577
+ filter,
578
+ ], '');
579
+ const only = shape.groupBy.length === 1 ? shape.groupBy[0].of : null;
580
+ const subfolder = only === 'subfolder';
581
+ const folders = only === 'folder' || subfolder ? storedFolders(bound, plan, subfolder) : null;
582
+ const folderKey = subfolder ? subfolderOf('f.path', prefix) : fragment('f.path');
583
+ const zeros = [
584
+ ...(sized ? ['0', '0', '0', '0'] : []),
585
+ ...(dated ? ['NULL'] : []),
586
+ ...(greatest === null ? [] : ['NULL', 'NULL', 'NULL']),
587
+ ];
588
+ const source = folders === null
589
+ ? inner
590
+ : joined([
591
+ inner,
592
+ fragment(` UNION ALL SELECT 'string' AS k0, ${folderKey.sql} AS v0, NULL AS i0, 0 AS n${zeros.map((one) => `, ${one}`).join('')} FROM replica_folders f WHERE `, folderKey.bind),
593
+ folders,
594
+ ], '');
595
+ const held = keyed
596
+ .map((_, index) => `k${index}, i${index}, CASE WHEN i${index} IS NULL THEN v${index} END`)
597
+ .join(', ');
598
+ const ranked = greatest === null
599
+ ? source
600
+ : joined([
601
+ fragment(`SELECT *, ROW_NUMBER() OVER (PARTITION BY ${held} ORDER BY `),
602
+ sql.sorted(bound.collate, greatestTerm(GREATEST)),
603
+ fragment(', gv) AS gr FROM ('),
604
+ source,
605
+ fragment(')'),
606
+ ], '');
607
+ const columns = [
608
+ ...keyed.map((_, index) => `k${index}, min(v${index}) AS v${index}, i${index}`),
609
+ 'sum(n) AS n',
610
+ ...(sized ? ['sum(dn) AS dn', 'sum(ds) AS ds', 'sum(fn) AS fn', 'sum(fs) AS fs'] : []),
611
+ ...(dated ? ['max(u) AS u'] : []),
612
+ ...(greatest === null
613
+ ? []
614
+ : ['gk', 'gv', 'gi'].map((column) => `min(CASE WHEN gr = 1 THEN ${column} END) AS ${column}`)),
615
+ ].join(', ');
616
+ const outer = (index, order) => ({
617
+ ordering: order,
618
+ kind: fragment(`k${index}`),
619
+ value: fragment(`v${index}`),
620
+ });
621
+ const termOf = (by, order) => {
622
+ if (by === 'count')
623
+ return { ordering: order, kind: fragment("'number'"), value: fragment('n') };
624
+ if (by === 'size') {
625
+ return { ordering: order, kind: fragment("'number'"), value: fragment('(ds + fs)') };
626
+ }
627
+ if (by === 'updated') {
628
+ return {
629
+ ordering: order,
630
+ kind: fragment("(CASE WHEN u IS NULL THEN 'null' ELSE 'string' END)"),
631
+ value: fragment("substr(u, instr(u, ' ') + 1)"),
632
+ instant: fragment("substr(u, 1, instr(u, ' ') - 1)"),
633
+ };
634
+ }
635
+ if (by === 'greatest')
636
+ return greatestTerm(order);
637
+ return { ...outer(by, order), instant: fragment(`i${by}`) };
638
+ };
639
+ const valueOf = (row, by) => {
640
+ if (by === 'count')
641
+ return Number(row.n);
642
+ if (by === 'size')
643
+ return Number(row.ds) + Number(row.fs);
644
+ if (by === 'updated') {
645
+ const text = latestOf(row.u);
646
+ return text === null ? null : sql.sortValue('string', text, row.u.split(' ')[0]);
647
+ }
648
+ if (by === 'greatest')
649
+ return sortedOf(row.gk, row.gv, row.gi);
650
+ return sortedValue(row, by);
651
+ };
652
+ const ordered = shape.orderBy.map(({ by, direction, nulls }) => termOf(by, { ...GROUP_ORDER, direction, nulls: nulls ?? GROUP_ORDER.nulls }));
653
+ const ties = keyed.flatMap((_, index) => [
654
+ { ...outer(index, GROUP_TIES), total: true },
655
+ { ordering: GROUP_TIES, kind: fragment("'string'"), value: fragment(`k${index}`) },
656
+ ]);
657
+ const cursor = plan.after?.values ?? null;
658
+ const after = cursor === null
659
+ ? null
660
+ : (() => {
661
+ const tieValues = cursor[shape.orderBy.length].flatMap((value) => [value, heldKind(value ?? null)]);
662
+ return sql.following(ordered.map((term, index) => sql.beyond(bound.collate, term, cursor[index])), sql.following(ties
663
+ .slice(0, -1)
664
+ .map((term, index) => sql.beyond(bound.collate, term, tieValues[index])), sql.beyond(bound.collate, ties.at(-1), tieValues.at(-1)).beyond));
665
+ })();
666
+ const grouping = joined([fragment(`SELECT ${columns} FROM (`), ranked, fragment(`) GROUP BY ${held}`)], '');
667
+ const windowed = joined([
668
+ fragment('SELECT * FROM ('),
669
+ grouping,
670
+ fragment(')'),
671
+ ...(after === null ? [] : [fragment(' WHERE '), after]),
672
+ fragment(' ORDER BY '),
673
+ joined([...ordered, ...ties].map((term) => sql.sorted(bound.collate, term)), ', '),
674
+ ...(plan.limit === null ? [] : [fragment(' LIMIT ?', [plan.limit + 1])]),
675
+ ], '');
676
+ const rows = connection.rows(windowed.sql, windowed.bind);
677
+ const kept = plan.limit === null ? rows : rows.slice(0, plan.limit);
678
+ const keyOf = (row) => keyed.map((_, index) => decoded(row[`k${index}`], row[`v${index}`]));
679
+ const found = kept.map((row) => ({
680
+ key: keyOf(row),
681
+ count: Number(row.n),
682
+ ...(shape.size
683
+ ? {
684
+ documents: { count: Number(row.dn), size: Number(row.ds) },
685
+ files: { count: Number(row.fn), size: Number(row.fs) },
686
+ }
687
+ : {}),
688
+ ...(shape.updated ? { updated: latestOf(row.u) } : {}),
689
+ ...(shape.greatest === null ? {} : { greatest: decoded(row.gk, row.gv) }),
690
+ }));
691
+ const last = kept.at(-1);
692
+ const next = plan.limit === null || rows.length <= plan.limit
693
+ ? undefined
694
+ : last === undefined
695
+ ? (plan.after?.text ?? FROM_START)
696
+ : cursorOf([...shape.orderBy.map(({ by }) => valueOf(last, by)), keyOf(last)]);
697
+ const total = plan.total
698
+ ? Number(connection.rows(joined([fragment('SELECT count(*) AS held FROM ('), grouping, fragment(')')], '').sql, grouping.bind)[0].held)
699
+ : undefined;
700
+ return {
701
+ groups: found,
702
+ ...(next === undefined ? {} : { next }),
703
+ ...(total === undefined ? {} : { total }),
704
+ };
705
+ }
706
+ function boundOf(connection, plan, rooms) {
707
+ return {
708
+ rooms: [...rooms].sort((left, right) => left.locator < right.locator ? -1 : left.locator > right.locator ? 1 : 0),
709
+ keys: new Map(rooms.map(({ locator, key }) => [locator, key])),
710
+ reach: plan.reach,
711
+ collate: collating(connection),
712
+ };
713
+ }
714
+ export function answerQuery(connection, plan, rooms, budget = null) {
715
+ const bound = boundOf(connection, plan, rooms);
716
+ if (rooms.length === 0) {
717
+ return plan.shape.kind === 'groups'
718
+ ? { groups: [], ...(plan.total ? { total: 0 } : {}) }
719
+ : { documents: [], ...(plan.total ? { total: 0 } : {}) };
720
+ }
721
+ return plan.shape.kind === 'groups'
722
+ ? groups(connection, bound, plan, plan.shape)
723
+ : documents(connection, bound, plan, plan.shape, budget);
724
+ }