alinea 2.0.0-preview.13 → 2.0.0-preview.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/dist/.init/content/pages/welcome.json +1 -1
  2. package/dist/.mcp/later/dev.json +1 -1
  3. package/dist/.mcp/online/dev.json +1 -1
  4. package/dist/adapter/next/cms.js +6 -2
  5. package/dist/adapter/next/context.js +1 -4
  6. package/dist/adapter/next/handler.js +12 -1
  7. package/dist/backend/CommitEntries.d.ts +17 -0
  8. package/dist/backend/CommitEntries.js +39 -0
  9. package/dist/backend/Handler.d.ts +9 -0
  10. package/dist/backend/Handler.js +80 -20
  11. package/dist/backend/api/BasicAuth.js +5 -1
  12. package/dist/backend/api/CreateBackend.js +2 -2
  13. package/dist/backend/api/DatabaseApi.js +18 -5
  14. package/dist/backend/api/GithubApi.d.ts +13 -0
  15. package/dist/backend/api/GithubApi.js +170 -30
  16. package/dist/backend/api/database.js +2 -2
  17. package/dist/backend/util/Syncable.d.ts +6 -0
  18. package/dist/backend/util/Syncable.js +3 -1
  19. package/dist/bundled.d.ts +911 -151
  20. package/dist/chunks/{chunk-5H4D5JQL.js → chunk-2PP7WJUJ.js} +1 -11
  21. package/dist/chunks/{chunk-QHHMKJUO.js → chunk-7DDPSWRY.js} +465 -2
  22. package/dist/chunks/{chunk-HOETF6HN.js → chunk-PMGIIYDD.js} +1 -0
  23. package/dist/chunks/{chunk-3QFSGTGF.js → chunk-SCV3TEXM.js} +1 -1
  24. package/dist/chunks/{chunk-UPY2RUZF.js → chunk-TWBREZWC.js} +30 -0
  25. package/dist/cli/Generate.js +20 -7
  26. package/dist/cli/Mcp.js +1 -1
  27. package/dist/cli/Serve.js +1 -1
  28. package/dist/cli/bin.js +1 -1
  29. package/dist/cli/build/BuildEmitter.d.ts +5 -1
  30. package/dist/cli/build/BuildEmitter.js +8 -3
  31. package/dist/cli/generate/CompileConfig.js +10 -6
  32. package/dist/cli/generate/DevDB.d.ts +4 -1
  33. package/dist/cli/generate/DevDB.js +15 -7
  34. package/dist/cli/generate/GenerateDashboard.js +1 -0
  35. package/dist/cli/generate/MediaLocations.d.ts +18 -0
  36. package/dist/cli/generate/MediaLocations.js +69 -0
  37. package/dist/cli/serve/CreateLocalServer.js +1 -0
  38. package/dist/cli/serve/mcp/DevMcp.js +1 -1
  39. package/dist/cloud/CloudRemote.js +21 -7
  40. package/dist/cms.d.ts +4 -2
  41. package/dist/components/Alert.js +2 -2
  42. package/dist/components/AppShell.js +2 -2
  43. package/dist/components/AsyncList.d.ts +52 -0
  44. package/dist/components/AsyncList.js +69 -0
  45. package/dist/components/Breadcrumbs.js +6 -6
  46. package/dist/components/Button.js +4 -4
  47. package/dist/components/Calendar.js +18 -18
  48. package/dist/components/Checkbox.js +10 -10
  49. package/dist/components/CheckboxGroup.js +2 -2
  50. package/dist/components/Collapsible.js +5 -5
  51. package/dist/components/ColorSwatch.js +2 -2
  52. package/dist/components/ColorSwatchPicker.js +4 -4
  53. package/dist/components/ComboBox.d.ts +27 -9
  54. package/dist/components/ComboBox.js +40 -9
  55. package/dist/components/Command.js +13 -13
  56. package/dist/components/ContentCard.js +28 -28
  57. package/dist/components/ContentGrid.js +11 -11
  58. package/dist/components/DataList.js +1 -1
  59. package/dist/components/DateField.d.ts +4 -0
  60. package/dist/components/DateField.js +2 -2
  61. package/dist/components/DatePicker.js +2 -2
  62. package/dist/components/DateRangePicker.js +2 -2
  63. package/dist/components/Dialog.d.ts +14 -3
  64. package/dist/components/Dialog.js +38 -11
  65. package/dist/components/DropZone.js +6 -6
  66. package/dist/components/DropdownMenu.js +8 -8
  67. package/dist/components/Empty.js +4 -4
  68. package/dist/components/Field.d.ts +1 -1
  69. package/dist/components/Field.js +18 -12
  70. package/dist/components/Form.d.ts +23 -0
  71. package/dist/components/Form.js +64 -0
  72. package/dist/components/Link.d.ts +8 -1
  73. package/dist/components/List.js +27 -27
  74. package/dist/components/MediaPreview.js +5 -5
  75. package/dist/components/MultipleSelect.d.ts +13 -8
  76. package/dist/components/MultipleSelect.js +25 -25
  77. package/dist/components/NavRail.js +7 -7
  78. package/dist/components/NumberField.d.ts +5 -1
  79. package/dist/components/NumberField.js +6 -4
  80. package/dist/components/Page.js +6 -6
  81. package/dist/components/Popover.js +2 -2
  82. package/dist/components/PreviewFrame.js +8 -8
  83. package/dist/components/RadioGroup.js +10 -10
  84. package/dist/components/Resizable.js +1 -1
  85. package/dist/components/SearchField.js +8 -8
  86. package/dist/components/Select.d.ts +13 -10
  87. package/dist/components/Select.js +18 -32
  88. package/dist/components/Sheet.js +6 -6
  89. package/dist/components/Sidebar.js +5 -5
  90. package/dist/components/SortableList.js +12 -12
  91. package/dist/components/Spinner.js +4 -4
  92. package/dist/components/Stack.d.ts +20 -0
  93. package/dist/components/Stack.js +45 -0
  94. package/dist/components/Surface.js +1 -1
  95. package/dist/components/Switch.js +5 -5
  96. package/dist/components/Table.d.ts +11 -1
  97. package/dist/components/Table.js +81 -53
  98. package/dist/components/Tabs.js +6 -6
  99. package/dist/components/TagGroup.js +10 -10
  100. package/dist/components/TextField.d.ts +7 -1
  101. package/dist/components/TextField.js +20 -11
  102. package/dist/components/TimeField.d.ts +6 -1
  103. package/dist/components/TimeField.js +51 -26
  104. package/dist/components/Timestamp.js +1 -1
  105. package/dist/components/ToggleGroup.js +2 -2
  106. package/dist/components/Toolbar.js +3 -3
  107. package/dist/components/Tooltip.js +5 -4
  108. package/dist/components/Tree.js +27 -27
  109. package/dist/components/internal/DateControl.js +11 -11
  110. package/dist/components/internal/FormScope.d.ts +2 -0
  111. package/dist/components/internal/FormScope.js +8 -0
  112. package/dist/components/internal/ListBoxGroup.d.ts +12 -0
  113. package/dist/components/internal/ListBoxGroup.js +46 -0
  114. package/dist/components/internal/ListBoxOption.d.ts +2 -2
  115. package/dist/components/internal/ListBoxOption.js +5 -5
  116. package/dist/components/internal/Locale.js +2 -2
  117. package/dist/components/internal/PopoverSurface.js +5 -4
  118. package/dist/components/internal/SelectionCheckbox.js +5 -5
  119. package/dist/components/internal/Slot.js +1 -1
  120. package/dist/components/internal/ToggleButton.js +4 -4
  121. package/dist/components/internal/Trigger.js +2 -2
  122. package/dist/components/internal/useDragDrop.js +2 -2
  123. package/dist/components/types.d.ts +4 -0
  124. package/dist/components.d.ts +3 -0
  125. package/dist/components.js +3 -0
  126. package/dist/core/Client.js +12 -1
  127. package/dist/core/Config.d.ts +6 -0
  128. package/dist/core/Config.js +12 -0
  129. package/dist/core/DashboardEntry.d.ts +19 -0
  130. package/dist/core/DashboardEntry.js +0 -0
  131. package/dist/core/Document.d.ts +2 -2
  132. package/dist/core/Document.js +10 -3
  133. package/dist/core/EntryFields.d.ts +8 -2
  134. package/dist/core/Field.d.ts +5 -0
  135. package/dist/core/Filter.d.ts +19 -2
  136. package/dist/core/Filter.js +5 -0
  137. package/dist/core/Graph.d.ts +10 -2
  138. package/dist/core/Overview.d.ts +74 -5
  139. package/dist/core/Overview.js +1 -0
  140. package/dist/core/Root.js +1 -1
  141. package/dist/core/Scope.d.ts +5 -0
  142. package/dist/core/Scope.js +22 -5
  143. package/dist/core/Type.d.ts +25 -6
  144. package/dist/core/Type.js +25 -7
  145. package/dist/core/db/LocalStore.d.ts +7 -0
  146. package/dist/core/field/CreateField.d.ts +1 -2
  147. package/dist/core/source/Source.d.ts +2 -0
  148. package/dist/core/source/Source.js +11 -0
  149. package/dist/core/util/Async.d.ts +2 -0
  150. package/dist/core/util/Async.js +12 -1
  151. package/dist/core/util/Checks.js +1 -1
  152. package/dist/core/util/Slugs.js +4 -3
  153. package/dist/dashboard/App.css +13 -11
  154. package/dist/dashboard/App.d.ts +1 -0
  155. package/dist/dashboard/App.js +22 -6
  156. package/dist/dashboard/Href.d.ts +37 -0
  157. package/dist/dashboard/Href.js +46 -0
  158. package/dist/dashboard/app/ActivityStatus.js +34 -12
  159. package/dist/dashboard/app/AuthView.js +2 -2
  160. package/dist/dashboard/app/BlockSettings.d.ts +9 -0
  161. package/dist/dashboard/app/BlockSettings.js +16 -0
  162. package/dist/dashboard/app/BlockSheet.js +3 -3
  163. package/dist/dashboard/app/BlockSheetStoryFrame.js +2 -2
  164. package/dist/dashboard/app/CompactField.js +15 -15
  165. package/dist/dashboard/app/CroppedImage.js +2 -2
  166. package/dist/dashboard/app/DashboardLayout.d.ts +1 -1
  167. package/dist/dashboard/app/DashboardLayout.js +12 -0
  168. package/dist/dashboard/app/EntryActions.d.ts +11 -0
  169. package/dist/dashboard/app/EntryActions.js +30 -0
  170. package/dist/dashboard/app/EntryFields.d.ts +15 -2
  171. package/dist/dashboard/app/EntryFields.js +26 -5
  172. package/dist/dashboard/app/EntryHeader.js +17 -7
  173. package/dist/dashboard/app/EntryReferences.js +2 -2
  174. package/dist/dashboard/app/EntrySidebar.js +25 -23
  175. package/dist/dashboard/app/EntrySidebarPreview.js +2 -2
  176. package/dist/dashboard/app/EntryValidationModal.js +3 -3
  177. package/dist/dashboard/app/Explorer.js +84 -45
  178. package/dist/dashboard/app/ExplorerBatchActions.js +4 -4
  179. package/dist/dashboard/app/ExplorerControls.js +5 -5
  180. package/dist/dashboard/app/ExplorerList.js +2 -2
  181. package/dist/dashboard/app/ExplorerModal.js +3 -3
  182. package/dist/dashboard/app/ExplorerOverviewHeader.d.ts +6 -0
  183. package/dist/dashboard/app/ExplorerOverviewHeader.js +28 -0
  184. package/dist/dashboard/app/ExplorerTable.js +7 -7
  185. package/dist/dashboard/app/ExplorerTabs.d.ts +12 -0
  186. package/dist/dashboard/app/ExplorerTabs.js +42 -0
  187. package/dist/dashboard/app/ImageEditor.js +31 -31
  188. package/dist/dashboard/app/LinkPicker.js +3 -3
  189. package/dist/dashboard/app/LocaleMenu.js +9 -9
  190. package/dist/dashboard/app/MediaPreviewCell.js +5 -5
  191. package/dist/dashboard/app/OverviewCell.js +12 -10
  192. package/dist/dashboard/app/RemoveDialog.js +5 -0
  193. package/dist/dashboard/app/SearchBar.js +4 -4
  194. package/dist/dashboard/app/SettingsTab.d.ts +6 -0
  195. package/dist/dashboard/app/SettingsTab.js +27 -0
  196. package/dist/dashboard/app/SidebarTree.js +19 -19
  197. package/dist/dashboard/app/WorkspaceMenu.js +6 -6
  198. package/dist/dashboard/app/WorkspaceRoots.js +102 -98
  199. package/dist/dashboard/app/editor/FileEditor.js +18 -18
  200. package/dist/dashboard/app/pages/EntryPage.js +42 -39
  201. package/dist/dashboard/app/pages/RootPage.js +3 -3
  202. package/dist/dashboard/app/pages/SplashPage.js +94 -79
  203. package/dist/dashboard/app/pages/UsersPage.js +9 -9
  204. package/dist/dashboard/app/ui/DashboardModal.js +14 -14
  205. package/dist/dashboard/atoms/activity.d.ts +1 -0
  206. package/dist/dashboard/atoms/activity.js +8 -5
  207. package/dist/dashboard/atoms/config.d.ts +2 -0
  208. package/dist/dashboard/atoms/dashboard.d.ts +11 -0
  209. package/dist/dashboard/atoms/dashboard.js +8 -0
  210. package/dist/dashboard/atoms/editor.d.ts +7 -1
  211. package/dist/dashboard/atoms/editor.js +14 -2
  212. package/dist/dashboard/atoms/explorer.d.ts +15 -0
  213. package/dist/dashboard/atoms/explorer.js +43 -14
  214. package/dist/dashboard/atoms/graph.d.ts +5 -0
  215. package/dist/dashboard/atoms/graph.js +26 -1
  216. package/dist/dashboard/atoms/nav.d.ts +16 -10
  217. package/dist/dashboard/atoms/nav.js +96 -36
  218. package/dist/dashboard/atoms/overview.d.ts +26 -4
  219. package/dist/dashboard/atoms/overview.js +41 -12
  220. package/dist/dashboard/atoms/root.js +7 -1
  221. package/dist/dashboard/boot/ActivityEvent.d.ts +1 -1
  222. package/dist/dashboard/boot/Boot.js +6 -0
  223. package/dist/dashboard/boot/DashboardWorker.d.ts +6 -0
  224. package/dist/dashboard/boot/DashboardWorker.js +89 -8
  225. package/dist/dashboard/boot/LoadWorker.js +3 -0
  226. package/dist/dashboard/boot/SyncEvent.d.ts +7 -0
  227. package/dist/dashboard/boot/SyncEvent.js +14 -0
  228. package/dist/dashboard/boot/WorkerDB.d.ts +1 -0
  229. package/dist/dashboard/boot/WorkerDB.js +3 -0
  230. package/dist/dashboard/cms/EntryTable.d.ts +4 -3
  231. package/dist/dashboard/cms/EntryTable.js +2 -2
  232. package/dist/dashboard/cms/ViewProps.d.ts +3 -2
  233. package/dist/dashboard/cms/hooks.d.ts +34 -18
  234. package/dist/dashboard/cms/hooks.js +43 -18
  235. package/dist/dashboard/contexts.d.ts +3 -2
  236. package/dist/dashboard/contexts.js +3 -2
  237. package/dist/dashboard/hook/UseEntryEditor.d.ts +1 -1
  238. package/dist/dashboard/hook/UseEntryEditor.js +7 -3
  239. package/dist/dashboard/icons.d.ts +1 -0
  240. package/dist/dashboard/icons.js +15 -0
  241. package/dist/database/BrowserEntryStore.js +35 -13
  242. package/dist/database/DatabaseTables.js +1 -1
  243. package/dist/database/EntryDatabase.d.ts +10 -1
  244. package/dist/database/EntryDatabase.js +31 -10
  245. package/dist/database/EntryStore.js +1 -11
  246. package/dist/database/entry/EntryTable.d.ts +5 -3
  247. package/dist/database/entry/EntryTable.js +25 -8
  248. package/dist/database/query/Condition.d.ts +4 -1
  249. package/dist/database/query/Condition.js +16 -5
  250. package/dist/database/query/EntryQuery.js +59 -7
  251. package/dist/database/query/Relation.d.ts +13 -1
  252. package/dist/database/query/Relation.js +19 -1
  253. package/dist/database/query/Search.d.ts +7 -1
  254. package/dist/database/query/Search.js +54 -9
  255. package/dist/database/sync/EntrySyncer.d.ts +2 -0
  256. package/dist/database/sync/EntrySyncer.js +4 -2
  257. package/dist/database/sync/Ingest.d.ts +2 -1
  258. package/dist/database/sync/Ingest.js +20 -6
  259. package/dist/field/code/CodeField.view.js +12 -12
  260. package/dist/field/link/EntryLink.d.ts +4 -5
  261. package/dist/field/link/FileLink.d.ts +2 -2
  262. package/dist/field/link/ImageLink.d.ts +2 -5
  263. package/dist/field/link/ImageLink.js +0 -1
  264. package/dist/field/link/LinkField.view.js +18 -15
  265. package/dist/field/list/ListField.view.js +50 -35
  266. package/dist/field/metadata/MetadataField.view.js +14 -14
  267. package/dist/field/richtext/RichTextBlock.js +39 -35
  268. package/dist/field/richtext/RichTextExtensions.js +23 -23
  269. package/dist/field/richtext/RichTextField.view.js +6 -6
  270. package/dist/field/richtext/RichTextInsertMenu.js +5 -5
  271. package/dist/field/richtext/RichTextToolbar.js +26 -1
  272. package/dist/field/richtext/Toolbar.d.ts +9 -1
  273. package/dist/field/richtext/Toolbar.js +12 -2
  274. package/dist/field/select/SelectField.view.js +1 -0
  275. package/dist/index.css +174 -23
  276. package/dist/index.d.ts +1 -0
  277. package/dist/index.js +3 -1
  278. package/dist/next.d.ts +2 -0
  279. package/dist/next.edge.d.ts +2 -0
  280. package/dist/picker/entry/EntryPicker.d.ts +3 -1
  281. package/dist/picker/entry/EntryPicker.js +15 -1
  282. package/dist/preview/react.js +3 -0
  283. package/dist/query.d.ts +26 -14
  284. package/dist/query.js +4 -0
  285. package/dist/types.d.ts +8 -5
  286. package/dist/ui/Stack.d.ts +4 -2
  287. package/docs/ai-setup.md +3 -3
  288. package/docs/cli.md +4 -2
  289. package/docs/components/combo-box.md +105 -16
  290. package/docs/components/date-field.md +1 -1
  291. package/docs/components/dialog.md +51 -0
  292. package/docs/components/form.md +67 -0
  293. package/docs/components/link.md +10 -0
  294. package/docs/components/multiple-select.md +9 -17
  295. package/docs/components/number-field.md +5 -1
  296. package/docs/components/select.md +9 -20
  297. package/docs/components/stack.md +42 -0
  298. package/docs/components/table.md +83 -0
  299. package/docs/components/text-field.md +9 -1
  300. package/docs/components/time-field.md +5 -1
  301. package/docs/components.md +39 -1
  302. package/docs/configuration.md +3 -1
  303. package/docs/custom-fields.md +135 -7
  304. package/docs/deploy/self-host.md +2 -0
  305. package/docs/deploy.md +7 -4
  306. package/docs/fields/image.md +0 -1
  307. package/docs/fields/list.md +19 -0
  308. package/docs/fields/object.md +5 -3
  309. package/docs/fields/rich-text.md +31 -0
  310. package/docs/index.md +8 -6
  311. package/docs/instant-publishing.md +23 -2
  312. package/docs/overviews.md +128 -4
  313. package/docs/query/filtering.md +2 -0
  314. package/docs/query/related.md +58 -9
  315. package/docs/query.md +52 -1
  316. package/docs/quickstart.md +5 -5
  317. package/docs/schema/document.md +2 -0
  318. package/docs/schema/type.md +3 -1
  319. package/docs/upgrading.md +100 -19
  320. package/llms-full.txt +1022 -125
  321. package/package.json +1 -1
  322. package/readme.md +2 -2
  323. package/dist/chunks/{chunk-4ZA6KZHS.js → chunk-6FQ5IMNA.js} +3 -3
