alinea 2.0.0-preview.12 → 2.0.0-preview.14

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 (421) hide show
  1. package/dist/.init/content/pages/welcome.json +1 -1
  2. package/dist/.mcp/later/dev.json +1 -1
  3. package/dist/.mcp/online/dev.json +1 -1
  4. package/dist/adapter/next/cms.js +22 -10
  5. package/dist/adapter/next/context.js +1 -4
  6. package/dist/adapter/next/dashboardUrl.d.ts +6 -0
  7. package/dist/adapter/next/dashboardUrl.js +13 -0
  8. package/dist/adapter/next/handler.js +12 -1
  9. package/dist/adapter/next/syncCheck.d.ts +15 -2
  10. package/dist/adapter/next/syncCheck.js +26 -9
  11. package/dist/backend/CommitEntries.d.ts +17 -0
  12. package/dist/backend/CommitEntries.js +39 -0
  13. package/dist/backend/Handler.d.ts +9 -0
  14. package/dist/backend/Handler.js +84 -20
  15. package/dist/backend/api/BasicAuth.js +5 -1
  16. package/dist/backend/api/BlobStore.d.ts +21 -0
  17. package/dist/backend/api/BlobStore.js +63 -0
  18. package/dist/backend/api/CreateBackend.js +10 -8
  19. package/dist/backend/api/DatabaseApi.d.ts +4 -1
  20. package/dist/backend/api/DatabaseApi.js +104 -7
  21. package/dist/backend/api/GithubApi.d.ts +13 -0
  22. package/dist/backend/api/GithubApi.js +213 -50
  23. package/dist/backend/api/database.js +6 -6
  24. package/dist/backend/util/Syncable.d.ts +6 -0
  25. package/dist/backend/util/Syncable.js +3 -1
  26. package/dist/bundled.d.ts +1582 -426
  27. package/dist/chunks/{bun-sqlite-5SHXN3AT.js → bun-sqlite-RI6BYU5S.js} +3 -3
  28. package/dist/chunks/{chunk-ASYKC6IE.js → chunk-2PP7WJUJ.js} +2 -12
  29. package/dist/chunks/{chunk-QJGGYF6E.js → chunk-6FQ5IMNA.js} +7 -7
  30. package/dist/chunks/{chunk-JOF663EG.js → chunk-7DDPSWRY.js} +465 -2
  31. package/dist/chunks/{chunk-ETWOY4AL.js → chunk-A2I6MCCS.js} +1 -1
  32. package/dist/chunks/{chunk-W5AXN7GH.js → chunk-CYVOGRSD.js} +1 -2
  33. package/dist/chunks/{chunk-TIJTT5ZI.js → chunk-G43W7AME.js} +1 -0
  34. package/dist/chunks/{chunk-RE523BQ7.js → chunk-PMGIIYDD.js} +5830 -5830
  35. package/dist/chunks/{chunk-AIU6CMVT.js → chunk-PTYZMV5G.js} +1 -1
  36. package/dist/chunks/{chunk-66YZ47TY.js → chunk-RAR44T5O.js} +1 -1
  37. package/dist/chunks/{chunk-OHGAVV4G.js → chunk-SCV3TEXM.js} +2 -3
  38. package/dist/chunks/{chunk-UPY2RUZF.js → chunk-TWBREZWC.js} +30 -0
  39. package/dist/chunks/{chunk-QSN6RH6X.js → chunk-VA6NC3MI.js} +1 -1
  40. package/dist/chunks/{node-sqlite-XVNTNMCY.js → node-sqlite-C7VJ2NSN.js} +3 -3
  41. package/dist/cli/Generate.js +20 -7
  42. package/dist/cli/Mcp.js +1 -1
  43. package/dist/cli/Serve.js +1 -1
  44. package/dist/cli/bin.js +2 -3
  45. package/dist/cli/build/BuildEmitter.d.ts +5 -1
  46. package/dist/cli/build/BuildEmitter.js +8 -3
  47. package/dist/cli/generate/CompileConfig.js +10 -6
  48. package/dist/cli/generate/DevDB.d.ts +4 -1
  49. package/dist/cli/generate/DevDB.js +15 -7
  50. package/dist/cli/generate/GenerateDashboard.js +1 -0
  51. package/dist/cli/generate/MediaLocations.d.ts +18 -0
  52. package/dist/cli/generate/MediaLocations.js +69 -0
  53. package/dist/cli/serve/CreateLocalServer.js +1 -0
  54. package/dist/cli/serve/mcp/DevMcp.js +1 -1
  55. package/dist/cli/serve/mcp/McpTools.js +29 -1
  56. package/dist/cloud/CloudRemote.js +21 -7
  57. package/dist/cms.d.ts +4 -2
  58. package/dist/components/Alert.js +3 -3
  59. package/dist/components/AsyncList.d.ts +52 -0
  60. package/dist/components/AsyncList.js +69 -0
  61. package/dist/components/Badge.d.ts +3 -1
  62. package/dist/components/Badge.js +5 -3
  63. package/dist/components/Breadcrumbs.js +4 -4
  64. package/dist/components/Button.d.ts +6 -1
  65. package/dist/components/Button.js +14 -5
  66. package/dist/components/Calendar.js +20 -20
  67. package/dist/components/Checkbox.js +10 -10
  68. package/dist/components/CheckboxGroup.js +4 -4
  69. package/dist/components/Code.js +2 -2
  70. package/dist/components/Collapsible.js +3 -3
  71. package/dist/components/ColorSwatch.js +2 -2
  72. package/dist/components/ColorSwatchPicker.js +4 -4
  73. package/dist/components/ComboBox.d.ts +27 -9
  74. package/dist/components/ComboBox.js +42 -11
  75. package/dist/components/Command.js +14 -13
  76. package/dist/components/ContentCard.d.ts +3 -1
  77. package/dist/components/ContentCard.js +46 -24
  78. package/dist/components/ContentGrid.js +17 -17
  79. package/dist/components/DataList.js +3 -3
  80. package/dist/components/DateField.d.ts +4 -0
  81. package/dist/components/DateField.js +2 -2
  82. package/dist/components/DatePicker.js +2 -2
  83. package/dist/components/DateRangePicker.js +2 -2
  84. package/dist/components/Dialog.d.ts +14 -3
  85. package/dist/components/Dialog.js +36 -9
  86. package/dist/components/DropZone.js +5 -5
  87. package/dist/components/DropdownMenu.js +9 -9
  88. package/dist/components/Empty.js +4 -4
  89. package/dist/components/Field.d.ts +1 -1
  90. package/dist/components/Field.js +17 -11
  91. package/dist/components/Form.d.ts +23 -0
  92. package/dist/components/Form.js +64 -0
  93. package/dist/components/Link.d.ts +8 -1
  94. package/dist/components/List.js +24 -24
  95. package/dist/components/MediaPreview.js +7 -7
  96. package/dist/components/MultipleSelect.d.ts +13 -8
  97. package/dist/components/MultipleSelect.js +21 -21
  98. package/dist/components/NavRail.js +6 -6
  99. package/dist/components/NumberField.d.ts +5 -1
  100. package/dist/components/NumberField.js +6 -4
  101. package/dist/components/Page.js +6 -6
  102. package/dist/components/Popover.js +2 -2
  103. package/dist/components/PreviewFrame.js +6 -6
  104. package/dist/components/RadioGroup.js +9 -9
  105. package/dist/components/Resizable.js +4 -4
  106. package/dist/components/SearchField.d.ts +6 -1
  107. package/dist/components/SearchField.js +10 -8
  108. package/dist/components/Select.d.ts +13 -10
  109. package/dist/components/Select.js +16 -30
  110. package/dist/components/Sheet.d.ts +50 -0
  111. package/dist/components/Sheet.js +202 -0
  112. package/dist/components/Sidebar.js +5 -5
  113. package/dist/components/SortableList.d.ts +34 -30
  114. package/dist/components/SortableList.js +160 -263
  115. package/dist/components/Spinner.js +4 -4
  116. package/dist/components/Stack.d.ts +20 -0
  117. package/dist/components/Stack.js +45 -0
  118. package/dist/components/Surface.js +2 -2
  119. package/dist/components/Switch.js +6 -6
  120. package/dist/components/Table.d.ts +14 -2
  121. package/dist/components/Table.js +90 -51
  122. package/dist/components/Tabs.js +4 -4
  123. package/dist/components/TagGroup.js +10 -10
  124. package/dist/components/TextField.d.ts +7 -1
  125. package/dist/components/TextField.js +19 -10
  126. package/dist/components/TimeField.d.ts +6 -1
  127. package/dist/components/TimeField.js +48 -23
  128. package/dist/components/Timestamp.js +1 -1
  129. package/dist/components/ToggleGroup.js +2 -2
  130. package/dist/components/Toolbar.js +3 -3
  131. package/dist/components/Tooltip.js +6 -5
  132. package/dist/components/Tree.js +24 -24
  133. package/dist/components/internal/DateControl.js +9 -9
  134. package/dist/components/internal/FormScope.d.ts +2 -0
  135. package/dist/components/internal/FormScope.js +8 -0
  136. package/dist/components/internal/ListBoxGroup.d.ts +12 -0
  137. package/dist/components/internal/ListBoxGroup.js +46 -0
  138. package/dist/components/internal/ListBoxOption.d.ts +2 -2
  139. package/dist/components/internal/ListBoxOption.js +7 -7
  140. package/dist/components/internal/Locale.js +2 -2
  141. package/dist/components/internal/PopoverSurface.js +3 -2
  142. package/dist/components/internal/SelectionCheckbox.js +6 -6
  143. package/dist/components/internal/Slot.js +1 -1
  144. package/dist/components/internal/ToggleButton.js +4 -4
  145. package/dist/components/internal/Trigger.js +2 -2
  146. package/dist/components/internal/sortableDrag.d.ts +19 -0
  147. package/dist/components/internal/sortableDrag.js +175 -0
  148. package/dist/components/internal/useDragDrop.js +2 -2
  149. package/dist/components/types.d.ts +4 -0
  150. package/dist/components.d.ts +4 -0
  151. package/dist/components.js +4 -0
  152. package/dist/core/Client.js +12 -1
  153. package/dist/core/Config.d.ts +12 -1
  154. package/dist/core/Config.js +12 -0
  155. package/dist/core/DashboardEntry.d.ts +19 -0
  156. package/dist/core/DashboardEntry.js +0 -0
  157. package/dist/core/Document.d.ts +9 -1
  158. package/dist/core/Document.js +40 -8
  159. package/dist/core/EntryFields.d.ts +8 -2
  160. package/dist/core/Field.d.ts +5 -0
  161. package/dist/core/Filter.d.ts +19 -2
  162. package/dist/core/Filter.js +5 -0
  163. package/dist/core/Graph.d.ts +10 -2
  164. package/dist/core/Overview.d.ts +76 -9
  165. package/dist/core/Overview.js +1 -0
  166. package/dist/core/Root.js +1 -1
  167. package/dist/core/Scope.d.ts +5 -0
  168. package/dist/core/Scope.js +22 -5
  169. package/dist/core/TextDoc.d.ts +4 -0
  170. package/dist/core/Type.d.ts +25 -6
  171. package/dist/core/Type.js +25 -7
  172. package/dist/core/Validation.d.ts +0 -1
  173. package/dist/core/Validation.js +0 -6
  174. package/dist/core/db/LocalStore.d.ts +7 -0
  175. package/dist/core/field/CreateField.d.ts +1 -2
  176. package/dist/core/field/RichTextField.js +23 -4
  177. package/dist/core/media/MediaTypes.js +1 -1
  178. package/dist/core/source/GithubSource.d.ts +4 -0
  179. package/dist/core/source/GithubSource.js +127 -25
  180. package/dist/core/source/GithubUsage.d.ts +21 -0
  181. package/dist/core/source/GithubUsage.js +47 -0
  182. package/dist/core/source/Source.d.ts +2 -0
  183. package/dist/core/source/Source.js +11 -0
  184. package/dist/core/util/Async.d.ts +2 -0
  185. package/dist/core/util/Async.js +12 -1
  186. package/dist/core/util/Checks.js +1 -1
  187. package/dist/core/util/Slugs.js +4 -3
  188. package/dist/dashboard/App.css +13 -11
  189. package/dist/dashboard/App.d.ts +3 -2
  190. package/dist/dashboard/App.js +24 -6
  191. package/dist/dashboard/Href.d.ts +37 -0
  192. package/dist/dashboard/Href.js +46 -0
  193. package/dist/dashboard/ViewKeys.d.ts +1 -0
  194. package/dist/dashboard/ViewKeys.js +1 -0
  195. package/dist/dashboard/app/ActivityStatus.js +34 -12
  196. package/dist/dashboard/app/AuthView.js +6 -6
  197. package/dist/dashboard/app/BlockSettings.d.ts +9 -0
  198. package/dist/dashboard/app/BlockSettings.js +16 -0
  199. package/dist/dashboard/app/BlockSheet.d.ts +50 -0
  200. package/dist/dashboard/app/BlockSheet.js +207 -0
  201. package/dist/dashboard/app/BlockSheetStoryFrame.d.ts +6 -0
  202. package/dist/dashboard/app/BlockSheetStoryFrame.js +29 -0
  203. package/dist/dashboard/app/CompactField.js +14 -14
  204. package/dist/dashboard/app/CroppedImage.js +2 -2
  205. package/dist/dashboard/app/DashboardLayout.d.ts +1 -1
  206. package/dist/dashboard/app/DashboardLayout.js +15 -1
  207. package/dist/dashboard/app/EntryActions.d.ts +11 -0
  208. package/dist/dashboard/app/EntryActions.js +30 -0
  209. package/dist/dashboard/app/EntryFields.d.ts +15 -2
  210. package/dist/dashboard/app/EntryFields.js +24 -3
  211. package/dist/dashboard/app/EntryHeader.js +45 -36
  212. package/dist/dashboard/app/EntryReferences.js +5 -5
  213. package/dist/dashboard/app/EntrySidebar.js +24 -22
  214. package/dist/dashboard/app/EntrySidebarPreview.js +2 -2
  215. package/dist/dashboard/app/EntryStatusIcon.d.ts +23 -0
  216. package/dist/dashboard/app/EntryStatusIcon.js +58 -0
  217. package/dist/dashboard/app/EntryValidationModal.d.ts +6 -5
  218. package/dist/dashboard/app/EntryValidationModal.js +17 -17
  219. package/dist/dashboard/app/Explorer.d.ts +3 -1
  220. package/dist/dashboard/app/Explorer.js +90 -47
  221. package/dist/dashboard/app/ExplorerBatchActions.d.ts +3 -1
  222. package/dist/dashboard/app/ExplorerBatchActions.js +70 -45
  223. package/dist/dashboard/app/ExplorerCards.js +7 -1
  224. package/dist/dashboard/app/ExplorerControls.js +4 -4
  225. package/dist/dashboard/app/ExplorerList.js +7 -7
  226. package/dist/dashboard/app/ExplorerModal.js +4 -4
  227. package/dist/dashboard/app/ExplorerOverviewHeader.d.ts +6 -0
  228. package/dist/dashboard/app/ExplorerOverviewHeader.js +28 -0
  229. package/dist/dashboard/app/ExplorerTable.js +11 -8
  230. package/dist/dashboard/app/ExplorerTabs.d.ts +12 -0
  231. package/dist/dashboard/app/ExplorerTabs.js +42 -0
  232. package/dist/dashboard/app/ImageEditor.js +26 -26
  233. package/dist/dashboard/app/ImagePicker.js +3 -13
  234. package/dist/dashboard/app/LinkPicker.js +27 -25
  235. package/dist/dashboard/app/LocaleMenu.js +7 -7
  236. package/dist/dashboard/app/MediaPreviewCell.js +4 -4
  237. package/dist/dashboard/app/MoveConfirmDialog.d.ts +5 -0
  238. package/dist/dashboard/app/MoveConfirmDialog.js +79 -0
  239. package/dist/dashboard/app/OverviewCell.js +11 -14
  240. package/dist/dashboard/app/PendingUploadsView.js +3 -3
  241. package/dist/dashboard/app/ReadOnlyBadge.d.ts +1 -1
  242. package/dist/dashboard/app/RemoveDialog.d.ts +17 -0
  243. package/dist/dashboard/app/{DeleteDialog.js → RemoveDialog.js} +72 -27
  244. package/dist/dashboard/app/RemoveRedirect.d.ts +7 -0
  245. package/dist/dashboard/app/RemoveRedirect.js +123 -0
  246. package/dist/dashboard/app/SearchBar.d.ts +9 -0
  247. package/dist/dashboard/app/SearchBar.js +31 -0
  248. package/dist/dashboard/app/SettingsTab.d.ts +6 -0
  249. package/dist/dashboard/app/SettingsTab.js +27 -0
  250. package/dist/dashboard/app/SidebarTree.js +26 -71
  251. package/dist/dashboard/app/WorkspaceMenu.js +9 -9
  252. package/dist/dashboard/app/WorkspaceRoots.js +108 -104
  253. package/dist/dashboard/app/editor/FileEditor.js +17 -17
  254. package/dist/dashboard/app/modals/CreateEntry.js +3 -3
  255. package/dist/dashboard/app/pages/EntryPage.d.ts +2 -1
  256. package/dist/dashboard/app/pages/EntryPage.js +65 -43
  257. package/dist/dashboard/app/pages/RootPage.js +3 -3
  258. package/dist/dashboard/app/pages/SplashPage.js +91 -76
  259. package/dist/dashboard/app/pages/UsersPage.js +11 -11
  260. package/dist/dashboard/app/ui/DashboardModal.js +14 -13
  261. package/dist/dashboard/atoms/activity.d.ts +1 -0
  262. package/dist/dashboard/atoms/activity.js +8 -5
  263. package/dist/dashboard/atoms/config.d.ts +2 -0
  264. package/dist/dashboard/atoms/dashboard.d.ts +11 -0
  265. package/dist/dashboard/atoms/dashboard.js +9 -1
  266. package/dist/dashboard/atoms/editor.d.ts +7 -1
  267. package/dist/dashboard/atoms/editor.js +14 -2
  268. package/dist/dashboard/atoms/entry.js +1 -14
  269. package/dist/dashboard/atoms/entryTable.js +1 -0
  270. package/dist/dashboard/atoms/explorer.d.ts +19 -0
  271. package/dist/dashboard/atoms/explorer.js +62 -21
  272. package/dist/dashboard/atoms/graph.d.ts +5 -0
  273. package/dist/dashboard/atoms/graph.js +27 -2
  274. package/dist/dashboard/atoms/link.js +5 -2
  275. package/dist/dashboard/atoms/move.d.ts +5 -0
  276. package/dist/dashboard/atoms/move.js +29 -3
  277. package/dist/dashboard/atoms/moveConfirmation.d.ts +34 -0
  278. package/dist/dashboard/atoms/moveConfirmation.js +95 -0
  279. package/dist/dashboard/atoms/nav.d.ts +22 -12
  280. package/dist/dashboard/atoms/nav.js +200 -65
  281. package/dist/dashboard/atoms/overview.d.ts +32 -9
  282. package/dist/dashboard/atoms/overview.js +43 -28
  283. package/dist/dashboard/atoms/remove.d.ts +112 -0
  284. package/dist/dashboard/atoms/remove.js +268 -0
  285. package/dist/dashboard/atoms/root.d.ts +2 -2
  286. package/dist/dashboard/atoms/root.js +48 -27
  287. package/dist/dashboard/atoms/user.js +1 -1
  288. package/dist/dashboard/atoms/utils.js +1 -1
  289. package/dist/dashboard/boot/ActivityEvent.d.ts +1 -1
  290. package/dist/dashboard/boot/Boot.js +31 -17
  291. package/dist/dashboard/boot/DashboardWorker.d.ts +7 -0
  292. package/dist/dashboard/boot/DashboardWorker.js +99 -54
  293. package/dist/dashboard/boot/LoadWorker.js +20 -10
  294. package/dist/dashboard/boot/SyncEvent.d.ts +7 -0
  295. package/dist/dashboard/boot/SyncEvent.js +14 -0
  296. package/dist/dashboard/boot/WorkerDB.d.ts +1 -0
  297. package/dist/dashboard/boot/WorkerDB.js +3 -0
  298. package/dist/dashboard/cms/EntryTable.d.ts +4 -3
  299. package/dist/dashboard/cms/EntryTable.js +6 -3
  300. package/dist/dashboard/cms/ViewProps.d.ts +3 -2
  301. package/dist/dashboard/cms/hooks.d.ts +34 -18
  302. package/dist/dashboard/cms/hooks.js +43 -18
  303. package/dist/dashboard/contexts.d.ts +3 -2
  304. package/dist/dashboard/contexts.js +3 -2
  305. package/dist/dashboard/hook/UseEntryEditor.d.ts +1 -1
  306. package/dist/dashboard/hook/UseEntryEditor.js +7 -3
  307. package/dist/dashboard/icons.d.ts +1 -0
  308. package/dist/dashboard/icons.js +15 -0
  309. package/dist/database/BrowserEntryStore.d.ts +12 -62
  310. package/dist/database/BrowserEntryStore.js +116 -193
  311. package/dist/database/DatabaseTables.js +2 -2
  312. package/dist/database/EntryDatabase.d.ts +10 -1
  313. package/dist/database/EntryDatabase.js +45 -4
  314. package/dist/database/EntryStore.js +1 -11
  315. package/dist/database/EntryTransaction.js +0 -35
  316. package/dist/database/EntryUrlAliases.d.ts +4 -0
  317. package/dist/database/EntryUrlAliases.js +16 -10
  318. package/dist/database/driver/RuntimeDatabase.js +3 -3
  319. package/dist/database/driver/WasmDatabase.d.ts +5 -18
  320. package/dist/database/driver/WasmDatabase.js +15 -16
  321. package/dist/database/entry/EntryTable.d.ts +5 -3
  322. package/dist/database/entry/EntryTable.js +26 -9
  323. package/dist/database/query/Condition.d.ts +4 -1
  324. package/dist/database/query/Condition.js +16 -5
  325. package/dist/database/query/EntryQuery.js +60 -8
  326. package/dist/database/query/Relation.d.ts +13 -1
  327. package/dist/database/query/Relation.js +20 -2
  328. package/dist/database/query/Search.d.ts +7 -1
  329. package/dist/database/query/Search.js +55 -10
  330. package/dist/database/sync/EntrySyncer.d.ts +2 -0
  331. package/dist/database/sync/EntrySyncer.js +4 -2
  332. package/dist/database/sync/Ingest.d.ts +2 -1
  333. package/dist/database/sync/Ingest.js +20 -6
  334. package/dist/field/code/CodeField.view.js +13 -13
  335. package/dist/field/link/EntryLink.d.ts +8 -9
  336. package/dist/field/link/FileLink.d.ts +6 -6
  337. package/dist/field/link/ImageLink.d.ts +6 -9
  338. package/dist/field/link/ImageLink.js +0 -1
  339. package/dist/field/link/Link.d.ts +1 -1
  340. package/dist/field/link/LinkField.d.ts +5 -0
  341. package/dist/field/link/LinkField.view.js +251 -374
  342. package/dist/field/link/UrlLink.d.ts +4 -4
  343. package/dist/field/list/ListField.d.ts +2 -0
  344. package/dist/field/list/ListField.view.d.ts +15 -0
  345. package/dist/field/list/ListField.view.js +356 -299
  346. package/dist/field/metadata/MetadataField.d.ts +17 -7
  347. package/dist/field/metadata/MetadataField.js +14 -3
  348. package/dist/field/metadata/MetadataField.view.d.ts +1 -0
  349. package/dist/field/metadata/MetadataField.view.js +24 -20
  350. package/dist/field/richtext/RichTextBlock.d.ts +3 -1
  351. package/dist/field/richtext/RichTextBlock.js +181 -74
  352. package/dist/field/richtext/RichTextBlockHost.js +1 -1
  353. package/dist/field/richtext/RichTextExtensions.js +28 -28
  354. package/dist/field/richtext/RichTextField.view.js +10 -4
  355. package/dist/field/richtext/RichTextInsertMenu.js +45 -29
  356. package/dist/field/richtext/RichTextToolbar.js +26 -1
  357. package/dist/field/richtext/Toolbar.d.ts +9 -1
  358. package/dist/field/richtext/Toolbar.js +12 -2
  359. package/dist/field/select/SelectField.view.js +1 -0
  360. package/dist/field/views.js +2 -0
  361. package/dist/index.css +664 -332
  362. package/dist/index.d.ts +1 -0
  363. package/dist/index.js +3 -1
  364. package/dist/next.d.ts +2 -0
  365. package/dist/next.edge.d.ts +2 -0
  366. package/dist/picker/entry/EntryPicker.d.ts +3 -1
  367. package/dist/picker/entry/EntryPicker.js +15 -1
  368. package/dist/picker/url/UrlPicker.js +1 -0
  369. package/dist/preview/react.js +3 -0
  370. package/dist/query.d.ts +26 -14
  371. package/dist/query.js +4 -0
  372. package/dist/types.d.ts +8 -5
  373. package/dist/ui/Stack.d.ts +4 -2
  374. package/docs/ai-setup.md +3 -3
  375. package/docs/cli.md +4 -2
  376. package/docs/components/badge.md +1 -0
  377. package/docs/components/button.md +1 -0
  378. package/docs/components/combo-box.md +105 -16
  379. package/docs/components/content-card.md +1 -0
  380. package/docs/components/date-field.md +1 -1
  381. package/docs/components/dialog.md +51 -0
  382. package/docs/components/form.md +67 -0
  383. package/docs/components/link.md +10 -0
  384. package/docs/components/multiple-select.md +9 -17
  385. package/docs/components/number-field.md +5 -1
  386. package/docs/components/search-field.md +1 -0
  387. package/docs/components/select.md +9 -20
  388. package/docs/components/sortable-list.md +5 -20
  389. package/docs/components/stack.md +42 -0
  390. package/docs/components/table.md +84 -0
  391. package/docs/components/text-field.md +9 -1
  392. package/docs/components/time-field.md +5 -1
  393. package/docs/components.md +39 -1
  394. package/docs/configuration.md +4 -2
  395. package/docs/custom-fields.md +135 -7
  396. package/docs/deploy/self-host.md +3 -1
  397. package/docs/deploy.md +9 -5
  398. package/docs/fields/image.md +0 -1
  399. package/docs/fields/list.md +19 -0
  400. package/docs/fields/object.md +5 -3
  401. package/docs/fields/rich-text.md +31 -0
  402. package/docs/fields.md +1 -1
  403. package/docs/index.md +9 -7
  404. package/docs/instant-publishing.md +17 -11
  405. package/docs/overviews.md +131 -8
  406. package/docs/query/filtering.md +2 -0
  407. package/docs/query/related.md +58 -9
  408. package/docs/query.md +52 -1
  409. package/docs/quickstart.md +5 -5
  410. package/docs/schema/document.md +2 -0
  411. package/docs/schema/type.md +3 -1
  412. package/docs/upgrading.md +101 -19
  413. package/llms-full.txt +1036 -163
  414. package/package.json +2 -3
  415. package/readme.md +2 -2
  416. package/dist/core/db/EntryValidationError.d.ts +0 -13
  417. package/dist/core/db/EntryValidationError.js +0 -23
  418. package/dist/dashboard/app/DeleteDialog.d.ts +0 -11
  419. package/dist/dashboard/atoms/delete.d.ts +0 -68
  420. package/dist/dashboard/atoms/delete.js +0 -143
  421. package/dist/chunks/{chunk-MOWR65LM.js → chunk-PYDOMKJZ.js} +71 -71
