@elytracms/studio 0.0.92 → 0.0.94
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/convex/analytics-cost.test.ts +817 -0
- package/convex/asset-alt-migration.test.ts +127 -0
- package/convex/asset-replace.test.ts +168 -0
- package/convex/asset-revalidate.test.ts +6 -6
- package/convex/assets.ts +41 -5
- package/convex/auth.ts +5 -2
- package/convex/authority.ts +35 -11
- package/convex/cms.ts +25 -11
- package/convex/contract-parity.test.ts +25 -3
- package/convex/delivery.ts +12 -9
- package/convex/document-revalidate.test.ts +10 -12
- package/convex/guard.ts +9 -7
- package/convex/http-routes.test.ts +18 -8
- package/convex/http.ts +23 -17
- package/convex/ingest.test.ts +326 -0
- package/convex/ingest.ts +281 -0
- package/convex/invites.ts +13 -5
- package/convex/members.ts +10 -4
- package/convex/orphan-hydration.test.ts +116 -0
- package/convex/rail-write-parity.test.ts +11 -8
- package/convex/record-functions.ts +325 -0
- package/convex/records.test.ts +156 -0
- package/convex/redirect-migration.test.ts +205 -0
- package/convex/redirect-revalidate.test.ts +5 -1
- package/convex/redirects.ts +120 -79
- package/convex/role-policy.test.ts +315 -0
- package/convex/roles.ts +83 -0
- package/convex/schema.ts +36 -0
- package/convex/triggers.ts +21 -1
- package/dist/addon.d.ts +71 -0
- package/dist/addon.js +67 -0
- package/dist/addon.js.map +1 -0
- package/dist/components/account/account-settings.js +2 -2
- package/dist/components/account/account-settings.js.map +1 -1
- package/dist/components/account/credential-panel.js +2 -2
- package/dist/components/account/credential-panel.js.map +1 -1
- package/dist/components/account/self-account-schema.d.ts +6 -15
- package/dist/components/account/self-account-schema.js +52 -34
- package/dist/components/account/self-account-schema.js.map +1 -1
- package/dist/components/account/self-account-view-source.d.ts +1 -1
- package/dist/components/account/self-account-view-source.js +6 -4
- package/dist/components/account/self-account-view-source.js.map +1 -1
- package/dist/components/ai/ai-chat-panel-lazy.js +1 -1
- package/dist/components/ai/ai-chat-panel-lazy.js.map +1 -1
- package/dist/components/ai/ai-chat-panel.js +6 -6
- package/dist/components/ai/ai-chat-panel.js.map +1 -1
- package/dist/components/ai/ai-cms-panel.js +2 -2
- package/dist/components/ai/ai-cms-panel.js.map +1 -1
- package/dist/components/ai/ai-panel-toggle.js +4 -4
- package/dist/components/ai/ai-panel-toggle.js.map +1 -1
- package/dist/components/ai/chat-ui.js +2 -2
- package/dist/components/ai/chat-ui.js.map +1 -1
- package/dist/components/ai/proposal-card.d.ts +2 -2
- package/dist/components/ai/proposal-card.js +4 -3
- package/dist/components/ai/proposal-card.js.map +1 -1
- package/dist/components/ai/proposal-issues.js +2 -2
- package/dist/components/ai/proposal-issues.js.map +1 -1
- package/dist/components/ai/translate-document-action.js +2 -2
- package/dist/components/ai/translate-document-action.js.map +1 -1
- package/dist/components/ai/translate-document-dialog.js +10 -8
- package/dist/components/ai/translate-document-dialog.js.map +1 -1
- package/dist/components/ai/translate-skipped.d.ts +19 -0
- package/dist/components/ai/translate-skipped.js +46 -0
- package/dist/components/ai/translate-skipped.js.map +1 -0
- package/dist/components/app-views/default-modules.d.ts +50 -2
- package/dist/components/app-views/default-modules.js +65 -20
- package/dist/components/app-views/default-modules.js.map +1 -1
- package/dist/components/app-views/module.d.ts +53 -3
- package/dist/components/app-views/module.js +14 -7
- package/dist/components/app-views/module.js.map +1 -1
- package/dist/components/app-views/sidebar.js +1 -1
- package/dist/components/app-views/sidebar.js.map +1 -1
- package/dist/components/app-views/studio-config.d.ts +11 -2
- package/dist/components/app-views/studio-config.js.map +1 -1
- package/dist/components/assets/asset-detail.js +20 -10
- package/dist/components/assets/asset-detail.js.map +1 -1
- package/dist/components/assets/asset-grid.js +2 -2
- package/dist/components/assets/asset-grid.js.map +1 -1
- package/dist/components/assets/asset-metadata.d.ts +15 -4
- package/dist/components/assets/asset-metadata.js +15 -2
- package/dist/components/assets/asset-metadata.js.map +1 -1
- package/dist/components/assets/asset-search.js +4 -1
- package/dist/components/assets/asset-search.js.map +1 -1
- package/dist/components/assets/asset-selection.d.ts +0 -8
- package/dist/components/assets/asset-selection.js +4 -23
- package/dist/components/assets/asset-selection.js.map +1 -1
- package/dist/components/assets/asset-thumb.js +6 -5
- package/dist/components/assets/asset-thumb.js.map +1 -1
- package/dist/components/assets/assets-collection-view-source.d.ts +5 -5
- package/dist/components/assets/assets-collection-view-source.js +9 -5
- package/dist/components/assets/assets-collection-view-source.js.map +1 -1
- package/dist/components/assets/assets-module.js +1 -1
- package/dist/components/assets/assets-module.js.map +1 -1
- package/dist/components/assets/assets-store.d.ts +16 -2
- package/dist/components/assets/assets-store.js +28 -6
- package/dist/components/assets/assets-store.js.map +1 -1
- package/dist/components/assets/assets-view-preference.js +1 -1
- package/dist/components/assets/assets-view-preference.js.map +1 -1
- package/dist/components/assets/replace-file.d.ts +22 -0
- package/dist/components/assets/replace-file.js +59 -0
- package/dist/components/assets/replace-file.js.map +1 -0
- package/dist/components/auth-context.d.ts +1 -1
- package/dist/components/auth-context.js +6 -6
- package/dist/components/auth-context.js.map +1 -1
- package/dist/components/builder/blocks-model.d.ts +15 -27
- package/dist/components/builder/blocks-model.js +38 -44
- package/dist/components/builder/blocks-model.js.map +1 -1
- package/dist/components/builder/blocks-panel.d.ts +8 -4
- package/dist/components/builder/blocks-panel.js +10 -9
- package/dist/components/builder/blocks-panel.js.map +1 -1
- package/dist/components/builder/builder-store.d.ts +104 -15
- package/dist/components/builder/builder-store.js +157 -32
- package/dist/components/builder/builder-store.js.map +1 -1
- package/dist/components/builder/canvas-body.d.ts +30 -0
- package/dist/components/builder/canvas-body.js +38 -0
- package/dist/components/builder/canvas-body.js.map +1 -0
- package/dist/components/builder/canvas-breakpoints.js +1 -1
- package/dist/components/builder/canvas-breakpoints.js.map +1 -1
- package/dist/components/builder/canvas-chrome-css.js +7 -0
- package/dist/components/builder/canvas-chrome-css.js.map +1 -1
- package/dist/components/builder/canvas-dnd.d.ts +9 -9
- package/dist/components/builder/canvas-dnd.js +6 -6
- package/dist/components/builder/canvas-dnd.js.map +1 -1
- package/dist/components/builder/canvas-inline-editing.d.ts +1 -1
- package/dist/components/builder/canvas-inline-editing.js.map +1 -1
- package/dist/components/builder/canvas-overlay-geometry.d.ts +1 -1
- package/dist/components/builder/canvas-overlay-geometry.js.map +1 -1
- package/dist/components/builder/canvas-render-context.d.ts +28 -20
- package/dist/components/builder/canvas-render-context.js +46 -47
- package/dist/components/builder/canvas-render-context.js.map +1 -1
- package/dist/components/builder/canvas-runs.d.ts +45 -0
- package/dist/components/builder/canvas-runs.js +51 -0
- package/dist/components/builder/canvas-runs.js.map +1 -0
- package/dist/components/builder/canvas-theme-notice.js +2 -2
- package/dist/components/builder/canvas-theme-notice.js.map +1 -1
- package/dist/components/builder/canvas.js +35 -8
- package/dist/components/builder/canvas.js.map +1 -1
- package/dist/components/builder/clipboard-actions.d.ts +16 -1
- package/dist/components/builder/clipboard-actions.js +48 -11
- package/dist/components/builder/clipboard-actions.js.map +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js +1 -1
- package/dist/components/builder/composition-field-editor-lazy.js.map +1 -1
- package/dist/components/builder/composition-field-editor.d.ts +4 -16
- package/dist/components/builder/composition-field-editor.js +32 -43
- package/dist/components/builder/composition-field-editor.js.map +1 -1
- package/dist/components/builder/delete-key.d.ts +5 -0
- package/dist/components/builder/delete-key.js +4 -0
- package/dist/components/builder/delete-key.js.map +1 -1
- package/dist/components/builder/drag-session.d.ts +1 -1
- package/dist/components/builder/drag-session.js.map +1 -1
- package/dist/components/builder/fixture-thumbnail.js +1 -1
- package/dist/components/builder/fixture-thumbnail.js.map +1 -1
- package/dist/components/builder/insert-context.d.ts +18 -26
- package/dist/components/builder/insert-context.js +47 -94
- package/dist/components/builder/insert-context.js.map +1 -1
- package/dist/components/builder/layout-inspector.js +1 -1
- package/dist/components/builder/layout-inspector.js.map +1 -1
- package/dist/components/builder/node-title.js +1 -1
- package/dist/components/builder/node-title.js.map +1 -1
- package/dist/components/builder/page-tree.d.ts +4 -4
- package/dist/components/builder/page-tree.js +8 -8
- package/dist/components/builder/page-tree.js.map +1 -1
- package/dist/components/builder/placement-refusal.d.ts +18 -0
- package/dist/components/builder/placement-refusal.js +46 -0
- package/dist/components/builder/placement-refusal.js.map +1 -0
- package/dist/components/builder/property-inspector.js +8 -4
- package/dist/components/builder/property-inspector.js.map +1 -1
- package/dist/components/builder/reuse-context.js +68 -30
- package/dist/components/builder/reuse-context.js.map +1 -1
- package/dist/components/builder/reuse.d.ts +79 -1
- package/dist/components/builder/reuse.js +70 -3
- package/dist/components/builder/reuse.js.map +1 -1
- package/dist/components/builder/rich-text-toolbar.js +1 -1
- package/dist/components/builder/rich-text-toolbar.js.map +1 -1
- package/dist/components/builder/run-inspector.d.ts +11 -0
- package/dist/components/builder/run-inspector.js +33 -0
- package/dist/components/builder/run-inspector.js.map +1 -0
- package/dist/components/builder/selection-overlay.js +3 -3
- package/dist/components/builder/selection-overlay.js.map +1 -1
- package/dist/components/builder/spacing-handles.d.ts +2 -2
- package/dist/components/builder/spacing-handles.js +3 -3
- package/dist/components/builder/spacing-handles.js.map +1 -1
- package/dist/components/cms/asset-field.d.ts +38 -1
- package/dist/components/cms/asset-field.js +184 -60
- package/dist/components/cms/asset-field.js.map +1 -1
- package/dist/components/cms/asset-picker-core.d.ts +16 -0
- package/dist/components/cms/asset-picker-core.js +20 -0
- package/dist/components/cms/asset-picker-core.js.map +1 -1
- package/dist/components/cms/asset-picker.js +14 -20
- package/dist/components/cms/asset-picker.js.map +1 -1
- package/dist/components/cms/bulk-actions.d.ts +4 -20
- package/dist/components/cms/bulk-actions.js +5 -26
- package/dist/components/cms/bulk-actions.js.map +1 -1
- package/dist/components/cms/bulk-bar.d.ts +2 -2
- package/dist/components/cms/bulk-bar.js +2 -2
- package/dist/components/cms/bulk-bar.js.map +1 -1
- package/dist/components/cms/cell-edit.d.ts +1 -1
- package/dist/components/cms/cell-edit.js +1 -1
- package/dist/components/cms/cell-edit.js.map +1 -1
- package/dist/components/cms/cell.d.ts +1 -1
- package/dist/components/cms/cell.js +3 -3
- package/dist/components/cms/cell.js.map +1 -1
- package/dist/components/cms/cms-index.js +2 -2
- package/dist/components/cms/cms-index.js.map +1 -1
- package/dist/components/cms/cms-labels.js +2 -2
- package/dist/components/cms/cms-labels.js.map +1 -1
- package/dist/components/cms/cms-store.d.ts +7 -6
- package/dist/components/cms/cms-store.js +23 -43
- package/dist/components/cms/cms-store.js.map +1 -1
- package/dist/components/cms/collection-table.js +7 -7
- package/dist/components/cms/collection-table.js.map +1 -1
- package/dist/components/cms/detail-editor.d.ts +5 -2
- package/dist/components/cms/detail-editor.js +20 -15
- package/dist/components/cms/detail-editor.js.map +1 -1
- package/dist/components/cms/details-pane.js +1 -1
- package/dist/components/cms/details-pane.js.map +1 -1
- package/dist/components/cms/doc-state.js +1 -1
- package/dist/components/cms/doc-state.js.map +1 -1
- package/dist/components/cms/document-diff-view.d.ts +3 -1
- package/dist/components/cms/document-diff-view.js +17 -12
- package/dist/components/cms/document-diff-view.js.map +1 -1
- package/dist/components/cms/document-editor-context.js +1 -1
- package/dist/components/cms/document-editor-context.js.map +1 -1
- package/dist/components/cms/document-staging.js +1 -1
- package/dist/components/cms/document-staging.js.map +1 -1
- package/dist/components/cms/document-url-path.js +2 -2
- package/dist/components/cms/document-url-path.js.map +1 -1
- package/dist/components/cms/duplicate-document.d.ts +1 -1
- package/dist/components/cms/duplicate-document.js.map +1 -1
- package/dist/components/cms/editable-cell.js +3 -3
- package/dist/components/cms/editable-cell.js.map +1 -1
- package/dist/components/cms/field-anchors.d.ts +7 -4
- package/dist/components/cms/field-anchors.js +9 -7
- package/dist/components/cms/field-anchors.js.map +1 -1
- package/dist/components/cms/field-collaboration.d.ts +1 -1
- package/dist/components/cms/field-collaboration.js +1 -1
- package/dist/components/cms/field-collaboration.js.map +1 -1
- package/dist/components/cms/field-connection.d.ts +1 -1
- package/dist/components/cms/field-connection.js +2 -2
- package/dist/components/cms/field-connection.js.map +1 -1
- package/dist/components/cms/field-control-lib.d.ts +15 -1
- package/dist/components/cms/field-control-lib.js +23 -4
- package/dist/components/cms/field-control-lib.js.map +1 -1
- package/dist/components/cms/field-control.js +4 -4
- package/dist/components/cms/field-control.js.map +1 -1
- package/dist/components/cms/field-renderer.d.ts +1 -1
- package/dist/components/cms/field-renderer.js +2 -2
- package/dist/components/cms/field-renderer.js.map +1 -1
- package/dist/components/cms/filter-builder.d.ts +1 -1
- package/dist/components/cms/filter-builder.js +3 -3
- package/dist/components/cms/filter-builder.js.map +1 -1
- package/dist/components/cms/gate-validation.d.ts +21 -30
- package/dist/components/cms/gate-validation.js +11 -32
- package/dist/components/cms/gate-validation.js.map +1 -1
- package/dist/components/cms/import-export.js +2 -2
- package/dist/components/cms/import-export.js.map +1 -1
- package/dist/components/cms/issue-copy.js +1 -1
- package/dist/components/cms/issue-copy.js.map +1 -1
- package/dist/components/cms/issues-pane.js +3 -3
- package/dist/components/cms/issues-pane.js.map +1 -1
- package/dist/components/cms/object-array-field.js +6 -6
- package/dist/components/cms/object-array-field.js.map +1 -1
- package/dist/components/cms/prop-fields.d.ts +7 -2
- package/dist/components/cms/prop-fields.js +14 -7
- package/dist/components/cms/prop-fields.js.map +1 -1
- package/dist/components/cms/reference-confirm.d.ts +1 -1
- package/dist/components/cms/reference-confirm.js +2 -2
- package/dist/components/cms/reference-confirm.js.map +1 -1
- package/dist/components/cms/relation-picker.js +2 -2
- package/dist/components/cms/relation-picker.js.map +1 -1
- package/dist/components/cms/rich-text/block-style.js +1 -1
- package/dist/components/cms/rich-text/block-style.js.map +1 -1
- package/dist/components/cms/rich-text/component-embed.js +4 -4
- package/dist/components/cms/rich-text/component-embed.js.map +1 -1
- package/dist/components/cms/rich-text/editor.js +3 -3
- package/dist/components/cms/rich-text/editor.js.map +1 -1
- package/dist/components/cms/rich-text/embed-picker.d.ts +1 -1
- package/dist/components/cms/rich-text/embed-picker.js +3 -3
- package/dist/components/cms/rich-text/embed-picker.js.map +1 -1
- package/dist/components/cms/rich-text/embed-props.js +3 -3
- package/dist/components/cms/rich-text/embed-props.js.map +1 -1
- package/dist/components/cms/rich-text/extensions.js +1 -1
- package/dist/components/cms/rich-text/extensions.js.map +1 -1
- package/dist/components/cms/rich-text/font-size-control.d.ts +1 -1
- package/dist/components/cms/rich-text/font-size-control.js +2 -2
- package/dist/components/cms/rich-text/font-size-control.js.map +1 -1
- package/dist/components/cms/rich-text/insert.d.ts +20 -9
- package/dist/components/cms/rich-text/insert.js +25 -17
- package/dist/components/cms/rich-text/insert.js.map +1 -1
- package/dist/components/cms/rich-text/lazy.js +1 -1
- package/dist/components/cms/rich-text/lazy.js.map +1 -1
- package/dist/components/cms/rich-text/link-control.js +2 -2
- package/dist/components/cms/rich-text/link-control.js.map +1 -1
- package/dist/components/cms/rich-text/slash-command.js +3 -3
- package/dist/components/cms/rich-text/slash-command.js.map +1 -1
- package/dist/components/cms/rich-text/toolbar.js +1 -1
- package/dist/components/cms/rich-text/toolbar.js.map +1 -1
- package/dist/components/cms/rich-text/unknown-node.js +2 -2
- package/dist/components/cms/rich-text/unknown-node.js.map +1 -1
- package/dist/components/cms/rich-text-preview.js +2 -2
- package/dist/components/cms/rich-text-preview.js.map +1 -1
- package/dist/components/cms/row-actions.d.ts +1 -1
- package/dist/components/cms/row-actions.js.map +1 -1
- package/dist/components/cms/scalar-value-editor.d.ts +1 -1
- package/dist/components/cms/scalar-value-editor.js +2 -2
- package/dist/components/cms/scalar-value-editor.js.map +1 -1
- package/dist/components/cms/use-discard-controller.d.ts +1 -1
- package/dist/components/cms/use-discard-controller.js +1 -1
- package/dist/components/cms/use-discard-controller.js.map +1 -1
- package/dist/components/cms/use-document-draft.d.ts +7 -1
- package/dist/components/cms/use-document-draft.js +16 -2
- package/dist/components/cms/use-document-draft.js.map +1 -1
- package/dist/components/cms/use-document-versions.js +1 -1
- package/dist/components/cms/use-document-versions.js.map +1 -1
- package/dist/components/cms/use-field-focus.d.ts +1 -1
- package/dist/components/cms/use-field-focus.js +2 -2
- package/dist/components/cms/use-field-focus.js.map +1 -1
- package/dist/components/cms/use-live-url.d.ts +1 -1
- package/dist/components/cms/use-live-url.js +3 -3
- package/dist/components/cms/use-live-url.js.map +1 -1
- package/dist/components/cms/use-publish-controller.d.ts +2 -3
- package/dist/components/cms/use-publish-controller.js +8 -20
- package/dist/components/cms/use-publish-controller.js.map +1 -1
- package/dist/components/cms/use-published-perspective.d.ts +1 -1
- package/dist/components/cms/use-published-perspective.js.map +1 -1
- package/dist/components/cms/use-restore-version.d.ts +1 -1
- package/dist/components/cms/use-restore-version.js +1 -1
- package/dist/components/cms/use-restore-version.js.map +1 -1
- package/dist/components/cms/version-compare-lazy.js +1 -1
- package/dist/components/cms/version-compare-lazy.js.map +1 -1
- package/dist/components/cms/version-compare.js +1 -1
- package/dist/components/cms/version-compare.js.map +1 -1
- package/dist/components/cms/versions-pane.js +11 -4
- package/dist/components/cms/versions-pane.js.map +1 -1
- package/dist/components/cms/view-bar.js +3 -3
- package/dist/components/cms/view-bar.js.map +1 -1
- package/dist/components/collection-view/cms-collection-view-source.d.ts +1 -1
- package/dist/components/collection-view/cms-collection-view-source.js +5 -3
- package/dist/components/collection-view/cms-collection-view-source.js.map +1 -1
- package/dist/components/collection-view/collection-view-context.d.ts +1 -1
- package/dist/components/collection-view/collection-view-context.js +1 -1
- package/dist/components/collection-view/collection-view-context.js.map +1 -1
- package/dist/components/collection-view/collection-view-surface.d.ts +13 -0
- package/dist/components/collection-view/collection-view-surface.js +18 -0
- package/dist/components/collection-view/collection-view-surface.js.map +1 -0
- package/dist/components/components/component-detail.js +10 -10
- package/dist/components/components/component-detail.js.map +1 -1
- package/dist/components/components/component-preview.d.ts +2 -2
- package/dist/components/components/component-preview.js +5 -5
- package/dist/components/components/component-preview.js.map +1 -1
- package/dist/components/components/component-status.d.ts +1 -1
- package/dist/components/components/component-status.js +1 -1
- package/dist/components/components/component-status.js.map +1 -1
- package/dist/components/components/components-index.js +2 -2
- package/dist/components/components/components-index.js.map +1 -1
- package/dist/components/components/field-inspector.js +1 -1
- package/dist/components/components/field-inspector.js.map +1 -1
- package/dist/components/components/registry-browser.js +3 -3
- package/dist/components/components/registry-browser.js.map +1 -1
- package/dist/components/components/taxonomy.d.ts +1 -1
- package/dist/components/components/taxonomy.js.map +1 -1
- package/dist/components/components/transfer-dialog.js +4 -3
- package/dist/components/components/transfer-dialog.js.map +1 -1
- package/dist/components/components/transfer.d.ts +10 -10
- package/dist/components/components/transfer.js +13 -12
- package/dist/components/components/transfer.js.map +1 -1
- package/dist/components/current-user-menu.js +1 -1
- package/dist/components/current-user-menu.js.map +1 -1
- package/dist/components/delivery-health-banner.js +2 -2
- package/dist/components/delivery-health-banner.js.map +1 -1
- package/dist/components/deployment-setup-gate.js +3 -3
- package/dist/components/deployment-setup-gate.js.map +1 -1
- package/dist/components/dev-login-badge.js +2 -2
- package/dist/components/dev-login-badge.js.map +1 -1
- package/dist/components/guards.js +1 -1
- package/dist/components/guards.js.map +1 -1
- package/dist/components/inspector/document-panes.d.ts +54 -0
- package/dist/components/inspector/document-panes.js +41 -0
- package/dist/components/inspector/document-panes.js.map +1 -0
- package/dist/components/inspector/inspector-sidebar.js +1 -1
- package/dist/components/inspector/inspector-sidebar.js.map +1 -1
- package/dist/components/nav-issues-registry.d.ts +53 -0
- package/dist/components/nav-issues-registry.js +69 -0
- package/dist/components/nav-issues-registry.js.map +1 -0
- package/dist/components/nav-issues.js +4 -4
- package/dist/components/nav-issues.js.map +1 -1
- package/dist/components/operation-error.d.ts +1 -1
- package/dist/components/operation-error.js +4 -4
- package/dist/components/operation-error.js.map +1 -1
- package/dist/components/playground-badge.js +2 -2
- package/dist/components/playground-badge.js.map +1 -1
- package/dist/components/project-list.js +3 -3
- package/dist/components/project-list.js.map +1 -1
- package/dist/components/project-session.d.ts +2 -2
- package/dist/components/project-session.js +5 -5
- package/dist/components/project-session.js.map +1 -1
- package/dist/components/project-switcher.js +2 -2
- package/dist/components/project-switcher.js.map +1 -1
- package/dist/components/rail-store-status.js +1 -1
- package/dist/components/rail-store-status.js.map +1 -1
- package/dist/components/rail-view-source.d.ts +6 -5
- package/dist/components/rail-view-source.js +4 -3
- package/dist/components/rail-view-source.js.map +1 -1
- package/dist/components/records/record-lens.d.ts +51 -0
- package/dist/components/records/record-lens.js +156 -0
- package/dist/components/records/record-lens.js.map +1 -0
- package/dist/components/records/record-rail.d.ts +86 -0
- package/dist/components/records/record-rail.js +241 -0
- package/dist/components/records/record-rail.js.map +1 -0
- package/dist/components/redirects/redirects-schema.js +1 -1
- package/dist/components/redirects/redirects-schema.js.map +1 -1
- package/dist/components/references/references-panel.js +2 -2
- package/dist/components/references/references-panel.js.map +1 -1
- package/dist/components/section-nav.js +27 -11
- package/dist/components/section-nav.js.map +1 -1
- package/dist/components/section-state.js +2 -2
- package/dist/components/section-state.js.map +1 -1
- package/dist/components/section-surface.js +2 -2
- package/dist/components/section-surface.js.map +1 -1
- package/dist/components/session-expired-overlay.js +3 -3
- package/dist/components/session-expired-overlay.js.map +1 -1
- package/dist/components/settings-form.js +1 -1
- package/dist/components/settings-form.js.map +1 -1
- package/dist/components/sign-in-needed-badge.js +1 -1
- package/dist/components/sign-in-needed-badge.js.map +1 -1
- package/dist/components/sign-in-view.d.ts +2 -2
- package/dist/components/sign-in-view.js +5 -5
- package/dist/components/sign-in-view.js.map +1 -1
- package/dist/components/studio-context.js +3 -3
- package/dist/components/studio-context.js.map +1 -1
- package/dist/components/studio-project-screen.js +30 -3
- package/dist/components/studio-project-screen.js.map +1 -1
- package/dist/components/studio-shell.js +6 -5
- package/dist/components/studio-shell.js.map +1 -1
- package/dist/components/theme-context.d.ts +1 -1
- package/dist/components/theme-context.js +2 -2
- package/dist/components/theme-context.js.map +1 -1
- package/dist/components/use-hydration-phase.d.ts +1 -1
- package/dist/components/use-hydration-phase.js +1 -1
- package/dist/components/use-hydration-phase.js.map +1 -1
- package/dist/components/users/invite-user-bar.d.ts +27 -0
- package/dist/components/users/invite-user-bar.js +25 -9
- package/dist/components/users/invite-user-bar.js.map +1 -1
- package/dist/components/users/role-policy-drift-issues.d.ts +38 -0
- package/dist/components/users/role-policy-drift-issues.js +71 -0
- package/dist/components/users/role-policy-drift-issues.js.map +1 -0
- package/dist/components/users/users-collection-view-source.d.ts +1 -1
- package/dist/components/users/users-collection-view-source.js +4 -2
- package/dist/components/users/users-collection-view-source.js.map +1 -1
- package/dist/components/users/users-module.js +1 -1
- package/dist/components/users/users-module.js.map +1 -1
- package/dist/components/users/users-schema.d.ts +10 -1
- package/dist/components/users/users-schema.js +45 -25
- package/dist/components/users/users-schema.js.map +1 -1
- package/dist/components/users/users-store.js +3 -3
- package/dist/components/users/users-store.js.map +1 -1
- package/dist/components/workspace-projects.d.ts +1 -1
- package/dist/components/workspace-projects.js +1 -1
- package/dist/components/workspace-projects.js.map +1 -1
- package/dist/lib/ai/apply-content.js +56 -116
- package/dist/lib/ai/apply-content.js.map +1 -1
- package/dist/lib/ai/chat-context.d.ts +8 -0
- package/dist/lib/ai/chat-context.js +37 -6
- package/dist/lib/ai/chat-context.js.map +1 -1
- package/dist/lib/ai/chat-state.d.ts +6 -0
- package/dist/lib/ai/chat-state.js.map +1 -1
- package/dist/lib/ai/constraints.d.ts +20 -5
- package/dist/lib/ai/constraints.js +125 -33
- package/dist/lib/ai/constraints.js.map +1 -1
- package/dist/lib/ai/fake-provider.d.ts +16 -3
- package/dist/lib/ai/fake-provider.js +22 -6
- package/dist/lib/ai/fake-provider.js.map +1 -1
- package/dist/lib/ai/index.d.ts +3 -1
- package/dist/lib/ai/index.js +3 -1
- package/dist/lib/ai/index.js.map +1 -1
- package/dist/lib/ai/leash.d.ts +34 -0
- package/dist/lib/ai/leash.js +28 -0
- package/dist/lib/ai/leash.js.map +1 -0
- package/dist/lib/ai/node-handler.d.ts +1 -1
- package/dist/lib/ai/node-handler.js.map +1 -1
- package/dist/lib/ai/rich-text-input.d.ts +8 -8
- package/dist/lib/ai/rich-text-input.js +2 -1
- package/dist/lib/ai/rich-text-input.js.map +1 -1
- package/dist/lib/ai/rich-text-translate.d.ts +27 -4
- package/dist/lib/ai/rich-text-translate.js +28 -10
- package/dist/lib/ai/rich-text-translate.js.map +1 -1
- package/dist/lib/ai/shape.d.ts +13 -13
- package/dist/lib/ai/shape.js +69 -93
- package/dist/lib/ai/shape.js.map +1 -1
- package/dist/lib/ai/tools.d.ts +36 -41
- package/dist/lib/ai/tools.js +83 -87
- package/dist/lib/ai/tools.js.map +1 -1
- package/dist/lib/ai/translate-document.d.ts +32 -4
- package/dist/lib/ai/translate-document.js +145 -47
- package/dist/lib/ai/translate-document.js.map +1 -1
- package/dist/lib/ai/types.d.ts +34 -23
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/ai/update-fields.d.ts +15 -12
- package/dist/lib/ai/update-fields.js +112 -188
- package/dist/lib/ai/update-fields.js.map +1 -1
- package/dist/lib/ai/writer-form.d.ts +116 -0
- package/dist/lib/ai/writer-form.js +429 -0
- package/dist/lib/ai/writer-form.js.map +1 -0
- package/dist/lib/assets/upload.d.ts +31 -2
- package/dist/lib/assets/upload.js +61 -6
- package/dist/lib/assets/upload.js.map +1 -1
- package/dist/lib/clipboard/os-clipboard.d.ts +10 -2
- package/dist/lib/clipboard/os-clipboard.js +8 -4
- package/dist/lib/clipboard/os-clipboard.js.map +1 -1
- package/dist/lib/clipboard/paste-issues.js +1 -1
- package/dist/lib/clipboard/paste-issues.js.map +1 -1
- package/dist/lib/clipboard/paste-target.d.ts +18 -9
- package/dist/lib/clipboard/paste-target.js +10 -9
- package/dist/lib/clipboard/paste-target.js.map +1 -1
- package/dist/lib/cms/field-patch.d.ts +0 -10
- package/dist/lib/cms/field-patch.js +4 -30
- package/dist/lib/cms/field-patch.js.map +1 -1
- package/dist/lib/cms/published-documents.js +1 -1
- package/dist/lib/cms/published-documents.js.map +1 -1
- package/dist/lib/cms-views/view-model.d.ts +9 -9
- package/dist/lib/cms-views/view-model.js +2 -2
- package/dist/lib/cms-views/view-model.js.map +1 -1
- package/dist/lib/collection-view/collection-view-source.d.ts +14 -5
- package/dist/lib/collection-view/collection-view-source.js.map +1 -1
- package/dist/lib/data/collection-content.js +3 -2
- package/dist/lib/data/collection-content.js.map +1 -1
- package/dist/lib/data/content-session-refresh.d.ts +1 -1
- package/dist/lib/data/content-session-refresh.js.map +1 -1
- package/dist/lib/data/playground-unload-guard.d.ts +1 -1
- package/dist/lib/data/playground-unload-guard.js +1 -1
- package/dist/lib/data/playground-unload-guard.js.map +1 -1
- package/dist/lib/dnd/drop-rules.d.ts +25 -31
- package/dist/lib/dnd/drop-rules.js +53 -59
- package/dist/lib/dnd/drop-rules.js.map +1 -1
- package/dist/lib/engine-version.d.ts +1 -1
- package/dist/lib/engine-version.js +1 -1
- package/dist/lib/engine-version.js.map +1 -1
- package/dist/lib/i18n/i18n.js +1 -1
- package/dist/lib/i18n/i18n.js.map +1 -1
- package/dist/lib/i18n/messages.de.js +44 -3
- package/dist/lib/i18n/messages.de.js.map +1 -1
- package/dist/lib/i18n/messages.en.d.ts +38 -2
- package/dist/lib/i18n/messages.en.js +51 -3
- package/dist/lib/i18n/messages.en.js.map +1 -1
- package/dist/lib/operations/project-runtime-config.d.ts +34 -6
- package/dist/lib/operations/project-runtime-config.js +50 -10
- package/dist/lib/operations/project-runtime-config.js.map +1 -1
- package/dist/lib/operations/publish-with-revision.d.ts +33 -1
- package/dist/lib/operations/publish-with-revision.js +32 -0
- package/dist/lib/operations/publish-with-revision.js.map +1 -1
- package/dist/lib/operations/runtime-validator.d.ts +19 -68
- package/dist/lib/operations/runtime-validator.js +12 -138
- package/dist/lib/operations/runtime-validator.js.map +1 -1
- package/dist/lib/operations/studio-operations.d.ts +17 -2
- package/dist/lib/operations/studio-operations.js +18 -11
- package/dist/lib/operations/studio-operations.js.map +1 -1
- package/dist/lib/persistence/convex-adapter.d.ts +16 -3
- package/dist/lib/persistence/convex-adapter.js +83 -47
- package/dist/lib/persistence/convex-adapter.js.map +1 -1
- package/dist/lib/persistence/index.js +14 -1
- package/dist/lib/persistence/index.js.map +1 -1
- package/dist/lib/persona/labels.js +4 -2
- package/dist/lib/persona/labels.js.map +1 -1
- package/dist/lib/query/operations-query.d.ts +1 -1
- package/dist/lib/query/operations-query.js +1 -1
- package/dist/lib/query/operations-query.js.map +1 -1
- package/dist/lib/references/reference-model.js +1 -1
- package/dist/lib/references/reference-model.js.map +1 -1
- package/dist/lib/references/references-store.js +1 -1
- package/dist/lib/references/references-store.js.map +1 -1
- package/dist/lib/workspace/index.d.ts +14 -11
- package/dist/lib/workspace/index.js +20 -24
- package/dist/lib/workspace/index.js.map +1 -1
- package/dist/mount.d.ts +28 -0
- package/dist/mount.js +27 -0
- package/dist/mount.js.map +1 -0
- package/dist/router/root.js +10 -10
- package/dist/router/root.js.map +1 -1
- package/dist/ui/badge.d.ts +1 -1
- package/dist/ui/button.d.ts +2 -2
- package/package.json +15 -11
- package/dist/components/cms/cms-composition-validators.d.ts +0 -16
- package/dist/components/cms/cms-composition-validators.js +0 -133
- package/dist/components/cms/cms-composition-validators.js.map +0 -1
- package/dist/components/cms/publish-clean.d.ts +0 -28
- package/dist/components/cms/publish-clean.js +0 -38
- package/dist/components/cms/publish-clean.js.map +0 -1
- package/dist/components/redirects/redirects-collection-view-source.d.ts +0 -53
- package/dist/components/redirects/redirects-collection-view-source.js +0 -146
- package/dist/components/redirects/redirects-collection-view-source.js.map +0 -1
- package/dist/components/redirects/redirects-module.d.ts +0 -21
- package/dist/components/redirects/redirects-module.js +0 -40
- package/dist/components/redirects/redirects-module.js.map +0 -1
- package/dist/components/redirects/redirects-store.d.ts +0 -68
- package/dist/components/redirects/redirects-store.js +0 -132
- package/dist/components/redirects/redirects-store.js.map +0 -1
- package/dist/lib/assets/asset-placements.d.ts +0 -28
- package/dist/lib/assets/asset-placements.js +0 -83
- package/dist/lib/assets/asset-placements.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rich-text-translate.js","sourceRoot":"","sources":["../../../src/lib/ai/rich-text-translate.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Structure-preserving rich-text translation (EC-332).\n *\n * The load-bearing invariant: **the model never emits node structure.** Each\n * translatable block's inline content is serialized to a single plain string in which\n * every text run is wrapped in an opaque placeholder token (`[[s{k}]]…[[/s{k}]]`) and\n * every non-text inline atom (image, embed, componentEmbed, hardBreak, mention, any\n * unknown inline node) is a self-closing token (`[[a{k}]]`). The model translates ONLY\n * the human-readable text between tokens; on reconstruction each token is mapped back to\n * the ORIGINAL stored mark stack / atom node — byte-for-byte — so marks, links, internal\n * `attrs.ref` references, and embeds survive by construction. Only `text` leaves change.\n *\n * This ports the Sanity precedent's `blockToMarkdown` / `markdownToChildren` moves to\n * Elytra's ProseMirror storage, but uses opaque placeholder tokens rather than literal\n * markdown: Elytra marks (`strike`/`underline`/`link` + its `attrs.ref` envelope) do not\n * round-trip through markdown losslessly, and reference-index integrity (EC-286) requires\n * the `ref` bytes to be preserved exactly — which token-indexed restoration guarantees.\n *\n * Robust against a misbehaving translator: if a translated block's tokens don't parse\n * back to a coherent structure, the ORIGINAL block is kept unchanged (never a broken\n * tree). Determinism: given a deterministic segment transform the output is identical\n * every run. See `rich-text-translate.test.ts` for the round-trip property tests.\n */\nimport { createRichTextValue, parseRichTextValue } from '@elytracms/core/rich-text'\nimport type {\n RichTextDoc,\n RichTextMark,\n RichTextNode,\n RichTextValue,\n} from '@elytracms/core/rich-text'\n\n// --- Token vocabulary -------------------------------------------------------------\n\nconst spanOpen = (k: number): string => `[[s${k}]]`\nconst spanClose = (k: number): string => `[[/s${k}]]`\nconst atomToken = (k: number): string => `[[a${k}]]`\n\n/** Matches every placeholder token: open/close spans and atoms. */\nconst TOKEN_RE = /\\[\\[(\\/?)([sa])(\\d+)\\]\\]/g\n\n/** True when a string contains any placeholder token (non-global; no shared state). */\nfunction containsToken(value: string): boolean {\n return /\\[\\[\\/?[sa]\\d+\\]\\]/.test(value)\n}\n\n/** Key-order-insensitive canonical JSON for de-duplicating identical mark stacks. */\nfunction canonicalJson(value: unknown): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null'\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`)\n return `{${entries.join(',')}}`\n}\n\n// --- Block-unit model -------------------------------------------------------------\n\n/** The per-block side tables the rebuild uses to restore marks + atoms verbatim. */\ninterface BlockTables {\n /** Path (content-index chain) to the text-bearing node within the doc. */\n path: number[]\n /** De-duplicated mark stacks; a span token's index selects one. */\n markStacks: RichTextMark[][]\n /** Inline atom nodes (image/embed/componentEmbed/hardBreak/…), preserved verbatim. */\n atoms: RichTextNode[]\n}\n\n/** One translatable rich-text block: an id stable within the field + its encoded text. */\nexport interface RichTextBlockUnit {\n /** Stable within the field (the block's content-index path, dot-joined). */\n id: string\n /** The encoded translatable string (text runs + placeholder tokens). */\n text: string\n}\n\n/** The extraction plan: the original value + every block's restoration tables. */\nexport interface RichTextPlan {\n value: RichTextValue\n blocks: Map<string, BlockTables>\n}\n\n/** True when a node is a text leaf (`{ type: 'text', text }`). */\nfunction isTextNode(node: RichTextNode): boolean {\n return node.type === 'text' && typeof node.text === 'string'\n}\n\n/** True when a node's direct children include a text leaf (⇒ it is an inline/text block). */\nfunction hasDirectText(node: RichTextNode): boolean {\n return Array.isArray(node.content) && node.content.some(isTextNode)\n}\n\n/** Encode one text-bearing block's inline content into a token string + tables. */\nfunction encodeBlock(node: RichTextNode): { encoded: string; markStacks: RichTextMark[][]; atoms: RichTextNode[] } {\n const markStacks: RichTextMark[][] = []\n const markIndex = new Map<string, number>()\n const atoms: RichTextNode[] = []\n const stackKey = (marks: RichTextMark[] | undefined): number => {\n const key = canonicalJson(marks ?? [])\n const existing = markIndex.get(key)\n if (existing !== undefined) return existing\n const idx = markStacks.length\n markStacks.push(marks ? marks.map((m) => ({ ...m })) : [])\n markIndex.set(key, idx)\n return idx\n }\n const parts: string[] = []\n for (const child of node.content ?? []) {\n if (isTextNode(child)) {\n const k = stackKey(child.marks)\n parts.push(`${spanOpen(k)}${child.text ?? ''}${spanClose(k)}`)\n } else {\n const k = atoms.length\n atoms.push(child)\n parts.push(atomToken(k))\n }\n }\n return { encoded: parts.join(''), markStacks, atoms }\n}\n\n/** True when an encoded block holds at least one non-whitespace translatable segment. */\nfunction hasTranslatableText(encoded: string): boolean {\n return encoded.replace(TOKEN_RE, '').trim().length > 0\n}\n\n/**\n * Walk a rich-text value and extract its translatable blocks. Deterministic; never\n * throws. A value that is not parseable rich text yields no units (and a plan whose\n * rebuild is a no-op). Code blocks are skipped (code is not prose).\n */\nexport function extractRichTextUnits(raw: unknown): { units: RichTextBlockUnit[]; plan: RichTextPlan } {\n const parsed = parseRichTextValue(raw)\n const blocks = new Map<string, BlockTables>()\n const units: RichTextBlockUnit[] = []\n if (!parsed.ok) {\n return { units, plan: { value: { version: 2, content: [] }, blocks } }\n }\n const value = parsed.value\n\n const walk = (node: RichTextNode, path: number[]): void => {\n if (node.type === 'codeBlock') return\n if (hasDirectText(node)) {\n const { encoded, markStacks, atoms } = encodeBlock(node)\n if (hasTranslatableText(encoded)) {\n const id = path.join('.')\n blocks.set(id, { path, markStacks, atoms })\n units.push({ id, text: encoded })\n }\n return\n }\n node.content?.forEach((child, i) => walk(child, [...path, i]))\n }\n\n (value.content as RichTextNode[]).forEach((node, i) => walk(node, [i]))\n return { units, plan: { value, blocks } }\n}\n\n// --- Reconstruction ---------------------------------------------------------------\n\n/**\n * Parse an encoded (translated) block string back into inline nodes, or `null` on any\n * structural mismatch. Strict on purpose: the block is only rewritten when the translated\n * string is token-faithful — every atom token present exactly once, every span opened and\n * closed, and no non-whitespace text stranded outside a span (which would mean a dropped\n * formatting/reference token). Anything else keeps the ORIGINAL block, so an internal\n * link, image ref, or embed can never be silently lost to a garbled translation.\n */\nfunction decodeBlock(translated: string, tables: BlockTables): RichTextNode[] | null {\n const out: RichTextNode[] = []\n const atomsUsed = new Set<number>()\n let cursor = 0\n const pushText = (text: string, marks: RichTextMark[]): void => {\n if (text.length === 0) return\n out.push(marks.length > 0 ? { type: 'text', text, marks: marks.map((m) => ({ ...m })) } : { type: 'text', text })\n }\n const re = new RegExp(TOKEN_RE.source, 'g')\n let match: RegExpExecArray | null\n while ((match = re.exec(translated)) !== null) {\n const [token, closing, kind, digits] = match\n const index = Number(digits)\n const before = translated.slice(cursor, match.index)\n // Only whitespace may sit between/around tokens (a translator's added spacing);\n // stranded words mean a formatting/reference token was dropped → reject.\n if (before.trim().length > 0) return null\n if (kind === 'a') {\n const atom = tables.atoms[index]\n if (!atom || atomsUsed.has(index)) return null\n atomsUsed.add(index)\n out.push(atom)\n cursor = match.index + token.length\n continue\n }\n // kind === 's'\n if (closing === '/') return null // an unmatched close — garbled structure\n const marks = tables.markStacks[index]\n if (!marks) return null\n const close = spanClose(index)\n const closeAt = translated.indexOf(close, re.lastIndex)\n if (closeAt === -1) return null\n const inner = translated.slice(re.lastIndex, closeAt)\n // Inner must not itself contain tokens (spans never nest in our encoding).\n if (containsToken(inner)) return null\n pushText(inner, marks)\n cursor = closeAt + close.length\n re.lastIndex = cursor\n }\n if (translated.slice(cursor).trim().length > 0) return null\n // Every atom the block carried (image/embed/ref) must survive the translation.\n if (atomsUsed.size !== tables.atoms.length) return null\n return out\n}\n\n/** Deep-clone a node array and replace the inline content at `path` with `content`. */\nfunction replaceAtPath(nodes: RichTextNode[], path: number[], content: RichTextNode[]): RichTextNode[] {\n const [head, ...rest] = path\n return nodes.map((node, i) => {\n if (i !== head) return node\n if (rest.length === 0) return { ...node, content }\n return { ...node, content: replaceAtPath(node.content ?? [], rest, content) }\n })\n}\n\n/**\n * Rebuild a rich-text value from a plan + a per-block translation map. A block absent\n * from `translations` (skipped/aborted) or whose translated tokens don't parse keeps its\n * ORIGINAL content — never a broken structure. Returns a fresh `{ version, doc }` value.\n */\nexport function rebuildRichText(\n plan: RichTextPlan,\n translations: ReadonlyMap<string, string>,\n): RichTextValue {\n let content = (plan.value.content as RichTextNode[]).map((n) => n)\n for (const [id, tables] of plan.blocks) {\n const translated = translations.get(id)\n if (translated === undefined) continue\n const decoded = decodeBlock(translated, tables)\n if (!decoded) continue\n content = replaceAtPath(content, tables.path, decoded)\n }\n const doc: RichTextDoc = { type: 'doc', content }\n return createRichTextValue(doc)\n}\n\n// --- Deterministic segment transform (FakeProvider + tests) -----------------------\n\n/**\n * Apply `fn` to only the human-readable text segments of an encoded block (the runs\n * between placeholder tokens), leaving every token exactly in place. This is how the\n * deterministic FakeProvider \"translates\" a block and how the property tests apply a\n * real text change without ever touching structure. Pure.\n */\nexport function mapEncodedSegments(encoded: string, fn: (segment: string) => string): string {\n const re = new RegExp(TOKEN_RE.source, 'g')\n let out = ''\n let cursor = 0\n let match: RegExpExecArray | null\n while ((match = re.exec(encoded)) !== null) {\n out += fn(encoded.slice(cursor, match.index)) + match[0]\n cursor = match.index + match[0].length\n }\n out += fn(encoded.slice(cursor))\n return out\n}\n"]}
|
|
1
|
+
{"version":3,"file":"rich-text-translate.js","sourceRoot":"","sources":["../../../src/lib/ai/rich-text-translate.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Structure-preserving rich-text translation (EC-332).\n *\n * The load-bearing invariant: **the model never emits node structure.** Each\n * translatable block's inline content is serialized to a single plain string in which\n * every text run is wrapped in an opaque placeholder token (`[[s{k}]]…[[/s{k}]]`) and\n * every non-text inline atom (image, embed, componentEmbed, hardBreak, mention, any\n * unknown inline node) is a self-closing token (`[[a{k}]]`). The model translates ONLY\n * the human-readable text between tokens; on reconstruction each token is mapped back to\n * the ORIGINAL stored mark stack / atom node — byte-for-byte — so marks, links, internal\n * `attrs.ref` references, and embeds survive by construction. Only `text` leaves change.\n *\n * This ports the Sanity precedent's `blockToMarkdown` / `markdownToChildren` moves to\n * Elytra's ProseMirror storage, but uses opaque placeholder tokens rather than literal\n * markdown: Elytra marks (`strike`/`underline`/`link` + its `attrs.ref` envelope) do not\n * round-trip through markdown losslessly, and reference-index integrity (EC-286) requires\n * the `ref` bytes to be preserved exactly — which token-indexed restoration guarantees.\n *\n * Robust against a misbehaving translator: if a translated block's tokens don't parse\n * back to a coherent structure, the ORIGINAL block is kept unchanged (never a broken\n * tree). Determinism: given a deterministic segment transform the output is identical\n * every run. See `rich-text-translate.test.ts` for the round-trip property tests.\n */\nimport { createRichTextValue, parseRichTextValue } from '@elytracms/core/rich-text'\nimport type {\n RichTextDoc,\n RichTextMark,\n RichTextNode,\n RichTextValue,\n} from '@elytracms/core/rich-text'\n\n// --- Token vocabulary -------------------------------------------------------------\n\nconst spanOpen = (k: number): string => `[[s${k}]]`\nconst spanClose = (k: number): string => `[[/s${k}]]`\nconst atomToken = (k: number): string => `[[a${k}]]`\n\n/** Matches every placeholder token: open/close spans and atoms. */\nconst TOKEN_RE = /\\[\\[(\\/?)([sa])(\\d+)\\]\\]/g\n\n/** True when a string contains any placeholder token (non-global; no shared state). */\nfunction containsToken(value: string): boolean {\n return /\\[\\[\\/?[sa]\\d+\\]\\]/.test(value)\n}\n\n/** Key-order-insensitive canonical JSON for de-duplicating identical mark stacks. */\nfunction canonicalJson(value: unknown): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null'\n if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`)\n return `{${entries.join(',')}}`\n}\n\n// --- Block-unit model -------------------------------------------------------------\n\n/** The per-block side tables the rebuild uses to restore marks + atoms verbatim. */\ninterface BlockTables {\n /** Path (content-index chain) to the text-bearing node within the doc. */\n path: number[]\n /** De-duplicated mark stacks; a span token's index selects one. */\n markStacks: RichTextMark[][]\n /** Inline atom nodes (image/embed/componentEmbed/hardBreak/…), preserved verbatim. */\n atoms: RichTextNode[]\n}\n\n/** One translatable rich-text block: an id stable within the field + its encoded text. */\nexport interface RichTextBlockUnit {\n /** Stable within the field (the block's content-index path, dot-joined). */\n id: string\n /** The encoded translatable string (text runs + placeholder tokens). */\n text: string\n}\n\n/** The extraction plan: the original value + every block's restoration tables. */\nexport interface RichTextPlan {\n value: RichTextValue\n blocks: Map<string, BlockTables>\n}\n\n/** True when a node is a text leaf (`{ type: 'text', text }`). */\nfunction isTextNode(node: RichTextNode): boolean {\n return node.type === 'text' && typeof node.text === 'string'\n}\n\n/** True when a node's direct children include a text leaf (⇒ it is an inline/text block). */\nfunction hasDirectText(node: RichTextNode): boolean {\n return Array.isArray(node.content) && node.content.some(isTextNode)\n}\n\n/** Encode one text-bearing block's inline content into a token string + tables. */\nfunction encodeBlock(node: RichTextNode): { encoded: string; markStacks: RichTextMark[][]; atoms: RichTextNode[] } {\n const markStacks: RichTextMark[][] = []\n const markIndex = new Map<string, number>()\n const atoms: RichTextNode[] = []\n const stackKey = (marks: RichTextMark[] | undefined): number => {\n const key = canonicalJson(marks ?? [])\n const existing = markIndex.get(key)\n if (existing !== undefined) return existing\n const idx = markStacks.length\n markStacks.push(marks ? marks.map((m) => ({ ...m })) : [])\n markIndex.set(key, idx)\n return idx\n }\n const parts: string[] = []\n for (const child of node.content ?? []) {\n if (isTextNode(child)) {\n const k = stackKey(child.marks)\n parts.push(`${spanOpen(k)}${child.text ?? ''}${spanClose(k)}`)\n } else {\n const k = atoms.length\n atoms.push(child)\n parts.push(atomToken(k))\n }\n }\n return { encoded: parts.join(''), markStacks, atoms }\n}\n\n/** True when an encoded block holds at least one non-whitespace translatable segment. */\nfunction hasTranslatableText(encoded: string): boolean {\n return encoded.replace(TOKEN_RE, '').trim().length > 0\n}\n\n/**\n * Walk a rich-text value — or any `{ version: 2, content }` sequence, a `blocks` value's\n * typography included — and extract its translatable blocks. Deterministic; never\n * throws. A value that is not a readable sequence yields no units, a plan whose rebuild\n * is a no-op, and `readable: false` — the caller REPORTS it (EC-593: translate never\n * skips silently). Code blocks are skipped (code is not prose). Component nodes are not\n * text here: their props are positions of the value traversal, translated by the caller.\n *\n * The walk reads the STORED value once the parse proved it readable, so every node the\n * rebuild does not touch — a component node, an atom — comes back byte-for-byte as\n * stored (a parse is a validation, not a copy to write from).\n */\nexport function extractRichTextUnits(raw: unknown): {\n units: RichTextBlockUnit[]\n plan: RichTextPlan\n readable: boolean\n} {\n const parsed = parseRichTextValue(raw)\n const blocks = new Map<string, BlockTables>()\n const units: RichTextBlockUnit[] = []\n if (!parsed.ok) {\n return { units, plan: { value: { version: 2, content: [] }, blocks }, readable: false }\n }\n // A readable value whose `content` the parse defaulted (absent) has nothing stored to keep.\n const value = Array.isArray((raw as { content?: unknown }).content) ? (raw as RichTextValue) : parsed.value\n\n const walk = (node: RichTextNode, path: number[]): void => {\n if (node.type === 'codeBlock') return\n if (hasDirectText(node)) {\n const { encoded, markStacks, atoms } = encodeBlock(node)\n if (hasTranslatableText(encoded)) {\n const id = path.join('.')\n blocks.set(id, { path, markStacks, atoms })\n units.push({ id, text: encoded })\n }\n return\n }\n node.content?.forEach((child, i) => walk(child, [...path, i]))\n }\n\n (value.content as RichTextNode[]).forEach((node, i) => walk(node, [i]))\n return { units, plan: { value, blocks }, readable: true }\n}\n\n// --- Reconstruction ---------------------------------------------------------------\n\n/**\n * Parse an encoded (translated) block string back into inline nodes, or `null` on any\n * structural mismatch. Strict on purpose: the block is only rewritten when the translated\n * string is token-faithful — every atom token present exactly once, every span opened and\n * closed, and no non-whitespace text stranded outside a span (which would mean a dropped\n * formatting/reference token). Anything else keeps the ORIGINAL block, so an internal\n * link, image ref, or embed can never be silently lost to a garbled translation.\n */\nfunction decodeBlock(translated: string, tables: BlockTables): RichTextNode[] | null {\n const out: RichTextNode[] = []\n const atomsUsed = new Set<number>()\n let cursor = 0\n const pushText = (text: string, marks: RichTextMark[]): void => {\n if (text.length === 0) return\n out.push(marks.length > 0 ? { type: 'text', text, marks: marks.map((m) => ({ ...m })) } : { type: 'text', text })\n }\n const re = new RegExp(TOKEN_RE.source, 'g')\n let match: RegExpExecArray | null\n while ((match = re.exec(translated)) !== null) {\n const [token, closing, kind, digits] = match\n const index = Number(digits)\n const before = translated.slice(cursor, match.index)\n // Only whitespace may sit between/around tokens (a translator's added spacing);\n // stranded words mean a formatting/reference token was dropped → reject.\n if (before.trim().length > 0) return null\n if (kind === 'a') {\n const atom = tables.atoms[index]\n if (!atom || atomsUsed.has(index)) return null\n atomsUsed.add(index)\n out.push(atom)\n cursor = match.index + token.length\n continue\n }\n // kind === 's'\n if (closing === '/') return null // an unmatched close — garbled structure\n const marks = tables.markStacks[index]\n if (!marks) return null\n const close = spanClose(index)\n const closeAt = translated.indexOf(close, re.lastIndex)\n if (closeAt === -1) return null\n const inner = translated.slice(re.lastIndex, closeAt)\n // Inner must not itself contain tokens (spans never nest in our encoding).\n if (containsToken(inner)) return null\n pushText(inner, marks)\n cursor = closeAt + close.length\n re.lastIndex = cursor\n }\n if (translated.slice(cursor).trim().length > 0) return null\n // Every atom the block carried (image/embed/ref) must survive the translation.\n if (atomsUsed.size !== tables.atoms.length) return null\n return out\n}\n\n/** Deep-clone a node array and replace the inline content at `path` with `content`. */\nfunction replaceAtPath(nodes: RichTextNode[], path: number[], content: RichTextNode[]): RichTextNode[] {\n const [head, ...rest] = path\n return nodes.map((node, i) => {\n if (i !== head) return node\n if (rest.length === 0) return { ...node, content }\n return { ...node, content: replaceAtPath(node.content ?? [], rest, content) }\n })\n}\n\n/** {@link rebuildRichTextReport}'s outcome: the value, and which blocks did and did not apply. */\nexport interface RichTextRebuild {\n value: RichTextValue\n /** Block ids whose translation was applied. */\n applied: string[]\n /**\n * Block ids whose translation came back but did not decode token-faithfully — the\n * ORIGINAL block was kept (never a broken tree), so it is untranslated: EC-593 makes\n * the caller report it rather than count it as done.\n */\n rejected: string[]\n}\n\n/**\n * Rebuild a rich-text value from a plan + a per-block translation map. A block absent\n * from `translations` (skipped/aborted) or whose translated tokens don't parse keeps its\n * ORIGINAL content — never a broken structure. Returns a fresh `{ version: 2, content }`\n * value, and the blocks it applied and rejected.\n */\nexport function rebuildRichTextReport(\n plan: RichTextPlan,\n translations: ReadonlyMap<string, string>,\n): RichTextRebuild {\n let content = (plan.value.content as RichTextNode[]).map((n) => n)\n const applied: string[] = []\n const rejected: string[] = []\n for (const [id, tables] of plan.blocks) {\n const translated = translations.get(id)\n if (translated === undefined) continue\n const decoded = decodeBlock(translated, tables)\n if (!decoded) {\n rejected.push(id)\n continue\n }\n applied.push(id)\n content = replaceAtPath(content, tables.path, decoded)\n }\n const doc: RichTextDoc = { type: 'doc', content }\n return { value: createRichTextValue(doc), applied, rejected }\n}\n\n/** {@link rebuildRichTextReport}'s value alone. */\nexport function rebuildRichText(\n plan: RichTextPlan,\n translations: ReadonlyMap<string, string>,\n): RichTextValue {\n return rebuildRichTextReport(plan, translations).value\n}\n\n// --- Deterministic segment transform (FakeProvider + tests) -----------------------\n\n/**\n * Apply `fn` to only the human-readable text segments of an encoded block (the runs\n * between placeholder tokens), leaving every token exactly in place. This is how the\n * deterministic FakeProvider \"translates\" a block and how the property tests apply a\n * real text change without ever touching structure. Pure.\n */\nexport function mapEncodedSegments(encoded: string, fn: (segment: string) => string): string {\n const re = new RegExp(TOKEN_RE.source, 'g')\n let out = ''\n let cursor = 0\n let match: RegExpExecArray | null\n while ((match = re.exec(encoded)) !== null) {\n out += fn(encoded.slice(cursor, match.index)) + match[0]\n cursor = match.index + match[0].length\n }\n out += fn(encoded.slice(cursor))\n return out\n}\n"]}
|
package/dist/lib/ai/shape.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CollectionDef, JsonSchema } from '@elytracms/core/cms-core';
|
|
2
|
-
import type {
|
|
3
|
-
/** A relation field's resolved candidate set
|
|
2
|
+
import type { WriterLeash } from './types.js';
|
|
3
|
+
/** A top-level relation field's resolved candidate set (the enum the model chose from). */
|
|
4
4
|
export interface TargetRelationField {
|
|
5
5
|
fieldName: string;
|
|
6
6
|
/** The relation's target collection id. */
|
|
@@ -9,7 +9,7 @@ export interface TargetRelationField {
|
|
|
9
9
|
/** The allowed candidate ids (the enum the model chose from). */
|
|
10
10
|
candidateIds: string[];
|
|
11
11
|
}
|
|
12
|
-
/**
|
|
12
|
+
/** A top-level asset field's resolved candidate set (EC-381). */
|
|
13
13
|
export interface TargetAssetField {
|
|
14
14
|
fieldName: string;
|
|
15
15
|
multiple: boolean;
|
|
@@ -22,26 +22,26 @@ export interface TargetShape {
|
|
|
22
22
|
schema: JsonSchema;
|
|
23
23
|
/** Field names removed by `ai.exclude` (untouchable). */
|
|
24
24
|
excluded: string[];
|
|
25
|
+
/** Field names omitted because a writer never writes them — a composition (4c). */
|
|
26
|
+
compositions: string[];
|
|
25
27
|
/** Field names whose values route under `localized[locale]`. */
|
|
26
28
|
localizedFields: string[];
|
|
27
|
-
/**
|
|
29
|
+
/** Top-level relation fields with their candidate enums. */
|
|
28
30
|
relationFields: TargetRelationField[];
|
|
29
|
-
/**
|
|
31
|
+
/** Top-level asset fields with their candidate enums (EC-381). */
|
|
30
32
|
assetFields: TargetAssetField[];
|
|
31
33
|
}
|
|
32
34
|
export interface BuildTargetShapeInput {
|
|
33
35
|
collection: CollectionDef;
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
/** Asset candidate ids by field — an asset field with none is omitted from the shape (EC-381). */
|
|
37
|
-
assetCandidates?: AssetCandidates;
|
|
36
|
+
/** What the model searched — the id leash at every position (absent → nothing searched). */
|
|
37
|
+
leash?: WriterLeash;
|
|
38
38
|
/** UI locale for resolving field labels into descriptions. */
|
|
39
39
|
locale?: string;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
* Build the leash-aware target shape from a collection +
|
|
43
|
-
* never throws. Reuses `collectionJsonSchema` as the base (its
|
|
44
|
-
* gives every property its writer form (EC-556)
|
|
45
|
-
* excluded fields.
|
|
42
|
+
* Build the leash-aware target shape from a collection + what the model searched.
|
|
43
|
+
* Deterministic; never throws. Reuses `collectionJsonSchema` as the base (its
|
|
44
|
+
* `required` list), then gives every property its writer form (EC-556) — id enums at
|
|
45
|
+
* every depth (EC-593) — and strips excluded fields and compositions.
|
|
46
46
|
*/
|
|
47
47
|
export declare function buildTargetShape(input: BuildTargetShapeInput): TargetShape;
|
package/dist/lib/ai/shape.js
CHANGED
|
@@ -2,24 +2,28 @@
|
|
|
2
2
|
* Target-shape derivation for `applyContentWithShape` (EC-331).
|
|
3
3
|
*
|
|
4
4
|
* Reuses the existing collection→JSON-Schema derivation (`collectionJsonSchema`,
|
|
5
|
-
* cms-core) rather than writing a new one, then applies the
|
|
6
|
-
*
|
|
5
|
+
* cms-core) rather than writing a new one, then applies the leashes the AI engine
|
|
6
|
+
* adds on top:
|
|
7
7
|
*
|
|
8
8
|
* - `ai.exclude` fields are removed entirely (the model never sees them → cannot
|
|
9
9
|
* write them).
|
|
10
|
-
* - relation
|
|
11
|
-
* when
|
|
12
|
-
* as valid structured output.
|
|
13
|
-
* - asset
|
|
14
|
-
* omitted when none
|
|
15
|
-
* proposed asset id must be a real, `listAssets`-returned
|
|
16
|
-
*
|
|
17
|
-
*
|
|
10
|
+
* - relation ids become an ENUM over the searched candidates of the relation's
|
|
11
|
+
* target (or the position is omitted when none were searched) — so a hallucinated
|
|
12
|
+
* relation id is not representable as valid structured output.
|
|
13
|
+
* - asset ids become an ENUM over the searched library assets that fit the
|
|
14
|
+
* position's `accept` (or the position is omitted when none fit) — EC-381, the SAME
|
|
15
|
+
* leash as relations: a proposed asset id must be a real, `listAssets`-returned
|
|
16
|
+
* library asset (empty beats wrong; the assistant never generates or uploads).
|
|
17
|
+
* EC-593: both leashes hold at EVERY depth — a relation in an object, an asset in a
|
|
18
|
+
* repeater item — not only on a top-level field (`writerFieldSchema(field, leash)`).
|
|
18
19
|
* - rich-text fields are MARKDOWN STRINGS (EC-379): the description names the
|
|
19
|
-
* allowed subset, and the engine converts md → the `{ version,
|
|
20
|
-
*
|
|
21
|
-
* EMBEDS only — markdown cannot express an embed, so the leash is
|
|
22
|
-
* construction (and still enforced downstream by `validateRichTextEmbeds`).
|
|
20
|
+
* allowed subset, and the engine converts md → the `{ version: 2, content }` storage
|
|
21
|
+
* value at proposal-build time (`writer-form.ts`). The field's `allow` config leashes
|
|
22
|
+
* component EMBEDS only — markdown cannot express an embed, so the leash is
|
|
23
|
+
* satisfied by construction (and still enforced downstream by `validateRichTextEmbeds`).
|
|
24
|
+
* - a composition (`blocks`) has NO writer form (ratified 4c, EC-593): it is omitted,
|
|
25
|
+
* and the schema's own description says so — the omission is visible, never a field
|
|
26
|
+
* the model simply never hears of.
|
|
23
27
|
*
|
|
24
28
|
* Localized fields stay flat in the shape (the model authors one locale at a time);
|
|
25
29
|
* the engine routes their values under `localized[locale]`.
|
|
@@ -46,12 +50,12 @@
|
|
|
46
50
|
* EC-556 — the projection is VALIDATOR-facing (the value at rest: asset/relation
|
|
47
51
|
* envelopes, the v2 rich-text object). Every property here is its WRITER form
|
|
48
52
|
* (`writerFieldSchema`), derived from that projection at every depth — a bare asset
|
|
49
|
-
* id, a
|
|
50
|
-
*
|
|
51
|
-
* top-level relation/asset id enums (EC-381) override the writer form there.
|
|
53
|
+
* id, a relation id, a markdown string — so an asset, relation or rich-text SUBFIELD of
|
|
54
|
+
* an `object` is told the same form as a top-level one.
|
|
52
55
|
*/
|
|
53
56
|
import { collectionJsonSchema, resolveLabel } from '@elytracms/core/cms-core';
|
|
54
57
|
import { constraintSentence, toModelFacingSchema, writerFieldSchema } from './constraints.js';
|
|
58
|
+
import { NO_SEARCHED_IDS } from './leash.js';
|
|
55
59
|
/**
|
|
56
60
|
* A field's model-facing description: resolved label + optional `ai.hint` + the
|
|
57
61
|
* constraint clause (EC-473). The constraints are stated in PROSE because that is
|
|
@@ -66,17 +70,19 @@ function describe(field, locale) {
|
|
|
66
70
|
return parts.length > 0 ? parts.join(' — ') : undefined;
|
|
67
71
|
}
|
|
68
72
|
/**
|
|
69
|
-
* Build the leash-aware target shape from a collection +
|
|
70
|
-
* never throws. Reuses `collectionJsonSchema` as the base (its
|
|
71
|
-
* gives every property its writer form (EC-556)
|
|
72
|
-
* excluded fields.
|
|
73
|
+
* Build the leash-aware target shape from a collection + what the model searched.
|
|
74
|
+
* Deterministic; never throws. Reuses `collectionJsonSchema` as the base (its
|
|
75
|
+
* `required` list), then gives every property its writer form (EC-556) — id enums at
|
|
76
|
+
* every depth (EC-593) — and strips excluded fields and compositions.
|
|
73
77
|
*/
|
|
74
78
|
export function buildTargetShape(input) {
|
|
79
|
+
const leash = input.leash ?? NO_SEARCHED_IDS;
|
|
75
80
|
const base = collectionJsonSchema(input.collection);
|
|
76
81
|
const properties = {
|
|
77
82
|
...(base.properties ?? {}),
|
|
78
83
|
};
|
|
79
84
|
const excluded = [];
|
|
85
|
+
const compositions = [];
|
|
80
86
|
const localizedFields = [];
|
|
81
87
|
const relationFields = [];
|
|
82
88
|
const assetFields = [];
|
|
@@ -88,82 +94,46 @@ export function buildTargetShape(input) {
|
|
|
88
94
|
delete properties[field.name];
|
|
89
95
|
continue;
|
|
90
96
|
}
|
|
97
|
+
// EC-556: the writer form at every depth (rich text → markdown, EC-379 / D2;
|
|
98
|
+
// nested asset / relation → the bare forms the write-time lens wraps); EC-593:
|
|
99
|
+
// every id an enum over what was searched for that position.
|
|
100
|
+
const written = writerFieldSchema(field, leash);
|
|
101
|
+
if (written === undefined) {
|
|
102
|
+
// No writer form: a composition (4c), or a relation / asset with nothing
|
|
103
|
+
// searched for it — the model cannot emit a value, so a hallucinated id is
|
|
104
|
+
// structurally impossible (empty beats wrong).
|
|
105
|
+
if (field.type === 'blocks')
|
|
106
|
+
compositions.push(field.name);
|
|
107
|
+
delete properties[field.name];
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
91
110
|
if (field.type === 'relation') {
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
// No candidate ids ⇒ omit the field: the model cannot emit a relation value
|
|
95
|
-
// at all, so a hallucinated id is structurally impossible.
|
|
96
|
-
delete properties[field.name];
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
const ids = candidates.map((c) => c.id);
|
|
100
|
-
const label = candidates
|
|
101
|
-
.map((c) => (c.label ? `${c.id} (${c.label})` : c.id))
|
|
102
|
-
.join(', ');
|
|
103
|
-
const idSchema = {
|
|
104
|
-
type: 'string',
|
|
105
|
-
enum: ids,
|
|
106
|
-
description: `Choose one of: ${label}`,
|
|
107
|
-
};
|
|
108
|
-
properties[field.name] = field.multiple ? { type: 'array', items: idSchema } : idSchema;
|
|
109
|
-
relationFields.push({
|
|
110
|
-
fieldName: field.name,
|
|
111
|
-
target: field.target,
|
|
112
|
-
multiple: field.multiple,
|
|
113
|
-
candidateIds: ids,
|
|
114
|
-
});
|
|
111
|
+
const candidateIds = leash.relationCandidates(field.target).map((c) => c.id);
|
|
112
|
+
relationFields.push({ fieldName: field.name, target: field.target, multiple: field.multiple, candidateIds });
|
|
115
113
|
}
|
|
116
114
|
else if (field.type === 'asset') {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// searched assets ⇒ omit the field, so the proposal simply leaves it empty
|
|
120
|
-
// (an invented / to-be-uploaded id is structurally unrepresentable).
|
|
121
|
-
const candidates = input.assetCandidates?.[field.name] ?? [];
|
|
122
|
-
if (candidates.length === 0) {
|
|
123
|
-
delete properties[field.name];
|
|
124
|
-
continue;
|
|
125
|
-
}
|
|
126
|
-
const ids = candidates.map((c) => c.id);
|
|
127
|
-
const label = candidates
|
|
128
|
-
.map((c) => (c.label ? `${c.id} (${c.label})` : c.id))
|
|
129
|
-
.join(', ');
|
|
130
|
-
const idSchema = {
|
|
131
|
-
type: 'string',
|
|
132
|
-
enum: ids,
|
|
133
|
-
description: `Choose an EXISTING library asset id, one of: ${label}. Leave empty if none fits.`,
|
|
134
|
-
};
|
|
135
|
-
properties[field.name] = field.multiple ? { type: 'array', items: idSchema } : idSchema;
|
|
136
|
-
assetFields.push({ fieldName: field.name, multiple: field.multiple, candidateIds: ids });
|
|
137
|
-
}
|
|
138
|
-
else {
|
|
139
|
-
// EC-556: the writer form at every depth (rich text → markdown, EC-379 / D2;
|
|
140
|
-
// nested asset / relation → the bare forms the write-time lens wraps).
|
|
141
|
-
properties[field.name] = writerFieldSchema(field);
|
|
115
|
+
const candidateIds = leash.assetCandidates(field).map((c) => c.id);
|
|
116
|
+
assetFields.push({ fieldName: field.name, multiple: field.multiple, candidateIds });
|
|
142
117
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
? desc
|
|
161
|
-
: constraints !== undefined
|
|
162
|
-
? `${existing} — ${constraints}`
|
|
163
|
-
: existing;
|
|
164
|
-
}
|
|
165
|
-
properties[field.name] = next;
|
|
118
|
+
// EC-473: a `pattern` next to a `format` is 200+ characters that teach the
|
|
119
|
+
// model nothing — the format (and the prose constraints below) say it better.
|
|
120
|
+
const stripped = toModelFacingSchema(written);
|
|
121
|
+
const desc = describe(field, input.locale);
|
|
122
|
+
// Preserve an existing description (e.g. relation enum / rich-text note), but
|
|
123
|
+
// append the constraint clause to it — the enum leash and the value rules are
|
|
124
|
+
// different facts and the model needs both.
|
|
125
|
+
const next = stripped === written ? { ...written } : { ...stripped };
|
|
126
|
+
if (desc !== undefined) {
|
|
127
|
+
const existing = typeof next.description === 'string' ? next.description : undefined;
|
|
128
|
+
const constraints = constraintSentence(field);
|
|
129
|
+
next.description =
|
|
130
|
+
existing === undefined
|
|
131
|
+
? desc
|
|
132
|
+
: constraints !== undefined
|
|
133
|
+
? `${existing} — ${constraints}`
|
|
134
|
+
: existing;
|
|
166
135
|
}
|
|
136
|
+
properties[field.name] = next;
|
|
167
137
|
}
|
|
168
138
|
const requiredIn = Array.isArray(base.required) ? base.required : [];
|
|
169
139
|
const required = requiredIn.filter((n) => !excluded.includes(n) && properties[n] !== undefined);
|
|
@@ -172,7 +142,13 @@ export function buildTargetShape(input) {
|
|
|
172
142
|
properties,
|
|
173
143
|
additionalProperties: false,
|
|
174
144
|
...(required.length > 0 ? { required } : {}),
|
|
145
|
+
// 4c (EC-593): the omission is said, not silent.
|
|
146
|
+
...(compositions.length > 0
|
|
147
|
+
? {
|
|
148
|
+
description: `Not part of this object: ${compositions.join(', ')} — page-builder composition${compositions.length > 1 ? 's' : ''} whose components are added, moved and removed on the canvas, never by the assistant.`,
|
|
149
|
+
}
|
|
150
|
+
: {}),
|
|
175
151
|
};
|
|
176
|
-
return { schema, excluded, localizedFields, relationFields, assetFields };
|
|
152
|
+
return { schema, excluded, compositions, localizedFields, relationFields, assetFields };
|
|
177
153
|
}
|
|
178
154
|
//# sourceMappingURL=shape.js.map
|
package/dist/lib/ai/shape.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shape.js","sourceRoot":"","sources":["../../../src/lib/ai/shape.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Target-shape derivation for `applyContentWithShape` (EC-331).\n *\n * Reuses the existing collection→JSON-Schema derivation (`collectionJsonSchema`,\n * cms-core) rather than writing a new one, then applies the three leashes the AI\n * engine adds on top:\n *\n * - `ai.exclude` fields are removed entirely (the model never sees them → cannot\n * write them).\n * - relation fields become an ENUM over the provided candidate ids (or are omitted\n * when no candidates exist) — so a hallucinated relation id is not representable\n * as valid structured output.\n * - asset/image fields become an ENUM over the searched library asset ids (or are\n * omitted when none were searched) — EC-381, the SAME leash as relations: a\n * proposed asset id must be a real, `listAssets`-returned library asset, and a\n * create shape with no searched assets simply has no asset property (empty beats\n * wrong; the assistant never generates or uploads).\n * - rich-text fields are MARKDOWN STRINGS (EC-379): the description names the\n * allowed subset, and the engine converts md → the `{ version, doc }` storage\n * envelope at proposal-build time. The field's `allow` config leashes component\n * EMBEDS only — markdown cannot express an embed, so the leash is satisfied by\n * construction (and still enforced downstream by `validateRichTextEmbeds`).\n *\n * Localized fields stay flat in the shape (the model authors one locale at a time);\n * the engine routes their values under `localized[locale]`.\n *\n * EC-473 — the shape now consumes the ONE constraint projection, so a rule declared\n * in config actually reaches the model:\n *\n * - `collectionJsonSchema` merges declarative rules with the fragment derived from\n * a schema-form `validation.validate`, so `{ min: 2 }` and `z.string().min(2)`\n * arrive identically (and a declared DATE RANGE arrives at all — it previously\n * reached no consumer whatsoever);\n * - a rich-text field no longer OVERWRITES its constraints: the markdown schema is\n * merged over them instead of replacing them (measured drift, EC-474 §2);\n * - a `pattern` is dropped wherever a `format` explains the same thing (a\n * 200-character email regex teaches a model nothing and costs real tokens);\n * - a field whose projection is knowingly partial (`schemaComplete: false`) SAYS SO\n * in its description rather than presenting the partial rule as the whole rule.\n *\n * EC-505 — one further narrowing of what the model is TOLD (never of what the engine\n * accepts): a `date` property is `type: 'string'` + `format: 'date-time'` rather than\n * the `['string','number']` union the general projection carries, because the model is\n * asked to author an ISO instant (EC-472) and some providers reject a union `type`.\n *\n * EC-556 — the projection is VALIDATOR-facing (the value at rest: asset/relation\n * envelopes, the v2 rich-text object). Every property here is its WRITER form\n * (`writerFieldSchema`), derived from that projection at every depth — a bare asset\n * id, a `{ collection, id }` reference, a markdown string — so an asset, relation or\n * rich-text SUBFIELD of an `object` is told the same form as a top-level one. The\n * top-level relation/asset id enums (EC-381) override the writer form there.\n */\nimport { collectionJsonSchema, resolveLabel } from '@elytracms/core/cms-core'\nimport type { CollectionDef, FieldDef, JsonSchema } from '@elytracms/core/cms-core'\nimport { constraintSentence, toModelFacingSchema, writerFieldSchema } from './constraints'\nimport type { AssetCandidates, RelationCandidates } from './types'\n\n/** A relation field's resolved candidate set — how the engine maps ids → reference envelopes. */\nexport interface TargetRelationField {\n fieldName: string\n /** The relation's target collection id. */\n target: string\n multiple: boolean\n /** The allowed candidate ids (the enum the model chose from). */\n candidateIds: string[]\n}\n\n/** An asset field's resolved candidate set — how the engine maps ids → asset envelopes (EC-381). */\nexport interface TargetAssetField {\n fieldName: string\n multiple: boolean\n /** The allowed library asset ids (the enum the model chose from). */\n candidateIds: string[]\n}\n\n/** The derived target shape + the metadata the engine needs to post-process the output. */\nexport interface TargetShape {\n /** The JSON Schema the structured output must satisfy. */\n schema: JsonSchema\n /** Field names removed by `ai.exclude` (untouchable). */\n excluded: string[]\n /** Field names whose values route under `localized[locale]`. */\n localizedFields: string[]\n /** Relation fields with their candidate enums. */\n relationFields: TargetRelationField[]\n /** Asset fields with their candidate enums (EC-381). */\n assetFields: TargetAssetField[]\n}\n\nexport interface BuildTargetShapeInput {\n collection: CollectionDef\n /** Relation candidate ids by field — a relation field with none is omitted from the shape. */\n candidates?: RelationCandidates\n /** Asset candidate ids by field — an asset field with none is omitted from the shape (EC-381). */\n assetCandidates?: AssetCandidates\n /** UI locale for resolving field labels into descriptions. */\n locale?: string\n}\n\n/**\n * A field's model-facing description: resolved label + optional `ai.hint` + the\n * constraint clause (EC-473). The constraints are stated in PROSE because that is\n * what a model acts on — the same facts also ride as JSON-Schema keywords for the\n * structured-output validator, which is a different reader.\n */\nfunction describe(field: FieldDef, locale?: string): string | undefined {\n const label = resolveLabel(field.form?.label, locale) ?? field.name\n const hint = field.ai?.hint\n const constraints = constraintSentence(field)\n const parts = [label, hint, constraints].filter(\n (p): p is string => typeof p === 'string' && p.length > 0,\n )\n return parts.length > 0 ? parts.join(' — ') : undefined\n}\n\n/**\n * Build the leash-aware target shape from a collection + candidates. Deterministic;\n * never throws. Reuses `collectionJsonSchema` as the base (its `required` list), then\n * gives every property its writer form (EC-556) or its id-enum leash, and strips\n * excluded fields.\n */\nexport function buildTargetShape(input: BuildTargetShapeInput): TargetShape {\n const base = collectionJsonSchema(input.collection)\n const properties: Record<string, JsonSchema> = {\n ...((base.properties as Record<string, JsonSchema>) ?? {}),\n }\n const excluded: string[] = []\n const localizedFields: string[] = []\n const relationFields: TargetRelationField[] = []\n const assetFields: TargetAssetField[] = []\n\n for (const field of input.collection.fields) {\n if (field.localized) localizedFields.push(field.name)\n\n if (field.ai?.exclude) {\n excluded.push(field.name)\n delete properties[field.name]\n continue\n }\n\n if (field.type === 'relation') {\n const candidates = input.candidates?.[field.name] ?? []\n if (candidates.length === 0) {\n // No candidate ids ⇒ omit the field: the model cannot emit a relation value\n // at all, so a hallucinated id is structurally impossible.\n delete properties[field.name]\n continue\n }\n const ids = candidates.map((c) => c.id)\n const label = candidates\n .map((c) => (c.label ? `${c.id} (${c.label})` : c.id))\n .join(', ')\n const idSchema: JsonSchema = {\n type: 'string',\n enum: ids,\n description: `Choose one of: ${label}`,\n }\n properties[field.name] = field.multiple ? { type: 'array', items: idSchema } : idSchema\n relationFields.push({\n fieldName: field.name,\n target: field.target,\n multiple: field.multiple,\n candidateIds: ids,\n })\n } else if (field.type === 'asset') {\n // EC-381 — same leash as relations: an asset field is an enum over the ids\n // the model actually searched in the media library (`listAssets`). No\n // searched assets ⇒ omit the field, so the proposal simply leaves it empty\n // (an invented / to-be-uploaded id is structurally unrepresentable).\n const candidates = input.assetCandidates?.[field.name] ?? []\n if (candidates.length === 0) {\n delete properties[field.name]\n continue\n }\n const ids = candidates.map((c) => c.id)\n const label = candidates\n .map((c) => (c.label ? `${c.id} (${c.label})` : c.id))\n .join(', ')\n const idSchema: JsonSchema = {\n type: 'string',\n enum: ids,\n description: `Choose an EXISTING library asset id, one of: ${label}. Leave empty if none fits.`,\n }\n properties[field.name] = field.multiple ? { type: 'array', items: idSchema } : idSchema\n assetFields.push({ fieldName: field.name, multiple: field.multiple, candidateIds: ids })\n } else {\n // EC-556: the writer form at every depth (rich text → markdown, EC-379 / D2;\n // nested asset / relation → the bare forms the write-time lens wraps).\n properties[field.name] = writerFieldSchema(field)\n }\n\n const prop = properties[field.name]\n if (prop) {\n // EC-473: a `pattern` next to a `format` is 200+ characters that teach the\n // model nothing — the format (and the prose constraints below) say it better.\n // EC-505: and a `date`'s `['string','number']` union becomes the ISO string\n // the model is actually asked to author.\n const stripped = toModelFacingSchema(prop)\n const desc = describe(field, input.locale)\n // Preserve an existing description (e.g. relation enum / rich-text note), but\n // append the constraint clause to it — the enum leash and the value rules are\n // different facts and the model needs both.\n const next: JsonSchema = stripped === prop ? prop : { ...stripped }\n if (desc !== undefined) {\n const existing = typeof next.description === 'string' ? next.description : undefined\n const constraints = constraintSentence(field)\n next.description =\n existing === undefined\n ? desc\n : constraints !== undefined\n ? `${existing} — ${constraints}`\n : existing\n }\n properties[field.name] = next\n }\n }\n\n const requiredIn = Array.isArray(base.required) ? (base.required as string[]) : []\n const required = requiredIn.filter((n) => !excluded.includes(n) && properties[n] !== undefined)\n\n const schema: JsonSchema = {\n type: 'object',\n properties,\n additionalProperties: false,\n ...(required.length > 0 ? { required } : {}),\n }\n return { schema, excluded, localizedFields, relationFields, assetFields }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"shape.js","sourceRoot":"","sources":["../../../src/lib/ai/shape.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Target-shape derivation for `applyContentWithShape` (EC-331).\n *\n * Reuses the existing collection→JSON-Schema derivation (`collectionJsonSchema`,\n * cms-core) rather than writing a new one, then applies the leashes the AI engine\n * adds on top:\n *\n * - `ai.exclude` fields are removed entirely (the model never sees them → cannot\n * write them).\n * - relation ids become an ENUM over the searched candidates of the relation's\n * target (or the position is omitted when none were searched) — so a hallucinated\n * relation id is not representable as valid structured output.\n * - asset ids become an ENUM over the searched library assets that fit the\n * position's `accept` (or the position is omitted when none fit) — EC-381, the SAME\n * leash as relations: a proposed asset id must be a real, `listAssets`-returned\n * library asset (empty beats wrong; the assistant never generates or uploads).\n * EC-593: both leashes hold at EVERY depth — a relation in an object, an asset in a\n * repeater item — not only on a top-level field (`writerFieldSchema(field, leash)`).\n * - rich-text fields are MARKDOWN STRINGS (EC-379): the description names the\n * allowed subset, and the engine converts md → the `{ version: 2, content }` storage\n * value at proposal-build time (`writer-form.ts`). The field's `allow` config leashes\n * component EMBEDS only — markdown cannot express an embed, so the leash is\n * satisfied by construction (and still enforced downstream by `validateRichTextEmbeds`).\n * - a composition (`blocks`) has NO writer form (ratified 4c, EC-593): it is omitted,\n * and the schema's own description says so — the omission is visible, never a field\n * the model simply never hears of.\n *\n * Localized fields stay flat in the shape (the model authors one locale at a time);\n * the engine routes their values under `localized[locale]`.\n *\n * EC-473 — the shape now consumes the ONE constraint projection, so a rule declared\n * in config actually reaches the model:\n *\n * - `collectionJsonSchema` merges declarative rules with the fragment derived from\n * a schema-form `validation.validate`, so `{ min: 2 }` and `z.string().min(2)`\n * arrive identically (and a declared DATE RANGE arrives at all — it previously\n * reached no consumer whatsoever);\n * - a rich-text field no longer OVERWRITES its constraints: the markdown schema is\n * merged over them instead of replacing them (measured drift, EC-474 §2);\n * - a `pattern` is dropped wherever a `format` explains the same thing (a\n * 200-character email regex teaches a model nothing and costs real tokens);\n * - a field whose projection is knowingly partial (`schemaComplete: false`) SAYS SO\n * in its description rather than presenting the partial rule as the whole rule.\n *\n * EC-505 — one further narrowing of what the model is TOLD (never of what the engine\n * accepts): a `date` property is `type: 'string'` + `format: 'date-time'` rather than\n * the `['string','number']` union the general projection carries, because the model is\n * asked to author an ISO instant (EC-472) and some providers reject a union `type`.\n *\n * EC-556 — the projection is VALIDATOR-facing (the value at rest: asset/relation\n * envelopes, the v2 rich-text object). Every property here is its WRITER form\n * (`writerFieldSchema`), derived from that projection at every depth — a bare asset\n * id, a relation id, a markdown string — so an asset, relation or rich-text SUBFIELD of\n * an `object` is told the same form as a top-level one.\n */\nimport { collectionJsonSchema, resolveLabel } from '@elytracms/core/cms-core'\nimport type { CollectionDef, FieldDef, JsonSchema } from '@elytracms/core/cms-core'\nimport { constraintSentence, toModelFacingSchema, writerFieldSchema } from './constraints'\nimport { NO_SEARCHED_IDS } from './leash'\nimport type { WriterLeash } from './types'\n\n/** A top-level relation field's resolved candidate set (the enum the model chose from). */\nexport interface TargetRelationField {\n fieldName: string\n /** The relation's target collection id. */\n target: string\n multiple: boolean\n /** The allowed candidate ids (the enum the model chose from). */\n candidateIds: string[]\n}\n\n/** A top-level asset field's resolved candidate set (EC-381). */\nexport interface TargetAssetField {\n fieldName: string\n multiple: boolean\n /** The allowed library asset ids (the enum the model chose from). */\n candidateIds: string[]\n}\n\n/** The derived target shape + the metadata the engine needs to post-process the output. */\nexport interface TargetShape {\n /** The JSON Schema the structured output must satisfy. */\n schema: JsonSchema\n /** Field names removed by `ai.exclude` (untouchable). */\n excluded: string[]\n /** Field names omitted because a writer never writes them — a composition (4c). */\n compositions: string[]\n /** Field names whose values route under `localized[locale]`. */\n localizedFields: string[]\n /** Top-level relation fields with their candidate enums. */\n relationFields: TargetRelationField[]\n /** Top-level asset fields with their candidate enums (EC-381). */\n assetFields: TargetAssetField[]\n}\n\nexport interface BuildTargetShapeInput {\n collection: CollectionDef\n /** What the model searched — the id leash at every position (absent → nothing searched). */\n leash?: WriterLeash\n /** UI locale for resolving field labels into descriptions. */\n locale?: string\n}\n\n/**\n * A field's model-facing description: resolved label + optional `ai.hint` + the\n * constraint clause (EC-473). The constraints are stated in PROSE because that is\n * what a model acts on — the same facts also ride as JSON-Schema keywords for the\n * structured-output validator, which is a different reader.\n */\nfunction describe(field: FieldDef, locale?: string): string | undefined {\n const label = resolveLabel(field.form?.label, locale) ?? field.name\n const hint = field.ai?.hint\n const constraints = constraintSentence(field)\n const parts = [label, hint, constraints].filter(\n (p): p is string => typeof p === 'string' && p.length > 0,\n )\n return parts.length > 0 ? parts.join(' — ') : undefined\n}\n\n/**\n * Build the leash-aware target shape from a collection + what the model searched.\n * Deterministic; never throws. Reuses `collectionJsonSchema` as the base (its\n * `required` list), then gives every property its writer form (EC-556) — id enums at\n * every depth (EC-593) — and strips excluded fields and compositions.\n */\nexport function buildTargetShape(input: BuildTargetShapeInput): TargetShape {\n const leash = input.leash ?? NO_SEARCHED_IDS\n const base = collectionJsonSchema(input.collection)\n const properties: Record<string, JsonSchema> = {\n ...((base.properties as Record<string, JsonSchema>) ?? {}),\n }\n const excluded: string[] = []\n const compositions: string[] = []\n const localizedFields: string[] = []\n const relationFields: TargetRelationField[] = []\n const assetFields: TargetAssetField[] = []\n\n for (const field of input.collection.fields) {\n if (field.localized) localizedFields.push(field.name)\n\n if (field.ai?.exclude) {\n excluded.push(field.name)\n delete properties[field.name]\n continue\n }\n\n // EC-556: the writer form at every depth (rich text → markdown, EC-379 / D2;\n // nested asset / relation → the bare forms the write-time lens wraps); EC-593:\n // every id an enum over what was searched for that position.\n const written = writerFieldSchema(field, leash)\n if (written === undefined) {\n // No writer form: a composition (4c), or a relation / asset with nothing\n // searched for it — the model cannot emit a value, so a hallucinated id is\n // structurally impossible (empty beats wrong).\n if (field.type === 'blocks') compositions.push(field.name)\n delete properties[field.name]\n continue\n }\n if (field.type === 'relation') {\n const candidateIds = leash.relationCandidates(field.target).map((c) => c.id)\n relationFields.push({ fieldName: field.name, target: field.target, multiple: field.multiple, candidateIds })\n } else if (field.type === 'asset') {\n const candidateIds = leash.assetCandidates(field).map((c) => c.id)\n assetFields.push({ fieldName: field.name, multiple: field.multiple, candidateIds })\n }\n\n // EC-473: a `pattern` next to a `format` is 200+ characters that teach the\n // model nothing — the format (and the prose constraints below) say it better.\n const stripped = toModelFacingSchema(written)\n const desc = describe(field, input.locale)\n // Preserve an existing description (e.g. relation enum / rich-text note), but\n // append the constraint clause to it — the enum leash and the value rules are\n // different facts and the model needs both.\n const next: JsonSchema = stripped === written ? { ...written } : { ...stripped }\n if (desc !== undefined) {\n const existing = typeof next.description === 'string' ? next.description : undefined\n const constraints = constraintSentence(field)\n next.description =\n existing === undefined\n ? desc\n : constraints !== undefined\n ? `${existing} — ${constraints}`\n : existing\n }\n properties[field.name] = next\n }\n\n const requiredIn = Array.isArray(base.required) ? (base.required as string[]) : []\n const required = requiredIn.filter((n) => !excluded.includes(n) && properties[n] !== undefined)\n\n const schema: JsonSchema = {\n type: 'object',\n properties,\n additionalProperties: false,\n ...(required.length > 0 ? { required } : {}),\n // 4c (EC-593): the omission is said, not silent.\n ...(compositions.length > 0\n ? {\n description: `Not part of this object: ${compositions.join(', ')} — page-builder composition${compositions.length > 1 ? 's' : ''} whose components are added, moved and removed on the canvas, never by the assistant.`,\n }\n : {}),\n }\n return { schema, excluded, compositions, localizedFields, relationFields, assetFields }\n}\n"]}
|
package/dist/lib/ai/tools.d.ts
CHANGED
|
@@ -33,25 +33,18 @@
|
|
|
33
33
|
*/
|
|
34
34
|
import { z } from 'zod';
|
|
35
35
|
import type { CmsDocument, CollectionDef, CollectionRegistry, LocaleConfig, ValidateDocumentOptions } from '@elytracms/core/cms-core';
|
|
36
|
-
import type {
|
|
36
|
+
import type { SearchedAsset, SearchedDocument } from './leash.js';
|
|
37
|
+
import type { TranslateSkip } from './translate-document.js';
|
|
37
38
|
import type { ChatProposal } from './chat-state.js';
|
|
38
|
-
import type { AiProvider,
|
|
39
|
+
import type { AiProvider, ResolvedKnowledge, WriterLeash } from './types.js';
|
|
39
40
|
/** A document the model may reference as a relation target (from a `searchDocuments` read). */
|
|
40
|
-
export
|
|
41
|
-
id: string;
|
|
42
|
-
title?: string;
|
|
43
|
-
}
|
|
41
|
+
export type DocumentSearchResult = SearchedDocument;
|
|
44
42
|
/**
|
|
45
43
|
* An asset the model may reference (from a `listAssets` read). EC-588: it carries the
|
|
46
|
-
* two facts a field's `accept` is judged on, so each asset
|
|
47
|
-
* filtered to what that
|
|
44
|
+
* two facts a field's `accept` is judged on, so each asset position's candidates can be
|
|
45
|
+
* filtered to what that position takes — and the model can see what each asset is.
|
|
48
46
|
*/
|
|
49
|
-
export
|
|
50
|
-
id: string;
|
|
51
|
-
title?: string;
|
|
52
|
-
contentType: string;
|
|
53
|
-
filename: string;
|
|
54
|
-
}
|
|
47
|
+
export type AssetSearchResult = SearchedAsset;
|
|
55
48
|
/**
|
|
56
49
|
* The client-side, operations-backed readers the executor calls. The studio binds
|
|
57
50
|
* these to the user's authenticated operations client; tests bind an in-memory fake.
|
|
@@ -201,26 +194,18 @@ export type ChatToolOutcome = {
|
|
|
201
194
|
};
|
|
202
195
|
/**
|
|
203
196
|
* The per-conversation candidate store: every `searchDocuments` result is recorded
|
|
204
|
-
* per
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
197
|
+
* per collection, every `listAssets` result into one flat library pool (EC-381), and
|
|
198
|
+
* the terminal tools may only reference the ids gathered here. The store IS the
|
|
199
|
+
* {@link WriterLeash} the engines ask per position (EC-593): a relation by its target
|
|
200
|
+
* collection, an asset by its declaration's `accept` (EC-588) — so a nested relation, a
|
|
201
|
+
* component's asset prop and a top-level field are leashed by the same two questions
|
|
202
|
+
* (the create enum + drop, the update loud refusal). Held by the panel per
|
|
203
|
+
* conversation — a new conversation (EC-380 clear) starts an empty store.
|
|
210
204
|
*/
|
|
211
|
-
export interface ChatCandidateStore {
|
|
205
|
+
export interface ChatCandidateStore extends WriterLeash {
|
|
212
206
|
record(collection: string, results: readonly DocumentSearchResult[]): void;
|
|
213
207
|
/** Record every `listAssets` result into the flat library pool (EC-381). */
|
|
214
208
|
recordAssets(results: readonly AssetSearchResult[]): void;
|
|
215
|
-
/** Candidates for a target collection's relation fields, keyed by field name. */
|
|
216
|
-
candidatesFor(collection: CollectionDef): RelationCandidates;
|
|
217
|
-
/**
|
|
218
|
-
* Candidates for a target collection's asset/image fields (EC-381), keyed by
|
|
219
|
-
* field name — the one flat searched-asset pool (`listAssets` has no field
|
|
220
|
-
* dimension), filtered per field by that field's `accept` (EC-588). A field with no
|
|
221
|
-
* fitting candidate is absent, so it is omitted from the shape (empty beats wrong).
|
|
222
|
-
*/
|
|
223
|
-
assetCandidatesFor(collection: CollectionDef): AssetCandidates;
|
|
224
209
|
}
|
|
225
210
|
export declare function createChatCandidateStore(): ChatCandidateStore;
|
|
226
211
|
export interface ChatToolExecutorOptions {
|
|
@@ -249,8 +234,6 @@ export interface ChatToolExecutorOptions {
|
|
|
249
234
|
currentDocument?: () => CurrentDocumentRef | null | undefined;
|
|
250
235
|
/** Current knowledge snapshot (thunk — the body thunk refreshes it per request). */
|
|
251
236
|
knowledge?: () => ResolvedKnowledge | undefined;
|
|
252
|
-
/** Component manifests — context for `allow`-leashed richText/blocks fields. */
|
|
253
|
-
components?: readonly ComponentManifest[];
|
|
254
237
|
/** Target locale for localized fields. */
|
|
255
238
|
locale?: string;
|
|
256
239
|
/** Locale config — required for `translateDocument`. */
|
|
@@ -260,7 +243,9 @@ export interface ChatToolExecutorOptions {
|
|
|
260
243
|
* `gateValidateOptions(...)`, forwarded whole to every engine below. EC-473
|
|
261
244
|
* made the assistant's gate the editor's gate; passing one bag instead of five
|
|
262
245
|
* keys makes it stay that way, because a key can no longer be dropped by being
|
|
263
|
-
* forgotten at one of the three call sites this replaced.
|
|
246
|
+
* forgotten at one of the three call sites this replaced. Its `lookupManifest` is
|
|
247
|
+
* also how the writer-form converter and the read form reach component props
|
|
248
|
+
* (EC-593) — the props the validator judges, no second component source.
|
|
264
249
|
*/
|
|
265
250
|
validateOptions?: ValidateDocumentOptions;
|
|
266
251
|
/** Max repair attempts inside `applyContentWithShape`. */
|
|
@@ -272,16 +257,26 @@ export interface ChatToolExecutorOptions {
|
|
|
272
257
|
export interface ChatToolExecutor {
|
|
273
258
|
execute(call: ChatToolCall): Promise<ChatToolOutcome>;
|
|
274
259
|
}
|
|
260
|
+
/** A translate skip as one model-facing phrase: `menu[1].label (de): the provider returned no text`. */
|
|
261
|
+
export declare function describeTranslateSkip(skip: TranslateSkip): string;
|
|
275
262
|
/**
|
|
276
|
-
* The
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
* its `{ version,
|
|
281
|
-
*
|
|
282
|
-
*
|
|
263
|
+
* The `getDocument` output (EC-379 → EC-593): the document in the form the model reads
|
|
264
|
+
* — the ONE converter's read side (`toWriterDocument`), so rich text is a markdown
|
|
265
|
+
* string at EVERY depth where the engine's loss-guarded inverse can express it
|
|
266
|
+
* faithfully (a top-level field, an object sub-field, a repeater item, a component's
|
|
267
|
+
* rich-text prop), and keeps its stored `{ version: 2, content }` value otherwise (embeds,
|
|
268
|
+
* internal `ref` links, out-of-subset marks), so nothing the model later echoes back can
|
|
269
|
+
* silently lose structure. It used to project TOP-LEVEL rich text only (SHAPES #29: a
|
|
270
|
+
* nested value read as v2 while every writer was told markdown).
|
|
271
|
+
*
|
|
272
|
+
* `components` is the catalog's prop half (RULES F3d): for every component on the
|
|
273
|
+
* document, the props `updateFields` may change and the rules each value must satisfy,
|
|
274
|
+
* read off the same declarations the validator judges (`lookupManifest`). Applies to
|
|
275
|
+
* the TOOL OUTPUT only; the proposal/apply paths always read the raw stored document.
|
|
283
276
|
*/
|
|
284
|
-
export declare function
|
|
277
|
+
export declare function modelFacingDocument(document: CmsDocument, collection: CollectionDef, validateOptions?: ValidateDocumentOptions): CmsDocument & {
|
|
278
|
+
components?: Record<string, string[]>;
|
|
279
|
+
};
|
|
285
280
|
/**
|
|
286
281
|
* Build the client executor map. Every entry validates its input against
|
|
287
282
|
* {@link chatToolDefinitions}' zod schema (the route validates UIMessages only
|