@1agh/maude 1.0.11 → 1.3.0
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 +3 -1
- package/apps/studio/acp/bridge.ts +18 -13
- package/apps/studio/acp/index.ts +43 -7
- package/apps/studio/acp/transcript-io.ts +394 -0
- package/apps/studio/acp/transcript.ts +103 -38
- package/apps/studio/annotations-layer.tsx +191 -116
- package/apps/studio/annotations-model.ts +39 -0
- package/apps/studio/annotations-sync.ts +50 -0
- package/apps/studio/api.ts +821 -55
- package/apps/studio/bin/_import-figma.mjs +57 -11
- package/apps/studio/bin/_seed-fixture.mjs +163 -0
- package/apps/studio/bin/annotate.mjs +3 -1
- package/apps/studio/bin/server-up.sh +17 -2
- package/apps/studio/canvas-edit.ts +391 -7
- package/apps/studio/canvas-imports.ts +89 -0
- package/apps/studio/canvas-lib.tsx +82 -18
- package/apps/studio/canvas-notice-message.ts +16 -0
- package/apps/studio/canvas-notifications.tsx +19 -0
- package/apps/studio/canvas-shell.tsx +57 -6
- package/apps/studio/client/app.jsx +943 -248
- package/apps/studio/client/apply-edit-request.ts +55 -0
- package/apps/studio/client/export-center.jsx +125 -67
- package/apps/studio/client/github.js +38 -1
- package/apps/studio/client/index-loader.ts +76 -0
- package/apps/studio/client/panels/CloudBar.jsx +52 -6
- package/apps/studio/client/panels/GitPanel.jsx +103 -0
- package/apps/studio/client/panels/OnboardingWizard.jsx +36 -7
- package/apps/studio/client/panels/RepoBranchSwitcher.jsx +92 -27
- package/apps/studio/client/panels/SourceConflictPanel.jsx +166 -0
- package/apps/studio/client/panels/SyncPanel.jsx +238 -4
- package/apps/studio/client/panels/TeamProjects.jsx +376 -0
- package/apps/studio/client/panels/file-deep-link-dialog.jsx +54 -0
- package/apps/studio/client/photo-knobs.jsx +3 -0
- package/apps/studio/client/report-bug.jsx +19 -1
- package/apps/studio/client/share-dialog.jsx +91 -0
- package/apps/studio/client/share-link.js +94 -0
- package/apps/studio/client/styles/3-shell-maude.css +69 -18
- package/apps/studio/client/styles/4-components.css +44 -1
- package/apps/studio/client/tree-row-menu.jsx +48 -3
- package/apps/studio/client/whats-new.jsx +26 -32
- package/apps/studio/cloud/endpoints.ts +132 -0
- package/apps/studio/collab/index.ts +4 -0
- package/apps/studio/collab/persistence.ts +137 -17
- package/apps/studio/collab/registry.ts +19 -21
- package/apps/studio/collab/room.ts +13 -2
- package/apps/studio/context.ts +59 -0
- package/apps/studio/debug-bundle.ts +13 -0
- package/apps/studio/dist/client.bundle.js +1911 -1879
- package/apps/studio/dist/runtime/.min-sizes.json +1 -0
- package/apps/studio/dist/runtime/sonner.js +1 -0
- package/apps/studio/dist/styles.css +1 -1
- package/apps/studio/export-dialog.tsx +54 -0
- package/apps/studio/exporters/degraded.ts +19 -3
- package/apps/studio/exporters/ghostscript.ts +194 -0
- package/apps/studio/exporters/pdf-fonts.ts +261 -0
- package/apps/studio/exporters/pdf.ts +152 -3
- package/apps/studio/exporters/remote.ts +36 -1
- package/apps/studio/fs-watch.ts +45 -1
- package/apps/studio/http.ts +296 -19
- package/apps/studio/inspect.ts +19 -0
- package/apps/studio/managed-projects.ts +150 -0
- package/apps/studio/mem.ts +45 -9
- package/apps/studio/notifications.tsx +292 -0
- package/apps/studio/runtime-bundle.ts +2 -0
- package/apps/studio/server.ts +69 -6
- package/apps/studio/sync/accepted-cold-start.ts +225 -0
- package/apps/studio/sync/accepted-link.ts +320 -0
- package/apps/studio/sync/action-stage.ts +343 -0
- package/apps/studio/sync/agent.ts +104 -45
- package/apps/studio/sync/asset-push.ts +1 -47
- package/apps/studio/sync/cell-file-events.ts +3 -0
- package/apps/studio/sync/codec.ts +192 -34
- package/apps/studio/sync/cold-start-apply.ts +7 -0
- package/apps/studio/sync/cold-start.ts +118 -22
- package/apps/studio/sync/comment-identity.ts +68 -0
- package/apps/studio/sync/connection-state.ts +15 -1
- package/apps/studio/sync/ctl-provider.ts +15 -2
- package/apps/studio/sync/document-discovery.ts +81 -0
- package/apps/studio/sync/file-ledger.ts +117 -0
- package/apps/studio/sync/file-membership.ts +10 -2
- package/apps/studio/sync/file-plane.ts +926 -47
- package/apps/studio/sync/index.ts +1786 -36
- package/apps/studio/sync/journal-client.ts +13 -1
- package/apps/studio/sync/migrate-seed.ts +150 -22
- package/apps/studio/sync/poke.ts +4 -2
- package/apps/studio/sync/presentation.ts +311 -5
- package/apps/studio/sync/projection.ts +820 -23
- package/apps/studio/sync/repeated-module.ts +73 -0
- package/apps/studio/sync/retry-after.ts +169 -0
- package/apps/studio/sync/revision-barrier.ts +129 -0
- package/apps/studio/sync/seed-progress.ts +218 -0
- package/apps/studio/sync/seed-repair.ts +46 -0
- package/apps/studio/sync/source-merge.ts +100 -0
- package/apps/studio/sync/source-ops.ts +289 -0
- package/apps/studio/sync/source-recovery.ts +70 -0
- package/apps/studio/sync/source-validation.ts +56 -0
- package/apps/studio/sync/status.ts +201 -6
- package/apps/studio/sync/transaction-client.ts +460 -0
- package/apps/studio/sync/transport-error.ts +111 -0
- package/apps/studio/sync/writer-registry.ts +236 -0
- package/apps/studio/text-caret.ts +35 -0
- package/apps/studio/undo-hud.tsx +9 -87
- package/apps/studio/use-canvas-media-drop.tsx +4 -39
- package/apps/studio/whats-new.json +173 -0
- package/cli/bin/maude.mjs +2 -0
- package/cli/commands/codex.mjs +191 -0
- package/cli/commands/harness.mjs +1051 -0
- package/cli/commands/help.mjs +13 -0
- package/cli/lib/design-link.mjs +190 -2
- package/cli/lib/harness/capabilities.mjs +302 -0
- package/cli/lib/harness/codex-runtime.mjs +892 -0
- package/cli/lib/harness/compatibility.mjs +27 -0
- package/cli/lib/harness/discover-claude.mjs +1393 -0
- package/cli/lib/harness/managed-state.mjs +1296 -0
- package/cli/lib/harness/model.mjs +86 -0
- package/cli/lib/harness/secrets.mjs +390 -0
- package/cli/lib/harness/targets/codex.mjs +1141 -0
- package/cli/lib/harness/targets/opencode.mjs +807 -0
- package/cli/lib/harness/transaction.mjs +1099 -0
- package/cli/lib/hubs-config.mjs +35 -8
- package/cli/templates/harness/opencode/maude-projector.ts +58 -0
- package/docs/harness-capability-matrix.md +125 -0
- package/docs/harness-environment-projection.md +95 -0
- package/package.json +33 -9
- package/plugins/design/dependencies.json +3 -3
- package/plugins/design/templates/_shell.html +47 -0
- package/plugins/flow/.claude-plugin/config.schema.json +8 -0
- package/plugins/flow/dependencies.json +3 -3
- package/apps/studio/.ai/cache/_stats.json +0 -7
- package/apps/studio/bin/_agent-browser-safe.test.mjs +0 -165
- package/apps/studio/bin/_curl-local.test.mjs +0 -280
- package/apps/studio/bin/_pw-launch.test.mjs +0 -90
- package/apps/studio/bin/_smart-frames.test.mjs +0 -195
- package/apps/studio/bin/_transcribe.test.mjs +0 -80
- package/apps/studio/bun.lock +0 -928
- package/apps/studio/draw/test/animate.test.ts +0 -172
- package/apps/studio/draw/test/brush.test.ts +0 -213
- package/apps/studio/draw/test/composition.test.ts +0 -141
- package/apps/studio/draw/test/geometry.test.ts +0 -199
- package/apps/studio/draw/test/gradient.test.ts +0 -167
- package/apps/studio/draw/test/layout.test.ts +0 -120
- package/apps/studio/draw/test/morph.test.ts +0 -107
- package/apps/studio/draw/test/optimize.test.ts +0 -40
- package/apps/studio/draw/test/palette.test.ts +0 -123
- package/apps/studio/draw/test/primitives.test.ts +0 -129
- package/apps/studio/draw/test/serialize-animate.test.ts +0 -142
- package/apps/studio/draw/test/serialize.test.ts +0 -105
- package/apps/studio/figma/assets.test.ts +0 -464
- package/apps/studio/figma/client.test.ts +0 -395
- package/apps/studio/figma/codegen-client.test.ts +0 -276
- package/apps/studio/figma/codegen-fonts.test.ts +0 -103
- package/apps/studio/figma/codegen-values.test.ts +0 -179
- package/apps/studio/figma/comments-to-strokes.test.ts +0 -194
- package/apps/studio/figma/fig-decode.test.ts +0 -788
- package/apps/studio/figma/fig-differential.test.ts +0 -182
- package/apps/studio/figma/fig-translator.test.ts +0 -192
- package/apps/studio/figma/fig-vector.test.ts +0 -113
- package/apps/studio/figma/from-codegen.test.ts +0 -408
- package/apps/studio/figma/sanitize.test.ts +0 -325
- package/apps/studio/figma/tailwind-map.test.ts +0 -142
- package/apps/studio/figma/to-artboard.test.ts +0 -808
- package/apps/studio/figma/to-render.test.ts +0 -180
- package/apps/studio/figma/to-strokes-roundtrip.test.ts +0 -152
- package/apps/studio/figma/to-strokes.test.ts +0 -705
- package/apps/studio/figma/to-tokens.test.ts +0 -321
- package/apps/studio/figma/url.test.ts +0 -167
- package/apps/studio/footage/schema.test.ts +0 -287
- package/apps/studio/generation/adapters/groq.test.ts +0 -80
- package/apps/studio/generation/audio-library.test.ts +0 -95
- package/apps/studio/generation/captions.test.ts +0 -78
- package/apps/studio/generation/elevenlabs.test.ts +0 -223
- package/apps/studio/generation/gemini.test.ts +0 -350
- package/apps/studio/generation/jobs.test.ts +0 -128
- package/apps/studio/generation/keys.test.ts +0 -75
- package/apps/studio/generation/prefs.test.ts +0 -95
- package/apps/studio/generation/types.test.ts +0 -123
- package/apps/studio/generation/whisper-models.test.ts +0 -179
- package/apps/studio/test/_helpers.ts +0 -108
- package/apps/studio/test/acp-activity-endpoint.test.ts +0 -183
- package/apps/studio/test/acp-activity.test.ts +0 -154
- package/apps/studio/test/acp-ai-activity.test.ts +0 -182
- package/apps/studio/test/acp-attachment-serve.test.ts +0 -74
- package/apps/studio/test/acp-bootstrap-brief.test.ts +0 -178
- package/apps/studio/test/acp-branch-guard.test.ts +0 -123
- package/apps/studio/test/acp-bridge-lifetime.test.ts +0 -475
- package/apps/studio/test/acp-bridge.test.ts +0 -545
- package/apps/studio/test/acp-capabilities.test.ts +0 -123
- package/apps/studio/test/acp-caps-bridge.test.ts +0 -279
- package/apps/studio/test/acp-commands.test.ts +0 -119
- package/apps/studio/test/acp-elicitation-bridge.test.ts +0 -480
- package/apps/studio/test/acp-elicitation.test.ts +0 -251
- package/apps/studio/test/acp-env.test.ts +0 -95
- package/apps/studio/test/acp-mode-banner.test.ts +0 -45
- package/apps/studio/test/acp-origin-gate.test.ts +0 -160
- package/apps/studio/test/acp-permission-prompt.test.ts +0 -251
- package/apps/studio/test/acp-permission.test.ts +0 -278
- package/apps/studio/test/acp-plugin-bootstrap.test.ts +0 -89
- package/apps/studio/test/acp-session-allowed-tools.test.ts +0 -235
- package/apps/studio/test/acp-session-plugins.test.ts +0 -209
- package/apps/studio/test/acp-toolgroup.test.ts +0 -76
- package/apps/studio/test/acp-transcript-view.test.ts +0 -71
- package/apps/studio/test/acp-transcript.test.ts +0 -238
- package/apps/studio/test/acp-usage-bridge.test.ts +0 -141
- package/apps/studio/test/acp-usage.test.ts +0 -143
- package/apps/studio/test/acp-write-gate.test.ts +0 -323
- package/apps/studio/test/acp-write-scope.test.ts +0 -533
- package/apps/studio/test/active-state.test.ts +0 -186
- package/apps/studio/test/activity.test.ts +0 -189
- package/apps/studio/test/ai-activity.test.ts +0 -113
- package/apps/studio/test/annotate-write.test.ts +0 -1074
- package/apps/studio/test/annotation-strokes-command.test.ts +0 -166
- package/apps/studio/test/annotations-align.test.ts +0 -88
- package/apps/studio/test/annotations-api.test.ts +0 -145
- package/apps/studio/test/annotations-bindings.test.ts +0 -262
- package/apps/studio/test/annotations-draw-modifiers.test.ts +0 -206
- package/apps/studio/test/annotations-groups.test.ts +0 -231
- package/apps/studio/test/annotations-layer.test.ts +0 -1011
- package/apps/studio/test/annotations-roundtrip.test.ts +0 -663
- package/apps/studio/test/annotations-snap.test.ts +0 -135
- package/apps/studio/test/annotations-text-format.test.ts +0 -396
- package/apps/studio/test/artboard-activity-overlay.test.tsx +0 -56
- package/apps/studio/test/artboard-guides-overlay.test.tsx +0 -152
- package/apps/studio/test/artboard-hug-commit.test.ts +0 -36
- package/apps/studio/test/artboard-kinds.test.tsx +0 -83
- package/apps/studio/test/artboard-selection-attrs.test.ts +0 -69
- package/apps/studio/test/asset-api.test.ts +0 -149
- package/apps/studio/test/assets-s3.test.ts +0 -249
- package/apps/studio/test/binary-smoke.test.ts +0 -47
- package/apps/studio/test/boot-self-heal.test.ts +0 -87
- package/apps/studio/test/browse-posture.test.tsx +0 -107
- package/apps/studio/test/bundle-smoke.test.ts +0 -46
- package/apps/studio/test/camera-reveal.test.tsx +0 -173
- package/apps/studio/test/canvas-artifacts.test.ts +0 -125
- package/apps/studio/test/canvas-build-sandbox.test.ts +0 -69
- package/apps/studio/test/canvas-build.test.ts +0 -141
- package/apps/studio/test/canvas-create-api.test.ts +0 -676
- package/apps/studio/test/canvas-cursors.test.ts +0 -164
- package/apps/studio/test/canvas-edit.test.ts +0 -470
- package/apps/studio/test/canvas-freshness-deps.test.ts +0 -64
- package/apps/studio/test/canvas-header.test.ts +0 -127
- package/apps/studio/test/canvas-hide-chrome.test.ts +0 -88
- package/apps/studio/test/canvas-hmr-runtime.test.tsx +0 -114
- package/apps/studio/test/canvas-lib-inline.test.ts +0 -146
- package/apps/studio/test/canvas-lib-motion.test.ts +0 -131
- package/apps/studio/test/canvas-lib-resolver.test.ts +0 -111
- package/apps/studio/test/canvas-list-watch.test.ts +0 -388
- package/apps/studio/test/canvas-media-drop.test.ts +0 -187
- package/apps/studio/test/canvas-meta-api.test.ts +0 -707
- package/apps/studio/test/canvas-move-api.test.ts +0 -587
- package/apps/studio/test/canvas-origin-gate.test.ts +0 -417
- package/apps/studio/test/canvas-pipeline.test.ts +0 -180
- package/apps/studio/test/canvas-rects.test.ts +0 -198
- package/apps/studio/test/canvas-reorder.test.ts +0 -211
- package/apps/studio/test/canvas-route.test.ts +0 -179
- package/apps/studio/test/canvas-shell-base.test.ts +0 -107
- package/apps/studio/test/canvas-shell-build-error.test.ts +0 -52
- package/apps/studio/test/canvas-url.test.ts +0 -138
- package/apps/studio/test/canvas-wheel-delta.test.ts +0 -42
- package/apps/studio/test/canvas-zoom-floor.test.ts +0 -73
- package/apps/studio/test/capture-core.test.ts +0 -32
- package/apps/studio/test/capture-determinism-shape.test.ts +0 -135
- package/apps/studio/test/chat-attachments.test.ts +0 -117
- package/apps/studio/test/chat-context.test.ts +0 -129
- package/apps/studio/test/chat-markdown.test.tsx +0 -58
- package/apps/studio/test/clip-addressing.test.ts +0 -738
- package/apps/studio/test/clip-ops.test.ts +0 -922
- package/apps/studio/test/cloud-build.test.ts +0 -95
- package/apps/studio/test/cloud-connect-note.test.ts +0 -135
- package/apps/studio/test/cloud-endpoints.test.ts +0 -590
- package/apps/studio/test/cloud-history-hardening.test.ts +0 -165
- package/apps/studio/test/cloud-history-posture.test.ts +0 -230
- package/apps/studio/test/cloud-managed-save-surfaces.test.ts +0 -90
- package/apps/studio/test/cloud-renew.test.ts +0 -205
- package/apps/studio/test/cloud-session-role.test.ts +0 -161
- package/apps/studio/test/cloud-shell-surfaces.test.ts +0 -170
- package/apps/studio/test/cold-start-apply.test.ts +0 -303
- package/apps/studio/test/collab-annotations-bridge.test.ts +0 -55
- package/apps/studio/test/collab-awareness-bridge.test.ts +0 -223
- package/apps/studio/test/collab-bridge.test.ts +0 -79
- package/apps/studio/test/collab-loopback.test.ts +0 -63
- package/apps/studio/test/collab-origin-gate.test.ts +0 -323
- package/apps/studio/test/collab-protocol.test.ts +0 -146
- package/apps/studio/test/collab-readonly-gate.test.ts +0 -194
- package/apps/studio/test/collab-reseed-guard.test.ts +0 -78
- package/apps/studio/test/collab-room.test.ts +0 -188
- package/apps/studio/test/collab-session-survival.test.tsx +0 -176
- package/apps/studio/test/collab-stress.test.ts +0 -129
- package/apps/studio/test/comment-mount.test.ts +0 -97
- package/apps/studio/test/comment-relay-origin-gate.test.ts +0 -117
- package/apps/studio/test/comments-api.test.ts +0 -277
- package/apps/studio/test/comments-fs-rebroadcast.test.ts +0 -155
- package/apps/studio/test/comments-overlay.test.ts +0 -117
- package/apps/studio/test/compile-entry.test.ts +0 -133
- package/apps/studio/test/component-map.test.ts +0 -48
- package/apps/studio/test/config-projection.test.ts +0 -104
- package/apps/studio/test/config-reload.test.ts +0 -230
- package/apps/studio/test/config-version.test.ts +0 -88
- package/apps/studio/test/context-resolve-tokens.test.ts +0 -97
- package/apps/studio/test/convert-to-absolute.test.ts +0 -333
- package/apps/studio/test/credential-grammar.test.ts +0 -107
- package/apps/studio/test/csp-canvas-shell.test.ts +0 -62
- package/apps/studio/test/csrf-write-guard.test.ts +0 -141
- package/apps/studio/test/debug-bundle.test.ts +0 -105
- package/apps/studio/test/design-setup-readiness.test.ts +0 -155
- package/apps/studio/test/detach-component.test.ts +0 -94
- package/apps/studio/test/dns-rebinding-guard.test.ts +0 -101
- package/apps/studio/test/dom-selection.test.ts +0 -198
- package/apps/studio/test/dynamic-text-edit.test.ts +0 -162
- package/apps/studio/test/edit-css-occurrence.test.ts +0 -81
- package/apps/studio/test/edit-persistence.test.ts +0 -91
- package/apps/studio/test/edit-scope-api.test.ts +0 -119
- package/apps/studio/test/edit-source-command.test.ts +0 -142
- package/apps/studio/test/edit-suppress.test.ts +0 -170
- package/apps/studio/test/editing-presence.test.ts +0 -103
- package/apps/studio/test/element-resize.test.ts +0 -136
- package/apps/studio/test/element-structural-api.test.ts +0 -434
- package/apps/studio/test/element-structural-edit.test.ts +0 -750
- package/apps/studio/test/ensure-browser.test.ts +0 -92
- package/apps/studio/test/equal-spacing-detector.test.ts +0 -257
- package/apps/studio/test/export-assemble.test.ts +0 -53
- package/apps/studio/test/export-browser-lane.test.ts +0 -126
- package/apps/studio/test/export-capture-fidelity.test.ts +0 -146
- package/apps/studio/test/export-capture-hygiene.test.ts +0 -675
- package/apps/studio/test/export-center.test.tsx +0 -287
- package/apps/studio/test/export-e2e-lanes.test.ts +0 -857
- package/apps/studio/test/export-format-scope-coherence.test.ts +0 -89
- package/apps/studio/test/export-lane.test.ts +0 -245
- package/apps/studio/test/export-shim-multi-capture.test.ts +0 -83
- package/apps/studio/test/exporters/canva.test.ts +0 -63
- package/apps/studio/test/exporters/degraded-propagation.test.ts +0 -123
- package/apps/studio/test/exporters/endpoint.test.ts +0 -121
- package/apps/studio/test/exporters/history.test.ts +0 -107
- package/apps/studio/test/exporters/html.test.ts +0 -26
- package/apps/studio/test/exporters/jobs.test.ts +0 -342
- package/apps/studio/test/exporters/pdf.test.ts +0 -85
- package/apps/studio/test/exporters/png.test.ts +0 -103
- package/apps/studio/test/exporters/pptx-deck.test.ts +0 -112
- package/apps/studio/test/exporters/pptx.test.ts +0 -31
- package/apps/studio/test/exporters/pw-launch.test.ts +0 -49
- package/apps/studio/test/exporters/runtime.test.ts +0 -59
- package/apps/studio/test/exporters/scope.test.ts +0 -292
- package/apps/studio/test/exporters/svg.test.ts +0 -29
- package/apps/studio/test/exporters/unsupported-media.test.ts +0 -123
- package/apps/studio/test/exporters/zip.test.ts +0 -104
- package/apps/studio/test/fetch-asset-gate.test.ts +0 -189
- package/apps/studio/test/figjam-v3-model.test.ts +0 -342
- package/apps/studio/test/figma-explode.test.ts +0 -438
- package/apps/studio/test/figma-provenance.test.ts +0 -108
- package/apps/studio/test/figma-routes.test.ts +0 -294
- package/apps/studio/test/file-lock.test.ts +0 -84
- package/apps/studio/test/fixtures/backgrounding-claude-auth.sh +0 -12
- package/apps/studio/test/fixtures/fake-claude-auth.mjs +0 -23
- package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +0 -1
- package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +0 -158
- package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +0 -44
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +0 -46
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +0 -42
- package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +0 -63
- package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +0 -51
- package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +0 -50
- package/apps/studio/test/fixtures/mock-acp-agent-slow.mjs +0 -45
- package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +0 -56
- package/apps/studio/test/fixtures/mock-acp-agent-wedged.mjs +0 -37
- package/apps/studio/test/fixtures/mock-acp-agent-write.mjs +0 -118
- package/apps/studio/test/fixtures/mock-acp-agent.mjs +0 -69
- package/apps/studio/test/fixtures/noisy-claude-auth.mjs +0 -27
- package/apps/studio/test/fixtures/perf-canvas.mjs +0 -201
- package/apps/studio/test/fixtures/phase-20-annotations.svg +0 -1
- package/apps/studio/test/fixtures/phase-21-annotations.svg +0 -1
- package/apps/studio/test/fixtures/tiny-subset.woff2 +0 -0
- package/apps/studio/test/fixtures/video-comp-fixture.tsx +0 -82
- package/apps/studio/test/fixtures/wedged-claude-auth.sh +0 -11
- package/apps/studio/test/footage-store.test.ts +0 -100
- package/apps/studio/test/fs-mkdir-api.test.ts +0 -248
- package/apps/studio/test/fs-watch.test.ts +0 -41
- package/apps/studio/test/gemma-models.test.ts +0 -245
- package/apps/studio/test/generate-route.test.ts +0 -106
- package/apps/studio/test/git-api.test.ts +0 -0
- package/apps/studio/test/git-branches.test.ts +0 -341
- package/apps/studio/test/git-cloud-posture.test.ts +0 -59
- package/apps/studio/test/git-grouping.test.ts +0 -106
- package/apps/studio/test/git-lifecycle.test.ts +0 -100
- package/apps/studio/test/git-log-format.test.ts +0 -95
- package/apps/studio/test/git-safe-rel.test.ts +0 -132
- package/apps/studio/test/git-system-engine.test.ts +0 -135
- package/apps/studio/test/git-watch.test.ts +0 -97
- package/apps/studio/test/github-api.test.ts +0 -577
- package/apps/studio/test/grid-track-handles.test.ts +0 -160
- package/apps/studio/test/handoff-static-frames.test.ts +0 -215
- package/apps/studio/test/handoff.test.ts +0 -329
- package/apps/studio/test/history-rollback.test.ts +0 -88
- package/apps/studio/test/hmr-broadcast.test.ts +0 -236
- package/apps/studio/test/hmr-classify.test.ts +0 -87
- package/apps/studio/test/hub-link.test.ts +0 -80
- package/apps/studio/test/identity-cache.test.ts +0 -83
- package/apps/studio/test/import-asset-browser.test.ts +0 -64
- package/apps/studio/test/import-asset-route.test.ts +0 -71
- package/apps/studio/test/import-asset.test.ts +0 -427
- package/apps/studio/test/import-brand-browser.test.ts +0 -88
- package/apps/studio/test/import-brand-route.test.ts +0 -120
- package/apps/studio/test/import-brand.test.ts +0 -206
- package/apps/studio/test/import-figma.test.ts +0 -667
- package/apps/studio/test/import-tokens.test.ts +0 -722
- package/apps/studio/test/index-data-canvas-ds.test.ts +0 -97
- package/apps/studio/test/input-router.test.ts +0 -757
- package/apps/studio/test/inspect-script-syntax.test.ts +0 -53
- package/apps/studio/test/inspect-selections.test.ts +0 -219
- package/apps/studio/test/knob-props-authored.test.ts +0 -87
- package/apps/studio/test/layers-synthetic-groups.test.ts +0 -96
- package/apps/studio/test/locator.test.ts +0 -214
- package/apps/studio/test/marquee-overlay.test.ts +0 -94
- package/apps/studio/test/media-commit-chain.test.ts +0 -210
- package/apps/studio/test/mode-toggle.test.tsx +0 -226
- package/apps/studio/test/motion-sample.test.ts +0 -35
- package/apps/studio/test/move-artboards-command.test.ts +0 -108
- package/apps/studio/test/participants-chrome.test.ts +0 -65
- package/apps/studio/test/paths.test.ts +0 -88
- package/apps/studio/test/pdf-print-boxes.test.ts +0 -359
- package/apps/studio/test/peer-selection-follows-camera.test.tsx +0 -131
- package/apps/studio/test/perf-harness.ts +0 -193
- package/apps/studio/test/phase-3.6-smoke.test.ts +0 -79
- package/apps/studio/test/photo-bg-remove-validation.test.ts +0 -71
- package/apps/studio/test/photo-canvas-bundle.test.ts +0 -79
- package/apps/studio/test/photo-edit-api.test.ts +0 -201
- package/apps/studio/test/photo-filters.test.ts +0 -149
- package/apps/studio/test/photo-pipeline.test.ts +0 -106
- package/apps/studio/test/photo-taxonomy.test.ts +0 -96
- package/apps/studio/test/print-marks.test.ts +0 -113
- package/apps/studio/test/print-units.test.ts +0 -173
- package/apps/studio/test/read-annotations.test.ts +0 -727
- package/apps/studio/test/read-only-gate.test.ts +0 -188
- package/apps/studio/test/read-only-role.test.ts +0 -95
- package/apps/studio/test/readiness.test.ts +0 -337
- package/apps/studio/test/remote-ahead-behind-cache.test.ts +0 -89
- package/apps/studio/test/reorder-api.test.ts +0 -210
- package/apps/studio/test/repo-concurrency.test.ts +0 -146
- package/apps/studio/test/repo-lock.test.ts +0 -350
- package/apps/studio/test/report-proxy.test.ts +0 -125
- package/apps/studio/test/ripple.test.ts +0 -197
- package/apps/studio/test/runtime-bundle-error-mapping.test.ts +0 -43
- package/apps/studio/test/runtime-bundle.test.ts +0 -69
- package/apps/studio/test/sanitize-annotation-svg.test.ts +0 -238
- package/apps/studio/test/scaffold-design.test.ts +0 -122
- package/apps/studio/test/server-lifecycle.test.ts +0 -38
- package/apps/studio/test/session-runtime-state.test.ts +0 -226
- package/apps/studio/test/session-scope.test.ts +0 -100
- package/apps/studio/test/shared-doc-cell-pairing.test.ts +0 -642
- package/apps/studio/test/shared-doc-convergence.test.ts +0 -265
- package/apps/studio/test/shared-doc-foundation.test.ts +0 -63
- package/apps/studio/test/shared-doc-migrate.test.ts +0 -369
- package/apps/studio/test/shared-doc-projection.test.ts +0 -238
- package/apps/studio/test/shell-importmap.test.ts +0 -56
- package/apps/studio/test/sizing-mode.test.ts +0 -102
- package/apps/studio/test/slash-commands.test.ts +0 -117
- package/apps/studio/test/snap-distance-pill.test.ts +0 -68
- package/apps/studio/test/spacing-handles.test.ts +0 -138
- package/apps/studio/test/specimen-select.test.ts +0 -88
- package/apps/studio/test/sync-agent.test.ts +0 -660
- package/apps/studio/test/sync-annotations-cold-start.test.ts +0 -302
- package/apps/studio/test/sync-asset-push.test.ts +0 -931
- package/apps/studio/test/sync-atomic-write.test.ts +0 -62
- package/apps/studio/test/sync-attach-incremental.test.ts +0 -527
- package/apps/studio/test/sync-autocommit.test.ts +0 -537
- package/apps/studio/test/sync-canvas-path.test.ts +0 -200
- package/apps/studio/test/sync-cell-write-nudge.test.ts +0 -346
- package/apps/studio/test/sync-codec.test.ts +0 -230
- package/apps/studio/test/sync-cold-seed-dedup.test.ts +0 -187
- package/apps/studio/test/sync-cold-start.test.ts +0 -304
- package/apps/studio/test/sync-connect-honesty.test.ts +0 -114
- package/apps/studio/test/sync-connection-state.test.ts +0 -337
- package/apps/studio/test/sync-ctl-channel.test.ts +0 -508
- package/apps/studio/test/sync-decide-file.test.ts +0 -420
- package/apps/studio/test/sync-doc-name.test.ts +0 -281
- package/apps/studio/test/sync-echo-guard.test.ts +0 -96
- package/apps/studio/test/sync-file-ledger.test.ts +0 -334
- package/apps/studio/test/sync-file-membership.test.ts +0 -347
- package/apps/studio/test/sync-file-plane.test.ts +0 -976
- package/apps/studio/test/sync-file-pull.test.ts +0 -333
- package/apps/studio/test/sync-fresh-link-parity.test.ts +0 -267
- package/apps/studio/test/sync-fs-mirror.test.ts +0 -181
- package/apps/studio/test/sync-hardening.test.ts +0 -586
- package/apps/studio/test/sync-hub-listing.test.ts +0 -46
- package/apps/studio/test/sync-hubs-config.test.ts +0 -133
- package/apps/studio/test/sync-incident-replay.test.ts +0 -211
- package/apps/studio/test/sync-journal.test.ts +0 -176
- package/apps/studio/test/sync-meta-codec.test.ts +0 -199
- package/apps/studio/test/sync-migrate-flat-fallback.test.ts +0 -98
- package/apps/studio/test/sync-move-retirement.test.ts +0 -231
- package/apps/studio/test/sync-panel-surface.test.ts +0 -153
- package/apps/studio/test/sync-path-pull.test.ts +0 -531
- package/apps/studio/test/sync-presentation.test.ts +0 -262
- package/apps/studio/test/sync-pull-budget.test.ts +0 -169
- package/apps/studio/test/sync-remote-docs.test.ts +0 -333
- package/apps/studio/test/sync-resync-routes.test.ts +0 -168
- package/apps/studio/test/sync-runtime.test.ts +0 -1675
- package/apps/studio/test/sync-seed-defers-to-hub.test.ts +0 -83
- package/apps/studio/test/sync-settings-routes.test.ts +0 -195
- package/apps/studio/test/sync-settings.test.ts +0 -151
- package/apps/studio/test/sync-status.test.ts +0 -261
- package/apps/studio/test/sync-supervisor.test.ts +0 -262
- package/apps/studio/test/sync-tombstone-apply.test.ts +0 -111
- package/apps/studio/test/sync-trash.test.ts +0 -132
- package/apps/studio/test/sync-two-peer-discovery.test.ts +0 -343
- package/apps/studio/test/sync-untrusted.test.ts +0 -103
- package/apps/studio/test/system-endpoint.test.ts +0 -144
- package/apps/studio/test/text-editability-stamp.test.ts +0 -131
- package/apps/studio/test/timeline-comp-target.test.ts +0 -139
- package/apps/studio/test/timeline-media-cache.test.ts +0 -41
- package/apps/studio/test/timeline-parse.test.ts +0 -184
- package/apps/studio/test/timeline-scale.test.ts +0 -65
- package/apps/studio/test/timeline-snap.test.ts +0 -85
- package/apps/studio/test/tool-palette-insert-anchor.test.ts +0 -86
- package/apps/studio/test/tool-palette.test.tsx +0 -55
- package/apps/studio/test/tour-overlay.test.tsx +0 -90
- package/apps/studio/test/trusted-request-host.test.ts +0 -66
- package/apps/studio/test/undo-sequence-byte-compare.test.ts +0 -211
- package/apps/studio/test/undo-stack.test.ts +0 -244
- package/apps/studio/test/use-agent-presence.test.tsx +0 -117
- package/apps/studio/test/use-annotation-resize.test.ts +0 -402
- package/apps/studio/test/use-annotation-selection.test.tsx +0 -77
- package/apps/studio/test/use-artboard-drag.test.ts +0 -324
- package/apps/studio/test/use-canvas-activity.test.tsx +0 -206
- package/apps/studio/test/use-collab.test.ts +0 -0
- package/apps/studio/test/use-cursor-modifiers.test.ts +0 -59
- package/apps/studio/test/use-keyboard-discipline.test.ts +0 -27
- package/apps/studio/test/use-selection-set.test.tsx +0 -166
- package/apps/studio/test/use-snap-guides.test.ts +0 -271
- package/apps/studio/test/use-tool-mode.test.tsx +0 -186
- package/apps/studio/test/use-undo-stack.test.tsx +0 -223
- package/apps/studio/test/video-asset.test.ts +0 -163
- package/apps/studio/test/video-comp-fixture.test.ts +0 -50
- package/apps/studio/test/video-comp.test.ts +0 -270
- package/apps/studio/test/video-encode-lib.test.ts +0 -63
- package/apps/studio/test/video-render-bridge.test.ts +0 -171
- package/apps/studio/test/whats-new.test.ts +0 -113
- package/apps/studio/test/whisper-setup.test.ts +0 -97
- package/apps/studio/test/workspace-containment.test.ts +0 -382
- package/apps/studio/test/workspace-signin.test.ts +0 -292
- package/apps/studio/test/ws-handshake.test.ts +0 -33
- package/cli/commands/cache.test.mjs +0 -166
- package/cli/commands/design-link.test.mjs +0 -419
- package/cli/commands/design.test.mjs +0 -240
- package/cli/commands/doctor.test.mjs +0 -237
- package/cli/commands/hub-workspace.test.mjs +0 -275
- package/cli/commands/hub.test.mjs +0 -225
- package/cli/commands/kg.test.mjs +0 -142
- package/cli/commands/preflight.test.mjs +0 -46
- package/cli/lib/argv.test.mjs +0 -46
- package/cli/lib/cache.test.mjs +0 -303
- package/cli/lib/cell-plan.test.mjs +0 -225
- package/cli/lib/config-lint.test.mjs +0 -117
- package/cli/lib/ddr-to-kgai.test.mjs +0 -118
- package/cli/lib/design-ownership.test.mjs +0 -329
- package/cli/lib/fetch-asset.test.mjs +0 -213
- package/cli/lib/figma-codegen-reachability.test.mjs +0 -104
- package/cli/lib/figma-import-controls.test.mjs +0 -70
- package/cli/lib/flow-design-integration.test.mjs +0 -165
- package/cli/lib/gitignore-block.test.mjs +0 -136
- package/cli/lib/gitignore-drift.test.mjs +0 -156
- package/cli/lib/hubs-config.test.mjs +0 -165
- package/cli/lib/pkg-root.test.mjs +0 -111
- package/cli/lib/plugin-cli-reachability.test.mjs +0 -57
- package/cli/lib/plugin-name-namespace.test.mjs +0 -71
- package/cli/lib/reconstruct-toolset.test.mjs +0 -194
- package/cli/lib/stack-detect.test.mjs +0 -121
- package/cli/lib/update-check.test.mjs +0 -32
- package/cli/lib/workspace-plan.test.mjs +0 -699
|
@@ -1,417 +0,0 @@
|
|
|
1
|
-
// Canvas-origin route gate — A1/A2 (DDR-060 F1 re-audit). Boots a real server
|
|
2
|
-
// with MAUDE_CANVAS_ORIGIN_SPLIT=1 and probes the segregated canvas origin to
|
|
3
|
-
// prove: (1) the %2f-encoded path-traversal that previously leaked repo files
|
|
4
|
-
// outside designRoot is now 403'd; (2) privileged routes stay 403; (3) legit
|
|
5
|
-
// designRoot assets + the shell still serve; (4) the shell carries the hardened
|
|
6
|
-
// CSP. See .ai/logs/security-reviews/phase-9.1-t2-f1-cross-origin-reaudit.md.
|
|
7
|
-
|
|
8
|
-
import { describe, expect, test } from 'bun:test';
|
|
9
|
-
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
10
|
-
import { join } from 'node:path';
|
|
11
|
-
|
|
12
|
-
import { bootServer, killProc, makeSandbox, nextPort } from './_helpers.ts';
|
|
13
|
-
|
|
14
|
-
async function readCanvasOrigin(designRoot: string): Promise<string> {
|
|
15
|
-
// server.ts writes _server.json under the designRoot once both listeners bind.
|
|
16
|
-
for (let i = 0; i < 40; i++) {
|
|
17
|
-
try {
|
|
18
|
-
const info = JSON.parse(readFileSync(join(designRoot, '_server.json'), 'utf8'));
|
|
19
|
-
if (info.canvasOrigin) return info.canvasOrigin as string;
|
|
20
|
-
} catch {
|
|
21
|
-
/* not written yet */
|
|
22
|
-
}
|
|
23
|
-
await Bun.sleep(50);
|
|
24
|
-
}
|
|
25
|
-
throw new Error('canvasOrigin never appeared in _server.json');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
describe('canvas-origin gate — A1/A2 traversal + privilege containment', () => {
|
|
29
|
-
test('%2f traversal out of designRoot is 403; privileged 403; legit 200; CSP hardened', async () => {
|
|
30
|
-
const { root, designRoot } = makeSandbox();
|
|
31
|
-
// A real asset OUTSIDE .design/ but INSIDE repoRoot — the traversal target.
|
|
32
|
-
writeFileSync(join(root, 'secret-outside.css'), '.leak{content:"SECRET"}');
|
|
33
|
-
// A legit canvas asset under designRoot (the allowed case).
|
|
34
|
-
mkdirSync(join(designRoot, 'ui'), { recursive: true });
|
|
35
|
-
writeFileSync(
|
|
36
|
-
join(designRoot, 'ui', 'Gate.tsx'),
|
|
37
|
-
'export default function G(){return <main/>}\n'
|
|
38
|
-
);
|
|
39
|
-
// DDR-210 — RUNTIME STATE, AT DEPTH.
|
|
40
|
-
//
|
|
41
|
-
// The underscore rule narrowed from "any path segment" to "the first path
|
|
42
|
-
// segment", because a design system's `preview/_components.css` is
|
|
43
|
-
// versioned, shipped, and was 403ing. The narrowing is sound only while
|
|
44
|
-
// DDR-115's taxonomy stays first-segment — which it is today, and which
|
|
45
|
-
// nothing was asserting. `git/service.ts`'s `isMaudeRuntimeState` matches
|
|
46
|
-
// those names at ANY depth, so the two lists now encode different shapes:
|
|
47
|
-
// a future nested runtime dir would be correctly gitignored, correctly
|
|
48
|
-
// hidden from the Changes list, and SERVED TO THE UNTRUSTED CANVAS.
|
|
49
|
-
// These two files are the tripwire.
|
|
50
|
-
mkdirSync(join(designRoot, '_history', 'gate'), { recursive: true });
|
|
51
|
-
writeFileSync(join(designRoot, '_history', 'gate', 'snap.tsx'), 'export default 1\n');
|
|
52
|
-
mkdirSync(join(designRoot, 'system', 'ds', 'preview'), { recursive: true });
|
|
53
|
-
writeFileSync(join(designRoot, 'system', 'ds', 'preview', '_components.css'), '.a{}\n');
|
|
54
|
-
|
|
55
|
-
const port = nextPort();
|
|
56
|
-
const proc = await bootServer(root, port, { MAUDE_CANVAS_ORIGIN_SPLIT: '1' });
|
|
57
|
-
try {
|
|
58
|
-
const canvas = await readCanvasOrigin(designRoot);
|
|
59
|
-
const code = async (p: string) =>
|
|
60
|
-
(await fetch(canvas + p, { signal: AbortSignal.timeout(2000) })).status;
|
|
61
|
-
|
|
62
|
-
// (1) The confirmed bypass — must now be 403, not 200.
|
|
63
|
-
expect(await code('/.design/..%2fsecret-outside.css')).toBe(403);
|
|
64
|
-
expect(await code('/.design/ui/..%2f..%2fsecret-outside.css')).toBe(403);
|
|
65
|
-
// Double-encoded + mixed-case variants stay closed.
|
|
66
|
-
expect(await code('/.design/%2e%2e%2fsecret-outside.css')).toBe(403);
|
|
67
|
-
|
|
68
|
-
// (2) Privileged / out-of-scope routes stay 403 on the canvas origin.
|
|
69
|
-
// /_api/canvas (Phase 22 file-write) is deliberately NOT in the canvas
|
|
70
|
-
// origin's route allowlist — an untrusted canvas iframe must never create
|
|
71
|
-
// arbitrary .tsx files. It is reachable ONLY from the main origin.
|
|
72
|
-
// Phase 12 (DDR-103) — the in-canvas direct-edit write routes are
|
|
73
|
-
// MAIN-ORIGIN ONLY: absent from CANVAS_SAFE_API + startCanvasServer's
|
|
74
|
-
// route map, so the untrusted canvas iframe origin must never reach a
|
|
75
|
-
// source-write endpoint. A GET here 403s at the gate (not 405 from the
|
|
76
|
-
// handler), proving the route is unreachable on this origin.
|
|
77
|
-
for (const p of [
|
|
78
|
-
'/_config',
|
|
79
|
-
'/_sync-status',
|
|
80
|
-
// Cloud Phase 3 Task 3 — the sign-in handler RECEIVES A PASSWORD, and
|
|
81
|
-
// the disclosure route is main-origin UI chrome. Both are absent from
|
|
82
|
-
// CANVAS_SAFE_API and from startCanvasServer's route map, so a GET
|
|
83
|
-
// 403s at the gate rather than 405-ing from the handler — which is what
|
|
84
|
-
// proves the route is unreachable on this origin at all.
|
|
85
|
-
'/_api/workspace/sign-in',
|
|
86
|
-
'/_api/workspace/disclosure',
|
|
87
|
-
'/_api/export',
|
|
88
|
-
// feature-background-export-notification-center — the background job
|
|
89
|
-
// routes carry the same privileged data class as /_api/export (job
|
|
90
|
-
// status, progress, and eventually the rendered bytes); absent from
|
|
91
|
-
// CANVAS_SAFE_API + startCanvasServer's routes (dual-allowlist).
|
|
92
|
-
'/_api/export-jobs',
|
|
93
|
-
'/_api/export-jobs/download',
|
|
94
|
-
'/_api/canvas',
|
|
95
|
-
// feature-file-tree-drag-drop-folders (Task 5) — move/mkdir are
|
|
96
|
-
// file-write routes, same MAIN-ORIGIN-ONLY posture as /_api/canvas
|
|
97
|
-
// right above: absent from CANVAS_SAFE_API + startCanvasServer's
|
|
98
|
-
// routes, so a GET here 403s at the gate, not 405 from the handler.
|
|
99
|
-
'/_api/fs-move',
|
|
100
|
-
'/_api/fs-mkdir',
|
|
101
|
-
'/_api/edit-css',
|
|
102
|
-
'/_api/edit-text',
|
|
103
|
-
'/_api/edit-attr',
|
|
104
|
-
// DDR-148 — raw canvas source for the Timeline parser is MAIN-ORIGIN
|
|
105
|
-
// ONLY (the untrusted canvas iframe must never read project source).
|
|
106
|
-
// Cloud Phase 23 C3 — the Maude Cloud lane holds the PERSONAL TOKEN
|
|
107
|
-
// and can write linkedHub + a hub credential. Absent from
|
|
108
|
-
// CANVAS_SAFE_API and from startCanvasServer's routes; the security
|
|
109
|
-
// pass confirmed that by reading, which is exactly the kind of fact
|
|
110
|
-
// that should be pinned rather than re-read (validate 2026-07-30).
|
|
111
|
-
'/_api/cloud/status',
|
|
112
|
-
'/_api/cloud/projects',
|
|
113
|
-
'/_api/cloud/signin/start',
|
|
114
|
-
'/_api/cloud/signin/poll',
|
|
115
|
-
'/_api/cloud/signout',
|
|
116
|
-
'/_api/cloud/attach',
|
|
117
|
-
'/_api/cloud/attach/code',
|
|
118
|
-
'/_api/canvas-source',
|
|
119
|
-
// DDR-148 — Timeline drag-to-retime is a source-write, MAIN-ORIGIN ONLY.
|
|
120
|
-
'/_api/retime-sequence',
|
|
121
|
-
// DDR-150 P2 — the clip enumerator reads project source structure, so it
|
|
122
|
-
// is MAIN-ORIGIN ONLY (same boundary as /_api/canvas-source): the
|
|
123
|
-
// untrusted canvas iframe must never enumerate the comp it renders.
|
|
124
|
-
'/_api/comp-clips',
|
|
125
|
-
// DDR-150 P3 — clip removal is a source-write, MAIN-ORIGIN ONLY.
|
|
126
|
-
'/_api/remove-sequence',
|
|
127
|
-
// DDR-150 P4 — clip insert is a source-write, MAIN-ORIGIN ONLY.
|
|
128
|
-
'/_api/insert-sequence',
|
|
129
|
-
// DDR-150 P5 — z-order reorder is a source-write, MAIN-ORIGIN ONLY.
|
|
130
|
-
'/_api/reorder-sequence',
|
|
131
|
-
// enhanced-video-editing Phase 2 — the parametric clip-verb door (speed ·
|
|
132
|
-
// trim · split · transition · layer-order · set-text · to-overlay · …) is
|
|
133
|
-
// a source-write, MAIN-ORIGIN ONLY; the layer-order verb rides this route
|
|
134
|
-
// and does NOT widen the canvas surface (security review 2026-07-30).
|
|
135
|
-
'/_api/clip-edit',
|
|
136
|
-
// enhanced-video-editing Task 7 — the filmstrip/waveform cache holds
|
|
137
|
-
// dataURL frames of local footage; MAIN-ORIGIN ONLY + DNS-rebinding guard.
|
|
138
|
-
'/_api/timeline-media',
|
|
139
|
-
// DDR-150 dogfood — array-src replace is a source-write, MAIN-ORIGIN ONLY.
|
|
140
|
-
'/_api/edit-array-src',
|
|
141
|
-
// DDR-150 dogfood — clip hide/show is a source-write, MAIN-ORIGIN ONLY.
|
|
142
|
-
'/_api/toggle-hide',
|
|
143
|
-
// Phase 12.1 (DDR-138) — node-move reorder is a source-write, MAIN-ORIGIN
|
|
144
|
-
// ONLY. The canvas iframe requests a reorder over the dgn:* bus; the shell
|
|
145
|
-
// performs the write. A GET here 403s at the gate (route unreachable on
|
|
146
|
-
// this origin), not 405 from the handler. Same boundary for the Cmd+Z
|
|
147
|
-
// revert route (whole-file swap — strictly more powerful than a move).
|
|
148
|
-
'/_api/reorder',
|
|
149
|
-
'/_api/reorder-revert',
|
|
150
|
-
// Stage I (feature-element-editing-robustness) — general element
|
|
151
|
-
// structural edits (delete / insert / new-artboard) + free-hand artboard
|
|
152
|
-
// resize (D4) are ALL source-writes, MAIN-ORIGIN ONLY. The untrusted
|
|
153
|
-
// canvas iframe requests them over the dgn:* bus; the shell performs the
|
|
154
|
-
// write. A GET from the canvas origin must 403 at the gate (route
|
|
155
|
-
// unreachable on this origin), never 405 from a reached handler.
|
|
156
|
-
'/_api/delete-element',
|
|
157
|
-
'/_api/insert-element',
|
|
158
|
-
'/_api/duplicate-element',
|
|
159
|
-
'/_api/insert-artboard',
|
|
160
|
-
'/_api/resize-artboard',
|
|
161
|
-
'/_api/delete-artboard',
|
|
162
|
-
// feature-3-web-artboards T3 — "Duplicate at width…" is a source-write,
|
|
163
|
-
// same MAIN-ORIGIN-ONLY posture as the other artboard structural ops
|
|
164
|
-
// right above.
|
|
165
|
-
'/_api/duplicate-artboard',
|
|
166
|
-
// feature-bug-report-button — the debug bundle reveals server logs +
|
|
167
|
-
// project context, the submit proxy triggers outbound network, and the
|
|
168
|
-
// fallback writes to disk. All three are MAIN-ORIGIN ONLY: absent from
|
|
169
|
-
// CANVAS_SAFE_API + startCanvasServer's routes, so a GET from the
|
|
170
|
-
// canvas origin 403s at the gate, never reaching the handler.
|
|
171
|
-
'/_api/debug-bundle',
|
|
172
|
-
'/_api/report',
|
|
173
|
-
'/_api/report-fallback',
|
|
174
|
-
// …and the shell capture SPAWNS A PROCESS (screenshot.sh → a headless
|
|
175
|
-
// browser) and renders the whole project. Reachable from the canvas
|
|
176
|
-
// origin it would be a resource-exhaustion lever driven by untrusted
|
|
177
|
-
// canvas code, so it carries the same main-origin-only posture.
|
|
178
|
-
'/_api/shell-shot',
|
|
179
|
-
// Stage F1 — the AssetPicker's asset-listing GET is a shell (main-origin)
|
|
180
|
-
// concern; the untrusted canvas iframe must not enumerate project media.
|
|
181
|
-
'/_api/assets',
|
|
182
|
-
// Stage H — the edit-scope (local-vs-shared) verdict is a shell badge
|
|
183
|
-
// concern; the untrusted canvas origin must not reach it (dual-allowlist).
|
|
184
|
-
'/_api/edit-scope',
|
|
185
|
-
// Phase 27 (E2) — every /_api/git/* route is MAIN-ORIGIN ONLY: absent from
|
|
186
|
-
// CANVAS_SAFE_API + startCanvasServer's `routes` map. A GET from the
|
|
187
|
-
// canvas origin must 403 at the gate (not 405 from a reached handler),
|
|
188
|
-
// proving the route is unreachable on this origin. Guards the dual-
|
|
189
|
-
// allowlist invariant for the token-bearing publish/get-latest endpoints.
|
|
190
|
-
'/_api/git/status',
|
|
191
|
-
'/_api/git/log',
|
|
192
|
-
'/_api/git/diff',
|
|
193
|
-
'/_api/git/commit',
|
|
194
|
-
'/_api/git/discard',
|
|
195
|
-
'/_api/git/push',
|
|
196
|
-
'/_api/git/pull',
|
|
197
|
-
'/_api/git/resolve',
|
|
198
|
-
// Phase 29 (E4) — drafts: list + create + switch + fold are MAIN-ORIGIN ONLY.
|
|
199
|
-
'/_api/git/branches',
|
|
200
|
-
'/_api/git/branch',
|
|
201
|
-
'/_api/git/checkout',
|
|
202
|
-
'/_api/git/fold',
|
|
203
|
-
// Remote drafts: token-bearing fetch — MAIN-ORIGIN ONLY.
|
|
204
|
-
'/_api/git/fetch',
|
|
205
|
-
// Phase 28 (E3) — every /_api/github/* route is MAIN-ORIGIN ONLY (absent
|
|
206
|
-
// from CANVAS_SAFE_API + startCanvasServer's `routes` map) and token-bearing.
|
|
207
|
-
// The untrusted canvas iframe origin must never reach identity/create-repo/
|
|
208
|
-
// invite/repos — a request from this origin 403s at the gate. This is also
|
|
209
|
-
// the guard that the GitHub token (server-held, keychain) can never be
|
|
210
|
-
// exfiltrated to the canvas realm.
|
|
211
|
-
'/_api/github/identity',
|
|
212
|
-
'/_api/github/repos',
|
|
213
|
-
'/_api/github/create-repo',
|
|
214
|
-
'/_api/github/invite',
|
|
215
|
-
'/_api/github/clone',
|
|
216
|
-
'/_api/github/create-project',
|
|
217
|
-
// DDR-216 D3 — every /_api/figma/* route is MAIN-ORIGIN ONLY (absent from
|
|
218
|
-
// CANVAS_SAFE_API + startCanvasServer's `routes` map). A canvas-reachable
|
|
219
|
-
// Figma route would be a token-exfiltration primitive AND an SSRF
|
|
220
|
-
// primitive at once: `connect` stores the user's PAT, `probe` spends it,
|
|
221
|
-
// and `status` would leak whether one exists. A request from this origin
|
|
222
|
-
// 403s at the gate rather than reaching a handler. (The complementary
|
|
223
|
-
// cross-SITE guard — `isTrustedRequestHost` + `sameOriginWrite` on the
|
|
224
|
-
// MAIN origin, which allowlist omission does nothing about — lives in
|
|
225
|
-
// figma-routes.test.ts; both halves are required, neither is sufficient.)
|
|
226
|
-
'/_api/figma/status',
|
|
227
|
-
'/_api/figma/connect',
|
|
228
|
-
'/_api/figma/probe',
|
|
229
|
-
'/_api/figma/import',
|
|
230
|
-
// DDR-219 D2 — the codegen route is privileged-origin only. From the
|
|
231
|
-
// canvas it would be a read primitive over the user's OPEN Figma
|
|
232
|
-
// document plus a write into the design root.
|
|
233
|
-
'/_api/figma/explode',
|
|
234
|
-
// Local-only project create (mkdir + git init + scaffold) — writes to disk,
|
|
235
|
-
// no token; MAIN-ORIGIN ONLY, so the canvas origin must 403 at the gate.
|
|
236
|
-
'/_api/project/create-local',
|
|
237
|
-
'/_api/design/init',
|
|
238
|
-
// Phase 29 (E4) Door C — the hub-link credential write is MAIN-ORIGIN ONLY;
|
|
239
|
-
// the untrusted canvas origin must never reach it (it writes the global
|
|
240
|
-
// ~/.config/maude/hubs.json token store).
|
|
241
|
-
'/_api/hub/link',
|
|
242
|
-
// feature-sync-resync-and-out-of-process-sweep — the Resync control and
|
|
243
|
-
// the sweep cancel are MAIN-ORIGIN ONLY (absent from CANVAS_SAFE_API +
|
|
244
|
-
// startCanvasServer's routes). From the canvas origin, resync would be
|
|
245
|
-
// an amplification primitive driven by untrusted content: one request
|
|
246
|
-
// re-authenticates every document against the person's own hub and
|
|
247
|
-
// re-runs a whole-project upload, spending their DDR-102 rate-limit
|
|
248
|
-
// budget. A GET here 403s at the gate, never 405 from a reached handler.
|
|
249
|
-
'/_api/sync/resync',
|
|
250
|
-
'/_api/sync/cancel-assets',
|
|
251
|
-
// feature-before-first-external-users Task 2 — the sync toggles write
|
|
252
|
-
// `.design/config.json` linkedHub and can restart the sync runtime.
|
|
253
|
-
// From the canvas origin this would let untrusted content turn
|
|
254
|
-
// delete-propagation on or settle a first-anchor hold against the
|
|
255
|
-
// user — MAIN-ORIGIN ONLY, absent from both allowlists.
|
|
256
|
-
'/_api/sync/settings',
|
|
257
|
-
// Ownership mutations (adopt/detach) rewrite `.gitignore` + the git
|
|
258
|
-
// index and can drop the hub link — MAIN-ORIGIN ONLY, same posture.
|
|
259
|
-
'/_api/sync/ownership',
|
|
260
|
-
// Task 3 (F-6) — restore MOVES parked files into the live project and
|
|
261
|
-
// prune DELETES them; untrusted canvas content gets neither primitive.
|
|
262
|
-
'/_api/sync/trash',
|
|
263
|
-
// ACP chat attachments (POST upload + GET thumbnail serve) are MAIN-ORIGIN
|
|
264
|
-
// ONLY — absent from CANVAS_SAFE_API + startCanvasServer's routes. The
|
|
265
|
-
// untrusted canvas origin must never read (or write) the user's pasted
|
|
266
|
-
// chat images; a GET here 403s at the gate, not 404 from the handler.
|
|
267
|
-
'/_api/acp/attachment',
|
|
268
|
-
// feature-acp-turn-notifications Task 3 — the native shell's poller
|
|
269
|
-
// reads this to decide whether to notify for a non-visible project.
|
|
270
|
-
// MAIN-ORIGIN ONLY, same posture as /_api/acp/attachment right above:
|
|
271
|
-
// absent from CANVAS_SAFE_API + startCanvasServer's routes, so the
|
|
272
|
-
// untrusted canvas iframe origin must never learn ANY project's chat
|
|
273
|
-
// activity — including, per Decision D, its own.
|
|
274
|
-
'/_api/acp/activity',
|
|
275
|
-
// Phase 4 (feature-whiteboard-annotation-improvements) — the sticker
|
|
276
|
-
// catalogue + bundled sticker PNGs are MAIN-ORIGIN ONLY, same posture
|
|
277
|
-
// as /_api/assets above: the StickerPicker is shell UI, absent from
|
|
278
|
-
// CANVAS_SAFE_API + startCanvasServer's routes. A GET here 403s at
|
|
279
|
-
// the gate on both — the catalogue route and the static PNG serve.
|
|
280
|
-
'/_api/stickers',
|
|
281
|
-
'/_stickers/some-pack/some-sticker.png',
|
|
282
|
-
// DDR-166 Phase 1 / T2 — the bundled intro-video media serve is
|
|
283
|
-
// MAIN-ORIGIN ONLY, same posture as /_stickers above: a Maude-product
|
|
284
|
-
// asset, absent from CANVAS_SAFE_API + startCanvasServer's routes.
|
|
285
|
-
'/_media/intro.mp4',
|
|
286
|
-
// feature-footage-analysis-director — the footage-analysis + EDL sidecar
|
|
287
|
-
// route is MAIN-ORIGIN ONLY (written by the analyst/director agents over
|
|
288
|
-
// loopback; absent from CANVAS_SAFE_API + startCanvasServer's routes). The
|
|
289
|
-
// untrusted canvas origin must never read/write the director's analysis —
|
|
290
|
-
// a GET here 403s at the gate, not 405 from a reached handler.
|
|
291
|
-
'/_api/footage',
|
|
292
|
-
// feature-ai-media-generation (DDR-16x) — the generation job queue +
|
|
293
|
-
// provider catalogue + key routes are MAIN-ORIGIN ONLY (they resolve a
|
|
294
|
-
// provider KEY and make outbound provider calls). The untrusted canvas
|
|
295
|
-
// origin must never reach them — it sees only the produced /assets/<sha8>.
|
|
296
|
-
// A GET here 403s at the gate, not 405/200 from a reached handler.
|
|
297
|
-
'/_api/generate-jobs',
|
|
298
|
-
'/_api/generate/providers',
|
|
299
|
-
'/_api/generate/keys',
|
|
300
|
-
// Task 2.6 — the non-secret transcription-engine preference writes into
|
|
301
|
-
// .design/config.json + hot-reloads; still MAIN-ORIGIN ONLY (the canvas
|
|
302
|
-
// must not steer the user's engine choice).
|
|
303
|
-
'/_api/generate/prefs',
|
|
304
|
-
// Task 2.5 — reuse-before-you-pay audio search + history re-download
|
|
305
|
-
// resolve the provider key server-side; MAIN-ORIGIN ONLY.
|
|
306
|
-
'/_api/generate/audio-search',
|
|
307
|
-
'/_api/generate/audio-reuse',
|
|
308
|
-
// Task 2.7 — managed whisper-model download (egress + local disk write);
|
|
309
|
-
// MAIN-ORIGIN ONLY.
|
|
310
|
-
'/_api/generate/whisper-model',
|
|
311
|
-
// DDR-166 plan, Phase 2 (T6) — the design-setup readiness probe (project/
|
|
312
|
-
// design-system/first-canvas/brand-assets progress) is a shell/onboarding
|
|
313
|
-
// concern, same posture as /_api/preflight above; MAIN-ORIGIN ONLY.
|
|
314
|
-
'/_api/setup-readiness',
|
|
315
|
-
// DDR-167 (Phase 3 / T10) — local-file SVG/PDF ingestion is a
|
|
316
|
-
// file-write + local-file-adjacent surface (Decision 4); MAIN-ORIGIN
|
|
317
|
-
// ONLY, same posture as /_api/asset's containment but privileged
|
|
318
|
-
// rather than canvas-reachable — the untrusted canvas iframe must
|
|
319
|
-
// never drive an import.
|
|
320
|
-
'/_api/import-asset',
|
|
321
|
-
// DDR-173 (Phase 3 / T12) — brand-file typed-cue extraction is a
|
|
322
|
-
// file-write surface (writes assets/logos/*), same privileged
|
|
323
|
-
// posture as /_api/import-asset above; MAIN-ORIGIN ONLY.
|
|
324
|
-
'/_api/import-brand',
|
|
325
|
-
'/package.json',
|
|
326
|
-
]) {
|
|
327
|
-
expect(await code(p)).toBe(403);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
// (3) Legit surfaces still serve.
|
|
331
|
-
expect(await code('/_health')).toBe(200);
|
|
332
|
-
expect(await code('/_canvas-shell.html')).toBe(200);
|
|
333
|
-
// A real .tsx canvas under designRoot transpiles + serves (200).
|
|
334
|
-
expect(await code('/.design/ui/Gate.tsx')).toBe(200);
|
|
335
|
-
// DDR-210 — the two halves of the narrowed underscore rule, pinned.
|
|
336
|
-
// First-segment runtime state stays refused AT DEPTH…
|
|
337
|
-
expect(await code('/.design/_history/gate/snap.tsx')).toBe(403);
|
|
338
|
-
// …and a NESTED underscore that is versioned design-system content is
|
|
339
|
-
// served. This one is the reason the rule was narrowed: it 403'd, so
|
|
340
|
-
// every canvas in every project rendered without its component styles.
|
|
341
|
-
expect(await code('/.design/system/ds/preview/_components.css')).toBe(200);
|
|
342
|
-
// Phase 23 — the capped image-upload route IS reachable from the canvas
|
|
343
|
-
// origin (it must be — drag-drop/paste/picker run inside the iframe). A GET
|
|
344
|
-
// returns 405 (method-not-allowed) which proves the route is REACHED via
|
|
345
|
-
// the canvas server's explicit `routes` allowlist — NOT 403 (gate-blocked)
|
|
346
|
-
// or 404 (fell through to file-serve). Guards the dual-allowlist invariant:
|
|
347
|
-
// CANVAS_SAFE_API + the startCanvasServer `routes` map must stay in sync.
|
|
348
|
-
expect(await code('/_api/asset')).toBe(405);
|
|
349
|
-
|
|
350
|
-
// (4) The shell carries the hardened CSP (A6).
|
|
351
|
-
const shell = await fetch(`${canvas}/_canvas-shell.html`);
|
|
352
|
-
const csp = shell.headers.get('content-security-policy') ?? '';
|
|
353
|
-
expect(csp).toContain("connect-src 'self'");
|
|
354
|
-
// DDR-148 — media-src must be present so a video-comp's <Video>/<Audio>
|
|
355
|
-
// isn't blocked by default-src 'none' (black video / silent audio).
|
|
356
|
-
expect(csp).toContain("media-src 'self'");
|
|
357
|
-
expect(csp).toContain("webrtc 'block'");
|
|
358
|
-
expect(csp).toContain('frame-ancestors');
|
|
359
|
-
expect(csp).toContain(`http://localhost:${port}`);
|
|
360
|
-
|
|
361
|
-
// (5) DDR-148 attacker F1 — the EXPORT/CAPTURE render (?hide-chrome=1) on
|
|
362
|
-
// the MAIN origin (where the normal shell CSP is off) MUST carry the
|
|
363
|
-
// network-locked capture CSP, else a canvas <Video src="http://internal">
|
|
364
|
-
// or comp fetch() turns ⌘E into read-SSRF + exfil-via-artifact.
|
|
365
|
-
const capMain = await fetch(`http://localhost:${port}/_canvas-shell.html?hide-chrome=1`);
|
|
366
|
-
const capCsp = capMain.headers.get('content-security-policy') ?? '';
|
|
367
|
-
expect(capCsp).toContain("media-src 'self'"); // <Video src=external> blocked
|
|
368
|
-
expect(capCsp).toContain("connect-src 'self'"); // comp fetch(external) blocked
|
|
369
|
-
expect(capCsp).toContain("img-src 'self'");
|
|
370
|
-
expect(capCsp).toContain("object-src 'none'");
|
|
371
|
-
// A normal main-origin shell (no capture flag) stays CSP-off (back-compat).
|
|
372
|
-
const plainMain = await fetch(`http://localhost:${port}/_canvas-shell.html`);
|
|
373
|
-
expect(plainMain.headers.get('content-security-policy')).toBeNull();
|
|
374
|
-
} finally {
|
|
375
|
-
await killProc(proc);
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
describe('an SVG served from the canvas origin is not a scripting document', () => {
|
|
381
|
-
test('.svg carries an inert CSP; other static types are untouched', async () => {
|
|
382
|
-
// The canvas origin serves the TENANT's own SVGs. `image/svg+xml` is inert
|
|
383
|
-
// as an <img>, a CSS url() or a <use> target — none of those are browsing
|
|
384
|
-
// contexts — but navigate to one and it is a page, with <script> in it,
|
|
385
|
-
// running on this origin. Only the shell HTML ever got a CSP, so that was a
|
|
386
|
-
// stored-XSS primitive on a `*.<zone>` origin: step one of a three-link
|
|
387
|
-
// cross-tenant chain (an adversarial pass found it; DDR-210 names the other
|
|
388
|
-
// two links). `default-src 'none'; sandbox` costs the legitimate uses
|
|
389
|
-
// nothing, because the policy never applies to them.
|
|
390
|
-
const { root, designRoot } = makeSandbox();
|
|
391
|
-
mkdirSync(join(designRoot, 'system', 'ds', 'assets'), { recursive: true });
|
|
392
|
-
writeFileSync(
|
|
393
|
-
join(designRoot, 'system', 'ds', 'assets', 'mark.svg'),
|
|
394
|
-
'<svg xmlns="http://www.w3.org/2000/svg"><script>1</script></svg>\n'
|
|
395
|
-
);
|
|
396
|
-
writeFileSync(join(designRoot, 'system', 'ds', 'assets', 'tokens.css'), ':root{}\n');
|
|
397
|
-
|
|
398
|
-
const port = nextPort();
|
|
399
|
-
const proc = await bootServer(root, port, { MAUDE_CANVAS_ORIGIN_SPLIT: '1' });
|
|
400
|
-
try {
|
|
401
|
-
const canvas = await readCanvasOrigin(designRoot);
|
|
402
|
-
const head = async (p: string) =>
|
|
403
|
-
(await fetch(canvas + p, { signal: AbortSignal.timeout(2000) })).headers;
|
|
404
|
-
|
|
405
|
-
const svg = await head('/.design/system/ds/assets/mark.svg');
|
|
406
|
-
expect(svg.get('content-type')).toContain('image/svg+xml');
|
|
407
|
-
expect(svg.get('content-security-policy')).toBe("default-src 'none'; sandbox");
|
|
408
|
-
|
|
409
|
-
// …and only SVG. A stylesheet under a CSP that forbids everything is a
|
|
410
|
-
// stylesheet that does not apply.
|
|
411
|
-
const css = await head('/.design/system/ds/assets/tokens.css');
|
|
412
|
-
expect(css.get('content-security-policy')).toBeNull();
|
|
413
|
-
} finally {
|
|
414
|
-
killProc(proc);
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
});
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test';
|
|
2
|
-
import { parseSync } from 'oxc-parser';
|
|
3
|
-
|
|
4
|
-
import { TranspileError, transpileCanvasSource } from '../canvas-pipeline.ts';
|
|
5
|
-
|
|
6
|
-
const FIXTURE = `
|
|
7
|
-
import { useState } from 'react';
|
|
8
|
-
|
|
9
|
-
export default function DocsSite() {
|
|
10
|
-
const [count, setCount] = useState(0);
|
|
11
|
-
return (
|
|
12
|
-
<section className="hero">
|
|
13
|
-
<h1>Hello, world.</h1>
|
|
14
|
-
<p data-dc-element="hero-copy">Subhead with <em>emphasis</em>.</p>
|
|
15
|
-
<button type="button" onClick={() => setCount((c) => c + 1)}>
|
|
16
|
-
Click {count}
|
|
17
|
-
</button>
|
|
18
|
-
</section>
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
`;
|
|
22
|
-
|
|
23
|
-
describe('transpileCanvasSource — Pass 1 (ID injection)', () => {
|
|
24
|
-
test('determinism: same source produces same locator map across calls', () => {
|
|
25
|
-
const a = transpileCanvasSource('/x/DocsSite.tsx', FIXTURE);
|
|
26
|
-
const b = transpileCanvasSource('/x/DocsSite.tsx', FIXTURE);
|
|
27
|
-
expect(Object.keys(a.locator).sort()).toEqual(Object.keys(b.locator).sort());
|
|
28
|
-
expect(a.withIds).toEqual(b.withIds);
|
|
29
|
-
expect(a.etag).toEqual(b.etag);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('every JSX element gets exactly one data-cd-id', () => {
|
|
33
|
-
const r = transpileCanvasSource('/x/DocsSite.tsx', FIXTURE);
|
|
34
|
-
// Fixture has 5 JSX elements: section, h1, p, em, button.
|
|
35
|
-
const ids = r.withIds.match(/ data-cd-id="[0-9a-f]{8}"/g) ?? [];
|
|
36
|
-
expect(ids.length).toBe(5);
|
|
37
|
-
expect(Object.keys(r.locator).length).toBe(5);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('IDs are 8 lowercase hex characters', () => {
|
|
41
|
-
const r = transpileCanvasSource('/x/DocsSite.tsx', FIXTURE);
|
|
42
|
-
for (const id of Object.keys(r.locator)) {
|
|
43
|
-
expect(id).toMatch(/^[0-9a-f]{8}$/);
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
test('whitespace-only edits inside the component preserve IDs', () => {
|
|
48
|
-
const a = transpileCanvasSource('/x/DocsSite.tsx', FIXTURE);
|
|
49
|
-
const withExtraSpaces = FIXTURE.replace('Hello, world.', ' Hello, world. ');
|
|
50
|
-
const b = transpileCanvasSource('/x/DocsSite.tsx', withExtraSpaces);
|
|
51
|
-
// Whitespace doesn't change componentName or pre-order index of any JSX
|
|
52
|
-
// element, so the same ID set must result.
|
|
53
|
-
expect(Object.keys(b.locator).sort()).toEqual(Object.keys(a.locator).sort());
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test('inserting a sibling shifts the element-to-ID mapping (documented contract)', () => {
|
|
57
|
-
// The contract per DDR-019:
|
|
58
|
-
// - ID is computed from (componentName, preOrderIndex).
|
|
59
|
-
// - The SET of IDs is therefore a function of element count, not content
|
|
60
|
-
// — every position-hash that existed before still exists (superset).
|
|
61
|
-
// - But the SOURCE ELEMENT attached to a given ID shifts: ID at idx=1 used
|
|
62
|
-
// to point to <h1>, after inserting <span> above it now points to <span>.
|
|
63
|
-
// This is the "selection jumps" failure mode Phase-12's (componentName,
|
|
64
|
-
// jsxPath) fallback recovers.
|
|
65
|
-
const a = transpileCanvasSource('/x/DocsSite.tsx', FIXTURE);
|
|
66
|
-
const withInsert = FIXTURE.replace(
|
|
67
|
-
'<section className="hero">',
|
|
68
|
-
'<section className="hero">\n <span>NEW</span>'
|
|
69
|
-
);
|
|
70
|
-
const b = transpileCanvasSource('/x/DocsSite.tsx', withInsert);
|
|
71
|
-
|
|
72
|
-
// Count: one new JSX element => one new ID.
|
|
73
|
-
expect(Object.keys(b.locator).length).toBe(Object.keys(a.locator).length + 1);
|
|
74
|
-
|
|
75
|
-
// Superset: every old position-hash still appears.
|
|
76
|
-
for (const id of Object.keys(a.locator)) expect(b.locator[id]).toBeDefined();
|
|
77
|
-
|
|
78
|
-
// jsxPath-at-ID shifts: the IDs that used to point at h1/p/em/button now
|
|
79
|
-
// point at different element types (or at the new <span>). Concretely:
|
|
80
|
-
// count how many shared IDs have an UNCHANGED jsxPath after the insert.
|
|
81
|
-
let unchangedPaths = 0;
|
|
82
|
-
for (const id of Object.keys(a.locator)) {
|
|
83
|
-
const before = a.locator[id]?.jsxPath.join('>');
|
|
84
|
-
const after = b.locator[id]?.jsxPath.join('>');
|
|
85
|
-
if (before === after) unchangedPaths += 1;
|
|
86
|
-
}
|
|
87
|
-
// Only the first element (the <section> wrapper, idx 0) is unaffected by
|
|
88
|
-
// the insert that happens inside it.
|
|
89
|
-
expect(unchangedPaths).toBe(1);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test('locator entries carry canvas, line, col, jsxPath, componentName', () => {
|
|
93
|
-
const r = transpileCanvasSource('/x/DocsSite.tsx', FIXTURE);
|
|
94
|
-
for (const entry of Object.values(r.locator)) {
|
|
95
|
-
expect(entry.canvas).toBe('/x/DocsSite.tsx');
|
|
96
|
-
expect(typeof entry.line).toBe('number');
|
|
97
|
-
expect(entry.line).toBeGreaterThan(0);
|
|
98
|
-
expect(typeof entry.col).toBe('number');
|
|
99
|
-
expect(Array.isArray(entry.jsxPath)).toBe(true);
|
|
100
|
-
expect(entry.jsxPath.length).toBeGreaterThan(0);
|
|
101
|
-
expect(entry.componentName).toBe('DocsSite');
|
|
102
|
-
}
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('jsxPath reflects element-type nesting', () => {
|
|
106
|
-
const r = transpileCanvasSource('/x/DocsSite.tsx', FIXTURE);
|
|
107
|
-
const paths = Object.values(r.locator)
|
|
108
|
-
.map((e) => e.jsxPath.join('>'))
|
|
109
|
-
.sort();
|
|
110
|
-
// Expected, sorted: section, section>button, section>h1, section>p, section>p>em
|
|
111
|
-
expect(paths).toEqual(['section', 'section>button', 'section>h1', 'section>p', 'section>p>em']);
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
describe('transpileCanvasSource — Pass 2 (JSX -> JS)', () => {
|
|
116
|
-
test('output is parseable JS (no syntax errors)', () => {
|
|
117
|
-
const r = transpileCanvasSource('/x/DocsSite.tsx', FIXTURE);
|
|
118
|
-
const parsed = parseSync('/x/DocsSite.js', r.js, { sourceType: 'module' });
|
|
119
|
-
expect(parsed.errors.length).toBe(0);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test('output preserves the default export', () => {
|
|
123
|
-
const r = transpileCanvasSource('/x/DocsSite.tsx', FIXTURE);
|
|
124
|
-
expect(r.js).toMatch(/export default/);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
test('output references data-cd-id on every emitted element', () => {
|
|
128
|
-
const r = transpileCanvasSource('/x/DocsSite.tsx', FIXTURE);
|
|
129
|
-
const dcCount = (r.js.match(/"data-cd-id":\s*"[0-9a-f]{8}"/g) ?? []).length;
|
|
130
|
-
expect(dcCount).toBe(5);
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
describe('transpileCanvasSource — robustness', () => {
|
|
135
|
-
test('idempotent: re-running on the post-pass-1 source does not double-inject', () => {
|
|
136
|
-
// Re-parsing the post-pass-1 source through the pipeline should detect the
|
|
137
|
-
// existing data-cd-id attribute on every element and skip injection.
|
|
138
|
-
const first = transpileCanvasSource('/x/DocsSite.tsx', FIXTURE);
|
|
139
|
-
const second = transpileCanvasSource('/x/DocsSite.tsx', first.withIds);
|
|
140
|
-
const firstIds = (first.withIds.match(/ data-cd-id="/g) ?? []).length;
|
|
141
|
-
const secondIds = (second.withIds.match(/ data-cd-id="/g) ?? []).length;
|
|
142
|
-
expect(secondIds).toBe(firstIds);
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test('handles arrow-function component bound to a PascalCase const', () => {
|
|
146
|
-
const src = `
|
|
147
|
-
const Hello = () => <h1>hi</h1>;
|
|
148
|
-
export default Hello;
|
|
149
|
-
`;
|
|
150
|
-
const r = transpileCanvasSource('/x/Hello.tsx', src);
|
|
151
|
-
expect(Object.keys(r.locator).length).toBe(1);
|
|
152
|
-
expect(Object.values(r.locator)[0]?.componentName).toBe('Hello');
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
test('handles JSXMemberExpression element names (motion.div)', () => {
|
|
156
|
-
const src = `
|
|
157
|
-
const motion = { div: 'div' };
|
|
158
|
-
export default function MotionCanvas() {
|
|
159
|
-
return <motion.div>boop</motion.div>;
|
|
160
|
-
}
|
|
161
|
-
`;
|
|
162
|
-
const r = transpileCanvasSource('/x/MotionCanvas.tsx', src);
|
|
163
|
-
expect(Object.keys(r.locator).length).toBe(1);
|
|
164
|
-
const entry = Object.values(r.locator)[0];
|
|
165
|
-
expect(entry?.jsxPath).toEqual(['motion.div']);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test('throws TranspileError on a malformed source', () => {
|
|
169
|
-
const broken = 'export default function X() { return <div></span>; }';
|
|
170
|
-
expect(() => transpileCanvasSource('/x/Broken.tsx', broken)).toThrow(TranspileError);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test('source with no JSX produces empty locator + still-valid JS', () => {
|
|
174
|
-
const src = 'export default function X() { return 42; }';
|
|
175
|
-
const r = transpileCanvasSource('/x/X.tsx', src);
|
|
176
|
-
expect(Object.keys(r.locator).length).toBe(0);
|
|
177
|
-
const parsed = parseSync('/x/X.js', r.js, { sourceType: 'module' });
|
|
178
|
-
expect(parsed.errors.length).toBe(0);
|
|
179
|
-
});
|
|
180
|
-
});
|