alinea 2.0.0-preview.12 → 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 +22 -10
- package/dist/adapter/next/context.js +1 -4
- package/dist/adapter/next/dashboardUrl.d.ts +6 -0
- package/dist/adapter/next/dashboardUrl.js +13 -0
- package/dist/adapter/next/handler.js +12 -1
- package/dist/adapter/next/syncCheck.d.ts +15 -2
- package/dist/adapter/next/syncCheck.js +26 -9
- 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 +84 -20
- package/dist/backend/api/BasicAuth.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 +104 -7
- package/dist/backend/api/GithubApi.d.ts +13 -0
- package/dist/backend/api/GithubApi.js +213 -50
- package/dist/backend/api/database.js +6 -6
- package/dist/backend/util/Syncable.d.ts +6 -0
- package/dist/backend/util/Syncable.js +3 -1
- package/dist/bundled.d.ts +1582 -426
- package/dist/chunks/{bun-sqlite-5SHXN3AT.js → bun-sqlite-RI6BYU5S.js} +3 -3
- package/dist/chunks/{chunk-ASYKC6IE.js → chunk-2PP7WJUJ.js} +2 -12
- package/dist/chunks/{chunk-QJGGYF6E.js → chunk-6FQ5IMNA.js} +7 -7
- package/dist/chunks/{chunk-JOF663EG.js → chunk-7DDPSWRY.js} +465 -2
- 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-PMGIIYDD.js} +5830 -5830
- package/dist/chunks/{chunk-AIU6CMVT.js → chunk-PTYZMV5G.js} +1 -1
- package/dist/chunks/{chunk-66YZ47TY.js → chunk-RAR44T5O.js} +1 -1
- package/dist/chunks/{chunk-OHGAVV4G.js → chunk-SCV3TEXM.js} +2 -3
- package/dist/chunks/{chunk-UPY2RUZF.js → chunk-TWBREZWC.js} +30 -0
- 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/Generate.js +20 -7
- package/dist/cli/Mcp.js +1 -1
- package/dist/cli/Serve.js +1 -1
- package/dist/cli/bin.js +2 -3
- 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/cli/serve/mcp/McpTools.js +29 -1
- package/dist/cloud/CloudRemote.js +21 -7
- package/dist/cms.d.ts +4 -2
- package/dist/components/Alert.js +3 -3
- package/dist/components/AsyncList.d.ts +52 -0
- package/dist/components/AsyncList.js +69 -0
- package/dist/components/Badge.d.ts +3 -1
- package/dist/components/Badge.js +5 -3
- package/dist/components/Breadcrumbs.js +4 -4
- package/dist/components/Button.d.ts +6 -1
- package/dist/components/Button.js +14 -5
- package/dist/components/Calendar.js +20 -20
- package/dist/components/Checkbox.js +10 -10
- 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 +4 -4
- package/dist/components/ComboBox.d.ts +27 -9
- package/dist/components/ComboBox.js +42 -11
- 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 +3 -3
- 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 +36 -9
- package/dist/components/DropZone.js +5 -5
- package/dist/components/DropdownMenu.js +9 -9
- package/dist/components/Empty.js +4 -4
- package/dist/components/Field.d.ts +1 -1
- package/dist/components/Field.js +17 -11
- 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 +24 -24
- package/dist/components/MediaPreview.js +7 -7
- package/dist/components/MultipleSelect.d.ts +13 -8
- package/dist/components/MultipleSelect.js +21 -21
- package/dist/components/NavRail.js +6 -6
- 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 +6 -6
- 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 +10 -8
- package/dist/components/Select.d.ts +13 -10
- package/dist/components/Select.js +16 -30
- package/dist/components/Sheet.d.ts +50 -0
- package/dist/components/Sheet.js +202 -0
- package/dist/components/Sidebar.js +5 -5
- package/dist/components/SortableList.d.ts +34 -30
- package/dist/components/SortableList.js +160 -263
- 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 +2 -2
- package/dist/components/Switch.js +6 -6
- package/dist/components/Table.d.ts +14 -2
- package/dist/components/Table.js +90 -51
- package/dist/components/Tabs.js +4 -4
- package/dist/components/TagGroup.js +10 -10
- package/dist/components/TextField.d.ts +7 -1
- package/dist/components/TextField.js +19 -10
- package/dist/components/TimeField.d.ts +6 -1
- package/dist/components/TimeField.js +48 -23
- 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 +6 -5
- package/dist/components/Tree.js +24 -24
- package/dist/components/internal/DateControl.js +9 -9
- 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 +7 -7
- package/dist/components/internal/Locale.js +2 -2
- package/dist/components/internal/PopoverSurface.js +3 -2
- package/dist/components/internal/SelectionCheckbox.js +6 -6
- 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/sortableDrag.d.ts +19 -0
- package/dist/components/internal/sortableDrag.js +175 -0
- package/dist/components/internal/useDragDrop.js +2 -2
- package/dist/components/types.d.ts +4 -0
- package/dist/components.d.ts +4 -0
- package/dist/components.js +4 -0
- package/dist/core/Client.js +12 -1
- package/dist/core/Config.d.ts +12 -1
- 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 +9 -1
- package/dist/core/Document.js +40 -8
- 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 +76 -9
- 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/TextDoc.d.ts +4 -0
- package/dist/core/Type.d.ts +25 -6
- package/dist/core/Type.js +25 -7
- package/dist/core/Validation.d.ts +0 -1
- package/dist/core/Validation.js +0 -6
- package/dist/core/db/LocalStore.d.ts +7 -0
- package/dist/core/field/CreateField.d.ts +1 -2
- 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/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 +3 -2
- package/dist/dashboard/App.js +24 -6
- package/dist/dashboard/Href.d.ts +37 -0
- package/dist/dashboard/Href.js +46 -0
- package/dist/dashboard/ViewKeys.d.ts +1 -0
- package/dist/dashboard/ViewKeys.js +1 -0
- package/dist/dashboard/app/ActivityStatus.js +34 -12
- package/dist/dashboard/app/AuthView.js +6 -6
- package/dist/dashboard/app/BlockSettings.d.ts +9 -0
- package/dist/dashboard/app/BlockSettings.js +16 -0
- 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 +14 -14
- package/dist/dashboard/app/CroppedImage.js +2 -2
- package/dist/dashboard/app/DashboardLayout.d.ts +1 -1
- package/dist/dashboard/app/DashboardLayout.js +15 -1
- 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 +24 -3
- package/dist/dashboard/app/EntryHeader.js +45 -36
- package/dist/dashboard/app/EntryReferences.js +5 -5
- package/dist/dashboard/app/EntrySidebar.js +24 -22
- package/dist/dashboard/app/EntrySidebarPreview.js +2 -2
- 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 +17 -17
- package/dist/dashboard/app/Explorer.d.ts +3 -1
- package/dist/dashboard/app/Explorer.js +90 -47
- package/dist/dashboard/app/ExplorerBatchActions.d.ts +3 -1
- package/dist/dashboard/app/ExplorerBatchActions.js +70 -45
- package/dist/dashboard/app/ExplorerCards.js +7 -1
- package/dist/dashboard/app/ExplorerControls.js +4 -4
- package/dist/dashboard/app/ExplorerList.js +7 -7
- package/dist/dashboard/app/ExplorerModal.js +4 -4
- package/dist/dashboard/app/ExplorerOverviewHeader.d.ts +6 -0
- package/dist/dashboard/app/ExplorerOverviewHeader.js +28 -0
- package/dist/dashboard/app/ExplorerTable.js +11 -8
- package/dist/dashboard/app/ExplorerTabs.d.ts +12 -0
- package/dist/dashboard/app/ExplorerTabs.js +42 -0
- package/dist/dashboard/app/ImageEditor.js +26 -26
- package/dist/dashboard/app/ImagePicker.js +3 -13
- package/dist/dashboard/app/LinkPicker.js +27 -25
- package/dist/dashboard/app/LocaleMenu.js +7 -7
- package/dist/dashboard/app/MediaPreviewCell.js +4 -4
- package/dist/dashboard/app/MoveConfirmDialog.d.ts +5 -0
- package/dist/dashboard/app/MoveConfirmDialog.js +79 -0
- package/dist/dashboard/app/OverviewCell.js +11 -14
- 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} +72 -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/SettingsTab.d.ts +6 -0
- package/dist/dashboard/app/SettingsTab.js +27 -0
- package/dist/dashboard/app/SidebarTree.js +26 -71
- package/dist/dashboard/app/WorkspaceMenu.js +9 -9
- package/dist/dashboard/app/WorkspaceRoots.js +108 -104
- package/dist/dashboard/app/editor/FileEditor.js +17 -17
- 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 +65 -43
- package/dist/dashboard/app/pages/RootPage.js +3 -3
- package/dist/dashboard/app/pages/SplashPage.js +91 -76
- package/dist/dashboard/app/pages/UsersPage.js +11 -11
- package/dist/dashboard/app/ui/DashboardModal.js +14 -13
- 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 +9 -1
- package/dist/dashboard/atoms/editor.d.ts +7 -1
- package/dist/dashboard/atoms/editor.js +14 -2
- package/dist/dashboard/atoms/entry.js +1 -14
- package/dist/dashboard/atoms/entryTable.js +1 -0
- package/dist/dashboard/atoms/explorer.d.ts +19 -0
- package/dist/dashboard/atoms/explorer.js +62 -21
- package/dist/dashboard/atoms/graph.d.ts +5 -0
- package/dist/dashboard/atoms/graph.js +27 -2
- 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 +22 -12
- package/dist/dashboard/atoms/nav.js +200 -65
- package/dist/dashboard/atoms/overview.d.ts +32 -9
- package/dist/dashboard/atoms/overview.js +43 -28
- 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 +48 -27
- package/dist/dashboard/atoms/user.js +1 -1
- package/dist/dashboard/atoms/utils.js +1 -1
- package/dist/dashboard/boot/ActivityEvent.d.ts +1 -1
- package/dist/dashboard/boot/Boot.js +31 -17
- package/dist/dashboard/boot/DashboardWorker.d.ts +7 -0
- package/dist/dashboard/boot/DashboardWorker.js +99 -54
- package/dist/dashboard/boot/LoadWorker.js +20 -10
- 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 +6 -3
- 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.d.ts +12 -62
- package/dist/database/BrowserEntryStore.js +116 -193
- package/dist/database/DatabaseTables.js +2 -2
- package/dist/database/EntryDatabase.d.ts +10 -1
- package/dist/database/EntryDatabase.js +45 -4
- package/dist/database/EntryStore.js +1 -11
- 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.d.ts +5 -3
- package/dist/database/entry/EntryTable.js +26 -9
- package/dist/database/query/Condition.d.ts +4 -1
- package/dist/database/query/Condition.js +16 -5
- package/dist/database/query/EntryQuery.js +60 -8
- package/dist/database/query/Relation.d.ts +13 -1
- package/dist/database/query/Relation.js +20 -2
- package/dist/database/query/Search.d.ts +7 -1
- package/dist/database/query/Search.js +55 -10
- 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 +13 -13
- package/dist/field/link/EntryLink.d.ts +8 -9
- package/dist/field/link/FileLink.d.ts +6 -6
- package/dist/field/link/ImageLink.d.ts +6 -9
- package/dist/field/link/ImageLink.js +0 -1
- 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 +251 -374
- 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 +356 -299
- 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 +24 -20
- package/dist/field/richtext/RichTextBlock.d.ts +3 -1
- package/dist/field/richtext/RichTextBlock.js +181 -74
- package/dist/field/richtext/RichTextBlockHost.js +1 -1
- package/dist/field/richtext/RichTextExtensions.js +28 -28
- package/dist/field/richtext/RichTextField.view.js +10 -4
- package/dist/field/richtext/RichTextInsertMenu.js +45 -29
- 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/field/views.js +2 -0
- package/dist/index.css +664 -332
- 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/picker/url/UrlPicker.js +1 -0
- 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/badge.md +1 -0
- package/docs/components/button.md +1 -0
- package/docs/components/combo-box.md +105 -16
- package/docs/components/content-card.md +1 -0
- 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/search-field.md +1 -0
- package/docs/components/select.md +9 -20
- package/docs/components/sortable-list.md +5 -20
- package/docs/components/stack.md +42 -0
- package/docs/components/table.md +84 -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 +4 -2
- package/docs/custom-fields.md +135 -7
- package/docs/deploy/self-host.md +3 -1
- package/docs/deploy.md +9 -5
- 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/fields.md +1 -1
- package/docs/index.md +9 -7
- package/docs/instant-publishing.md +17 -11
- package/docs/overviews.md +131 -8
- 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 +101 -19
- package/llms-full.txt +1036 -163
- package/package.json +2 -3
- package/readme.md +2 -2
- 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
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
|
@@ -8,23 +8,24 @@ import {
|
|
|
8
8
|
applyPreview as applyPreviewUpdate,
|
|
9
9
|
decodePreviewRequest
|
|
10
10
|
} from "alinea/backend/resolver/ParsePreview";
|
|
11
|
-
import {
|
|
11
|
+
import { dashboardUrl } from "./dashboardUrl.js";
|
|
12
|
+
import { createThrottledSync, syncWait } from "alinea/backend/util/Syncable";
|
|
12
13
|
import { Client } from "alinea/core/Client";
|
|
13
14
|
import { CMS } from "alinea/core/CMS";
|
|
14
|
-
import { Config } from "alinea/core/Config";
|
|
15
15
|
import {
|
|
16
16
|
withDevelopmentKey
|
|
17
17
|
} from "alinea/core/Connection";
|
|
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";
|
|
24
25
|
import { requestContext } from "./context.js";
|
|
25
26
|
import { cachedResolve, developmentRevision } from "./devCache.js";
|
|
26
27
|
import { RenderStats, summarizeQuery, timed } from "./renderStats.js";
|
|
27
|
-
import { syncIfStale } from "./syncCheck.js";
|
|
28
|
+
import { contentShaWindow, syncIfStale, tagContentSha } from "./syncCheck.js";
|
|
28
29
|
import { jsx } from "react/jsx-runtime";
|
|
29
30
|
var preValidatedRemote = { validate: false };
|
|
30
31
|
var NextCMS = class extends CMS {
|
|
@@ -66,6 +67,10 @@ var NextCMS = class extends CMS {
|
|
|
66
67
|
* returns a fresh holder, so nothing is recorded there.
|
|
67
68
|
*/
|
|
68
69
|
#render = cache(() => ({}));
|
|
70
|
+
/** Build renders join the content sha tag once, see `tagContentSha`. */
|
|
71
|
+
#tagContent = cache(
|
|
72
|
+
() => tagContentSha(contentShaWindow(this.config.syncInterval))
|
|
73
|
+
);
|
|
69
74
|
/** One content revision per render, see `developmentRevision`. */
|
|
70
75
|
#devRevision = cache(() => developmentRevision());
|
|
71
76
|
#isDraft = cache(async () => {
|
|
@@ -95,7 +100,10 @@ var NextCMS = class extends CMS {
|
|
|
95
100
|
(row) => timed(row, () => this.#syncDb(db, client, void 0))
|
|
96
101
|
);
|
|
97
102
|
try {
|
|
98
|
-
const sha = await
|
|
103
|
+
const sha = await within(
|
|
104
|
+
db.syncWith(client, preValidatedRemote),
|
|
105
|
+
syncWait
|
|
106
|
+
);
|
|
99
107
|
this.#syncedAt = Date.now();
|
|
100
108
|
return sha;
|
|
101
109
|
} catch (error) {
|
|
@@ -201,12 +209,19 @@ var NextCMS = class extends CMS {
|
|
|
201
209
|
const answer = () => stats && row ? stats.executed(row, () => db.resolve(request)) : db.resolve(request);
|
|
202
210
|
const syncInterval = request.disableSync ? Number.POSITIVE_INFINITY : request.syncInterval ?? this.config.syncInterval;
|
|
203
211
|
if (hasPreview) return answer();
|
|
204
|
-
if (
|
|
212
|
+
if (isBuild) {
|
|
213
|
+
await this.#tagContent();
|
|
214
|
+
} else {
|
|
205
215
|
const tracked = {
|
|
206
216
|
sha: db.sha,
|
|
207
217
|
syncWith: () => this.#syncDb(db, client, stats)
|
|
208
218
|
};
|
|
209
|
-
const settled = !isDraft && await syncIfStale(
|
|
219
|
+
const settled = !isDraft && await syncIfStale(
|
|
220
|
+
tracked,
|
|
221
|
+
client,
|
|
222
|
+
syncInterval,
|
|
223
|
+
contentShaWindow(this.config.syncInterval)
|
|
224
|
+
);
|
|
210
225
|
if (!settled)
|
|
211
226
|
await this.throttle(() => this.#syncDb(db, client, stats), syncInterval);
|
|
212
227
|
}
|
|
@@ -264,16 +279,13 @@ var NextCMS = class extends CMS {
|
|
|
264
279
|
if (!stats) return null;
|
|
265
280
|
const { default: dynamic } = await import("next/dynamic.js");
|
|
266
281
|
const { isDev, handlerUrl } = await requestContext(this.config);
|
|
267
|
-
let adminPath = Config.adminPath(this.config);
|
|
268
|
-
if (!adminPath.startsWith("/")) adminPath = `/${adminPath}`;
|
|
269
|
-
const dashboardUrl = isDev ? adminPath : new URL(`${adminPath}.html`, handlerUrl).href;
|
|
270
282
|
const NextPreviews = dynamic(() => import("./previews.js"), {
|
|
271
283
|
ssr: false
|
|
272
284
|
});
|
|
273
285
|
return /* @__PURE__ */ jsx(
|
|
274
286
|
NextPreviews,
|
|
275
287
|
{
|
|
276
|
-
dashboardUrl,
|
|
288
|
+
dashboardUrl: dashboardUrl(this.config, isDev, handlerUrl),
|
|
277
289
|
widget,
|
|
278
290
|
stats: widget && showStats ? stats.settled() : void 0,
|
|
279
291
|
workspace,
|
|
@@ -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
|
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Config } from 'alinea/core/Config';
|
|
2
|
+
/**
|
|
3
|
+
* The dashboard as the site serves it: in development the site proxies the
|
|
4
|
+
* admin path to the dev server, in production it serves the generated file.
|
|
5
|
+
*/
|
|
6
|
+
export declare function dashboardUrl(config: Config, isDev: boolean, handlerUrl: URL | string): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import "../../chunks/chunk-CQ52GEHY.js";
|
|
2
|
+
|
|
3
|
+
// src/adapter/next/dashboardUrl.ts
|
|
4
|
+
import { Config } from "alinea/core/Config";
|
|
5
|
+
function dashboardUrl(config, isDev, handlerUrl) {
|
|
6
|
+
if (!isDev)
|
|
7
|
+
return new URL(`/${Config.dashboardFile(config)}`, handlerUrl).href;
|
|
8
|
+
const adminPath = Config.adminPath(config);
|
|
9
|
+
return adminPath.startsWith("/") ? adminPath : `/${adminPath}`;
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
dashboardUrl
|
|
13
|
+
};
|
|
@@ -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
|
}
|
|
@@ -6,13 +6,26 @@ interface SyncableDB {
|
|
|
6
6
|
syncWith(source: RemoteSource, options?: SyncOptions): Promise<string>;
|
|
7
7
|
}
|
|
8
8
|
export declare const CONTENT_SHA_TAG = "alinea-content-sha";
|
|
9
|
+
/**
|
|
10
|
+
* How long renders share the latest content sha. Configured through the
|
|
11
|
+
* config's `syncInterval`, never a query's: all renders share one entry.
|
|
12
|
+
*/
|
|
13
|
+
export declare function contentShaWindow(syncInterval?: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* Tag a static render with the content sha without looking it up. Renders at
|
|
16
|
+
* build skip `syncIfStale`, so this is how their pages join the tag that
|
|
17
|
+
* commits revalidate, and get the same time-based safety net as pages
|
|
18
|
+
* rendered at runtime.
|
|
19
|
+
*/
|
|
20
|
+
export declare function tagContentSha(revalidate?: number): Promise<void>;
|
|
9
21
|
export declare function revalidateContentSha(): Promise<void>;
|
|
10
22
|
/**
|
|
11
23
|
* Sync the isolate-local db only when it is behind the shared content sha.
|
|
12
24
|
* Returns true when the freshness question is settled here (synced, forced,
|
|
13
25
|
* or explicitly disabled) and the caller can skip its throttled sync.
|
|
14
26
|
* Returns false when the sha could not be determined, so the caller falls
|
|
15
|
-
* back to its existing throttle behavior.
|
|
27
|
+
* back to its existing throttle behavior. `shaWindow` is how long renders
|
|
28
|
+
* share the latest sha, see `contentShaWindow`.
|
|
16
29
|
*/
|
|
17
|
-
export declare function syncIfStale(db: SyncableDB, client: Client, syncInterval?: number, options?: SyncOptions): Promise<boolean>;
|
|
30
|
+
export declare function syncIfStale(db: SyncableDB, client: Client, syncInterval?: number, shaWindow?: number, options?: SyncOptions): Promise<boolean>;
|
|
18
31
|
export {};
|
|
@@ -2,22 +2,27 @@ import "../../chunks/chunk-CQ52GEHY.js";
|
|
|
2
2
|
|
|
3
3
|
// src/adapter/next/syncCheck.ts
|
|
4
4
|
var CONTENT_SHA_TAG = "alinea-content-sha";
|
|
5
|
-
var SHA_REVALIDATE_SECONDS =
|
|
5
|
+
var SHA_REVALIDATE_SECONDS = 300;
|
|
6
|
+
function contentShaWindow(syncInterval) {
|
|
7
|
+
if (syncInterval !== void 0 && syncInterval > 0 && Number.isFinite(syncInterval))
|
|
8
|
+
return syncInterval;
|
|
9
|
+
return SHA_REVALIDATE_SECONDS;
|
|
10
|
+
}
|
|
6
11
|
var inflight;
|
|
7
|
-
function latestSha(client) {
|
|
12
|
+
function latestSha(client, revalidate) {
|
|
8
13
|
if (inflight) return inflight;
|
|
9
|
-
inflight = loadLatestSha(client).finally(() => {
|
|
14
|
+
inflight = loadLatestSha(client, revalidate).finally(() => {
|
|
10
15
|
inflight = void 0;
|
|
11
16
|
});
|
|
12
17
|
return inflight;
|
|
13
18
|
}
|
|
14
|
-
async function loadLatestSha(client) {
|
|
19
|
+
async function loadLatestSha(client, revalidate) {
|
|
15
20
|
try {
|
|
16
21
|
const { unstable_cache } = await import("next/cache.js");
|
|
17
22
|
const getSha = unstable_cache(
|
|
18
23
|
() => client.getSha(),
|
|
19
24
|
["alinea-content-sha"],
|
|
20
|
-
{ revalidate
|
|
25
|
+
{ revalidate, tags: [CONTENT_SHA_TAG] }
|
|
21
26
|
);
|
|
22
27
|
return await getSha();
|
|
23
28
|
} catch {
|
|
@@ -28,26 +33,38 @@ async function loadLatestSha(client) {
|
|
|
28
33
|
}
|
|
29
34
|
}
|
|
30
35
|
}
|
|
36
|
+
async function tagContentSha(revalidate = SHA_REVALIDATE_SECONDS) {
|
|
37
|
+
try {
|
|
38
|
+
const { unstable_cache } = await import("next/cache.js");
|
|
39
|
+
await unstable_cache(async () => true, ["alinea-content-sha-tag"], {
|
|
40
|
+
revalidate,
|
|
41
|
+
tags: [CONTENT_SHA_TAG]
|
|
42
|
+
})();
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
}
|
|
31
46
|
async function revalidateContentSha() {
|
|
32
47
|
try {
|
|
33
48
|
const { revalidateTag } = await import("next/cache.js");
|
|
34
|
-
revalidateTag(CONTENT_SHA_TAG,
|
|
49
|
+
revalidateTag(CONTENT_SHA_TAG, "max");
|
|
35
50
|
} catch {
|
|
36
51
|
}
|
|
37
52
|
}
|
|
38
|
-
async function syncIfStale(db, client, syncInterval, options) {
|
|
53
|
+
async function syncIfStale(db, client, syncInterval, shaWindow = SHA_REVALIDATE_SECONDS, options) {
|
|
39
54
|
if (syncInterval === Number.POSITIVE_INFINITY) return true;
|
|
40
55
|
if (syncInterval === 0) {
|
|
41
56
|
await db.syncWith(client, options);
|
|
42
57
|
return true;
|
|
43
58
|
}
|
|
44
|
-
const expected = await latestSha(client);
|
|
59
|
+
const expected = await latestSha(client, shaWindow);
|
|
45
60
|
if (!expected) return false;
|
|
46
61
|
if (expected !== await db.sha) await db.syncWith(client, options);
|
|
47
62
|
return true;
|
|
48
63
|
}
|
|
49
64
|
export {
|
|
50
65
|
CONTENT_SHA_TAG,
|
|
66
|
+
contentShaWindow,
|
|
51
67
|
revalidateContentSha,
|
|
52
|
-
syncIfStale
|
|
68
|
+
syncIfStale,
|
|
69
|
+
tagContentSha
|
|
53
70
|
};
|
|
@@ -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,57 +285,92 @@ 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();
|
|
370
|
+
if (!dev && shas.some((sha) => !tree.hasSha(sha))) {
|
|
371
|
+
await syncForRead(local.syncWith(cnx));
|
|
372
|
+
tree = await local.source.getTree();
|
|
373
|
+
}
|
|
332
374
|
const fromLocal = [];
|
|
333
375
|
const fromRemote = [];
|
|
334
376
|
for (const sha of shas) {
|
|
@@ -417,6 +459,28 @@ function createHandler({
|
|
|
417
459
|
}
|
|
418
460
|
};
|
|
419
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
|
+
}
|
|
420
484
|
function redirectFile(location, cacheControl) {
|
|
421
485
|
return new Response(null, {
|
|
422
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" ? {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CommitApi, SyncApi } from 'alinea/core/Connection';
|
|
2
|
+
/**
|
|
3
|
+
* Content blobs kept where every handler instance finds them, such as the
|
|
4
|
+
* backend database: an instance that is behind reads what another one
|
|
5
|
+
* fetched or committed, instead of fetching it from the remote again.
|
|
6
|
+
*/
|
|
7
|
+
export interface BlobStore {
|
|
8
|
+
/** The stored blobs among `shas`. */
|
|
9
|
+
readBlobs(shas: ReadonlyArray<string>): Promise<Map<string, Uint8Array>>;
|
|
10
|
+
/** Keep blobs, found again by their sha. */
|
|
11
|
+
storeBlobs(blobs: ReadonlyArray<[sha: string, blob: Uint8Array]>): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export declare function isBlobStore<T extends object>(value: T): value is T & BlobStore;
|
|
14
|
+
/**
|
|
15
|
+
* Answer from the store what it has, and fetch the rest from the remote,
|
|
16
|
+
* storing it. The store only saves requests: when it fails, the remote
|
|
17
|
+
* answers instead.
|
|
18
|
+
*/
|
|
19
|
+
export declare function storedBlobs(store: BlobStore, getBlobs: SyncApi['getBlobs']): SyncApi['getBlobs'];
|
|
20
|
+
/** Commit through the remote, and store the committed content. */
|
|
21
|
+
export declare function storedWrites(store: BlobStore, write: CommitApi['write']): CommitApi['write'];
|