@@ -1,3 +1,6 @@
1
+ import {
2
+ pLimit
3
+ } from "../../chunks/chunk-U5Z2VE7Z.js";
1
4
  import "../../chunks/chunk-CQ52GEHY.js";
2
5
 
3
6
  // src/backend/api/GithubApi.ts
@@ -26,10 +29,12 @@ var GithubApi = class extends GithubSource {
26
29
  }
27
30
  /**
28
31
  * Commit through GraphQL, which also answers the content sha before and
29
- * after: a save spends none of the REST rate limit.
32
+ * after: a save spends none of the REST rate limit. A configured author
33
+ * commits through REST, writing only the commit there.
30
34
  */
31
35
  async #write(request) {
32
- const { oid: currentCommit, contentSha: currentSha } = await this.#getLatestCommit();
36
+ const current = await this.#getLatestCommit();
37
+ const currentSha = current.contentSha;
33
38
  if (currentSha !== request.fromSha)
34
39
  throw new ShaMismatchError(currentSha, request.fromSha);
35
40
  const { user } = request;
@@ -39,8 +44,13 @@ var GithubApi = class extends GithubSource {
39
44
 
40
45
  Co-authored-by: ${user.name} <${user.email}>`;
41
46
  }
42
- const newCommit = await this.#applyChangesToRepo(
43
- currentCommit,
47
+ const newCommit = this.#options.author ? await this.#commitThroughRest(
48
+ current,
49
+ request.changes,
50
+ commitMessage,
51
+ this.#options.author
52
+ ) : await this.#applyChangesToRepo(
53
+ current.oid,
44
54
  request.changes,
45
55
  commitMessage
46
56
  );
@@ -94,6 +104,7 @@ Co-authored-by: ${user.name} <${user.email}>`;
94
104
  if (requestCount === maxRequests) break;
95
105
  requestCount++;
96
106
  const versions = [...queue].filter((v) => !seen.has(v));
107
+ if (versions.length === 0) break;
97
108
  for (const v of versions) seen.add(v);
98
109
  queue.length = 0;
99
110
  const aliasMap = versions.map((v, idx) => ({
@@ -101,9 +112,10 @@ Co-authored-by: ${user.name} <${user.email}>`;
101
112
  version: v,
102
113
  path: join(rootDir, v)
103
114
  }));
115
+ const params = aliasMap.map(({ alias }) => `$${alias}: String!`).join(", ");
104
116
  const query = aliasMap.map(
105
- ({ alias, path }) => `
106
- ${alias}: history(path: "${path}", first: 100) {
117
+ ({ alias }) => `
118
+ ${alias}: history(path: $${alias}, first: 100) {
107
119
  nodes {
108
120
  oid
109
121
  committedDate
@@ -113,14 +125,16 @@ Co-authored-by: ${user.name} <${user.email}>`;
113
125
  }`
114
126
  ).join("");
115
127
  const gql = `
116
- query GetFileHistory($owner: String!, $repo: String!, $branch: String!) {
128
+ query GetFileHistory($owner: String!, $repo: String!, $branch: String!, ${params}) {
117
129
  repository(owner: $owner, name: $repo) {
118
130
  ref(qualifiedName: $branch) {
119
131
  target { ... on Commit {${query}} }
120
132
  }
121
133
  }
122
134
  }`;
123
- const result = await this.#graphQL(gql, { owner, repo, branch }, authToken);
135
+ const variables = { owner, repo, branch };
136
+ for (const { alias, path } of aliasMap) variables[alias] = path;
137
+ const result = await this.#graphQL(gql, variables, authToken);
124
138
  for (const { alias, version, path } of aliasMap) {
125
139
  const commits = result.data.repository.ref.target[alias]?.nodes || [];
126
140
  if (!commits.length) continue;
@@ -216,37 +230,147 @@ Co-authored-by: ${user.name} <${user.email}>`;
216
230
  throw error;
217
231
  });
218
232
  }
233
+ /**
234
+ * Write a commit with its own author through the REST API: a tree on the
235
+ * current one, the commit, and the branch moved to it. Uploaded files are
236
+ * stored as blobs first, entries are passed in the tree as text.
237
+ */
238
+ async #commitThroughRest(parent, changes, commitMessage, author) {
239
+ const { branch } = this.#options;
240
+ if (!parent.treeOid)
241
+ throw new Error(`GitHub did not answer the tree of ${parent.oid}`);
242
+ const limit = pLimit(4);
243
+ let entries = await Promise.all(
244
+ changes.map((change) => limit(() => this.#treeEntry(change)))
245
+ );
246
+ const writeTree = () => this.#rest("POST", "git/trees", {
247
+ base_tree: parent.treeOid,
248
+ tree: entries
249
+ });
250
+ let tree;
251
+ try {
252
+ tree = await writeTree();
253
+ } catch (error) {
254
+ const removed = entries.filter((entry) => entry.sha === null);
255
+ if (!(error instanceof HttpError && error.code === 422) || !removed.length)
256
+ throw error;
257
+ const existing = await this.#existingPaths(
258
+ parent.oid,
259
+ removed.map((entry) => entry.path)
260
+ );
261
+ if (existing.size === removed.length) throw error;
262
+ entries = entries.filter(
263
+ (entry) => entry.sha !== null || existing.has(entry.path)
264
+ );
265
+ tree = await writeTree();
266
+ }
267
+ assert(isRecord(tree) && typeof tree.sha === "string");
268
+ const commit = await this.#rest("POST", "git/commits", {
269
+ message: commitMessage,
270
+ tree: tree.sha,
271
+ parents: [parent.oid],
272
+ author
273
+ });
274
+ assert(isRecord(commit) && typeof commit.sha === "string");
275
+ const ref = `git/refs/heads/${branch.split("/").map(encodeURIComponent).join("/")}`;
276
+ try {
277
+ await this.#rest("PATCH", ref, { sha: commit.sha, force: false });
278
+ } catch (error) {
279
+ if (error instanceof HttpError && error.code === 422 && /fast.forward/i.test(error.message))
280
+ throw new HttpError(409, error.message);
281
+ throw error;
282
+ }
283
+ return this.#getCommit(commit.sha);
284
+ }
285
+ /** The paths that exist in a commit. */
286
+ async #existingPaths(commit, paths) {
287
+ const { owner, repo, authToken } = this.#options;
288
+ const params = paths.map((_, i) => `$p${i}: String!`).join(", ");
289
+ const fields = paths.map((_, i) => `p${i}: object(expression: $p${i}) { oid }`).join("\n");
290
+ const variables = { owner, repo };
291
+ for (const [i, path] of paths.entries())
292
+ variables[`p${i}`] = `${commit}:${path}`;
293
+ const result = await this.#graphQL(
294
+ `query ExistingPaths($owner: String!, $repo: String!, ${params}) {
295
+ repository(owner: $owner, name: $repo) {
296
+ ${fields}
297
+ }
298
+ }`,
299
+ variables,
300
+ authToken
301
+ );
302
+ const repository = result.data.repository;
303
+ return new Set(paths.filter((_, i) => isRecord(repository[`p${i}`])));
304
+ }
305
+ async #treeEntry(change) {
306
+ const path = this.#changePath(change);
307
+ const mode = "100644";
308
+ switch (change.op) {
309
+ case "addContent":
310
+ return { path, mode, type: "blob", content: change.contents };
311
+ case "uploadFile": {
312
+ const blob = await this.#rest("POST", "git/blobs", {
313
+ content: await this.#fetchUploadedContent(change.url),
314
+ encoding: "base64"
315
+ });
316
+ assert(isRecord(blob) && typeof blob.sha === "string");
317
+ return { path, mode, type: "blob", sha: blob.sha };
318
+ }
319
+ case "deleteContent":
320
+ case "removeFile":
321
+ return { path, mode, type: "blob", sha: null };
322
+ }
323
+ }
324
+ /** The repository path a change writes or removes. */
325
+ #changePath(change) {
326
+ switch (change.op) {
327
+ case "addContent":
328
+ case "deleteContent":
329
+ return repositoryPath(this.contentLocation, change.path);
330
+ case "uploadFile":
331
+ case "removeFile":
332
+ return repositoryPath(this.#options.rootDir, change.location);
333
+ }
334
+ }
335
+ async #rest(method, path, body) {
336
+ const { owner, repo, authToken } = this.#options;
337
+ const response = await this.githubFetch(
338
+ `https://api.github.com/repos/${owner}/${repo}/${path}`,
339
+ {
340
+ method,
341
+ headers: {
342
+ Authorization: `Bearer ${authToken}`,
343
+ "Content-Type": "application/json"
344
+ },
345
+ body: JSON.stringify(body)
346
+ }
347
+ );
348
+ if (!response.ok)
349
+ throw new HttpError(
350
+ response.status,
351
+ `GitHub ${method} ${path} failed: ${await response.text()}`
352
+ );
353
+ return response.json();
354
+ }
219
355
  async #processChanges(changes) {
