@jini-ai/admin 0.3.9 → 0.4.0
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/CHANGELOG.md +58 -0
- package/README.md +71 -37
- package/dist/agent-plugins/adapters/http.d.ts +7 -0
- package/dist/agent-plugins/adapters/http.js +15 -0
- package/dist/agent-plugins/adapters/memory.d.ts +8 -0
- package/dist/agent-plugins/adapters/memory.js +25 -0
- package/dist/agent-plugins/agent-plugins.module.d.ts +51 -0
- package/dist/agent-plugins/agent-plugins.module.js +6 -0
- package/dist/agent-plugins/conformance/agent-plugins-api.conformance.d.ts +12 -0
- package/dist/agent-plugins/conformance/agent-plugins-api.conformance.js +47 -0
- package/dist/agent-plugins/controllers/agent-plugin-files.controller.d.ts +20 -0
- package/dist/agent-plugins/controllers/agent-plugin-files.controller.js +43 -0
- package/dist/agent-plugins/controllers/agent-plugins.controller.d.ts +25 -0
- package/dist/agent-plugins/controllers/agent-plugins.controller.js +91 -0
- package/dist/agent-plugins/index.d.ts +9 -0
- package/dist/agent-plugins/index.js +9 -0
- package/dist/agent-plugins/messages.en.d.ts +23 -0
- package/dist/agent-plugins/messages.en.js +14 -0
- package/dist/agent-plugins/models.d.ts +57 -0
- package/dist/agent-plugins/models.js +2 -0
- package/dist/agent-plugins/package-file-tree.d.ts +111 -0
- package/dist/agent-plugins/package-file-tree.js +188 -0
- package/dist/agent-plugins/ports.d.ts +18 -0
- package/dist/agent-plugins/ports.js +3 -0
- package/dist/agent-plugins/react/components/AgentPluginFileContent.d.ts +5 -0
- package/dist/agent-plugins/react/components/AgentPluginFileContent.js +9 -0
- package/dist/agent-plugins/react/components/AgentPluginFileTree.d.ts +9 -0
- package/dist/agent-plugins/react/components/AgentPluginFileTree.js +7 -0
- package/dist/agent-plugins/react/components/AgentPluginInspector.d.ts +3 -0
- package/dist/agent-plugins/react/components/AgentPluginInspector.js +11 -0
- package/dist/agent-plugins/react/components/AgentPluginListPanel.d.ts +6 -0
- package/dist/agent-plugins/react/components/AgentPluginListPanel.js +10 -0
- package/dist/agent-plugins/react/components/AgentPluginRow.d.ts +5 -0
- package/dist/agent-plugins/react/components/AgentPluginRow.js +8 -0
- package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.d.ts +90 -0
- package/dist/agent-plugins/react/hooks/AgentPluginInspector.hooks.js +74 -0
- package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.d.ts +67 -0
- package/dist/agent-plugins/react/hooks/AgentPluginPanel.hooks.js +50 -0
- package/dist/agent-plugins/react/hooks/AgentPluginRow.hooks.d.ts +55 -0
- package/dist/agent-plugins/react/hooks/AgentPluginRow.hooks.js +6 -0
- package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.d.ts +64 -0
- package/dist/agent-plugins/react/hooks/AgentPluginsPage.hooks.js +39 -0
- package/dist/agent-plugins/react/hooks/AgentPluginsPorts.hooks.d.ts +7 -0
- package/dist/agent-plugins/react/hooks/AgentPluginsPorts.hooks.js +10 -0
- package/dist/agent-plugins/react/hooks/MarketplaceTab.hooks.d.ts +6 -0
- package/dist/agent-plugins/react/hooks/MarketplaceTab.hooks.js +3 -0
- package/dist/agent-plugins/react/index.d.ts +245 -0
- package/dist/agent-plugins/react/index.js +13 -0
- package/dist/agent-plugins/react/pages/AgentPluginsPage.d.ts +4 -0
- package/dist/agent-plugins/react/pages/AgentPluginsPage.js +12 -0
- package/dist/agent-plugins/react/tabs/DownloadedTab.d.ts +4 -0
- package/dist/agent-plugins/react/tabs/DownloadedTab.js +5 -0
- package/dist/agent-plugins/react/tabs/InstalledTab.d.ts +4 -0
- package/dist/agent-plugins/react/tabs/InstalledTab.js +5 -0
- package/dist/agent-plugins/react/tabs/MarketplaceTab.d.ts +4 -0
- package/dist/agent-plugins/react/tabs/MarketplaceTab.js +10 -0
- package/dist/agent-plugins/rules.d.ts +33 -0
- package/dist/agent-plugins/rules.js +51 -0
- package/dist/browser/index.d.ts +1 -0
- package/dist/browser/index.js +1 -0
- package/dist/browser/navigation.d.ts +19 -4
- package/dist/browser/navigation.js +8 -8
- package/dist/browser/shell-navigation.d.ts +12 -0
- package/dist/browser/shell-navigation.js +16 -0
- package/dist/contracts/credential-management.d.ts +8 -0
- package/dist/contracts/credential-management.js +3 -0
- package/dist/contracts/media-picker.d.ts +17 -0
- package/dist/contracts/media-picker.js +5 -0
- package/dist/contracts/playground-render-target.d.ts +18 -0
- package/dist/contracts/playground-render-target.js +3 -0
- package/dist/core/entities/rules.d.ts +138 -0
- package/dist/core/entities/rules.js +344 -0
- package/dist/core/gated/types.d.ts +8 -4
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +1 -0
- package/dist/core/manifest/rules.d.ts +24 -6
- package/dist/core/manifest/rules.js +12 -11
- package/dist/core/module/controller-store.d.ts +22 -0
- package/dist/core/module/controller-store.js +32 -0
- package/dist/core/module/create-admin.d.ts +41 -0
- package/dist/core/module/create-admin.js +187 -0
- package/dist/core/module/define-admin-module.d.ts +4 -0
- package/dist/core/module/define-admin-module.js +5 -0
- package/dist/core/module/index.d.ts +7 -0
- package/dist/core/module/index.js +4 -0
- package/dist/core/module/token.d.ts +19 -0
- package/dist/core/module/token.js +4 -0
- package/dist/core/module/types.d.ts +79 -0
- package/dist/core/module/types.js +2 -0
- package/dist/core/permissions/rules.d.ts +4 -1
- package/dist/core/permissions/rules.js +1 -1
- package/dist/core/ports/analytics.d.ts +1 -1
- package/dist/core/ports/auth.d.ts +2 -2
- package/dist/core/ports/comments.d.ts +9 -6
- package/dist/core/ports/database.d.ts +13 -10
- package/dist/core/ports/entities.d.ts +302 -0
- package/dist/core/ports/entities.js +47 -0
- package/dist/core/ports/forms.d.ts +19 -7
- package/dist/core/ports/identity.d.ts +42 -17
- package/dist/core/ports/index.d.ts +2 -0
- package/dist/core/ports/integrations.d.ts +9 -4
- package/dist/core/ports/media.d.ts +16 -7
- package/dist/core/ports/members.d.ts +7 -3
- package/dist/core/ports/menus.d.ts +43 -0
- package/dist/core/ports/menus.js +43 -0
- package/dist/core/ports/plugins.d.ts +3 -2
- package/dist/core/ports/recovery.d.ts +10 -6
- package/dist/core/ports/redirects.d.ts +17 -7
- package/dist/core/ports/seo.d.ts +12 -6
- package/dist/core/ports/shell.d.ts +41 -0
- package/dist/core/ports/shell.js +2 -0
- package/dist/core/ports/workspace.d.ts +3 -3
- package/dist/core/routing/rules.d.ts +17 -4
- package/dist/core/routing/rules.js +4 -4
- package/dist/core/transport/errors.d.ts +13 -4
- package/dist/core/transport/errors.js +4 -4
- package/dist/core/transport/http.d.ts +26 -16
- package/dist/core/transport/http.js +59 -21
- package/dist/core/transport/types.d.ts +26 -14
- package/dist/core/transport/types.js +9 -7
- package/dist/media/adapters/http.d.ts +27 -0
- package/dist/media/adapters/http.js +44 -0
- package/dist/media/adapters/memory.d.ts +11 -0
- package/dist/media/adapters/memory.js +139 -0
- package/dist/media/conformance/media-api.conformance.d.ts +7 -0
- package/dist/media/conformance/media-api.conformance.js +69 -0
- package/dist/media/controllers/edit-media.controller.d.ts +24 -0
- package/dist/media/controllers/edit-media.controller.js +69 -0
- package/dist/media/controllers/library.controller.d.ts +47 -0
- package/dist/media/controllers/library.controller.js +173 -0
- package/dist/media/controllers/providers.controller.d.ts +30 -0
- package/dist/media/controllers/providers.controller.js +76 -0
- package/dist/media/html-attributes.d.ts +82 -0
- package/dist/media/html-attributes.js +204 -0
- package/dist/media/index.d.ts +12 -0
- package/dist/media/index.js +9 -0
- package/dist/media/media.module.d.ts +83 -0
- package/dist/media/media.module.js +43 -0
- package/dist/media/messages.en.d.ts +24 -0
- package/dist/media/messages.en.js +24 -0
- package/dist/media/models.d.ts +72 -0
- package/dist/media/models.js +2 -0
- package/dist/media/ports.d.ts +75 -0
- package/dist/media/ports.js +7 -0
- package/dist/media/react/components/EditMediaPanel.d.ts +3 -0
- package/dist/media/react/components/EditMediaPanel.js +10 -0
- package/dist/media/react/components/MediaCard.d.ts +5 -0
- package/dist/media/react/components/MediaCard.js +17 -0
- package/dist/media/react/components/MediaDialog.d.ts +3 -0
- package/dist/media/react/components/MediaDialog.js +8 -0
- package/dist/media/react/components/MediaGrid.d.ts +4 -0
- package/dist/media/react/components/MediaGrid.js +8 -0
- package/dist/media/react/components/MediaIcons.d.ts +14 -0
- package/dist/media/react/components/MediaIcons.js +39 -0
- package/dist/media/react/components/MediaLightbox.d.ts +5 -0
- package/dist/media/react/components/MediaLightbox.js +12 -0
- package/dist/media/react/components/MediaPicker.d.ts +3 -0
- package/dist/media/react/components/MediaPicker.js +10 -0
- package/dist/media/react/components/MediaPurgeDialog.d.ts +4 -0
- package/dist/media/react/components/MediaPurgeDialog.js +9 -0
- package/dist/media/react/components/UploadButton.d.ts +3 -0
- package/dist/media/react/components/UploadButton.js +8 -0
- package/dist/media/react/hooks/EditMediaPanel.hooks.d.ts +84 -0
- package/dist/media/react/hooks/EditMediaPanel.hooks.js +105 -0
- package/dist/media/react/hooks/LibraryTab.hooks.d.ts +103 -0
- package/dist/media/react/hooks/LibraryTab.hooks.js +164 -0
- package/dist/media/react/hooks/MediaCard.hooks.d.ts +79 -0
- package/dist/media/react/hooks/MediaCard.hooks.js +63 -0
- package/dist/media/react/hooks/MediaDialog.hooks.d.ts +10 -0
- package/dist/media/react/hooks/MediaDialog.hooks.js +20 -0
- package/dist/media/react/hooks/MediaGrid.hooks.d.ts +28 -0
- package/dist/media/react/hooks/MediaGrid.hooks.js +6 -0
- package/dist/media/react/hooks/MediaLightbox.hooks.d.ts +27 -0
- package/dist/media/react/hooks/MediaLightbox.hooks.js +64 -0
- package/dist/media/react/hooks/MediaPage.hooks.d.ts +68 -0
- package/dist/media/react/hooks/MediaPage.hooks.js +68 -0
- package/dist/media/react/hooks/MediaPicker.hooks.d.ts +26 -0
- package/dist/media/react/hooks/MediaPicker.hooks.js +25 -0
- package/dist/media/react/hooks/MediaPorts.hooks.d.ts +9 -0
- package/dist/media/react/hooks/MediaPorts.hooks.js +10 -0
- package/dist/media/react/hooks/MediaPurgeDialog.hooks.d.ts +22 -0
- package/dist/media/react/hooks/MediaPurgeDialog.hooks.js +6 -0
- package/dist/media/react/hooks/ProvidersTab.hooks.d.ts +55 -0
- package/dist/media/react/hooks/ProvidersTab.hooks.js +124 -0
- package/dist/media/react/hooks/UploadButton.hooks.d.ts +48 -0
- package/dist/media/react/hooks/UploadButton.hooks.js +118 -0
- package/dist/media/react/index.d.ts +373 -0
- package/dist/media/react/index.js +73 -0
- package/dist/media/react/pages/MediaPage.d.ts +4 -0
- package/dist/media/react/pages/MediaPage.js +9 -0
- package/dist/media/react/tabs/LibraryTab.d.ts +4 -0
- package/dist/media/react/tabs/LibraryTab.js +14 -0
- package/dist/media/react/tabs/ProvidersTab.d.ts +4 -0
- package/dist/media/react/tabs/ProvidersTab.js +10 -0
- package/dist/media/rules.d.ts +80 -0
- package/dist/media/rules.js +127 -0
- package/dist/media/styles.d.ts +4 -0
- package/dist/media/styles.js +5 -0
- package/dist/playground/adapters/http.d.ts +8 -0
- package/dist/playground/adapters/http.js +4 -0
- package/dist/playground/adapters/memory.d.ts +10 -0
- package/dist/playground/adapters/memory.js +30 -0
- package/dist/playground/conformance/playground-api.conformance.d.ts +6 -0
- package/dist/playground/conformance/playground-api.conformance.js +37 -0
- package/dist/playground/controllers/playground.controller.d.ts +16 -0
- package/dist/playground/controllers/playground.controller.js +21 -0
- package/dist/playground/index.d.ts +7 -0
- package/dist/playground/index.js +5 -0
- package/dist/playground/messages.en.d.ts +9 -0
- package/dist/playground/messages.en.js +2 -0
- package/dist/playground/models.d.ts +4 -0
- package/dist/playground/models.js +2 -0
- package/dist/playground/playground.module.d.ts +31 -0
- package/dist/playground/playground.module.js +5 -0
- package/dist/playground/ports.d.ts +5 -0
- package/dist/playground/ports.js +2 -0
- package/dist/playground/react/components/Canvas.d.ts +3 -0
- package/dist/playground/react/components/Canvas.js +9 -0
- package/dist/playground/react/hooks/Canvas.hooks.d.ts +7 -0
- package/dist/playground/react/hooks/Canvas.hooks.js +18 -0
- package/dist/playground/react/hooks/PlaygroundPage.hooks.d.ts +8 -0
- package/dist/playground/react/hooks/PlaygroundPage.hooks.js +2 -0
- package/dist/playground/react/hooks/PlaygroundPorts.hooks.d.ts +4 -0
- package/dist/playground/react/hooks/PlaygroundPorts.hooks.js +6 -0
- package/dist/playground/react/index.d.ts +136 -0
- package/dist/playground/react/index.js +10 -0
- package/dist/playground/react/pages/PlaygroundPage.d.ts +4 -0
- package/dist/playground/react/pages/PlaygroundPage.js +11 -0
- package/dist/playground/react/tabs/CanvasTab.d.ts +4 -0
- package/dist/playground/react/tabs/CanvasTab.js +5 -0
- package/dist/playground/rules.d.ts +4 -0
- package/dist/playground/rules.js +2 -0
- package/dist/react/OverlayHost.d.ts +3 -0
- package/dist/react/OverlayHost.hooks.d.ts +9 -0
- package/dist/react/OverlayHost.hooks.js +9 -0
- package/dist/react/OverlayHost.js +7 -0
- package/dist/react/bind-react.d.ts +46 -0
- package/dist/react/bind-react.js +28 -0
- package/dist/react/components/ConfirmButton/ConfirmButton.hooks.d.ts +7 -1
- package/dist/react/components/ConfirmButton/ConfirmButton.hooks.js +2 -2
- package/dist/react/components/ConfirmButton/ConfirmButton.js +1 -1
- package/dist/react/components/ConfirmDialog/ConfirmDialog.d.ts +15 -3
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.d.ts +17 -3
- package/dist/react/components/ConfirmDialog/ConfirmDialog.hooks.js +3 -3
- package/dist/react/components/ConfirmDialog/ConfirmDialog.js +6 -6
- package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.d.ts +6 -2
- package/dist/react/components/InteractiveHtmlEditor/InteractiveHtmlEditor.js +7 -8
- package/dist/react/components/RowMenu/RowMenu.d.ts +5 -1
- package/dist/react/components/RowMenu/RowMenu.hooks.d.ts +8 -2
- package/dist/react/components/RowMenu/RowMenu.hooks.js +4 -4
- package/dist/react/components/RowMenu/RowMenu.js +6 -6
- package/dist/react/components/Sidebar.d.ts +1 -1
- package/dist/react/components/Sidebar.js +103 -7
- package/dist/react/entities/EntityCell.d.ts +10 -0
- package/dist/react/entities/EntityCell.js +18 -0
- package/dist/react/entities/EntityDetail.d.ts +3 -0
- package/dist/react/entities/EntityDetail.js +21 -0
- package/dist/react/entities/EntityEdit.d.ts +13 -0
- package/dist/react/entities/EntityEdit.js +45 -0
- package/dist/react/entities/EntityField.d.ts +20 -0
- package/dist/react/entities/EntityField.js +31 -0
- package/dist/react/entities/EntityIndex.d.ts +3 -0
- package/dist/react/entities/EntityIndex.js +10 -0
- package/dist/react/entities/EntityList.d.ts +7 -0
- package/dist/react/entities/EntityList.js +52 -0
- package/dist/react/entities/contribution.d.ts +26 -0
- package/dist/react/entities/contribution.js +78 -0
- package/dist/react/entities/index.d.ts +10 -0
- package/dist/react/entities/index.js +7 -0
- package/dist/react/entities/rules.d.ts +179 -0
- package/dist/react/entities/rules.js +224 -0
- package/dist/react/entities/types.d.ts +45 -0
- package/dist/react/entities/types.js +5 -0
- package/dist/react/entities/use-entity-edit.d.ts +35 -0
- package/dist/react/entities/use-entity-edit.js +98 -0
- package/dist/react/entities/use-entity-read.d.ts +22 -0
- package/dist/react/entities/use-entity-read.js +27 -0
- package/dist/react/hooks/use-nav-sections.d.ts +12 -3
- package/dist/react/hooks/use-nav-sections.js +14 -14
- package/dist/react/hooks/use-sidebar-rail.d.ts +7 -1
- package/dist/react/hooks/use-sidebar-rail.js +11 -11
- package/dist/react/index.d.ts +3 -1
- package/dist/react/index.js +5 -2
- package/dist/react/kit/Kit.hooks.d.ts +13 -0
- package/dist/react/kit/Kit.hooks.js +10 -0
- package/dist/react/kit/Native.hooks.d.ts +38 -0
- package/dist/react/kit/Native.hooks.js +65 -0
- package/dist/react/kit/index.d.ts +12 -0
- package/dist/react/kit/index.js +21 -0
- package/dist/react/kit/native.d.ts +3 -0
- package/dist/react/kit/native.js +21 -0
- package/dist/react/kit/types.d.ts +40 -0
- package/dist/react/kit/types.js +2 -0
- package/dist/react/overlay.d.ts +36 -0
- package/dist/react/overlay.js +35 -0
- package/dist/react/overlays.d.ts +38 -0
- package/dist/react/overlays.js +46 -0
- package/dist/react/shell/AdminShell.d.ts +9 -0
- package/dist/react/shell/AdminShell.hooks.d.ts +15 -0
- package/dist/react/shell/AdminShell.hooks.js +27 -0
- package/dist/react/shell/AdminShell.js +46 -0
- package/dist/react/shell/index.d.ts +7 -0
- package/dist/react/shell/index.js +5 -0
- package/dist/react/shell/model.d.ts +25 -0
- package/dist/react/shell/model.js +29 -0
- package/dist/react/shell/theme-ports.d.ts +28 -0
- package/dist/react/shell/theme-ports.js +2 -0
- package/dist/react/shell/types.d.ts +99 -0
- package/dist/react/shell/types.js +2 -0
- package/dist/react/shell/use-admin-shell-session.d.ts +13 -0
- package/dist/react/shell/use-admin-shell-session.js +81 -0
- package/dist/react/shell/use-admin-theme.d.ts +16 -0
- package/dist/react/shell/use-admin-theme.js +57 -0
- package/dist/react/types.d.ts +6 -4
- package/dist/react/types.js +2 -2
- package/dist/react/use-controller.d.ts +16 -0
- package/dist/react/use-controller.js +18 -0
- package/dist/security/adapters/http.d.ts +16 -0
- package/dist/security/adapters/http.js +94 -0
- package/dist/security/adapters/memory.d.ts +15 -0
- package/dist/security/adapters/memory.js +60 -0
- package/dist/security/conformance/security-api.conformance.d.ts +10 -0
- package/dist/security/conformance/security-api.conformance.js +48 -0
- package/dist/security/controllers/access-tokens.controller.d.ts +44 -0
- package/dist/security/controllers/access-tokens.controller.js +112 -0
- package/dist/security/controllers/other-credentials.controller.d.ts +30 -0
- package/dist/security/controllers/other-credentials.controller.js +74 -0
- package/dist/security/controllers/root-key.controller.d.ts +35 -0
- package/dist/security/controllers/root-key.controller.js +90 -0
- package/dist/security/index.d.ts +9 -0
- package/dist/security/index.js +9 -0
- package/dist/security/messages.en.d.ts +12 -0
- package/dist/security/messages.en.js +2 -0
- package/dist/security/models.d.ts +112 -0
- package/dist/security/models.js +2 -0
- package/dist/security/ports.d.ts +58 -0
- package/dist/security/ports.js +5 -0
- package/dist/security/react/components/CredentialEditor.d.ts +3 -0
- package/dist/security/react/components/CredentialEditor.js +8 -0
- package/dist/security/react/hooks/AccessTokensTab.hooks.d.ts +94 -0
- package/dist/security/react/hooks/AccessTokensTab.hooks.js +36 -0
- package/dist/security/react/hooks/CredentialEditor.hooks.d.ts +49 -0
- package/dist/security/react/hooks/CredentialEditor.hooks.js +14 -0
- package/dist/security/react/hooks/RootKeyTab.hooks.d.ts +29 -0
- package/dist/security/react/hooks/RootKeyTab.hooks.js +19 -0
- package/dist/security/react/hooks/SecurityPage.hooks.d.ts +17 -0
- package/dist/security/react/hooks/SecurityPage.hooks.js +12 -0
- package/dist/security/react/hooks/SecurityPorts.hooks.d.ts +9 -0
- package/dist/security/react/hooks/SecurityPorts.hooks.js +6 -0
- package/dist/security/react/index.d.ts +215 -0
- package/dist/security/react/index.js +10 -0
- package/dist/security/react/pages/SecurityPage.d.ts +4 -0
- package/dist/security/react/pages/SecurityPage.js +10 -0
- package/dist/security/react/tabs/AccessTokensTab.d.ts +4 -0
- package/dist/security/react/tabs/AccessTokensTab.js +10 -0
- package/dist/security/react/tabs/RootKeyTab.d.ts +4 -0
- package/dist/security/react/tabs/RootKeyTab.js +9 -0
- package/dist/security/rules.d.ts +57 -0
- package/dist/security/rules.js +63 -0
- package/dist/security/security.module.d.ts +45 -0
- package/dist/security/security.module.js +5 -0
- package/dist/source-control/adapters/http.d.ts +6 -0
- package/dist/source-control/adapters/http.js +13 -0
- package/dist/source-control/adapters/memory.d.ts +9 -0
- package/dist/source-control/adapters/memory.js +35 -0
- package/dist/source-control/conformance/source-control-api.conformance.d.ts +10 -0
- package/dist/source-control/conformance/source-control-api.conformance.js +48 -0
- package/dist/source-control/controllers/source-control.controller.d.ts +26 -0
- package/dist/source-control/controllers/source-control.controller.js +70 -0
- package/dist/source-control/index.d.ts +8 -0
- package/dist/source-control/index.js +6 -0
- package/dist/source-control/messages.en.d.ts +11 -0
- package/dist/source-control/messages.en.js +2 -0
- package/dist/source-control/models.d.ts +70 -0
- package/dist/source-control/models.js +2 -0
- package/dist/source-control/ports.d.ts +23 -0
- package/dist/source-control/ports.js +3 -0
- package/dist/source-control/react/components/SourceControlCredentialFields.d.ts +5 -0
- package/dist/source-control/react/components/SourceControlCredentialFields.js +8 -0
- package/dist/source-control/react/components/SourceControlProviderRow.d.ts +5 -0
- package/dist/source-control/react/components/SourceControlProviderRow.js +7 -0
- package/dist/source-control/react/hooks/ProvidersTab.hooks.d.ts +45 -0
- package/dist/source-control/react/hooks/ProvidersTab.hooks.js +20 -0
- package/dist/source-control/react/hooks/SourceControlCredentialFields.hooks.d.ts +43 -0
- package/dist/source-control/react/hooks/SourceControlCredentialFields.hooks.js +9 -0
- package/dist/source-control/react/hooks/SourceControlPage.hooks.d.ts +17 -0
- package/dist/source-control/react/hooks/SourceControlPage.hooks.js +5 -0
- package/dist/source-control/react/hooks/SourceControlPorts.hooks.d.ts +9 -0
- package/dist/source-control/react/hooks/SourceControlPorts.hooks.js +6 -0
- package/dist/source-control/react/index.d.ts +156 -0
- package/dist/source-control/react/index.js +10 -0
- package/dist/source-control/react/pages/SourceControlPage.d.ts +4 -0
- package/dist/source-control/react/pages/SourceControlPage.js +11 -0
- package/dist/source-control/react/tabs/ProvidersTab.d.ts +4 -0
- package/dist/source-control/react/tabs/ProvidersTab.js +10 -0
- package/dist/source-control/rules.d.ts +32 -0
- package/dist/source-control/rules.js +35 -0
- package/dist/source-control/source-control.module.d.ts +36 -0
- package/dist/source-control/source-control.module.js +6 -0
- package/package.json +207 -4
|
@@ -13,23 +13,25 @@
|
|
|
13
13
|
* factory over it — makes both work:
|
|
14
14
|
*
|
|
15
15
|
* ```ts
|
|
16
|
-
* const transport = createHttpTransport({ baseUrl: '/api/admin/v1' });
|
|
16
|
+
* const transport = createHttpTransport({ baseUrl: '/api/admin/v1', fetch: ({ url }, init) => fetch(url, init) });
|
|
17
17
|
*
|
|
18
|
-
* const client = createAdminClient(transport, {
|
|
19
|
-
* identity:
|
|
20
|
-
* media:
|
|
18
|
+
* const client = createAdminClient({ transport, groups: {
|
|
19
|
+
* identity: createHostIdentityRoutes, // host implementation of AdminIdentityPort
|
|
20
|
+
* media: createHostMediaRoutes, // host implementation of AdminMediaPort
|
|
21
21
|
* posts: createHostPostRoutes, // the host's own, same signature
|
|
22
22
|
* widgets: createHostWidgetRoutes, // the host's own
|
|
23
|
-
* });
|
|
23
|
+
* } });
|
|
24
24
|
*
|
|
25
|
-
* await client.identity.listUsers();
|
|
26
|
-
* await client.posts.list();
|
|
25
|
+
* await client.identity.listUsers({}); // host adapter over Jini's port contract
|
|
26
|
+
* await client.posts.list({}); // the host's own port
|
|
27
27
|
* ```
|
|
28
28
|
*
|
|
29
29
|
* Both halves share one auth policy, one error class, one place to add retries or tracing. A host
|
|
30
30
|
* adding a route is a factory plus one line in the map — it never edits a Jini file, and it never
|
|
31
31
|
* grows a second `fetch` wrapper with its own subtly different error handling, which is exactly
|
|
32
32
|
* how `api.ts` reached 1,548 lines.
|
|
33
|
+
* This package ships port contracts and the assembler, not backend route factories;
|
|
34
|
+
* the host supplies each factory and may share it with other hosts without changing this seam.
|
|
33
35
|
*
|
|
34
36
|
* ## Why `AdminTransport` is an interface and not just a function
|
|
35
37
|
*
|
|
@@ -51,25 +53,35 @@ export interface AdminTransport {
|
|
|
51
53
|
* @param path Route-group-relative path, always starting with `/` (`/users`, `/media/abc`).
|
|
52
54
|
* The base is the transport's concern — a route group that embeds a base is not portable to
|
|
53
55
|
* a host that mounts the admin API somewhere else.
|
|
54
|
-
* @param init Standard `RequestInit
|
|
55
|
-
* cannot honour, but must respect `method` and `body`.
|
|
56
|
+
* @param init Standard `RequestInit` plus an optional success observer. A transport that is not
|
|
57
|
+
* HTTP-backed may ignore fields it cannot honour, but must respect `method` and `body`.
|
|
56
58
|
* @throws {AdminApiError} on any non-success response.
|
|
57
59
|
*/
|
|
58
|
-
request<T>(
|
|
60
|
+
request<T>(requiredArgs: {
|
|
61
|
+
readonly path: string;
|
|
62
|
+
}, optionalArgs?: RequestInit & {
|
|
63
|
+
/** Called only on success with the response metadata, after parsing and before resolving.
|
|
64
|
+
* Reading ETag here lets route adapters send If-Match without duplicating transport logic. */
|
|
65
|
+
readonly onOk?: (args: {
|
|
66
|
+
readonly response: Response;
|
|
67
|
+
}) => void;
|
|
68
|
+
}): Promise<T>;
|
|
59
69
|
}
|
|
60
70
|
/**
|
|
61
71
|
* A route group: turns a transport into a typed set of operations.
|
|
62
72
|
*
|
|
63
|
-
*
|
|
64
|
-
* the identical signature — that symmetry is what lets
|
|
73
|
+
* Host implementations of this package's ports and a host's own route groups use
|
|
74
|
+
* the identical signature — that symmetry is what lets shared and host-owned groups sit in
|
|
65
75
|
* the same client with no adapter between them.
|
|
66
76
|
*/
|
|
67
|
-
export type AdminRouteGroupFactory<TPort> = (
|
|
77
|
+
export type AdminRouteGroupFactory<TPort> = (requiredArgs: {
|
|
78
|
+
readonly transport: AdminTransport;
|
|
79
|
+
}) => TPort;
|
|
68
80
|
/**
|
|
69
81
|
* The assembled client: one object carrying every wired route group.
|
|
70
82
|
*
|
|
71
83
|
* `TGroups` is inferred from the factory map handed to `createAdminClient`, so a host's own
|
|
72
|
-
* groups are as type-safe as
|
|
84
|
+
* groups are as type-safe as shared ones with no declaration merging or module augmentation.
|
|
73
85
|
*/
|
|
74
86
|
export type AdminClient<TGroups extends Record<string, AdminRouteGroupFactory<unknown>>> = {
|
|
75
87
|
readonly [K in keyof TGroups]: ReturnType<TGroups[K]>;
|
|
@@ -13,23 +13,25 @@
|
|
|
13
13
|
* factory over it — makes both work:
|
|
14
14
|
*
|
|
15
15
|
* ```ts
|
|
16
|
-
* const transport = createHttpTransport({ baseUrl: '/api/admin/v1' });
|
|
16
|
+
* const transport = createHttpTransport({ baseUrl: '/api/admin/v1', fetch: ({ url }, init) => fetch(url, init) });
|
|
17
17
|
*
|
|
18
|
-
* const client = createAdminClient(transport, {
|
|
19
|
-
* identity:
|
|
20
|
-
* media:
|
|
18
|
+
* const client = createAdminClient({ transport, groups: {
|
|
19
|
+
* identity: createHostIdentityRoutes, // host implementation of AdminIdentityPort
|
|
20
|
+
* media: createHostMediaRoutes, // host implementation of AdminMediaPort
|
|
21
21
|
* posts: createHostPostRoutes, // the host's own, same signature
|
|
22
22
|
* widgets: createHostWidgetRoutes, // the host's own
|
|
23
|
-
* });
|
|
23
|
+
* } });
|
|
24
24
|
*
|
|
25
|
-
* await client.identity.listUsers();
|
|
26
|
-
* await client.posts.list();
|
|
25
|
+
* await client.identity.listUsers({}); // host adapter over Jini's port contract
|
|
26
|
+
* await client.posts.list({}); // the host's own port
|
|
27
27
|
* ```
|
|
28
28
|
*
|
|
29
29
|
* Both halves share one auth policy, one error class, one place to add retries or tracing. A host
|
|
30
30
|
* adding a route is a factory plus one line in the map — it never edits a Jini file, and it never
|
|
31
31
|
* grows a second `fetch` wrapper with its own subtly different error handling, which is exactly
|
|
32
32
|
* how `api.ts` reached 1,548 lines.
|
|
33
|
+
* This package ships port contracts and the assembler, not backend route factories;
|
|
34
|
+
* the host supplies each factory and may share it with other hosts without changing this seam.
|
|
33
35
|
*
|
|
34
36
|
* ## Why `AdminTransport` is an interface and not just a function
|
|
35
37
|
*
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { MediaApiPort } from '../ports.js';
|
|
2
|
+
import type { MediaRequestOptions } from '../models.js';
|
|
3
|
+
/** Authentication, workspace addressing, transport retries and error decoding belong to the host.
|
|
4
|
+
* Retryable errors remain intact so the library can recover automatically after an outage.
|
|
5
|
+
* The adapter never reaches for global fetch, tokens or a hardcoded workspace. */
|
|
6
|
+
export interface MediaTransportPort {
|
|
7
|
+
request<T>(required: {
|
|
8
|
+
path: string;
|
|
9
|
+
method: 'GET' | 'POST' | 'PATCH' | 'DELETE';
|
|
10
|
+
body?: unknown;
|
|
11
|
+
}, optional?: MediaRequestOptions): Promise<T>;
|
|
12
|
+
url(required: {
|
|
13
|
+
path: string;
|
|
14
|
+
}, optional?: Record<string, never>): string;
|
|
15
|
+
}
|
|
16
|
+
export declare function createHttpMediaApi({ transport, basePath }: {
|
|
17
|
+
transport: MediaTransportPort;
|
|
18
|
+
basePath: string;
|
|
19
|
+
}, { replacePath, restorePath, }?: {
|
|
20
|
+
replacePath?: (required: {
|
|
21
|
+
id: string;
|
|
22
|
+
}, optional?: Record<string, never>) => string;
|
|
23
|
+
restorePath?: (required: {
|
|
24
|
+
id: string;
|
|
25
|
+
}, optional?: Record<string, never>) => string;
|
|
26
|
+
}): MediaApiPort;
|
|
27
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { queryMedia } from '../rules.js';
|
|
2
|
+
export function createHttpMediaApi({ transport, basePath }, { replacePath, restorePath, } = {}) {
|
|
3
|
+
const base = basePath.replace(/\/$/, '');
|
|
4
|
+
const path = (id) => `${base}/${encodeURIComponent(id)}`;
|
|
5
|
+
async function asset(method, target, body, options) {
|
|
6
|
+
options.signal?.throwIfAborted();
|
|
7
|
+
const response = await transport.request({ path: target, method, ...(body === undefined ? {} : { body }) }, options);
|
|
8
|
+
return response.media;
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
replaceSupported: !!replacePath,
|
|
12
|
+
restoreSupported: !!restorePath,
|
|
13
|
+
async list(query, options = {}) {
|
|
14
|
+
options.signal?.throwIfAborted();
|
|
15
|
+
const response = await transport.request({ path: base, method: 'GET' }, options);
|
|
16
|
+
return queryMedia({ media: response.media, query });
|
|
17
|
+
},
|
|
18
|
+
upload(input, { signal, ...metadata } = {}) {
|
|
19
|
+
return asset('POST', base, { ...input, ...metadata }, signal ? { signal } : {});
|
|
20
|
+
},
|
|
21
|
+
update({ id, patch }, options = {}) {
|
|
22
|
+
return asset('PATCH', path(id), patch, options);
|
|
23
|
+
},
|
|
24
|
+
...(replacePath ? { replace({ id, upload }, options = {}) {
|
|
25
|
+
return asset('POST', replacePath({ id }), upload, options);
|
|
26
|
+
} } : {}),
|
|
27
|
+
trash({ id }, options = {}) {
|
|
28
|
+
return asset('POST', `${path(id)}/trash`, undefined, options);
|
|
29
|
+
},
|
|
30
|
+
...(restorePath ? { restore({ id }, options = {}) {
|
|
31
|
+
return asset('POST', restorePath({ id }), undefined, options);
|
|
32
|
+
} } : {}),
|
|
33
|
+
delete({ id }, options = {}) {
|
|
34
|
+
options.signal?.throwIfAborted();
|
|
35
|
+
return transport.request({ path: path(id), method: 'DELETE' }, options);
|
|
36
|
+
},
|
|
37
|
+
originalUrl({ id, version }, _optional = {}) {
|
|
38
|
+
// Replacement preserves the id; the refreshed version must identify its new bytes.
|
|
39
|
+
const revision = version === undefined ? '' : `?v=${encodeURIComponent(version)}`;
|
|
40
|
+
return transport.url({ path: `${path(id)}/original${revision}` });
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { MediaApiPort, MediaProvidersPort } from '../ports.js';
|
|
2
|
+
import type { MediaAsset, MediaProvider } from '../models.js';
|
|
3
|
+
export declare function createMemoryMediaApi({ assets }: {
|
|
4
|
+
assets?: readonly MediaAsset[];
|
|
5
|
+
}, { now }?: {
|
|
6
|
+
now?: () => string;
|
|
7
|
+
}): MediaApiPort;
|
|
8
|
+
export declare function createMemoryMediaProviders({ providers }: {
|
|
9
|
+
providers?: readonly MediaProvider[];
|
|
10
|
+
}, _optional?: Record<string, never>): MediaProvidersPort;
|
|
11
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { queryMedia } from '../rules.js';
|
|
2
|
+
export function createMemoryMediaApi({ assets = [] }, { now = () => new Date().toISOString() } = {}) {
|
|
3
|
+
const items = new Map(assets.map((item) => [item.id, Object.freeze({ ...item })]));
|
|
4
|
+
let sequence = 0;
|
|
5
|
+
function check(options) {
|
|
6
|
+
options.signal?.throwIfAborted();
|
|
7
|
+
}
|
|
8
|
+
function get(id) {
|
|
9
|
+
const item = items.get(id);
|
|
10
|
+
if (!item)
|
|
11
|
+
throw new Error(`Media not found: ${id}`);
|
|
12
|
+
return item;
|
|
13
|
+
}
|
|
14
|
+
function put(item) {
|
|
15
|
+
const frozen = Object.freeze(item);
|
|
16
|
+
items.set(item.id, frozen);
|
|
17
|
+
return frozen;
|
|
18
|
+
}
|
|
19
|
+
function content(input) {
|
|
20
|
+
// The REAL server ignores the declared string and stores sniffContentType(bytes) instead.
|
|
21
|
+
// This fake has no bytes to sniff — its input is an opaque base64 string a test made up —
|
|
22
|
+
// so it echoes what the caller declared. Tests needing disagreement must seed assets directly.
|
|
23
|
+
return { byteSize: Math.max(0, Math.floor(input.dataBase64.length * 3 / 4) - (input.dataBase64.match(/=+$/)?.[0].length ?? 0)), contentType: input.contentType, sha256: `memory-content-${++sequence}` };
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
replaceSupported: true,
|
|
27
|
+
restoreSupported: true,
|
|
28
|
+
async list(query, options = {}) {
|
|
29
|
+
check(options);
|
|
30
|
+
return Object.freeze(queryMedia({ media: [...items.values()], query }));
|
|
31
|
+
},
|
|
32
|
+
async upload(input, options = {}) {
|
|
33
|
+
check(options);
|
|
34
|
+
let id;
|
|
35
|
+
do {
|
|
36
|
+
id = `memory-${++sequence}`;
|
|
37
|
+
} while (items.has(id));
|
|
38
|
+
const timestamp = now();
|
|
39
|
+
return put({
|
|
40
|
+
id,
|
|
41
|
+
title: options.title ?? input.filename,
|
|
42
|
+
alt: options.alt ?? '',
|
|
43
|
+
caption: options.caption ?? '',
|
|
44
|
+
credit: options.credit ?? '',
|
|
45
|
+
slug: options.slug ?? id,
|
|
46
|
+
width: options.width ?? null,
|
|
47
|
+
height: options.height ?? null,
|
|
48
|
+
cssClass: options.cssClass ?? null,
|
|
49
|
+
htmlAttributes: options.htmlAttributes ?? null,
|
|
50
|
+
status: 'active',
|
|
51
|
+
publicUrl: null,
|
|
52
|
+
createdAt: timestamp,
|
|
53
|
+
updatedAt: timestamp,
|
|
54
|
+
version: 1,
|
|
55
|
+
...content(input),
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
async update({ id, patch }, options = {}) {
|
|
59
|
+
check(options);
|
|
60
|
+
const previous = get(id);
|
|
61
|
+
const metadata = Object.fromEntries(Object.entries(patch).filter(([key, value]) => ['title', 'slug', 'alt', 'caption', 'credit', 'width', 'height', 'cssClass', 'htmlAttributes'].includes(key) && value !== undefined));
|
|
62
|
+
return put({ ...previous, ...metadata, version: previous.version + 1, updatedAt: now() });
|
|
63
|
+
},
|
|
64
|
+
async replace({ id, upload }, options = {}) {
|
|
65
|
+
check(options);
|
|
66
|
+
const previous = get(id);
|
|
67
|
+
if (previous.status !== 'active')
|
|
68
|
+
throw new Error('Cannot replace trashed media');
|
|
69
|
+
return put({
|
|
70
|
+
...previous,
|
|
71
|
+
...content(upload),
|
|
72
|
+
version: previous.version + 1,
|
|
73
|
+
updatedAt: now(),
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
async trash({ id }, options = {}) {
|
|
77
|
+
check(options);
|
|
78
|
+
const previous = get(id);
|
|
79
|
+
return put({
|
|
80
|
+
...previous,
|
|
81
|
+
status: 'trashed',
|
|
82
|
+
publicUrl: null,
|
|
83
|
+
version: previous.version + 1,
|
|
84
|
+
updatedAt: now(),
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
async restore({ id }, options = {}) {
|
|
88
|
+
check(options);
|
|
89
|
+
const previous = get(id);
|
|
90
|
+
return put({ ...previous, status: 'active', version: previous.version + 1, updatedAt: now() });
|
|
91
|
+
},
|
|
92
|
+
async delete({ id }, options = {}) {
|
|
93
|
+
check(options);
|
|
94
|
+
if (get(id).status !== 'trashed')
|
|
95
|
+
throw new Error('Trash media before permanent deletion');
|
|
96
|
+
items.delete(id);
|
|
97
|
+
return { purged: true };
|
|
98
|
+
},
|
|
99
|
+
// Distinct memory:// scheme so a URL assertion fails if the consumer calls a real client.
|
|
100
|
+
originalUrl: ({ id }) => `memory://media-original/${encodeURIComponent(id)}`,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export function createMemoryMediaProviders({ providers = [] }, _optional = {}) {
|
|
104
|
+
const items = new Map(providers.map((item) => [item.id, Object.freeze({ ...item })]));
|
|
105
|
+
function write(id, configured) {
|
|
106
|
+
const previous = items.get(id);
|
|
107
|
+
if (!previous)
|
|
108
|
+
throw new Error('Unknown provider');
|
|
109
|
+
const next = Object.freeze({ ...previous, configured });
|
|
110
|
+
items.set(id, next);
|
|
111
|
+
return next;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
async list(_required, options = {}) {
|
|
115
|
+
options.signal?.throwIfAborted();
|
|
116
|
+
return Object.freeze([...items.values()]);
|
|
117
|
+
},
|
|
118
|
+
async saveSettings({ id, baseUrl, model }, options = {}) {
|
|
119
|
+
options.signal?.throwIfAborted();
|
|
120
|
+
const previous = items.get(id);
|
|
121
|
+
if (!previous)
|
|
122
|
+
throw new Error('Unknown provider');
|
|
123
|
+
const next = Object.freeze({ ...previous, baseUrl, model });
|
|
124
|
+
items.set(id, next);
|
|
125
|
+
return next;
|
|
126
|
+
},
|
|
127
|
+
async saveCredential({ id, credential }, options = {}) {
|
|
128
|
+
options.signal?.throwIfAborted();
|
|
129
|
+
if (!credential.trim())
|
|
130
|
+
throw new Error('Credential is required');
|
|
131
|
+
return write(id, true);
|
|
132
|
+
},
|
|
133
|
+
async removeCredential({ id }, options = {}) {
|
|
134
|
+
options.signal?.throwIfAborted();
|
|
135
|
+
return write(id, false);
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MediaApiPort } from '../ports.js';
|
|
2
|
+
/** Destructive checklist: run only against an isolated backend with no production data.
|
|
3
|
+
* Returns named checks and throws at the first failed contract. No test-framework dependency. */
|
|
4
|
+
export declare function runMediaApiConformance({ api }: {
|
|
5
|
+
api: MediaApiPort;
|
|
6
|
+
}, _optional?: Record<string, never>): Promise<readonly string[]>;
|
|
7
|
+
//# sourceMappingURL=media-api.conformance.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { canReplaceMedia } from '../rules.js';
|
|
2
|
+
/** Destructive checklist: run only against an isolated backend with no production data.
|
|
3
|
+
* Returns named checks and throws at the first failed contract. No test-framework dependency. */
|
|
4
|
+
export async function runMediaApiConformance({ api }, _optional = {}) {
|
|
5
|
+
const checks = [];
|
|
6
|
+
function assert(condition, name) {
|
|
7
|
+
if (!condition)
|
|
8
|
+
throw new Error(`Media API conformance: ${name}`);
|
|
9
|
+
checks.push(name);
|
|
10
|
+
}
|
|
11
|
+
async function rejects(run, name) {
|
|
12
|
+
let rejected = false;
|
|
13
|
+
try {
|
|
14
|
+
await run();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
rejected = true;
|
|
18
|
+
}
|
|
19
|
+
assert(rejected, name);
|
|
20
|
+
}
|
|
21
|
+
const input = {
|
|
22
|
+
filename: 'conformance.png',
|
|
23
|
+
contentType: 'image/png',
|
|
24
|
+
dataBase64: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aT1sAAAAASUVORK5CYII=',
|
|
25
|
+
};
|
|
26
|
+
const created = await api.upload(input, { alt: 'initial alt' });
|
|
27
|
+
assert(created.id.length > 0 && created.status === 'active', 'upload creates an active asset');
|
|
28
|
+
try {
|
|
29
|
+
const before = await api.list({});
|
|
30
|
+
assert(before.some((item) => item.id === created.id), 'list contains upload');
|
|
31
|
+
const updated = await api.update({ id: created.id, patch: { title: 'renamed' } });
|
|
32
|
+
assert(updated.title === 'renamed' && updated.alt === 'initial alt', 'partial metadata patch preserves untouched fields');
|
|
33
|
+
assert(created.title === 'conformance.png', 'prior snapshots do not mutate');
|
|
34
|
+
const images = await api.list({ filter: 'images' });
|
|
35
|
+
assert(images.every((item) => item.contentType?.startsWith('image/')), 'image filter');
|
|
36
|
+
assert(typeof api.originalUrl({ id: created.id }) === 'string', 'original URL is synchronous');
|
|
37
|
+
await rejects(() => api.delete({ id: created.id }), 'active asset cannot be purged');
|
|
38
|
+
if (canReplaceMedia({ api })) {
|
|
39
|
+
const replaced = await api.replace({
|
|
40
|
+
id: created.id,
|
|
41
|
+
upload: { ...input, filename: 'replacement.png' },
|
|
42
|
+
});
|
|
43
|
+
assert(replaced.id === created.id &&
|
|
44
|
+
replaced.alt === updated.alt &&
|
|
45
|
+
replaced.title === updated.title, 'replace retains identity and metadata');
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
assert(api.replace === undefined || api.replaceSupported === false, 'replacement is optional and unavailable');
|
|
49
|
+
}
|
|
50
|
+
const abort = new AbortController();
|
|
51
|
+
abort.abort();
|
|
52
|
+
await rejects(() => api.list({}, { signal: abort.signal }), 'aborted list rejects');
|
|
53
|
+
await rejects(() => api.update({ id: created.id, patch: { title: 'must not save' } }, { signal: abort.signal }), 'aborted write rejects');
|
|
54
|
+
assert((await api.list({})).find((item) => item.id === created.id)?.title === 'renamed', 'aborted write has no effects');
|
|
55
|
+
const trashed = await api.trash({ id: created.id });
|
|
56
|
+
assert(trashed.status === 'trashed', 'trash is a distinct state');
|
|
57
|
+
assert((await api.delete({ id: created.id })).purged, 'trashed asset can be purged');
|
|
58
|
+
assert(!(await api.list({})).some((item) => item.id === created.id), 'purge removes asset');
|
|
59
|
+
await rejects(() => api.update({ id: created.id, patch: {} }), 'missing asset rejects');
|
|
60
|
+
return Object.freeze(checks);
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
if ((await api.list({})).some((item) => item.id === created.id)) {
|
|
64
|
+
await api.trash({ id: created.id });
|
|
65
|
+
await api.delete({ id: created.id });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=media-api.conformance.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { MediaApiPort } from '../ports.js';
|
|
2
|
+
import type { MediaAsset, MediaMetadataPatch, UploadInput } from '../models.js';
|
|
3
|
+
export declare function createEditMediaController({ api, item }: {
|
|
4
|
+
api: MediaApiPort;
|
|
5
|
+
item: MediaAsset;
|
|
6
|
+
}, _optional?: Record<string, never>): {
|
|
7
|
+
getSnapshot: () => Readonly<{
|
|
8
|
+
draft: MediaMetadataPatch;
|
|
9
|
+
saving: boolean;
|
|
10
|
+
error: string | null;
|
|
11
|
+
}>;
|
|
12
|
+
subscribe: ({ listener }: {
|
|
13
|
+
listener: () => void;
|
|
14
|
+
}) => () => void;
|
|
15
|
+
dispose: () => void;
|
|
16
|
+
setDraft({ patch }: {
|
|
17
|
+
patch: MediaMetadataPatch;
|
|
18
|
+
}, _optional?: Record<string, never>): void;
|
|
19
|
+
save(_required?: Record<string, never>, _optional?: Record<string, never>): Promise<boolean>;
|
|
20
|
+
replace({ upload }: {
|
|
21
|
+
upload: UploadInput;
|
|
22
|
+
}, _optional?: Record<string, never>): Promise<boolean>;
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=edit-media.controller.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createControllerStore } from '../../core/module/controller-store.js';
|
|
2
|
+
import { diffMediaMetadata, canReplaceMedia } from '../rules.js';
|
|
3
|
+
export function createEditMediaController({ api, item }, _optional = {}) {
|
|
4
|
+
// Frozen at mount, exactly like draft's initializer — stays the value draft was seeded
|
|
5
|
+
// from even as the live list advances underneath it. Never diff against a drifting item.
|
|
6
|
+
let baseline = Object.freeze({ ...item });
|
|
7
|
+
const store = createControllerStore({
|
|
8
|
+
initial: {
|
|
9
|
+
draft: metadataDraft({ item }),
|
|
10
|
+
saving: false,
|
|
11
|
+
error: null,
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
async function perform(run) {
|
|
15
|
+
if (store.signal.aborted || store.getSnapshot().saving)
|
|
16
|
+
return false;
|
|
17
|
+
store.set({ patch: { saving: true, error: null } });
|
|
18
|
+
try {
|
|
19
|
+
const saved = await run();
|
|
20
|
+
if (store.signal.aborted)
|
|
21
|
+
return false;
|
|
22
|
+
baseline = saved;
|
|
23
|
+
store.set({
|
|
24
|
+
patch: {
|
|
25
|
+
draft: metadataDraft({ item: baseline }),
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
store.set({
|
|
32
|
+
patch: { error: error instanceof Error ? error.message : 'Failed to save media metadata' },
|
|
33
|
+
});
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
store.set({ patch: { saving: false } });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
getSnapshot: store.getSnapshot,
|
|
42
|
+
subscribe: store.subscribe,
|
|
43
|
+
dispose: store.dispose,
|
|
44
|
+
setDraft({ patch }, _optional = {}) {
|
|
45
|
+
if (!store.getSnapshot().saving)
|
|
46
|
+
store.set({ patch: { draft: { ...store.getSnapshot().draft, ...patch } } });
|
|
47
|
+
},
|
|
48
|
+
async save(_required = {}, _optional = {}) {
|
|
49
|
+
const patch = diffMediaMetadata({ item: baseline, draft: store.getSnapshot().draft });
|
|
50
|
+
if (Object.keys(patch).length === 0)
|
|
51
|
+
return true;
|
|
52
|
+
return perform(() => api.update({ id: item.id, patch }, { signal: store.signal }));
|
|
53
|
+
},
|
|
54
|
+
async replace({ upload }, _optional = {}) {
|
|
55
|
+
if (!canReplaceMedia({ api })) {
|
|
56
|
+
store.set({ patch: { error: 'File replacement is unavailable' } });
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return perform(() => api.replace({ id: item.id, upload }, { signal: store.signal }));
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
/** Do not invent CMS fields for hosts which omit them: undefined is an untouched field. */
|
|
64
|
+
function metadataDraft({ item }) {
|
|
65
|
+
return Object.fromEntries(Object.entries({ title: item.title, slug: item.slug, alt: item.alt,
|
|
66
|
+
caption: item.caption, credit: item.credit, width: item.width, height: item.height,
|
|
67
|
+
cssClass: item.cssClass, htmlAttributes: item.htmlAttributes }).filter(([, value]) => value !== undefined));
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=edit-media.controller.js.map
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { countMediaTabs } from '../rules.js';
|
|
2
|
+
import type { MediaApiPort } from '../ports.js';
|
|
3
|
+
import type { MediaAsset, MediaQuery, UploadInput } from '../models.js';
|
|
4
|
+
export interface LibraryState {
|
|
5
|
+
readonly items: readonly MediaAsset[] | null;
|
|
6
|
+
readonly query: MediaQuery;
|
|
7
|
+
readonly loading: boolean;
|
|
8
|
+
readonly busy: boolean;
|
|
9
|
+
readonly pendingIds: readonly string[];
|
|
10
|
+
readonly error: string | null;
|
|
11
|
+
readonly pendingPurge: MediaAsset | null;
|
|
12
|
+
readonly hasUntyped: boolean;
|
|
13
|
+
readonly counts: ReturnType<typeof countMediaTabs> | null;
|
|
14
|
+
}
|
|
15
|
+
export declare function createLibraryController({ api }: {
|
|
16
|
+
api: MediaApiPort;
|
|
17
|
+
}, { query }?: {
|
|
18
|
+
query?: MediaQuery;
|
|
19
|
+
}): {
|
|
20
|
+
getSnapshot: () => Readonly<LibraryState>;
|
|
21
|
+
subscribe: ({ listener }: {
|
|
22
|
+
listener: () => void;
|
|
23
|
+
}) => () => void;
|
|
24
|
+
load: (_required?: Record<string, never>, _optional?: Record<string, never>) => Promise<void>;
|
|
25
|
+
setQuery({ query }: {
|
|
26
|
+
query: MediaQuery;
|
|
27
|
+
}, _optional?: Record<string, never>): Promise<void>;
|
|
28
|
+
upload({ input, alt }: {
|
|
29
|
+
input: UploadInput;
|
|
30
|
+
alt?: string;
|
|
31
|
+
}, _optional?: Record<string, never>): Promise<boolean>;
|
|
32
|
+
trash({ id }: {
|
|
33
|
+
id: string;
|
|
34
|
+
}, _optional?: Record<string, never>): Promise<boolean>;
|
|
35
|
+
restore({ id }: {
|
|
36
|
+
id: string;
|
|
37
|
+
}, _optional?: Record<string, never>): Promise<boolean>;
|
|
38
|
+
requestPurge({ item }: {
|
|
39
|
+
item: MediaAsset;
|
|
40
|
+
}, _optional?: Record<string, never>): void;
|
|
41
|
+
cancelPurge(_required?: Record<string, never>, _optional?: Record<string, never>): void;
|
|
42
|
+
confirmPurge({ confirmed }: {
|
|
43
|
+
confirmed: boolean;
|
|
44
|
+
}, _optional?: Record<string, never>): Promise<boolean>;
|
|
45
|
+
dispose(_required?: Record<string, never>, _optional?: Record<string, never>): void;
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=library.controller.d.ts.map
|