alinea 2.0.0-preview.7 → 2.0.0-preview.8

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 (283) hide show
  1. package/dist/.init/content/pages/welcome.json +1 -1
  2. package/dist/LICENSES.md +120 -120
  3. package/dist/adapter/next/cms.d.ts +18 -4
  4. package/dist/adapter/next/cms.js +125 -36
  5. package/dist/adapter/next/handler.d.ts +5 -1
  6. package/dist/adapter/next/handler.js +14 -7
  7. package/dist/adapter/next/node.d.ts +5 -0
  8. package/dist/adapter/next/node.js +16 -0
  9. package/dist/adapter/next/previews.d.ts +4 -1
  10. package/dist/adapter/next/previews.js +13 -1
  11. package/dist/adapter/next/renderStats.d.ts +24 -0
  12. package/dist/adapter/next/renderStats.js +74 -0
  13. package/dist/adapter/next/syncCheck.d.ts +8 -2
  14. package/dist/adapter/next/syncCheck.js +3 -3
  15. package/dist/adapter/next/with-alinea.js +0 -18
  16. package/dist/backend/Handler.d.ts +2 -2
  17. package/dist/backend/Handler.js +33 -12
  18. package/dist/backend/api/CreateBackend.js +15 -3
  19. package/dist/backend/api/DatabaseApi.js +22 -135
  20. package/dist/backend/api/database.js +15 -3
  21. package/dist/backend/resolver/ParsePreview.d.ts +2 -2
  22. package/dist/backend/router/NodeHandler.js +2 -0
  23. package/dist/backend/store/GeneratedDatabase.d.ts +5 -0
  24. package/dist/backend/store/GeneratedDatabase.js +31 -0
  25. package/dist/backend/store/GeneratedDatabaseNode.d.ts +4 -0
  26. package/dist/backend/store/GeneratedDatabaseNode.js +35 -0
  27. package/dist/backend/util/ORM.js +4 -2
  28. package/dist/bundled.d.ts +1470 -442
  29. package/dist/chunks/bun-sqlite-ZZDB7YQQ.js +18 -0
  30. package/dist/chunks/chunk-2BJ2IFQM.js +1085 -0
  31. package/dist/chunks/chunk-2PIFHEYR.js +364 -0
  32. package/dist/chunks/{chunk-X47OVDF3.js → chunk-2U43BB6P.js} +356 -1000
  33. package/dist/chunks/chunk-76VMZTLN.js +55 -0
  34. package/dist/chunks/chunk-A42DP5T7.js +94 -0
  35. package/dist/chunks/chunk-ATF42T2B.js +35 -0
  36. package/dist/chunks/{chunk-NWQXN4SB.js → chunk-ETBQTP2S.js} +40 -29
  37. package/dist/chunks/chunk-IL7FJQAY.js +94 -0
  38. package/dist/chunks/chunk-K62JP6HR.js +22 -0
  39. package/dist/chunks/chunk-K7LHQXI4.js +103 -0
  40. package/dist/chunks/chunk-M7KDJEGH.js +495 -0
  41. package/dist/chunks/chunk-N47Y62CI.js +396 -0
  42. package/dist/chunks/chunk-NLZLRQ5U.js +100 -0
  43. package/dist/chunks/chunk-QBPYWU6C.js +24 -0
  44. package/dist/chunks/{chunk-6P4UVRPH.js → chunk-RWWYUNVX.js} +2 -1
  45. package/dist/chunks/chunk-TFMUSWGG.js +605 -0
  46. package/dist/chunks/{chunk-I4GC3E65.js → chunk-UPY2RUZF.js} +0 -1
  47. package/dist/chunks/chunk-XCZXR3C2.js +112 -0
  48. package/dist/chunks/chunk-XTWYBWP2.js +81 -0
  49. package/dist/chunks/node-sqlite-7CSGL27V.js +18 -0
  50. package/dist/cli/Generate.js +74 -64
  51. package/dist/cli/Serve.js +1 -1
  52. package/dist/cli/bin.js +2 -1
  53. package/dist/cli/generate/CopyStaticFiles.js +17 -14
  54. package/dist/cli/generate/DevDB.d.ts +22 -5
  55. package/dist/cli/generate/DevDB.js +136 -24
  56. package/dist/cli/serve/CreateLocalServer.js +22 -4
  57. package/dist/cli/serve/LiveReload.d.ts +1 -1
  58. package/dist/cli/serve/LiveReload.js +3 -0
  59. package/dist/cloud/CloudRemote.js +2 -1
  60. package/dist/components/Calendar.js +6 -6
  61. package/dist/components/Checkbox.js +3 -3
  62. package/dist/components/ColorSwatchPicker.js +2 -2
  63. package/dist/components/ComboBox.js +9 -9
  64. package/dist/components/DateField.js +1 -1
  65. package/dist/components/DatePicker.js +6 -6
  66. package/dist/components/DateRangePicker.js +6 -6
  67. package/dist/components/Disclosure.js +9 -9
  68. package/dist/components/Label.js +5 -5
  69. package/dist/components/List.js +33 -33
  70. package/dist/components/Menu.js +3 -3
  71. package/dist/components/Modal.js +3 -3
  72. package/dist/components/MultipleSelect.js +25 -25
  73. package/dist/components/NumberField.js +6 -6
  74. package/dist/components/ProgressCircle.js +3 -3
  75. package/dist/components/RadioGroup.js +2 -2
  76. package/dist/components/SearchField.js +5 -5
  77. package/dist/components/Select.js +7 -7
  78. package/dist/components/Surface.js +2 -2
  79. package/dist/components/Table.js +6 -6
  80. package/dist/components/Tabs.js +2 -2
  81. package/dist/components/TextField.js +6 -6
  82. package/dist/components/Toggle.js +2 -2
  83. package/dist/components/Toolbar.js +2 -2
  84. package/dist/components/Tooltip.js +4 -4
  85. package/dist/components/Tree.d.ts +2 -1
  86. package/dist/components/Tree.js +5 -5
  87. package/dist/core/Document.js +15 -3
  88. package/dist/core/Entry.d.ts +3 -0
  89. package/dist/core/Entry.js +4 -0
  90. package/dist/core/EntrySeed.d.ts +19 -0
  91. package/dist/core/EntrySeed.js +136 -0
  92. package/dist/core/Field.d.ts +7 -0
  93. package/dist/core/Field.js +4 -0
  94. package/dist/core/Filter.d.ts +40 -2
  95. package/dist/core/Filter.js +145 -0
  96. package/dist/core/Graph.d.ts +9 -0
  97. package/dist/core/ListRow.d.ts +1 -0
  98. package/dist/core/ListRow.js +1 -0
  99. package/dist/core/Reference.d.ts +1 -0
  100. package/dist/core/Root.js +1 -1
  101. package/dist/core/Scope.d.ts +2 -0
  102. package/dist/core/Scope.js +16 -1
  103. package/dist/core/Type.d.ts +2 -1
  104. package/dist/core/Type.js +13 -1
  105. package/dist/core/db/EntryAliases.js +1 -11
  106. package/dist/core/db/EntryReference.d.ts +0 -6
  107. package/dist/core/db/IndexEvent.d.ts +2 -1
  108. package/dist/core/db/IndexEvent.js +11 -3
  109. package/dist/core/db/LinkResolver.d.ts +6 -6
  110. package/dist/core/db/LinkResolver.js +0 -52
  111. package/dist/core/db/LocalStore.d.ts +25 -0
  112. package/dist/core/field/ListField.js +19 -0
  113. package/dist/core/field/RecordField.js +8 -0
  114. package/dist/core/field/RichTextField.js +42 -0
  115. package/dist/core/field/UnionField.js +8 -0
  116. package/dist/core/source/FSSource.d.ts +14 -2
  117. package/dist/core/source/FSSource.js +58 -13
  118. package/dist/core/source/GitUtils.d.ts +4 -0
  119. package/dist/core/source/GitUtils.js +9 -10
  120. package/dist/core/source/GithubSource.js +22 -11
  121. package/dist/core/source/IndexedDBSource.d.ts +2 -1
  122. package/dist/core/source/IndexedDBSource.js +67 -53
  123. package/dist/core/source/MemorySource.js +5 -3
  124. package/dist/core/source/OverlaySource.d.ts +5 -2
  125. package/dist/core/source/OverlaySource.js +31 -3
  126. package/dist/core/source/Source.d.ts +3 -0
  127. package/dist/core/source/Source.js +15 -2
  128. package/dist/core/source/Tree.d.ts +10 -7
  129. package/dist/core/source/Tree.js +118 -70
  130. package/dist/core/source/Utils.js +6 -3
  131. package/dist/core/util/Arrays.d.ts +1 -0
  132. package/dist/core/util/Arrays.js +10 -0
  133. package/dist/core/util/Async.d.ts +7 -0
  134. package/dist/core/util/Async.js +15 -0
  135. package/dist/core/util/Checks.js +1 -1
  136. package/dist/core/util/EntryFilenames.d.ts +22 -1
  137. package/dist/core/util/EntryFilenames.js +56 -2
  138. package/dist/core/util/IndexedDB.d.ts +4 -0
  139. package/dist/core/util/IndexedDB.js +20 -0
  140. package/dist/core/util/Paths.js +5 -1
  141. package/dist/dashboard/App.js +3 -3
  142. package/dist/dashboard/ViewKeys.d.ts +1 -0
  143. package/dist/dashboard/ViewKeys.js +1 -0
  144. package/dist/dashboard/app/AccessDenied.js +4 -4
  145. package/dist/dashboard/app/ActivityStatus.js +11 -11
  146. package/dist/dashboard/app/AppShell.js +2 -2
  147. package/dist/dashboard/app/AuthView.js +9 -9
  148. package/dist/dashboard/app/Badge.js +2 -2
  149. package/dist/dashboard/app/CompactField.js +10 -10
  150. package/dist/dashboard/app/DashboardErrorBoundary.js +6 -6
  151. package/dist/dashboard/app/EntryFields.js +2 -2
  152. package/dist/dashboard/app/EntryHeader.js +10 -10
  153. package/dist/dashboard/app/EntryReferences.js +8 -8
  154. package/dist/dashboard/app/EntrySidebar.js +8 -8
  155. package/dist/dashboard/app/EntrySidebarPreview.js +11 -11
  156. package/dist/dashboard/app/EntryTranslationBanner.js +6 -6
  157. package/dist/dashboard/app/Explorer.js +44 -44
  158. package/dist/dashboard/app/ExplorerCards.js +30 -30
  159. package/dist/dashboard/app/ExplorerFileCard.js +17 -17
  160. package/dist/dashboard/app/ExplorerList.js +14 -14
  161. package/dist/dashboard/app/ExplorerModal.js +5 -5
  162. package/dist/dashboard/app/ExplorerTable.js +47 -47
  163. package/dist/dashboard/app/LinkPicker.js +2 -2
  164. package/dist/dashboard/app/LocaleMenu.js +6 -6
  165. package/dist/dashboard/app/SidebarTree.js +76 -40
  166. package/dist/dashboard/app/WorkspaceMenu.js +10 -10
  167. package/dist/dashboard/app/WorkspaceRoots.js +27 -27
  168. package/dist/dashboard/app/editor/FileEditor.js +24 -24
  169. package/dist/dashboard/app/editor/FilePreview.js +6 -6
  170. package/dist/dashboard/app/modals/CreateEntry.js +4 -4
  171. package/dist/dashboard/app/pages/EntryPage.js +15 -15
  172. package/dist/dashboard/app/pages/RootPage.js +3 -3
  173. package/dist/dashboard/app/pages/SplashPage.js +47 -47
  174. package/dist/dashboard/app/pages/UsersPage.js +36 -36
  175. package/dist/dashboard/app/ui/DashboardModal.js +17 -17
  176. package/dist/dashboard/app/ui/Rail.js +4 -4
  177. package/dist/dashboard/atoms/config.d.ts +4 -4
  178. package/dist/dashboard/atoms/entry.d.ts +8 -9
  179. package/dist/dashboard/atoms/entry.js +34 -4
  180. package/dist/dashboard/atoms/explorer.d.ts +2 -0
  181. package/dist/dashboard/atoms/explorer.js +7 -2
  182. package/dist/dashboard/boot/Boot.js +2 -3
  183. package/dist/dashboard/boot/DashboardWorker.d.ts +3 -3
  184. package/dist/dashboard/boot/DashboardWorker.js +78 -27
  185. package/dist/dashboard/boot/LoadWorker.js +2 -3
  186. package/dist/dashboard/icons.d.ts +2 -0
  187. package/dist/dashboard/icons.js +40 -0
  188. package/dist/dashboard/plugins/alineaFixturePlugin.js +14 -3
  189. package/dist/database/BrowserEntryStore.d.ts +30 -0
  190. package/dist/database/BrowserEntryStore.js +168 -0
  191. package/dist/database/DatabaseSource.d.ts +13 -0
  192. package/dist/database/DatabaseSource.js +25 -0
  193. package/dist/database/DatabaseTables.d.ts +38 -0
  194. package/dist/database/DatabaseTables.js +53 -0
  195. package/dist/database/EntryDatabase.d.ts +21 -0
  196. package/dist/database/EntryDatabase.js +138 -0
  197. package/dist/database/EntryLayer.d.ts +93 -0
  198. package/dist/database/EntryLayer.js +459 -0
  199. package/dist/database/EntryStore.d.ts +60 -0
  200. package/dist/database/EntryStore.js +269 -0
  201. package/dist/database/EntryTransaction.d.ts +33 -0
  202. package/dist/database/EntryTransaction.js +889 -0
  203. package/dist/database/EntryUrlAliases.d.ts +2 -0
  204. package/dist/database/EntryUrlAliases.js +69 -0
  205. package/dist/database/LocalDB.d.ts +39 -0
  206. package/dist/database/LocalDB.js +79 -0
  207. package/dist/database/Version.d.ts +9 -0
  208. package/dist/database/Version.js +13 -0
  209. package/dist/database/driver/RuntimeDatabase.d.ts +8 -0
  210. package/dist/database/driver/RuntimeDatabase.js +33 -0
  211. package/dist/database/driver/WasmDatabase.d.ts +8 -0
  212. package/dist/database/driver/WasmDatabase.js +33 -0
  213. package/dist/database/entry/EntryData.d.ts +13 -0
  214. package/dist/database/entry/EntryData.js +51 -0
  215. package/dist/database/entry/EntryTable.d.ts +173 -0
  216. package/dist/database/entry/EntryTable.js +137 -0
  217. package/dist/database/entry/EntryView.d.ts +22 -0
  218. package/dist/database/entry/EntryView.js +106 -0
  219. package/dist/database/query/Condition.d.ts +8 -0
  220. package/dist/database/query/Condition.js +117 -0
  221. package/dist/database/query/EntryQuery.d.ts +43 -0
  222. package/dist/database/query/EntryQuery.js +414 -0
  223. package/dist/database/query/EntryReferences.d.ts +6 -0
  224. package/dist/database/query/EntryReferences.js +68 -0
  225. package/dist/database/query/Relation.d.ts +11 -0
  226. package/dist/database/query/Relation.js +113 -0
  227. package/dist/database/query/ResolveQuery.d.ts +15 -0
  228. package/dist/database/query/ResolveQuery.js +154 -0
  229. package/dist/database/query/Search.d.ts +47 -0
  230. package/dist/database/query/Search.js +169 -0
  231. package/dist/database/sync/Derive.d.ts +8 -0
  232. package/dist/database/sync/Derive.js +270 -0
  233. package/dist/database/sync/EntryParser.d.ts +4 -0
  234. package/dist/database/sync/EntryParser.js +87 -0
  235. package/dist/database/sync/EntrySyncer.d.ts +37 -0
  236. package/dist/database/sync/EntrySyncer.js +117 -0
  237. package/dist/database/sync/Ingest.d.ts +21 -0
  238. package/dist/database/sync/Ingest.js +136 -0
  239. package/dist/database/sync/SyncQueries.d.ts +157 -0
  240. package/dist/database/sync/SyncQueries.js +192 -0
  241. package/dist/field/code/CodeField.view.js +12 -12
  242. package/dist/field/link/LinkField.js +23 -0
  243. package/dist/field/link/LinkField.view.js +41 -40
  244. package/dist/field/list/ListField.view.js +177 -155
  245. package/dist/field/metadata/MetadataField.d.ts +28 -3
  246. package/dist/field/metadata/MetadataField.js +44 -7
  247. package/dist/field/metadata/MetadataField.view.d.ts +5 -0
  248. package/dist/field/metadata/MetadataField.view.js +34 -19
  249. package/dist/field/richtext/RichTextBlock.js +2 -2
  250. package/dist/field/richtext/RichTextDocument.js +4 -3
  251. package/dist/field/richtext/RichTextExtensions.d.ts +1 -1
  252. package/dist/field/richtext/RichTextExtensions.js +32 -28
  253. package/dist/field/richtext/RichTextField.view.js +15 -6
  254. package/dist/field/views.js +2 -0
  255. package/dist/next.cjs +0 -18
  256. package/dist/next.d.ts +4 -3
  257. package/dist/next.edge.d.ts +1 -1
  258. package/dist/next.js +1 -2
  259. package/dist/picker/entry/EntryPicker.js +2 -1
  260. package/dist/picker/url/UrlPicker.js +1 -0
  261. package/dist/preview/widget.d.ts +16 -0
  262. package/dist/preview/widget.js +70 -1
  263. package/dist/query.d.ts +28 -0
  264. package/package.json +2 -1
  265. package/dist/backend/store/GeneratedSource.d.ts +0 -2
  266. package/dist/backend/store/GeneratedSource.js +0 -21
  267. package/dist/chunks/chunk-G6E35DMJ.js +0 -1997
  268. package/dist/core/db/EntryDB.d.ts +0 -19
  269. package/dist/core/db/EntryDB.js +0 -48
  270. package/dist/core/db/EntryIndex.d.ts +0 -143
  271. package/dist/core/db/EntryIndex.js +0 -2773
  272. package/dist/core/db/EntryReferenceIndex.d.ts +0 -21
  273. package/dist/core/db/EntryReferenceIndex.js +0 -131
  274. package/dist/core/db/EntryResolver.d.ts +0 -56
  275. package/dist/core/db/EntryResolver.js +0 -699
  276. package/dist/core/db/EntryTarget.d.ts +0 -5
  277. package/dist/core/db/EntryTransaction.d.ts +0 -31
  278. package/dist/core/db/EntryTransaction.js +0 -956
  279. package/dist/core/db/LocalDB.d.ts +0 -42
  280. package/dist/core/db/LocalDB.js +0 -127
  281. package/dist/core/db/TestDB.d.ts +0 -19
  282. package/dist/core/db/TestDB.js +0 -50
  283. /package/dist/core/db/{EntryTarget.js → LocalStore.js} +0 -0
