@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
|
@@ -0,0 +1,945 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthoredComputeIdentitySchema,
|
|
3
|
+
AuthoredGraphNodeV1Schema,
|
|
4
|
+
AuthoredWorkflowGraphV1Schema,
|
|
5
|
+
MAX_SERIALIZED_GRAPH_NODES,
|
|
6
|
+
MAX_SERIALIZED_NODE_DEPENDENCIES,
|
|
7
|
+
MAX_SERIALIZED_REF_PATH_SEGMENTS,
|
|
8
|
+
NodeKeySegmentSchema,
|
|
9
|
+
OperationDiscoverySchema,
|
|
10
|
+
SerializedRefV1Schema,
|
|
11
|
+
LEGACY_TRUSTED_COMPUTE_BRAND,
|
|
12
|
+
TRUSTED_COMPUTE_BRAND,
|
|
13
|
+
TRUSTED_COMPUTE_VERSION,
|
|
14
|
+
WORKFLOW_GRAPH_VERSION,
|
|
15
|
+
WORKFLOW_REF_BRAND,
|
|
16
|
+
WORKFLOW_REF_VERSION,
|
|
17
|
+
WorkflowIdentitySchema,
|
|
18
|
+
type AuthoredGraphNodeV1,
|
|
19
|
+
type AuthoredWorkflowGraphV1,
|
|
20
|
+
type AnyTrustedComputeDefinition,
|
|
21
|
+
type GraphInputValue,
|
|
22
|
+
type OperationContract,
|
|
23
|
+
type OperationDiscovery,
|
|
24
|
+
type OperationDiscoverySource,
|
|
25
|
+
type OperationInputValue,
|
|
26
|
+
type OperationKind,
|
|
27
|
+
type Ref,
|
|
28
|
+
type SerializedRefV1,
|
|
29
|
+
type TrustedComputeDefinition,
|
|
30
|
+
type WorkflowIdentity,
|
|
31
|
+
type WorkflowOutputBinding,
|
|
32
|
+
type WorkflowOutputValue,
|
|
33
|
+
type WorkflowRegistryProjection,
|
|
34
|
+
} from "./contracts.js"
|
|
35
|
+
import { parseCodeBoundary } from "./boundary.js"
|
|
36
|
+
import { SlopcameraCodeError } from "./errors.js"
|
|
37
|
+
import { deepFreezeJson } from "./json-snapshot.js"
|
|
38
|
+
import { boundedOperationDiscoveryList } from "./projection.js"
|
|
39
|
+
|
|
40
|
+
export type OperationDiscoveryProvider =
|
|
41
|
+
| OperationDiscoverySource
|
|
42
|
+
| Pick<WorkflowRegistryProjection, "discovery">
|
|
43
|
+
|
|
44
|
+
export interface OperationNodeOptions {
|
|
45
|
+
/** Adds causal dependencies without threading a producer value into input. */
|
|
46
|
+
readonly after?: Ref<unknown> | readonly Ref<unknown>[]
|
|
47
|
+
readonly label?: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface UntypedOperationRequest {
|
|
51
|
+
readonly input: unknown
|
|
52
|
+
readonly kind: OperationKind
|
|
53
|
+
readonly version: number
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface WorkflowFragment<Input, Output> {
|
|
57
|
+
build(builder: WorkflowGraphBuilder, input: Input): Output
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface BuilderState {
|
|
61
|
+
authoredValues: number
|
|
62
|
+
readonly computes: Map<string, AnyTrustedComputeDefinition>
|
|
63
|
+
readonly discovery: ReadonlyMap<string, OperationDiscovery>
|
|
64
|
+
readonly nodes: Map<string, AuthoredGraphNodeV1>
|
|
65
|
+
readonly references: WeakSet<object>
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface EncodedInput {
|
|
69
|
+
readonly dependencies: readonly string[]
|
|
70
|
+
readonly value: GraphInputValue
|
|
71
|
+
readonly values: number
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const MAX_AUTHORING_VALUE_DEPTH = 128
|
|
75
|
+
const MAX_AUTHORING_VALUES = 1_000_000
|
|
76
|
+
const NORMALIZED_LEGACY_COMPUTES = new WeakMap<
|
|
77
|
+
object,
|
|
78
|
+
AnyTrustedComputeDefinition
|
|
79
|
+
>()
|
|
80
|
+
|
|
81
|
+
interface EncodingBudget {
|
|
82
|
+
consumed: number
|
|
83
|
+
readonly maximum: number
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function consumeAuthoringValue(budget: EncodingBudget): void {
|
|
87
|
+
if (budget.consumed >= budget.maximum) {
|
|
88
|
+
throw new SlopcameraCodeError(
|
|
89
|
+
"invalid-data",
|
|
90
|
+
`Workflow authoring values exceed the ${String(MAX_AUTHORING_VALUES)} value limit.`,
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
budget.consumed += 1
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function consumeAuthoringValues(
|
|
97
|
+
budget: EncodingBudget,
|
|
98
|
+
additional: number,
|
|
99
|
+
): void {
|
|
100
|
+
requireAuthoringCapacity(budget, additional)
|
|
101
|
+
budget.consumed += additional
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function requireAuthoringCapacity(
|
|
105
|
+
budget: EncodingBudget,
|
|
106
|
+
additional: number,
|
|
107
|
+
): void {
|
|
108
|
+
if (additional > budget.maximum - budget.consumed) {
|
|
109
|
+
throw new SlopcameraCodeError(
|
|
110
|
+
"invalid-data",
|
|
111
|
+
`Workflow authoring values exceed the ${String(MAX_AUTHORING_VALUES)} value limit.`,
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function encodingBudget(state: BuilderState): EncodingBudget {
|
|
117
|
+
return {
|
|
118
|
+
consumed: 0,
|
|
119
|
+
maximum: MAX_AUTHORING_VALUES - state.authoredValues,
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function requireStateCapacity(state: BuilderState, additional: number): void {
|
|
124
|
+
if (additional > MAX_AUTHORING_VALUES - state.authoredValues) {
|
|
125
|
+
throw new SlopcameraCodeError(
|
|
126
|
+
"invalid-data",
|
|
127
|
+
`Workflow authoring values exceed the ${String(MAX_AUTHORING_VALUES)} value limit.`,
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function normalizeTrustedComputeDefinition<Input, Output>(
|
|
133
|
+
definition: TrustedComputeDefinition<Input, Output>,
|
|
134
|
+
): TrustedComputeDefinition<Input, Output> | undefined {
|
|
135
|
+
if (definition[TRUSTED_COMPUTE_BRAND] === true) return definition
|
|
136
|
+
if (Reflect.get(definition, LEGACY_TRUSTED_COMPUTE_BRAND) !== true) {
|
|
137
|
+
return undefined
|
|
138
|
+
}
|
|
139
|
+
const existing = NORMALIZED_LEGACY_COMPUTES.get(definition)
|
|
140
|
+
if (existing !== undefined) {
|
|
141
|
+
return existing as TrustedComputeDefinition<Input, Output>
|
|
142
|
+
}
|
|
143
|
+
const normalized = Object.freeze({
|
|
144
|
+
[TRUSTED_COMPUTE_BRAND]: true as const,
|
|
145
|
+
bounds: definition.bounds,
|
|
146
|
+
inputSchema: definition.inputSchema,
|
|
147
|
+
inputSchemaId: definition.inputSchemaId,
|
|
148
|
+
key: definition.key,
|
|
149
|
+
outputSchema: definition.outputSchema,
|
|
150
|
+
outputSchemaId: definition.outputSchemaId,
|
|
151
|
+
run: definition.run,
|
|
152
|
+
}) satisfies TrustedComputeDefinition<Input, Output>
|
|
153
|
+
NORMALIZED_LEGACY_COMPUTES.set(definition, normalized)
|
|
154
|
+
return normalized
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function discoveryKey(kind: OperationKind, version: number): string {
|
|
158
|
+
return `${kind}@${String(version)}`
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function providerDiscovery(
|
|
162
|
+
provider: OperationDiscoveryProvider,
|
|
163
|
+
): readonly unknown[] {
|
|
164
|
+
const source = provider as Partial<OperationDiscoverySource>
|
|
165
|
+
const discovery = typeof source.list === "function"
|
|
166
|
+
? source.list()
|
|
167
|
+
: (provider as Pick<WorkflowRegistryProjection, "discovery">).discovery
|
|
168
|
+
return boundedOperationDiscoveryList(discovery)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function operationContractValue<Input, Output>(
|
|
172
|
+
discovery: Pick<OperationDiscovery, "inputSchemaId" | "kind" | "outputSchemaId" | "version">,
|
|
173
|
+
): OperationContract<Input, Output> {
|
|
174
|
+
return Object.freeze({
|
|
175
|
+
inputSchemaId: discovery.inputSchemaId,
|
|
176
|
+
kind: discovery.kind,
|
|
177
|
+
outputSchemaId: discovery.outputSchemaId,
|
|
178
|
+
version: discovery.version,
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function isPlainRecord(value: object): value is Record<string, unknown> {
|
|
183
|
+
const prototype: unknown = Object.getPrototypeOf(value)
|
|
184
|
+
return prototype === Object.prototype || prototype === null
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function rejectEnumerableSymbols(
|
|
188
|
+
descriptors: object,
|
|
189
|
+
name: string,
|
|
190
|
+
): void {
|
|
191
|
+
if (Object.getOwnPropertySymbols(descriptors).some(
|
|
192
|
+
symbol => (Reflect.get(descriptors, symbol) as PropertyDescriptor | undefined)
|
|
193
|
+
?.enumerable === true,
|
|
194
|
+
)) {
|
|
195
|
+
throw new SlopcameraCodeError(
|
|
196
|
+
"invalid-data",
|
|
197
|
+
`${name} cannot contain enumerable symbol properties.`,
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function cloneSerializedRef(reference: Ref<unknown>): SerializedRefV1 {
|
|
203
|
+
return deepFreezeJson(parseCodeBoundary(
|
|
204
|
+
SerializedRefV1Schema,
|
|
205
|
+
reference.serialized,
|
|
206
|
+
"workflow reference",
|
|
207
|
+
))
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function serializedReferenceValueCount(reference: SerializedRefV1): number {
|
|
211
|
+
const path = reference.$ref.path
|
|
212
|
+
// Root object + its two fields + nested $ref fields + optional path values.
|
|
213
|
+
return path === undefined ? 5 : 6 + path.length
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function isOwnedRef(value: unknown, state: BuilderState): value is Ref<unknown> {
|
|
217
|
+
return typeof value === "object"
|
|
218
|
+
&& value !== null
|
|
219
|
+
&& WORKFLOW_REF_BRAND in value
|
|
220
|
+
&& state.references.has(value)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function encodeInputValue(
|
|
224
|
+
input: unknown,
|
|
225
|
+
state: BuilderState,
|
|
226
|
+
dependencies: Set<string>,
|
|
227
|
+
ancestors: Set<object>,
|
|
228
|
+
depth: number,
|
|
229
|
+
budget: EncodingBudget,
|
|
230
|
+
): GraphInputValue {
|
|
231
|
+
if (depth > MAX_AUTHORING_VALUE_DEPTH) {
|
|
232
|
+
throw new SlopcameraCodeError(
|
|
233
|
+
"invalid-data",
|
|
234
|
+
`Workflow input nesting exceeds ${String(MAX_AUTHORING_VALUE_DEPTH)} levels.`,
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
if (isOwnedRef(input, state)) {
|
|
238
|
+
const serialized = cloneSerializedRef(input)
|
|
239
|
+
consumeAuthoringValues(budget, serializedReferenceValueCount(serialized))
|
|
240
|
+
dependencies.add(serialized.$ref.nodeKey)
|
|
241
|
+
return serialized
|
|
242
|
+
}
|
|
243
|
+
consumeAuthoringValue(budget)
|
|
244
|
+
if (typeof input === "object" && input !== null && WORKFLOW_REF_BRAND in input) {
|
|
245
|
+
throw new SlopcameraCodeError(
|
|
246
|
+
"invalid-data",
|
|
247
|
+
"Use a typed Ref value created by this workflow graph builder.",
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
if (input === null || typeof input === "boolean" || typeof input === "string") {
|
|
251
|
+
return input
|
|
252
|
+
}
|
|
253
|
+
if (typeof input === "number") {
|
|
254
|
+
if (!Number.isFinite(input)) {
|
|
255
|
+
throw new SlopcameraCodeError(
|
|
256
|
+
"invalid-data",
|
|
257
|
+
"Workflow node input numbers must be finite.",
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
return Object.is(input, -0) ? 0 : input
|
|
261
|
+
}
|
|
262
|
+
if (typeof input !== "object") {
|
|
263
|
+
throw new SlopcameraCodeError(
|
|
264
|
+
"invalid-data",
|
|
265
|
+
`Workflow node input cannot contain ${typeof input} values.`,
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
if (ancestors.has(input)) {
|
|
269
|
+
throw new SlopcameraCodeError(
|
|
270
|
+
"invalid-data",
|
|
271
|
+
"Workflow node input cannot contain cycles.",
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
ancestors.add(input)
|
|
275
|
+
try {
|
|
276
|
+
if (Array.isArray(input)) {
|
|
277
|
+
const length = input.length
|
|
278
|
+
requireAuthoringCapacity(budget, length)
|
|
279
|
+
const descriptors = Object.getOwnPropertyDescriptors(input)
|
|
280
|
+
rejectEnumerableSymbols(descriptors, "Workflow node input arrays")
|
|
281
|
+
const keys = Object.keys(descriptors)
|
|
282
|
+
.filter(key => descriptors[key]?.enumerable === true)
|
|
283
|
+
if (
|
|
284
|
+
keys.length !== length
|
|
285
|
+
|| keys.some((key, index) => key !== String(index))
|
|
286
|
+
) {
|
|
287
|
+
throw new SlopcameraCodeError(
|
|
288
|
+
"invalid-data",
|
|
289
|
+
"Workflow node input arrays must be dense and cannot have named properties.",
|
|
290
|
+
)
|
|
291
|
+
}
|
|
292
|
+
const encoded: GraphInputValue[] = []
|
|
293
|
+
for (let index = 0; index < length; index += 1) {
|
|
294
|
+
const descriptor = descriptors[String(index)]
|
|
295
|
+
if (
|
|
296
|
+
descriptor === undefined
|
|
297
|
+
|| descriptor.get !== undefined
|
|
298
|
+
|| descriptor.set !== undefined
|
|
299
|
+
) {
|
|
300
|
+
throw new SlopcameraCodeError(
|
|
301
|
+
"invalid-data",
|
|
302
|
+
"Workflow node input arrays must contain plain data elements.",
|
|
303
|
+
)
|
|
304
|
+
}
|
|
305
|
+
encoded.push(encodeInputValue(
|
|
306
|
+
descriptor.value,
|
|
307
|
+
state,
|
|
308
|
+
dependencies,
|
|
309
|
+
ancestors,
|
|
310
|
+
depth + 1,
|
|
311
|
+
budget,
|
|
312
|
+
))
|
|
313
|
+
}
|
|
314
|
+
return encoded
|
|
315
|
+
}
|
|
316
|
+
if (!isPlainRecord(input)) {
|
|
317
|
+
throw new SlopcameraCodeError(
|
|
318
|
+
"invalid-data",
|
|
319
|
+
"Workflow node input accepts only JSON values and typed workflow references.",
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
if (Object.hasOwn(input, "$ref")) {
|
|
323
|
+
throw new SlopcameraCodeError(
|
|
324
|
+
"invalid-data",
|
|
325
|
+
"Use a typed Ref value instead of constructing the reserved $ref field.",
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
const descriptors = Object.getOwnPropertyDescriptors(input)
|
|
329
|
+
rejectEnumerableSymbols(descriptors, "Workflow node inputs")
|
|
330
|
+
const keys = Object.keys(descriptors)
|
|
331
|
+
.filter(key => descriptors[key]?.enumerable === true)
|
|
332
|
+
.sort()
|
|
333
|
+
requireAuthoringCapacity(budget, keys.length)
|
|
334
|
+
const encoded: Record<string, GraphInputValue> = {}
|
|
335
|
+
for (const key of keys) {
|
|
336
|
+
if (key === "__proto__") {
|
|
337
|
+
throw new SlopcameraCodeError(
|
|
338
|
+
"invalid-data",
|
|
339
|
+
"Workflow inputs cannot contain the reserved __proto__ object key.",
|
|
340
|
+
)
|
|
341
|
+
}
|
|
342
|
+
const descriptor = descriptors[key]
|
|
343
|
+
if (
|
|
344
|
+
descriptor === undefined
|
|
345
|
+
|| descriptor.get !== undefined
|
|
346
|
+
|| descriptor.set !== undefined
|
|
347
|
+
) {
|
|
348
|
+
throw new SlopcameraCodeError(
|
|
349
|
+
"invalid-data",
|
|
350
|
+
"Workflow node input properties must be plain data properties.",
|
|
351
|
+
)
|
|
352
|
+
}
|
|
353
|
+
encoded[key] = encodeInputValue(
|
|
354
|
+
descriptor.value,
|
|
355
|
+
state,
|
|
356
|
+
dependencies,
|
|
357
|
+
ancestors,
|
|
358
|
+
depth + 1,
|
|
359
|
+
budget,
|
|
360
|
+
)
|
|
361
|
+
}
|
|
362
|
+
return encoded
|
|
363
|
+
} finally {
|
|
364
|
+
ancestors.delete(input)
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function encodeOperationInput(input: unknown, state: BuilderState): EncodedInput {
|
|
369
|
+
const dependencies = new Set<string>()
|
|
370
|
+
const budget = encodingBudget(state)
|
|
371
|
+
const value = encodeInputValue(input, state, dependencies, new Set(), 0, budget)
|
|
372
|
+
return {
|
|
373
|
+
dependencies: [...dependencies].sort((left, right) => left.localeCompare(right)),
|
|
374
|
+
value,
|
|
375
|
+
values: budget.consumed,
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function encodeControlDependencies(
|
|
380
|
+
after: OperationNodeOptions["after"],
|
|
381
|
+
state: BuilderState,
|
|
382
|
+
): readonly string[] {
|
|
383
|
+
if (after === undefined) return []
|
|
384
|
+
const dependencies = new Set<string>()
|
|
385
|
+
const appendReference = (reference: unknown): void => {
|
|
386
|
+
if (!isOwnedRef(reference, state)) {
|
|
387
|
+
throw new SlopcameraCodeError(
|
|
388
|
+
"invalid-data",
|
|
389
|
+
"Operation control dependencies must be typed Ref values created by this workflow graph builder.",
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
dependencies.add(cloneSerializedRef(reference).$ref.nodeKey)
|
|
393
|
+
}
|
|
394
|
+
if (!Array.isArray(after)) {
|
|
395
|
+
appendReference(after)
|
|
396
|
+
return [...dependencies]
|
|
397
|
+
}
|
|
398
|
+
const length = after.length
|
|
399
|
+
if (length > MAX_SERIALIZED_NODE_DEPENDENCIES) {
|
|
400
|
+
throw new SlopcameraCodeError(
|
|
401
|
+
"invalid-data",
|
|
402
|
+
`Operation control dependencies cannot exceed ${String(MAX_SERIALIZED_NODE_DEPENDENCIES)} entries.`,
|
|
403
|
+
)
|
|
404
|
+
}
|
|
405
|
+
const descriptors = Object.getOwnPropertyDescriptors(after)
|
|
406
|
+
rejectEnumerableSymbols(descriptors, "Operation control dependencies")
|
|
407
|
+
const keys = Object.keys(descriptors)
|
|
408
|
+
.filter(key => descriptors[key]?.enumerable === true)
|
|
409
|
+
if (
|
|
410
|
+
keys.length !== length
|
|
411
|
+
|| keys.some((key, index) => key !== String(index))
|
|
412
|
+
) {
|
|
413
|
+
throw new SlopcameraCodeError(
|
|
414
|
+
"invalid-data",
|
|
415
|
+
"Operation control dependencies must be a dense array without named properties.",
|
|
416
|
+
)
|
|
417
|
+
}
|
|
418
|
+
for (let index = 0; index < length; index += 1) {
|
|
419
|
+
const descriptor = descriptors[String(index)]
|
|
420
|
+
if (
|
|
421
|
+
descriptor === undefined
|
|
422
|
+
|| descriptor.get !== undefined
|
|
423
|
+
|| descriptor.set !== undefined
|
|
424
|
+
) {
|
|
425
|
+
throw new SlopcameraCodeError(
|
|
426
|
+
"invalid-data",
|
|
427
|
+
"Operation control dependencies must contain plain data elements.",
|
|
428
|
+
)
|
|
429
|
+
}
|
|
430
|
+
appendReference(descriptor.value)
|
|
431
|
+
}
|
|
432
|
+
return [...dependencies].sort((left, right) => left.localeCompare(right))
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function combineDependencies(
|
|
436
|
+
dataDependencies: readonly string[],
|
|
437
|
+
controlDependencies: readonly string[],
|
|
438
|
+
): readonly string[] {
|
|
439
|
+
return [...new Set([...dataDependencies, ...controlDependencies])]
|
|
440
|
+
.sort((left, right) => left.localeCompare(right))
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function encodeOutputValue(
|
|
444
|
+
output: unknown,
|
|
445
|
+
state: BuilderState,
|
|
446
|
+
ancestors: Set<object>,
|
|
447
|
+
depth: number,
|
|
448
|
+
budget: EncodingBudget,
|
|
449
|
+
): WorkflowOutputBinding {
|
|
450
|
+
if (depth > MAX_AUTHORING_VALUE_DEPTH) {
|
|
451
|
+
throw new SlopcameraCodeError(
|
|
452
|
+
"invalid-data",
|
|
453
|
+
`Workflow output nesting exceeds ${String(MAX_AUTHORING_VALUE_DEPTH)} levels.`,
|
|
454
|
+
)
|
|
455
|
+
}
|
|
456
|
+
if (isOwnedRef(output, state)) {
|
|
457
|
+
const serialized = cloneSerializedRef(output)
|
|
458
|
+
consumeAuthoringValues(budget, serializedReferenceValueCount(serialized))
|
|
459
|
+
return serialized
|
|
460
|
+
}
|
|
461
|
+
consumeAuthoringValue(budget)
|
|
462
|
+
if (typeof output !== "object" || output === null) {
|
|
463
|
+
throw new SlopcameraCodeError(
|
|
464
|
+
"invalid-data",
|
|
465
|
+
"Workflow outputs must contain only typed references, arrays, and named objects.",
|
|
466
|
+
)
|
|
467
|
+
}
|
|
468
|
+
if (ancestors.has(output)) {
|
|
469
|
+
throw new SlopcameraCodeError("invalid-data", "Workflow outputs cannot contain cycles.")
|
|
470
|
+
}
|
|
471
|
+
ancestors.add(output)
|
|
472
|
+
try {
|
|
473
|
+
if (Array.isArray(output)) {
|
|
474
|
+
const length = output.length
|
|
475
|
+
requireAuthoringCapacity(budget, length)
|
|
476
|
+
const descriptors = Object.getOwnPropertyDescriptors(output)
|
|
477
|
+
rejectEnumerableSymbols(descriptors, "Workflow output arrays")
|
|
478
|
+
const keys = Object.keys(descriptors)
|
|
479
|
+
.filter(key => descriptors[key]?.enumerable === true)
|
|
480
|
+
if (
|
|
481
|
+
keys.length !== length
|
|
482
|
+
|| keys.some((key, index) => key !== String(index))
|
|
483
|
+
) {
|
|
484
|
+
throw new SlopcameraCodeError(
|
|
485
|
+
"invalid-data",
|
|
486
|
+
"Workflow output arrays must be dense and cannot have named properties.",
|
|
487
|
+
)
|
|
488
|
+
}
|
|
489
|
+
const encoded: WorkflowOutputBinding[] = []
|
|
490
|
+
for (let index = 0; index < length; index += 1) {
|
|
491
|
+
const descriptor = descriptors[String(index)]
|
|
492
|
+
if (
|
|
493
|
+
descriptor === undefined
|
|
494
|
+
|| descriptor.get !== undefined
|
|
495
|
+
|| descriptor.set !== undefined
|
|
496
|
+
) {
|
|
497
|
+
throw new SlopcameraCodeError(
|
|
498
|
+
"invalid-data",
|
|
499
|
+
"Workflow output arrays must contain plain data elements.",
|
|
500
|
+
)
|
|
501
|
+
}
|
|
502
|
+
encoded.push(encodeOutputValue(
|
|
503
|
+
descriptor.value,
|
|
504
|
+
state,
|
|
505
|
+
ancestors,
|
|
506
|
+
depth + 1,
|
|
507
|
+
budget,
|
|
508
|
+
))
|
|
509
|
+
}
|
|
510
|
+
return encoded
|
|
511
|
+
}
|
|
512
|
+
if (!isPlainRecord(output)) {
|
|
513
|
+
throw new SlopcameraCodeError(
|
|
514
|
+
"invalid-data",
|
|
515
|
+
"Workflow outputs accept only typed references, arrays, and plain objects.",
|
|
516
|
+
)
|
|
517
|
+
}
|
|
518
|
+
if (Object.hasOwn(output, "$ref")) {
|
|
519
|
+
throw new SlopcameraCodeError(
|
|
520
|
+
"invalid-data",
|
|
521
|
+
"Use a typed Ref value instead of constructing the reserved $ref field.",
|
|
522
|
+
)
|
|
523
|
+
}
|
|
524
|
+
const descriptors = Object.getOwnPropertyDescriptors(output)
|
|
525
|
+
rejectEnumerableSymbols(descriptors, "Workflow outputs")
|
|
526
|
+
const keys = Object.keys(descriptors)
|
|
527
|
+
.filter(key => descriptors[key]?.enumerable === true)
|
|
528
|
+
.sort()
|
|
529
|
+
requireAuthoringCapacity(budget, keys.length)
|
|
530
|
+
const encoded: Record<string, WorkflowOutputBinding> = {}
|
|
531
|
+
for (const key of keys) {
|
|
532
|
+
if (key === "__proto__") {
|
|
533
|
+
throw new SlopcameraCodeError(
|
|
534
|
+
"invalid-data",
|
|
535
|
+
"Workflow outputs cannot contain the reserved __proto__ object key.",
|
|
536
|
+
)
|
|
537
|
+
}
|
|
538
|
+
const descriptor = descriptors[key]
|
|
539
|
+
if (
|
|
540
|
+
descriptor === undefined
|
|
541
|
+
|| descriptor.get !== undefined
|
|
542
|
+
|| descriptor.set !== undefined
|
|
543
|
+
) {
|
|
544
|
+
throw new SlopcameraCodeError(
|
|
545
|
+
"invalid-data",
|
|
546
|
+
"Workflow output properties must be plain data properties.",
|
|
547
|
+
)
|
|
548
|
+
}
|
|
549
|
+
encoded[key] = encodeOutputValue(
|
|
550
|
+
descriptor.value,
|
|
551
|
+
state,
|
|
552
|
+
ancestors,
|
|
553
|
+
depth + 1,
|
|
554
|
+
budget,
|
|
555
|
+
)
|
|
556
|
+
}
|
|
557
|
+
return encoded
|
|
558
|
+
} finally {
|
|
559
|
+
ancestors.delete(output)
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
function createReference<Output>(
|
|
564
|
+
nodeKey: string,
|
|
565
|
+
schemaId: string,
|
|
566
|
+
state: BuilderState,
|
|
567
|
+
path: readonly (number | string)[] = [],
|
|
568
|
+
): Ref<Output> {
|
|
569
|
+
if (path.length > MAX_SERIALIZED_REF_PATH_SEGMENTS) {
|
|
570
|
+
throw new SlopcameraCodeError(
|
|
571
|
+
"invalid-data",
|
|
572
|
+
`Workflow reference paths cannot exceed ${String(MAX_SERIALIZED_REF_PATH_SEGMENTS)} segments.`,
|
|
573
|
+
)
|
|
574
|
+
}
|
|
575
|
+
const serialized = deepFreezeJson({
|
|
576
|
+
$ref: {
|
|
577
|
+
nodeKey,
|
|
578
|
+
...(path.length === 0 ? {} : { path: [...path] }),
|
|
579
|
+
schemaId,
|
|
580
|
+
},
|
|
581
|
+
version: WORKFLOW_REF_VERSION,
|
|
582
|
+
} satisfies SerializedRefV1)
|
|
583
|
+
const reference = Object.freeze({
|
|
584
|
+
[WORKFLOW_REF_BRAND]: (): Output => {
|
|
585
|
+
throw new SlopcameraCodeError(
|
|
586
|
+
"internal",
|
|
587
|
+
"A workflow reference type marker is not executable.",
|
|
588
|
+
)
|
|
589
|
+
},
|
|
590
|
+
at: (index: number) => {
|
|
591
|
+
if (!Number.isSafeInteger(index) || index < 0) {
|
|
592
|
+
throw new SlopcameraCodeError(
|
|
593
|
+
"invalid-data",
|
|
594
|
+
"Workflow reference array indexes must be nonnegative safe integers.",
|
|
595
|
+
)
|
|
596
|
+
}
|
|
597
|
+
if (path.length >= MAX_SERIALIZED_REF_PATH_SEGMENTS) {
|
|
598
|
+
throw new SlopcameraCodeError(
|
|
599
|
+
"invalid-data",
|
|
600
|
+
`Workflow reference paths cannot exceed ${String(MAX_SERIALIZED_REF_PATH_SEGMENTS)} segments.`,
|
|
601
|
+
)
|
|
602
|
+
}
|
|
603
|
+
return createReference(nodeKey, schemaId, state, [...path, index]) as (
|
|
604
|
+
Output extends readonly (infer Item)[] ? Ref<Item> : never
|
|
605
|
+
)
|
|
606
|
+
},
|
|
607
|
+
select: <Key extends Extract<keyof Output, string>>(key: Key): Ref<Output[Key]> => {
|
|
608
|
+
if (typeof key !== "string" || key.length < 1 || key.length > 128) {
|
|
609
|
+
throw new SlopcameraCodeError(
|
|
610
|
+
"invalid-data",
|
|
611
|
+
"Workflow reference field names must contain 1–128 characters.",
|
|
612
|
+
)
|
|
613
|
+
}
|
|
614
|
+
if (path.length >= MAX_SERIALIZED_REF_PATH_SEGMENTS) {
|
|
615
|
+
throw new SlopcameraCodeError(
|
|
616
|
+
"invalid-data",
|
|
617
|
+
`Workflow reference paths cannot exceed ${String(MAX_SERIALIZED_REF_PATH_SEGMENTS)} segments.`,
|
|
618
|
+
)
|
|
619
|
+
}
|
|
620
|
+
return createReference<Output[Key]>(nodeKey, schemaId, state, [...path, key])
|
|
621
|
+
},
|
|
622
|
+
serialized,
|
|
623
|
+
}) as unknown as Ref<Output>
|
|
624
|
+
state.references.add(reference)
|
|
625
|
+
return reference
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
function createState(provider: OperationDiscoveryProvider): BuilderState {
|
|
629
|
+
const discovery = new Map<string, OperationDiscovery>()
|
|
630
|
+
for (const input of providerDiscovery(provider)) {
|
|
631
|
+
const item = parseCodeBoundary(
|
|
632
|
+
OperationDiscoverySchema,
|
|
633
|
+
input,
|
|
634
|
+
"operation discovery entry",
|
|
635
|
+
)
|
|
636
|
+
const key = discoveryKey(item.kind, item.version)
|
|
637
|
+
if (discovery.has(key)) {
|
|
638
|
+
throw new SlopcameraCodeError(
|
|
639
|
+
"conflict",
|
|
640
|
+
`Duplicate operation discovery entry: ${key}`,
|
|
641
|
+
{ kind: item.kind, version: item.version },
|
|
642
|
+
)
|
|
643
|
+
}
|
|
644
|
+
discovery.set(key, item)
|
|
645
|
+
}
|
|
646
|
+
return {
|
|
647
|
+
// Root graph, nodes array, version, workflow object, and its three fields.
|
|
648
|
+
// The output subtree is charged temporarily by build().
|
|
649
|
+
authoredValues: 7,
|
|
650
|
+
computes: new Map(),
|
|
651
|
+
discovery,
|
|
652
|
+
nodes: new Map(),
|
|
653
|
+
references: new WeakSet(),
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
export function defineWorkflowFragment<Input, Output>(
|
|
658
|
+
build: (builder: WorkflowGraphBuilder, input: Input) => Output,
|
|
659
|
+
): WorkflowFragment<Input, Output> {
|
|
660
|
+
if (typeof build !== "function") {
|
|
661
|
+
throw new SlopcameraCodeError(
|
|
662
|
+
"invalid-data",
|
|
663
|
+
"Workflow fragments require a build function.",
|
|
664
|
+
)
|
|
665
|
+
}
|
|
666
|
+
return Object.freeze({ build })
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
export function operationContract<Input, Output>(
|
|
670
|
+
provider: OperationDiscoveryProvider,
|
|
671
|
+
kind: OperationKind,
|
|
672
|
+
version: number,
|
|
673
|
+
): OperationContract<Input, Output> {
|
|
674
|
+
const discovery = createState(provider).discovery.get(discoveryKey(kind, version))
|
|
675
|
+
if (discovery === undefined) {
|
|
676
|
+
throw new SlopcameraCodeError(
|
|
677
|
+
"unsupported-plan",
|
|
678
|
+
`Unsupported operation: ${kind}@${String(version)}`,
|
|
679
|
+
{ kind, version },
|
|
680
|
+
)
|
|
681
|
+
}
|
|
682
|
+
return operationContractValue<Input, Output>(discovery)
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
export class WorkflowGraphBuilder {
|
|
686
|
+
readonly #namespace: readonly string[]
|
|
687
|
+
readonly #state: BuilderState
|
|
688
|
+
|
|
689
|
+
private constructor(state: BuilderState, namespace: readonly string[]) {
|
|
690
|
+
this.#namespace = namespace
|
|
691
|
+
this.#state = state
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
static create(provider: OperationDiscoveryProvider): WorkflowGraphBuilder {
|
|
695
|
+
return new WorkflowGraphBuilder(createState(provider), [])
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
namespace(segmentInput: string): WorkflowGraphBuilder {
|
|
699
|
+
const segment = parseCodeBoundary(
|
|
700
|
+
NodeKeySegmentSchema,
|
|
701
|
+
segmentInput,
|
|
702
|
+
"workflow namespace segment",
|
|
703
|
+
)
|
|
704
|
+
return new WorkflowGraphBuilder(this.#state, [...this.#namespace, segment])
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
fragment<Input, Output>(
|
|
708
|
+
namespace: string,
|
|
709
|
+
fragment: WorkflowFragment<Input, Output>,
|
|
710
|
+
input: Input,
|
|
711
|
+
): Output {
|
|
712
|
+
return fragment.build(this.namespace(namespace), input)
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
operation<Input, Output>(
|
|
716
|
+
keyInput: string,
|
|
717
|
+
contract: OperationContract<Input, Output>,
|
|
718
|
+
input: OperationInputValue<Input>,
|
|
719
|
+
options: OperationNodeOptions = {},
|
|
720
|
+
): Ref<Output> {
|
|
721
|
+
return this.#addOperation<Output>(keyInput, {
|
|
722
|
+
input,
|
|
723
|
+
inputSchemaId: contract.inputSchemaId,
|
|
724
|
+
kind: contract.kind,
|
|
725
|
+
outputSchemaId: contract.outputSchemaId,
|
|
726
|
+
version: contract.version,
|
|
727
|
+
}, options)
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
operationByKind<Output>(
|
|
731
|
+
keyInput: string,
|
|
732
|
+
request: UntypedOperationRequest,
|
|
733
|
+
options: OperationNodeOptions = {},
|
|
734
|
+
): Ref<Output> {
|
|
735
|
+
const discovery = this.#state.discovery.get(
|
|
736
|
+
discoveryKey(request.kind, request.version),
|
|
737
|
+
)
|
|
738
|
+
if (discovery === undefined) {
|
|
739
|
+
throw new SlopcameraCodeError(
|
|
740
|
+
"unsupported-plan",
|
|
741
|
+
`Unsupported operation: ${request.kind}@${String(request.version)}`,
|
|
742
|
+
{ kind: request.kind, version: request.version },
|
|
743
|
+
)
|
|
744
|
+
}
|
|
745
|
+
return this.#addOperation<Output>(keyInput, {
|
|
746
|
+
input: request.input,
|
|
747
|
+
inputSchemaId: discovery.inputSchemaId,
|
|
748
|
+
kind: request.kind,
|
|
749
|
+
outputSchemaId: discovery.outputSchemaId,
|
|
750
|
+
version: request.version,
|
|
751
|
+
}, options)
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
compute<Input, Output>(
|
|
755
|
+
keyInput: string,
|
|
756
|
+
definition: TrustedComputeDefinition<Input, Output>,
|
|
757
|
+
input: OperationInputValue<Input>,
|
|
758
|
+
options: OperationNodeOptions = {},
|
|
759
|
+
): Ref<Output> {
|
|
760
|
+
if (typeof definition !== "object" || definition === null) {
|
|
761
|
+
throw new SlopcameraCodeError(
|
|
762
|
+
"invalid-data",
|
|
763
|
+
"Compute nodes require a definition created by defineCompute().",
|
|
764
|
+
)
|
|
765
|
+
}
|
|
766
|
+
const normalizedDefinition = normalizeTrustedComputeDefinition(definition)
|
|
767
|
+
if (normalizedDefinition === undefined) {
|
|
768
|
+
throw new SlopcameraCodeError(
|
|
769
|
+
"invalid-data",
|
|
770
|
+
"Compute nodes require a definition created by defineCompute().",
|
|
771
|
+
)
|
|
772
|
+
}
|
|
773
|
+
const compute = parseCodeBoundary(
|
|
774
|
+
AuthoredComputeIdentitySchema,
|
|
775
|
+
{
|
|
776
|
+
bounds: normalizedDefinition.bounds,
|
|
777
|
+
key: normalizedDefinition.key,
|
|
778
|
+
version: TRUSTED_COMPUTE_VERSION,
|
|
779
|
+
},
|
|
780
|
+
"trusted compute identity",
|
|
781
|
+
)
|
|
782
|
+
const existing = this.#state.computes.get(compute.key)
|
|
783
|
+
if (existing !== undefined && existing !== normalizedDefinition) {
|
|
784
|
+
throw new SlopcameraCodeError(
|
|
785
|
+
"conflict",
|
|
786
|
+
`Duplicate trusted compute key: ${compute.key}`,
|
|
787
|
+
{ key: compute.key },
|
|
788
|
+
)
|
|
789
|
+
}
|
|
790
|
+
const key = this.#nodeKey(keyInput)
|
|
791
|
+
const encoded = encodeOperationInput(input, this.#state)
|
|
792
|
+
const controlDependencies = encodeControlDependencies(options.after, this.#state)
|
|
793
|
+
const dependencies = combineDependencies(
|
|
794
|
+
encoded.dependencies,
|
|
795
|
+
controlDependencies,
|
|
796
|
+
)
|
|
797
|
+
const nodeValues = encoded.values
|
|
798
|
+
+ 15
|
|
799
|
+
+ controlDependencies.length
|
|
800
|
+
+ dependencies.length
|
|
801
|
+
+ (options.label === undefined ? 0 : 1)
|
|
802
|
+
requireStateCapacity(this.#state, nodeValues)
|
|
803
|
+
const node = parseCodeBoundary(AuthoredGraphNodeV1Schema, {
|
|
804
|
+
controlDependencies,
|
|
805
|
+
dependencies,
|
|
806
|
+
executor: { compute, kind: "compute" },
|
|
807
|
+
input: encoded.value,
|
|
808
|
+
inputSchemaId: normalizedDefinition.inputSchemaId,
|
|
809
|
+
key,
|
|
810
|
+
...(options.label === undefined ? {} : { label: options.label }),
|
|
811
|
+
outputSchemaId: normalizedDefinition.outputSchemaId,
|
|
812
|
+
}, "authored compute node")
|
|
813
|
+
this.#state.computes.set(compute.key, normalizedDefinition)
|
|
814
|
+
this.#state.nodes.set(key, node)
|
|
815
|
+
this.#state.authoredValues += nodeValues
|
|
816
|
+
return createReference<Output>(
|
|
817
|
+
key,
|
|
818
|
+
normalizedDefinition.outputSchemaId,
|
|
819
|
+
this.#state,
|
|
820
|
+
)
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
computeDefinitions(): readonly AnyTrustedComputeDefinition[] {
|
|
824
|
+
return Object.freeze(
|
|
825
|
+
[...this.#state.computes.values()]
|
|
826
|
+
.sort((left, right) => left.key.localeCompare(right.key)),
|
|
827
|
+
)
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
build(
|
|
831
|
+
workflowInput: WorkflowIdentity,
|
|
832
|
+
outputs: WorkflowOutputValue,
|
|
833
|
+
): AuthoredWorkflowGraphV1 {
|
|
834
|
+
const workflow = parseCodeBoundary(
|
|
835
|
+
WorkflowIdentitySchema,
|
|
836
|
+
workflowInput,
|
|
837
|
+
"workflow identity",
|
|
838
|
+
)
|
|
839
|
+
const outputBudget = encodingBudget(this.#state)
|
|
840
|
+
const encodedOutputs = encodeOutputValue(
|
|
841
|
+
outputs,
|
|
842
|
+
this.#state,
|
|
843
|
+
new Set(),
|
|
844
|
+
0,
|
|
845
|
+
outputBudget,
|
|
846
|
+
)
|
|
847
|
+
return deepFreezeJson(parseCodeBoundary(AuthoredWorkflowGraphV1Schema, {
|
|
848
|
+
nodes: [...this.#state.nodes.values()]
|
|
849
|
+
.sort((left, right) => left.key.localeCompare(right.key)),
|
|
850
|
+
outputs: encodedOutputs,
|
|
851
|
+
version: WORKFLOW_GRAPH_VERSION,
|
|
852
|
+
workflow,
|
|
853
|
+
}, "authored workflow graph"))
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
#nodeKey(keyInput: string): string {
|
|
857
|
+
const keySegment = parseCodeBoundary(
|
|
858
|
+
NodeKeySegmentSchema,
|
|
859
|
+
keyInput,
|
|
860
|
+
"workflow node key segment",
|
|
861
|
+
)
|
|
862
|
+
const key = [...this.#namespace, keySegment].join("/")
|
|
863
|
+
if (this.#state.nodes.has(key)) {
|
|
864
|
+
throw new SlopcameraCodeError(
|
|
865
|
+
"conflict",
|
|
866
|
+
`Duplicate workflow node key: ${key}`,
|
|
867
|
+
{ nodeKey: key },
|
|
868
|
+
)
|
|
869
|
+
}
|
|
870
|
+
if (this.#state.nodes.size >= MAX_SERIALIZED_GRAPH_NODES) {
|
|
871
|
+
throw new SlopcameraCodeError(
|
|
872
|
+
"invalid-data",
|
|
873
|
+
`Workflow nodes cannot exceed ${String(MAX_SERIALIZED_GRAPH_NODES)} entries.`,
|
|
874
|
+
)
|
|
875
|
+
}
|
|
876
|
+
return key
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
#addOperation<Output>(
|
|
880
|
+
keyInput: string,
|
|
881
|
+
request: {
|
|
882
|
+
readonly input: unknown
|
|
883
|
+
readonly inputSchemaId: string
|
|
884
|
+
readonly kind: OperationKind
|
|
885
|
+
readonly outputSchemaId: string
|
|
886
|
+
readonly version: number
|
|
887
|
+
},
|
|
888
|
+
options: OperationNodeOptions,
|
|
889
|
+
): Ref<Output> {
|
|
890
|
+
const key = this.#nodeKey(keyInput)
|
|
891
|
+
const discovery = this.#state.discovery.get(discoveryKey(request.kind, request.version))
|
|
892
|
+
if (discovery === undefined) {
|
|
893
|
+
throw new SlopcameraCodeError(
|
|
894
|
+
"unsupported-plan",
|
|
895
|
+
`Unsupported operation: ${request.kind}@${String(request.version)}`,
|
|
896
|
+
{ kind: request.kind, version: request.version },
|
|
897
|
+
)
|
|
898
|
+
}
|
|
899
|
+
if (
|
|
900
|
+
request.inputSchemaId !== discovery.inputSchemaId
|
|
901
|
+
|| request.outputSchemaId !== discovery.outputSchemaId
|
|
902
|
+
) {
|
|
903
|
+
throw new SlopcameraCodeError(
|
|
904
|
+
"invalid-data",
|
|
905
|
+
`Operation contract schema mismatch for ${request.kind}@${String(request.version)}.`,
|
|
906
|
+
{
|
|
907
|
+
actualInputSchemaId: request.inputSchemaId,
|
|
908
|
+
actualOutputSchemaId: request.outputSchemaId,
|
|
909
|
+
expectedInputSchemaId: discovery.inputSchemaId,
|
|
910
|
+
expectedOutputSchemaId: discovery.outputSchemaId,
|
|
911
|
+
kind: request.kind,
|
|
912
|
+
version: request.version,
|
|
913
|
+
},
|
|
914
|
+
)
|
|
915
|
+
}
|
|
916
|
+
const encoded = encodeOperationInput(request.input, this.#state)
|
|
917
|
+
const controlDependencies = encodeControlDependencies(options.after, this.#state)
|
|
918
|
+
const dependencies = combineDependencies(
|
|
919
|
+
encoded.dependencies,
|
|
920
|
+
controlDependencies,
|
|
921
|
+
)
|
|
922
|
+
const nodeValues = encoded.values
|
|
923
|
+
+ 11
|
|
924
|
+
+ controlDependencies.length
|
|
925
|
+
+ dependencies.length
|
|
926
|
+
+ (options.label === undefined ? 0 : 1)
|
|
927
|
+
requireStateCapacity(this.#state, nodeValues)
|
|
928
|
+
const node = parseCodeBoundary(AuthoredGraphNodeV1Schema, {
|
|
929
|
+
controlDependencies,
|
|
930
|
+
dependencies,
|
|
931
|
+
executor: {
|
|
932
|
+
kind: "operation",
|
|
933
|
+
operation: { kind: discovery.kind, version: discovery.version },
|
|
934
|
+
},
|
|
935
|
+
input: encoded.value,
|
|
936
|
+
inputSchemaId: discovery.inputSchemaId,
|
|
937
|
+
key,
|
|
938
|
+
...(options.label === undefined ? {} : { label: options.label }),
|
|
939
|
+
outputSchemaId: discovery.outputSchemaId,
|
|
940
|
+
}, "authored operation node")
|
|
941
|
+
this.#state.nodes.set(key, node)
|
|
942
|
+
this.#state.authoredValues += nodeValues
|
|
943
|
+
return createReference<Output>(key, discovery.outputSchemaId, this.#state)
|
|
944
|
+
}
|
|
945
|
+
}
|