@rynt/sdk 0.9.53
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/README.md +122 -0
- package/REGISTRIES.md +189 -0
- package/env.d.ts +11 -0
- package/host-shims.d.ts +30 -0
- package/package.json +88 -0
- package/src/extension-marketplace/api-types.ts +141 -0
- package/src/extension-marketplace/client.ts +296 -0
- package/src/extension-marketplace/index.ts +22 -0
- package/src/extension-marketplace/schemas.ts +178 -0
- package/src/extensions/ExtensionRoutePage.vue +17 -0
- package/src/extensions/context.ts +37 -0
- package/src/extensions/disabled-folder.ts +21 -0
- package/src/extensions/extension-expose-map.ts +5 -0
- package/src/extensions/extension-expose.ts +48 -0
- package/src/extensions/graph.ts +67 -0
- package/src/extensions/index.ts +251 -0
- package/src/extensions/invite-handler/types.ts +20 -0
- package/src/extensions/launcher-entities/create-launcher-entity.ts +25 -0
- package/src/extensions/launcher-entities/keys.ts +46 -0
- package/src/extensions/launcher-entities/launcher-entity-components.ts +177 -0
- package/src/extensions/launcher-entities/props-map.ts +69 -0
- package/src/extensions/launcher-entities/registry.ts +32 -0
- package/src/extensions/launcher-models/apis/accounts-contracts.ts +102 -0
- package/src/extensions/launcher-models/apis/launcher-model-apis.ts +553 -0
- package/src/extensions/launcher-models/keys.ts +23 -0
- package/src/extensions/launcher-models/public.ts +9 -0
- package/src/extensions/launcher-models/registry-core.ts +34 -0
- package/src/extensions/manifest-types.ts +22 -0
- package/src/extensions/manifest.ts +46 -0
- package/src/extensions/marketplace-open-key.ts +26 -0
- package/src/extensions/plugin-types.ts +44 -0
- package/src/extensions/plugin.ts +62 -0
- package/src/extensions/registries/bootstrap.ts +11 -0
- package/src/extensions/registries/builtins/account-provider.ts +6 -0
- package/src/extensions/registries/builtins/app-topbar-left-widgets.ts +6 -0
- package/src/extensions/registries/builtins/app-topbar-right-widgets.ts +6 -0
- package/src/extensions/registries/builtins/app-topbar-status-widgets.ts +6 -0
- package/src/extensions/registries/builtins/build-card-actions.ts +6 -0
- package/src/extensions/registries/builtins/build-card-after-meta.ts +6 -0
- package/src/extensions/registries/builtins/build-card-before-media.ts +6 -0
- package/src/extensions/registries/builtins/build-card-before-meta.ts +6 -0
- package/src/extensions/registries/builtins/build-card-footer-actions.ts +6 -0
- package/src/extensions/registries/builtins/build-detail-after-content.ts +6 -0
- package/src/extensions/registries/builtins/build-detail-before-content.ts +6 -0
- package/src/extensions/registries/builtins/build-detail-before-hero.ts +6 -0
- package/src/extensions/registries/builtins/build-detail-header-actions.ts +6 -0
- package/src/extensions/registries/builtins/build-detail-mod-row-actions.ts +6 -0
- package/src/extensions/registries/builtins/build-detail-resourcepack-row-actions.ts +6 -0
- package/src/extensions/registries/builtins/build-detail-right-column-bottom.ts +6 -0
- package/src/extensions/registries/builtins/build-detail-right-column-top.ts +6 -0
- package/src/extensions/registries/builtins/dialog-footer-actions.ts +6 -0
- package/src/extensions/registries/builtins/feed-after-content.ts +6 -0
- package/src/extensions/registries/builtins/feed-before-content.ts +6 -0
- package/src/extensions/registries/builtins/file-editor.ts +19 -0
- package/src/extensions/registries/builtins/friends-after-list.ts +6 -0
- package/src/extensions/registries/builtins/friends-before-list.ts +6 -0
- package/src/extensions/registries/builtins/index.ts +141 -0
- package/src/extensions/registries/builtins/invite-handler.ts +7 -0
- package/src/extensions/registries/builtins/library-after-content.ts +6 -0
- package/src/extensions/registries/builtins/library-before-content.ts +6 -0
- package/src/extensions/registries/builtins/loader.ts +8 -0
- package/src/extensions/registries/builtins/map-card-actions.ts +6 -0
- package/src/extensions/registries/builtins/map-card-after-meta.ts +6 -0
- package/src/extensions/registries/builtins/map-card-before-meta.ts +6 -0
- package/src/extensions/registries/builtins/map-card-footer-actions.ts +6 -0
- package/src/extensions/registries/builtins/map-detail-after-content.ts +6 -0
- package/src/extensions/registries/builtins/map-detail-before-content.ts +6 -0
- package/src/extensions/registries/builtins/map-detail-header-actions.ts +6 -0
- package/src/extensions/registries/builtins/markdown-editor-tiptap-extensions.ts +7 -0
- package/src/extensions/registries/builtins/markdown-editor-toolbar-actions.ts +6 -0
- package/src/extensions/registries/builtins/markdown-renderer-after-content.ts +6 -0
- package/src/extensions/registries/builtins/markdown-renderer-before-content.ts +6 -0
- package/src/extensions/registries/builtins/mod-details-footer-actions.ts +6 -0
- package/src/extensions/registries/builtins/mod-manage-actions.ts +6 -0
- package/src/extensions/registries/builtins/mod-provider.ts +5 -0
- package/src/extensions/registries/builtins/nav.ts +7 -0
- package/src/extensions/registries/builtins/page.ts +13 -0
- package/src/extensions/registries/builtins/projects-after-content.ts +6 -0
- package/src/extensions/registries/builtins/projects-before-content.ts +6 -0
- package/src/extensions/registries/builtins/resourcepack-manage-actions.ts +7 -0
- package/src/extensions/registries/builtins/server-card-actions.ts +6 -0
- package/src/extensions/registries/builtins/server-card-after-meta.ts +6 -0
- package/src/extensions/registries/builtins/server-card-before-meta.ts +6 -0
- package/src/extensions/registries/builtins/server-card-footer-actions.ts +6 -0
- package/src/extensions/registries/builtins/server-detail-after-content.ts +6 -0
- package/src/extensions/registries/builtins/server-detail-before-content.ts +6 -0
- package/src/extensions/registries/builtins/server-detail-header-actions.ts +6 -0
- package/src/extensions/registries/builtins/settings-after-sections.ts +6 -0
- package/src/extensions/registries/builtins/settings-before-sections.ts +6 -0
- package/src/extensions/registries/builtins/settings-section-widgets.ts +6 -0
- package/src/extensions/registries/builtins/shaderpack-manage-actions.ts +7 -0
- package/src/extensions/registries/builtins/shell.ts +5 -0
- package/src/extensions/registries/builtins/sidebar-after-content.ts +6 -0
- package/src/extensions/registries/builtins/sidebar-before-content.ts +6 -0
- package/src/extensions/registries/builtins/sidebar-footer-widgets.ts +6 -0
- package/src/extensions/registries/builtins/sidebar-header-widgets.ts +6 -0
- package/src/extensions/registries/builtins/sidebar.ts +11 -0
- package/src/extensions/registries/builtins/theme.ts +5 -0
- package/src/extensions/registries/builtins/user-card-after-meta.ts +6 -0
- package/src/extensions/registries/builtins/user-card-before-meta.ts +6 -0
- package/src/extensions/registries/builtins/user-menu-actions.ts +6 -0
- package/src/extensions/registries/builtins/user-menu-after-actions.ts +6 -0
- package/src/extensions/registries/builtins/user-menu-before-actions.ts +6 -0
- package/src/extensions/registries/builtins/user-strip.ts +5 -0
- package/src/extensions/registries/clear-extension-ui-registries.ts +15 -0
- package/src/extensions/registries/define-extension-registry.ts +58 -0
- package/src/extensions/registries/extension-host-api.ts +41 -0
- package/src/extensions/registries/extension-registry-api.ts +103 -0
- package/src/extensions/registries/extension-registry-payload-map.ts +9 -0
- package/src/extensions/registries/extension-scope.ts +41 -0
- package/src/extensions/registries/get-registry.ts +23 -0
- package/src/extensions/registries/index.ts +58 -0
- package/src/extensions/registries/manifest-rynt.ts +193 -0
- package/src/extensions/registries/registry-slot.ts +40 -0
- package/src/extensions/registries/registry-value-map.ts +89 -0
- package/src/extensions/registries/store.ts +206 -0
- package/src/extensions/resolve-extensions.ts +245 -0
- package/src/extensions/router-bridge.ts +103 -0
- package/src/extensions/session.ts +6 -0
- package/src/extensions/slug.ts +23 -0
- package/src/extensions/version.ts +147 -0
- package/src/host/extensions-composables.ts +33 -0
- package/src/host/extensions-init.ts +194 -0
- package/src/host/index.ts +11 -0
- package/src/host/launcher-models/index.ts +4 -0
- package/src/index.ts +229 -0
- package/src/minecraft-loader/base-loader.ts +102 -0
- package/src/minecraft-loader/index.ts +11 -0
- package/src/minecraft-loader/loader-registry.ts +72 -0
- package/src/shared/api/assets.ts +112 -0
- package/src/shared/api/auth.ts +283 -0
- package/src/shared/api/builds.ts +647 -0
- package/src/shared/api/config.ts +19 -0
- package/src/shared/api/download-stats.ts +103 -0
- package/src/shared/api/downloads.ts +36 -0
- package/src/shared/api/entity-authorship.ts +60 -0
- package/src/shared/api/events.ts +393 -0
- package/src/shared/api/friends.ts +140 -0
- package/src/shared/api/graphql.ts +87 -0
- package/src/shared/api/index.ts +23 -0
- package/src/shared/api/invites.ts +262 -0
- package/src/shared/api/library.ts +44 -0
- package/src/shared/api/maps.ts +385 -0
- package/src/shared/api/notify-websocket.ts +140 -0
- package/src/shared/api/posts.ts +357 -0
- package/src/shared/api/projectServers.ts +379 -0
- package/src/shared/api/serverMembers.ts +173 -0
- package/src/shared/api/users.ts +294 -0
- package/src/shared/composables/buildEditor/useBuildEditor.ts +66 -0
- package/src/shared/composables/buildManifest/buildManifest.ts +447 -0
- package/src/shared/composables/filesEditor/filesEditor.ts +346 -0
- package/src/shared/composables/index.ts +10 -0
- package/src/shared/composables/modsEditor/modsEditor.ts +1678 -0
- package/src/shared/composables/registrySlot/registry-slot-utils.ts +25 -0
- package/src/shared/composables/registrySlot/useRegistrySlotMissing.ts +35 -0
- package/src/shared/composables/resourcePacksEditor/resourcePacksEditor.ts +448 -0
- package/src/shared/composables/shaderPacksEditor/shaderPacksEditor.ts +395 -0
- package/src/shared/composables/useSkinRender.ts +70 -0
- package/src/shared/composables/useZlDeepLink.ts +178 -0
- package/src/shared/definitions/defineGraphCache.ts +216 -0
- package/src/shared/definitions/defineStore.ts +32 -0
- package/src/shared/definitions/index.ts +2 -0
- package/src/shared/minecraft-types/build-manifest.ts +611 -0
- package/src/shared/minecraft-types/index.ts +3 -0
- package/src/shared/minecraft-types/launcher-versions.ts +32 -0
- package/src/shared/minecraft-types/minecraft-launcher-types.ts +276 -0
- package/src/shared/mocks/index.ts +1 -0
- package/src/shared/mocks/navigation.ts +17 -0
- package/src/shared/mods/http.ts +45 -0
- package/src/shared/mods/index.ts +5 -0
- package/src/shared/mods/marketplace-editor-search.ts +266 -0
- package/src/shared/mods/marketplace-search-utils.ts +42 -0
- package/src/shared/mods/mod-marketplace-registry.ts +66 -0
- package/src/shared/mods/mod-marketplace-types.ts +28 -0
- package/src/shared/mods/providers/curseforge.ts +464 -0
- package/src/shared/mods/providers/index.ts +8 -0
- package/src/shared/mods/providers/modrinth.ts +402 -0
- package/src/shared/mods/resolve-mods-provider-loader-ids.ts +77 -0
- package/src/shared/mods/types.ts +76 -0
- package/src/shared/styles/index.css +713 -0
- package/src/shared/themes/index.ts +23 -0
- package/src/shared/themes/theme-tokens-black.json +126 -0
- package/src/shared/themes/theme-tokens-classic.json +126 -0
- package/src/shared/themes/theme-tokens-pink.json +126 -0
- package/src/shared/themes/theme-tokens.json +126 -0
- package/src/shared/themes/types.ts +85 -0
- package/src/shared/types/API_DOCUMENTATION.md +422 -0
- package/src/shared/types/account.ts +40 -0
- package/src/shared/types/build.ts +8 -0
- package/src/shared/types/entities.ts +181 -0
- package/src/shared/types/index.ts +6 -0
- package/src/shared/types/invite-payloads.ts +60 -0
- package/src/shared/types/navigation.ts +16 -0
- package/src/shared/types/running-build.ts +51 -0
- package/src/shared/types/serverMember.ts +17 -0
- package/src/shared/types/user.ts +55 -0
- package/src/shared/ui/base/Avatar.vue +262 -0
- package/src/shared/ui/base/Badge.vue +47 -0
- package/src/shared/ui/base/Button.vue +78 -0
- package/src/shared/ui/base/Divider.vue +42 -0
- package/src/shared/ui/base/Icon.vue +597 -0
- package/src/shared/ui/base/StatusIndicator.vue +44 -0
- package/src/shared/ui/base/index.ts +7 -0
- package/src/shared/ui/cards/InviteCard.vue +47 -0
- package/src/shared/ui/cards/index.ts +2 -0
- package/src/shared/ui/dialog/Dialog.vue +71 -0
- package/src/shared/ui/dialog/DialogContent.vue +31 -0
- package/src/shared/ui/dialog/DialogFooter.vue +14 -0
- package/src/shared/ui/dialog/DialogHeader.vue +41 -0
- package/src/shared/ui/dialog/index.ts +5 -0
- package/src/shared/ui/editors/AttachmentImagesEditor.vue +133 -0
- package/src/shared/ui/editors/ContentAttachmentsDisplay.vue +76 -0
- package/src/shared/ui/editors/MarkdownEditor.vue +956 -0
- package/src/shared/ui/editors/MarkdownRenderer.vue +299 -0
- package/src/shared/ui/editors/RichContentImageViewer.vue +85 -0
- package/src/shared/ui/editors/SocialPostMediaZone.vue +320 -0
- package/src/shared/ui/editors/index.ts +6 -0
- package/src/shared/ui/editors/markdown-editor-gallery.ts +234 -0
- package/src/shared/ui/editors/markdown-editor-image.ts +178 -0
- package/src/shared/ui/form/Checkbox.vue +38 -0
- package/src/shared/ui/form/FormField.vue +30 -0
- package/src/shared/ui/form/FormGrid.vue +38 -0
- package/src/shared/ui/form/ImageEditor.vue +598 -0
- package/src/shared/ui/form/Input.vue +72 -0
- package/src/shared/ui/form/Range.vue +65 -0
- package/src/shared/ui/form/Select.vue +76 -0
- package/src/shared/ui/form/Switch.vue +38 -0
- package/src/shared/ui/form/Textarea.vue +144 -0
- package/src/shared/ui/form/index.ts +9 -0
- package/src/shared/ui/index.ts +9 -0
- package/src/shared/ui/layout/BusyOverlay.vue +31 -0
- package/src/shared/ui/layout/Callout.vue +44 -0
- package/src/shared/ui/layout/Card.vue +38 -0
- package/src/shared/ui/layout/Container.vue +36 -0
- package/src/shared/ui/layout/EmptyState.vue +99 -0
- package/src/shared/ui/layout/EntityMediaRow.vue +54 -0
- package/src/shared/ui/layout/FilterResultsLayout.vue +22 -0
- package/src/shared/ui/layout/FloatingPanel.vue +37 -0
- package/src/shared/ui/layout/FullscreenDimmer.vue +11 -0
- package/src/shared/ui/layout/Grid.vue +40 -0
- package/src/shared/ui/layout/Inline.vue +59 -0
- package/src/shared/ui/layout/LoadingState.vue +39 -0
- package/src/shared/ui/layout/MediaBox.vue +47 -0
- package/src/shared/ui/layout/OverlayPanel.vue +28 -0
- package/src/shared/ui/layout/OverlayWaitPanel.vue +22 -0
- package/src/shared/ui/layout/PageSection.vue +43 -0
- package/src/shared/ui/layout/PageToolbar.vue +29 -0
- package/src/shared/ui/layout/Panel.vue +39 -0
- package/src/shared/ui/layout/ProgressBar.vue +49 -0
- package/src/shared/ui/layout/Section.vue +30 -0
- package/src/shared/ui/layout/SegmentedControl.vue +43 -0
- package/src/shared/ui/layout/SelectableCard.vue +46 -0
- package/src/shared/ui/layout/SelectableRow.vue +41 -0
- package/src/shared/ui/layout/Skeleton.vue +25 -0
- package/src/shared/ui/layout/SkeletonAvatar.vue +30 -0
- package/src/shared/ui/layout/SkeletonEntityCard.vue +20 -0
- package/src/shared/ui/layout/SkeletonFeedPost.vue +22 -0
- package/src/shared/ui/layout/SkeletonGrid.vue +18 -0
- package/src/shared/ui/layout/SkeletonListRow.vue +31 -0
- package/src/shared/ui/layout/SkeletonText.vue +25 -0
- package/src/shared/ui/layout/Stack.vue +42 -0
- package/src/shared/ui/layout/StateBlock.vue +44 -0
- package/src/shared/ui/layout/TwoPaneLayout.vue +35 -0
- package/src/shared/ui/layout/VirtualList.vue +160 -0
- package/src/shared/ui/layout/index.ts +35 -0
- package/src/shared/ui/layout/skeletonSurfaceStyles.ts +24 -0
- package/src/shared/ui/navigation/NavItem.vue +139 -0
- package/src/shared/ui/navigation/Tab.vue +61 -0
- package/src/shared/ui/navigation/Tabs.vue +37 -0
- package/src/shared/ui/navigation/index.ts +4 -0
- package/src/shared/ui/primitives/Action.vue +19 -0
- package/src/shared/ui/primitives/Block.vue +28 -0
- package/src/shared/ui/primitives/CanvasView.vue +19 -0
- package/src/shared/ui/primitives/Control.vue +24 -0
- package/src/shared/ui/primitives/ControlSelect.vue +19 -0
- package/src/shared/ui/primitives/ControlTextarea.vue +17 -0
- package/src/shared/ui/primitives/FieldLabel.vue +19 -0
- package/src/shared/ui/primitives/Form.vue +19 -0
- package/src/shared/ui/primitives/Heading.vue +29 -0
- package/src/shared/ui/primitives/Image.vue +17 -0
- package/src/shared/ui/primitives/LineBreak.vue +3 -0
- package/src/shared/ui/primitives/Link.vue +19 -0
- package/src/shared/ui/primitives/List.vue +28 -0
- package/src/shared/ui/primitives/ListItem.vue +19 -0
- package/src/shared/ui/primitives/OptionItem.vue +19 -0
- package/src/shared/ui/primitives/Text.vue +28 -0
- package/src/shared/ui/primitives/VideoView.vue +19 -0
- package/src/shared/ui/primitives/index.ts +19 -0
- package/src/shared/ui/primitives/resolveElement.ts +25 -0
- package/src/shared/ui/special/AngularAccent.vue +106 -0
- package/src/shared/ui/special/ExtensionRegistrySlotButton.vue +143 -0
- package/src/shared/ui/special/InfoRow.vue +39 -0
- package/src/shared/ui/special/LogViewer.vue +53 -0
- package/src/shared/ui/special/PageHeader.vue +23 -0
- package/src/shared/ui/special/RegistrySlotMissingCallout.vue +48 -0
- package/src/shared/ui/special/WelcomeCard.vue +32 -0
- package/src/shared/ui/special/index.ts +9 -0
- package/src/shared/utils/app-paths.ts +50 -0
- package/src/shared/utils/attachments.ts +16 -0
- package/src/shared/utils/autostart.ts +213 -0
- package/src/shared/utils/build-files.ts +439 -0
- package/src/shared/utils/build-manifest-init.ts +176 -0
- package/src/shared/utils/cloudinary.ts +67 -0
- package/src/shared/utils/cn.ts +7 -0
- package/src/shared/utils/download-stats-week.ts +165 -0
- package/src/shared/utils/entity-api-to-cache.ts +84 -0
- package/src/shared/utils/entity-build-from-api.ts +1 -0
- package/src/shared/utils/entity-display.ts +27 -0
- package/src/shared/utils/entity-map-from-api.ts +1 -0
- package/src/shared/utils/file-hash.ts +65 -0
- package/src/shared/utils/formatSize.ts +5 -0
- package/src/shared/utils/formatTime.ts +157 -0
- package/src/shared/utils/getAccountSkinRender.ts +32 -0
- package/src/shared/utils/index.ts +34 -0
- package/src/shared/utils/local-mods.ts +678 -0
- package/src/shared/utils/local-settings.ts +217 -0
- package/src/shared/utils/member-join-stats.ts +35 -0
- package/src/shared/utils/platform.ts +86 -0
- package/src/shared/utils/play-host-slug.ts +92 -0
- package/src/shared/utils/rich-content.ts +294 -0
- package/src/shared/utils/safeRequest.ts +23 -0
- package/src/shared/utils/semver.ts +81 -0
- package/src/shared/utils/serverPermissions.ts +155 -0
- package/src/shared/utils/skin-render-cache.ts +372 -0
- package/src/shared/utils/stripMarkdown.ts +45 -0
- package/src/shared/utils/transliterate.ts +74 -0
- package/src/shared/utils/updateAccountSkinRender.ts +64 -0
- package/src/shared/utils/updater.ts +218 -0
- package/src/shared/utils/uploadImage.ts +195 -0
- package/src/shared/utils/user-status.ts +9 -0
- package/src/tiptap/index.ts +7 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ApproveSubmissionRequest,
|
|
3
|
+
CreateSubmissionRequest,
|
|
4
|
+
DeveloperPublishedExtension,
|
|
5
|
+
ExtensionArtifactUploadResult,
|
|
6
|
+
ExtensionSubmission,
|
|
7
|
+
MarketplaceApiError,
|
|
8
|
+
MarketplaceExtensionDetail,
|
|
9
|
+
MarketplaceSearchQuery,
|
|
10
|
+
MarketplaceSearchResponse,
|
|
11
|
+
MarketplaceSlotLookupResult,
|
|
12
|
+
MarketplaceSlotQuery,
|
|
13
|
+
PublishExtensionResponse,
|
|
14
|
+
RejectSubmissionRequest,
|
|
15
|
+
SubmissionStatus,
|
|
16
|
+
UpdateExtensionListingRequest,
|
|
17
|
+
ExtensionListingMeta,
|
|
18
|
+
} from './api-types';
|
|
19
|
+
import { marketplaceSlotLookupResultSchema } from './schemas';
|
|
20
|
+
|
|
21
|
+
export interface ExtensionMarketplaceClientOptions {
|
|
22
|
+
baseUrl: string;
|
|
23
|
+
getAuthToken?: () => string | Promise<string | null>;
|
|
24
|
+
fetchImpl?: typeof fetch;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function readApiError(res: Response): Promise<MarketplaceApiError> {
|
|
28
|
+
try {
|
|
29
|
+
const body = (await res.json()) as MarketplaceApiError;
|
|
30
|
+
if (body?.code && body?.message) {
|
|
31
|
+
return body;
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
/* ignore */
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
code: 'VALIDATION_ERROR',
|
|
38
|
+
message: res.statusText || `HTTP ${res.status}`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function requestJson<T>(
|
|
43
|
+
fetchFn: typeof fetch,
|
|
44
|
+
url: string,
|
|
45
|
+
init: RequestInit,
|
|
46
|
+
): Promise<T> {
|
|
47
|
+
const res = await fetchFn(url, init);
|
|
48
|
+
if (!res.ok) {
|
|
49
|
+
throw await readApiError(res);
|
|
50
|
+
}
|
|
51
|
+
if (res.status === 204) {
|
|
52
|
+
return undefined as T;
|
|
53
|
+
}
|
|
54
|
+
return (await res.json()) as T;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ExtensionMarketplaceClient {
|
|
58
|
+
lookupSlot(query: MarketplaceSlotQuery): Promise<MarketplaceSlotLookupResult>;
|
|
59
|
+
getExtension(id: string, version?: string): Promise<MarketplaceExtensionDetail>;
|
|
60
|
+
search(query: MarketplaceSearchQuery): Promise<MarketplaceSearchResponse>;
|
|
61
|
+
|
|
62
|
+
uploadArtifact(file: File): Promise<ExtensionArtifactUploadResult>;
|
|
63
|
+
listMyPublishedExtensions(): Promise<DeveloperPublishedExtension[]>;
|
|
64
|
+
getMyPublishedExtension(extensionId: string): Promise<DeveloperPublishedExtension>;
|
|
65
|
+
listMySubmissions(status?: SubmissionStatus): Promise<ExtensionSubmission[]>;
|
|
66
|
+
getMySubmission(id: string): Promise<ExtensionSubmission>;
|
|
67
|
+
createSubmission(body: CreateSubmissionRequest): Promise<ExtensionSubmission>;
|
|
68
|
+
submitForReview(id: string): Promise<ExtensionSubmission>;
|
|
69
|
+
deleteSubmission(id: string): Promise<void>;
|
|
70
|
+
|
|
71
|
+
updateSubmissionListing(
|
|
72
|
+
id: string,
|
|
73
|
+
body: UpdateExtensionListingRequest,
|
|
74
|
+
): Promise<ExtensionSubmission>;
|
|
75
|
+
updatePublishedExtensionListing(
|
|
76
|
+
extensionId: string,
|
|
77
|
+
body: UpdateExtensionListingRequest,
|
|
78
|
+
): Promise<ExtensionListingMeta & { id: string; version: string }>;
|
|
79
|
+
|
|
80
|
+
listPendingSubmissions(options?: {
|
|
81
|
+
status?: SubmissionStatus;
|
|
82
|
+
limit?: number;
|
|
83
|
+
offset?: number;
|
|
84
|
+
}): Promise<{ items: ExtensionSubmission[]; total: number }>;
|
|
85
|
+
getAdminSubmission(id: string): Promise<ExtensionSubmission>;
|
|
86
|
+
approveSubmission(
|
|
87
|
+
id: string,
|
|
88
|
+
body?: ApproveSubmissionRequest,
|
|
89
|
+
): Promise<PublishExtensionResponse>;
|
|
90
|
+
rejectSubmission(
|
|
91
|
+
id: string,
|
|
92
|
+
body?: RejectSubmissionRequest,
|
|
93
|
+
): Promise<ExtensionSubmission>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function createExtensionMarketplaceClient(
|
|
97
|
+
options: ExtensionMarketplaceClientOptions,
|
|
98
|
+
): ExtensionMarketplaceClient {
|
|
99
|
+
const base = options.baseUrl.replace(/\/+$/u, '');
|
|
100
|
+
const fetchFn = options.fetchImpl ?? fetch;
|
|
101
|
+
|
|
102
|
+
async function authHeaders(json = false): Promise<HeadersInit> {
|
|
103
|
+
const h: Record<string, string> = {
|
|
104
|
+
Accept: 'application/json',
|
|
105
|
+
};
|
|
106
|
+
if (json) {
|
|
107
|
+
h['Content-Type'] = 'application/json';
|
|
108
|
+
}
|
|
109
|
+
if (options.getAuthToken) {
|
|
110
|
+
const token = await options.getAuthToken();
|
|
111
|
+
if (token) {
|
|
112
|
+
h.Authorization = `Bearer ${token}`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return h;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
async lookupSlot(query) {
|
|
120
|
+
const params = new URLSearchParams({
|
|
121
|
+
registryId: query.registryId,
|
|
122
|
+
key: query.key,
|
|
123
|
+
});
|
|
124
|
+
const raw = await requestJson<unknown>(
|
|
125
|
+
fetchFn,
|
|
126
|
+
`${base}/api/v1/extensions/slots?${params}`,
|
|
127
|
+
{ headers: await authHeaders() },
|
|
128
|
+
);
|
|
129
|
+
const parsed = marketplaceSlotLookupResultSchema.safeParse(raw);
|
|
130
|
+
if (!parsed.success) {
|
|
131
|
+
throw {
|
|
132
|
+
code: 'VALIDATION_ERROR',
|
|
133
|
+
message: parsed.error.message,
|
|
134
|
+
} satisfies MarketplaceApiError;
|
|
135
|
+
}
|
|
136
|
+
return parsed.data as MarketplaceSlotLookupResult;
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
async getExtension(id, version) {
|
|
140
|
+
const path = version
|
|
141
|
+
? `${base}/api/v1/extensions/${encodeURIComponent(id)}/versions/${encodeURIComponent(version)}`
|
|
142
|
+
: `${base}/api/v1/extensions/${encodeURIComponent(id)}`;
|
|
143
|
+
return requestJson(fetchFn, path, { headers: await authHeaders() });
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
async search(query) {
|
|
147
|
+
const params = new URLSearchParams();
|
|
148
|
+
if (query.q) params.set('q', query.q);
|
|
149
|
+
if (query.registryId) params.set('registryId', query.registryId);
|
|
150
|
+
if (query.limit != null) params.set('limit', String(query.limit));
|
|
151
|
+
if (query.offset != null) params.set('offset', String(query.offset));
|
|
152
|
+
const qs = params.toString();
|
|
153
|
+
return requestJson(
|
|
154
|
+
fetchFn,
|
|
155
|
+
`${base}/api/v1/extensions/search${qs ? `?${qs}` : ''}`,
|
|
156
|
+
{ headers: await authHeaders() },
|
|
157
|
+
);
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
async listMyPublishedExtensions() {
|
|
161
|
+
const res = await requestJson<{ items: DeveloperPublishedExtension[] }>(
|
|
162
|
+
fetchFn,
|
|
163
|
+
`${base}/api/v1/developer/published-extensions`,
|
|
164
|
+
{ headers: await authHeaders() },
|
|
165
|
+
);
|
|
166
|
+
return res.items;
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
async getMyPublishedExtension(extensionId) {
|
|
170
|
+
return requestJson(
|
|
171
|
+
fetchFn,
|
|
172
|
+
`${base}/api/v1/developer/published-extensions/${encodeURIComponent(extensionId)}`,
|
|
173
|
+
{ headers: await authHeaders() },
|
|
174
|
+
);
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
async uploadArtifact(file) {
|
|
178
|
+
const form = new FormData();
|
|
179
|
+
form.append('file', file);
|
|
180
|
+
return requestJson(fetchFn, `${base}/api/v1/developer/artifacts`, {
|
|
181
|
+
method: 'POST',
|
|
182
|
+
headers: await authHeaders(),
|
|
183
|
+
body: form,
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
async listMySubmissions(status) {
|
|
188
|
+
const params = status ? `?status=${encodeURIComponent(status)}` : '';
|
|
189
|
+
const res = await requestJson<{ items: ExtensionSubmission[] }>(
|
|
190
|
+
fetchFn,
|
|
191
|
+
`${base}/api/v1/developer/submissions${params}`,
|
|
192
|
+
{ headers: await authHeaders() },
|
|
193
|
+
);
|
|
194
|
+
return res.items;
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
async getMySubmission(id) {
|
|
198
|
+
return requestJson(fetchFn, `${base}/api/v1/developer/submissions/${encodeURIComponent(id)}`, {
|
|
199
|
+
headers: await authHeaders(),
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
async createSubmission(body) {
|
|
204
|
+
return requestJson(fetchFn, `${base}/api/v1/developer/submissions`, {
|
|
205
|
+
method: 'POST',
|
|
206
|
+
headers: await authHeaders(true),
|
|
207
|
+
body: JSON.stringify(body),
|
|
208
|
+
});
|
|
209
|
+
},
|
|
210
|
+
|
|
211
|
+
async submitForReview(id) {
|
|
212
|
+
return requestJson(
|
|
213
|
+
fetchFn,
|
|
214
|
+
`${base}/api/v1/developer/submissions/${encodeURIComponent(id)}/submit`,
|
|
215
|
+
{ method: 'POST', headers: await authHeaders() },
|
|
216
|
+
);
|
|
217
|
+
},
|
|
218
|
+
|
|
219
|
+
async deleteSubmission(id) {
|
|
220
|
+
await requestJson(
|
|
221
|
+
fetchFn,
|
|
222
|
+
`${base}/api/v1/developer/submissions/${encodeURIComponent(id)}`,
|
|
223
|
+
{ method: 'DELETE', headers: await authHeaders() },
|
|
224
|
+
);
|
|
225
|
+
},
|
|
226
|
+
|
|
227
|
+
async updateSubmissionListing(id, body) {
|
|
228
|
+
return requestJson(
|
|
229
|
+
fetchFn,
|
|
230
|
+
`${base}/api/v1/developer/submissions/${encodeURIComponent(id)}/listing`,
|
|
231
|
+
{
|
|
232
|
+
method: 'PATCH',
|
|
233
|
+
headers: await authHeaders(true),
|
|
234
|
+
body: JSON.stringify(body),
|
|
235
|
+
},
|
|
236
|
+
);
|
|
237
|
+
},
|
|
238
|
+
|
|
239
|
+
async updatePublishedExtensionListing(extensionId, body) {
|
|
240
|
+
return requestJson(
|
|
241
|
+
fetchFn,
|
|
242
|
+
`${base}/api/v1/developer/published-extensions/${encodeURIComponent(extensionId)}/listing`,
|
|
243
|
+
{
|
|
244
|
+
method: 'PATCH',
|
|
245
|
+
headers: await authHeaders(true),
|
|
246
|
+
body: JSON.stringify(body),
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
async listPendingSubmissions(opts) {
|
|
252
|
+
const params = new URLSearchParams();
|
|
253
|
+
if (opts?.status) params.set('status', opts.status);
|
|
254
|
+
if (opts?.limit != null) params.set('limit', String(opts.limit));
|
|
255
|
+
if (opts?.offset != null) params.set('offset', String(opts.offset));
|
|
256
|
+
const qs = params.toString();
|
|
257
|
+
return requestJson(
|
|
258
|
+
fetchFn,
|
|
259
|
+
`${base}/api/v1/admin/submissions${qs ? `?${qs}` : ''}`,
|
|
260
|
+
{ headers: await authHeaders() },
|
|
261
|
+
);
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
async getAdminSubmission(id) {
|
|
265
|
+
return requestJson(
|
|
266
|
+
fetchFn,
|
|
267
|
+
`${base}/api/v1/admin/submissions/${encodeURIComponent(id)}`,
|
|
268
|
+
{ headers: await authHeaders() },
|
|
269
|
+
);
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
async approveSubmission(id, body) {
|
|
273
|
+
return requestJson(
|
|
274
|
+
fetchFn,
|
|
275
|
+
`${base}/api/v1/admin/submissions/${encodeURIComponent(id)}/approve`,
|
|
276
|
+
{
|
|
277
|
+
method: 'POST',
|
|
278
|
+
headers: await authHeaders(true),
|
|
279
|
+
body: JSON.stringify(body ?? {}),
|
|
280
|
+
},
|
|
281
|
+
);
|
|
282
|
+
},
|
|
283
|
+
|
|
284
|
+
async rejectSubmission(id, body) {
|
|
285
|
+
return requestJson(
|
|
286
|
+
fetchFn,
|
|
287
|
+
`${base}/api/v1/admin/submissions/${encodeURIComponent(id)}/reject`,
|
|
288
|
+
{
|
|
289
|
+
method: 'POST',
|
|
290
|
+
headers: await authHeaders(true),
|
|
291
|
+
body: JSON.stringify(body ?? {}),
|
|
292
|
+
},
|
|
293
|
+
);
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type * from './api-types.js';
|
|
2
|
+
export {
|
|
3
|
+
approveSubmissionRequestSchema,
|
|
4
|
+
createSubmissionRequestSchema,
|
|
5
|
+
marketplaceManifestContributesRyntSchema,
|
|
6
|
+
marketplaceSearchQuerySchema,
|
|
7
|
+
marketplaceSlotQuerySchema,
|
|
8
|
+
MARKETPLACE_MAX_EXTENSION_API,
|
|
9
|
+
publishExtensionRequestSchema,
|
|
10
|
+
rejectSubmissionRequestSchema,
|
|
11
|
+
submissionStatusSchema,
|
|
12
|
+
updateExtensionListingRequestSchema,
|
|
13
|
+
validateManifestForMarketplacePublish,
|
|
14
|
+
validateSubmissionManifest,
|
|
15
|
+
type ValidateSubmissionManifestOptions,
|
|
16
|
+
} from './schemas.js';
|
|
17
|
+
export {
|
|
18
|
+
createExtensionMarketplaceClient,
|
|
19
|
+
type ExtensionMarketplaceClient,
|
|
20
|
+
type ExtensionMarketplaceClientOptions,
|
|
21
|
+
} from './client.js';
|
|
22
|
+
export { isValidSemver, isVersionGreater } from '../shared/utils/semver.js';
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
import { parseExtensionApiMajor } from '../extensions/version';
|
|
4
|
+
import { isValidSemver, isVersionGreater } from '../shared/utils/semver';
|
|
5
|
+
|
|
6
|
+
const registryDeclSchema = z.object({
|
|
7
|
+
id: z.string().min(1),
|
|
8
|
+
title: z.string().optional(),
|
|
9
|
+
description: z.string().optional(),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const registryExtendSchema = z.object({
|
|
13
|
+
registryId: z.string().min(1),
|
|
14
|
+
keys: z.array(z.string().min(1)).min(1),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export const marketplaceManifestContributesRyntSchema = z
|
|
18
|
+
.object({
|
|
19
|
+
declaresRegistries: z.array(registryDeclSchema).optional(),
|
|
20
|
+
extendsRegistries: z.array(registryExtendSchema).optional(),
|
|
21
|
+
})
|
|
22
|
+
.strict();
|
|
23
|
+
|
|
24
|
+
export const submissionStatusSchema = z.enum([
|
|
25
|
+
'draft',
|
|
26
|
+
'pending',
|
|
27
|
+
'approved',
|
|
28
|
+
'rejected',
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
/** Принимает объект или JSON-строку (legacy / ошибочный клиент). */
|
|
32
|
+
export const marketplaceManifestRecordSchema = z.preprocess((val) => {
|
|
33
|
+
if (typeof val !== 'string') return val;
|
|
34
|
+
try {
|
|
35
|
+
const parsed: unknown = JSON.parse(val);
|
|
36
|
+
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
37
|
+
return parsed;
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
/* keep string — z.record ниже отклонит */
|
|
41
|
+
}
|
|
42
|
+
return val;
|
|
43
|
+
}, z.record(z.unknown()));
|
|
44
|
+
|
|
45
|
+
export const createSubmissionRequestSchema = z.object({
|
|
46
|
+
artifactId: z.string().uuid(),
|
|
47
|
+
manifest: marketplaceManifestRecordSchema,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export const rejectSubmissionRequestSchema = z.object({
|
|
51
|
+
reason: z.string().optional(),
|
|
52
|
+
reviewNote: z.string().optional(),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
export const approveSubmissionRequestSchema = z.object({
|
|
56
|
+
reviewNote: z.string().optional(),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
export const publishExtensionRequestSchema = z.object({
|
|
60
|
+
manifest: z.record(z.unknown()),
|
|
61
|
+
downloadUrl: z.string().url(),
|
|
62
|
+
artifactSha256: z.string().optional(),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export const marketplaceSlotQuerySchema = z.object({
|
|
66
|
+
registryId: z.string().min(1),
|
|
67
|
+
key: z.string().min(1),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export const marketplaceExtensionSummarySchema = z.object({
|
|
71
|
+
id: z.string().min(1),
|
|
72
|
+
name: z.string().min(1),
|
|
73
|
+
version: z.string().min(1),
|
|
74
|
+
description: z.string().optional(),
|
|
75
|
+
iconUrl: z.string().optional(),
|
|
76
|
+
authorName: z.string().optional(),
|
|
77
|
+
downloadUrl: z.string().url(),
|
|
78
|
+
artifactSha256: z.string().optional(),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export const updateExtensionListingRequestSchema = z.object({
|
|
82
|
+
listingDescriptionHtml: z.string().optional(),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
export const marketplaceSlotLookupResultSchema = z.discriminatedUnion('kind', [
|
|
86
|
+
z.object({ kind: z.literal('none') }),
|
|
87
|
+
z.object({
|
|
88
|
+
kind: z.literal('one'),
|
|
89
|
+
extension: marketplaceExtensionSummarySchema,
|
|
90
|
+
}),
|
|
91
|
+
z.object({
|
|
92
|
+
kind: z.literal('many'),
|
|
93
|
+
extensions: z.array(marketplaceExtensionSummarySchema).min(2),
|
|
94
|
+
}),
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
export const marketplaceSearchQuerySchema = z.object({
|
|
98
|
+
q: z.string().optional(),
|
|
99
|
+
registryId: z.string().optional(),
|
|
100
|
+
limit: z.coerce.number().int().min(1).max(100).optional(),
|
|
101
|
+
offset: z.coerce.number().int().min(0).optional(),
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
/** Максимальная Extension API, которую принимает каталог при публикации. */
|
|
105
|
+
export const MARKETPLACE_MAX_EXTENSION_API = 1;
|
|
106
|
+
|
|
107
|
+
export function validateManifestForMarketplacePublish(
|
|
108
|
+
manifest: Record<string, unknown>,
|
|
109
|
+
): { ok: true } | { ok: false; error: string } {
|
|
110
|
+
const id = manifest.id;
|
|
111
|
+
if (typeof id !== 'string' || !id.trim()) {
|
|
112
|
+
return { ok: false, error: 'manifest.id обязателен' };
|
|
113
|
+
}
|
|
114
|
+
const version = manifest.version;
|
|
115
|
+
if (typeof version !== 'string' || !version.trim()) {
|
|
116
|
+
return { ok: false, error: 'manifest.version обязателен' };
|
|
117
|
+
}
|
|
118
|
+
const apiMajor = parseExtensionApiMajor(manifest.extensionApi);
|
|
119
|
+
if (apiMajor === null) {
|
|
120
|
+
return { ok: false, error: 'extensionApi обязателен (например "1")' };
|
|
121
|
+
}
|
|
122
|
+
if (apiMajor > MARKETPLACE_MAX_EXTENSION_API) {
|
|
123
|
+
return {
|
|
124
|
+
ok: false,
|
|
125
|
+
error: `extensionApi ${apiMajor} не поддерживается каталогом (max ${MARKETPLACE_MAX_EXTENSION_API})`,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const raw = (manifest.contributes as Record<string, unknown> | undefined)?.rynt;
|
|
129
|
+
if (raw !== undefined) {
|
|
130
|
+
const parsed = marketplaceManifestContributesRyntSchema.safeParse(raw);
|
|
131
|
+
if (!parsed.success) {
|
|
132
|
+
return {
|
|
133
|
+
ok: false,
|
|
134
|
+
error: parsed.error.issues.map((i) => i.message).join('; '),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return { ok: true };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface ValidateSubmissionManifestOptions {
|
|
142
|
+
/** Последняя опубликованная версия (если обновление). */
|
|
143
|
+
latestPublishedVersion?: string;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Клиентская проверка перед загрузкой архива (сервер повторяет при сохранении заявки).
|
|
148
|
+
*/
|
|
149
|
+
export function validateSubmissionManifest(
|
|
150
|
+
manifest: Record<string, unknown>,
|
|
151
|
+
options?: ValidateSubmissionManifestOptions,
|
|
152
|
+
): { ok: true } | { ok: false; error: string } {
|
|
153
|
+
const base = validateManifestForMarketplacePublish(manifest);
|
|
154
|
+
if (!base.ok) return base;
|
|
155
|
+
|
|
156
|
+
const version = String(manifest.version);
|
|
157
|
+
if (!isValidSemver(version)) {
|
|
158
|
+
return {
|
|
159
|
+
ok: false,
|
|
160
|
+
error: `manifest.version «${version}» — не semver (ожидается MAJOR.MINOR.PATCH)`,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const latest = options?.latestPublishedVersion?.trim();
|
|
165
|
+
if (latest) {
|
|
166
|
+
if (!isValidSemver(latest)) {
|
|
167
|
+
return { ok: false, error: 'Некорректная версия в каталоге' };
|
|
168
|
+
}
|
|
169
|
+
if (!isVersionGreater(version, latest)) {
|
|
170
|
+
return {
|
|
171
|
+
ok: false,
|
|
172
|
+
error: `Версия ${version} должна быть выше опубликованной ${latest}`,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return { ok: true };
|
|
178
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
import type { Component } from 'vue';
|
|
4
|
+
|
|
5
|
+
const props = defineProps<{
|
|
6
|
+
pageComponent: Component;
|
|
7
|
+
extensionId: string;
|
|
8
|
+
registryKey: string;
|
|
9
|
+
}>();
|
|
10
|
+
|
|
11
|
+
/** Стабильный ключ: remount страницы — через suspend RouterView, не через смену import. */
|
|
12
|
+
const viewKey = computed(() => `${props.extensionId}:${props.registryKey}`);
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<component :is="pageComponent" :key="viewKey" />
|
|
17
|
+
</template>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RyntExtensionContext,
|
|
3
|
+
RyntExtensionHostApis,
|
|
4
|
+
} from './plugin-types';
|
|
5
|
+
import { setExtensionExpose } from './extension-expose';
|
|
6
|
+
import { getExtensionRegistry } from './registries/define-extension-registry';
|
|
7
|
+
import { withExtensionScope } from './registries/extension-scope';
|
|
8
|
+
|
|
9
|
+
export function createRyntExtensionContext(
|
|
10
|
+
extensionId: string,
|
|
11
|
+
hostApis?: RyntExtensionHostApis,
|
|
12
|
+
): RyntExtensionContext {
|
|
13
|
+
return {
|
|
14
|
+
extensionId,
|
|
15
|
+
hostApis,
|
|
16
|
+
registerRegistryEntry<T>(entry: {
|
|
17
|
+
registryId: string;
|
|
18
|
+
key: string;
|
|
19
|
+
value: T;
|
|
20
|
+
order?: number;
|
|
21
|
+
}) {
|
|
22
|
+
console.info(
|
|
23
|
+
`[rynt/extensions] registry «${entry.registryId}» / «${entry.key}» ← «${extensionId}»`,
|
|
24
|
+
);
|
|
25
|
+
withExtensionScope(extensionId, () => {
|
|
26
|
+
getExtensionRegistry<T>(entry.registryId).register(
|
|
27
|
+
entry.key,
|
|
28
|
+
entry.value,
|
|
29
|
+
entry.order,
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
defineExpose<T extends object>(value: T) {
|
|
34
|
+
setExtensionExpose(extensionId, value);
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Суффикс имени каталога: расширение в `*.disabled` не загружается лоадером. */
|
|
2
|
+
export const RYNT_EXTENSION_DISABLED_DIR_SUFFIX = '.disabled' as const;
|
|
3
|
+
|
|
4
|
+
export function isRyntExtensionBundleDirDisabled(dirname: string): boolean {
|
|
5
|
+
return dirname.endsWith(RYNT_EXTENSION_DISABLED_DIR_SUFFIX);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Имя папки без суффикса `.disabled`. */
|
|
9
|
+
export function ryntExtensionBundleDirBaseLabel(dirname: string): string {
|
|
10
|
+
return isRyntExtensionBundleDirDisabled(dirname)
|
|
11
|
+
? dirname.slice(
|
|
12
|
+
0,
|
|
13
|
+
dirname.length - RYNT_EXTENSION_DISABLED_DIR_SUFFIX.length,
|
|
14
|
+
)
|
|
15
|
+
: dirname;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function ryntExtensionBundleDisabledFolderName(enabledDirName: string): string {
|
|
19
|
+
if (isRyntExtensionBundleDirDisabled(enabledDirName)) return enabledDirName;
|
|
20
|
+
return `${enabledDirName}${RYNT_EXTENSION_DISABLED_DIR_SUFFIX}`;
|
|
21
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ComputedRef } from 'vue';
|
|
2
|
+
import { computed, shallowRef } from 'vue';
|
|
3
|
+
|
|
4
|
+
import type { ExtensionExposeMap } from '@rynt/sdk/extension-expose-map';
|
|
5
|
+
|
|
6
|
+
const exposes = new Map<string, unknown>();
|
|
7
|
+
|
|
8
|
+
/** Для `computed` в компонентах: expose могут появиться после загрузки другого расширения. */
|
|
9
|
+
export const extensionExposeRevision = shallowRef(0);
|
|
10
|
+
|
|
11
|
+
function bumpExposeRevision(): void {
|
|
12
|
+
extensionExposeRevision.value += 1;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function setExtensionExpose(extensionId: string, value: unknown): void {
|
|
16
|
+
exposes.set(extensionId, value);
|
|
17
|
+
bumpExposeRevision();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function clearExtensionExposes(): void {
|
|
21
|
+
exposes.clear();
|
|
22
|
+
bumpExposeRevision();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function clearExtensionExpose(extensionId: string): void {
|
|
26
|
+
if (exposes.delete(extensionId)) {
|
|
27
|
+
bumpExposeRevision();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getExtensionExpose<K extends keyof ExtensionExposeMap>(
|
|
32
|
+
id: K,
|
|
33
|
+
): ExtensionExposeMap[K] | undefined;
|
|
34
|
+
export function getExtensionExpose(id: string): unknown | undefined;
|
|
35
|
+
export function getExtensionExpose(id: string): unknown | undefined {
|
|
36
|
+
return exposes.get(id);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function useExtensionExpose<K extends keyof ExtensionExposeMap>(
|
|
40
|
+
id: K,
|
|
41
|
+
): ComputedRef<ExtensionExposeMap[K] | undefined>;
|
|
42
|
+
export function useExtensionExpose(id: string): ComputedRef<unknown | undefined>;
|
|
43
|
+
export function useExtensionExpose(id: string): ComputedRef<unknown | undefined> {
|
|
44
|
+
return computed(() => {
|
|
45
|
+
void extensionExposeRevision.value;
|
|
46
|
+
return exposes.get(id);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ExtensionDependencyGraph } from './manifest-types';
|
|
2
|
+
|
|
3
|
+
export interface ExtensionTopoNode {
|
|
4
|
+
id: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface TopoSortManifest {
|
|
8
|
+
id: string;
|
|
9
|
+
extensionDependencies?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Проверка циклов (DFS) и топологический порядок по `extensionDependencies`.
|
|
14
|
+
*/
|
|
15
|
+
export function topoSortExtensions<T extends TopoSortManifest>(
|
|
16
|
+
manifests: Map<string, T>,
|
|
17
|
+
): { ok: true; order: string[] } | { ok: false; error: string } {
|
|
18
|
+
const ids = new Set<string>(manifests.keys());
|
|
19
|
+
const graph: ExtensionDependencyGraph = {};
|
|
20
|
+
const missingDeps: string[] = [];
|
|
21
|
+
|
|
22
|
+
for (const [id, m] of manifests) {
|
|
23
|
+
const deps = m.extensionDependencies ?? {};
|
|
24
|
+
graph[id] = { ...deps };
|
|
25
|
+
for (const depId of Object.keys(deps)) {
|
|
26
|
+
if (!ids.has(depId)) {
|
|
27
|
+
missingDeps.push(`${id} depends on unknown extension "${depId}"`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (missingDeps.length > 0) {
|
|
33
|
+
return { ok: false, error: missingDeps.join('; ') };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const visiting = new Set<string>();
|
|
37
|
+
const visited = new Set<string>();
|
|
38
|
+
const order: string[] = [];
|
|
39
|
+
|
|
40
|
+
const visit = (id: string): void => {
|
|
41
|
+
if (visited.has(id)) return;
|
|
42
|
+
if (visiting.has(id)) {
|
|
43
|
+
throw new Error(`Cycle detected involving "${id}"`);
|
|
44
|
+
}
|
|
45
|
+
visiting.add(id);
|
|
46
|
+
const deps = Object.keys(graph[id] ?? {});
|
|
47
|
+
for (const dep of deps) {
|
|
48
|
+
visit(dep);
|
|
49
|
+
}
|
|
50
|
+
visiting.delete(id);
|
|
51
|
+
visited.add(id);
|
|
52
|
+
order.push(id);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
for (const id of ids) {
|
|
57
|
+
visit(id);
|
|
58
|
+
}
|
|
59
|
+
} catch (e) {
|
|
60
|
+
return {
|
|
61
|
+
ok: false,
|
|
62
|
+
error: e instanceof Error ? e.message : String(e),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return { ok: true, order };
|
|
67
|
+
}
|