220
- const { rootDir } = this.#options;
221
356
  const additions = Array();
222
357
  const deletions = Array();
223
358
  for (const change of changes) {
359
+ const path = this.#changePath(change);
224
360
  switch (change.op) {
225
- case "addContent": {
226
- additions.push({
227
- path: repositoryPath(this.contentLocation, change.path),
228
- contents: btoa(change.contents)
229
- });
361
+ case "addContent":
362
+ additions.push({ path, contents: btoa(change.contents) });
230
363
  break;
231
- }
232
- case "uploadFile": {
233
- const file = repositoryPath(rootDir, change.location);
364
+ case "uploadFile":
234
365
  additions.push({
235
- path: file,
366
+ path,
236
367
  contents: await this.#fetchUploadedContent(change.url)
237
368
  });
238
369
  break;
239
- }
240
- case "deleteContent": {
241
- const file = repositoryPath(this.contentLocation, change.path);
242
- deletions.push({ path: file });
243
- break;
244
- }
245
- case "removeFile": {
246
- const file = repositoryPath(rootDir, change.location);
247
- deletions.push({ path: file });
370
+ case "deleteContent":
371
+ case "removeFile":
372
+ deletions.push({ path });
248
373
  break;
249
- }
250
374
  }
251
375
  }
252
376
  return { additions, deletions };
