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.
- 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 +6 -2
- package/dist/adapter/next/context.js +1 -4
- package/dist/adapter/next/handler.js +12 -1
- package/dist/backend/CommitEntries.d.ts +17 -0
- package/dist/backend/CommitEntries.js +39 -0
- package/dist/backend/Handler.d.ts +9 -0
- package/dist/backend/Handler.js +80 -20
- package/dist/backend/api/BasicAuth.js +5 -1
- package/dist/backend/api/CreateBackend.js +2 -2
- package/dist/backend/api/DatabaseApi.js +18 -5
- package/dist/backend/api/GithubApi.d.ts +13 -0
- package/dist/backend/api/GithubApi.js +170 -30
- package/dist/backend/api/database.js +2 -2
- package/dist/backend/util/Syncable.d.ts +6 -0
- package/dist/backend/util/Syncable.js +3 -1
- package/dist/bundled.d.ts +911 -151
- package/dist/chunks/{chunk-5H4D5JQL.js → chunk-2PP7WJUJ.js} +1 -11
- package/dist/chunks/{chunk-QHHMKJUO.js → chunk-7DDPSWRY.js} +465 -2
- package/dist/chunks/{chunk-HOETF6HN.js → chunk-PMGIIYDD.js} +1 -0
- package/dist/chunks/{chunk-3QFSGTGF.js → chunk-SCV3TEXM.js} +1 -1
- package/dist/chunks/{chunk-UPY2RUZF.js → chunk-TWBREZWC.js} +30 -0
- package/dist/cli/Generate.js +20 -7
- package/dist/cli/Mcp.js +1 -1
- package/dist/cli/Serve.js +1 -1
- package/dist/cli/bin.js +1 -1
- package/dist/cli/build/BuildEmitter.d.ts +5 -1
- package/dist/cli/build/BuildEmitter.js +8 -3
- package/dist/cli/generate/CompileConfig.js +10 -6
- package/dist/cli/generate/DevDB.d.ts +4 -1
- package/dist/cli/generate/DevDB.js +15 -7
- package/dist/cli/generate/GenerateDashboard.js +1 -0
- package/dist/cli/generate/MediaLocations.d.ts +18 -0
- package/dist/cli/generate/MediaLocations.js +69 -0
- package/dist/cli/serve/CreateLocalServer.js +1 -0
- package/dist/cli/serve/mcp/DevMcp.js +1 -1
- package/dist/cloud/CloudRemote.js +21 -7
- package/dist/cms.d.ts +4 -2
- package/dist/components/Alert.js +2 -2
- package/dist/components/AppShell.js +2 -2
- package/dist/components/AsyncList.d.ts +52 -0
- package/dist/components/AsyncList.js +69 -0
- package/dist/components/Breadcrumbs.js +6 -6
- package/dist/components/Button.js +4 -4
- package/dist/components/Calendar.js +18 -18
- package/dist/components/Checkbox.js +10 -10
- package/dist/components/CheckboxGroup.js +2 -2
- package/dist/components/Collapsible.js +5 -5
- package/dist/components/ColorSwatch.js +2 -2
- package/dist/components/ColorSwatchPicker.js +4 -4
- package/dist/components/ComboBox.d.ts +27 -9
- package/dist/components/ComboBox.js +40 -9
- package/dist/components/Command.js +13 -13
- package/dist/components/ContentCard.js +28 -28
- package/dist/components/ContentGrid.js +11 -11
- package/dist/components/DataList.js +1 -1
- package/dist/components/DateField.d.ts +4 -0
- 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.d.ts +14 -3
- package/dist/components/Dialog.js +38 -11
- package/dist/components/DropZone.js +6 -6
- package/dist/components/DropdownMenu.js +8 -8
- package/dist/components/Empty.js +4 -4
- package/dist/components/Field.d.ts +1 -1
- package/dist/components/Field.js +18 -12
- package/dist/components/Form.d.ts +23 -0
- package/dist/components/Form.js +64 -0
- package/dist/components/Link.d.ts +8 -1
- package/dist/components/List.js +27 -27
- package/dist/components/MediaPreview.js +5 -5
- package/dist/components/MultipleSelect.d.ts +13 -8
- package/dist/components/MultipleSelect.js +25 -25
- package/dist/components/NavRail.js +7 -7
- package/dist/components/NumberField.d.ts +5 -1
- package/dist/components/NumberField.js +6 -4
- package/dist/components/Page.js +6 -6
- package/dist/components/Popover.js +2 -2
- package/dist/components/PreviewFrame.js +8 -8
- package/dist/components/RadioGroup.js +10 -10
- package/dist/components/Resizable.js +1 -1
- package/dist/components/SearchField.js +8 -8
- package/dist/components/Select.d.ts +13 -10
- package/dist/components/Select.js +18 -32
- package/dist/components/Sheet.js +6 -6
- package/dist/components/Sidebar.js +5 -5
- package/dist/components/SortableList.js +12 -12
- package/dist/components/Spinner.js +4 -4
- package/dist/components/Stack.d.ts +20 -0
- package/dist/components/Stack.js +45 -0
- package/dist/components/Surface.js +1 -1
- package/dist/components/Switch.js +5 -5
- package/dist/components/Table.d.ts +11 -1
- package/dist/components/Table.js +81 -53
- package/dist/components/Tabs.js +6 -6
- package/dist/components/TagGroup.js +10 -10
- package/dist/components/TextField.d.ts +7 -1
- package/dist/components/TextField.js +20 -11
- package/dist/components/TimeField.d.ts +6 -1
- package/dist/components/TimeField.js +51 -26
- package/dist/components/Timestamp.js +1 -1
- package/dist/components/ToggleGroup.js +2 -2
- package/dist/components/Toolbar.js +3 -3
- package/dist/components/Tooltip.js +5 -4
- package/dist/components/Tree.js +27 -27
- package/dist/components/internal/DateControl.js +11 -11
- package/dist/components/internal/FormScope.d.ts +2 -0
- package/dist/components/internal/FormScope.js +8 -0
- package/dist/components/internal/ListBoxGroup.d.ts +12 -0
- package/dist/components/internal/ListBoxGroup.js +46 -0
- package/dist/components/internal/ListBoxOption.d.ts +2 -2
- package/dist/components/internal/ListBoxOption.js +5 -5
- package/dist/components/internal/Locale.js +2 -2
- package/dist/components/internal/PopoverSurface.js +5 -4
- package/dist/components/internal/SelectionCheckbox.js +5 -5
- package/dist/components/internal/Slot.js +1 -1
- package/dist/components/internal/ToggleButton.js +4 -4
- package/dist/components/internal/Trigger.js +2 -2
- package/dist/components/internal/useDragDrop.js +2 -2
- package/dist/components/types.d.ts +4 -0
- package/dist/components.d.ts +3 -0
- package/dist/components.js +3 -0
- package/dist/core/Client.js +12 -1
- package/dist/core/Config.d.ts +6 -0
- package/dist/core/Config.js +12 -0
- package/dist/core/DashboardEntry.d.ts +19 -0
- package/dist/core/DashboardEntry.js +0 -0
- package/dist/core/Document.d.ts +2 -2
- package/dist/core/Document.js +10 -3
- package/dist/core/EntryFields.d.ts +8 -2
- package/dist/core/Field.d.ts +5 -0
- package/dist/core/Filter.d.ts +19 -2
- package/dist/core/Filter.js +5 -0
- package/dist/core/Graph.d.ts +10 -2
- package/dist/core/Overview.d.ts +74 -5
- package/dist/core/Overview.js +1 -0
- package/dist/core/Root.js +1 -1
- package/dist/core/Scope.d.ts +5 -0
- package/dist/core/Scope.js +22 -5
- package/dist/core/Type.d.ts +25 -6
- package/dist/core/Type.js +25 -7
- package/dist/core/db/LocalStore.d.ts +7 -0
- package/dist/core/field/CreateField.d.ts +1 -2
- package/dist/core/source/Source.d.ts +2 -0
- package/dist/core/source/Source.js +11 -0
- package/dist/core/util/Async.d.ts +2 -0
- package/dist/core/util/Async.js +12 -1
- package/dist/core/util/Checks.js +1 -1
- package/dist/core/util/Slugs.js +4 -3
- package/dist/dashboard/App.css +13 -11
- package/dist/dashboard/App.d.ts +1 -0
- package/dist/dashboard/App.js +22 -6
- package/dist/dashboard/Href.d.ts +37 -0
- package/dist/dashboard/Href.js +46 -0
- package/dist/dashboard/app/ActivityStatus.js +34 -12
- package/dist/dashboard/app/AuthView.js +2 -2
- package/dist/dashboard/app/BlockSettings.d.ts +9 -0
- package/dist/dashboard/app/BlockSettings.js +16 -0
- package/dist/dashboard/app/BlockSheet.js +3 -3
- package/dist/dashboard/app/BlockSheetStoryFrame.js +2 -2
- package/dist/dashboard/app/CompactField.js +15 -15
- package/dist/dashboard/app/CroppedImage.js +2 -2
- package/dist/dashboard/app/DashboardLayout.d.ts +1 -1
- package/dist/dashboard/app/DashboardLayout.js +12 -0
- package/dist/dashboard/app/EntryActions.d.ts +11 -0
- package/dist/dashboard/app/EntryActions.js +30 -0
- package/dist/dashboard/app/EntryFields.d.ts +15 -2
- package/dist/dashboard/app/EntryFields.js +26 -5
- package/dist/dashboard/app/EntryHeader.js +17 -7
- package/dist/dashboard/app/EntryReferences.js +2 -2
- package/dist/dashboard/app/EntrySidebar.js +25 -23
- package/dist/dashboard/app/EntrySidebarPreview.js +2 -2
- package/dist/dashboard/app/EntryValidationModal.js +3 -3
- package/dist/dashboard/app/Explorer.js +84 -45
- package/dist/dashboard/app/ExplorerBatchActions.js +4 -4
- package/dist/dashboard/app/ExplorerControls.js +5 -5
- package/dist/dashboard/app/ExplorerList.js +2 -2
- package/dist/dashboard/app/ExplorerModal.js +3 -3
- package/dist/dashboard/app/ExplorerOverviewHeader.d.ts +6 -0
- package/dist/dashboard/app/ExplorerOverviewHeader.js +28 -0
- package/dist/dashboard/app/ExplorerTable.js +7 -7
- package/dist/dashboard/app/ExplorerTabs.d.ts +12 -0
- package/dist/dashboard/app/ExplorerTabs.js +42 -0
- package/dist/dashboard/app/ImageEditor.js +31 -31
- package/dist/dashboard/app/LinkPicker.js +3 -3
- package/dist/dashboard/app/LocaleMenu.js +9 -9
- package/dist/dashboard/app/MediaPreviewCell.js +5 -5
- package/dist/dashboard/app/OverviewCell.js +12 -10
- package/dist/dashboard/app/RemoveDialog.js +5 -0
- package/dist/dashboard/app/SearchBar.js +4 -4
- package/dist/dashboard/app/SettingsTab.d.ts +6 -0
- package/dist/dashboard/app/SettingsTab.js +27 -0
- package/dist/dashboard/app/SidebarTree.js +19 -19
- package/dist/dashboard/app/WorkspaceMenu.js +6 -6
- package/dist/dashboard/app/WorkspaceRoots.js +102 -98
- package/dist/dashboard/app/editor/FileEditor.js +18 -18
- package/dist/dashboard/app/pages/EntryPage.js +42 -39
- package/dist/dashboard/app/pages/RootPage.js +3 -3
- package/dist/dashboard/app/pages/SplashPage.js +94 -79
- package/dist/dashboard/app/pages/UsersPage.js +9 -9
- package/dist/dashboard/app/ui/DashboardModal.js +14 -14
- package/dist/dashboard/atoms/activity.d.ts +1 -0
- package/dist/dashboard/atoms/activity.js +8 -5
- package/dist/dashboard/atoms/config.d.ts +2 -0
- package/dist/dashboard/atoms/dashboard.d.ts +11 -0
- package/dist/dashboard/atoms/dashboard.js +8 -0
- package/dist/dashboard/atoms/editor.d.ts +7 -1
- package/dist/dashboard/atoms/editor.js +14 -2
- package/dist/dashboard/atoms/explorer.d.ts +15 -0
- package/dist/dashboard/atoms/explorer.js +43 -14
- package/dist/dashboard/atoms/graph.d.ts +5 -0
- package/dist/dashboard/atoms/graph.js +26 -1
- package/dist/dashboard/atoms/nav.d.ts +16 -10
- package/dist/dashboard/atoms/nav.js +96 -36
- package/dist/dashboard/atoms/overview.d.ts +26 -4
- package/dist/dashboard/atoms/overview.js +41 -12
- package/dist/dashboard/atoms/root.js +7 -1
- package/dist/dashboard/boot/ActivityEvent.d.ts +1 -1
- package/dist/dashboard/boot/Boot.js +6 -0
- package/dist/dashboard/boot/DashboardWorker.d.ts +6 -0
- package/dist/dashboard/boot/DashboardWorker.js +89 -8
- package/dist/dashboard/boot/LoadWorker.js +3 -0
- package/dist/dashboard/boot/SyncEvent.d.ts +7 -0
- package/dist/dashboard/boot/SyncEvent.js +14 -0
- package/dist/dashboard/boot/WorkerDB.d.ts +1 -0
- package/dist/dashboard/boot/WorkerDB.js +3 -0
- package/dist/dashboard/cms/EntryTable.d.ts +4 -3
- package/dist/dashboard/cms/EntryTable.js +2 -2
- package/dist/dashboard/cms/ViewProps.d.ts +3 -2
- package/dist/dashboard/cms/hooks.d.ts +34 -18
- package/dist/dashboard/cms/hooks.js +43 -18
- package/dist/dashboard/contexts.d.ts +3 -2
- package/dist/dashboard/contexts.js +3 -2
- package/dist/dashboard/hook/UseEntryEditor.d.ts +1 -1
- package/dist/dashboard/hook/UseEntryEditor.js +7 -3
- package/dist/dashboard/icons.d.ts +1 -0
- package/dist/dashboard/icons.js +15 -0
- package/dist/database/BrowserEntryStore.js +35 -13
- package/dist/database/DatabaseTables.js +1 -1
- package/dist/database/EntryDatabase.d.ts +10 -1
- package/dist/database/EntryDatabase.js +31 -10
- package/dist/database/EntryStore.js +1 -11
- package/dist/database/entry/EntryTable.d.ts +5 -3
- package/dist/database/entry/EntryTable.js +25 -8
- package/dist/database/query/Condition.d.ts +4 -1
- package/dist/database/query/Condition.js +16 -5
- package/dist/database/query/EntryQuery.js +59 -7
- package/dist/database/query/Relation.d.ts +13 -1
- package/dist/database/query/Relation.js +19 -1
- package/dist/database/query/Search.d.ts +7 -1
- package/dist/database/query/Search.js +54 -9
- package/dist/database/sync/EntrySyncer.d.ts +2 -0
- package/dist/database/sync/EntrySyncer.js +4 -2
- package/dist/database/sync/Ingest.d.ts +2 -1
- package/dist/database/sync/Ingest.js +20 -6
- package/dist/field/code/CodeField.view.js +12 -12
- package/dist/field/link/EntryLink.d.ts +4 -5
- package/dist/field/link/FileLink.d.ts +2 -2
- package/dist/field/link/ImageLink.d.ts +2 -5
- package/dist/field/link/ImageLink.js +0 -1
- package/dist/field/link/LinkField.view.js +18 -15
- package/dist/field/list/ListField.view.js +50 -35
- package/dist/field/metadata/MetadataField.view.js +14 -14
- package/dist/field/richtext/RichTextBlock.js +39 -35
- package/dist/field/richtext/RichTextExtensions.js +23 -23
- package/dist/field/richtext/RichTextField.view.js +6 -6
- package/dist/field/richtext/RichTextInsertMenu.js +5 -5
- package/dist/field/richtext/RichTextToolbar.js +26 -1
- package/dist/field/richtext/Toolbar.d.ts +9 -1
- package/dist/field/richtext/Toolbar.js +12 -2
- package/dist/field/select/SelectField.view.js +1 -0
- package/dist/index.css +174 -23
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/next.d.ts +2 -0
- package/dist/next.edge.d.ts +2 -0
- package/dist/picker/entry/EntryPicker.d.ts +3 -1
- package/dist/picker/entry/EntryPicker.js +15 -1
- package/dist/preview/react.js +3 -0
- package/dist/query.d.ts +26 -14
- package/dist/query.js +4 -0
- package/dist/types.d.ts +8 -5
- package/dist/ui/Stack.d.ts +4 -2
- package/docs/ai-setup.md +3 -3
- package/docs/cli.md +4 -2
- package/docs/components/combo-box.md +105 -16
- package/docs/components/date-field.md +1 -1
- package/docs/components/dialog.md +51 -0
- package/docs/components/form.md +67 -0
- package/docs/components/link.md +10 -0
- package/docs/components/multiple-select.md +9 -17
- package/docs/components/number-field.md +5 -1
- package/docs/components/select.md +9 -20
- package/docs/components/stack.md +42 -0
- package/docs/components/table.md +83 -0
- package/docs/components/text-field.md +9 -1
- package/docs/components/time-field.md +5 -1
- package/docs/components.md +39 -1
- package/docs/configuration.md +3 -1
- package/docs/custom-fields.md +135 -7
- package/docs/deploy/self-host.md +2 -0
- package/docs/deploy.md +7 -4
- package/docs/fields/image.md +0 -1
- package/docs/fields/list.md +19 -0
- package/docs/fields/object.md +5 -3
- package/docs/fields/rich-text.md +31 -0
- package/docs/index.md +8 -6
- package/docs/instant-publishing.md +23 -2
- package/docs/overviews.md +128 -4
- package/docs/query/filtering.md +2 -0
- package/docs/query/related.md +58 -9
- package/docs/query.md +52 -1
- package/docs/quickstart.md +5 -5
- package/docs/schema/document.md +2 -0
- package/docs/schema/type.md +3 -1
- package/docs/upgrading.md +100 -19
- package/llms-full.txt +1022 -125
- package/package.json +1 -1
- package/readme.md +2 -2
- package/dist/chunks/{chunk-4ZA6KZHS.js → chunk-6FQ5IMNA.js} +3 -3
package/dist/.mcp/later/dev.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"url":"http://localhost:1","pid":
|
|
1
|
+
{"url":"http://localhost:1","pid":3063}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"url":"http://localhost:
|
|
1
|
+
{"url":"http://localhost:34865","pid":3063}
|
package/dist/adapter/next/cms.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
decodePreviewRequest
|
|
10
10
|
} from "alinea/backend/resolver/ParsePreview";
|
|
11
11
|
import { dashboardUrl } from "./dashboardUrl.js";
|
|
12
|
-
import { createThrottledSync } from "alinea/backend/util/Syncable";
|
|
12
|
+
import { createThrottledSync, syncWait } from "alinea/backend/util/Syncable";
|
|
13
13
|
import { Client } from "alinea/core/Client";
|
|
14
14
|
import { CMS } from "alinea/core/CMS";
|
|
15
15
|
import {
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import { outcome } from "alinea/core/Outcome";
|
|
19
19
|
import { getScope } from "alinea/core/Scope";
|
|
20
20
|
import { trace } from "alinea/core/Trace";
|
|
21
|
+
import { within } from "alinea/core/util/Async";
|
|
21
22
|
import { getPreviewPayloadFromCookies } from "alinea/preview/PreviewCookies";
|
|
22
23
|
import { Headers } from "@alinea/iso";
|
|
23
24
|
import { cache } from "react";
|
|
@@ -99,7 +100,10 @@ var NextCMS = class extends CMS {
|
|
|
99
100
|
(row) => timed(row, () => this.#syncDb(db, client, void 0))
|
|
100
101
|
);
|
|
101
102
|
try {
|
|
102
|
-
const sha = await
|
|
103
|
+
const sha = await within(
|
|
104
|
+
db.syncWith(client, preValidatedRemote),
|
|
105
|
+
syncWait
|
|
106
|
+
);
|
|
103
107
|
this.#syncedAt = Date.now();
|
|
104
108
|
return sha;
|
|
105
109
|
} catch (error) {
|
|
@@ -15,13 +15,10 @@ async function requestContext(config, request) {
|
|
|
15
15
|
applyAuth: (init) => forwardDevelopmentCredentials(request, apiKey, init)
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
const nodeEnv = process.env.NODE_ENV;
|
|
19
|
-
const baseUrl = Config.baseUrl(config, nodeEnv);
|
|
20
|
-
if (!baseUrl) throw new Error(`Missing baseUrl in config for ${nodeEnv}`);
|
|
21
18
|
const localOrigin = process.env.__NEXT_PRIVATE_ORIGIN;
|
|
22
19
|
return {
|
|
23
20
|
isDev: false,
|
|
24
|
-
handlerUrl:
|
|
21
|
+
handlerUrl: Config.serverHandlerUrl(config),
|
|
25
22
|
apiKey,
|
|
26
23
|
publicUrl: localOrigin ? new URL(localOrigin) : void 0
|
|
27
24
|
};
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
createHandler as createCoreHandler
|
|
12
12
|
} from "alinea/backend/Handler";
|
|
13
|
+
import { committedEntries, removedEntries } from "alinea/backend/CommitEntries";
|
|
13
14
|
import { HandleAction } from "alinea/backend/HandleAction";
|
|
14
15
|
import { JWTPreviews } from "alinea/backend/util/JWTPreviews";
|
|
15
16
|
import { CloudRemote } from "alinea/cloud/CloudRemote";
|
|
@@ -79,18 +80,28 @@ function createHandlerWithDatabase(input, openGeneratedDatabase) {
|
|
|
79
80
|
});
|
|
80
81
|
let sha;
|
|
81
82
|
let user;
|
|
83
|
+
let before = [];
|
|
82
84
|
try {
|
|
83
85
|
user = await devServer.user();
|
|
84
86
|
if (!user) return failure(401, "Unauthorized");
|
|
85
87
|
const adjusted = await options.beforeCommit?.({ mutations, user });
|
|
86
88
|
if (adjusted) mutations = [...adjusted];
|
|
89
|
+
if (options.afterCommit)
|
|
90
|
+
before = await committedEntries(devServer, mutations).catch(() => []);
|
|
87
91
|
({ sha } = await devServer.mutate(mutations));
|
|
88
92
|
} catch (error) {
|
|
89
93
|
if (error instanceof HttpError) return failure(error.code, error.message);
|
|
90
94
|
throw error;
|
|
91
95
|
}
|
|
92
96
|
try {
|
|
93
|
-
await
|
|
97
|
+
const entries = options.afterCommit ? await committedEntries(devServer, mutations) : [];
|
|
98
|
+
await afterCommit({
|
|
99
|
+
mutations,
|
|
100
|
+
sha,
|
|
101
|
+
user,
|
|
102
|
+
entries,
|
|
103
|
+
removed: removedEntries(before, entries)
|
|
104
|
+
});
|
|
94
105
|
} catch (error) {
|
|
95
106
|
console.error("Alinea afterCommit hook failed", error);
|
|
96
107
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DashboardEntry } from 'alinea/core/DashboardEntry';
|
|
2
|
+
import type { Mutation } from 'alinea/core/db/Mutation';
|
|
3
|
+
import type { Resolver } from 'alinea/core/Resolver';
|
|
4
|
+
/** An entry that no longer exists after a commit */
|
|
5
|
+
export interface RemovedEntry {
|
|
6
|
+
id: string;
|
|
7
|
+
type: string;
|
|
8
|
+
locale: string | null;
|
|
9
|
+
/** The url the entry had before the commit */
|
|
10
|
+
url: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The latest version, in every locale, of the entries that mutations change
|
|
14
|
+
*/
|
|
15
|
+
export declare function committedEntries(resolver: Resolver, mutations: ReadonlyArray<Mutation>): Promise<Array<DashboardEntry>>;
|
|
16
|
+
/** The entries of `before` that are not in `after` */
|
|
17
|
+
export declare function removedEntries(before: Array<DashboardEntry>, after: Array<DashboardEntry>): Array<RemovedEntry>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import "../chunks/chunk-CQ52GEHY.js";
|
|
2
|
+
|
|
3
|
+
// src/backend/CommitEntries.ts
|
|
4
|
+
import { Entry } from "alinea/core/Entry";
|
|
5
|
+
var select = {
|
|
6
|
+
id: Entry.id,
|
|
7
|
+
type: Entry.type,
|
|
8
|
+
title: Entry.title,
|
|
9
|
+
path: Entry.path,
|
|
10
|
+
url: Entry.url,
|
|
11
|
+
locale: Entry.locale,
|
|
12
|
+
status: Entry.status,
|
|
13
|
+
workspace: Entry.workspace,
|
|
14
|
+
root: Entry.root,
|
|
15
|
+
parentId: Entry.parentId,
|
|
16
|
+
data: Entry.data
|
|
17
|
+
};
|
|
18
|
+
function entryIds(mutations) {
|
|
19
|
+
const ids = mutations.map((mutation) => "id" in mutation ? mutation.id : "");
|
|
20
|
+
return [...new Set(ids.filter(Boolean))];
|
|
21
|
+
}
|
|
22
|
+
async function committedEntries(resolver, mutations) {
|
|
23
|
+
const ids = entryIds(mutations);
|
|
24
|
+
if (ids.length === 0) return [];
|
|
25
|
+
return await resolver.resolve({
|
|
26
|
+
id: { in: ids },
|
|
27
|
+
status: "preferDraft",
|
|
28
|
+
select
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function removedEntries(before, after) {
|
|
32
|
+
const key = (entry) => `${entry.id}:${entry.locale}`;
|
|
33
|
+
const remaining = new Set(after.map(key));
|
|
34
|
+
return before.filter((entry) => !remaining.has(key(entry))).map(({ id, type, locale, url }) => ({ id, type, locale, url }));
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
committedEntries,
|
|
38
|
+
removedEntries
|
|
39
|
+
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { CMS } from 'alinea/core/CMS';
|
|
2
2
|
import type { AuthedContext, RemoteConnection, RequestContext } from 'alinea/core/Connection';
|
|
3
3
|
import type { LocalStore } from 'alinea/core/db/LocalStore';
|
|
4
|
+
import type { DashboardEntry } from 'alinea/core/DashboardEntry';
|
|
5
|
+
import { type RemovedEntry } from './CommitEntries.js';
|
|
4
6
|
import type { Mutation } from 'alinea/core/db/Mutation';
|
|
5
7
|
import type { User } from 'alinea/core/User';
|
|
6
8
|
export interface Handler {
|
|
@@ -14,6 +16,13 @@ export interface BeforeCommitContext {
|
|
|
14
16
|
}
|
|
15
17
|
export interface AfterCommitContext extends BeforeCommitContext {
|
|
16
18
|
sha: string;
|
|
19
|
+
/**
|
|
20
|
+
* The entries the mutations changed as they are after the commit: the
|
|
21
|
+
* latest version in each locale
|
|
22
|
+
*/
|
|
23
|
+
entries: Array<DashboardEntry>;
|
|
24
|
+
/** Entries that no longer exist after the commit, with the url they had */
|
|
25
|
+
removed: Array<RemovedEntry>;
|
|
17
26
|
}
|
|
18
27
|
export interface HandlerHooks {
|
|
19
28
|
beforeCommit?(context: BeforeCommitContext): HookResponse<ReadonlyArray<Mutation>>;
|
package/dist/backend/Handler.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
object,
|
|
5
5
|
optional,
|
|
6
6
|
string
|
|
7
|
-
} from "../chunks/chunk-
|
|
7
|
+
} from "../chunks/chunk-TWBREZWC.js";
|
|
8
8
|
import {
|
|
9
9
|
PLazy
|
|
10
10
|
} from "../chunks/chunk-IKINPSS5.js";
|
|
@@ -19,6 +19,10 @@ import {
|
|
|
19
19
|
} from "alinea/core/BlobTransport";
|
|
20
20
|
import { Config } from "alinea/core/Config";
|
|
21
21
|
import { developmentKeyHeader } from "alinea/core/Connection";
|
|
22
|
+
import {
|
|
23
|
+
committedEntries,
|
|
24
|
+
removedEntries
|
|
25
|
+
} from "./CommitEntries.js";
|
|
22
26
|
import { Entry } from "alinea/core/Entry";
|
|
23
27
|
import { ErrorCode, HttpError } from "alinea/core/HttpError";
|
|
24
28
|
import { MediaLocation } from "alinea/core/media/MediaLocation";
|
|
@@ -27,15 +31,18 @@ import { assertUploadSize } from "alinea/core/media/UploadLimits";
|
|
|
27
31
|
import { Permission, Policy } from "alinea/core/Role";
|
|
28
32
|
import { getScope } from "alinea/core/Scope";
|
|
29
33
|
import { ShaMismatchError } from "alinea/core/source/ShaMismatchError";
|
|
34
|
+
import { sourceAtTree } from "alinea/core/source/Source";
|
|
30
35
|
import { base64 } from "alinea/core/util/Encoding";
|
|
36
|
+
import { within } from "alinea/core/util/Async";
|
|
31
37
|
import { isRecord } from "alinea/core/util/Objects";
|
|
32
|
-
import {
|
|
38
|
+
import { fileVersions } from "alinea/core/util/EntryFilenames";
|
|
39
|
+
import { isAbsolute, normalize, relative } from "alinea/core/util/Paths";
|
|
33
40
|
import { InvalidCredentialsError, MissingCredentialsError } from "./Auth.js";
|
|
34
41
|
import { HandleAction } from "./HandleAction.js";
|
|
35
42
|
import { proxyMediaUrl } from "./api/ProxyMedia.js";
|
|
36
43
|
import { applyPreview, decodePreviewRequest } from "./resolver/ParsePreview.js";
|
|
37
44
|
import { compressResponse } from "./router/Router.js";
|
|
38
|
-
import { createThrottledSync } from "./util/Syncable.js";
|
|
45
|
+
import { createThrottledSync, syncWait } from "./util/Syncable.js";
|
|
39
46
|
var PrepareBody = object({
|
|
40
47
|
filename: string,
|
|
41
48
|
size: optional(number)
|
|
@@ -53,9 +60,9 @@ function createHandler({
|
|
|
53
60
|
const local = await db;
|
|
54
61
|
const simulateLatency = process.env.ALINEA_LATENCY;
|
|
55
62
|
if (simulateLatency) await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
56
|
-
async function syncForRead(
|
|
63
|
+
async function syncForRead(sync) {
|
|
57
64
|
try {
|
|
58
|
-
await
|
|
65
|
+
await within(sync, syncWait);
|
|
59
66
|
} catch (error) {
|
|
60
67
|
console.warn(
|
|
61
68
|
`Alinea could not sync with the remote, serving current content: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -64,7 +71,7 @@ function createHandler({
|
|
|
64
71
|
}
|
|
65
72
|
async function periodicSync(cnx, syncInterval) {
|
|
66
73
|
if (dev) return;
|
|
67
|
-
return throttle(() =>
|
|
74
|
+
return syncForRead(throttle(() => local.syncWith(cnx), syncInterval));
|
|
68
75
|
}
|
|
69
76
|
try {
|
|
70
77
|
const previews = new JWTPreviews(context.apiKey);
|
|
@@ -258,7 +265,7 @@ function createHandler({
|
|
|
258
265
|
} else {
|
|
259
266
|
const preview = await decodePreviewRequest(query.preview);
|
|
260
267
|
if ("contentHash" in preview && await local.sha !== preview.contentHash)
|
|
261
|
-
await syncForRead(cnx);
|
|
268
|
+
await syncForRead(local.syncWith(cnx));
|
|
262
269
|
query.preview = await applyPreview(local, preview);
|
|
263
270
|
}
|
|
264
271
|
return Response.json(await local.resolve(query) ?? null);
|
|
@@ -278,59 +285,90 @@ function createHandler({
|
|
|
278
285
|
user: user.claims
|
|
279
286
|
});
|
|
280
287
|
if (adjusted) mutations = adjusted;
|
|
288
|
+
const before = hooks.afterCommit ? await committedEntries(local, mutations) : [];
|
|
281
289
|
const attempt = async (retry = 0) => {
|
|
282
290
|
if (retry > 0) await local.syncWith(cnx);
|
|
283
291
|
const request2 = {
|
|
284
292
|
...await local.request(mutations, policy),
|
|
285
293
|
user: user.claims
|
|
286
294
|
};
|
|
295
|
+
let sha2;
|
|
287
296
|
try {
|
|
288
|
-
|
|
289
|
-
if (sha2 === request2.intoSha) {
|
|
290
|
-
await local.write(request2);
|
|
291
|
-
} else {
|
|
292
|
-
sha2 = await local.syncWith(cnx);
|
|
293
|
-
}
|
|
294
|
-
return sha2;
|
|
297
|
+
sha2 = (await cnx.write(request2)).sha;
|
|
295
298
|
} catch (error) {
|
|
296
299
|
const isConflict = error instanceof ShaMismatchError || error instanceof HttpError && error.code === 409 || error instanceof Response && error.status === 409;
|
|
297
300
|
if (isConflict && retry < 3) return attempt(retry + 1);
|
|
298
301
|
throw error;
|
|
299
302
|
}
|
|
303
|
+
try {
|
|
304
|
+
if (sha2 === request2.intoSha) await local.write(request2);
|
|
305
|
+
else sha2 = await local.syncWith(cnx);
|
|
306
|
+
} catch {
|
|
307
|
+
sha2 = await local.syncWith(cnx).catch(() => sha2);
|
|
308
|
+
}
|
|
309
|
+
return sha2;
|
|
300
310
|
};
|
|
301
311
|
const sha = await attempt();
|
|
302
312
|
try {
|
|
303
|
-
|
|
313
|
+
if (hooks.afterCommit) {
|
|
314
|
+
const entries = await committedEntries(local, mutations);
|
|
315
|
+
await hooks.afterCommit({
|
|
316
|
+
mutations,
|
|
317
|
+
sha,
|
|
318
|
+
user: user.claims,
|
|
319
|
+
entries,
|
|
320
|
+
removed: removedEntries(before, entries)
|
|
321
|
+
});
|
|
322
|
+
}
|
|
304
323
|
} catch (error) {
|
|
305
324
|
console.error("Alinea afterCommit hook failed", error);
|
|
306
325
|
}
|
|
307
326
|
return Response.json({ sha });
|
|
308
327
|
}
|
|
309
328
|
if (action === HandleAction.History && request.method === "GET") {
|
|
310
|
-
expectUser();
|
|
329
|
+
const user = expectUser();
|
|
311
330
|
expectJson();
|
|
312
331
|
const file = string(url.searchParams.get("file"));
|
|
313
332
|
const revisionId = string.nullable(url.searchParams.get("revisionId"));
|
|
314
|
-
const
|
|
333
|
+
const record = revisionId ? await cnx.revisionData(file, revisionId) : void 0;
|
|
334
|
+
const contentDir = Config.contentDir(cms.config);
|
|
335
|
+
const owners = () => historyEntries(local, contentDir, file, record);
|
|
336
|
+
let entries = await owners();
|
|
337
|
+
if (entries.length === 0) {
|
|
338
|
+
await syncForRead(local.syncWith(cnx));
|
|
339
|
+
entries = await owners();
|
|
340
|
+
}
|
|
341
|
+
if (entries.length === 0)
|
|
342
|
+
throw new HttpError(ErrorCode.NotFound, "Entry not found");
|
|
343
|
+
const policy = await user.policy;
|
|
344
|
+
for (const entry of entries) policy.assert(Permission.Read, entry);
|
|
345
|
+
const result = revisionId ? record : await cnx.revisions(file);
|
|
315
346
|
return Response.json(result ?? null);
|
|
316
347
|
}
|
|
317
348
|
if (action === HandleAction.Tree && request.method === "GET") {
|
|
318
349
|
expectJson();
|
|
319
350
|
const sha = string(url.searchParams.get("sha"));
|
|
320
|
-
|
|
351
|
+
let source = cnx;
|
|
352
|
+
try {
|
|
353
|
+
const remoteTree = await cnx.getTreeIfDifferent(sha);
|
|
354
|
+
if (!remoteTree) return Response.json(null);
|
|
355
|
+
source = sourceAtTree(cnx, remoteTree);
|
|
356
|
+
} catch {
|
|
357
|
+
}
|
|
358
|
+
await syncForRead(local.syncWith(source));
|
|
321
359
|
const tree = await local.getTreeIfDifferent(sha);
|
|
322
360
|
return compressResponse(request, Response.json(tree ?? null));
|
|
323
361
|
}
|
|
324
362
|
if (action === HandleAction.Sha && request.method === "GET") {
|
|
325
363
|
expectJson();
|
|
326
|
-
await syncForRead(cnx);
|
|
364
|
+
await syncForRead(local.syncWith(cnx));
|
|
327
365
|
return Response.json(await local.sha);
|
|
328
366
|
}
|
|
329
367
|
if (action === HandleAction.Blob && request.method === "POST") {
|
|
330
368
|
const { shas } = object({ shas: array(string) })(await body);
|
|
331
369
|
let tree = await local.source.getTree();
|
|
332
370
|
if (!dev && shas.some((sha) => !tree.hasSha(sha))) {
|
|
333
|
-
await syncForRead(cnx);
|
|
371
|
+
await syncForRead(local.syncWith(cnx));
|
|
334
372
|
tree = await local.source.getTree();
|
|
335
373
|
}
|
|
336
374
|
const fromLocal = [];
|
|
@@ -421,6 +459,28 @@ function createHandler({
|
|
|
421
459
|
}
|
|
422
460
|
};
|
|
423
461
|
}
|
|
462
|
+
var historyResource = {
|
|
463
|
+
id: Entry.id,
|
|
464
|
+
type: Entry.type,
|
|
465
|
+
workspace: Entry.workspace,
|
|
466
|
+
root: Entry.root,
|
|
467
|
+
parents: Entry.parents,
|
|
468
|
+
locale: Entry.locale
|
|
469
|
+
};
|
|
470
|
+
async function historyEntries(local, contentDir, file, record) {
|
|
471
|
+
const filePath = relative(contentDir, file);
|
|
472
|
+
if (filePath.startsWith("../")) return [];
|
|
473
|
+
const status = "all";
|
|
474
|
+
const select = historyResource;
|
|
475
|
+
const atFile = await local.find({
|
|
476
|
+
filePath: { in: fileVersions(filePath) },
|
|
477
|
+
status,
|
|
478
|
+
select
|
|
479
|
+
});
|
|
480
|
+
const id = record?._id;
|
|
481
|
+
if (atFile.length > 0 || typeof id !== "string") return atFile;
|
|
482
|
+
return local.find({ id, status, select });
|
|
483
|
+
}
|
|
424
484
|
function redirectFile(location, cacheControl) {
|
|
425
485
|
return new Response(null, {
|
|
426
486
|
status: 307,
|
|
@@ -42,7 +42,11 @@ var BasicAuth = class {
|
|
|
42
42
|
const [scheme, token] = auth.split(" ", 2);
|
|
43
43
|
if (scheme !== "Basic")
|
|
44
44
|
throw new MissingCredentialsError("Invalid authorization scheme");
|
|
45
|
-
const
|
|
45
|
+
const credentials = atob(token);
|
|
46
|
+
const colon = credentials.indexOf(":");
|
|
47
|
+
if (colon === -1) throw new InvalidCredentialsError("Invalid credentials");
|
|
48
|
+
const username = credentials.slice(0, colon);
|
|
49
|
+
const password = credentials.slice(colon + 1);
|
|
46
50
|
const authorized = await this.#verify(username, password);
|
|
47
51
|
if (!authorized) throw new InvalidCredentialsError("Invalid credentials");
|
|
48
52
|
const user = typeof authorized === "boolean" ? {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
driver_exports
|
|
3
|
-
} from "../../chunks/chunk-
|
|
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";
|
|
@@ -2,12 +2,11 @@ import {
|
|
|
2
2
|
include
|
|
3
3
|
} from "../../chunks/chunk-XTWYBWP2.js";
|
|
4
4
|
import {
|
|
5
|
-
blob,
|
|
6
5
|
id,
|
|
7
6
|
integer,
|
|
8
7
|
text,
|
|
9
8
|
varchar
|
|
10
|
-
} from "../../chunks/chunk-
|
|
9
|
+
} from "../../chunks/chunk-2PP7WJUJ.js";
|
|
11
10
|
import {
|
|
12
11
|
Builder
|
|
13
12
|
} from "../../chunks/chunk-2BJ2IFQM.js";
|
|
@@ -43,11 +42,11 @@ import { slugify } from "alinea/core/util/Slugs";
|
|
|
43
42
|
import { HandleAction } from "../HandleAction.js";
|
|
44
43
|
var UploadTable = table("alinea_upload", {
|
|
45
44
|
entryId: varchar(void 0, { length: 27 }).primaryKey(),
|
|
46
|
-
content:
|
|
45
|
+
content: largeBlob().notNull()
|
|
47
46
|
});
|
|
48
47
|
var UserTable = table("alinea_user", {
|
|
49
48
|
id: id(),
|
|
50
|
-
email:
|
|
49
|
+
email: indexedText().notNull().unique(),
|
|
51
50
|
name: text()
|
|
52
51
|
});
|
|
53
52
|
var UserRoleTable = table("alinea_user_role", {
|
|
@@ -61,7 +60,9 @@ var BlobTable = table("alinea_blob", {
|
|
|
61
60
|
usedAt: integer().notNull()
|
|
62
61
|
});
|
|
63
62
|
var tables = [UploadTable, UserTable, UserRoleTable, BlobTable];
|
|
63
|
+
var maxParams = 100;
|
|
64
64
|
var blobsPerStatement = 50;
|
|
65
|
+
var blobsPerInsert = Math.floor(maxParams / 3);
|
|
65
66
|
var day = 24 * 60 * 60;
|
|
66
67
|
var blobRetention = 30 * day;
|
|
67
68
|
var blobCleanups = /* @__PURE__ */ new WeakMap();
|
|
@@ -141,7 +142,7 @@ var DatabaseApi = class {
|
|
|
141
142
|
const db = await this.#db;
|
|
142
143
|
const now = seconds();
|
|
143
144
|
const unique = [...new Map(blobs)];
|
|
144
|
-
for (const batch of chunks(unique,
|
|
145
|
+
for (const batch of chunks(unique, blobsPerInsert)) {
|
|
145
146
|
const shas = batch.map(([sha]) => sha);
|
|
146
147
|
const stored = new Set(
|
|
147
148
|
await db.select(BlobTable.sha).from(BlobTable).where(inArray(BlobTable.sha, shas))
|
|
@@ -252,6 +253,18 @@ function largeBlob() {
|
|
|
252
253
|
}
|
|
253
254
|
});
|
|
254
255
|
}
|
|
256
|
+
function indexedText() {
|
|
257
|
+
return new Column({
|
|
258
|
+
type: new ColumnType(
|
|
259
|
+
"text",
|
|
260
|
+
[],
|
|
261
|
+
sql.universal({
|
|
262
|
+
mysql: sql`varchar(255)`,
|
|
263
|
+
default: sql`text`
|
|
264
|
+
})
|
|
265
|
+
)
|
|
266
|
+
});
|
|
267
|
+
}
|
|
255
268
|
async function renewBlobs(db, shas, now) {
|
|
256
269
|
if (shas.length === 0) return;
|
|
257
270
|
await db.update(BlobTable).set({ usedAt: now }).where(and(inArray(BlobTable.sha, shas), lt(BlobTable.usedAt, now - day)));
|
|
@@ -3,6 +3,19 @@ import type { EntryRecord } from 'alinea/core/EntryRecord';
|
|
|
3
3
|
import type { CommitRequest } from 'alinea/core/db/CommitRequest';
|
|
4
4
|
import { GithubSource, type GithubSourceOptions } from 'alinea/core/source/GithubSource';
|
|
5
5
|
export interface GithubOptions extends GithubSourceOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Commit as this author instead of the account that owns the token. Such
|
|
8
|
+
* commits are written through the REST API, which GraphQL commits do not
|
|
9
|
+
* allow, so GitHub does not mark them as verified. A save usually spends
|
|
10
|
+
* three REST requests, plus one per uploaded file. Removing a file that is
|
|
11
|
+
* already gone costs one more tree request, and a branch that moved in
|
|
12
|
+
* the meantime repeats the save.
|
|
13
|
+
*/
|
|
14
|
+
author?: GithubCommitAuthor;
|
|
15
|
+
}
|
|
16
|
+
export interface GithubCommitAuthor {
|
|
17
|
+
name: string;
|
|
18
|
+
email: string;
|
|
6
19
|
}
|
|
7
20
|
export declare class GithubApi extends GithubSource implements HistoryApi, CommitApi, SyncApi {
|
|
8
21
|
#private;
|