@@ -1,5 +1,5 @@
1
1
  {
2
- "_id": "3KPEwd5jxca0cmXOExux5RBSXpo",
2
+ "_id": "3Kg2pCrguJIYIFElVMkyXOQsdZa",
3
3
  "_type": "Page",
4
4
  "_index": "a0",
5
5
  "_seeded": "welcome.json",
@@ -1 +1 @@
1
- {"url":"http://localhost:1","pid":2819}
1
+ {"url":"http://localhost:1","pid":3063}
@@ -1 +1 @@
1
- {"url":"http://localhost:40589","pid":2819}
1
+ {"url":"http://localhost:34865","pid":3063}
@@ -8,23 +8,24 @@ import {
8
8
  applyPreview as applyPreviewUpdate,
9
9
  decodePreviewRequest
10
10
  } from "alinea/backend/resolver/ParsePreview";
11
- import { createThrottledSync } from "alinea/backend/util/Syncable";
11
+ import { dashboardUrl } from "./dashboardUrl.js";
12
+ import { createThrottledSync, syncWait } from "alinea/backend/util/Syncable";
12
13
  import { Client } from "alinea/core/Client";
13
14
  import { CMS } from "alinea/core/CMS";
14
- import { Config } from "alinea/core/Config";
15
15
  import {
16
16
  withDevelopmentKey
17
17
  } from "alinea/core/Connection";
18
18
  import { outcome } from "alinea/core/Outcome";
19
19
  import { getScope } from "alinea/core/Scope";
20
20
  import { trace } from "alinea/core/Trace";
21
+ import { within } from "alinea/core/util/Async";
21
22
  import { getPreviewPayloadFromCookies } from "alinea/preview/PreviewCookies";
22
23
  import { Headers } from "@alinea/iso";
23
24
  import { cache } from "react";
24
25
  import { requestContext } from "./context.js";
25
26
  import { cachedResolve, developmentRevision } from "./devCache.js";
26
27
  import { RenderStats, summarizeQuery, timed } from "./renderStats.js";
27
- import { syncIfStale } from "./syncCheck.js";
28
+ import { contentShaWindow, syncIfStale, tagContentSha } from "./syncCheck.js";
28
29
  import { jsx } from "react/jsx-runtime";
29
30
  var preValidatedRemote = { validate: false };
30
31
  var NextCMS = class extends CMS {
@@ -66,6 +67,10 @@ var NextCMS = class extends CMS {
66
67
  * returns a fresh holder, so nothing is recorded there.
67
68
  */
68
69
  #render = cache(() => ({}));
70
+ /** Build renders join the content sha tag once, see `tagContentSha`. */
71
+ #tagContent = cache(
72
+ () => tagContentSha(contentShaWindow(this.config.syncInterval))
73
+ );
69
74
  /** One content revision per render, see `developmentRevision`. */
70
75
  #devRevision = cache(() => developmentRevision());
71
76
  #isDraft = cache(async () => {
@@ -95,7 +100,10 @@ var NextCMS = class extends CMS {
95
100
  (row) => timed(row, () => this.#syncDb(db, client, void 0))
96
101
  );
97
102
  try {
98
- const sha = await db.syncWith(client, preValidatedRemote);
103
+ const sha = await within(
104
+ db.syncWith(client, preValidatedRemote),
105
+ syncWait
106
+ );
99
107
  this.#syncedAt = Date.now();
100
108
  return sha;
101
109
  } catch (error) {
@@ -201,12 +209,19 @@ var NextCMS = class extends CMS {
201
209
  const answer = () => stats && row ? stats.executed(row, () => db.resolve(request)) : db.resolve(request);
202
210
  const syncInterval = request.disableSync ? Number.POSITIVE_INFINITY : request.syncInterval ?? this.config.syncInterval;
203
211
  if (hasPreview) return answer();
204
- if (!isBuild) {
212
+ if (isBuild) {
213
+ await this.#tagContent();
214
+ } else {
205
215
  const tracked = {
206
216
  sha: db.sha,
207
217
  syncWith: () => this.#syncDb(db, client, stats)
208
218
  };
209
- const settled = !isDraft && await syncIfStale(tracked, client, syncInterval);
219
+ const settled = !isDraft && await syncIfStale(
220
+ tracked,
221
+ client,
222
+ syncInterval,
223
+ contentShaWindow(this.config.syncInterval)
224
+ );
210
225
  if (!settled)
211
226
  await this.throttle(() => this.#syncDb(db, client, stats), syncInterval);
212
227
  }
@@ -264,16 +279,13 @@ var NextCMS = class extends CMS {
264
279
  if (!stats) return null;
265
280
  const { default: dynamic } = await import("next/dynamic.js");
266
281
  const { isDev, handlerUrl } = await requestContext(this.config);
267
- let adminPath = Config.adminPath(this.config);
268
- if (!adminPath.startsWith("/")) adminPath = `/${adminPath}`;
269
- const dashboardUrl = isDev ? adminPath : new URL(`${adminPath}.html`, handlerUrl).href;
270
282
  const NextPreviews = dynamic(() => import("./previews.js"), {
271
283
  ssr: false
272
284
  });
273
285
  return /* @__PURE__ */ jsx(
274
286
  NextPreviews,
275
287
  {
276
- dashboardUrl,
288
+ dashboardUrl: dashboardUrl(this.config, isDev, handlerUrl),
277
289
  widget,
278
290
  stats: widget && showStats ? stats.settled() : void 0,
279
291
  workspace,
@@ -15,13 +15,10 @@ async function requestContext(config, request) {
15
15
  applyAuth: (init) => forwardDevelopmentCredentials(request, apiKey, init)
16
16
  };
17
17
  }
18
- const nodeEnv = process.env.NODE_ENV;
19
- const baseUrl = Config.baseUrl(config, nodeEnv);
20
- if (!baseUrl) throw new Error(`Missing baseUrl in config for ${nodeEnv}`);
21
18
  const localOrigin = process.env.__NEXT_PRIVATE_ORIGIN;
22
19
  return {
23
20
  isDev: false,
24
- handlerUrl: new URL(Config.handlerUrl(config), baseUrl),
21
+ handlerUrl: Config.serverHandlerUrl(config),
25
22
  apiKey,
26
23
  publicUrl: localOrigin ? new URL(localOrigin) : void 0
27
24
  };
@@ -0,0 +1,6 @@
1
+ import { Config } from 'alinea/core/Config';
2
+ /**
3
+ * The dashboard as the site serves it: in development the site proxies the
4
+ * admin path to the dev server, in production it serves the generated file.
5
+ */
6
+ export declare function dashboardUrl(config: Config, isDev: boolean, handlerUrl: URL | string): string;
@@ -0,0 +1,13 @@
1
+ import "../../chunks/chunk-CQ52GEHY.js";
2
+
3
+ // src/adapter/next/dashboardUrl.ts
4
+ import { Config } from "alinea/core/Config";
5
+ function dashboardUrl(config, isDev, handlerUrl) {
6
+ if (!isDev)
7
+ return new URL(`/${Config.dashboardFile(config)}`, handlerUrl).href;
8
+ const adminPath = Config.adminPath(config);
9
+ return adminPath.startsWith("/") ? adminPath : `/${adminPath}`;
10
+ }
11
+ export {
12
+ dashboardUrl
13
+ };
@@ -10,6 +10,7 @@ import {
10
10
  import {
11
11
  createHandler as createCoreHandler
12
12
  } from "alinea/backend/Handler";
13
+ import { committedEntries, removedEntries } from "alinea/backend/CommitEntries";
13
14
  import { HandleAction } from "alinea/backend/HandleAction";
14
15
  import { JWTPreviews } from "alinea/backend/util/JWTPreviews";
15
16
  import { CloudRemote } from "alinea/cloud/CloudRemote";
@@ -79,18 +80,28 @@ function createHandlerWithDatabase(input, openGeneratedDatabase) {
79
80
  });
80
81
  let sha;
81
82
  let user;
83
+ let before = [];
82
84
  try {
83
85
  user = await devServer.user();
84
86
  if (!user) return failure(401, "Unauthorized");
85
87
  const adjusted = await options.beforeCommit?.({ mutations, user });
86
88
  if (adjusted) mutations = [...adjusted];
89
+ if (options.afterCommit)
90
+ before = await committedEntries(devServer, mutations).catch(() => []);
87
91
  ({ sha } = await devServer.mutate(mutations));
88
92
  } catch (error) {
89
93
  if (error instanceof HttpError) return failure(error.code, error.message);
90
94
  throw error;
91
95
  }
92
96
  try {
93
- await afterCommit({ mutations, sha, user });
97
+ const entries = options.afterCommit ? await committedEntries(devServer, mutations) : [];
98
+ await afterCommit({
99
+ mutations,
100
+ sha,
101
+ user,
102
+ entries,
103
+ removed: removedEntries(before, entries)
104
+ });
94
105
  } catch (error) {
95
106
  console.error("Alinea afterCommit hook failed", error);
96
107
  }
@@ -6,13 +6,26 @@ interface SyncableDB {
6
6
  syncWith(source: RemoteSource, options?: SyncOptions): Promise<string>;
7
7
  }
8
8
  export declare const CONTENT_SHA_TAG = "alinea-content-sha";
9
+ /**
10
+ * How long renders share the latest content sha. Configured through the
11
+ * config's `syncInterval`, never a query's: all renders share one entry.
12
+ */
13
+ export declare function contentShaWindow(syncInterval?: number): number;
14
+ /**
15
+ * Tag a static render with the content sha without looking it up. Renders at
16
+ * build skip `syncIfStale`, so this is how their pages join the tag that
17
+ * commits revalidate, and get the same time-based safety net as pages
18
+ * rendered at runtime.
19
+ */
20
+ export declare function tagContentSha(revalidate?: number): Promise<void>;
9
21
  export declare function revalidateContentSha(): Promise<void>;
10
22
  /**
11
23
  * Sync the isolate-local db only when it is behind the shared content sha.
12
24
  * Returns true when the freshness question is settled here (synced, forced,
13
25
  * or explicitly disabled) and the caller can skip its throttled sync.
14
26
  * Returns false when the sha could not be determined, so the caller falls
15
- * back to its existing throttle behavior.
27
+ * back to its existing throttle behavior. `shaWindow` is how long renders
28
+ * share the latest sha, see `contentShaWindow`.
16
29
  */
17
- export declare function syncIfStale(db: SyncableDB, client: Client, syncInterval?: number, options?: SyncOptions): Promise<boolean>;
30
+ export declare function syncIfStale(db: SyncableDB, client: Client, syncInterval?: number, shaWindow?: number, options?: SyncOptions): Promise<boolean>;
18
31
  export {};
@@ -2,22 +2,27 @@ import "../../chunks/chunk-CQ52GEHY.js";
2
2
 
3
3
  // src/adapter/next/syncCheck.ts
4
4
  var CONTENT_SHA_TAG = "alinea-content-sha";
5
- var SHA_REVALIDATE_SECONDS = 60;
5
+ var SHA_REVALIDATE_SECONDS = 300;
6
+ function contentShaWindow(syncInterval) {
7
+ if (syncInterval !== void 0 && syncInterval > 0 && Number.isFinite(syncInterval))
8
+ return syncInterval;
9
+ return SHA_REVALIDATE_SECONDS;
10
+ }
6
11
  var inflight;
7
- function latestSha(client) {
12
+ function latestSha(client, revalidate) {
8
13
  if (inflight) return inflight;
9
- inflight = loadLatestSha(client).finally(() => {
14
+ inflight = loadLatestSha(client, revalidate).finally(() => {
10
15
  inflight = void 0;
11
16
  });
12
17
  return inflight;
13
18
  }
14
- async function loadLatestSha(client) {
19
+ async function loadLatestSha(client, revalidate) {
15
20
  try {
16
21
  const { unstable_cache } = await import("next/cache.js");
17
22
  const getSha = unstable_cache(
18
23
  () => client.getSha(),
19
24
  ["alinea-content-sha"],
20
- { revalidate: SHA_REVALIDATE_SECONDS, tags: [CONTENT_SHA_TAG] }
25
+ { revalidate, tags: [CONTENT_SHA_TAG] }
21
26
  );
22
27
  return await getSha();
23
28
  } catch {
@@ -28,26 +33,38 @@ async function loadLatestSha(client) {
28
33
  }
29
34
  }
30
35
  }
36
+ async function tagContentSha(revalidate = SHA_REVALIDATE_SECONDS) {
37
+ try {
38
+ const { unstable_cache } = await import("next/cache.js");
39
+ await unstable_cache(async () => true, ["alinea-content-sha-tag"], {
40
+ revalidate,
41
+ tags: [CONTENT_SHA_TAG]
42
+ })();
43
+ } catch {
44
+ }
45
+ }
31
46
  async function revalidateContentSha() {
32
47
  try {
33
48
  const { revalidateTag } = await import("next/cache.js");
34
- revalidateTag(CONTENT_SHA_TAG, { expire: 0 });
49
+ revalidateTag(CONTENT_SHA_TAG, "max");
35
50
  } catch {
36
51
  }
37
52
  }
38
- async function syncIfStale(db, client, syncInterval, options) {
53
+ async function syncIfStale(db, client, syncInterval, shaWindow = SHA_REVALIDATE_SECONDS, options) {
39
54
  if (syncInterval === Number.POSITIVE_INFINITY) return true;
40
55
  if (syncInterval === 0) {
41
56
  await db.syncWith(client, options);
42
57
  return true;
43
58
  }
44
- const expected = await latestSha(client);
59
+ const expected = await latestSha(client, shaWindow);
45
60
  if (!expected) return false;
46
61
  if (expected !== await db.sha) await db.syncWith(client, options);
47
62
  return true;
48
63
  }
49
64
  export {
50
65
  CONTENT_SHA_TAG,
66
+ contentShaWindow,
51
67
  revalidateContentSha,
52
- syncIfStale
68
+ syncIfStale,
69
+ tagContentSha
53
70
  };
@@ -0,0 +1,17 @@
1
+ import type { DashboardEntry } from 'alinea/core/DashboardEntry';
2
+ import type { Mutation } from 'alinea/core/db/Mutation';
3
+ import type { Resolver } from 'alinea/core/Resolver';
4
+ /** An entry that no longer exists after a commit */
5
+ export interface RemovedEntry {
6
+ id: string;
7
+ type: string;
8
+ locale: string | null;
9
+ /** The url the entry had before the commit */
10
+ url: string;
11
+ }
12
+ /**
13
+ * The latest version, in every locale, of the entries that mutations change
14
+ */
15
+ export declare function committedEntries(resolver: Resolver, mutations: ReadonlyArray<Mutation>): Promise<Array<DashboardEntry>>;
16
+ /** The entries of `before` that are not in `after` */
17
+ export declare function removedEntries(before: Array<DashboardEntry>, after: Array<DashboardEntry>): Array<RemovedEntry>;
@@ -0,0 +1,39 @@
1
+ import "../chunks/chunk-CQ52GEHY.js";
2
+
3
+ // src/backend/CommitEntries.ts
4
+ import { Entry } from "alinea/core/Entry";
5
+ var select = {
6
+ id: Entry.id,
7
+ type: Entry.type,
8
+ title: Entry.title,
9
+ path: Entry.path,
10
+ url: Entry.url,
11
+ locale: Entry.locale,
12
+ status: Entry.status,
13
+ workspace: Entry.workspace,
14
+ root: Entry.root,
15
+ parentId: Entry.parentId,
16
+ data: Entry.data
17
+ };
18
+ function entryIds(mutations) {
19
+ const ids = mutations.map((mutation) => "id" in mutation ? mutation.id : "");
20
+ return [...new Set(ids.filter(Boolean))];
21
+ }
22
+ async function committedEntries(resolver, mutations) {
23
+ const ids = entryIds(mutations);
24
+ if (ids.length === 0) return [];
25
+ return await resolver.resolve({
26
+ id: { in: ids },
27
+ status: "preferDraft",
28
+ select
29
+ });
30
+ }
31
+ function removedEntries(before, after) {
32
+ const key = (entry) => `${entry.id}:${entry.locale}`;
33
+ const remaining = new Set(after.map(key));
34
+ return before.filter((entry) => !remaining.has(key(entry))).map(({ id, type, locale, url }) => ({ id, type, locale, url }));
35
+ }
36
+ export {
37
+ committedEntries,
38
+ removedEntries
39
+ };
@@ -1,6 +1,8 @@
1
1
  import type { CMS } from 'alinea/core/CMS';
2
2
  import type { AuthedContext, RemoteConnection, RequestContext } from 'alinea/core/Connection';
3
3
  import type { LocalStore } from 'alinea/core/db/LocalStore';
4
+ import type { DashboardEntry } from 'alinea/core/DashboardEntry';
5
+ import { type RemovedEntry } from './CommitEntries.js';
4
6
  import type { Mutation } from 'alinea/core/db/Mutation';
5
7
  import type { User } from 'alinea/core/User';
6
8
  export interface Handler {
@@ -14,6 +16,13 @@ export interface BeforeCommitContext {
14
16
  }
15
17
  export interface AfterCommitContext extends BeforeCommitContext {
16
18
  sha: string;
19
+ /**
20
+ * The entries the mutations changed as they are after the commit: the
21
+ * latest version in each locale
22
+ */
23
+ entries: Array<DashboardEntry>;
24
+ /** Entries that no longer exist after the commit, with the url they had */
25
+ removed: Array<RemovedEntry>;
17
26
  }
18
27
  export interface HandlerHooks {
19
28
  beforeCommit?(context: BeforeCommitContext): HookResponse<ReadonlyArray<Mutation>>;
@@ -4,7 +4,7 @@ import {
4
4
  object,
5
5
  optional,
6
6
  string
7
- } from "../chunks/chunk-UPY2RUZF.js";
7
+ } from "../chunks/chunk-TWBREZWC.js";
8
8
  import {
9
9
  PLazy
10
10
  } from "../chunks/chunk-IKINPSS5.js";
@@ -19,6 +19,10 @@ import {
19
19
  } from "alinea/core/BlobTransport";
20
20
  import { Config } from "alinea/core/Config";
21
21
  import { developmentKeyHeader } from "alinea/core/Connection";
22
+ import {
23
+ committedEntries,
24
+ removedEntries
25
+ } from "./CommitEntries.js";
22
26
  import { Entry } from "alinea/core/Entry";
23
27
  import { ErrorCode, HttpError } from "alinea/core/HttpError";
24
28
  import { MediaLocation } from "alinea/core/media/MediaLocation";
@@ -27,15 +31,18 @@ import { assertUploadSize } from "alinea/core/media/UploadLimits";
27
31
  import { Permission, Policy } from "alinea/core/Role";
28
32
  import { getScope } from "alinea/core/Scope";
29
33
  import { ShaMismatchError } from "alinea/core/source/ShaMismatchError";
34
+ import { sourceAtTree } from "alinea/core/source/Source";
30
35
  import { base64 } from "alinea/core/util/Encoding";
36
+ import { within } from "alinea/core/util/Async";
31
37
  import { isRecord } from "alinea/core/util/Objects";
32
- import { isAbsolute, normalize } from "alinea/core/util/Paths";
38
+ import { fileVersions } from "alinea/core/util/EntryFilenames";
39
+ import { isAbsolute, normalize, relative } from "alinea/core/util/Paths";
33
40
  import { InvalidCredentialsError, MissingCredentialsError } from "./Auth.js";
34
41
  import { HandleAction } from "./HandleAction.js";
35
42
  import { proxyMediaUrl } from "./api/ProxyMedia.js";
36
43
  import { applyPreview, decodePreviewRequest } from "./resolver/ParsePreview.js";
37
44
  import { compressResponse } from "./router/Router.js";
38
- import { createThrottledSync } from "./util/Syncable.js";
45
+ import { createThrottledSync, syncWait } from "./util/Syncable.js";
39
46
  var PrepareBody = object({
40
47
  filename: string,
41
48
  size: optional(number)
@@ -53,9 +60,9 @@ function createHandler({
53
60
  const local = await db;
54
61
  const simulateLatency = process.env.ALINEA_LATENCY;
55
62
  if (simulateLatency) await new Promise((resolve) => setTimeout(resolve, 2e3));
56
- async function syncForRead(cnx) {
63
+ async function syncForRead(sync) {
57
64
  try {
58
- await local.syncWith(cnx);
65
+ await within(sync, syncWait);
59
66
  } catch (error) {
60
67
  console.warn(
61
68
  `Alinea could not sync with the remote, serving current content: ${error instanceof Error ? error.message : String(error)}`
@@ -64,7 +71,7 @@ function createHandler({
64
71
  }
65
72
  async function periodicSync(cnx, syncInterval) {
66
73
  if (dev) return;
67
- return throttle(() => syncForRead(cnx), syncInterval);
74
+ return syncForRead(throttle(() => local.syncWith(cnx), syncInterval));
68
75
  }
69
76
  try {
70
77
  const previews = new JWTPreviews(context.apiKey);
@@ -258,7 +265,7 @@ function createHandler({
258
265
  } else {
259
266
  const preview = await decodePreviewRequest(query.preview);
260
267
  if ("contentHash" in preview && await local.sha !== preview.contentHash)
261
- await syncForRead(cnx);
268
+ await syncForRead(local.syncWith(cnx));
262
269
  query.preview = await applyPreview(local, preview);
263
270
  }
264
271
  return Response.json(await local.resolve(query) ?? null);
@@ -278,57 +285,92 @@ function createHandler({
278
285
  user: user.claims
279
286
  });
280
287
  if (adjusted) mutations = adjusted;
288
+ const before = hooks.afterCommit ? await committedEntries(local, mutations) : [];
281
289
  const attempt = async (retry = 0) => {
282
290
  if (retry > 0) await local.syncWith(cnx);
283
291
  const request2 = {
284
292
  ...await local.request(mutations, policy),
285
293
  user: user.claims
286
294
  };
295
+ let sha2;
287
296
  try {
288
- let { sha: sha2 } = await cnx.write(request2);
289
- if (sha2 === request2.intoSha) {
290
- await local.write(request2);
291
- } else {
292
- sha2 = await local.syncWith(cnx);
293
- }
294
- return sha2;
297
+ sha2 = (await cnx.write(request2)).sha;
295
298
  } catch (error) {
296
299
  const isConflict = error instanceof ShaMismatchError || error instanceof HttpError && error.code === 409 || error instanceof Response && error.status === 409;
297
300
  if (isConflict && retry < 3) return attempt(retry + 1);
298
301
  throw error;
299
302
  }
303
+ try {
304
+ if (sha2 === request2.intoSha) await local.write(request2);
305
+ else sha2 = await local.syncWith(cnx);
306
+ } catch {
307
+ sha2 = await local.syncWith(cnx).catch(() => sha2);
308
+ }
309
+ return sha2;
300
310
  };
301
311
  const sha = await attempt();
302
312
  try {
303
- await hooks.afterCommit?.({ mutations, sha, user: user.claims });
313
+ if (hooks.afterCommit) {
314
+ const entries = await committedEntries(local, mutations);
315
+ await hooks.afterCommit({
316
+ mutations,
317
+ sha,
318
+ user: user.claims,
319
+ entries,
320
+ removed: removedEntries(before, entries)
321
+ });
322
+ }
304
323
  } catch (error) {
305
324
  console.error("Alinea afterCommit hook failed", error);
306
325
  }
307
326
  return Response.json({ sha });
308
327
  }
309
328
  if (action === HandleAction.History && request.method === "GET") {
310
- expectUser();
329
+ const user = expectUser();
311
330
  expectJson();
312
331
  const file = string(url.searchParams.get("file"));
313
332
  const revisionId = string.nullable(url.searchParams.get("revisionId"));
314
- const result = await (revisionId ? cnx.revisionData(file, revisionId) : cnx.revisions(file));
333
+ const record = revisionId ? await cnx.revisionData(file, revisionId) : void 0;
334
+ const contentDir = Config.contentDir(cms.config);
335
+ const owners = () => historyEntries(local, contentDir, file, record);
336
+ let entries = await owners();
337
+ if (entries.length === 0) {
338
+ await syncForRead(local.syncWith(cnx));
339
+ entries = await owners();
340
+ }
341
+ if (entries.length === 0)
342
+ throw new HttpError(ErrorCode.NotFound, "Entry not found");
343
+ const policy = await user.policy;
344
+ for (const entry of entries) policy.assert(Permission.Read, entry);
345
+ const result = revisionId ? record : await cnx.revisions(file);
315
346
  return Response.json(result ?? null);
316
347
  }
317
348
  if (action === HandleAction.Tree && request.method === "GET") {
318
349
  expectJson();
319
350
  const sha = string(url.searchParams.get("sha"));
320
- await syncForRead(cnx);
351
+ let source = cnx;
352
+ try {
353
+ const remoteTree = await cnx.getTreeIfDifferent(sha);
354
+ if (!remoteTree) return Response.json(null);
355
+ source = sourceAtTree(cnx, remoteTree);
356
+ } catch {
357
+ }
358
+ await syncForRead(local.syncWith(source));
321
359
  const tree = await local.getTreeIfDifferent(sha);
322
360
  return compressResponse(request, Response.json(tree ?? null));
323
361
  }
324
362
  if (action === HandleAction.Sha && request.method === "GET") {
325
363
  expectJson();
326
- await syncForRead(cnx);
364
+ await syncForRead(local.syncWith(cnx));
327
365
  return Response.json(await local.sha);
328
366
  }
329
367
  if (action === HandleAction.Blob && request.method === "POST") {
330
368
  const { shas } = object({ shas: array(string) })(await body);
331
- const tree = await local.source.getTree();
369
+ let tree = await local.source.getTree();
370
+ if (!dev && shas.some((sha) => !tree.hasSha(sha))) {
371
+ await syncForRead(local.syncWith(cnx));
372
+ tree = await local.source.getTree();
373
+ }
332
374
  const fromLocal = [];
333
375
  const fromRemote = [];
334
376
  for (const sha of shas) {
@@ -417,6 +459,28 @@ function createHandler({
417
459
  }
418
460
  };
419
461
  }
462
+ var historyResource = {
463
+ id: Entry.id,
464
+ type: Entry.type,
465
+ workspace: Entry.workspace,
466
+ root: Entry.root,
467
+ parents: Entry.parents,
468
+ locale: Entry.locale
469
+ };
470
+ async function historyEntries(local, contentDir, file, record) {
471
+ const filePath = relative(contentDir, file);
472
+ if (filePath.startsWith("../")) return [];
473
+ const status = "all";
474
+ const select = historyResource;
475
+ const atFile = await local.find({
476
+ filePath: { in: fileVersions(filePath) },
477
+ status,
478
+ select
479
+ });
480
+ const id = record?._id;
481
+ if (atFile.length > 0 || typeof id !== "string") return atFile;
482
+ return local.find({ id, status, select });
483
+ }
420
484
  function redirectFile(location, cacheControl) {
421
485
  return new Response(null, {
422
486
  status: 307,
@@ -42,7 +42,11 @@ var BasicAuth = class {
42
42
  const [scheme, token] = auth.split(" ", 2);
43
43
  if (scheme !== "Basic")
44
44
  throw new MissingCredentialsError("Invalid authorization scheme");
45
- const [username, password] = atob(token).split(":");
45
+ const credentials = atob(token);
46
+ const colon = credentials.indexOf(":");
47
+ if (colon === -1) throw new InvalidCredentialsError("Invalid credentials");
48
+ const username = credentials.slice(0, colon);
49
+ const password = credentials.slice(colon + 1);
46
50
  const authorized = await this.#verify(username, password);
47
51
  if (!authorized) throw new InvalidCredentialsError("Invalid credentials");
48
52
  const user = typeof authorized === "boolean" ? {
@@ -0,0 +1,21 @@
1
+ import type { CommitApi, SyncApi } from 'alinea/core/Connection';
2
+ /**
3
+ * Content blobs kept where every handler instance finds them, such as the
4
+ * backend database: an instance that is behind reads what another one
5
+ * fetched or committed, instead of fetching it from the remote again.
6
+ */
7
+ export interface BlobStore {
8
+ /** The stored blobs among `shas`. */
9
+ readBlobs(shas: ReadonlyArray<string>): Promise<Map<string, Uint8Array>>;
10
+ /** Keep blobs, found again by their sha. */
11
+ storeBlobs(blobs: ReadonlyArray<[sha: string, blob: Uint8Array]>): Promise<void>;
12
+ }
13
+ export declare function isBlobStore<T extends object>(value: T): value is T & BlobStore;
14
+ /**
15
+ * Answer from the store what it has, and fetch the rest from the remote,
16
+ * storing it. The store only saves requests: when it fails, the remote
17
+ * answers instead.
18
+ */
19
+ export declare function storedBlobs(store: BlobStore, getBlobs: SyncApi['getBlobs']): SyncApi['getBlobs'];
20
+ /** Commit through the remote, and store the committed content. */
21
+ export declare function storedWrites(store: BlobStore, write: CommitApi['write']): CommitApi['write'];