alinea 2.0.0-preview.1 → 2.0.0-preview.2

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 (201) hide show
  1. package/dist/.init/content/pages/welcome.json +1 -1
  2. package/dist/LICENSES.md +61 -34
  3. package/dist/adapter/next/DevRemote.d.ts +3 -0
  4. package/dist/adapter/next/DevRemote.js +37 -0
  5. package/dist/adapter/next/ForwardCredentials.d.ts +1 -0
  6. package/dist/adapter/next/ForwardCredentials.js +17 -0
  7. package/dist/adapter/next/cms.js +86 -38
  8. package/dist/adapter/next/context.d.ts +1 -1
  9. package/dist/adapter/next/context.js +11 -3
  10. package/dist/adapter/next/handler.d.ts +2 -0
  11. package/dist/adapter/next/handler.js +23 -16
  12. package/dist/adapter/next/with-alinea.js +18 -16
  13. package/dist/backend/Handler.d.ts +14 -13
  14. package/dist/backend/Handler.js +53 -22
  15. package/dist/backend/resolver/ParsePreview.d.ts +8 -4
  16. package/dist/backend/resolver/ParsePreview.js +40 -43
  17. package/dist/bundled.d.ts +255 -60
  18. package/dist/chunks/{chunk-LUBOGJCG.js → chunk-6V4TAD2N.js} +1 -1
  19. package/dist/chunks/{chunk-TL7ZW6BX.js → chunk-A3XXZBOQ.js} +2535 -2999
  20. package/dist/chunks/chunk-AC5ATYI3.js +1434 -0
  21. package/dist/chunks/{chunk-7ORQ7BYK.js → chunk-KMLLLZ6X.js} +202 -269
  22. package/dist/chunks/{chunk-IWN3T3FL.js → chunk-O7XWTMCB.js} +1 -1
  23. package/dist/chunks/{chunk-53DZYORR.js → chunk-PAYSVIYI.js} +76 -176
  24. package/dist/chunks/{chunk-YK3OSMDM.js → chunk-S4AU6SRS.js} +188 -158
  25. package/dist/chunks/{chunk-FRYHNUEK.js → chunk-UVOEM4AH.js} +6 -3
  26. package/dist/chunks/{chunk-I7X64JTZ.js → chunk-VXZM6V4P.js} +4 -4
  27. package/dist/cli/Generate.js +3 -13
  28. package/dist/cli/Serve.js +7 -3
  29. package/dist/cli/bin.js +8 -5
  30. package/dist/cli/generate/CopyStaticFiles.js +2 -3
  31. package/dist/cli/generate/DevDB.d.ts +1 -1
  32. package/dist/cli/generate/DevDB.js +3 -5
  33. package/dist/cli/serve/CreateLocalServer.d.ts +1 -1
  34. package/dist/cli/serve/CreateLocalServer.js +3 -2
  35. package/dist/cli/serve/DevServer.d.ts +3 -2
  36. package/dist/cli/serve/DevServer.js +60 -3
  37. package/dist/cli/serve/ServeContext.d.ts +1 -0
  38. package/dist/cli/serve/StartServer.d.ts +1 -0
  39. package/dist/cli/serve/StartServer.js +6 -1
  40. package/dist/cli/util/EnsureLibs.js +2 -2
  41. package/dist/cli/util/EnsureNode.js +2 -2
  42. package/dist/cloud/CloudRemote.js +1 -1
  43. package/dist/components/Breadcrumbs.js +8 -8
  44. package/dist/components/Button.js +6 -6
  45. package/dist/components/Calendar.js +9 -9
  46. package/dist/components/Checkbox.js +9 -9
  47. package/dist/components/CheckboxGroup.js +6 -6
  48. package/dist/components/ColorSwatch.js +6 -6
  49. package/dist/components/ColorSwatchPicker.js +6 -6
  50. package/dist/components/ComboBox.js +16 -16
  51. package/dist/components/DateField.js +9 -9
  52. package/dist/components/DatePicker.js +11 -11
  53. package/dist/components/DateRangePicker.js +14 -14
  54. package/dist/components/Dialog.js +6 -6
  55. package/dist/components/Disclosure.js +14 -14
  56. package/dist/components/DropZone.js +6 -6
  57. package/dist/components/Label.js +14 -14
  58. package/dist/components/Link.js +6 -6
  59. package/dist/components/List.js +32 -32
  60. package/dist/components/Menu.js +10 -10
  61. package/dist/components/Modal.js +8 -8
  62. package/dist/components/MultipleSelect.js +25 -25
  63. package/dist/components/NumberField.d.ts +2 -1
  64. package/dist/components/NumberField.js +25 -15
  65. package/dist/components/Popover.js +6 -6
  66. package/dist/components/ProgressCircle.js +9 -9
  67. package/dist/components/RadioGroup.js +8 -8
  68. package/dist/components/SearchField.js +15 -15
  69. package/dist/components/Select.js +14 -14
  70. package/dist/components/Surface.js +2 -2
  71. package/dist/components/Switch.js +8 -8
  72. package/dist/components/Table.js +14 -14
  73. package/dist/components/Tabs.js +7 -7
  74. package/dist/components/TagGroup.js +8 -8
  75. package/dist/components/TextField.js +10 -10
  76. package/dist/components/TimeField.js +8 -8
  77. package/dist/components/Toggle.js +8 -8
  78. package/dist/components/Toolbar.js +9 -9
  79. package/dist/components/Tooltip.js +7 -7
  80. package/dist/components/Tree.js +10 -10
  81. package/dist/core/Client.js +3 -2
  82. package/dist/core/Config.d.ts +4 -0
  83. package/dist/core/Config.js +6 -0
  84. package/dist/core/Connection.d.ts +2 -2
  85. package/dist/core/Connection.js +7 -0
  86. package/dist/core/Graph.d.ts +3 -4
  87. package/dist/core/ListRow.d.ts +2 -0
  88. package/dist/core/OrderBy.d.ts +7 -3
  89. package/dist/core/TextDoc.d.ts +11 -0
  90. package/dist/core/Trace.d.ts +8 -0
  91. package/dist/core/Trace.js +32 -0
  92. package/dist/core/db/CommitRequest.d.ts +0 -3
  93. package/dist/core/db/CommitRequest.js +0 -9
  94. package/dist/core/db/EntryDB.js +1 -1
  95. package/dist/core/db/EntryIndex.d.ts +10 -0
  96. package/dist/core/db/EntryIndex.js +117 -9
  97. package/dist/core/db/EntryResolver.js +104 -29
  98. package/dist/core/db/EntryTransaction.d.ts +1 -2
  99. package/dist/core/db/EntryTransaction.js +25 -13
  100. package/dist/core/db/LocalDB.d.ts +2 -3
  101. package/dist/core/db/LocalDB.js +24 -19
  102. package/dist/core/field/RichTextField.d.ts +30 -3
  103. package/dist/core/field/RichTextField.js +125 -41
  104. package/dist/core/media/MediaAltField.d.ts +1 -0
  105. package/dist/core/media/MediaAltField.js +9 -1
  106. package/dist/core/source/OverlaySource.d.ts +12 -0
  107. package/dist/core/source/OverlaySource.js +50 -0
  108. package/dist/core/source/Source.d.ts +1 -1
  109. package/dist/core/source/Source.js +3 -3
  110. package/dist/dashboard/App.css +158 -0
  111. package/dist/dashboard/App.js +3 -3
  112. package/dist/dashboard/app/AccessDenied.js +1 -1
  113. package/dist/dashboard/app/ActivityStatus.js +14 -14
  114. package/dist/dashboard/app/AppShell.js +2 -2
  115. package/dist/dashboard/app/AuthView.js +12 -12
  116. package/dist/dashboard/app/Badge.js +2 -2
  117. package/dist/dashboard/app/CompactField.js +13 -13
  118. package/dist/dashboard/app/DashboardErrorBoundary.js +6 -6
  119. package/dist/dashboard/app/DashboardLayout.js +22 -19
  120. package/dist/dashboard/app/EntryHeader.js +10 -10
  121. package/dist/dashboard/app/EntryReferences.js +9 -9
  122. package/dist/dashboard/app/EntrySidebar.d.ts +1 -1
  123. package/dist/dashboard/app/EntrySidebar.js +19 -18
  124. package/dist/dashboard/app/EntrySidebarPreview.d.ts +1 -1
  125. package/dist/dashboard/app/EntrySidebarPreview.js +20 -15
  126. package/dist/dashboard/app/EntryTranslationBanner.js +6 -6
  127. package/dist/dashboard/app/Explorer.js +46 -46
  128. package/dist/dashboard/app/ExplorerCards.js +42 -42
  129. package/dist/dashboard/app/ExplorerFileCard.js +16 -16
  130. package/dist/dashboard/app/ExplorerList.js +15 -15
  131. package/dist/dashboard/app/ExplorerModal.js +5 -5
  132. package/dist/dashboard/app/ExplorerTable.js +54 -54
  133. package/dist/dashboard/app/ExternalLinkPicker.js +15 -4
  134. package/dist/dashboard/app/LinkPicker.js +6 -6
  135. package/dist/dashboard/app/LocaleMenu.js +11 -11
  136. package/dist/dashboard/app/SidebarLayout.d.ts +8 -0
  137. package/dist/dashboard/app/SidebarLayout.js +1169 -0
  138. package/dist/dashboard/app/SidebarTree.js +44 -44
  139. package/dist/dashboard/app/WorkspaceMenu.js +17 -17
  140. package/dist/dashboard/app/WorkspaceRoots.js +26 -26
  141. package/dist/dashboard/app/editor/FileEditor.js +28 -29
  142. package/dist/dashboard/app/editor/FilePreview.js +2 -2
  143. package/dist/dashboard/app/editor/MediaAltField.view.d.ts +1 -1
  144. package/dist/dashboard/app/editor/MediaAltField.view.js +6 -9
  145. package/dist/dashboard/app/modals/CreateEntry.js +4 -4
  146. package/dist/dashboard/app/pages/EntryPage.js +49 -25
  147. package/dist/dashboard/app/pages/UsersPage.js +38 -38
  148. package/dist/dashboard/app/ui/DashboardModal.js +22 -22
  149. package/dist/dashboard/app/ui/Rail.js +2 -2
  150. package/dist/dashboard/app/ui/Sidebar.js +2 -2
  151. package/dist/dashboard/atoms/auth.js +2 -0
  152. package/dist/dashboard/atoms/dashboard.d.ts +9 -0
  153. package/dist/dashboard/atoms/dashboard.js +17 -0
  154. package/dist/dashboard/atoms/editor.d.ts +7 -1
  155. package/dist/dashboard/atoms/editor.js +4 -1
  156. package/dist/dashboard/atoms/entry.d.ts +3 -1
  157. package/dist/dashboard/atoms/entry.js +55 -15
  158. package/dist/dashboard/atoms/explorer.js +3 -8
  159. package/dist/dashboard/atoms/preview.d.ts +3 -0
  160. package/dist/dashboard/atoms/preview.js +18 -1
  161. package/dist/dashboard/hooks.d.ts +4 -2
  162. package/dist/dashboard/hooks.js +9 -5
  163. package/dist/dashboard/icons.d.ts +1 -0
  164. package/dist/dashboard/icons.js +10 -0
  165. package/dist/field/check/CheckField.view.js +2 -2
  166. package/dist/field/code/CodeField.view.js +13 -13
  167. package/dist/field/date/DateField.view.js +12 -2
  168. package/dist/field/link/Link.js +2 -2
  169. package/dist/field/link/LinkField.view.js +88 -77
  170. package/dist/field/list/ListField.view.js +22 -33
  171. package/dist/field/metadata/MetadataField.view.js +20 -20
  172. package/dist/field/number/NumberField.d.ts +2 -0
  173. package/dist/field/number/NumberField.view.js +4 -2
  174. package/dist/field/richtext/PickTextLink.d.ts +7 -0
  175. package/dist/field/richtext/PickTextLink.js +104 -2
  176. package/dist/field/richtext/ReferenceLink.d.ts +4 -1
  177. package/dist/field/richtext/ReferenceLink.js +18 -4
  178. package/dist/field/richtext/RichTextDocument.d.ts +7 -2
  179. package/dist/field/richtext/RichTextDocument.js +34 -5
  180. package/dist/field/richtext/RichTextExtensions.d.ts +3 -1
  181. package/dist/field/richtext/RichTextExtensions.js +2704 -7467
  182. package/dist/field/richtext/RichTextField.d.ts +4 -0
  183. package/dist/field/richtext/RichTextField.story.d.ts +4 -0
  184. package/dist/field/richtext/RichTextField.view.js +44 -21
  185. package/dist/field/richtext/RichTextInsertMenu.js +281 -495
  186. package/dist/field/richtext/RichTextToolbar.d.ts +5 -2
  187. package/dist/field/richtext/RichTextToolbar.js +38 -3
  188. package/dist/field/richtext/Toolbar.d.ts +19 -1
  189. package/dist/field/richtext/Toolbar.js +18 -13
  190. package/dist/field/richtext/extensions/Anchor.js +1 -1
  191. package/dist/field/richtext/extensions/Link.js +1 -1
  192. package/dist/field/richtext/extensions/Small.js +1 -1
  193. package/dist/field/select/SelectField.view.js +8 -6
  194. package/dist/index.css +219 -14
  195. package/dist/next.cjs +18 -16
  196. package/dist/next.d.ts +2 -1
  197. package/dist/next.edge.d.ts +2 -1
  198. package/dist/ui/RichText.d.ts +1 -0
  199. package/dist/ui/RichText.js +29 -2
  200. package/package.json +6 -3
  201. package/dist/chunks/{chunk-JFROOUG6.js → chunk-NIVGVRDS.js} +17 -17