@@ -267,6 +391,20 @@ Co-authored-by: ${user.name} <${user.email}>`;
267
391
  authToken
268
392
  ).then((result) => commitState(result.data.repository.ref.target));
269
393
  }
394
+ async #getCommit(oid) {
395
+ const { owner, repo, authToken } = this.#options;
396
+ return this.#graphQL(
397
+ `query GetCommit($owner: String!, $repo: String!, $oid: GitObjectID!, $content: String!) {
398
+ repository(owner: $owner, name: $repo) {
399
+ object(oid: $oid) {
400
+ ${commitStateFields}
401
+ }
402
+ }
403
+ }`,
404
+ { owner, repo, oid, content: this.contentLocation },
405
+ authToken
406
+ ).then((result) => commitState(result.data.repository.object));
407
+ }
270
408
  async #fetchUploadedContent(url) {
271
409
  const response = await fetch(url);
272
410
  if (!response.ok)
@@ -277,12 +415,14 @@ Co-authored-by: ${user.name} <${user.email}>`;
277
415
  function repositoryPath(...segments) {
278
416
  return relative("/", join("/", ...segments));
279
417
  }
280
- var commitStateFields = `oid ... on Commit { file(path: $content) { oid } }`;
418
+ var commitStateFields = `oid ... on Commit { tree { oid } file(path: $content) { oid } }`;
281
419
  function commitState(commit) {
282
420
  assert(isRecord(commit) && typeof commit.oid === "string");
421
+ const tree = commit.tree;
422
+ const treeOid = isRecord(tree) && typeof tree.oid === "string" ? tree.oid : void 0;
283
423
  const file = commit.file;
284
424
  const contentSha = isRecord(file) && typeof file.oid === "string" ? file.oid : ReadonlyTree.EMPTY.sha;
285
- return { oid: commit.oid, contentSha };
425
+ return { oid: commit.oid, treeOid, contentSha };
286
426
  }
