@hraness/slopcamera 3.2.4
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/LICENSE +21 -0
- package/NOTICE.md +60 -0
- package/PRIVACY.md +46 -0
- package/README.md +311 -0
- package/SECURITY.md +21 -0
- package/apps/desktop/README.md +966 -0
- package/apps/desktop/analysis/protocol.ts +223 -0
- package/apps/desktop/application/capability-binding.ts +594 -0
- package/apps/desktop/application/context.ts +97 -0
- package/apps/desktop/application/creative-iteration.ts +1385 -0
- package/apps/desktop/application/default-registry.ts +146 -0
- package/apps/desktop/application/directing-contract.ts +218 -0
- package/apps/desktop/application/directing-plan.ts +178 -0
- package/apps/desktop/application/errors.ts +18 -0
- package/apps/desktop/application/gateway-port.ts +867 -0
- package/apps/desktop/application/html-overlay-browser-runtime.ts +687 -0
- package/apps/desktop/application/html-overlay-integrity.ts +337 -0
- package/apps/desktop/application/html-overlay-renderer.ts +45 -0
- package/apps/desktop/application/index.ts +13 -0
- package/apps/desktop/application/operation-completion-checkpoint.ts +333 -0
- package/apps/desktop/application/operation-effects.ts +94 -0
- package/apps/desktop/application/operation.ts +224 -0
- package/apps/desktop/application/operations/analysis/faces.ts +244 -0
- package/apps/desktop/application/operations/analysis/music.ts +248 -0
- package/apps/desktop/application/operations/analysis/project-auto-zooms.ts +302 -0
- package/apps/desktop/application/operations/analysis/project-inactivity.ts +381 -0
- package/apps/desktop/application/operations/analysis/scenes.ts +272 -0
- package/apps/desktop/application/operations/analysis/shared.ts +202 -0
- package/apps/desktop/application/operations/derive/edit-batch.ts +967 -0
- package/apps/desktop/application/operations/derive/follow-faces.ts +454 -0
- package/apps/desktop/application/operations/edit/bind-candidate-revision.ts +91 -0
- package/apps/desktop/application/operations/edit/create-candidate-revision.ts +377 -0
- package/apps/desktop/application/operations/edit/create-revision.ts +255 -0
- package/apps/desktop/application/operations/edit/freeze-revision.ts +182 -0
- package/apps/desktop/application/operations/gateway/image.ts +41 -0
- package/apps/desktop/application/operations/gateway/index.ts +17 -0
- package/apps/desktop/application/operations/gateway/shared.ts +122 -0
- package/apps/desktop/application/operations/gateway/speech.ts +41 -0
- package/apps/desktop/application/operations/gateway/transcription.ts +42 -0
- package/apps/desktop/application/operations/gateway/video.ts +41 -0
- package/apps/desktop/application/operations/index.ts +31 -0
- package/apps/desktop/application/operations/iteration/create-candidate.ts +240 -0
- package/apps/desktop/application/operations/iteration/create-matrix.ts +117 -0
- package/apps/desktop/application/operations/iteration/select.ts +155 -0
- package/apps/desktop/application/operations/iteration/shared.ts +250 -0
- package/apps/desktop/application/operations/media/audio-effects.ts +376 -0
- package/apps/desktop/application/operations/media/binding.ts +79 -0
- package/apps/desktop/application/operations/media/capabilities.ts +112 -0
- package/apps/desktop/application/operations/media/color-grade.ts +343 -0
- package/apps/desktop/application/operations/media/html-overlay.ts +802 -0
- package/apps/desktop/application/operations/media/index.ts +8 -0
- package/apps/desktop/application/operations/media/ingest.ts +303 -0
- package/apps/desktop/application/operations/media/overlay.ts +729 -0
- package/apps/desktop/application/operations/media/shared.ts +845 -0
- package/apps/desktop/application/operations/media/transform-platform.ts +120 -0
- package/apps/desktop/application/operations/project/commit-edits.ts +2054 -0
- package/apps/desktop/application/operations/project/promote-selection.ts +495 -0
- package/apps/desktop/application/operations/project/snapshot.ts +95 -0
- package/apps/desktop/application/operations/recording/index.ts +5 -0
- package/apps/desktop/application/operations/recording/pause.ts +34 -0
- package/apps/desktop/application/operations/recording/resume.ts +34 -0
- package/apps/desktop/application/operations/recording/shared.ts +199 -0
- package/apps/desktop/application/operations/recording/start.ts +169 -0
- package/apps/desktop/application/operations/recording/stop.ts +34 -0
- package/apps/desktop/application/operations/render/bind-candidate-output.ts +324 -0
- package/apps/desktop/application/operations/render/materialize-selection.ts +322 -0
- package/apps/desktop/application/operations/render/project-caption-plan.ts +287 -0
- package/apps/desktop/application/operations/render/project-plan.ts +939 -0
- package/apps/desktop/application/operations/render/project-spatial-receipt.ts +70 -0
- package/apps/desktop/application/operations/render/project.ts +1922 -0
- package/apps/desktop/application/operations/shared.ts +67 -0
- package/apps/desktop/application/operations/slopcamera-portable.ts +1188 -0
- package/apps/desktop/application/operations/slopcamera-visuals.ts +784 -0
- package/apps/desktop/application/operations/spatial-project.ts +209 -0
- package/apps/desktop/application/operations/spatial-render.ts +415 -0
- package/apps/desktop/application/operations/spatial-scene.ts +97 -0
- package/apps/desktop/application/operations/studio.ts +143 -0
- package/apps/desktop/application/output-publication-lease.ts +163 -0
- package/apps/desktop/application/project-publication-lease.ts +233 -0
- package/apps/desktop/application/project-store.ts +196 -0
- package/apps/desktop/application/receipts.ts +533 -0
- package/apps/desktop/application/registry.ts +182 -0
- package/apps/desktop/application/spatial-assets.ts +675 -0
- package/apps/desktop/application/spatial-compositor-cadence.ts +23 -0
- package/apps/desktop/application/spatial-project-authority.ts +35 -0
- package/apps/desktop/application/spatial-project-lease.ts +126 -0
- package/apps/desktop/application/spatial-project-render-preparation.ts +225 -0
- package/apps/desktop/application/spatial-project-store.ts +286 -0
- package/apps/desktop/application/spatial-render-projection.ts +201 -0
- package/apps/desktop/application/spatial-render.ts +677 -0
- package/apps/desktop/application/spatial-spz.ts +44 -0
- package/apps/desktop/application/spatial-world-import.ts +135 -0
- package/apps/desktop/application/spatial-world-provenance.ts +46 -0
- package/apps/desktop/application/studio-port.ts +58 -0
- package/apps/desktop/application/verified-receipt-reconciliation.ts +1574 -0
- package/apps/desktop/capture/protocol.ts +689 -0
- package/apps/desktop/cli/analyzer.ts +406 -0
- package/apps/desktop/cli/args.ts +3181 -0
- package/apps/desktop/cli/asset-ingest.ts +747 -0
- package/apps/desktop/cli/atomic-render-effects.ts +210 -0
- package/apps/desktop/cli/atomic-render-platform.ts +205 -0
- package/apps/desktop/cli/atomic-render.ts +50 -0
- package/apps/desktop/cli/audio-analysis.ts +252 -0
- package/apps/desktop/cli/bounded-map.ts +39 -0
- package/apps/desktop/cli/bundle-service.ts +256 -0
- package/apps/desktop/cli/capabilities.ts +212 -0
- package/apps/desktop/cli/capture-bundle.ts +1117 -0
- package/apps/desktop/cli/command-host-resources.ts +251 -0
- package/apps/desktop/cli/commands.ts +6836 -0
- package/apps/desktop/cli/directing-blob.ts +291 -0
- package/apps/desktop/cli/directing-media.ts +517 -0
- package/apps/desktop/cli/directing-quote.ts +89 -0
- package/apps/desktop/cli/directing-service.ts +288 -0
- package/apps/desktop/cli/emoji-assets.ts +538 -0
- package/apps/desktop/cli/errors.ts +29 -0
- package/apps/desktop/cli/face-analysis-service.ts +940 -0
- package/apps/desktop/cli/filter-script.ts +76 -0
- package/apps/desktop/cli/gateway-application-port.ts +1142 -0
- package/apps/desktop/cli/gateway-credential.ts +81 -0
- package/apps/desktop/cli/gateway-media-artifacts.ts +723 -0
- package/apps/desktop/cli/gateway-media-catalog.ts +922 -0
- package/apps/desktop/cli/gateway-media-service.ts +3106 -0
- package/apps/desktop/cli/gateway-media-signature.ts +122 -0
- package/apps/desktop/cli/gateway-provider-options.ts +562 -0
- package/apps/desktop/cli/gateway-scene-provider.ts +391 -0
- package/apps/desktop/cli/help.ts +501 -0
- package/apps/desktop/cli/html-overlay-gpu.ts +97 -0
- package/apps/desktop/cli/html-overlay-renderer.ts +2478 -0
- package/apps/desktop/cli/io.ts +290 -0
- package/apps/desktop/cli/main.ts +123 -0
- package/apps/desktop/cli/media-effects-service.ts +885 -0
- package/apps/desktop/cli/media-ingest-model.ts +364 -0
- package/apps/desktop/cli/media-ingest-platform.ts +227 -0
- package/apps/desktop/cli/media-ingest-program.ts +200 -0
- package/apps/desktop/cli/media-ingest.ts +30 -0
- package/apps/desktop/cli/music-analysis-service.ts +434 -0
- package/apps/desktop/cli/mutation-lock.ts +530 -0
- package/apps/desktop/cli/native-assets.d.ts +12 -0
- package/apps/desktop/cli/native-media-runtime.macos.ts +7 -0
- package/apps/desktop/cli/overlay-playback.ts +177 -0
- package/apps/desktop/cli/paths.ts +265 -0
- package/apps/desktop/cli/portable-surface.ts +242 -0
- package/apps/desktop/cli/project-camera-receipt.ts +112 -0
- package/apps/desktop/cli/project-face-camera.ts +394 -0
- package/apps/desktop/cli/project-inactivity-service.ts +552 -0
- package/apps/desktop/cli/project-media-integrity.ts +121 -0
- package/apps/desktop/cli/project-renderer.ts +1526 -0
- package/apps/desktop/cli/project-service.ts +519 -0
- package/apps/desktop/cli/project-state-transaction.ts +520 -0
- package/apps/desktop/cli/query-limits.ts +1 -0
- package/apps/desktop/cli/recording-controller.ts +1567 -0
- package/apps/desktop/cli/recording-daemon.ts +931 -0
- package/apps/desktop/cli/recording-ref.ts +56 -0
- package/apps/desktop/cli/renamed-environment.ts +6 -0
- package/apps/desktop/cli/renderer.ts +1439 -0
- package/apps/desktop/cli/run-cli-test-helper.ts +60 -0
- package/apps/desktop/cli/scene-analysis-service.ts +879 -0
- package/apps/desktop/cli/spatial-compositor-verifier.ts +213 -0
- package/apps/desktop/cli/spatial-project-service.ts +90 -0
- package/apps/desktop/cli/spatial-scene-service.ts +105 -0
- package/apps/desktop/cli/spatial-world-service.ts +39 -0
- package/apps/desktop/cli/speech-analysis-service.ts +682 -0
- package/apps/desktop/cli/studio-assemble.ts +58 -0
- package/apps/desktop/cli/studio-command.ts +60 -0
- package/apps/desktop/cli/studio-custody.ts +60 -0
- package/apps/desktop/cli/studio-encode.ts +216 -0
- package/apps/desktop/cli/studio-exr.ts +52 -0
- package/apps/desktop/cli/studio-files.ts +179 -0
- package/apps/desktop/cli/studio-output-validation.ts +162 -0
- package/apps/desktop/cli/studio-process.ts +145 -0
- package/apps/desktop/cli/studio-runtime.ts +77 -0
- package/apps/desktop/cli/studio-scaffold.ts +70 -0
- package/apps/desktop/cli/studio-service.ts +301 -0
- package/apps/desktop/cli/studio-spatial-asset.ts +248 -0
- package/apps/desktop/cli/studio-template-names.ts +3 -0
- package/apps/desktop/cli/studio-workflow.ts +19 -0
- package/apps/desktop/cli/time.ts +25 -0
- package/apps/desktop/cli/workflow-code.ts +584 -0
- package/apps/desktop/cli/workflow-runs.ts +576 -0
- package/apps/desktop/code/advanced.ts +139 -0
- package/apps/desktop/code/application-node-planner.ts +1288 -0
- package/apps/desktop/code/compiler.ts +329 -0
- package/apps/desktop/code/contracts.ts +13 -0
- package/apps/desktop/code/creative-recipes.ts +94 -0
- package/apps/desktop/code/define-workflow.ts +115 -0
- package/apps/desktop/code/editing.ts +284 -0
- package/apps/desktop/code/file-candidate-provenance.ts +420 -0
- package/apps/desktop/code/file-candidate.ts +69 -0
- package/apps/desktop/code/graph-builder.ts +10 -0
- package/apps/desktop/code/host-resource-policy.ts +55 -0
- package/apps/desktop/code/plan-contracts.ts +159 -0
- package/apps/desktop/code/planning.ts +361 -0
- package/apps/desktop/code/public.ts +134 -0
- package/apps/desktop/code/reference-projection.ts +62 -0
- package/apps/desktop/code/run-contracts.ts +466 -0
- package/apps/desktop/code/run-store.ts +2478 -0
- package/apps/desktop/code/runtime-identity.ts +328 -0
- package/apps/desktop/code/scheduler.ts +3067 -0
- package/apps/desktop/code/semantic-builder.ts +1436 -0
- package/apps/desktop/code/source-bundle.ts +1641 -0
- package/apps/desktop/code/source-typecheck.ts +211 -0
- package/apps/desktop/code/testing.ts +79 -0
- package/apps/desktop/code/worker-client.ts +1675 -0
- package/apps/desktop/code/worker-effects.ts +141 -0
- package/apps/desktop/code/worker-entry.ts +435 -0
- package/apps/desktop/code/worker-lease-guardian.ts +179 -0
- package/apps/desktop/code/worker-process-identity.ts +484 -0
- package/apps/desktop/code/worker-protocol.ts +211 -0
- package/apps/desktop/code/workflow-effects.ts +132 -0
- package/apps/desktop/contracts/analysis.ts +949 -0
- package/apps/desktop/contracts/edit.ts +576 -0
- package/apps/desktop/contracts/index.ts +9 -0
- package/apps/desktop/contracts/media-effects.ts +139 -0
- package/apps/desktop/contracts/project-render.ts +644 -0
- package/apps/desktop/contracts/project.ts +740 -0
- package/apps/desktop/contracts/recording.ts +1434 -0
- package/apps/desktop/contracts/render.ts +334 -0
- package/apps/desktop/contracts/runtime.ts +155 -0
- package/apps/desktop/contracts/spatial-compositor.ts +58 -0
- package/apps/desktop/contracts/spatial-project.ts +159 -0
- package/apps/desktop/contracts/spatial-world.ts +48 -0
- package/apps/desktop/contracts/video-effects.ts +120 -0
- package/apps/desktop/core/alignment-apply.ts +400 -0
- package/apps/desktop/core/audio-alignment.ts +466 -0
- package/apps/desktop/core/auto-zoom.ts +209 -0
- package/apps/desktop/core/canonical-json.ts +14 -0
- package/apps/desktop/core/emoji.ts +210 -0
- package/apps/desktop/core/events.ts +409 -0
- package/apps/desktop/core/face-framing.ts +533 -0
- package/apps/desktop/core/face-tracks.ts +326 -0
- package/apps/desktop/core/inactivity.ts +137 -0
- package/apps/desktop/core/index.ts +30 -0
- package/apps/desktop/core/intervals.ts +123 -0
- package/apps/desktop/core/montage.ts +236 -0
- package/apps/desktop/core/music-analysis.ts +518 -0
- package/apps/desktop/core/plan.ts +244 -0
- package/apps/desktop/core/project-auto-zoom.ts +75 -0
- package/apps/desktop/core/project-camera-spatial.ts +676 -0
- package/apps/desktop/core/project-camera.ts +403 -0
- package/apps/desktop/core/project-captions.ts +888 -0
- package/apps/desktop/core/project-inactivity.ts +125 -0
- package/apps/desktop/core/project-metadata.ts +927 -0
- package/apps/desktop/core/project-plan.ts +375 -0
- package/apps/desktop/core/project-render-plan.ts +303 -0
- package/apps/desktop/core/project-time.ts +469 -0
- package/apps/desktop/core/render-plan.ts +442 -0
- package/apps/desktop/core/scene-cache.ts +174 -0
- package/apps/desktop/core/scene-sampling.ts +448 -0
- package/apps/desktop/core/spatial-compositor.ts +22 -0
- package/apps/desktop/core/spatial-durability.ts +99 -0
- package/apps/desktop/core/spatial-project.ts +169 -0
- package/apps/desktop/core/spatial-shot-clock.ts +32 -0
- package/apps/desktop/core/speech-analysis.ts +333 -0
- package/apps/desktop/core/storage.ts +970 -0
- package/apps/desktop/core/time-map.ts +91 -0
- package/apps/desktop/core/time.ts +61 -0
- package/apps/desktop/core/video-effects.ts +660 -0
- package/apps/desktop/dist/cli/NebulaSans-Bold-26se8aek.otf +0 -0
- package/apps/desktop/dist/cli/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
- package/apps/desktop/dist/cli/NebulaSans-Book-5ax05zvn.woff2 +0 -0
- package/apps/desktop/dist/cli/NebulaSans-Book-8cenzchw.otf +0 -0
- package/apps/desktop/dist/cli/main.js +5086 -0
- package/apps/desktop/html-overlay/catalog.ts +171 -0
- package/apps/desktop/html-overlay/contracts.ts +372 -0
- package/apps/desktop/html-overlay/execution-profile.ts +77 -0
- package/apps/desktop/html-overlay/index.ts +98 -0
- package/apps/desktop/html-overlay/libraries.ts +263 -0
- package/apps/desktop/html-overlay/random.ts +49 -0
- package/apps/desktop/html-overlay/runtime.ts +813 -0
- package/apps/desktop/html-overlay/scaffolds.ts +792 -0
- package/apps/desktop/html-overlay/spark-runtime.ts +88 -0
- package/apps/desktop/html-overlay/spatial-splat-runtime.ts +60 -0
- package/apps/desktop/html-overlay/spatial.ts +720 -0
- package/apps/desktop/studio/assets/README.md +39 -0
- package/apps/desktop/studio/assets/contracts.ts +129 -0
- package/apps/desktop/studio/assets/network.ts +64 -0
- package/apps/desktop/studio/assets/poly-haven.ts +148 -0
- package/apps/desktop/studio/drivers/blender_driver.py +466 -0
- package/apps/desktop/studio/drivers/cadquery_driver.py +176 -0
- package/apps/desktop/studio/education/driver.py +308 -0
- package/apps/desktop/studio/education/lesson.py +180 -0
- package/apps/desktop/studio/education/toolkit.py +249 -0
- package/apps/desktop/workflows/chaptered-demo.ts +48 -0
- package/apps/desktop/workflows/creative-iteration.ts +107 -0
- package/apps/desktop/workflows/creative-selection.ts +92 -0
- package/apps/desktop/workflows/fragments.ts +486 -0
- package/apps/desktop/workflows/index.ts +93 -0
- package/apps/desktop/workflows/polished-screen-demo.ts +128 -0
- package/apps/desktop/workflows/scene-direction.ts +12 -0
- package/apps/desktop/workflows/social-variants.ts +75 -0
- package/apps/desktop/workflows/talking-head-cleanup.ts +53 -0
- package/dist/NebulaSans-Bold-26se8aek.otf +0 -0
- package/dist/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
- package/dist/NebulaSans-Book-5ax05zvn.woff2 +0 -0
- package/dist/NebulaSans-Book-8cenzchw.otf +0 -0
- package/dist/cli.js +479 -0
- package/dist/code/advanced.js +270 -0
- package/dist/code/index.js +2240 -0
- package/dist/generate.js +24 -0
- package/dist/host-resources.js +26 -0
- package/dist/index-42zsesc1.js +2257 -0
- package/dist/index-7308egqr.js +48 -0
- package/dist/index-8txs6fkn.js +712 -0
- package/dist/index-mcy8z0br.js +192 -0
- package/dist/index-p63wavx0.js +2029 -0
- package/dist/index-r7gdhmsp.js +424 -0
- package/dist/index-sh6xbav6.js +1219 -0
- package/dist/index-z1w83f81.js +4 -0
- package/dist/index-z7239b4h.js +2363 -0
- package/dist/index-zfeg9hmw.js +2013 -0
- package/dist/index.js +241 -0
- package/dist/operations.js +29 -0
- package/dist/skill-install-dh0ntqba.js +10 -0
- package/dist/vectorize/worker.js +142 -0
- package/dist/workflow.js +16 -0
- package/docs/README.md +38 -0
- package/docs/studio.md +215 -0
- package/examples/studio/blender/README.md +26 -0
- package/examples/studio/blender/character.py +152 -0
- package/examples/studio/blender/cloth.py +101 -0
- package/examples/studio/blender/color_chart.py +29 -0
- package/examples/studio/blender/fluid.py +78 -0
- package/examples/studio/blender/import_model.py +74 -0
- package/examples/studio/blender/product.py +53 -0
- package/examples/studio/blender/shaded_street.py +481 -0
- package/examples/studio/blender/studio_scene.py +147 -0
- package/examples/studio/cadquery/README.md +13 -0
- package/examples/studio/cadquery/bracket.py +36 -0
- package/examples/studio/cadquery/import_step.py +12 -0
- package/examples/studio/education/README.md +46 -0
- package/examples/studio/education/lesson.json +99 -0
- package/examples/studio/education/scene.py +7 -0
- package/examples/studio/hybrid-scene.ts +96 -0
- package/examples/studio/native-workflow.ts +14 -0
- package/examples/studio/vgpu/README.md +64 -0
- package/examples/studio/vgpu/heat-field.mjs +290 -0
- package/package.json +304 -0
- package/packages/scene/src/index.ts +1 -0
- package/packages/scene/src/scene.ts +215 -0
- package/schema/diagram.schema.json +334 -0
- package/skills/slopcamera/SKILL.md +45 -0
- package/skills/slopcamera/agents/openai.yaml +4 -0
- package/skills/slopcamera/references/customization.md +77 -0
- package/skills/slopcamera/references/diagrams.md +153 -0
- package/skills/slopcamera/references/directed-scenes.md +42 -0
- package/skills/slopcamera/references/directing-video.md +18 -0
- package/skills/slopcamera/references/educational-video.md +26 -0
- package/skills/slopcamera/references/gateway-media.md +134 -0
- package/skills/slopcamera/references/install.md +62 -0
- package/skills/slopcamera/references/native-studio.md +31 -0
- package/skills/slopcamera/references/reference-led-3d.md +140 -0
- package/skills/slopcamera/references/rubber-stamp-examples/poster-example-1.jpg +0 -0
- package/skills/slopcamera/references/rubber-stamp-examples/poster-example-2.jpg +0 -0
- package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-1.png +0 -0
- package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-2.png +0 -0
- package/skills/slopcamera/references/rubber-stamp-field-notes.md +144 -0
- package/skills/slopcamera/references/vectorization.md +29 -0
- package/skills/slopcamera/references/video-projects.md +187 -0
- package/skills/slopcamera/references/visual-communication.md +120 -0
- package/skills/slopcamera/references/workflows-sdk.md +45 -0
- package/skills/slopcamera/scripts/compose-rubber-stamp-field-note.ts +229 -0
- package/src/artifacts.ts +106 -0
- package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
- package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
- package/src/assets/fonts/nebula-sans/PROVENANCE.md +21 -0
- package/src/assets.d.ts +9 -0
- package/src/cli.ts +601 -0
- package/src/cloud-errors.ts +26 -0
- package/src/code/advanced.ts +10 -0
- package/src/code/boundary.ts +21 -0
- package/src/code/canonical-json.ts +129 -0
- package/src/code/compiler.ts +953 -0
- package/src/code/contracts.ts +576 -0
- package/src/code/define-workflow.ts +334 -0
- package/src/code/errors.ts +41 -0
- package/src/code/graph-builder.ts +945 -0
- package/src/code/index.ts +121 -0
- package/src/code/json-snapshot.ts +899 -0
- package/src/code/json-utf8.ts +88 -0
- package/src/code/portable-builder.ts +136 -0
- package/src/code/projection.ts +378 -0
- package/src/code/public-operations.ts +450 -0
- package/src/code/runtime.ts +580 -0
- package/src/code/sha256.ts +36 -0
- package/src/config.ts +180 -0
- package/src/desktop.ts +253 -0
- package/src/fs.ts +10 -0
- package/src/generate.ts +704 -0
- package/src/host-resource-posix.ts +161 -0
- package/src/host-resources.ts +1749 -0
- package/src/icons.ts +58 -0
- package/src/index.ts +200 -0
- package/src/label-layout.ts +130 -0
- package/src/layout.ts +413 -0
- package/src/lint.ts +127 -0
- package/src/mcp/boundary.ts +354 -0
- package/src/mcp/index.ts +33 -0
- package/src/mcp/server.ts +272 -0
- package/src/mcp/tools.ts +963 -0
- package/src/mcp/types.ts +71 -0
- package/src/operations.ts +775 -0
- package/src/parse.ts +861 -0
- package/src/process-environment.ts +17 -0
- package/src/render.ts +437 -0
- package/src/skill-install.ts +49 -0
- package/src/spatial-scene/authoring.ts +27 -0
- package/src/spatial-scene/camera-track.ts +83 -0
- package/src/spatial-scene/contracts.ts +277 -0
- package/src/spatial-scene/evaluate.ts +145 -0
- package/src/spatial-scene/gltf.ts +558 -0
- package/src/spatial-scene/identity.ts +260 -0
- package/src/spatial-scene/index.ts +10 -0
- package/src/spatial-scene/inspect.ts +68 -0
- package/src/spatial-scene/math.ts +280 -0
- package/src/spatial-scene/patch.ts +131 -0
- package/src/spatial-scene/time.ts +53 -0
- package/src/studio/contracts.ts +129 -0
- package/src/studio/index.ts +2 -0
- package/src/studio/plan.ts +156 -0
- package/src/studio/shared.ts +41 -0
- package/src/theme.ts +68 -0
- package/src/tldr.ts +474 -0
- package/src/types.ts +212 -0
- package/src/vectorize/archive.ts +101 -0
- package/src/vectorize/command.ts +797 -0
- package/src/vectorize/index.ts +27 -0
- package/src/vectorize/limits.ts +61 -0
- package/src/vectorize/metrics.ts +351 -0
- package/src/vectorize/pixels.ts +323 -0
- package/src/vectorize/supervisor.ts +339 -0
- package/src/vectorize/svg.ts +188 -0
- package/src/vectorize/tool.ts +542 -0
- package/src/vectorize/types.ts +113 -0
- package/src/vectorize/vectorize.ts +634 -0
- package/src/vectorize/worker-protocol.ts +40 -0
- package/src/vectorize/worker.ts +209 -0
- package/src/version.ts +1 -0
- package/src/workflow.ts +409 -0
package/src/parse.ts
ADDED
|
@@ -0,0 +1,861 @@
|
|
|
1
|
+
import {
|
|
2
|
+
diagramVersion,
|
|
3
|
+
type Anchor,
|
|
4
|
+
type BoxLabelRow,
|
|
5
|
+
type BoxShape,
|
|
6
|
+
type DiagramEdge,
|
|
7
|
+
type DiagramFontFamily,
|
|
8
|
+
type DiagramSource,
|
|
9
|
+
type DiagramShape,
|
|
10
|
+
type DiagramSpec,
|
|
11
|
+
type LineShape,
|
|
12
|
+
type StackAlign,
|
|
13
|
+
type StackDiagramEdge,
|
|
14
|
+
type StackDiagramSource,
|
|
15
|
+
type StackDirection,
|
|
16
|
+
type StackLayout,
|
|
17
|
+
type StackShape,
|
|
18
|
+
type TextShape,
|
|
19
|
+
type TextWeight,
|
|
20
|
+
type Tone,
|
|
21
|
+
} from "./types.js"
|
|
22
|
+
import { resolveDiagramSource } from "./layout.js"
|
|
23
|
+
|
|
24
|
+
const tones = new Set<Tone>([
|
|
25
|
+
"neutral",
|
|
26
|
+
"blue",
|
|
27
|
+
"orange",
|
|
28
|
+
"green",
|
|
29
|
+
"red",
|
|
30
|
+
"purple",
|
|
31
|
+
"yellow",
|
|
32
|
+
])
|
|
33
|
+
const anchors = new Set<Anchor>(["auto", "top", "right", "bottom", "left"])
|
|
34
|
+
const fontFamilies = new Set<DiagramFontFamily>(["default", "mono"])
|
|
35
|
+
const stackDirections = new Set<StackDirection>(["horizontal", "vertical"])
|
|
36
|
+
const stackAlignments = new Set<StackAlign>(["start", "center", "end"])
|
|
37
|
+
const idPattern = /^[A-Za-z0-9][A-Za-z0-9_-]*$/
|
|
38
|
+
const namePattern = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
|
|
39
|
+
|
|
40
|
+
export class DiagramValidationError extends Error {
|
|
41
|
+
readonly issues: readonly string[]
|
|
42
|
+
|
|
43
|
+
constructor(issues: readonly string[]) {
|
|
44
|
+
super(`Invalid diagram specification:\n${issues.map((issue) => `- ${issue}`).join("\n")}`)
|
|
45
|
+
this.name = "DiagramValidationError"
|
|
46
|
+
this.issues = issues
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
51
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function readString(
|
|
55
|
+
record: Record<string, unknown>,
|
|
56
|
+
key: string,
|
|
57
|
+
at: string,
|
|
58
|
+
issues: string[],
|
|
59
|
+
): string | undefined {
|
|
60
|
+
const value = record[key]
|
|
61
|
+
if (typeof value !== "string") {
|
|
62
|
+
issues.push(`${at}.${key} must be a string`)
|
|
63
|
+
return undefined
|
|
64
|
+
}
|
|
65
|
+
return value
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function readOptionalString(
|
|
69
|
+
record: Record<string, unknown>,
|
|
70
|
+
key: string,
|
|
71
|
+
at: string,
|
|
72
|
+
issues: string[],
|
|
73
|
+
): string | undefined {
|
|
74
|
+
const value = record[key]
|
|
75
|
+
if (value === undefined) return undefined
|
|
76
|
+
if (typeof value !== "string") {
|
|
77
|
+
issues.push(`${at}.${key} must be a string when present`)
|
|
78
|
+
return undefined
|
|
79
|
+
}
|
|
80
|
+
return value
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function readNumber(
|
|
84
|
+
record: Record<string, unknown>,
|
|
85
|
+
key: string,
|
|
86
|
+
at: string,
|
|
87
|
+
issues: string[],
|
|
88
|
+
options: { readonly positive?: boolean; readonly nonNegative?: boolean } = {},
|
|
89
|
+
): number | undefined {
|
|
90
|
+
const value = record[key]
|
|
91
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
92
|
+
issues.push(`${at}.${key} must be a finite number`)
|
|
93
|
+
return undefined
|
|
94
|
+
}
|
|
95
|
+
if (options.positive && value <= 0) {
|
|
96
|
+
issues.push(`${at}.${key} must be greater than zero`)
|
|
97
|
+
return undefined
|
|
98
|
+
}
|
|
99
|
+
if (options.nonNegative && value < 0) {
|
|
100
|
+
issues.push(`${at}.${key} must be zero or greater`)
|
|
101
|
+
return undefined
|
|
102
|
+
}
|
|
103
|
+
return value
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function readOptionalNumber(
|
|
107
|
+
record: Record<string, unknown>,
|
|
108
|
+
key: string,
|
|
109
|
+
at: string,
|
|
110
|
+
issues: string[],
|
|
111
|
+
options: { readonly positive?: boolean; readonly nonNegative?: boolean } = {},
|
|
112
|
+
): number | undefined {
|
|
113
|
+
if (record[key] === undefined) return undefined
|
|
114
|
+
return readNumber(record, key, at, issues, options)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function readOptionalTone(
|
|
118
|
+
record: Record<string, unknown>,
|
|
119
|
+
key: string,
|
|
120
|
+
at: string,
|
|
121
|
+
issues: string[],
|
|
122
|
+
): Tone | undefined {
|
|
123
|
+
const value = record[key]
|
|
124
|
+
if (value === undefined) return undefined
|
|
125
|
+
if (typeof value !== "string" || !tones.has(value as Tone)) {
|
|
126
|
+
issues.push(`${at}.${key} must be one of ${[...tones].join(", ")}`)
|
|
127
|
+
return undefined
|
|
128
|
+
}
|
|
129
|
+
return value as Tone
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function readOptionalFontFamily(
|
|
133
|
+
record: Record<string, unknown>,
|
|
134
|
+
key: string,
|
|
135
|
+
at: string,
|
|
136
|
+
issues: string[],
|
|
137
|
+
): DiagramFontFamily | undefined {
|
|
138
|
+
const value = record[key]
|
|
139
|
+
if (value === undefined) return undefined
|
|
140
|
+
if (typeof value !== "string" || !fontFamilies.has(value as DiagramFontFamily)) {
|
|
141
|
+
issues.push(`${at}.${key} must be default or mono`)
|
|
142
|
+
return undefined
|
|
143
|
+
}
|
|
144
|
+
return value as DiagramFontFamily
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function readOptionalWeight(
|
|
148
|
+
record: Record<string, unknown>,
|
|
149
|
+
key: string,
|
|
150
|
+
at: string,
|
|
151
|
+
issues: string[],
|
|
152
|
+
): TextWeight | undefined {
|
|
153
|
+
const value = record[key]
|
|
154
|
+
if (value === undefined) return undefined
|
|
155
|
+
if (typeof value !== "number" || ![400, 500, 600, 700].includes(value)) {
|
|
156
|
+
issues.push(`${at}.${key} must be 400, 500, 600, or 700`)
|
|
157
|
+
return undefined
|
|
158
|
+
}
|
|
159
|
+
return value as TextWeight
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function parseLabelRows(
|
|
163
|
+
value: unknown,
|
|
164
|
+
at: string,
|
|
165
|
+
issues: string[],
|
|
166
|
+
): readonly [BoxLabelRow, ...BoxLabelRow[]] | undefined {
|
|
167
|
+
if (value === undefined) return undefined
|
|
168
|
+
if (!Array.isArray(value)) {
|
|
169
|
+
issues.push(`${at}.labelRows must be an array when present`)
|
|
170
|
+
return undefined
|
|
171
|
+
}
|
|
172
|
+
if (value.length < 1 || value.length > 4) {
|
|
173
|
+
issues.push(`${at}.labelRows must contain between 1 and 4 rows`)
|
|
174
|
+
}
|
|
175
|
+
const rows: BoxLabelRow[] = []
|
|
176
|
+
for (const [index, candidate] of value.entries()) {
|
|
177
|
+
const rowAt = `${at}.labelRows[${index}]`
|
|
178
|
+
if (!isRecord(candidate)) {
|
|
179
|
+
issues.push(`${rowAt} must be an object`)
|
|
180
|
+
continue
|
|
181
|
+
}
|
|
182
|
+
validateKnownKeys(
|
|
183
|
+
candidate,
|
|
184
|
+
new Set(["text", "fontSize", "fontFamily", "weight"]),
|
|
185
|
+
rowAt,
|
|
186
|
+
issues,
|
|
187
|
+
)
|
|
188
|
+
const text = readString(candidate, "text", rowAt, issues)
|
|
189
|
+
const fontSize = readOptionalNumber(candidate, "fontSize", rowAt, issues, {
|
|
190
|
+
positive: true,
|
|
191
|
+
})
|
|
192
|
+
const fontFamily = readOptionalFontFamily(candidate, "fontFamily", rowAt, issues)
|
|
193
|
+
const weight = readOptionalWeight(candidate, "weight", rowAt, issues)
|
|
194
|
+
if (text !== undefined) {
|
|
195
|
+
rows.push({
|
|
196
|
+
text,
|
|
197
|
+
...(fontSize === undefined ? {} : { fontSize }),
|
|
198
|
+
...(fontFamily === undefined ? {} : { fontFamily }),
|
|
199
|
+
...(weight === undefined ? {} : { weight }),
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return rows.length === 0 ? undefined : (rows as [BoxLabelRow, ...BoxLabelRow[]])
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function validateKnownKeys(
|
|
207
|
+
record: Record<string, unknown>,
|
|
208
|
+
allowed: ReadonlySet<string>,
|
|
209
|
+
at: string,
|
|
210
|
+
issues: string[],
|
|
211
|
+
): void {
|
|
212
|
+
for (const key of Object.keys(record)) {
|
|
213
|
+
if (!allowed.has(key)) issues.push(`${at}.${key} is not supported`)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function parseBoxLabelContent(
|
|
218
|
+
value: Record<string, unknown>,
|
|
219
|
+
at: string,
|
|
220
|
+
issues: string[],
|
|
221
|
+
): Readonly<Record<string, unknown>> {
|
|
222
|
+
const label = readOptionalString(value, "label", at, issues)
|
|
223
|
+
const labelFontSize = readOptionalNumber(value, "labelFontSize", at, issues, {
|
|
224
|
+
positive: true,
|
|
225
|
+
})
|
|
226
|
+
const labelFontFamily = readOptionalFontFamily(value, "labelFontFamily", at, issues)
|
|
227
|
+
const labelWeight = readOptionalWeight(value, "labelWeight", at, issues)
|
|
228
|
+
const labelRows = parseLabelRows(value.labelRows, at, issues)
|
|
229
|
+
const labelRowGap = readOptionalNumber(value, "labelRowGap", at, issues, {
|
|
230
|
+
nonNegative: true,
|
|
231
|
+
})
|
|
232
|
+
const hasLegacyStyle =
|
|
233
|
+
value.labelFontSize !== undefined ||
|
|
234
|
+
value.labelFontFamily !== undefined ||
|
|
235
|
+
value.labelWeight !== undefined
|
|
236
|
+
if (value.label === undefined && hasLegacyStyle) {
|
|
237
|
+
issues.push(`${at} cannot style a label that is not present`)
|
|
238
|
+
}
|
|
239
|
+
if (value.labelRows !== undefined && value.label !== undefined) {
|
|
240
|
+
issues.push(`${at}.label and ${at}.labelRows are mutually exclusive`)
|
|
241
|
+
}
|
|
242
|
+
if (value.labelRows !== undefined && hasLegacyStyle) {
|
|
243
|
+
issues.push(`${at}.labelRows cannot be combined with legacy label styling`)
|
|
244
|
+
}
|
|
245
|
+
if (value.labelRows === undefined && value.labelRowGap !== undefined) {
|
|
246
|
+
issues.push(`${at}.labelRowGap requires labelRows`)
|
|
247
|
+
}
|
|
248
|
+
if (labelRows !== undefined) {
|
|
249
|
+
return {
|
|
250
|
+
labelRows,
|
|
251
|
+
...(labelRowGap === undefined ? {} : { labelRowGap }),
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (label !== undefined) {
|
|
255
|
+
return {
|
|
256
|
+
label,
|
|
257
|
+
...(labelFontSize === undefined ? {} : { labelFontSize }),
|
|
258
|
+
...(labelFontFamily === undefined ? {} : { labelFontFamily }),
|
|
259
|
+
...(labelWeight === undefined ? {} : { labelWeight }),
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return {}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function parseBase(
|
|
266
|
+
record: Record<string, unknown>,
|
|
267
|
+
at: string,
|
|
268
|
+
issues: string[],
|
|
269
|
+
): {
|
|
270
|
+
readonly id?: string
|
|
271
|
+
readonly x?: number
|
|
272
|
+
readonly y?: number
|
|
273
|
+
readonly tone?: Tone
|
|
274
|
+
readonly opacity?: number
|
|
275
|
+
} {
|
|
276
|
+
const id = readString(record, "id", at, issues)
|
|
277
|
+
if (id !== undefined && !idPattern.test(id)) {
|
|
278
|
+
issues.push(`${at}.id must contain only letters, numbers, underscores, or hyphens`)
|
|
279
|
+
}
|
|
280
|
+
const opacity = readOptionalNumber(record, "opacity", at, issues, { nonNegative: true })
|
|
281
|
+
if (opacity !== undefined && opacity > 1) issues.push(`${at}.opacity must not exceed 1`)
|
|
282
|
+
const x = readNumber(record, "x", at, issues)
|
|
283
|
+
const y = readNumber(record, "y", at, issues)
|
|
284
|
+
const tone = readOptionalTone(record, "tone", at, issues)
|
|
285
|
+
return {
|
|
286
|
+
...(id === undefined ? {} : { id }),
|
|
287
|
+
...(x === undefined ? {} : { x }),
|
|
288
|
+
...(y === undefined ? {} : { y }),
|
|
289
|
+
...(tone === undefined ? {} : { tone }),
|
|
290
|
+
...(opacity === undefined ? {} : { opacity }),
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function parseShape(value: unknown, index: number, issues: string[]): DiagramShape | null {
|
|
295
|
+
const at = `shapes[${index}]`
|
|
296
|
+
if (!isRecord(value)) {
|
|
297
|
+
issues.push(`${at} must be an object`)
|
|
298
|
+
return null
|
|
299
|
+
}
|
|
300
|
+
const type = readString(value, "type", at, issues)
|
|
301
|
+
const base = parseBase(value, at, issues)
|
|
302
|
+
if (base.id === undefined || base.x === undefined || base.y === undefined || type === undefined) {
|
|
303
|
+
return null
|
|
304
|
+
}
|
|
305
|
+
const requiredBase = {
|
|
306
|
+
id: base.id,
|
|
307
|
+
x: base.x,
|
|
308
|
+
y: base.y,
|
|
309
|
+
...(base.tone === undefined ? {} : { tone: base.tone }),
|
|
310
|
+
...(base.opacity === undefined ? {} : { opacity: base.opacity }),
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (type === "rect" || type === "ellipse") {
|
|
314
|
+
validateKnownKeys(
|
|
315
|
+
value,
|
|
316
|
+
new Set([
|
|
317
|
+
"id",
|
|
318
|
+
"type",
|
|
319
|
+
"x",
|
|
320
|
+
"y",
|
|
321
|
+
"tone",
|
|
322
|
+
"opacity",
|
|
323
|
+
"width",
|
|
324
|
+
"height",
|
|
325
|
+
"radius",
|
|
326
|
+
"label",
|
|
327
|
+
"labelFontSize",
|
|
328
|
+
"labelFontFamily",
|
|
329
|
+
"labelWeight",
|
|
330
|
+
"labelRows",
|
|
331
|
+
"labelRowGap",
|
|
332
|
+
"icon",
|
|
333
|
+
"iconSize",
|
|
334
|
+
"strokeWidth",
|
|
335
|
+
"fill",
|
|
336
|
+
]),
|
|
337
|
+
at,
|
|
338
|
+
issues,
|
|
339
|
+
)
|
|
340
|
+
const width = readNumber(value, "width", at, issues, { positive: true })
|
|
341
|
+
const height = readNumber(value, "height", at, issues, { positive: true })
|
|
342
|
+
const labelContent = parseBoxLabelContent(value, at, issues)
|
|
343
|
+
const fill = value.fill
|
|
344
|
+
if (fill !== undefined && typeof fill !== "boolean") issues.push(`${at}.fill must be a boolean`)
|
|
345
|
+
if (width === undefined || height === undefined) return null
|
|
346
|
+
return {
|
|
347
|
+
...requiredBase,
|
|
348
|
+
type,
|
|
349
|
+
width,
|
|
350
|
+
height,
|
|
351
|
+
...(readOptionalNumber(value, "radius", at, issues, { nonNegative: true }) === undefined
|
|
352
|
+
? {}
|
|
353
|
+
: { radius: value.radius as number }),
|
|
354
|
+
...labelContent,
|
|
355
|
+
...(readOptionalString(value, "icon", at, issues) === undefined
|
|
356
|
+
? {}
|
|
357
|
+
: { icon: value.icon as string }),
|
|
358
|
+
...(readOptionalNumber(value, "iconSize", at, issues, { positive: true }) === undefined
|
|
359
|
+
? {}
|
|
360
|
+
: { iconSize: value.iconSize as number }),
|
|
361
|
+
...(readOptionalNumber(value, "strokeWidth", at, issues, { nonNegative: true }) === undefined
|
|
362
|
+
? {}
|
|
363
|
+
: { strokeWidth: value.strokeWidth as number }),
|
|
364
|
+
...(typeof fill === "boolean" ? { fill } : {}),
|
|
365
|
+
} as BoxShape
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (type === "text") {
|
|
369
|
+
validateKnownKeys(
|
|
370
|
+
value,
|
|
371
|
+
new Set([
|
|
372
|
+
"id",
|
|
373
|
+
"type",
|
|
374
|
+
"x",
|
|
375
|
+
"y",
|
|
376
|
+
"tone",
|
|
377
|
+
"opacity",
|
|
378
|
+
"text",
|
|
379
|
+
"width",
|
|
380
|
+
"fontSize",
|
|
381
|
+
"fontFamily",
|
|
382
|
+
"weight",
|
|
383
|
+
"align",
|
|
384
|
+
]),
|
|
385
|
+
at,
|
|
386
|
+
issues,
|
|
387
|
+
)
|
|
388
|
+
const text = readString(value, "text", at, issues)
|
|
389
|
+
const fontFamily = readOptionalFontFamily(value, "fontFamily", at, issues)
|
|
390
|
+
const weight = readOptionalWeight(value, "weight", at, issues)
|
|
391
|
+
const align = value.align
|
|
392
|
+
if (align !== undefined && !["start", "middle", "end"].includes(align as string)) {
|
|
393
|
+
issues.push(`${at}.align must be start, middle, or end`)
|
|
394
|
+
}
|
|
395
|
+
if (text === undefined) return null
|
|
396
|
+
return {
|
|
397
|
+
...requiredBase,
|
|
398
|
+
type,
|
|
399
|
+
text,
|
|
400
|
+
...(readOptionalNumber(value, "width", at, issues, { positive: true }) === undefined
|
|
401
|
+
? {}
|
|
402
|
+
: { width: value.width as number }),
|
|
403
|
+
...(readOptionalNumber(value, "fontSize", at, issues, { positive: true }) === undefined
|
|
404
|
+
? {}
|
|
405
|
+
: { fontSize: value.fontSize as number }),
|
|
406
|
+
...(fontFamily === undefined ? {} : { fontFamily }),
|
|
407
|
+
...(weight === undefined ? {} : { weight }),
|
|
408
|
+
...(align === undefined ? {} : { align: align as "start" | "middle" | "end" }),
|
|
409
|
+
} satisfies TextShape
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (type === "line") {
|
|
413
|
+
validateKnownKeys(
|
|
414
|
+
value,
|
|
415
|
+
new Set(["id", "type", "x", "y", "tone", "opacity", "x2", "y2", "strokeWidth"]),
|
|
416
|
+
at,
|
|
417
|
+
issues,
|
|
418
|
+
)
|
|
419
|
+
const x2 = readNumber(value, "x2", at, issues)
|
|
420
|
+
const y2 = readNumber(value, "y2", at, issues)
|
|
421
|
+
if (x2 === undefined || y2 === undefined) return null
|
|
422
|
+
return {
|
|
423
|
+
...requiredBase,
|
|
424
|
+
type,
|
|
425
|
+
x2,
|
|
426
|
+
y2,
|
|
427
|
+
...(readOptionalNumber(value, "strokeWidth", at, issues, { positive: true }) === undefined
|
|
428
|
+
? {}
|
|
429
|
+
: { strokeWidth: value.strokeWidth as number }),
|
|
430
|
+
} satisfies LineShape
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
issues.push(`${at}.type must be rect, ellipse, text, or line`)
|
|
434
|
+
return null
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function parseStackShape(value: unknown, index: number, issues: string[]): StackShape | null {
|
|
438
|
+
const at = `shapes[${index}]`
|
|
439
|
+
if (!isRecord(value)) {
|
|
440
|
+
issues.push(`${at} must be an object`)
|
|
441
|
+
return null
|
|
442
|
+
}
|
|
443
|
+
validateKnownKeys(
|
|
444
|
+
value,
|
|
445
|
+
new Set([
|
|
446
|
+
"id",
|
|
447
|
+
"type",
|
|
448
|
+
"tone",
|
|
449
|
+
"opacity",
|
|
450
|
+
"width",
|
|
451
|
+
"height",
|
|
452
|
+
"radius",
|
|
453
|
+
"label",
|
|
454
|
+
"labelFontSize",
|
|
455
|
+
"labelFontFamily",
|
|
456
|
+
"labelWeight",
|
|
457
|
+
"labelRows",
|
|
458
|
+
"labelRowGap",
|
|
459
|
+
"icon",
|
|
460
|
+
"iconSize",
|
|
461
|
+
"strokeWidth",
|
|
462
|
+
"fill",
|
|
463
|
+
]),
|
|
464
|
+
at,
|
|
465
|
+
issues,
|
|
466
|
+
)
|
|
467
|
+
const id = readString(value, "id", at, issues)
|
|
468
|
+
if (id !== undefined && !idPattern.test(id)) {
|
|
469
|
+
issues.push(`${at}.id must contain only letters, numbers, underscores, or hyphens`)
|
|
470
|
+
}
|
|
471
|
+
const type = readString(value, "type", at, issues)
|
|
472
|
+
if (type !== undefined && type !== "rect" && type !== "ellipse") {
|
|
473
|
+
issues.push(`${at}.type must be rect or ellipse in a stack layout`)
|
|
474
|
+
}
|
|
475
|
+
const width = readNumber(value, "width", at, issues, { positive: true })
|
|
476
|
+
const height = readNumber(value, "height", at, issues, { positive: true })
|
|
477
|
+
const labelContent = parseBoxLabelContent(value, at, issues)
|
|
478
|
+
const tone = readOptionalTone(value, "tone", at, issues)
|
|
479
|
+
const opacity = readOptionalNumber(value, "opacity", at, issues, { nonNegative: true })
|
|
480
|
+
if (opacity !== undefined && opacity > 1) issues.push(`${at}.opacity must not exceed 1`)
|
|
481
|
+
const radius = readOptionalNumber(value, "radius", at, issues, { nonNegative: true })
|
|
482
|
+
const icon = readOptionalString(value, "icon", at, issues)
|
|
483
|
+
const iconSize = readOptionalNumber(value, "iconSize", at, issues, { positive: true })
|
|
484
|
+
const strokeWidth = readOptionalNumber(value, "strokeWidth", at, issues, {
|
|
485
|
+
nonNegative: true,
|
|
486
|
+
})
|
|
487
|
+
const fill = value.fill
|
|
488
|
+
if (fill !== undefined && typeof fill !== "boolean") issues.push(`${at}.fill must be a boolean`)
|
|
489
|
+
if (
|
|
490
|
+
id === undefined ||
|
|
491
|
+
(type !== "rect" && type !== "ellipse") ||
|
|
492
|
+
width === undefined ||
|
|
493
|
+
height === undefined
|
|
494
|
+
) {
|
|
495
|
+
return null
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
id,
|
|
499
|
+
type,
|
|
500
|
+
width,
|
|
501
|
+
height,
|
|
502
|
+
...(tone === undefined ? {} : { tone }),
|
|
503
|
+
...(opacity === undefined ? {} : { opacity }),
|
|
504
|
+
...(radius === undefined ? {} : { radius }),
|
|
505
|
+
...labelContent,
|
|
506
|
+
...(icon === undefined ? {} : { icon }),
|
|
507
|
+
...(iconSize === undefined ? {} : { iconSize }),
|
|
508
|
+
...(strokeWidth === undefined ? {} : { strokeWidth }),
|
|
509
|
+
...(typeof fill === "boolean" ? { fill } : {}),
|
|
510
|
+
} as StackShape
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
function parseEdge(value: unknown, index: number, issues: string[]): DiagramEdge | null {
|
|
514
|
+
const at = `edges[${index}]`
|
|
515
|
+
if (!isRecord(value)) {
|
|
516
|
+
issues.push(`${at} must be an object`)
|
|
517
|
+
return null
|
|
518
|
+
}
|
|
519
|
+
validateKnownKeys(
|
|
520
|
+
value,
|
|
521
|
+
new Set([
|
|
522
|
+
"id",
|
|
523
|
+
"from",
|
|
524
|
+
"to",
|
|
525
|
+
"label",
|
|
526
|
+
"tone",
|
|
527
|
+
"start",
|
|
528
|
+
"end",
|
|
529
|
+
"bend",
|
|
530
|
+
"arrowhead",
|
|
531
|
+
"startPosition",
|
|
532
|
+
"endPosition",
|
|
533
|
+
"labelFontSize",
|
|
534
|
+
"labelFontFamily",
|
|
535
|
+
"labelWeight",
|
|
536
|
+
"labelPosition",
|
|
537
|
+
"labelOffset",
|
|
538
|
+
]),
|
|
539
|
+
at,
|
|
540
|
+
issues,
|
|
541
|
+
)
|
|
542
|
+
const id = readString(value, "id", at, issues)
|
|
543
|
+
const from = readString(value, "from", at, issues)
|
|
544
|
+
const to = readString(value, "to", at, issues)
|
|
545
|
+
if (id !== undefined && !idPattern.test(id)) issues.push(`${at}.id has unsupported characters`)
|
|
546
|
+
const start = value.start
|
|
547
|
+
const end = value.end
|
|
548
|
+
if (start !== undefined && (typeof start !== "string" || !anchors.has(start as Anchor))) {
|
|
549
|
+
issues.push(`${at}.start must be auto, top, right, bottom, or left`)
|
|
550
|
+
}
|
|
551
|
+
if (end !== undefined && (typeof end !== "string" || !anchors.has(end as Anchor))) {
|
|
552
|
+
issues.push(`${at}.end must be auto, top, right, bottom, or left`)
|
|
553
|
+
}
|
|
554
|
+
const startPosition = readOptionalNumber(value, "startPosition", at, issues, {
|
|
555
|
+
nonNegative: true,
|
|
556
|
+
})
|
|
557
|
+
if (startPosition !== undefined && startPosition > 1) {
|
|
558
|
+
issues.push(`${at}.startPosition must not exceed 1`)
|
|
559
|
+
}
|
|
560
|
+
const endPosition = readOptionalNumber(value, "endPosition", at, issues, {
|
|
561
|
+
nonNegative: true,
|
|
562
|
+
})
|
|
563
|
+
if (endPosition !== undefined && endPosition > 1) {
|
|
564
|
+
issues.push(`${at}.endPosition must not exceed 1`)
|
|
565
|
+
}
|
|
566
|
+
const arrowhead = value.arrowhead
|
|
567
|
+
if (
|
|
568
|
+
arrowhead !== undefined &&
|
|
569
|
+
!["arrow", "triangle", "none"].includes(arrowhead as string)
|
|
570
|
+
) {
|
|
571
|
+
issues.push(`${at}.arrowhead must be arrow, triangle, or none`)
|
|
572
|
+
}
|
|
573
|
+
const label = readOptionalString(value, "label", at, issues)
|
|
574
|
+
const labelFontSize = readOptionalNumber(value, "labelFontSize", at, issues, {
|
|
575
|
+
positive: true,
|
|
576
|
+
})
|
|
577
|
+
const labelFontFamily = readOptionalFontFamily(value, "labelFontFamily", at, issues)
|
|
578
|
+
const labelWeight = readOptionalWeight(value, "labelWeight", at, issues)
|
|
579
|
+
const labelPosition = readOptionalNumber(value, "labelPosition", at, issues, {
|
|
580
|
+
nonNegative: true,
|
|
581
|
+
})
|
|
582
|
+
if (labelPosition !== undefined && labelPosition > 1) {
|
|
583
|
+
issues.push(`${at}.labelPosition must not exceed 1`)
|
|
584
|
+
}
|
|
585
|
+
const labelOffset = readOptionalNumber(value, "labelOffset", at, issues)
|
|
586
|
+
if (
|
|
587
|
+
value.label === undefined &&
|
|
588
|
+
[
|
|
589
|
+
value.labelFontSize,
|
|
590
|
+
value.labelFontFamily,
|
|
591
|
+
value.labelWeight,
|
|
592
|
+
value.labelPosition,
|
|
593
|
+
value.labelOffset,
|
|
594
|
+
].some((candidate) => candidate !== undefined)
|
|
595
|
+
) {
|
|
596
|
+
issues.push(`${at} cannot style or position a label that is not present`)
|
|
597
|
+
}
|
|
598
|
+
if (id === undefined || from === undefined || to === undefined) return null
|
|
599
|
+
return {
|
|
600
|
+
id,
|
|
601
|
+
from,
|
|
602
|
+
to,
|
|
603
|
+
...(label === undefined
|
|
604
|
+
? {}
|
|
605
|
+
: {
|
|
606
|
+
label,
|
|
607
|
+
...(labelFontSize === undefined ? {} : { labelFontSize }),
|
|
608
|
+
...(labelFontFamily === undefined ? {} : { labelFontFamily }),
|
|
609
|
+
...(labelWeight === undefined ? {} : { labelWeight }),
|
|
610
|
+
...(labelPosition === undefined ? {} : { labelPosition }),
|
|
611
|
+
...(labelOffset === undefined ? {} : { labelOffset }),
|
|
612
|
+
}),
|
|
613
|
+
...(readOptionalTone(value, "tone", at, issues) === undefined
|
|
614
|
+
? {}
|
|
615
|
+
: { tone: value.tone as Tone }),
|
|
616
|
+
...(start === undefined ? {} : { start: start as Anchor }),
|
|
617
|
+
...(end === undefined ? {} : { end: end as Anchor }),
|
|
618
|
+
...(startPosition === undefined ? {} : { startPosition }),
|
|
619
|
+
...(endPosition === undefined ? {} : { endPosition }),
|
|
620
|
+
...(readOptionalNumber(value, "bend", at, issues) === undefined
|
|
621
|
+
? {}
|
|
622
|
+
: { bend: value.bend as number }),
|
|
623
|
+
...(arrowhead === undefined
|
|
624
|
+
? {}
|
|
625
|
+
: { arrowhead: arrowhead as "arrow" | "triangle" | "none" }),
|
|
626
|
+
} as DiagramEdge
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function parseStackLayout(value: unknown, issues: string[]): StackLayout | null {
|
|
630
|
+
if (!isRecord(value)) {
|
|
631
|
+
issues.push("layout must be an object")
|
|
632
|
+
return null
|
|
633
|
+
}
|
|
634
|
+
validateKnownKeys(value, new Set(["type", "direction", "gap", "align"]), "layout", issues)
|
|
635
|
+
if (value.type !== "stack") issues.push("layout.type must be stack")
|
|
636
|
+
const direction = value.direction
|
|
637
|
+
if (
|
|
638
|
+
typeof direction !== "string" ||
|
|
639
|
+
!stackDirections.has(direction as StackDirection)
|
|
640
|
+
) {
|
|
641
|
+
issues.push("layout.direction must be horizontal or vertical")
|
|
642
|
+
}
|
|
643
|
+
const gap = readOptionalNumber(value, "gap", "layout", issues, { nonNegative: true })
|
|
644
|
+
const align = value.align
|
|
645
|
+
if (
|
|
646
|
+
align !== undefined &&
|
|
647
|
+
(typeof align !== "string" || !stackAlignments.has(align as StackAlign))
|
|
648
|
+
) {
|
|
649
|
+
issues.push("layout.align must be start, center, or end")
|
|
650
|
+
}
|
|
651
|
+
if (
|
|
652
|
+
value.type !== "stack" ||
|
|
653
|
+
typeof direction !== "string" ||
|
|
654
|
+
!stackDirections.has(direction as StackDirection)
|
|
655
|
+
) {
|
|
656
|
+
return null
|
|
657
|
+
}
|
|
658
|
+
return {
|
|
659
|
+
type: "stack",
|
|
660
|
+
direction: direction as StackDirection,
|
|
661
|
+
...(gap === undefined ? {} : { gap }),
|
|
662
|
+
...(align === undefined || !stackAlignments.has(align as StackAlign)
|
|
663
|
+
? {}
|
|
664
|
+
: { align: align as StackAlign }),
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
export function parseDiagramSource(value: unknown): DiagramSource {
|
|
669
|
+
const issues: string[] = []
|
|
670
|
+
if (!isRecord(value)) throw new DiagramValidationError(["root must be an object"])
|
|
671
|
+
const isStackSource = "layout" in value
|
|
672
|
+
validateKnownKeys(
|
|
673
|
+
value,
|
|
674
|
+
new Set([
|
|
675
|
+
"$schema",
|
|
676
|
+
"version",
|
|
677
|
+
"name",
|
|
678
|
+
"canvas",
|
|
679
|
+
"shapes",
|
|
680
|
+
"edges",
|
|
681
|
+
...(isStackSource ? ["layout"] : []),
|
|
682
|
+
]),
|
|
683
|
+
"root",
|
|
684
|
+
issues,
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
if (value.version !== diagramVersion) issues.push(`version must be ${diagramVersion}`)
|
|
688
|
+
const name = readString(value, "name", "root", issues)
|
|
689
|
+
if (name !== undefined && !namePattern.test(name)) {
|
|
690
|
+
issues.push("name must be lowercase kebab-case")
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const canvasValue = value.canvas
|
|
694
|
+
let canvas: DiagramSpec["canvas"] | null = null
|
|
695
|
+
if (!isRecord(canvasValue)) {
|
|
696
|
+
issues.push("canvas must be an object")
|
|
697
|
+
} else {
|
|
698
|
+
validateKnownKeys(canvasValue, new Set(["width", "height", "padding"]), "canvas", issues)
|
|
699
|
+
const width = readNumber(canvasValue, "width", "canvas", issues, { positive: true })
|
|
700
|
+
const height = readNumber(canvasValue, "height", "canvas", issues, { positive: true })
|
|
701
|
+
const padding = readOptionalNumber(canvasValue, "padding", "canvas", issues, {
|
|
702
|
+
nonNegative: true,
|
|
703
|
+
})
|
|
704
|
+
if (width !== undefined && height !== undefined) {
|
|
705
|
+
canvas = { width, height, ...(padding === undefined ? {} : { padding }) }
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
const layout = isStackSource ? parseStackLayout(value.layout, issues) : null
|
|
710
|
+
const shapesValue = value.shapes
|
|
711
|
+
const positionedShapes: DiagramShape[] = []
|
|
712
|
+
const stackShapes: StackShape[] = []
|
|
713
|
+
if (!Array.isArray(shapesValue)) {
|
|
714
|
+
issues.push("shapes must be an array")
|
|
715
|
+
} else {
|
|
716
|
+
if (isStackSource && (shapesValue.length < 1 || shapesValue.length > 9)) {
|
|
717
|
+
issues.push("stack layouts must contain between 1 and 9 shapes")
|
|
718
|
+
}
|
|
719
|
+
for (const [index, shape] of shapesValue.entries()) {
|
|
720
|
+
if (isStackSource) {
|
|
721
|
+
const parsed = parseStackShape(shape, index, issues)
|
|
722
|
+
if (parsed !== null) stackShapes.push(parsed)
|
|
723
|
+
} else {
|
|
724
|
+
const parsed = parseShape(shape, index, issues)
|
|
725
|
+
if (parsed !== null) positionedShapes.push(parsed)
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const edgesValue = value.edges
|
|
731
|
+
const edges: DiagramEdge[] = []
|
|
732
|
+
if (edgesValue !== undefined) {
|
|
733
|
+
if (!Array.isArray(edgesValue)) {
|
|
734
|
+
issues.push("edges must be an array when present")
|
|
735
|
+
} else {
|
|
736
|
+
for (const [index, edge] of edgesValue.entries()) {
|
|
737
|
+
const parsed = parseEdge(edge, index, issues)
|
|
738
|
+
if (parsed !== null) edges.push(parsed)
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
const shapes = isStackSource ? stackShapes : positionedShapes
|
|
744
|
+
const allIds = new Set<string>()
|
|
745
|
+
for (const [kind, records] of [
|
|
746
|
+
["shape", shapes],
|
|
747
|
+
["edge", edges],
|
|
748
|
+
] as const) {
|
|
749
|
+
for (const record of records) {
|
|
750
|
+
if (allIds.has(record.id)) issues.push(`${kind} id ${record.id} is duplicated`)
|
|
751
|
+
allIds.add(record.id)
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
const connectableIds = new Set(
|
|
755
|
+
shapes
|
|
756
|
+
.filter(
|
|
757
|
+
(shape): shape is BoxShape | StackShape =>
|
|
758
|
+
shape.type === "rect" || shape.type === "ellipse",
|
|
759
|
+
)
|
|
760
|
+
.map((shape) => shape.id),
|
|
761
|
+
)
|
|
762
|
+
for (const edge of edges) {
|
|
763
|
+
if (!connectableIds.has(edge.from)) issues.push(`edge ${edge.id} has unknown or non-connectable from id ${edge.from}`)
|
|
764
|
+
if (!connectableIds.has(edge.to)) issues.push(`edge ${edge.id} has unknown or non-connectable to id ${edge.to}`)
|
|
765
|
+
if (edge.from === edge.to) issues.push(`edge ${edge.id} cannot connect a shape to itself`)
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (isStackSource) {
|
|
769
|
+
const indexes = new Map(stackShapes.map((shape, index) => [shape.id, index]))
|
|
770
|
+
const connectedPairs = new Set<string>()
|
|
771
|
+
for (const edge of edges) {
|
|
772
|
+
const fromIndex = indexes.get(edge.from)
|
|
773
|
+
const toIndex = indexes.get(edge.to)
|
|
774
|
+
if (
|
|
775
|
+
fromIndex !== undefined &&
|
|
776
|
+
toIndex !== undefined &&
|
|
777
|
+
Math.abs(fromIndex - toIndex) !== 1
|
|
778
|
+
) {
|
|
779
|
+
issues.push(`edge ${edge.id} must connect adjacent stack shapes`)
|
|
780
|
+
}
|
|
781
|
+
if (fromIndex !== undefined && toIndex !== undefined && fromIndex !== toIndex) {
|
|
782
|
+
const pair = [fromIndex, toIndex].sort((left, right) => left - right).join(":")
|
|
783
|
+
if (connectedPairs.has(pair)) {
|
|
784
|
+
issues.push(`edge ${edge.id} duplicates a connection between the same stack shapes`)
|
|
785
|
+
}
|
|
786
|
+
connectedPairs.add(pair)
|
|
787
|
+
}
|
|
788
|
+
if (edge.start !== undefined && edge.start !== "auto") {
|
|
789
|
+
issues.push(`edge ${edge.id}.start must be auto or omitted in a stack layout`)
|
|
790
|
+
}
|
|
791
|
+
if (edge.end !== undefined && edge.end !== "auto") {
|
|
792
|
+
issues.push(`edge ${edge.id}.end must be auto or omitted in a stack layout`)
|
|
793
|
+
}
|
|
794
|
+
if (edge.bend !== undefined && edge.bend !== 0) {
|
|
795
|
+
issues.push(`edge ${edge.id}.bend must be 0 or omitted in a stack layout`)
|
|
796
|
+
}
|
|
797
|
+
if (edge.startPosition !== undefined) {
|
|
798
|
+
issues.push(`edge ${edge.id}.startPosition is not supported in a stack layout`)
|
|
799
|
+
}
|
|
800
|
+
if (edge.endPosition !== undefined) {
|
|
801
|
+
issues.push(`edge ${edge.id}.endPosition is not supported in a stack layout`)
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
if (
|
|
807
|
+
issues.length > 0 ||
|
|
808
|
+
name === undefined ||
|
|
809
|
+
canvas === null ||
|
|
810
|
+
(isStackSource && layout === null)
|
|
811
|
+
) {
|
|
812
|
+
throw new DiagramValidationError(issues)
|
|
813
|
+
}
|
|
814
|
+
const common = {
|
|
815
|
+
...("$schema" in value && typeof value.$schema === "string" ? { $schema: value.$schema } : {}),
|
|
816
|
+
version: diagramVersion,
|
|
817
|
+
name,
|
|
818
|
+
canvas,
|
|
819
|
+
} as const
|
|
820
|
+
if (isStackSource) {
|
|
821
|
+
const stackEdges: readonly StackDiagramEdge[] = edges.map((edge) => ({
|
|
822
|
+
id: edge.id,
|
|
823
|
+
from: edge.from,
|
|
824
|
+
to: edge.to,
|
|
825
|
+
...(edge.label === undefined ? {} : { label: edge.label }),
|
|
826
|
+
...(edge.tone === undefined ? {} : { tone: edge.tone }),
|
|
827
|
+
...(edge.start === "auto" ? { start: edge.start } : {}),
|
|
828
|
+
...(edge.end === "auto" ? { end: edge.end } : {}),
|
|
829
|
+
...(edge.bend === 0 ? { bend: edge.bend } : {}),
|
|
830
|
+
...(edge.arrowhead === undefined ? {} : { arrowhead: edge.arrowhead }),
|
|
831
|
+
...(edge.label === undefined
|
|
832
|
+
? {}
|
|
833
|
+
: {
|
|
834
|
+
...(edge.labelFontSize === undefined ? {} : { labelFontSize: edge.labelFontSize }),
|
|
835
|
+
...(edge.labelFontFamily === undefined
|
|
836
|
+
? {}
|
|
837
|
+
: { labelFontFamily: edge.labelFontFamily }),
|
|
838
|
+
...(edge.labelWeight === undefined ? {} : { labelWeight: edge.labelWeight }),
|
|
839
|
+
...(edge.labelPosition === undefined
|
|
840
|
+
? {}
|
|
841
|
+
: { labelPosition: edge.labelPosition }),
|
|
842
|
+
...(edge.labelOffset === undefined ? {} : { labelOffset: edge.labelOffset }),
|
|
843
|
+
}),
|
|
844
|
+
})) as readonly StackDiagramEdge[]
|
|
845
|
+
return {
|
|
846
|
+
...common,
|
|
847
|
+
layout: layout as StackLayout,
|
|
848
|
+
shapes: stackShapes,
|
|
849
|
+
...(edgesValue === undefined ? {} : { edges: stackEdges }),
|
|
850
|
+
} satisfies StackDiagramSource
|
|
851
|
+
}
|
|
852
|
+
return {
|
|
853
|
+
...common,
|
|
854
|
+
shapes: positionedShapes,
|
|
855
|
+
...(edgesValue === undefined ? {} : { edges }),
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
export function parseDiagramSpec(value: unknown): DiagramSpec {
|
|
860
|
+
return resolveDiagramSource(parseDiagramSource(value))
|
|
861
|
+
}
|