@xemahq/ui-kernel 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/biome-host/biome-builders.d.ts.map +1 -1
- package/dist/lib/biome-host/biome-builders.js +3 -1
- package/dist/lib/biome-host/biome-builders.js.map +1 -1
- package/dist/lib/biome-host/biome-registry.d.ts.map +1 -1
- package/dist/lib/biome-host/biome-registry.js +14 -9
- package/dist/lib/biome-host/biome-registry.js.map +1 -1
- package/dist/lib/biome-host/biome-scope-hooks.d.ts +11 -0
- package/dist/lib/biome-host/biome-scope-hooks.d.ts.map +1 -0
- package/dist/lib/biome-host/biome-scope-hooks.js +25 -0
- package/dist/lib/biome-host/biome-scope-hooks.js.map +1 -0
- package/dist/lib/biome-host/biome-scope.d.ts +1 -10
- package/dist/lib/biome-host/biome-scope.d.ts.map +1 -1
- package/dist/lib/biome-host/biome-scope.js +1 -24
- package/dist/lib/biome-host/biome-scope.js.map +1 -1
- package/dist/lib/biome-host/biome-scoped-query.d.ts +1 -1
- package/dist/lib/biome-host/biome-scoped-query.d.ts.map +1 -1
- package/dist/lib/biome-host/biome-scoped-query.js +1 -1
- package/dist/lib/biome-host/biome-scoped-query.js.map +1 -1
- package/dist/lib/biome-host/create-biome-orval-config.js +2 -2
- package/dist/lib/biome-host/create-biome-orval-config.js.map +1 -1
- package/dist/lib/biome-host/errors.js.map +1 -1
- package/dist/lib/biome-host/index.d.ts +1 -1
- package/dist/lib/biome-host/index.d.ts.map +1 -1
- package/dist/lib/biome-host/index.js +1 -1
- package/dist/lib/biome-host/index.js.map +1 -1
- package/dist/lib/biome-host/session-profiles.d.ts.map +1 -1
- package/dist/lib/biome-host/session-profiles.js +4 -1
- package/dist/lib/biome-host/session-profiles.js.map +1 -1
- package/dist/lib/capabilities/capability-context.d.ts +9 -0
- package/dist/lib/capabilities/capability-context.d.ts.map +1 -0
- package/dist/lib/capabilities/capability-context.js +13 -0
- package/dist/lib/capabilities/capability-context.js.map +1 -0
- package/dist/lib/capabilities/capability-provider.d.ts +2 -8
- package/dist/lib/capabilities/capability-provider.d.ts.map +1 -1
- package/dist/lib/capabilities/capability-provider.js +1 -12
- package/dist/lib/capabilities/capability-provider.js.map +1 -1
- package/dist/lib/capabilities/index.d.ts +1 -0
- package/dist/lib/capabilities/index.d.ts.map +1 -1
- package/dist/lib/capabilities/index.js +1 -0
- package/dist/lib/capabilities/index.js.map +1 -1
- package/dist/lib/capabilities/use-capability.d.ts.map +1 -1
- package/dist/lib/capabilities/use-capability.js +1 -1
- package/dist/lib/capabilities/use-capability.js.map +1 -1
- package/dist/lib/contribution-registry/contribution-registry.d.ts +24 -0
- package/dist/lib/contribution-registry/contribution-registry.d.ts.map +1 -0
- package/dist/lib/contribution-registry/contribution-registry.js +67 -0
- package/dist/lib/contribution-registry/contribution-registry.js.map +1 -0
- package/dist/lib/contribution-registry/index.d.ts +3 -0
- package/dist/lib/contribution-registry/index.d.ts.map +1 -0
- package/dist/lib/contribution-registry/index.js +3 -0
- package/dist/lib/contribution-registry/index.js.map +1 -0
- package/dist/lib/contribution-registry/renderable-registry.d.ts +33 -0
- package/dist/lib/contribution-registry/renderable-registry.d.ts.map +1 -0
- package/dist/lib/contribution-registry/renderable-registry.js +51 -0
- package/dist/lib/contribution-registry/renderable-registry.js.map +1 -0
- package/dist/lib/system-bus/deeplink.js.map +1 -1
- package/dist/lib/system-bus/host-ports.d.ts +2 -1
- package/dist/lib/system-bus/host-ports.d.ts.map +1 -1
- package/dist/lib/system-bus/intent-registry.d.ts.map +1 -1
- package/dist/lib/system-bus/intent-registry.js.map +1 -1
- package/dist/offline/index.d.ts +2 -1
- package/dist/offline/index.d.ts.map +1 -1
- package/dist/offline/index.js +2 -1
- package/dist/offline/index.js.map +1 -1
- package/dist/offline/offline-status-badge.js +1 -1
- package/dist/offline/offline-status-badge.js.map +1 -1
- package/dist/offline/outbox.js.map +1 -1
- package/dist/offline/react-hooks.d.ts +7 -0
- package/dist/offline/react-hooks.d.ts.map +1 -0
- package/dist/offline/react-hooks.js +25 -0
- package/dist/offline/react-hooks.js.map +1 -0
- package/dist/offline/react.d.ts +0 -4
- package/dist/offline/react.d.ts.map +1 -1
- package/dist/offline/react.js +2 -24
- package/dist/offline/react.js.map +1 -1
- package/dist/offline/storage.d.ts.map +1 -1
- package/dist/offline/storage.js +8 -5
- package/dist/offline/storage.js.map +1 -1
- package/dist/registry/index.d.ts +1 -0
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +1 -0
- package/dist/registry/index.js.map +1 -1
- package/dist/registry/lib/primitives/SessionEventCard.d.ts.map +1 -1
- package/dist/registry/lib/primitives/SessionEventCard.js +1 -1
- package/dist/registry/lib/primitives/SessionEventCard.js.map +1 -1
- package/dist/registry/lib/session-context-provider.d.ts +1 -3
- package/dist/registry/lib/session-context-provider.d.ts.map +1 -1
- package/dist/registry/lib/session-context-provider.js +2 -15
- package/dist/registry/lib/session-context-provider.js.map +1 -1
- package/dist/registry/lib/session-context.d.ts +5 -0
- package/dist/registry/lib/session-context.d.ts.map +1 -0
- package/dist/registry/lib/session-context.js +15 -0
- package/dist/registry/lib/session-context.js.map +1 -0
- package/dist/registry/lib/session-selectors.d.ts.map +1 -1
- package/dist/registry/lib/session-selectors.js +3 -1
- package/dist/registry/lib/session-selectors.js.map +1 -1
- package/dist/ui/branding/app-branding-context.d.ts +9 -0
- package/dist/ui/branding/app-branding-context.d.ts.map +1 -0
- package/dist/ui/branding/app-branding-context.js +10 -0
- package/dist/ui/branding/app-branding-context.js.map +1 -0
- package/dist/ui/branding/app-branding-provider.d.ts +0 -6
- package/dist/ui/branding/app-branding-provider.d.ts.map +1 -1
- package/dist/ui/branding/app-branding-provider.js +1 -8
- package/dist/ui/branding/app-branding-provider.js.map +1 -1
- package/dist/ui/capture/field-spec-to-zod.d.ts +1 -0
- package/dist/ui/capture/field-spec-to-zod.d.ts.map +1 -1
- package/dist/ui/capture/field-spec-to-zod.js +19 -0
- package/dist/ui/capture/field-spec-to-zod.js.map +1 -1
- package/dist/ui/capture/field-spec-validate.d.ts +4 -0
- package/dist/ui/capture/field-spec-validate.d.ts.map +1 -0
- package/dist/ui/capture/field-spec-validate.js +129 -0
- package/dist/ui/capture/field-spec-validate.js.map +1 -0
- package/dist/ui/capture/field-spec.d.ts +13 -1
- package/dist/ui/capture/field-spec.d.ts.map +1 -1
- package/dist/ui/capture/field-spec.js +25 -0
- package/dist/ui/capture/field-spec.js.map +1 -1
- package/dist/ui/capture/index.d.ts +4 -2
- package/dist/ui/capture/index.d.ts.map +1 -1
- package/dist/ui/capture/index.js +4 -2
- package/dist/ui/capture/index.js.map +1 -1
- package/dist/ui/capture/schema-field-input.d.ts.map +1 -1
- package/dist/ui/capture/schema-field-input.js +40 -13
- package/dist/ui/capture/schema-field-input.js.map +1 -1
- package/dist/ui/capture/submit-values.d.ts +6 -0
- package/dist/ui/capture/submit-values.d.ts.map +1 -0
- package/dist/ui/capture/submit-values.js +28 -0
- package/dist/ui/capture/submit-values.js.map +1 -0
- package/dist/ui/capture/use-prefill.d.ts.map +1 -1
- package/dist/ui/capture/use-prefill.js +3 -1
- package/dist/ui/capture/use-prefill.js.map +1 -1
- package/dist/ui/capture/use-wizard.d.ts +1 -1
- package/dist/ui/capture/use-wizard.d.ts.map +1 -1
- package/dist/ui/capture/use-wizard.js +1 -1
- package/dist/ui/capture/use-wizard.js.map +1 -1
- package/dist/ui/capture/wizard.d.ts.map +1 -1
- package/dist/ui/capture/wizard.js.map +1 -1
- package/dist/ui/index.d.ts +6 -1
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +6 -1
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/primitives/alert-dialog.js +1 -1
- package/dist/ui/primitives/alert-dialog.js.map +1 -1
- package/dist/ui/primitives/badge-variants.d.ts +4 -0
- package/dist/ui/primitives/badge-variants.d.ts.map +1 -0
- package/dist/ui/primitives/badge-variants.js +15 -0
- package/dist/ui/primitives/badge-variants.js.map +1 -0
- package/dist/ui/primitives/badge.d.ts +3 -5
- package/dist/ui/primitives/badge.d.ts.map +1 -1
- package/dist/ui/primitives/badge.js +2 -15
- package/dist/ui/primitives/badge.js.map +1 -1
- package/dist/ui/primitives/button-variants.d.ts +5 -0
- package/dist/ui/primitives/button-variants.d.ts.map +1 -0
- package/dist/ui/primitives/button-variants.js +24 -0
- package/dist/ui/primitives/button-variants.js.map +1 -0
- package/dist/ui/primitives/button.d.ts +3 -6
- package/dist/ui/primitives/button.d.ts.map +1 -1
- package/dist/ui/primitives/button.js +2 -24
- package/dist/ui/primitives/button.js.map +1 -1
- package/dist/ui/primitives/form-stepper.d.ts +1 -13
- package/dist/ui/primitives/form-stepper.d.ts.map +1 -1
- package/dist/ui/primitives/form-stepper.js +1 -6
- package/dist/ui/primitives/form-stepper.js.map +1 -1
- package/dist/ui/primitives/form-stepper.types.d.ts +14 -0
- package/dist/ui/primitives/form-stepper.types.d.ts.map +1 -0
- package/dist/ui/primitives/form-stepper.types.js +7 -0
- package/dist/ui/primitives/form-stepper.types.js.map +1 -0
- package/dist/ui/primitives/overflow-tabs.d.ts +5 -0
- package/dist/ui/primitives/overflow-tabs.d.ts.map +1 -1
- package/dist/ui/primitives/overflow-tabs.js +4 -4
- package/dist/ui/primitives/overflow-tabs.js.map +1 -1
- package/dist/ui/primitives/preview-device-frame.js +1 -1
- package/dist/ui/primitives/preview-device-frame.js.map +1 -1
- package/dist/ui/primitives/tag-multi-select.d.ts.map +1 -1
- package/dist/ui/primitives/tag-multi-select.js +1 -1
- package/dist/ui/primitives/tag-multi-select.js.map +1 -1
- package/dist/ui/primitives/url-preview-chrome.d.ts +8 -0
- package/dist/ui/primitives/url-preview-chrome.d.ts.map +1 -0
- package/dist/ui/primitives/url-preview-chrome.js +40 -0
- package/dist/ui/primitives/url-preview-chrome.js.map +1 -0
- package/dist/vista/GenericCatalogueSurface.d.ts +10 -0
- package/dist/vista/GenericCatalogueSurface.d.ts.map +1 -0
- package/dist/vista/GenericCatalogueSurface.js +80 -0
- package/dist/vista/GenericCatalogueSurface.js.map +1 -0
- package/dist/vista/SurfaceFallbackCard.d.ts +9 -0
- package/dist/vista/SurfaceFallbackCard.d.ts.map +1 -0
- package/dist/vista/SurfaceFallbackCard.js +44 -0
- package/dist/vista/SurfaceFallbackCard.js.map +1 -0
- package/dist/vista/VistaPanel.d.ts +21 -0
- package/dist/vista/VistaPanel.d.ts.map +1 -0
- package/dist/vista/VistaPanel.js +31 -0
- package/dist/vista/VistaPanel.js.map +1 -0
- package/dist/vista/html/CustomUiFrame.d.ts +20 -0
- package/dist/vista/html/CustomUiFrame.d.ts.map +1 -0
- package/dist/vista/html/CustomUiFrame.js +69 -0
- package/dist/vista/html/CustomUiFrame.js.map +1 -0
- package/dist/vista/html/custom-ui-frame-bridge.d.ts +13 -0
- package/dist/vista/html/custom-ui-frame-bridge.d.ts.map +1 -0
- package/dist/vista/html/custom-ui-frame-bridge.js +30 -0
- package/dist/vista/html/custom-ui-frame-bridge.js.map +1 -0
- package/dist/vista/html/custom-ui-frame-contract.d.ts +29 -0
- package/dist/vista/html/custom-ui-frame-contract.d.ts.map +1 -0
- package/dist/vista/html/custom-ui-frame-contract.js +16 -0
- package/dist/vista/html/custom-ui-frame-contract.js.map +1 -0
- package/dist/vista/html/custom-ui-intents.d.ts +20 -0
- package/dist/vista/html/custom-ui-intents.d.ts.map +1 -0
- package/dist/vista/html/custom-ui-intents.js +57 -0
- package/dist/vista/html/custom-ui-intents.js.map +1 -0
- package/dist/vista/html/custom-ui-srcdoc.d.ts +17 -0
- package/dist/vista/html/custom-ui-srcdoc.d.ts.map +1 -0
- package/dist/vista/html/custom-ui-srcdoc.js +70 -0
- package/dist/vista/html/custom-ui-srcdoc.js.map +1 -0
- package/dist/vista/html/index.d.ts +6 -0
- package/dist/vista/html/index.d.ts.map +1 -0
- package/dist/vista/html/index.js +6 -0
- package/dist/vista/html/index.js.map +1 -0
- package/dist/vista/index.d.ts +22 -0
- package/dist/vista/index.d.ts.map +1 -0
- package/dist/vista/index.js +22 -0
- package/dist/vista/index.js.map +1 -0
- package/dist/vista/surface-catalogue.d.ts +5 -0
- package/dist/vista/surface-catalogue.d.ts.map +1 -0
- package/dist/vista/surface-catalogue.js +30 -0
- package/dist/vista/surface-catalogue.js.map +1 -0
- package/dist/vista/surface-icons.d.ts +7 -0
- package/dist/vista/surface-icons.d.ts.map +1 -0
- package/dist/vista/surface-icons.js +26 -0
- package/dist/vista/surface-icons.js.map +1 -0
- package/dist/vista/surface-registry.d.ts +7 -0
- package/dist/vista/surface-registry.d.ts.map +1 -0
- package/dist/vista/surface-registry.js +7 -0
- package/dist/vista/surface-registry.js.map +1 -0
- package/dist/vista/surfaces/ChartSurface.d.ts +4 -0
- package/dist/vista/surfaces/ChartSurface.d.ts.map +1 -0
- package/dist/vista/surfaces/ChartSurface.js +17 -0
- package/dist/vista/surfaces/ChartSurface.js.map +1 -0
- package/dist/vista/surfaces/FilesSurface.d.ts +4 -0
- package/dist/vista/surfaces/FilesSurface.d.ts.map +1 -0
- package/dist/vista/surfaces/FilesSurface.js +77 -0
- package/dist/vista/surfaces/FilesSurface.js.map +1 -0
- package/dist/vista/surfaces/FormSurface.d.ts +4 -0
- package/dist/vista/surfaces/FormSurface.d.ts.map +1 -0
- package/dist/vista/surfaces/FormSurface.js +124 -0
- package/dist/vista/surfaces/FormSurface.js.map +1 -0
- package/dist/vista/surfaces/HtmlSurface.d.ts +4 -0
- package/dist/vista/surfaces/HtmlSurface.d.ts.map +1 -0
- package/dist/vista/surfaces/HtmlSurface.js +11 -0
- package/dist/vista/surfaces/HtmlSurface.js.map +1 -0
- package/dist/vista/surfaces/TableSurface.d.ts +4 -0
- package/dist/vista/surfaces/TableSurface.d.ts.map +1 -0
- package/dist/vista/surfaces/TableSurface.js +42 -0
- package/dist/vista/surfaces/TableSurface.js.map +1 -0
- package/dist/vista/surfaces/UrlSurface.d.ts +4 -0
- package/dist/vista/surfaces/UrlSurface.d.ts.map +1 -0
- package/dist/vista/surfaces/UrlSurface.js +7 -0
- package/dist/vista/surfaces/UrlSurface.js.map +1 -0
- package/dist/vista/surfaces/blob-surfaces.d.ts +6 -0
- package/dist/vista/surfaces/blob-surfaces.d.ts.map +1 -0
- package/dist/vista/surfaces/blob-surfaces.js +38 -0
- package/dist/vista/surfaces/blob-surfaces.js.map +1 -0
- package/dist/vista/surfaces/elicitation-transport.d.ts +27 -0
- package/dist/vista/surfaces/elicitation-transport.d.ts.map +1 -0
- package/dist/vista/surfaces/elicitation-transport.js +193 -0
- package/dist/vista/surfaces/elicitation-transport.js.map +1 -0
- package/dist/vista/surfaces/file-surface-utils.d.ts +13 -0
- package/dist/vista/surfaces/file-surface-utils.d.ts.map +1 -0
- package/dist/vista/surfaces/file-surface-utils.js +46 -0
- package/dist/vista/surfaces/file-surface-utils.js.map +1 -0
- package/dist/vista/surfaces/index.d.ts +12 -0
- package/dist/vista/surfaces/index.d.ts.map +1 -0
- package/dist/vista/surfaces/index.js +12 -0
- package/dist/vista/surfaces/index.js.map +1 -0
- package/dist/vista/surfaces/register-surfaces.d.ts +2 -0
- package/dist/vista/surfaces/register-surfaces.d.ts.map +1 -0
- package/dist/vista/surfaces/register-surfaces.js +80 -0
- package/dist/vista/surfaces/register-surfaces.js.map +1 -0
- package/dist/vista/surfaces/text-surfaces.d.ts +8 -0
- package/dist/vista/surfaces/text-surfaces.d.ts.map +1 -0
- package/dist/vista/surfaces/text-surfaces.js +54 -0
- package/dist/vista/surfaces/text-surfaces.js.map +1 -0
- package/dist/vista/use-vista-tabs.d.ts +30 -0
- package/dist/vista/use-vista-tabs.d.ts.map +1 -0
- package/dist/vista/use-vista-tabs.js +77 -0
- package/dist/vista/use-vista-tabs.js.map +1 -0
- package/dist/vista/vista-code-renderer-context.d.ts +9 -0
- package/dist/vista/vista-code-renderer-context.d.ts.map +1 -0
- package/dist/vista/vista-code-renderer-context.js +6 -0
- package/dist/vista/vista-code-renderer-context.js.map +1 -0
- package/dist/vista/vista-code-renderer.d.ts +7 -0
- package/dist/vista/vista-code-renderer.d.ts.map +1 -0
- package/dist/vista/vista-code-renderer.js +6 -0
- package/dist/vista/vista-code-renderer.js.map +1 -0
- package/dist/vista/vista-file-context.d.ts +8 -0
- package/dist/vista/vista-file-context.d.ts.map +1 -0
- package/dist/vista/vista-file-context.js +6 -0
- package/dist/vista/vista-file-context.js.map +1 -0
- package/dist/vista/vista-file-source.d.ts +15 -0
- package/dist/vista/vista-file-source.d.ts.map +1 -0
- package/dist/vista/vista-file-source.js +66 -0
- package/dist/vista/vista-file-source.js.map +1 -0
- package/dist/vista/vista-form-context.d.ts +13 -0
- package/dist/vista/vista-form-context.d.ts.map +1 -0
- package/dist/vista/vista-form-context.js +6 -0
- package/dist/vista/vista-form-context.js.map +1 -0
- package/dist/vista/vista-form-host.d.ts +7 -0
- package/dist/vista/vista-form-host.d.ts.map +1 -0
- package/dist/vista/vista-form-host.js +6 -0
- package/dist/vista/vista-form-host.js.map +1 -0
- package/dist/vista/vista-model.d.ts +12 -0
- package/dist/vista/vista-model.d.ts.map +1 -0
- package/dist/vista/vista-model.js +72 -0
- package/dist/vista/vista-model.js.map +1 -0
- package/dist/vista/vista-open-context.d.ts +7 -0
- package/dist/vista/vista-open-context.d.ts.map +1 -0
- package/dist/vista/vista-open-context.js +6 -0
- package/dist/vista/vista-open-context.js.map +1 -0
- package/dist/vista/vista-open-state.d.ts +5 -0
- package/dist/vista/vista-open-state.d.ts.map +1 -0
- package/dist/vista/vista-open-state.js +6 -0
- package/dist/vista/vista-open-state.js.map +1 -0
- package/dist/vista/vista-payloads.d.ts +106 -0
- package/dist/vista/vista-payloads.d.ts.map +1 -0
- package/dist/vista/vista-payloads.js +302 -0
- package/dist/vista/vista-payloads.js.map +1 -0
- package/dist/vista/vista-tabs-manifest.d.ts +17 -0
- package/dist/vista/vista-tabs-manifest.d.ts.map +1 -0
- package/dist/vista/vista-tabs-manifest.js +60 -0
- package/dist/vista/vista-tabs-manifest.js.map +1 -0
- package/dist/vista/vista-tools.d.ts +41 -0
- package/dist/vista/vista-tools.d.ts.map +1 -0
- package/dist/vista/vista-tools.js +113 -0
- package/dist/vista/vista-tools.js.map +1 -0
- package/package.json +4 -4
- package/src/index.ts +5 -0
- package/src/lib/biome-host/biome-builders.ts +4 -1
- package/src/lib/biome-host/biome-registry.ts +31 -12
- package/src/lib/biome-host/biome-scope-hooks.ts +39 -0
- package/src/lib/biome-host/biome-scope.tsx +1 -68
- package/src/lib/biome-host/biome-scoped-query.ts +4 -1
- package/src/lib/biome-host/create-biome-orval-config.ts +2 -2
- package/src/lib/biome-host/errors.ts +1 -1
- package/src/lib/biome-host/index.ts +1 -1
- package/src/lib/biome-host/session-profiles.ts +7 -1
- package/src/lib/capabilities/capability-context.ts +25 -0
- package/src/lib/capabilities/capability-provider.tsx +5 -34
- package/src/lib/capabilities/index.ts +1 -0
- package/src/lib/capabilities/use-capability.ts +4 -1
- package/src/lib/contribution-registry/contribution-registry.spec.ts +141 -0
- package/src/lib/contribution-registry/contribution-registry.ts +202 -0
- package/src/lib/contribution-registry/index.ts +9 -0
- package/src/lib/contribution-registry/renderable-registry.ts +142 -0
- package/src/lib/system-bus/deeplink.ts +1 -1
- package/src/lib/system-bus/host-ports.ts +2 -1
- package/src/lib/system-bus/intent-registry.ts +1 -2
- package/src/offline/index.ts +4 -2
- package/src/offline/offline-status-badge.tsx +1 -1
- package/src/offline/outbox.test.ts +23 -16
- package/src/offline/outbox.ts +1 -1
- package/src/offline/react-hooks.ts +37 -0
- package/src/offline/react.tsx +1 -40
- package/src/offline/storage.ts +11 -6
- package/src/registry/index.ts +1 -0
- package/src/registry/lib/primitives/SessionEventCard.tsx +4 -2
- package/src/registry/lib/session-context-provider.tsx +8 -25
- package/src/registry/lib/session-context.ts +21 -0
- package/src/registry/lib/session-selectors.ts +3 -1
- package/src/ui/branding/app-branding-context.ts +20 -0
- package/src/ui/branding/app-branding-provider.tsx +5 -27
- package/src/ui/capture/field-spec-to-zod.ts +22 -0
- package/src/ui/capture/field-spec-validate.ts +147 -0
- package/src/ui/capture/field-spec.ts +62 -0
- package/src/ui/capture/index.ts +9 -1
- package/src/ui/capture/schema-field-input.tsx +141 -15
- package/src/ui/capture/submit-values.test.ts +70 -0
- package/src/ui/capture/submit-values.ts +63 -0
- package/src/ui/capture/use-prefill.ts +4 -1
- package/src/ui/capture/use-wizard.ts +2 -2
- package/src/ui/capture/wizard.tsx +2 -1
- package/src/ui/index.ts +11 -2
- package/src/ui/primitives/alert-dialog.tsx +1 -1
- package/src/ui/primitives/badge-variants.ts +21 -0
- package/src/ui/primitives/badge.tsx +3 -19
- package/src/ui/primitives/button-variants.ts +31 -0
- package/src/ui/primitives/button.tsx +3 -28
- package/src/ui/primitives/form-stepper.tsx +1 -16
- package/src/ui/primitives/form-stepper.types.ts +15 -0
- package/src/ui/primitives/overflow-tabs.tsx +24 -4
- package/src/ui/primitives/preview-device-frame.tsx +1 -1
- package/src/ui/primitives/tag-multi-select.tsx +3 -4
- package/src/ui/primitives/url-preview-chrome.tsx +102 -0
- package/src/vista/GenericCatalogueSurface.tsx +177 -0
- package/src/vista/SurfaceFallbackCard.tsx +109 -0
- package/src/vista/VistaPanel.tsx +155 -0
- package/src/vista/form-payload.spec.ts +139 -0
- package/src/vista/html/CustomUiFrame.spec.tsx +125 -0
- package/src/vista/html/CustomUiFrame.tsx +204 -0
- package/src/vista/html/custom-ui-frame-bridge.spec.ts +191 -0
- package/src/vista/html/custom-ui-frame-bridge.ts +92 -0
- package/src/vista/html/custom-ui-frame-contract.ts +88 -0
- package/src/vista/html/custom-ui-intents.spec.ts +133 -0
- package/src/vista/html/custom-ui-intents.ts +151 -0
- package/src/vista/html/custom-ui-srcdoc.spec.ts +151 -0
- package/src/vista/html/custom-ui-srcdoc.ts +193 -0
- package/src/vista/html/index.ts +13 -0
- package/src/vista/index.ts +52 -0
- package/src/vista/surface-catalogue.spec.ts +140 -0
- package/src/vista/surface-catalogue.ts +84 -0
- package/src/vista/surface-icons.ts +57 -0
- package/src/vista/surface-registry.ts +56 -0
- package/src/vista/surfaces/ChartSurface.tsx +34 -0
- package/src/vista/surfaces/FilesSurface.tsx +202 -0
- package/src/vista/surfaces/FormSurface.tsx +363 -0
- package/src/vista/surfaces/HtmlSurface.tsx +43 -0
- package/src/vista/surfaces/TableSurface.tsx +99 -0
- package/src/vista/surfaces/UrlSurface.tsx +40 -0
- package/src/vista/surfaces/blob-surfaces.tsx +129 -0
- package/src/vista/surfaces/elicitation-transport.spec.ts +369 -0
- package/src/vista/surfaces/elicitation-transport.ts +353 -0
- package/src/vista/surfaces/file-surface-utils.ts +62 -0
- package/src/vista/surfaces/index.ts +24 -0
- package/src/vista/surfaces/register-surfaces.tsx +137 -0
- package/src/vista/surfaces/text-surfaces.tsx +119 -0
- package/src/vista/use-vista-tabs.ts +188 -0
- package/src/vista/vista-code-renderer-context.ts +16 -0
- package/src/vista/vista-code-renderer.tsx +34 -0
- package/src/vista/vista-file-context.ts +14 -0
- package/src/vista/vista-file-source.tsx +140 -0
- package/src/vista/vista-form-context.ts +20 -0
- package/src/vista/vista-form-host.tsx +53 -0
- package/src/vista/vista-model.ts +130 -0
- package/src/vista/vista-open-context.tsx +32 -0
- package/src/vista/vista-open-state.ts +11 -0
- package/src/vista/vista-payloads.ts +512 -0
- package/src/vista/vista-tabs-manifest.ts +123 -0
- package/src/vista/vista-tools.ts +220 -0
- package/dist/lib/biome-host/response-envelope.d.ts +0 -3
- package/dist/lib/biome-host/response-envelope.d.ts.map +0 -1
- package/dist/lib/biome-host/response-envelope.js +0 -21
- package/dist/lib/biome-host/response-envelope.js.map +0 -1
- package/src/lib/biome-host/response-envelope.ts +0 -69
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createContributionRegistry` — the ONE two-layer closed+open registry core
|
|
3
|
+
* the Xema frontend uses everywhere a biome may contribute into a set the
|
|
4
|
+
* platform also ships a closed first-party version of.
|
|
5
|
+
*
|
|
6
|
+
* Before this module the exact same bookkeeping was hand-written four times
|
|
7
|
+
* (in-chat widgets, biome output renderers, open reply variants, and — next —
|
|
8
|
+
* Vista surfaces), each with slightly different guards. This is that core,
|
|
9
|
+
* extracted once (reuse register U1).
|
|
10
|
+
*
|
|
11
|
+
* ── The two layers ──────────────────────────────────────────────────────────
|
|
12
|
+
* • a CLOSED first-party set, registered at module load and keyed by a
|
|
13
|
+
* platform enum value;
|
|
14
|
+
* • an OPEN biome set, registered at runtime (a biome registers from its
|
|
15
|
+
* `FrontendBiome.init()` hook) and keyed by a namespaced `<biomeId>:<kind>`
|
|
16
|
+
* slug.
|
|
17
|
+
*
|
|
18
|
+
* ── The invariants (all fail-loud, none silent) ─────────────────────────────
|
|
19
|
+
* • RESERVED KINDS. Every first-party kind is reserved: `register()` throws
|
|
20
|
+
* if a biome tries to claim one. The open set can never shadow the closed
|
|
21
|
+
* one.
|
|
22
|
+
* • DUPLICATE GUARD. Registering the EXACT same entry object twice is a
|
|
23
|
+
* no-op (this is what tolerates React Fast Refresh / a double-invoked
|
|
24
|
+
* `init()`); registering a DIFFERENT entry for a kind that is already
|
|
25
|
+
* taken is an error — two biomes cannot silently shadow each other.
|
|
26
|
+
* • The one exception is {@link DuplicateContributionPolicy.FirstWins}, for
|
|
27
|
+
* registries rebuilt on a hot path where a throw would crash a render
|
|
28
|
+
* (the biome output-renderer overlay). It is NOT a silent fallback: an
|
|
29
|
+
* `onDuplicate` reporter is REQUIRED, and the registry factory throws at
|
|
30
|
+
* construction if it is missing.
|
|
31
|
+
*
|
|
32
|
+
* Resolution itself lives in the caller (or in `renderable-registry.ts` for the
|
|
33
|
+
* versioned + payload-validated flavour) and NEVER throws.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/** What to do when a DIFFERENT entry is registered for an already-taken kind. */
|
|
37
|
+
export enum DuplicateContributionPolicy {
|
|
38
|
+
/**
|
|
39
|
+
* Throw. The default, and correct for every registry whose registrations
|
|
40
|
+
* happen once at module load or once per biome `init()`.
|
|
41
|
+
*/
|
|
42
|
+
Throw = 'throw',
|
|
43
|
+
/**
|
|
44
|
+
* Keep the first registration and report the loser through `onDuplicate`.
|
|
45
|
+
* For registries REBUILT on a hot path (e.g. per render) where throwing
|
|
46
|
+
* would take down the UI. Requires `onDuplicate` — there is no silent mode.
|
|
47
|
+
*/
|
|
48
|
+
FirstWins = 'first-wins',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Options for {@link createContributionRegistry}. */
|
|
52
|
+
export interface ContributionRegistryOptions<TEntry> {
|
|
53
|
+
/** Registry name, used verbatim in every thrown message. */
|
|
54
|
+
readonly name: string;
|
|
55
|
+
/**
|
|
56
|
+
* The CLOSED first-party kinds. Reserved: `register()` refuses them so a
|
|
57
|
+
* biome can never shadow a platform kind. Typically `Object.values(SomeEnum)`.
|
|
58
|
+
*/
|
|
59
|
+
readonly reservedKinds?: Iterable<string>;
|
|
60
|
+
/** Duplicate handling. Defaults to {@link DuplicateContributionPolicy.Throw}. */
|
|
61
|
+
readonly duplicatePolicy?: DuplicateContributionPolicy;
|
|
62
|
+
/**
|
|
63
|
+
* Reporter for a rejected duplicate under
|
|
64
|
+
* {@link DuplicateContributionPolicy.FirstWins}. REQUIRED under that policy.
|
|
65
|
+
*/
|
|
66
|
+
readonly onDuplicate?: (
|
|
67
|
+
kind: string,
|
|
68
|
+
existing: TEntry,
|
|
69
|
+
rejected: TEntry,
|
|
70
|
+
) => void;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The two-layer registry surface. */
|
|
74
|
+
export interface ContributionRegistry<TEntry> {
|
|
75
|
+
/** Registry name (diagnostics). */
|
|
76
|
+
readonly name: string;
|
|
77
|
+
/**
|
|
78
|
+
* Register a FIRST-PARTY entry for a closed platform kind. Called once per
|
|
79
|
+
* kind at module load.
|
|
80
|
+
*/
|
|
81
|
+
registerFirstParty(kind: string, entry: TEntry): void;
|
|
82
|
+
/**
|
|
83
|
+
* OPEN seam: register a biome entry under its namespaced `<biomeId>:<kind>`
|
|
84
|
+
* slug. Throws when the slug is a reserved first-party kind.
|
|
85
|
+
*/
|
|
86
|
+
register(kind: string, entry: TEntry): void;
|
|
87
|
+
/** True when SOME entry (first-party or biome) is registered for the kind. */
|
|
88
|
+
has(kind: string): boolean;
|
|
89
|
+
/** The entry for a kind — first-party wins — or `undefined`. */
|
|
90
|
+
get(kind: string): TEntry | undefined;
|
|
91
|
+
/** True when the kind belongs to the closed first-party set. */
|
|
92
|
+
isReserved(kind: string): boolean;
|
|
93
|
+
/** Every registered kind (sorted) — diagnostics + tests. */
|
|
94
|
+
list(): readonly string[];
|
|
95
|
+
/** Only the OPEN (biome-contributed) kinds (sorted). */
|
|
96
|
+
listOpen(): readonly string[];
|
|
97
|
+
/** Flattened `kind -> entry` view, first-party wins. Diagnostics + overlays. */
|
|
98
|
+
entries(): ReadonlyMap<string, TEntry>;
|
|
99
|
+
/** Test-only: clear the OPEN set (the first-party set stays). */
|
|
100
|
+
__resetOpenForTests(): void;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function createContributionRegistry<TEntry>(
|
|
104
|
+
options: ContributionRegistryOptions<TEntry>,
|
|
105
|
+
): ContributionRegistry<TEntry> {
|
|
106
|
+
const {
|
|
107
|
+
name,
|
|
108
|
+
reservedKinds,
|
|
109
|
+
duplicatePolicy = DuplicateContributionPolicy.Throw,
|
|
110
|
+
onDuplicate,
|
|
111
|
+
} = options;
|
|
112
|
+
|
|
113
|
+
if (
|
|
114
|
+
duplicatePolicy === DuplicateContributionPolicy.FirstWins &&
|
|
115
|
+
onDuplicate === undefined
|
|
116
|
+
) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`${name} registry: DuplicateContributionPolicy.FirstWins requires an ` +
|
|
119
|
+
`'onDuplicate' reporter — a dropped contribution must never be silent.`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const reserved = new Set<string>(reservedKinds ?? []);
|
|
124
|
+
const firstParty = new Map<string, TEntry>();
|
|
125
|
+
const open = new Map<string, TEntry>();
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Idempotent for the IDENTICAL entry (Fast Refresh / double-invoked
|
|
129
|
+
* `init()`); an error for a DIFFERENT one. Returns whether the entry landed.
|
|
130
|
+
*/
|
|
131
|
+
function put(target: Map<string, TEntry>, kind: string, entry: TEntry): void {
|
|
132
|
+
const existing = target.get(kind);
|
|
133
|
+
if (existing === undefined) {
|
|
134
|
+
target.set(kind, entry);
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (existing === entry) return;
|
|
138
|
+
if (duplicatePolicy === DuplicateContributionPolicy.FirstWins) {
|
|
139
|
+
// `onDuplicate` is guaranteed present under this policy (checked above).
|
|
140
|
+
(onDuplicate as (k: string, e: TEntry, r: TEntry) => void)(
|
|
141
|
+
kind,
|
|
142
|
+
existing,
|
|
143
|
+
entry,
|
|
144
|
+
);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
throw new Error(
|
|
148
|
+
`${name} registry: a DIFFERENT entry is already registered for kind ` +
|
|
149
|
+
`'${kind}'. Two contributors cannot claim the same slug.`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
name,
|
|
155
|
+
|
|
156
|
+
registerFirstParty(kind: string, entry: TEntry): void {
|
|
157
|
+
put(firstParty, kind, entry);
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
register(kind: string, entry: TEntry): void {
|
|
161
|
+
if (reserved.has(kind)) {
|
|
162
|
+
throw new Error(
|
|
163
|
+
`${name} registry: '${kind}' is a reserved first-party kind and ` +
|
|
164
|
+
`cannot be overridden by a biome. Use a namespaced ` +
|
|
165
|
+
`'<biomeId>:${kind}' slug.`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
put(open, kind, entry);
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
has(kind: string): boolean {
|
|
172
|
+
return firstParty.has(kind) || open.has(kind);
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
get(kind: string): TEntry | undefined {
|
|
176
|
+
return firstParty.get(kind) ?? open.get(kind);
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
isReserved(kind: string): boolean {
|
|
180
|
+
return reserved.has(kind);
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
list(): readonly string[] {
|
|
184
|
+
return [...firstParty.keys(), ...open.keys()].sort();
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
listOpen(): readonly string[] {
|
|
188
|
+
return [...open.keys()].sort();
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
entries(): ReadonlyMap<string, TEntry> {
|
|
192
|
+
// First-party last so it wins on a key present in both (which `register`
|
|
193
|
+
// already forbids for reserved kinds, but `entries()` must not depend on
|
|
194
|
+
// that to be correct).
|
|
195
|
+
return new Map<string, TEntry>([...open, ...firstParty]);
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
__resetOpenForTests(): void {
|
|
199
|
+
open.clear();
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared two-layer (closed first-party + open biome) contribution registry
|
|
3
|
+
* core, and its versioned/validated renderable flavour.
|
|
4
|
+
*
|
|
5
|
+
* One implementation, four consumers: in-chat ChatWidgets, biome output
|
|
6
|
+
* renderers, open reply variants, and Vista surfaces.
|
|
7
|
+
*/
|
|
8
|
+
export * from './contribution-registry';
|
|
9
|
+
export * from './renderable-registry';
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The RENDERABLE flavour of {@link createContributionRegistry}: a two-layer
|
|
3
|
+
* closed+open registry whose entries are versioned renderers with a narrowing
|
|
4
|
+
* payload validator, plus the fail-safe resolution every such plane needs.
|
|
5
|
+
*
|
|
6
|
+
* This is the shape the in-chat ChatWidget plane and the Vista render plane
|
|
7
|
+
* both want: an envelope `{ kind, version, payload }` arrives from an agent,
|
|
8
|
+
* and the frontend must turn it into something drawable WITHOUT ever throwing
|
|
9
|
+
* and WITHOUT ever silently drawing a coerced half-widget. Resolution returns a
|
|
10
|
+
* typed reason instead, which the caller renders as a safe fallback card.
|
|
11
|
+
*/
|
|
12
|
+
import {
|
|
13
|
+
createContributionRegistry,
|
|
14
|
+
type ContributionRegistry,
|
|
15
|
+
type ContributionRegistryOptions,
|
|
16
|
+
} from './contribution-registry';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Why an envelope fell back to the safe card. Populated on the resolution so
|
|
20
|
+
* the Studio debugger (and dev logs) can see WHY — mirrors the Model Resolution
|
|
21
|
+
* Matrix's `ResolvedModelDecision`. Closed set.
|
|
22
|
+
*/
|
|
23
|
+
export enum ContributionFallbackReason {
|
|
24
|
+
UnknownKind = 'unknown-kind',
|
|
25
|
+
InvalidPayload = 'invalid-payload',
|
|
26
|
+
UnsupportedVersion = 'unsupported-version',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** The minimum an agent-emitted envelope must carry to be resolvable. */
|
|
30
|
+
export interface ContributionEnvelope {
|
|
31
|
+
readonly kind: string;
|
|
32
|
+
readonly version: number;
|
|
33
|
+
readonly payload: unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* A versioned renderer. `validate` narrows the wire payload to the renderer's
|
|
38
|
+
* own type (returning `null` routes to the fallback with `InvalidPayload`) and
|
|
39
|
+
* MUST NOT throw; `render` draws from the narrowed payload.
|
|
40
|
+
*/
|
|
41
|
+
export interface RenderableContributionEntry<TRendered, TPayload = unknown> {
|
|
42
|
+
/** Highest schema version this renderer supports. */
|
|
43
|
+
readonly version: number;
|
|
44
|
+
/** Narrowing validator. Returns the typed payload, or `null`. MUST NOT throw. */
|
|
45
|
+
validate(payload: unknown): TPayload | null;
|
|
46
|
+
/** Draw from the narrowed payload. */
|
|
47
|
+
render(payload: TPayload): TRendered;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The outcome of resolving an envelope — inspectable for the Studio debugger. */
|
|
51
|
+
export type RenderableResolution<TRendered> =
|
|
52
|
+
| {
|
|
53
|
+
readonly matched: true;
|
|
54
|
+
readonly kind: string;
|
|
55
|
+
readonly version: number;
|
|
56
|
+
readonly rendered: TRendered;
|
|
57
|
+
}
|
|
58
|
+
| {
|
|
59
|
+
readonly matched: false;
|
|
60
|
+
readonly kind: string;
|
|
61
|
+
readonly reason: ContributionFallbackReason;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/** A {@link ContributionRegistry} of versioned renderers, plus `resolve`. */
|
|
65
|
+
export interface RenderableContributionRegistry<TRendered>
|
|
66
|
+
extends Omit<
|
|
67
|
+
ContributionRegistry<RenderableContributionEntry<TRendered>>,
|
|
68
|
+
'registerFirstParty' | 'register'
|
|
69
|
+
> {
|
|
70
|
+
registerFirstParty<TPayload>(
|
|
71
|
+
kind: string,
|
|
72
|
+
entry: RenderableContributionEntry<TRendered, TPayload>,
|
|
73
|
+
): void;
|
|
74
|
+
register<TPayload>(
|
|
75
|
+
kind: string,
|
|
76
|
+
entry: RenderableContributionEntry<TRendered, TPayload>,
|
|
77
|
+
): void;
|
|
78
|
+
/**
|
|
79
|
+
* Resolve an envelope. Pure + deterministic so it can run in render, and it
|
|
80
|
+
* NEVER throws — an unknown kind, a payload the entry rejects, or a `version`
|
|
81
|
+
* newer than the entry supports ALL yield `matched: false` with a typed
|
|
82
|
+
* reason the caller renders as the safe fallback card.
|
|
83
|
+
*/
|
|
84
|
+
resolve(envelope: ContributionEnvelope): RenderableResolution<TRendered>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function createRenderableContributionRegistry<TRendered>(
|
|
88
|
+
options: ContributionRegistryOptions<RenderableContributionEntry<TRendered>>,
|
|
89
|
+
): RenderableContributionRegistry<TRendered> {
|
|
90
|
+
const registry =
|
|
91
|
+
createContributionRegistry<RenderableContributionEntry<TRendered>>(options);
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
...registry,
|
|
95
|
+
|
|
96
|
+
registerFirstParty<TPayload>(
|
|
97
|
+
kind: string,
|
|
98
|
+
entry: RenderableContributionEntry<TRendered, TPayload>,
|
|
99
|
+
): void {
|
|
100
|
+
registry.registerFirstParty(kind, entry);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
register<TPayload>(
|
|
104
|
+
kind: string,
|
|
105
|
+
entry: RenderableContributionEntry<TRendered, TPayload>,
|
|
106
|
+
): void {
|
|
107
|
+
registry.register(kind, entry);
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
resolve(envelope: ContributionEnvelope): RenderableResolution<TRendered> {
|
|
111
|
+
const entry = registry.get(envelope.kind);
|
|
112
|
+
if (entry === undefined) {
|
|
113
|
+
return {
|
|
114
|
+
matched: false,
|
|
115
|
+
kind: envelope.kind,
|
|
116
|
+
reason: ContributionFallbackReason.UnknownKind,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (envelope.version > entry.version) {
|
|
120
|
+
return {
|
|
121
|
+
matched: false,
|
|
122
|
+
kind: envelope.kind,
|
|
123
|
+
reason: ContributionFallbackReason.UnsupportedVersion,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const narrowed = entry.validate(envelope.payload);
|
|
127
|
+
if (narrowed === null) {
|
|
128
|
+
return {
|
|
129
|
+
matched: false,
|
|
130
|
+
kind: envelope.kind,
|
|
131
|
+
reason: ContributionFallbackReason.InvalidPayload,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
matched: true,
|
|
136
|
+
kind: envelope.kind,
|
|
137
|
+
version: entry.version,
|
|
138
|
+
rendered: entry.render(narrowed),
|
|
139
|
+
};
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -144,7 +144,7 @@ export function parseDeeplink(raw: string): ParsedDeeplink {
|
|
|
144
144
|
}
|
|
145
145
|
return {
|
|
146
146
|
kind: DeeplinkTargetKind.Object,
|
|
147
|
-
objectRef: `${DEEPLINK_SCHEME}${body}
|
|
147
|
+
objectRef: `${DEEPLINK_SCHEME}${body}`,
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
150
|
case DeeplinkTargetKind.Capability: {
|
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
CapabilityInvokeMeta,
|
|
4
4
|
CapabilityInvokeResult,
|
|
5
5
|
} from './capability-invoke';
|
|
6
|
+
import type { ParsedDeeplink } from './deeplink';
|
|
6
7
|
import type { PaletteEntry, PaletteQuery } from './palette';
|
|
7
8
|
import type {
|
|
8
9
|
OpenWindowRequest,
|
|
@@ -92,5 +93,5 @@ export interface WindowManager {
|
|
|
92
93
|
* window) — fail-fast, never a silent no-op.
|
|
93
94
|
*/
|
|
94
95
|
export interface DeeplinkResolver {
|
|
95
|
-
resolve(target:
|
|
96
|
+
resolve(target: ParsedDeeplink): Promise<void>;
|
|
96
97
|
}
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
} from '@xemahq/kernel-contracts/object';
|
|
6
6
|
import { ActionRefKind } from './enums';
|
|
7
7
|
import type {
|
|
8
|
-
ActionRef,
|
|
9
8
|
IntentBus,
|
|
10
9
|
IntentRegistration,
|
|
11
10
|
ResolvedIntent,
|
|
@@ -24,7 +23,7 @@ function assertValidRegistration(registration: IntentRegistration): void {
|
|
|
24
23
|
if (!VALID_ACTION_KINDS.has(action.kind)) {
|
|
25
24
|
throw new Error(
|
|
26
25
|
`[xema-ui-kernel] IntentRegistration from "${registration.biomeId}" ` +
|
|
27
|
-
`has an ActionRef with unknown kind "${
|
|
26
|
+
`has an ActionRef with unknown kind "${action.kind}". ` +
|
|
28
27
|
`Expected one of: ${[...VALID_ACTION_KINDS].join(', ')}.`,
|
|
29
28
|
);
|
|
30
29
|
}
|
package/src/offline/index.ts
CHANGED
|
@@ -41,11 +41,13 @@ export {
|
|
|
41
41
|
} from './mutation';
|
|
42
42
|
export {
|
|
43
43
|
OutboxProvider,
|
|
44
|
+
type OutboxProviderProps,
|
|
45
|
+
} from './react';
|
|
46
|
+
export {
|
|
44
47
|
useOutbox,
|
|
45
48
|
useOutboxSnapshot,
|
|
46
49
|
useOnlineStatus,
|
|
47
|
-
|
|
48
|
-
} from './react';
|
|
50
|
+
} from './react-hooks';
|
|
49
51
|
export {
|
|
50
52
|
OfflineStatusBadge,
|
|
51
53
|
type OfflineStatusBadgeProps,
|
|
@@ -25,7 +25,7 @@ function makeOutbox(overrides: Partial<OutboxOptions> = {}): {
|
|
|
25
25
|
describe('Outbox', () => {
|
|
26
26
|
it('queues while offline and delivers on reconnect', async () => {
|
|
27
27
|
const { outbox, online } = makeOutbox();
|
|
28
|
-
const send = vi.fn(
|
|
28
|
+
const send = vi.fn(() => Promise.resolve());
|
|
29
29
|
outbox.registerExecutor(KIND, send);
|
|
30
30
|
|
|
31
31
|
online.reportOffline();
|
|
@@ -42,8 +42,9 @@ describe('Outbox', () => {
|
|
|
42
42
|
it('delivers immediately when online and removes the entry; idempotency key is stable', async () => {
|
|
43
43
|
const { outbox } = makeOutbox();
|
|
44
44
|
const keys: string[] = [];
|
|
45
|
-
outbox.registerExecutor(KIND,
|
|
45
|
+
outbox.registerExecutor(KIND, (_p, ctx: OutboxExecuteContext) => {
|
|
46
46
|
keys.push(ctx.idempotencyKey);
|
|
47
|
+
return Promise.resolve();
|
|
47
48
|
});
|
|
48
49
|
await outbox.enqueue(KIND, { a: 1 });
|
|
49
50
|
await outbox.drain();
|
|
@@ -55,11 +56,12 @@ describe('Outbox', () => {
|
|
|
55
56
|
it('retries a 5xx and eventually succeeds', async () => {
|
|
56
57
|
const { outbox } = makeOutbox();
|
|
57
58
|
let calls = 0;
|
|
58
|
-
outbox.registerExecutor(KIND,
|
|
59
|
+
outbox.registerExecutor(KIND, () => {
|
|
59
60
|
calls += 1;
|
|
60
61
|
if (calls < 3) {
|
|
61
|
-
|
|
62
|
+
return Promise.reject({ status: 503, message: 'unavailable' });
|
|
62
63
|
}
|
|
64
|
+
return Promise.resolve();
|
|
63
65
|
});
|
|
64
66
|
await outbox.enqueue(KIND, { a: 1 });
|
|
65
67
|
await outbox.drain();
|
|
@@ -69,29 +71,29 @@ describe('Outbox', () => {
|
|
|
69
71
|
|
|
70
72
|
it('dead-letters a 4xx immediately (no pointless retries)', async () => {
|
|
71
73
|
const { outbox } = makeOutbox();
|
|
72
|
-
const send = vi.fn(
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
const send = vi.fn(() =>
|
|
75
|
+
Promise.reject({ status: 400, message: 'bad request' }),
|
|
76
|
+
);
|
|
75
77
|
outbox.registerExecutor(KIND, send);
|
|
76
78
|
await outbox.enqueue(KIND, { a: 1 });
|
|
77
79
|
await outbox.drain();
|
|
78
80
|
expect(send).toHaveBeenCalledTimes(1);
|
|
79
81
|
const snap = outbox.getSnapshot();
|
|
80
82
|
expect(snap.failedCount).toBe(1);
|
|
81
|
-
expect(snap.entries[0]
|
|
83
|
+
expect(snap.entries[0].status).toBe(OutboxEntryStatus.Failed);
|
|
82
84
|
});
|
|
83
85
|
|
|
84
86
|
it('dead-letters after exhausting retries on persistent 5xx', async () => {
|
|
85
87
|
const { outbox } = makeOutbox({ maxAttempts: 3 });
|
|
86
88
|
let calls = 0;
|
|
87
|
-
outbox.registerExecutor(KIND,
|
|
89
|
+
outbox.registerExecutor(KIND, () => {
|
|
88
90
|
calls += 1;
|
|
89
|
-
|
|
91
|
+
return Promise.reject({ status: 503 });
|
|
90
92
|
});
|
|
91
93
|
await outbox.enqueue(KIND, { a: 1 });
|
|
92
94
|
await outbox.drain();
|
|
93
95
|
expect(calls).toBe(3);
|
|
94
|
-
expect(outbox.getSnapshot().entries[0]
|
|
96
|
+
expect(outbox.getSnapshot().entries[0].status).toBe(OutboxEntryStatus.Failed);
|
|
95
97
|
});
|
|
96
98
|
|
|
97
99
|
it('a network failure never advances the attempt counter or dead-letters', async () => {
|
|
@@ -99,15 +101,19 @@ describe('Outbox', () => {
|
|
|
99
101
|
const timers: Array<() => void> = [];
|
|
100
102
|
const { outbox } = makeOutbox({ scheduler: (fn) => void timers.push(fn) });
|
|
101
103
|
let succeed = false;
|
|
102
|
-
outbox.registerExecutor(KIND,
|
|
104
|
+
outbox.registerExecutor(KIND, () => {
|
|
103
105
|
if (!succeed) {
|
|
104
|
-
|
|
106
|
+
return Promise.reject({
|
|
107
|
+
name: 'TypeError',
|
|
108
|
+
message: 'Failed to fetch',
|
|
109
|
+
});
|
|
105
110
|
}
|
|
111
|
+
return Promise.resolve();
|
|
106
112
|
});
|
|
107
113
|
|
|
108
114
|
await outbox.enqueue(KIND, { a: 1 });
|
|
109
115
|
await outbox.drain();
|
|
110
|
-
const parked = outbox.getSnapshot().entries[0]
|
|
116
|
+
const parked = outbox.getSnapshot().entries[0];
|
|
111
117
|
expect(parked.status).toBe(OutboxEntryStatus.Pending);
|
|
112
118
|
expect(parked.attempts).toBe(0); // offline is not the payload's fault
|
|
113
119
|
expect(outbox.getSnapshot().failedCount).toBe(0);
|
|
@@ -121,8 +127,9 @@ describe('Outbox', () => {
|
|
|
121
127
|
it('delivers queued entries in FIFO order', async () => {
|
|
122
128
|
const { outbox, online } = makeOutbox();
|
|
123
129
|
const order: number[] = [];
|
|
124
|
-
outbox.registerExecutor(KIND,
|
|
130
|
+
outbox.registerExecutor(KIND, (p: { n: number }) => {
|
|
125
131
|
order.push(p.n);
|
|
132
|
+
return Promise.resolve();
|
|
126
133
|
});
|
|
127
134
|
online.reportOffline();
|
|
128
135
|
await outbox.enqueue(KIND, { n: 1 });
|
|
@@ -150,7 +157,7 @@ describe('Outbox', () => {
|
|
|
150
157
|
online.reportOffline(); // keep it queued so we can observe the recovery
|
|
151
158
|
const outbox = new Outbox({ storage, online, scheduler: (fn) => fn() });
|
|
152
159
|
await outbox.start();
|
|
153
|
-
expect(outbox.getSnapshot().entries[0]
|
|
160
|
+
expect(outbox.getSnapshot().entries[0].status).toBe(OutboxEntryStatus.Pending);
|
|
154
161
|
outbox.stop();
|
|
155
162
|
});
|
|
156
163
|
});
|
package/src/offline/outbox.ts
CHANGED
|
@@ -159,7 +159,7 @@ export class Outbox {
|
|
|
159
159
|
updatedAt: ts,
|
|
160
160
|
...(opts.label !== undefined ? { label: opts.label } : {}),
|
|
161
161
|
};
|
|
162
|
-
await this.storage.put(entry
|
|
162
|
+
await this.storage.put(entry);
|
|
163
163
|
await this.refresh();
|
|
164
164
|
void this.drain();
|
|
165
165
|
return entry;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createContext, useContext, useSyncExternalStore } from 'react';
|
|
2
|
+
|
|
3
|
+
import { Outbox } from './outbox';
|
|
4
|
+
|
|
5
|
+
import type { OutboxSnapshot } from './types';
|
|
6
|
+
|
|
7
|
+
export const OutboxContext = createContext<Outbox | null>(null);
|
|
8
|
+
|
|
9
|
+
export function useOutbox(): Outbox {
|
|
10
|
+
const ctx = useContext(OutboxContext);
|
|
11
|
+
if (!ctx) {
|
|
12
|
+
throw new Error('useOutbox must be used within an <OutboxProvider>.');
|
|
13
|
+
}
|
|
14
|
+
return ctx;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function resolveOutbox(explicit: Outbox | undefined, ctx: Outbox | null): Outbox {
|
|
18
|
+
const instance = explicit ?? ctx;
|
|
19
|
+
if (!instance) {
|
|
20
|
+
throw new Error('An <OutboxProvider> or an explicit outbox is required.');
|
|
21
|
+
}
|
|
22
|
+
return instance;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function useOutboxSnapshot(outbox?: Outbox): OutboxSnapshot {
|
|
26
|
+
const ctx = useContext(OutboxContext);
|
|
27
|
+
const instance = resolveOutbox(outbox, ctx);
|
|
28
|
+
return useSyncExternalStore(
|
|
29
|
+
(cb) => instance.subscribe(cb),
|
|
30
|
+
() => instance.getSnapshot(),
|
|
31
|
+
() => instance.getSnapshot(),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function useOnlineStatus(outbox?: Outbox): boolean {
|
|
36
|
+
return useOutboxSnapshot(outbox).online;
|
|
37
|
+
}
|
package/src/offline/react.tsx
CHANGED
|
@@ -1,18 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
createContext,
|
|
3
|
-
useContext,
|
|
4
2
|
useEffect,
|
|
5
3
|
useMemo,
|
|
6
4
|
useRef,
|
|
7
|
-
useSyncExternalStore,
|
|
8
5
|
type ReactNode,
|
|
9
6
|
} from 'react';
|
|
10
7
|
|
|
11
8
|
import { Outbox, type OutboxOptions } from './outbox';
|
|
12
|
-
|
|
13
|
-
import type { OutboxSnapshot } from './types';
|
|
14
|
-
|
|
15
|
-
const OutboxContext = createContext<Outbox | null>(null);
|
|
9
|
+
import { OutboxContext } from './react-hooks';
|
|
16
10
|
|
|
17
11
|
export interface OutboxProviderProps {
|
|
18
12
|
/** Bring your own Outbox, or let the provider create one from `options`. */
|
|
@@ -36,36 +30,3 @@ export function OutboxProvider({ outbox, options, children }: OutboxProviderProp
|
|
|
36
30
|
|
|
37
31
|
return <OutboxContext.Provider value={instance}>{children}</OutboxContext.Provider>;
|
|
38
32
|
}
|
|
39
|
-
|
|
40
|
-
export function useOutbox(): Outbox {
|
|
41
|
-
const ctx = useContext(OutboxContext);
|
|
42
|
-
if (!ctx) {
|
|
43
|
-
throw new Error('useOutbox must be used within an <OutboxProvider>.');
|
|
44
|
-
}
|
|
45
|
-
return ctx;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function resolveOutbox(explicit: Outbox | undefined, ctx: Outbox | null): Outbox {
|
|
49
|
-
const instance = explicit ?? ctx;
|
|
50
|
-
if (!instance) {
|
|
51
|
-
throw new Error('An <OutboxProvider> or an explicit outbox is required.');
|
|
52
|
-
}
|
|
53
|
-
return instance;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Subscribe to the outbox state (offline badge, pending count, dead-letter
|
|
57
|
-
* list). Re-renders only when the snapshot changes. */
|
|
58
|
-
export function useOutboxSnapshot(outbox?: Outbox): OutboxSnapshot {
|
|
59
|
-
const ctx = useContext(OutboxContext);
|
|
60
|
-
const instance = resolveOutbox(outbox, ctx);
|
|
61
|
-
return useSyncExternalStore(
|
|
62
|
-
(cb) => instance.subscribe(cb),
|
|
63
|
-
() => instance.getSnapshot(),
|
|
64
|
-
() => instance.getSnapshot(),
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Current connectivity as tracked by the outbox. */
|
|
69
|
-
export function useOnlineStatus(outbox?: Outbox): boolean {
|
|
70
|
-
return useOutboxSnapshot(outbox).online;
|
|
71
|
-
}
|
package/src/offline/storage.ts
CHANGED
|
@@ -4,20 +4,25 @@ import type { OutboxEntry, OutboxStorage } from './types';
|
|
|
4
4
|
export class InMemoryOutboxStorage implements OutboxStorage {
|
|
5
5
|
private readonly map = new Map<string, OutboxEntry>();
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
return
|
|
7
|
+
getAll(): Promise<OutboxEntry[]> {
|
|
8
|
+
return Promise.resolve(
|
|
9
|
+
[...this.map.values()].sort((a, b) => a.createdAt - b.createdAt),
|
|
10
|
+
);
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
put(entry: OutboxEntry): Promise<void> {
|
|
12
14
|
this.map.set(entry.id, { ...entry });
|
|
15
|
+
return Promise.resolve();
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
delete(id: string): Promise<void> {
|
|
16
19
|
this.map.delete(id);
|
|
20
|
+
return Promise.resolve();
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
|
|
23
|
+
clear(): Promise<void> {
|
|
20
24
|
this.map.clear();
|
|
25
|
+
return Promise.resolve();
|
|
21
26
|
}
|
|
22
27
|
}
|
|
23
28
|
|
|
@@ -69,7 +74,7 @@ export class IndexedDbOutboxStorage implements OutboxStorage {
|
|
|
69
74
|
transaction.onabort = () =>
|
|
70
75
|
reject(transaction.error ?? new Error('indexedDB tx aborted'));
|
|
71
76
|
transaction.oncomplete = () =>
|
|
72
|
-
resolve(request ?
|
|
77
|
+
resolve(request ? request.result : undefined);
|
|
73
78
|
transaction.onerror = () =>
|
|
74
79
|
reject(transaction.error ?? new Error('indexedDB tx error'));
|
|
75
80
|
});
|