@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,333 +0,0 @@
|
|
|
1
|
-
// The gap between what a project HOLDS and what this machine carries.
|
|
2
|
-
//
|
|
3
|
-
// Regression cover for the reported "Open in Maude does nothing": the desktop
|
|
4
|
-
// synced 72/72 and was accurate about the wrong set — three canvases lived only
|
|
5
|
-
// on the hub and no local counter could ever show it.
|
|
6
|
-
|
|
7
|
-
import { describe, expect, test } from 'bun:test';
|
|
8
|
-
import { canvasSlugFromRel } from '../canvas-slug.ts';
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
describeRemoteDiff,
|
|
12
|
-
diffRemoteDocs,
|
|
13
|
-
fetchRemoteListing,
|
|
14
|
-
pullTargets,
|
|
15
|
-
slugFromDocName,
|
|
16
|
-
stateDocumentGone,
|
|
17
|
-
tombstonedSlugs,
|
|
18
|
-
} from '../sync/remote-docs.ts';
|
|
19
|
-
|
|
20
|
-
const okFetch = (documents: unknown) =>
|
|
21
|
-
(async () =>
|
|
22
|
-
new Response(JSON.stringify({ documents }), {
|
|
23
|
-
status: 200,
|
|
24
|
-
headers: { 'content-type': 'application/json' },
|
|
25
|
-
})) as unknown as typeof fetch;
|
|
26
|
-
|
|
27
|
-
describe('fetchRemoteListing', () => {
|
|
28
|
-
test('reads the hub listing', async () => {
|
|
29
|
-
const listing = await fetchRemoteListing(
|
|
30
|
-
'https://hub.example',
|
|
31
|
-
't',
|
|
32
|
-
okFetch([{ name: 'ui-welcome', bytes: 2931 }])
|
|
33
|
-
);
|
|
34
|
-
expect(listing?.documents).toEqual([{ name: 'ui-welcome', bytes: 2931 }]);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
test('carries the token as a bearer, and strips a trailing slash', async () => {
|
|
38
|
-
let seenUrl = '';
|
|
39
|
-
let seenAuth = '';
|
|
40
|
-
const spy = (async (url: string, init: RequestInit) => {
|
|
41
|
-
seenUrl = String(url);
|
|
42
|
-
seenAuth = String((init.headers as Record<string, string>).authorization);
|
|
43
|
-
return new Response(JSON.stringify({ documents: [] }), { status: 200 });
|
|
44
|
-
}) as unknown as typeof fetch;
|
|
45
|
-
await fetchRemoteListing('https://hub.example/', 'tok123', spy);
|
|
46
|
-
expect(seenUrl).toBe('https://hub.example/api/documents');
|
|
47
|
-
expect(seenAuth).toBe('Bearer tok123');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test('an unreachable or old hub yields null, never a throw', async () => {
|
|
51
|
-
// Load-bearing: sync must proceed when the listing cannot be had. A hub
|
|
52
|
-
// without the route (404) must not degrade into a failed sync.
|
|
53
|
-
const boom = (async () => {
|
|
54
|
-
throw new Error('offline');
|
|
55
|
-
}) as unknown as typeof fetch;
|
|
56
|
-
expect(await fetchRemoteListing('https://h', 't', boom)).toBeNull();
|
|
57
|
-
|
|
58
|
-
const notFound = (async () => new Response('nope', { status: 404 })) as unknown as typeof fetch;
|
|
59
|
-
expect(await fetchRemoteListing('https://h', 't', notFound)).toBeNull();
|
|
60
|
-
|
|
61
|
-
const junk = (async () =>
|
|
62
|
-
new Response(JSON.stringify({ documents: 'not-an-array' }), {
|
|
63
|
-
status: 200,
|
|
64
|
-
})) as unknown as typeof fetch;
|
|
65
|
-
expect(await fetchRemoteListing('https://h', 't', junk)).toBeNull();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('reads the tombstones a peer needs to stop resurrecting a delete', async () => {
|
|
69
|
-
const withStones = (async () =>
|
|
70
|
-
new Response(
|
|
71
|
-
JSON.stringify({ documents: [], tombstones: [{ name: 'ui-gone', deletedAt: 42 }] }),
|
|
72
|
-
{ status: 200 }
|
|
73
|
-
)) as unknown as typeof fetch;
|
|
74
|
-
const listing = await fetchRemoteListing('https://h', 't', withStones);
|
|
75
|
-
expect(listing?.tombstones).toEqual([{ name: 'ui-gone', deletedAt: 42 }]);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test('a hub with no tombstone route reports no deletions, not a failure', async () => {
|
|
79
|
-
// "This hub cannot say" and "nothing was deleted" must behave identically:
|
|
80
|
-
// change nothing on disk. Anything else would make an old hub delete work.
|
|
81
|
-
const listing = await fetchRemoteListing(
|
|
82
|
-
'https://h',
|
|
83
|
-
't',
|
|
84
|
-
okFetch([{ name: 'ui-welcome', bytes: 1 }])
|
|
85
|
-
);
|
|
86
|
-
expect(listing?.tombstones).toEqual([]);
|
|
87
|
-
expect(listing?.documents).toHaveLength(1);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test('a junk tombstone field is read as no deletions', async () => {
|
|
91
|
-
const hostile = (async () =>
|
|
92
|
-
new Response(JSON.stringify({ documents: [], tombstones: 'everything' }), {
|
|
93
|
-
status: 200,
|
|
94
|
-
})) as unknown as typeof fetch;
|
|
95
|
-
expect((await fetchRemoteListing('https://h', 't', hostile))?.tombstones).toEqual([]);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe('tombstonedSlugs', () => {
|
|
100
|
-
test('names only the slugs this peer actually holds', () => {
|
|
101
|
-
const slugs = tombstonedSlugs(
|
|
102
|
-
[
|
|
103
|
-
{ name: 'ui-gone', deletedAt: 1 },
|
|
104
|
-
{ name: 'ui-never-had-it', deletedAt: 2 },
|
|
105
|
-
],
|
|
106
|
-
['ui-gone', 'ui-welcome']
|
|
107
|
-
);
|
|
108
|
-
expect(slugs).toEqual(['ui-gone']);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test('refuses a name that could place a file outside the design root', () => {
|
|
112
|
-
// The one hub-supplied signal whose effect is to REMOVE work, so it runs
|
|
113
|
-
// through the same gate a pull does.
|
|
114
|
-
expect(
|
|
115
|
-
tombstonedSlugs(
|
|
116
|
-
[
|
|
117
|
-
{ name: '../../etc/passwd', deletedAt: 1 },
|
|
118
|
-
{ name: 'ui/../../x', deletedAt: 1 },
|
|
119
|
-
{ name: 'ui-real.tsx', deletedAt: 1 },
|
|
120
|
-
],
|
|
121
|
-
['passwd', 'x', 'ui-real', 'ui-real.tsx']
|
|
122
|
-
)
|
|
123
|
-
).toEqual([]);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test('deduplicates a hub naming the same canvas flat and namespaced', () => {
|
|
127
|
-
expect(
|
|
128
|
-
tombstonedSlugs(
|
|
129
|
-
[
|
|
130
|
-
{ name: 'ui-gone', deletedAt: 1 },
|
|
131
|
-
{ name: 'ws/acme/main/ui-gone', deletedAt: 2 },
|
|
132
|
-
],
|
|
133
|
-
['ui-gone']
|
|
134
|
-
)
|
|
135
|
-
).toEqual(['ui-gone']);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
describe('stateDocumentGone', () => {
|
|
140
|
-
test('DELETEs the document, percent-encoding the name', async () => {
|
|
141
|
-
let seenUrl = '';
|
|
142
|
-
let seenMethod = '';
|
|
143
|
-
const spy = (async (url: string, init: RequestInit) => {
|
|
144
|
-
seenUrl = String(url);
|
|
145
|
-
seenMethod = String(init.method);
|
|
146
|
-
return new Response('{}', { status: 200 });
|
|
147
|
-
}) as unknown as typeof fetch;
|
|
148
|
-
const ok = await stateDocumentGone('https://hub.example/', 't', 'ws/acme/main/ui-x', {
|
|
149
|
-
fetchImpl: spy,
|
|
150
|
-
});
|
|
151
|
-
expect(ok).toBe(true);
|
|
152
|
-
expect(seenMethod).toBe('DELETE');
|
|
153
|
-
expect(seenUrl).toBe('https://hub.example/api/documents/ws%2Facme%2Fmain%2Fui-x');
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
test('POSTs to revive, so a re-created name is not eaten by its gravestone', async () => {
|
|
157
|
-
let seenMethod = '';
|
|
158
|
-
const spy = (async (_url: string, init: RequestInit) => {
|
|
159
|
-
seenMethod = String(init.method);
|
|
160
|
-
return new Response('{}', { status: 200 });
|
|
161
|
-
}) as unknown as typeof fetch;
|
|
162
|
-
await stateDocumentGone('https://h', 't', 'ui-x', { revive: true, fetchImpl: spy });
|
|
163
|
-
expect(seenMethod).toBe('POST');
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
test('an old or unreachable hub is false, never a throw', async () => {
|
|
167
|
-
// The local delete already happened; a failed call costs the propagation.
|
|
168
|
-
const boom = (async () => {
|
|
169
|
-
throw new Error('offline');
|
|
170
|
-
}) as unknown as typeof fetch;
|
|
171
|
-
expect(await stateDocumentGone('https://h', 't', 'ui-x', { fetchImpl: boom })).toBe(false);
|
|
172
|
-
const gone = (async () => new Response('nope', { status: 404 })) as unknown as typeof fetch;
|
|
173
|
-
expect(await stateDocumentGone('https://h', 't', 'ui-x', { fetchImpl: gone })).toBe(false);
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
describe('diffRemoteDocs', () => {
|
|
178
|
-
test('names the canvases this machine can never receive', () => {
|
|
179
|
-
// The reported case, in miniature.
|
|
180
|
-
const diff = diffRemoteDocs(
|
|
181
|
-
['ui-home', 'ui-about'],
|
|
182
|
-
[
|
|
183
|
-
{ name: 'ui-home', bytes: 1 },
|
|
184
|
-
{ name: 'ui-about', bytes: 1 },
|
|
185
|
-
{ name: 'ui-welcome', bytes: 2931 },
|
|
186
|
-
]
|
|
187
|
-
);
|
|
188
|
-
expect(diff.shared).toEqual(['ui-about', 'ui-home']);
|
|
189
|
-
expect(diff.hubOnly.map((d) => d.name)).toEqual(['ui-welcome']);
|
|
190
|
-
expect(diff.localOnly).toEqual([]);
|
|
191
|
-
expect(diff.reachable).toBe(true);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
test('a canvas created here but not yet pushed is localOnly, not a gap', () => {
|
|
195
|
-
const diff = diffRemoteDocs(['ui-new'], []);
|
|
196
|
-
expect(diff.localOnly).toEqual(['ui-new']);
|
|
197
|
-
expect(diff.hubOnly).toEqual([]);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
test('an unreachable hub reports NOT reachable rather than a false gap', () => {
|
|
201
|
-
// Reporting every local canvas as "hub-only missing" would be worse than
|
|
202
|
-
// saying nothing — it would invent an alarm out of a network blip.
|
|
203
|
-
const diff = diffRemoteDocs(['ui-home'], null);
|
|
204
|
-
expect(diff.reachable).toBe(false);
|
|
205
|
-
expect(diff.hubOnly).toEqual([]);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
test('compares WIRE names — a namespaced hub vs flat local is a real mismatch', () => {
|
|
209
|
-
// If a caller passed raw slugs against a namespaced hub, everything would
|
|
210
|
-
// look missing on both sides. The diff reports exactly that rather than
|
|
211
|
-
// papering over it, because it IS the doc-identity bug (DDR-192 §5).
|
|
212
|
-
const diff = diffRemoteDocs(['ui-home'], [{ name: 'ws/acme/main/ui-home', bytes: 1 }]);
|
|
213
|
-
expect(diff.shared).toEqual([]);
|
|
214
|
-
expect(diff.hubOnly.map((d) => d.name)).toEqual(['ws/acme/main/ui-home']);
|
|
215
|
-
expect(diff.localOnly).toEqual(['ui-home']);
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
describe('describeRemoteDiff', () => {
|
|
220
|
-
test('says what is missing, with names', () => {
|
|
221
|
-
const msg = describeRemoteDiff(
|
|
222
|
-
diffRemoteDocs(
|
|
223
|
-
[],
|
|
224
|
-
[
|
|
225
|
-
{ name: 'ui-welcome', bytes: 1 },
|
|
226
|
-
{ name: 'ui-how_to_use_maude', bytes: 1 },
|
|
227
|
-
]
|
|
228
|
-
)
|
|
229
|
-
);
|
|
230
|
-
expect(msg).toContain('2 canvases');
|
|
231
|
-
expect(msg).toContain('ui-welcome');
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
test('stays quiet when there is nothing to report', () => {
|
|
235
|
-
expect(describeRemoteDiff(diffRemoteDocs(['a'], [{ name: 'a', bytes: 1 }]))).toBeNull();
|
|
236
|
-
expect(describeRemoteDiff(diffRemoteDocs(['a'], null))).toBeNull();
|
|
237
|
-
});
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
describe('pulling hub-only documents down', () => {
|
|
241
|
-
const join = (...p: string[]) => p.join('/');
|
|
242
|
-
const resolve = (p: string) => p.replace(/\/\.\//g, '/');
|
|
243
|
-
|
|
244
|
-
test('maps a flat and a namespaced doc name to the same slug', () => {
|
|
245
|
-
expect(slugFromDocName('ui-welcome')).toBe('ui-welcome');
|
|
246
|
-
expect(slugFromDocName('ws/acme/main/ui-welcome')).toBe('ui-welcome');
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test('lands hub-only canvases where the tree can SEE them', () => {
|
|
250
|
-
// Flat, because a slug cannot be un-flattened — but inside the canvas group
|
|
251
|
-
// the slug came from, not at the design root. The tree and `scanCanvases`
|
|
252
|
-
// enumerate `canvasGroups`, so the old design-root target produced a file
|
|
253
|
-
// nobody could see and that never synced onward.
|
|
254
|
-
const targets = pullTargets(
|
|
255
|
-
[
|
|
256
|
-
{ name: 'ui-welcome', bytes: 1 },
|
|
257
|
-
{ name: 'ws/acme/main/ui-home', bytes: 1 },
|
|
258
|
-
],
|
|
259
|
-
'/p/.design',
|
|
260
|
-
join,
|
|
261
|
-
resolve,
|
|
262
|
-
'/'
|
|
263
|
-
);
|
|
264
|
-
expect(targets.map((t) => t.bodyAbs)).toEqual([
|
|
265
|
-
'/p/.design/ui/welcome.tsx',
|
|
266
|
-
'/p/.design/ui/home.tsx',
|
|
267
|
-
]);
|
|
268
|
-
// …and each still slugs back to the document it came from. `ui/ui-welcome.tsx`
|
|
269
|
-
// would be visible AND a different document.
|
|
270
|
-
expect(canvasSlugFromRel('ui/welcome.tsx', '.design')).toBe('ui-welcome');
|
|
271
|
-
// The wire name is kept — the provider must open the document the HUB has,
|
|
272
|
-
// not a name re-derived from the local path.
|
|
273
|
-
expect(targets[1].docName).toBe('ws/acme/main/ui-home');
|
|
274
|
-
// No path arrived, so nothing was believed.
|
|
275
|
-
expect(targets.every((t) => t.fromPath === false)).toBe(true);
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
test('uses the path a document carries, once it is known', () => {
|
|
279
|
-
// The listing cannot supply this — it carries names and byte counts only.
|
|
280
|
-
// The runtime re-resolves per document after that document has synced;
|
|
281
|
-
// `pathFor` is that hop, made testable.
|
|
282
|
-
const targets = pullTargets(
|
|
283
|
-
[{ name: 'ws/acme/main/ui-2026-social-summer-camp', bytes: 1 }],
|
|
284
|
-
'/p/.design',
|
|
285
|
-
join,
|
|
286
|
-
resolve,
|
|
287
|
-
'/',
|
|
288
|
-
{
|
|
289
|
-
designRel: '.design',
|
|
290
|
-
canvasGroups: [{ path: 'system' }, { path: 'ui' }],
|
|
291
|
-
pathFor: () => 'ui/2026/social/summer-camp.tsx',
|
|
292
|
-
}
|
|
293
|
-
);
|
|
294
|
-
expect(targets[0].bodyAbs).toBe('/p/.design/ui/2026/social/summer-camp.tsx');
|
|
295
|
-
expect(targets[0].fromPath).toBe(true);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
test('a refused path degrades to the fallback and says why', () => {
|
|
299
|
-
const refusals: string[] = [];
|
|
300
|
-
const targets = pullTargets(
|
|
301
|
-
[{ name: 'ui-welcome', bytes: 1 }],
|
|
302
|
-
'/p/.design',
|
|
303
|
-
join,
|
|
304
|
-
resolve,
|
|
305
|
-
'/',
|
|
306
|
-
{
|
|
307
|
-
designRel: '.design',
|
|
308
|
-
canvasGroups: [{ path: 'ui' }],
|
|
309
|
-
// A perfectly well-formed path that simply is not this document's —
|
|
310
|
-
// the case rule 7 exists for.
|
|
311
|
-
pathFor: () => 'ui/somebody-elses-canvas.tsx',
|
|
312
|
-
onRefused: (slug, reason) => refusals.push(`${slug}: ${reason}`),
|
|
313
|
-
}
|
|
314
|
-
);
|
|
315
|
-
expect(targets[0].bodyAbs).toBe('/p/.design/ui/welcome.tsx');
|
|
316
|
-
expect(targets[0].fromPath).toBe(false);
|
|
317
|
-
expect(refusals.length).toBe(1);
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
test('a document NAME can never write outside the design root', () => {
|
|
321
|
-
// Hub-controlled input, last point before a create. Dots are refused
|
|
322
|
-
// outright, so traversal cannot even form a candidate path.
|
|
323
|
-
const evil = [
|
|
324
|
-
{ name: '../../etc/passwd', bytes: 1 },
|
|
325
|
-
{ name: '..', bytes: 1 },
|
|
326
|
-
{ name: 'ui/../../escape', bytes: 1 },
|
|
327
|
-
{ name: 'a b', bytes: 1 },
|
|
328
|
-
{ name: '', bytes: 1 },
|
|
329
|
-
{ name: 'x'.repeat(500), bytes: 1 },
|
|
330
|
-
];
|
|
331
|
-
expect(pullTargets(evil, '/p/.design', join, resolve, '/')).toEqual([]);
|
|
332
|
-
});
|
|
333
|
-
});
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
// `/_api/sync/resync` + `/_api/sync/cancel-assets` — the MAIN-origin half.
|
|
2
|
-
//
|
|
3
|
-
// `canvas-origin-gate.test.ts` already asserts both routes are unreachable from
|
|
4
|
-
// the segregated canvas origin. That alone proves the wrong property: the main
|
|
5
|
-
// origin is reachable from any website the user happens to visit while the dev
|
|
6
|
-
// server is up. A cross-site POST that lands here would re-authenticate every
|
|
7
|
-
// document against the person's own hub and re-run a whole-project upload — an
|
|
8
|
-
// amplification primitive against their own DDR-102 rate-limit budget, with an
|
|
9
|
-
// opaque response and a very real side effect.
|
|
10
|
-
//
|
|
11
|
-
// So this file asserts the complementary half — the same posture the Figma
|
|
12
|
-
// routes carry (DDR-216 D3): cross-origin write → 403, foreign Host → 403,
|
|
13
|
-
// wrong method → 405. Both halves are required; neither is sufficient.
|
|
14
|
-
|
|
15
|
-
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
16
|
-
import { readFileSync } from 'node:fs';
|
|
17
|
-
import { join } from 'node:path';
|
|
18
|
-
import type { Subprocess } from 'bun';
|
|
19
|
-
|
|
20
|
-
import { bootServer, killProc, makeSandbox, nextPort } from './_helpers.ts';
|
|
21
|
-
|
|
22
|
-
const HTTP = readFileSync(join(import.meta.dir, '..', 'http.ts'), 'utf8');
|
|
23
|
-
|
|
24
|
-
let proc: Subprocess;
|
|
25
|
-
let base: string;
|
|
26
|
-
|
|
27
|
-
const ROUTES = ['/_api/sync/resync', '/_api/sync/cancel-assets'];
|
|
28
|
-
|
|
29
|
-
beforeAll(async () => {
|
|
30
|
-
const { root } = makeSandbox();
|
|
31
|
-
const port = nextPort();
|
|
32
|
-
base = `http://localhost:${port}`;
|
|
33
|
-
proc = await bootServer(root, port);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
afterAll(async () => {
|
|
37
|
-
await killProc(proc);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe('cross-origin writes are refused (the CSRF guard)', () => {
|
|
41
|
-
for (const route of ROUTES) {
|
|
42
|
-
test(`POST ${route} from another origin is 403`, async () => {
|
|
43
|
-
const res = await fetch(`${base}${route}`, {
|
|
44
|
-
method: 'POST',
|
|
45
|
-
headers: { Origin: 'https://evil.example' },
|
|
46
|
-
});
|
|
47
|
-
expect(res.status).toBe(403);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
test(`a CORS-simple POST to ${route} is still refused`, async () => {
|
|
51
|
-
// No preflight, unreadable response — but the side effect would land.
|
|
52
|
-
const res = await fetch(`${base}${route}`, {
|
|
53
|
-
method: 'POST',
|
|
54
|
-
headers: { 'Content-Type': 'text/plain', Origin: 'https://evil.example' },
|
|
55
|
-
body: '{}',
|
|
56
|
-
});
|
|
57
|
-
expect(res.status).toBe(403);
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
describe('a foreign Host header is refused (the DNS-rebinding guard)', () => {
|
|
63
|
-
for (const route of ROUTES) {
|
|
64
|
-
test(`POST ${route} with a rebinding Host is 403`, async () => {
|
|
65
|
-
const res = await fetch(`${base}${route}`, {
|
|
66
|
-
method: 'POST',
|
|
67
|
-
headers: { Host: 'sync.127.0.0.1.nip.io' },
|
|
68
|
-
});
|
|
69
|
-
expect(res.status).toBe(403);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// A refusal the panel cannot explain is a refusal the person cannot act on.
|
|
75
|
-
// Every 403 here used to be plain text, and `SyncPanel.jsx` renders a fixed
|
|
76
|
-
// "Resync could not start." for any response carrying no `detail` — so the two
|
|
77
|
-
// gates above, a dead supervisor and a genuine bug all produced one causeless
|
|
78
|
-
// sentence. These assert the answer is machine-readable AND human-readable; the
|
|
79
|
-
// gates themselves are asserted, unchanged, above.
|
|
80
|
-
describe('every refusal explains itself', () => {
|
|
81
|
-
for (const route of ROUTES) {
|
|
82
|
-
test(`a cross-origin POST ${route} answers JSON with a reason and a detail`, async () => {
|
|
83
|
-
const res = await fetch(`${base}${route}`, {
|
|
84
|
-
method: 'POST',
|
|
85
|
-
headers: { Origin: 'https://evil.example' },
|
|
86
|
-
});
|
|
87
|
-
expect(res.headers.get('content-type')).toContain('application/json');
|
|
88
|
-
const json = (await res.json()) as { ok: boolean; reason?: string; detail?: string };
|
|
89
|
-
expect(json.ok).toBe(false);
|
|
90
|
-
expect(json.reason).toBe('cross-origin');
|
|
91
|
-
expect(json.detail && json.detail.length > 0).toBe(true);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test(`a rebinding-Host POST ${route} answers JSON with a reason and a detail`, async () => {
|
|
95
|
-
const res = await fetch(`${base}${route}`, {
|
|
96
|
-
method: 'POST',
|
|
97
|
-
headers: { Host: 'sync.127.0.0.1.nip.io' },
|
|
98
|
-
});
|
|
99
|
-
const json = (await res.json()) as { ok: boolean; reason?: string; detail?: string };
|
|
100
|
-
expect(json.ok).toBe(false);
|
|
101
|
-
expect(json.reason).toBe('untrusted-host');
|
|
102
|
-
expect(json.detail && json.detail.length > 0).toBe(true);
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
test('a refusal names no request internals back to the caller', async () => {
|
|
107
|
-
// The presented Origin/Host is diagnostic and belongs in the server log —
|
|
108
|
-
// reflecting it would hand a canvas iframe a way to read back what it sent.
|
|
109
|
-
const res = await fetch(`${base}/_api/sync/resync`, {
|
|
110
|
-
method: 'POST',
|
|
111
|
-
headers: { Origin: 'https://evil.example' },
|
|
112
|
-
});
|
|
113
|
-
expect(await res.text()).not.toContain('evil.example');
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
describe('method gating', () => {
|
|
118
|
-
for (const route of ROUTES) {
|
|
119
|
-
test(`GET ${route} is 405 — resync is a write`, async () => {
|
|
120
|
-
expect((await fetch(`${base}${route}`)).status).toBe(405);
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// Source-level, because these two properties only show up on a LINKED project
|
|
126
|
-
// with a live hub — a state a booted sandbox cannot reach — and they are
|
|
127
|
-
// exactly the two that would be silently wrong.
|
|
128
|
-
describe('the route is wired to the whole-sync restart, and refuses rather than queues', () => {
|
|
129
|
-
test('restart() is called with NO argument — this route can never unlink', () => {
|
|
130
|
-
// `restart(null)` is the DETACH lane: it clears the in-memory link and
|
|
131
|
-
// cycles to solo. A resync that passed a falsy value through would
|
|
132
|
-
// disconnect the person's project instead of re-checking it.
|
|
133
|
-
expect(HTTP).toContain('const sync = await control.restart();');
|
|
134
|
-
expect(HTTP).not.toMatch(/sync\/resync[\s\S]{0,900}restart\(null\)/);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
test('a cycle in flight is a 409, not a second full re-link', () => {
|
|
138
|
-
expect(HTTP).toMatch(/control\.busy\?\.\(\)/);
|
|
139
|
-
expect(HTTP).toMatch(/status: 409[\s\S]{0,120}'busy'/);
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
test('cancel reaches the sweep through the live runtime, not the supervisor', () => {
|
|
143
|
-
expect(HTTP).toContain('ctx.syncControl?.current?.()?.cancelAssetSweep() ?? false');
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
describe('same-origin behaviour on an unlinked project', () => {
|
|
148
|
-
test('resync answers honestly rather than pretending it synced', async () => {
|
|
149
|
-
// The sandbox project has no linkedHub, so the supervisor is present but
|
|
150
|
-
// declines. The panel hides the control in this state; the ROUTE still has
|
|
151
|
-
// to give a truthful answer to anything that reaches it.
|
|
152
|
-
const res = await fetch(`${base}/_api/sync/resync`, { method: 'POST' });
|
|
153
|
-
expect(res.status).toBe(200);
|
|
154
|
-
const json = (await res.json()) as {
|
|
155
|
-
ok: boolean;
|
|
156
|
-
sync?: { syncing: boolean; reason?: string };
|
|
157
|
-
};
|
|
158
|
-
expect(json.ok).toBe(true);
|
|
159
|
-
expect(json.sync?.syncing).toBe(false);
|
|
160
|
-
expect(json.sync?.reason).toBe('unlinked');
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
test('cancelling with no sweep in flight says so instead of erroring', async () => {
|
|
164
|
-
const res = await fetch(`${base}/_api/sync/cancel-assets`, { method: 'POST' });
|
|
165
|
-
expect(res.status).toBe(200);
|
|
166
|
-
expect(await res.json()).toEqual({ ok: true, cancelled: false });
|
|
167
|
-
});
|
|
168
|
-
});
|