@1agh/maude 1.0.11 → 1.2.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/apps/studio/acp/bridge.ts +18 -13
- package/apps/studio/acp/transcript-io.ts +394 -0
- package/apps/studio/acp/transcript.ts +103 -38
- package/apps/studio/api.ts +139 -26
- package/apps/studio/bin/_import-figma.mjs +57 -11
- package/apps/studio/bin/_seed-fixture.mjs +163 -0
- package/apps/studio/canvas-imports.ts +89 -0
- package/apps/studio/client/app.jsx +257 -7
- package/apps/studio/client/export-center.jsx +15 -7
- package/apps/studio/client/panels/SyncPanel.jsx +125 -4
- package/apps/studio/client/report-bug.jsx +19 -1
- package/apps/studio/client/styles/3-shell-maude.css +34 -0
- package/apps/studio/client/styles/4-components.css +26 -0
- package/apps/studio/collab/persistence.ts +132 -16
- package/apps/studio/collab/registry.ts +10 -18
- package/apps/studio/debug-bundle.ts +13 -0
- package/apps/studio/dist/client.bundle.js +1128 -1128
- 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 +7 -1
- package/apps/studio/mem.ts +45 -9
- package/apps/studio/server.ts +11 -4
- package/apps/studio/sync/agent.ts +136 -29
- package/apps/studio/sync/asset-push.ts +1 -47
- package/apps/studio/sync/codec.ts +122 -31
- 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/file-ledger.ts +117 -0
- package/apps/studio/sync/file-plane.ts +623 -27
- package/apps/studio/sync/index.ts +711 -20
- package/apps/studio/sync/journal-client.ts +13 -1
- package/apps/studio/sync/migrate-seed.ts +40 -3
- package/apps/studio/sync/presentation.ts +26 -3
- package/apps/studio/sync/retry-after.ts +169 -0
- package/apps/studio/sync/seed-progress.ts +218 -0
- package/apps/studio/sync/status.ts +122 -5
- package/apps/studio/sync/transport-error.ts +111 -0
- package/apps/studio/whats-new.json +72 -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 +889 -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 +32 -9
- package/plugins/flow/.claude-plugin/config.schema.json +8 -0
- 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,249 +0,0 @@
|
|
|
1
|
-
// Cloud Phase 3 Task 2 — the S3/R2 asset lane.
|
|
2
|
-
//
|
|
3
|
-
// Exercised against a live in-process S3-shaped server rather than MinIO: the
|
|
4
|
-
// properties worth proving are content-addressing, verification of bytes
|
|
5
|
-
// received from a semi-trusted hub, and never failing a local save because a
|
|
6
|
-
// bucket was unreachable. None of those need a storage vendor.
|
|
7
|
-
|
|
8
|
-
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
9
|
-
|
|
10
|
-
import { createServer, type Server } from 'node:http';
|
|
11
|
-
import type { AddressInfo } from 'node:net';
|
|
12
|
-
// The HUB's independent SigV4 implementation — imported to pin the two against
|
|
13
|
-
// each other, exactly as the doc-namespace grammars are pinned.
|
|
14
|
-
import { signRequest as hubSignRequest } from '../../hub/src/s3.mjs';
|
|
15
|
-
import {
|
|
16
|
-
createAssetMirror,
|
|
17
|
-
type S3Config,
|
|
18
|
-
s3ConfigFromEnv,
|
|
19
|
-
sha8,
|
|
20
|
-
sha8FromAssetPath,
|
|
21
|
-
signRequest,
|
|
22
|
-
verifyAssetBytes,
|
|
23
|
-
} from '../assets-s3.ts';
|
|
24
|
-
|
|
25
|
-
let server: Server;
|
|
26
|
-
let store: Map<string, Uint8Array>;
|
|
27
|
-
let seenAuth: string[];
|
|
28
|
-
let cfg: S3Config;
|
|
29
|
-
|
|
30
|
-
beforeAll(async () => {
|
|
31
|
-
store = new Map();
|
|
32
|
-
seenAuth = [];
|
|
33
|
-
server = createServer((req, res) => {
|
|
34
|
-
seenAuth.push(req.headers.authorization ?? '');
|
|
35
|
-
const key = decodeURIComponent(
|
|
36
|
-
new URL(req.url ?? '/', 'http://x').pathname.replace('/maude-assets/', '')
|
|
37
|
-
);
|
|
38
|
-
if (req.method === 'PUT') {
|
|
39
|
-
const chunks: Buffer[] = [];
|
|
40
|
-
req.on('data', (c) => chunks.push(c));
|
|
41
|
-
req.on('end', () => {
|
|
42
|
-
store.set(key, new Uint8Array(Buffer.concat(chunks)));
|
|
43
|
-
res.writeHead(200).end();
|
|
44
|
-
});
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
if (req.method === 'HEAD') {
|
|
48
|
-
res.writeHead(store.has(key) ? 200 : 404).end();
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (req.method === 'GET') {
|
|
52
|
-
const body = store.get(key);
|
|
53
|
-
if (!body) {
|
|
54
|
-
res.writeHead(404).end();
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
res.writeHead(200).end(Buffer.from(body));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
res.writeHead(405).end();
|
|
61
|
-
});
|
|
62
|
-
await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', () => resolve()));
|
|
63
|
-
cfg = {
|
|
64
|
-
endpoint: `http://127.0.0.1:${(server.address() as AddressInfo).port}`,
|
|
65
|
-
bucket: 'maude-assets',
|
|
66
|
-
accessKeyId: 'AKIAEXAMPLE',
|
|
67
|
-
secretAccessKey: 'secret',
|
|
68
|
-
region: 'auto',
|
|
69
|
-
};
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
afterAll(async () => {
|
|
73
|
-
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const bytesFor = (s: string) => new TextEncoder().encode(s);
|
|
77
|
-
const assetPath = (b: Uint8Array, ext = 'png') => `assets/${sha8(b)}.${ext}`;
|
|
78
|
-
|
|
79
|
-
describe('content addressing', () => {
|
|
80
|
-
test('sha8 is stable and differs for different bytes', () => {
|
|
81
|
-
const a = bytesFor('hello');
|
|
82
|
-
expect(sha8(a)).toBe(sha8(new Uint8Array(a)));
|
|
83
|
-
expect(sha8(a)).not.toBe(sha8(bytesFor('hellp')));
|
|
84
|
-
expect(sha8(a)).toMatch(/^[0-9a-f]{8}$/);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('sha8FromAssetPath accepts content-addressed paths and refuses others', () => {
|
|
88
|
-
expect(sha8FromAssetPath('assets/deadbeef.png')).toBe('deadbeef');
|
|
89
|
-
expect(sha8FromAssetPath('assets/deadbeef')).toBe('deadbeef');
|
|
90
|
-
expect(sha8FromAssetPath('assets/deadbeef.mp4')).toBe('deadbeef');
|
|
91
|
-
// The shapes the REAL corpus contains beyond saveAsset's own output — a
|
|
92
|
-
// dotted sidecar and hash+label ingested footage. A stricter pattern
|
|
93
|
-
// classified these as "not content addressed", which made the mirror
|
|
94
|
-
// refuse legitimate assets as unverifiable.
|
|
95
|
-
expect(sha8FromAssetPath('assets/deadbeef.photo.json')).toBe('deadbeef');
|
|
96
|
-
expect(sha8FromAssetPath('assets/deadbeef.footage.json')).toBe('deadbeef');
|
|
97
|
-
expect(sha8FromAssetPath('assets/deadbeef-cloud.mp4')).toBe('deadbeef');
|
|
98
|
-
expect(sha8FromAssetPath('assets/deadbeef-cloud.srt')).toBe('deadbeef');
|
|
99
|
-
// Legacy / hand-placed / traversal shapes are not content-addressed. The
|
|
100
|
-
// 8 hex chars must be a COMPLETE token — `deadbeef1` is a different name.
|
|
101
|
-
expect(sha8FromAssetPath('assets/my-photo.png')).toBe(null);
|
|
102
|
-
expect(sha8FromAssetPath('assets/DEADBEEF.png')).toBe(null);
|
|
103
|
-
expect(sha8FromAssetPath('assets/deadbeef1.png')).toBe(null);
|
|
104
|
-
expect(sha8FromAssetPath('assets/deadbeefcafe.png')).toBe(null);
|
|
105
|
-
expect(sha8FromAssetPath('assets/sub/deadbeef.png')).toBe(null);
|
|
106
|
-
expect(sha8FromAssetPath('../assets/deadbeef.png')).toBe(null);
|
|
107
|
-
expect(sha8FromAssetPath('')).toBe(null);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test('verifyAssetBytes is the check that makes a semi-trusted hub usable', () => {
|
|
111
|
-
const bytes = bytesFor('the real image');
|
|
112
|
-
const path = assetPath(bytes);
|
|
113
|
-
expect(verifyAssetBytes(path, bytes)).toBe(true);
|
|
114
|
-
// ...including the labelled and sidecar shapes.
|
|
115
|
-
expect(verifyAssetBytes(`assets/${sha8(bytes)}-cloud.mp4`, bytes)).toBe(true);
|
|
116
|
-
expect(verifyAssetBytes(`assets/${sha8(bytes)}.photo.json`, bytes)).toBe(true);
|
|
117
|
-
// Substituted content under the same name — the attack DDR-054 describes.
|
|
118
|
-
expect(verifyAssetBytes(path, bytesFor('malicious replacement'))).toBe(false);
|
|
119
|
-
// A path with no content address cannot be verified, so it is not trusted.
|
|
120
|
-
expect(verifyAssetBytes('assets/logo.png', bytes)).toBe(false);
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
describe('config', () => {
|
|
125
|
-
test('an unconfigured lane is the default, not an error', () => {
|
|
126
|
-
expect(s3ConfigFromEnv({})).toBe(null);
|
|
127
|
-
// Partial config must not half-enable the lane.
|
|
128
|
-
expect(s3ConfigFromEnv({ MAUDE_S3_BUCKET: 'b', MAUDE_S3_ENDPOINT: 'e' })).toBe(null);
|
|
129
|
-
const mirror = createAssetMirror(null);
|
|
130
|
-
expect(mirror.configured).toBe(false);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
test('a full env set configures it, trailing slash trimmed, region defaulted', () => {
|
|
134
|
-
const resolved = s3ConfigFromEnv({
|
|
135
|
-
MAUDE_S3_ENDPOINT: 'https://acct.r2.cloudflarestorage.com/',
|
|
136
|
-
MAUDE_S3_BUCKET: 'maude-assets',
|
|
137
|
-
MAUDE_S3_ACCESS_KEY_ID: 'AKIA',
|
|
138
|
-
MAUDE_S3_SECRET_ACCESS_KEY: 'secret',
|
|
139
|
-
});
|
|
140
|
-
expect(resolved?.endpoint).toBe('https://acct.r2.cloudflarestorage.com');
|
|
141
|
-
expect(resolved?.region).toBe('auto');
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
describe('SigV4 — pinned against the hub implementation', () => {
|
|
146
|
-
test('studio and hub sign the same request identically', () => {
|
|
147
|
-
// Two independent implementations in two independently-built packages. If
|
|
148
|
-
// they drift, one of them is wrong against real R2 and nobody would notice
|
|
149
|
-
// until a deploy.
|
|
150
|
-
const shared = {
|
|
151
|
-
endpoint: 'https://acct.r2.cloudflarestorage.com',
|
|
152
|
-
bucket: 'maude-assets',
|
|
153
|
-
accessKeyId: 'AKIAEXAMPLE',
|
|
154
|
-
secretAccessKey: 'wJalrXUtnFEMI',
|
|
155
|
-
region: 'auto',
|
|
156
|
-
};
|
|
157
|
-
const now = new Date('2026-07-28T20:30:00Z');
|
|
158
|
-
for (const key of ['assets/deadbeef.png', 'assets/a b+c!.png', 'assets/ünïcode.png']) {
|
|
159
|
-
for (const body of [null, bytesFor('payload')]) {
|
|
160
|
-
const mine = signRequest(shared, { method: 'PUT', key, body, now });
|
|
161
|
-
const theirs = hubSignRequest(shared, { method: 'PUT', key, body, now });
|
|
162
|
-
expect(mine.headers.authorization).toBe(theirs.headers.authorization);
|
|
163
|
-
expect(mine.url).toBe(theirs.url);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test('the payload is signed, so different bytes give a different signature', () => {
|
|
169
|
-
const now = new Date('2026-07-28T20:30:00Z');
|
|
170
|
-
const a = signRequest(cfg, { method: 'PUT', key: 'assets/x.png', body: bytesFor('a'), now });
|
|
171
|
-
const b = signRequest(cfg, { method: 'PUT', key: 'assets/x.png', body: bytesFor('b'), now });
|
|
172
|
-
expect(a.headers.authorization).not.toBe(b.headers.authorization);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
describe('the mirror, against a live S3-shaped server', () => {
|
|
177
|
-
test('push then pull round-trips, and every request is signed', async () => {
|
|
178
|
-
const mirror = createAssetMirror(cfg);
|
|
179
|
-
const bytes = bytesFor('a small png, pretend');
|
|
180
|
-
const rel = assetPath(bytes);
|
|
181
|
-
|
|
182
|
-
expect(await mirror.push(rel, bytes)).toBe(true);
|
|
183
|
-
expect(await mirror.has(rel)).toBe(true);
|
|
184
|
-
const pulled = await mirror.pull(rel);
|
|
185
|
-
expect(pulled).not.toBe(null);
|
|
186
|
-
expect([...(pulled ?? [])]).toEqual([...bytes]);
|
|
187
|
-
|
|
188
|
-
expect(seenAuth.length).toBeGreaterThan(0);
|
|
189
|
-
expect(seenAuth.every((a) => a.startsWith('AWS4-HMAC-SHA256 Credential='))).toBe(true);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
test('push is idempotent — content addressing means a re-push is a no-op', async () => {
|
|
193
|
-
const mirror = createAssetMirror(cfg);
|
|
194
|
-
const bytes = bytesFor('idempotent content');
|
|
195
|
-
const rel = assetPath(bytes);
|
|
196
|
-
expect(await mirror.push(rel, bytes)).toBe(true);
|
|
197
|
-
const sizeAfterFirst = store.size;
|
|
198
|
-
expect(await mirror.push(rel, bytes)).toBe(true);
|
|
199
|
-
expect(store.size).toBe(sizeAfterFirst);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
test('a missing object pulls as null, not an error', async () => {
|
|
203
|
-
const mirror = createAssetMirror(cfg);
|
|
204
|
-
expect(await mirror.pull('assets/00000000.png')).toBe(null);
|
|
205
|
-
expect(await mirror.has('assets/00000000.png')).toBe(false);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
test('SUBSTITUTED bytes are REFUSED — the hub cannot swap an asset', async () => {
|
|
209
|
-
// Plant content under a name it does not hash to, exactly as a compromised
|
|
210
|
-
// or malicious hub would. The pull must refuse rather than write it to disk,
|
|
211
|
-
// where it would poison every peer that later mirrors from us.
|
|
212
|
-
const honest = bytesFor('the real asset');
|
|
213
|
-
const rel = assetPath(honest);
|
|
214
|
-
store.set(rel, bytesFor('malicious replacement of the same length!!'));
|
|
215
|
-
|
|
216
|
-
const warnings: string[] = [];
|
|
217
|
-
const mirror = createAssetMirror(cfg, { log: { warn: (m: string) => warnings.push(m) } });
|
|
218
|
-
expect(await mirror.pull(rel)).toBe(null);
|
|
219
|
-
expect(warnings.join(' ')).toMatch(/does not hash to its own name/);
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
test('an unreachable bucket NEVER fails the caller — it returns false', async () => {
|
|
223
|
-
// The local file is already on disk and in git; the bucket is the redundant
|
|
224
|
-
// copy. Throwing here would turn a network blip into a failed save.
|
|
225
|
-
const dead = createAssetMirror(
|
|
226
|
-
{ ...cfg, endpoint: 'http://127.0.0.1:1' },
|
|
227
|
-
{ log: { warn: () => {} } }
|
|
228
|
-
);
|
|
229
|
-
expect(await dead.push('assets/deadbeef.png', bytesFor('x'))).toBe(false);
|
|
230
|
-
expect(await dead.pull('assets/deadbeef.png')).toBe(null);
|
|
231
|
-
expect(await dead.has('assets/deadbeef.png')).toBe(false);
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
test('a non-2xx response is handled like a miss, not a crash', async () => {
|
|
235
|
-
const mirror = createAssetMirror(
|
|
236
|
-
{ ...cfg, bucket: 'wrong-bucket' },
|
|
237
|
-
{ log: { warn: () => {} } }
|
|
238
|
-
);
|
|
239
|
-
expect(await mirror.pull('assets/deadbeef.png')).toBe(null);
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
test('the unconfigured mirror answers safely for every verb', async () => {
|
|
243
|
-
const mirror = createAssetMirror(null);
|
|
244
|
-
expect(await mirror.push('assets/deadbeef.png', bytesFor('x'))).toBe(false);
|
|
245
|
-
expect(await mirror.pull('assets/deadbeef.png')).toBe(null);
|
|
246
|
-
expect(await mirror.has('assets/deadbeef.png')).toBe(false);
|
|
247
|
-
expect(mirror.describe).toBe('none');
|
|
248
|
-
});
|
|
249
|
-
});
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
// Smoke: dist/maude-<platform> compiles + spawns + exits cleanly.
|
|
2
|
-
//
|
|
3
|
-
// We run this only when the compiled binary is already present. The full
|
|
4
|
-
// `bun build --compile` step costs ~150 ms; CI matrix already proves it on
|
|
5
|
-
// every release tag (Task 13). Local devs see this skip silently unless they
|
|
6
|
-
// have already run `bun run build.ts --release`.
|
|
7
|
-
|
|
8
|
-
import { describe, expect, test } from 'bun:test';
|
|
9
|
-
import { join } from 'node:path';
|
|
10
|
-
|
|
11
|
-
const PLATFORM_SLUG = (() => {
|
|
12
|
-
const p = process.platform;
|
|
13
|
-
const a = process.arch;
|
|
14
|
-
if (p === 'darwin') return a === 'arm64' ? 'darwin-arm64' : 'darwin-x64';
|
|
15
|
-
if (p === 'linux') return a === 'arm64' ? 'linux-arm64' : 'linux-x64';
|
|
16
|
-
if (p === 'win32') return 'win32-x64';
|
|
17
|
-
return null;
|
|
18
|
-
})();
|
|
19
|
-
|
|
20
|
-
describe('binary smoke', () => {
|
|
21
|
-
test('compiled binary boots and exits 0 on --help / handles unknown args gracefully', async () => {
|
|
22
|
-
if (!PLATFORM_SLUG) {
|
|
23
|
-
console.log('binary-smoke: unsupported host platform; skipping');
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
const binPath = join(import.meta.dir, '..', 'dist', `maude-${PLATFORM_SLUG}`);
|
|
27
|
-
const exists = await Bun.file(binPath).exists();
|
|
28
|
-
if (!exists) {
|
|
29
|
-
console.log(
|
|
30
|
-
`binary-smoke: ${binPath} not built; skipping (run \`bun run build.ts --release\`)`
|
|
31
|
-
);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// The compiled binary is the dev-server entry — invoking it with --root
|
|
36
|
-
// pointing at a temp dir without .design/ causes a fail-loud exit (1).
|
|
37
|
-
// That's enough to prove the binary is a valid executable.
|
|
38
|
-
const proc = Bun.spawn({
|
|
39
|
-
cmd: [binPath, '--root', '/tmp/nonexistent-maude-test'],
|
|
40
|
-
stdout: 'pipe',
|
|
41
|
-
stderr: 'pipe',
|
|
42
|
-
env: { ...process.env, NO_OPEN: '1' },
|
|
43
|
-
});
|
|
44
|
-
const code = await proc.exited;
|
|
45
|
-
expect(code === 0 || code === 1).toBe(true);
|
|
46
|
-
});
|
|
47
|
-
});
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
// Boot artifact check — Phase 19 / DDR-044, simplified in v0.18.1.
|
|
2
|
-
// v0.18.0 tried to `bun install` + `bun run build.ts` when node_modules/react
|
|
3
|
-
// was missing; that misfired in every install scenario (see boot-self-heal.ts
|
|
4
|
-
// header). v0.18.1 ships pre-built runtime bundles + just verifies they're
|
|
5
|
-
// reachable. Missing artifact == broken install, not first-boot gap.
|
|
6
|
-
|
|
7
|
-
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
|
8
|
-
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
9
|
-
import { tmpdir } from 'node:os';
|
|
10
|
-
import { join } from 'node:path';
|
|
11
|
-
|
|
12
|
-
import { bootSelfHeal, type SelfHealOptions } from '../boot-self-heal.ts';
|
|
13
|
-
|
|
14
|
-
let TMP: string;
|
|
15
|
-
|
|
16
|
-
beforeEach(() => {
|
|
17
|
-
TMP = mkdtempSync(join(tmpdir(), 'maude-self-heal-'));
|
|
18
|
-
mkdirSync(join(TMP, 'dist', 'runtime'), { recursive: true });
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
afterEach(() => {
|
|
22
|
-
rmSync(TMP, { recursive: true, force: true });
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
function harness(extra: Partial<SelfHealOptions> = {}) {
|
|
26
|
-
const logs: string[] = [];
|
|
27
|
-
let exited: number | null = null;
|
|
28
|
-
const opts: SelfHealOptions = {
|
|
29
|
-
here: TMP,
|
|
30
|
-
log: (m) => logs.push(m),
|
|
31
|
-
exit: ((code: number) => {
|
|
32
|
-
exited = code;
|
|
33
|
-
throw new Error(`__exit:${code}`);
|
|
34
|
-
}) as never,
|
|
35
|
-
...extra,
|
|
36
|
-
};
|
|
37
|
-
return { opts, logs, getExited: () => exited };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function seedAll() {
|
|
41
|
-
writeFileSync(join(TMP, 'dist', 'client.bundle.js'), '/* stub */');
|
|
42
|
-
writeFileSync(join(TMP, 'dist', 'runtime', 'react.js'), '/* stub */');
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
describe('bootSelfHeal', () => {
|
|
46
|
-
test('passes when both required artifacts present', async () => {
|
|
47
|
-
seedAll();
|
|
48
|
-
const { opts, logs } = harness();
|
|
49
|
-
const result = await bootSelfHeal(opts);
|
|
50
|
-
expect(result.verified).toEqual(['client.bundle.js', 'runtime/react.js']);
|
|
51
|
-
expect(logs).toEqual([]);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('exits 1 with remediation when dist/client.bundle.js missing', async () => {
|
|
55
|
-
writeFileSync(join(TMP, 'dist', 'runtime', 'react.js'), '/* stub */');
|
|
56
|
-
const { opts, logs, getExited } = harness();
|
|
57
|
-
await expect(bootSelfHeal(opts)).rejects.toThrow('__exit:1');
|
|
58
|
-
expect(getExited()).toBe(1);
|
|
59
|
-
const msg = logs.join('\n');
|
|
60
|
-
expect(msg).toMatch(/dist\/client\.bundle\.js/);
|
|
61
|
-
expect(msg).toMatch(/npm uninstall -g @1agh\/maude/);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test('exits 1 with remediation when dist/runtime/react.js missing', async () => {
|
|
65
|
-
writeFileSync(join(TMP, 'dist', 'client.bundle.js'), '/* stub */');
|
|
66
|
-
const { opts, logs, getExited } = harness();
|
|
67
|
-
await expect(bootSelfHeal(opts)).rejects.toThrow('__exit:1');
|
|
68
|
-
expect(getExited()).toBe(1);
|
|
69
|
-
const msg = logs.join('\n');
|
|
70
|
-
expect(msg).toMatch(/dist\/runtime\/react\.js/);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('lists ALL missing artifacts in one message (not first-fail-only)', async () => {
|
|
74
|
-
// Nothing seeded — both missing.
|
|
75
|
-
const { opts, logs } = harness();
|
|
76
|
-
await expect(bootSelfHeal(opts)).rejects.toThrow('__exit:1');
|
|
77
|
-
const msg = logs.join('\n');
|
|
78
|
-
expect(msg).toMatch(/dist\/client\.bundle\.js/);
|
|
79
|
-
expect(msg).toMatch(/dist\/runtime\/react\.js/);
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test('remediation surfaces the looked-under path so user can verify', async () => {
|
|
83
|
-
const { opts, logs } = harness();
|
|
84
|
-
await expect(bootSelfHeal(opts)).rejects.toThrow();
|
|
85
|
-
expect(logs.join('\n')).toContain(TMP);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
// browse-posture — feature-4 (browse/move split, DDR-187). The LOAD-BEARING
|
|
2
|
-
// gate: a DOM-level proof that the boot-default `browse` tool is a pure native
|
|
3
|
-
// pass-through (a real click handler under the cursor fires, nothing is
|
|
4
|
-
// claimed), while the `move` (select) tool CLAIMS the same bare click (native
|
|
5
|
-
// handler suppressed, onSelect dispatched). This promotes the input-router
|
|
6
|
-
// comment-invariant ("bare clicks pass through") into an enforced test — the
|
|
7
|
-
// canvas-origin-gate equivalent for the input posture.
|
|
8
|
-
//
|
|
9
|
-
// Needs a live DOM (capture-phase listeners + event dispatch + a real React
|
|
10
|
-
// effect) — register happy-dom + drive a createRoot, same convention as
|
|
11
|
-
// canvas-hmr-runtime.test.tsx / specimen-select.test.ts.
|
|
12
|
-
|
|
13
|
-
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
14
|
-
import { GlobalRegistrator } from '@happy-dom/global-registrator';
|
|
15
|
-
|
|
16
|
-
beforeAll(() => {
|
|
17
|
-
GlobalRegistrator.register();
|
|
18
|
-
(globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
|
|
19
|
-
});
|
|
20
|
-
afterAll(() => {
|
|
21
|
-
GlobalRegistrator.unregister();
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
import { act, useRef } from 'react';
|
|
25
|
-
import { createRoot, type Root } from 'react-dom/client';
|
|
26
|
-
|
|
27
|
-
import { type RouterCallbacks, type Tool, useInputRouter } from '../input-router.tsx';
|
|
28
|
-
|
|
29
|
-
// A minimal host that mounts the router over a single native <button>. The
|
|
30
|
-
// active tool is read fresh at event time via `getActiveTool` (a ref-backed
|
|
31
|
-
// getter), so a test can flip the tool without re-rendering.
|
|
32
|
-
function Harness({ getTool, callbacks }: { getTool: () => Tool; callbacks: RouterCallbacks }) {
|
|
33
|
-
const hostRef = useRef<HTMLDivElement | null>(null);
|
|
34
|
-
useInputRouter({ hostRef, getActiveTool: getTool, callbacks });
|
|
35
|
-
return (
|
|
36
|
-
<div ref={hostRef} className="dc-canvas">
|
|
37
|
-
<button type="button" id="native-btn">
|
|
38
|
-
Click me
|
|
39
|
-
</button>
|
|
40
|
-
</div>
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function fireBareLeftClick(el: Element): void {
|
|
45
|
-
// The full native sequence the router gates: pointerdown → mousedown → click.
|
|
46
|
-
el.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true, cancelable: true, button: 0 }));
|
|
47
|
-
el.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true, button: 0 }));
|
|
48
|
-
el.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, button: 0 }));
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
describe('browse tool — native pass-through (boot default)', () => {
|
|
52
|
-
test('bare click: native button handler FIRES, onSelect NOT dispatched', () => {
|
|
53
|
-
const el = document.createElement('div');
|
|
54
|
-
document.body.appendChild(el);
|
|
55
|
-
const root: Root = createRoot(el);
|
|
56
|
-
let selectCalls = 0;
|
|
57
|
-
let nativeClicks = 0;
|
|
58
|
-
|
|
59
|
-
act(() => {
|
|
60
|
-
root.render(
|
|
61
|
-
<Harness getTool={() => 'browse'} callbacks={{ onSelect: () => (selectCalls += 1) }} />
|
|
62
|
-
);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
const btn = el.querySelector('#native-btn') as HTMLButtonElement;
|
|
66
|
-
btn.addEventListener('click', () => (nativeClicks += 1));
|
|
67
|
-
act(() => {
|
|
68
|
-
fireBareLeftClick(btn);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
expect(selectCalls).toBe(0); // browse never selects
|
|
72
|
-
expect(nativeClicks).toBe(1); // the mock stays alive
|
|
73
|
-
|
|
74
|
-
act(() => root.unmount());
|
|
75
|
-
el.remove();
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
describe('move (select) tool — claims the bare click', () => {
|
|
80
|
-
test('bare click: native button handler SUPPRESSED, onSelect dispatched', () => {
|
|
81
|
-
const el = document.createElement('div');
|
|
82
|
-
document.body.appendChild(el);
|
|
83
|
-
const root: Root = createRoot(el);
|
|
84
|
-
let selectCalls = 0;
|
|
85
|
-
let nativeClicks = 0;
|
|
86
|
-
|
|
87
|
-
act(() => {
|
|
88
|
-
root.render(
|
|
89
|
-
<Harness getTool={() => 'move'} callbacks={{ onSelect: () => (selectCalls += 1) }} />
|
|
90
|
-
);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const btn = el.querySelector('#native-btn') as HTMLButtonElement;
|
|
94
|
-
btn.addEventListener('click', () => (nativeClicks += 1));
|
|
95
|
-
act(() => {
|
|
96
|
-
fireBareLeftClick(btn);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
// Router claims the pointerdown (dispatches select) and swallows the
|
|
100
|
-
// synthetic click so the button under the cursor never activates.
|
|
101
|
-
expect(selectCalls).toBe(1);
|
|
102
|
-
expect(nativeClicks).toBe(0);
|
|
103
|
-
|
|
104
|
-
act(() => root.unmount());
|
|
105
|
-
el.remove();
|
|
106
|
-
});
|
|
107
|
-
});
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
// Smoke: build.ts produces a parsable IIFE bundle that ships React 19.
|
|
2
|
-
//
|
|
3
|
-
// Builds into a DISPOSABLE directory (`MAUDE_DIST_DIR`), never the real one.
|
|
4
|
-
// It used to write its unminified dev output straight over the committed
|
|
5
|
-
// release artifacts — `dist/client.bundle.js` at 14 MB where 2 MB ships — so
|
|
6
|
-
// merely running the test suite staged a broken release, silently, with no
|
|
7
|
-
// server running and no obvious culprit. CLAUDE.md carried this for a while as
|
|
8
|
-
// "observed, root cause unconfirmed". This test was the cause.
|
|
9
|
-
|
|
10
|
-
import { describe, expect, test } from 'bun:test';
|
|
11
|
-
import { mkdtempSync, rmSync } from 'node:fs';
|
|
12
|
-
import { tmpdir } from 'node:os';
|
|
13
|
-
import { join } from 'node:path';
|
|
14
|
-
import { spawnSync } from 'bun';
|
|
15
|
-
|
|
16
|
-
describe('client bundle', () => {
|
|
17
|
-
// A whole client build, so it sits right on bun's 5s default and tips over
|
|
18
|
-
// whenever the machine is busy. Given headroom explicitly rather than left as
|
|
19
|
-
// a flake — the thing under test is the bundle, not the wall clock.
|
|
20
|
-
test('build.ts produces a parsable IIFE bundle that ships React', async () => {
|
|
21
|
-
const here = join(import.meta.dir, '..');
|
|
22
|
-
const out = mkdtempSync(join(tmpdir(), 'maude-bundle-smoke-'));
|
|
23
|
-
try {
|
|
24
|
-
const result = spawnSync({
|
|
25
|
-
cmd: ['bun', 'run', join(here, 'build.ts')],
|
|
26
|
-
cwd: here,
|
|
27
|
-
stdout: 'pipe',
|
|
28
|
-
stderr: 'pipe',
|
|
29
|
-
env: { ...process.env, NODE_ENV: 'development', MAUDE_DIST_DIR: out },
|
|
30
|
-
});
|
|
31
|
-
expect(result.exitCode).toBe(0);
|
|
32
|
-
|
|
33
|
-
const bundle = Bun.file(join(out, 'client.bundle.js'));
|
|
34
|
-
expect(await bundle.exists()).toBe(true);
|
|
35
|
-
expect(bundle.size).toBeGreaterThan(50_000);
|
|
36
|
-
|
|
37
|
-
const src = await bundle.text();
|
|
38
|
-
// React 19 internal marker — exact constants change across minor versions,
|
|
39
|
-
// so we look for a stable substring on the IIFE shell + a known React API.
|
|
40
|
-
expect(src.includes('createRoot') || src.includes('react-dom')).toBe(true);
|
|
41
|
-
expect(src.includes('useState') || src.includes('react')).toBe(true);
|
|
42
|
-
} finally {
|
|
43
|
-
rmSync(out, { recursive: true, force: true });
|
|
44
|
-
}
|
|
45
|
-
}, 60_000);
|
|
46
|
-
});
|