package/dist/bundled.d.ts CHANGED
@@ -44,28 +44,42 @@ declare module 'alinea/adapter/next/cms' {
44
44
  import { CMS } from 'alinea/core/CMS';
45
45
  import { Config } from 'alinea/core/Config';
46
46
  import type { UploadResponse } from 'alinea/core/Connection';
47
- import { LocalDB } from 'alinea/core/db/LocalDB';
47
+ import type { LocalStore } from 'alinea/core/db/LocalStore';
48
48
  import type { Mutation } from 'alinea/core/db/Mutation';
49
49
  import type { GraphQuery } from 'alinea/core/Graph';
50
50
  import type { User } from 'alinea/core/User';
51
51
  import PLazy from 'p-lazy';
52
+ import type { DatabaseOptions } from 'rado';
52
53
  export interface PreviewProps {
53
54
  widget?: boolean;
55
+ /** Show the queries and syncs of each draft render in the widget */
56
+ stats?: boolean;
54
57
  workspace?: string;
55
58
  root?: string;
56
59
  }
60
+ export type OpenBundledDatabase = (config: Config, options?: DatabaseOptions) => Promise<LocalStore>;
61
+ export interface SyncStatus {
62
+ /** Whether queries are answered from the bundled database or the handler. */
63
+ source: 'database' | 'handler';
64
+ /** The content revision the answering side is at. */
65
+ sha: string | undefined;
66
+ /** When this isolate last synced its bundled database with the handler. */
67
+ syncedAt: Date | undefined;
68
+ }
57
69
  export class NextCMS<Definition extends Config = Config> extends CMS<Definition> {
58
70
  #private;
59
- constructor(config: Definition);
71
+ bundledDb: PLazy<LocalStore>;
72
+ constructor(config: Definition, openBundledDatabase?: OpenBundledDatabase);
60
73
  throttle: (sync: () => Promise<unknown>, interval?: number) => Promise<void>;
61
- bundledDb: PLazy<LocalDB>;
74
+ /** Where queries are answered from and how fresh that side is. */
75
+ status(): Promise<SyncStatus>;
62
76
  resolve<Query extends GraphQuery>(query: Query): Promise<any>;
63
77
  user(): Promise<User | undefined>;
64
78
  mutate(mutations: Array<Mutation>): Promise<{
65
79
  sha: string;
66
80
  }>;
67
81
  prepareUpload(file: string): Promise<UploadResponse>;
68
- previews: ({ widget, workspace, root }: PreviewProps) => Promise<import("react").JSX.Element | null>;
82
+ previews: ({ widget, stats: showStats, workspace, root }: PreviewProps) => Promise<import("react").JSX.Element | null>;
69
83
  }
70
84
  export function createCMS<Definition extends Config>(config: Definition): NextCMS<Definition>;
71
85
 
@@ -98,27 +112,44 @@ declare module 'alinea/adapter/next/handler' {
98
112
  import { type BackendFactory, type BackendOptions } from 'alinea/backend/api/CreateBackend';
99
113
  import { type HandlerHooks } from 'alinea/backend/Handler';
100
114
  import { Config } from 'alinea/core/Config';
115
+ import type { LocalStore } from 'alinea/core/db/LocalStore';
101
116
  import { NextCMS } from 'alinea/adapter/next/cms';
102
117
  type Handler = (request: Request) => Promise<Response>;
103
118
  export interface NextHandlerOptions extends HandlerHooks {
104
119
  cms: NextCMS;
105
120
  backend?: BackendFactory | BackendOptions;
106
121
  }
107
- export function createHandler(input: NextCMS | NextHandlerOptions): Handler;
122
+ export type OpenGeneratedDatabase = (config: Config) => Promise<LocalStore>;
123
+ export function createHandlerWithDatabase(input: NextCMS | NextHandlerOptions, openGeneratedDatabase?: OpenGeneratedDatabase): Handler;
108
124
  export function handlerPathname(config: Config, requestUrl: URL): string;
125
+ /** A handler without a generated database, as used in the Edge runtime. */
126
+ export function createHandler(input: NextCMS | NextHandlerOptions): Handler;
109
127
  export {};
110
128
 
111
129
  }
112
130
 
131
+ declare module 'alinea/adapter/next/node' {
132
+
133
+ import type { Config } from 'alinea/core/Config';
134
+ import { NextCMS } from 'alinea/adapter/next/cms';
135
+ import { type NextHandlerOptions } from 'alinea/adapter/next/handler';
136
+ export function createCMS<Definition extends Config>(config: Definition): NextCMS<Definition>;
137
+ export function createHandler(input: NextCMS | NextHandlerOptions): (request: Request) => Promise<Response>;
138
+
139
+ }
140
+
113
141
  declare module 'alinea/adapter/next/previews' {
114
142
 
143
+ import { type PreviewStats } from 'alinea/preview/widget';
115
144
  export interface NextPreviewsProps {
116
145
  dashboardUrl: string;
117
146
  widget?: boolean;
147
+ /** Resolves once the render's CMS queries settled. */
148
+ stats?: Promise<PreviewStats>;
118
149
  root?: string;
119
150
  workspace?: string;
120
151
  }
121
- export default function NextPreviews({ dashboardUrl, widget, root, workspace }: NextPreviewsProps): import("react").JSX.Element | null;
152
+ export default function NextPreviews({ dashboardUrl, widget, stats, root, workspace }: NextPreviewsProps): import("react").JSX.Element | null;
122
153
  /**
123
154
  * Wrapper around `router.refresh()` from `next/navigation` `useRouter()` to return Promise, and resolve after refresh completed
124
155
  * @returns Refresh function
@@ -133,10 +164,44 @@ declare module 'alinea/adapter/next/previews.spec' {
133
164
 
134
165
  }
135
166
 
167
+ declare module 'alinea/adapter/next/renderStats' {
168
+
169
+ import type { GraphQuery } from 'alinea/core/Graph';
170
+ import type { PreviewStat, PreviewStats } from 'alinea/preview/widget';
171
+ /** Collects the CMS queries and syncs of one draft render. */
172
+ export class RenderStats {
173
+ #private;
174
+ rows: Array<PreviewStat>;
175
+ statements: number;
176
+ sqlMs: number;
177
+ /** Add a row and count it as pending until `run` settles; see `timed`. */
178
+ track<T>(stat: Omit<PreviewStat, 'durationMs'>, run: (row: PreviewStat) => Promise<T>): Promise<T>;
179
+ statement(durationMs: number): void;
180
+ /**
181
+ * Resolves once nothing ran for `quietMs`, so queries of server components
182
+ * that render after the caller are counted, or after `maxMs` regardless.
183
+ */
184
+ settled(quietMs?: number, maxMs?: number): Promise<PreviewStats>;
185
+ }
186
+ /**
187
+ * Time the work a row stands for, such as answering a query without the sync
188
+ * it waited for.
189
+ */
190
+ export function timed<T>(row: PreviewStat | undefined, run: () => Promise<T>): Promise<T>;
191
+ /** A short description such as `first(type, url=/about)`. */
192
+ export function summarizeQuery(query: GraphQuery): string;
193
+
194
+ }
195
+
136
196
  declare module 'alinea/adapter/next/syncCheck' {
137
197
 
138
198
  import type { Client } from 'alinea/core/Client';
139
- import type { LocalDB } from 'alinea/core/db/LocalDB';
199
+ import type { SyncOptions } from 'alinea/core/db/LocalStore';
200
+ import type { RemoteSource } from 'alinea/core/source/Source';
201
+ interface SyncableDB {
202
+ readonly sha: string | Promise<string>;
203
+ syncWith(source: RemoteSource, options?: SyncOptions): Promise<string>;
204
+ }
140
205
  export const CONTENT_SHA_TAG = "alinea-content-sha";
141
206
  export function revalidateContentSha(): Promise<void>;
142
207
  /**
@@ -146,7 +211,8 @@ declare module 'alinea/adapter/next/syncCheck' {
146
211
  * Returns false when the sha could not be determined, so the caller falls
147
212
  * back to its existing throttle behavior.
148
213
  */
149
- export function syncIfStale(db: LocalDB, client: Client, syncInterval?: number): Promise<boolean>;
214
+ export function syncIfStale(db: SyncableDB, client: Client, syncInterval?: number, options?: SyncOptions): Promise<boolean>;
215
+ export {};
150
216
 
151
217
  }
152
218
 
@@ -506,7 +572,7 @@ declare module 'alinea/backend/Handler' {
506
572
 
507
573
  import type { CMS } from 'alinea/core/CMS';
508
574
  import type { AuthedContext, RemoteConnection, RequestContext } from 'alinea/core/Connection';
509
- import { LocalDB } from 'alinea/core/db/LocalDB';
575
+ import type { LocalStore } from 'alinea/core/db/LocalStore';
510
576
  import type { Mutation } from 'alinea/core/db/Mutation';
511
577
  export interface Handler {
512
578
  (request: Request, context: RequestContext): Promise<Response>;
@@ -524,7 +590,7 @@ declare module 'alinea/backend/Handler' {
524
590
  }
525
591
  export interface HandlerOptions extends HandlerHooks {
526
592
  cms: CMS;
527
- db: LocalDB | Promise<LocalDB>;
593
+ db: LocalStore | Promise<LocalStore>;
528
594
  remote?: (context: RequestContext) => RemoteConnection;
529
595
  forwardMutations?(request: Request, context: AuthedContext): Promise<Response | undefined>;
530
596
  }
@@ -562,15 +628,15 @@ declare module 'alinea/backend/Previews' {
562
628
 
563
629
  declare module 'alinea/backend/resolver/ParsePreview' {
564
630
 
565
- import type { LocalDB } from 'alinea/core/db/LocalDB';
566
631
  import { Entry } from 'alinea/core/Entry';
567
632
  import type { PreviewRequest, PreviewUpdate } from 'alinea/core/Preview';
633
+ import type { Graph } from 'alinea/core/Graph';
568
634
  export interface DecodedEntryPreview {
569
635
  entry: Entry;
570
636
  }
571
637
  export type DecodedPreviewRequest = PreviewUpdate | DecodedEntryPreview;
572
638
  export function decodePreviewRequest(preview: PreviewRequest): Promise<DecodedPreviewRequest>;
573
- export function applyPreview(local: LocalDB, preview: DecodedPreviewRequest): Promise<PreviewRequest | undefined>;
639
+ export function applyPreview(local: Graph, preview: DecodedPreviewRequest): Promise<PreviewRequest | undefined>;
574
640
 
575
641
  }
576
642
 
@@ -737,16 +803,28 @@ declare module 'alinea/backend/router/Router' {
737
803
 
738
804
  }
739
805
 
740
- declare module 'alinea/backend/store/GeneratedRelease' {
806
+ declare module 'alinea/backend/store/GeneratedDatabase' {
741
807
 
742
- export const generatedRelease: Promise<string>;
808
+ import type { Config } from 'alinea/core/Config';
809
+ import { EntryStore } from 'alinea/database/EntryStore';
810
+ import type { Database } from 'rado';
811
+ /** Add one writable, connection-local layer over a generated database. */
812
+ export function createGeneratedDatabase(config: Config, db: Database): Promise<EntryStore>;
743
813
 
744
814
  }
745
815
 
746
- declare module 'alinea/backend/store/GeneratedSource' {
816
+ declare module 'alinea/backend/store/GeneratedDatabaseNode' {
747
817
 
748
- import { MemorySource } from 'alinea/core/source/MemorySource';
749
- export const generatedSource: Promise<MemorySource>;
818
+ import type { Config } from 'alinea/core/Config';
819
+ import type { DatabaseOptions } from 'rado';
820
+ /** Open the traced generated file through the native SQLite driver. */
821
+ export function generatedDatabase(config: Config, options?: DatabaseOptions): Promise<import("alinea/database/EntryStore").EntryStore>;
822
+
823
+ }
824
+
825
+ declare module 'alinea/backend/store/GeneratedRelease' {
826
+
827
+ export const generatedRelease: Promise<string>;
750
828
 
751
829
  }
752
830
 
@@ -872,23 +950,40 @@ declare module 'alinea/cli/generate/DevDB' {
872
950
 
873
951
  import { Config } from 'alinea/core/Config';
874
952
  import type { UploadResponse } from 'alinea/core/Connection';
875
- import type { CommitRequest } from 'alinea/core/db/CommitRequest';
876
- import { LocalDB } from 'alinea/core/db/LocalDB';
953
+ import { type CommitRequest } from 'alinea/core/db/CommitRequest';
877
954
  import { CachedFSSource } from 'alinea/core/source/FSSource';
955
+ import { EntryDatabase } from 'alinea/database/EntryDatabase';
956
+ import { EntryStore } from 'alinea/database/EntryStore';
878
957
  export interface DevDBOptions {
879
958
  config: Config;
880
959
  rootDir: string;
960
+ databasePath: string;
961
+ configFingerprint?: string;
881
962
  dashboardUrl: string | undefined;
882
963
  }
883
964
  export interface WatchFiles {
884
965
  files: Array<string>;
885
966
  dirs: Array<string>;
886
967
  }
887
- export class DevDB extends LocalDB {
968
+ /** The persistent generated database used by dev, build and the local handler. */
969
+ export class DevDB extends EntryStore {
888
970
  #private;
889
- source: CachedFSSource;
890
- constructor(options: DevDBOptions);
971
+ readonly source: CachedFSSource;
972
+ readonly database: EntryDatabase;
973
+ private constructor();
974
+ /** Whether the source was restored from a previously built database. */
975
+ get hydrated(): boolean;
976
+ /** How many entries the last sync changed; zero when everything was reused. */
977
+ get lastSyncChanges(): number;
978
+ static create(options: DevDBOptions): Promise<DevDB>;
979
+ /**
980
+ * Adopt a changed config in place: every entry is derived again from the
981
+ * content files while the database stays open, so the dev server keeps
982
+ * serving through the rebuild and the hydrated file stats stay valid.
983
+ */
984
+ reconfigure(options: Pick<DevDBOptions, 'config' | 'configFingerprint' | 'dashboardUrl'>): Promise<void>;
891
985
  sync(): Promise<string>;
986
+ finalize(): Promise<number>;
892
987
  fix(): Promise<void>;
893
988
  watchFiles(): Promise<WatchFiles>;
894
989
  isInMediaLocation(file: string): boolean;
@@ -1032,7 +1127,7 @@ declare module 'alinea/cli/serve/LiveReload' {
1032
1127
  export class LiveReload {
1033
1128
  clients: Array<Client>;
1034
1129
  reload(type: 'refetch' | 'refresh' | 'reload'): void;
1035
- register(client: Client): void;
1130
+ register(client: Client): () => void;
1036
1131
  }
1037
1132
  export {};
1038
1133
 
@@ -2027,11 +2122,12 @@ declare module 'alinea/components/Tooltip' {
2027
2122
 
2028
2123
  declare module 'alinea/components/Tree' {
2029
2124
 
2030
- import { type ReactNode } from 'react';
2125
+ import { type ReactNode, type Ref } from 'react';
2031
2126
  import { type TreeItemContentProps as AriaTreeItemContentProps, type TreeItemProps as AriaTreeItemProps, type TreeProps as AriaTreeProps } from 'react-aria-components';
2032
2127
  import { type IconProps } from 'alinea/components/Icon';
2033
2128
  export interface TreeProps<T extends object> extends Omit<AriaTreeProps<T>, 'className'> {
2034
2129
  className?: string;
2130
+ ref?: Ref<HTMLDivElement>;
2035
2131
  }
2036
2132
  export function Tree<T extends object>(props: TreeProps<T>): import("react").JSX.Element;
2037
2133
  export interface TreeItemContentProps extends Omit<AriaTreeItemContentProps, 'children'> {
@@ -2394,178 +2490,6 @@ declare module 'alinea/core/db/EntryAliases' {
2394
2490
 
2395
2491
  }
2396
2492
 
2397
- declare module 'alinea/core/db/EntryDB' {
2398
-
2399
- import type { Config } from 'alinea/core/Config';
2400
- import type { LocalConnection, UploadMetadata, UploadResponse } from 'alinea/core/Connection';
2401
- import type { Source } from 'alinea/core/source/Source';
2402
- import { type CommitRequest } from 'alinea/core/db/CommitRequest';
2403
- import { LocalDB } from 'alinea/core/db/LocalDB';
2404
- import type { Mutation } from 'alinea/core/db/Mutation';
2405
- export class EntryDB extends LocalDB {
2406
- connect: () => Promise<LocalConnection>;
2407
- constructor(config: Config, source: Source, connect: () => Promise<LocalConnection>);
2408
- mutate(mutations: Array<Mutation>): Promise<{
2409
- sha: string;
2410
- remote: Promise<string>;
2411
- }>;
2412
- write(request: CommitRequest): Promise<{
2413
- sha: string;
2414
- }>;
2415
- prepareUpload(file: string, metadata?: UploadMetadata): Promise<UploadResponse>;
2416
- syncWithRemote(): Promise<string>;
2417
- }
2418
-
2419
- }
2420
-
2421
- declare module 'alinea/core/db/EntryIndex' {
2422
-
2423
- import { Config } from 'alinea/core/Config';
2424
- import type { Entry, EntryStatus } from 'alinea/core/Entry';
2425
- import type { ChangesBatch } from 'alinea/core/source/Change';
2426
- import { type Source } from 'alinea/core/source/Source';
2427
- import { ReadonlyTree } from 'alinea/core/source/Tree';
2428
- import { Type } from 'alinea/core/Type';
2429
- import type { EntryReferenceQuery, EntryReferenceResult } from 'alinea/core/db/EntryReference';
2430
- import { EntryReferenceIndex } from 'alinea/core/db/EntryReferenceIndex';
2431
- import { EntryTransaction } from 'alinea/core/db/EntryTransaction';
2432
- export interface EntryFilter {
2433
- ids?: ReadonlyArray<string>;
2434
- search?: string;
2435
- condition?(entry: Entry): boolean;
2436
- }
2437
- export interface EntryCondition {
2438
- search?: string;
2439
- nodes?: Iterable<EntryNode>;
2440
- node?(node: EntryNode): boolean;
2441
- language?(language: EntryLanguageNode): boolean;
2442
- entry?(entry: Entry): boolean;
2443
- }
2444
- interface EntryNodeQuery {
2445
- ids?: ReadonlyArray<string>;
2446
- parentIds?: ReadonlyArray<string | null>;
2447
- urls?: ReadonlyArray<string>;
2448
- workspaces?: ReadonlyArray<string>;
2449
- roots?: ReadonlyArray<string>;
2450
- types?: ReadonlyArray<string>;
2451
- }
2452
- export function combineConditions(a: EntryCondition, b: EntryCondition): EntryCondition;
2453
- interface EntryVersionData {
2454
- id: string;
2455
- type: string;
2456
- index: string;
2457
- searchableText?: string;
2458
- title: string;
2459
- data: Record<string, unknown>;
2460
- seeded: string | null;
2461
- rowHash: string;
2462
- fileHash: string;
2463
- }
2464
- interface EntryVersion extends EntryVersionData {
2465
- locale: string | null;
2466
- workspace: string;
2467
- root: string;
2468
- path: string;
2469
- status: EntryStatus;
2470
- parentDir: string;
2471
- childrenDir: string;
2472
- filePath: string;
2473
- level: number;
2474
- }
2475
- class EntryLanguage extends Map<EntryStatus, EntryVersion> {
2476
- readonly locale: string | null;
2477
- readonly parentDir: string;
2478
- readonly selfDir: string;
2479
- constructor(versions: Array<EntryVersion>);
2480
- }
2481
- class EntryCollection extends Map<string | null, EntryLanguage> {
2482
- versions: Array<EntryVersion>;
2483
- readonly type: string;
2484
- constructor(versions: Array<EntryVersion>);
2485
- }
2486
- class EntryLanguageNode {
2487
- #private;
2488
- private node;
2489
- private language;
2490
- inheritedStatus: EntryStatus | undefined;
2491
- main: EntryVersion;
2492
- active: EntryVersion;
2493
- url: string;
2494
- locale: string | null;
2495
- path: string;
2496
- readonly seeded: string | null;
2497
- constructor(node: EntryNode, language: EntryLanguage);
2498
- [Symbol.iterator](): MapIterator<[EntryStatus, EntryVersion]>;
2499
- has(status: EntryStatus): boolean;
2500
- get parentPaths(): string[];
2501
- get entries(): Entry<Record<string, unknown>>[];
2502
- filter(filter: EntryCondition): Generator<Entry>;
2503
- }
2504
- export class EntryNode extends Map<string | null, EntryLanguageNode> {
2505
- graph: EntryGraph;
2506
- entryType: Type;
2507
- parent: EntryNode | null;
2508
- children: () => Iterable<EntryNode>;
2509
- readonly id: string;
2510
- readonly index: string;
2511
- readonly parentId: string | null;
2512
- readonly parents: Array<string>;
2513
- readonly workspace: string;
2514
- readonly root: string;
2515
- readonly type: string;
2516
- readonly level: number;
2517
- constructor(graph: EntryGraph, entryType: Type, parent: EntryNode | null, children: () => Iterable<EntryNode>, collection: EntryCollection);
2518
- filter(filter: EntryCondition): Generator<Entry>;
2519
- }
2520
- export class EntryGraph {
2521
- #private;
2522
- nodes: Array<EntryNode>;
2523
- constructor(config: Config, versionData: Map<string, EntryVersionData>, seeds: Map<string, Seed>);
2524
- byId(id: string): EntryNode | undefined;
2525
- candidateNodes(query: EntryNodeQuery): Array<EntryNode> | undefined;
2526
- byUrl(url: string): Iterable<Entry>;
2527
- byAlias(alias: string): Iterable<Entry>;
2528
- get config(): Config;
2529
- byDir(dir: string): EntryNode | undefined;
2530
- entryByFilePath(filePath: string): Entry | undefined;
2531
- withChanges(batch: ChangesBatch): EntryGraph;
2532
- filter({ search, ...filter }: EntryCondition): Generator<Entry>;
2533
- }
2534
- export class EntryIndex extends EventTarget {
2535
- #private;
2536
- tree: ReadonlyTree;
2537
- initialSync: ReadonlyTree | undefined;
2538
- graph: EntryGraph;
2539
- constructor(config: Config);
2540
- clone(): EntryIndex;
2541
- get sha(): string;
2542
- filter(filter: EntryCondition): Iterable<Entry>;
2543
- findFirst<T extends Record<string, unknown>>(filter: (entry: Entry) => boolean): Entry<T> | undefined;
2544
- findMany(filter: (entry: Entry) => boolean): Iterable<Entry>;
2545
- findByUrl(url: string, filter: (entry: Entry) => boolean): Entry | undefined;
2546
- referencesTo(query: EntryReferenceQuery): Promise<EntryReferenceResult>;
2547
- referencesFrom(sourceId: string): Promise<import("alinea/core/db/EntryReference").EntryReference[]>;
2548
- get references(): Promise<EntryReferenceIndex>;
2549
- sync(source: Source): Promise<string>;
2550
- syncWith(source: Source): Promise<string>;
2551
- indexChanges(batch: ChangesBatch, targetTree?: ReadonlyTree): Promise<string>;
2552
- seed(source: Source): Promise<void>;
2553
- byId(id: string): EntryNode | undefined;
2554
- fix(source: Source): Promise<void>;
2555
- transaction(source: Source): Promise<EntryTransaction>;
2556
- }
2557
- interface Seed {
2558
- seedId: string;
2559
- type: string;
2560
- workspace: string;
2561
- root: string;
2562
- locale: string | null;
2563
- data: Record<string, any>;
2564
- }
2565
- export {};
2566
-
2567
- }
2568
-
2569
2493
  declare module 'alinea/core/db/EntryReference' {
2570
2494
 
2571
2495
  import type { EntryStatus } from 'alinea/core/Entry';
@@ -2600,151 +2524,12 @@ declare module 'alinea/core/db/EntryReference' {
2600
2524
  export interface EntryReferenceResult {
2601
2525
  references: Array<EntryReference>;
2602
2526
  total: number;
2603
- scan: EntryReferenceScan;
2604
2527
  }
2605
2528
  export interface FieldReferenceContext {
2606
2529
  path: Array<string>;
2607
2530
  label?: string;
2608
2531
  }
2609
2532
  export function referenceFieldPath(path: Array<string>): string;
2610
- export interface EntryReferenceScan {
2611
- scanned: number;
2612
- total: number;
2613
- complete: boolean;
2614
- }
2615
-
2616
- }
2617
-
2618
- declare module 'alinea/core/db/EntryReferenceIndex' {
2619
-
2620
- import type { Config } from 'alinea/core/Config';
2621
- import type { EntryCondition } from 'alinea/core/db/EntryIndex';
2622
- import { type EntryReference, type EntryReferenceQuery, type EntryReferenceResult, type EntryReferenceScan } from 'alinea/core/db/EntryReference';
2623
- import type { Entry } from 'alinea/core/Entry';
2624
- export interface EntryReferenceGraph {
2625
- config: Config;
2626
- filter(filter: EntryCondition): Iterable<Entry>;
2627
- }
2628
- export interface EntryReferenceBuildOptions {
2629
- onProgress?: (scan: EntryReferenceScan) => void;
2630
- }
2631
- export class EntryReferenceIndex {
2632
- #private;
2633
- static build(graph: EntryReferenceGraph, options?: EntryReferenceBuildOptions): Promise<EntryReferenceIndex>;
2634
- constructor(config: Config);
2635
- referencesTo(query: EntryReferenceQuery): EntryReferenceResult;
2636
- referencesFrom(sourceId: string): Array<EntryReference>;
2637
- replaceEntry(entry: Entry): Set<string>;
2638
- removeFile(filePath: string): Set<string>;
2639
- replaceFile(filePath: string, references: Array<EntryReference>): Set<string>;
2640
- }
2641
-
2642
- }
2643
-
2644
- declare module 'alinea/core/db/EntryResolver' {
2645
-
2646
- import type { Config } from 'alinea/core/Config';
2647
- import { Entry as EntryExprs, type Entry } from 'alinea/core/Entry';
2648
- import type { Expr } from 'alinea/core/Expr';
2649
- import { Field } from 'alinea/core/Field';
2650
- import type { Filter } from 'alinea/core/Filter';
2651
- import { type AnyQueryResult, type Edge, type EdgeQuery, type GraphQuery, type Projection, type Status } from 'alinea/core/Graph';
2652
- import { type HasExpr } from 'alinea/core/Internal';
2653
- import type { Resolver } from 'alinea/core/Resolver';
2654
- import type { Type } from 'alinea';
2655
- import { type EntryCondition, type EntryFilter, type EntryGraph, type EntryIndex } from 'alinea/core/db/EntryIndex';
2656
- import { LinkResolver } from 'alinea/core/db/LinkResolver';
2657
- type Interim = any;
2658
- export interface PostContext {
2659
- linkResolver: LinkResolver;
2660
- }
2661
- export class EntryResolver implements Resolver {
2662
- #private;
2663
- config: Config;
2664
- index: EntryIndex;
2665
- constructor(config: Config, index: EntryIndex);
2666
- call(ctx: ResolveContext, entry: Entry, internal: {
2667
- method: string;
2668
- args: Array<Expr>;
2669
- }): unknown;
2670
- field(entry: Entry, field: Expr): unknown;
2671
- expr(ctx: ResolveContext, entry: Entry, expr: Expr): unknown;
2672
- projectTypes(types: Array<Type>): Array<[string, Expr]>;
2673
- projection(query: GraphQuery<Projection>): Projection;
2674
- sourceFilter(ctx: ResolveContext, entry: Entry, query: EdgeQuery): EntryCondition;
2675
- selectProjection(ctx: ResolveContext, entry: Entry, value: Projection): unknown;
2676
- select(ctx: ResolveContext, entry: Entry | null, query: GraphQuery<Projection>): unknown;
2677
- condition(ctx: ResolveContext, query: EdgeQuery): EntryFilter;
2678
- isSingleResult(query: GraphQuery & Partial<Edge>): boolean;
2679
- query(ctx: ResolveContext, query: GraphQuery<Projection>, preFilter?: EntryCondition): {
2680
- entries: EntryExprs<Record<string, unknown>>[];
2681
- getUnprocessed: () => unknown;
2682
- getProcessed: () => Promise<any>;
2683
- };
2684
- postField(ctx: PostContext, interim: Interim, field: Field): Promise<unknown>;
2685
- postExpr(ctx: PostContext, interim: Interim, expr: HasExpr): Promise<unknown>;
2686
- postRow(ctx: PostContext, interim: Interim, query: GraphQuery<Projection>): Promise<unknown>;
2687
- post(ctx: PostContext, interim: Interim, input: EdgeQuery<Projection>): Promise<unknown>;
2688
- resolve<Query extends GraphQuery>(query: Query): Promise<AnyQueryResult<Query>>;
2689
- }
2690
- export interface ResolveContext {
2691
- status: Status;
2692
- locale?: string | null;
2693
- graph: EntryGraph;
2694
- searchTerms?: string;
2695
- }
2696
- export function statusChecker(status: Status): Check;
2697
- interface Check {
2698
- (input: Entry): boolean;
2699
- }
2700
- export function filterChecker(filter: Filter, getField?: (input: any, name: string) => any): Check;
2701
- export {};
2702
-
2703
- }
2704
-
2705
- declare module 'alinea/core/db/EntryTarget' {
2706
-
2707
- import type { RemoteSource } from 'alinea/core/source/Source';
2708
- import type { CommitRequest } from 'alinea/core/db/CommitRequest';
2709
- export interface EntryTarget extends RemoteSource {
2710
- commit(request: CommitRequest): Promise<string>;
2711
- }
2712
-
2713
- }
2714
-
2715
- declare module 'alinea/core/db/EntryTransaction' {
2716
-
2717
- import { Config } from 'alinea/core/Config';
2718
- import { Policy } from 'alinea/core/Role';
2719
- import type { Source } from 'alinea/core/source/Source';
2720
- import type { ReadonlyTree } from 'alinea/core/source/Tree';
2721
- import { type CommitChange } from 'alinea/core/db/CommitRequest';
2722
- import type { EntryIndex } from 'alinea/core/db/EntryIndex';
2723
- import type { ArchiveMutation, CreateMutation, MoveMutation, Mutation, PublishMutation, RemoveFileMutation, RemoveMutation, UnpublishMutation, UpdateMutation, UploadFileMutation } from 'alinea/core/db/Mutation';
2724
- type Op<T> = Omit<T, 'op'>;
2725
- export class EntryTransaction {
2726
- #private;
2727
- constructor(config: Config, index: EntryIndex, source: Source, from: ReadonlyTree, policy?: Policy);
2728
- get empty(): boolean;
2729
- create({ locale, type, data, root, workspace, fromSeed, parentId, id, insertOrder, status, overwrite }: Op<CreateMutation>): this;
2730
- update({ id, locale, status, set }: Op<UpdateMutation>): this;
2731
- publish({ id, locale, status }: Op<PublishMutation>): this;
2732
- unpublish({ id, locale }: Op<UnpublishMutation>): this;
2733
- archive({ id, locale }: Op<ArchiveMutation>): this;
2734
- move({ id, target, dropPosition, targetType }: Op<MoveMutation>): this;
2735
- remove({ id, locale, status }: Op<RemoveMutation>): this;
2736
- removeFile(mutation: Op<RemoveFileMutation>): this;
2737
- uploadFile(mutation: Op<UploadFileMutation>): this;
2738
- description(): string;
2739
- apply(mutations: ReadonlyArray<Mutation>): Promise<void>;
2740
- toRequest(): Promise<{
2741
- fromSha: string;
2742
- intoSha: string;
2743
- description: string;
2744
- changes: CommitChange[];
2745
- }>;
2746
- }
2747
- export {};
2748
2533
 
2749
2534
  }
2750
2535
 
@@ -2767,8 +2552,9 @@ declare module 'alinea/core/db/EntryUrlConflictError' {
2767
2552
  declare module 'alinea/core/db/IndexEvent' {
2768
2553
 
2769
2554
  export class IndexEvent extends Event {
2770
- data: IndexOp;
2771
2555
  static readonly type = "index";
2556
+ static [Symbol.hasInstance](value: unknown): boolean;
2557
+ data: IndexOp;
2772
2558
  constructor(data: IndexOp);
2773
2559
  }
2774
2560
  export type IndexOp = {
@@ -2792,17 +2578,17 @@ declare module 'alinea/core/db/IndexEvent' {
2792
2578
  declare module 'alinea/core/db/LinkResolver' {
2793
2579
 
2794
2580
  import type { InferProjection, Projection } from 'alinea/core/Graph';
2795
- import type { EntryResolver, ResolveContext } from 'alinea/core/db/EntryResolver';
2581
+ import type { Config } from 'alinea/core/Config';
2796
2582
  export interface LinkTarget {
2797
2583
  entryId: string;
2798
2584
  locale?: string;
2799
2585
  }
2800
- export class LinkResolver {
2801
- resolver: EntryResolver;
2802
- private ctx;
2586
+ /** Field postprocessors depend on query capabilities, not an index backend. */
2587
+ export interface LinkResolver {
2588
+ config: Config;
2803
2589
  locale: string | null;
2804
- constructor(resolver: EntryResolver, ctx: ResolveContext, locale: string | null);
2805
- resolveLinks<P extends Projection>(projection: P, entryIds: ReadonlyArray<string>, locale?: string | null | undefined): Promise<Array<InferProjection<P>>>;
2590
+ includedAtBuild(filePath: string): boolean | Promise<boolean>;
2591
+ resolveLinks<P extends Projection>(projection: P, entryIds: ReadonlyArray<string>, locale?: string | null): Promise<Array<InferProjection<P>>>;
2806
2592
  resolveTargets<P extends Projection & {
2807
2593
  id: unknown;
2808
2594
  }>(projection: P, targets: ReadonlyArray<LinkTarget>): Promise<Array<InferProjection<P> | undefined>>;
@@ -2810,49 +2596,32 @@ declare module 'alinea/core/db/LinkResolver' {
2810
2596
 
2811
2597
  }
2812
2598
 
2813
- declare module 'alinea/core/db/LocalDB' {
2599
+ declare module 'alinea/core/db/LocalStore' {
2814
2600
 
2815
- import type { Config } from 'alinea/core/Config';
2816
- import type { SyncApi, UploadMetadata, UploadResponse } from 'alinea/core/Connection';
2817
- import type { AnyQueryResult, GraphQuery } from 'alinea/core/Graph';
2818
2601
  import type { Policy } from 'alinea/core/Role';
2819
- import type { ChangesBatch } from 'alinea/core/source/Change';
2820
- import type { GetBlobsOptions } from 'alinea/core/source/Source';
2821
- import { type Source } from 'alinea/core/source/Source';
2822
- import { type CommitRequest } from 'alinea/core/db/CommitRequest';
2823
- import { EntryIndex } from 'alinea/core/db/EntryIndex';
2824
- import type { EntryReferenceQuery, EntryReferenceResult } from 'alinea/core/db/EntryReference';
2602
+ import type { RemoteSource, Source } from 'alinea/core/source/Source';
2603
+ import type { CommitRequest } from 'alinea/core/db/CommitRequest';
2825
2604
  import type { Mutation } from 'alinea/core/db/Mutation';
2826
- import { WriteableGraph } from 'alinea/core/db/WriteableGraph';
2827
- export class LocalDB extends WriteableGraph {
2828
- #private;
2829
- config: Config;
2830
- index: EntryIndex;
2605
+ import type { WriteableGraph } from 'alinea/core/db/WriteableGraph';
2606
+ export interface SyncOptions {
2607
+ /** Skip entry validation for a remote whose entries were already validated. */
2608
+ validate?: boolean;
2609
+ }
2610
+ /**
2611
+ * A locally available entry store: a writeable graph backed by a source that
2612
+ * can be synced against a remote and committed to. Implemented by the SQLite
2613
+ * engine (`EntryStore`) and the `LocalDB` convenience facade.
2614
+ */
2615
+ export interface LocalStore extends WriteableGraph, RemoteSource {
2831
2616
  source: Source;
2832
- constructor(config: Config, source?: Source, index?: EntryIndex);
2833
- resolve<Query extends GraphQuery>(query: Query): Promise<AnyQueryResult<Query>>;
2834
- referencesTo(query: EntryReferenceQuery): Promise<EntryReferenceResult>;
2835
- get sha(): string;
2836
- indexChanges(batch: ChangesBatch): Promise<string>;
2837
- applyChanges(batch: ChangesBatch): Promise<void>;
2838
- getTreeIfDifferent(sha: string): Promise<import("alinea/core/source/Tree").ReadonlyTree | undefined>;
2839
- getBlobs(shas: ReadonlyArray<string>, options?: GetBlobsOptions): AsyncIterable<[sha: string, blob: Uint8Array<ArrayBufferLike>]>;
2617
+ sha: string | Promise<string>;
2840
2618
  sync(): Promise<string>;
2841
- syncWith(remote: SyncApi): Promise<string>;
2842
- logEntries(): Promise<void>;
2843
- request(mutations: ReadonlyArray<Mutation>, policy?: Policy): Promise<{
2844
- fromSha: string;
2845
- intoSha: string;
2846
- description: string;
2847
- changes: import("alinea/core/db/CommitRequest").CommitChange[];
2848
- }>;
2849
- mutate(mutations: Array<Mutation>): Promise<{
2850
- sha: string;
2851
- }>;
2619
+ syncWith(remote: RemoteSource, options?: SyncOptions): Promise<string>;
2620
+ includedAtBuild(filePath: string): boolean | Promise<boolean>;
2621
+ request(mutations: ReadonlyArray<Mutation>, policy?: Policy): Promise<CommitRequest>;
2852
2622
  write(request: CommitRequest): Promise<{
2853
2623
  sha: string;
2854
2624
  }>;
2855
- prepareUpload(file: string, _metadata?: UploadMetadata): Promise<UploadResponse>;
2856
2625
  }
2857
2626
 
2858
2627
  }
@@ -3034,30 +2803,6 @@ declare module 'alinea/core/db/Operation' {
3034
2803
 
3035
2804
  }
3036
2805
 
3037
- declare module 'alinea/core/db/TestDB' {
3038
-
3039
- import type { LocalConnection } from 'alinea/core/Connection';
3040
- import { type User, type UserInput } from 'alinea/core/User';
3041
- import { LocalDB } from 'alinea/core/db/LocalDB';
3042
- export class TestDB extends LocalDB implements LocalConnection {
3043
- capabilities(): Promise<{
3044
- users: boolean;
3045
- }>;
3046
- previewToken(): Promise<string>;
3047
- user(): Promise<User>;
3048
- enrichUser(user: User): Promise<User>;
3049
- listUsers(): Promise<Array<User>>;
3050
- createUser(user: UserInput): Promise<User>;
3051
- updateUser(user: UserInput): Promise<User>;
3052
- removeUser(): Promise<void>;
3053
- revisions(): Promise<never[]>;
3054
- revisionData(): Promise<undefined>;
3055
- getDraft(): Promise<undefined>;
3056
- storeDraft(): Promise<void>;
3057
- }
3058
-
3059
- }
3060
-
3061
2806
  declare module 'alinea/core/db/WriteableGraph' {
3062
2807
 
3063
2808
  import type { Infer } from 'alinea';
@@ -3174,6 +2919,8 @@ declare module 'alinea/core/Entry' {
3174
2919
  export const ALT_STATUS: Array<EntryStatus>;
3175
2920
  export interface Entry<Data extends object = Record<string, unknown>> {
3176
2921
  id: string;
2922
+ /** Physical authored version before status inheritance is applied. */
2923
+ versionStatus?: EntryStatus;
3177
2924
  status: EntryStatus;
3178
2925
  title: string;
3179
2926
  type: string;
@@ -3206,6 +2953,7 @@ declare module 'alinea/core/Entry' {
3206
2953
  }
3207
2954
  export const Entry: {
3208
2955
  id: Expr<string>;
2956
+ versionStatus: Expr<EntryStatus>;
3209
2957
  status: Expr<EntryStatus>;
3210
2958
  title: Expr<string>;
3211
2959
  type: Expr<string>;
@@ -3328,6 +3076,30 @@ declare module 'alinea/core/EntryRecord' {
3328
3076
 
3329
3077
  }
3330
3078
 
3079
+ declare module 'alinea/core/EntrySeed' {
3080
+
3081
+ import { type Config as ConfigType } from 'alinea/core/Config';
3082
+ import type { Mutation } from 'alinea/core/db/Mutation';
3083
+ import type { Graph } from 'alinea/core/Graph';
3084
+ export interface EntrySeed {
3085
+ id: string;
3086
+ type: string;
3087
+ workspace: string;
3088
+ root: string;
3089
+ locale: string | null;
3090
+ filePath: string;
3091
+ nodePath: string;
3092
+ parentNodePath: string | null;
3093
+ seedPath: string;
3094
+ data: Record<string, unknown>;
3095
+ }
3096
+ export function entrySeeds(config: ConfigType): ReadonlyArray<EntrySeed>;
3097
+ /** Plan the creations needed to materialize every configured seed. */
3098
+ export function seedMutations(graph: Graph, config: ConfigType): Promise<Array<Mutation>>;
3099
+ export function seedData(config: ConfigType, seedPath: string | null, data: Record<string, unknown>, location: Pick<EntrySeed, 'workspace' | 'root' | 'locale'>): Record<string, unknown>;
3100
+
3101
+ }
3102
+
3331
3103
  declare module 'alinea/core/Expr' {
3332
3104
 
3333
3105
  import { type HasExpr, internalExpr } from 'alinea/core/Internal';
@@ -3390,6 +3162,10 @@ declare module 'alinea/core/Field' {
3390
3162
  export interface FieldAnchorContext {
3391
3163
  anchors: Set<string>;
3392
3164
  }
3165
+ export interface FieldLocalizeContext {
3166
+ locale: string;
3167
+ entryIds: Set<string>;
3168
+ }
3393
3169
  export type WithoutLabel<Options extends FieldOptions<any>> = Omit<Options, 'label'>;
3394
3170
  export interface FieldMeta<StoredValue, QueryValue, Mutator, Options> {
3395
3171
  options: Options & FieldOptions<StoredValue>;
@@ -3404,6 +3180,8 @@ declare module 'alinea/core/Field' {
3404
3180
  references?: (value: StoredValue, context: FieldReferenceContext) => Array<EntryReferenceTarget>;
3405
3181
  anchors?: (value: StoredValue, context: FieldReferenceContext) => Array<EntryAnchorTarget>;
3406
3182
  normalizeAnchors?: (value: StoredValue, context: FieldAnchorContext) => StoredValue;
3183
+ /** Point entry links to entryIds at the given locale */
3184
+ localizeLinks?: (value: StoredValue, context: FieldLocalizeContext) => StoredValue;
3407
3185
  beforeSave?: (context: FieldBeforeSaveContext<StoredValue>) => StoredValue;
3408
3186
  }
3409
3187
  export type FieldBeforeSaveAction = 'create' | 'update' | 'publish' | 'translate';
@@ -3450,6 +3228,7 @@ declare module 'alinea/core/Field' {
3450
3228
  function references<StoredValue, QueryValue, Mutator, Options>(field: Field<StoredValue, QueryValue, Mutator, Options>, value: StoredValue, context: FieldReferenceContext): Array<EntryReferenceTarget>;
3451
3229
  function anchors<StoredValue, QueryValue, Mutator, Options>(field: Field<StoredValue, QueryValue, Mutator, Options>, value: StoredValue, context: FieldReferenceContext): Array<EntryAnchorTarget>;
3452
3230
  function normalizeAnchors<StoredValue>(field: HasField, value: StoredValue, context: FieldAnchorContext): StoredValue;
3231
+ function localizeLinks<StoredValue>(field: HasField, value: StoredValue, context: FieldLocalizeContext): StoredValue;
3453
3232
  function isField(value: any): value is Field;
3454
3233
  }
3455
3234
  export {};
@@ -3619,8 +3398,6 @@ declare module 'alinea/core/Filter' {
3619
3398
  type FieldOps<Fields> = {
3620
3399
  [K in keyof Fields]?: Condition<Fields[K]>;
3621
3400
  };
3622
- export interface AnyCondition<Value> extends Ops<Value>, ArrayOps<Value>, ObjectOps<Value> {
3623
- }
3624
3401
  export type Condition<Value> = [Value] extends [Primitive] ? Ops<Value> | Value : [Value] extends [Array<any>] ? ArrayOps<Value[0]> : ObjectOps<Value>;
3625
3402
  type AndCondition<Fields> = {
3626
3403
  and: Array<Filter<Fields> | undefined>;
@@ -3629,6 +3406,46 @@ declare module 'alinea/core/Filter' {
3629
3406
  or: Array<Filter<Fields> | undefined>;
3630
3407
  };
3631
3408
  export type Filter<Fields = unknown> = AndCondition<Fields> | OrCondition<Fields> | FieldOps<Fields>;
3409
+ /** A validated filter over named fields, shared by every filter backend. */
3410
+ export type FilterNode = {
3411
+ op: 'and';
3412
+ nodes: Array<FilterNode>;
3413
+ } | {
3414
+ op: 'or';
3415
+ nodes: Array<FilterNode>;
3416
+ } | {
3417
+ op: 'field';
3418
+ name: string;
3419
+ condition: ConditionNode;
3420
+ };
3421
+ /** A validated condition on one value. */
3422
+ export type ConditionNode = {
3423
+ op: 'and';
3424
+ nodes: Array<ConditionNode>;
3425
+ } | {
3426
+ op: 'or';
3427
+ nodes: Array<ConditionNode>;
3428
+ } | {
3429
+ op: 'is' | 'isNot' | 'gt' | 'gte' | 'lt' | 'lte';
3430
+ value: unknown;
3431
+ } | {
3432
+ op: 'in' | 'notIn';
3433
+ values: Array<unknown>;
3434
+ } | {
3435
+ op: 'startsWith';
3436
+ value: string;
3437
+ } | {
3438
+ op: 'has';
3439
+ filter: FilterNode;
3440
+ } | {
3441
+ op: 'includes';
3442
+ item: ConditionNode;
3443
+ };
3444
+ export function parseFilter(filter: unknown): FilterNode;
3445
+ export function parseCondition(condition: unknown): ConditionNode;
3446
+ type FilterCheck<Input> = (input: Input) => boolean;
3447
+ type FieldAccessor<Input> = (input: Input, name: string) => unknown;
3448
+ export function filterChecker<Input = unknown>(filter: Filter, getField?: FieldAccessor<Input>): FilterCheck<Input>;
3632
3449
  export {};
3633
3450
 
3634
3451
  }
@@ -3637,6 +3454,7 @@ declare module 'alinea/core/Graph' {
3637
3454
 
3638
3455
  import type { Root, Workspace } from 'alinea/types';
3639
3456
  import type { Config } from 'alinea/core/Config';
3457
+ import type { EntryStatus } from 'alinea/core/Entry';
3640
3458
  import type { EntryFields } from 'alinea/core/EntryFields';
3641
3459
  import type { Expr } from 'alinea/core/Expr';
3642
3460
  import type { Condition, Filter } from 'alinea/core/Filter';
@@ -3738,8 +3556,16 @@ declare module 'alinea/core/Graph' {
3738
3556
  id?: Condition<string>;
3739
3557
  /** Filter by parentId */
3740
3558
  parentId?: Condition<string | null>;
3559
+ /** Filter by the physical authored status before inheritance. */
3560
+ versionStatus?: Condition<EntryStatus>;
3561
+ /** Filter by whether this is the preferred version for its locale. */
3562
+ main?: Condition<boolean>;
3741
3563
  /** Filter by path */
3742
3564
  path?: Condition<string>;
3565
+ /** Filter by the full source file path. */
3566
+ filePath?: Condition<string>;
3567
+ /** Filter by the configured seed identity. */
3568
+ seeded?: Condition<string | null>;
3743
3569
  /** Filter by url */
3744
3570
  url?: Condition<string>;
3745
3571
  /** Filter by a metadata URL alias */
@@ -3969,6 +3795,7 @@ declare module 'alinea/core/ListRow' {
3969
3795
  const id = "_id";
3970
3796
  const index = "_index";
3971
3797
  const type = "_type";
3798
+ const label = "_label";
3972
3799
  }
3973
3800
 
3974
3801
  }
@@ -4329,6 +4156,7 @@ declare module 'alinea/core/Reference' {
4329
4156
  export interface Reference {
4330
4157
  _id: string;
4331
4158
  _type: string;
4159
+ _label?: string;
4332
4160
  }
4333
4161
  export namespace Reference {
4334
4162
  const id = "_id";
@@ -4592,6 +4420,8 @@ declare module 'alinea/core/Scope' {
4592
4420
  keyOf(entity: Entity): string;
4593
4421
  locationOf(entity: Entity): string[] | undefined;
4594
4422
  nameOf(entity: Entity): string | undefined;
4423
+ /** Configured spellings of a locale, matched case-insensitively. */
4424
+ locales(locale: string): Array<string>;
4595
4425
  stringify(input: any): string;
4596
4426
  parse<Result>(input: string): Result;
4597
4427
  }
@@ -4719,11 +4549,23 @@ declare module 'alinea/core/source/FSSource' {
4719
4549
  import type { ChangesBatch } from 'alinea/core/source/Change';
4720
4550
  import type { GetBlobsOptions, Source } from 'alinea/core/source/Source';
4721
4551
  import { ReadonlyTree, WriteableTree } from 'alinea/core/source/Tree';
4552
+ /** The filesystem metadata used to detect that a file did not change. */
4553
+ export interface FileStat {
4554
+ mtimeMs: number;
4555
+ size: number;
4556
+ }
4722
4557
  export class FSSource implements Source {
4723
4558
  #private;
4724
4559
  constructor(cwd: string);
4560
+ /**
4561
+ * Restore a previously persisted tree and the file stats it was built from,
4562
+ * so unchanged files are not read again.
4563
+ */
4564
+ hydrate(tree: ReadonlyTree, stats: ReadonlyMap<string, FileStat>): void;
4565
+ /** The file stats of every file in the current tree. */
4566
+ fileStats(): ReadonlyMap<string, FileStat>;
4725
4567
  getTree(): Promise<ReadonlyTree>;
4726
- getFile(current: ReadonlyTree, builder: WriteableTree, file: string): Promise<readonly [string, Uint8Array<ArrayBufferLike>] | undefined>;
4568
+ getFile(current: ReadonlyTree, builder: WriteableTree, filePath: string): Promise<readonly [string, Uint8Array<ArrayBufferLike>] | undefined>;
4727
4569
  getTreeIfDifferent(sha: string): Promise<ReadonlyTree | undefined>;
4728
4570
  getBlobs(shas: ReadonlyArray<string>, options?: GetBlobsOptions): AsyncGenerator<[sha: string, blob: Uint8Array]>;
4729
4571
  readBlob(sha: string, file: string, signal?: AbortSignal): Promise<[sha: string, blob: Uint8Array]>;
@@ -4732,7 +4574,7 @@ declare module 'alinea/core/source/FSSource' {
4732
4574
  export class CachedFSSource extends FSSource {
4733
4575
  #private;
4734
4576
  constructor(cwd: string);
4735
- refresh: () => Promise<ReadonlyTree>;
4577
+ refresh(): Promise<ReadonlyTree>;
4736
4578
  getTree(): Promise<ReadonlyTree>;
4737
4579
  applyChanges(batch: ChangesBatch): Promise<void>;
4738
4580
  getFile(current: ReadonlyTree, builder: WriteableTree, file: string): Promise<readonly [string, Uint8Array<ArrayBufferLike>] | undefined>;
@@ -4790,6 +4632,10 @@ declare module 'alinea/core/source/GitUtils' {
4790
4632
  * Serializes a TreeData object into a Git tree object as a Uint8Array.
4791
4633
  */
4792
4634
  export function serializeTreeEntries(entries: Array<Entry>): Uint8Array;
4635
+ /**
4636
+ * Compares two TreeEntry objects for sorting.
4637
+ */
4638
+ export function compareTreeEntries(a: Entry, b: Entry): number;
4793
4639
  /**
4794
4640
  * Parses a Git tree object from a Uint8Array into a TreeEntry object.
4795
4641
  */
@@ -4812,7 +4658,7 @@ declare module 'alinea/core/source/GitUtils' {
4812
4658
  declare module 'alinea/core/source/IndexedDBSource' {
4813
4659
 
4814
4660
  import type { ChangesBatch } from 'alinea/core/source/Change';
4815
- import type { GetBlobsOptions, Source } from 'alinea/core/source/Source';
4661
+ import type { GetBlobsOptions, RemoteSource, Source } from 'alinea/core/source/Source';
4816
4662
  import { ReadonlyTree } from 'alinea/core/source/Tree';
4817
4663
  export class IndexedDBSource implements Source {
4818
4664
  #private;
@@ -4821,6 +4667,7 @@ declare module 'alinea/core/source/IndexedDBSource' {
4821
4667
  getTreeIfDifferent(sha: string): Promise<ReadonlyTree | undefined>;
4822
4668
  getBlobs(shas: ReadonlyArray<string>, options?: GetBlobsOptions): AsyncGenerator<[sha: string, blob: Uint8Array]>;
4823
4669
  applyChanges(batch: ChangesBatch): Promise<void>;
4670
+ applyChangesFrom(remote: RemoteSource, batch: ChangesBatch, tree: ReadonlyTree): Promise<void>;
4824
4671
  }
4825
4672
 
4826
4673
  }
@@ -4845,8 +4692,8 @@ declare module 'alinea/core/source/MemorySource' {
4845
4692
  declare module 'alinea/core/source/OverlaySource' {
4846
4693
 
4847
4694
  import type { ChangesBatch } from 'alinea/core/source/Change';
4848
- import type { GetBlobsOptions, Source } from 'alinea/core/source/Source';
4849
- import type { ReadonlyTree } from 'alinea/core/source/Tree';
4695
+ import type { GetBlobsOptions, RemoteSource, Source } from 'alinea/core/source/Source';
4696
+ import { type ReadonlyTree } from 'alinea/core/source/Tree';
4850
4697
  export class OverlaySource implements Source {
4851
4698
  #private;
4852
4699
  constructor(source: Source, tree: ReadonlyTree);
@@ -4855,6 +4702,9 @@ declare module 'alinea/core/source/OverlaySource' {
4855
4702
  getTreeIfDifferent(sha: string): Promise<ReadonlyTree | undefined>;
4856
4703
  getBlobs(shas: ReadonlyArray<string>, options?: GetBlobsOptions): AsyncGenerator<[sha: string, blob: Uint8Array]>;
4857
4704
  applyChanges(batch: ChangesBatch): Promise<void>;
4705
+ applyChangesFrom(remote: RemoteSource, batch: ChangesBatch, tree: ReadonlyTree): Promise<void>;
4706
+ /** Apply a batch while reusing an already received or compiled target tree. */
4707
+ applyChangesTo(batch: ChangesBatch, tree?: ReadonlyTree): Promise<void>;
4858
4708
  }
4859
4709
 
4860
4710
  }
@@ -4884,7 +4734,10 @@ declare module 'alinea/core/source/Source' {
4884
4734
  export interface Source extends RemoteSource {
4885
4735
  getTree(): Promise<ReadonlyTree>;
4886
4736
  applyChanges(batch: ChangesBatch): Promise<void>;
4737
+ /** Consume remote blobs incrementally instead of materializing one bundle. */
4738
+ applyChangesFrom?(remote: RemoteSource, batch: ChangesBatch, tree: ReadonlyTree): Promise<void>;
4887
4739
  }
4740
+ export function applyChangesFrom(source: Source, remote: RemoteSource, batch: ChangesBatch, tree: ReadonlyTree): Promise<void>;
4888
4741
  export function bundleContents(source: RemoteSource, batch: ChangesBatch): Promise<ChangesBatch>;
4889
4742
  export function diff(source: Source, remote: RemoteSource): Promise<ChangesBatch>;
4890
4743
  export function syncWith(source: Source, remote: RemoteSource): Promise<ChangesBatch>;
@@ -4951,7 +4804,6 @@ declare module 'alinea/core/source/Tree' {
4951
4804
  readonly sha: string;
4952
4805
  readonly mode: string;
4953
4806
  constructor({ sha, mode }: BaseEntry);
4954
- clone(): Leaf;
4955
4807
  toJSON(): BaseEntry;
4956
4808
  }
4957
4809
  class TreeBase<Node extends TreeBase<Node>> {
@@ -4975,10 +4827,14 @@ declare module 'alinea/core/source/Tree' {
4975
4827
  readonly sha: string;
4976
4828
  static readonly EMPTY: ReadonlyTree;
4977
4829
  constructor({ sha, entries }: Tree);
4830
+ /** A tree over built nodes, which it shares instead of copying. */
4831
+ static fromNodes(nodes: Map<string, ReadonlyTree | Leaf>, sha?: string): Promise<ReadonlyTree>;
4978
4832
  get isEmpty(): boolean;
4979
4833
  get entries(): Array<Entry>;
4980
- get shas(): ReadonlySet<string>;
4834
+ /** A path holding the blob, if this tree has it. */
4835
+ pathOf(sha: string): string | undefined;
4981
4836
  hasSha(sha: string): boolean;
4837
+ /** A writable tree sharing these nodes until they change. */
4982
4838
  clone(): WriteableTree;
4983
4839
  toJSON(): {
4984
4840
  sha: string;
@@ -4991,18 +4847,18 @@ declare module 'alinea/core/source/Tree' {
4991
4847
  };
4992
4848
  withChanges(batch: ChangesBatch): Promise<ReadonlyTree>;
4993
4849
  static fromFlat(tree: FlatTree): ReadonlyTree;
4994
- diff(that: TreeBase<any>): ChangesBatch;
4850
+ diff(that: ReadonlyTree | WriteableTree): ChangesBatch;
4995
4851
  }
4996
- export class WriteableTree extends TreeBase<WriteableTree> {
4852
+ /** A mutable tree, which copies shared readonly subtrees once they change. */
4853
+ export class WriteableTree extends TreeBase<WriteableTree | ReadonlyTree> {
4997
4854
  #private;
4998
4855
  constructor({ sha, entries }?: Tree);
4999
- add(path: string, input: {
5000
- clone(): WriteableTree | Leaf;
5001
- } | string): void;
4856
+ add(path: string, input: WriteableTree | ReadonlyTree | Leaf | string): void;
5002
4857
  remove(path: string): boolean;
5003
4858
  rename(from: string, to: string): void;
5004
4859
  applyChanges(batch: ChangesBatch): void;
5005
4860
  getSha(): Promise<string>;
4861
+ /** Compile, reusing the nodes of a previous tree wherever they are equal. */
5006
4862
  compile(previous?: ReadonlyTree): Promise<ReadonlyTree>;
5007
4863
  clone(): WriteableTree;
5008
4864
  }
@@ -5159,7 +5015,7 @@ declare module 'alinea/core/Type' {
5159
5015
  import type { Config } from 'alinea/core/Config';
5160
5016
  import type { EntryStatus } from 'alinea/core/Entry';
5161
5017
  import type { Expr } from 'alinea/core/Expr';
5162
- import { Field, type EntryAnchorTarget, type FieldAnchorContext, type FieldBeforeSaveContext } from 'alinea/core/Field';
5018
+ import { Field, type EntryAnchorTarget, type FieldAnchorContext, type FieldLocalizeContext, type FieldBeforeSaveContext } from 'alinea/core/Field';
5163
5019
  import { type HasType } from 'alinea/core/Internal';
5164
5020
  import type { Label } from 'alinea/core/Label';
5165
5021
  import type { OrderBy } from 'alinea/core/OrderBy';
@@ -5205,6 +5061,7 @@ declare module 'alinea/core/Type' {
5205
5061
  function initialValue(type: Type): Record<string, unknown>;
5206
5062
  function withInitialValue(type: Type, value: Record<string, unknown>): Record<string, unknown>;
5207
5063
  function normalizeAnchors(type: Type, value: Record<string, unknown>, context?: FieldAnchorContext): Record<string, unknown>;
5064
+ function localizeLinks(type: Type, value: Record<string, unknown>, context: FieldLocalizeContext): Record<string, unknown>;
5208
5065
  function beforeSave(type: Type, value: Record<string, unknown>, context: Omit<FieldBeforeSaveContext<unknown>, 'value'>): Record<string, unknown>;
5209
5066
  function applyLinks(type: Type, value: Record<string, unknown>, loader: import('./db/LinkResolver.js').LinkResolver): Promise<void>;
5210
5067
  function preview(type: Type): Preview | undefined;
@@ -5306,6 +5163,12 @@ declare module 'alinea/core/util/ArrayBuffers' {
5306
5163
 
5307
5164
  }
5308
5165
 
5166
+ declare module 'alinea/core/util/Arrays' {
5167
+
5168
+ export function chunks<T>(items: ReadonlyArray<T>, size: number): Generator<Array<T>>;
5169
+
5170
+ }
5171
+
5309
5172
  declare module 'alinea/core/util/Assert' {
5310
5173
 
5311
5174
  export function assert(value: unknown, message?: string): asserts value;
@@ -5324,7 +5187,14 @@ declare module 'alinea/core/util/Async' {
5324
5187
  /** Map concurrently while yielding results as they complete. */
5325
5188
  export function mapConcurrent<T, TResult>(iterable: Iterable<T>, mapper: (item: T, signal?: AbortSignal) => Promise<TResult>, options: MapConcurrentOptions): AsyncGenerator<TResult>;
5326
5189
  export function genEffect<T, TReturn>(gen: AsyncIterable<T, TReturn>, effect: (result: T) => void): AsyncIterable<T>;
5327
-
5190
+ /** Run tasks one after another; a failed task does not block the next one. */
5191
+ export class TaskQueue {
5192
+ #private;
5193
+ run<T>(task: () => Promise<T>): Promise<T>;
5194
+ /** Resolve once every task queued so far has settled. */
5195
+ drain(): Promise<void>;
5196
+ }
5197
+
5328
5198
  }
5329
5199
 
5330
5200
  declare module 'alinea/core/util/BufferToBase64' {
@@ -5483,10 +5353,29 @@ declare module 'alinea/core/util/Encoding' {
5483
5353
 
5484
5354
  declare module 'alinea/core/util/EntryFilenames' {
5485
5355
 
5486
- import type { Config } from 'alinea/core/Config';
5356
+ import { Config } from 'alinea/core/Config';
5487
5357
  import { type Entry, type EntryStatus } from 'alinea/core/Entry';
5488
5358
  import type { EntryUrlInput, Type } from 'alinea/core/Type';
5489
5359
  export function entryInfo(fileName: string): [name: string, status: EntryStatus];
5360
+ /** The workspace/root/locale layout encoded in a source file path. */
5361
+ export interface EntryFileLocation {
5362
+ workspace: string;
5363
+ root: string;
5364
+ locale: string | null;
5365
+ path: string;
5366
+ versionStatus: EntryStatus;
5367
+ parentDir: string;
5368
+ childrenDir: string;
5369
+ /** Segments taken up by the workspace, root and locale. */
5370
+ levelOffset: number;
5371
+ level: number;
5372
+ parentPaths: Array<string>;
5373
+ }
5374
+ /**
5375
+ * Read back the layout `Config.filePath` and `entryChildrenDir` write:
5376
+ * `[workspace/]root/[locale/]...parents/path[.status].json`.
5377
+ */
5378
+ export function parseEntryFilePath(config: Config, filePath: string): EntryFileLocation;
5490
5379
  export function entryChildrenDir(config: Config, entry: {
5491
5380
  workspace: string;
5492
5381
  root: string;
@@ -5494,6 +5383,8 @@ declare module 'alinea/core/util/EntryFilenames' {
5494
5383
  path: string;
5495
5384
  status: EntryStatus;
5496
5385
  }, parentPaths: Array<string>): string;
5386
+ /** The `<childrenDir>[.status].json` file holding a single entry version. */
5387
+ export function entryVersionFile(childrenDir: string, status: EntryStatus): string;
5497
5388
  export function entryFilepath(config: Config, entry: {
5498
5389
  workspace: string;
5499
5390
  root: string;
@@ -5562,6 +5453,15 @@ declare module 'alinea/core/util/Identifiers' {
5562
5453
 
5563
5454
  }
5564
5455
 
5456
+ declare module 'alinea/core/util/IndexedDB' {
5457
+
5458
+ /** Resolve once an IndexedDB transaction has committed. */
5459
+ export function transactionComplete(tx: IDBTransaction): Promise<void>;
5460
+ /** Resolve with the result of an IndexedDB request. */
5461
+ export function requestResult<T>(request: IDBRequest<T>): Promise<T>;
5462
+
5463
+ }
5464
+
5565
5465
  declare module 'alinea/core/util/JWT' {
5566
5466
 
5567
5467
  const algorithms: {
@@ -7000,10 +6900,6 @@ declare module 'alinea/dashboard/atoms/config' {
7000
6900
  export const workspaceAtom: (workspaceKey: string) => import("jotai").Atom<import("alinea/core/Workspace").WorkspaceInternal>;
7001
6901
  export const viewAtoms: (key: string) => import("jotai").Atom<ComponentType | undefined>;
7002
6902
  export const typeAtoms: (typeName: string) => import("jotai").Atom<{
7003
- type: import("alinea/types").Type;
7004
- customView: ((props: {
7005
- type: import("alinea/types").Type;
7006
- }) => import("react").ReactNode) | ComponentType | undefined;
7007
6903
  fields: import("alinea/core/Type").FieldsDefinition;
7008
6904
  contains?: Array<string | import("alinea/types").Type>;
7009
6905
  orderChildrenBy?: import("alinea/core/OrderBy").OrderBy | Array<import("alinea/core/OrderBy").OrderBy>;
@@ -7021,6 +6917,10 @@ declare module 'alinea/dashboard/atoms/config' {
7021
6917
  label: string;
7022
6918
  allFields: Record<string, import("alinea/core").Field>;
7023
6919
  sections: Array<import("alinea/core/Section").Section>;
6920
+ type: import("alinea/types").Type;
6921
+ customView: ((props: {
6922
+ type: import("alinea/types").Type;
6923
+ }) => import("react").ReactNode) | ComponentType | undefined;
7024
6924
  }>;
7025
6925
 
7026
6926
  }
@@ -7183,7 +7083,7 @@ declare module 'alinea/dashboard/atoms/editor' {
7183
7083
 
7184
7084
  declare module 'alinea/dashboard/atoms/entry' {
7185
7085
 
7186
- import type { EntryReference, EntryReferenceScan } from 'alinea/core/db/EntryReference';
7086
+ import type { EntryReference } from 'alinea/core/db/EntryReference';
7187
7087
  import { Entry, EntryStatus } from 'alinea/core/Entry';
7188
7088
  import type { Order } from 'alinea/core/Graph';
7189
7089
  import { type EntryDefaultView } from 'alinea/core/Type';
@@ -7209,7 +7109,6 @@ declare module 'alinea/dashboard/atoms/entry' {
7209
7109
  export interface EntryReferences {
7210
7110
  references: Array<EntryReferenceWithSource>;
7211
7111
  total: number;
7212
- scan: EntryReferenceScan;
7213
7112
  }
7214
7113
  export interface EntryReferenceWithSource {
7215
7114
  reference: EntryReference;
@@ -7262,10 +7161,8 @@ declare module 'alinea/dashboard/atoms/entry' {
7262
7161
  init: null;
7263
7162
  };
7264
7163
  previewEntryReady: Atom<Promise<{
7265
- title: string;
7266
- path: string;
7267
- data: Record<string, unknown>;
7268
7164
  id: string;
7165
+ versionStatus?: EntryStatus;
7269
7166
  status: EntryStatus;
7270
7167
  type: string;
7271
7168
  seeded: string | null;
@@ -7285,12 +7182,13 @@ declare module 'alinea/dashboard/atoms/entry' {
7285
7182
  fileHash: string;
7286
7183
  url: string;
7287
7184
  searchableText: string;
7288
- }>>;
7289
- previewEntry: Atom<{
7290
7185
  title: string;
7291
7186
  path: string;
7292
7187
  data: Record<string, unknown>;
7188
+ }>>;
7189
+ previewEntry: Atom<{
7293
7190
  id: string;
7191
+ versionStatus?: EntryStatus;
7294
7192
  status: EntryStatus;
7295
7193
  type: string;
7296
7194
  seeded: string | null;
@@ -7310,6 +7208,9 @@ declare module 'alinea/dashboard/atoms/entry' {
7310
7208
  fileHash: string;
7311
7209
  url: string;
7312
7210
  searchableText: string;
7211
+ title: string;
7212
+ path: string;
7213
+ data: Record<string, unknown>;
7313
7214
  } | undefined>;
7314
7215
  previewUrlReady: Atom<Promise<string | undefined>>;
7315
7216
  previewUrlState: Atom<readonly [false, string | undefined] | readonly [true, string | undefined]>;
@@ -7370,7 +7271,6 @@ declare module 'alinea/dashboard/atoms/entry' {
7370
7271
  };
7371
7272
  }[];
7372
7273
  total: number;
7373
- scan: EntryReferenceScan;
7374
7274
  }>>;
7375
7275
  incomingReferences: Atom<{
7376
7276
  references: {
@@ -7388,7 +7288,6 @@ declare module 'alinea/dashboard/atoms/entry' {
7388
7288
  };
7389
7289
  }[];
7390
7290
  total: number;
7391
- scan: EntryReferenceScan;
7392
7291
  } | undefined>;
7393
7292
  type: Atom<string>;
7394
7293
  parentId: Atom<string | null>;
@@ -7469,6 +7368,8 @@ declare module 'alinea/dashboard/atoms/explorer' {
7469
7368
  import type { DragItem, DragTypes, DropOperation, DropTarget } from '@react-types/shared';
7470
7369
  import type { DroppableCollectionOnItemDropEvent, Key } from 'react-aria-components';
7471
7370
  export const dashboardEntryOverviewColumnCount = 5;
7371
+ /** The best ranked matches a search loads into the explorer. */
7372
+ export const searchResultLimit = 100;
7472
7373
  export type ExplorerView = 'card' | 'row';
7473
7374
  export type ExplorerSortBy = 'title' | 'path' | 'size' | 'id' | 'index';
7474
7375
  export type ExplorerSortDirections = 'asc' | 'desc';
@@ -8106,14 +8007,14 @@ declare module 'alinea/dashboard/boot/DashboardWorker' {
8106
8007
  import type { Config } from 'alinea/core/Config';
8107
8008
  import type { LocalConnection } from 'alinea/core/Connection';
8108
8009
  import type { EntryReferenceQuery, EntryReferenceResult } from 'alinea/core/db/EntryReference';
8109
- import { LocalDB } from 'alinea/core/db/LocalDB';
8110
8010
  import type { Mutation } from 'alinea/core/db/Mutation';
8111
8011
  import type { Source } from 'alinea/core/source/Source';
8012
+ import { EntryStore } from 'alinea/database/EntryStore';
8112
8013
  import { type Activity } from 'alinea/dashboard/boot/ActivityEvent';
8113
8014
  export class DashboardWorker extends EventTarget {
8114
8015
  #private;
8115
- constructor(source: Source);
8116
- get db(): LocalDB | Promise<LocalDB>;
8016
+ constructor(source?: Source);
8017
+ get db(): EntryStore | Promise<EntryStore>;
8117
8018
  sync(): Promise<string>;
8118
8019
  sha(): Promise<string>;
8119
8020
  queue(id: string, mutations: Array<Mutation>): Promise<string>;
@@ -8596,6 +8497,8 @@ declare module 'alinea/dashboard/icons' {
8596
8497
  export function IcOutlineTableRows(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
8597
8498
  export function IcOutlineViewList(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
8598
8499
  export function IcRoundDescription(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
8500
+ export function IcRoundBolt(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
8501
+ export function IcRoundInfo(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
8599
8502
  export function IcTwotoneDescription(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
8600
8503
  export function IcTwotoneFolder(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
8601
8504
  export function IcRoundInsertDriveFile(props: SVGProps<SVGSVGElement>): import("react").JSX.Element;
@@ -8800,6 +8703,7 @@ declare module 'alinea/dashboard/ViewKeys' {
8800
8703
  MetadataTimestampInput: string;
8801
8704
  MetadataUserInput: string;
8802
8705
  MetadataInput: string;
8706
+ MetadataDetailsView: string;
8803
8707
  NumberInput: string;
8804
8708
  ObjectInput: string;
8805
8709
  PathInput: string;
@@ -8814,6 +8718,1055 @@ declare module 'alinea/dashboard/ViewKeys' {
8814
8718
 
8815
8719
  }
8816
8720
 
8721
+ declare module 'alinea/database/BrowserEntryStore' {
8722
+
8723
+ import type { Config } from 'alinea/core/Config';
8724
+ import type { Mutation } from 'alinea/core/db/Mutation';
8725
+ import type { CommitRequest } from 'alinea/core/db/CommitRequest';
8726
+ import type { SyncOptions } from 'alinea/core/db/LocalStore';
8727
+ import type { RemoteSource } from 'alinea/core/source/Source';
8728
+ import { EntryStore } from 'alinea/database/EntryStore';
8729
+ export interface BrowserEntryStoreOptions {
8730
+ indexedDB: IDBFactory;
8731
+ name: string;
8732
+ revision: string;
8733
+ }
8734
+ /** WASM entry store persisted as one SQLite file in IndexedDB. */
8735
+ export class BrowserEntryStore extends EntryStore {
8736
+ #private;
8737
+ static open(config: Config, options: BrowserEntryStoreOptions): Promise<BrowserEntryStore>;
8738
+ private constructor();
8739
+ sync(): Promise<string>;
8740
+ syncWith(remote: RemoteSource, options?: SyncOptions): Promise<string>;
8741
+ mutate(mutations: Array<Mutation>): Promise<{
8742
+ sha: string;
8743
+ }>;
8744
+ write(request: CommitRequest): Promise<{
8745
+ sha: string;
8746
+ }>;
8747
+ close(): Promise<void>;
8748
+ /** Close a superseded store without persisting stale bytes over its
8749
+ * replacement's cache. Supersede always implies a revision change, so the
8750
+ * persisted revision would be discarded on the next open anyway. */
8751
+ abandon(): Promise<void>;
8752
+ }
8753
+
8754
+ }
8755
+
8756
+ declare module 'alinea/database/DatabaseSource' {
8757
+
8758
+ import type { ChangesBatch } from 'alinea/core/source/Change';
8759
+ import type { GetBlobsOptions, Source } from 'alinea/core/source/Source';
8760
+ import type { ReadonlyTree } from 'alinea/core/source/Tree';
8761
+ import type { EntryLayer } from 'alinea/database/EntryLayer';
8762
+ /** Read-only Source view backed by exact payload text in an entry database. */
8763
+ export class DatabaseSource implements Source {
8764
+ #private;
8765
+ constructor(database: EntryLayer);
8766
+ getTree(): Promise<ReadonlyTree>;
8767
+ getTreeIfDifferent(sha: string): Promise<ReadonlyTree | undefined>;
8768
+ getBlobs(shas: ReadonlyArray<string>, options?: GetBlobsOptions): AsyncGenerator<[sha: string, blob: Uint8Array<ArrayBufferLike>], any, any>;
8769
+ applyChanges(_batch: ChangesBatch): Promise<void>;
8770
+ }
8771
+
8772
+ }
8773
+
8774
+ declare module 'alinea/database/DatabaseTables' {
8775
+
8776
+ import type { Tree } from 'alinea/core/source/Tree';
8777
+ export const DatabaseStateColumns: {
8778
+ id: import("rado").Column<number, [false, false]>;
8779
+ revision: import("rado").Column<string, [false, boolean]>;
8780
+ /** Merkle tree matching the indexed source revision. */
8781
+ tree: import("rado").JsonColumn<Tree, import("rado").Nullability>;
8782
+ };
8783
+ export const DatabaseStateTable: import("rado").Table<{
8784
+ id: import("rado").Column<number, [false, false]>;
8785
+ revision: import("rado").Column<string, [false, boolean]>;
8786
+ /** Merkle tree matching the indexed source revision. */
8787
+ tree: import("rado").JsonColumn<Tree, import("rado").Nullability>;
8788
+ }, "alinea_database_state">;
8789
+ export const DatabaseMetadataColumns: {
8790
+ id: import("rado").Column<number, [false, false]>;
8791
+ /** The database version whose layout the tables have. */
8792
+ version: import("rado").Column<number, [false, boolean]>;
8793
+ configFingerprint: import("rado").Column<string, [false, boolean]>;
8794
+ };
8795
+ export const DatabaseMetadataTable: import("rado").Table<{
8796
+ id: import("rado").Column<number, [false, false]>;
8797
+ /** The database version whose layout the tables have. */
8798
+ version: import("rado").Column<number, [false, boolean]>;
8799
+ configFingerprint: import("rado").Column<string, [false, boolean]>;
8800
+ }, "alinea_database_metadata">;
8801
+ /** Filesystem metadata of the source files indexed by this database. */
8802
+ export const SourceFileColumns: {
8803
+ path: import("rado").Column<string, [false, false]>;
8804
+ /** Last modification time in milliseconds. */
8805
+ mtime: import("rado").Column<number, [false, boolean]>;
8806
+ size: import("rado").Column<number, [false, boolean]>;
8807
+ };
8808
+ export const SourceFileTable: import("rado").Table<{
8809
+ path: import("rado").Column<string, [false, false]>;
8810
+ /** Last modification time in milliseconds. */
8811
+ mtime: import("rado").Column<number, [false, boolean]>;
8812
+ size: import("rado").Column<number, [false, boolean]>;
8813
+ }, "alinea_source_file">;
8814
+
8815
+ }
8816
+
8817
+ declare module 'alinea/database/driver/RuntimeDatabase' {
8818
+
8819
+ import type { Database, DatabaseOptions } from 'rado';
8820
+ export interface RuntimeDatabaseOptions extends DatabaseOptions {
8821
+ path?: string;
8822
+ data?: Uint8Array;
8823
+ readonly?: boolean;
8824
+ }
8825
+ /** Select a native Node/Bun driver for files and WASM for byte buffers. */
8826
+ export function runtimeDatabase(options?: RuntimeDatabaseOptions): Promise<Database>;
8827
+
8828
+ }
8829
+
8830
+ declare module 'alinea/database/driver/WasmDatabase' {
8831
+
8832
+ import { connect } from 'rado/driver/sql.js';
8833
+ export interface WasmDatabaseHandle {
8834
+ database: ReturnType<typeof connect>;
8835
+ export(): Uint8Array;
8836
+ }
8837
+ /** Open an in-memory WASM connection, optionally from a complete SQLite file. */
8838
+ export function openWasmDatabase(data?: Uint8Array): Promise<WasmDatabaseHandle>;
8839
+ export function wasmDatabase(data?: Uint8Array): Promise<import("rado").SyncDatabase<"sqlite">>;
8840
+
8841
+ }
8842
+
8843
+ declare module 'alinea/database/entry/EntryData' {
8844
+
8845
+ import { type Database, type Sql } from 'rado';
8846
+ import { type EntryIndexTarget } from 'alinea/database/entry/EntryTable';
8847
+ /**
8848
+ * Whether this SQLite stores entry data as JSONB, which reads paths of large
8849
+ * documents without parsing them. Older versions store JSON text.
8850
+ */
8851
+ export function supportsJsonb(db: Database): Promise<boolean>;
8852
+ /** Whether the entry index holds rows stored as JSONB. */
8853
+ export function hasJsonbRows(db: Database): Promise<boolean>;
8854
+ /** Fail clearly when this SQLite cannot read the JSONB rows of a database. */
8855
+ export function assertReadableData(db: Database): Promise<void>;
8856
+ /** The data as JSON text, whether stored as text or JSONB. */
8857
+ export function entryDataText(entry: Pick<EntryIndexTarget, 'data'>): Sql<string>;
8858
+
8859
+ }
8860
+
8861
+ declare module 'alinea/database/entry/EntryTable' {
8862
+
8863
+ import type { Entry, EntryStatus } from 'alinea/core/Entry';
8864
+ import { type Table } from 'rado';
8865
+ /** One complete authored entry version. Only arrays and authored data are JSON. */
8866
+ export const EntryIndexColumns: {
8867
+ /**
8868
+ * Also the rowid of the version's full-text search row. Declared, so VACUUM
8869
+ * and copies into overlay tables keep it.
8870
+ */
8871
+ rowid: import("rado").Column<number, [false, false]>;
8872
+ versionId: import("rado").Column<string, [false, boolean]>;
8873
+ id: import("rado").Column<string, [false, boolean]>;
8874
+ locale: import("rado").Column<string, import("rado").Nullability>;
8875
+ versionStatus: import("rado").Column<EntryStatus, [false, boolean]>;
8876
+ status: import("rado").Column<EntryStatus, [false, boolean]>;
8877
+ type: import("rado").Column<string, [false, boolean]>;
8878
+ title: import("rado").Column<string, [false, boolean]>;
8879
+ workspace: import("rado").Column<string, [false, boolean]>;
8880
+ root: import("rado").Column<string, [false, boolean]>;
8881
+ sourceRoot: import("rado").Column<string, import("rado").Nullability>;
8882
+ parentId: import("rado").Column<string, import("rado").Nullability>;
8883
+ parents: import("rado").Column<string[], [false, boolean]>;
8884
+ level: import("rado").Column<number, [false, boolean]>;
8885
+ index: import("rado").Column<string, [false, boolean]>;
8886
+ path: import("rado").Column<string, [false, boolean]>;
8887
+ /** Full source path of this authored version. */
8888
+ filePath: import("rado").Column<string, [false, boolean]>;
8889
+ fileHash: import("rado").Column<string, [false, boolean]>;
8890
+ parentDir: import("rado").Column<string, [false, boolean]>;
8891
+ childrenDir: import("rado").Column<string, [false, boolean]>;
8892
+ url: import("rado").Column<string, [false, boolean]>;
8893
+ active: import("rado").Column<boolean, [false, boolean]>;
8894
+ main: import("rado").Column<boolean, [false, boolean]>;
8895
+ visible: import("rado").Column<boolean, [false, boolean]>;
8896
+ seeded: import("rado").Column<string, import("rado").Nullability>;
8897
+ rowHash: import("rado").Column<string, [false, boolean]>;
8898
+ /** Hash of this entry's child directory in the synced source tree. */
8899
+ childrenSha: import("rado").Column<string, import("rado").Nullability>;
8900
+ /** Exact source blob for seeded rows whose expanded data differs. */
8901
+ payload: import("rado").Column<string, import("rado").Nullability>;
8902
+ /**
8903
+ * Exact source JSON, or expanded JSON for seeded rows. Stored as JSONB where
8904
+ * SQLite supports it; read it as text with `entryDataText`.
8905
+ */
8906
+ data: import("rado").Column<string, [false, boolean]>;
8907
+ };
8908
+ export function entryIndexTable(name: string, temporary?: boolean): Table<{
8909
+ /**
8910
+ * Also the rowid of the version's full-text search row. Declared, so VACUUM
8911
+ * and copies into overlay tables keep it.
8912
+ */
8913
+ rowid: import("rado").Column<number, [false, false]>;
8914
+ versionId: import("rado").Column<string, [false, boolean]>;
8915
+ id: import("rado").Column<string, [false, boolean]>;
8916
+ locale: import("rado").Column<string, import("rado").Nullability>;
8917
+ versionStatus: import("rado").Column<EntryStatus, [false, boolean]>;
8918
+ status: import("rado").Column<EntryStatus, [false, boolean]>;
8919
+ type: import("rado").Column<string, [false, boolean]>;
8920
+ title: import("rado").Column<string, [false, boolean]>;
8921
+ workspace: import("rado").Column<string, [false, boolean]>;
8922
+ root: import("rado").Column<string, [false, boolean]>;
8923
+ sourceRoot: import("rado").Column<string, import("rado").Nullability>;
8924
+ parentId: import("rado").Column<string, import("rado").Nullability>;
8925
+ parents: import("rado").Column<string[], [false, boolean]>;
8926
+ level: import("rado").Column<number, [false, boolean]>;
8927
+ index: import("rado").Column<string, [false, boolean]>;
8928
+ path: import("rado").Column<string, [false, boolean]>;
8929
+ /** Full source path of this authored version. */
8930
+ filePath: import("rado").Column<string, [false, boolean]>;
8931
+ fileHash: import("rado").Column<string, [false, boolean]>;
8932
+ parentDir: import("rado").Column<string, [false, boolean]>;
8933
+ childrenDir: import("rado").Column<string, [false, boolean]>;
8934
+ url: import("rado").Column<string, [false, boolean]>;
8935
+ active: import("rado").Column<boolean, [false, boolean]>;
8936
+ main: import("rado").Column<boolean, [false, boolean]>;
8937
+ visible: import("rado").Column<boolean, [false, boolean]>;
8938
+ seeded: import("rado").Column<string, import("rado").Nullability>;
8939
+ rowHash: import("rado").Column<string, [false, boolean]>;
8940
+ /** Hash of this entry's child directory in the synced source tree. */
8941
+ childrenSha: import("rado").Column<string, import("rado").Nullability>;
8942
+ /** Exact source blob for seeded rows whose expanded data differs. */
8943
+ payload: import("rado").Column<string, import("rado").Nullability>;
8944
+ /**
8945
+ * Exact source JSON, or expanded JSON for seeded rows. Stored as JSONB where
8946
+ * SQLite supports it; read it as text with `entryDataText`.
8947
+ */
8948
+ data: import("rado").Column<string, [false, boolean]>;
8949
+ }, string>;
8950
+ export type EntryIndexTarget = Table<typeof EntryIndexColumns>;
8951
+ export const EntryIndexTable: Table<{
8952
+ /**
8953
+ * Also the rowid of the version's full-text search row. Declared, so VACUUM
8954
+ * and copies into overlay tables keep it.
8955
+ */
8956
+ rowid: import("rado").Column<number, [false, false]>;
8957
+ versionId: import("rado").Column<string, [false, boolean]>;
8958
+ id: import("rado").Column<string, [false, boolean]>;
8959
+ locale: import("rado").Column<string, import("rado").Nullability>;
8960
+ versionStatus: import("rado").Column<EntryStatus, [false, boolean]>;
8961
+ status: import("rado").Column<EntryStatus, [false, boolean]>;
8962
+ type: import("rado").Column<string, [false, boolean]>;
8963
+ title: import("rado").Column<string, [false, boolean]>;
8964
+ workspace: import("rado").Column<string, [false, boolean]>;
8965
+ root: import("rado").Column<string, [false, boolean]>;
8966
+ sourceRoot: import("rado").Column<string, import("rado").Nullability>;
8967
+ parentId: import("rado").Column<string, import("rado").Nullability>;
8968
+ parents: import("rado").Column<string[], [false, boolean]>;
8969
+ level: import("rado").Column<number, [false, boolean]>;
8970
+ index: import("rado").Column<string, [false, boolean]>;
8971
+ path: import("rado").Column<string, [false, boolean]>;
8972
+ /** Full source path of this authored version. */
8973
+ filePath: import("rado").Column<string, [false, boolean]>;
8974
+ fileHash: import("rado").Column<string, [false, boolean]>;
8975
+ parentDir: import("rado").Column<string, [false, boolean]>;
8976
+ childrenDir: import("rado").Column<string, [false, boolean]>;
8977
+ url: import("rado").Column<string, [false, boolean]>;
8978
+ active: import("rado").Column<boolean, [false, boolean]>;
8979
+ main: import("rado").Column<boolean, [false, boolean]>;
8980
+ visible: import("rado").Column<boolean, [false, boolean]>;
8981
+ seeded: import("rado").Column<string, import("rado").Nullability>;
8982
+ rowHash: import("rado").Column<string, [false, boolean]>;
8983
+ /** Hash of this entry's child directory in the synced source tree. */
8984
+ childrenSha: import("rado").Column<string, import("rado").Nullability>;
8985
+ /** Exact source blob for seeded rows whose expanded data differs. */
8986
+ payload: import("rado").Column<string, import("rado").Nullability>;
8987
+ /**
8988
+ * Exact source JSON, or expanded JSON for seeded rows. Stored as JSONB where
8989
+ * SQLite supports it; read it as text with `entryDataText`.
8990
+ */
8991
+ data: import("rado").Column<string, [false, boolean]>;
8992
+ }, string>;
8993
+ /** An Entry plus the physical-version and local-index fields. */
8994
+ export interface IndexedEntry extends Entry {
8995
+ versionStatus: EntryStatus;
8996
+ /** Exact source text matching fileHash. */
8997
+ payload?: string;
8998
+ /** False for authored versions suppressed by inherited status in normal queries. */
8999
+ visible?: boolean;
9000
+ /** First source-directory segment below the content root (not the URL slug). */
9001
+ sourceRoot?: string | null;
9002
+ /** Hash of this entry's child directory in the synced source tree. */
9003
+ childrenSha?: string | null;
9004
+ }
9005
+ export function entryIndexRow(entry: IndexedEntry): {
9006
+ versionId: string;
9007
+ id: string;
9008
+ locale: string | null;
9009
+ versionStatus: EntryStatus;
9010
+ status: EntryStatus;
9011
+ type: string;
9012
+ title: string;
9013
+ workspace: string;
9014
+ root: string;
9015
+ sourceRoot: string | null;
9016
+ parentId: string | null;
9017
+ parents: string[];
9018
+ level: number;
9019
+ index: string;
9020
+ path: string;
9021
+ filePath: string;
9022
+ fileHash: string;
9023
+ parentDir: string;
9024
+ childrenDir: string;
9025
+ url: string;
9026
+ active: boolean;
9027
+ main: boolean;
9028
+ visible: boolean;
9029
+ seeded: string | null;
9030
+ rowHash: string;
9031
+ childrenSha: string | null;
9032
+ payload: string | null;
9033
+ data: string;
9034
+ };
9035
+ export function storedEntryData(value: unknown, path: string): Record<string, unknown>;
9036
+
9037
+ }
9038
+
9039
+ declare module 'alinea/database/entry/EntryView' {
9040
+
9041
+ import { type Database } from 'rado';
9042
+ import type { EntrySyncTarget } from 'alinea/database/sync/EntrySyncer';
9043
+ /** The tables reads of a target go to: an unwritten view reads its parent's. */
9044
+ export function entryReadTarget(target: EntrySyncTarget): EntrySyncTarget;
9045
+ /**
9046
+ * Named, copy-on-write entry and search tables over another target. A view
9047
+ * reads its parent until the first write copies the parent's rows into
9048
+ * temporary tables of its own, which from then on no longer follow the parent.
9049
+ */
9050
+ export class EntryView {
9051
+ #private;
9052
+ readonly target: EntrySyncTarget;
9053
+ private constructor();
9054
+ static create(db: Database, name: string, parent: EntrySyncTarget, revision: string): Promise<EntryView>;
9055
+ get readTarget(): EntrySyncTarget;
9056
+ /**
9057
+ * Copy the parent's rows into this view's tables. Await this before every
9058
+ * write through the view: until then reads go to the parent.
9059
+ */
9060
+ diverge(): Promise<void>;
9061
+ close(): Promise<void>;
9062
+ }
9063
+
9064
+ }
9065
+
9066
+ declare module 'alinea/database/EntryDatabase' {
9067
+
9068
+ import type { Config } from 'alinea/core/Config';
9069
+ import type { FileStat } from 'alinea/core/source/FSSource';
9070
+ import { type Database } from 'rado';
9071
+ import { EntryLayer, type EntryDatabaseOptions, type EntrySyncResult } from 'alinea/database/EntryLayer';
9072
+ /** A queryable entry database owning its SQLite connection. */
9073
+ export class EntryDatabase extends EntryLayer {
9074
+ #private;
9075
+ constructor(config: Config, db: Database, options?: EntryDatabaseOptions);
9076
+ static createSchema(db: Database, revision: string, configFingerprint?: string): Promise<void>;
9077
+ /**
9078
+ * Derive every entry again with another config, recording the fingerprint
9079
+ * so a later open recognizes the database as written by that config.
9080
+ */
9081
+ reindex(config: Config, configFingerprint?: string): Promise<EntrySyncResult>;
9082
+ /** File stats recorded by the last filesystem sync of this database. */
9083
+ getSourceFileStats(): Promise<Map<string, FileStat>>;
9084
+ /** Replace the recorded file stats after syncing from a filesystem source. */
9085
+ setSourceFileStats(stats: ReadonlyMap<string, FileStat>): Promise<void>;
9086
+ compact(): Promise<void>;
9087
+ protected releaseLayer(): Promise<void>;
9088
+ }
9089
+
9090
+ }
9091
+
9092
+ declare module 'alinea/database/EntryLayer' {
9093
+
9094
+ import type { Config } from 'alinea/core/Config';
9095
+ import type { CommitRequest } from 'alinea/core/db/CommitRequest';
9096
+ import type { EntryReferenceQuery, EntryReferenceResult } from 'alinea/core/db/EntryReference';
9097
+ import type { Mutation } from 'alinea/core/db/Mutation';
9098
+ import type { SyncOptions } from 'alinea/core/db/LocalStore';
9099
+ import { Graph, type AnyQueryResult, type GraphQuery } from 'alinea/core/Graph';
9100
+ import { Policy } from 'alinea/core/Role';
9101
+ import { OverlaySource } from 'alinea/core/source/OverlaySource';
9102
+ import { type GetBlobsOptions, type RemoteSource, type Source } from 'alinea/core/source/Source';
9103
+ import { ReadonlyTree } from 'alinea/core/source/Tree';
9104
+ import { TaskQueue } from 'alinea/core/util/Async';
9105
+ import { type Database } from 'rado';
9106
+ import { EntryView } from 'alinea/database/entry/EntryView';
9107
+ import { EntrySyncer, type EntrySyncTarget } from 'alinea/database/sync/EntrySyncer';
9108
+ export interface EntryLayerState {
9109
+ /** Read connection, or the write transaction of a working layer. */
9110
+ db: Database;
9111
+ /** Serializes every task on the connection, shared by its overlays. */
9112
+ queue: TaskQueue;
9113
+ syncer: EntrySyncer;
9114
+ target: EntrySyncTarget;
9115
+ /** The copy-on-write table behind an overlay's target. */
9116
+ view?: EntryView;
9117
+ /** The layer an overlay was created from. */
9118
+ parent?: EntryLayer;
9119
+ tree?: ReadonlyTree;
9120
+ initialTree?: ReadonlyTree;
9121
+ /** The connection is already inside a transaction: never open nested ones. */
9122
+ transactional?: boolean;
9123
+ }
9124
+ export interface EntryDatabaseOptions {
9125
+ includedAtBuild?(filePath: string): boolean | Promise<boolean>;
9126
+ }
9127
+ export interface EntryApplyOptions {
9128
+ source: Source;
9129
+ policy?: Policy;
9130
+ }
9131
+ export type EntryChangeListener = (change: EntrySyncResult) => void;
9132
+ export interface EntrySyncResult {
9133
+ revision: string;
9134
+ /** Includes source changes, deletions, and entries changed by inheritance. */
9135
+ changedEntryIds: ReadonlyArray<string>;
9136
+ }
9137
+ export interface EntryApplyResult extends EntrySyncResult {
9138
+ request: CommitRequest;
9139
+ }
9140
+ export interface EntryDatabaseOverlay extends AsyncDisposable {
9141
+ database: EntryLayer;
9142
+ source: OverlaySource;
9143
+ close(): Promise<void>;
9144
+ }
9145
+ /** One queryable layer of the entry index: a database or a view over one. */
9146
+ export class EntryLayer extends Graph implements AsyncDisposable {
9147
+ #private;
9148
+ constructor(config: Config, options: EntryDatabaseOptions, state: EntryLayerState);
9149
+ get config(): Config;
9150
+ includedAtBuild(filePath: string): boolean | Promise<boolean>;
9151
+ /** Whether this layer was closed, and answers only from a replacement. */
9152
+ get closed(): boolean;
9153
+ /**
9154
+ * Synchronize a source through this layer's single prepared syncer, without
9155
+ * keeping an in-memory copy of its entries.
9156
+ */
9157
+ syncWith(source: RemoteSource, options?: SyncOptions): Promise<EntrySyncResult>;
9158
+ /**
9159
+ * Adopt another config and derive every entry again from the payloads this
9160
+ * layer stores. The rows of the previous config are replaced within one
9161
+ * transaction, so reads keep answering from them until the replacement is
9162
+ * committed. Only a layer without overlays can be reindexed.
9163
+ */
9164
+ protected reindexEntries(config: Config, reset?: (tx: Database) => Promise<void>): Promise<EntrySyncResult>;
9165
+ /** Atomically plan and apply mutations through a private working layer. */
9166
+ apply(mutations: ReadonlyArray<Mutation>, options: EntryApplyOptions): Promise<EntryApplyResult>;
9167
+ /** Plan mutations like apply, then roll them back. */
9168
+ plan(mutations: ReadonlyArray<Mutation>, options: EntryApplyOptions): Promise<CommitRequest>;
9169
+ close(): Promise<void>;
9170
+ /** Release what this layer owns, serialized with the read connection. */
9171
+ protected releaseLayer(): Promise<void>;
9172
+ [Symbol.asyncDispose](): Promise<void>;
9173
+ getRevision(): Promise<string>;
9174
+ getTree(): Promise<ReadonlyTree>;
9175
+ getBlobs(shas: ReadonlyArray<string>, options?: GetBlobsOptions): AsyncGenerator<[sha: string, blob: Uint8Array]>;
9176
+ /** Run a task serialized with every other read on this connection. */
9177
+ protected withReadConnection<T>(run: () => Promise<T>): Promise<T>;
9178
+ /** Create and synchronize a copy-on-write layer over this one. */
9179
+ overlay(source: RemoteSource): Promise<EntryLayer>;
9180
+ /** Create an initially empty writable source and database layer. */
9181
+ createOverlay(): Promise<EntryDatabaseOverlay>;
9182
+ resolve<const Query extends GraphQuery>(query: Query): Promise<AnyQueryResult<Query>>;
9183
+ /** Scan references in bounded pages without retaining an entry index. */
9184
+ referencesTo(query: EntryReferenceQuery): Promise<EntryReferenceResult>;
9185
+ onChange(listener: EntryChangeListener): () => void;
9186
+ }
9187
+
9188
+ }
9189
+
9190
+ declare module 'alinea/database/EntryStore' {
9191
+
9192
+ import type { Config } from 'alinea/core/Config';
9193
+ import type { GetBlobsOptions, RemoteSource, Source } from 'alinea/core/source/Source';
9194
+ import { ReadonlyTree } from 'alinea/core/source/Tree';
9195
+ import type { AnyQueryResult, GraphQuery } from 'alinea/core/Graph';
9196
+ import type { Mutation } from 'alinea/core/db/Mutation';
9197
+ import type { Policy } from 'alinea/core/Role';
9198
+ import type { EntryReferenceQuery, EntryReferenceResult } from 'alinea/core/db/EntryReference';
9199
+ import { type CommitRequest } from 'alinea/core/db/CommitRequest';
9200
+ import type { LocalStore, SyncOptions } from 'alinea/core/db/LocalStore';
9201
+ import { WriteableGraph } from 'alinea/core/db/WriteableGraph';
9202
+ import type { UploadMetadata, UploadResponse } from 'alinea/core/Connection';
9203
+ import type { EntryChangeListener, EntryLayer } from 'alinea/database/EntryLayer';
9204
+ export interface EntryStoreOptions {
9205
+ ownsDatabase?: boolean;
9206
+ sourceFollowsDatabase?: boolean;
9207
+ close?: () => Promise<void>;
9208
+ }
9209
+ /** Source and commit lifecycle around the transport-neutral SQLite database. */
9210
+ export class EntryStore extends WriteableGraph implements LocalStore, AsyncDisposable {
9211
+ #private;
9212
+ /** Replaced only by a reindex, which derives every entry again. */
9213
+ config: Config;
9214
+ readonly database: EntryLayer;
9215
+ readonly source: Source;
9216
+ constructor(config: Config, database: EntryLayer, source: Source, options?: EntryStoreOptions);
9217
+ static memory(config: Config, source: Source): Promise<EntryStore>;
9218
+ get sha(): Promise<string>;
9219
+ includedAtBuild(filePath: string): boolean | Promise<boolean>;
9220
+ /**
9221
+ * Preview queries share a single overlay, which each query first brings to
9222
+ * its own payload over the current store revision. The overlay diffs from
9223
+ * the tree it shows, so a switch restores the previously previewed entry and
9224
+ * follows store syncs without copying the entries again.
9225
+ */
9226
+ resolve<Query extends GraphQuery>(query: Query): Promise<AnyQueryResult<Query>>;
9227
+ /** Close the preview overlay, which keeps this store's database open. */
9228
+ protected closePreviews(): Promise<void>;
9229
+ referencesTo(query: EntryReferenceQuery): Promise<EntryReferenceResult>;
9230
+ onChange(listener: EntryChangeListener): () => void;
9231
+ sync(): Promise<string>;
9232
+ /** Keep the writable source and its query database at one remote revision. */
9233
+ syncWith(remote: RemoteSource, options?: SyncOptions): Promise<string>;
9234
+ /** Create a persistent copy-on-write session over this store. */
9235
+ overlay(remote: RemoteSource): Promise<EntryStore>;
9236
+ /** Plan a commit without changing this store. */
9237
+ request(mutations: ReadonlyArray<Mutation>, policy?: Policy): Promise<CommitRequest>;
9238
+ mutate(mutations: Array<Mutation>): Promise<{
9239
+ sha: string;
9240
+ }>;
9241
+ write(request: CommitRequest): Promise<{
9242
+ sha: string;
9243
+ }>;
9244
+ getTreeIfDifferent(sha: string): Promise<ReadonlyTree | undefined>;
9245
+ getBlobs(shas: ReadonlyArray<string>, options?: GetBlobsOptions): AsyncIterable<[sha: string, blob: Uint8Array<ArrayBufferLike>]>;
9246
+ prepareUpload(_file: string, _metadata?: UploadMetadata): Promise<UploadResponse>;
9247
+ /** Whether the underlying database was closed. */
9248
+ get closed(): boolean;
9249
+ close(): Promise<void>;
9250
+ [Symbol.asyncDispose](): Promise<void>;
9251
+ }
9252
+
9253
+ }
9254
+
9255
+ declare module 'alinea/database/EntryTransaction' {
9256
+
9257
+ import { type Policy } from 'alinea/core/Role';
9258
+ import { OverlaySource } from 'alinea/core/source/OverlaySource';
9259
+ import { SourceTransaction } from 'alinea/core/source/Source';
9260
+ import type { ReadonlyTree } from 'alinea/core/source/Tree';
9261
+ import { type CommitRequest } from 'alinea/core/db/CommitRequest';
9262
+ import type { ArchiveMutation, CreateMutation, MoveMutation, Mutation, PublishMutation, RemoveFileMutation, RemoveMutation, UnpublishMutation, UpdateMutation, UploadFileMutation } from 'alinea/core/db/Mutation';
9263
+ import type { EntryLayer } from 'alinea/database/EntryLayer';
9264
+ type Op<T> = Omit<T, 'op'>;
9265
+ /**
9266
+ * Plans mutations inside the receiver's write transaction. Each mutation is
9267
+ * flushed so subsequent mutations query its result without retaining an
9268
+ * in-memory entry index. SQLite rolls the full batch back on failure.
9269
+ */
9270
+ export class EntryTransaction implements AsyncDisposable {
9271
+ #private;
9272
+ /** @internal Constructed by EntryLayer.apply. */
9273
+ constructor(workingDatabase: EntryLayer, workingSource: OverlaySource, sourceTransaction: SourceTransaction, from: ReadonlyTree, policy: Policy);
9274
+ get changedEntryIds(): Array<string>;
9275
+ apply(mutations: ReadonlyArray<Mutation>): Promise<void>;
9276
+ create({ locale, type, data, root, workspace, fromSeed, parentId, id, insertOrder, status, overwrite }: Op<CreateMutation>): Promise<void>;
9277
+ update({ id, locale, status, set }: Op<UpdateMutation>): Promise<void>;
9278
+ publish({ id, locale, status }: Op<PublishMutation>): Promise<void>;
9279
+ unpublish({ id, locale }: Op<UnpublishMutation>): Promise<void>;
9280
+ archive({ id, locale }: Op<ArchiveMutation>): Promise<void>;
9281
+ move({ id, target, dropPosition, targetType }: Op<MoveMutation>): Promise<void>;
9282
+ remove({ id, locale, status }: Op<RemoveMutation>): Promise<void>;
9283
+ removeFile(mutation: Op<RemoveFileMutation>): void;
9284
+ uploadFile(mutation: Op<UploadFileMutation>): void;
9285
+ toRequest(): Promise<CommitRequest>;
9286
+ close(): Promise<void>;
9287
+ [Symbol.asyncDispose](): Promise<void>;
9288
+ }
9289
+ export {};
9290
+
9291
+ }
9292
+
9293
+ declare module 'alinea/database/EntryUrlAliases' {
9294
+
9295
+ import { Type } from 'alinea/core/Type';
9296
+ export function dataWithUrlAlias(type: Type, data: Record<string, unknown>, previousUrl: string, currentUrl: string): Record<string, unknown>;
9297
+
9298
+ }
9299
+
9300
+ declare module 'alinea/database/LocalDB' {
9301
+
9302
+ import type { AnyQueryResult, GraphQuery } from 'alinea/core/Graph';
9303
+ import type { GetBlobsOptions, RemoteSource, Source } from 'alinea/core/source/Source';
9304
+ import type { Config } from 'alinea/core/Config';
9305
+ import type { UploadMetadata, UploadResponse } from 'alinea/core/Connection';
9306
+ import type { CommitRequest } from 'alinea/core/db/CommitRequest';
9307
+ import type { EntryReferenceQuery, EntryReferenceResult } from 'alinea/core/db/EntryReference';
9308
+ import type { LocalStore, SyncOptions } from 'alinea/core/db/LocalStore';
9309
+ import type { Mutation } from 'alinea/core/db/Mutation';
9310
+ import { WriteableGraph } from 'alinea/core/db/WriteableGraph';
9311
+ import type { Policy } from 'alinea/core/Role';
9312
+ /**
9313
+ * Sync-constructor convenience over `EntryStore.memory()`.
9314
+ * This facade is backed by the SQLite engine.
9315
+ */
9316
+ export class LocalDB extends WriteableGraph implements LocalStore, AsyncDisposable {
9317
+ #private;
9318
+ readonly config: Config;
9319
+ readonly source: Source;
9320
+ readonly events: EventTarget;
9321
+ constructor(config: Config, source?: Source);
9322
+ get sha(): Promise<string>;
9323
+ includedAtBuild(filePath: string): Promise<boolean>;
9324
+ resolve<Query extends GraphQuery>(query: Query): Promise<AnyQueryResult<Query>>;
9325
+ referencesTo(query: EntryReferenceQuery): Promise<EntryReferenceResult>;
9326
+ getTreeIfDifferent(sha: string): Promise<import("alinea/core/source/Tree").ReadonlyTree | undefined>;
9327
+ getBlobs(shas: ReadonlyArray<string>, options?: GetBlobsOptions): AsyncIterable<[sha: string, blob: Uint8Array<ArrayBufferLike>]>;
9328
+ sync(): Promise<string>;
9329
+ syncWith(remote: RemoteSource, options?: SyncOptions): Promise<string>;
9330
+ request(mutations: ReadonlyArray<Mutation>, policy?: Policy): Promise<CommitRequest>;
9331
+ mutate(mutations: Array<Mutation>): Promise<{
9332
+ sha: string;
9333
+ }>;
9334
+ write(request: CommitRequest): Promise<{
9335
+ sha: string;
9336
+ }>;
9337
+ prepareUpload(_file: string, _metadata?: UploadMetadata): Promise<UploadResponse>;
9338
+ close(): Promise<void>;
9339
+ [Symbol.asyncDispose](): Promise<void>;
9340
+ }
9341
+
9342
+ }
9343
+
9344
+ declare module 'alinea/database/query/Condition' {
9345
+
9346
+ import { type HasSql, type Sql } from 'rado';
9347
+ type FieldSql = (name: string) => HasSql;
9348
+ export function jsonField(target: HasSql, segments: Array<string>): HasSql<unknown>;
9349
+ /** Compile a condition; `depth` names the array items it iterates. */
9350
+ export function compileCondition(field: HasSql, condition: unknown, depth?: number): Sql<boolean>;
9351
+ export function compileFilter(filter: unknown, field: FieldSql): Sql<boolean>;
9352
+ export function arrayIncludes(field: HasSql, predicate: (item: HasSql) => Sql<boolean>, depth?: number): Sql<boolean>;
9353
+ export {};
9354
+
9355
+ }
9356
+
9357
+ declare module 'alinea/database/query/EntryQuery' {
9358
+
9359
+ import type { Config } from 'alinea/core/Config';
9360
+ import type { Field } from 'alinea/core/Field';
9361
+ import type { GraphQuery, Status } from 'alinea/core/Graph';
9362
+ import { type Scope } from 'alinea/core/Scope';
9363
+ import { type HasSql, type SelectionInput, type Sql } from 'rado';
9364
+ import { type EntryIndexTarget } from 'alinea/database/entry/EntryTable';
9365
+ import { searchQuery, type EntrySearchTarget } from 'alinea/database/query/Search';
9366
+ interface RelationProjection {
9367
+ path: Array<string>;
9368
+ plan: ProjectionPlan;
9369
+ }
9370
+ interface FieldProjection {
9371
+ path: Array<string>;
9372
+ field: Field;
9373
+ }
9374
+ export interface ProjectionPlan {
9375
+ count: boolean;
9376
+ single: boolean;
9377
+ /** Rows carry the selection as `value`, next to the columns they need. */
9378
+ wrapped: boolean;
9379
+ relations: Array<RelationProjection>;
9380
+ fields: Array<FieldProjection>;
9381
+ }
9382
+ /** Rows visible under a query status; the default addresses published rows. */
9383
+ export function statusCondition(entry: EntryIndexTarget, status?: Status): Sql<boolean>;
9384
+ /**
9385
+ * Rows in one locale; null is unlocalized. Stored locales use the configured
9386
+ * spelling, so a requested locale matches any case of it.
9387
+ */
9388
+ export function localeCondition(scope: Scope, entry: EntryIndexTarget, locale: string | null | HasSql<string | null>): Sql<boolean>;
9389
+ interface EntryQueryOptions {
9390
+ source?: EntryIndexTarget;
9391
+ search?: ReturnType<typeof searchQuery>;
9392
+ entry?: EntryIndexTarget;
9393
+ depth?: number;
9394
+ baseEntry?: EntryIndexTarget;
9395
+ searchTable?: EntrySearchTarget;
9396
+ }
9397
+ export function compileEntryQuery(config: Config, query: GraphQuery, options?: EntryQueryOptions): {
9398
+ rows: import("rado").Select<SelectionInput, import("rado/core/MetaData").QueryMeta>;
9399
+ plan: ProjectionPlan;
9400
+ };
9401
+ export {};
9402
+
9403
+ }
9404
+
9405
+ declare module 'alinea/database/query/EntryReferences' {
9406
+
9407
+ import type { Config } from 'alinea/core/Config';
9408
+ import type { EntryReferenceQuery, EntryReferenceResult } from 'alinea/core/db/EntryReference';
9409
+ import { type Database } from 'rado';
9410
+ import { type EntryIndexTarget } from 'alinea/database/entry/EntryTable';
9411
+ /** Scan references in bounded pages on the caller's active transaction. */
9412
+ export function queryEntryReferences(config: Config, db: Database, entry: EntryIndexTarget, query: EntryReferenceQuery): Promise<EntryReferenceResult>;
9413
+
9414
+ }
9415
+
9416
+ declare module 'alinea/database/query/Relation' {
9417
+
9418
+ import type { EdgeQuery } from 'alinea/core/Graph';
9419
+ import { type Sql } from 'rado';
9420
+ import type { EntryIndexTarget } from 'alinea/database/entry/EntryTable';
9421
+ /** Expand the stored references as SQL rows, retaining list order and duplicates. */
9422
+ export function linkRelation(source: EntryIndexTarget, field: string, multiple: boolean): {
9423
+ target: Sql<unknown>;
9424
+ id: Sql<string>;
9425
+ ordinal: Sql<number>;
9426
+ };
9427
+ /** Restrict related identities in SQL before the query's own filters/paging. */
9428
+ export function relationCondition(entry: EntryIndexTarget, query: EdgeQuery, source: EntryIndexTarget): Sql<boolean>;
9429
+
9430
+ }
9431
+
9432
+ declare module 'alinea/database/query/ResolveQuery' {
9433
+
9434
+ import type { Config } from 'alinea/core/Config';
9435
+ import type { GraphQuery } from 'alinea/core/Graph';
9436
+ import { type Database } from 'rado';
9437
+ import type { EntryIndexTarget } from 'alinea/database/entry/EntryTable';
9438
+ import type { EntrySearchTarget, SearchQuery } from 'alinea/database/query/Search';
9439
+ /** Query dependencies bound to one connection and its current transaction. */
9440
+ export interface EntryQueryContext {
9441
+ config: Config;
9442
+ database: Database;
9443
+ entries: EntryIndexTarget;
9444
+ searchTable: EntrySearchTarget;
9445
+ search(input: GraphQuery['search']): Promise<SearchQuery | undefined>;
9446
+ includedAtBuild(filePath: string): boolean | Promise<boolean>;
9447
+ }
9448
+ export function resolveEntryQuery(query: GraphQuery, context: EntryQueryContext): Promise<unknown>;
9449
+
9450
+ }
9451
+
9452
+ declare module 'alinea/database/query/Search' {
9453
+
9454
+ import { type Database, type HasSql, type Sql, type Table } from 'rado';
9455
+ import type { EntryIndexTarget } from 'alinea/database/entry/EntryTable';
9456
+ /**
9457
+ * The FTS5 index of an entry table. Sync writes a row for every entry version
9458
+ * under the rowid of its entry row; the body is the version's searchable text.
9459
+ */
9460
+ const EntrySearchColumns: {
9461
+ rowid: import("rado").Column<number, [false, false]>;
9462
+ title: import("rado").Column<string, [false, boolean]>;
9463
+ body: import("rado").Column<string, [false, boolean]>;
9464
+ };
9465
+ export type EntrySearchTarget = Table<typeof EntrySearchColumns>;
9466
+ export function entrySearchTable(name: string, temporary?: boolean): EntrySearchTarget;
9467
+ export const EntrySearchTable: EntrySearchTarget;
9468
+ export interface SearchQuery {
9469
+ target: Sql;
9470
+ identity: Sql<boolean>;
9471
+ condition: Sql<boolean>;
9472
+ rank: Sql<number>;
9473
+ snippet(start: HasSql, end: HasSql, cutOff: HasSql, limit: HasSql): Sql<string>;
9474
+ }
9475
+ export function createSearch(db: Database, search: EntrySearchTarget): Promise<void>;
9476
+ /** The searchable text of an entry row, read from its search row. */
9477
+ export function searchableText(entry: EntryIndexTarget, search: EntrySearchTarget): Sql<string>;
9478
+ export function searchTokens(input: string | Array<string> | undefined): RegExpMatchArray | [] | undefined;
9479
+ export interface SearchQueryOptions {
9480
+ /** Indexed spellings a token may also match, besides its prefix. */
9481
+ alternatives?(token: string): ReadonlyArray<string>;
9482
+ }
9483
+ export function searchQuery(input: string | Array<string> | undefined, entry: EntryIndexTarget, target: EntrySearchTarget, options?: SearchQueryOptions): SearchQuery | undefined;
9484
+ /** The edits a token may be away from an indexed term: none below five
9485
+ * characters, one up to fourteen, then one more per ten. */
9486
+ export function fuzzyDistance(token: string): number;
9487
+ export function dropVocabulary(db: Database, search: EntrySearchTarget): Promise<void>;
9488
+ /**
9489
+ * The terms of one FTS5 table, loaded once per database revision. Query tokens
9490
+ * expand to indexed terms within a small edit distance, so a typo still
9491
+ * finds the entry.
9492
+ */
9493
+ export class SearchVocabulary {
9494
+ #private;
9495
+ load(db: Database, search: EntrySearchTarget, revision: string): Promise<void>;
9496
+ /** Indexed terms close to a token, most frequent first; prefixes of the
9497
+ * token are left to the prefix match. */
9498
+ alternatives(token: string): Array<string>;
9499
+ }
9500
+ export {};
9501
+
9502
+ }
9503
+
9504
+ declare module 'alinea/database/sync/Derive' {
9505
+
9506
+ import type { Config } from 'alinea/core/Config';
9507
+ import { type SyncChanges } from 'alinea/database/sync/Ingest';
9508
+ import type { SyncQueries } from 'alinea/database/sync/SyncQueries';
9509
+ /**
9510
+ * Derive parents, statuses and urls for what a merge changed, and for what
9511
+ * inherits from it. Resolves to the sorted ids of every entry that changed.
9512
+ */
9513
+ export function deriveEntries(config: Config, queries: SyncQueries, changes: SyncChanges, validate: boolean): Promise<Array<string>>;
9514
+
9515
+ }
9516
+
9517
+ declare module 'alinea/database/sync/EntryParser' {
9518
+
9519
+ import type { Config } from 'alinea/core/Config';
9520
+ import type { IndexedEntry } from 'alinea/database/entry/EntryTable';
9521
+ /** Parse one source blob into the entry fields that do not depend on its tree. */
9522
+ export function parseSourceEntry(config: Config, filePath: string, fileHash: string, blob: Uint8Array): IndexedEntry;
9523
+
9524
+ }
9525
+
9526
+ declare module 'alinea/database/sync/EntrySyncer' {
9527
+
9528
+ import type { Config } from 'alinea/core/Config';
9529
+ import type { RemoteSource } from 'alinea/core/source/Source';
9530
+ import { ReadonlyTree } from 'alinea/core/source/Tree';
9531
+ import type { Database, Table } from 'rado';
9532
+ import { type DatabaseStateColumns } from 'alinea/database/DatabaseTables';
9533
+ import { type EntryIndexTarget } from 'alinea/database/entry/EntryTable';
9534
+ import { type EntrySearchTarget } from 'alinea/database/query/Search';
9535
+ export interface EntrySyncTarget {
9536
+ entries: EntryIndexTarget;
9537
+ state: Table<typeof DatabaseStateColumns>;
9538
+ /** The full-text index of the entries, written along with them. */
9539
+ search: EntrySearchTarget;
9540
+ /**
9541
+ * Whether the state records the synced source tree, so the database can be
9542
+ * reopened and synced from it. A temporary overlay keeps its tree in memory.
9543
+ */
9544
+ recordsTree?: boolean;
9545
+ }
9546
+ export const EntrySyncRoot: EntrySyncTarget;
9547
+ export interface EntrySyncOptions {
9548
+ previousTree?: ReadonlyTree;
9549
+ withinTransaction?: boolean;
9550
+ /** Skip entry validation for a source whose entries were already validated. */
9551
+ validate?: boolean;
9552
+ }
9553
+ /** Prepared, serialized source synchronization for one database connection. */
9554
+ export class EntrySyncer {
9555
+ #private;
9556
+ constructor(config: Config, db: Database);
9557
+ /** Derive later syncs with another config; prepared statements are shared. */
9558
+ reconfigure(config: Config): Promise<void>;
9559
+ /** Stream a source/tree diff directly into the canonical SQLite table. */
9560
+ sync(target: EntrySyncTarget, source: RemoteSource, tree: ReadonlyTree, fromRevision: string, options?: EntrySyncOptions): Promise<Array<string>>;
9561
+ /** Release prepared statements belonging to a closed named target. */
9562
+ release(target: EntrySyncTarget): Promise<void>;
9563
+ close(): Promise<void>;
9564
+ }
9565
+
9566
+ }
9567
+
9568
+ declare module 'alinea/database/sync/Ingest' {
9569
+
9570
+ import type { Config } from 'alinea/core/Config';
9571
+ import type { RemoteSource } from 'alinea/core/source/Source';
9572
+ import { ReadonlyTree } from 'alinea/core/source/Tree';
9573
+ import { type SyncQueries } from 'alinea/database/sync/SyncQueries';
9574
+ /** What merging a source tree into the entry table changed. */
9575
+ export interface SyncChanges {
9576
+ /** Entries with a version that was added, replaced or removed. */
9577
+ touched: Set<string>;
9578
+ /** Versions written from their source file, with the url parsed from it. */
9579
+ inserted: Set<string>;
9580
+ /** Entries with a touched version that has children: those inherit from it. */
9581
+ parents: Set<string>;
9582
+ /** Child directories of removed or replaced versions that had children. */
9583
+ childDirs: Set<string>;
9584
+ /** Entries whose child directory changed in the source tree. */
9585
+ containers: Set<string>;
9586
+ }
9587
+ /** Whether a directory hash stands for a directory with files. */
9588
+ export function hasChildren(childrenSha: string | null): boolean;
9589
+ /** Write the files that differ between two source trees to the entry table. */
9590
+ export function mergeTrees(config: Config, source: RemoteSource, previousTree: ReadonlyTree, tree: ReadonlyTree, queries: SyncQueries): Promise<SyncChanges>;
9591
+
9592
+ }
9593
+
9594
+ declare module 'alinea/database/sync/SyncQueries' {
9595
+
9596
+ import type { EntryStatus } from 'alinea/core/Entry';
9597
+ import type { Tree } from 'alinea/core/source/Tree';
9598
+ import { type Database } from 'rado';
9599
+ import { type entryIndexRow } from 'alinea/database/entry/EntryTable';
9600
+ import type { EntrySyncTarget } from 'alinea/database/sync/EntrySyncer';
9601
+ export const sqliteBatchSize = 5000;
9602
+ /**
9603
+ * Named parameters bypass column encoders, so bind SQLite values explicitly for
9604
+ * the placeholders of {@link insertEntryQuery}.
9605
+ */
9606
+ export function insertEntryValues(row: ReturnType<typeof entryIndexRow>): {
9607
+ versionId: string;
9608
+ id: string;
9609
+ locale: string | null;
9610
+ versionStatus: EntryStatus;
9611
+ status: EntryStatus;
9612
+ type: string;
9613
+ title: string;
9614
+ workspace: string;
9615
+ root: string;
9616
+ sourceRoot: string | null;
9617
+ parentId: string | null;
9618
+ level: number;
9619
+ index: string;
9620
+ path: string;
9621
+ filePath: string;
9622
+ fileHash: string;
9623
+ parentDir: string;
9624
+ childrenDir: string;
9625
+ url: string;
9626
+ seeded: string | null;
9627
+ rowHash: string;
9628
+ childrenSha: string | null;
9629
+ payload: string | null;
9630
+ data: string;
9631
+ parents: string;
9632
+ active: number;
9633
+ main: number;
9634
+ visible: number;
9635
+ };
9636
+ export function prepareSyncQueries(db: Database, target: EntrySyncTarget,
9637
+ /** Store entry data as JSONB, on a SQLite that reads it. */
9638
+ jsonb: boolean): {
9639
+ revision: import("rado").PreparedQuery<{
9640
+ revision: string;
9641
+ }, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9642
+ tree: import("rado").PreparedQuery<{
9643
+ tree: Tree;
9644
+ }, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9645
+ setRevision: import("rado").PreparedQuery<import("rado/core/MetaData").MutationResultBase, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9646
+ entryCount: import("rado").PreparedQuery<{
9647
+ value: number;
9648
+ }, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9649
+ insertEntry: import("rado").PreparedQuery<import("rado/core/MetaData").MutationResultBase, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9650
+ /** Index the entry row inserted last, under its rowid. */
9651
+ insertSearch: import("rado").PreparedQuery<import("rado/core/MetaData").MutationResultBase, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9652
+ /** Versions stored at these file paths or under these version ids. */
9653
+ storedFiles: import("rado").PreparedQuery<{
9654
+ id: string;
9655
+ filePath: string;
9656
+ childrenDir: string;
9657
+ childrenSha: string | null;
9658
+ }[], Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9659
+ /** Delete the versions stored at these file paths or version ids. */
9660
+ deleteFiles: import("rado").PreparedQuery<import("rado/core/MetaData").MutationResultBase, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9661
+ /** Delete the search rows of those versions, before the versions. */
9662
+ deleteSearch: import("rado").PreparedQuery<import("rado/core/MetaData").MutationResultBase, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9663
+ directories: import("rado").PreparedQuery<{
9664
+ id: string;
9665
+ childrenDir: string;
9666
+ childrenSha: string | null;
9667
+ }[], Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9668
+ updateChildrenSha: import("rado").PreparedQuery<import("rado/core/MetaData").MutationResultBase, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9669
+ /** Every version of these entries. */
9670
+ versionsOf: import("rado").PreparedQuery<{
9671
+ versionId: string;
9672
+ id: string;
9673
+ locale: string | null;
9674
+ versionStatus: EntryStatus;
9675
+ type: string;
9676
+ index: string;
9677
+ workspace: string;
9678
+ root: string;
9679
+ path: string;
9680
+ filePath: string;
9681
+ level: number;
9682
+ parentDir: string;
9683
+ childrenDir: string;
9684
+ childrenSha: string | null;
9685
+ parentId: string | null;
9686
+ parents: string[];
9687
+ status: EntryStatus;
9688
+ active: boolean;
9689
+ main: boolean;
9690
+ visible: boolean;
9691
+ url: string;
9692
+ }[], Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9693
+ /** Every version stored below one directory. */
9694
+ versionsBelow: import("rado").PreparedQuery<{
9695
+ versionId: string;
9696
+ id: string;
9697
+ locale: string | null;
9698
+ versionStatus: EntryStatus;
9699
+ type: string;
9700
+ index: string;
9701
+ workspace: string;
9702
+ root: string;
9703
+ path: string;
9704
+ filePath: string;
9705
+ level: number;
9706
+ parentDir: string;
9707
+ childrenDir: string;
9708
+ childrenSha: string | null;
9709
+ parentId: string | null;
9710
+ parents: string[];
9711
+ status: EntryStatus;
9712
+ active: boolean;
9713
+ main: boolean;
9714
+ visible: boolean;
9715
+ url: string;
9716
+ }[], Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9717
+ /** The entry owning each directory: children stored there have it as parent. */
9718
+ owners: import("rado").PreparedQuery<{
9719
+ childrenDir: string;
9720
+ id: string;
9721
+ }[], Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9722
+ statusesOf: import("rado").PreparedQuery<{
9723
+ id: string;
9724
+ locale: string | null;
9725
+ status: EntryStatus;
9726
+ }[], Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9727
+ dataOf: import("rado").PreparedQuery<{
9728
+ versionId: string;
9729
+ data: string;
9730
+ }[], Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9731
+ updateVersion: import("rado").PreparedQuery<import("rado/core/MetaData").MutationResultBase, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9732
+ /** An entry whose versions disagree on what all of them must share. */
9733
+ mismatchedEntry: import("rado").PreparedQuery<{
9734
+ id: string;
9735
+ }, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9736
+ /** A language whose versions disagree on their location. */
9737
+ mismatchedLanguage: import("rado").PreparedQuery<{
9738
+ id: string;
9739
+ locale: string | null;
9740
+ }, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9741
+ /** A language without exactly one active and one main version. */
9742
+ invalidStatus: import("rado").PreparedQuery<{
9743
+ id: string;
9744
+ locale: string | null;
9745
+ }, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9746
+ /** A version listed among its own parents. */
9747
+ cyclicVersion: import("rado").PreparedQuery<{
9748
+ filePath: string;
9749
+ }, Record<string, unknown>, import("rado/core/MetaData").QueryMeta>;
9750
+ free(): void;
9751
+ };
9752
+ export type SyncQueries = ReturnType<typeof prepareSyncQueries>;
9753
+
9754
+ }
9755
+
9756
+ declare module 'alinea/database/Version' {
9757
+
9758
+ /**
9759
+ * Bump when the SQLite schema or the data derived into generated databases
9760
+ * changes incompatibly. Generated database files and browser caches are keyed
9761
+ * by this version rather than the package version, so releases that keep the
9762
+ * schema reuse the databases that are already there.
9763
+ */
9764
+ export const databaseVersion = 5;
9765
+ export const generatedDatabaseFile = "database-v5.sqlite";
9766
+ export function versionedCacheName(name: string): string;
9767
+
9768
+ }
9769
+
8817
9770
  declare module 'alinea/edit' {
8818
9771
 
8819
9772
  import type { Reference } from 'alinea/core/Reference';
@@ -9608,6 +10561,7 @@ declare module 'alinea/field/metadata/MetadataField' {
9608
10561
  import type { FieldOptions } from 'alinea/core/Field';
9609
10562
  import { RecordField } from 'alinea/core/field/RecordField';
9610
10563
  import { ScalarField } from 'alinea/core/field/ScalarField';
10564
+ import { type Section, type SectionData } from 'alinea/core/Section';
9611
10565
  import { Type } from 'alinea/core/Type';
9612
10566
  import { type ImageField, type ImageLink } from 'alinea/field/link';
9613
10567
  import { type ObjectField } from 'alinea/field/object';
@@ -9620,19 +10574,23 @@ declare module 'alinea/field/metadata/MetadataField' {
9620
10574
  export interface MetadataUserOptions extends FieldOptions<MetadataAuditUser> {
9621
10575
  width?: number;
9622
10576
  }
9623
- export interface MetadataFields {
10577
+ export interface MetadataSeoFields {
9624
10578
  title: TextField;
9625
10579
  description: TextField;
9626
- aliases: AliasesField;
9627
10580
  openGraph: ObjectField<{
9628
10581
  image: ImageField;
9629
10582
  title: TextField;
9630
10583
  description: TextField;
9631
10584
  }>;
10585
+ }
10586
+ export interface MetadataDetailsFields {
9632
10587
  createdAt: MetadataTimestampField;
9633
10588
  createdBy: MetadataUserField;
9634
10589
  updatedAt: MetadataTimestampField;
9635
10590
  updatedBy: MetadataUserField;
10591
+ aliases: AliasesField;
10592
+ }
10593
+ export interface MetadataFields extends MetadataSeoFields, MetadataDetailsFields {
9636
10594
  }
9637
10595
  export interface MetadataAuditUser {
9638
10596
  name: string;
@@ -9654,6 +10612,15 @@ declare module 'alinea/field/metadata/MetadataField' {
9654
10612
  }
9655
10613
  export interface MetadataOptions extends FieldOptions<Metadata> {
9656
10614
  fields: Type<MetadataFields>;
10615
+ seo: Type<MetadataSeoFields>;
10616
+ details: Type<MetadataDetailsFields>;
10617
+ /** The details are rendered by a separate `metadataDetails` section */
10618
+ detailsSection?: boolean;
10619
+ }
10620
+ export interface MetadataConfig {
10621
+ /** Leave the details out of the field view, render them with
10622
+ * `metadataDetails(field)` instead */
10623
+ detailsSection?: boolean;
9657
10624
  }
9658
10625
  export class MetadataField extends RecordField<Metadata, MetadataOptions> {
9659
10626
  }
@@ -9663,12 +10630,24 @@ declare module 'alinea/field/metadata/MetadataField' {
9663
10630
  }
9664
10631
  export class MetadataUserField extends ScalarField<MetadataAuditUser, MetadataUserOptions> {
9665
10632
  }
9666
- export function metadata(label?: string): MetadataField;
10633
+ export function metadata(label?: string, { detailsSection }?: MetadataConfig): MetadataField;
10634
+ export class MetadataDetailsSection implements SectionData {
10635
+ field: MetadataField;
10636
+ view: string;
10637
+ definition: {};
10638
+ fields: {};
10639
+ sections: never[];
10640
+ constructor(field: MetadataField);
10641
+ }
10642
+ /** Renders the created/updated details and aliases of a metadata field
10643
+ * created with `detailsSection: true` */
10644
+ export function metadataDetails(field: MetadataField): Section;
9667
10645
 
9668
10646
  }
9669
10647
 
9670
10648
  declare module 'alinea/field/metadata/MetadataField.view' {
9671
10649
 
10650
+ import { Section } from 'alinea/core/Section';
9672
10651
  import { MetadataField, MetadataTimestampField, MetadataUserField } from 'alinea/field/metadata';
9673
10652
  export interface MetadataFieldViewProps {
9674
10653
  field: MetadataField;
@@ -9682,6 +10661,10 @@ declare module 'alinea/field/metadata/MetadataField.view' {
9682
10661
  }
9683
10662
  export function MetadataUserFieldView({ field }: MetadataUserFieldViewProps): import("react").JSX.Element;
9684
10663
  export function MetadataFieldView({ field }: MetadataFieldViewProps): import("react").JSX.Element;
10664
+ export interface MetadataDetailsViewProps {
10665
+ section: Section;
10666
+ }
10667
+ export function MetadataDetailsView({ section }: MetadataDetailsViewProps): import("react").JSX.Element;
9685
10668
 
9686
10669
  }
9687
10670
 
@@ -10112,7 +11095,7 @@ declare module 'alinea/field/richtext/RichTextExtensions' {
10112
11095
  Highlight: import("@tiptap/core").Mark<import("@tiptap/extension-highlight").HighlightOptions, any>;
10113
11096
  UndoRedo: Extension<import("@tiptap/extensions").UndoRedoOptions, any>;
10114
11097
  };
10115
- export function defaultExtensionConfig(getEntryAnchors?: () => Iterable<string>, enableImageResize?: boolean): {
11098
+ export function defaultExtensionConfig(getEntryAnchors?: () => Iterable<string>, enableImageResize?: boolean, enableTables?: boolean): {
10116
11099
  Document: TipTapNode<any, any>;
10117
11100
  Text: TipTapNode<any, any>;
10118
11101
  Paragraph: TipTapNode<import("@tiptap/extension-paragraph").ParagraphOptions, any>;
@@ -10756,16 +11739,17 @@ declare module 'alinea' {
10756
11739
 
10757
11740
  declare module 'alinea/next' {
10758
11741
 
10759
- export { createCMS } from 'alinea/adapter/next/cms';
10760
- export { createHandler, type NextHandlerOptions } from 'alinea/adapter/next/handler';
10761
- export type { AfterCommitContext, BeforeCommitContext } from 'alinea/backend/Handler';
11742
+ export type { SyncStatus } from 'alinea/adapter/next/cms';
11743
+ export type { NextHandlerOptions } from 'alinea/adapter/next/handler';
11744
+ export { createCMS, createHandler } from 'alinea/adapter/next/node';
10762
11745
  export { withAlinea } from 'alinea/adapter/next/with-alinea';
11746
+ export type { AfterCommitContext, BeforeCommitContext } from 'alinea/backend/Handler';
10763
11747
 
10764
11748
  }
10765
11749
 
10766
11750
  declare module 'alinea/next.edge' {
10767
11751
 
10768
- export { createCMS } from 'alinea/adapter/next/cms';
11752
+ export { createCMS, type SyncStatus } from 'alinea/adapter/next/cms';
10769
11753
  export { createHandler, type NextHandlerOptions } from 'alinea/adapter/next/handler';
10770
11754
  export type { AfterCommitContext, BeforeCommitContext } from 'alinea/backend/Handler';
10771
11755
 
@@ -10978,6 +11962,20 @@ declare module 'alinea/preview/RegisterPreview' {
10978
11962
  declare module 'alinea/preview/widget' {
10979
11963
 
10980
11964
  import type { DOMAttributes } from 'react';
11965
+ export interface PreviewStat {
11966
+ kind: 'query' | 'sync';
11967
+ summary: string;
11968
+ /** Whether the query was answered by the bundled database or the handler. */
11969
+ source?: 'database' | 'handler';
11970
+ durationMs: number;
11971
+ }
11972
+ /** The CMS work of one render, shown in the widget. */
11973
+ export interface PreviewStats {
11974
+ rows: Array<PreviewStat>;
11975
+ /** SQL statements run in this process for the render. */
11976
+ statements: number;
11977
+ sqlMs: number;
11978
+ }
10981
11979
  export function registerPreviewWidget(): void;
10982
11980
  global {
10983
11981
  namespace JSX {
@@ -10986,6 +11984,8 @@ declare module 'alinea/preview/widget' {
10986
11984
  adminUrl: string;
10987
11985
  editUrl: string;
10988
11986
  livePreview?: 'connected' | 'warning' | 'loading';
11987
+ /** JSON encoded PreviewStats */
11988
+ stats?: string;
10989
11989
  };
10990
11990
  }
10991
11991
  }
@@ -11019,7 +12019,11 @@ declare module 'alinea/query' {
11019
12019
  count?: true;
11020
12020
  id?: import("alinea/core/Filter").Condition<string>;
11021
12021
  parentId?: import("alinea/core/Filter").Condition<string | null>;
12022
+ versionStatus?: import("alinea/core/Filter").Condition<import("alinea/core/Entry").EntryStatus>;
12023
+ main?: import("alinea/core/Filter").Condition<boolean>;
11022
12024
  path?: import("alinea/core/Filter").Condition<string>;
12025
+ filePath?: import("alinea/core/Filter").Condition<string>;
12026
+ seeded?: import("alinea/core/Filter").Condition<string | null>;
11023
12027
  url?: import("alinea/core/Filter").Condition<string>;
11024
12028
  alias?: import("alinea/core/Filter").Condition<string>;
11025
12029
  createdAt?: import("alinea/core/Filter").Condition<number | null>;
@@ -11054,7 +12058,11 @@ declare module 'alinea/query' {
11054
12058
  count?: true;
11055
12059
  id?: import("alinea/core/Filter").Condition<string>;
11056
12060
  parentId?: import("alinea/core/Filter").Condition<string | null>;
12061
+ versionStatus?: import("alinea/core/Filter").Condition<import("alinea/core/Entry").EntryStatus>;
12062
+ main?: import("alinea/core/Filter").Condition<boolean>;
11057
12063
  path?: import("alinea/core/Filter").Condition<string>;
12064
+ filePath?: import("alinea/core/Filter").Condition<string>;
12065
+ seeded?: import("alinea/core/Filter").Condition<string | null>;
11058
12066
  url?: import("alinea/core/Filter").Condition<string>;
11059
12067
  alias?: import("alinea/core/Filter").Condition<string>;
11060
12068
  createdAt?: import("alinea/core/Filter").Condition<number | null>;
@@ -11089,7 +12097,11 @@ declare module 'alinea/query' {
11089
12097
  count?: true;
11090
12098
  id?: import("alinea/core/Filter").Condition<string>;
11091
12099
  parentId?: import("alinea/core/Filter").Condition<string | null>;
12100
+ versionStatus?: import("alinea/core/Filter").Condition<import("alinea/core/Entry").EntryStatus>;
12101
+ main?: import("alinea/core/Filter").Condition<boolean>;
11092
12102
  path?: import("alinea/core/Filter").Condition<string>;
12103
+ filePath?: import("alinea/core/Filter").Condition<string>;
12104
+ seeded?: import("alinea/core/Filter").Condition<string | null>;
11093
12105
  url?: import("alinea/core/Filter").Condition<string>;
11094
12106
  alias?: import("alinea/core/Filter").Condition<string>;
11095
12107
  createdAt?: import("alinea/core/Filter").Condition<number | null>;
@@ -11124,7 +12136,11 @@ declare module 'alinea/query' {
11124
12136
  count?: true;
11125
12137
  id?: import("alinea/core/Filter").Condition<string>;
11126
12138
  parentId?: import("alinea/core/Filter").Condition<string | null>;
12139
+ versionStatus?: import("alinea/core/Filter").Condition<import("alinea/core/Entry").EntryStatus>;
12140
+ main?: import("alinea/core/Filter").Condition<boolean>;
11127
12141
  path?: import("alinea/core/Filter").Condition<string>;
12142
+ filePath?: import("alinea/core/Filter").Condition<string>;
12143
+ seeded?: import("alinea/core/Filter").Condition<string | null>;
11128
12144
  url?: import("alinea/core/Filter").Condition<string>;
11129
12145
  alias?: import("alinea/core/Filter").Condition<string>;
11130
12146
  createdAt?: import("alinea/core/Filter").Condition<number | null>;
@@ -11157,7 +12173,11 @@ declare module 'alinea/query' {
11157
12173
  count?: true;
11158
12174
  id?: import("alinea/core/Filter").Condition<string>;
11159
12175
  parentId?: import("alinea/core/Filter").Condition<string | null>;
12176
+ versionStatus?: import("alinea/core/Filter").Condition<import("alinea/core/Entry").EntryStatus>;
12177
+ main?: import("alinea/core/Filter").Condition<boolean>;
11160
12178
  path?: import("alinea/core/Filter").Condition<string>;
12179
+ filePath?: import("alinea/core/Filter").Condition<string>;
12180
+ seeded?: import("alinea/core/Filter").Condition<string | null>;
11161
12181
  url?: import("alinea/core/Filter").Condition<string>;
11162
12182
  alias?: import("alinea/core/Filter").Condition<string>;
11163
12183
  createdAt?: import("alinea/core/Filter").Condition<number | null>;
@@ -11189,7 +12209,11 @@ declare module 'alinea/query' {
11189
12209
  count?: true;
11190
12210
  id?: import("alinea/core/Filter").Condition<string>;
11191
12211
  parentId?: import("alinea/core/Filter").Condition<string | null>;
12212
+ versionStatus?: import("alinea/core/Filter").Condition<import("alinea/core/Entry").EntryStatus>;
12213
+ main?: import("alinea/core/Filter").Condition<boolean>;
11192
12214
  path?: import("alinea/core/Filter").Condition<string>;
12215
+ filePath?: import("alinea/core/Filter").Condition<string>;
12216
+ seeded?: import("alinea/core/Filter").Condition<string | null>;
11193
12217
  url?: import("alinea/core/Filter").Condition<string>;
11194
12218
  alias?: import("alinea/core/Filter").Condition<string>;
11195
12219
  createdAt?: import("alinea/core/Filter").Condition<number | null>;
@@ -11221,7 +12245,11 @@ declare module 'alinea/query' {
11221
12245
  count?: true;
11222
12246
  id?: import("alinea/core/Filter").Condition<string>;
11223
12247
  parentId?: import("alinea/core/Filter").Condition<string | null>;
12248
+ versionStatus?: import("alinea/core/Filter").Condition<import("alinea/core/Entry").EntryStatus>;
12249
+ main?: import("alinea/core/Filter").Condition<boolean>;
11224
12250
  path?: import("alinea/core/Filter").Condition<string>;
12251
+ filePath?: import("alinea/core/Filter").Condition<string>;
12252
+ seeded?: import("alinea/core/Filter").Condition<string | null>;
11225
12253
  url?: import("alinea/core/Filter").Condition<string>;
11226
12254
  alias?: import("alinea/core/Filter").Condition<string>;
11227
12255
  createdAt?: import("alinea/core/Filter").Condition<number | null>;