alinea 2.0.0-preview.12 → 2.0.0-preview.13
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.
- package/dist/.init/content/pages/welcome.json +1 -1
- package/dist/.mcp/later/dev.json +1 -1
- package/dist/.mcp/online/dev.json +1 -1
- package/dist/adapter/next/cms.js +16 -8
- package/dist/adapter/next/dashboardUrl.d.ts +6 -0
- package/dist/adapter/next/dashboardUrl.js +13 -0
- package/dist/adapter/next/syncCheck.d.ts +15 -2
- package/dist/adapter/next/syncCheck.js +26 -9
- package/dist/backend/Handler.js +5 -1
- package/dist/backend/api/BlobStore.d.ts +21 -0
- package/dist/backend/api/BlobStore.js +63 -0
- package/dist/backend/api/CreateBackend.js +10 -8
- package/dist/backend/api/DatabaseApi.d.ts +4 -1
- package/dist/backend/api/DatabaseApi.js +88 -4
- package/dist/backend/api/GithubApi.js +47 -24
- package/dist/backend/api/database.js +6 -6
- package/dist/bundled.d.ts +676 -280
- package/dist/chunks/{bun-sqlite-5SHXN3AT.js → bun-sqlite-RI6BYU5S.js} +3 -3
- package/dist/chunks/{chunk-OHGAVV4G.js → chunk-3QFSGTGF.js} +2 -3
- package/dist/chunks/{chunk-QJGGYF6E.js → chunk-4ZA6KZHS.js} +5 -5
- package/dist/chunks/{chunk-ASYKC6IE.js → chunk-5H4D5JQL.js} +1 -1
- package/dist/chunks/{chunk-ETWOY4AL.js → chunk-A2I6MCCS.js} +1 -1
- package/dist/chunks/{chunk-W5AXN7GH.js → chunk-CYVOGRSD.js} +1 -2
- package/dist/chunks/{chunk-TIJTT5ZI.js → chunk-G43W7AME.js} +1 -0
- package/dist/chunks/{chunk-RE523BQ7.js → chunk-HOETF6HN.js} +5829 -5830
- package/dist/chunks/{chunk-AIU6CMVT.js → chunk-PTYZMV5G.js} +1 -1
- package/dist/chunks/{chunk-JOF663EG.js → chunk-QHHMKJUO.js} +1 -1
- package/dist/chunks/{chunk-66YZ47TY.js → chunk-RAR44T5O.js} +1 -1
- package/dist/chunks/{chunk-QSN6RH6X.js → chunk-VA6NC3MI.js} +1 -1
- package/dist/chunks/{node-sqlite-XVNTNMCY.js → node-sqlite-C7VJ2NSN.js} +3 -3
- package/dist/cli/Mcp.js +1 -1
- package/dist/cli/Serve.js +1 -1
- package/dist/cli/bin.js +2 -3
- package/dist/cli/serve/mcp/DevMcp.js +1 -1
- package/dist/cli/serve/mcp/McpTools.js +29 -1
- package/dist/cloud/CloudRemote.js +1 -1
- package/dist/components/Alert.js +2 -2
- package/dist/components/AppShell.js +2 -2
- package/dist/components/Badge.d.ts +3 -1
- package/dist/components/Badge.js +5 -3
- package/dist/components/Breadcrumbs.js +7 -7
- package/dist/components/Button.d.ts +6 -1
- package/dist/components/Button.js +16 -7
- package/dist/components/Calendar.js +20 -20
- package/dist/components/Checkbox.js +7 -7
- package/dist/components/CheckboxGroup.js +4 -4
- package/dist/components/Code.js +2 -2
- package/dist/components/Collapsible.js +3 -3
- package/dist/components/ColorSwatch.js +2 -2
- package/dist/components/ColorSwatchPicker.js +2 -2
- package/dist/components/ComboBox.js +7 -7
- package/dist/components/Command.js +14 -13
- package/dist/components/ContentCard.d.ts +3 -1
- package/dist/components/ContentCard.js +46 -24
- package/dist/components/ContentGrid.js +17 -17
- package/dist/components/DataList.js +2 -2
- package/dist/components/DateField.js +2 -2
- package/dist/components/DatePicker.js +2 -2
- package/dist/components/DateRangePicker.js +2 -2
- package/dist/components/Dialog.js +7 -7
- package/dist/components/DropZone.js +6 -6
- package/dist/components/DropdownMenu.js +9 -9
- package/dist/components/Empty.js +5 -5
- package/dist/components/Field.js +8 -8
- package/dist/components/List.js +27 -27
- package/dist/components/MediaPreview.js +5 -5
- package/dist/components/MultipleSelect.js +22 -22
- package/dist/components/NavRail.js +4 -4
- package/dist/components/NumberField.js +4 -4
- package/dist/components/Page.js +3 -3
- package/dist/components/Popover.js +2 -2
- package/dist/components/PreviewFrame.js +9 -9
- package/dist/components/RadioGroup.js +9 -9
- package/dist/components/Resizable.js +4 -4
- package/dist/components/SearchField.d.ts +6 -1
- package/dist/components/SearchField.js +5 -3
- package/dist/components/Select.js +13 -13
- package/dist/components/Sheet.d.ts +50 -0
- package/dist/components/Sheet.js +202 -0
- package/dist/components/Sidebar.js +4 -4
- package/dist/components/SortableList.d.ts +34 -30
- package/dist/components/SortableList.js +161 -264
- package/dist/components/Spinner.js +2 -2
- package/dist/components/Surface.js +2 -2
- package/dist/components/Switch.js +8 -8
- package/dist/components/Table.d.ts +3 -1
- package/dist/components/Table.js +57 -46
- package/dist/components/Tabs.js +5 -5
- package/dist/components/TagGroup.js +8 -8
- package/dist/components/TextField.js +10 -10
- package/dist/components/TimeField.js +6 -6
- package/dist/components/Timestamp.js +1 -1
- package/dist/components/ToggleGroup.js +2 -2
- package/dist/components/Toolbar.js +2 -2
- package/dist/components/Tooltip.js +5 -5
- package/dist/components/Tree.js +24 -24
- package/dist/components/internal/DateControl.js +12 -12
- package/dist/components/internal/ListBoxOption.js +6 -6
- package/dist/components/internal/Locale.js +2 -2
- package/dist/components/internal/PopoverSurface.js +4 -4
- package/dist/components/internal/SelectionCheckbox.js +5 -5
- package/dist/components/internal/Slot.js +1 -1
- package/dist/components/internal/ToggleButton.js +3 -3
- package/dist/components/internal/Trigger.js +2 -2
- package/dist/components/internal/sortableDrag.d.ts +19 -0
- package/dist/components/internal/sortableDrag.js +175 -0
- package/dist/components/internal/useDragDrop.js +2 -2
- package/dist/components.d.ts +1 -0
- package/dist/components.js +1 -0
- package/dist/core/Config.d.ts +6 -1
- package/dist/core/Document.d.ts +9 -1
- package/dist/core/Document.js +31 -6
- package/dist/core/Overview.d.ts +2 -4
- package/dist/core/TextDoc.d.ts +4 -0
- package/dist/core/Validation.d.ts +0 -1
- package/dist/core/Validation.js +0 -6
- package/dist/core/field/RichTextField.js +23 -4
- package/dist/core/media/MediaTypes.js +1 -1
- package/dist/core/source/GithubSource.d.ts +4 -0
- package/dist/core/source/GithubSource.js +127 -25
- package/dist/core/source/GithubUsage.d.ts +21 -0
- package/dist/core/source/GithubUsage.js +47 -0
- package/dist/dashboard/App.d.ts +2 -2
- package/dist/dashboard/App.js +5 -3
- package/dist/dashboard/ViewKeys.d.ts +1 -0
- package/dist/dashboard/ViewKeys.js +1 -0
- package/dist/dashboard/app/ActivityStatus.js +7 -7
- package/dist/dashboard/app/AuthView.js +5 -5
- package/dist/dashboard/app/BlockSheet.d.ts +50 -0
- package/dist/dashboard/app/BlockSheet.js +207 -0
- package/dist/dashboard/app/BlockSheetStoryFrame.d.ts +6 -0
- package/dist/dashboard/app/BlockSheetStoryFrame.js +29 -0
- package/dist/dashboard/app/CompactField.js +16 -16
- package/dist/dashboard/app/DashboardLayout.js +3 -1
- package/dist/dashboard/app/EntryFields.js +2 -2
- package/dist/dashboard/app/EntryHeader.js +33 -34
- package/dist/dashboard/app/EntryReferences.js +7 -7
- package/dist/dashboard/app/EntrySidebar.js +15 -15
- package/dist/dashboard/app/EntrySidebarPreview.js +3 -3
- package/dist/dashboard/app/EntryStatusIcon.d.ts +23 -0
- package/dist/dashboard/app/EntryStatusIcon.js +58 -0
- package/dist/dashboard/app/EntryValidationModal.d.ts +6 -5
- package/dist/dashboard/app/EntryValidationModal.js +20 -20
- package/dist/dashboard/app/Explorer.d.ts +3 -1
- package/dist/dashboard/app/Explorer.js +48 -44
- package/dist/dashboard/app/ExplorerBatchActions.d.ts +3 -1
- package/dist/dashboard/app/ExplorerBatchActions.js +68 -43
- package/dist/dashboard/app/ExplorerCards.js +7 -1
- package/dist/dashboard/app/ExplorerControls.js +6 -6
- package/dist/dashboard/app/ExplorerList.js +5 -5
- package/dist/dashboard/app/ExplorerModal.js +2 -2
- package/dist/dashboard/app/ExplorerTable.js +11 -8
- package/dist/dashboard/app/ImageEditor.js +28 -28
- package/dist/dashboard/app/ImagePicker.js +3 -13
- package/dist/dashboard/app/LinkPicker.js +28 -26
- package/dist/dashboard/app/LocaleMenu.js +4 -4
- package/dist/dashboard/app/MediaPreviewCell.js +1 -1
- package/dist/dashboard/app/MoveConfirmDialog.d.ts +5 -0
- package/dist/dashboard/app/MoveConfirmDialog.js +79 -0
- package/dist/dashboard/app/OverviewCell.js +8 -13
- package/dist/dashboard/app/PendingUploadsView.js +3 -3
- package/dist/dashboard/app/ReadOnlyBadge.d.ts +1 -1
- package/dist/dashboard/app/RemoveDialog.d.ts +17 -0
- package/dist/dashboard/app/{DeleteDialog.js → RemoveDialog.js} +67 -27
- package/dist/dashboard/app/RemoveRedirect.d.ts +7 -0
- package/dist/dashboard/app/RemoveRedirect.js +123 -0
- package/dist/dashboard/app/SearchBar.d.ts +9 -0
- package/dist/dashboard/app/SearchBar.js +31 -0
- package/dist/dashboard/app/SidebarTree.js +27 -72
- package/dist/dashboard/app/WorkspaceMenu.js +7 -7
- package/dist/dashboard/app/WorkspaceRoots.js +12 -12
- package/dist/dashboard/app/editor/FileEditor.js +14 -14
- package/dist/dashboard/app/modals/CreateEntry.js +3 -3
- package/dist/dashboard/app/pages/EntryPage.d.ts +2 -1
- package/dist/dashboard/app/pages/EntryPage.js +34 -15
- package/dist/dashboard/app/pages/SplashPage.js +37 -37
- package/dist/dashboard/app/pages/UsersPage.js +12 -12
- package/dist/dashboard/app/ui/DashboardModal.js +15 -14
- package/dist/dashboard/atoms/dashboard.js +1 -1
- package/dist/dashboard/atoms/entry.js +1 -14
- package/dist/dashboard/atoms/entryTable.js +1 -0
- package/dist/dashboard/atoms/explorer.d.ts +4 -0
- package/dist/dashboard/atoms/explorer.js +19 -7
- package/dist/dashboard/atoms/graph.js +1 -1
- package/dist/dashboard/atoms/link.js +5 -2
- package/dist/dashboard/atoms/move.d.ts +5 -0
- package/dist/dashboard/atoms/move.js +29 -3
- package/dist/dashboard/atoms/moveConfirmation.d.ts +34 -0
- package/dist/dashboard/atoms/moveConfirmation.js +95 -0
- package/dist/dashboard/atoms/nav.d.ts +9 -5
- package/dist/dashboard/atoms/nav.js +120 -45
- package/dist/dashboard/atoms/overview.d.ts +6 -5
- package/dist/dashboard/atoms/overview.js +2 -16
- package/dist/dashboard/atoms/remove.d.ts +112 -0
- package/dist/dashboard/atoms/remove.js +268 -0
- package/dist/dashboard/atoms/root.d.ts +2 -2
- package/dist/dashboard/atoms/root.js +41 -26
- package/dist/dashboard/atoms/user.js +1 -1
- package/dist/dashboard/atoms/utils.js +1 -1
- package/dist/dashboard/boot/Boot.js +25 -17
- package/dist/dashboard/boot/DashboardWorker.d.ts +1 -0
- package/dist/dashboard/boot/DashboardWorker.js +10 -46
- package/dist/dashboard/boot/LoadWorker.js +17 -10
- package/dist/dashboard/cms/EntryTable.js +8 -5
- package/dist/database/BrowserEntryStore.d.ts +12 -62
- package/dist/database/BrowserEntryStore.js +97 -196
- package/dist/database/DatabaseTables.js +2 -2
- package/dist/database/EntryDatabase.js +21 -1
- package/dist/database/EntryTransaction.js +0 -35
- package/dist/database/EntryUrlAliases.d.ts +4 -0
- package/dist/database/EntryUrlAliases.js +16 -10
- package/dist/database/driver/RuntimeDatabase.js +3 -3
- package/dist/database/driver/WasmDatabase.d.ts +5 -18
- package/dist/database/driver/WasmDatabase.js +15 -16
- package/dist/database/entry/EntryTable.js +2 -2
- package/dist/database/query/EntryQuery.js +1 -1
- package/dist/database/query/Relation.js +1 -1
- package/dist/database/query/Search.js +2 -2
- package/dist/field/code/CodeField.view.js +16 -16
- package/dist/field/link/EntryLink.d.ts +4 -4
- package/dist/field/link/FileLink.d.ts +4 -4
- package/dist/field/link/ImageLink.d.ts +4 -4
- package/dist/field/link/Link.d.ts +1 -1
- package/dist/field/link/LinkField.d.ts +5 -0
- package/dist/field/link/LinkField.view.js +244 -370
- package/dist/field/link/UrlLink.d.ts +4 -4
- package/dist/field/list/ListField.d.ts +2 -0
- package/dist/field/list/ListField.view.d.ts +15 -0
- package/dist/field/list/ListField.view.js +343 -301
- package/dist/field/metadata/MetadataField.d.ts +17 -7
- package/dist/field/metadata/MetadataField.js +14 -3
- package/dist/field/metadata/MetadataField.view.d.ts +1 -0
- package/dist/field/metadata/MetadataField.view.js +25 -21
- package/dist/field/richtext/RichTextBlock.d.ts +3 -1
- package/dist/field/richtext/RichTextBlock.js +177 -74
- package/dist/field/richtext/RichTextBlockHost.js +1 -1
- package/dist/field/richtext/RichTextExtensions.js +31 -31
- package/dist/field/richtext/RichTextField.view.js +14 -8
- package/dist/field/richtext/RichTextInsertMenu.js +43 -27
- package/dist/field/views.js +2 -0
- package/dist/index.css +490 -309
- package/dist/picker/url/UrlPicker.js +1 -0
- package/docs/components/badge.md +1 -0
- package/docs/components/button.md +1 -0
- package/docs/components/content-card.md +1 -0
- package/docs/components/search-field.md +1 -0
- package/docs/components/sortable-list.md +5 -20
- package/docs/components/table.md +1 -0
- package/docs/configuration.md +1 -1
- package/docs/deploy/self-host.md +1 -1
- package/docs/deploy.md +5 -4
- package/docs/fields.md +1 -1
- package/docs/index.md +1 -1
- package/docs/instant-publishing.md +7 -22
- package/docs/overviews.md +3 -4
- package/docs/upgrading.md +3 -2
- package/llms-full.txt +31 -55
- package/package.json +2 -3
- package/dist/core/db/EntryValidationError.d.ts +0 -13
- package/dist/core/db/EntryValidationError.js +0 -23
- package/dist/dashboard/app/DeleteDialog.d.ts +0 -11
- package/dist/dashboard/atoms/delete.d.ts +0 -68
- package/dist/dashboard/atoms/delete.js +0 -143
- package/dist/chunks/{chunk-MOWR65LM.js → chunk-PYDOMKJZ.js} +71 -71
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import "../../chunks/chunk-CQ52GEHY.js";
|
|
2
|
+
|
|
3
|
+
// src/components/internal/sortableDrag.ts
|
|
4
|
+
var threshold = 4;
|
|
5
|
+
var duration = 200;
|
|
6
|
+
var easing = "cubic-bezier(0.2, 0, 0, 1)";
|
|
7
|
+
var scrollEdge = 48;
|
|
8
|
+
var scrollSpeed = 12;
|
|
9
|
+
var settling = /* @__PURE__ */ new Map();
|
|
10
|
+
function pressSortableItem({
|
|
11
|
+
event,
|
|
12
|
+
list,
|
|
13
|
+
item,
|
|
14
|
+
items,
|
|
15
|
+
onDrop
|
|
16
|
+
}) {
|
|
17
|
+
const startX = event.clientX;
|
|
18
|
+
const startY = event.clientY;
|
|
19
|
+
let pointerY = startY;
|
|
20
|
+
let drag = null;
|
|
21
|
+
function onMove(event2) {
|
|
22
|
+
if (event2.pointerId !== pointerId) return;
|
|
23
|
+
pointerY = event2.clientY;
|
|
24
|
+
if (!drag) {
|
|
25
|
+
const distance = Math.hypot(event2.clientX - startX, pointerY - startY);
|
|
26
|
+
if (distance < threshold) return;
|
|
27
|
+
drag = startDrag();
|
|
28
|
+
if (!drag) return finish(false);
|
|
29
|
+
}
|
|
30
|
+
drag.update();
|
|
31
|
+
}
|
|
32
|
+
function onUp(event2) {
|
|
33
|
+
if (event2.pointerId === pointerId) finish(true);
|
|
34
|
+
}
|
|
35
|
+
function onCancel(event2) {
|
|
36
|
+
if (event2.pointerId === pointerId) finish(false);
|
|
37
|
+
}
|
|
38
|
+
function onKeyDown(event2) {
|
|
39
|
+
if (event2.key !== "Escape" || !drag) return;
|
|
40
|
+
event2.preventDefault();
|
|
41
|
+
event2.stopPropagation();
|
|
42
|
+
finish(false);
|
|
43
|
+
}
|
|
44
|
+
function onBlur() {
|
|
45
|
+
finish(false);
|
|
46
|
+
}
|
|
47
|
+
const pointerId = event.pointerId;
|
|
48
|
+
window.addEventListener("pointermove", onMove, true);
|
|
49
|
+
window.addEventListener("pointerup", onUp, true);
|
|
50
|
+
window.addEventListener("pointercancel", onCancel, true);
|
|
51
|
+
window.addEventListener("keydown", onKeyDown, true);
|
|
52
|
+
window.addEventListener("blur", onBlur);
|
|
53
|
+
function finish(commit) {
|
|
54
|
+
window.removeEventListener("pointermove", onMove, true);
|
|
55
|
+
window.removeEventListener("pointerup", onUp, true);
|
|
56
|
+
window.removeEventListener("pointercancel", onCancel, true);
|
|
57
|
+
window.removeEventListener("keydown", onKeyDown, true);
|
|
58
|
+
window.removeEventListener("blur", onBlur);
|
|
59
|
+
if (!drag) return;
|
|
60
|
+
drag.end(commit);
|
|
61
|
+
drag = null;
|
|
62
|
+
const block = (event2) => {
|
|
63
|
+
if (!(event2.target instanceof Node) || !list.contains(event2.target))
|
|
64
|
+
return;
|
|
65
|
+
event2.preventDefault();
|
|
66
|
+
event2.stopPropagation();
|
|
67
|
+
};
|
|
68
|
+
window.addEventListener("click", block, true);
|
|
69
|
+
setTimeout(() => window.removeEventListener("click", block, true), 100);
|
|
70
|
+
}
|
|
71
|
+
function startDrag() {
|
|
72
|
+
for (const settle of Array.from(settling.values())) settle();
|
|
73
|
+
const elements = items();
|
|
74
|
+
const from = elements.indexOf(item);
|
|
75
|
+
if (from === -1) return null;
|
|
76
|
+
const rects = elements.map((element) => element.getBoundingClientRect());
|
|
77
|
+
const scroller = scrollParent(list);
|
|
78
|
+
const startScroll = scroller.scrollTop;
|
|
79
|
+
const others = elements.filter((element) => element !== item);
|
|
80
|
+
let to = from;
|
|
81
|
+
let frame = 0;
|
|
82
|
+
const userSelect = document.body.style.userSelect;
|
|
83
|
+
document.body.style.userSelect = "none";
|
|
84
|
+
document.documentElement.dataset.sortableDragging = "";
|
|
85
|
+
window.getSelection()?.removeAllRanges();
|
|
86
|
+
list.dataset.dragging = "";
|
|
87
|
+
item.dataset.dragging = "";
|
|
88
|
+
for (const other of others)
|
|
89
|
+
other.style.transition = `transform ${duration}ms ${easing}`;
|
|
90
|
+
function offset() {
|
|
91
|
+
const dy = pointerY - startY + scroller.scrollTop - startScroll;
|
|
92
|
+
const min = rects[0].top - rects[from].top;
|
|
93
|
+
const max = rects[rects.length - 1].bottom - rects[from].bottom;
|
|
94
|
+
return Math.min(max, Math.max(min, dy));
|
|
95
|
+
}
|
|
96
|
+
function update() {
|
|
97
|
+
const dy = offset();
|
|
98
|
+
item.style.transform = `translateY(${dy}px)`;
|
|
99
|
+
const top = rects[from].top + dy;
|
|
100
|
+
const bottom = rects[from].bottom + dy;
|
|
101
|
+
to = from;
|
|
102
|
+
for (let index = from - 1; index >= 0; index--)
|
|
103
|
+
if (top < middle(rects[index])) to = index;
|
|
104
|
+
for (let index = from + 1; index < rects.length; index++)
|
|
105
|
+
if (bottom > middle(rects[index])) to = index;
|
|
106
|
+
const size = rects[from].height;
|
|
107
|
+
elements.forEach((element, index) => {
|
|
108
|
+
if (element === item) return;
|
|
109
|
+
const shift = from < to && index > from && index <= to ? -size : to < from && index >= to && index < from ? size : 0;
|
|
110
|
+
element.style.transform = shift ? `translateY(${shift}px)` : "";
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function autoScroll() {
|
|
114
|
+
const bounds = scrollBounds(scroller);
|
|
115
|
+
const step = pointerY < bounds.top + scrollEdge ? -scrollSpeed : pointerY > bounds.bottom - scrollEdge ? scrollSpeed : 0;
|
|
116
|
+
if (step) {
|
|
117
|
+
const before = scroller.scrollTop;
|
|
118
|
+
scroller.scrollTop += step;
|
|
119
|
+
if (scroller.scrollTop !== before) update();
|
|
120
|
+
}
|
|
121
|
+
frame = requestAnimationFrame(autoScroll);
|
|
122
|
+
}
|
|
123
|
+
frame = requestAnimationFrame(autoScroll);
|
|
124
|
+
function end(commit) {
|
|
125
|
+
cancelAnimationFrame(frame);
|
|
126
|
+
document.body.style.userSelect = userSelect;
|
|
127
|
+
delete document.documentElement.dataset.sortableDragging;
|
|
128
|
+
const moved = commit && to !== from;
|
|
129
|
+
for (const other of others) {
|
|
130
|
+
if (moved) other.style.transition = "";
|
|
131
|
+
other.style.transform = "";
|
|
132
|
+
}
|
|
133
|
+
const visualTop = item.getBoundingClientRect().top;
|
|
134
|
+
item.style.transform = "";
|
|
135
|
+
if (moved) onDrop(item, elements[to], to > from ? "after" : "before");
|
|
136
|
+
const distance = visualTop - item.getBoundingClientRect().top;
|
|
137
|
+
item.style.transition = "none";
|
|
138
|
+
item.style.transform = `translateY(${distance}px)`;
|
|
139
|
+
item.getBoundingClientRect();
|
|
140
|
+
item.style.transition = `transform ${duration}ms ${easing}`;
|
|
141
|
+
item.style.transform = "";
|
|
142
|
+
delete list.dataset.dragging;
|
|
143
|
+
const timeout = setTimeout(settle, duration);
|
|
144
|
+
function settle() {
|
|
145
|
+
clearTimeout(timeout);
|
|
146
|
+
settling.delete(item);
|
|
147
|
+
item.style.transition = "";
|
|
148
|
+
for (const other of others) other.style.transition = "";
|
|
149
|
+
delete item.dataset.dragging;
|
|
150
|
+
}
|
|
151
|
+
settling.set(item, settle);
|
|
152
|
+
}
|
|
153
|
+
return { update, end };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function middle(rect) {
|
|
157
|
+
return rect.top + rect.height / 2;
|
|
158
|
+
}
|
|
159
|
+
function scrollParent(element) {
|
|
160
|
+
for (let parent = element.parentElement; parent; parent = parent.parentElement) {
|
|
161
|
+
const { overflowY } = getComputedStyle(parent);
|
|
162
|
+
if ((overflowY === "auto" || overflowY === "scroll") && parent.scrollHeight > parent.clientHeight)
|
|
163
|
+
return parent;
|
|
164
|
+
}
|
|
165
|
+
return document.scrollingElement ?? document.documentElement;
|
|
166
|
+
}
|
|
167
|
+
function scrollBounds(scroller) {
|
|
168
|
+
if (scroller === document.scrollingElement)
|
|
169
|
+
return { top: 0, bottom: window.innerHeight };
|
|
170
|
+
const rect = scroller.getBoundingClientRect();
|
|
171
|
+
return { top: rect.top, bottom: rect.bottom };
|
|
172
|
+
}
|
|
173
|
+
export {
|
|
174
|
+
pressSortableItem
|
|
175
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
$32ef394310635fd5$export$2cfc5be7a55829f6,
|
|
3
3
|
$f9554a667e4f0374$export$62ed72bc21f6b8a6
|
|
4
|
-
} from "../../chunks/chunk-
|
|
4
|
+
} from "../../chunks/chunk-QHHMKJUO.js";
|
|
5
5
|
import "../../chunks/chunk-NUFRM6SI.js";
|
|
6
|
-
import "../../chunks/chunk-
|
|
6
|
+
import "../../chunks/chunk-HOETF6HN.js";
|
|
7
7
|
import "../../chunks/chunk-RKDPDEF7.js";
|
|
8
8
|
import "../../chunks/chunk-PSSMQKAH.js";
|
|
9
9
|
import "../../chunks/chunk-CQ52GEHY.js";
|
package/dist/components.d.ts
CHANGED
|
@@ -42,6 +42,7 @@ export * from './components/RadioGroup.js';
|
|
|
42
42
|
export * from './components/Resizable.js';
|
|
43
43
|
export * from './components/SearchField.js';
|
|
44
44
|
export * from './components/Select.js';
|
|
45
|
+
export * from './components/Sheet.js';
|
|
45
46
|
export * from './components/Sidebar.js';
|
|
46
47
|
export * from './components/SortableList.js';
|
|
47
48
|
export * from './components/Spinner.js';
|
package/dist/components.js
CHANGED
|
@@ -43,6 +43,7 @@ export * from "./components/RadioGroup.js";
|
|
|
43
43
|
export * from "./components/Resizable.js";
|
|
44
44
|
export * from "./components/SearchField.js";
|
|
45
45
|
export * from "./components/Select.js";
|
|
46
|
+
export * from "./components/Sheet.js";
|
|
46
47
|
export * from "./components/Sidebar.js";
|
|
47
48
|
export * from "./components/SortableList.js";
|
|
48
49
|
export * from "./components/Spinner.js";
|
package/dist/core/Config.d.ts
CHANGED
|
@@ -19,7 +19,12 @@ export interface Config {
|
|
|
19
19
|
preview?: Preview;
|
|
20
20
|
/** Every edit will pass through a draft status before being published */
|
|
21
21
|
enableDrafts?: boolean;
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Fallback interval in seconds for polling content updates (freshness is
|
|
24
|
+
* primarily sha-driven). In Next.js it also sets how long renders share the
|
|
25
|
+
* latest content sha, which bounds how soon edits made outside the handler
|
|
26
|
+
* show up: five minutes when unset.
|
|
27
|
+
*/
|
|
23
28
|
syncInterval?: number;
|
|
24
29
|
/** Maximum file upload size in bytes */
|
|
25
30
|
maxUploadSize?: number;
|
package/dist/core/Document.d.ts
CHANGED
|
@@ -7,4 +7,12 @@ export type Document = {
|
|
|
7
7
|
path: PathField;
|
|
8
8
|
metadata: MetadataField;
|
|
9
9
|
};
|
|
10
|
-
export
|
|
10
|
+
export type DocumentConfig<Fields, Seo, Details> = (TypeConfig<Fields> | ContainerTypeConfig<Fields>) & {
|
|
11
|
+
/** Fields shown in the SEO tab, above the previews */
|
|
12
|
+
seo?: Seo;
|
|
13
|
+
/** Fields shown in the Details tab, above the created and updated details */
|
|
14
|
+
details?: Details;
|
|
15
|
+
};
|
|
16
|
+
export declare function document<Fields extends FieldsDefinition = {}, Seo extends FieldsDefinition = {}, Details extends FieldsDefinition = {}>(label: string, { fields, seo, details, ...config }: DocumentConfig<Fields, Seo, Details>): Type<Document & Fields & Seo & Details>;
|
|
17
|
+
/** Whether `type` was created with `Config.document` */
|
|
18
|
+
export declare function isDocument(type: Type): boolean;
|
package/dist/core/Document.js
CHANGED
|
@@ -8,24 +8,40 @@ import {
|
|
|
8
8
|
} from "alinea/dashboard/icons";
|
|
9
9
|
import {
|
|
10
10
|
metadata as createMetadata,
|
|
11
|
-
metadataDetails
|
|
11
|
+
metadataDetails,
|
|
12
|
+
metadataPreviews
|
|
12
13
|
} from "alinea/field/metadata";
|
|
13
14
|
import { path as createPath } from "alinea/field/path";
|
|
14
15
|
import { tab, tabs } from "alinea/field/tabs";
|
|
15
16
|
import { text } from "alinea/field/text";
|
|
17
|
+
import { Section } from "./Section.js";
|
|
16
18
|
import {
|
|
17
19
|
type
|
|
18
20
|
} from "./Type.js";
|
|
21
|
+
import { entries, keys } from "./util/Objects.js";
|
|
19
22
|
var documentMarker = /* @__PURE__ */ Symbol.for("@alinea.Document");
|
|
20
23
|
function documentFields() {
|
|
21
24
|
return {
|
|
22
25
|
title: text("Title", { required: true, width: 0.5 }),
|
|
23
26
|
path: createPath("Path", { required: true, width: 0.5 }),
|
|
24
|
-
metadata: createMetadata("Metadata", {
|
|
27
|
+
metadata: createMetadata("Metadata", { sections: true })
|
|
25
28
|
};
|
|
26
29
|
}
|
|
27
|
-
function document(label, {
|
|
30
|
+
function document(label, {
|
|
31
|
+
fields = {},
|
|
32
|
+
seo = {},
|
|
33
|
+
details = {},
|
|
34
|
+
...config
|
|
35
|
+
}) {
|
|
28
36
|
const { title, path, metadata } = documentFields();
|
|
37
|
+
const taken = /* @__PURE__ */ new Set([
|
|
38
|
+
...keys({ title, path, metadata }),
|
|
39
|
+
...fieldNames(fields)
|
|
40
|
+
]);
|
|
41
|
+
for (const name of [...fieldNames(seo), ...fieldNames(details)]) {
|
|
42
|
+
if (taken.has(name)) throw new Error(`Field "${name}" is defined twice`);
|
|
43
|
+
taken.add(name);
|
|
44
|
+
}
|
|
29
45
|
const fieldsWithMeta = tabs(
|
|
30
46
|
tab("Document", {
|
|
31
47
|
icon: IcRoundDescription,
|
|
@@ -33,11 +49,11 @@ function document(label, { fields, ...config }) {
|
|
|
33
49
|
}),
|
|
34
50
|
tab("SEO", {
|
|
35
51
|
icon: IcRoundBolt,
|
|
36
|
-
fields: { metadata }
|
|
52
|
+
fields: { metadata, ...seo, ...metadataPreviews() }
|
|
37
53
|
}),
|
|
38
54
|
tab("Details", {
|
|
39
55
|
icon: IcRoundInfo,
|
|
40
|
-
fields: { ...metadataDetails(metadata) }
|
|
56
|
+
fields: { ...details, ...metadataDetails(metadata) }
|
|
41
57
|
})
|
|
42
58
|
);
|
|
43
59
|
const result = type(label, {
|
|
@@ -46,6 +62,15 @@ function document(label, { fields, ...config }) {
|
|
|
46
62
|
});
|
|
47
63
|
return Object.assign(result, { [documentMarker]: true });
|
|
48
64
|
}
|
|
65
|
+
function isDocument(type2) {
|
|
66
|
+
return documentMarker in type2;
|
|
67
|
+
}
|
|
68
|
+
function fieldNames(definition) {
|
|
69
|
+
return entries(definition).flatMap(
|
|
70
|
+
([name, value]) => Section.isSection(value) ? keys(Section.fields(value)) : [name]
|
|
71
|
+
);
|
|
72
|
+
}
|
|
49
73
|
export {
|
|
50
|
-
document
|
|
74
|
+
document,
|
|
75
|
+
isDocument
|
|
51
76
|
};
|
package/dist/core/Overview.d.ts
CHANGED
|
@@ -70,7 +70,7 @@ export type OverviewSortDirection = 'asc' | 'desc';
|
|
|
70
70
|
export interface OverviewSort {
|
|
71
71
|
/**
|
|
72
72
|
* A key of `overview.sorts`, of `overview.columns`, or a built-in column:
|
|
73
|
-
* `title`, `type`, `
|
|
73
|
+
* `title`, `type`, `updated`, `author`
|
|
74
74
|
*/
|
|
75
75
|
column: string;
|
|
76
76
|
direction: OverviewSortDirection;
|
|
@@ -180,8 +180,6 @@ export interface OverviewColumn<Value = any> extends OverviewColumnBase<Value> {
|
|
|
180
180
|
export interface OverviewBuiltins {
|
|
181
181
|
/** The entry type, shown by default when the children have several types */
|
|
182
182
|
type?: boolean;
|
|
183
|
-
/** The publication status, shown by default when the statuses differ */
|
|
184
|
-
status?: boolean;
|
|
185
183
|
/** When the entry was last edited, shown by default when recorded */
|
|
186
184
|
updated?: boolean;
|
|
187
185
|
/** Who last edited the entry, shown by default when recorded */
|
|
@@ -192,7 +190,7 @@ export interface OverviewOptions {
|
|
|
192
190
|
/**
|
|
193
191
|
* Columns shown after the title and the built-in columns, or before the
|
|
194
192
|
* built-in columns with `position: 'start'`. A column keyed `type`,
|
|
195
|
-
* `
|
|
193
|
+
* `updated` or `author` replaces that built-in column.
|
|
196
194
|
*/
|
|
197
195
|
columns?: Record<string, OverviewColumn>;
|
|
198
196
|
/** Show or hide the built-in columns */
|
package/dist/core/TextDoc.d.ts
CHANGED
|
@@ -70,6 +70,10 @@ export interface ImageNode extends ElementNode {
|
|
|
70
70
|
export interface BlockNode {
|
|
71
71
|
_id: string;
|
|
72
72
|
_type: string;
|
|
73
|
+
/** A label editors give the block, its anchor follows it by default */
|
|
74
|
+
_label?: string;
|
|
75
|
+
/** Lets links point to the block, unique within the entry */
|
|
76
|
+
_anchor?: string;
|
|
73
77
|
}
|
|
74
78
|
export declare namespace BlockNode {
|
|
75
79
|
const id = "_id";
|
|
@@ -53,4 +53,3 @@ export declare function validateType(type: Type, value: unknown, context: FieldV
|
|
|
53
53
|
/** Validate entry data: required, min, max and validate options */
|
|
54
54
|
export declare function validateEntry(type: Type, data: unknown, options?: ValidateEntryOptions): Array<FieldValidationError>;
|
|
55
55
|
export declare function formatFieldPath(path: FieldPath): string;
|
|
56
|
-
export declare function formatValidationErrors(errors: ReadonlyArray<FieldValidationError>): string;
|
package/dist/core/Validation.js
CHANGED
|
@@ -104,15 +104,9 @@ function formatFieldPath(path) {
|
|
|
104
104
|
}
|
|
105
105
|
return result;
|
|
106
106
|
}
|
|
107
|
-
function formatValidationErrors(errors) {
|
|
108
|
-
return errors.map(
|
|
109
|
-
(error) => `- ${formatFieldPath(error.path)} (${error.labels.join(" \u203A ")}): ${error.message}`
|
|
110
|
-
).join("\n");
|
|
111
|
-
}
|
|
112
107
|
export {
|
|
113
108
|
fieldError,
|
|
114
109
|
formatFieldPath,
|
|
115
|
-
formatValidationErrors,
|
|
116
110
|
policyFieldOptions,
|
|
117
111
|
scopeGetter,
|
|
118
112
|
trackedFieldOptions,
|
|
@@ -1911,8 +1911,11 @@ function normalizeRichTextAnchors(schema, doc, anchors) {
|
|
|
1911
1911
|
function normalizeNode(node, insideHeading, activeManualAnchors2) {
|
|
1912
1912
|
if (Node.isBlock(node)) {
|
|
1913
1913
|
activeManualAnchors2.clear();
|
|
1914
|
+
const source2 = blockAnchor(node);
|
|
1915
|
+
const unique2 = createUniqueAnchor(source2, anchors);
|
|
1916
|
+
const anchored = source2 !== void 0 && unique2 !== node._anchor ? { ...node, _anchor: unique2 } : node;
|
|
1914
1917
|
const type = schema?.[node[Node.type]];
|
|
1915
|
-
return type ? Type.normalizeAnchors(type,
|
|
1918
|
+
return type ? Type.normalizeAnchors(type, anchored, { anchors }) : anchored;
|
|
1916
1919
|
}
|
|
1917
1920
|
if (Node.isText(node)) {
|
|
1918
1921
|
const marks = node.marks;
|
|
@@ -2052,20 +2055,36 @@ function richTextAnchors(schema, doc, context) {
|
|
|
2052
2055
|
}
|
|
2053
2056
|
});
|
|
2054
2057
|
doc.forEach((row, index) => {
|
|
2055
|
-
if (!
|
|
2056
|
-
const
|
|
2058
|
+
if (!Node.isBlock(row)) return;
|
|
2059
|
+
const segment = row._id ?? String(index);
|
|
2060
|
+
const anchor = blockAnchor(row);
|
|
2061
|
+
if (anchor && !anchors.has(anchor)) {
|
|
2062
|
+
anchors.add(anchor);
|
|
2063
|
+
result.push({
|
|
2064
|
+
id: anchor,
|
|
2065
|
+
label: `#${anchor}`,
|
|
2066
|
+
fieldPath: referenceFieldPath([...path, segment, anchor]),
|
|
2067
|
+
fieldLabel: label
|
|
2068
|
+
});
|
|
2069
|
+
}
|
|
2070
|
+
const type = schema?.[row[Node.type]];
|
|
2057
2071
|
if (!type) return;
|
|
2058
2072
|
result.push(
|
|
2059
2073
|
...Type.anchors(
|
|
2060
2074
|
type,
|
|
2061
2075
|
row,
|
|
2062
|
-
[...path,
|
|
2076
|
+
[...path, segment],
|
|
2063
2077
|
[...labels, Type.label(type)]
|
|
2064
2078
|
)
|
|
2065
2079
|
);
|
|
2066
2080
|
});
|
|
2067
2081
|
return result;
|
|
2068
2082
|
}
|
|
2083
|
+
function blockAnchor(block) {
|
|
2084
|
+
if (typeof block._anchor === "string") return block._anchor;
|
|
2085
|
+
if (typeof block._label === "string" && block._label)
|
|
2086
|
+
return slugify(block._label);
|
|
2087
|
+
}
|
|
2069
2088
|
function richTextReferences(schema, doc, context) {
|
|
2070
2089
|
const { path, label, labels } = context;
|
|
2071
2090
|
const result = [];
|
|
@@ -82,7 +82,7 @@ function mediaOverview() {
|
|
|
82
82
|
MediaLibrary: new Expr({ type: "value", value: 0 })
|
|
83
83
|
});
|
|
84
84
|
return {
|
|
85
|
-
builtins: { type: false
|
|
85
|
+
builtins: { type: false },
|
|
86
86
|
// Folders first in their manual order, files have none and come after,
|
|
87
87
|
// newest first: entry ids start with their creation time. Indexes are
|
|
88
88
|
// keys that only order as stored.
|
|
@@ -13,6 +13,10 @@ export declare function normalizeGithubSourceOptions<Options extends GithubSourc
|
|
|
13
13
|
export declare class GithubSource implements Source {
|
|
14
14
|
#private;
|
|
15
15
|
constructor(options: GithubSourceOptions);
|
|
16
|
+
/** Request GitHub, counting the request against its budget. */
|
|
17
|
+
protected githubFetch(input: string, init?: RequestInit): Promise<Response>;
|
|
18
|
+
/** Run an operation, and log what it spent of GitHub's budgets. */
|
|
19
|
+
protected spending<T>(operation: string, run: () => Promise<T>): Promise<T>;
|
|
16
20
|
protected get contentLocation(): string;
|
|
17
21
|
getTree(): Promise<ReadonlyTree>;
|
|
18
22
|
shaAt(ref: string): Promise<string>;
|
|
@@ -8,9 +8,14 @@ import * as paths from "alinea/core/util/Paths";
|
|
|
8
8
|
import { HttpError } from "../HttpError.js";
|
|
9
9
|
import { assert } from "../util/Assert.js";
|
|
10
10
|
import { isRecord } from "alinea/core/util/Objects";
|
|
11
|
+
import { chunks } from "../util/Arrays.js";
|
|
12
|
+
import { hashBlob } from "./GitUtils.js";
|
|
13
|
+
import { GithubUsage } from "./GithubUsage.js";
|
|
11
14
|
import { Leaf, ReadonlyTree } from "./Tree.js";
|
|
12
15
|
var shaCache = /* @__PURE__ */ new Map();
|
|
13
16
|
var truncatedTrees = /* @__PURE__ */ new Set();
|
|
17
|
+
var blobsPerQuery = 100;
|
|
18
|
+
var smallestBlobQuery = 25;
|
|
14
19
|
function normalizeGithubSourceOptions(options) {
|
|
15
20
|
return {
|
|
16
21
|
...options,
|
|
@@ -22,9 +27,27 @@ var GithubSource = class {
|
|
|
22
27
|
#current = ReadonlyTree.EMPTY;
|
|
23
28
|
#options;
|
|
24
29
|
#limit = pLimit(8);
|
|
30
|
+
/** Blob queries in flight, each holding up to a hundred blobs in memory. */
|
|
31
|
+
#blobQueries = pLimit(2);
|
|
32
|
+
#usage = new GithubUsage();
|
|
25
33
|
constructor(options) {
|
|
26
34
|
this.#options = normalizeGithubSourceOptions(options);
|
|
27
35
|
}
|
|
36
|
+
/** Request GitHub, counting the request against its budget. */
|
|
37
|
+
async githubFetch(input, init) {
|
|
38
|
+
const response = await fetch(input, init);
|
|
39
|
+
this.#usage.record(response);
|
|
40
|
+
return response;
|
|
41
|
+
}
|
|
42
|
+
/** Run an operation, and log what it spent of GitHub's budgets. */
|
|
43
|
+
async spending(operation, run) {
|
|
44
|
+
const before = this.#usage.count();
|
|
45
|
+
try {
|
|
46
|
+
return await run();
|
|
47
|
+
} finally {
|
|
48
|
+
this.#usage.log(operation, before);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
28
51
|
get contentLocation() {
|
|
29
52
|
const { contentDir, rootDir } = this.#options;
|
|
30
53
|
return paths.join(rootDir, contentDir);
|
|
@@ -42,7 +65,7 @@ var GithubSource = class {
|
|
|
42
65
|
const cached = shaCache.get(url);
|
|
43
66
|
const headers = new Headers({ Authorization: `Bearer ${authToken}` });
|
|
44
67
|
if (cached) headers.set("If-None-Match", cached.etag);
|
|
45
|
-
const parentInfo = await
|
|
68
|
+
const parentInfo = await this.githubFetch(url, { headers });
|
|
46
69
|
if (parentInfo.status === 304) {
|
|
47
70
|
assert(cached, "Received 304 without a cached GitHub response");
|
|
48
71
|
return cached.sha;
|
|
@@ -59,10 +82,12 @@ var GithubSource = class {
|
|
|
59
82
|
else shaCache.delete(url);
|
|
60
83
|
return sha;
|
|
61
84
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
85
|
+
getTreeIfDifferent(sha) {
|
|
86
|
+
return this.spending("sync", async () => {
|
|
87
|
+
const remoteSha = await this.shaAt(this.#options.branch);
|
|
88
|
+
if (remoteSha === sha) return void 0;
|
|
89
|
+
return this.#fetchTree(remoteSha, this.contentLocation);
|
|
90
|
+
});
|
|
66
91
|
}
|
|
67
92
|
/**
|
|
68
93
|
* GitHub truncates recursive listings above 100k entries or 7 MB, so a
|
|
@@ -73,7 +98,7 @@ var GithubSource = class {
|
|
|
73
98
|
const key = `${owner}/${repo}:${location}`;
|
|
74
99
|
const list = async (query) => {
|
|
75
100
|
const response = await this.#limit(
|
|
76
|
-
() =>
|
|
101
|
+
() => this.githubFetch(
|
|
77
102
|
`https://api.github.com/repos/${owner}/${repo}/git/trees/${sha}${query}`,
|
|
78
103
|
{ headers: { Authorization: `Bearer ${authToken}` } }
|
|
79
104
|
)
|
|
@@ -101,28 +126,105 @@ var GithubSource = class {
|
|
|
101
126
|
return ReadonlyTree.fromNodes(new Map(nodes), sha);
|
|
102
127
|
}
|
|
103
128
|
async *getBlobs(shas, options = {}) {
|
|
129
|
+
const before = this.#usage.count();
|
|
130
|
+
const batches = Array.from(
|
|
131
|
+
chunks(shas, blobsPerQuery),
|
|
132
|
+
(batch) => this.#blobQueries(() => this.#queryBlobs(batch, options.signal))
|
|
133
|
+
);
|
|
134
|
+
for (const batch of batches) batch.catch(() => {
|
|
135
|
+
});
|
|
136
|
+
try {
|
|
137
|
+
for (const batch of batches) yield* await batch;
|
|
138
|
+
} finally {
|
|
139
|
+
this.#usage.log("blobs", before);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Look blobs up in one GraphQL query, which costs a single point of
|
|
144
|
+
* GitHub's GraphQL budget however many it holds, where the REST API counts
|
|
145
|
+
* a request per blob. A query GitHub cannot answer in time, such as one
|
|
146
|
+
* for many large files, is asked again in halves, down to a small one.
|
|
147
|
+
*/
|
|
148
|
+
async #queryBlobs(shas, signal) {
|
|
149
|
+
const blobs = await this.#graphqlBlobs(shas, signal);
|
|
150
|
+
if (blobs) return blobs;
|
|
151
|
+
if (shas.length <= smallestBlobQuery)
|
|
152
|
+
throw new HttpError(502, "Failed to get blobs: GitHub did not answer");
|
|
153
|
+
const half = Math.ceil(shas.length / 2);
|
|
154
|
+
return [
|
|
155
|
+
...await this.#queryBlobs(shas.slice(0, half), signal),
|
|
156
|
+
...await this.#queryBlobs(shas.slice(half), signal)
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Blobs found by one GraphQL query, or undefined if GitHub could not
|
|
161
|
+
* answer it, such as when it timed out. Blobs it has no text for, such as
|
|
162
|
+
* binary or very large files, are fetched one by one.
|
|
163
|
+
*/
|
|
164
|
+
async #graphqlBlobs(shas, signal) {
|
|
104
165
|
const { owner, repo, authToken } = this.#options;
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
166
|
+
const params = shas.map((_, i) => `$b${i}: GitObjectID!`).join(", ");
|
|
167
|
+
const fields = shas.map(
|
|
168
|
+
(_, i) => `b${i}: object(oid: $b${i}) { ... on Blob { text isBinary isTruncated } }`
|
|
169
|
+
).join("\n");
|
|
170
|
+
const query = `query ($owner: String!, $repo: String!, ${params}) {
|
|
171
|
+
repository(owner: $owner, name: $repo) {
|
|
172
|
+
${fields}
|
|
173
|
+
}
|
|
174
|
+
}`;
|
|
175
|
+
const variables = { owner, repo };
|
|
176
|
+
for (const [i, sha] of shas.entries()) variables[`b${i}`] = sha;
|
|
177
|
+
const response = await this.githubFetch("https://api.github.com/graphql", {
|
|
178
|
+
method: "POST",
|
|
179
|
+
headers: {
|
|
180
|
+
Authorization: `Bearer ${authToken}`,
|
|
181
|
+
"Content-Type": "application/json"
|
|
182
|
+
},
|
|
183
|
+
body: JSON.stringify({ query, variables }),
|
|
184
|
+
signal
|
|
116
185
|
});
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
186
|
+
if (response.status >= 500) return void 0;
|
|
187
|
+
if (!response.ok) throw await githubError(response, "Failed to get blobs");
|
|
188
|
+
const result = await response.json();
|
|
189
|
+
assert(isRecord(result));
|
|
190
|
+
const errors = Array.isArray(result.errors) ? result.errors : [];
|
|
191
|
+
if (errors.length > 0) {
|
|
192
|
+
const typed = errors.some(
|
|
193
|
+
(error) => isRecord(error) && typeof error.type === "string"
|
|
194
|
+
);
|
|
195
|
+
if (!typed) return void 0;
|
|
196
|
+
const reasons = errors.map(
|
|
197
|
+
(error) => isRecord(error) ? String(error.message) : String(error)
|
|
198
|
+
);
|
|
199
|
+
throw new Error(`Failed to get blobs: ${reasons.join("; ")}`);
|
|
200
|
+
}
|
|
201
|
+
const data = isRecord(result.data) ? result.data : {};
|
|
202
|
+
const repository = isRecord(data.repository) ? data.repository : {};
|
|
203
|
+
const encoder = new TextEncoder();
|
|
204
|
+
const blobs = Array();
|
|
205
|
+
for (const [i, sha] of shas.entries()) {
|
|
206
|
+
const blob = repository[`b${i}`];
|
|
207
|
+
const text = isRecord(blob) && !blob.isBinary && !blob.isTruncated ? blob.text : void 0;
|
|
208
|
+
const bytes = typeof text === "string" ? encoder.encode(text) : void 0;
|
|
209
|
+
blobs.push([
|
|
210
|
+
sha,
|
|
211
|
+
bytes && await hashBlob(bytes) === sha ? bytes : await this.#fetchBlob(sha, signal)
|
|
212
|
+
]);
|
|
125
213
|
}
|
|
214
|
+
return blobs;
|
|
215
|
+
}
|
|
216
|
+
async #fetchBlob(sha, signal) {
|
|
217
|
+
const { owner, repo, authToken } = this.#options;
|
|
218
|
+
const response = await this.githubFetch(
|
|
219
|
+
`https://api.github.com/repos/${owner}/${repo}/git/blobs/${sha}`,
|
|
220
|
+
{ headers: { Authorization: `Bearer ${authToken}` }, signal }
|
|
221
|
+
);
|
|
222
|
+
if (!response.ok) throw await githubError(response, "Failed to get blob");
|
|
223
|
+
const blobData = await response.json();
|
|
224
|
+
assert(blobData.encoding === "base64");
|
|
225
|
+
assert(typeof blobData.content === "string");
|
|
226
|
+
assert(blobData.size > 0);
|
|
227
|
+
return Uint8Array.from(atob(blobData.content), (c) => c.charCodeAt(0));
|
|
126
228
|
}
|
|
127
229
|
async applyChanges(batch) {
|
|
128
230
|
throw new Error("Not implemented");
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Counts the GitHub requests a source makes against its REST and GraphQL
|
|
3
|
+
* budgets, and logs what each operation spent and what is left, so the logs
|
|
4
|
+
* show where a rate limit goes. Answers of 304 are free and not counted.
|
|
5
|
+
*/
|
|
6
|
+
export declare class GithubUsage {
|
|
7
|
+
#private;
|
|
8
|
+
rest: number;
|
|
9
|
+
graphql: number;
|
|
10
|
+
record(response: Response): void;
|
|
11
|
+
/** The requests counted so far, to log what an operation adds. */
|
|
12
|
+
count(): {
|
|
13
|
+
rest: number;
|
|
14
|
+
graphql: number;
|
|
15
|
+
};
|
|
16
|
+
/** Log what `operation` spent since `before`, if anything. */
|
|
17
|
+
log(operation: string, before: {
|
|
18
|
+
rest: number;
|
|
19
|
+
graphql: number;
|
|
20
|
+
}): void;
|
|
21
|
+
}
|