@agent-native/core 0.80.4 → 0.80.6
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +28 -2
- package/corpus/core/docs/content/locales/ar-SA/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/ar-SA/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/de-DE/real-time-collaboration.mdx +3 -3
- package/corpus/core/docs/content/locales/de-DE/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/es-ES/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/es-ES/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/fr-FR/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/fr-FR/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/hi-IN/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/ja-JP/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/ja-JP/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/ko-KR/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/ko-KR/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/pt-BR/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/pt-BR/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-CN/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-CN/template-design.mdx +1 -1
- package/corpus/core/docs/content/locales/zh-TW/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/locales/zh-TW/template-design.mdx +1 -1
- package/corpus/core/docs/content/real-time-collaboration.mdx +2 -2
- package/corpus/core/docs/content/template-design.mdx +1 -1
- package/corpus/core/package.json +3 -7
- package/corpus/core/src/action.ts +21 -0
- package/corpus/core/src/agent/durable-background.ts +32 -11
- package/corpus/core/src/agent/production-agent.ts +11 -1
- package/corpus/core/src/agent/run-loop-with-resume.ts +3 -1
- package/corpus/core/src/brand-kit/fig/index.ts +17 -37
- package/corpus/core/src/cli/create.ts +1 -0
- package/corpus/core/src/cli/index.ts +1 -1
- package/corpus/core/src/cli/mcp-config-writers.ts +187 -51
- package/corpus/core/src/cli/skills.ts +18 -3
- package/corpus/core/src/client/AssistantChat.tsx +11 -1
- package/corpus/core/src/client/agent-chat.ts +12 -1
- package/corpus/core/src/client/composer/PromptComposer.tsx +100 -41
- package/corpus/core/src/client/org/OrgSwitcher.tsx +1 -1
- package/corpus/core/src/client/sharing/ShareButton.tsx +3 -27
- package/corpus/core/src/collab/presence.ts +1 -1
- package/corpus/core/src/mcp/connect-route.ts +34 -5
- package/corpus/core/src/server/action-discovery.ts +4 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +20 -0
- package/corpus/core/src/sharing/registry.ts +17 -0
- package/corpus/core/src/templates/default/pnpm-workspace.yaml +1 -0
- package/corpus/core/src/templates/workspace-root/pnpm-workspace.yaml +1 -0
- package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +2 -0
- package/corpus/templates/analytics/actions/update-dashboard.ts +8 -0
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +1 -1
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +117 -33
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +4 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +7 -0
- package/corpus/templates/analytics/changelog/2026-06-27-dashboards-can-now-nest-under-a-parent-in-the-sidebar-via-a-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-06-29-analytics-pages-now-sit-closer-under-the-header-with-tighter.md +6 -0
- package/corpus/templates/assets/actions/generate-image-batch.ts +3 -0
- package/corpus/templates/assets/actions/generate-image.ts +3 -0
- package/corpus/templates/assets/changelog/2026-06-29-longer-agent-generation-window.md +6 -0
- package/corpus/templates/assets/netlify.toml +3 -3
- package/corpus/templates/assets/server/plugins/agent-chat.ts +4 -1
- package/corpus/templates/clips/app/components/capture-install-options.tsx +7 -8
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +1 -1
- package/corpus/templates/content/AGENTS.md +57 -44
- package/corpus/templates/content/README.md +16 -0
- package/corpus/templates/content/actions/_builder-cms-read-client.ts +125 -0
- package/corpus/templates/content/actions/_builder-cms-source-adapter.ts +28 -6
- package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +238 -69
- package/corpus/templates/content/actions/_builder-cms-write-settings.ts +125 -43
- package/corpus/templates/content/actions/_database-source-utils.ts +746 -97
- package/corpus/templates/content/actions/_database-utils.ts +17 -14
- package/corpus/templates/content/actions/_federation-join.ts +14 -2
- package/corpus/templates/content/actions/_join-suggestion.ts +89 -14
- package/corpus/templates/content/actions/_local-file-documents.ts +2 -1
- package/corpus/templates/content/actions/attach-content-database-source.ts +124 -5
- package/corpus/templates/content/actions/bind-content-database-source-field.ts +256 -0
- package/corpus/templates/content/actions/change-content-database-source-role.ts +420 -0
- package/corpus/templates/content/actions/create-document.ts +8 -0
- package/corpus/templates/content/actions/execute-builder-source-batch.ts +282 -0
- package/corpus/templates/content/actions/execute-builder-source-execution.ts +220 -8
- package/corpus/templates/content/actions/get-content-database.ts +10 -2
- package/corpus/templates/content/actions/prepare-builder-source-execution.ts +19 -2
- package/corpus/templates/content/actions/prepare-builder-source-review.ts +47 -15
- package/corpus/templates/content/actions/refresh-content-database-source.ts +11 -4
- package/corpus/templates/content/actions/review-content-database-source-change-set.ts +6 -2
- package/corpus/templates/content/actions/set-content-database-source-write-mode.ts +32 -12
- package/corpus/templates/content/actions/stage-builder-revision.ts +14 -10
- package/corpus/templates/content/actions/validate-builder-source-execution.ts +25 -3
- package/corpus/templates/content/app/blocks/BuilderDocsBlocks.tsx +10 -0
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +1 -0
- package/corpus/templates/content/app/components/editor/ContentReferencePreview.tsx +185 -0
- package/corpus/templates/content/app/components/editor/DocumentDatabase.tsx +1520 -553
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +3 -0
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +150 -1
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +19 -0
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +647 -66
- package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +543 -204
- package/corpus/templates/content/app/components/editor/extensions/ContentReferenceNode.tsx +82 -0
- package/corpus/templates/content/app/global.css +9 -0
- package/corpus/templates/content/app/hooks/use-content-database.ts +62 -0
- package/corpus/templates/content/app/i18n/zh-TW.ts +76 -0
- package/corpus/templates/content/app/i18n-data.ts +924 -0
- package/corpus/templates/content/changelog/2026-06-29-builder-review-checks-now-preserve-publish-and-unpublish-cho.md +6 -0
- package/corpus/templates/content/changelog/2026-06-29-database-sources-can-now-be-added-as-more-rows-or-matched.md +6 -0
- package/corpus/templates/content/changelog/2026-06-29-fixed-loading-the-final-rows-in-larger-databases-without-bre.md +6 -0
- package/corpus/templates/content/changelog/2026-06-29-reusable-mdx-references-now-preview-linked-local-documents-i.md +6 -0
- package/corpus/templates/content/shared/api.ts +91 -1
- package/corpus/templates/content/shared/builder-docs-blocks.ts +8 -0
- package/corpus/templates/content/shared/builder-mdx.ts +141 -4
- package/corpus/templates/content/shared/nfm.ts +62 -0
- package/corpus/templates/content/vite.config.ts +14 -7
- package/corpus/templates/design/.agents/skills/design-systems/SKILL.md +10 -11
- package/corpus/templates/design/AGENTS.md +5 -0
- package/corpus/templates/design/_gitignore +7 -0
- package/corpus/templates/design/actions/apply-visual-edit.ts +22 -12
- package/corpus/templates/design/actions/create-design-system.ts +15 -4
- package/corpus/templates/design/actions/create-file.ts +20 -1
- package/corpus/templates/design/actions/delete-design-system.ts +2 -2
- package/corpus/templates/design/actions/duplicate-design.ts +35 -9
- package/corpus/templates/design/actions/generate-design.ts +146 -69
- package/corpus/templates/design/actions/generate-screens.ts +51 -19
- package/corpus/templates/design/actions/get-design-snapshot.ts +5 -1
- package/corpus/templates/design/actions/get-design.ts +2 -0
- package/corpus/templates/design/actions/import-design-project.ts +28 -9
- package/corpus/templates/design/actions/import-document.ts +22 -8
- package/corpus/templates/design/actions/index-design-system-with-builder.ts +6 -153
- package/corpus/templates/design/actions/navigate.ts +105 -54
- package/corpus/templates/design/actions/set-default-design-system.ts +17 -3
- package/corpus/templates/design/actions/show-design-questions.ts +2 -0
- package/corpus/templates/design/actions/update-design.ts +2 -1
- package/corpus/templates/design/actions/view-screen.ts +12 -6
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +4 -4
- package/corpus/templates/design/app/components/design/DesignCanvas.tsx +159 -41
- package/corpus/templates/design/app/components/design/DesignExtensionsPanel.tsx +8 -4
- package/corpus/templates/design/app/components/design/DrawOverlay.tsx +37 -42
- package/corpus/templates/design/app/components/design/EditPanel.tsx +336 -183
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +153 -34
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +135 -33
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +6 -0
- package/corpus/templates/design/app/components/design/TweaksPanel.tsx +2 -1
- package/corpus/templates/design/app/components/design/inspector/AlignmentMatrix.tsx +4 -10
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +84 -43
- package/corpus/templates/design/app/components/design/inspector/ConstraintsWidget.tsx +4 -4
- package/corpus/templates/design/app/components/design/inspector/{FigmaColorPicker.tsx → DesignColorPicker.tsx} +89 -62
- package/corpus/templates/design/app/components/design/inspector/ExportSettingsPanel.tsx +14 -26
- package/corpus/templates/design/app/components/design/inspector/GradientEditor.tsx +40 -12
- package/corpus/templates/design/app/components/design/inspector/ImageFillControls.tsx +13 -4
- package/corpus/templates/design/app/components/design/inspector/SHADER_INTEGRATION.md +13 -13
- package/corpus/templates/design/app/components/design/inspector/ScrubInput.tsx +13 -9
- package/corpus/templates/design/app/components/design/inspector/ShaderControls.tsx +13 -4
- package/corpus/templates/design/app/components/design/inspector/ShaderFillsPanel.tsx +6 -6
- package/corpus/templates/design/app/components/design/inspector/{figma-icons.tsx → design-icons.tsx} +2 -2
- package/corpus/templates/design/app/components/design/inspector/index.ts +12 -12
- package/corpus/templates/design/app/components/design/inspector/scrub-input-utils.ts +5 -2
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +3 -3
- package/corpus/templates/design/app/components/layout/Layout.tsx +5 -2
- package/corpus/templates/design/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/design/app/components/visual-editor/CanvasCommentPins.tsx +167 -91
- package/corpus/templates/design/app/components/visual-editor/DrawOverlay.tsx +16 -3
- package/corpus/templates/design/app/components/visual-editor/SaveStatusIndicator.tsx +15 -0
- package/corpus/templates/design/app/global.css +4 -0
- package/corpus/templates/design/app/hooks/use-design-system.ts +4 -1
- package/corpus/templates/design/app/hooks/use-navigation-state.ts +38 -3
- package/corpus/templates/design/app/hooks/useDesignHotkeys.ts +9 -2
- package/corpus/templates/design/app/i18n/zh-TW.ts +22 -0
- package/corpus/templates/design/app/i18n-data.ts +527 -0
- package/corpus/templates/design/app/lib/design-systems.ts +6 -4
- package/corpus/templates/design/app/lib/design-templates.ts +15 -3
- package/corpus/templates/design/app/pages/DesignEditor.tsx +641 -158
- package/corpus/templates/design/app/pages/DesignSystemSetup.tsx +42 -34
- package/corpus/templates/design/app/pages/VisualEdit.tsx +100 -0
- package/corpus/templates/design/app/root.tsx +42 -9
- package/corpus/templates/design/app/routes/visual-edit.tsx +7 -0
- package/corpus/templates/design/changelog/2026-06-28-live-cursors-now-keep-names-beside-a-cleaner-pointer-marker.md +1 -1
- package/corpus/templates/design/changelog/2026-06-28-mode-switching-now-lives-in-a-compact-bottom-right-icon-dock.md +1 -1
- package/corpus/templates/design/changelog/2026-06-28-visual-code-design-mode.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-generation-prompts-now-show-connect-ai-before-sending.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-design-mode-top-chrome-now-groups-collaborators-preview-shar.md +1 -1
- package/corpus/templates/design/changelog/2026-06-29-fixed-design-editor-exports-and-gradient-edits-so-multi-scre.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-longer-hosted-agent-turns.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-png-export-handles-modern-css-color-functions-more-reliably.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-png-export-now-ignores-duplicate-clicks-while-a-download-is-.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-public-visual-edit-and-shared-design-links-now-invite-visito.md +6 -0
- package/corpus/templates/design/changelog/2026-06-29-visual-edit-actions-now-reject-missing-design-targets-before.md +6 -0
- package/corpus/templates/design/e2e/README.md +71 -0
- package/corpus/templates/design/e2e/global-setup.ts +157 -0
- package/corpus/templates/design/e2e/helpers.ts +148 -0
- package/corpus/templates/design/netlify.toml +3 -4
- package/corpus/templates/design/package.json +5 -1
- package/corpus/templates/design/playwright.config.ts +51 -0
- package/corpus/templates/design/server/handlers/index-design-system-with-builder.ts +121 -0
- package/corpus/templates/design/server/handlers/uploads.ts +89 -38
- package/corpus/templates/design/server/lib/builder-design-system-proxy.ts +181 -0
- package/corpus/templates/design/server/lib/coding-handoff.ts +33 -4
- package/corpus/templates/design/server/lib/design-export.ts +35 -8
- package/corpus/templates/design/server/plugins/agent-chat.ts +4 -0
- package/corpus/templates/design/server/plugins/auth.ts +10 -1
- package/corpus/templates/design/server/routes/api/index-design-system-with-builder.post.ts +1 -0
- package/corpus/templates/design/shared/api.ts +2 -0
- package/corpus/templates/design/shared/apply-edits.ts +12 -8
- package/corpus/templates/design/shared/canvas-math.ts +19 -1
- package/corpus/templates/design/shared/code-layer.ts +87 -28
- package/corpus/templates/design/shared/generation-session.ts +1 -0
- package/corpus/templates/design/shared/resolve-tweaks.ts +4 -3
- package/corpus/templates/design/shared/shader-presets.ts +10 -0
- package/corpus/templates/design/shared/source-mode.ts +9 -0
- package/corpus/templates/design/vitest.config.ts +2 -1
- package/corpus/templates/mail/app/components/layout/AppLayout.tsx +1 -1
- package/corpus/templates/plan/.agents/skills/visual-plan/references/canvas.md +1 -1
- package/corpus/templates/plan/AGENTS.md +1 -1
- package/corpus/templates/plan/actions/create-plan-design.ts +1 -1
- package/corpus/templates/plan/actions/validate-local-plan-source.ts +2 -10
- package/corpus/templates/plan/app/pages/PlansPage.tsx +1 -0
- package/corpus/templates/plan/docs/commenting-ux-plan.md +2 -2
- package/corpus/templates/plan/server/plan-content.ts +1 -1
- package/corpus/templates/plan/server/ui-plan-html.ts +2 -2
- package/corpus/templates/slides/actions/index-design-system-with-builder.ts +6 -153
- package/corpus/templates/slides/app/components/design-system/DesignSystemSetup.tsx +53 -38
- package/corpus/templates/slides/app/components/design-system/{fig-import-response.ts → builder-index-response.ts} +9 -6
- package/corpus/templates/slides/app/components/visual-editor/MultiSelectChip.tsx +1 -1
- package/corpus/templates/slides/changelog/2026-06-29-design-system-setup-now-indexes-uploaded-design-files-throug.md +6 -0
- package/corpus/templates/slides/changelog/2026-06-29-longer-hosted-agent-turns.md +6 -0
- package/corpus/templates/slides/netlify.toml +3 -6
- package/corpus/templates/slides/server/handlers/index-design-system-with-builder.ts +116 -0
- package/corpus/templates/slides/server/lib/builder-design-system-proxy.ts +179 -0
- package/corpus/templates/slides/server/plugins/agent-chat.ts +4 -1
- package/corpus/templates/slides/server/routes/api/index-design-system-with-builder.post.ts +1 -0
- package/dist/action.d.ts +15 -0
- package/dist/action.d.ts.map +1 -1
- package/dist/action.js +6 -0
- package/dist/action.js.map +1 -1
- package/dist/agent/durable-background.d.ts +9 -7
- package/dist/agent/durable-background.d.ts.map +1 -1
- package/dist/agent/durable-background.js +27 -11
- package/dist/agent/durable-background.js.map +1 -1
- package/dist/agent/production-agent.d.ts +7 -0
- package/dist/agent/production-agent.d.ts.map +1 -1
- package/dist/agent/production-agent.js +4 -1
- package/dist/agent/production-agent.js.map +1 -1
- package/dist/agent/run-loop-with-resume.d.ts +2 -1
- package/dist/agent/run-loop-with-resume.d.ts.map +1 -1
- package/dist/agent/run-loop-with-resume.js +2 -2
- package/dist/agent/run-loop-with-resume.js.map +1 -1
- package/dist/brand-kit/fig/index.d.ts +4 -4
- package/dist/brand-kit/fig/index.d.ts.map +1 -1
- package/dist/brand-kit/fig/index.js +14 -27
- package/dist/brand-kit/fig/index.js.map +1 -1
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +1 -0
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/index.js +1 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mcp-config-writers.d.ts +15 -7
- package/dist/cli/mcp-config-writers.d.ts.map +1 -1
- package/dist/cli/mcp-config-writers.js +198 -45
- package/dist/cli/mcp-config-writers.js.map +1 -1
- package/dist/cli/skills.d.ts +1 -1
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +18 -3
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +11 -1
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/agent-chat.d.ts.map +1 -1
- package/dist/client/agent-chat.js +15 -2
- package/dist/client/agent-chat.js.map +1 -1
- package/dist/client/composer/PromptComposer.d.ts.map +1 -1
- package/dist/client/composer/PromptComposer.js +35 -1
- package/dist/client/composer/PromptComposer.js.map +1 -1
- package/dist/client/org/OrgSwitcher.js +1 -1
- package/dist/client/org/OrgSwitcher.js.map +1 -1
- package/dist/client/sharing/ShareButton.d.ts +1 -1
- package/dist/client/sharing/ShareButton.d.ts.map +1 -1
- package/dist/client/sharing/ShareButton.js +2 -15
- package/dist/client/sharing/ShareButton.js.map +1 -1
- package/dist/collab/presence.d.ts +1 -1
- package/dist/collab/presence.js +1 -1
- package/dist/collab/presence.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +2 -2
- package/dist/mcp/connect-route.d.ts.map +1 -1
- package/dist/mcp/connect-route.js +34 -5
- package/dist/mcp/connect-route.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +8 -8
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/action-discovery.d.ts.map +1 -1
- package/dist/server/action-discovery.js +5 -0
- package/dist/server/action-discovery.js.map +1 -1
- package/dist/server/agent-chat-plugin.d.ts +6 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +12 -3
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/sharing/registry.d.ts.map +1 -1
- package/dist/sharing/registry.js +11 -0
- package/dist/sharing/registry.js.map +1 -1
- package/dist/templates/default/pnpm-workspace.yaml +1 -0
- package/dist/templates/workspace-root/pnpm-workspace.yaml +1 -0
- package/docs/content/locales/ar-SA/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/ar-SA/template-design.mdx +1 -1
- package/docs/content/locales/de-DE/real-time-collaboration.mdx +3 -3
- package/docs/content/locales/de-DE/template-design.mdx +1 -1
- package/docs/content/locales/es-ES/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/es-ES/template-design.mdx +1 -1
- package/docs/content/locales/fr-FR/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/fr-FR/template-design.mdx +1 -1
- package/docs/content/locales/hi-IN/template-design.mdx +1 -1
- package/docs/content/locales/ja-JP/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/ja-JP/template-design.mdx +1 -1
- package/docs/content/locales/ko-KR/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/ko-KR/template-design.mdx +1 -1
- package/docs/content/locales/pt-BR/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/pt-BR/template-design.mdx +1 -1
- package/docs/content/locales/zh-CN/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/zh-CN/template-design.mdx +1 -1
- package/docs/content/locales/zh-TW/real-time-collaboration.mdx +2 -2
- package/docs/content/locales/zh-TW/template-design.mdx +1 -1
- package/docs/content/real-time-collaboration.mdx +2 -2
- package/docs/content/template-design.mdx +1 -1
- package/package.json +3 -7
- package/src/templates/default/pnpm-workspace.yaml +1 -0
- package/src/templates/workspace-root/pnpm-workspace.yaml +1 -0
- package/corpus/core/src/brand-kit/fig/decode.ts +0 -410
- package/corpus/core/src/brand-kit/fig/extract-design-system.ts +0 -947
- package/corpus/core/src/brand-kit/fig/fig-to-html.ts +0 -1906
- package/corpus/templates/design/actions/import-figma.ts +0 -55
- package/corpus/templates/design/changelog/2026-06-28-figma-style-code-design-mode.md +0 -6
- package/corpus/templates/design/server/handlers/import-figma-system.ts +0 -86
- package/corpus/templates/design/server/routes/api/import-figma-system.post.ts +0 -1
- package/corpus/templates/slides/server/handlers/import-figma-system.ts +0 -101
- package/corpus/templates/slides/server/routes/api/import-figma-system.post.ts +0 -1
- package/dist/brand-kit/fig/decode.d.ts +0 -33
- package/dist/brand-kit/fig/decode.d.ts.map +0 -1
- package/dist/brand-kit/fig/decode.js +0 -358
- package/dist/brand-kit/fig/decode.js.map +0 -1
- package/dist/brand-kit/fig/extract-design-system.d.ts +0 -44
- package/dist/brand-kit/fig/extract-design-system.d.ts.map +0 -1
- package/dist/brand-kit/fig/extract-design-system.js +0 -752
- package/dist/brand-kit/fig/extract-design-system.js.map +0 -1
- package/dist/brand-kit/fig/fig-to-html.d.ts +0 -246
- package/dist/brand-kit/fig/fig-to-html.d.ts.map +0 -1
- package/dist/brand-kit/fig/fig-to-html.js +0 -1506
- package/dist/brand-kit/fig/fig-to-html.js.map +0 -1
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { assertAccess } from "@agent-native/core/sharing";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
type BuilderSourceBatchItemResult,
|
|
7
|
+
type ContentDatabaseSource,
|
|
8
|
+
type ContentDatabaseSourceChangeSet,
|
|
9
|
+
type ExecuteBuilderSourceBatchRequest,
|
|
10
|
+
type ExecuteBuilderSourceBatchResponse,
|
|
11
|
+
type ExecuteBuilderSourceBatchTransition,
|
|
12
|
+
} from "../shared/api.js";
|
|
13
|
+
import {
|
|
14
|
+
getContentDatabaseSourceSnapshotForWrite,
|
|
15
|
+
resolveDatabaseForSourceMutation,
|
|
16
|
+
} from "./_database-source-utils.js";
|
|
17
|
+
import {
|
|
18
|
+
executeBuilderSourceExecutionWithDeps,
|
|
19
|
+
realExecutionDeps,
|
|
20
|
+
} from "./execute-builder-source-execution.js";
|
|
21
|
+
import prepareBuilderSourceExecution from "./prepare-builder-source-execution.js";
|
|
22
|
+
|
|
23
|
+
type DatabaseRecord = NonNullable<
|
|
24
|
+
Awaited<ReturnType<typeof resolveDatabaseForSourceMutation>>
|
|
25
|
+
>;
|
|
26
|
+
|
|
27
|
+
export const DEFAULT_BUILDER_SOURCE_BATCH_CONCURRENCY = 3;
|
|
28
|
+
export const MAX_BUILDER_SOURCE_BATCH_CONCURRENCY = 8;
|
|
29
|
+
|
|
30
|
+
export interface ExecuteBuilderSourceBatchDeps {
|
|
31
|
+
resolveDatabase: (
|
|
32
|
+
args: Pick<ExecuteBuilderSourceBatchRequest, "databaseId" | "documentId">,
|
|
33
|
+
) => Promise<DatabaseRecord | null>;
|
|
34
|
+
assertEditor: (database: DatabaseRecord) => Promise<void>;
|
|
35
|
+
getSourceSnapshot: (
|
|
36
|
+
database: DatabaseRecord,
|
|
37
|
+
) => Promise<ContentDatabaseSource | null>;
|
|
38
|
+
runOne: (
|
|
39
|
+
changeSetId: string,
|
|
40
|
+
transition?: ExecuteBuilderSourceBatchTransition,
|
|
41
|
+
) => Promise<BuilderSourceBatchItemResult>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function realBatchDeps(
|
|
45
|
+
args: Pick<
|
|
46
|
+
ExecuteBuilderSourceBatchRequest,
|
|
47
|
+
"databaseId" | "documentId" | "sourceId"
|
|
48
|
+
>,
|
|
49
|
+
): ExecuteBuilderSourceBatchDeps {
|
|
50
|
+
return {
|
|
51
|
+
resolveDatabase: (request) => resolveDatabaseForSourceMutation(request),
|
|
52
|
+
assertEditor: async (database) => {
|
|
53
|
+
await assertAccess("document", database.documentId, "editor");
|
|
54
|
+
},
|
|
55
|
+
getSourceSnapshot: (database) =>
|
|
56
|
+
getContentDatabaseSourceSnapshotForWrite(database, args.sourceId),
|
|
57
|
+
runOne: async (changeSetId, transition) => {
|
|
58
|
+
const executionArgs = {
|
|
59
|
+
databaseId: args.databaseId,
|
|
60
|
+
documentId: args.documentId,
|
|
61
|
+
sourceId: args.sourceId,
|
|
62
|
+
changeSetId,
|
|
63
|
+
publicationTransition: transition?.publicationTransition,
|
|
64
|
+
confirmUnpublish: transition?.confirmUnpublish,
|
|
65
|
+
};
|
|
66
|
+
if (transition?.publicationTransition) {
|
|
67
|
+
await prepareBuilderSourceExecution.run(executionArgs);
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
await executeBuilderSourceExecutionWithDeps(
|
|
71
|
+
executionArgs,
|
|
72
|
+
realExecutionDeps(args.sourceId),
|
|
73
|
+
);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (!isMissingGateMessage(errorMessage(error))) {
|
|
76
|
+
throw error;
|
|
77
|
+
}
|
|
78
|
+
await prepareBuilderSourceExecution.run(executionArgs);
|
|
79
|
+
await executeBuilderSourceExecutionWithDeps(
|
|
80
|
+
executionArgs,
|
|
81
|
+
realExecutionDeps(args.sourceId),
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
return { changeSetId, status: "succeeded" };
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function errorMessage(error: unknown) {
|
|
90
|
+
return error instanceof Error && error.message
|
|
91
|
+
? error.message
|
|
92
|
+
: "Unknown Builder batch error.";
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function isMissingGateMessage(message: string) {
|
|
96
|
+
return /prepare the builder execution gate/i.test(message);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function isBlockedMessage(message: string) {
|
|
100
|
+
return [
|
|
101
|
+
/blocked/i,
|
|
102
|
+
/approve/i,
|
|
103
|
+
/disabled/i,
|
|
104
|
+
/not allowed/i,
|
|
105
|
+
/no longer exists/i,
|
|
106
|
+
/changed since/i,
|
|
107
|
+
/already running/i,
|
|
108
|
+
/requires/i,
|
|
109
|
+
/not ready/i,
|
|
110
|
+
/refresh/i,
|
|
111
|
+
/not executable/i,
|
|
112
|
+
].some((pattern) => pattern.test(message));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function clampConcurrency(value: number | undefined) {
|
|
116
|
+
if (!value || !Number.isFinite(value)) {
|
|
117
|
+
return DEFAULT_BUILDER_SOURCE_BATCH_CONCURRENCY;
|
|
118
|
+
}
|
|
119
|
+
return Math.max(1, Math.min(MAX_BUILDER_SOURCE_BATCH_CONCURRENCY, value));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function uniqueIds(ids: string[]) {
|
|
123
|
+
return Array.from(new Set(ids.filter((id) => id.trim())));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function hasSucceededExecution(changeSet: ContentDatabaseSourceChangeSet) {
|
|
127
|
+
return changeSet.executions.some(
|
|
128
|
+
(execution) => execution.state === "succeeded",
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function defaultBatchChangeSetIds(source: ContentDatabaseSource) {
|
|
133
|
+
return source.changeSets
|
|
134
|
+
.filter(
|
|
135
|
+
(changeSet) =>
|
|
136
|
+
changeSet.direction === "outbound" && changeSet.state === "approved",
|
|
137
|
+
)
|
|
138
|
+
.map((changeSet) => changeSet.id);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function explicitBatchBlocker(
|
|
142
|
+
changeSet: ContentDatabaseSourceChangeSet | undefined,
|
|
143
|
+
) {
|
|
144
|
+
if (!changeSet) return "Source change-set not found.";
|
|
145
|
+
if (changeSet.direction !== "outbound") {
|
|
146
|
+
return "Only outbound Builder change sets can be executed.";
|
|
147
|
+
}
|
|
148
|
+
if (
|
|
149
|
+
changeSet.state !== "pending_push" &&
|
|
150
|
+
changeSet.state !== "staged_revision" &&
|
|
151
|
+
changeSet.state !== "approved"
|
|
152
|
+
) {
|
|
153
|
+
return "Approve the Builder change set before executing it.";
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function runBoundedPool<T>(
|
|
159
|
+
items: T[],
|
|
160
|
+
maxConcurrency: number,
|
|
161
|
+
worker: (item: T, index: number) => Promise<BuilderSourceBatchItemResult>,
|
|
162
|
+
) {
|
|
163
|
+
const results = new Array<BuilderSourceBatchItemResult>(items.length);
|
|
164
|
+
let nextIndex = 0;
|
|
165
|
+
const workerCount = Math.min(maxConcurrency, items.length);
|
|
166
|
+
await Promise.all(
|
|
167
|
+
Array.from({ length: workerCount }, async () => {
|
|
168
|
+
while (nextIndex < items.length) {
|
|
169
|
+
const index = nextIndex;
|
|
170
|
+
nextIndex += 1;
|
|
171
|
+
results[index] = await worker(items[index], index);
|
|
172
|
+
}
|
|
173
|
+
}),
|
|
174
|
+
);
|
|
175
|
+
return results;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function summarize(results: BuilderSourceBatchItemResult[]) {
|
|
179
|
+
return {
|
|
180
|
+
total: results.length,
|
|
181
|
+
succeeded: results.filter((result) => result.status === "succeeded").length,
|
|
182
|
+
blocked: results.filter((result) => result.status === "blocked").length,
|
|
183
|
+
failed: results.filter((result) => result.status === "failed").length,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export async function executeBuilderSourceBatchWithDeps(
|
|
188
|
+
args: ExecuteBuilderSourceBatchRequest,
|
|
189
|
+
deps: ExecuteBuilderSourceBatchDeps,
|
|
190
|
+
): Promise<ExecuteBuilderSourceBatchResponse> {
|
|
191
|
+
const database = await deps.resolveDatabase(args);
|
|
192
|
+
if (!database) throw new Error("Database not found.");
|
|
193
|
+
await deps.assertEditor(database);
|
|
194
|
+
|
|
195
|
+
const source = await deps.getSourceSnapshot(database);
|
|
196
|
+
if (!source || source.sourceType !== "builder-cms") {
|
|
197
|
+
throw new Error("Attach a Builder CMS source before executing writes.");
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const ids = uniqueIds(args.changeSetIds ?? defaultBatchChangeSetIds(source));
|
|
201
|
+
const changeSetsById = new Map(
|
|
202
|
+
source.changeSets.map((changeSet) => [changeSet.id, changeSet]),
|
|
203
|
+
);
|
|
204
|
+
const maxConcurrency = clampConcurrency(args.maxConcurrency);
|
|
205
|
+
const results = await runBoundedPool(
|
|
206
|
+
ids,
|
|
207
|
+
maxConcurrency,
|
|
208
|
+
async (changeSetId) => {
|
|
209
|
+
const changeSet = changeSetsById.get(changeSetId);
|
|
210
|
+
if (changeSet && hasSucceededExecution(changeSet)) {
|
|
211
|
+
return {
|
|
212
|
+
changeSetId,
|
|
213
|
+
status: "succeeded",
|
|
214
|
+
message: "Builder execution already succeeded; skipped.",
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const blocker = explicitBatchBlocker(changeSet);
|
|
219
|
+
if (blocker) {
|
|
220
|
+
return { changeSetId, status: "blocked", message: blocker };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
return await deps.runOne(changeSetId, args.transitions?.[changeSetId]);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
const message = errorMessage(error);
|
|
227
|
+
return {
|
|
228
|
+
changeSetId,
|
|
229
|
+
status: isBlockedMessage(message) ? "blocked" : "failed",
|
|
230
|
+
message,
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
summary: summarize(results),
|
|
238
|
+
results,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const transitionSchema = z.object({
|
|
243
|
+
publicationTransition: z.enum(["publish", "unpublish"]).optional(),
|
|
244
|
+
confirmUnpublish: z.boolean().optional(),
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
export default defineAction({
|
|
248
|
+
description:
|
|
249
|
+
"Execute a bounded batch of approved outbound Builder CMS change-sets. Each item uses the existing prepare and execute gates, continues on item errors, and does not publish or unpublish unless an explicit per-change-set transition is provided.",
|
|
250
|
+
schema: z.object({
|
|
251
|
+
databaseId: z.string().optional().describe("Database ID"),
|
|
252
|
+
documentId: z.string().optional().describe("Database document/page ID"),
|
|
253
|
+
sourceId: z
|
|
254
|
+
.string()
|
|
255
|
+
.optional()
|
|
256
|
+
.describe("Target source ID (defaults to the primary source)"),
|
|
257
|
+
changeSetIds: z
|
|
258
|
+
.array(z.string())
|
|
259
|
+
.optional()
|
|
260
|
+
.describe(
|
|
261
|
+
"Approved source change-set IDs. Defaults to all approved outbound Builder change-sets.",
|
|
262
|
+
),
|
|
263
|
+
maxConcurrency: z
|
|
264
|
+
.number()
|
|
265
|
+
.int()
|
|
266
|
+
.min(1)
|
|
267
|
+
.max(MAX_BUILDER_SOURCE_BATCH_CONCURRENCY)
|
|
268
|
+
.optional()
|
|
269
|
+
.describe("Maximum Builder change-sets to execute at once."),
|
|
270
|
+
transitions: z
|
|
271
|
+
.record(z.string(), transitionSchema)
|
|
272
|
+
.optional()
|
|
273
|
+
.describe(
|
|
274
|
+
"Optional per-change-set publication transitions. Omit by default to preserve publication state.",
|
|
275
|
+
),
|
|
276
|
+
}),
|
|
277
|
+
run: async (
|
|
278
|
+
args: ExecuteBuilderSourceBatchRequest,
|
|
279
|
+
): Promise<ExecuteBuilderSourceBatchResponse> => {
|
|
280
|
+
return executeBuilderSourceBatchWithDeps(args, realBatchDeps(args));
|
|
281
|
+
},
|
|
282
|
+
});
|
|
@@ -13,6 +13,10 @@ import {
|
|
|
13
13
|
type ContentDatabaseSourcePushMode,
|
|
14
14
|
type ExecuteBuilderSourceExecutionRequest,
|
|
15
15
|
} from "../shared/api.js";
|
|
16
|
+
import {
|
|
17
|
+
type BuilderCmsEntryLiveState,
|
|
18
|
+
readBuilderCmsEntryLiveState,
|
|
19
|
+
} from "./_builder-cms-read-client.js";
|
|
16
20
|
import { builderCmsQualifiedId } from "./_builder-cms-source-adapter.js";
|
|
17
21
|
import type {
|
|
18
22
|
BuilderCmsExecutionPayload,
|
|
@@ -21,6 +25,7 @@ import type {
|
|
|
21
25
|
import {
|
|
22
26
|
buildBuilderCmsExecutionPlan,
|
|
23
27
|
builderCmsExecutionIdempotencyKey,
|
|
28
|
+
resolveBuilderCmsExecutionPushMode,
|
|
24
29
|
validateBuilderCmsExecutionDryRun,
|
|
25
30
|
} from "./_builder-cms-write-adapter.js";
|
|
26
31
|
import {
|
|
@@ -28,7 +33,7 @@ import {
|
|
|
28
33
|
executeBuilderCmsWrite,
|
|
29
34
|
} from "./_builder-cms-write-client.js";
|
|
30
35
|
import {
|
|
31
|
-
|
|
36
|
+
getContentDatabaseSourceSnapshotForWrite,
|
|
32
37
|
resolveDatabaseForSourceMutation,
|
|
33
38
|
} from "./_database-source-utils.js";
|
|
34
39
|
import { getContentDatabaseResponse } from "./_database-utils.js";
|
|
@@ -94,6 +99,10 @@ export interface ExecuteBuilderSourceExecutionDeps {
|
|
|
94
99
|
executeWrite: (args: {
|
|
95
100
|
request: BuilderCmsExecutionPayload["request"];
|
|
96
101
|
}) => ReturnType<typeof executeBuilderCmsWrite>;
|
|
102
|
+
readLiveEntry: (args: {
|
|
103
|
+
model: string;
|
|
104
|
+
entryId: string;
|
|
105
|
+
}) => Promise<BuilderCmsEntryLiveState>;
|
|
97
106
|
reconcileWrite: (args: {
|
|
98
107
|
database: DatabaseRecord;
|
|
99
108
|
source: ContentDatabaseSource;
|
|
@@ -206,6 +215,127 @@ function proposedSourceDisplayKey(
|
|
|
206
215
|
: fallback;
|
|
207
216
|
}
|
|
208
217
|
|
|
218
|
+
function builderCmsResponseRecord(
|
|
219
|
+
value: unknown,
|
|
220
|
+
): Record<string, unknown> | null {
|
|
221
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
222
|
+
? (value as Record<string, unknown>)
|
|
223
|
+
: null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function builderCmsResponseValue(
|
|
227
|
+
value: unknown,
|
|
228
|
+
keys: string[],
|
|
229
|
+
): number | string | null {
|
|
230
|
+
const record = builderCmsResponseRecord(value);
|
|
231
|
+
if (!record) return null;
|
|
232
|
+
for (const key of keys) {
|
|
233
|
+
const direct = record[key];
|
|
234
|
+
if (typeof direct === "string" && direct.trim()) return direct.trim();
|
|
235
|
+
if (typeof direct === "number" && Number.isFinite(direct)) return direct;
|
|
236
|
+
}
|
|
237
|
+
for (const key of ["entry", "result", "content", "data"]) {
|
|
238
|
+
const nested = builderCmsResponseValue(record[key], keys);
|
|
239
|
+
if (nested !== null) return nested;
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function builderCmsAuthoritativeLastUpdated(
|
|
245
|
+
writeResult: BuilderCmsWriteResult,
|
|
246
|
+
fallback: string,
|
|
247
|
+
) {
|
|
248
|
+
return String(
|
|
249
|
+
builderCmsResponseValue(writeResult.responseBody, [
|
|
250
|
+
"lastUpdated",
|
|
251
|
+
"lastUpdatedAt",
|
|
252
|
+
"updatedAt",
|
|
253
|
+
"last_source_updated_at",
|
|
254
|
+
]) ?? fallback,
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function sourceRowForChangeSet(
|
|
259
|
+
source: ContentDatabaseSource,
|
|
260
|
+
changeSet: ContentDatabaseSourceChangeSet,
|
|
261
|
+
) {
|
|
262
|
+
return (
|
|
263
|
+
source.rows.find(
|
|
264
|
+
(row) =>
|
|
265
|
+
row.documentId === changeSet.documentId ||
|
|
266
|
+
row.databaseItemId === changeSet.databaseItemId,
|
|
267
|
+
) ?? null
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function requiresLivePreflight(effect: BuilderCmsExecutionPayload["effect"]) {
|
|
272
|
+
return (
|
|
273
|
+
effect === "update_in_place" ||
|
|
274
|
+
effect === "publish" ||
|
|
275
|
+
effect === "unpublish"
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function normalizedLiveTimestamp(value: number | string | null | undefined) {
|
|
280
|
+
return value === null || value === undefined ? null : String(value);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function toEpochMs(value: number | string | null | undefined) {
|
|
284
|
+
if (value === null || value === undefined) return null;
|
|
285
|
+
if (typeof value === "number") {
|
|
286
|
+
return Number.isFinite(value) ? value : null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const trimmed = value.trim();
|
|
290
|
+
if (!trimmed) return null;
|
|
291
|
+
|
|
292
|
+
const numeric = Number(trimmed);
|
|
293
|
+
if (Number.isFinite(numeric)) return numeric;
|
|
294
|
+
|
|
295
|
+
const parsed = Date.parse(trimmed);
|
|
296
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function liveTimestampsDiffer(args: {
|
|
300
|
+
liveLastUpdated: number | string | null | undefined;
|
|
301
|
+
baselineLastUpdated: string | null | undefined;
|
|
302
|
+
}) {
|
|
303
|
+
const liveEpoch = toEpochMs(args.liveLastUpdated);
|
|
304
|
+
const baselineEpoch = toEpochMs(args.baselineLastUpdated);
|
|
305
|
+
if (liveEpoch !== null && baselineEpoch !== null) {
|
|
306
|
+
return liveEpoch !== baselineEpoch;
|
|
307
|
+
}
|
|
308
|
+
return (
|
|
309
|
+
normalizedLiveTimestamp(args.liveLastUpdated) !==
|
|
310
|
+
normalizedLiveTimestamp(args.baselineLastUpdated)
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function livePreflightBlockMessage(args: {
|
|
315
|
+
liveState: BuilderCmsEntryLiveState;
|
|
316
|
+
baselineLastUpdated: string | null;
|
|
317
|
+
effect: BuilderCmsExecutionPayload["effect"];
|
|
318
|
+
}) {
|
|
319
|
+
if (!args.liveState.exists) {
|
|
320
|
+
return "Builder entry no longer exists; refresh the source.";
|
|
321
|
+
}
|
|
322
|
+
if (
|
|
323
|
+
liveTimestampsDiffer({
|
|
324
|
+
liveLastUpdated: args.liveState.lastUpdated,
|
|
325
|
+
baselineLastUpdated: args.baselineLastUpdated,
|
|
326
|
+
})
|
|
327
|
+
) {
|
|
328
|
+
return "Builder entry changed since this diff was approved; refresh and re-review.";
|
|
329
|
+
}
|
|
330
|
+
if (args.effect === "publish" && args.liveState.published !== "draft") {
|
|
331
|
+
return "Entry is already published.";
|
|
332
|
+
}
|
|
333
|
+
if (args.effect === "unpublish" && args.liveState.published !== "published") {
|
|
334
|
+
return "Entry is not currently published.";
|
|
335
|
+
}
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
|
|
209
339
|
export function builderCmsReconciledSourceRowPatch(args: {
|
|
210
340
|
source: ContentDatabaseSource;
|
|
211
341
|
changeSet: ContentDatabaseSourceChangeSet;
|
|
@@ -222,6 +352,10 @@ export function builderCmsReconciledSourceRowPatch(args: {
|
|
|
222
352
|
const entryId =
|
|
223
353
|
args.writeResult.entryId ?? args.plan.payload.target.entryId ?? null;
|
|
224
354
|
if (!entryId) return null;
|
|
355
|
+
const sourceUpdatedAt = builderCmsAuthoritativeLastUpdated(
|
|
356
|
+
args.writeResult,
|
|
357
|
+
args.now,
|
|
358
|
+
);
|
|
225
359
|
|
|
226
360
|
return {
|
|
227
361
|
sourceRowId: entryId,
|
|
@@ -237,7 +371,7 @@ export function builderCmsReconciledSourceRowPatch(args: {
|
|
|
237
371
|
syncState: "idle",
|
|
238
372
|
freshness: "fresh",
|
|
239
373
|
lastSyncedAt: args.now,
|
|
240
|
-
lastSourceUpdatedAt:
|
|
374
|
+
lastSourceUpdatedAt: sourceUpdatedAt,
|
|
241
375
|
updatedAt: args.now,
|
|
242
376
|
};
|
|
243
377
|
}
|
|
@@ -316,21 +450,24 @@ async function reconcileBuilderCmsWrite(args: {
|
|
|
316
450
|
syncState: "idle",
|
|
317
451
|
freshness: "fresh",
|
|
318
452
|
lastRefreshedAt: args.now,
|
|
319
|
-
lastSourceUpdatedAt:
|
|
453
|
+
lastSourceUpdatedAt: patch.lastSourceUpdatedAt,
|
|
320
454
|
lastError: null,
|
|
321
455
|
updatedAt: args.now,
|
|
322
456
|
})
|
|
323
457
|
.where(eq(schema.contentDatabaseSources.id, args.source.id));
|
|
324
458
|
}
|
|
325
459
|
|
|
326
|
-
function realExecutionDeps(
|
|
460
|
+
export function realExecutionDeps(
|
|
461
|
+
sourceId?: string,
|
|
462
|
+
): ExecuteBuilderSourceExecutionDeps {
|
|
327
463
|
return {
|
|
328
464
|
now: () => new Date().toISOString(),
|
|
329
465
|
resolveDatabase: (args) => resolveDatabaseForSourceMutation(args),
|
|
330
466
|
assertEditor: async (database) => {
|
|
331
467
|
await assertAccess("document", database.documentId, "editor");
|
|
332
468
|
},
|
|
333
|
-
getSourceSnapshot: (database) =>
|
|
469
|
+
getSourceSnapshot: (database) =>
|
|
470
|
+
getContentDatabaseSourceSnapshotForWrite(database, sourceId),
|
|
334
471
|
getExecution: async (args) => {
|
|
335
472
|
const [execution] = await getDb()
|
|
336
473
|
.select()
|
|
@@ -426,6 +563,7 @@ function realExecutionDeps(): ExecuteBuilderSourceExecutionDeps {
|
|
|
426
563
|
.where(eq(schema.contentDatabaseSourceExecutions.id, args.executionId));
|
|
427
564
|
},
|
|
428
565
|
executeWrite: (args) => executeBuilderCmsWrite(args),
|
|
566
|
+
readLiveEntry: (args) => readBuilderCmsEntryLiveState(args),
|
|
429
567
|
reconcileWrite: reconcileBuilderCmsWrite,
|
|
430
568
|
getResponse: (databaseId) => getContentDatabaseResponse(databaseId),
|
|
431
569
|
};
|
|
@@ -452,18 +590,28 @@ export async function executeBuilderSourceExecutionWithDeps(
|
|
|
452
590
|
throw new Error("Only outbound Builder change sets can be executed.");
|
|
453
591
|
}
|
|
454
592
|
|
|
593
|
+
const resolvedPushMode = resolveBuilderCmsExecutionPushMode({
|
|
594
|
+
source,
|
|
595
|
+
changeSet,
|
|
596
|
+
});
|
|
597
|
+
const effectivePushMode =
|
|
598
|
+
resolvedPushMode === "none" ? "autosave" : resolvedPushMode;
|
|
455
599
|
const pushMode = executablePushMode(
|
|
456
|
-
args.pushModeConfirmation ??
|
|
600
|
+
args.pushModeConfirmation ?? effectivePushMode,
|
|
457
601
|
);
|
|
458
602
|
if (!pushMode) {
|
|
459
603
|
throw new Error(
|
|
460
604
|
"Builder execution requires Autosave, Draft, or Publish push mode.",
|
|
461
605
|
);
|
|
462
606
|
}
|
|
607
|
+
// The gate key is keyed on the RAW resolved push mode (matching the plan in
|
|
608
|
+
// buildBuilderCmsExecutionPlan) — NOT on pushModeConfirmation. Keying on the
|
|
609
|
+
// confirmation would let a caller's confirmation diverge the key from the
|
|
610
|
+
// prepared gate; the confirmation is still validated inside the plan below.
|
|
463
611
|
const expectedKey = builderCmsExecutionIdempotencyKey({
|
|
464
612
|
sourceId: source.id,
|
|
465
613
|
changeSetId: changeSet.id,
|
|
466
|
-
pushMode,
|
|
614
|
+
pushMode: resolvedPushMode,
|
|
467
615
|
});
|
|
468
616
|
if (args.idempotencyKey && args.idempotencyKey !== expectedKey) {
|
|
469
617
|
throw new Error(
|
|
@@ -495,6 +643,8 @@ export async function executeBuilderSourceExecutionWithDeps(
|
|
|
495
643
|
source,
|
|
496
644
|
changeSet,
|
|
497
645
|
pushModeConfirmation: pushMode,
|
|
646
|
+
publicationTransition: args.publicationTransition,
|
|
647
|
+
confirmUnpublish: args.confirmUnpublish,
|
|
498
648
|
});
|
|
499
649
|
const storedPayload = parsePayload(execution.payloadJson);
|
|
500
650
|
const validatedPayload = validateBuilderCmsExecutionDryRun({
|
|
@@ -590,6 +740,53 @@ export async function executeBuilderSourceExecutionWithDeps(
|
|
|
590
740
|
return deps.getResponse(database.id);
|
|
591
741
|
}
|
|
592
742
|
|
|
743
|
+
if (requiresLivePreflight(plan.payload.effect)) {
|
|
744
|
+
const entryId = plan.payload.target.entryId;
|
|
745
|
+
if (!entryId) {
|
|
746
|
+
const message = "Builder entry no longer exists; refresh the source.";
|
|
747
|
+
await deps.updateExecutionState({
|
|
748
|
+
executionId: execution.id,
|
|
749
|
+
state: "blocked",
|
|
750
|
+
summary: `${plan.summary} Execution blocked before write.`,
|
|
751
|
+
payload: validatedPayload,
|
|
752
|
+
lastError: message,
|
|
753
|
+
now,
|
|
754
|
+
});
|
|
755
|
+
throw new Error(message);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const liveState = await deps.readLiveEntry({
|
|
759
|
+
model: plan.payload.target.model,
|
|
760
|
+
entryId,
|
|
761
|
+
});
|
|
762
|
+
const targetRow = sourceRowForChangeSet(source, changeSet);
|
|
763
|
+
const message = livePreflightBlockMessage({
|
|
764
|
+
liveState,
|
|
765
|
+
baselineLastUpdated: targetRow?.lastSourceUpdatedAt ?? null,
|
|
766
|
+
effect: plan.payload.effect,
|
|
767
|
+
});
|
|
768
|
+
if (message) {
|
|
769
|
+
await deps.updateExecutionState({
|
|
770
|
+
executionId: execution.id,
|
|
771
|
+
state: "blocked",
|
|
772
|
+
summary: `${plan.summary} Execution blocked before write.`,
|
|
773
|
+
payload: {
|
|
774
|
+
...validatedPayload,
|
|
775
|
+
livePreflight: {
|
|
776
|
+
checkedAt: now,
|
|
777
|
+
exists: liveState.exists,
|
|
778
|
+
published: liveState.published,
|
|
779
|
+
lastUpdated: liveState.lastUpdated,
|
|
780
|
+
id: liveState.id,
|
|
781
|
+
},
|
|
782
|
+
},
|
|
783
|
+
lastError: message,
|
|
784
|
+
now,
|
|
785
|
+
});
|
|
786
|
+
throw new Error(message);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
593
790
|
const claimed = await deps.claimExecution({
|
|
594
791
|
executionId: execution.id,
|
|
595
792
|
summary: `Running Builder ${plan.pushMode} execution.`,
|
|
@@ -661,6 +858,10 @@ export default defineAction({
|
|
|
661
858
|
schema: z.object({
|
|
662
859
|
databaseId: z.string().optional().describe("Database ID"),
|
|
663
860
|
documentId: z.string().optional().describe("Database document/page ID"),
|
|
861
|
+
sourceId: z
|
|
862
|
+
.string()
|
|
863
|
+
.optional()
|
|
864
|
+
.describe("Target source ID (defaults to the primary source)"),
|
|
664
865
|
changeSetId: z.string().describe("Approved source change-set ID"),
|
|
665
866
|
idempotencyKey: z
|
|
666
867
|
.string()
|
|
@@ -670,10 +871,21 @@ export default defineAction({
|
|
|
670
871
|
.enum(["autosave", "draft", "publish"])
|
|
671
872
|
.optional()
|
|
672
873
|
.describe("Explicit push mode confirmation for the live write"),
|
|
874
|
+
publicationTransition: z
|
|
875
|
+
.enum(["publish", "unpublish"])
|
|
876
|
+
.optional()
|
|
877
|
+
.describe("Explicit publication transition to validate at write time"),
|
|
878
|
+
confirmUnpublish: z
|
|
879
|
+
.boolean()
|
|
880
|
+
.optional()
|
|
881
|
+
.describe("Required explicit confirmation for unpublish transitions"),
|
|
673
882
|
}),
|
|
674
883
|
run: async (
|
|
675
884
|
args: ExecuteBuilderSourceExecutionRequest,
|
|
676
885
|
): Promise<ContentDatabaseResponse> => {
|
|
677
|
-
return executeBuilderSourceExecutionWithDeps(
|
|
886
|
+
return executeBuilderSourceExecutionWithDeps(
|
|
887
|
+
args,
|
|
888
|
+
realExecutionDeps(args.sourceId),
|
|
889
|
+
);
|
|
678
890
|
},
|
|
679
891
|
});
|
|
@@ -8,7 +8,10 @@ import type {
|
|
|
8
8
|
ContentDatabaseResponse,
|
|
9
9
|
ContentDatabaseUnavailableResponse,
|
|
10
10
|
} from "../shared/api.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
CONTENT_DATABASE_MAX_READ_LIMIT,
|
|
13
|
+
getContentDatabaseResponse,
|
|
14
|
+
} from "./_database-utils.js";
|
|
12
15
|
|
|
13
16
|
export default defineAction({
|
|
14
17
|
description:
|
|
@@ -16,7 +19,12 @@ export default defineAction({
|
|
|
16
19
|
schema: z.object({
|
|
17
20
|
databaseId: z.string().optional().describe("Database ID"),
|
|
18
21
|
documentId: z.string().optional().describe("Database document/page ID"),
|
|
19
|
-
limit: z.coerce
|
|
22
|
+
limit: z.coerce
|
|
23
|
+
.number()
|
|
24
|
+
.int()
|
|
25
|
+
.min(1)
|
|
26
|
+
.max(CONTENT_DATABASE_MAX_READ_LIMIT)
|
|
27
|
+
.optional(),
|
|
20
28
|
offset: z.coerce.number().int().min(0).optional(),
|
|
21
29
|
}),
|
|
22
30
|
http: { method: "GET" },
|
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
} from "../shared/api.js";
|
|
11
11
|
import { buildBuilderCmsExecutionPlan } from "./_builder-cms-write-adapter.js";
|
|
12
12
|
import {
|
|
13
|
-
|
|
13
|
+
getContentDatabaseSourceSnapshotForWrite,
|
|
14
14
|
resolveDatabaseForSourceMutation,
|
|
15
15
|
} from "./_database-source-utils.js";
|
|
16
16
|
import { getContentDatabaseResponse } from "./_database-utils.js";
|
|
@@ -21,11 +21,23 @@ export default defineAction({
|
|
|
21
21
|
schema: z.object({
|
|
22
22
|
databaseId: z.string().optional().describe("Database ID"),
|
|
23
23
|
documentId: z.string().optional().describe("Database document/page ID"),
|
|
24
|
+
sourceId: z
|
|
25
|
+
.string()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Target source ID (defaults to the primary source)"),
|
|
24
28
|
changeSetId: z.string().describe("Approved source change-set ID"),
|
|
25
29
|
pushModeConfirmation: z
|
|
26
30
|
.enum(["autosave", "draft", "publish"])
|
|
27
31
|
.optional()
|
|
28
32
|
.describe("Explicit push mode confirmation for the planned write"),
|
|
33
|
+
publicationTransition: z
|
|
34
|
+
.enum(["publish", "unpublish"])
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("Explicit publication transition to validate at write time"),
|
|
37
|
+
confirmUnpublish: z
|
|
38
|
+
.boolean()
|
|
39
|
+
.optional()
|
|
40
|
+
.describe("Required explicit confirmation for unpublish transitions"),
|
|
29
41
|
}),
|
|
30
42
|
run: async (
|
|
31
43
|
args: PrepareBuilderSourceExecutionRequest,
|
|
@@ -34,7 +46,10 @@ export default defineAction({
|
|
|
34
46
|
if (!database) throw new Error("Database not found.");
|
|
35
47
|
await assertAccess("document", database.documentId, "editor");
|
|
36
48
|
|
|
37
|
-
const source = await
|
|
49
|
+
const source = await getContentDatabaseSourceSnapshotForWrite(
|
|
50
|
+
database,
|
|
51
|
+
args.sourceId,
|
|
52
|
+
);
|
|
38
53
|
if (!source || source.sourceType !== "builder-cms") {
|
|
39
54
|
throw new Error(
|
|
40
55
|
"Attach a Builder CMS source before preparing execution.",
|
|
@@ -50,6 +65,8 @@ export default defineAction({
|
|
|
50
65
|
source,
|
|
51
66
|
changeSet,
|
|
52
67
|
pushModeConfirmation: args.pushModeConfirmation,
|
|
68
|
+
publicationTransition: args.publicationTransition,
|
|
69
|
+
confirmUnpublish: args.confirmUnpublish,
|
|
53
70
|
});
|
|
54
71
|
const now = new Date().toISOString();
|
|
55
72
|
const db = getDb();
|