287
427
  function isMissingFile(error) {
288
428
  return isRecord(error) && error.type === "NOT_FOUND" && Array.isArray(error.path) && error.path.at(-1) === "file";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  driver_exports
3
- } from "../../chunks/chunk-4ZA6KZHS.js";
4
- import "../../chunks/chunk-VA6NC3MI.js";
3
+ } from "../../chunks/chunk-6FQ5IMNA.js";
5
4
  import "../../chunks/chunk-A2I6MCCS.js";
5
+ import "../../chunks/chunk-VA6NC3MI.js";
6
6
  import "../../chunks/chunk-RAR44T5O.js";
7
7
  import "../../chunks/chunk-PTYZMV5G.js";
8
8
  import "../../chunks/chunk-N47Y62CI.js";
@@ -1 +1,7 @@
1
+ /**
2
+ * How long a read waits for a sync with the remote. A slower or hung remote
3
+ * must not hold up every read: past this the read answers with the content
4
+ * it has, while the sync carries on.
5
+ */
6
+ export declare const syncWait = 10000;
1
7
  export declare function createThrottledSync(): (sync: () => Promise<unknown>, interval?: number) => Promise<void>;
@@ -4,6 +4,7 @@ import {
4
4
  import "../../chunks/chunk-CQ52GEHY.js";
5
5
 
6
6
  // src/backend/util/Syncable.ts
7
+ var syncWait = 1e4;
7
8
  function createThrottledSync() {
8
9
  let lastSync = 0;
9
10
  const limit = pLimit(1);
@@ -18,5 +19,6 @@ function createThrottledSync() {
18
19
  };
19
20
  }
20
21
  export {
21
- createThrottledSync
22
+ createThrottledSync,
23
+ syncWait
22
24
  };