@beyondwork/docx-react-component 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -104
- package/package.json +66 -15
- package/src/api/public-types.ts +1 -1
- package/src/compare/diff-engine.ts +530 -0
- package/src/compare/export-redlines.ts +162 -0
- package/src/compare/snapshot.ts +37 -0
- package/src/core/commands/index.ts +1 -1
- package/src/core/state/editor-state.ts +2 -2
- package/src/index.ts +45 -0
- package/src/legal/bookmarks.ts +196 -0
- package/src/legal/cross-references.ts +356 -0
- package/src/legal/defined-terms.ts +203 -0
- package/src/runtime/document-runtime.ts +3 -5
- package/src/runtime/table-commands.ts +4 -1
- package/src/runtime/table-schema.ts +17 -2
- package/src/runtime/virtualized-rendering.ts +258 -0
- package/src/ui/WordReviewEditor.tsx +256 -35
- package/src/ui-tailwind/editor-surface/tw-editor-surface.tsx +2 -2
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +16 -2
- package/.codex/config.toml +0 -5
- package/.corepack/v1/pnpm/10.30.3/.corepack +0 -1
- package/.corepack/v1/pnpm/10.30.3/LICENSE +0 -22
- package/.corepack/v1/pnpm/10.30.3/README.md +0 -240
- package/.corepack/v1/pnpm/10.30.3/dist/node-gyp-bin/node-gyp +0 -6
- package/.corepack/v1/pnpm/10.30.3/dist/node-gyp-bin/node-gyp.cmd +0 -5
- package/.corepack/v1/pnpm/10.30.3/dist/pnpm.cjs +0 -195400
- package/.corepack/v1/pnpm/10.30.3/dist/pnpmrc +0 -2
- package/.corepack/v1/pnpm/10.30.3/dist/reflink.darwin-arm64-2HJ4WGO6.node +0 -0
- package/.corepack/v1/pnpm/10.30.3/dist/reflink.darwin-x64-3G3H6IW4.node +0 -0
- package/.corepack/v1/pnpm/10.30.3/dist/reflink.win32-arm64-msvc-Q6BARPPB.node +0 -0
- package/.corepack/v1/pnpm/10.30.3/dist/reflink.win32-x64-msvc-J2TZHRQI.node +0 -0
- package/.corepack/v1/pnpm/10.30.3/dist/templates/completion.bash +0 -31
- package/.corepack/v1/pnpm/10.30.3/dist/templates/completion.fish +0 -22
- package/.corepack/v1/pnpm/10.30.3/dist/templates/completion.ps1 +0 -193
- package/.corepack/v1/pnpm/10.30.3/dist/templates/completion.zsh +0 -27
- package/.corepack/v1/pnpm/10.30.3/dist/vendor/fastlist-0.3.0-x64.exe +0 -0
- package/.corepack/v1/pnpm/10.30.3/dist/vendor/fastlist-0.3.0-x86.exe +0 -0
- package/.corepack/v1/pnpm/10.30.3/dist/worker.js +0 -10119
- package/.corepack/v1/pnpm/10.30.3/package.json +0 -192
- package/.cursor/mcp.json +0 -7
- package/.github/workflows/ci.yml +0 -35
- package/.mcp.json +0 -7
- package/.openclaw/workspace-state.json +0 -4
- package/.pnpmrc.json +0 -1
- package/.wave-launch.sh +0 -7
- package/.workspace-marker +0 -1
- package/AGENTS.md +0 -78
- package/CHANGELOG.md +0 -177
- package/DESIGN.md +0 -929
- package/HEARTBEAT.md +0 -7
- package/IDENTITY.md +0 -23
- package/SOUL.md +0 -36
- package/TOOLS.md +0 -40
- package/USER.md +0 -17
- package/docs/README.md +0 -107
- package/docs/agents/wave-cont-eval-role.md +0 -36
- package/docs/agents/wave-cont-qa-role.md +0 -52
- package/docs/agents/wave-deploy-verifier-role.md +0 -34
- package/docs/agents/wave-design-role.md +0 -47
- package/docs/agents/wave-documentation-role.md +0 -34
- package/docs/agents/wave-infra-role.md +0 -34
- package/docs/agents/wave-integration-role.md +0 -37
- package/docs/agents/wave-launcher-role.md +0 -41
- package/docs/agents/wave-orchestrator-role.md +0 -52
- package/docs/agents/wave-planner-role.md +0 -39
- package/docs/agents/wave-security-role.md +0 -40
- package/docs/architecture/docx/README.md +0 -10
- package/docs/architecture/future/README.md +0 -8
- package/docs/architecture/ooxml-upgrade-analysis.md +0 -134
- package/docs/architecture/platform/shared-openxml-editor-platform.md +0 -153
- package/docs/architecture/xlsx/canonical-workbook-model-and-commands.md +0 -187
- package/docs/architecture/xlsx/spreadsheet-editor-frontend-architecture.md +0 -150
- package/docs/comment-redline-overview.md +0 -350
- package/docs/concepts/context7-vs-skills.md +0 -118
- package/docs/concepts/operating-modes.md +0 -91
- package/docs/concepts/runtime-agnostic-orchestration.md +0 -111
- package/docs/concepts/what-is-a-wave.md +0 -217
- package/docs/context7/bundles.json +0 -222
- package/docs/context7/planner-agent/README.md +0 -28
- package/docs/context7/planner-agent/manifest.json +0 -83
- package/docs/context7/planner-agent/papers/cooperbench-why-coding-agents-cannot-be-your-teammates-yet.md +0 -3283
- package/docs/context7/planner-agent/papers/dova-deliberation-first-multi-agent-orchestration-for-autonomous-research-automation.md +0 -1699
- package/docs/context7/planner-agent/papers/dpbench-large-language-models-struggle-with-simultaneous-coordination.md +0 -2251
- package/docs/context7/planner-agent/papers/incremental-planning-to-control-a-blackboard-based-problem-solver.md +0 -1729
- package/docs/context7/planner-agent/papers/silo-bench-a-scalable-environment-for-evaluating-distributed-coordination-in-multi-agent-llm-systems.md +0 -3747
- package/docs/context7/planner-agent/papers/todoevolve-learning-to-architect-agent-planning-systems.md +0 -1675
- package/docs/context7/planner-agent/papers/verified-multi-agent-orchestration-a-plan-execute-verify-replan-framework-for-complex-query-resolution.md +0 -1173
- package/docs/context7/planner-agent/papers/why-do-multi-agent-llm-systems-fail.md +0 -5211
- package/docs/context7/planner-agent/topics/planning-and-orchestration.md +0 -24
- package/docs/evals/arm-templates/README.md +0 -13
- package/docs/evals/arm-templates/full-wave.json +0 -15
- package/docs/evals/arm-templates/single-agent.json +0 -15
- package/docs/evals/benchmark-catalog.json +0 -670
- package/docs/evals/cases/README.md +0 -47
- package/docs/evals/cases/wave-blackboard-inbox-targeting.json +0 -73
- package/docs/evals/cases/wave-contradiction-conflict.json +0 -104
- package/docs/evals/cases/wave-expert-routing-preservation.json +0 -69
- package/docs/evals/cases/wave-hidden-profile-private-evidence.json +0 -81
- package/docs/evals/cases/wave-premature-closure-guard.json +0 -71
- package/docs/evals/cases/wave-silo-cross-agent-state.json +0 -77
- package/docs/evals/cases/wave-simultaneous-lockstep.json +0 -92
- package/docs/evals/external-benchmarks.json +0 -85
- package/docs/evals/external-command-config.sample.json +0 -9
- package/docs/evals/external-command-config.swe-bench-pro.json +0 -8
- package/docs/evals/pilots/README.md +0 -47
- package/docs/evals/pilots/swe-bench-pro-public-full-wave-review-10.json +0 -64
- package/docs/evals/pilots/swe-bench-pro-public-pilot.json +0 -111
- package/docs/evals/wave-benchmark-program.md +0 -302
- package/docs/guides/planner.md +0 -220
- package/docs/guides/recommendations-0.8.9.md +0 -133
- package/docs/guides/signal-wrappers.md +0 -165
- package/docs/guides/terminal-surfaces.md +0 -96
- package/docs/image copy.png +0 -0
- package/docs/image.png +0 -0
- package/docs/images/image.png +0 -0
- package/docs/legal-feedback-architecture.md +0 -498
- package/docs/plans/component-cutover-matrix.json +0 -1072
- package/docs/plans/component-cutover-matrix.md +0 -307
- package/docs/plans/context7-wave-orchestrator.md +0 -155
- package/docs/plans/current-state.md +0 -198
- package/docs/plans/docx/README.md +0 -9
- package/docs/plans/examples/wave-benchmark-improvement.md +0 -108
- package/docs/plans/examples/wave-example-live-proof.md +0 -435
- package/docs/plans/master-plan.md +0 -224
- package/docs/plans/migration.md +0 -538
- package/docs/plans/operations/README.md +0 -7
- package/docs/plans/operations/wave-10-word-certification.md +0 -87
- package/docs/plans/operations/wave-8-railway-staging.md +0 -153
- package/docs/plans/operations/wave-9-manual-certification.md +0 -73
- package/docs/plans/platform/README.md +0 -9
- package/docs/plans/reference/legal-checklist-coverage.md +0 -258
- package/docs/plans/wave-orchestrator.md +0 -423
- package/docs/plans/waves/README.md +0 -75
- package/docs/plans/waves/completed/wave-0.md +0 -195
- package/docs/plans/waves/completed/wave-1.md +0 -379
- package/docs/plans/waves/completed/wave-10.md +0 -670
- package/docs/plans/waves/completed/wave-11.md +0 -335
- package/docs/plans/waves/completed/wave-12.md +0 -417
- package/docs/plans/waves/completed/wave-13.md +0 -316
- package/docs/plans/waves/completed/wave-14.md +0 -319
- package/docs/plans/waves/completed/wave-15.md +0 -321
- package/docs/plans/waves/completed/wave-16.md +0 -316
- package/docs/plans/waves/completed/wave-17.md +0 -331
- package/docs/plans/waves/completed/wave-18.md +0 -328
- package/docs/plans/waves/completed/wave-2.md +0 -438
- package/docs/plans/waves/completed/wave-3.md +0 -435
- package/docs/plans/waves/completed/wave-4.md +0 -430
- package/docs/plans/waves/completed/wave-5.md +0 -430
- package/docs/plans/waves/completed/wave-6.md +0 -430
- package/docs/plans/waves/completed/wave-7.md +0 -526
- package/docs/plans/waves/completed/wave-8.md +0 -596
- package/docs/plans/waves/completed/wave-9.md +0 -552
- package/docs/plans/waves/deferred/README.md +0 -14
- package/docs/plans/waves/deferred/encrypted-intake-contracts.md +0 -282
- package/docs/plans/waves/deferred/legal-feedback-wave-expansion.md +0 -308
- package/docs/plans/waves/deferred/wave-encrypted-intake.md +0 -451
- package/docs/plans/waves/design/README.md +0 -5
- package/docs/plans/waves/design/wave-1-a1.md +0 -309
- package/docs/plans/waves/reviews/README.md +0 -5
- package/docs/plans/waves/reviews/wave-0-cont-qa.md +0 -151
- package/docs/plans/waves/reviews/wave-1-cont-qa.md +0 -46
- package/docs/plans/waves/reviews/wave-10-accessibility-and-design.md +0 -51
- package/docs/plans/waves/reviews/wave-10-cont-qa.md +0 -24
- package/docs/plans/waves/reviews/wave-10-dashboard-proof.md +0 -46
- package/docs/plans/waves/reviews/wave-10-performance-signoff.md +0 -55
- package/docs/plans/waves/reviews/wave-10-regression-proof.md +0 -23
- package/docs/plans/waves/reviews/wave-10-release-audit.md +0 -31
- package/docs/plans/waves/reviews/wave-10-service-proof.md +0 -83
- package/docs/plans/waves/reviews/wave-10-word-certification.md +0 -31
- package/docs/plans/waves/reviews/wave-18-ai-contract-closure.md +0 -277
- package/docs/plans/waves/reviews/wave-18-cont-qa.md +0 -255
- package/docs/plans/waves/reviews/wave-18-parity-proof.md +0 -271
- package/docs/plans/waves/reviews/wave-19-cont-qa.md +0 -59
- package/docs/plans/waves/reviews/wave-2-cont-qa.md +0 -72
- package/docs/plans/waves/reviews/wave-20-cont-qa.md +0 -60
- package/docs/plans/waves/reviews/wave-25-cont-qa.md +0 -48
- package/docs/plans/waves/reviews/wave-28-cont-qa.md +0 -46
- package/docs/plans/waves/reviews/wave-29-cont-qa.md +0 -53
- package/docs/plans/waves/reviews/wave-3-cont-qa.md +0 -53
- package/docs/plans/waves/reviews/wave-3-core-proof.md +0 -77
- package/docs/plans/waves/reviews/wave-3-validator-proof.md +0 -73
- package/docs/plans/waves/reviews/wave-32-cont-qa.md +0 -43
- package/docs/plans/waves/reviews/wave-33-cont-qa.md +0 -526
- package/docs/plans/waves/reviews/wave-34-cont-qa.md +0 -100
- package/docs/plans/waves/reviews/wave-35-cont-qa.md +0 -145
- package/docs/plans/waves/reviews/wave-4-cont-qa.md +0 -47
- package/docs/plans/waves/reviews/wave-4-structure-proof.md +0 -69
- package/docs/plans/waves/reviews/wave-5-comment-proof.md +0 -158
- package/docs/plans/waves/reviews/wave-5-cont-qa.md +0 -68
- package/docs/plans/waves/reviews/wave-6-cont-qa.md +0 -416
- package/docs/plans/waves/reviews/wave-6-redline-proof.md +0 -130
- package/docs/plans/waves/reviews/wave-7-cont-qa.md +0 -82
- package/docs/plans/waves/reviews/wave-7-ooxml-compliance.md +0 -85
- package/docs/plans/waves/reviews/wave-7-preservation-proof.md +0 -119
- package/docs/plans/waves/reviews/wave-7-trust-ux.md +0 -87
- package/docs/plans/waves/reviews/wave-8-accessibility-and-design.md +0 -128
- package/docs/plans/waves/reviews/wave-8-cont-qa.md +0 -92
- package/docs/plans/waves/reviews/wave-8-live-proof.md +0 -140
- package/docs/plans/waves/reviews/wave-8-security.md +0 -47
- package/docs/plans/waves/reviews/wave-9-editor-embedding.md +0 -39
- package/docs/plans/waves/reviews/wave-9-fixture-runner.md +0 -56
- package/docs/plans/waves/reviews/wave-9-live-proof.md +0 -105
- package/docs/plans/waves/reviews/wave-9-usability-and-performance.md +0 -152
- package/docs/plans/waves/specs/README.md +0 -5
- package/docs/plans/waves/specs/wave-1-component-boundaries.md +0 -322
- package/docs/plans/waves/specs/wave-1-ooxml-contracts.md +0 -323
- package/docs/plans/waves/specs/wave-1-review-and-ui-contracts.md +0 -339
- package/docs/plans/waves/specs/wave-1-runtime-contracts.md +0 -509
- package/docs/plans/waves/wave-19.md +0 -341
- package/docs/plans/waves/wave-20.md +0 -308
- package/docs/plans/waves/wave-21.md +0 -289
- package/docs/plans/waves/wave-22.md +0 -221
- package/docs/plans/waves/wave-23.md +0 -295
- package/docs/plans/waves/wave-24.md +0 -286
- package/docs/plans/waves/wave-25.md +0 -313
- package/docs/plans/waves/wave-26.md +0 -300
- package/docs/plans/waves/wave-27.md +0 -299
- package/docs/plans/waves/wave-28.md +0 -368
- package/docs/plans/waves/wave-29.md +0 -303
- package/docs/plans/waves/wave-30.md +0 -307
- package/docs/plans/waves/wave-31.md +0 -231
- package/docs/plans/waves/wave-32.md +0 -152
- package/docs/plans/waves/wave-33.md +0 -147
- package/docs/plans/waves/wave-34.md +0 -148
- package/docs/plans/waves/wave-35.md +0 -141
- package/docs/plans/waves/wave-36.md +0 -146
- package/docs/plans/xlsx/README.md +0 -14
- package/docs/plans/xlsx/xlsx-fixture-corpus-and-certification-plan.md +0 -126
- package/docs/reference/cli-reference.md +0 -600
- package/docs/reference/coordination-and-closure.md +0 -487
- package/docs/reference/deep-research-report (15).md +0 -25
- package/docs/reference/docx/README.md +0 -10
- package/docs/reference/legal-checklist.md +0 -445
- package/docs/reference/live-proof-waves.md +0 -199
- package/docs/reference/ooxml-compliance.md +0 -129
- package/docs/reference/ooxml-feature-parity-matrix.md +0 -172
- package/docs/reference/platform/shared-ooxml-platform-guidance.md +0 -77
- package/docs/reference/prototype-agent-prompt-legal-fidelity.md +0 -155
- package/docs/reference/public-api.md +0 -456
- package/docs/reference/repository-guidance.md +0 -58
- package/docs/reference/runtime-config/README.md +0 -182
- package/docs/reference/runtime-config/claude.md +0 -110
- package/docs/reference/runtime-config/codex.md +0 -82
- package/docs/reference/runtime-config/opencode.md +0 -93
- package/docs/reference/sample-waves.md +0 -105
- package/docs/reference/skills.md +0 -237
- package/docs/reference/templates/AGENTS.md +0 -78
- package/docs/reference/templates/HEARTBEAT.md +0 -7
- package/docs/reference/templates/IDENTITY.md +0 -23
- package/docs/reference/templates/SOUL.md +0 -36
- package/docs/reference/templates/TOOLS.md +0 -40
- package/docs/reference/templates/USER.md +0 -17
- package/docs/reference/wave-control.md +0 -184
- package/docs/reference/wave-planning-lessons.md +0 -167
- package/docs/reference/word-review-editor-frontend-architecture.md +0 -479
- package/docs/reference/word-review-editor-ux-guide.md +0 -253
- package/docs/reference/xlsx/xlsx-ooxml-compliance.md +0 -137
- package/docs/research/agent-context-sources.md +0 -178
- package/docs/research/coordination-failure-review.md +0 -290
- package/docs/research/docx-react-component/Canonical Document Schema Specification for a React-based Word-compatible Editor.md +0 -2317
- package/docs/research/docx-react-component/Feature Compatibility Matrix for a React Word Compatible Legal Editor v1.md +0 -219
- package/docs/research/docx-react-component/React Component Architecture and Front-End Structure Specification for a Word-Compatible Legal Review Editor.md +0 -1112
- package/docs/research/docx-react-component/document_compatibility_and_testing_spec.md +0 -751
- package/docs/research/xlsx/raw/README.md +0 -13
- package/docs/roadmap.md +0 -174
- package/docs/superpowers/plans/2026-03-28-harness-control-bar.md +0 -677
- package/docs/superpowers/specs/2026-03-28-harness-control-bar-design.md +0 -274
- package/docs/xlsx-react/README.md +0 -38
- package/docs/xlsx-react/agent-llm-interaction-layer-docx-xlsx.md +0 -621
- package/docs/xlsx-react/canonical-workbook-model-and-commands.md +0 -948
- package/docs/xlsx-react/shared-openxml-editor-platform-docx-xlsx.md +0 -228
- package/docs/xlsx-react/spreadsheet-editor-component-architecture.md +0 -809
- package/docs/xlsx-react/spreadsheet-editor-frontend-architecture.md +0 -537
- package/docs/xlsx-react/spreadsheet-editor-ux-guide.md +0 -520
- package/docs/xlsx-react/xlsx-editor-research-pack.md +0 -871
- package/docs/xlsx-react/xlsx-fixture-corpus-and-certification-plan.md +0 -436
- package/docs/xlsx-react/xlsx-ooxml-compliance.md +0 -320
- package/examples/README.md +0 -16
- package/memory/MEMORY.md +0 -24
- package/pnpm-workspace.yaml +0 -4
- package/scripts/check-no-authored-js.sh +0 -13
- package/scripts/context7-api-check.sh +0 -65
- package/scripts/context7-export-env.sh +0 -42
- package/scripts/run-context7-mcp.sh +0 -8
- package/scripts/run-workspace-tests.sh +0 -15
- package/scripts/start-wave-10-local.sh +0 -189
- package/scripts/wave-agent-attach.sh +0 -47
- package/scripts/wave-auto-answer.sh +0 -118
- package/scripts/wave-dashboard-attach.sh +0 -13
- package/scripts/wave-launch.sh +0 -273
- package/scripts/wave-overnight-supervisor.sh +0 -145
- package/scripts/wave-status.sh +0 -379
- package/scripts/wave-watch.sh +0 -231
- package/services/README.md +0 -17
- package/services/openxml-validator/Dockerfile +0 -29
- package/services/openxml-validator/OpenXmlValidator.Api.csproj +0 -12
- package/services/openxml-validator/Program.cs +0 -436
- package/services/openxml-validator/README.md +0 -152
- package/services/openxml-validator/railway.json +0 -16
- package/services/react-word-editor/.tmp-a4/src/api/public-types.ts +0 -318
- package/services/react-word-editor/.tmp-a4/src/ui/WordReviewEditor.tsx +0 -1302
- package/services/react-word-editor/.tmp-a4/src/ui/editor-surface/editor-surface.tsx +0 -546
- package/services/react-word-editor/.tmp-a4/test/ui/word-review-editor.test.tsx +0 -146
- package/services/react-word-editor/.tmp-a4-build/src/api/public-types.js +0 -2
- package/services/react-word-editor/.tmp-a4-build/src/ui/WordReviewEditor.js +0 -818
- package/services/react-word-editor/.tmp-a4-build/src/ui/editor-surface/editor-surface.js +0 -229
- package/services/react-word-editor/.tmp-a4-build/test/ui/word-review-editor.test.js +0 -121
- package/services/react-word-editor/.tmp-wave-4-a3-tsconfig.json +0 -21
- package/services/react-word-editor/.tmp-wave-4-a3-tsconfig.tsbuildinfo +0 -1
- package/services/react-word-editor/Dockerfile +0 -26
- package/services/react-word-editor/README.md +0 -254
- package/services/react-word-editor/app/api/certification/route.ts +0 -79
- package/services/react-word-editor/app/api/demo-sessions/route.ts +0 -109
- package/services/react-word-editor/app/api/deploy-health/route.ts +0 -23
- package/services/react-word-editor/app/api/exports/[exportId]/route.ts +0 -34
- package/services/react-word-editor/app/api/exports/route.ts +0 -81
- package/services/react-word-editor/app/api/fixtures/[fixtureId]/run/route.ts +0 -100
- package/services/react-word-editor/app/api/health/route.ts +0 -70
- package/services/react-word-editor/app/api/runs/[runId]/route.ts +0 -36
- package/services/react-word-editor/app/api/scenarios/[scenarioId]/run/route.ts +0 -85
- package/services/react-word-editor/app/api/sessions/[sessionId]/route.ts +0 -199
- package/services/react-word-editor/app/api/sessions/[sessionId]/source/route.ts +0 -45
- package/services/react-word-editor/app/api/uploads/route.ts +0 -70
- package/services/react-word-editor/app/api/validate/route.ts +0 -310
- package/services/react-word-editor/app/certification/[runId]/page.tsx +0 -14
- package/services/react-word-editor/app/certification/page.tsx +0 -32
- package/services/react-word-editor/app/dashboard/page.tsx +0 -7
- package/services/react-word-editor/app/demo/page.tsx +0 -30
- package/services/react-word-editor/app/demo/prototype-client.tsx +0 -1080
- package/services/react-word-editor/app/editor/[sessionId]/page.tsx +0 -33
- package/services/react-word-editor/app/fixtures/page.tsx +0 -7
- package/services/react-word-editor/app/globals.css +0 -121
- package/services/react-word-editor/app/layout.tsx +0 -32
- package/services/react-word-editor/app/page.tsx +0 -30
- package/services/react-word-editor/app/runs/[runId]/page.tsx +0 -34
- package/services/react-word-editor/app/wave-10-word-review/page.tsx +0 -7
- package/services/react-word-editor/components/harness-control-bar.tsx +0 -289
- package/services/react-word-editor/components/harness-editor-session-client.tsx +0 -1214
- package/services/react-word-editor/components/harness-workspace-page.tsx +0 -715
- package/services/react-word-editor/components/reduced-motion-toggle.tsx +0 -79
- package/services/react-word-editor/components/workspace-certification-panel.tsx +0 -307
- package/services/react-word-editor/lib/certification-bundle.ts +0 -796
- package/services/react-word-editor/lib/certification-store.ts +0 -661
- package/services/react-word-editor/lib/demo-fixtures.test.mjs +0 -195
- package/services/react-word-editor/lib/demo-fixtures.ts +0 -1519
- package/services/react-word-editor/lib/editor-session-summary.test.mjs +0 -68
- package/services/react-word-editor/lib/editor-session-summary.ts +0 -14
- package/services/react-word-editor/lib/editor-session.ts +0 -228
- package/services/react-word-editor/lib/exports-route.test.mjs +0 -32
- package/services/react-word-editor/lib/harness-client.ts +0 -347
- package/services/react-word-editor/lib/harness-config.json +0 -30
- package/services/react-word-editor/lib/harness-config.test.mjs +0 -31
- package/services/react-word-editor/lib/harness-config.ts +0 -21
- package/services/react-word-editor/lib/harness-editor-datastore.test.mjs +0 -220
- package/services/react-word-editor/lib/harness-editor-datastore.ts +0 -161
- package/services/react-word-editor/lib/private-mode.test.mjs +0 -42
- package/services/react-word-editor/lib/private-mode.ts +0 -61
- package/services/react-word-editor/lib/regression-report.test.mjs +0 -352
- package/services/react-word-editor/lib/regression-report.ts +0 -896
- package/services/react-word-editor/lib/run-artifacts.ts +0 -934
- package/services/react-word-editor/lib/run-history.ts +0 -755
- package/services/react-word-editor/lib/scenario-artifacts.test.mjs +0 -41
- package/services/react-word-editor/lib/scenario-artifacts.ts +0 -44
- package/services/react-word-editor/lib/storage.ts +0 -953
- package/services/react-word-editor/lib/validator-client.test.mjs +0 -54
- package/services/react-word-editor/lib/validator-client.ts +0 -95
- package/services/react-word-editor/lib/workspace-navigation.ts +0 -79
- package/services/react-word-editor/middleware.ts +0 -35
- package/services/react-word-editor/next-env.d.ts +0 -6
- package/services/react-word-editor/next.config.mjs +0 -15
- package/services/react-word-editor/package.json +0 -38
- package/services/react-word-editor/postcss.config.mjs +0 -8
- package/services/react-word-editor/railway.json +0 -21
- package/services/react-word-editor/scripts/wave-10-certification.mjs +0 -101
- package/services/react-word-editor/scripts/wave-9-live-usability-pilot.mjs +0 -911
- package/services/react-word-editor/tsconfig.json +0 -39
- package/services/react-word-editor/tsconfig.tsbuildinfo +0 -1
- package/skills/README.md +0 -48
- package/skills/domain-docx-compatibility/SKILL.md +0 -44
- package/skills/domain-docx-compatibility/skill.json +0 -19
- package/skills/domain-editor-architecture/SKILL.md +0 -49
- package/skills/domain-editor-architecture/skill.json +0 -19
- package/skills/domain-legal-review/SKILL.md +0 -39
- package/skills/domain-legal-review/skill.json +0 -19
- package/skills/provider-aws/SKILL.md +0 -117
- package/skills/provider-aws/adapters/claude.md +0 -1
- package/skills/provider-aws/adapters/codex.md +0 -1
- package/skills/provider-aws/references/service-verification.md +0 -39
- package/skills/provider-aws/skill.json +0 -54
- package/skills/provider-custom-deploy/SKILL.md +0 -64
- package/skills/provider-custom-deploy/skill.json +0 -50
- package/skills/provider-docker-compose/SKILL.md +0 -96
- package/skills/provider-docker-compose/adapters/local.md +0 -1
- package/skills/provider-docker-compose/skill.json +0 -53
- package/skills/provider-github-release/SKILL.md +0 -121
- package/skills/provider-github-release/adapters/claude.md +0 -1
- package/skills/provider-github-release/adapters/codex.md +0 -1
- package/skills/provider-github-release/skill.json +0 -55
- package/skills/provider-kubernetes/SKILL.md +0 -143
- package/skills/provider-kubernetes/adapters/claude.md +0 -1
- package/skills/provider-kubernetes/adapters/codex.md +0 -1
- package/skills/provider-kubernetes/references/kubectl-patterns.md +0 -58
- package/skills/provider-kubernetes/skill.json +0 -52
- package/skills/provider-railway/SKILL.md +0 -123
- package/skills/provider-railway/adapters/claude.md +0 -1
- package/skills/provider-railway/adapters/codex.md +0 -1
- package/skills/provider-railway/adapters/local.md +0 -1
- package/skills/provider-railway/adapters/opencode.md +0 -1
- package/skills/provider-railway/references/verification-commands.md +0 -39
- package/skills/provider-railway/skill.json +0 -71
- package/skills/provider-ssh-manual/SKILL.md +0 -97
- package/skills/provider-ssh-manual/skill.json +0 -54
- package/skills/repo-coding-rules/SKILL.md +0 -55
- package/skills/repo-coding-rules/skill.json +0 -34
- package/skills/role-cont-eval/SKILL.md +0 -91
- package/skills/role-cont-eval/adapters/codex.md +0 -1
- package/skills/role-cont-eval/skill.json +0 -36
- package/skills/role-cont-qa/SKILL.md +0 -100
- package/skills/role-cont-qa/adapters/claude.md +0 -1
- package/skills/role-cont-qa/skill.json +0 -36
- package/skills/role-deploy/SKILL.md +0 -97
- package/skills/role-deploy/skill.json +0 -36
- package/skills/role-design/SKILL.md +0 -50
- package/skills/role-design/skill.json +0 -36
- package/skills/role-documentation/SKILL.md +0 -76
- package/skills/role-documentation/skill.json +0 -36
- package/skills/role-implementation/SKILL.md +0 -45
- package/skills/role-implementation/skill.json +0 -36
- package/skills/role-infra/SKILL.md +0 -81
- package/skills/role-infra/skill.json +0 -36
- package/skills/role-integration/SKILL.md +0 -91
- package/skills/role-integration/skill.json +0 -36
- package/skills/role-planner/SKILL.md +0 -39
- package/skills/role-planner/skill.json +0 -21
- package/skills/role-research/SKILL.md +0 -65
- package/skills/role-research/skill.json +0 -36
- package/skills/role-security/SKILL.md +0 -60
- package/skills/role-security/skill.json +0 -36
- package/skills/runtime-claude/SKILL.md +0 -66
- package/skills/runtime-claude/skill.json +0 -36
- package/skills/runtime-codex/SKILL.md +0 -58
- package/skills/runtime-codex/skill.json +0 -36
- package/skills/runtime-local/SKILL.md +0 -46
- package/skills/runtime-local/skill.json +0 -36
- package/skills/runtime-opencode/SKILL.md +0 -58
- package/skills/runtime-opencode/skill.json +0 -36
- package/skills/signal-hygiene/SKILL.md +0 -51
- package/skills/signal-hygiene/skill.json +0 -20
- package/skills/tui-design/SKILL.md +0 -77
- package/skills/tui-design/references/tui-design.md +0 -259
- package/skills/tui-design/skill.json +0 -36
- package/skills/wave-core/SKILL.md +0 -141
- package/skills/wave-core/references/marker-syntax.md +0 -70
- package/skills/wave-core/skill.json +0 -35
- package/test/README.md +0 -16
- package/test/core/formatting-commands.test.ts +0 -285
- package/test/core/image-commands.test.ts +0 -298
- package/test/core/mapping.test.ts +0 -186
- package/test/core/text-commands.test.ts +0 -176
- package/test/fixtures/docx/F01-basic-contract.docx +0 -0
- package/test/fixtures/docx/F01-basic-contract.md +0 -33
- package/test/fixtures/docx/F02-headings-styles.docx +0 -0
- package/test/fixtures/docx/F02-headings-styles.md +0 -33
- package/test/fixtures/docx/F03-legal-outline-numbering.docx +0 -0
- package/test/fixtures/docx/F03-legal-outline-numbering.md +0 -34
- package/test/fixtures/docx/F04-restart-numbering-schedules.docx +0 -0
- package/test/fixtures/docx/F04-restart-numbering-schedules.md +0 -33
- package/test/fixtures/docx/F05-table-heavy-agreement.docx +0 -0
- package/test/fixtures/docx/F05-table-heavy-agreement.md +0 -34
- package/test/fixtures/docx/F06-merged-cells-signature-table.docx +0 -0
- package/test/fixtures/docx/F06-merged-cells-signature-table.md +0 -34
- package/test/fixtures/docx/F07-inline-images-exhibit.docx +0 -0
- package/test/fixtures/docx/F07-inline-images-exhibit.md +0 -34
- package/test/fixtures/docx/F08-hyperlinks.docx +0 -0
- package/test/fixtures/docx/F08-hyperlinks.md +0 -33
- package/test/fixtures/docx/F09-comments-single-paragraph.docx +0 -0
- package/test/fixtures/docx/F09-comments-single-paragraph.md +0 -33
- package/test/fixtures/docx/F10-threaded-comments-resolve.docx +0 -0
- package/test/fixtures/docx/F10-threaded-comments-resolve.md +0 -33
- package/test/fixtures/docx/F11-redlines-basic.docx +0 -0
- package/test/fixtures/docx/F11-redlines-basic.md +0 -33
- package/test/fixtures/docx/F12-redlines-paragraph-joins-splits.docx +0 -0
- package/test/fixtures/docx/F12-redlines-paragraph-joins-splits.md +0 -33
- package/test/fixtures/docx/F13-comments-on-deleted-text.docx +0 -0
- package/test/fixtures/docx/F13-comments-on-deleted-text.md +0 -33
- package/test/fixtures/docx/F14-revisions-in-tables-and-lists.docx +0 -0
- package/test/fixtures/docx/F14-revisions-in-tables-and-lists.md +0 -33
- package/test/fixtures/docx/F15-sections-headers-footers.docx +0 -0
- package/test/fixtures/docx/F15-sections-headers-footers.md +0 -33
- package/test/fixtures/docx/F16-footnotes-endnotes.docx +0 -0
- package/test/fixtures/docx/F16-footnotes-endnotes.md +0 -33
- package/test/fixtures/docx/F17-fields-and-toc.docx +0 -0
- package/test/fixtures/docx/F17-fields-and-toc.md +0 -33
- package/test/fixtures/docx/F18-content-controls-template.docx +0 -0
- package/test/fixtures/docx/F18-content-controls-template.md +0 -33
- package/test/fixtures/docx/F19-custom-xml-doc-assembly.docx +0 -0
- package/test/fixtures/docx/F19-custom-xml-doc-assembly.md +0 -35
- package/test/fixtures/docx/F20-unknown-ooxml-and-alternatecontent.docx +0 -0
- package/test/fixtures/docx/F20-unknown-ooxml-and-alternatecontent.md +0 -33
- package/test/fixtures/docx/F21-malformed-broken-docx.docx +0 -0
- package/test/fixtures/docx/F21-malformed-broken-docx.md +0 -33
- package/test/fixtures/docx/README.md +0 -74
- package/test/fixtures/docx/certification-manifest.json +0 -104
- package/test/fixtures/docx/fixtures.manifest.json +0 -196
- package/test/fixtures/encrypted-docx/README.md +0 -27
- package/test/fixtures/encrypted-docx/certification-manifest.json +0 -9
- package/test/fixtures/encrypted-docx/fixtures.manifest.json +0 -47
- package/test/fixtures/scenarios/docx/README.md +0 -25
- package/test/fixtures/scenarios/docx/S01-sow-template.docx +0 -0
- package/test/fixtures/scenarios/docx/S01-sow-template.md +0 -30
- package/test/fixtures/scenarios/docx/S02-bw-partner-user-licence-agreement-redlines.docx +0 -0
- package/test/fixtures/scenarios/docx/S02-bw-partner-user-licence-agreement-redlines.md +0 -32
- package/test/fixtures/scenarios/docx/scenario-manifest.json +0 -53
- package/test/formats/xlsx/io/xlsx-import.test.ts +0 -766
- package/test/formats/xlsx/model/workbook.test.ts +0 -669
- package/test/helpers/dom-setup.ts +0 -124
- package/test/io/comment-roundtrip.test.ts +0 -272
- package/test/io/complex-content-roundtrip.test.ts +0 -632
- package/test/io/docx-compatibility-regression.test.ts +0 -199
- package/test/io/docx-session.test.ts +0 -1495
- package/test/io/footnotes-roundtrip.test.ts +0 -318
- package/test/io/headers-footers-roundtrip.test.ts +0 -547
- package/test/io/numbering-roundtrip.test.ts +0 -234
- package/test/io/package-reader.test.ts +0 -199
- package/test/io/paragraph-properties-roundtrip.test.ts +0 -129
- package/test/io/preserved-package-roundtrip.test.ts +0 -365
- package/test/io/property-completeness.test.ts +0 -292
- package/test/io/revision-roundtrip.test.ts +0 -347
- package/test/io/structural-blocks.test.ts +0 -202
- package/test/io/table-media-roundtrip.test.ts +0 -448
- package/test/io/table-properties-roundtrip.test.ts +0 -569
- package/test/io/table-roundtrip.test.ts +0 -302
- package/test/io/text-roundtrip.test.ts +0 -344
- package/test/model/canonical-document.test.ts +0 -285
- package/test/preservation/opaque-fragment-store.test.ts +0 -121
- package/test/preservation/package-preservation.test.ts +0 -395
- package/test/preservation/store.test.ts +0 -84
- package/test/review/comment-remapping.test.ts +0 -220
- package/test/review/comment-store.test.ts +0 -180
- package/test/review/move-revisions.test.ts +0 -143
- package/test/review/property-change-revisions.test.ts +0 -225
- package/test/review/revision-actions.test.ts +0 -330
- package/test/review/revision-store.test.ts +0 -193
- package/test/runtime/session-capabilities.test.ts +0 -260
- package/test/runtime/table-commands.test.ts +0 -356
- package/test/runtime/table-schema.test.ts +0 -221
- package/test/runtime/tracked-changes-toggle.test.ts +0 -107
- package/test/ui/comment-review-surface.test.tsx +0 -114
- package/test/ui/reduced-motion-toggle.test.tsx +0 -137
- package/test/ui/word-review-editor.imported-scenarios.test.tsx +0 -169
- package/test/ui/word-review-editor.interaction.test.tsx +0 -1198
- package/test/ui/word-review-editor.test.js +0 -188
- package/test/ui/word-review-editor.test.tsx +0 -280
- package/test/ui-tailwind/search-plugin.test.ts +0 -286
- package/test/validation/compatibility-engine.test.ts +0 -336
- package/test/validation/compatibility-report.test.ts +0 -189
- package/test/validation/low-priority-word-surfaces.test.ts +0 -282
- package/test/validation/malformed-doc.test.ts +0 -113
- package/test-results/.last-run.json +0 -4
- package/wave.config.json +0 -406
|
@@ -1,911 +0,0 @@
|
|
|
1
|
-
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { createRequire } from "node:module";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
|
|
6
|
-
const require = createRequire(import.meta.url);
|
|
7
|
-
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
const serviceRootDir = path.resolve(moduleDir, "..");
|
|
9
|
-
const repoRootDir = path.resolve(serviceRootDir, "../..");
|
|
10
|
-
|
|
11
|
-
async function loadPlaywright() {
|
|
12
|
-
try {
|
|
13
|
-
return require("playwright");
|
|
14
|
-
} catch {
|
|
15
|
-
const fallbackPath = path.join(
|
|
16
|
-
repoRootDir,
|
|
17
|
-
".tmp/wave-8-railway-pilot/node_modules/playwright",
|
|
18
|
-
);
|
|
19
|
-
try {
|
|
20
|
-
await access(fallbackPath);
|
|
21
|
-
return require(fallbackPath);
|
|
22
|
-
} catch {
|
|
23
|
-
throw new Error(
|
|
24
|
-
"Playwright is not installed. Add it to the harness package or restore the cached Wave 8 pilot toolchain before running the Wave 9 pilot.",
|
|
25
|
-
);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const { firefox } = await loadPlaywright();
|
|
31
|
-
|
|
32
|
-
const BASE_URL =
|
|
33
|
-
process.env.HARNESS_PUBLIC_URL ??
|
|
34
|
-
"https://docx-react-component-early-access.up.railway.app";
|
|
35
|
-
const DOCX_MIME_TYPE =
|
|
36
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
37
|
-
const OUTPUT_DIR = path.join(repoRootDir, ".tmp/wave-9-pilot");
|
|
38
|
-
const SCREENSHOT_DIR = path.join(OUTPUT_DIR, "screenshots");
|
|
39
|
-
const RAW_DIR = path.join(OUTPUT_DIR, "raw");
|
|
40
|
-
|
|
41
|
-
const performanceBudgets = {
|
|
42
|
-
loadMs: {
|
|
43
|
-
plainText: 5_000,
|
|
44
|
-
commentHeavy: 6_000,
|
|
45
|
-
redlineHeavy: 12_000,
|
|
46
|
-
},
|
|
47
|
-
reviewNavigationMs: 1_000,
|
|
48
|
-
revisionActionMs: 1_500,
|
|
49
|
-
exportMs: {
|
|
50
|
-
plainText: 5_000,
|
|
51
|
-
commentHeavy: 8_000,
|
|
52
|
-
redlineHeavy: 15_000,
|
|
53
|
-
},
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const runs = [
|
|
57
|
-
{
|
|
58
|
-
id: "plain-text-f01",
|
|
59
|
-
label: "F01 Basic contract skeleton",
|
|
60
|
-
category: "plainText",
|
|
61
|
-
filePath: path.join(repoRootDir, "test/fixtures/docx/F01-basic-contract.docx"),
|
|
62
|
-
fileName: "F01-basic-contract.docx",
|
|
63
|
-
sourceKind: "fixture",
|
|
64
|
-
tasks: {
|
|
65
|
-
commentNavigation: false,
|
|
66
|
-
exportAndValidate: true,
|
|
67
|
-
markupSwitch: true,
|
|
68
|
-
revisionAction: false,
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
id: "comment-heavy-f10",
|
|
73
|
-
label: "F10 Threaded comments and resolve",
|
|
74
|
-
category: "commentHeavy",
|
|
75
|
-
filePath: path.join(repoRootDir, "test/fixtures/docx/F10-threaded-comments-resolve.docx"),
|
|
76
|
-
fileName: "F10-threaded-comments-resolve.docx",
|
|
77
|
-
sourceKind: "fixture",
|
|
78
|
-
tasks: {
|
|
79
|
-
commentNavigation: true,
|
|
80
|
-
exportAndValidate: true,
|
|
81
|
-
markupSwitch: true,
|
|
82
|
-
revisionAction: false,
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
id: "redline-heavy-s02",
|
|
87
|
-
label: "S02 BW Partner User Licence Agreement with review markup",
|
|
88
|
-
category: "redlineHeavy",
|
|
89
|
-
filePath: path.join(
|
|
90
|
-
repoRootDir,
|
|
91
|
-
"test/fixtures/scenarios/docx/S02-bw-partner-user-licence-agreement-redlines.docx",
|
|
92
|
-
),
|
|
93
|
-
fileName: "S02-bw-partner-user-licence-agreement-redlines.docx",
|
|
94
|
-
sourceKind: "scenario",
|
|
95
|
-
tasks: {
|
|
96
|
-
commentNavigation: false,
|
|
97
|
-
exportAndValidate: true,
|
|
98
|
-
markupSwitch: true,
|
|
99
|
-
revisionAction: true,
|
|
100
|
-
},
|
|
101
|
-
},
|
|
102
|
-
];
|
|
103
|
-
|
|
104
|
-
const comparisonRuns = [
|
|
105
|
-
{
|
|
106
|
-
id: "table-heavy-f05",
|
|
107
|
-
label: "F05 Table-heavy agreement",
|
|
108
|
-
filePath: path.join(repoRootDir, "test/fixtures/docx/F05-table-heavy-agreement.docx"),
|
|
109
|
-
fileName: "F05-table-heavy-agreement.docx",
|
|
110
|
-
focus: "table-heavy",
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
id: "table-preserve-s01",
|
|
114
|
-
label: "S01 SOW template",
|
|
115
|
-
filePath: path.join(repoRootDir, "test/fixtures/scenarios/docx/S01-sow-template.docx"),
|
|
116
|
-
fileName: "S01-sow-template.docx",
|
|
117
|
-
focus: "table-and-preserve-only",
|
|
118
|
-
},
|
|
119
|
-
];
|
|
120
|
-
|
|
121
|
-
async function ensureDirs() {
|
|
122
|
-
await mkdir(OUTPUT_DIR, { recursive: true });
|
|
123
|
-
await mkdir(SCREENSHOT_DIR, { recursive: true });
|
|
124
|
-
await mkdir(RAW_DIR, { recursive: true });
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function assert(condition, message) {
|
|
128
|
-
if (!condition) {
|
|
129
|
-
throw new Error(message);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
async function writeJson(relativePath, value) {
|
|
134
|
-
const targetPath = path.join(OUTPUT_DIR, relativePath);
|
|
135
|
-
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
136
|
-
await writeFile(targetPath, `${JSON.stringify(value, null, 2)}\n`);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
async function uploadDoc(filePath, fileName) {
|
|
140
|
-
const bytes = await readFile(filePath);
|
|
141
|
-
const form = new FormData();
|
|
142
|
-
form.set(
|
|
143
|
-
"file",
|
|
144
|
-
new File([bytes], fileName, {
|
|
145
|
-
type: DOCX_MIME_TYPE,
|
|
146
|
-
}),
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
const response = await fetch(`${BASE_URL}/api/uploads`, {
|
|
150
|
-
body: form,
|
|
151
|
-
method: "POST",
|
|
152
|
-
});
|
|
153
|
-
assert(response.ok, `Upload failed for ${fileName} with ${response.status}.`);
|
|
154
|
-
return response.json();
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
async function waitForBridge(page, timeout = 60_000) {
|
|
158
|
-
await page.waitForFunction(() => Boolean(window.__DOCX_REACT_HARNESS__), undefined, {
|
|
159
|
-
timeout,
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
async function waitForEditorReady(page, timeout = 120_000) {
|
|
164
|
-
await page.waitForFunction(
|
|
165
|
-
() =>
|
|
166
|
-
Boolean(window.__DOCX_REACT_HARNESS__) &&
|
|
167
|
-
window.__DOCX_REACT_HARNESS__.getState().eventLog.some((entry) =>
|
|
168
|
-
entry.detail.startsWith("Editor ready with"),
|
|
169
|
-
),
|
|
170
|
-
undefined,
|
|
171
|
-
{ timeout },
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async function getHarnessState(page) {
|
|
176
|
-
return page.evaluate(() => window.__DOCX_REACT_HARNESS__.getState());
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
async function scrapeSignals(page) {
|
|
180
|
-
return page.evaluate(() => {
|
|
181
|
-
const sections = [...document.querySelectorAll("section")];
|
|
182
|
-
const runtimeSignals = sections.find((section) =>
|
|
183
|
-
section.textContent?.includes("Runtime Signals"),
|
|
184
|
-
);
|
|
185
|
-
const warningHeader = runtimeSignals
|
|
186
|
-
? [...runtimeSignals.querySelectorAll("p")].find((node) => node.textContent === "Warnings")
|
|
187
|
-
: undefined;
|
|
188
|
-
const warningList = warningHeader?.parentElement?.querySelectorAll("li") ?? [];
|
|
189
|
-
const warnings = [...warningList].map((item) => {
|
|
190
|
-
const parts = [...item.querySelectorAll("div")].map((node) => node.textContent?.trim());
|
|
191
|
-
return { code: parts[0], message: parts[1] };
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
const errorsHeader = runtimeSignals
|
|
195
|
-
? [...runtimeSignals.querySelectorAll("p")].find((node) => node.textContent === "Errors")
|
|
196
|
-
: undefined;
|
|
197
|
-
const errorList = errorsHeader?.parentElement?.querySelectorAll("li") ?? [];
|
|
198
|
-
const errors = [...errorList].map((item) => {
|
|
199
|
-
const parts = [...item.querySelectorAll("div")].map((node) => node.textContent?.trim());
|
|
200
|
-
return { code: parts[0], message: parts[1] };
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
return { errors, warnings };
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
async function takeScreenshot(page, filePath) {
|
|
208
|
-
await page.screenshot({ path: filePath });
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
async function getMarkupChip(page) {
|
|
212
|
-
return page.evaluate(() => {
|
|
213
|
-
const canvas = document.querySelector('[aria-label="Document canvas"]');
|
|
214
|
-
if (!canvas) {
|
|
215
|
-
return null;
|
|
216
|
-
}
|
|
217
|
-
return [...canvas.querySelectorAll("span")]
|
|
218
|
-
.map((node) => node.textContent?.trim())
|
|
219
|
-
.find((text) => text?.startsWith("markup "));
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
async function clickMarkupOption(page, label) {
|
|
224
|
-
const section = page.locator('[aria-label="Markup display"]');
|
|
225
|
-
await section
|
|
226
|
-
.getByRole("button", { name: new RegExp(`^${label}\\b`, "i") })
|
|
227
|
-
.evaluate((node) => node.click());
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
async function measureMarkupSwitch(page) {
|
|
231
|
-
const before = await getMarkupChip(page);
|
|
232
|
-
const started = Date.now();
|
|
233
|
-
await clickMarkupOption(page, "Clean");
|
|
234
|
-
|
|
235
|
-
try {
|
|
236
|
-
await page.waitForFunction(
|
|
237
|
-
() => {
|
|
238
|
-
const canvas = document.querySelector('[aria-label="Document canvas"]');
|
|
239
|
-
if (!canvas) {
|
|
240
|
-
return false;
|
|
241
|
-
}
|
|
242
|
-
return [...canvas.querySelectorAll("span")].some((node) =>
|
|
243
|
-
node.textContent?.trim().startsWith("markup clean"),
|
|
244
|
-
);
|
|
245
|
-
},
|
|
246
|
-
undefined,
|
|
247
|
-
{ timeout: 2_000 },
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
return {
|
|
251
|
-
after: await getMarkupChip(page),
|
|
252
|
-
before,
|
|
253
|
-
latencyMs: Date.now() - started,
|
|
254
|
-
status: "passed",
|
|
255
|
-
};
|
|
256
|
-
} catch {
|
|
257
|
-
return {
|
|
258
|
-
after: await getMarkupChip(page),
|
|
259
|
-
before,
|
|
260
|
-
blocker:
|
|
261
|
-
"Markup display controls render, but the mounted canvas chip stays fixed at the initial mode.",
|
|
262
|
-
latencyMs: null,
|
|
263
|
-
status: "blocked",
|
|
264
|
-
};
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
async function measureReadySequence(page) {
|
|
269
|
-
await page.waitForTimeout(600);
|
|
270
|
-
const harnessState = await getHarnessState(page);
|
|
271
|
-
const eventLog = Array.isArray(harnessState?.eventLog) ? harnessState.eventLog : [];
|
|
272
|
-
const readyEvents = eventLog.filter((entry) =>
|
|
273
|
-
entry.detail.startsWith("Editor ready with"),
|
|
274
|
-
);
|
|
275
|
-
|
|
276
|
-
return {
|
|
277
|
-
blocker:
|
|
278
|
-
readyEvents.length <= 1
|
|
279
|
-
? null
|
|
280
|
-
: `Expected one ready event after load, but recorded ${readyEvents.length}.`,
|
|
281
|
-
eventLog,
|
|
282
|
-
readyEventCount: readyEvents.length,
|
|
283
|
-
status: readyEvents.length <= 1 ? "passed" : "blocked",
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
async function measureCommentNavigation(page) {
|
|
288
|
-
const sidebar = page.locator('[aria-label="Comment sidebar"]');
|
|
289
|
-
if ((await sidebar.count()) === 0) {
|
|
290
|
-
return {
|
|
291
|
-
blocker: "Comment sidebar is not rendered.",
|
|
292
|
-
latencyMs: null,
|
|
293
|
-
status: "not-tested",
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const threads = sidebar.locator("[data-comment-thread-id]");
|
|
298
|
-
const threadCount = await threads.count();
|
|
299
|
-
if (threadCount === 0) {
|
|
300
|
-
return {
|
|
301
|
-
blocker: "No mounted comment thread is available for navigation.",
|
|
302
|
-
latencyMs: null,
|
|
303
|
-
status: "not-tested",
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
let targetIndex = 0;
|
|
308
|
-
for (let index = 0; index < threadCount; index += 1) {
|
|
309
|
-
if ((await threads.nth(index).getAttribute("aria-current")) !== "true") {
|
|
310
|
-
targetIndex = index;
|
|
311
|
-
break;
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const targetThread = threads.nth(targetIndex);
|
|
316
|
-
const commentId = await targetThread.getAttribute("data-comment-thread-id");
|
|
317
|
-
if (!commentId) {
|
|
318
|
-
return {
|
|
319
|
-
blocker: "Mounted comment thread metadata is missing a stable thread id.",
|
|
320
|
-
latencyMs: null,
|
|
321
|
-
status: "blocked",
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const started = Date.now();
|
|
326
|
-
await targetThread.locator('[data-testid="comment-thread-open"]').click();
|
|
327
|
-
await page.waitForFunction(
|
|
328
|
-
(threadId) => {
|
|
329
|
-
const thread = document.querySelector(`[data-comment-thread-id="${threadId}"]`);
|
|
330
|
-
return thread?.getAttribute("aria-current") === "true";
|
|
331
|
-
},
|
|
332
|
-
commentId,
|
|
333
|
-
{ timeout: 10_000 },
|
|
334
|
-
);
|
|
335
|
-
|
|
336
|
-
return {
|
|
337
|
-
commentId,
|
|
338
|
-
latencyMs: Date.now() - started,
|
|
339
|
-
status: "passed",
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
async function assessRevisionNavigation(page) {
|
|
344
|
-
return page.evaluate(() => {
|
|
345
|
-
const sidebar = document.querySelector('[aria-label="Revision review sidebar"]');
|
|
346
|
-
if (!sidebar) {
|
|
347
|
-
return {
|
|
348
|
-
blocker: "Revision review sidebar is not rendered.",
|
|
349
|
-
status: "not-tested",
|
|
350
|
-
};
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const entryCards = [...sidebar.querySelectorAll("section")].filter((section) =>
|
|
354
|
-
section.textContent?.includes("Accept and reject"),
|
|
355
|
-
);
|
|
356
|
-
const navigationTargets = [...sidebar.querySelectorAll("[data-revision-nav], button[aria-label*='Open']")];
|
|
357
|
-
|
|
358
|
-
if (entryCards.length === 0) {
|
|
359
|
-
return {
|
|
360
|
-
blocker: "No actionable revision cards were present in the mounted sidebar.",
|
|
361
|
-
status: "not-tested",
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if (navigationTargets.length === 0) {
|
|
366
|
-
return {
|
|
367
|
-
blocker:
|
|
368
|
-
"Revision cards expose accept/reject actions only; there is no direct anchor-jump or open-in-canvas navigation control.",
|
|
369
|
-
status: "blocked",
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
return {
|
|
374
|
-
blocker: null,
|
|
375
|
-
status: "passed",
|
|
376
|
-
};
|
|
377
|
-
});
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
async function readRevisionPendingCount(page) {
|
|
381
|
-
return page.evaluate(() => {
|
|
382
|
-
const valueNode = document.querySelector(
|
|
383
|
-
'[data-testid="revision-runtime-pending"] [data-summary-value="true"]',
|
|
384
|
-
);
|
|
385
|
-
if (valueNode?.textContent) {
|
|
386
|
-
const parsed = Number(valueNode.textContent.trim());
|
|
387
|
-
if (Number.isFinite(parsed)) {
|
|
388
|
-
return parsed;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
const sidebar = document.querySelector('[aria-label="Revision review sidebar"]');
|
|
393
|
-
if (!sidebar) {
|
|
394
|
-
return null;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
const chips = [...sidebar.querySelectorAll("div")].map((node) =>
|
|
398
|
-
node.textContent?.replace(/\s+/g, " ").trim(),
|
|
399
|
-
);
|
|
400
|
-
const pendingChip = chips.find((text) => text?.includes("Runtime pending"));
|
|
401
|
-
if (!pendingChip) {
|
|
402
|
-
return null;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
const match = pendingChip.match(/^(\d+)/);
|
|
406
|
-
return match ? Number(match[1]) : null;
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
async function measureRevisionAction(page) {
|
|
411
|
-
await clickMarkupOption(page, "Clean");
|
|
412
|
-
await page.waitForFunction(
|
|
413
|
-
() => {
|
|
414
|
-
const pendingValue = document.querySelector(
|
|
415
|
-
'[data-testid="revision-runtime-pending"] [data-summary-value="true"]',
|
|
416
|
-
);
|
|
417
|
-
return Boolean(pendingValue?.textContent);
|
|
418
|
-
},
|
|
419
|
-
undefined,
|
|
420
|
-
{ timeout: 15_000 },
|
|
421
|
-
);
|
|
422
|
-
|
|
423
|
-
const before = await readRevisionPendingCount(page);
|
|
424
|
-
if (before === null || before === 0) {
|
|
425
|
-
return {
|
|
426
|
-
blocker: "No pending revisions were available for a single-item accept action.",
|
|
427
|
-
latencyMs: null,
|
|
428
|
-
status: "not-tested",
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
const sidebar = page.locator('[aria-label="Revision review sidebar"]');
|
|
433
|
-
await sidebar.getByRole("button", { exact: true, name: "Accept" }).first().waitFor({
|
|
434
|
-
state: "visible",
|
|
435
|
-
timeout: 15_000,
|
|
436
|
-
});
|
|
437
|
-
const acceptButton = sidebar.getByRole("button", { exact: true, name: "Accept" }).first();
|
|
438
|
-
const started = Date.now();
|
|
439
|
-
await acceptButton.click();
|
|
440
|
-
|
|
441
|
-
try {
|
|
442
|
-
await page.waitForFunction(
|
|
443
|
-
(expectedMax) => {
|
|
444
|
-
const valueNode = document.querySelector(
|
|
445
|
-
'[data-testid="revision-runtime-pending"] [data-summary-value="true"]',
|
|
446
|
-
);
|
|
447
|
-
if (!valueNode?.textContent) {
|
|
448
|
-
return false;
|
|
449
|
-
}
|
|
450
|
-
const parsed = Number(valueNode.textContent.trim());
|
|
451
|
-
return Number.isFinite(parsed) && parsed < expectedMax;
|
|
452
|
-
},
|
|
453
|
-
before,
|
|
454
|
-
{ timeout: 15_000 },
|
|
455
|
-
);
|
|
456
|
-
|
|
457
|
-
return {
|
|
458
|
-
afterPending: await readRevisionPendingCount(page),
|
|
459
|
-
beforePending: before,
|
|
460
|
-
latencyMs: Date.now() - started,
|
|
461
|
-
status: "passed",
|
|
462
|
-
};
|
|
463
|
-
} catch {
|
|
464
|
-
return {
|
|
465
|
-
afterPending: await readRevisionPendingCount(page),
|
|
466
|
-
beforePending: before,
|
|
467
|
-
blocker:
|
|
468
|
-
"Single-item accept did not produce a visible pending-count transition inside the mounted review rail.",
|
|
469
|
-
latencyMs: null,
|
|
470
|
-
status: "blocked",
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
async function measureExport(page, category, runId) {
|
|
476
|
-
const before = await page.evaluate(
|
|
477
|
-
() => window.__DOCX_REACT_HARNESS__.getState().latestExport?.exportId ?? null,
|
|
478
|
-
);
|
|
479
|
-
const started = Date.now();
|
|
480
|
-
await page.evaluate(async () => {
|
|
481
|
-
const bridge = window.__DOCX_REACT_HARNESS__;
|
|
482
|
-
if (!bridge) {
|
|
483
|
-
throw new Error("Harness bridge is not available.");
|
|
484
|
-
}
|
|
485
|
-
await bridge.exportCurrentSession();
|
|
486
|
-
});
|
|
487
|
-
await page.waitForFunction(
|
|
488
|
-
(previousId) => {
|
|
489
|
-
const currentId = window.__DOCX_REACT_HARNESS__.getState().latestExport?.exportId ?? null;
|
|
490
|
-
return Boolean(currentId) && currentId !== previousId;
|
|
491
|
-
},
|
|
492
|
-
before,
|
|
493
|
-
{ timeout: 120_000 },
|
|
494
|
-
);
|
|
495
|
-
const exportState = await page.evaluate(() => window.__DOCX_REACT_HARNESS__.getState().latestExport);
|
|
496
|
-
const latencyMs = Date.now() - started;
|
|
497
|
-
|
|
498
|
-
return {
|
|
499
|
-
export: exportState,
|
|
500
|
-
latencyMs,
|
|
501
|
-
screenshot: path.relative(
|
|
502
|
-
OUTPUT_DIR,
|
|
503
|
-
path.join(SCREENSHOT_DIR, `${runId}-after-export.png`),
|
|
504
|
-
),
|
|
505
|
-
status: "passed",
|
|
506
|
-
withinBudget: latencyMs <= performanceBudgets.exportMs[category],
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
async function measureValidation(page) {
|
|
511
|
-
const before = await page.evaluate(
|
|
512
|
-
() => window.__DOCX_REACT_HARNESS__.getState().latestValidation?.runId ?? null,
|
|
513
|
-
);
|
|
514
|
-
const started = Date.now();
|
|
515
|
-
await page.getByTestId("harness-validate-button").evaluate((node) => node.click());
|
|
516
|
-
await page.waitForFunction(
|
|
517
|
-
(previousId) => {
|
|
518
|
-
const currentId =
|
|
519
|
-
window.__DOCX_REACT_HARNESS__.getState().latestValidation?.runId ?? null;
|
|
520
|
-
return Boolean(currentId) && currentId !== previousId;
|
|
521
|
-
},
|
|
522
|
-
before,
|
|
523
|
-
{ timeout: 120_000 },
|
|
524
|
-
);
|
|
525
|
-
const validationState = await page.evaluate(
|
|
526
|
-
() => window.__DOCX_REACT_HARNESS__.getState().latestValidation,
|
|
527
|
-
);
|
|
528
|
-
return {
|
|
529
|
-
latencyMs: Date.now() - started,
|
|
530
|
-
status: "passed",
|
|
531
|
-
validation: validationState,
|
|
532
|
-
};
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
async function scrapeDocumentSurface(page) {
|
|
536
|
-
return page.evaluate(() => {
|
|
537
|
-
const canvas = document.querySelector('[aria-label="Document canvas"]');
|
|
538
|
-
const documentSurface = document.querySelector('[aria-label="Document surface"]');
|
|
539
|
-
return {
|
|
540
|
-
canvasHeaderChips: canvas
|
|
541
|
-
? [...canvas.querySelectorAll("span")]
|
|
542
|
-
.map((node) => node.textContent?.trim())
|
|
543
|
-
.filter(Boolean)
|
|
544
|
-
: [],
|
|
545
|
-
documentSurfaceText: documentSurface?.textContent?.slice(0, 1200) ?? "",
|
|
546
|
-
};
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
async function openEditorRun(browser, config) {
|
|
551
|
-
const upload = await uploadDoc(config.filePath, config.fileName);
|
|
552
|
-
const page = await browser.newPage({ viewport: { height: 1200, width: 1600 } });
|
|
553
|
-
const sessionUrl = `${BASE_URL}/editor/${upload.session.sessionId}?wave9=${Date.now()}`;
|
|
554
|
-
const loadStarted = Date.now();
|
|
555
|
-
await page.goto(sessionUrl, { timeout: 120_000, waitUntil: "domcontentloaded" });
|
|
556
|
-
await waitForBridge(page, 120_000);
|
|
557
|
-
await waitForEditorReady(page, 120_000);
|
|
558
|
-
const loadLatencyMs = Date.now() - loadStarted;
|
|
559
|
-
|
|
560
|
-
const screenshotPath = path.join(SCREENSHOT_DIR, `${config.id}-loaded.png`);
|
|
561
|
-
await takeScreenshot(page, screenshotPath);
|
|
562
|
-
|
|
563
|
-
const initialState = await getHarnessState(page);
|
|
564
|
-
const readySequence = await measureReadySequence(page);
|
|
565
|
-
const signals = await scrapeSignals(page);
|
|
566
|
-
const documentSurface = await scrapeDocumentSurface(page);
|
|
567
|
-
|
|
568
|
-
const result = {
|
|
569
|
-
category: config.category,
|
|
570
|
-
documentId: upload.session.documentId,
|
|
571
|
-
documentSurface,
|
|
572
|
-
fileName: config.fileName,
|
|
573
|
-
filePath: config.filePath,
|
|
574
|
-
id: config.id,
|
|
575
|
-
initialState,
|
|
576
|
-
label: config.label,
|
|
577
|
-
loadLatencyMs,
|
|
578
|
-
loadWithinBudget: loadLatencyMs <= performanceBudgets.loadMs[config.category],
|
|
579
|
-
runtimeSignals: signals,
|
|
580
|
-
screenshots: [path.relative(OUTPUT_DIR, screenshotPath)],
|
|
581
|
-
sessionId: upload.session.sessionId,
|
|
582
|
-
sessionUrl,
|
|
583
|
-
sourceId: upload.source.sourceId,
|
|
584
|
-
sourceKind: config.sourceKind,
|
|
585
|
-
tasks: {
|
|
586
|
-
readySequence:
|
|
587
|
-
readySequence ?? {
|
|
588
|
-
blocker: "Ready sequence capture did not return a result.",
|
|
589
|
-
eventLog: [],
|
|
590
|
-
readyEventCount: null,
|
|
591
|
-
status: "blocked",
|
|
592
|
-
},
|
|
593
|
-
},
|
|
594
|
-
};
|
|
595
|
-
|
|
596
|
-
if (config.tasks.markupSwitch) {
|
|
597
|
-
try {
|
|
598
|
-
result.tasks.markupSwitch = await measureMarkupSwitch(page);
|
|
599
|
-
} catch (error) {
|
|
600
|
-
result.tasks.markupSwitch = {
|
|
601
|
-
blocker: error instanceof Error ? error.message : "Markup switch measurement failed.",
|
|
602
|
-
latencyMs: null,
|
|
603
|
-
status: "blocked",
|
|
604
|
-
};
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
if (config.tasks.commentNavigation) {
|
|
609
|
-
try {
|
|
610
|
-
result.tasks.commentNavigation = await measureCommentNavigation(page);
|
|
611
|
-
} catch (error) {
|
|
612
|
-
result.tasks.commentNavigation = {
|
|
613
|
-
blocker:
|
|
614
|
-
error instanceof Error ? error.message : "Comment navigation measurement failed.",
|
|
615
|
-
latencyMs: null,
|
|
616
|
-
status: "blocked",
|
|
617
|
-
};
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
try {
|
|
622
|
-
result.tasks.revisionNavigation = await assessRevisionNavigation(page);
|
|
623
|
-
} catch (error) {
|
|
624
|
-
result.tasks.revisionNavigation = {
|
|
625
|
-
blocker:
|
|
626
|
-
error instanceof Error ? error.message : "Revision navigation assessment failed.",
|
|
627
|
-
status: "blocked",
|
|
628
|
-
};
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
if (config.tasks.revisionAction) {
|
|
632
|
-
try {
|
|
633
|
-
result.tasks.revisionAction = await measureRevisionAction(page);
|
|
634
|
-
} catch (error) {
|
|
635
|
-
result.tasks.revisionAction = {
|
|
636
|
-
blocker: error instanceof Error ? error.message : "Revision action measurement failed.",
|
|
637
|
-
latencyMs: null,
|
|
638
|
-
status: "blocked",
|
|
639
|
-
};
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
if (config.tasks.exportAndValidate) {
|
|
644
|
-
try {
|
|
645
|
-
result.tasks.export = await measureExport(page, config.category, config.id);
|
|
646
|
-
const exportScreenshot = path.join(SCREENSHOT_DIR, `${config.id}-after-export.png`);
|
|
647
|
-
await takeScreenshot(page, exportScreenshot);
|
|
648
|
-
result.screenshots.push(path.relative(OUTPUT_DIR, exportScreenshot));
|
|
649
|
-
} catch (error) {
|
|
650
|
-
result.tasks.export = {
|
|
651
|
-
blocker: error instanceof Error ? error.message : "Export measurement failed.",
|
|
652
|
-
latencyMs: null,
|
|
653
|
-
status: "blocked",
|
|
654
|
-
withinBudget: false,
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
if (result.tasks.export.status === "passed") {
|
|
659
|
-
try {
|
|
660
|
-
result.tasks.validate = await measureValidation(page);
|
|
661
|
-
const validateScreenshot = path.join(SCREENSHOT_DIR, `${config.id}-after-validate.png`);
|
|
662
|
-
await takeScreenshot(page, validateScreenshot);
|
|
663
|
-
result.screenshots.push(path.relative(OUTPUT_DIR, validateScreenshot));
|
|
664
|
-
|
|
665
|
-
const runId = result.tasks.validate.validation?.runId;
|
|
666
|
-
if (runId) {
|
|
667
|
-
const [runJson, runPage] = await Promise.all([
|
|
668
|
-
fetch(`${BASE_URL}/api/runs/${runId}`).then((response) => response.json()),
|
|
669
|
-
browser.newPage({ viewport: { height: 1200, width: 1600 } }),
|
|
670
|
-
]);
|
|
671
|
-
await runPage.goto(`${BASE_URL}/runs/${runId}`, {
|
|
672
|
-
timeout: 120_000,
|
|
673
|
-
waitUntil: "networkidle",
|
|
674
|
-
});
|
|
675
|
-
const runScreenshot = path.join(SCREENSHOT_DIR, `${config.id}-run-page.png`);
|
|
676
|
-
await takeScreenshot(runPage, runScreenshot);
|
|
677
|
-
await runPage.close();
|
|
678
|
-
|
|
679
|
-
result.screenshots.push(path.relative(OUTPUT_DIR, runScreenshot));
|
|
680
|
-
result.validationArtifact = runJson;
|
|
681
|
-
}
|
|
682
|
-
} catch (error) {
|
|
683
|
-
result.tasks.validate = {
|
|
684
|
-
blocker: error instanceof Error ? error.message : "Validation measurement failed.",
|
|
685
|
-
latencyMs: null,
|
|
686
|
-
status: "blocked",
|
|
687
|
-
};
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
result.finalState = (await getHarnessState(page)) ?? null;
|
|
693
|
-
await writeJson(path.join("raw", `${config.id}.json`), result);
|
|
694
|
-
await page.close();
|
|
695
|
-
return result;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
async function openComparisonRun(browser, config) {
|
|
699
|
-
const upload = await uploadDoc(config.filePath, config.fileName);
|
|
700
|
-
const page = await browser.newPage({ viewport: { height: 1200, width: 1600 } });
|
|
701
|
-
const sessionUrl = `${BASE_URL}/editor/${upload.session.sessionId}?wave9=${Date.now()}`;
|
|
702
|
-
await page.goto(sessionUrl, { timeout: 120_000, waitUntil: "domcontentloaded" });
|
|
703
|
-
await waitForBridge(page, 120_000);
|
|
704
|
-
await waitForEditorReady(page, 120_000);
|
|
705
|
-
|
|
706
|
-
const screenshotPath = path.join(SCREENSHOT_DIR, `${config.id}-loaded.png`);
|
|
707
|
-
await takeScreenshot(page, screenshotPath);
|
|
708
|
-
const signals = await scrapeSignals(page);
|
|
709
|
-
const documentSurface = await scrapeDocumentSurface(page);
|
|
710
|
-
const result = {
|
|
711
|
-
documentId: upload.session.documentId,
|
|
712
|
-
documentSurface,
|
|
713
|
-
focus: config.focus,
|
|
714
|
-
id: config.id,
|
|
715
|
-
label: config.label,
|
|
716
|
-
runtimeSignals: signals,
|
|
717
|
-
screenshots: [path.relative(OUTPUT_DIR, screenshotPath)],
|
|
718
|
-
sessionId: upload.session.sessionId,
|
|
719
|
-
sessionUrl,
|
|
720
|
-
sourceId: upload.source.sourceId,
|
|
721
|
-
};
|
|
722
|
-
await writeJson(path.join("raw", `${config.id}.json`), result);
|
|
723
|
-
await page.close();
|
|
724
|
-
return result;
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
async function inspectPrototype(browser) {
|
|
728
|
-
const page = await browser.newPage({ viewport: { height: 1200, width: 1600 } });
|
|
729
|
-
const started = Date.now();
|
|
730
|
-
await page.goto(`${BASE_URL}/demo?wave9=${Date.now()}`, {
|
|
731
|
-
timeout: 120_000,
|
|
732
|
-
waitUntil: "networkidle",
|
|
733
|
-
});
|
|
734
|
-
await page.waitForFunction(
|
|
735
|
-
() => document.body.textContent?.includes("Internal prototype · non-production"),
|
|
736
|
-
undefined,
|
|
737
|
-
{ timeout: 30_000 },
|
|
738
|
-
);
|
|
739
|
-
const screenshotPath = path.join(SCREENSHOT_DIR, "prototype-review.png");
|
|
740
|
-
const screenshotRelativePath = path.relative(OUTPUT_DIR, screenshotPath);
|
|
741
|
-
await takeScreenshot(page, screenshotPath);
|
|
742
|
-
const result = await page.evaluate(
|
|
743
|
-
({ loadMs, screenshot }) => {
|
|
744
|
-
const body = document.body;
|
|
745
|
-
return {
|
|
746
|
-
loadLatencyMs: loadMs,
|
|
747
|
-
postureLabel: body.textContent?.includes("Internal prototype · non-production") ?? false,
|
|
748
|
-
reducedMotionToggle: body.textContent?.includes("Reduced motion") ?? false,
|
|
749
|
-
screenshot,
|
|
750
|
-
sessionSwitcherVisible: Boolean(document.querySelector('[role="combobox"]')),
|
|
751
|
-
sourceLabel: [...document.querySelectorAll("h1, h2, h3")]
|
|
752
|
-
.map((node) => node.textContent?.trim())
|
|
753
|
-
.find(Boolean),
|
|
754
|
-
unifiedRailTabs: ["Comments", "Changes", "Health"].every((label) =>
|
|
755
|
-
[...document.querySelectorAll("button")].some((button) =>
|
|
756
|
-
button.textContent?.replace(/\s+/g, " ").includes(label),
|
|
757
|
-
),
|
|
758
|
-
),
|
|
759
|
-
};
|
|
760
|
-
},
|
|
761
|
-
{
|
|
762
|
-
loadMs: Date.now() - started,
|
|
763
|
-
screenshot: screenshotRelativePath,
|
|
764
|
-
},
|
|
765
|
-
);
|
|
766
|
-
await page.close();
|
|
767
|
-
return result;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
function summarizeSmoke(measuredRuns) {
|
|
771
|
-
return measuredRuns.map((run) => ({
|
|
772
|
-
commentNavigationLatencyMs: run.tasks.commentNavigation?.latencyMs ?? null,
|
|
773
|
-
commentNavigationWithinBudget:
|
|
774
|
-
run.tasks.commentNavigation?.latencyMs == null
|
|
775
|
-
? null
|
|
776
|
-
: run.tasks.commentNavigation.latencyMs <= performanceBudgets.reviewNavigationMs,
|
|
777
|
-
documentId: run.documentId,
|
|
778
|
-
errors: run.runtimeSignals.errors,
|
|
779
|
-
exportId: run.tasks.export?.export?.exportId ?? null,
|
|
780
|
-
exportLatencyMs: run.tasks.export?.latencyMs ?? null,
|
|
781
|
-
exportWithinBudget: run.tasks.export?.withinBudget ?? null,
|
|
782
|
-
id: run.id,
|
|
783
|
-
label: run.label,
|
|
784
|
-
loadLatencyMs: run.loadLatencyMs,
|
|
785
|
-
loadWithinBudget: run.loadWithinBudget,
|
|
786
|
-
packageFindingCount: run.tasks.validate?.validation?.packageFindingCount ?? null,
|
|
787
|
-
readyEventCount: run.tasks.readySequence?.readyEventCount ?? null,
|
|
788
|
-
readySequenceStatus: run.tasks.readySequence?.status ?? null,
|
|
789
|
-
revisionActionLatencyMs: run.tasks.revisionAction?.latencyMs ?? null,
|
|
790
|
-
revisionActionWithinBudget:
|
|
791
|
-
run.tasks.revisionAction?.latencyMs == null
|
|
792
|
-
? null
|
|
793
|
-
: run.tasks.revisionAction.latencyMs <= performanceBudgets.revisionActionMs,
|
|
794
|
-
sdkFindingCount: run.tasks.validate?.validation?.sdkFindingCount ?? null,
|
|
795
|
-
sessionId: run.sessionId,
|
|
796
|
-
sourceId: run.sourceId,
|
|
797
|
-
taskStatuses: Object.fromEntries(
|
|
798
|
-
Object.entries(run.tasks).map(([key, value]) => [key, value.status]),
|
|
799
|
-
),
|
|
800
|
-
validationIsValid: run.tasks.validate?.validation?.isValid ?? null,
|
|
801
|
-
validationRunId: run.tasks.validate?.validation?.runId ?? null,
|
|
802
|
-
warnings: run.runtimeSignals.warnings,
|
|
803
|
-
}));
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
function summarizeEditorLoadEvents(measuredRuns) {
|
|
807
|
-
return measuredRuns.map((run) => ({
|
|
808
|
-
eventLog: run.tasks.readySequence?.eventLog ?? [],
|
|
809
|
-
id: run.id,
|
|
810
|
-
readyEventCount: run.tasks.readySequence?.readyEventCount ?? null,
|
|
811
|
-
status: run.tasks.readySequence?.status ?? null,
|
|
812
|
-
}));
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
function summarizeEditorSessions(measuredRuns) {
|
|
816
|
-
return measuredRuns.map((run) => ({
|
|
817
|
-
documentId: run.documentId,
|
|
818
|
-
exportId: run.tasks.export?.export?.exportId ?? null,
|
|
819
|
-
id: run.id,
|
|
820
|
-
sessionId: run.sessionId,
|
|
821
|
-
sourceLoaded: run.initialState?.sourceLoaded ?? null,
|
|
822
|
-
validationRunId: run.tasks.validate?.validation?.runId ?? null,
|
|
823
|
-
warningCount: run.initialState?.warningCount ?? null,
|
|
824
|
-
}));
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
async function main() {
|
|
828
|
-
await ensureDirs();
|
|
829
|
-
const browser = await firefox.launch({ headless: true });
|
|
830
|
-
|
|
831
|
-
try {
|
|
832
|
-
const measuredRuns = [];
|
|
833
|
-
for (const config of runs) {
|
|
834
|
-
try {
|
|
835
|
-
measuredRuns.push(await openEditorRun(browser, config));
|
|
836
|
-
} catch (error) {
|
|
837
|
-
measuredRuns.push({
|
|
838
|
-
category: config.category,
|
|
839
|
-
fileName: config.fileName,
|
|
840
|
-
filePath: config.filePath,
|
|
841
|
-
id: config.id,
|
|
842
|
-
label: config.label,
|
|
843
|
-
runtimeSignals: { errors: [], warnings: [] },
|
|
844
|
-
sourceKind: config.sourceKind,
|
|
845
|
-
taskError: error instanceof Error ? error.message : "Run failed before task capture.",
|
|
846
|
-
tasks: {},
|
|
847
|
-
});
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
const comparisons = [];
|
|
852
|
-
for (const config of comparisonRuns) {
|
|
853
|
-
try {
|
|
854
|
-
comparisons.push(await openComparisonRun(browser, config));
|
|
855
|
-
} catch (error) {
|
|
856
|
-
comparisons.push({
|
|
857
|
-
focus: config.focus,
|
|
858
|
-
id: config.id,
|
|
859
|
-
label: config.label,
|
|
860
|
-
taskError:
|
|
861
|
-
error instanceof Error ? error.message : "Comparison run failed before capture.",
|
|
862
|
-
});
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
let prototype;
|
|
867
|
-
try {
|
|
868
|
-
prototype = await inspectPrototype(browser);
|
|
869
|
-
} catch (error) {
|
|
870
|
-
prototype = {
|
|
871
|
-
blocker:
|
|
872
|
-
error instanceof Error ? error.message : "Prototype inspection failed before capture.",
|
|
873
|
-
status: "blocked",
|
|
874
|
-
};
|
|
875
|
-
}
|
|
876
|
-
const capturedAtUtc = new Date().toISOString();
|
|
877
|
-
const performanceSmoke = {
|
|
878
|
-
baseUrl: BASE_URL,
|
|
879
|
-
budgets: performanceBudgets,
|
|
880
|
-
capturedAtUtc,
|
|
881
|
-
comparisonRuns: comparisons,
|
|
882
|
-
measuredRuns: summarizeSmoke(measuredRuns),
|
|
883
|
-
prototype,
|
|
884
|
-
};
|
|
885
|
-
|
|
886
|
-
await writeJson("performance-smoke.json", performanceSmoke);
|
|
887
|
-
await writeJson("editor-load-events.json", {
|
|
888
|
-
baseUrl: BASE_URL,
|
|
889
|
-
capturedAtUtc,
|
|
890
|
-
runs: summarizeEditorLoadEvents(measuredRuns),
|
|
891
|
-
});
|
|
892
|
-
await writeJson("editor-session-smoke.json", {
|
|
893
|
-
baseUrl: BASE_URL,
|
|
894
|
-
capturedAtUtc,
|
|
895
|
-
runs: summarizeEditorSessions(measuredRuns),
|
|
896
|
-
});
|
|
897
|
-
await writeJson("raw-results.json", {
|
|
898
|
-
baseUrl: BASE_URL,
|
|
899
|
-
capturedAtUtc,
|
|
900
|
-
comparisons,
|
|
901
|
-
measuredRuns,
|
|
902
|
-
prototype,
|
|
903
|
-
});
|
|
904
|
-
|
|
905
|
-
console.log(JSON.stringify(performanceSmoke, null, 2));
|
|
906
|
-
} finally {
|
|
907
|
-
await browser.close();
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
await main();
|