@@ -1840,7 +1840,7 @@ import { IndexEvent } from "./IndexEvent.js";
1840
1840
  function combineConditions(a, b) {
1841
1841
  return {
1842
1842
  search: a.search ?? b.search,
1843
- nodes: a.nodes ?? b.nodes,
1843
+ nodes: combineNodes(a.nodes, b.nodes),
1844
1844
  node(node) {
1845
1845
  return (a.node ? a.node(node) : true) && (b.node ? b.node(node) : true);
1846
1846
  },
@@ -1852,6 +1852,14 @@ function combineConditions(a, b) {
1852
1852
  }
1853
1853
  };
1854
1854
  }
1855
+ function combineNodes(a, b) {
1856
+ if (!a) return b;
1857
+ if (!b) return a;
1858
+ return matchingNodes(b, new Set(a));
1859
+ }
1860
+ function* matchingNodes(nodes, included) {
1861
+ for (const node of nodes) if (included.has(node)) yield node;
1862
+ }
1855
1863
  var EntryLanguage = class extends Map {
1856
1864
  locale;
1857
1865
  parentDir;
@@ -2082,7 +2090,16 @@ var EntryGraph = class _EntryGraph {
2082
2090
  #filesById = /* @__PURE__ */ new Map();
2083
2091
  #byId = /* @__PURE__ */ new Map();
2084
2092
  #byDir = /* @__PURE__ */ new Map();
2085
- #childrenByParentId = /* @__PURE__ */ new Map();
2093
+ #nodesBy = {
2094
+ parentId: /* @__PURE__ */ new Map(),
2095
+ url: /* @__PURE__ */ new Map(),
2096
+ workspace: /* @__PURE__ */ new Map(),
2097
+ root: /* @__PURE__ */ new Map(),
2098
+ type: /* @__PURE__ */ new Map(),
2099
+ workspaceRoot: /* @__PURE__ */ new Map(),
2100
+ workspaceRootType: /* @__PURE__ */ new Map()
2101
+ };
2102
+ #nodeOrder = /* @__PURE__ */ new Map();
2086
2103
  nodes;
2087
2104
  #singleWorkspace;
2088
2105
  #search;
@@ -2106,16 +2123,92 @@ var EntryGraph = class _EntryGraph {
2106
2123
  this.#byDir.set(dir, version.id);
2107
2124
  }
2108
2125
  this.nodes = [...this.#filesById.keys()].map((file) => this.#mkNode(file)).sort((a, b) => compareStrings(a.index, b.index));
2109
- for (const node of this.nodes) {
2110
- if (!node.parentId) continue;
2111
- const children = this.#childrenByParentId.get(node.parentId) ?? [];
2112
- children.push(node);
2113
- this.#childrenByParentId.set(node.parentId, children);
2126
+ for (const [order, node] of this.nodes.entries()) {
2127
+ this.#nodeOrder.set(node, order);
2128
+ this.#indexNode(node);
2114
2129
  }
2115
2130
  }
2116
2131
  byId(id) {
2117
2132
  return this.#byId.get(id);
2118
2133
  }
2134
+ candidateNodes(query) {
2135
+ const candidates = [];
2136
+ const { ids, parentIds, urls, workspaces, roots, types } = query;
2137
+ if (ids) {
2138
+ const nodes = ids.flatMap((id) => this.byId(id) ?? []);
2139
+ return this.#ordered(nodes).filter((node) => this.#matches(node, query));
2140
+ }
2141
+ if (parentIds)
2142
+ candidates.push(this.#fromIndex(this.#nodesBy.parentId, parentIds));
2143
+ if (urls) candidates.push(this.#fromIndex(this.#nodesBy.url, urls));
2144
+ if (workspaces && roots && types) {
2145
+ const keys2 = workspaces.flatMap(
2146
+ (workspace) => roots.flatMap(
2147
+ (root) => types.map((type) => indexKey(workspace, root, type))
2148
+ )
2149
+ );
2150
+ candidates.push(this.#fromIndex(this.#nodesBy.workspaceRootType, keys2));
2151
+ } else if (workspaces && roots) {
2152
+ const keys2 = workspaces.flatMap(
2153
+ (workspace) => roots.map((root) => indexKey(workspace, root))
2154
+ );
2155
+ candidates.push(this.#fromIndex(this.#nodesBy.workspaceRoot, keys2));
2156
+ } else {
2157
+ if (workspaces)
2158
+ candidates.push(this.#fromIndex(this.#nodesBy.workspace, workspaces));
2159
+ if (roots) candidates.push(this.#fromIndex(this.#nodesBy.root, roots));
2160
+ if (types) candidates.push(this.#fromIndex(this.#nodesBy.type, types));
2161
+ }
2162
+ if (candidates.length === 0) return void 0;
2163
+ candidates.sort((a, b) => a.length - b.length);
2164
+ const [smallest, ...rest] = candidates;
2165
+ const intersection = rest.reduce(
2166
+ (result, nodes) => result.intersection(new Set(nodes)),
2167
+ new Set(smallest)
2168
+ );
2169
+ return Array.from(intersection);
2170
+ }
2171
+ #matches(node, query) {
2172
+ const { parentIds, urls, workspaces, roots, types } = query;
2173
+ if (parentIds && !parentIds.includes(node.parentId)) return false;
2174
+ if (workspaces && !workspaces.includes(node.workspace)) return false;
2175
+ if (roots && !roots.includes(node.root)) return false;
2176
+ if (types && !types.includes(node.type)) return false;
2177
+ if (urls) {
2178
+ const matchesUrl = Array.from(node.values()).some(
2179
+ (language) => urls.includes(language.url)
2180
+ );
2181
+ if (!matchesUrl) return false;
2182
+ }
2183
+ return true;
2184
+ }
2185
+ #indexNode(node) {
2186
+ addToIndex(this.#nodesBy.parentId, node.parentId, node);
2187
+ addToIndex(this.#nodesBy.workspace, node.workspace, node);
2188
+ addToIndex(this.#nodesBy.root, node.root, node);
2189
+ addToIndex(this.#nodesBy.type, node.type, node);
2190
+ addToIndex(
2191
+ this.#nodesBy.workspaceRoot,
2192
+ indexKey(node.workspace, node.root),
2193
+ node
2194
+ );
2195
+ addToIndex(
2196
+ this.#nodesBy.workspaceRootType,
2197
+ indexKey(node.workspace, node.root, node.type),
2198
+ node
2199
+ );
2200
+ const urls = new Set(Array.from(node.values(), (language) => language.url));
2201
+ for (const url of urls) addToIndex(this.#nodesBy.url, url, node);
2202
+ }
2203
+ #fromIndex(index, keys2) {
2204
+ if (keys2.length === 1) return [...index.get(keys2[0]) ?? []];
2205
+ return this.#ordered(keys2.flatMap((key) => index.get(key) ?? []));
2206
+ }
2207
+ #ordered(nodes) {
2208
+ return Array.from(new Set(nodes)).sort(
2209
+ (a, b) => this.#nodeOrder.get(a) - this.#nodeOrder.get(b)
2210
+ );
2211
+ }
2119
2212
  get config() {
2120
2213
  return this.#config;
2121
2214
  }
@@ -2267,13 +2360,21 @@ var EntryGraph = class _EntryGraph {
2267
2360
  this,
2268
2361
  type,
2269
2362
  parent,
2270
- () => this.#childrenByParentId.get(id) ?? [],
2363
+ () => this.#nodesBy.parentId.get(id) ?? [],
2271
2364
  collection
2272
2365
  );
2273
2366
  this.#byId.set(id, node);
2274
2367
  return node;
2275
2368
  }
2276
2369
  };
2370
+ function addToIndex(index, key, node) {
2371
+ const nodes = index.get(key) ?? [];
2372
+ nodes.push(node);
2373
+ index.set(key, nodes);
2374
+ }
2375
+ function indexKey(...parts) {
2376
+ return JSON.stringify(parts);
2377
+ }
2277
2378
  var VersionParser = class extends Map {
2278
2379
  parse(sha, blob) {
2279
2380
  if (super.has(sha)) return super.get(sha);
@@ -2335,7 +2436,7 @@ function changedParentIds(previous, next) {
2335
2436
  }
2336
2437
  return parents;
2337
2438
  }
2338
- var EntryIndex = class extends EventTarget {
2439
+ var EntryIndex = class _EntryIndex extends EventTarget {
2339
2440
  tree = ReadonlyTree.EMPTY;
2340
2441
  initialSync;
2341
2442
  graph;
@@ -2352,6 +2453,13 @@ var EntryIndex = class extends EventTarget {
2352
2453
  this.graph = new EntryGraph(config, /* @__PURE__ */ new Map(), this.#seeds);
2353
2454
  this.#singleWorkspace = Config.multipleWorkspaces(config) ? void 0 : keys(config.workspaces)[0];
2354
2455
  }
2456
+ clone() {
2457
+ const clone = new _EntryIndex(this.#config);
2458
+ clone.tree = this.tree;
2459
+ clone.initialSync = this.initialSync;
2460
+ clone.graph = this.graph;
2461
+ return clone;
2462
+ }
2355
2463
  get sha() {
2356
2464
  return this.tree.sha;
2357
2465
  }
@@ -42,6 +42,7 @@ var EntryResolver = class {
42
42
  config;
43
43
  index;
44
44
  #scope;
45
+ #previewGraphs = /* @__PURE__ */ new WeakMap();
45
46
  call(ctx, entry, internal) {
46
47
  switch (internal.method) {
47
48
  case "snippet": {
@@ -149,9 +150,12 @@ var EntryResolver = class {
149
150
  }
150
151
  case "children": {
151
152
  const depth = query?.depth ?? 1;
153
+ const self = ctx.graph.byId(entry.id);
152
154
  return {
153
- entry: (e) => e.filePath.startsWith(entry.childrenDir),
154
- node: (node) => node.level > entry.level && node.level <= entry.level + depth
155
+ nodes: ctx.graph.candidateNodes({
156
+ ids: descendantIds(self, depth)
157
+ }),
158
+ language: (language) => language.locale === entry.locale
155
159
  };
156
160
  }
157
161
  case "parents": {
@@ -221,8 +225,7 @@ var EntryResolver = class {
221
225
  if (name.startsWith("_")) return entryFieldValue(entry, name.slice(1));
222
226
  return entry.data[name];
223
227
  });
224
- const multipleIds = typeof query.id === "object" && query.id !== null && query.id.in;
225
- const ids = Array.isArray(multipleIds) ? multipleIds : typeof query.id === "string" ? [query.id] : void 0;
228
+ const ids = equalityValues(query.id);
226
229
  return {
227
230
  ids,
228
231
  condition(entry) {
@@ -248,7 +251,7 @@ var EntryResolver = class {
248
251
  const { ids, condition } = this.condition(ctx, edge);
249
252
  const filter = {
250
253
  search: Array.isArray(search) ? search.join(" ") : search,
251
- node: (node) => ids ? ids.includes(node.id) : true,
254
+ nodes: preFilter?.nodes ? void 0 : this.#candidateNodes(ctx.graph, query, ids),
252
255
  entry: condition
253
256
  };
254
257
  let entries2 = Array.from(
@@ -269,17 +272,33 @@ var EntryResolver = class {
269
272
  const orders = Array.isArray(orderBy) ? orderBy : [orderBy];
270
273
  entries2.sort((a, b) => {
271
274
  for (const order of orders) {
272
- const expr = order.asc ?? order.desc;
275
+ const ascending = order.asc !== void 0;
276
+ const descending = order.desc !== void 0;
277
+ assert(
278
+ ascending !== descending,
279
+ "orderBy must specify exactly one direction"
280
+ );
281
+ const expr = ascending ? order.asc : order.desc;
273
282
  const valueA = this.expr(ctx, a, expr);
274
283
  const valueB = this.expr(ctx, b, expr);
284
+ const nullishA = valueA === null || valueA === void 0;
285
+ const nullishB = valueB === null || valueB === void 0;
286
+ if (nullishA || nullishB) {
287
+ if (nullishA !== nullishB) return nullishA ? 1 : -1;
288
+ continue;
289
+ }
275
290
  const strings = typeof valueA === "string" && typeof valueB === "string";
276
291
  const numbers = typeof valueA === "number" && typeof valueB === "number";
292
+ const booleans = typeof valueA === "boolean" && typeof valueB === "boolean";
277
293
  if (strings) {
278
294
  const compare = order.caseSensitive ? compareStrings(valueA, valueB) : valueA.localeCompare(valueB, void 0, { numeric: true });
279
- if (compare !== 0) return order.asc ? compare : -compare;
295
+ if (compare !== 0) return ascending ? compare : -compare;
280
296
  } else if (numbers) {
281
297
  if (valueA !== valueB)
282
- return order.asc ? valueA - valueB : valueB - valueA;
298
+ return ascending ? valueA - valueB : valueB - valueA;
299
+ } else if (booleans && valueA !== valueB) {
300
+ const compare = Number(valueA) - Number(valueB);
301
+ return ascending ? compare : -compare;
283
302
  }
284
303
  }
285
304
  return 0;
@@ -334,6 +353,31 @@ var EntryResolver = class {
334
353
  getProcessed
335
354
  };
336
355
  }
356
+ #candidateNodes(graph, query, ids) {
357
+ const location = Array.isArray(query.location) ? query.location : query.location && this.#scope.locationOf(query.location);
358
+ const indexedLocation = location && location.length >= 1 && location.length <= 3 ? location : void 0;
359
+ const workspaceQuery = isRecord(query.workspace) && hasWorkspace(query.workspace) ? this.#scope.nameOf(query.workspace) : query.workspace;
360
+ const rootQuery = isRecord(query.root) && hasRoot(query.root) ? this.#scope.nameOf(query.root) : query.root;
361
+ const workspaces = intersectValues(
362
+ equalityValues(workspaceQuery),
363
+ indexedLocation?.[0] ? [indexedLocation[0]] : void 0
364
+ );
365
+ const roots = intersectValues(
366
+ equalityValues(rootQuery),
367
+ indexedLocation?.[1] ? [indexedLocation[1]] : void 0
368
+ );
369
+ const types = query.type ? (Array.isArray(query.type) ? query.type : [query.type]).map(
370
+ (type) => this.#scope.nameOf(type)
371
+ ) : void 0;
372
+ return graph.candidateNodes({
373
+ ids,
374
+ parentIds: equalityValues(query.parentId),
375
+ urls: equalityValues(query.url),
376
+ workspaces,
377
+ roots,
378
+ types
379
+ });
380
+ }
337
381
  async postField(ctx, interim, field) {
338
382
  return Field.queryValue(field, interim, ctx.linkResolver);
339
383
  }
@@ -374,27 +418,7 @@ var EntryResolver = class {
374
418
  return interim;
375
419
  }
376
420
  async resolve(query) {
377
- const { preview } = query;
378
- const previewEntry = preview && "entry" in preview ? preview.entry : void 0;
379
- let graph = this.index.graph;
380
- if (previewEntry)
381
- graph = graph.withChanges({
382
- fromSha: this.index.tree.sha,
383
- changes: [
384
- {
385
- op: "add",
386
- path: previewEntry.filePath,
387
- sha: previewEntry.fileHash,
388
- contents: new TextEncoder().encode(
389
- JSON.stringify(
390
- createRecord(previewEntry, previewEntry.status),
391
- null,
392
- 2
393
- )
394
- )
395
- }
396
- ]
397
- });
421
+ const graph = this.#graphFor(query.preview);
398
422
  const ctx = {
399
423
  status: query.status ?? "published",
400
424
  locale: query.locale,
@@ -403,6 +427,32 @@ var EntryResolver = class {
403
427
  };
404
428
  return this.query(ctx, query).getProcessed();
405
429
  }
430
+ #graphFor(preview) {
431
+ const baseGraph = this.index.graph;
432
+ if (!preview || !("entry" in preview)) return baseGraph;
433
+ const cached = this.#previewGraphs.get(preview);
434
+ if (cached?.baseGraph === baseGraph) return cached.previewGraph;
435
+ const previewEntry = preview.entry;
436
+ const previewGraph = baseGraph.withChanges({
437
+ fromSha: this.index.tree.sha,
438
+ changes: [
439
+ {
440
+ op: "add",
441
+ path: previewEntry.filePath,
442
+ sha: previewEntry.fileHash,
443
+ contents: new TextEncoder().encode(
444
+ JSON.stringify(
445
+ createRecord(previewEntry, previewEntry.status),
446
+ null,
447
+ 2
448
+ )
449
+ )
450
+ }
451
+ ]
452
+ });
453
+ this.#previewGraphs.set(preview, { baseGraph, previewGraph });
454
+ return previewGraph;
455
+ }
406
456
  };
407
457
  function statusChecker(status) {
408
458
  switch (status) {
@@ -420,6 +470,31 @@ function statusChecker(status) {
420
470
  return (entry) => true;
421
471
  }
422
472
  }
473
+ function descendantIds(node, depth) {
474
+ if (!node || depth <= 0) return [];
475
+ const result = [];
476
+ let level = [node];
477
+ for (let currentDepth = 1; currentDepth <= depth; currentDepth++) {
478
+ level = level.flatMap((parent) => Array.from(parent.children()));
479
+ if (level.length === 0) break;
480
+ for (const child of level) result.push(child.id);
481
+ }
482
+ return result;
483
+ }
484
+ function equalityValues(condition) {
485
+ if (condition === void 0) return void 0;
486
+ if (typeof condition !== "object" || condition === null)
487
+ return [condition];
488
+ const operations = condition;
489
+ if (operations.is !== void 0) return [operations.is];
490
+ if (Array.isArray(operations.in)) return operations.in;
491
+ return void 0;
492
+ }
493
+ function intersectValues(a, b) {
494
+ if (!a) return b;
495
+ if (!b) return a;
496
+ return Array.from(new Set(a).intersection(new Set(b)));
497
+ }
423
498
  function entryChecker(scope, query) {
424
499
  const root = isRecord(query.root) && hasRoot(query.root) ? scope.nameOf(query.root) : query.root;
425
500
  const workspace = isRecord(query.workspace) && hasWorkspace(query.workspace) ? scope.nameOf(query.workspace) : query.workspace;
@@ -20,12 +20,11 @@ export declare class EntryTransaction {
20
20
  removeFile(mutation: Op<RemoveFileMutation>): this;
21
21
  uploadFile(mutation: Op<UploadFileMutation>): this;
22
22
  description(): string;
23
- apply(mutations: Array<Mutation>): void;
23
+ apply(mutations: ReadonlyArray<Mutation>): Promise<void>;
24
24
  toRequest(): Promise<{
25
25
  fromSha: string;
26
26
  intoSha: string;
27
27
  description: string;
28
- checks: [path: string, sha: string][];
29
28
  changes: CommitChange[];
30
29
  }>;
31
30
  }
@@ -4,7 +4,6 @@ import "../../chunks/chunk-CQ52GEHY.js";
4
4
  import { Config } from "alinea/core/Config";
5
5
  import { createRecord } from "alinea/core/EntryRecord";
6
6
  import { Field } from "alinea/core/Field";
7
- import { createId } from "alinea/core/Id";
8
7
  import { getRoot } from "alinea/core/Internal";
9
8
  import { ListRow } from "alinea/core/ListRow";
10
9
  import { Type } from "alinea/core/Type";
@@ -22,13 +21,13 @@ import { slugify } from "alinea/core/util/Slugs";
22
21
  import { unreachable } from "alinea/core/util/Types";
23
22
  import { Permission, Policy } from "../Role.js";
24
23
  import { ShaMismatchError } from "../source/ShaMismatchError.js";
24
+ import { OverlaySource } from "../source/OverlaySource.js";
25
25
  import { SourceTransaction } from "../source/Source.js";
26
26
  import { assert } from "../util/Assert.js";
27
27
  import { commitChanges } from "./CommitRequest.js";
28
28
  import { aliasesFromData, aliasUrl } from "./EntryAliases.js";
29
29
  import { EntryUrlConflictError } from "./EntryUrlConflictError.js";
30
30
  var EntryTransaction = class {
31
- #checks = [];
32
31
  #messages = [];
33
32
  #config;
34
33
  #index;
@@ -36,12 +35,16 @@ var EntryTransaction = class {
36
35
  #fileChanges = [];
37
36
  #policy;
38
37
  #urlClaims;
38
+ #workingSource;
39
+ #workingTree;
39
40
  constructor(config, index, source, from, policy = Policy.ALLOW_ALL) {
40
41
  if (index.sha !== from.sha) throw new ShaMismatchError(index.sha, from.sha);
41
42
  this.#config = config;
42
- this.#index = index;
43
+ this.#index = index.clone();
43
44
  this.#tx = new SourceTransaction(source, from);
44
45
  this.#policy = policy;
46
+ this.#workingSource = new OverlaySource(source, from);
47
+ this.#workingTree = from;
45
48
  }
46
49
  get empty() {
47
50
  return this.#messages.length === 0;
@@ -54,11 +57,12 @@ var EntryTransaction = class {
54
57
  workspace,
55
58
  fromSeed,
56
59
  parentId = null,
57
- id = createId(),
60
+ id,
58
61
  insertOrder = "last",
59
62
  status = "published",
60
63
  overwrite = false
61
64
  }) {
65
+ assert(id, "Create mutation is missing an id");
62
66
  const config = this.#config;
63
67
  const index = this.#index;
64
68
  const existing = index.byId(id);
@@ -103,7 +107,6 @@ var EntryTransaction = class {
103
107
  return entry.id === parentId && entry.locale === locale && entry.main;
104
108
  });
105
109
  assert(parent, `Parent not found: ${parentId}`);
106
- this.#checks.push([parent.filePath, parent.fileHash]);
107
110
  this.#policy.assert(Permission.Create, parent);
108
111
  }
109
112
  const siblings = Array.from(
@@ -259,7 +262,6 @@ var EntryTransaction = class {
259
262
  workspace: entry.workspace,
260
263
  locale
261
264
  });
262
- this.#checks.push([entry.filePath, entry.fileHash]);
263
265
  const childrenDir = paths.join(entry.parentDir, path);
264
266
  const filePath = `${childrenDir}${entry.status === "published" ? "" : `.${entry.status}`}.json`;
265
267
  if (entry.status === "published") {
@@ -407,7 +409,6 @@ var EntryTransaction = class {
407
409
  return entry.id === id && entry.locale !== locale;
408
410
  });
409
411
  for (const translation of translations) {
410
- this.#checks.push([translation.filePath, translation.fileHash]);
411
412
  const record = createRecord(
412
413
  {
413
414
  id,
@@ -541,7 +542,6 @@ var EntryTransaction = class {
541
542
  for (const [_, version] of versions) {
542
543
  this.#tx.remove(version.filePath);
543
544
  }
544
- this.#checks.push([entry.filePath, entry.fileHash]);
545
545
  this.#tx.remove(entry.filePath);
546
546
  const record = createRecord({ ...entry, path, data }, "published");
547
547
  const contents = new TextEncoder().encode(JSON.stringify(record, null, 2));
@@ -563,7 +563,6 @@ var EntryTransaction = class {
563
563
  if (version === mainEntry) continue;
564
564
  this.#tx.remove(version.filePath);
565
565
  }
566
- this.#checks.push([mainEntry.filePath, mainEntry.fileHash]);
567
566
  this.#tx.rename(mainEntry.filePath, `${mainEntry.childrenDir}.draft.json`);
568
567
  this.#messages.push(this.#reportOp("unpublish", mainEntry.title));
569
568
  return this;
@@ -578,7 +577,6 @@ var EntryTransaction = class {
578
577
  if (version === mainEntry) continue;
579
578
  this.#tx.remove(version.filePath);
580
579
  }
581
- this.#checks.push([mainEntry.filePath, mainEntry.fileHash]);
582
580
  this.#tx.rename(
583
581
  mainEntry.filePath,
584
582
  `${mainEntry.childrenDir}.archived.json`
@@ -753,7 +751,6 @@ var EntryTransaction = class {
753
751
  if (entry.status === "published")
754
752
  assert(!entry.seeded, `Cannot remove seeded entry ${entry.filePath}`);
755
753
  info = entry;
756
- this.#checks.push([entry.filePath, entry.fileHash]);
757
754
  this.#tx.remove(entry.filePath);
758
755
  if (entry.status !== "draft") {
759
756
  this.#tx.remove(entry.childrenDir);
@@ -810,7 +807,7 @@ var EntryTransaction = class {
810
807
  #reportOp(op, title) {
811
808
  return `(${op}) ${title}`;
812
809
  }
813
- apply(mutations) {
810
+ async apply(mutations) {
814
811
  for (const mutation of mutations) {
815
812
  switch (mutation.op) {
816
813
  case "create":
@@ -843,15 +840,30 @@ var EntryTransaction = class {
843
840
  default:
844
841
  unreachable(mutation);
845
842
  }
843
+ await this.#flush();
846
844
  }
847
845
  }
846
+ async #flush() {
847
+ const { from, into, changes } = await this.#tx.compile(this.#workingTree);
848
+ const batch = {
849
+ fromSha: from.sha,
850
+ changes
851
+ };
852
+ if (batch.changes.length > 0) {
853
+ await Promise.all([
854
+ this.#workingSource.applyChanges(batch),
855
+ this.#index.indexChanges(batch, into)
856
+ ]);
857
+ }
858
+ this.#workingTree = into;
859
+ this.#urlClaims = void 0;
860
+ }
848
861
  async toRequest() {
849
862
  const { from, into, changes } = await this.#tx.compile();
850
863
  return {
851
864
  fromSha: from.sha,
852
865
  intoSha: into.sha,
853
866
  description: this.description(),
854
- checks: this.#checks,
855
867
  changes: this.#fileChanges.concat(commitChanges(changes))
856
868
  };
857
869
  }
@@ -15,7 +15,7 @@ export declare class LocalDB extends WriteableGraph {
15
15
  config: Config;
16
16
  index: EntryIndex;
17
17
  source: Source;
18
- constructor(config: Config, source?: Source);
18
+ constructor(config: Config, source?: Source, index?: EntryIndex);
19
19
  resolve<Query extends GraphQuery>(query: Query): Promise<AnyQueryResult<Query>>;
20
20
  referencesTo(query: EntryReferenceQuery): Promise<EntryReferenceResult>;
21
21
  get sha(): string;
@@ -26,11 +26,10 @@ export declare class LocalDB extends WriteableGraph {
26
26
  sync(): Promise<string>;
27
27
  syncWith(remote: SyncApi): Promise<string>;
28
28
  logEntries(): Promise<void>;
29
- request(mutations: Array<Mutation>, policy?: Policy): Promise<{
29
+ request(mutations: ReadonlyArray<Mutation>, policy?: Policy): Promise<{
30
30
  fromSha: string;
31
31
  intoSha: string;
32
32
  description: string;
33
- checks: [path: string, sha: string][];
34
33
  changes: import("./CommitRequest.js").CommitChange[];
35
34
  }>;
36
35
  mutate(mutations: Array<Mutation>): Promise<{
@@ -4,6 +4,7 @@ import {
4
4
  import "../../chunks/chunk-CQ52GEHY.js";
5
5
 
6
6
  // src/core/db/LocalDB.ts
7
+ import { trace } from "alinea/core/Trace";
7
8
  import { Entry } from "../Entry.js";
8
9
  import { MemorySource } from "../source/MemorySource.js";
9
10
  import { diff } from "../source/Source.js";
@@ -14,10 +15,9 @@ import { EntryTransaction } from "./EntryTransaction.js";
14
15
  import { WriteableGraph } from "./WriteableGraph.js";
15
16
  var limit = pLimit(1);
16
17
  var LocalDB = class extends WriteableGraph {
17
- constructor(config, source = new MemorySource()) {
18
+ constructor(config, source = new MemorySource(), index = new EntryIndex(config)) {
18
19
  super();
19
20
  this.config = config;
20
- const index = new EntryIndex(config);
21
21
  const resolver = new EntryResolver(config, index);
22
22
  this.#resolver = resolver;
23
23
  this.index = index;
@@ -28,7 +28,8 @@ var LocalDB = class extends WriteableGraph {
28
28
  source;
29
29
  #resolver;
30
30
  resolve(query) {
31
- return this.#resolver.resolve(query);
31
+ const span = trace(this.config, "alinea.local_db.resolve");
32
+ return span(() => this.#resolver.resolve(query));
32
33
  }
33
34
  referencesTo(query) {
34
35
  return this.index.referencesTo(query);
@@ -49,23 +50,27 @@ var LocalDB = class extends WriteableGraph {
49
50
  return this.source.getBlobs(shas, options);
50
51
  }
51
52
  async sync() {
52
- return this.index.sync(this.source);
53
+ const span = trace(this.config, "alinea.local_db.sync");
54
+ return span(() => this.index.sync(this.source));
53
55
  }
54
56
  syncWith(remote) {
55
- return limit(async () => {
56
- const batch = await diff(this.source, remote);
57
- const canIndexChanges = this.index.sha === batch.fromSha;
58
- if (batch.changes.length > 0)
59
- await Promise.all([
60
- this.source.applyChanges(batch),
61
- canIndexChanges ? this.index.indexChanges(batch) : void 0
62
- ]);
63
- if (canIndexChanges) {
64
- await this.index.seed(this.source);
65
- return this.index.sha;
66
- }
67
- return this.sync();
68
- });
57
+ const span = trace(this.config, "alinea.local_db.sync_with");
58
+ return span(
59
+ () => limit(async () => {
60
+ const batch = await diff(this.source, remote);
61
+ const canIndexChanges = this.index.sha === batch.fromSha;
62
+ if (batch.changes.length > 0)
63
+ await Promise.all([
64
+ this.source.applyChanges(batch),
65
+ canIndexChanges ? this.index.indexChanges(batch) : void 0
66
+ ]);
67
+ if (canIndexChanges) {
68
+ await this.index.seed(this.source);
69
+ return this.index.sha;
70
+ }
71
+ return this.sync();
72
+ })
73
+ );
69
74
  }
70
75
  async logEntries() {
71
76
  const entries = await this.find({
@@ -99,7 +104,7 @@ var LocalDB = class extends WriteableGraph {
99
104
  from,
100
105
  policy
101
106
  );
102
- tx.apply(mutations);
107
+ await tx.apply(mutations);
103
108
  return tx.toRequest();
104
109
  }
105
110
  async mutate(mutations) {