@supernova-studio/client 1.96.5 → 1.96.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +101428 -143199
- package/dist/index.d.ts +101428 -143199
- package/dist/index.js +489 -99
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +5607 -5217
- package/dist/index.mjs.map +1 -1
- package/package.json +20 -9
- package/src/api/client.ts +62 -0
- package/src/api/conversion/analytics/index.ts +3 -0
- package/src/api/conversion/analytics/page-visits-to-comparison-dto.ts +65 -0
- package/src/api/conversion/analytics/page-visits-to-dto.ts +68 -0
- package/src/api/conversion/analytics/page-visits-to-heatmap-dto.ts +49 -0
- package/src/api/conversion/documentation/documentation-group-v1-to-dto.ts +114 -0
- package/src/api/conversion/documentation/documentation-group-v2-to-dto.ts +91 -0
- package/src/api/conversion/documentation/documentation-item-configuration-v1-to-dto.ts +52 -0
- package/src/api/conversion/documentation/documentation-item-configuration-v2-to-dto.ts +34 -0
- package/src/api/conversion/documentation/documentation-page-to-dto-utils.ts +134 -0
- package/src/api/conversion/documentation/documentation-page-v1-to-dto.ts +69 -0
- package/src/api/conversion/documentation/documentation-page-v2-to-dto.ts +84 -0
- package/src/api/conversion/documentation/documentation-settings-to-dto.ts +14 -0
- package/src/api/conversion/documentation/index.ts +8 -0
- package/src/api/conversion/export/index.ts +1 -0
- package/src/api/conversion/export/pipeline.ts +27 -0
- package/src/api/conversion/index.ts +4 -0
- package/src/api/conversion/integrations/git.ts +37 -0
- package/src/api/conversion/integrations/index.ts +2 -0
- package/src/api/conversion/integrations/integration.ts +37 -0
- package/src/api/dto/access-tokens/access-token.ts +45 -0
- package/src/api/dto/access-tokens/index.ts +1 -0
- package/src/api/dto/aux/color.ts +7 -0
- package/src/api/dto/aux/index.ts +4 -0
- package/src/api/dto/aux/meta.ts +8 -0
- package/src/api/dto/aux/object-metadata.ts +29 -0
- package/src/api/dto/aux/pagination.ts +8 -0
- package/src/api/dto/bff/app-bootstrap-data.ts +20 -0
- package/src/api/dto/bff/index.ts +1 -0
- package/src/api/dto/collections/index.ts +1 -0
- package/src/api/dto/collections/token-collection.ts +32 -0
- package/src/api/dto/design-systems/brand.ts +44 -0
- package/src/api/dto/design-systems/code-component-usage.ts +52 -0
- package/src/api/dto/design-systems/code-component.ts +101 -0
- package/src/api/dto/design-systems/code-history.ts +43 -0
- package/src/api/dto/design-systems/code-scan.ts +29 -0
- package/src/api/dto/design-systems/code-snapshots.ts +46 -0
- package/src/api/dto/design-systems/component.ts +40 -0
- package/src/api/dto/design-systems/contact.ts +11 -0
- package/src/api/dto/design-systems/data-source.ts +216 -0
- package/src/api/dto/design-systems/design-system.ts +75 -0
- package/src/api/dto/design-systems/documentation-search.ts +31 -0
- package/src/api/dto/design-systems/elements-diff.ts +14 -0
- package/src/api/dto/design-systems/figma-variables.ts +122 -0
- package/src/api/dto/design-systems/file.ts +71 -0
- package/src/api/dto/design-systems/import-job.ts +62 -0
- package/src/api/dto/design-systems/index.ts +24 -0
- package/src/api/dto/design-systems/members.ts +52 -0
- package/src/api/dto/design-systems/redirects.ts +33 -0
- package/src/api/dto/design-systems/role.ts +11 -0
- package/src/api/dto/design-systems/stats.ts +20 -0
- package/src/api/dto/design-systems/storybook.ts +45 -0
- package/src/api/dto/design-systems/user-design-systems.ts +10 -0
- package/src/api/dto/design-systems/version-room.ts +12 -0
- package/src/api/dto/design-systems/version.ts +56 -0
- package/src/api/dto/design-systems/view.ts +45 -0
- package/src/api/dto/design-tokens/design-token.ts +78 -0
- package/src/api/dto/design-tokens/index.ts +1 -0
- package/src/api/dto/documentation/analytics-v2.ts +80 -0
- package/src/api/dto/documentation/analytics.ts +32 -0
- package/src/api/dto/documentation/anchor.ts +15 -0
- package/src/api/dto/documentation/approvals.ts +21 -0
- package/src/api/dto/documentation/block-definition.ts +28 -0
- package/src/api/dto/documentation/block.ts +6 -0
- package/src/api/dto/documentation/documentation-page-snapshot.ts +18 -0
- package/src/api/dto/documentation/documentation.ts +34 -0
- package/src/api/dto/documentation/index.ts +11 -0
- package/src/api/dto/documentation/link-preview.ts +23 -0
- package/src/api/dto/documentation/publish.ts +23 -0
- package/src/api/dto/documentation/room.ts +12 -0
- package/src/api/dto/elements/components/figma-component-group.ts +19 -0
- package/src/api/dto/elements/components/figma-component.ts +56 -0
- package/src/api/dto/elements/components/index.ts +2 -0
- package/src/api/dto/elements/documentation/configuration.ts +20 -0
- package/src/api/dto/elements/documentation/draft-state.ts +37 -0
- package/src/api/dto/elements/documentation/group-action.ts +108 -0
- package/src/api/dto/elements/documentation/group-v1.ts +35 -0
- package/src/api/dto/elements/documentation/group-v2.ts +117 -0
- package/src/api/dto/elements/documentation/hierarchy.ts +25 -0
- package/src/api/dto/elements/documentation/index.ts +16 -0
- package/src/api/dto/elements/documentation/item-configuration-v1.ts +24 -0
- package/src/api/dto/elements/documentation/item-configuration-v2.ts +14 -0
- package/src/api/dto/elements/documentation/metadata.ts +8 -0
- package/src/api/dto/elements/documentation/page-actions-v2.ts +138 -0
- package/src/api/dto/elements/documentation/page-content.ts +15 -0
- package/src/api/dto/elements/documentation/page-dependencies.ts +30 -0
- package/src/api/dto/elements/documentation/page-v1.ts +21 -0
- package/src/api/dto/elements/documentation/page-v2.ts +132 -0
- package/src/api/dto/elements/documentation/settings.ts +9 -0
- package/src/api/dto/elements/documentation/structure.ts +37 -0
- package/src/api/dto/elements/elements-action-v2.ts +125 -0
- package/src/api/dto/elements/figma-nodes/figma-node-structure.ts +27 -0
- package/src/api/dto/elements/figma-nodes/figma-node-v1.ts +32 -0
- package/src/api/dto/elements/figma-nodes/figma-node-v2.ts +32 -0
- package/src/api/dto/elements/figma-nodes/figma-node.ts +103 -0
- package/src/api/dto/elements/figma-nodes/index.ts +5 -0
- package/src/api/dto/elements/figma-nodes/node-actions-v2.ts +25 -0
- package/src/api/dto/elements/frame-node-structures/frame-node-structure.ts +17 -0
- package/src/api/dto/elements/frame-node-structures/index.ts +1 -0
- package/src/api/dto/elements/get-elements-v2.ts +27 -0
- package/src/api/dto/elements/index.ts +7 -0
- package/src/api/dto/elements/properties/index.ts +2 -0
- package/src/api/dto/elements/properties/property-definitions.ts +75 -0
- package/src/api/dto/elements/properties/property-values.ts +50 -0
- package/src/api/dto/events/forge-project.ts +51 -0
- package/src/api/dto/events/index.ts +2 -0
- package/src/api/dto/events/workspace.ts +81 -0
- package/src/api/dto/export/exporter-property.ts +114 -0
- package/src/api/dto/export/exporter.ts +115 -0
- package/src/api/dto/export/filter.ts +20 -0
- package/src/api/dto/export/index.ts +5 -0
- package/src/api/dto/export/job.ts +114 -0
- package/src/api/dto/export/pipeline.ts +60 -0
- package/src/api/dto/figma-components/assets/download.ts +30 -0
- package/src/api/dto/figma-components/assets/index.ts +1 -0
- package/src/api/dto/figma-components/index.ts +1 -0
- package/src/api/dto/figma-exporter/figma-node.ts +11 -0
- package/src/api/dto/figma-exporter/index.ts +1 -0
- package/src/api/dto/files/files.ts +94 -0
- package/src/api/dto/files/index.ts +1 -0
- package/src/api/dto/forge/agent.ts +38 -0
- package/src/api/dto/forge/artifact.ts +94 -0
- package/src/api/dto/forge/feature-messages.ts +551 -0
- package/src/api/dto/forge/feature-room.ts +12 -0
- package/src/api/dto/forge/figma-node.ts +117 -0
- package/src/api/dto/forge/index.ts +28 -0
- package/src/api/dto/forge/iteration-message-old.ts +43 -0
- package/src/api/dto/forge/knowledge-item.ts +67 -0
- package/src/api/dto/forge/memory.ts +67 -0
- package/src/api/dto/forge/participant.ts +44 -0
- package/src/api/dto/forge/project-action.ts +123 -0
- package/src/api/dto/forge/project-artifact-room.ts +12 -0
- package/src/api/dto/forge/project-artifact.ts +85 -0
- package/src/api/dto/forge/project-context-feedback.ts +141 -0
- package/src/api/dto/forge/project-context-override.ts +41 -0
- package/src/api/dto/forge/project-context-scope.ts +23 -0
- package/src/api/dto/forge/project-context-v2.ts +228 -0
- package/src/api/dto/forge/project-context.ts +72 -0
- package/src/api/dto/forge/project-feature.ts +86 -0
- package/src/api/dto/forge/project-figma-node.ts +9 -0
- package/src/api/dto/forge/project-file.ts +48 -0
- package/src/api/dto/forge/project-invitation.ts +46 -0
- package/src/api/dto/forge/project-iteration-old.ts +54 -0
- package/src/api/dto/forge/project-member.ts +67 -0
- package/src/api/dto/forge/project-room.ts +12 -0
- package/src/api/dto/forge/project-section.ts +37 -0
- package/src/api/dto/forge/project.ts +105 -0
- package/src/api/dto/forge/relation.ts +34 -0
- package/src/api/dto/forge/threads.ts +153 -0
- package/src/api/dto/index.ts +25 -0
- package/src/api/dto/liveblocks/auth-response.ts +7 -0
- package/src/api/dto/liveblocks/index.ts +1 -0
- package/src/api/dto/mcp/index.ts +1 -0
- package/src/api/dto/mcp/stream.ts +17 -0
- package/src/api/dto/notifications/index.ts +2 -0
- package/src/api/dto/notifications/notifications.ts +65 -0
- package/src/api/dto/notifications/settings.ts +10 -0
- package/src/api/dto/portal/index.ts +1 -0
- package/src/api/dto/portal/portal-settings.ts +45 -0
- package/src/api/dto/sandboxes/index.ts +1 -0
- package/src/api/dto/sandboxes/template.ts +143 -0
- package/src/api/dto/storybook-sites/index.ts +1 -0
- package/src/api/dto/storybook-sites/storybook-sites.ts +60 -0
- package/src/api/dto/themes/index.ts +2 -0
- package/src/api/dto/themes/override.ts +27 -0
- package/src/api/dto/themes/theme.ts +53 -0
- package/src/api/dto/threads/index.ts +1 -0
- package/src/api/dto/threads/threads.ts +280 -0
- package/src/api/dto/trail-events/index.ts +1 -0
- package/src/api/dto/trail-events/trail-events.ts +172 -0
- package/src/api/dto/users/authenticated-user.ts +92 -0
- package/src/api/dto/users/index.ts +2 -0
- package/src/api/dto/users/user.ts +27 -0
- package/src/api/dto/workspaces/billing.ts +156 -0
- package/src/api/dto/workspaces/enums.ts +13 -0
- package/src/api/dto/workspaces/git.ts +32 -0
- package/src/api/dto/workspaces/index.ts +15 -0
- package/src/api/dto/workspaces/integrations.ts +34 -0
- package/src/api/dto/workspaces/invitations.ts +47 -0
- package/src/api/dto/workspaces/ip-whitelist.ts +33 -0
- package/src/api/dto/workspaces/knowledge-items-search.ts +37 -0
- package/src/api/dto/workspaces/login-screen.ts +42 -0
- package/src/api/dto/workspaces/membership.ts +55 -0
- package/src/api/dto/workspaces/npm-registry.ts +62 -0
- package/src/api/dto/workspaces/products.ts +45 -0
- package/src/api/dto/workspaces/subscription.ts +170 -0
- package/src/api/dto/workspaces/transfer-ownership.ts +7 -0
- package/src/api/dto/workspaces/untyped-data.ts +35 -0
- package/src/api/dto/workspaces/workspace.ts +49 -0
- package/src/api/endpoints/auth-tokens.ts +28 -0
- package/src/api/endpoints/codegen/codegen.ts +16 -0
- package/src/api/endpoints/codegen/ds-pipelines.ts +37 -0
- package/src/api/endpoints/codegen/exporters.ts +54 -0
- package/src/api/endpoints/codegen/index.ts +5 -0
- package/src/api/endpoints/codegen/jobs.ts +15 -0
- package/src/api/endpoints/codegen/pipelines.ts +55 -0
- package/src/api/endpoints/design-system/analytics.ts +14 -0
- package/src/api/endpoints/design-system/bff.ts +13 -0
- package/src/api/endpoints/design-system/code-snapshots.ts +47 -0
- package/src/api/endpoints/design-system/contact.ts +12 -0
- package/src/api/endpoints/design-system/design-systems.ts +90 -0
- package/src/api/endpoints/design-system/figma-node-structures.ts +20 -0
- package/src/api/endpoints/design-system/index.ts +11 -0
- package/src/api/endpoints/design-system/members.ts +24 -0
- package/src/api/endpoints/design-system/redirects.ts +46 -0
- package/src/api/endpoints/design-system/sources.ts +80 -0
- package/src/api/endpoints/design-system/storybook.ts +28 -0
- package/src/api/endpoints/design-system/versions/brands.ts +45 -0
- package/src/api/endpoints/design-system/versions/documentation.ts +107 -0
- package/src/api/endpoints/design-system/versions/ds-components.ts +30 -0
- package/src/api/endpoints/design-system/versions/elements-action.ts +65 -0
- package/src/api/endpoints/design-system/versions/elements.ts +27 -0
- package/src/api/endpoints/design-system/versions/figma-component-groups.ts +13 -0
- package/src/api/endpoints/design-system/versions/figma-components.ts +15 -0
- package/src/api/endpoints/design-system/versions/figma-frame-structures.ts +13 -0
- package/src/api/endpoints/design-system/versions/files.ts +43 -0
- package/src/api/endpoints/design-system/versions/import-jobs.ts +13 -0
- package/src/api/endpoints/design-system/versions/index.ts +19 -0
- package/src/api/endpoints/design-system/versions/overrides.ts +17 -0
- package/src/api/endpoints/design-system/versions/property-definitions.ts +43 -0
- package/src/api/endpoints/design-system/versions/property-values.ts +25 -0
- package/src/api/endpoints/design-system/versions/stats.ts +12 -0
- package/src/api/endpoints/design-system/versions/themes.ts +35 -0
- package/src/api/endpoints/design-system/versions/token-collections.ts +13 -0
- package/src/api/endpoints/design-system/versions/token-groups.ts +30 -0
- package/src/api/endpoints/design-system/versions/tokens.ts +29 -0
- package/src/api/endpoints/design-system/versions/versions.ts +90 -0
- package/src/api/endpoints/feedback.ts +60 -0
- package/src/api/endpoints/files.ts +25 -0
- package/src/api/endpoints/forge/agents.ts +54 -0
- package/src/api/endpoints/forge/artifacts.ts +54 -0
- package/src/api/endpoints/forge/documents.ts +18 -0
- package/src/api/endpoints/forge/feature-artifacts.ts +39 -0
- package/src/api/endpoints/forge/feature-iterations.ts +49 -0
- package/src/api/endpoints/forge/feature-messages.ts +68 -0
- package/src/api/endpoints/forge/features.ts +63 -0
- package/src/api/endpoints/forge/forge.ts +38 -0
- package/src/api/endpoints/forge/index.ts +16 -0
- package/src/api/endpoints/forge/iteration-tags.ts +13 -0
- package/src/api/endpoints/forge/memory.ts +41 -0
- package/src/api/endpoints/forge/project-contexts.ts +49 -0
- package/src/api/endpoints/forge/project-files.ts +41 -0
- package/src/api/endpoints/forge/project-invitations.ts +39 -0
- package/src/api/endpoints/forge/project-iterations.ts +65 -0
- package/src/api/endpoints/forge/project-members.ts +55 -0
- package/src/api/endpoints/forge/projects.ts +56 -0
- package/src/api/endpoints/index.ts +13 -0
- package/src/api/endpoints/liveblocks.ts +14 -0
- package/src/api/endpoints/mcp/index.ts +1 -0
- package/src/api/endpoints/mcp/streams.ts +21 -0
- package/src/api/endpoints/sandboxes/builds.ts +30 -0
- package/src/api/endpoints/sandboxes/index.ts +3 -0
- package/src/api/endpoints/sandboxes/sandboxes.ts +13 -0
- package/src/api/endpoints/sandboxes/templates.ts +16 -0
- package/src/api/endpoints/storybook-sites.ts +42 -0
- package/src/api/endpoints/threads.ts +38 -0
- package/src/api/endpoints/users.ts +26 -0
- package/src/api/endpoints/workspaces/billing.ts +24 -0
- package/src/api/endpoints/workspaces/chat-threads.ts +95 -0
- package/src/api/endpoints/workspaces/index.ts +9 -0
- package/src/api/endpoints/workspaces/integrations.ts +15 -0
- package/src/api/endpoints/workspaces/invites.ts +34 -0
- package/src/api/endpoints/workspaces/knowledge-items.ts +59 -0
- package/src/api/endpoints/workspaces/members.ts +31 -0
- package/src/api/endpoints/workspaces/npm-register.ts +20 -0
- package/src/api/endpoints/workspaces/subscription.ts +31 -0
- package/src/api/endpoints/workspaces/workspaces.ts +84 -0
- package/src/api/index.ts +6 -0
- package/src/api/payloads/design-systems/code-snapshots.ts +27 -0
- package/src/api/payloads/design-systems/index.ts +3 -0
- package/src/api/payloads/design-systems/update-design-system.ts +42 -0
- package/src/api/payloads/design-systems/version.ts +29 -0
- package/src/api/payloads/documentation/analytics.ts +36 -0
- package/src/api/payloads/documentation/block-definitions.ts +18 -0
- package/src/api/payloads/documentation/design-data-doc-diff.ts +7 -0
- package/src/api/payloads/documentation/index.ts +3 -0
- package/src/api/payloads/export/index.ts +1 -0
- package/src/api/payloads/export/pipeline.ts +111 -0
- package/src/api/payloads/index.ts +6 -0
- package/src/api/payloads/liveblocks/auth.ts +7 -0
- package/src/api/payloads/liveblocks/index.ts +1 -0
- package/src/api/payloads/users/index.ts +1 -0
- package/src/api/payloads/users/notifications/index.ts +1 -0
- package/src/api/payloads/users/notifications/notification-settings.ts +17 -0
- package/src/api/payloads/workspaces/index.ts +2 -0
- package/src/api/payloads/workspaces/workspace-configuration.ts +62 -0
- package/src/api/payloads/workspaces/workspace-integrations.ts +35 -0
- package/src/api/transport/index.ts +2 -0
- package/src/api/transport/request-executor-error.ts +60 -0
- package/src/api/transport/request-executor.ts +94 -0
- package/src/events/design-system.ts +21 -0
- package/src/events/event.ts +6 -0
- package/src/events/index.ts +2 -0
- package/src/index.ts +5 -0
- package/src/sync/docs-local-action-executor.ts +412 -0
- package/src/sync/docs-structure-repo.ts +283 -0
- package/src/sync/index.ts +5 -0
- package/src/sync/project-content-repo.ts +215 -0
- package/src/sync/project-local-action-executor.ts +415 -0
- package/src/sync/types.ts +37 -0
- package/src/utils/figma.ts +72 -0
- package/src/utils/hash.ts +62 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/query.ts +41 -0
- package/src/utils/redirect-validation.ts +76 -0
- package/src/yjs/design-system-content/documentation-hierarchy.ts +35 -0
- package/src/yjs/design-system-content/index.ts +2 -0
- package/src/yjs/design-system-content/item-configuration.ts +62 -0
- package/src/yjs/docs-editor/blocks-to-prosemirror.ts +764 -0
- package/src/yjs/docs-editor/index.ts +7 -0
- package/src/yjs/docs-editor/list-tree-builder.ts +127 -0
- package/src/yjs/docs-editor/mock.ts +2271 -0
- package/src/yjs/docs-editor/model/block.ts +8 -0
- package/src/yjs/docs-editor/model/index.ts +2 -0
- package/src/yjs/docs-editor/model/page.ts +8 -0
- package/src/yjs/docs-editor/prosemirror/index.ts +3 -0
- package/src/yjs/docs-editor/prosemirror/inner-editor-schema.ts +145 -0
- package/src/yjs/docs-editor/prosemirror/main-editor-schema.ts +577 -0
- package/src/yjs/docs-editor/prosemirror/types.ts +19 -0
- package/src/yjs/docs-editor/prosemirror-to-blocks.ts +986 -0
- package/src/yjs/docs-editor/utils.ts +115 -0
- package/src/yjs/feature-room/backend.ts +36 -0
- package/src/yjs/feature-room/base.ts +109 -0
- package/src/yjs/feature-room/frontend.ts +53 -0
- package/src/yjs/feature-room/index.ts +3 -0
- package/src/yjs/forge-project-room/backend.ts +59 -0
- package/src/yjs/forge-project-room/base.ts +190 -0
- package/src/yjs/forge-project-room/index.ts +2 -0
- package/src/yjs/index.ts +7 -0
- package/src/yjs/thread-room/backend.ts +32 -0
- package/src/yjs/thread-room/base.ts +71 -0
- package/src/yjs/thread-room/frontend.ts +39 -0
- package/src/yjs/thread-room/index.ts +3 -0
- package/src/yjs/utils/index.ts +1 -0
- package/src/yjs/utils/key-value-storage.ts +57 -0
- package/src/yjs/version-room/backend.ts +101 -0
- package/src/yjs/version-room/base.ts +323 -0
- package/src/yjs/version-room/compute-dto.ts +387 -0
- package/src/yjs/version-room/frontend.ts +76 -0
- package/src/yjs/version-room/index.ts +6 -0
- package/src/yjs/version-room/types.ts +31 -0
- package/src/yjs/version-room/utils.ts +109 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import {
|
|
2
|
+
defaultDocumentationItemConfigurationV2,
|
|
3
|
+
generateShortPersistentId,
|
|
4
|
+
mapByUnique,
|
|
5
|
+
RoomType,
|
|
6
|
+
slugify,
|
|
7
|
+
} from "@supernova-studio/model";
|
|
8
|
+
import { DTOElementActionInput, DTOElementActionInputOfType } from "../api";
|
|
9
|
+
import { VersionRoomApproval, VersionRoomBaseYDocState, VersionRoomDocsPage, VersionRoomDocsPageGroup } from "../yjs";
|
|
10
|
+
import { LocalApproval, LocalDocsPage, LocalDocsPageGroup } from "./types";
|
|
11
|
+
|
|
12
|
+
//
|
|
13
|
+
// Types
|
|
14
|
+
//
|
|
15
|
+
|
|
16
|
+
type Page = LocalDocsPage | VersionRoomDocsPage;
|
|
17
|
+
type Group = LocalDocsPageGroup | VersionRoomDocsPageGroup;
|
|
18
|
+
type ApprovalState = LocalApproval | VersionRoomApproval;
|
|
19
|
+
|
|
20
|
+
//
|
|
21
|
+
// Interface
|
|
22
|
+
//
|
|
23
|
+
|
|
24
|
+
type Input = LocalExecutorConfig & {
|
|
25
|
+
actions: DTOElementActionInput[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export function applyActionsLocally(input: Input) {
|
|
29
|
+
const actionExecutor = new LocalDocsElementActionExecutor(input);
|
|
30
|
+
actionExecutor.applyActions(input.actions);
|
|
31
|
+
return actionExecutor.localState;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//
|
|
35
|
+
// Executor
|
|
36
|
+
//
|
|
37
|
+
|
|
38
|
+
type LocalExecutorConfig = {
|
|
39
|
+
userId: string;
|
|
40
|
+
designSystemVersionId: string;
|
|
41
|
+
remoteState: VersionRoomBaseYDocState;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export class LocalDocsElementActionExecutor {
|
|
45
|
+
private readonly userId: string;
|
|
46
|
+
private readonly designSystemVersionId: string;
|
|
47
|
+
|
|
48
|
+
private readonly pages: Map<string, Page>;
|
|
49
|
+
private readonly groups: Map<string, Group>;
|
|
50
|
+
private readonly approvalStates: Map<string, ApprovalState>;
|
|
51
|
+
private readonly pageLiveblockRoomIds: Record<string, string>;
|
|
52
|
+
|
|
53
|
+
constructor(config: LocalExecutorConfig) {
|
|
54
|
+
const { designSystemVersionId, remoteState, userId } = config;
|
|
55
|
+
|
|
56
|
+
this.userId = userId;
|
|
57
|
+
this.designSystemVersionId = designSystemVersionId;
|
|
58
|
+
|
|
59
|
+
// Seed the state with remote state
|
|
60
|
+
this.pages = mapByUnique(remoteState.pages, p => p.persistentId);
|
|
61
|
+
this.groups = mapByUnique(remoteState.groups, p => p.persistentId);
|
|
62
|
+
this.approvalStates = mapByUnique(remoteState.approvals, a => a.pagePersistentId);
|
|
63
|
+
this.pageLiveblockRoomIds = { ...remoteState.pageLiveblockRoomIds };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get localState() {
|
|
67
|
+
return {
|
|
68
|
+
pages: Array.from(this.pages.values()),
|
|
69
|
+
groups: Array.from(this.groups.values()),
|
|
70
|
+
approvals: Array.from(this.approvalStates.values()),
|
|
71
|
+
pageLiveblockRoomIds: this.pageLiveblockRoomIds,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
applyActions(trx: DTOElementActionInput[]) {
|
|
76
|
+
trx.forEach(trx => this.applyTransaction(trx));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
applyTransaction(trx: DTOElementActionInput) {
|
|
80
|
+
switch (trx.type) {
|
|
81
|
+
// Groups
|
|
82
|
+
case "DocumentationGroupCreate":
|
|
83
|
+
return this.documentationGroupCreate(trx);
|
|
84
|
+
case "DocumentationGroupUpdate":
|
|
85
|
+
return this.documentationGroupUpdate(trx);
|
|
86
|
+
case "DocumentationGroupMove":
|
|
87
|
+
return this.documentationGroupMove(trx);
|
|
88
|
+
|
|
89
|
+
// Groups - unsupported
|
|
90
|
+
case "DocumentationGroupDelete":
|
|
91
|
+
case "DocumentationGroupDuplicate":
|
|
92
|
+
case "DocumentationGroupRestore":
|
|
93
|
+
throw new Error(`Transaction type ${trx.type} is not yet implemented`);
|
|
94
|
+
|
|
95
|
+
// Pages
|
|
96
|
+
case "DocumentationPageCreate":
|
|
97
|
+
return this.documentationPageCreate(trx);
|
|
98
|
+
case "DocumentationPageUpdate":
|
|
99
|
+
return this.documentationPageUpdate(trx);
|
|
100
|
+
case "DocumentationPageMove":
|
|
101
|
+
return this.documentationPageMove(trx);
|
|
102
|
+
case "DocumentationPageDelete":
|
|
103
|
+
return this.documentationPageDelete(trx);
|
|
104
|
+
|
|
105
|
+
// Pages - unsupported
|
|
106
|
+
case "DocumentationPageApprovalStateChange":
|
|
107
|
+
return this.documentationApprovalStateUpdate(trx);
|
|
108
|
+
|
|
109
|
+
case "DocumentationPageDuplicate":
|
|
110
|
+
case "DocumentationPageRestore":
|
|
111
|
+
throw new Error(`Transaction type ${trx.type} is not yet implemented`);
|
|
112
|
+
|
|
113
|
+
// Tabs
|
|
114
|
+
case "DocumentationTabCreate":
|
|
115
|
+
return this.documentationTabCreate(trx);
|
|
116
|
+
|
|
117
|
+
case "DocumentationTabGroupDelete":
|
|
118
|
+
throw new Error(`Transaction type ${trx.type} is not yet implemented`);
|
|
119
|
+
|
|
120
|
+
// Won't ever be supported
|
|
121
|
+
case "FigmaNodeRenderAsync":
|
|
122
|
+
throw new Error(`Transaction type ${trx.type} is not a documentation element action`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
//
|
|
127
|
+
// Pages
|
|
128
|
+
//
|
|
129
|
+
|
|
130
|
+
private documentationPageCreate(trx: DTOElementActionInputOfType<"DocumentationPageCreate">) {
|
|
131
|
+
const { input } = trx;
|
|
132
|
+
const { persistentId } = input;
|
|
133
|
+
|
|
134
|
+
if (this.pages.has(input.persistentId)) {
|
|
135
|
+
// TODO Artem: uncomment when this is synced with remote state
|
|
136
|
+
// throw new Error(`Cannot create page: persistent id ${input.persistentId} already exists`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!this.groups.has(input.parentPersistentId)) {
|
|
141
|
+
throw new Error(`Cannot create page: parent persistent id ${input.parentPersistentId} was not found`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const localPage: LocalDocsPage = {
|
|
145
|
+
persistentId,
|
|
146
|
+
createdAt: new Date(),
|
|
147
|
+
parentPersistentId: input.parentPersistentId,
|
|
148
|
+
shortPersistentId: generateShortPersistentId(),
|
|
149
|
+
slug: slugify(input.title),
|
|
150
|
+
meta: { name: input.title },
|
|
151
|
+
updatedAt: new Date(),
|
|
152
|
+
data: {
|
|
153
|
+
// TODO Artem: move somewhere reusable
|
|
154
|
+
configuration: input.configuration
|
|
155
|
+
? { ...defaultDocumentationItemConfigurationV2, ...input.configuration }
|
|
156
|
+
: input.configuration,
|
|
157
|
+
},
|
|
158
|
+
sortOrder: this.calculateSortOrder(input.parentPersistentId, input.afterPersistentId),
|
|
159
|
+
designSystemVersionId: this.designSystemVersionId,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
this.pages.set(persistentId, localPage);
|
|
163
|
+
|
|
164
|
+
const roomId = `${RoomType.DocumentationPage}:${this.designSystemVersionId}:${persistentId}`;
|
|
165
|
+
this.pageLiveblockRoomIds[persistentId] = roomId;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private documentationPageUpdate(trx: DTOElementActionInputOfType<"DocumentationPageUpdate">) {
|
|
169
|
+
const { input } = trx;
|
|
170
|
+
|
|
171
|
+
const existingPage = this.pages.get(input.id);
|
|
172
|
+
if (!existingPage) {
|
|
173
|
+
throw new Error(`Cannot update page: page id ${input.id} was not found`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const localPage: LocalDocsPage = {
|
|
177
|
+
...existingPage,
|
|
178
|
+
userSlug: undefined,
|
|
179
|
+
meta: {
|
|
180
|
+
...existingPage.meta,
|
|
181
|
+
name: input.title ?? existingPage.meta.name,
|
|
182
|
+
},
|
|
183
|
+
data: {
|
|
184
|
+
// TODO Artem: move somewhere reusable
|
|
185
|
+
configuration: input.configuration
|
|
186
|
+
? { ...(existingPage.data.configuration ?? defaultDocumentationItemConfigurationV2), ...input.configuration }
|
|
187
|
+
: existingPage.data.configuration,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
this.pages.set(localPage.persistentId, localPage);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private documentationPageMove(trx: DTOElementActionInputOfType<"DocumentationPageMove">) {
|
|
195
|
+
const { input } = trx;
|
|
196
|
+
|
|
197
|
+
if (!this.groups.has(input.parentPersistentId)) {
|
|
198
|
+
throw new Error(`Cannot move page: page parent id ${input.parentPersistentId} was not found`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const existingPage = this.pages.get(input.id);
|
|
202
|
+
if (!existingPage) {
|
|
203
|
+
throw new Error(`Cannot update page: page id ${input.id} was not found`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const localPage: LocalDocsPage = {
|
|
207
|
+
...existingPage,
|
|
208
|
+
userSlug: undefined,
|
|
209
|
+
sortOrder: this.calculateSortOrder(input.parentPersistentId, input.afterPersistentId),
|
|
210
|
+
parentPersistentId: input.parentPersistentId,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
this.pages.set(localPage.persistentId, localPage);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
private documentationPageDelete(trx: DTOElementActionInputOfType<"DocumentationPageDelete">) {
|
|
217
|
+
const { input } = trx;
|
|
218
|
+
|
|
219
|
+
if (!this.pages.delete(trx.input.id)) {
|
|
220
|
+
throw new Error(`Cannot delete page: page id ${input.id} was not found`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
delete this.pageLiveblockRoomIds[trx.input.id];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
//
|
|
227
|
+
// Group
|
|
228
|
+
//
|
|
229
|
+
|
|
230
|
+
private documentationGroupCreate(trx: DTOElementActionInputOfType<"DocumentationGroupCreate">) {
|
|
231
|
+
const { input } = trx;
|
|
232
|
+
|
|
233
|
+
if (this.groups.has(input.persistentId)) {
|
|
234
|
+
// TODO Artem: uncomment when this is synced with remote state
|
|
235
|
+
// throw new Error(`Cannot create page: persistent id ${input.persistentId} already exists`);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const localGroup: LocalDocsPageGroup = {
|
|
240
|
+
parentPersistentId: input.parentPersistentId,
|
|
241
|
+
persistentId: input.persistentId,
|
|
242
|
+
shortPersistentId: generateShortPersistentId(),
|
|
243
|
+
slug: slugify(input.title),
|
|
244
|
+
meta: { name: input.title },
|
|
245
|
+
createdAt: new Date(),
|
|
246
|
+
updatedAt: new Date(),
|
|
247
|
+
data: {
|
|
248
|
+
// TODO Artem: move somewhere reusable
|
|
249
|
+
configuration: input.configuration
|
|
250
|
+
? { ...defaultDocumentationItemConfigurationV2, ...input.configuration }
|
|
251
|
+
: input.configuration,
|
|
252
|
+
},
|
|
253
|
+
sortOrder: this.calculateSortOrder(input.parentPersistentId, input.afterPersistentId),
|
|
254
|
+
designSystemVersionId: this.designSystemVersionId,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
this.groups.set(localGroup.persistentId, localGroup);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private documentationGroupUpdate(trx: DTOElementActionInputOfType<"DocumentationGroupUpdate">) {
|
|
261
|
+
const { input } = trx;
|
|
262
|
+
|
|
263
|
+
const existingGroup = this.groups.get(input.id);
|
|
264
|
+
if (!existingGroup) {
|
|
265
|
+
throw new Error(`Cannot update group: group id ${input.id} was not found`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const localGroup: LocalDocsPageGroup = {
|
|
269
|
+
...existingGroup,
|
|
270
|
+
userSlug: undefined,
|
|
271
|
+
meta: {
|
|
272
|
+
...existingGroup.meta,
|
|
273
|
+
name: input.title ?? existingGroup.meta.name,
|
|
274
|
+
},
|
|
275
|
+
data: {
|
|
276
|
+
...existingGroup.data,
|
|
277
|
+
|
|
278
|
+
// TODO Artem: move somewhere reusable
|
|
279
|
+
configuration: input.configuration
|
|
280
|
+
? {
|
|
281
|
+
...(existingGroup.data?.configuration ?? defaultDocumentationItemConfigurationV2),
|
|
282
|
+
...input.configuration,
|
|
283
|
+
}
|
|
284
|
+
: existingGroup.data?.configuration,
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
this.groups.set(localGroup.persistentId, localGroup);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private documentationGroupMove(trx: DTOElementActionInputOfType<"DocumentationGroupMove">) {
|
|
292
|
+
const { input } = trx;
|
|
293
|
+
|
|
294
|
+
if (!this.groups.has(input.parentPersistentId)) {
|
|
295
|
+
throw new Error(`Cannot move group: group parent id ${input.parentPersistentId} was not found`);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const existingGroup = this.groups.get(input.id);
|
|
299
|
+
if (!existingGroup) {
|
|
300
|
+
throw new Error(`Cannot update group: group id ${input.id} was not found`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const localGroup: LocalDocsPageGroup = {
|
|
304
|
+
...existingGroup,
|
|
305
|
+
userSlug: undefined,
|
|
306
|
+
sortOrder: this.calculateSortOrder(input.parentPersistentId, input.afterPersistentId),
|
|
307
|
+
parentPersistentId: input.parentPersistentId,
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
this.groups.set(localGroup.persistentId, localGroup);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
//
|
|
314
|
+
// Tabs
|
|
315
|
+
//
|
|
316
|
+
|
|
317
|
+
private documentationTabCreate(trx: DTOElementActionInputOfType<"DocumentationTabCreate">) {
|
|
318
|
+
const { input } = trx;
|
|
319
|
+
|
|
320
|
+
const page = this.pages.get(input.fromItemPersistentId);
|
|
321
|
+
if (!page) {
|
|
322
|
+
throw new Error(`Cannot create tab: page id ${input.fromItemPersistentId} was not found`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Create tab group in place of the page
|
|
326
|
+
const tabGroup: LocalDocsPageGroup = {
|
|
327
|
+
parentPersistentId: page.parentPersistentId,
|
|
328
|
+
persistentId: input.persistentId,
|
|
329
|
+
shortPersistentId: generateShortPersistentId(),
|
|
330
|
+
slug: page.slug,
|
|
331
|
+
meta: page.meta,
|
|
332
|
+
createdAt: new Date(),
|
|
333
|
+
updatedAt: new Date(),
|
|
334
|
+
data: {
|
|
335
|
+
behavior: "Tabs",
|
|
336
|
+
configuration: page?.data.configuration,
|
|
337
|
+
},
|
|
338
|
+
sortOrder: page.sortOrder,
|
|
339
|
+
designSystemVersionId: this.designSystemVersionId,
|
|
340
|
+
};
|
|
341
|
+
this.groups.set(input.persistentId, tabGroup);
|
|
342
|
+
|
|
343
|
+
const newLocalPage: LocalDocsPage = {
|
|
344
|
+
...page,
|
|
345
|
+
userSlug: undefined,
|
|
346
|
+
sortOrder: 0,
|
|
347
|
+
parentPersistentId: input.persistentId,
|
|
348
|
+
meta: { name: input.tabName },
|
|
349
|
+
};
|
|
350
|
+
this.pages.set(newLocalPage.persistentId, newLocalPage);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
//
|
|
354
|
+
// Approval states
|
|
355
|
+
//
|
|
356
|
+
|
|
357
|
+
private documentationApprovalStateUpdate(trx: DTOElementActionInputOfType<"DocumentationPageApprovalStateChange">) {
|
|
358
|
+
const { input } = trx;
|
|
359
|
+
|
|
360
|
+
const existingApproval = this.approvalStates.get(input.persistentId);
|
|
361
|
+
|
|
362
|
+
if (input.approvalState) {
|
|
363
|
+
this.approvalStates.set(input.persistentId, {
|
|
364
|
+
approvalState: input.approvalState,
|
|
365
|
+
createdAt: existingApproval?.createdAt ?? new Date(),
|
|
366
|
+
designSystemVersionId: this.designSystemVersionId,
|
|
367
|
+
pagePersistentId: input.persistentId,
|
|
368
|
+
updatedAt: new Date(),
|
|
369
|
+
updatedByUserId: this.userId,
|
|
370
|
+
} satisfies LocalApproval);
|
|
371
|
+
} else {
|
|
372
|
+
this.approvalStates.delete(input.persistentId);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
//
|
|
377
|
+
// Utils
|
|
378
|
+
//
|
|
379
|
+
|
|
380
|
+
private calculateSortOrder(parentPersistentId: string, afterPersistentId?: string | null): number {
|
|
381
|
+
const sortOrderStep = Math.pow(2, 16);
|
|
382
|
+
|
|
383
|
+
const neighbours = [
|
|
384
|
+
...Array.from(this.pages.values()).filter(p => p.parentPersistentId === parentPersistentId),
|
|
385
|
+
...Array.from(this.groups.values()).filter(g => g.parentPersistentId === parentPersistentId),
|
|
386
|
+
];
|
|
387
|
+
|
|
388
|
+
// If no neighbors, just assign 0
|
|
389
|
+
if (!neighbours.length) return 0;
|
|
390
|
+
|
|
391
|
+
neighbours.sort((lhs, rhs) => lhs.sortOrder - rhs.sortOrder);
|
|
392
|
+
|
|
393
|
+
// Null afterPersistentId means beginning
|
|
394
|
+
if (afterPersistentId === null) return neighbours[0].sortOrder - sortOrderStep;
|
|
395
|
+
|
|
396
|
+
// Undefined afterPersistentId means default behavior (end of the list)
|
|
397
|
+
if (!afterPersistentId) return neighbours[neighbours.length - 1].sortOrder + sortOrderStep;
|
|
398
|
+
|
|
399
|
+
const index = neighbours.findIndex(e => e.persistentId === afterPersistentId);
|
|
400
|
+
|
|
401
|
+
// If page is not found or adding after the last item is requested, assign last position
|
|
402
|
+
if (index < 0 || index === neighbours.length - 1) {
|
|
403
|
+
return neighbours[neighbours.length - 1].sortOrder + sortOrderStep;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// Insert in between items
|
|
407
|
+
const left = neighbours[index].sortOrder;
|
|
408
|
+
const right = neighbours[index + 1]?.sortOrder ?? left + sortOrderStep * 2;
|
|
409
|
+
|
|
410
|
+
return (right + left) / 2;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { OmitStrict } from "@supernova-studio/model";
|
|
2
|
+
import PQueue from "p-queue";
|
|
3
|
+
import * as Y from "yjs";
|
|
4
|
+
import {
|
|
5
|
+
DTODocumentationHierarchyV2,
|
|
6
|
+
DTODocumentationSettings,
|
|
7
|
+
DTOElementActionInput,
|
|
8
|
+
DTOElementActionInputOfType,
|
|
9
|
+
DTOPageBlockDefinition,
|
|
10
|
+
} from "../api";
|
|
11
|
+
import { DocumentationPageEditorModel, generatePageContentHash, VersionRoomBaseYDoc } from "../yjs";
|
|
12
|
+
import { computeDocsHierarchy } from "../yjs/version-room/compute-dto";
|
|
13
|
+
import { VersionRoomBaseYDocState } from "../yjs/version-room/types";
|
|
14
|
+
import { applyActionsLocally } from "./docs-local-action-executor";
|
|
15
|
+
import { SupportedElementActionType } from "./types";
|
|
16
|
+
|
|
17
|
+
type YObserver = () => void;
|
|
18
|
+
type HierarchyObserver = (hierarchy: DTODocumentationHierarchyV2) => void;
|
|
19
|
+
type SettingsObserver = (settings: DTODocumentationSettings) => void;
|
|
20
|
+
type ErrorObserver<T> = (error: unknown, actionMetadata: T | undefined) => void;
|
|
21
|
+
|
|
22
|
+
type TransactionExecutor = (trx: DTOElementActionInput) => Promise<void>;
|
|
23
|
+
type InternalTransactionExecutor<T> = (trx: InternalAction<T>) => Promise<void>;
|
|
24
|
+
type TransactionIdGenerator = () => string;
|
|
25
|
+
|
|
26
|
+
type Action = DTOElementActionInputOfType<SupportedElementActionType>;
|
|
27
|
+
type InputAction<T> = OmitStrict<Action, "tId">;
|
|
28
|
+
type InternalAction<T> = { action: Action; metadata?: T };
|
|
29
|
+
|
|
30
|
+
export class DocsStructureRepository<T> {
|
|
31
|
+
private userId: string;
|
|
32
|
+
private designSystemVersionId: string;
|
|
33
|
+
|
|
34
|
+
private readonly yDoc: Y.Doc;
|
|
35
|
+
private readonly yObserver: YObserver;
|
|
36
|
+
private _yState: VersionRoomBaseYDocState | undefined;
|
|
37
|
+
|
|
38
|
+
private _currentHierarchy: DTODocumentationHierarchyV2 | undefined;
|
|
39
|
+
private _currentSettings: DTODocumentationSettings | undefined;
|
|
40
|
+
|
|
41
|
+
private localActions: DTOElementActionInput[] = [];
|
|
42
|
+
private readonly actionQueue: TransactionQueue<T>;
|
|
43
|
+
private readonly hierarchyObservers = new Set<HierarchyObserver>();
|
|
44
|
+
private readonly settingsObservers = new Set<SettingsObserver>();
|
|
45
|
+
private readonly errorObservers = new Set<ErrorObserver<T>>();
|
|
46
|
+
private readonly initCallbacks = new Set<() => void>();
|
|
47
|
+
|
|
48
|
+
private readonly transactionIdGenerator: TransactionIdGenerator;
|
|
49
|
+
private readonly transactionExecutor: TransactionExecutor;
|
|
50
|
+
|
|
51
|
+
constructor(config: {
|
|
52
|
+
userId: string;
|
|
53
|
+
designSystemVersionId: string;
|
|
54
|
+
yDoc: Y.Doc;
|
|
55
|
+
transactionExecutor: TransactionExecutor;
|
|
56
|
+
transactionIdGenerator: TransactionIdGenerator;
|
|
57
|
+
}) {
|
|
58
|
+
this.userId = config.userId;
|
|
59
|
+
this.designSystemVersionId = config.designSystemVersionId;
|
|
60
|
+
this.yDoc = config.yDoc;
|
|
61
|
+
|
|
62
|
+
this.yObserver = this.yDoc.on("update", () => this.onYUpdate()) as YObserver;
|
|
63
|
+
this.onYUpdate();
|
|
64
|
+
|
|
65
|
+
this.transactionExecutor = config.transactionExecutor;
|
|
66
|
+
this.transactionIdGenerator = config.transactionIdGenerator;
|
|
67
|
+
|
|
68
|
+
this.actionQueue = new TransactionQueue(action => this.executeInternalAction(action));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
//
|
|
72
|
+
// Lifecycle
|
|
73
|
+
//
|
|
74
|
+
|
|
75
|
+
get isInitialized(): boolean {
|
|
76
|
+
return !!this._currentHierarchy;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
onInitialized(): Promise<void> {
|
|
80
|
+
if (this.isInitialized) return Promise.resolve();
|
|
81
|
+
return new Promise(resolve => {
|
|
82
|
+
this.initCallbacks.add(resolve);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
addHierarchyObserver(observer: HierarchyObserver) {
|
|
87
|
+
this.hierarchyObservers.add(observer);
|
|
88
|
+
if (this._currentHierarchy) observer(this._currentHierarchy);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
removeHierarchyObserver(observer: HierarchyObserver) {
|
|
92
|
+
this.hierarchyObservers.delete(observer);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
addSettingsObserver(observer: SettingsObserver) {
|
|
96
|
+
this.settingsObservers.add(observer);
|
|
97
|
+
if (this._currentSettings) observer(this._currentSettings);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
removeSettingsObserver(observer: SettingsObserver) {
|
|
101
|
+
this.settingsObservers.delete(observer);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
addErrorObserver(observer: ErrorObserver<T>) {
|
|
105
|
+
this.errorObservers.add(observer);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
removeErrorObserver(observer: ErrorObserver<T>) {
|
|
109
|
+
this.errorObservers.delete(observer);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
dispose() {
|
|
113
|
+
this.yDoc.off("update", this.yObserver);
|
|
114
|
+
this.hierarchyObservers.clear();
|
|
115
|
+
this.settingsObservers.clear();
|
|
116
|
+
this.errorObservers.clear();
|
|
117
|
+
this.actionQueue.clear();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
//
|
|
121
|
+
// Accessors
|
|
122
|
+
//
|
|
123
|
+
|
|
124
|
+
get currentHierarchy(): DTODocumentationHierarchyV2 {
|
|
125
|
+
const hierarchy = this._currentHierarchy;
|
|
126
|
+
if (!hierarchy) throw new Error(`Hierarchy cannot be accessed while it's still loading`);
|
|
127
|
+
return hierarchy;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
//
|
|
131
|
+
// Actions
|
|
132
|
+
//
|
|
133
|
+
|
|
134
|
+
executeAction(action: InputAction<T>, metadata?: T) {
|
|
135
|
+
void this.executeActionPromise(action, metadata);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
executeActionPromise(action: InputAction<T>, metadata?: T) {
|
|
139
|
+
const fullAction: DTOElementActionInput = { ...action, tId: this.transactionIdGenerator() };
|
|
140
|
+
this.localActions.push(fullAction);
|
|
141
|
+
this.refreshHierarchy();
|
|
142
|
+
return this.actionQueue.enqueue({ action: fullAction, metadata });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
notifyPageContentUpdated(
|
|
146
|
+
pagePersistentId: string,
|
|
147
|
+
content: DocumentationPageEditorModel,
|
|
148
|
+
definitions: DTOPageBlockDefinition[]
|
|
149
|
+
) {
|
|
150
|
+
const pageContentHash = generatePageContentHash(content, definitions);
|
|
151
|
+
new VersionRoomBaseYDoc(this.yDoc).updateDocumentationPageContentHashes({
|
|
152
|
+
[pagePersistentId]: pageContentHash,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private async executeInternalAction(action: InternalAction<T>) {
|
|
157
|
+
try {
|
|
158
|
+
return await this.transactionExecutor(action.action);
|
|
159
|
+
} catch (e: unknown) {
|
|
160
|
+
// Remove this action from local actions and reload hierarchy
|
|
161
|
+
this.localActions = this.localActions.filter(a => a.tId !== action.action.tId);
|
|
162
|
+
this.refreshHierarchy();
|
|
163
|
+
|
|
164
|
+
this.errorObservers.forEach(o => o(e, action.metadata));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
//
|
|
169
|
+
// Reactions
|
|
170
|
+
//
|
|
171
|
+
|
|
172
|
+
private refreshState() {
|
|
173
|
+
this.refreshHierarchy();
|
|
174
|
+
this.refreshSettings();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private refreshSettings() {
|
|
178
|
+
const yState = this._yState;
|
|
179
|
+
if (!yState) return;
|
|
180
|
+
|
|
181
|
+
// Calculate new settings
|
|
182
|
+
const newSettings: DTODocumentationSettings = {
|
|
183
|
+
isApprovalRequiredForPublishing: yState.settings.approvalRequiredForPublishing,
|
|
184
|
+
isApprovalsFeatureEnabled: yState.settings.isApprovalFeatureEnabled,
|
|
185
|
+
isDraftFeatureAdopted: yState.settings.isDraftFeatureAdopted,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// Only invoke when changed
|
|
189
|
+
if (
|
|
190
|
+
!this._currentSettings ||
|
|
191
|
+
newSettings.isApprovalRequiredForPublishing !== this._currentSettings.isApprovalRequiredForPublishing ||
|
|
192
|
+
newSettings.isApprovalsFeatureEnabled !== this._currentSettings.isApprovalsFeatureEnabled ||
|
|
193
|
+
newSettings.isDraftFeatureAdopted !== this._currentSettings.isDraftFeatureAdopted
|
|
194
|
+
) {
|
|
195
|
+
this._currentSettings = newSettings;
|
|
196
|
+
|
|
197
|
+
// Invoke observers
|
|
198
|
+
this.settingsObservers.forEach(o => o(newSettings));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private refreshHierarchy() {
|
|
203
|
+
const yState = this._yState;
|
|
204
|
+
if (!yState) return;
|
|
205
|
+
|
|
206
|
+
// Calculate new hierarchy
|
|
207
|
+
const hierarchy = this.calculateHierarchy(yState);
|
|
208
|
+
if (!hierarchy) return;
|
|
209
|
+
this._currentHierarchy = hierarchy;
|
|
210
|
+
|
|
211
|
+
// Invoke observers
|
|
212
|
+
this.hierarchyObservers.forEach(o => o(hierarchy));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private calculateHierarchy(yState: VersionRoomBaseYDocState) {
|
|
216
|
+
// Remove local actions that have been applied remotely
|
|
217
|
+
const executedTransactionIds = new Set(yState.executedTransactionIds);
|
|
218
|
+
const localActions = this.localActions.filter(a => a.tId && !executedTransactionIds.has(a.tId));
|
|
219
|
+
this.localActions = localActions;
|
|
220
|
+
|
|
221
|
+
// Apply local actions onto the Yjs state
|
|
222
|
+
const { pages, groups, approvals, pageLiveblockRoomIds } = applyActionsLocally({
|
|
223
|
+
userId: this.userId,
|
|
224
|
+
designSystemVersionId: this.designSystemVersionId,
|
|
225
|
+
remoteState: yState,
|
|
226
|
+
actions: localActions,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const hierarchy = computeDocsHierarchy(
|
|
230
|
+
{
|
|
231
|
+
// Merged remote + local
|
|
232
|
+
pages,
|
|
233
|
+
groups,
|
|
234
|
+
approvals,
|
|
235
|
+
pageLiveblockRoomIds,
|
|
236
|
+
|
|
237
|
+
// Remote only
|
|
238
|
+
groupSnapshots: yState.groupSnapshots,
|
|
239
|
+
pageContentHashes: yState.pageContentHashes,
|
|
240
|
+
pageSnapshots: yState.pageSnapshots,
|
|
241
|
+
settings: yState.settings,
|
|
242
|
+
},
|
|
243
|
+
{ includeDeletedContent: true }
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
return hierarchy;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private onYUpdate() {
|
|
250
|
+
const newState = new VersionRoomBaseYDoc(this.yDoc).getState();
|
|
251
|
+
if (newState.groups.length) {
|
|
252
|
+
this._yState = newState;
|
|
253
|
+
this.refreshState();
|
|
254
|
+
|
|
255
|
+
// Flush init callbacks
|
|
256
|
+
this.initCallbacks.forEach(f => f());
|
|
257
|
+
this.initCallbacks.clear();
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
class TransactionQueue<T> {
|
|
263
|
+
private readonly executor: InternalTransactionExecutor<T>;
|
|
264
|
+
private readonly queue = new PQueue({
|
|
265
|
+
concurrency: 1,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
constructor(executor: InternalTransactionExecutor<T>) {
|
|
269
|
+
this.executor = executor;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
enqueue(trx: InternalAction<T>) {
|
|
273
|
+
return this.queue.add(() => this.executor(trx));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
onEmpty() {
|
|
277
|
+
return this.queue.onEmpty();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
clear() {
|
|
281
|
+
this.queue.clear();
|
|
282
|
+
}
|
|
283
|
+
}
|