@ccslabs/xtend 0.1.0-rc.1 → 0.1.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/CHANGELOG.md +2 -0
- package/README.md +4 -0
- package/catalog/component-catalog-coverage.js +2 -0
- package/catalog/epic13-package-export-lock.js +11 -1
- package/catalog/epic13-rmt-production-readiness.js +0 -1
- package/catalog/epic18-rmt-action-effect-runtime.d.ts +36 -0
- package/catalog/epic18-rmt-action-effect-runtime.js +249 -0
- package/catalog/epic18-rmt-app-platform-authoring.d.ts +39 -0
- package/catalog/epic18-rmt-app-platform-authoring.js +319 -0
- package/catalog/epic18-rmt-app-platform-fixture.d.ts +33 -0
- package/catalog/epic18-rmt-app-platform-fixture.js +221 -0
- package/catalog/epic18-rmt-app-platform-release-handoff.d.ts +30 -0
- package/catalog/epic18-rmt-app-platform-release-handoff.js +231 -0
- package/catalog/epic18-rmt-app-platform-tooling.d.ts +38 -0
- package/catalog/epic18-rmt-app-platform-tooling.js +242 -0
- package/catalog/epic18-rmt-component-template-primitives.d.ts +33 -0
- package/catalog/epic18-rmt-component-template-primitives.js +240 -0
- package/catalog/epic18-rmt-dom-descriptor-renderer.d.ts +35 -0
- package/catalog/epic18-rmt-dom-descriptor-renderer.js +232 -0
- package/catalog/epic18-rmt-event-routing-runtime.d.ts +35 -0
- package/catalog/epic18-rmt-event-routing-runtime.js +234 -0
- package/catalog/epic18-rmt-state-selector-runtime.d.ts +34 -0
- package/catalog/epic18-rmt-state-selector-runtime.js +216 -0
- package/catalog/epic18-rmt-surface-resource-graph-runtime.d.ts +36 -0
- package/catalog/epic18-rmt-surface-resource-graph-runtime.js +256 -0
- package/catalog/surface-manager-controller.js +5 -1
- package/catalog/surface-manager-materialization.js +7 -1
- package/catalog/surface-manager-overlay-bridge.js +41 -6
- package/catalog/surface-manager-workbench-fixture.js +1 -1
- package/catalog/surface-type-capability-matrix.d.ts +61 -0
- package/catalog/surface-type-capability-matrix.js +183 -0
- package/catalog/type-exports-rmt.js +37 -1
- package/catalog/type-exports.js +3 -3
- package/components/icon-packs/lucide.js +4 -0
- package/components/manifest.json +2 -0
- package/components/prism-rmt.d.ts +34 -0
- package/components/prism-rmt.js +130 -0
- package/components/xcards.js +15 -0
- package/components/xcode.d.ts +36 -1
- package/components/xcode.js +215 -20
- package/components/xfooter.js +17 -0
- package/components/xheader.js +14 -0
- package/components/xhero.js +16 -1
- package/components/xlink.js +97 -14
- package/components/xmasonry.js +15 -0
- package/components/xplayer.d.ts +44 -2
- package/components/xplayer.js +242 -15
- package/components/xrouter.js +27 -2
- package/components/xsection.js +15 -0
- package/components/xsidepanel.js +10 -2
- package/components/xsurfacemanager-controller.d.ts +2 -1
- package/components/xsurfacemanager-controller.js +27 -3
- package/components/xsurfacemanager.d.ts +2 -0
- package/components/xsurfacemanager.js +20 -8
- package/components/xsurfaceoverlay-bridge.d.ts +20 -5
- package/components/xsurfaceoverlay-bridge.js +114 -18
- package/components/xsurfaceportal.d.ts +29 -0
- package/components/xsurfaceportal.js +122 -0
- package/components/xsurfaceregion.d.ts +50 -0
- package/components/xsurfaceregion.js +285 -0
- package/components/xsurfacewindow.js +2 -1
- package/components/xtooltip.js +89 -23
- package/docs/README.md +222 -298
- package/docs/changelog.md +107 -0
- package/docs/component-catalog-coverage.md +9 -9
- package/docs/component-platform.md +19 -1
- package/docs/component-ux-app-authoring.md +56 -63
- package/docs/components/xcode.md +83 -53
- package/docs/components/xsurfaceportal.md +32 -0
- package/docs/components/xsurfaceregion.md +37 -0
- package/docs/components.md +105 -69
- package/docs/de/README.md +264 -0
- package/docs/de/XTend-ADR.md +221 -0
- package/docs/de/a11y-keyboard-smokes.md +62 -0
- package/docs/de/about.md +18 -0
- package/docs/de/api.md +157 -0
- package/docs/de/best-practices.md +76 -0
- package/docs/de/changelog.md +107 -0
- package/docs/de/component-catalog-coverage.md +58 -0
- package/docs/de/component-lab.md +103 -0
- package/docs/de/component-long-tail-migration.md +41 -0
- package/docs/de/component-platform.md +177 -0
- package/docs/de/component-ux-app-authoring.md +123 -0
- package/docs/de/component-ux-authoring.md +96 -0
- package/docs/de/component-ux-gates.md +45 -0
- package/docs/de/components/x-rmt-lifecycle-demo-build.md +60 -0
- package/docs/de/components/xalert.md +81 -0
- package/docs/de/components/xbutton.md +103 -0
- package/docs/de/components/xcalendar.md +82 -0
- package/docs/de/components/xcards.md +128 -0
- package/docs/de/components/xcheckbox.md +102 -0
- package/docs/de/components/xcode.md +156 -0
- package/docs/de/components/xdialog.md +92 -0
- package/docs/de/components/xdrawer.md +84 -0
- package/docs/de/components/xfooter.md +126 -0
- package/docs/de/components/xform.md +128 -0
- package/docs/de/components/xheader.md +308 -0
- package/docs/de/components/xhero.md +142 -0
- package/docs/de/components/xicon.md +125 -0
- package/docs/de/components/xinput.md +129 -0
- package/docs/de/components/xlightbox.md +98 -0
- package/docs/de/components/xlink.md +109 -0
- package/docs/de/components/xmasonry.md +124 -0
- package/docs/de/components/xmenu.md +158 -0
- package/docs/de/components/xmodal.md +82 -0
- package/docs/de/components/xplayer.md +104 -0
- package/docs/de/components/xpopover.md +67 -0
- package/docs/de/components/xprogress.md +56 -0
- package/docs/de/components/xradio.md +103 -0
- package/docs/de/components/xrouter.md +260 -0
- package/docs/de/components/xsection.md +125 -0
- package/docs/de/components/xselect.md +105 -0
- package/docs/de/components/xsidepanel.md +30 -0
- package/docs/de/components/xspinner.md +102 -0
- package/docs/de/components/xstate.md +148 -0
- package/docs/de/components/xstatus.md +55 -0
- package/docs/de/components/xsummary.md +78 -0
- package/docs/de/components/xsurfacemanager.md +27 -0
- package/docs/de/components/xsurfacewindow.md +21 -0
- package/docs/de/components/xtabs.md +160 -0
- package/docs/de/components/xtextarea.md +98 -0
- package/docs/de/components/xtheme.md +167 -0
- package/docs/de/components/xtoast.md +62 -0
- package/docs/de/components/xtooltip.md +66 -0
- package/docs/de/components/xtype.md +82 -0
- package/docs/de/components/xutils.md +144 -0
- package/docs/de/components/xwriter.md +94 -0
- package/docs/de/components.md +153 -0
- package/docs/de/conditional-network-evidence-ci.md +38 -0
- package/docs/de/conditional-network-evidence.md +50 -0
- package/docs/de/core-migration-guide.md +110 -0
- package/docs/de/design-tokens.md +116 -0
- package/docs/de/docs-rmt-production-hardening.md +31 -0
- package/docs/de/enterprise-adoption.md +413 -0
- package/docs/de/enterprise-component-flex-release-handoff.md +129 -0
- package/docs/de/epic10-platform-gates.md +62 -0
- package/docs/de/epic10-release-handoff.md +81 -0
- package/docs/de/epic11-enterprise-ux-handoff.md +70 -0
- package/docs/de/epic12-rc0-handoff.md +61 -0
- package/docs/de/epic18-media-manager-vendor-upstream.md +318 -0
- package/docs/de/epic18-rmt-app-platform-release-handoff.md +67 -0
- package/docs/de/epic18-vendor-bugfixes.md +34 -0
- package/docs/de/existing-component-metadata.md +67 -0
- package/docs/de/hydration-performance-closure.md +34 -0
- package/docs/de/hydration-policies.md +71 -0
- package/docs/de/known-residual-triage.md +22 -0
- package/docs/de/manifest-import-policy.md +79 -0
- package/docs/de/manifest.md +112 -0
- package/docs/de/motion-contrast.md +67 -0
- package/docs/de/package-export-lock.md +44 -0
- package/docs/de/performance-measurements.md +106 -0
- package/docs/de/performance-regression.md +89 -0
- package/docs/de/performance.md +94 -0
- package/docs/de/previews/README.md +17 -0
- package/docs/de/prod-browser-csp-smokes.md +40 -0
- package/docs/de/public-component-types.md +79 -0
- package/docs/de/quick-start-guide.md +220 -0
- package/docs/de/rc0-adoption-guide.md +102 -0
- package/docs/de/rc0-gate-matrix.md +58 -0
- package/docs/de/rc1-gate-matrix-ci-handoff.md +56 -0
- package/docs/de/rc1-migration-notes.md +69 -0
- package/docs/de/rc1-readiness.md +46 -0
- package/docs/de/release-owner-acceptance.md +56 -0
- package/docs/de/release-report-pack-dry-run-evidence.md +39 -0
- package/docs/de/rmt-action-effect-runtime.md +81 -0
- package/docs/de/rmt-app-platform-authoring.md +54 -0
- package/docs/de/rmt-app-platform-fixture.md +46 -0
- package/docs/de/rmt-app-platform-migration-guide.md +88 -0
- package/docs/de/rmt-app-platform-tooling.md +79 -0
- package/docs/de/rmt-component-template-primitives.md +57 -0
- package/docs/de/rmt-dom-descriptor-renderer.md +64 -0
- package/docs/de/rmt-dsl-authoring-polish.md +145 -0
- package/docs/de/rmt-event-routing-runtime.md +81 -0
- package/docs/de/rmt-first-demo-app.md +77 -0
- package/docs/de/rmt-first-xtend-apps.md +129 -0
- package/docs/de/rmt-kernel-panic-recovery-incident-handoff.md +61 -0
- package/docs/de/rmt-kernel-security-hardening-migration.md +50 -0
- package/docs/de/rmt-kernel-trusted-output-authoring.md +69 -0
- package/docs/de/rmt-language-server.md +234 -0
- package/docs/de/rmt-lifecycle-demo.md +24 -0
- package/docs/de/rmt-linter.md +140 -0
- package/docs/de/rmt-node-ssr-adapter.md +100 -0
- package/docs/de/rmt-php-ssr-adapter.md +120 -0
- package/docs/de/rmt-production-readiness.md +63 -0
- package/docs/de/rmt-state-selector-runtime.md +47 -0
- package/docs/de/rmt-surface-resource-graph-runtime.md +92 -0
- package/docs/de/rmt-tooling-release-gates.md +77 -0
- package/docs/de/rmt-vnext-authoring.md +170 -0
- package/docs/de/rmt-vnext-component-primitives.md +188 -0
- package/docs/de/rmt-vnext-cross-surface-events.md +68 -0
- package/docs/de/rmt-vnext-enterprise-mfe-handoff.md +70 -0
- package/docs/de/rmt-vnext-fabric-bridge-evidence.md +81 -0
- package/docs/de/rmt-vnext-migration-notes.md +62 -0
- package/docs/de/rmt-vnext-primitive-authoring-tooling.md +247 -0
- package/docs/de/rmt-vnext-primitive-grammar-design.md +289 -0
- package/docs/de/rmt-vnext-primitive-lowering.md +108 -0
- package/docs/de/rmt-vnext-primitive-migration.md +119 -0
- package/docs/de/rmt-vnext-primitive-parser-ast.md +76 -0
- package/docs/de/rmt-vnext-primitive-semantic-graph.md +118 -0
- package/docs/de/rmt-vnext-primitives-compiler-backlog.md +739 -0
- package/docs/de/rmt-vnext-release-handoff.md +83 -0
- package/docs/de/rmt-vnext-remote-surfaces.md +90 -0
- package/docs/de/rmt-vnext-source-to-sea-gate.md +612 -0
- package/docs/de/rmt-vnext-surface-registry-enterprise.md +76 -0
- package/docs/de/screenreader-signals.md +56 -0
- package/docs/de/supply-chain-gates.md +100 -0
- package/docs/de/surface-manager-authoring-guide.md +94 -0
- package/docs/de/surface-manager-browser-lab.md +45 -0
- package/docs/de/surface-manager-component-lab.md +43 -0
- package/docs/de/surface-manager-controller.md +66 -0
- package/docs/de/surface-manager-layout-engines.md +32 -0
- package/docs/de/surface-manager-lazy-hydration.md +63 -0
- package/docs/de/surface-manager-migration-guide.md +122 -0
- package/docs/de/surface-manager-native-rmt-surfaces.md +38 -0
- package/docs/de/surface-manager-overlay-bridge.md +53 -0
- package/docs/de/surface-manager-persistence.md +30 -0
- package/docs/de/surface-manager-quality-gates.md +51 -0
- package/docs/de/surface-manager-release-handoff.md +68 -0
- package/docs/de/surface-manager-remote-policy.md +54 -0
- package/docs/de/surface-manager-rmt-authoring.md +102 -0
- package/docs/de/surface-manager-route-lifecycle.md +59 -0
- package/docs/de/surface-manager-runtime-release-handoff.md +69 -0
- package/docs/de/surface-manager-side-panel-runtime.md +36 -0
- package/docs/de/surface-manager-stack-policy.md +39 -0
- package/docs/de/surface-manager-window-runtime.md +47 -0
- package/docs/de/surface-manager-workbench-fixture.md +43 -0
- package/docs/de/third-party-design-authoring.md +406 -0
- package/docs/de/trusted-dom-boundary-browser-proof.md +32 -0
- package/docs/de/trusted-dom-sanitizing.md +110 -0
- package/docs/de/type-exports.md +61 -0
- package/docs/de/typescript-components.md +63 -0
- package/docs/de/visual-browser-regression.md +83 -0
- package/docs/de/visual-owner-artifacts.md +46 -0
- package/docs/de/visual-snapshot-automation.md +87 -0
- package/docs/de/xtend-api-types.md +55 -0
- package/docs/de/xtend-builder-types.md +55 -0
- package/docs/de/xtend-catalog-types.md +44 -0
- package/docs/de/xtend-fabric-rmt-lane-mapping.md +143 -0
- package/docs/de/xtend-fabric.md +474 -0
- package/docs/de/xtend-loader-types.md +58 -0
- package/docs/de/xtend-loader.md +265 -0
- package/docs/de/xtend-policy-types.md +38 -0
- package/docs/de/xtend-rmt-types.md +40 -0
- package/docs/de/xtend-vendor-types.md +36 -0
- package/docs/de/xtendrmt-app-dsl.md +334 -0
- package/docs/de/xtendrmt-migration-guide.md +266 -0
- package/docs/de/xtendrmt-native-authoring.md +333 -0
- package/docs/de/xtendrmt-overview.md +109 -0
- package/docs/de/xtendrmt-parsedown-scheduling.md +301 -0
- package/docs/de/xtendrmt-runtime-bridge.md +155 -0
- package/docs/en/README.md +163 -0
- package/docs/en/XTend-ADR.md +221 -0
- package/docs/en/a11y-keyboard-smokes.md +68 -0
- package/docs/en/about.md +25 -0
- package/docs/en/api.md +171 -0
- package/docs/en/best-practices.md +125 -0
- package/docs/en/changelog.md +104 -0
- package/docs/en/component-catalog-coverage.md +104 -0
- package/docs/en/component-lab.md +103 -0
- package/docs/en/component-long-tail-migration.md +41 -0
- package/docs/en/component-platform.md +243 -0
- package/docs/en/component-ux-app-authoring.md +118 -0
- package/docs/en/component-ux-authoring.md +96 -0
- package/docs/en/component-ux-gates.md +45 -0
- package/docs/en/components/x-rmt-lifecycle-demo-build.md +75 -0
- package/docs/en/components/xalert.md +94 -0
- package/docs/en/components/xbutton.md +118 -0
- package/docs/en/components/xcalendar.md +95 -0
- package/docs/en/components/xcards.md +139 -0
- package/docs/en/components/xcheckbox.md +118 -0
- package/docs/en/components/xcode.md +153 -0
- package/docs/en/components/xdialog.md +108 -0
- package/docs/en/components/xdrawer.md +110 -0
- package/docs/en/components/xfooter.md +138 -0
- package/docs/en/components/xform.md +147 -0
- package/docs/en/components/xheader.md +308 -0
- package/docs/en/components/xhero.md +157 -0
- package/docs/en/components/xicon.md +149 -0
- package/docs/en/components/xinput.md +147 -0
- package/docs/en/components/xlightbox.md +113 -0
- package/docs/en/components/xlink.md +130 -0
- package/docs/en/components/xmasonry.md +136 -0
- package/docs/en/components/xmenu.md +185 -0
- package/docs/en/components/xmodal.md +102 -0
- package/docs/en/components/xplayer.md +114 -0
- package/docs/en/components/xpopover.md +87 -0
- package/docs/en/components/xprogress.md +73 -0
- package/docs/en/components/xradio.md +119 -0
- package/docs/en/components/xrouter.md +260 -0
- package/docs/en/components/xsection.md +136 -0
- package/docs/en/components/xselect.md +122 -0
- package/docs/en/components/xsidepanel.md +48 -0
- package/docs/en/components/xspinner.md +118 -0
- package/docs/en/components/xstate.md +163 -0
- package/docs/en/components/xstatus.md +71 -0
- package/docs/en/components/xsummary.md +90 -0
- package/docs/en/components/xsurfacemanager.md +42 -0
- package/docs/en/components/xsurfacewindow.md +31 -0
- package/docs/en/components/xtabs.md +187 -0
- package/docs/en/components/xtextarea.md +115 -0
- package/docs/en/components/xtheme.md +203 -0
- package/docs/en/components/xtoast.md +78 -0
- package/docs/en/components/xtooltip.md +85 -0
- package/docs/en/components/xtype.md +91 -0
- package/docs/en/components/xutils.md +161 -0
- package/docs/en/components/xwriter.md +106 -0
- package/docs/en/components.md +151 -0
- package/docs/en/conditional-network-evidence-ci.md +38 -0
- package/docs/en/conditional-network-evidence.md +50 -0
- package/docs/en/core-migration-guide.md +110 -0
- package/docs/en/design-tokens.md +137 -0
- package/docs/en/docs-rmt-production-hardening.md +31 -0
- package/docs/en/enterprise-adoption.md +413 -0
- package/docs/en/enterprise-component-flex-release-handoff.md +129 -0
- package/docs/en/epic10-platform-gates.md +62 -0
- package/docs/en/epic10-release-handoff.md +81 -0
- package/docs/en/epic11-enterprise-ux-handoff.md +70 -0
- package/docs/en/epic12-rc0-handoff.md +61 -0
- package/docs/en/epic18-media-manager-vendor-upstream.md +232 -0
- package/docs/en/epic18-rmt-app-platform-release-handoff.md +60 -0
- package/docs/en/epic18-vendor-bugfixes.md +29 -0
- package/docs/en/existing-component-metadata.md +67 -0
- package/docs/en/hydration-performance-closure.md +34 -0
- package/docs/en/hydration-policies.md +75 -0
- package/docs/en/known-residual-triage.md +22 -0
- package/docs/en/manifest-import-policy.md +81 -0
- package/docs/en/manifest.md +135 -0
- package/docs/en/motion-contrast.md +67 -0
- package/docs/en/package-export-lock.md +44 -0
- package/docs/en/performance-measurements.md +106 -0
- package/docs/en/performance-regression.md +89 -0
- package/docs/en/performance.md +132 -0
- package/docs/en/previews/README.md +17 -0
- package/docs/en/prod-browser-csp-smokes.md +40 -0
- package/docs/en/public-component-types.md +79 -0
- package/docs/en/quick-start-guide.md +189 -0
- package/docs/en/rc0-adoption-guide.md +102 -0
- package/docs/en/rc0-gate-matrix.md +58 -0
- package/docs/en/rc1-gate-matrix-ci-handoff.md +56 -0
- package/docs/en/rc1-migration-notes.md +69 -0
- package/docs/en/rc1-readiness.md +46 -0
- package/docs/en/release-owner-acceptance.md +56 -0
- package/docs/en/release-report-pack-dry-run-evidence.md +39 -0
- package/docs/en/rmt-action-effect-runtime.md +101 -0
- package/docs/en/rmt-app-platform-authoring.md +47 -0
- package/docs/en/rmt-app-platform-fixture.md +35 -0
- package/docs/en/rmt-app-platform-migration-guide.md +75 -0
- package/docs/en/rmt-app-platform-tooling.md +58 -0
- package/docs/en/rmt-component-template-primitives.md +49 -0
- package/docs/en/rmt-dom-descriptor-renderer.md +54 -0
- package/docs/en/rmt-dsl-authoring-polish.md +143 -0
- package/docs/en/rmt-event-routing-runtime.md +98 -0
- package/docs/en/rmt-first-demo-app.md +87 -0
- package/docs/en/rmt-first-xtend-apps.md +127 -0
- package/docs/en/rmt-kernel-panic-recovery-incident-handoff.md +60 -0
- package/docs/en/rmt-kernel-security-hardening-migration.md +49 -0
- package/docs/en/rmt-kernel-trusted-output-authoring.md +68 -0
- package/docs/en/rmt-language-server.md +243 -0
- package/docs/en/rmt-lifecycle-demo.md +23 -0
- package/docs/en/rmt-linter.md +146 -0
- package/docs/en/rmt-node-ssr-adapter.md +99 -0
- package/docs/en/rmt-php-ssr-adapter.md +118 -0
- package/docs/en/rmt-production-readiness.md +63 -0
- package/docs/en/rmt-state-selector-runtime.md +34 -0
- package/docs/en/rmt-surface-resource-graph-runtime.md +68 -0
- package/docs/en/rmt-tooling-release-gates.md +77 -0
- package/docs/en/rmt-vnext-authoring.md +102 -0
- package/docs/en/rmt-vnext-component-primitives.md +185 -0
- package/docs/en/rmt-vnext-cross-surface-events.md +59 -0
- package/docs/en/rmt-vnext-enterprise-mfe-handoff.md +62 -0
- package/docs/en/rmt-vnext-fabric-bridge-evidence.md +64 -0
- package/docs/en/rmt-vnext-migration-notes.md +62 -0
- package/docs/en/rmt-vnext-primitive-authoring-tooling.md +174 -0
- package/docs/en/rmt-vnext-primitive-grammar-design.md +268 -0
- package/docs/en/rmt-vnext-primitive-lowering.md +91 -0
- package/docs/en/rmt-vnext-primitive-migration.md +93 -0
- package/docs/en/rmt-vnext-primitive-parser-ast.md +59 -0
- package/docs/en/rmt-vnext-primitive-semantic-graph.md +103 -0
- package/docs/en/rmt-vnext-primitives-compiler-backlog.md +327 -0
- package/docs/en/rmt-vnext-release-handoff.md +83 -0
- package/docs/en/rmt-vnext-remote-surfaces.md +81 -0
- package/docs/en/rmt-vnext-source-to-sea-gate.md +482 -0
- package/docs/en/rmt-vnext-surface-registry-enterprise.md +68 -0
- package/docs/en/screenreader-signals.md +56 -0
- package/docs/en/supply-chain-gates.md +106 -0
- package/docs/en/surface-manager-authoring-guide.md +94 -0
- package/docs/en/surface-manager-browser-lab.md +45 -0
- package/docs/en/surface-manager-component-lab.md +43 -0
- package/docs/en/surface-manager-controller.md +66 -0
- package/docs/en/surface-manager-layout-engines.md +32 -0
- package/docs/en/surface-manager-lazy-hydration.md +63 -0
- package/docs/en/surface-manager-migration-guide.md +113 -0
- package/docs/en/surface-manager-native-rmt-surfaces.md +38 -0
- package/docs/en/surface-manager-overlay-bridge.md +53 -0
- package/docs/en/surface-manager-persistence.md +30 -0
- package/docs/en/surface-manager-quality-gates.md +51 -0
- package/docs/en/surface-manager-release-handoff.md +68 -0
- package/docs/en/surface-manager-remote-policy.md +54 -0
- package/docs/en/surface-manager-rmt-authoring.md +89 -0
- package/docs/en/surface-manager-route-lifecycle.md +59 -0
- package/docs/en/surface-manager-runtime-release-handoff.md +69 -0
- package/docs/en/surface-manager-side-panel-runtime.md +36 -0
- package/docs/en/surface-manager-stack-policy.md +39 -0
- package/docs/en/surface-manager-window-runtime.md +47 -0
- package/docs/en/surface-manager-workbench-fixture.md +43 -0
- package/docs/en/third-party-design-authoring.md +406 -0
- package/docs/en/trusted-dom-boundary-browser-proof.md +32 -0
- package/docs/en/trusted-dom-sanitizing.md +124 -0
- package/docs/en/type-exports.md +61 -0
- package/docs/en/typescript-components.md +63 -0
- package/docs/en/visual-browser-regression.md +83 -0
- package/docs/en/visual-owner-artifacts.md +46 -0
- package/docs/en/visual-snapshot-automation.md +87 -0
- package/docs/en/xtend-api-types.md +55 -0
- package/docs/en/xtend-builder-types.md +55 -0
- package/docs/en/xtend-catalog-types.md +44 -0
- package/docs/en/xtend-fabric-rmt-lane-mapping.md +143 -0
- package/docs/en/xtend-fabric.md +474 -0
- package/docs/en/xtend-loader-types.md +58 -0
- package/docs/en/xtend-loader.md +265 -0
- package/docs/en/xtend-policy-types.md +38 -0
- package/docs/en/xtend-rmt-types.md +40 -0
- package/docs/en/xtend-vendor-types.md +36 -0
- package/docs/en/xtendrmt-app-dsl.md +331 -0
- package/docs/en/xtendrmt-migration-guide.md +256 -0
- package/docs/en/xtendrmt-native-authoring.md +336 -0
- package/docs/en/xtendrmt-overview.md +63 -0
- package/docs/en/xtendrmt-parsedown-scheduling.md +301 -0
- package/docs/en/xtendrmt-runtime-bridge.md +155 -0
- package/docs/enterprise-adoption.md +4 -2
- package/docs/epic18-media-manager-vendor-upstream.md +318 -0
- package/docs/epic18-rmt-app-platform-release-handoff.md +67 -0
- package/docs/epic18-vendor-bugfixes.md +34 -0
- package/docs/index.php +1056 -109
- package/docs/manifest.md +8 -2
- package/docs/menu.json +986 -133
- package/docs/package-export-lock.md +2 -2
- package/docs/public-component-types.md +2 -2
- package/docs/quick-start-guide.md +126 -58
- package/docs/rmt-action-effect-runtime.md +101 -0
- package/docs/rmt-app-platform-authoring.md +54 -0
- package/docs/rmt-app-platform-fixture.md +46 -0
- package/docs/rmt-app-platform-migration-guide.md +88 -0
- package/docs/rmt-app-platform-tooling.md +79 -0
- package/docs/rmt-component-template-primitives.md +57 -0
- package/docs/rmt-dom-descriptor-renderer.md +64 -0
- package/docs/rmt-dsl-authoring-polish.md +67 -44
- package/docs/rmt-event-routing-runtime.md +98 -0
- package/docs/rmt-first-demo-app.md +2 -2
- package/docs/rmt-first-xtend-apps.md +70 -46
- package/docs/rmt-language-server.md +61 -4
- package/docs/rmt-lifecycle-demo.md +1 -2
- package/docs/rmt-node-ssr-adapter.md +144 -0
- package/docs/rmt-php-ssr-adapter.md +158 -0
- package/docs/rmt-state-selector-runtime.md +47 -0
- package/docs/rmt-surface-resource-graph-runtime.md +92 -0
- package/docs/rmt-vnext-authoring.md +128 -18
- package/docs/rmt-vnext-component-primitives.md +188 -0
- package/docs/rmt-vnext-fabric-bridge-evidence.md +81 -0
- package/docs/rmt-vnext-primitive-authoring-tooling.md +247 -0
- package/docs/rmt-vnext-primitive-grammar-design.md +289 -0
- package/docs/rmt-vnext-primitive-lowering.md +108 -0
- package/docs/rmt-vnext-primitive-migration.md +119 -0
- package/docs/rmt-vnext-primitive-parser-ast.md +76 -0
- package/docs/rmt-vnext-primitive-semantic-graph.md +118 -0
- package/docs/rmt-vnext-primitives-compiler-backlog.md +742 -0
- package/docs/rmt-vnext-release-handoff.md +14 -0
- package/docs/rmt-vnext-source-to-sea-gate.md +629 -0
- package/docs/surface-manager-migration-guide.md +34 -6
- package/docs/surface-manager-overlay-bridge.md +9 -4
- package/docs/surface-manager-rmt-authoring.md +50 -34
- package/docs/surface-manager-workbench-fixture.md +1 -2
- package/docs/third-party-design-authoring.md +1 -1
- package/docs/type-exports.md +3 -3
- package/docs/utils/pageloader.js +811 -62
- package/docs/visual-browser-regression.md +1 -1
- package/docs/xtend-rmt-types.md +3 -2
- package/docs/xtendrmt-app-dsl.md +187 -122
- package/docs/xtendrmt-docs-shell-vnext.rmt +165 -0
- package/docs/xtendrmt-migration-guide.md +48 -17
- package/docs/xtendrmt-native-authoring.md +213 -217
- package/docs/xtendrmt-overview.md +81 -61
- package/docs/xtendrmt-parsedown-scheduling.md +23 -8
- package/fabric/package.json +1 -1
- package/package.json +684 -21
- package/tools/package.json +5 -1
- package/tools/rmt-editor/vscode/README.md +72 -5
- package/tools/rmt-editor/vscode/XTend-Logo.png +0 -0
- package/tools/rmt-editor/vscode/extension.d.ts +33 -0
- package/tools/rmt-editor/vscode/extension.js +1816 -7
- package/tools/rmt-editor/vscode/language-configuration.json +2 -1
- package/tools/rmt-editor/vscode/package.json +193 -2
- package/tools/rmt-editor/vscode/snippets/rmt.code-snippets +41 -0
- package/tools/rmt-editor/vscode/syntaxes/rmt.tmLanguage.json +103 -1
- package/tools/rmt-editor/vscode/templates/launch.json +70 -0
- package/tools/rmt-editor/vscode/templates/tasks.json +172 -0
- package/tools/rmt-editor/vscode/xtend-rmt-language-0.0.0-enterprise-readiness.vsix +0 -0
- package/tools/rmt-language/app-platform-tooling.d.ts +128 -0
- package/tools/rmt-language/app-platform-tooling.js +677 -0
- package/tools/rmt-language/completions.d.ts +5 -0
- package/tools/rmt-language/completions.js +185 -3
- package/tools/rmt-language/diagnostics.js +54 -0
- package/tools/rmt-language/hover.js +36 -0
- package/tools/rmt-language/rmt-tooling-public-types.d.ts +7 -0
- package/tools/rmt-language/rules/app-platform-policy.js +39 -0
- package/tools/rmt-language/rules/index.js +5 -1
- package/tools/rmt-language/semantic-graph.d.ts +6 -0
- package/tools/rmt-language/semantic-graph.js +928 -0
- package/tools/rmt-language/snippets/index.js +44 -0
- package/tools/rmt-language/snippets/rmt.code-snippets +41 -0
- package/tools/rmt-language/vnext-compatibility.d.ts +10 -0
- package/tools/rmt-language/vnext-compatibility.js +642 -0
- package/tools/rmt-language/vnext-compiler.d.ts +5 -0
- package/tools/rmt-language/vnext-compiler.js +863 -17
- package/tools/rmt-language/vnext-parser.js +725 -9
- package/tools/rmt-language/vnext-release.d.ts +1 -0
- package/tools/rmt-language/vnext-release.js +20 -0
- package/tools/rmt-language/vnext-source-to-sea.d.ts +33 -0
- package/tools/rmt-language/vnext-source-to-sea.js +2227 -0
- package/tools/rmt-language/vnext-surfaces.js +111 -52
- package/tools/rmt-language/vnext-tooling.d.ts +19 -1
- package/tools/rmt-language/vnext-tooling.js +1247 -5
- package/tools/rmt-language-server/protocol.js +3 -0
- package/tools/rmt-language-server/server.d.ts +2 -0
- package/tools/rmt-language-server/server.js +176 -22
- package/tools/rmt-linter/cli.d.ts +2 -0
- package/tools/rmt-linter/cli.js +62 -0
- package/xtend-builder/generators/registry.js +11 -0
- package/xtend-builder/generators/rmt-app-platform.js +239 -0
- package/xtend-builder/generators/rmt-lifecycle-demo.js +3 -11
- package/xtend-builder/lib/cli.js +38 -0
- package/xtend-builder/package.json +3 -3
- package/xtend-builder/scaffold.config.js +29 -2
- package/xtend.css +49 -2
- package/xtendrmt/package.json +49 -1
- package/xtendrmt/rmt-action-effect-runtime.d.ts +126 -0
- package/xtendrmt/rmt-action-effect-runtime.js +494 -0
- package/xtendrmt/rmt-component-capability-registry.d.ts +180 -0
- package/xtendrmt/rmt-component-capability-registry.js +636 -0
- package/xtendrmt/rmt-core.d.ts +6 -0
- package/xtendrmt/rmt-core.esm.js +32 -6
- package/xtendrmt/rmt-dom-descriptor-renderer.d.ts +107 -0
- package/xtendrmt/rmt-dom-descriptor-renderer.js +1066 -0
- package/xtendrmt/rmt-event-routing-runtime.d.ts +144 -0
- package/xtendrmt/rmt-event-routing-runtime.js +666 -0
- package/xtendrmt/rmt-lifecycle-demo.app.js +2 -2
- package/xtendrmt/rmt-lifecycle-demo.core.json +4 -0
- package/xtendrmt/rmt-lifecycle-demo.rmt-build.app.js +1 -1
- package/xtendrmt/rmt-lifecycle-demo.rmt-build.scaffold.json +2 -2
- package/xtendrmt/rmt-lifecycle-demo.scaffold.json +4 -4
- package/xtendrmt/rmt-native-shell-runtime.d.ts +77 -0
- package/xtendrmt/rmt-native-shell-runtime.js +309 -0
- package/xtendrmt/rmt-node-ssr-adapter.d.ts +197 -0
- package/xtendrmt/rmt-node-ssr-adapter.js +1006 -0
- package/xtendrmt/rmt-php-ssr-adapter.php +976 -0
- package/xtendrmt/rmt-runtime.browser.js +32 -6
- package/xtendrmt/rmt-runtime.esm.js +32 -6
- package/xtendrmt/rmt-state-selector-runtime.d.ts +166 -0
- package/xtendrmt/rmt-state-selector-runtime.js +866 -0
- package/xtendrmt/rmt-surface-resource-graph-runtime.d.ts +224 -0
- package/xtendrmt/rmt-surface-resource-graph-runtime.js +932 -0
- package/xtendrmt/rmt-vnext-enterprise-mfe-demo.core.json +3 -0
- package/xtendrmt/rmt-vnext-reference-demo.core.json +3 -0
- package/xtendrmt/xtendrmt-bestcase-demo.core.json +3420 -372
- package/xtendrmt/xtendrmt-bestcase-demo.js +424 -8
- package/xtendrmt/xtendrmt-bestcase-demo.rmt +214 -6
|
@@ -0,0 +1,2227 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const http = require('http');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { spawn, spawnSync } = require('child_process');
|
|
5
|
+
const { pathToFileURL } = require('url');
|
|
6
|
+
const {
|
|
7
|
+
RMT_APP_PLATFORM_RECORDS_SCHEMA,
|
|
8
|
+
RMT_KERNEL_BOUNDARY,
|
|
9
|
+
RMT_KERNEL_RECORDS_SCHEMA,
|
|
10
|
+
compileRmtVNextSource
|
|
11
|
+
} = require('./vnext-compiler');
|
|
12
|
+
const {
|
|
13
|
+
createXtendFabric,
|
|
14
|
+
CONTRACTS: FABRIC_CONTRACTS
|
|
15
|
+
} = require('../../fabric/xtend-fabric');
|
|
16
|
+
const {
|
|
17
|
+
CONTRACTS: FABRIC_RMT_CONTRACTS,
|
|
18
|
+
resolveRmtScheduleForFiber
|
|
19
|
+
} = require('../../fabric/rmt-lane-mapping');
|
|
20
|
+
|
|
21
|
+
const RMT_VNEXT_SOURCE_TO_SEA_SCHEMA = 'xtend.rmt.vnext.source-to-sea-gate.v1';
|
|
22
|
+
const RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_SCHEMA = 'xtend.rmt.vnext.source-to-sea-evidence.v1';
|
|
23
|
+
const RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_REPORT_SCHEMA = 'xtend.rmt.vnext.source-to-sea-evidence-report.v1';
|
|
24
|
+
const RMT_VNEXT_SOURCE_TO_SEA_OBJECT_MATRIX_SCHEMA = 'xtend.rmt.vnext.source-to-sea-object-matrix.v1';
|
|
25
|
+
const RMT_VNEXT_SOURCE_TO_SEA_CI_ARTIFACT_SCHEMA = 'xtend.rmt.vnext.source-to-sea-ci-artifact-validation.v1';
|
|
26
|
+
const RMT_VNEXT_SOURCE_TO_SEA_BROWSER_PROBE_SCHEMA = 'xtend.rmt.vnext.source-to-sea-browser-probe.v1';
|
|
27
|
+
const RMT_VNEXT_SOURCE_TO_SEA_BROWSER_RESULT_VALIDATION_SCHEMA = 'xtend.rmt.vnext.source-to-sea-browser-result-validation.v1';
|
|
28
|
+
const RMT_VNEXT_SOURCE_TO_SEA_WORKPACKAGE = 'RMT-VNEXT-PRIM-06';
|
|
29
|
+
const RMT_VNEXT_SOURCE_TO_SEA_CLEANUP_DIAGNOSTIC_CODES = Object.freeze({
|
|
30
|
+
resourceMissing: 'rmt.vnext.source_to_sea.cleanup_resource_missing',
|
|
31
|
+
ownerMismatch: 'rmt.vnext.source_to_sea.cleanup_owner_mismatch',
|
|
32
|
+
disposePolicyMissing: 'rmt.vnext.source_to_sea.cleanup_dispose_policy_missing',
|
|
33
|
+
kindMismatch: 'rmt.vnext.source_to_sea.cleanup_kind_mismatch'
|
|
34
|
+
});
|
|
35
|
+
const RMT_VNEXT_FABRIC_BRIDGE_EVIDENCE_SCHEMA = 'xtend.rmt.vnext.fabric-bridge-evidence.v1';
|
|
36
|
+
const RMT_VNEXT_FABRIC_BRIDGE_WORKPACKAGE = 'RMT-VNEXT-PRIM-05';
|
|
37
|
+
const RMT_VNEXT_BROWSER_EXECUTION_EVIDENCE_SCHEMA = 'xtend.rmt.vnext.browser-execution-evidence.v1';
|
|
38
|
+
const RMT_VNEXT_HOST_ADAPTER_TELEMETRY_SCHEMA = FABRIC_CONTRACTS.componentLifecycleTelemetry;
|
|
39
|
+
const RMT_VNEXT_ROUTE_COMPONENT_FIBER_EVIDENCE_SCHEMA = 'xtend.rmt.vnext.route-component-fiber-evidence.v1';
|
|
40
|
+
const RMT_VNEXT_ROUTE_COMPONENT_FIBER_SCENARIOS = Object.freeze({
|
|
41
|
+
component: Object.freeze({
|
|
42
|
+
componentRef: 'x-status',
|
|
43
|
+
routeRef: '/rmt-vnext-source-to-sea',
|
|
44
|
+
mountScheduleRef: 'component.visible.mount',
|
|
45
|
+
hydrateScheduleRef: 'component.idle.hydrate'
|
|
46
|
+
}),
|
|
47
|
+
route: Object.freeze({
|
|
48
|
+
routerRef: 'xtend.xrouter',
|
|
49
|
+
routeRef: '/rmt-vnext-source-to-sea',
|
|
50
|
+
routeId: 'rmt-vnext-source-to-sea',
|
|
51
|
+
navigateScheduleRef: 'ui.user-blocking.input',
|
|
52
|
+
renderScheduleRef: 'route.transition.render'
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
const RMT_VNEXT_FABRIC_BRIDGE_LANE_MATRIX = Object.freeze([
|
|
56
|
+
Object.freeze({ lane: 'user-blocking', kind: 'event.handler', phase: 'event' }),
|
|
57
|
+
Object.freeze({ lane: 'transition', kind: 'route.render', phase: 'render', routeRef: '/rmt-vnext-source-to-sea' }),
|
|
58
|
+
Object.freeze({ lane: 'idle', kind: 'component.hydrate', phase: 'hydrate' }),
|
|
59
|
+
Object.freeze({ lane: 'background', kind: 'component.disconnect', phase: 'disconnect' }),
|
|
60
|
+
Object.freeze({ lane: 'diagnostics', kind: 'diagnostics.snapshot', phase: 'diagnostics' })
|
|
61
|
+
]);
|
|
62
|
+
const RMT_VNEXT_SOURCE_TO_SEA_MODULE_PATH = 'tools/rmt-language/vnext-source-to-sea.js';
|
|
63
|
+
const RMT_VNEXT_SOURCE_TO_SEA_SUITE_PATH = 'tests/rmt-language/rmt_vnext_source_to_sea_suite.js';
|
|
64
|
+
const RMT_VNEXT_SOURCE_TO_SEA_FIXTURE_PATH = 'tests/rmt-language/fixtures/vnext-source-to-sea.rmt';
|
|
65
|
+
const RMT_VNEXT_SOURCE_TO_SEA_BROWSER_FIXTURE_PATH = 'tests/browser/fixtures/rmt-vnext-source-to-sea-smoke.html';
|
|
66
|
+
const RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_REPORT_PATH = '.xtend-test-results/xtend-rmt-vnext-source-to-sea-evidence.json';
|
|
67
|
+
const RMT_VNEXT_SOURCE_TO_SEA_RESULT_KEY = '__xtendRmtVNextSourceToSeaResult';
|
|
68
|
+
const RMT_VNEXT_SOURCE_TO_SEA_CI_BROWSER_DRIVER = 'chromedriver';
|
|
69
|
+
const RMT_VNEXT_SOURCE_TO_SEA_CI_BROWSER_NAME = 'chrome';
|
|
70
|
+
const RMT_VNEXT_SOURCE_TO_SEA_CI_WEBDRIVER_PORT = 9515;
|
|
71
|
+
const RMT_VNEXT_SOURCE_TO_SEA_SUPPORTED_BROWSER_DRIVERS = Object.freeze([
|
|
72
|
+
'webdriver',
|
|
73
|
+
'chromedriver',
|
|
74
|
+
'chrome',
|
|
75
|
+
'chromium',
|
|
76
|
+
'firefox',
|
|
77
|
+
'geckodriver',
|
|
78
|
+
'safari',
|
|
79
|
+
'safaridriver',
|
|
80
|
+
'edge',
|
|
81
|
+
'msedge',
|
|
82
|
+
'msedgedriver'
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
function toArray(value) {
|
|
86
|
+
return Array.isArray(value) ? value : [];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function escapeRegExp(value) {
|
|
90
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function parseJsonScript(html, scriptId) {
|
|
94
|
+
if (typeof html !== 'string' || !html) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const pattern = new RegExp(`<script[^>]*id=["']${escapeRegExp(scriptId)}["'][^>]*>([\\s\\S]*?)<\\/script>`, 'i');
|
|
99
|
+
const match = html.match(pattern);
|
|
100
|
+
if (!match) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
return JSON.parse(match[1]);
|
|
106
|
+
} catch (_) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function artifactCount(coreDocument) {
|
|
112
|
+
if (!coreDocument) {
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return [
|
|
117
|
+
coreDocument.appPlatform,
|
|
118
|
+
coreDocument.kernelRecords,
|
|
119
|
+
coreDocument.sourceMap,
|
|
120
|
+
coreDocument.states,
|
|
121
|
+
coreDocument.selectors,
|
|
122
|
+
coreDocument.actions,
|
|
123
|
+
coreDocument.surfaces,
|
|
124
|
+
coreDocument.events,
|
|
125
|
+
coreDocument.resources
|
|
126
|
+
].filter((artifact) => Array.isArray(artifact) ? artifact.length > 0 : Boolean(artifact)).length;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function findSurface(coreDocument, primitiveId) {
|
|
130
|
+
const appSurface = toArray(coreDocument && coreDocument.appPlatform && coreDocument.appPlatform.surfaces)
|
|
131
|
+
.find((surface) => surface.id === primitiveId);
|
|
132
|
+
const coreSurface = toArray(coreDocument && coreDocument.surfaces)
|
|
133
|
+
.find((surface) => surface.name === primitiveId || surface.id === `surface:${coreDocument.manifest && coreDocument.manifest.documentId}/${primitiveId}`);
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
appSurface: appSurface || null,
|
|
137
|
+
coreSurface: coreSurface || null
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function findEvent(coreDocument, coreSurface, actionId) {
|
|
142
|
+
const eventRefs = new Set(toArray(coreSurface && coreSurface.eventRefs));
|
|
143
|
+
return toArray(coreDocument && coreDocument.events)
|
|
144
|
+
.find((event) => event.action === actionId || eventRefs.has(event.id)) || null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function findSourceMapEntry(coreDocument, record) {
|
|
148
|
+
return toArray(coreDocument && coreDocument.sourceMap)
|
|
149
|
+
.find((entry) => entry.id === (record && record.sourceRef)) || null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function findSchedule(kernelRecords, coreSurface, preferredLane) {
|
|
153
|
+
const schedules = toArray(kernelRecords && kernelRecords.schedules);
|
|
154
|
+
return schedules.find((schedule) => schedule.scope && schedule.scope.surface === (coreSurface && coreSurface.id) && schedule.lane === preferredLane)
|
|
155
|
+
|| schedules.find((schedule) => schedule.lane === preferredLane)
|
|
156
|
+
|| schedules[0]
|
|
157
|
+
|| null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function findFiber(kernelRecords, schedule) {
|
|
161
|
+
const operationRefs = new Set(toArray(schedule && schedule.operationRefs));
|
|
162
|
+
return toArray(kernelRecords && kernelRecords.fibers)
|
|
163
|
+
.find((fiber) => operationRefs.has(fiber.operation))
|
|
164
|
+
|| toArray(kernelRecords && kernelRecords.fibers)[0]
|
|
165
|
+
|| null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function containsKernelHostImport(kernelRecords) {
|
|
169
|
+
return String(JSON.stringify(kernelRecords || {})).includes('@ccslabs/xtend/components');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function createCheck(name, ok, details = null) {
|
|
173
|
+
return { name, ok: Boolean(ok), details };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function createIncrementingClock() {
|
|
177
|
+
let tick = 0;
|
|
178
|
+
return () => new Date(Date.UTC(2026, 4, 19, 12, 0, tick++));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function wait(ms) {
|
|
182
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function requestJson(options, payload) {
|
|
186
|
+
return new Promise((resolve, reject) => {
|
|
187
|
+
const body = payload ? JSON.stringify(payload) : '';
|
|
188
|
+
const request = http.request({
|
|
189
|
+
...options,
|
|
190
|
+
headers: {
|
|
191
|
+
'content-type': 'application/json',
|
|
192
|
+
'content-length': Buffer.byteLength(body),
|
|
193
|
+
...(options.headers || {})
|
|
194
|
+
}
|
|
195
|
+
}, (response) => {
|
|
196
|
+
let data = '';
|
|
197
|
+
response.setEncoding('utf8');
|
|
198
|
+
response.on('data', (chunk) => {
|
|
199
|
+
data += chunk;
|
|
200
|
+
});
|
|
201
|
+
response.on('end', () => {
|
|
202
|
+
let parsed = null;
|
|
203
|
+
if (data) {
|
|
204
|
+
try {
|
|
205
|
+
parsed = JSON.parse(data);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
reject(error);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
resolve({
|
|
212
|
+
statusCode: response.statusCode,
|
|
213
|
+
body: parsed
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
request.on('error', reject);
|
|
219
|
+
if (body) {
|
|
220
|
+
request.write(body);
|
|
221
|
+
}
|
|
222
|
+
request.end();
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function parseWebDriverUrl(value) {
|
|
227
|
+
const target = new URL(value);
|
|
228
|
+
return {
|
|
229
|
+
hostname: target.hostname,
|
|
230
|
+
port: target.port || (target.protocol === 'https:' ? 443 : 80),
|
|
231
|
+
prefix: target.pathname && target.pathname !== '/' ? target.pathname.replace(/\/$/u, '') : ''
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function findSafariDriver() {
|
|
236
|
+
const candidates = [
|
|
237
|
+
'/System/Cryptexes/App/usr/bin/safaridriver',
|
|
238
|
+
'/usr/bin/safaridriver'
|
|
239
|
+
];
|
|
240
|
+
return candidates.find((candidate) => {
|
|
241
|
+
try {
|
|
242
|
+
fs.accessSync(candidate);
|
|
243
|
+
return true;
|
|
244
|
+
} catch (_) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}) || null;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function findExecutableOnPath(name) {
|
|
251
|
+
const pathEntries = String(process.env.PATH || '').split(path.delimiter).filter(Boolean);
|
|
252
|
+
for (const pathEntry of pathEntries) {
|
|
253
|
+
const candidate = path.join(pathEntry, name);
|
|
254
|
+
try {
|
|
255
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
256
|
+
return candidate;
|
|
257
|
+
} catch (_) {
|
|
258
|
+
// Keep scanning PATH.
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function executableCandidate(value, executableName = 'chromedriver') {
|
|
265
|
+
if (!value) {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
return [
|
|
269
|
+
value,
|
|
270
|
+
path.join(value, executableName)
|
|
271
|
+
];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function findChromeDriver(options = {}) {
|
|
275
|
+
if (options.chromeDriverPathOnly === true && options.chromeDriverPath) {
|
|
276
|
+
return executableCandidate(options.chromeDriverPath).find((candidate) => {
|
|
277
|
+
try {
|
|
278
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
279
|
+
return true;
|
|
280
|
+
} catch (_) {
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
}) || null;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const candidates = [
|
|
287
|
+
...executableCandidate(options.chromeDriverPath),
|
|
288
|
+
...executableCandidate(process.env.RMT_VNEXT_SOURCE_TO_SEA_CHROMEDRIVER),
|
|
289
|
+
...executableCandidate(process.env.CHROMEWEBDRIVER),
|
|
290
|
+
'/usr/local/share/chromedriver-linux64/chromedriver',
|
|
291
|
+
'/usr/bin/chromedriver',
|
|
292
|
+
'/snap/bin/chromium.chromedriver',
|
|
293
|
+
findExecutableOnPath('chromedriver')
|
|
294
|
+
].filter(Boolean);
|
|
295
|
+
|
|
296
|
+
return candidates.find((candidate) => {
|
|
297
|
+
try {
|
|
298
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
299
|
+
return true;
|
|
300
|
+
} catch (_) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}) || null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function findGeckoDriver(options = {}) {
|
|
307
|
+
const candidates = [
|
|
308
|
+
...executableCandidate(options.geckoDriverPath, 'geckodriver'),
|
|
309
|
+
...executableCandidate(process.env.RMT_VNEXT_SOURCE_TO_SEA_GECKODRIVER, 'geckodriver'),
|
|
310
|
+
...executableCandidate(process.env.GECKODRIVER, 'geckodriver'),
|
|
311
|
+
...executableCandidate(process.env.FIREFOXWEBDRIVER, 'geckodriver'),
|
|
312
|
+
'/usr/local/bin/geckodriver',
|
|
313
|
+
'/usr/bin/geckodriver',
|
|
314
|
+
'/snap/bin/geckodriver',
|
|
315
|
+
findExecutableOnPath('geckodriver')
|
|
316
|
+
].filter(Boolean);
|
|
317
|
+
|
|
318
|
+
return candidates.find((candidate) => {
|
|
319
|
+
try {
|
|
320
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
321
|
+
return true;
|
|
322
|
+
} catch (_) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
}) || null;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function findEdgeDriver(options = {}) {
|
|
329
|
+
const candidates = [
|
|
330
|
+
...executableCandidate(options.edgeDriverPath, 'msedgedriver'),
|
|
331
|
+
...executableCandidate(process.env.RMT_VNEXT_SOURCE_TO_SEA_EDGEDRIVER, 'msedgedriver'),
|
|
332
|
+
...executableCandidate(process.env.MSEDGEDRIVER, 'msedgedriver'),
|
|
333
|
+
...executableCandidate(process.env.EDGEWEBDRIVER, 'msedgedriver'),
|
|
334
|
+
'/usr/local/bin/msedgedriver',
|
|
335
|
+
'/usr/bin/msedgedriver',
|
|
336
|
+
findExecutableOnPath('msedgedriver')
|
|
337
|
+
].filter(Boolean);
|
|
338
|
+
|
|
339
|
+
return candidates.find((candidate) => {
|
|
340
|
+
try {
|
|
341
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
342
|
+
return true;
|
|
343
|
+
} catch (_) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
}) || null;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function findBrowserBinary(options = {}, envNames = [], executableNames = []) {
|
|
350
|
+
const candidates = [
|
|
351
|
+
options.browserBinary,
|
|
352
|
+
...envNames.map((name) => process.env[name]),
|
|
353
|
+
...executableNames.map((name) => findExecutableOnPath(name))
|
|
354
|
+
].filter(Boolean);
|
|
355
|
+
return candidates.find((candidate) => {
|
|
356
|
+
try {
|
|
357
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
358
|
+
return true;
|
|
359
|
+
} catch (_) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
}) || null;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function normalizeBrowserDriver(value) {
|
|
366
|
+
const driver = String(value || '').trim().toLowerCase();
|
|
367
|
+
if (driver === 'chrome' || driver === 'chromium') return 'chromedriver';
|
|
368
|
+
if (driver === 'safaridriver') return 'safari';
|
|
369
|
+
if (driver === 'gecko' || driver === 'geckodriver') return 'firefox';
|
|
370
|
+
if (driver === 'edge' || driver === 'msedge') return 'msedgedriver';
|
|
371
|
+
return driver;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function browserNameForDriver(driver, options = {}) {
|
|
375
|
+
if (options.browserName) return options.browserName;
|
|
376
|
+
if (driver === 'firefox') return 'firefox';
|
|
377
|
+
if (driver === 'safari') return 'safari';
|
|
378
|
+
if (driver === 'msedgedriver') return 'MicrosoftEdge';
|
|
379
|
+
if (driver === 'chromedriver') return process.env.RMT_VNEXT_SOURCE_TO_SEA_BROWSER_NAME || 'chrome';
|
|
380
|
+
return process.env.RMT_VNEXT_SOURCE_TO_SEA_BROWSER_NAME || 'chrome';
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function supportedBrowserDriver(value) {
|
|
384
|
+
return RMT_VNEXT_SOURCE_TO_SEA_SUPPORTED_BROWSER_DRIVERS.includes(String(value || '').trim().toLowerCase())
|
|
385
|
+
|| Boolean(normalizeBrowserDriver(value) && ['webdriver', 'chromedriver', 'firefox', 'safari', 'msedgedriver'].includes(normalizeBrowserDriver(value)));
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function detectAvailableBrowserDriver(options = {}) {
|
|
389
|
+
if (options.webDriverUrl || process.env.RMT_VNEXT_SOURCE_TO_SEA_WEBDRIVER_URL) return 'webdriver';
|
|
390
|
+
if (findGeckoDriver(options)) return 'firefox';
|
|
391
|
+
if (findChromeDriver(options)) return 'chromedriver';
|
|
392
|
+
if (findEdgeDriver(options)) return 'msedgedriver';
|
|
393
|
+
if (findSafariDriver()) return 'safari';
|
|
394
|
+
return '';
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async function waitForWebDriver(webDriver, timeoutMs = 5000) {
|
|
398
|
+
const started = Date.now();
|
|
399
|
+
while (Date.now() - started < timeoutMs) {
|
|
400
|
+
try {
|
|
401
|
+
const response = await requestJson({
|
|
402
|
+
hostname: webDriver.hostname,
|
|
403
|
+
port: webDriver.port,
|
|
404
|
+
path: `${webDriver.prefix}/status`,
|
|
405
|
+
method: 'GET'
|
|
406
|
+
});
|
|
407
|
+
if (response.statusCode >= 200 && response.statusCode < 500) {
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
} catch (_) {
|
|
411
|
+
await wait(150);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function childProcessHasExited(childProcess) {
|
|
418
|
+
return !childProcess || childProcess.exitCode !== null || childProcess.signalCode !== null;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function detachChildProcess(childProcess) {
|
|
422
|
+
if (!childProcess) return;
|
|
423
|
+
if (childProcess.stdout && typeof childProcess.stdout.destroy === 'function') childProcess.stdout.destroy();
|
|
424
|
+
if (childProcess.stderr && typeof childProcess.stderr.destroy === 'function') childProcess.stderr.destroy();
|
|
425
|
+
if (typeof childProcess.unref === 'function') childProcess.unref();
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function waitForChildProcessExit(childProcess, timeoutMs = 3000) {
|
|
429
|
+
if (childProcessHasExited(childProcess)) {
|
|
430
|
+
return Promise.resolve(true);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return new Promise((resolve) => {
|
|
434
|
+
let settled = false;
|
|
435
|
+
const cleanup = () => {
|
|
436
|
+
childProcess.off('exit', onExit);
|
|
437
|
+
childProcess.off('close', onExit);
|
|
438
|
+
clearTimeout(timer);
|
|
439
|
+
};
|
|
440
|
+
const onExit = () => {
|
|
441
|
+
if (settled) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
settled = true;
|
|
445
|
+
cleanup();
|
|
446
|
+
resolve(true);
|
|
447
|
+
};
|
|
448
|
+
const timer = setTimeout(() => {
|
|
449
|
+
if (settled) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
settled = true;
|
|
453
|
+
cleanup();
|
|
454
|
+
resolve(childProcessHasExited(childProcess));
|
|
455
|
+
}, timeoutMs);
|
|
456
|
+
|
|
457
|
+
childProcess.once('exit', onExit);
|
|
458
|
+
childProcess.once('close', onExit);
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function errorMessage(error) {
|
|
463
|
+
return error && error.message ? error.message : String(error);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
function signalSnapGeckoDriver(childProcess, signal = 'TERM') {
|
|
467
|
+
const pid = Number(childProcess && childProcess.pid);
|
|
468
|
+
if (!Number.isFinite(pid) || pid <= 0 || !findExecutableOnPath('snap')) {
|
|
469
|
+
return false;
|
|
470
|
+
}
|
|
471
|
+
const result = spawnSync('snap', ['run', '--shell', 'firefox.geckodriver', '-c', `kill -${signal} ${pid}`], {
|
|
472
|
+
stdio: 'ignore'
|
|
473
|
+
});
|
|
474
|
+
return result.status === 0;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
async function shutdownSpawnedWebDriver(childProcess, webDriver, options = {}) {
|
|
478
|
+
if (!childProcess) {
|
|
479
|
+
return {
|
|
480
|
+
ok: true,
|
|
481
|
+
method: 'none'
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const timeoutMs = Number(options.shutdownTimeoutMs || 3000);
|
|
486
|
+
const errors = [];
|
|
487
|
+
|
|
488
|
+
if (childProcessHasExited(childProcess)) {
|
|
489
|
+
return {
|
|
490
|
+
ok: true,
|
|
491
|
+
method: 'already-exited'
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (options.driver === 'chromedriver') {
|
|
496
|
+
try {
|
|
497
|
+
const response = await requestJson({
|
|
498
|
+
hostname: webDriver.hostname,
|
|
499
|
+
port: webDriver.port,
|
|
500
|
+
path: `${webDriver.prefix}/shutdown`,
|
|
501
|
+
method: 'GET'
|
|
502
|
+
});
|
|
503
|
+
if (response.statusCode >= 200 && response.statusCode < 500) {
|
|
504
|
+
const exited = await waitForChildProcessExit(childProcess, timeoutMs);
|
|
505
|
+
if (exited) {
|
|
506
|
+
return {
|
|
507
|
+
ok: true,
|
|
508
|
+
method: 'webdriver-shutdown'
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
errors.push(`webdriver shutdown did not exit within ${timeoutMs}ms`);
|
|
512
|
+
} else {
|
|
513
|
+
errors.push(`webdriver shutdown returned ${response.statusCode}`);
|
|
514
|
+
}
|
|
515
|
+
} catch (error) {
|
|
516
|
+
errors.push(`webdriver shutdown failed: ${errorMessage(error)}`);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (childProcessHasExited(childProcess)) {
|
|
521
|
+
return {
|
|
522
|
+
ok: true,
|
|
523
|
+
method: 'already-exited',
|
|
524
|
+
warnings: errors
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
try {
|
|
529
|
+
const signaled = childProcess.kill();
|
|
530
|
+
if (!signaled) {
|
|
531
|
+
errors.push('process signal was not accepted');
|
|
532
|
+
}
|
|
533
|
+
} catch (error) {
|
|
534
|
+
errors.push(`process signal failed: ${errorMessage(error)}`);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (await waitForChildProcessExit(childProcess, timeoutMs)) {
|
|
538
|
+
return {
|
|
539
|
+
ok: true,
|
|
540
|
+
method: 'process-signal',
|
|
541
|
+
warnings: errors
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (options.driver === 'firefox' && String(options.driverPath || '').includes('/snap/')) {
|
|
546
|
+
const snapSignaled = signalSnapGeckoDriver(childProcess, 'TERM');
|
|
547
|
+
if (!snapSignaled) {
|
|
548
|
+
errors.push('snap geckodriver signal was not accepted');
|
|
549
|
+
}
|
|
550
|
+
if (await waitForChildProcessExit(childProcess, timeoutMs)) {
|
|
551
|
+
return {
|
|
552
|
+
ok: true,
|
|
553
|
+
method: 'snap-shell-signal',
|
|
554
|
+
warnings: errors
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
detachChildProcess(childProcess);
|
|
560
|
+
|
|
561
|
+
return {
|
|
562
|
+
ok: false,
|
|
563
|
+
method: 'process-signal',
|
|
564
|
+
reason: `WebDriver process cleanup failed${errors.length ? `: ${errors.join('; ')}` : ''}`
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function createDefaultWebDriverCapabilities(options = {}) {
|
|
569
|
+
const browserName = browserNameForDriver(options.driver || 'webdriver', options);
|
|
570
|
+
const capabilities = {
|
|
571
|
+
browserName
|
|
572
|
+
};
|
|
573
|
+
if (browserName === 'chrome' || browserName === 'chromium') {
|
|
574
|
+
capabilities['goog:chromeOptions'] = {
|
|
575
|
+
args: [
|
|
576
|
+
'--headless=new',
|
|
577
|
+
'--disable-gpu',
|
|
578
|
+
'--no-sandbox',
|
|
579
|
+
'--window-size=1280,720'
|
|
580
|
+
]
|
|
581
|
+
};
|
|
582
|
+
} else if (browserName === 'firefox') {
|
|
583
|
+
const firefoxBinary = findBrowserBinary(options, [
|
|
584
|
+
'RMT_VNEXT_SOURCE_TO_SEA_FIREFOX_BINARY',
|
|
585
|
+
'FIREFOX_BIN'
|
|
586
|
+
]);
|
|
587
|
+
capabilities['moz:firefoxOptions'] = {
|
|
588
|
+
args: ['-headless']
|
|
589
|
+
};
|
|
590
|
+
if (firefoxBinary) {
|
|
591
|
+
capabilities['moz:firefoxOptions'].binary = firefoxBinary;
|
|
592
|
+
}
|
|
593
|
+
} else if (browserName === 'MicrosoftEdge' || browserName === 'edge' || browserName === 'msedge') {
|
|
594
|
+
capabilities.browserName = 'MicrosoftEdge';
|
|
595
|
+
capabilities['ms:edgeOptions'] = {
|
|
596
|
+
args: [
|
|
597
|
+
'--headless=new',
|
|
598
|
+
'--disable-gpu',
|
|
599
|
+
'--no-sandbox',
|
|
600
|
+
'--window-size=1280,720'
|
|
601
|
+
]
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
return {
|
|
605
|
+
capabilities: {
|
|
606
|
+
alwaysMatch: capabilities
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
function createBrowserExecutionUrl(options = {}) {
|
|
612
|
+
if (options.browserUrl) {
|
|
613
|
+
return options.browserUrl;
|
|
614
|
+
}
|
|
615
|
+
const rootDir = options.rootDir || process.cwd();
|
|
616
|
+
const fixturePath = options.browserFixturePath || RMT_VNEXT_SOURCE_TO_SEA_BROWSER_FIXTURE_PATH;
|
|
617
|
+
return pathToFileURL(path.resolve(rootDir, fixturePath)).href;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
async function executeWebDriverResult(webDriver, sessionId, resultKey, timeoutMs = 5000) {
|
|
621
|
+
const started = Date.now();
|
|
622
|
+
while (Date.now() - started < timeoutMs) {
|
|
623
|
+
const response = await requestJson({
|
|
624
|
+
hostname: webDriver.hostname,
|
|
625
|
+
port: webDriver.port,
|
|
626
|
+
path: `${webDriver.prefix}/session/${sessionId}/execute/sync`,
|
|
627
|
+
method: 'POST'
|
|
628
|
+
}, {
|
|
629
|
+
script: `return window[${JSON.stringify(resultKey)}] || null;`,
|
|
630
|
+
args: []
|
|
631
|
+
});
|
|
632
|
+
const value = response.body && response.body.value;
|
|
633
|
+
if (value && value.status && value.status !== 'pending') {
|
|
634
|
+
return value;
|
|
635
|
+
}
|
|
636
|
+
await wait(100);
|
|
637
|
+
}
|
|
638
|
+
throw new Error(`browser fixture did not publish ${resultKey}`);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
async function runWebDriverBrowserProbe(options = {}) {
|
|
642
|
+
const driver = normalizeBrowserDriver(options.driver || 'webdriver');
|
|
643
|
+
let webDriverUrl = options.webDriverUrl || process.env.RMT_VNEXT_SOURCE_TO_SEA_WEBDRIVER_URL || '';
|
|
644
|
+
let spawnedDriver = null;
|
|
645
|
+
let spawnedDriverPath = '';
|
|
646
|
+
const timeoutMs = Number(options.timeoutMs || (driver === 'firefox' ? 15000 : 5000));
|
|
647
|
+
|
|
648
|
+
if (driver === 'chromedriver' && !webDriverUrl) {
|
|
649
|
+
const driverPath = findChromeDriver(options);
|
|
650
|
+
if (!driverPath) {
|
|
651
|
+
throw new Error('chromedriver was not found');
|
|
652
|
+
}
|
|
653
|
+
const driverPort = Number(options.webDriverPort || process.env.RMT_VNEXT_SOURCE_TO_SEA_WEBDRIVER_PORT || RMT_VNEXT_SOURCE_TO_SEA_CI_WEBDRIVER_PORT);
|
|
654
|
+
spawnedDriver = spawn(driverPath, [`--port=${driverPort}`], {
|
|
655
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
656
|
+
});
|
|
657
|
+
spawnedDriverPath = driverPath;
|
|
658
|
+
webDriverUrl = `http://127.0.0.1:${driverPort}`;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (driver === 'firefox' && !webDriverUrl) {
|
|
662
|
+
const driverPath = findGeckoDriver(options);
|
|
663
|
+
if (!driverPath) {
|
|
664
|
+
throw new Error('geckodriver was not found');
|
|
665
|
+
}
|
|
666
|
+
const driverPort = Number(options.webDriverPort || process.env.RMT_VNEXT_SOURCE_TO_SEA_WEBDRIVER_PORT || 4444);
|
|
667
|
+
spawnedDriver = spawn(driverPath, ['--port', String(driverPort)], {
|
|
668
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
669
|
+
});
|
|
670
|
+
spawnedDriverPath = driverPath;
|
|
671
|
+
webDriverUrl = `http://127.0.0.1:${driverPort}`;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (driver === 'safari' && !webDriverUrl) {
|
|
675
|
+
const driverPath = findSafariDriver();
|
|
676
|
+
if (!driverPath) {
|
|
677
|
+
throw new Error('safaridriver was not found');
|
|
678
|
+
}
|
|
679
|
+
const driverPort = Number(options.webDriverPort || process.env.RMT_VNEXT_SOURCE_TO_SEA_WEBDRIVER_PORT || 57932);
|
|
680
|
+
spawnedDriver = spawn(driverPath, ['-p', String(driverPort)], {
|
|
681
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
682
|
+
});
|
|
683
|
+
spawnedDriverPath = driverPath;
|
|
684
|
+
webDriverUrl = `http://127.0.0.1:${driverPort}`;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (driver === 'msedgedriver' && !webDriverUrl) {
|
|
688
|
+
const driverPath = findEdgeDriver(options);
|
|
689
|
+
if (!driverPath) {
|
|
690
|
+
throw new Error('msedgedriver was not found');
|
|
691
|
+
}
|
|
692
|
+
const driverPort = Number(options.webDriverPort || process.env.RMT_VNEXT_SOURCE_TO_SEA_WEBDRIVER_PORT || 9516);
|
|
693
|
+
spawnedDriver = spawn(driverPath, [`--port=${driverPort}`], {
|
|
694
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
695
|
+
});
|
|
696
|
+
spawnedDriverPath = driverPath;
|
|
697
|
+
webDriverUrl = `http://127.0.0.1:${driverPort}`;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
if (!webDriverUrl) {
|
|
701
|
+
throw new Error('RMT_VNEXT_SOURCE_TO_SEA_WEBDRIVER_URL is required for webdriver mode');
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const webDriver = parseWebDriverUrl(webDriverUrl);
|
|
705
|
+
let sessionId = null;
|
|
706
|
+
|
|
707
|
+
try {
|
|
708
|
+
const ready = await waitForWebDriver(webDriver, timeoutMs);
|
|
709
|
+
if (!ready) {
|
|
710
|
+
throw new Error(`WebDriver endpoint did not become ready at ${webDriverUrl}`);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
const session = await requestJson({
|
|
714
|
+
hostname: webDriver.hostname,
|
|
715
|
+
port: webDriver.port,
|
|
716
|
+
path: `${webDriver.prefix}/session`,
|
|
717
|
+
method: 'POST'
|
|
718
|
+
}, createDefaultWebDriverCapabilities({ ...options, driver, browserName: browserNameForDriver(driver, options) }));
|
|
719
|
+
const sessionValue = session.body && session.body.value;
|
|
720
|
+
sessionId = sessionValue && (sessionValue.sessionId || sessionValue.id);
|
|
721
|
+
if (!sessionId) {
|
|
722
|
+
throw new Error(`WebDriver did not create a session: ${JSON.stringify(session.body || null)}`);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
await requestJson({
|
|
726
|
+
hostname: webDriver.hostname,
|
|
727
|
+
port: webDriver.port,
|
|
728
|
+
path: `${webDriver.prefix}/session/${sessionId}/url`,
|
|
729
|
+
method: 'POST'
|
|
730
|
+
}, {
|
|
731
|
+
url: createBrowserExecutionUrl(options)
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
return await executeWebDriverResult(webDriver, sessionId, options.resultKey || RMT_VNEXT_SOURCE_TO_SEA_RESULT_KEY, timeoutMs);
|
|
735
|
+
} finally {
|
|
736
|
+
if (sessionId) {
|
|
737
|
+
await requestJson({
|
|
738
|
+
hostname: webDriver.hostname,
|
|
739
|
+
port: webDriver.port,
|
|
740
|
+
path: `${webDriver.prefix}/session/${sessionId}`,
|
|
741
|
+
method: 'DELETE'
|
|
742
|
+
}).catch(() => {});
|
|
743
|
+
}
|
|
744
|
+
if (spawnedDriver) {
|
|
745
|
+
const cleanup = await shutdownSpawnedWebDriver(spawnedDriver, webDriver, { driver, driverPath: spawnedDriverPath });
|
|
746
|
+
if (!cleanup.ok) {
|
|
747
|
+
throw new Error(cleanup.reason || 'WebDriver process cleanup failed');
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function stripKernelLaneRef(value, fallback = 'visible') {
|
|
754
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
755
|
+
return fallback;
|
|
756
|
+
}
|
|
757
|
+
const segments = value.split('/');
|
|
758
|
+
const candidate = segments[segments.length - 1] || value;
|
|
759
|
+
return candidate.startsWith('lane:') ? fallback : candidate;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function lifecycleOpToFabricKind(op) {
|
|
763
|
+
switch (op) {
|
|
764
|
+
case 'mount':
|
|
765
|
+
return 'component.mount';
|
|
766
|
+
case 'destroy':
|
|
767
|
+
case 'disconnect':
|
|
768
|
+
return 'component.disconnect';
|
|
769
|
+
case 'render':
|
|
770
|
+
return 'component.render';
|
|
771
|
+
case 'hydrate':
|
|
772
|
+
default:
|
|
773
|
+
return 'component.hydrate';
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
function browserIncludesAttribute(html, attribute, value) {
|
|
778
|
+
if (typeof html !== 'string' || typeof attribute !== 'string' || typeof value !== 'string') {
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
781
|
+
return html.includes(`${attribute}="${value}"`);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
function safeFiberSegment(value) {
|
|
785
|
+
return String(value || 'rmt-vnext').replace(/[^a-zA-Z0-9._:-]/g, '-');
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function runFabricBridgeFiber(fabric, fiberInput = {}) {
|
|
789
|
+
const mapping = resolveRmtScheduleForFiber({
|
|
790
|
+
kind: fiberInput.kind,
|
|
791
|
+
lane: fiberInput.lane,
|
|
792
|
+
scope: fiberInput.scope,
|
|
793
|
+
componentRef: fiberInput.componentRef,
|
|
794
|
+
routeRef: fiberInput.routeRef
|
|
795
|
+
});
|
|
796
|
+
fabric.runFiber({
|
|
797
|
+
id: fiberInput.id,
|
|
798
|
+
kind: fiberInput.kind,
|
|
799
|
+
lane: mapping.fabricLane,
|
|
800
|
+
phase: fiberInput.phase,
|
|
801
|
+
source: fiberInput.source || 'rmt-vnext',
|
|
802
|
+
scope: fiberInput.scope,
|
|
803
|
+
componentRef: fiberInput.componentRef,
|
|
804
|
+
routeRef: fiberInput.routeRef,
|
|
805
|
+
correlationId: fiberInput.correlationId,
|
|
806
|
+
scheduleRef: mapping.scheduleRef,
|
|
807
|
+
endpointNameHint: mapping.endpointName,
|
|
808
|
+
metadata: fiberInput.metadata
|
|
809
|
+
}, () => ({ ok: true, lane: mapping.fabricLane, scheduleRef: mapping.scheduleRef }));
|
|
810
|
+
|
|
811
|
+
return {
|
|
812
|
+
requestedLane: fiberInput.lane,
|
|
813
|
+
kind: fiberInput.kind,
|
|
814
|
+
phase: fiberInput.phase,
|
|
815
|
+
id: fiberInput.id,
|
|
816
|
+
mapping
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
function createFabricBridgeLaneMatrix(fabric, context = {}) {
|
|
821
|
+
const primitiveSegment = safeFiberSegment(context.primitiveId);
|
|
822
|
+
return RMT_VNEXT_FABRIC_BRIDGE_LANE_MATRIX.map((entry, index) => runFabricBridgeFiber(fabric, {
|
|
823
|
+
id: `fiber:${primitiveSegment}/matrix/${entry.lane}/${index}`,
|
|
824
|
+
kind: entry.kind,
|
|
825
|
+
lane: entry.lane,
|
|
826
|
+
phase: entry.phase,
|
|
827
|
+
source: 'rmt-vnext-lane-matrix',
|
|
828
|
+
scope: context.primitiveId || entry.kind,
|
|
829
|
+
componentRef: context.componentRef,
|
|
830
|
+
routeRef: entry.routeRef,
|
|
831
|
+
correlationId: `${context.primitiveId || 'rmt-vnext'}:${entry.lane}`,
|
|
832
|
+
metadata: {
|
|
833
|
+
...context.metadata,
|
|
834
|
+
matrixLane: entry.lane,
|
|
835
|
+
matrixKind: entry.kind,
|
|
836
|
+
matrixWorkpackage: RMT_VNEXT_FABRIC_BRIDGE_WORKPACKAGE
|
|
837
|
+
}
|
|
838
|
+
}));
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
function createHostAdapterTelemetryInput(input = {}) {
|
|
842
|
+
const probeTelemetry = input.browserProbe && input.browserProbe.hostAdapterTelemetry;
|
|
843
|
+
const telemetry = probeTelemetry && typeof probeTelemetry === 'object' ? probeTelemetry : {};
|
|
844
|
+
const mapping = input.mapping || {};
|
|
845
|
+
const metadata = input.metadata || {};
|
|
846
|
+
return {
|
|
847
|
+
schema: RMT_VNEXT_HOST_ADAPTER_TELEMETRY_SCHEMA,
|
|
848
|
+
source: 'xtend.component-adapter',
|
|
849
|
+
operation: 'hydrate',
|
|
850
|
+
phase: 'hydrate',
|
|
851
|
+
status: 'ok',
|
|
852
|
+
adapterId: 'xtend.component',
|
|
853
|
+
componentId: input.primitiveId || metadata.primitiveId,
|
|
854
|
+
rmtComponentId: input.primitiveId || metadata.primitiveId,
|
|
855
|
+
tag: input.componentRef || metadata.componentRef,
|
|
856
|
+
scheduleRef: mapping.scheduleRef,
|
|
857
|
+
fabricLane: mapping.fabricLane,
|
|
858
|
+
rmtLane: mapping.rmtLane,
|
|
859
|
+
fiberKind: input.fabricKind || 'component.hydrate',
|
|
860
|
+
endpointNameHint: mapping.endpointName,
|
|
861
|
+
correlationId: input.primitiveId || metadata.primitiveId,
|
|
862
|
+
metadata: {
|
|
863
|
+
workpackage: RMT_VNEXT_FABRIC_BRIDGE_WORKPACKAGE,
|
|
864
|
+
primitiveId: input.primitiveId || metadata.primitiveId,
|
|
865
|
+
kernelScheduleRef: metadata.kernelScheduleRef,
|
|
866
|
+
kernelFiberRef: metadata.kernelFiberRef,
|
|
867
|
+
sourcePointer: metadata.sourcePointer,
|
|
868
|
+
hostAdapterEvidence: true
|
|
869
|
+
},
|
|
870
|
+
...telemetry,
|
|
871
|
+
metadata: {
|
|
872
|
+
workpackage: RMT_VNEXT_FABRIC_BRIDGE_WORKPACKAGE,
|
|
873
|
+
primitiveId: input.primitiveId || metadata.primitiveId,
|
|
874
|
+
kernelScheduleRef: metadata.kernelScheduleRef,
|
|
875
|
+
kernelFiberRef: metadata.kernelFiberRef,
|
|
876
|
+
sourcePointer: metadata.sourcePointer,
|
|
877
|
+
hostAdapterEvidence: true,
|
|
878
|
+
...(telemetry.metadata && typeof telemetry.metadata === 'object' ? telemetry.metadata : {})
|
|
879
|
+
}
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
function normalizeScenarioConfig(browserProbe) {
|
|
884
|
+
const probeConfig = browserProbe && browserProbe.routeComponentFibers && typeof browserProbe.routeComponentFibers === 'object'
|
|
885
|
+
? browserProbe.routeComponentFibers
|
|
886
|
+
: {};
|
|
887
|
+
const componentConfig = probeConfig.component && typeof probeConfig.component === 'object' ? probeConfig.component : {};
|
|
888
|
+
const routeConfig = probeConfig.route && typeof probeConfig.route === 'object' ? probeConfig.route : {};
|
|
889
|
+
return {
|
|
890
|
+
component: {
|
|
891
|
+
...RMT_VNEXT_ROUTE_COMPONENT_FIBER_SCENARIOS.component,
|
|
892
|
+
...componentConfig
|
|
893
|
+
},
|
|
894
|
+
route: {
|
|
895
|
+
...RMT_VNEXT_ROUTE_COMPONENT_FIBER_SCENARIOS.route,
|
|
896
|
+
...routeConfig
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
function createRouteAndComponentFiberRecords(fabric, context = {}) {
|
|
902
|
+
const scenario = normalizeScenarioConfig(context.browserProbe);
|
|
903
|
+
const metadata = {
|
|
904
|
+
...context.metadata,
|
|
905
|
+
routeComponentEvidence: true,
|
|
906
|
+
workpackage: RMT_VNEXT_FABRIC_BRIDGE_WORKPACKAGE
|
|
907
|
+
};
|
|
908
|
+
const componentRef = scenario.component.componentRef || context.componentRef || 'x-status';
|
|
909
|
+
const routeRef = scenario.route.routeRef || scenario.component.routeRef || '/rmt-vnext-source-to-sea';
|
|
910
|
+
const componentFibers = fabric.createComponentFiberInstrumentation(componentRef, {
|
|
911
|
+
scope: `${context.primitiveId || componentRef}.component`,
|
|
912
|
+
routeRef,
|
|
913
|
+
adapterRef: 'xtend.component',
|
|
914
|
+
hostRef: 'rmt-vnext-source-to-sea',
|
|
915
|
+
correlationId: `${context.primitiveId || componentRef}:component`
|
|
916
|
+
});
|
|
917
|
+
const routeFibers = fabric.createRouteFiberInstrumentation(scenario.route.routerRef || 'xtend.xrouter', {
|
|
918
|
+
scope: `${context.primitiveId || routeRef}.route`,
|
|
919
|
+
adapterRef: 'xtendrmt.xrouter',
|
|
920
|
+
hostRef: 'rmt-vnext-source-to-sea',
|
|
921
|
+
correlationId: `${context.primitiveId || routeRef}:route`
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
componentFibers.mount((fiber) => ({ mounted: true, fiberId: fiber.id }), {
|
|
925
|
+
routeRef,
|
|
926
|
+
scheduleRef: scenario.component.mountScheduleRef,
|
|
927
|
+
endpointNameHint: 'xtendrmt.component.mount',
|
|
928
|
+
metadata
|
|
929
|
+
});
|
|
930
|
+
componentFibers.hydrate((fiber) => ({ hydrated: true, fiberId: fiber.id }), {
|
|
931
|
+
routeRef,
|
|
932
|
+
scheduleRef: scenario.component.hydrateScheduleRef,
|
|
933
|
+
endpointNameHint: 'xtendrmt.component.hydrate',
|
|
934
|
+
metadata
|
|
935
|
+
});
|
|
936
|
+
routeFibers.navigate((fiber) => ({ navigated: true, fiberId: fiber.id, to: scenario.route.routeRef }), {
|
|
937
|
+
from: '/',
|
|
938
|
+
to: routeRef,
|
|
939
|
+
routeId: scenario.route.routeId || routeRef,
|
|
940
|
+
routeRef,
|
|
941
|
+
scheduleRef: scenario.route.navigateScheduleRef,
|
|
942
|
+
endpointNameHint: 'xtendrmt.ui.user-blocking',
|
|
943
|
+
metadata
|
|
944
|
+
});
|
|
945
|
+
routeFibers.render((fiber) => ({ rendered: true, fiberId: fiber.id, routeRef }), {
|
|
946
|
+
routeRef,
|
|
947
|
+
componentRef,
|
|
948
|
+
scheduleRef: scenario.route.renderScheduleRef,
|
|
949
|
+
endpointNameHint: 'xtendrmt.route.render',
|
|
950
|
+
metadata
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
return {
|
|
954
|
+
schema: RMT_VNEXT_ROUTE_COMPONENT_FIBER_EVIDENCE_SCHEMA,
|
|
955
|
+
componentScenario: scenario.component,
|
|
956
|
+
routeScenario: scenario.route,
|
|
957
|
+
expected: {
|
|
958
|
+
component: [
|
|
959
|
+
{ kind: 'component.mount', phase: 'mount', scheduleRef: scenario.component.mountScheduleRef, source: 'component' },
|
|
960
|
+
{ kind: 'component.hydrate', phase: 'hydrate', scheduleRef: scenario.component.hydrateScheduleRef, source: 'component' }
|
|
961
|
+
],
|
|
962
|
+
route: [
|
|
963
|
+
{ kind: 'route.navigate', phase: 'navigate', scheduleRef: scenario.route.navigateScheduleRef, source: 'router' },
|
|
964
|
+
{ kind: 'route.render', phase: 'render', scheduleRef: scenario.route.renderScheduleRef, source: 'router' }
|
|
965
|
+
]
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
function summarizeExpectedFiber(fibers, expected, telemetrySnapshot) {
|
|
971
|
+
const fiber = fibers.find((record) => record.kind === expected.kind
|
|
972
|
+
&& record.phase === expected.phase
|
|
973
|
+
&& record.scheduleRef === expected.scheduleRef
|
|
974
|
+
&& (!expected.source || record.source === expected.source)) || null;
|
|
975
|
+
const laneTelemetry = fiber && telemetrySnapshot && telemetrySnapshot.lanes
|
|
976
|
+
? telemetrySnapshot.lanes[fiber.lane]
|
|
977
|
+
: null;
|
|
978
|
+
return {
|
|
979
|
+
ok: Boolean(fiber && fiber.schema === FABRIC_CONTRACTS.fiber && fiber.status === 'completed' && laneTelemetry && laneTelemetry.scheduleRefs.includes(expected.scheduleRef)),
|
|
980
|
+
kind: expected.kind,
|
|
981
|
+
phase: expected.phase,
|
|
982
|
+
expectedScheduleRef: expected.scheduleRef,
|
|
983
|
+
expectedSource: expected.source || null,
|
|
984
|
+
fiber: fiber ? {
|
|
985
|
+
schema: fiber.schema,
|
|
986
|
+
id: fiber.id,
|
|
987
|
+
kind: fiber.kind,
|
|
988
|
+
phase: fiber.phase,
|
|
989
|
+
source: fiber.source,
|
|
990
|
+
lane: fiber.lane,
|
|
991
|
+
status: fiber.status,
|
|
992
|
+
scheduleRef: fiber.scheduleRef,
|
|
993
|
+
endpointNameHint: fiber.endpointNameHint,
|
|
994
|
+
componentRef: fiber.componentRef,
|
|
995
|
+
routeRef: fiber.routeRef,
|
|
996
|
+
correlationId: fiber.correlationId
|
|
997
|
+
} : null,
|
|
998
|
+
telemetry: laneTelemetry ? {
|
|
999
|
+
lane: laneTelemetry.lane,
|
|
1000
|
+
fiberCount: laneTelemetry.fiberCount,
|
|
1001
|
+
scheduleRefs: laneTelemetry.scheduleRefs
|
|
1002
|
+
} : null
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function summarizeRouteAndComponentFibers(plan, fibers, telemetrySnapshot) {
|
|
1007
|
+
const component = plan.expected.component.map((expected) => summarizeExpectedFiber(fibers, expected, telemetrySnapshot));
|
|
1008
|
+
const route = plan.expected.route.map((expected) => summarizeExpectedFiber(fibers, expected, telemetrySnapshot));
|
|
1009
|
+
return {
|
|
1010
|
+
schema: plan.schema,
|
|
1011
|
+
ok: component.every((entry) => entry.ok) && route.every((entry) => entry.ok),
|
|
1012
|
+
componentScenario: plan.componentScenario,
|
|
1013
|
+
routeScenario: plan.routeScenario,
|
|
1014
|
+
component,
|
|
1015
|
+
route,
|
|
1016
|
+
counts: {
|
|
1017
|
+
component: component.filter((entry) => entry.ok).length,
|
|
1018
|
+
route: route.filter((entry) => entry.ok).length,
|
|
1019
|
+
total: component.concat(route).filter((entry) => entry.ok).length
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
function createRmtVNextFabricBridgeEvidence(input = {}) {
|
|
1025
|
+
const schedule = input.schedule || null;
|
|
1026
|
+
const kernelFiber = input.fiber || null;
|
|
1027
|
+
const lifecycleRecord = input.lifecycleRecord || null;
|
|
1028
|
+
const sourceMapEntry = input.sourceMapEntry || null;
|
|
1029
|
+
const browserFixtureText = typeof input.browserFixtureText === 'string' ? input.browserFixtureText : '';
|
|
1030
|
+
const browserProbe = input.browserProbe || parseJsonScript(browserFixtureText, 'rmt-source-to-sea-probe') || null;
|
|
1031
|
+
const primitiveId = input.primitiveId || null;
|
|
1032
|
+
const componentRef = input.componentRef || null;
|
|
1033
|
+
const lane = (schedule && schedule.lane) || stripKernelLaneRef(kernelFiber && kernelFiber.lane);
|
|
1034
|
+
const fabricKind = lifecycleOpToFabricKind((kernelFiber && kernelFiber.op) || (lifecycleRecord && lifecycleRecord.op));
|
|
1035
|
+
const mapping = resolveRmtScheduleForFiber({
|
|
1036
|
+
kind: fabricKind,
|
|
1037
|
+
lane,
|
|
1038
|
+
scope: primitiveId,
|
|
1039
|
+
componentRef
|
|
1040
|
+
});
|
|
1041
|
+
const fabric = createXtendFabric({
|
|
1042
|
+
idPrefix: 'rmt.vnext.source-to-sea.fabric',
|
|
1043
|
+
now: createIncrementingClock(),
|
|
1044
|
+
markPerformance: false
|
|
1045
|
+
});
|
|
1046
|
+
const metadata = {
|
|
1047
|
+
workpackage: RMT_VNEXT_FABRIC_BRIDGE_WORKPACKAGE,
|
|
1048
|
+
primitiveId,
|
|
1049
|
+
componentRef,
|
|
1050
|
+
sourcePointer: sourceMapEntry && sourceMapEntry.corePointer,
|
|
1051
|
+
astPointer: sourceMapEntry && sourceMapEntry.astPointer,
|
|
1052
|
+
kernelScheduleRef: schedule && schedule.id,
|
|
1053
|
+
kernelFiberRef: kernelFiber && kernelFiber.id,
|
|
1054
|
+
kernelLifecycleRecordRef: lifecycleRecord && lifecycleRecord.id
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
let primaryRecord = null;
|
|
1058
|
+
if (kernelFiber && kernelFiber.id) {
|
|
1059
|
+
primaryRecord = runFabricBridgeFiber(fabric, {
|
|
1060
|
+
id: kernelFiber.id,
|
|
1061
|
+
kind: fabricKind,
|
|
1062
|
+
lane: mapping.fabricLane,
|
|
1063
|
+
phase: (kernelFiber && kernelFiber.op) || 'hydrate',
|
|
1064
|
+
source: 'rmt-vnext',
|
|
1065
|
+
scope: primitiveId || (schedule && schedule.id) || fabricKind,
|
|
1066
|
+
componentRef,
|
|
1067
|
+
correlationId: primitiveId || (kernelFiber && kernelFiber.id),
|
|
1068
|
+
scheduleRef: mapping.scheduleRef,
|
|
1069
|
+
endpointNameHint: mapping.endpointName,
|
|
1070
|
+
metadata
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
const laneMatrixRecords = createFabricBridgeLaneMatrix(fabric, {
|
|
1074
|
+
primitiveId,
|
|
1075
|
+
componentRef,
|
|
1076
|
+
metadata
|
|
1077
|
+
});
|
|
1078
|
+
const hostAdapterTelemetry = fabric.recordComponentTelemetry(createHostAdapterTelemetryInput({
|
|
1079
|
+
primitiveId,
|
|
1080
|
+
componentRef,
|
|
1081
|
+
fabricKind,
|
|
1082
|
+
mapping,
|
|
1083
|
+
metadata,
|
|
1084
|
+
browserProbe
|
|
1085
|
+
}));
|
|
1086
|
+
const routeComponentPlan = createRouteAndComponentFiberRecords(fabric, {
|
|
1087
|
+
primitiveId,
|
|
1088
|
+
componentRef,
|
|
1089
|
+
metadata,
|
|
1090
|
+
browserProbe
|
|
1091
|
+
});
|
|
1092
|
+
|
|
1093
|
+
const fabricFibers = fabric.getFibers();
|
|
1094
|
+
const completedFiber = fabricFibers.find((record) => record.id === (kernelFiber && kernelFiber.id)) || fabricFibers[0] || null;
|
|
1095
|
+
const telemetrySnapshot = fabric.createTelemetrySnapshot({
|
|
1096
|
+
id: 'telemetry:rmt-vnext-source-to-sea',
|
|
1097
|
+
source: 'rmt-vnext-source-to-sea',
|
|
1098
|
+
correlationId: primitiveId,
|
|
1099
|
+
metadata
|
|
1100
|
+
});
|
|
1101
|
+
const laneTelemetry = completedFiber && telemetrySnapshot.lanes
|
|
1102
|
+
? telemetrySnapshot.lanes[completedFiber.lane]
|
|
1103
|
+
: null;
|
|
1104
|
+
const laneMatrix = laneMatrixRecords.map((record) => {
|
|
1105
|
+
const matrixFiber = fabricFibers.find((fiberRecord) => fiberRecord.id === record.id) || null;
|
|
1106
|
+
const matrixLaneTelemetry = matrixFiber && telemetrySnapshot.lanes
|
|
1107
|
+
? telemetrySnapshot.lanes[matrixFiber.lane]
|
|
1108
|
+
: null;
|
|
1109
|
+
const ok = Boolean(
|
|
1110
|
+
record.mapping.ok === true
|
|
1111
|
+
&& matrixFiber
|
|
1112
|
+
&& matrixFiber.status === 'completed'
|
|
1113
|
+
&& matrixFiber.schema === FABRIC_CONTRACTS.fiber
|
|
1114
|
+
&& matrixLaneTelemetry
|
|
1115
|
+
&& matrixLaneTelemetry.scheduleRefs.includes(record.mapping.scheduleRef)
|
|
1116
|
+
);
|
|
1117
|
+
return {
|
|
1118
|
+
ok,
|
|
1119
|
+
lane: record.requestedLane,
|
|
1120
|
+
kind: record.kind,
|
|
1121
|
+
phase: record.phase,
|
|
1122
|
+
fiber: matrixFiber ? {
|
|
1123
|
+
id: matrixFiber.id,
|
|
1124
|
+
schema: matrixFiber.schema,
|
|
1125
|
+
status: matrixFiber.status,
|
|
1126
|
+
lane: matrixFiber.lane,
|
|
1127
|
+
scheduleRef: matrixFiber.scheduleRef,
|
|
1128
|
+
endpointNameHint: matrixFiber.endpointNameHint,
|
|
1129
|
+
source: matrixFiber.source
|
|
1130
|
+
} : null,
|
|
1131
|
+
mapping: {
|
|
1132
|
+
schema: record.mapping.schema,
|
|
1133
|
+
source: record.mapping.source,
|
|
1134
|
+
fabricLane: record.mapping.fabricLane,
|
|
1135
|
+
rmtLane: record.mapping.rmtLane,
|
|
1136
|
+
scheduleRef: record.mapping.scheduleRef,
|
|
1137
|
+
endpointName: record.mapping.endpointName
|
|
1138
|
+
},
|
|
1139
|
+
telemetry: matrixLaneTelemetry ? {
|
|
1140
|
+
lane: matrixLaneTelemetry.lane,
|
|
1141
|
+
fiberCount: matrixLaneTelemetry.fiberCount,
|
|
1142
|
+
scheduleRefs: matrixLaneTelemetry.scheduleRefs
|
|
1143
|
+
} : null
|
|
1144
|
+
};
|
|
1145
|
+
});
|
|
1146
|
+
const expectedMatrixLanes = RMT_VNEXT_FABRIC_BRIDGE_LANE_MATRIX.map((entry) => entry.lane);
|
|
1147
|
+
const actualMatrixLanes = laneMatrix.map((entry) => entry.lane);
|
|
1148
|
+
const browserExposesLane = Boolean(completedFiber && browserIncludesAttribute(browserFixtureText, 'data-xtend-fabric-lane', completedFiber.lane));
|
|
1149
|
+
const browserExposesFiber = Boolean(completedFiber && browserIncludesAttribute(browserFixtureText, 'data-xtend-fabric-fiber', completedFiber.id));
|
|
1150
|
+
const browserExposesSchedule = Boolean(completedFiber && browserIncludesAttribute(browserFixtureText, 'data-xtend-fabric-schedule', completedFiber.scheduleRef));
|
|
1151
|
+
const browserExposesHostTelemetry = browserIncludesAttribute(browserFixtureText, 'data-xtend-host-adapter-telemetry', RMT_VNEXT_HOST_ADAPTER_TELEMETRY_SCHEMA)
|
|
1152
|
+
|| browserFixtureText.includes(RMT_VNEXT_HOST_ADAPTER_TELEMETRY_SCHEMA);
|
|
1153
|
+
const componentTelemetrySummary = telemetrySnapshot.componentTelemetry;
|
|
1154
|
+
const componentTelemetryLane = componentTelemetrySummary && componentTelemetrySummary.lanes
|
|
1155
|
+
? componentTelemetrySummary.lanes[hostAdapterTelemetry.fabricLane]
|
|
1156
|
+
: null;
|
|
1157
|
+
const routeComponentFibers = summarizeRouteAndComponentFibers(routeComponentPlan, fabricFibers, telemetrySnapshot);
|
|
1158
|
+
const checks = [
|
|
1159
|
+
createCheck('fabric bridge mapping resolved', mapping.ok === true && mapping.schema === FABRIC_RMT_CONTRACTS.mapping, mapping.scheduleRef),
|
|
1160
|
+
createCheck('fabric runtime records vNext fiber', Boolean(completedFiber && completedFiber.schema === FABRIC_CONTRACTS.fiber && completedFiber.status === 'completed'), completedFiber && completedFiber.id),
|
|
1161
|
+
createCheck('fabric runtime keeps vNext lane', Boolean(completedFiber && completedFiber.lane === lane), completedFiber && completedFiber.lane),
|
|
1162
|
+
createCheck('fabric telemetry records lane', Boolean(laneTelemetry && laneTelemetry.fiberCount >= 1), laneTelemetry && laneTelemetry.lane),
|
|
1163
|
+
createCheck('fabric telemetry records schedule', Boolean(laneTelemetry && laneTelemetry.scheduleRefs.includes(mapping.scheduleRef)), mapping.scheduleRef),
|
|
1164
|
+
createCheck('fabric bridge lane matrix covers target lanes', expectedMatrixLanes.every((matrixLane) => actualMatrixLanes.includes(matrixLane)), actualMatrixLanes),
|
|
1165
|
+
createCheck('fabric bridge lane matrix passes', laneMatrix.every((entry) => entry.ok), laneMatrix.map((entry) => `${entry.lane}:${entry.mapping.scheduleRef}`)),
|
|
1166
|
+
createCheck('host adapter telemetry recorded', hostAdapterTelemetry.schema === RMT_VNEXT_HOST_ADAPTER_TELEMETRY_SCHEMA, hostAdapterTelemetry.schema),
|
|
1167
|
+
createCheck('host adapter telemetry matches mapping', hostAdapterTelemetry.scheduleRef === mapping.scheduleRef && hostAdapterTelemetry.fabricLane === mapping.fabricLane, hostAdapterTelemetry.scheduleRef),
|
|
1168
|
+
createCheck('fabric snapshot includes host adapter telemetry', Boolean(componentTelemetrySummary && componentTelemetrySummary.recordCount >= 1), componentTelemetrySummary && componentTelemetrySummary.recordCount),
|
|
1169
|
+
createCheck('host adapter telemetry lane summarizes schedule', Boolean(componentTelemetryLane && componentTelemetryLane.scheduleRefs.includes(mapping.scheduleRef)), mapping.scheduleRef),
|
|
1170
|
+
createCheck('browser exposes host adapter telemetry', browserExposesHostTelemetry, RMT_VNEXT_HOST_ADAPTER_TELEMETRY_SCHEMA),
|
|
1171
|
+
createCheck('component fiber instrumentation records mount and hydrate', routeComponentFibers.component.every((entry) => entry.ok), routeComponentFibers.component.map((entry) => entry.expectedScheduleRef)),
|
|
1172
|
+
createCheck('route fiber instrumentation records navigate and render', routeComponentFibers.route.every((entry) => entry.ok), routeComponentFibers.route.map((entry) => entry.expectedScheduleRef)),
|
|
1173
|
+
createCheck('browser exposes fabric lane', browserExposesLane, completedFiber && completedFiber.lane),
|
|
1174
|
+
createCheck('browser exposes fabric fiber', browserExposesFiber, completedFiber && completedFiber.id),
|
|
1175
|
+
createCheck('browser exposes fabric schedule', browserExposesSchedule, completedFiber && completedFiber.scheduleRef)
|
|
1176
|
+
];
|
|
1177
|
+
|
|
1178
|
+
return {
|
|
1179
|
+
schema: RMT_VNEXT_FABRIC_BRIDGE_EVIDENCE_SCHEMA,
|
|
1180
|
+
workpackage: RMT_VNEXT_FABRIC_BRIDGE_WORKPACKAGE,
|
|
1181
|
+
ok: checks.every((check) => check.ok),
|
|
1182
|
+
status: checks.every((check) => check.ok) ? 'passed' : 'failed',
|
|
1183
|
+
mapping: {
|
|
1184
|
+
schema: mapping.schema,
|
|
1185
|
+
scheduleSchema: mapping.schedule && mapping.schedule.schema,
|
|
1186
|
+
source: mapping.source,
|
|
1187
|
+
fabricLane: mapping.fabricLane,
|
|
1188
|
+
rmtLane: mapping.rmtLane,
|
|
1189
|
+
scheduleRef: mapping.scheduleRef,
|
|
1190
|
+
endpointName: mapping.endpointName,
|
|
1191
|
+
diagnostics: mapping.diagnostics
|
|
1192
|
+
},
|
|
1193
|
+
primary: primaryRecord ? {
|
|
1194
|
+
lane: primaryRecord.requestedLane,
|
|
1195
|
+
kind: primaryRecord.kind,
|
|
1196
|
+
scheduleRef: primaryRecord.mapping.scheduleRef,
|
|
1197
|
+
endpointName: primaryRecord.mapping.endpointName
|
|
1198
|
+
} : null,
|
|
1199
|
+
fiber: completedFiber ? {
|
|
1200
|
+
schema: completedFiber.schema,
|
|
1201
|
+
id: completedFiber.id,
|
|
1202
|
+
kind: completedFiber.kind,
|
|
1203
|
+
phase: completedFiber.phase,
|
|
1204
|
+
source: completedFiber.source,
|
|
1205
|
+
lane: completedFiber.lane,
|
|
1206
|
+
status: completedFiber.status,
|
|
1207
|
+
scheduleRef: completedFiber.scheduleRef,
|
|
1208
|
+
endpointNameHint: completedFiber.endpointNameHint,
|
|
1209
|
+
correlationId: completedFiber.correlationId,
|
|
1210
|
+
metadata: completedFiber.metadata
|
|
1211
|
+
} : null,
|
|
1212
|
+
telemetry: {
|
|
1213
|
+
schema: telemetrySnapshot.schema,
|
|
1214
|
+
id: telemetrySnapshot.id,
|
|
1215
|
+
source: telemetrySnapshot.source,
|
|
1216
|
+
fiberCount: telemetrySnapshot.fiberCount,
|
|
1217
|
+
lane: laneTelemetry ? {
|
|
1218
|
+
lane: laneTelemetry.lane,
|
|
1219
|
+
fiberCount: laneTelemetry.fiberCount,
|
|
1220
|
+
completedCount: laneTelemetry.completedCount,
|
|
1221
|
+
scheduleRefs: laneTelemetry.scheduleRefs
|
|
1222
|
+
} : null,
|
|
1223
|
+
laneMatrix
|
|
1224
|
+
},
|
|
1225
|
+
laneMatrix,
|
|
1226
|
+
routeComponentFibers,
|
|
1227
|
+
hostAdapter: {
|
|
1228
|
+
schema: hostAdapterTelemetry.schema,
|
|
1229
|
+
source: hostAdapterTelemetry.source,
|
|
1230
|
+
operation: hostAdapterTelemetry.operation,
|
|
1231
|
+
phase: hostAdapterTelemetry.phase,
|
|
1232
|
+
status: hostAdapterTelemetry.status,
|
|
1233
|
+
adapterId: hostAdapterTelemetry.adapterId,
|
|
1234
|
+
componentId: hostAdapterTelemetry.componentId,
|
|
1235
|
+
scheduleRef: hostAdapterTelemetry.scheduleRef,
|
|
1236
|
+
fabricLane: hostAdapterTelemetry.fabricLane,
|
|
1237
|
+
rmtLane: hostAdapterTelemetry.rmtLane,
|
|
1238
|
+
fiberKind: hostAdapterTelemetry.fiberKind,
|
|
1239
|
+
endpointNameHint: hostAdapterTelemetry.endpointNameHint,
|
|
1240
|
+
correlationId: hostAdapterTelemetry.correlationId,
|
|
1241
|
+
metadata: hostAdapterTelemetry.metadata,
|
|
1242
|
+
summary: componentTelemetrySummary ? {
|
|
1243
|
+
schema: componentTelemetrySummary.schema,
|
|
1244
|
+
recordCount: componentTelemetrySummary.recordCount,
|
|
1245
|
+
lane: componentTelemetryLane ? {
|
|
1246
|
+
lane: hostAdapterTelemetry.fabricLane,
|
|
1247
|
+
recordCount: componentTelemetryLane.recordCount,
|
|
1248
|
+
scheduleRefs: componentTelemetryLane.scheduleRefs
|
|
1249
|
+
} : null
|
|
1250
|
+
} : null
|
|
1251
|
+
},
|
|
1252
|
+
browser: {
|
|
1253
|
+
laneVisible: browserExposesLane,
|
|
1254
|
+
fiberVisible: browserExposesFiber,
|
|
1255
|
+
scheduleVisible: browserExposesSchedule,
|
|
1256
|
+
hostAdapterTelemetryVisible: browserExposesHostTelemetry
|
|
1257
|
+
},
|
|
1258
|
+
correlation: [
|
|
1259
|
+
{ layer: 'source', value: sourceMapEntry && sourceMapEntry.astPointer },
|
|
1260
|
+
{ layer: 'kernel.schedule', value: schedule && schedule.id },
|
|
1261
|
+
{ layer: 'kernel.fiber', value: kernelFiber && kernelFiber.id },
|
|
1262
|
+
{ layer: 'fabric.mapping', value: mapping.scheduleRef },
|
|
1263
|
+
{ layer: 'fabric.fiber', value: completedFiber && completedFiber.id },
|
|
1264
|
+
{ layer: 'host.adapter', value: hostAdapterTelemetry.id },
|
|
1265
|
+
{ layer: 'component.fibers', value: routeComponentFibers.component.map((entry) => entry.fiber && entry.fiber.id).filter(Boolean) },
|
|
1266
|
+
{ layer: 'route.fibers', value: routeComponentFibers.route.map((entry) => entry.fiber && entry.fiber.id).filter(Boolean) },
|
|
1267
|
+
{ layer: 'fabric.telemetry', value: telemetrySnapshot.id },
|
|
1268
|
+
{ layer: 'browser', value: completedFiber && `[data-xtend-fabric-fiber="${completedFiber.id}"]` }
|
|
1269
|
+
],
|
|
1270
|
+
checks
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
function createRmtVNextSourceToSeaEvidence(input, options = {}) {
|
|
1275
|
+
const compileResult = compileRmtVNextSource(input, options.compilerOptions || {});
|
|
1276
|
+
const coreDocument = compileResult.coreDocument || null;
|
|
1277
|
+
const appPlatform = coreDocument && coreDocument.appPlatform;
|
|
1278
|
+
const kernelRecords = coreDocument && coreDocument.kernelRecords;
|
|
1279
|
+
const browserFixtureText = typeof options.browserFixtureText === 'string' ? options.browserFixtureText : '';
|
|
1280
|
+
const browserProbe = options.browserProbe || parseJsonScript(browserFixtureText, 'rmt-source-to-sea-probe');
|
|
1281
|
+
const primitiveId = options.primitiveId
|
|
1282
|
+
|| (browserProbe && browserProbe.primitiveId)
|
|
1283
|
+
|| (toArray(appPlatform && appPlatform.surfaces)[0] && toArray(appPlatform && appPlatform.surfaces)[0].id)
|
|
1284
|
+
|| null;
|
|
1285
|
+
const actionId = options.actionId
|
|
1286
|
+
|| (browserProbe && browserProbe.expectedAction)
|
|
1287
|
+
|| (toArray(appPlatform && appPlatform.actions)[0] && toArray(appPlatform && appPlatform.actions)[0].id)
|
|
1288
|
+
|| null;
|
|
1289
|
+
const preferredLane = options.lane || (browserProbe && browserProbe.lane) || 'visible';
|
|
1290
|
+
const { appSurface, coreSurface } = findSurface(coreDocument, primitiveId);
|
|
1291
|
+
const eventRecord = findEvent(coreDocument, coreSurface, actionId);
|
|
1292
|
+
const eventSourceMap = findSourceMapEntry(coreDocument, eventRecord);
|
|
1293
|
+
const surfaceSourceMap = findSourceMapEntry(coreDocument, coreSurface);
|
|
1294
|
+
const sourceMapEntry = eventSourceMap || surfaceSourceMap || null;
|
|
1295
|
+
const schedule = findSchedule(kernelRecords, coreSurface, preferredLane);
|
|
1296
|
+
const fiber = findFiber(kernelRecords, schedule);
|
|
1297
|
+
const uiSelector = options.selector
|
|
1298
|
+
|| (browserProbe && browserProbe.selector)
|
|
1299
|
+
|| (primitiveId ? `[data-rmt-primitive-id="${primitiveId}"]` : null);
|
|
1300
|
+
const browserExposesPrimitive = Boolean(uiSelector && browserFixtureText.includes(uiSelector.replace(/\\"/g, '"')));
|
|
1301
|
+
const browserExposesResult = Boolean(browserFixtureText.includes(RMT_VNEXT_SOURCE_TO_SEA_RESULT_KEY));
|
|
1302
|
+
const browserText = options.expectedText || (browserProbe && browserProbe.expectedText);
|
|
1303
|
+
const expectedBrowserAction = options.expectedAction || (browserProbe && browserProbe.expectedAction);
|
|
1304
|
+
const browserEventObserved = Boolean(expectedBrowserAction === actionId && browserExposesResult);
|
|
1305
|
+
const browserViewportAsserted = Boolean(browserExposesPrimitive && browserFixtureText.includes('getBoundingClientRect'));
|
|
1306
|
+
const kernelLifecycleRecord = toArray(kernelRecords && kernelRecords.lifecycleRecords)
|
|
1307
|
+
.find((record) => fiber && record.id === fiber.operation) || null;
|
|
1308
|
+
const scheduleRef = schedule && schedule.id;
|
|
1309
|
+
const fiberRef = fiber && fiber.id;
|
|
1310
|
+
const fabricBridge = createRmtVNextFabricBridgeEvidence({
|
|
1311
|
+
primitiveId,
|
|
1312
|
+
componentRef: appSurface && appSurface.component,
|
|
1313
|
+
schedule,
|
|
1314
|
+
fiber,
|
|
1315
|
+
lifecycleRecord: kernelLifecycleRecord,
|
|
1316
|
+
sourceMapEntry,
|
|
1317
|
+
browserFixtureText,
|
|
1318
|
+
browserProbe
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
const checks = [
|
|
1322
|
+
createCheck('compiler result ok', compileResult.ok === true, compileResult.status),
|
|
1323
|
+
createCheck('semantic graph ok', Boolean(compileResult.primitiveSemanticGraph && compileResult.primitiveSemanticGraph.ok), compileResult.primitiveSemanticGraph && compileResult.primitiveSemanticGraph.diagnostics),
|
|
1324
|
+
createCheck('app platform artifact emitted', Boolean(appPlatform && appPlatform.schema === RMT_APP_PLATFORM_RECORDS_SCHEMA), appPlatform && appPlatform.schema),
|
|
1325
|
+
createCheck('kernel records emitted', Boolean(kernelRecords && kernelRecords.schema === RMT_KERNEL_RECORDS_SCHEMA), kernelRecords && kernelRecords.schema),
|
|
1326
|
+
createCheck('kernel boundary preserved', Boolean(kernelRecords && kernelRecords.boundary === RMT_KERNEL_BOUNDARY), kernelRecords && kernelRecords.boundary),
|
|
1327
|
+
createCheck('primitive surface lowered', Boolean(appSurface && coreSurface), primitiveId),
|
|
1328
|
+
createCheck('event action lowered', Boolean(eventRecord && eventRecord.action === actionId), eventRecord && eventRecord.action),
|
|
1329
|
+
createCheck('source pointer available', Boolean(sourceMapEntry && sourceMapEntry.corePointer), sourceMapEntry && sourceMapEntry.corePointer),
|
|
1330
|
+
createCheck('kernel schedule available', Boolean(scheduleRef), scheduleRef),
|
|
1331
|
+
createCheck('fabric fiber derivable', Boolean(fiberRef && fiber && fiber.source && fiber.source.kind === 'selector'), fiberRef),
|
|
1332
|
+
createCheck('lifecycle record correlates to fiber', Boolean(kernelLifecycleRecord), fiber && fiber.operation),
|
|
1333
|
+
createCheck('browser fixture declares probe schema', Boolean(browserProbe && browserProbe.schema === RMT_VNEXT_SOURCE_TO_SEA_BROWSER_PROBE_SCHEMA), browserProbe && browserProbe.schema),
|
|
1334
|
+
createCheck('browser fixture exposes primitive marker', browserExposesPrimitive, uiSelector),
|
|
1335
|
+
createCheck('browser fixture asserts viewport', browserViewportAsserted, uiSelector),
|
|
1336
|
+
createCheck('browser fixture observes event', browserEventObserved, actionId),
|
|
1337
|
+
createCheck('fabric bridge evidence passes', fabricBridge.ok === true, fabricBridge.status),
|
|
1338
|
+
createCheck('kernel does not expose host imports', !containsKernelHostImport(kernelRecords), kernelRecords && kernelRecords.boundary)
|
|
1339
|
+
];
|
|
1340
|
+
const ok = checks.every((check) => check.ok);
|
|
1341
|
+
|
|
1342
|
+
return {
|
|
1343
|
+
schema: RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_SCHEMA,
|
|
1344
|
+
gateSchema: RMT_VNEXT_SOURCE_TO_SEA_SCHEMA,
|
|
1345
|
+
workpackage: RMT_VNEXT_SOURCE_TO_SEA_WORKPACKAGE,
|
|
1346
|
+
ok,
|
|
1347
|
+
status: ok ? 'passed' : 'failed',
|
|
1348
|
+
source: input && (input.filePath || input.uri || null),
|
|
1349
|
+
primitiveId,
|
|
1350
|
+
sourcePointer: sourceMapEntry && sourceMapEntry.corePointer,
|
|
1351
|
+
sourceMap: sourceMapEntry ? {
|
|
1352
|
+
id: sourceMapEntry.id,
|
|
1353
|
+
astPointer: sourceMapEntry.astPointer,
|
|
1354
|
+
corePointer: sourceMapEntry.corePointer,
|
|
1355
|
+
range: sourceMapEntry.range
|
|
1356
|
+
} : null,
|
|
1357
|
+
compiler: {
|
|
1358
|
+
ok: compileResult.ok === true,
|
|
1359
|
+
status: compileResult.status,
|
|
1360
|
+
artifactCount: artifactCount(coreDocument),
|
|
1361
|
+
appPlatformSchema: appPlatform && appPlatform.schema,
|
|
1362
|
+
kernelRecordsSchema: kernelRecords && kernelRecords.schema
|
|
1363
|
+
},
|
|
1364
|
+
kernel: {
|
|
1365
|
+
ingested: Boolean(kernelRecords && scheduleRef && kernelLifecycleRecord),
|
|
1366
|
+
scheduleRef,
|
|
1367
|
+
lifecycleRecordRef: kernelLifecycleRecord && kernelLifecycleRecord.id,
|
|
1368
|
+
boundary: kernelRecords && kernelRecords.boundary
|
|
1369
|
+
},
|
|
1370
|
+
fabric: {
|
|
1371
|
+
schema: fabricBridge.schema,
|
|
1372
|
+
workpackage: fabricBridge.workpackage,
|
|
1373
|
+
lane: schedule && schedule.lane,
|
|
1374
|
+
rmtLane: fabricBridge.mapping.rmtLane,
|
|
1375
|
+
fiber: fiberRef,
|
|
1376
|
+
operation: fiber && fiber.operation,
|
|
1377
|
+
sourceKind: fiber && fiber.source && fiber.source.kind,
|
|
1378
|
+
scheduleRef: fabricBridge.mapping.scheduleRef,
|
|
1379
|
+
endpointName: fabricBridge.mapping.endpointName,
|
|
1380
|
+
telemetry: fabricBridge.telemetry,
|
|
1381
|
+
bridge: fabricBridge
|
|
1382
|
+
},
|
|
1383
|
+
ui: {
|
|
1384
|
+
selector: uiSelector,
|
|
1385
|
+
visible: browserViewportAsserted,
|
|
1386
|
+
text: browserText || null,
|
|
1387
|
+
component: appSurface && appSurface.component
|
|
1388
|
+
},
|
|
1389
|
+
browser: {
|
|
1390
|
+
fixture: options.browserFixturePath || null,
|
|
1391
|
+
resultKey: RMT_VNEXT_SOURCE_TO_SEA_RESULT_KEY,
|
|
1392
|
+
viewportAsserted: browserViewportAsserted,
|
|
1393
|
+
eventObserved: browserEventObserved,
|
|
1394
|
+
expectedAction: actionId,
|
|
1395
|
+
probeSchema: browserProbe && browserProbe.schema
|
|
1396
|
+
},
|
|
1397
|
+
correlation: [
|
|
1398
|
+
{ layer: 'source', value: sourceMapEntry && sourceMapEntry.astPointer },
|
|
1399
|
+
{ layer: 'compiler', value: primitiveId },
|
|
1400
|
+
{ layer: 'kernel', value: scheduleRef },
|
|
1401
|
+
{ layer: 'fabric', value: fiberRef },
|
|
1402
|
+
{ layer: 'ui', value: uiSelector },
|
|
1403
|
+
{ layer: 'browser', value: RMT_VNEXT_SOURCE_TO_SEA_RESULT_KEY }
|
|
1404
|
+
],
|
|
1405
|
+
diagnostics: compileResult.diagnostics || [],
|
|
1406
|
+
checks
|
|
1407
|
+
};
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
function createBrowserExecutionChecks(result, evidence = {}) {
|
|
1411
|
+
const kernelEvidence = evidence && evidence.kernel || {};
|
|
1412
|
+
const fabricEvidence = evidence && evidence.fabric || {};
|
|
1413
|
+
const expectedAction = evidence && evidence.browser && evidence.browser.expectedAction
|
|
1414
|
+
|| 'demo.feedback.save';
|
|
1415
|
+
return [
|
|
1416
|
+
createCheck('browser execution returned result', Boolean(result), result && result.status),
|
|
1417
|
+
createCheck('browser execution status passed', result && result.status === 'passed', result && result.errors),
|
|
1418
|
+
createCheck('browser execution primitive matches evidence', result && result.primitiveId === evidence.primitiveId, result && result.primitiveId),
|
|
1419
|
+
createCheck('browser execution schedule matches kernel', result && result.scheduleRef === kernelEvidence.scheduleRef, result && result.scheduleRef),
|
|
1420
|
+
createCheck('browser execution fiber matches Fabric bridge', result && result.fiberRef === fabricEvidence.fiber, result && result.fiberRef),
|
|
1421
|
+
createCheck('browser execution lane matches Fabric lane', result && result.lane === fabricEvidence.lane, result && result.lane),
|
|
1422
|
+
createCheck('browser execution schedule reaches Fabric', result && result.fabricScheduleRef === fabricEvidence.scheduleRef, result && result.fabricScheduleRef),
|
|
1423
|
+
createCheck('browser execution host telemetry visible', result && result.hostAdapterTelemetrySchema === RMT_VNEXT_HOST_ADAPTER_TELEMETRY_SCHEMA, result && result.hostAdapterTelemetrySchema),
|
|
1424
|
+
createCheck('browser execution observed action event', Boolean(result && toArray(result.events).some((event) => event.action === expectedAction)), result && result.events),
|
|
1425
|
+
createCheck('browser execution viewport checks pass', Boolean(result && toArray(result.checks).length > 0 && toArray(result.checks).every((check) => check.ok === true)), result && result.checks),
|
|
1426
|
+
createCheck('browser execution object matrix passes', Boolean(!result || !Array.isArray(result.objects) || (result.objects.length >= 4 && result.objects.every((entry) => entry.status === 'passed'))), result && result.objects),
|
|
1427
|
+
createCheck('browser execution cross-primitive events pass', Boolean(!result || !Array.isArray(result.crossPrimitiveEvents) || (result.crossPrimitiveEvents.length >= 2 && result.crossPrimitiveEvents.every((entry) => entry.status === 'passed'))), result && result.crossPrimitiveEvents),
|
|
1428
|
+
createCheck('browser execution cross-route event passes', Boolean(!result || !Array.isArray(result.crossPrimitiveEvents) || result.crossPrimitiveEvents.some((entry) => entry.stage === 'route-target' && entry.status === 'passed' && entry.sourceLane === 'transition' && entry.targetLane === 'transition')), result && result.crossPrimitiveEvents),
|
|
1429
|
+
createCheck('browser execution route switches pass', Boolean(!result || !Array.isArray(result.routeSwitches) || (result.routeSwitches.length >= 2 && result.routeSwitches.every((entry) => entry.status === 'passed' && entry.targetMounted === true && entry.targetVisible === true))), result && result.routeSwitches),
|
|
1430
|
+
createCheck('browser execution route lifecycle cycles pass', Boolean(!result || !Array.isArray(result.routeLifecycleCycles) || (result.routeLifecycleCycles.length >= 2 && result.routeLifecycleCycles.every((entry) => entry.status === 'passed' && entry.unmounted === true && entry.remounted === true && entry.resourceDisposed === true && entry.countsMatch === true))), result && result.routeLifecycleCycles),
|
|
1431
|
+
createCheck('browser execution route lifecycle targets are distinct', Boolean(!result || !Array.isArray(result.routeLifecycleCycles) || new Set(result.routeLifecycleCycles.map((entry) => entry.targetPrimitiveId)).size >= 2), result && result.routeLifecycleCycles)
|
|
1432
|
+
];
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
function createRmtVNextSourceToSeaBrowserResultValidation(result, evidence = {}) {
|
|
1436
|
+
const checks = createBrowserExecutionChecks(result, evidence);
|
|
1437
|
+
const ok = checks.every((check) => check.ok);
|
|
1438
|
+
const routeSwitches = toArray(result && result.routeSwitches);
|
|
1439
|
+
const routeLifecycleCycles = toArray(result && result.routeLifecycleCycles);
|
|
1440
|
+
const failedChecks = checks.filter((check) => !check.ok).map((check) => check.name);
|
|
1441
|
+
|
|
1442
|
+
return {
|
|
1443
|
+
schema: RMT_VNEXT_SOURCE_TO_SEA_BROWSER_RESULT_VALIDATION_SCHEMA,
|
|
1444
|
+
workpackage: RMT_VNEXT_SOURCE_TO_SEA_WORKPACKAGE,
|
|
1445
|
+
ok,
|
|
1446
|
+
status: ok ? 'passed' : 'failed',
|
|
1447
|
+
resultStatus: result && result.status || null,
|
|
1448
|
+
primitiveId: result && result.primitiveId || null,
|
|
1449
|
+
routeSwitchCount: routeSwitches.length,
|
|
1450
|
+
routeLifecycleCycleCount: routeLifecycleCycles.length,
|
|
1451
|
+
failedChecks,
|
|
1452
|
+
checks
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
|
|
1456
|
+
function normalizeSourceToSeaObjects(browserProbe) {
|
|
1457
|
+
const objects = toArray(browserProbe && browserProbe.objects)
|
|
1458
|
+
.filter((entry) => entry && typeof entry === 'object' && entry.primitiveId);
|
|
1459
|
+
if (objects.length > 0) {
|
|
1460
|
+
return objects;
|
|
1461
|
+
}
|
|
1462
|
+
if (browserProbe && browserProbe.primitiveId) {
|
|
1463
|
+
return [{
|
|
1464
|
+
primitiveId: browserProbe.primitiveId,
|
|
1465
|
+
selector: browserProbe.selector,
|
|
1466
|
+
expectedText: browserProbe.expectedText,
|
|
1467
|
+
expectedAction: browserProbe.expectedAction,
|
|
1468
|
+
scheduleRef: browserProbe.scheduleRef,
|
|
1469
|
+
fiberRef: browserProbe.fiberRef,
|
|
1470
|
+
lane: browserProbe.lane,
|
|
1471
|
+
fabricScheduleRef: browserProbe.fabricScheduleRef,
|
|
1472
|
+
fabricEndpoint: browserProbe.fabricEndpoint
|
|
1473
|
+
}];
|
|
1474
|
+
}
|
|
1475
|
+
return [];
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
function createObjectHostAdapterTelemetry(browserProbe, objectProbe) {
|
|
1479
|
+
const directTelemetry = objectProbe && objectProbe.hostAdapterTelemetry;
|
|
1480
|
+
if (directTelemetry && typeof directTelemetry === 'object') {
|
|
1481
|
+
return directTelemetry;
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
const inheritedTelemetry = browserProbe && browserProbe.hostAdapterTelemetry;
|
|
1485
|
+
if (!inheritedTelemetry || typeof inheritedTelemetry !== 'object') {
|
|
1486
|
+
return inheritedTelemetry || null;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
const primitiveId = objectProbe.primitiveId
|
|
1490
|
+
|| inheritedTelemetry.componentId
|
|
1491
|
+
|| inheritedTelemetry.rmtComponentId
|
|
1492
|
+
|| null;
|
|
1493
|
+
const lane = objectProbe.lane
|
|
1494
|
+
|| inheritedTelemetry.fabricLane
|
|
1495
|
+
|| inheritedTelemetry.rmtLane
|
|
1496
|
+
|| null;
|
|
1497
|
+
const componentRef = objectProbe.componentRef
|
|
1498
|
+
|| inheritedTelemetry.tag
|
|
1499
|
+
|| null;
|
|
1500
|
+
const scheduleRef = objectProbe.fabricScheduleRef
|
|
1501
|
+
|| inheritedTelemetry.scheduleRef
|
|
1502
|
+
|| null;
|
|
1503
|
+
|
|
1504
|
+
return {
|
|
1505
|
+
...inheritedTelemetry,
|
|
1506
|
+
componentId: primitiveId,
|
|
1507
|
+
rmtComponentId: primitiveId,
|
|
1508
|
+
tag: componentRef,
|
|
1509
|
+
scheduleRef,
|
|
1510
|
+
fabricLane: lane,
|
|
1511
|
+
rmtLane: lane,
|
|
1512
|
+
endpointNameHint: objectProbe.fabricEndpoint || inheritedTelemetry.endpointNameHint,
|
|
1513
|
+
correlationId: primitiveId || inheritedTelemetry.correlationId,
|
|
1514
|
+
metadata: {
|
|
1515
|
+
...(inheritedTelemetry.metadata && typeof inheritedTelemetry.metadata === 'object'
|
|
1516
|
+
? inheritedTelemetry.metadata
|
|
1517
|
+
: {}),
|
|
1518
|
+
primitiveId,
|
|
1519
|
+
componentRef
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
function createObjectRouteComponentFibers(browserProbe, objectProbe) {
|
|
1525
|
+
const directRouteComponentFibers = objectProbe && objectProbe.routeComponentFibers;
|
|
1526
|
+
if (directRouteComponentFibers && typeof directRouteComponentFibers === 'object') {
|
|
1527
|
+
return directRouteComponentFibers;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
const inheritedRouteComponentFibers = browserProbe && browserProbe.routeComponentFibers;
|
|
1531
|
+
if (!inheritedRouteComponentFibers || typeof inheritedRouteComponentFibers !== 'object') {
|
|
1532
|
+
return inheritedRouteComponentFibers || null;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
const componentConfig = inheritedRouteComponentFibers.component && typeof inheritedRouteComponentFibers.component === 'object'
|
|
1536
|
+
? inheritedRouteComponentFibers.component
|
|
1537
|
+
: {};
|
|
1538
|
+
const routeConfig = inheritedRouteComponentFibers.route && typeof inheritedRouteComponentFibers.route === 'object'
|
|
1539
|
+
? inheritedRouteComponentFibers.route
|
|
1540
|
+
: {};
|
|
1541
|
+
|
|
1542
|
+
return {
|
|
1543
|
+
...inheritedRouteComponentFibers,
|
|
1544
|
+
component: {
|
|
1545
|
+
...componentConfig,
|
|
1546
|
+
componentRef: objectProbe.componentRef || componentConfig.componentRef,
|
|
1547
|
+
routeRef: objectProbe.routeRef || componentConfig.routeRef || routeConfig.routeRef
|
|
1548
|
+
},
|
|
1549
|
+
route: routeConfig
|
|
1550
|
+
};
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
function createObjectBrowserProbe(browserProbe, objectProbe) {
|
|
1554
|
+
return {
|
|
1555
|
+
...(browserProbe || {}),
|
|
1556
|
+
...objectProbe,
|
|
1557
|
+
hostAdapterTelemetry: createObjectHostAdapterTelemetry(browserProbe, objectProbe),
|
|
1558
|
+
routeComponentFibers: createObjectRouteComponentFibers(browserProbe, objectProbe)
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
function summarizeSourceToSeaObject(objectProbe, evidence) {
|
|
1563
|
+
const checks = [
|
|
1564
|
+
createCheck('object source-to-sea evidence passes', evidence.ok === true, evidence.status),
|
|
1565
|
+
createCheck('object primitive id matches', evidence.primitiveId === objectProbe.primitiveId, evidence.primitiveId),
|
|
1566
|
+
createCheck('object action matches browser probe', evidence.browser.expectedAction === objectProbe.expectedAction, evidence.browser.expectedAction),
|
|
1567
|
+
createCheck('object kernel schedule matches browser probe', !objectProbe.scheduleRef || evidence.kernel.scheduleRef === objectProbe.scheduleRef, evidence.kernel.scheduleRef),
|
|
1568
|
+
createCheck('object fabric fiber matches browser probe', !objectProbe.fiberRef || evidence.fabric.fiber === objectProbe.fiberRef, evidence.fabric.fiber),
|
|
1569
|
+
createCheck('object fabric schedule matches browser probe', !objectProbe.fabricScheduleRef || evidence.fabric.scheduleRef === objectProbe.fabricScheduleRef, evidence.fabric.scheduleRef),
|
|
1570
|
+
createCheck('object UI selector matches browser probe', !objectProbe.selector || evidence.ui.selector === objectProbe.selector, evidence.ui.selector),
|
|
1571
|
+
createCheck('object UI text matches browser probe', !objectProbe.expectedText || evidence.ui.text === objectProbe.expectedText, evidence.ui.text)
|
|
1572
|
+
];
|
|
1573
|
+
|
|
1574
|
+
return {
|
|
1575
|
+
ok: checks.every((check) => check.ok),
|
|
1576
|
+
status: checks.every((check) => check.ok) ? 'passed' : 'failed',
|
|
1577
|
+
primitiveId: objectProbe.primitiveId,
|
|
1578
|
+
actionId: objectProbe.expectedAction || null,
|
|
1579
|
+
componentRef: objectProbe.componentRef || (evidence.ui && evidence.ui.component) || null,
|
|
1580
|
+
sourcePointer: evidence.sourcePointer,
|
|
1581
|
+
astPointer: evidence.sourceMap && evidence.sourceMap.astPointer,
|
|
1582
|
+
kernel: {
|
|
1583
|
+
scheduleRef: evidence.kernel.scheduleRef
|
|
1584
|
+
},
|
|
1585
|
+
fabric: {
|
|
1586
|
+
fiber: evidence.fabric.fiber,
|
|
1587
|
+
lane: evidence.fabric.lane,
|
|
1588
|
+
scheduleRef: evidence.fabric.scheduleRef,
|
|
1589
|
+
endpointName: evidence.fabric.endpointName
|
|
1590
|
+
},
|
|
1591
|
+
ui: evidence.ui,
|
|
1592
|
+
browser: evidence.browser,
|
|
1593
|
+
checks
|
|
1594
|
+
};
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
function createCrossPrimitiveEventMatrix(crossPrimitiveEvents, coreDocument, objects) {
|
|
1598
|
+
const actions = toArray(coreDocument && coreDocument.actions);
|
|
1599
|
+
const objectIds = new Set(objects.map((entry) => entry.primitiveId));
|
|
1600
|
+
return toArray(crossPrimitiveEvents).map((crossEvent) => {
|
|
1601
|
+
const sourceObject = objects.find((entry) => entry.primitiveId === crossEvent.sourcePrimitiveId) || null;
|
|
1602
|
+
const targetObject = objects.find((entry) => entry.primitiveId === crossEvent.targetPrimitiveId) || null;
|
|
1603
|
+
const sourceLane = sourceObject && sourceObject.fabric && sourceObject.fabric.lane || null;
|
|
1604
|
+
const targetLane = targetObject && targetObject.fabric && targetObject.fabric.lane || null;
|
|
1605
|
+
const action = actions.find((entry) => entry.name === crossEvent.actionId || entry.id === crossEvent.actionId) || null;
|
|
1606
|
+
const reducer = toArray(action && action.reducers)
|
|
1607
|
+
.find((entry) => entry.target === crossEvent.targetState) || null;
|
|
1608
|
+
const emit = toArray(action && action.emits)
|
|
1609
|
+
.find((entry) => entry.event === crossEvent.eventId) || null;
|
|
1610
|
+
const routeTargetStateOk = crossEvent.stage !== 'route-target'
|
|
1611
|
+
|| !crossEvent.targetState
|
|
1612
|
+
|| String(crossEvent.targetState).startsWith(`state.${crossEvent.targetPrimitiveId}.`);
|
|
1613
|
+
const routeTargetEventOk = crossEvent.stage !== 'route-target'
|
|
1614
|
+
|| !crossEvent.eventId
|
|
1615
|
+
|| String(crossEvent.eventId).startsWith(`${crossEvent.targetPrimitiveId}.`);
|
|
1616
|
+
const routeTargetStageOk = crossEvent.stage !== 'route-target' || (sourceLane === 'transition' && targetLane === 'transition');
|
|
1617
|
+
const checks = [
|
|
1618
|
+
createCheck('cross event source object exists', objectIds.has(crossEvent.sourcePrimitiveId), crossEvent.sourcePrimitiveId),
|
|
1619
|
+
createCheck('cross event target object exists', objectIds.has(crossEvent.targetPrimitiveId), crossEvent.targetPrimitiveId),
|
|
1620
|
+
createCheck('cross event action exists', Boolean(action), crossEvent.actionId),
|
|
1621
|
+
createCheck('cross event reducer targets another primitive state', Boolean(reducer), crossEvent.targetState),
|
|
1622
|
+
createCheck('cross event emitted', Boolean(emit), crossEvent.eventId),
|
|
1623
|
+
createCheck('cross event target lane matches expected', !crossEvent.lane || targetLane === crossEvent.lane, crossEvent.lane),
|
|
1624
|
+
createCheck('cross event route-target state belongs to target primitive', routeTargetStateOk, crossEvent.targetState),
|
|
1625
|
+
createCheck('cross event route-target event belongs to target primitive', routeTargetEventOk, crossEvent.eventId),
|
|
1626
|
+
createCheck('cross event route-target stage uses transition lanes', routeTargetStageOk, `${sourceLane || '?'} -> ${targetLane || '?'}`)
|
|
1627
|
+
];
|
|
1628
|
+
return {
|
|
1629
|
+
ok: checks.every((check) => check.ok),
|
|
1630
|
+
status: checks.every((check) => check.ok) ? 'passed' : 'failed',
|
|
1631
|
+
sourcePrimitiveId: crossEvent.sourcePrimitiveId,
|
|
1632
|
+
targetPrimitiveId: crossEvent.targetPrimitiveId,
|
|
1633
|
+
actionId: crossEvent.actionId,
|
|
1634
|
+
eventId: crossEvent.eventId || null,
|
|
1635
|
+
targetState: crossEvent.targetState || null,
|
|
1636
|
+
expectedText: crossEvent.expectedText || null,
|
|
1637
|
+
lane: crossEvent.lane || null,
|
|
1638
|
+
stage: crossEvent.stage || null,
|
|
1639
|
+
sourceLane,
|
|
1640
|
+
targetLane,
|
|
1641
|
+
reducer: reducer ? {
|
|
1642
|
+
target: reducer.target,
|
|
1643
|
+
value: reducer.value
|
|
1644
|
+
} : null,
|
|
1645
|
+
emit: emit ? {
|
|
1646
|
+
event: emit.event,
|
|
1647
|
+
payload: emit.payload
|
|
1648
|
+
} : null,
|
|
1649
|
+
checks
|
|
1650
|
+
};
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
function createRouteSwitchMatrix(routeSwitches, objects) {
|
|
1655
|
+
const objectIds = new Set(objects.map((entry) => entry.primitiveId));
|
|
1656
|
+
const defaultRouteScenario = RMT_VNEXT_ROUTE_COMPONENT_FIBER_SCENARIOS.route;
|
|
1657
|
+
return toArray(routeSwitches).map((routeSwitch) => {
|
|
1658
|
+
const targetObject = objects.find((entry) => entry.primitiveId === routeSwitch.targetPrimitiveId) || null;
|
|
1659
|
+
const hasRouteChange = Boolean(routeSwitch.from && routeSwitch.to && routeSwitch.from !== routeSwitch.to);
|
|
1660
|
+
const checks = [
|
|
1661
|
+
createCheck('route switch source object exists', objectIds.has(routeSwitch.sourcePrimitiveId), routeSwitch.sourcePrimitiveId),
|
|
1662
|
+
createCheck('route switch target object exists', !routeSwitch.targetPrimitiveId || objectIds.has(routeSwitch.targetPrimitiveId), routeSwitch.targetPrimitiveId),
|
|
1663
|
+
createCheck('route switch changes route', hasRouteChange, `${routeSwitch.from || '?'} -> ${routeSwitch.to || '?'}`),
|
|
1664
|
+
createCheck('route switch uses navigation schedule', routeSwitch.scheduleRef === defaultRouteScenario.navigateScheduleRef, routeSwitch.scheduleRef),
|
|
1665
|
+
createCheck('route switch uses render schedule', routeSwitch.renderScheduleRef === defaultRouteScenario.renderScheduleRef, routeSwitch.renderScheduleRef),
|
|
1666
|
+
createCheck('route switch uses transition lane', routeSwitch.lane === 'transition', routeSwitch.lane),
|
|
1667
|
+
createCheck('route switch target schedule matches object', !routeSwitch.targetScheduleRef || Boolean(targetObject && targetObject.kernel && targetObject.kernel.scheduleRef === routeSwitch.targetScheduleRef), routeSwitch.targetScheduleRef),
|
|
1668
|
+
createCheck('route switch target fiber matches object', !routeSwitch.targetFiberRef || Boolean(targetObject && targetObject.fabric && targetObject.fabric.fiber === routeSwitch.targetFiberRef), routeSwitch.targetFiberRef),
|
|
1669
|
+
createCheck('route switch target uses transition lane', !targetObject || targetObject.fabric.lane === 'transition', targetObject && targetObject.fabric && targetObject.fabric.lane)
|
|
1670
|
+
];
|
|
1671
|
+
return {
|
|
1672
|
+
ok: checks.every((check) => check.ok),
|
|
1673
|
+
status: checks.every((check) => check.ok) ? 'passed' : 'failed',
|
|
1674
|
+
id: routeSwitch.id || null,
|
|
1675
|
+
sourcePrimitiveId: routeSwitch.sourcePrimitiveId || null,
|
|
1676
|
+
targetPrimitiveId: routeSwitch.targetPrimitiveId || null,
|
|
1677
|
+
actionId: routeSwitch.actionId || null,
|
|
1678
|
+
from: routeSwitch.from || null,
|
|
1679
|
+
to: routeSwitch.to || null,
|
|
1680
|
+
routeId: routeSwitch.routeId || null,
|
|
1681
|
+
scheduleRef: routeSwitch.scheduleRef || null,
|
|
1682
|
+
renderScheduleRef: routeSwitch.renderScheduleRef || null,
|
|
1683
|
+
lane: routeSwitch.lane || null,
|
|
1684
|
+
targetScheduleRef: routeSwitch.targetScheduleRef || null,
|
|
1685
|
+
targetFiberRef: routeSwitch.targetFiberRef || null,
|
|
1686
|
+
targetExpectedText: routeSwitch.targetExpectedText || null,
|
|
1687
|
+
checks
|
|
1688
|
+
};
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
function createRouteLifecycleMatrix(routeLifecycleCycles, coreDocument, objects) {
|
|
1693
|
+
const objectIds = new Set(objects.map((entry) => entry.primitiveId));
|
|
1694
|
+
const resources = toArray(coreDocument && coreDocument.resources);
|
|
1695
|
+
const normalizeCycleResources = (cycle) => {
|
|
1696
|
+
const normalized = [];
|
|
1697
|
+
const seen = new Set();
|
|
1698
|
+
const addResource = (entry) => {
|
|
1699
|
+
const resourceId = typeof entry === 'string'
|
|
1700
|
+
? entry
|
|
1701
|
+
: entry && (entry.resourceId || entry.id || entry.name);
|
|
1702
|
+
if (!resourceId || seen.has(resourceId)) {
|
|
1703
|
+
return;
|
|
1704
|
+
}
|
|
1705
|
+
seen.add(resourceId);
|
|
1706
|
+
normalized.push({
|
|
1707
|
+
resourceId,
|
|
1708
|
+
kind: typeof entry === 'object' && entry ? entry.kind || null : null
|
|
1709
|
+
});
|
|
1710
|
+
};
|
|
1711
|
+
toArray(cycle.resources).forEach(addResource);
|
|
1712
|
+
toArray(cycle.resourceIds).forEach(addResource);
|
|
1713
|
+
addResource({
|
|
1714
|
+
resourceId: cycle.resourceId,
|
|
1715
|
+
kind: cycle.resourceKind || cycle.kind || null
|
|
1716
|
+
});
|
|
1717
|
+
return normalized;
|
|
1718
|
+
};
|
|
1719
|
+
return toArray(routeLifecycleCycles).map((cycle) => {
|
|
1720
|
+
const targetObject = objects.find((entry) => entry.primitiveId === cycle.targetPrimitiveId) || null;
|
|
1721
|
+
const resourceSpecs = normalizeCycleResources(cycle);
|
|
1722
|
+
const resourceResults = resourceSpecs.map((spec) => {
|
|
1723
|
+
const resource = resources.find((entry) => entry.name === spec.resourceId || entry.id === `resource:${String(spec.resourceId || '').toLowerCase()}`) || null;
|
|
1724
|
+
const diagnostics = [];
|
|
1725
|
+
|
|
1726
|
+
if (!resource) {
|
|
1727
|
+
diagnostics.push({
|
|
1728
|
+
code: RMT_VNEXT_SOURCE_TO_SEA_CLEANUP_DIAGNOSTIC_CODES.resourceMissing,
|
|
1729
|
+
level: 'error',
|
|
1730
|
+
message: `Route lifecycle cleanup resource ${spec.resourceId || '<missing>'} was not emitted by vNext lowering.`,
|
|
1731
|
+
targetPrimitiveId: cycle.targetPrimitiveId || null,
|
|
1732
|
+
resourceId: spec.resourceId || null
|
|
1733
|
+
});
|
|
1734
|
+
} else if (!resource.owner || resource.owner.id !== cycle.targetPrimitiveId) {
|
|
1735
|
+
diagnostics.push({
|
|
1736
|
+
code: RMT_VNEXT_SOURCE_TO_SEA_CLEANUP_DIAGNOSTIC_CODES.ownerMismatch,
|
|
1737
|
+
level: 'error',
|
|
1738
|
+
message: `Route lifecycle cleanup resource ${spec.resourceId} is not owned by ${cycle.targetPrimitiveId}.`,
|
|
1739
|
+
targetPrimitiveId: cycle.targetPrimitiveId || null,
|
|
1740
|
+
resourceId: spec.resourceId || null,
|
|
1741
|
+
owner: resource.owner || null
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
if (resource && (!resource.dispose || resource.dispose.text !== 'on surface.destroy')) {
|
|
1746
|
+
diagnostics.push({
|
|
1747
|
+
code: RMT_VNEXT_SOURCE_TO_SEA_CLEANUP_DIAGNOSTIC_CODES.disposePolicyMissing,
|
|
1748
|
+
level: 'error',
|
|
1749
|
+
message: `Route lifecycle cleanup resource ${spec.resourceId} must dispose on surface.destroy.`,
|
|
1750
|
+
targetPrimitiveId: cycle.targetPrimitiveId || null,
|
|
1751
|
+
resourceId: spec.resourceId || null,
|
|
1752
|
+
dispose: resource.dispose || null
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
if (resource && spec.kind && resource.kind !== spec.kind) {
|
|
1757
|
+
diagnostics.push({
|
|
1758
|
+
code: RMT_VNEXT_SOURCE_TO_SEA_CLEANUP_DIAGNOSTIC_CODES.kindMismatch,
|
|
1759
|
+
level: 'error',
|
|
1760
|
+
message: `Route lifecycle cleanup resource ${spec.resourceId} must use kind ${spec.kind}.`,
|
|
1761
|
+
targetPrimitiveId: cycle.targetPrimitiveId || null,
|
|
1762
|
+
resourceId: spec.resourceId || null,
|
|
1763
|
+
expectedKind: spec.kind,
|
|
1764
|
+
actualKind: resource.kind || null
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
return {
|
|
1769
|
+
ok: diagnostics.length === 0,
|
|
1770
|
+
resourceId: spec.resourceId || null,
|
|
1771
|
+
expectedKind: spec.kind || null,
|
|
1772
|
+
resource: resource ? {
|
|
1773
|
+
id: resource.id,
|
|
1774
|
+
name: resource.name,
|
|
1775
|
+
kind: resource.kind,
|
|
1776
|
+
owner: resource.owner,
|
|
1777
|
+
dispose: resource.dispose
|
|
1778
|
+
} : null,
|
|
1779
|
+
diagnostics
|
|
1780
|
+
};
|
|
1781
|
+
});
|
|
1782
|
+
const primaryResource = resourceResults[0] || null;
|
|
1783
|
+
const resourceChecks = resourceResults.flatMap((entry) => [
|
|
1784
|
+
createCheck('route lifecycle resource exists', Boolean(entry.resource), entry.resourceId),
|
|
1785
|
+
createCheck('route lifecycle resource owner matches target', Boolean(entry.resource && entry.resource.owner && entry.resource.owner.id === cycle.targetPrimitiveId), entry.resource && entry.resource.owner && entry.resource.owner.id),
|
|
1786
|
+
createCheck('route lifecycle resource dispose policy is surface destroy', Boolean(entry.resource && entry.resource.dispose && entry.resource.dispose.text === 'on surface.destroy'), entry.resource && entry.resource.dispose && entry.resource.dispose.text),
|
|
1787
|
+
createCheck('route lifecycle resource kind matches expected', !entry.expectedKind || Boolean(entry.resource && entry.resource.kind === entry.expectedKind), entry.expectedKind)
|
|
1788
|
+
]);
|
|
1789
|
+
const diagnostics = resourceResults.flatMap((entry) => entry.diagnostics);
|
|
1790
|
+
|
|
1791
|
+
const checks = [
|
|
1792
|
+
createCheck('route lifecycle target object exists', objectIds.has(cycle.targetPrimitiveId), cycle.targetPrimitiveId),
|
|
1793
|
+
createCheck('route lifecycle target uses transition lane', Boolean(targetObject && targetObject.fabric && targetObject.fabric.lane === 'transition'), targetObject && targetObject.fabric && targetObject.fabric.lane),
|
|
1794
|
+
createCheck('route lifecycle unmount schedule declared', cycle.unmountScheduleRef === 'ui.background.work', cycle.unmountScheduleRef),
|
|
1795
|
+
createCheck('route lifecycle remount schedule declared', cycle.remountScheduleRef === 'route.transition.render', cycle.remountScheduleRef),
|
|
1796
|
+
createCheck('route lifecycle declares cleanup resources', resourceResults.length >= 1, cycle.resourceId || cycle.resources),
|
|
1797
|
+
...resourceChecks,
|
|
1798
|
+
createCheck('route lifecycle expected unmount count declared', Number(cycle.expectedUnmountCount || 0) >= 1, cycle.expectedUnmountCount),
|
|
1799
|
+
createCheck('route lifecycle expected remount count declared', Number(cycle.expectedRemountCount || 0) >= 1, cycle.expectedRemountCount)
|
|
1800
|
+
];
|
|
1801
|
+
return {
|
|
1802
|
+
ok: checks.every((check) => check.ok),
|
|
1803
|
+
status: checks.every((check) => check.ok) ? 'passed' : 'failed',
|
|
1804
|
+
id: cycle.id || null,
|
|
1805
|
+
targetPrimitiveId: cycle.targetPrimitiveId || null,
|
|
1806
|
+
from: cycle.from || null,
|
|
1807
|
+
to: cycle.to || null,
|
|
1808
|
+
unmountScheduleRef: cycle.unmountScheduleRef || null,
|
|
1809
|
+
remountScheduleRef: cycle.remountScheduleRef || null,
|
|
1810
|
+
resourceId: primaryResource && primaryResource.resourceId || cycle.resourceId || null,
|
|
1811
|
+
resourceIds: resourceResults.map((entry) => entry.resourceId).filter(Boolean),
|
|
1812
|
+
resourceKinds: resourceResults.map((entry) => entry.resource && entry.resource.kind || entry.expectedKind).filter(Boolean),
|
|
1813
|
+
resource: primaryResource && primaryResource.resource || null,
|
|
1814
|
+
resources: resourceResults,
|
|
1815
|
+
diagnostics,
|
|
1816
|
+
expectedUnmountCount: cycle.expectedUnmountCount || null,
|
|
1817
|
+
expectedRemountCount: cycle.expectedRemountCount || null,
|
|
1818
|
+
checks
|
|
1819
|
+
};
|
|
1820
|
+
});
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
function createRmtVNextSourceToSeaObjectMatrix(input, options = {}) {
|
|
1824
|
+
const browserFixtureText = typeof options.browserFixtureText === 'string' ? options.browserFixtureText : '';
|
|
1825
|
+
const browserProbe = options.browserProbe || parseJsonScript(browserFixtureText, 'rmt-source-to-sea-probe') || {};
|
|
1826
|
+
const objects = normalizeSourceToSeaObjects(browserProbe);
|
|
1827
|
+
const compileResult = compileRmtVNextSource(input, options.compilerOptions || {});
|
|
1828
|
+
const coreDocument = compileResult.coreDocument || null;
|
|
1829
|
+
const primaryEvidence = options.primaryEvidence || null;
|
|
1830
|
+
const entries = objects.map((objectProbe) => {
|
|
1831
|
+
const objectBrowserProbe = createObjectBrowserProbe(browserProbe, objectProbe);
|
|
1832
|
+
const evidence = primaryEvidence && primaryEvidence.primitiveId === objectProbe.primitiveId
|
|
1833
|
+
? primaryEvidence
|
|
1834
|
+
: createRmtVNextSourceToSeaEvidence(input, {
|
|
1835
|
+
...options,
|
|
1836
|
+
browserProbe: objectBrowserProbe,
|
|
1837
|
+
primitiveId: objectProbe.primitiveId,
|
|
1838
|
+
actionId: objectProbe.expectedAction,
|
|
1839
|
+
expectedAction: objectProbe.expectedAction,
|
|
1840
|
+
expectedText: objectProbe.expectedText,
|
|
1841
|
+
selector: objectProbe.selector,
|
|
1842
|
+
lane: objectProbe.lane || options.lane,
|
|
1843
|
+
browserFixtureText,
|
|
1844
|
+
browserFixturePath: options.browserFixturePath
|
|
1845
|
+
});
|
|
1846
|
+
return summarizeSourceToSeaObject(objectProbe, evidence);
|
|
1847
|
+
});
|
|
1848
|
+
const crossPrimitiveEvents = createCrossPrimitiveEventMatrix(browserProbe.crossPrimitiveEvents, coreDocument, entries);
|
|
1849
|
+
const routeSwitches = createRouteSwitchMatrix(browserProbe.routeSwitches, entries);
|
|
1850
|
+
const routeLifecycleCycles = createRouteLifecycleMatrix(browserProbe.routeLifecycleCycles, coreDocument, entries);
|
|
1851
|
+
const primitiveIds = entries.map((entry) => entry.primitiveId);
|
|
1852
|
+
const lanes = entries.map((entry) => entry.fabric && entry.fabric.lane).filter(Boolean);
|
|
1853
|
+
const routeLifecycleTargets = routeLifecycleCycles.map((entry) => entry.targetPrimitiveId).filter(Boolean);
|
|
1854
|
+
const checks = [
|
|
1855
|
+
createCheck('object matrix compiler result ok', compileResult.ok === true, compileResult.status),
|
|
1856
|
+
createCheck('object matrix declares multiple objects', entries.length >= 4, primitiveIds),
|
|
1857
|
+
createCheck('object matrix primitive ids are unique', new Set(primitiveIds).size === primitiveIds.length, primitiveIds),
|
|
1858
|
+
createCheck('object matrix covers multiple lanes', new Set(lanes).size >= 2, lanes),
|
|
1859
|
+
createCheck('object matrix entries pass', entries.every((entry) => entry.ok), entries.filter((entry) => !entry.ok).map((entry) => entry.primitiveId)),
|
|
1860
|
+
createCheck('object matrix cross-primitive events pass', crossPrimitiveEvents.length >= 2 && crossPrimitiveEvents.every((entry) => entry.ok), crossPrimitiveEvents.map((entry) => `${entry.sourcePrimitiveId}->${entry.targetPrimitiveId}`)),
|
|
1861
|
+
createCheck('object matrix cross-route event passes', crossPrimitiveEvents.some((entry) => entry.stage === 'route-target' && entry.ok && entry.sourceLane === 'transition' && entry.targetLane === 'transition'), crossPrimitiveEvents.map((entry) => `${entry.stage || 'surface'}:${entry.sourcePrimitiveId}->${entry.targetPrimitiveId}`)),
|
|
1862
|
+
createCheck('object matrix route switches pass', routeSwitches.length >= 2 && routeSwitches.every((entry) => entry.ok), routeSwitches.map((entry) => `${entry.from}->${entry.to}`)),
|
|
1863
|
+
createCheck('object matrix route lifecycle cycles pass', routeLifecycleCycles.length >= 2 && routeLifecycleCycles.every((entry) => entry.ok), routeLifecycleTargets),
|
|
1864
|
+
createCheck('object matrix route lifecycle targets are distinct', new Set(routeLifecycleTargets).size >= 2, routeLifecycleTargets)
|
|
1865
|
+
];
|
|
1866
|
+
|
|
1867
|
+
return {
|
|
1868
|
+
schema: RMT_VNEXT_SOURCE_TO_SEA_OBJECT_MATRIX_SCHEMA,
|
|
1869
|
+
workpackage: RMT_VNEXT_SOURCE_TO_SEA_WORKPACKAGE,
|
|
1870
|
+
ok: checks.every((check) => check.ok),
|
|
1871
|
+
status: checks.every((check) => check.ok) ? 'passed' : 'failed',
|
|
1872
|
+
objectCount: entries.length,
|
|
1873
|
+
primitiveIds,
|
|
1874
|
+
lanes,
|
|
1875
|
+
objects: entries,
|
|
1876
|
+
crossPrimitiveEvents,
|
|
1877
|
+
routeSwitches,
|
|
1878
|
+
routeLifecycleCycles,
|
|
1879
|
+
checks
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
function resolveReportOutputPath(outputPath, rootDir = process.cwd()) {
|
|
1884
|
+
return path.isAbsolute(outputPath)
|
|
1885
|
+
? outputPath
|
|
1886
|
+
: path.resolve(rootDir, outputPath);
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
function readSourceToSeaFixture(rootDir, sourcePath) {
|
|
1890
|
+
return fs.readFileSync(path.resolve(rootDir, sourcePath), 'utf8');
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
function createRmtVNextSourceToSeaCiArtifactValidation(report = {}, options = {}) {
|
|
1894
|
+
const browserExecution = report.browserExecution || {};
|
|
1895
|
+
const result = browserExecution.result || {};
|
|
1896
|
+
const routeSwitches = toArray(result.routeSwitches);
|
|
1897
|
+
const routeLifecycleCycles = toArray(result.routeLifecycleCycles);
|
|
1898
|
+
const crossPrimitiveEvents = toArray(result.crossPrimitiveEvents);
|
|
1899
|
+
const auditLifecycle = routeLifecycleCycles.find((entry) => entry && entry.targetPrimitiveId === 'demo.feedback.audit') || {};
|
|
1900
|
+
const expectedAuditResources = ['demo.feedback.auditTimer', 'demo.feedback.auditSubscription'];
|
|
1901
|
+
const expectedAuditKinds = ['timer', 'subscription'];
|
|
1902
|
+
const shouldValidate = browserExecution.required === true || browserExecution.status === 'passed' || options.requireBrowserExecution === true;
|
|
1903
|
+
const expectedBrowserDriver = normalizeBrowserDriver(options.expectedBrowserDriver || RMT_VNEXT_SOURCE_TO_SEA_CI_BROWSER_DRIVER);
|
|
1904
|
+
|
|
1905
|
+
if (!shouldValidate) {
|
|
1906
|
+
return {
|
|
1907
|
+
schema: RMT_VNEXT_SOURCE_TO_SEA_CI_ARTIFACT_SCHEMA,
|
|
1908
|
+
workpackage: RMT_VNEXT_SOURCE_TO_SEA_WORKPACKAGE,
|
|
1909
|
+
ok: true,
|
|
1910
|
+
status: 'skipped',
|
|
1911
|
+
required: false,
|
|
1912
|
+
reason: 'CI artifact validation waits for browser-required evidence.',
|
|
1913
|
+
checks: [
|
|
1914
|
+
createCheck('ci artifact validation waits for required browser execution', browserExecution.status === 'skipped' || browserExecution.required !== true, browserExecution.status)
|
|
1915
|
+
]
|
|
1916
|
+
};
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
const checks = [
|
|
1920
|
+
createCheck('ci artifact report schema matches source-to-sea evidence report', report.schema === RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_REPORT_SCHEMA, report.schema),
|
|
1921
|
+
createCheck('ci artifact report belongs to PRIM-06', report.workpackage === RMT_VNEXT_SOURCE_TO_SEA_WORKPACKAGE, report.workpackage),
|
|
1922
|
+
createCheck('ci artifact report passed before validation', report.ok === true && report.status === 'passed', report.status),
|
|
1923
|
+
createCheck('ci artifact path is stable', report.artifact && report.artifact.path === RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_REPORT_PATH, report.artifact && report.artifact.path),
|
|
1924
|
+
createCheck('ci artifact browser execution is required', report.artifact && report.artifact.browserExecutionRequired === true && browserExecution.required === true, report.artifact && report.artifact.browserExecutionRequired),
|
|
1925
|
+
createCheck('ci artifact browser execution passed', report.artifact && report.artifact.browserExecutionStatus === 'passed' && browserExecution.status === 'passed', browserExecution.status),
|
|
1926
|
+
createCheck('ci artifact browser execution uses expected driver', normalizeBrowserDriver(browserExecution.driver) === expectedBrowserDriver, {
|
|
1927
|
+
actualDriver: browserExecution.driver,
|
|
1928
|
+
expectedDriver: expectedBrowserDriver
|
|
1929
|
+
}),
|
|
1930
|
+
createCheck('ci artifact object count matches Source-to-Sea contract', result.objectCount === 4, result.objectCount),
|
|
1931
|
+
createCheck('ci artifact cross-primitive event count matches contract', crossPrimitiveEvents.length === 2 && crossPrimitiveEvents.every((entry) => entry.status === 'passed'), crossPrimitiveEvents.length),
|
|
1932
|
+
createCheck('ci artifact includes cross-route event evidence', crossPrimitiveEvents.some((entry) => entry.stage === 'route-target' && entry.sourcePrimitiveId === 'demo.feedback.detail' && entry.targetPrimitiveId === 'demo.feedback.audit' && entry.sourceLane === 'transition' && entry.targetLane === 'transition'), crossPrimitiveEvents),
|
|
1933
|
+
createCheck('ci artifact route switches match contract', routeSwitches.length === 2 && routeSwitches.every((entry) => entry.status === 'passed' && entry.targetMounted === true && entry.targetVisible === true), routeSwitches),
|
|
1934
|
+
createCheck('ci artifact route lifecycle cycles match contract', routeLifecycleCycles.length === 2 && routeLifecycleCycles.every((entry) => entry.status === 'passed' && entry.unmounted === true && entry.remounted === true && entry.resourceDisposed === true && entry.countsMatch === true), routeLifecycleCycles),
|
|
1935
|
+
createCheck('ci artifact audit lifecycle records expected resources', expectedAuditResources.every((resourceId) => toArray(auditLifecycle.resourceIds).includes(resourceId)), auditLifecycle.resourceIds),
|
|
1936
|
+
createCheck('ci artifact audit lifecycle records expected resource kinds', expectedAuditKinds.every((kind) => toArray(auditLifecycle.resourceKinds).includes(kind)), auditLifecycle.resourceKinds),
|
|
1937
|
+
createCheck('ci artifact audit lifecycle keeps counts stable', auditLifecycle.unmountCount === 1 && auditLifecycle.remountCount === 1, {
|
|
1938
|
+
unmountCount: auditLifecycle.unmountCount,
|
|
1939
|
+
remountCount: auditLifecycle.remountCount
|
|
1940
|
+
})
|
|
1941
|
+
];
|
|
1942
|
+
const ok = checks.every((check) => check.ok);
|
|
1943
|
+
|
|
1944
|
+
return {
|
|
1945
|
+
schema: RMT_VNEXT_SOURCE_TO_SEA_CI_ARTIFACT_SCHEMA,
|
|
1946
|
+
workpackage: RMT_VNEXT_SOURCE_TO_SEA_WORKPACKAGE,
|
|
1947
|
+
ok,
|
|
1948
|
+
status: ok ? 'passed' : 'failed',
|
|
1949
|
+
required: true,
|
|
1950
|
+
driver: browserExecution.driver || null,
|
|
1951
|
+
artifactPath: report.artifact && report.artifact.path || null,
|
|
1952
|
+
checks
|
|
1953
|
+
};
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
function validateRmtVNextSourceToSeaCiArtifactFile(artifactPath = RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_REPORT_PATH, options = {}) {
|
|
1957
|
+
const rootDir = options.rootDir || process.cwd();
|
|
1958
|
+
const resolvedPath = resolveReportOutputPath(
|
|
1959
|
+
artifactPath || RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_REPORT_PATH,
|
|
1960
|
+
rootDir
|
|
1961
|
+
);
|
|
1962
|
+
|
|
1963
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
1964
|
+
return {
|
|
1965
|
+
schema: RMT_VNEXT_SOURCE_TO_SEA_CI_ARTIFACT_SCHEMA,
|
|
1966
|
+
workpackage: RMT_VNEXT_SOURCE_TO_SEA_WORKPACKAGE,
|
|
1967
|
+
ok: false,
|
|
1968
|
+
status: 'failed',
|
|
1969
|
+
required: true,
|
|
1970
|
+
driver: null,
|
|
1971
|
+
artifactPath: resolvedPath,
|
|
1972
|
+
replayed: true,
|
|
1973
|
+
checks: [
|
|
1974
|
+
createCheck('ci artifact file exists', false, resolvedPath)
|
|
1975
|
+
]
|
|
1976
|
+
};
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
let report = null;
|
|
1980
|
+
try {
|
|
1981
|
+
report = JSON.parse(fs.readFileSync(resolvedPath, 'utf8'));
|
|
1982
|
+
} catch (error) {
|
|
1983
|
+
return {
|
|
1984
|
+
schema: RMT_VNEXT_SOURCE_TO_SEA_CI_ARTIFACT_SCHEMA,
|
|
1985
|
+
workpackage: RMT_VNEXT_SOURCE_TO_SEA_WORKPACKAGE,
|
|
1986
|
+
ok: false,
|
|
1987
|
+
status: 'failed',
|
|
1988
|
+
required: true,
|
|
1989
|
+
driver: null,
|
|
1990
|
+
artifactPath: resolvedPath,
|
|
1991
|
+
replayed: true,
|
|
1992
|
+
checks: [
|
|
1993
|
+
createCheck('ci artifact file parses as JSON', false, error && error.message)
|
|
1994
|
+
]
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
const validation = createRmtVNextSourceToSeaCiArtifactValidation(report, {
|
|
1999
|
+
requireBrowserExecution: true,
|
|
2000
|
+
expectedBrowserDriver: options.expectedBrowserDriver || RMT_VNEXT_SOURCE_TO_SEA_CI_BROWSER_DRIVER
|
|
2001
|
+
});
|
|
2002
|
+
|
|
2003
|
+
return {
|
|
2004
|
+
...validation,
|
|
2005
|
+
artifactPath: resolvedPath,
|
|
2006
|
+
replayed: true
|
|
2007
|
+
};
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
async function createRmtVNextSourceToSeaEvidenceReport(options = {}) {
|
|
2011
|
+
const rootDir = options.rootDir || process.cwd();
|
|
2012
|
+
const sourcePath = options.sourcePath || RMT_VNEXT_SOURCE_TO_SEA_FIXTURE_PATH;
|
|
2013
|
+
const browserFixturePath = options.browserFixturePath || RMT_VNEXT_SOURCE_TO_SEA_BROWSER_FIXTURE_PATH;
|
|
2014
|
+
const sourceText = typeof options.sourceText === 'string'
|
|
2015
|
+
? options.sourceText
|
|
2016
|
+
: readSourceToSeaFixture(rootDir, sourcePath);
|
|
2017
|
+
const browserFixtureText = typeof options.browserFixtureText === 'string'
|
|
2018
|
+
? options.browserFixtureText
|
|
2019
|
+
: readSourceToSeaFixture(rootDir, browserFixturePath);
|
|
2020
|
+
const evidence = options.evidence || createRmtVNextSourceToSeaEvidence({
|
|
2021
|
+
text: sourceText,
|
|
2022
|
+
filePath: path.resolve(rootDir, sourcePath)
|
|
2023
|
+
}, {
|
|
2024
|
+
browserFixtureText,
|
|
2025
|
+
browserFixturePath
|
|
2026
|
+
});
|
|
2027
|
+
const browserExecution = options.browserExecution || await runRmtVNextSourceToSeaBrowserExecution(evidence, {
|
|
2028
|
+
rootDir,
|
|
2029
|
+
browserFixturePath,
|
|
2030
|
+
browserDriver: options.browserDriver,
|
|
2031
|
+
requireBrowserExecution: options.requireBrowserExecution,
|
|
2032
|
+
chromeDriverPath: options.chromeDriverPath,
|
|
2033
|
+
webDriverUrl: options.webDriverUrl,
|
|
2034
|
+
webDriverPort: options.webDriverPort,
|
|
2035
|
+
browserName: options.browserName,
|
|
2036
|
+
timeoutMs: options.timeoutMs
|
|
2037
|
+
});
|
|
2038
|
+
const objectMatrix = options.objectMatrix || createRmtVNextSourceToSeaObjectMatrix({
|
|
2039
|
+
text: sourceText,
|
|
2040
|
+
filePath: path.resolve(rootDir, sourcePath)
|
|
2041
|
+
}, {
|
|
2042
|
+
browserFixtureText,
|
|
2043
|
+
browserFixturePath,
|
|
2044
|
+
primaryEvidence: evidence
|
|
2045
|
+
});
|
|
2046
|
+
const baseChecks = [
|
|
2047
|
+
createCheck('source-to-sea evidence passes', evidence.ok === true, evidence.status),
|
|
2048
|
+
createCheck('source-to-sea object matrix passes', objectMatrix.ok === true, objectMatrix.primitiveIds),
|
|
2049
|
+
createCheck('browser execution evidence contract emitted', browserExecution.schema === RMT_VNEXT_BROWSER_EXECUTION_EVIDENCE_SCHEMA, browserExecution.schema),
|
|
2050
|
+
createCheck('browser execution policy satisfied', browserExecution.ok === true, browserExecution.reason || browserExecution.status)
|
|
2051
|
+
];
|
|
2052
|
+
const baseOk = baseChecks.every((check) => check.ok);
|
|
2053
|
+
const baseReport = {
|
|
2054
|
+
schema: RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_REPORT_SCHEMA,
|
|
2055
|
+
workpackage: RMT_VNEXT_SOURCE_TO_SEA_WORKPACKAGE,
|
|
2056
|
+
ok: baseOk,
|
|
2057
|
+
status: baseOk ? 'passed' : 'failed',
|
|
2058
|
+
source: sourcePath,
|
|
2059
|
+
browserFixture: browserFixturePath,
|
|
2060
|
+
resultKey: RMT_VNEXT_SOURCE_TO_SEA_RESULT_KEY,
|
|
2061
|
+
artifact: {
|
|
2062
|
+
path: RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_REPORT_PATH,
|
|
2063
|
+
browserExecutionRequired: browserExecution.required === true,
|
|
2064
|
+
browserExecutionStatus: browserExecution.status
|
|
2065
|
+
},
|
|
2066
|
+
evidence,
|
|
2067
|
+
objectMatrix,
|
|
2068
|
+
browserExecution,
|
|
2069
|
+
checks: baseChecks
|
|
2070
|
+
};
|
|
2071
|
+
const ciArtifactValidation = createRmtVNextSourceToSeaCiArtifactValidation(baseReport, {
|
|
2072
|
+
requireBrowserExecution: browserExecution.required === true,
|
|
2073
|
+
expectedBrowserDriver: browserExecution.driver || RMT_VNEXT_SOURCE_TO_SEA_CI_BROWSER_DRIVER
|
|
2074
|
+
});
|
|
2075
|
+
const checks = [
|
|
2076
|
+
...baseChecks,
|
|
2077
|
+
createCheck('source-to-sea CI artifact validation passes when browser required', browserExecution.required !== true || ciArtifactValidation.ok === true, ciArtifactValidation.status)
|
|
2078
|
+
];
|
|
2079
|
+
const ok = checks.every((check) => check.ok);
|
|
2080
|
+
|
|
2081
|
+
return {
|
|
2082
|
+
...baseReport,
|
|
2083
|
+
ok,
|
|
2084
|
+
status: ok ? 'passed' : 'failed',
|
|
2085
|
+
ciArtifactValidation,
|
|
2086
|
+
checks
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
async function writeRmtVNextSourceToSeaEvidenceReport(options = {}) {
|
|
2091
|
+
const rootDir = options.rootDir || process.cwd();
|
|
2092
|
+
const report = await createRmtVNextSourceToSeaEvidenceReport(options);
|
|
2093
|
+
const outputPath = resolveReportOutputPath(
|
|
2094
|
+
options.outputPath || RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_REPORT_PATH,
|
|
2095
|
+
rootDir
|
|
2096
|
+
);
|
|
2097
|
+
|
|
2098
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
2099
|
+
fs.writeFileSync(outputPath, `${JSON.stringify(report, null, 2)}\n`, 'utf8');
|
|
2100
|
+
|
|
2101
|
+
return {
|
|
2102
|
+
outputPath,
|
|
2103
|
+
report
|
|
2104
|
+
};
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
function resolveBrowserExecutionDriver(options = {}) {
|
|
2108
|
+
if (options.browserDriver) {
|
|
2109
|
+
return normalizeBrowserDriver(options.browserDriver);
|
|
2110
|
+
}
|
|
2111
|
+
if (options.requireBrowserExecution === true && process.env.RMT_VNEXT_SOURCE_TO_SEA_BROWSER_DRIVER) {
|
|
2112
|
+
return normalizeBrowserDriver(process.env.RMT_VNEXT_SOURCE_TO_SEA_BROWSER_DRIVER);
|
|
2113
|
+
}
|
|
2114
|
+
if (options.requireBrowserExecution === true && process.env.XTEND_BROWSER_SMOKE_DRIVER) {
|
|
2115
|
+
return normalizeBrowserDriver(process.env.XTEND_BROWSER_SMOKE_DRIVER);
|
|
2116
|
+
}
|
|
2117
|
+
if (options.requireBrowserExecution === true) {
|
|
2118
|
+
return detectAvailableBrowserDriver(options);
|
|
2119
|
+
}
|
|
2120
|
+
return normalizeBrowserDriver(process.env.RMT_VNEXT_SOURCE_TO_SEA_BROWSER_DRIVER || '');
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
async function runRmtVNextSourceToSeaBrowserExecution(evidence, options = {}) {
|
|
2124
|
+
const driver = resolveBrowserExecutionDriver(options);
|
|
2125
|
+
const base = {
|
|
2126
|
+
schema: RMT_VNEXT_BROWSER_EXECUTION_EVIDENCE_SCHEMA,
|
|
2127
|
+
workpackage: RMT_VNEXT_SOURCE_TO_SEA_WORKPACKAGE,
|
|
2128
|
+
resultKey: RMT_VNEXT_SOURCE_TO_SEA_RESULT_KEY,
|
|
2129
|
+
fixture: options.browserFixturePath || RMT_VNEXT_SOURCE_TO_SEA_BROWSER_FIXTURE_PATH,
|
|
2130
|
+
url: createBrowserExecutionUrl(options),
|
|
2131
|
+
driver: driver || null,
|
|
2132
|
+
required: options.requireBrowserExecution === true
|
|
2133
|
+
};
|
|
2134
|
+
|
|
2135
|
+
if (!driver) {
|
|
2136
|
+
return {
|
|
2137
|
+
...base,
|
|
2138
|
+
ok: options.requireBrowserExecution !== true,
|
|
2139
|
+
status: options.requireBrowserExecution === true ? 'failed' : 'skipped',
|
|
2140
|
+
mode: 'fixture-contract',
|
|
2141
|
+
reason: 'set RMT_VNEXT_SOURCE_TO_SEA_BROWSER_DRIVER=firefox, chromedriver, webdriver, safari or edge to execute the fixture in a browser',
|
|
2142
|
+
checks: [
|
|
2143
|
+
createCheck('browser execution fixture contract available', Boolean(evidence && evidence.browser && evidence.browser.resultKey === RMT_VNEXT_SOURCE_TO_SEA_RESULT_KEY), evidence && evidence.browser)
|
|
2144
|
+
]
|
|
2145
|
+
};
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
if (!supportedBrowserDriver(driver)) {
|
|
2149
|
+
return {
|
|
2150
|
+
...base,
|
|
2151
|
+
ok: options.requireBrowserExecution !== true,
|
|
2152
|
+
status: options.requireBrowserExecution === true ? 'failed' : 'skipped',
|
|
2153
|
+
mode: 'unsupported-driver',
|
|
2154
|
+
reason: `unsupported RMT vNext source-to-sea browser driver: ${driver}`,
|
|
2155
|
+
checks: [
|
|
2156
|
+
createCheck('browser execution driver supported', false, driver)
|
|
2157
|
+
]
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
try {
|
|
2162
|
+
const result = await runWebDriverBrowserProbe({
|
|
2163
|
+
...options,
|
|
2164
|
+
driver,
|
|
2165
|
+
resultKey: RMT_VNEXT_SOURCE_TO_SEA_RESULT_KEY
|
|
2166
|
+
});
|
|
2167
|
+
const resultValidation = createRmtVNextSourceToSeaBrowserResultValidation(result, evidence);
|
|
2168
|
+
return {
|
|
2169
|
+
...base,
|
|
2170
|
+
ok: resultValidation.ok,
|
|
2171
|
+
status: resultValidation.status,
|
|
2172
|
+
mode: driver,
|
|
2173
|
+
result,
|
|
2174
|
+
resultValidation,
|
|
2175
|
+
checks: resultValidation.checks
|
|
2176
|
+
};
|
|
2177
|
+
} catch (error) {
|
|
2178
|
+
return {
|
|
2179
|
+
...base,
|
|
2180
|
+
ok: options.requireBrowserExecution !== true,
|
|
2181
|
+
status: options.requireBrowserExecution === true ? 'failed' : 'skipped',
|
|
2182
|
+
mode: driver,
|
|
2183
|
+
reason: error && error.message ? error.message : String(error),
|
|
2184
|
+
checks: [
|
|
2185
|
+
createCheck('browser execution completed', false, error && error.message ? error.message : String(error))
|
|
2186
|
+
]
|
|
2187
|
+
};
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
|
|
2191
|
+
module.exports = {
|
|
2192
|
+
RMT_VNEXT_BROWSER_EXECUTION_EVIDENCE_SCHEMA,
|
|
2193
|
+
RMT_VNEXT_FABRIC_BRIDGE_EVIDENCE_SCHEMA,
|
|
2194
|
+
RMT_VNEXT_FABRIC_BRIDGE_LANE_MATRIX,
|
|
2195
|
+
RMT_VNEXT_FABRIC_BRIDGE_WORKPACKAGE,
|
|
2196
|
+
RMT_VNEXT_HOST_ADAPTER_TELEMETRY_SCHEMA,
|
|
2197
|
+
RMT_VNEXT_ROUTE_COMPONENT_FIBER_EVIDENCE_SCHEMA,
|
|
2198
|
+
RMT_VNEXT_ROUTE_COMPONENT_FIBER_SCENARIOS,
|
|
2199
|
+
RMT_VNEXT_SOURCE_TO_SEA_BROWSER_FIXTURE_PATH,
|
|
2200
|
+
RMT_VNEXT_SOURCE_TO_SEA_BROWSER_PROBE_SCHEMA,
|
|
2201
|
+
RMT_VNEXT_SOURCE_TO_SEA_BROWSER_RESULT_VALIDATION_SCHEMA,
|
|
2202
|
+
RMT_VNEXT_SOURCE_TO_SEA_CI_ARTIFACT_SCHEMA,
|
|
2203
|
+
RMT_VNEXT_SOURCE_TO_SEA_CI_BROWSER_DRIVER,
|
|
2204
|
+
RMT_VNEXT_SOURCE_TO_SEA_CI_BROWSER_NAME,
|
|
2205
|
+
RMT_VNEXT_SOURCE_TO_SEA_CI_WEBDRIVER_PORT,
|
|
2206
|
+
RMT_VNEXT_SOURCE_TO_SEA_CLEANUP_DIAGNOSTIC_CODES,
|
|
2207
|
+
RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_SCHEMA,
|
|
2208
|
+
RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_REPORT_PATH,
|
|
2209
|
+
RMT_VNEXT_SOURCE_TO_SEA_EVIDENCE_REPORT_SCHEMA,
|
|
2210
|
+
RMT_VNEXT_SOURCE_TO_SEA_FIXTURE_PATH,
|
|
2211
|
+
RMT_VNEXT_SOURCE_TO_SEA_MODULE_PATH,
|
|
2212
|
+
RMT_VNEXT_SOURCE_TO_SEA_OBJECT_MATRIX_SCHEMA,
|
|
2213
|
+
RMT_VNEXT_SOURCE_TO_SEA_RESULT_KEY,
|
|
2214
|
+
RMT_VNEXT_SOURCE_TO_SEA_SCHEMA,
|
|
2215
|
+
RMT_VNEXT_SOURCE_TO_SEA_SUITE_PATH,
|
|
2216
|
+
RMT_VNEXT_SOURCE_TO_SEA_SUPPORTED_BROWSER_DRIVERS,
|
|
2217
|
+
RMT_VNEXT_SOURCE_TO_SEA_WORKPACKAGE,
|
|
2218
|
+
createRmtVNextSourceToSeaCiArtifactValidation,
|
|
2219
|
+
createRmtVNextSourceToSeaBrowserResultValidation,
|
|
2220
|
+
validateRmtVNextSourceToSeaCiArtifactFile,
|
|
2221
|
+
createRmtVNextSourceToSeaEvidenceReport,
|
|
2222
|
+
createRmtVNextFabricBridgeEvidence,
|
|
2223
|
+
createRmtVNextSourceToSeaObjectMatrix,
|
|
2224
|
+
runRmtVNextSourceToSeaBrowserExecution,
|
|
2225
|
+
createRmtVNextSourceToSeaEvidence,
|
|
2226
|
+
writeRmtVNextSourceToSeaEvidenceReport
|
|
2227
|
+
};
|