@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.
Files changed (441) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +60 -0
  3. package/PRIVACY.md +46 -0
  4. package/README.md +311 -0
  5. package/SECURITY.md +21 -0
  6. package/apps/desktop/README.md +966 -0
  7. package/apps/desktop/analysis/protocol.ts +223 -0
  8. package/apps/desktop/application/capability-binding.ts +594 -0
  9. package/apps/desktop/application/context.ts +97 -0
  10. package/apps/desktop/application/creative-iteration.ts +1385 -0
  11. package/apps/desktop/application/default-registry.ts +146 -0
  12. package/apps/desktop/application/directing-contract.ts +218 -0
  13. package/apps/desktop/application/directing-plan.ts +178 -0
  14. package/apps/desktop/application/errors.ts +18 -0
  15. package/apps/desktop/application/gateway-port.ts +867 -0
  16. package/apps/desktop/application/html-overlay-browser-runtime.ts +687 -0
  17. package/apps/desktop/application/html-overlay-integrity.ts +337 -0
  18. package/apps/desktop/application/html-overlay-renderer.ts +45 -0
  19. package/apps/desktop/application/index.ts +13 -0
  20. package/apps/desktop/application/operation-completion-checkpoint.ts +333 -0
  21. package/apps/desktop/application/operation-effects.ts +94 -0
  22. package/apps/desktop/application/operation.ts +224 -0
  23. package/apps/desktop/application/operations/analysis/faces.ts +244 -0
  24. package/apps/desktop/application/operations/analysis/music.ts +248 -0
  25. package/apps/desktop/application/operations/analysis/project-auto-zooms.ts +302 -0
  26. package/apps/desktop/application/operations/analysis/project-inactivity.ts +381 -0
  27. package/apps/desktop/application/operations/analysis/scenes.ts +272 -0
  28. package/apps/desktop/application/operations/analysis/shared.ts +202 -0
  29. package/apps/desktop/application/operations/derive/edit-batch.ts +967 -0
  30. package/apps/desktop/application/operations/derive/follow-faces.ts +454 -0
  31. package/apps/desktop/application/operations/edit/bind-candidate-revision.ts +91 -0
  32. package/apps/desktop/application/operations/edit/create-candidate-revision.ts +377 -0
  33. package/apps/desktop/application/operations/edit/create-revision.ts +255 -0
  34. package/apps/desktop/application/operations/edit/freeze-revision.ts +182 -0
  35. package/apps/desktop/application/operations/gateway/image.ts +41 -0
  36. package/apps/desktop/application/operations/gateway/index.ts +17 -0
  37. package/apps/desktop/application/operations/gateway/shared.ts +122 -0
  38. package/apps/desktop/application/operations/gateway/speech.ts +41 -0
  39. package/apps/desktop/application/operations/gateway/transcription.ts +42 -0
  40. package/apps/desktop/application/operations/gateway/video.ts +41 -0
  41. package/apps/desktop/application/operations/index.ts +31 -0
  42. package/apps/desktop/application/operations/iteration/create-candidate.ts +240 -0
  43. package/apps/desktop/application/operations/iteration/create-matrix.ts +117 -0
  44. package/apps/desktop/application/operations/iteration/select.ts +155 -0
  45. package/apps/desktop/application/operations/iteration/shared.ts +250 -0
  46. package/apps/desktop/application/operations/media/audio-effects.ts +376 -0
  47. package/apps/desktop/application/operations/media/binding.ts +79 -0
  48. package/apps/desktop/application/operations/media/capabilities.ts +112 -0
  49. package/apps/desktop/application/operations/media/color-grade.ts +343 -0
  50. package/apps/desktop/application/operations/media/html-overlay.ts +802 -0
  51. package/apps/desktop/application/operations/media/index.ts +8 -0
  52. package/apps/desktop/application/operations/media/ingest.ts +303 -0
  53. package/apps/desktop/application/operations/media/overlay.ts +729 -0
  54. package/apps/desktop/application/operations/media/shared.ts +845 -0
  55. package/apps/desktop/application/operations/media/transform-platform.ts +120 -0
  56. package/apps/desktop/application/operations/project/commit-edits.ts +2054 -0
  57. package/apps/desktop/application/operations/project/promote-selection.ts +495 -0
  58. package/apps/desktop/application/operations/project/snapshot.ts +95 -0
  59. package/apps/desktop/application/operations/recording/index.ts +5 -0
  60. package/apps/desktop/application/operations/recording/pause.ts +34 -0
  61. package/apps/desktop/application/operations/recording/resume.ts +34 -0
  62. package/apps/desktop/application/operations/recording/shared.ts +199 -0
  63. package/apps/desktop/application/operations/recording/start.ts +169 -0
  64. package/apps/desktop/application/operations/recording/stop.ts +34 -0
  65. package/apps/desktop/application/operations/render/bind-candidate-output.ts +324 -0
  66. package/apps/desktop/application/operations/render/materialize-selection.ts +322 -0
  67. package/apps/desktop/application/operations/render/project-caption-plan.ts +287 -0
  68. package/apps/desktop/application/operations/render/project-plan.ts +939 -0
  69. package/apps/desktop/application/operations/render/project-spatial-receipt.ts +70 -0
  70. package/apps/desktop/application/operations/render/project.ts +1922 -0
  71. package/apps/desktop/application/operations/shared.ts +67 -0
  72. package/apps/desktop/application/operations/slopcamera-portable.ts +1188 -0
  73. package/apps/desktop/application/operations/slopcamera-visuals.ts +784 -0
  74. package/apps/desktop/application/operations/spatial-project.ts +209 -0
  75. package/apps/desktop/application/operations/spatial-render.ts +415 -0
  76. package/apps/desktop/application/operations/spatial-scene.ts +97 -0
  77. package/apps/desktop/application/operations/studio.ts +143 -0
  78. package/apps/desktop/application/output-publication-lease.ts +163 -0
  79. package/apps/desktop/application/project-publication-lease.ts +233 -0
  80. package/apps/desktop/application/project-store.ts +196 -0
  81. package/apps/desktop/application/receipts.ts +533 -0
  82. package/apps/desktop/application/registry.ts +182 -0
  83. package/apps/desktop/application/spatial-assets.ts +675 -0
  84. package/apps/desktop/application/spatial-compositor-cadence.ts +23 -0
  85. package/apps/desktop/application/spatial-project-authority.ts +35 -0
  86. package/apps/desktop/application/spatial-project-lease.ts +126 -0
  87. package/apps/desktop/application/spatial-project-render-preparation.ts +225 -0
  88. package/apps/desktop/application/spatial-project-store.ts +286 -0
  89. package/apps/desktop/application/spatial-render-projection.ts +201 -0
  90. package/apps/desktop/application/spatial-render.ts +677 -0
  91. package/apps/desktop/application/spatial-spz.ts +44 -0
  92. package/apps/desktop/application/spatial-world-import.ts +135 -0
  93. package/apps/desktop/application/spatial-world-provenance.ts +46 -0
  94. package/apps/desktop/application/studio-port.ts +58 -0
  95. package/apps/desktop/application/verified-receipt-reconciliation.ts +1574 -0
  96. package/apps/desktop/capture/protocol.ts +689 -0
  97. package/apps/desktop/cli/analyzer.ts +406 -0
  98. package/apps/desktop/cli/args.ts +3181 -0
  99. package/apps/desktop/cli/asset-ingest.ts +747 -0
  100. package/apps/desktop/cli/atomic-render-effects.ts +210 -0
  101. package/apps/desktop/cli/atomic-render-platform.ts +205 -0
  102. package/apps/desktop/cli/atomic-render.ts +50 -0
  103. package/apps/desktop/cli/audio-analysis.ts +252 -0
  104. package/apps/desktop/cli/bounded-map.ts +39 -0
  105. package/apps/desktop/cli/bundle-service.ts +256 -0
  106. package/apps/desktop/cli/capabilities.ts +212 -0
  107. package/apps/desktop/cli/capture-bundle.ts +1117 -0
  108. package/apps/desktop/cli/command-host-resources.ts +251 -0
  109. package/apps/desktop/cli/commands.ts +6836 -0
  110. package/apps/desktop/cli/directing-blob.ts +291 -0
  111. package/apps/desktop/cli/directing-media.ts +517 -0
  112. package/apps/desktop/cli/directing-quote.ts +89 -0
  113. package/apps/desktop/cli/directing-service.ts +288 -0
  114. package/apps/desktop/cli/emoji-assets.ts +538 -0
  115. package/apps/desktop/cli/errors.ts +29 -0
  116. package/apps/desktop/cli/face-analysis-service.ts +940 -0
  117. package/apps/desktop/cli/filter-script.ts +76 -0
  118. package/apps/desktop/cli/gateway-application-port.ts +1142 -0
  119. package/apps/desktop/cli/gateway-credential.ts +81 -0
  120. package/apps/desktop/cli/gateway-media-artifacts.ts +723 -0
  121. package/apps/desktop/cli/gateway-media-catalog.ts +922 -0
  122. package/apps/desktop/cli/gateway-media-service.ts +3106 -0
  123. package/apps/desktop/cli/gateway-media-signature.ts +122 -0
  124. package/apps/desktop/cli/gateway-provider-options.ts +562 -0
  125. package/apps/desktop/cli/gateway-scene-provider.ts +391 -0
  126. package/apps/desktop/cli/help.ts +501 -0
  127. package/apps/desktop/cli/html-overlay-gpu.ts +97 -0
  128. package/apps/desktop/cli/html-overlay-renderer.ts +2478 -0
  129. package/apps/desktop/cli/io.ts +290 -0
  130. package/apps/desktop/cli/main.ts +123 -0
  131. package/apps/desktop/cli/media-effects-service.ts +885 -0
  132. package/apps/desktop/cli/media-ingest-model.ts +364 -0
  133. package/apps/desktop/cli/media-ingest-platform.ts +227 -0
  134. package/apps/desktop/cli/media-ingest-program.ts +200 -0
  135. package/apps/desktop/cli/media-ingest.ts +30 -0
  136. package/apps/desktop/cli/music-analysis-service.ts +434 -0
  137. package/apps/desktop/cli/mutation-lock.ts +530 -0
  138. package/apps/desktop/cli/native-assets.d.ts +12 -0
  139. package/apps/desktop/cli/native-media-runtime.macos.ts +7 -0
  140. package/apps/desktop/cli/overlay-playback.ts +177 -0
  141. package/apps/desktop/cli/paths.ts +265 -0
  142. package/apps/desktop/cli/portable-surface.ts +242 -0
  143. package/apps/desktop/cli/project-camera-receipt.ts +112 -0
  144. package/apps/desktop/cli/project-face-camera.ts +394 -0
  145. package/apps/desktop/cli/project-inactivity-service.ts +552 -0
  146. package/apps/desktop/cli/project-media-integrity.ts +121 -0
  147. package/apps/desktop/cli/project-renderer.ts +1526 -0
  148. package/apps/desktop/cli/project-service.ts +519 -0
  149. package/apps/desktop/cli/project-state-transaction.ts +520 -0
  150. package/apps/desktop/cli/query-limits.ts +1 -0
  151. package/apps/desktop/cli/recording-controller.ts +1567 -0
  152. package/apps/desktop/cli/recording-daemon.ts +931 -0
  153. package/apps/desktop/cli/recording-ref.ts +56 -0
  154. package/apps/desktop/cli/renamed-environment.ts +6 -0
  155. package/apps/desktop/cli/renderer.ts +1439 -0
  156. package/apps/desktop/cli/run-cli-test-helper.ts +60 -0
  157. package/apps/desktop/cli/scene-analysis-service.ts +879 -0
  158. package/apps/desktop/cli/spatial-compositor-verifier.ts +213 -0
  159. package/apps/desktop/cli/spatial-project-service.ts +90 -0
  160. package/apps/desktop/cli/spatial-scene-service.ts +105 -0
  161. package/apps/desktop/cli/spatial-world-service.ts +39 -0
  162. package/apps/desktop/cli/speech-analysis-service.ts +682 -0
  163. package/apps/desktop/cli/studio-assemble.ts +58 -0
  164. package/apps/desktop/cli/studio-command.ts +60 -0
  165. package/apps/desktop/cli/studio-custody.ts +60 -0
  166. package/apps/desktop/cli/studio-encode.ts +216 -0
  167. package/apps/desktop/cli/studio-exr.ts +52 -0
  168. package/apps/desktop/cli/studio-files.ts +179 -0
  169. package/apps/desktop/cli/studio-output-validation.ts +162 -0
  170. package/apps/desktop/cli/studio-process.ts +145 -0
  171. package/apps/desktop/cli/studio-runtime.ts +77 -0
  172. package/apps/desktop/cli/studio-scaffold.ts +70 -0
  173. package/apps/desktop/cli/studio-service.ts +301 -0
  174. package/apps/desktop/cli/studio-spatial-asset.ts +248 -0
  175. package/apps/desktop/cli/studio-template-names.ts +3 -0
  176. package/apps/desktop/cli/studio-workflow.ts +19 -0
  177. package/apps/desktop/cli/time.ts +25 -0
  178. package/apps/desktop/cli/workflow-code.ts +584 -0
  179. package/apps/desktop/cli/workflow-runs.ts +576 -0
  180. package/apps/desktop/code/advanced.ts +139 -0
  181. package/apps/desktop/code/application-node-planner.ts +1288 -0
  182. package/apps/desktop/code/compiler.ts +329 -0
  183. package/apps/desktop/code/contracts.ts +13 -0
  184. package/apps/desktop/code/creative-recipes.ts +94 -0
  185. package/apps/desktop/code/define-workflow.ts +115 -0
  186. package/apps/desktop/code/editing.ts +284 -0
  187. package/apps/desktop/code/file-candidate-provenance.ts +420 -0
  188. package/apps/desktop/code/file-candidate.ts +69 -0
  189. package/apps/desktop/code/graph-builder.ts +10 -0
  190. package/apps/desktop/code/host-resource-policy.ts +55 -0
  191. package/apps/desktop/code/plan-contracts.ts +159 -0
  192. package/apps/desktop/code/planning.ts +361 -0
  193. package/apps/desktop/code/public.ts +134 -0
  194. package/apps/desktop/code/reference-projection.ts +62 -0
  195. package/apps/desktop/code/run-contracts.ts +466 -0
  196. package/apps/desktop/code/run-store.ts +2478 -0
  197. package/apps/desktop/code/runtime-identity.ts +328 -0
  198. package/apps/desktop/code/scheduler.ts +3067 -0
  199. package/apps/desktop/code/semantic-builder.ts +1436 -0
  200. package/apps/desktop/code/source-bundle.ts +1641 -0
  201. package/apps/desktop/code/source-typecheck.ts +211 -0
  202. package/apps/desktop/code/testing.ts +79 -0
  203. package/apps/desktop/code/worker-client.ts +1675 -0
  204. package/apps/desktop/code/worker-effects.ts +141 -0
  205. package/apps/desktop/code/worker-entry.ts +435 -0
  206. package/apps/desktop/code/worker-lease-guardian.ts +179 -0
  207. package/apps/desktop/code/worker-process-identity.ts +484 -0
  208. package/apps/desktop/code/worker-protocol.ts +211 -0
  209. package/apps/desktop/code/workflow-effects.ts +132 -0
  210. package/apps/desktop/contracts/analysis.ts +949 -0
  211. package/apps/desktop/contracts/edit.ts +576 -0
  212. package/apps/desktop/contracts/index.ts +9 -0
  213. package/apps/desktop/contracts/media-effects.ts +139 -0
  214. package/apps/desktop/contracts/project-render.ts +644 -0
  215. package/apps/desktop/contracts/project.ts +740 -0
  216. package/apps/desktop/contracts/recording.ts +1434 -0
  217. package/apps/desktop/contracts/render.ts +334 -0
  218. package/apps/desktop/contracts/runtime.ts +155 -0
  219. package/apps/desktop/contracts/spatial-compositor.ts +58 -0
  220. package/apps/desktop/contracts/spatial-project.ts +159 -0
  221. package/apps/desktop/contracts/spatial-world.ts +48 -0
  222. package/apps/desktop/contracts/video-effects.ts +120 -0
  223. package/apps/desktop/core/alignment-apply.ts +400 -0
  224. package/apps/desktop/core/audio-alignment.ts +466 -0
  225. package/apps/desktop/core/auto-zoom.ts +209 -0
  226. package/apps/desktop/core/canonical-json.ts +14 -0
  227. package/apps/desktop/core/emoji.ts +210 -0
  228. package/apps/desktop/core/events.ts +409 -0
  229. package/apps/desktop/core/face-framing.ts +533 -0
  230. package/apps/desktop/core/face-tracks.ts +326 -0
  231. package/apps/desktop/core/inactivity.ts +137 -0
  232. package/apps/desktop/core/index.ts +30 -0
  233. package/apps/desktop/core/intervals.ts +123 -0
  234. package/apps/desktop/core/montage.ts +236 -0
  235. package/apps/desktop/core/music-analysis.ts +518 -0
  236. package/apps/desktop/core/plan.ts +244 -0
  237. package/apps/desktop/core/project-auto-zoom.ts +75 -0
  238. package/apps/desktop/core/project-camera-spatial.ts +676 -0
  239. package/apps/desktop/core/project-camera.ts +403 -0
  240. package/apps/desktop/core/project-captions.ts +888 -0
  241. package/apps/desktop/core/project-inactivity.ts +125 -0
  242. package/apps/desktop/core/project-metadata.ts +927 -0
  243. package/apps/desktop/core/project-plan.ts +375 -0
  244. package/apps/desktop/core/project-render-plan.ts +303 -0
  245. package/apps/desktop/core/project-time.ts +469 -0
  246. package/apps/desktop/core/render-plan.ts +442 -0
  247. package/apps/desktop/core/scene-cache.ts +174 -0
  248. package/apps/desktop/core/scene-sampling.ts +448 -0
  249. package/apps/desktop/core/spatial-compositor.ts +22 -0
  250. package/apps/desktop/core/spatial-durability.ts +99 -0
  251. package/apps/desktop/core/spatial-project.ts +169 -0
  252. package/apps/desktop/core/spatial-shot-clock.ts +32 -0
  253. package/apps/desktop/core/speech-analysis.ts +333 -0
  254. package/apps/desktop/core/storage.ts +970 -0
  255. package/apps/desktop/core/time-map.ts +91 -0
  256. package/apps/desktop/core/time.ts +61 -0
  257. package/apps/desktop/core/video-effects.ts +660 -0
  258. package/apps/desktop/dist/cli/NebulaSans-Bold-26se8aek.otf +0 -0
  259. package/apps/desktop/dist/cli/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  260. package/apps/desktop/dist/cli/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  261. package/apps/desktop/dist/cli/NebulaSans-Book-8cenzchw.otf +0 -0
  262. package/apps/desktop/dist/cli/main.js +5086 -0
  263. package/apps/desktop/html-overlay/catalog.ts +171 -0
  264. package/apps/desktop/html-overlay/contracts.ts +372 -0
  265. package/apps/desktop/html-overlay/execution-profile.ts +77 -0
  266. package/apps/desktop/html-overlay/index.ts +98 -0
  267. package/apps/desktop/html-overlay/libraries.ts +263 -0
  268. package/apps/desktop/html-overlay/random.ts +49 -0
  269. package/apps/desktop/html-overlay/runtime.ts +813 -0
  270. package/apps/desktop/html-overlay/scaffolds.ts +792 -0
  271. package/apps/desktop/html-overlay/spark-runtime.ts +88 -0
  272. package/apps/desktop/html-overlay/spatial-splat-runtime.ts +60 -0
  273. package/apps/desktop/html-overlay/spatial.ts +720 -0
  274. package/apps/desktop/studio/assets/README.md +39 -0
  275. package/apps/desktop/studio/assets/contracts.ts +129 -0
  276. package/apps/desktop/studio/assets/network.ts +64 -0
  277. package/apps/desktop/studio/assets/poly-haven.ts +148 -0
  278. package/apps/desktop/studio/drivers/blender_driver.py +466 -0
  279. package/apps/desktop/studio/drivers/cadquery_driver.py +176 -0
  280. package/apps/desktop/studio/education/driver.py +308 -0
  281. package/apps/desktop/studio/education/lesson.py +180 -0
  282. package/apps/desktop/studio/education/toolkit.py +249 -0
  283. package/apps/desktop/workflows/chaptered-demo.ts +48 -0
  284. package/apps/desktop/workflows/creative-iteration.ts +107 -0
  285. package/apps/desktop/workflows/creative-selection.ts +92 -0
  286. package/apps/desktop/workflows/fragments.ts +486 -0
  287. package/apps/desktop/workflows/index.ts +93 -0
  288. package/apps/desktop/workflows/polished-screen-demo.ts +128 -0
  289. package/apps/desktop/workflows/scene-direction.ts +12 -0
  290. package/apps/desktop/workflows/social-variants.ts +75 -0
  291. package/apps/desktop/workflows/talking-head-cleanup.ts +53 -0
  292. package/dist/NebulaSans-Bold-26se8aek.otf +0 -0
  293. package/dist/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  294. package/dist/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  295. package/dist/NebulaSans-Book-8cenzchw.otf +0 -0
  296. package/dist/cli.js +479 -0
  297. package/dist/code/advanced.js +270 -0
  298. package/dist/code/index.js +2240 -0
  299. package/dist/generate.js +24 -0
  300. package/dist/host-resources.js +26 -0
  301. package/dist/index-42zsesc1.js +2257 -0
  302. package/dist/index-7308egqr.js +48 -0
  303. package/dist/index-8txs6fkn.js +712 -0
  304. package/dist/index-mcy8z0br.js +192 -0
  305. package/dist/index-p63wavx0.js +2029 -0
  306. package/dist/index-r7gdhmsp.js +424 -0
  307. package/dist/index-sh6xbav6.js +1219 -0
  308. package/dist/index-z1w83f81.js +4 -0
  309. package/dist/index-z7239b4h.js +2363 -0
  310. package/dist/index-zfeg9hmw.js +2013 -0
  311. package/dist/index.js +241 -0
  312. package/dist/operations.js +29 -0
  313. package/dist/skill-install-dh0ntqba.js +10 -0
  314. package/dist/vectorize/worker.js +142 -0
  315. package/dist/workflow.js +16 -0
  316. package/docs/README.md +38 -0
  317. package/docs/studio.md +215 -0
  318. package/examples/studio/blender/README.md +26 -0
  319. package/examples/studio/blender/character.py +152 -0
  320. package/examples/studio/blender/cloth.py +101 -0
  321. package/examples/studio/blender/color_chart.py +29 -0
  322. package/examples/studio/blender/fluid.py +78 -0
  323. package/examples/studio/blender/import_model.py +74 -0
  324. package/examples/studio/blender/product.py +53 -0
  325. package/examples/studio/blender/shaded_street.py +481 -0
  326. package/examples/studio/blender/studio_scene.py +147 -0
  327. package/examples/studio/cadquery/README.md +13 -0
  328. package/examples/studio/cadquery/bracket.py +36 -0
  329. package/examples/studio/cadquery/import_step.py +12 -0
  330. package/examples/studio/education/README.md +46 -0
  331. package/examples/studio/education/lesson.json +99 -0
  332. package/examples/studio/education/scene.py +7 -0
  333. package/examples/studio/hybrid-scene.ts +96 -0
  334. package/examples/studio/native-workflow.ts +14 -0
  335. package/examples/studio/vgpu/README.md +64 -0
  336. package/examples/studio/vgpu/heat-field.mjs +290 -0
  337. package/package.json +304 -0
  338. package/packages/scene/src/index.ts +1 -0
  339. package/packages/scene/src/scene.ts +215 -0
  340. package/schema/diagram.schema.json +334 -0
  341. package/skills/slopcamera/SKILL.md +45 -0
  342. package/skills/slopcamera/agents/openai.yaml +4 -0
  343. package/skills/slopcamera/references/customization.md +77 -0
  344. package/skills/slopcamera/references/diagrams.md +153 -0
  345. package/skills/slopcamera/references/directed-scenes.md +42 -0
  346. package/skills/slopcamera/references/directing-video.md +18 -0
  347. package/skills/slopcamera/references/educational-video.md +26 -0
  348. package/skills/slopcamera/references/gateway-media.md +134 -0
  349. package/skills/slopcamera/references/install.md +62 -0
  350. package/skills/slopcamera/references/native-studio.md +31 -0
  351. package/skills/slopcamera/references/reference-led-3d.md +140 -0
  352. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-1.jpg +0 -0
  353. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-2.jpg +0 -0
  354. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-1.png +0 -0
  355. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-2.png +0 -0
  356. package/skills/slopcamera/references/rubber-stamp-field-notes.md +144 -0
  357. package/skills/slopcamera/references/vectorization.md +29 -0
  358. package/skills/slopcamera/references/video-projects.md +187 -0
  359. package/skills/slopcamera/references/visual-communication.md +120 -0
  360. package/skills/slopcamera/references/workflows-sdk.md +45 -0
  361. package/skills/slopcamera/scripts/compose-rubber-stamp-field-note.ts +229 -0
  362. package/src/artifacts.ts +106 -0
  363. package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
  364. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
  365. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
  366. package/src/assets/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
  367. package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
  368. package/src/assets/fonts/nebula-sans/PROVENANCE.md +21 -0
  369. package/src/assets.d.ts +9 -0
  370. package/src/cli.ts +601 -0
  371. package/src/cloud-errors.ts +26 -0
  372. package/src/code/advanced.ts +10 -0
  373. package/src/code/boundary.ts +21 -0
  374. package/src/code/canonical-json.ts +129 -0
  375. package/src/code/compiler.ts +953 -0
  376. package/src/code/contracts.ts +576 -0
  377. package/src/code/define-workflow.ts +334 -0
  378. package/src/code/errors.ts +41 -0
  379. package/src/code/graph-builder.ts +945 -0
  380. package/src/code/index.ts +121 -0
  381. package/src/code/json-snapshot.ts +899 -0
  382. package/src/code/json-utf8.ts +88 -0
  383. package/src/code/portable-builder.ts +136 -0
  384. package/src/code/projection.ts +378 -0
  385. package/src/code/public-operations.ts +450 -0
  386. package/src/code/runtime.ts +580 -0
  387. package/src/code/sha256.ts +36 -0
  388. package/src/config.ts +180 -0
  389. package/src/desktop.ts +253 -0
  390. package/src/fs.ts +10 -0
  391. package/src/generate.ts +704 -0
  392. package/src/host-resource-posix.ts +161 -0
  393. package/src/host-resources.ts +1749 -0
  394. package/src/icons.ts +58 -0
  395. package/src/index.ts +200 -0
  396. package/src/label-layout.ts +130 -0
  397. package/src/layout.ts +413 -0
  398. package/src/lint.ts +127 -0
  399. package/src/mcp/boundary.ts +354 -0
  400. package/src/mcp/index.ts +33 -0
  401. package/src/mcp/server.ts +272 -0
  402. package/src/mcp/tools.ts +963 -0
  403. package/src/mcp/types.ts +71 -0
  404. package/src/operations.ts +775 -0
  405. package/src/parse.ts +861 -0
  406. package/src/process-environment.ts +17 -0
  407. package/src/render.ts +437 -0
  408. package/src/skill-install.ts +49 -0
  409. package/src/spatial-scene/authoring.ts +27 -0
  410. package/src/spatial-scene/camera-track.ts +83 -0
  411. package/src/spatial-scene/contracts.ts +277 -0
  412. package/src/spatial-scene/evaluate.ts +145 -0
  413. package/src/spatial-scene/gltf.ts +558 -0
  414. package/src/spatial-scene/identity.ts +260 -0
  415. package/src/spatial-scene/index.ts +10 -0
  416. package/src/spatial-scene/inspect.ts +68 -0
  417. package/src/spatial-scene/math.ts +280 -0
  418. package/src/spatial-scene/patch.ts +131 -0
  419. package/src/spatial-scene/time.ts +53 -0
  420. package/src/studio/contracts.ts +129 -0
  421. package/src/studio/index.ts +2 -0
  422. package/src/studio/plan.ts +156 -0
  423. package/src/studio/shared.ts +41 -0
  424. package/src/theme.ts +68 -0
  425. package/src/tldr.ts +474 -0
  426. package/src/types.ts +212 -0
  427. package/src/vectorize/archive.ts +101 -0
  428. package/src/vectorize/command.ts +797 -0
  429. package/src/vectorize/index.ts +27 -0
  430. package/src/vectorize/limits.ts +61 -0
  431. package/src/vectorize/metrics.ts +351 -0
  432. package/src/vectorize/pixels.ts +323 -0
  433. package/src/vectorize/supervisor.ts +339 -0
  434. package/src/vectorize/svg.ts +188 -0
  435. package/src/vectorize/tool.ts +542 -0
  436. package/src/vectorize/types.ts +113 -0
  437. package/src/vectorize/vectorize.ts +634 -0
  438. package/src/vectorize/worker-protocol.ts +40 -0
  439. package/src/vectorize/worker.ts +209 -0
  440. package/src/version.ts +1 -0
  441. package/src/workflow.ts +409 -0
@@ -0,0 +1,899 @@
1
+ import type { JsonValue } from "./contracts.js"
2
+ import { SlopcameraCodeError } from "./errors.js"
3
+ import { jsonStringUtf8ByteLength } from "./json-utf8.js"
4
+ import { createSha256HexHasher } from "./sha256.js"
5
+
6
+ export interface BoundedJsonSnapshot {
7
+ readonly bytes: number
8
+ readonly canonicalText?: string
9
+ readonly sha256: string
10
+ readonly value: JsonValue
11
+ readonly values: number
12
+ }
13
+
14
+ export interface BoundedJsonValueSnapshot {
15
+ readonly bytes: number
16
+ readonly value: JsonValue
17
+ readonly values: number
18
+ }
19
+
20
+ export interface BoundedCanonicalFingerprint {
21
+ readonly bytes: number
22
+ readonly sha256: string
23
+ }
24
+
25
+ export interface JsonSnapshotLimits {
26
+ readonly captureCanonicalText?: boolean
27
+ readonly maximumBytes?: number
28
+ readonly maximumDepth?: number
29
+ readonly maximumValues?: number
30
+ }
31
+
32
+ const DEFAULT_MAXIMUM_DEPTH = 128
33
+ const DEFAULT_MAXIMUM_VALUES = 1_000_000
34
+ const HASH_BUFFER_CODE_UNITS = 64 * 1024
35
+
36
+ type MutableJsonContainer = JsonValue[] | Record<string, JsonValue>
37
+ type StringPropertyDescriptors = Readonly<
38
+ Record<string, PropertyDescriptor | undefined>
39
+ >
40
+
41
+ interface Assignment {
42
+ readonly key: number | string
43
+ readonly target: MutableJsonContainer
44
+ }
45
+
46
+ interface VisitFrame {
47
+ readonly assignment?: Assignment
48
+ readonly depth: number
49
+ readonly kind: "visit"
50
+ readonly value: unknown
51
+ }
52
+
53
+ interface ArrayFrame {
54
+ readonly clone: JsonValue[] | undefined
55
+ readonly depth: number
56
+ readonly descriptors: StringPropertyDescriptors
57
+ readonly index: number
58
+ readonly kind: "array"
59
+ readonly source: object
60
+ readonly values: number
61
+ }
62
+
63
+ interface ObjectFrame {
64
+ readonly clone: Record<string, JsonValue> | undefined
65
+ readonly depth: number
66
+ readonly descriptors: StringPropertyDescriptors
67
+ readonly index: number
68
+ readonly keys: readonly string[]
69
+ readonly kind: "object"
70
+ readonly source: object
71
+ }
72
+
73
+ type SnapshotFrame = ArrayFrame | ObjectFrame | VisitFrame
74
+
75
+ interface BoundedJsonCapture {
76
+ readonly bytes: number
77
+ readonly canonicalText?: string
78
+ readonly sha256?: string
79
+ readonly value?: JsonValue
80
+ readonly values: number
81
+ }
82
+
83
+ interface JsonCaptureOptions {
84
+ readonly captureText: boolean
85
+ readonly captureValue: boolean
86
+ readonly hash: boolean
87
+ readonly hashPrefix?: string
88
+ /** Preserve canonicalJson's released Array.map domain without cloning extras. */
89
+ readonly ignoreNonIndexArrayProperties: boolean
90
+ }
91
+
92
+ interface FreezeFrame {
93
+ readonly exiting: boolean
94
+ readonly value: unknown
95
+ }
96
+
97
+ type MutableStructureContainer = unknown[] | Record<string, unknown>
98
+
99
+ interface StructureAssignment {
100
+ readonly key: number | string
101
+ readonly target: MutableStructureContainer
102
+ }
103
+
104
+ interface StructureVisitFrame {
105
+ readonly assignment?: StructureAssignment
106
+ readonly depth: number
107
+ readonly kind: "visit"
108
+ readonly value: unknown
109
+ }
110
+
111
+ interface StructureExitFrame {
112
+ readonly kind: "exit"
113
+ readonly source: object
114
+ }
115
+
116
+ type StructureFrame = StructureExitFrame | StructureVisitFrame
117
+
118
+ function invalidJson(
119
+ message: string,
120
+ details?: Readonly<Record<string, unknown>>,
121
+ ): never {
122
+ throw new SlopcameraCodeError("invalid-data", message, details)
123
+ }
124
+
125
+ function positiveLimit(value: number, name: string): number {
126
+ if (!Number.isSafeInteger(value) || value < 1) {
127
+ return invalidJson(`${name} must be a positive safe integer.`)
128
+ }
129
+ return value
130
+ }
131
+
132
+ function enumerableSymbolDescriptor(
133
+ descriptors: object,
134
+ ): boolean {
135
+ return Object.getOwnPropertySymbols(descriptors).some(
136
+ symbol => (Reflect.get(descriptors, symbol) as PropertyDescriptor | undefined)
137
+ ?.enumerable === true,
138
+ )
139
+ }
140
+
141
+ /**
142
+ * Bounds recursive plain-container structure before an extensible user schema
143
+ * runs. Foreign class instances and unsupported scalar kinds remain atomic so
144
+ * schema preprocessors and transforms retain their established input contract.
145
+ */
146
+ export function captureJsonStructure(
147
+ input: unknown,
148
+ name: string,
149
+ limits: JsonSnapshotLimits = {},
150
+ ): unknown {
151
+ const maximumDepth = positiveLimit(
152
+ limits.maximumDepth ?? DEFAULT_MAXIMUM_DEPTH,
153
+ "JSON structure depth limit",
154
+ )
155
+ const maximumValues = positiveLimit(
156
+ limits.maximumValues ?? DEFAULT_MAXIMUM_VALUES,
157
+ "JSON structure value limit",
158
+ )
159
+ const maximumBytes = limits.maximumBytes === undefined
160
+ ? undefined
161
+ : positiveLimit(limits.maximumBytes, "JSON structure byte limit")
162
+ const active = new WeakSet<object>()
163
+ const pending: StructureFrame[] = [{ depth: 0, kind: "visit", value: input }]
164
+ let bytes = 0
165
+ let discoveredValues = 1
166
+ let root: unknown
167
+ let rootAssigned = false
168
+ const addBytes = (additional: number): void => {
169
+ if (maximumBytes === undefined) return
170
+ if (additional > maximumBytes - bytes) {
171
+ return invalidJson(
172
+ `${name} contains more than ${String(maximumBytes)} bytes.`,
173
+ { actualLowerBound: bytes + additional, maximumBytes },
174
+ )
175
+ }
176
+ bytes += additional
177
+ }
178
+ const addJsonString = (value: string): void => {
179
+ if (maximumBytes === undefined) return
180
+ const additional = jsonStringUtf8ByteLength(value, maximumBytes - bytes)
181
+ if (additional === undefined) {
182
+ return invalidJson(
183
+ `${name} contains more than ${String(maximumBytes)} bytes.`,
184
+ { actualLowerBound: maximumBytes + 1, maximumBytes },
185
+ )
186
+ }
187
+ bytes += additional
188
+ }
189
+ const discover = (additional: number): void => {
190
+ if (additional > maximumValues - discoveredValues) {
191
+ return invalidJson(
192
+ `${name} contains more than ${String(maximumValues)} structural values.`,
193
+ { actualLowerBound: discoveredValues + additional, maximumValues },
194
+ )
195
+ }
196
+ discoveredValues += additional
197
+ }
198
+ const assign = (assignment: StructureAssignment | undefined, value: unknown): void => {
199
+ if (assignment === undefined) {
200
+ root = value
201
+ rootAssigned = true
202
+ return
203
+ }
204
+ Object.defineProperty(assignment.target, assignment.key, {
205
+ configurable: true,
206
+ enumerable: true,
207
+ value,
208
+ writable: true,
209
+ })
210
+ }
211
+
212
+ try {
213
+ while (pending.length > 0) {
214
+ const frame = pending.pop()
215
+ if (frame === undefined) continue
216
+ if (frame.kind === "exit") {
217
+ active.delete(frame.source)
218
+ continue
219
+ }
220
+ if (frame.depth > maximumDepth) {
221
+ return invalidJson(
222
+ `${name} nesting exceeds ${String(maximumDepth)} levels.`,
223
+ { actual: frame.depth, maximumDepth },
224
+ )
225
+ }
226
+ const value = frame.value
227
+ if (typeof value === "string") {
228
+ addJsonString(value)
229
+ assign(frame.assignment, value)
230
+ continue
231
+ }
232
+ if (value === null) {
233
+ addBytes(4)
234
+ assign(frame.assignment, value)
235
+ continue
236
+ }
237
+ if (typeof value === "boolean") {
238
+ addBytes(value ? 4 : 5)
239
+ assign(frame.assignment, value)
240
+ continue
241
+ }
242
+ if (typeof value === "number") {
243
+ if (Number.isFinite(value)) {
244
+ addBytes(JSON.stringify(Object.is(value, -0) ? 0 : value).length)
245
+ }
246
+ assign(frame.assignment, value)
247
+ continue
248
+ }
249
+ if (typeof value !== "object") {
250
+ assign(frame.assignment, value)
251
+ continue
252
+ }
253
+ const isArray = Array.isArray(value)
254
+ if (!isArray) {
255
+ const prototype: unknown = Object.getPrototypeOf(value)
256
+ if (prototype !== Object.prototype && prototype !== null) {
257
+ assign(frame.assignment, value)
258
+ continue
259
+ }
260
+ }
261
+ if (active.has(value)) {
262
+ return invalidJson(`${name} does not support cyclic plain-container values.`)
263
+ }
264
+
265
+ const arrayLength = isArray ? (value as unknown[]).length : undefined
266
+ if (arrayLength !== undefined && arrayLength > maximumValues - discoveredValues) {
267
+ return invalidJson(
268
+ `${name} contains more than ${String(maximumValues)} structural values.`,
269
+ { actualLowerBound: discoveredValues + arrayLength, maximumValues },
270
+ )
271
+ }
272
+ const descriptors = Object.getOwnPropertyDescriptors(value)
273
+ if (Object.getOwnPropertySymbols(descriptors).some(
274
+ symbol => (Reflect.get(descriptors, symbol) as PropertyDescriptor | undefined)
275
+ ?.enumerable === true,
276
+ )) {
277
+ return invalidJson(`${name} cannot contain enumerable symbol properties.`)
278
+ }
279
+ const keys = Object.keys(descriptors)
280
+ .filter(key => descriptors[key]?.enumerable === true)
281
+ const namedArrayKeys = arrayLength === undefined
282
+ ? []
283
+ : keys.filter((key) => {
284
+ const index = Number(key)
285
+ return !(
286
+ Number.isSafeInteger(index)
287
+ && index >= 0
288
+ && index < arrayLength
289
+ && String(index) === key
290
+ )
291
+ })
292
+ if (arrayLength !== undefined) {
293
+ discover(arrayLength)
294
+ discover(namedArrayKeys.length)
295
+ addBytes(2 + Math.max(0, arrayLength - 1))
296
+ for (const key of namedArrayKeys) {
297
+ addJsonString(key)
298
+ addBytes(1)
299
+ }
300
+ } else {
301
+ discover(keys.length)
302
+ addBytes(2 + Math.max(0, keys.length - 1))
303
+ for (const key of keys) {
304
+ addJsonString(key)
305
+ addBytes(1)
306
+ }
307
+ }
308
+
309
+ const clone: MutableStructureContainer = arrayLength === undefined
310
+ ? {}
311
+ : new Array<unknown>(arrayLength)
312
+ assign(frame.assignment, clone)
313
+ active.add(value)
314
+ pending.push({ kind: "exit", source: value })
315
+ for (let index = keys.length - 1; index >= 0; index -= 1) {
316
+ const key = keys[index]
317
+ if (key === undefined) continue
318
+ const descriptor = descriptors[key]
319
+ if (
320
+ descriptor === undefined
321
+ || descriptor.get !== undefined
322
+ || descriptor.set !== undefined
323
+ ) {
324
+ return invalidJson(`${name} properties must be plain data properties.`)
325
+ }
326
+ const numericKey = arrayLength !== undefined
327
+ && Number.isSafeInteger(Number(key))
328
+ && Number(key) >= 0
329
+ && Number(key) < arrayLength
330
+ && String(Number(key)) === key
331
+ ? Number(key)
332
+ : key
333
+ pending.push({
334
+ assignment: { key: numericKey, target: clone },
335
+ depth: frame.depth + 1,
336
+ kind: "visit",
337
+ value: descriptor.value,
338
+ })
339
+ }
340
+ }
341
+ } catch (error) {
342
+ if (error instanceof SlopcameraCodeError) throw error
343
+ throw new SlopcameraCodeError(
344
+ "invalid-data",
345
+ `${name} could not be safely inspected.`,
346
+ { cause: error instanceof Error ? error.message : String(error) },
347
+ )
348
+ }
349
+
350
+ if (!rootAssigned) {
351
+ return invalidJson(`${name} did not contain a capturable value.`)
352
+ }
353
+ return root
354
+ }
355
+
356
+ export function preflightJsonStructure(
357
+ input: unknown,
358
+ name: string,
359
+ limits: JsonSnapshotLimits = {},
360
+ ): void {
361
+ void captureJsonStructure(input, name, limits)
362
+ }
363
+
364
+ function assignValue(
365
+ assignment: Assignment | undefined,
366
+ value: JsonValue,
367
+ setRoot: (root: JsonValue) => void,
368
+ ): void {
369
+ if (assignment === undefined) {
370
+ setRoot(value)
371
+ return
372
+ }
373
+ if (Array.isArray(assignment.target)) {
374
+ assignment.target[assignment.key as number] = value
375
+ } else {
376
+ Object.defineProperty(assignment.target, assignment.key, {
377
+ configurable: true,
378
+ enumerable: true,
379
+ value,
380
+ writable: true,
381
+ })
382
+ }
383
+ }
384
+
385
+ function scalarJson(value: boolean | null | number | string): string {
386
+ if (typeof value === "number") {
387
+ if (!Number.isFinite(value)) {
388
+ return invalidJson("JSON snapshots do not support non-finite numbers.")
389
+ }
390
+ return JSON.stringify(Object.is(value, -0) ? 0 : value)
391
+ }
392
+ return JSON.stringify(value)
393
+ }
394
+
395
+ /**
396
+ * Validates, clones, canonically hashes, byte-bounds, and freezes JSON without
397
+ * recursive parsing or traversal. Object accessors are rejected from their
398
+ * descriptors and are never invoked.
399
+ */
400
+ function captureBoundedJson(
401
+ input: unknown,
402
+ maximumBytesInput: number,
403
+ name: string,
404
+ limits: JsonSnapshotLimits = {},
405
+ capture: JsonCaptureOptions,
406
+ ): BoundedJsonCapture {
407
+ const maximumBytes = positiveLimit(maximumBytesInput, "JSON snapshot byte limit")
408
+ const maximumDepth = positiveLimit(
409
+ limits.maximumDepth ?? DEFAULT_MAXIMUM_DEPTH,
410
+ "JSON snapshot depth limit",
411
+ )
412
+ const maximumValues = positiveLimit(
413
+ limits.maximumValues ?? DEFAULT_MAXIMUM_VALUES,
414
+ "JSON snapshot value limit",
415
+ )
416
+ const active = new WeakSet<object>()
417
+ const hash = capture.hash ? createSha256HexHasher() : undefined
418
+ let hashPart = capture.hashPrefix ?? ""
419
+ const flushHash = (): void => {
420
+ if (hash === undefined || hashPart.length === 0) return
421
+ hash.update(hashPart)
422
+ hashPart = ""
423
+ }
424
+ const captureCanonicalText = capture.captureText
425
+ const canonicalParts: string[] = []
426
+ let canonicalPart = ""
427
+ const pending: SnapshotFrame[] = [{ depth: 0, kind: "visit", value: input }]
428
+ let bytes = 0
429
+ let discoveredValues = 1
430
+ let root: JsonValue | undefined
431
+
432
+ const append = (text: string, exactBytes = text.length): void => {
433
+ if (exactBytes > maximumBytes - bytes) {
434
+ return invalidJson(
435
+ `${name} contains more than ${String(maximumBytes)} bytes.`,
436
+ { actualLowerBound: bytes + exactBytes, maximumBytes },
437
+ )
438
+ }
439
+ if (hash !== undefined) {
440
+ hashPart += text
441
+ if (hashPart.length >= HASH_BUFFER_CODE_UNITS) flushHash()
442
+ }
443
+ if (captureCanonicalText) {
444
+ canonicalPart += text
445
+ if (canonicalPart.length >= 64 * 1024) {
446
+ canonicalParts.push(canonicalPart)
447
+ canonicalPart = ""
448
+ }
449
+ }
450
+ bytes += exactBytes
451
+ }
452
+ const discover = (additional: number): void => {
453
+ if (additional > maximumValues - discoveredValues) {
454
+ return invalidJson(
455
+ `${name} contains more than ${String(maximumValues)} JSON values.`,
456
+ { actualLowerBound: discoveredValues + additional, maximumValues },
457
+ )
458
+ }
459
+ discoveredValues += additional
460
+ }
461
+ const setRoot = (value: JsonValue): void => {
462
+ root = value
463
+ }
464
+
465
+ try {
466
+ while (pending.length > 0) {
467
+ const frame = pending.pop()
468
+ if (frame === undefined) break
469
+
470
+ if (frame.kind === "array") {
471
+ if (frame.index === frame.values) {
472
+ append("]")
473
+ active.delete(frame.source)
474
+ if (frame.clone !== undefined) Object.freeze(frame.clone)
475
+ continue
476
+ }
477
+ if (frame.index > 0) append(",")
478
+ const descriptor = frame.descriptors[String(frame.index)]
479
+ if (
480
+ descriptor === undefined
481
+ || descriptor.get !== undefined
482
+ || descriptor.set !== undefined
483
+ ) {
484
+ return invalidJson(
485
+ `${name} arrays must contain plain data elements.`,
486
+ )
487
+ }
488
+ pending.push({ ...frame, index: frame.index + 1 })
489
+ pending.push({
490
+ ...(frame.clone === undefined
491
+ ? {}
492
+ : { assignment: { key: frame.index, target: frame.clone } }),
493
+ depth: frame.depth + 1,
494
+ kind: "visit",
495
+ value: descriptor.value,
496
+ })
497
+ continue
498
+ }
499
+
500
+ if (frame.kind === "object") {
501
+ if (frame.index === frame.keys.length) {
502
+ append("}")
503
+ active.delete(frame.source)
504
+ if (frame.clone !== undefined) Object.freeze(frame.clone)
505
+ continue
506
+ }
507
+ const key = frame.keys[frame.index]
508
+ if (key === undefined) {
509
+ return invalidJson(`${name} lost an object key during traversal.`)
510
+ }
511
+ if (frame.index > 0) append(",")
512
+ const keyBytes = jsonStringUtf8ByteLength(key, maximumBytes - bytes)
513
+ if (keyBytes === undefined) {
514
+ return invalidJson(
515
+ `${name} contains more than ${String(maximumBytes)} bytes.`,
516
+ { actualLowerBound: maximumBytes + 1, maximumBytes },
517
+ )
518
+ }
519
+ append(scalarJson(key), keyBytes)
520
+ append(":")
521
+ const descriptor = frame.descriptors[key]
522
+ if (
523
+ descriptor === undefined
524
+ || descriptor.get !== undefined
525
+ || descriptor.set !== undefined
526
+ ) {
527
+ return invalidJson(`${name} properties must be plain data properties.`)
528
+ }
529
+ pending.push({ ...frame, index: frame.index + 1 })
530
+ pending.push({
531
+ ...(frame.clone === undefined
532
+ ? {}
533
+ : { assignment: { key, target: frame.clone } }),
534
+ depth: frame.depth + 1,
535
+ kind: "visit",
536
+ value: descriptor.value,
537
+ })
538
+ continue
539
+ }
540
+
541
+ if (frame.depth > maximumDepth) {
542
+ return invalidJson(
543
+ `${name} nesting exceeds ${String(maximumDepth)} levels.`,
544
+ { actual: frame.depth, maximumDepth },
545
+ )
546
+ }
547
+ const value = frame.value
548
+ if (
549
+ value === null
550
+ || typeof value === "boolean"
551
+ || typeof value === "number"
552
+ || typeof value === "string"
553
+ ) {
554
+ const normalized = typeof value === "number" && Object.is(value, -0)
555
+ ? 0
556
+ : value
557
+ if (typeof value === "string") {
558
+ const stringBytes = jsonStringUtf8ByteLength(
559
+ value,
560
+ maximumBytes - bytes,
561
+ )
562
+ if (stringBytes === undefined) {
563
+ return invalidJson(
564
+ `${name} contains more than ${String(maximumBytes)} bytes.`,
565
+ { actualLowerBound: maximumBytes + 1, maximumBytes },
566
+ )
567
+ }
568
+ append(scalarJson(value), stringBytes)
569
+ } else {
570
+ append(scalarJson(value))
571
+ }
572
+ if (capture.captureValue) {
573
+ assignValue(frame.assignment, normalized, setRoot)
574
+ }
575
+ continue
576
+ }
577
+ if (typeof value !== "object") {
578
+ return invalidJson(`${name} does not support ${typeof value} values.`)
579
+ }
580
+ if (active.has(value)) {
581
+ return invalidJson(`${name} does not support cyclic values.`)
582
+ }
583
+ const arrayLength = Array.isArray(value) ? value.length : undefined
584
+ if (
585
+ arrayLength !== undefined
586
+ && arrayLength > maximumValues - discoveredValues
587
+ ) {
588
+ return invalidJson(
589
+ `${name} contains more than ${String(maximumValues)} JSON values.`,
590
+ {
591
+ actualLowerBound: discoveredValues + arrayLength,
592
+ maximumValues,
593
+ },
594
+ )
595
+ }
596
+ if (arrayLength !== undefined) {
597
+ const punctuationBytes = 2 + Math.max(0, arrayLength - 1)
598
+ if (punctuationBytes > maximumBytes - bytes) {
599
+ return invalidJson(
600
+ `${name} contains more than ${String(maximumBytes)} bytes.`,
601
+ { actualLowerBound: bytes + punctuationBytes, maximumBytes },
602
+ )
603
+ }
604
+ }
605
+ active.add(value)
606
+
607
+ if (Array.isArray(value)) {
608
+ const length = arrayLength
609
+ if (length === undefined) {
610
+ return invalidJson(`${name} lost an array length during traversal.`)
611
+ }
612
+ discoveredValues += length
613
+ const descriptors = Object.getOwnPropertyDescriptors(value)
614
+ if (!capture.ignoreNonIndexArrayProperties) {
615
+ if (enumerableSymbolDescriptor(descriptors)) {
616
+ return invalidJson(`${name} cannot contain enumerable symbol properties.`)
617
+ }
618
+ const keys = Object.keys(descriptors)
619
+ .filter(key => descriptors[key]?.enumerable === true)
620
+ if (
621
+ keys.length !== length
622
+ || keys.some((key, index) => key !== String(index))
623
+ ) {
624
+ return invalidJson(
625
+ `${name} arrays must be dense and cannot have named properties.`,
626
+ )
627
+ }
628
+ }
629
+ const clone = capture.captureValue ? [] as JsonValue[] : undefined
630
+ if (clone !== undefined) assignValue(frame.assignment, clone, setRoot)
631
+ append("[")
632
+ pending.push({
633
+ clone,
634
+ depth: frame.depth,
635
+ descriptors,
636
+ index: 0,
637
+ kind: "array",
638
+ source: value,
639
+ values: length,
640
+ })
641
+ continue
642
+ }
643
+
644
+ const prototype: unknown = Object.getPrototypeOf(value)
645
+ if (prototype !== Object.prototype && prototype !== null) {
646
+ return invalidJson(`${name} accepts only arrays and plain objects.`)
647
+ }
648
+ const descriptors = Object.getOwnPropertyDescriptors(value)
649
+ if (enumerableSymbolDescriptor(descriptors)) {
650
+ return invalidJson(`${name} cannot contain enumerable symbol properties.`)
651
+ }
652
+ const keys = Object.keys(descriptors)
653
+ .filter(key => descriptors[key]?.enumerable === true)
654
+ .sort()
655
+ discover(keys.length)
656
+ const clone: Record<string, JsonValue> | undefined = capture.captureValue
657
+ ? {}
658
+ : undefined
659
+ if (clone !== undefined) assignValue(frame.assignment, clone, setRoot)
660
+ append("{")
661
+ pending.push({
662
+ clone,
663
+ depth: frame.depth,
664
+ descriptors,
665
+ index: 0,
666
+ keys,
667
+ kind: "object",
668
+ source: value,
669
+ })
670
+ }
671
+ } catch (error) {
672
+ if (error instanceof SlopcameraCodeError) throw error
673
+ throw new SlopcameraCodeError(
674
+ "invalid-data",
675
+ `${name} could not be safely inspected.`,
676
+ { cause: error instanceof Error ? error.message : String(error) },
677
+ )
678
+ }
679
+
680
+ if (capture.captureValue && root === undefined) {
681
+ return invalidJson(`${name} did not contain a JSON value.`)
682
+ }
683
+ if (captureCanonicalText && canonicalPart.length > 0) {
684
+ canonicalParts.push(canonicalPart)
685
+ }
686
+ flushHash()
687
+ return Object.freeze({
688
+ bytes,
689
+ ...(captureCanonicalText
690
+ ? { canonicalText: canonicalParts.join("") }
691
+ : {}),
692
+ ...(hash === undefined ? {} : { sha256: hash.digestHex() }),
693
+ ...(capture.captureValue ? { value: root as JsonValue } : {}),
694
+ values: discoveredValues,
695
+ })
696
+ }
697
+
698
+ export function createBoundedJsonValueSnapshot(
699
+ input: unknown,
700
+ maximumBytesInput: number,
701
+ name: string,
702
+ limits: JsonSnapshotLimits = {},
703
+ ): BoundedJsonValueSnapshot {
704
+ const captured = captureBoundedJson(input, maximumBytesInput, name, limits, {
705
+ captureText: false,
706
+ captureValue: true,
707
+ hash: false,
708
+ ignoreNonIndexArrayProperties: false,
709
+ })
710
+ if (captured.value === undefined) {
711
+ return invalidJson(`${name} did not produce a complete JSON value snapshot.`)
712
+ }
713
+ return captured as BoundedJsonValueSnapshot
714
+ }
715
+
716
+ export function createBoundedJsonSnapshot(
717
+ input: unknown,
718
+ maximumBytesInput: number,
719
+ name: string,
720
+ limits: JsonSnapshotLimits = {},
721
+ ): BoundedJsonSnapshot {
722
+ const captured = captureBoundedJson(input, maximumBytesInput, name, limits, {
723
+ captureText: limits.captureCanonicalText === true,
724
+ captureValue: true,
725
+ hash: true,
726
+ ignoreNonIndexArrayProperties: false,
727
+ })
728
+ if (captured.sha256 === undefined || captured.value === undefined) {
729
+ return invalidJson(`${name} did not produce a complete JSON snapshot.`)
730
+ }
731
+ return captured as BoundedJsonSnapshot
732
+ }
733
+
734
+ export function createBoundedCanonicalJson(
735
+ input: unknown,
736
+ maximumBytesInput: number,
737
+ name: string,
738
+ limits: JsonSnapshotLimits = {},
739
+ ): string {
740
+ const captured = captureBoundedJson(input, maximumBytesInput, name, limits, {
741
+ captureText: true,
742
+ captureValue: false,
743
+ hash: false,
744
+ ignoreNonIndexArrayProperties: true,
745
+ })
746
+ if (captured.canonicalText === undefined) {
747
+ return invalidJson(`${name} did not produce canonical JSON text.`)
748
+ }
749
+ return captured.canonicalText
750
+ }
751
+
752
+ export function createBoundedCanonicalFingerprint(
753
+ input: unknown,
754
+ maximumBytesInput: number,
755
+ name: string,
756
+ limits: JsonSnapshotLimits = {},
757
+ hashPrefix?: string,
758
+ ): BoundedCanonicalFingerprint {
759
+ const captured = captureBoundedJson(input, maximumBytesInput, name, limits, {
760
+ captureText: false,
761
+ captureValue: false,
762
+ hash: true,
763
+ ignoreNonIndexArrayProperties: true,
764
+ ...(hashPrefix === undefined ? {} : { hashPrefix }),
765
+ })
766
+ if (captured.sha256 === undefined) {
767
+ return invalidJson(`${name} did not produce a canonical JSON identity.`)
768
+ }
769
+ return Object.freeze({ bytes: captured.bytes, sha256: captured.sha256 })
770
+ }
771
+
772
+ export function createBoundedCanonicalSha256(
773
+ input: unknown,
774
+ maximumBytesInput: number,
775
+ name: string,
776
+ limits: JsonSnapshotLimits = {},
777
+ hashPrefix?: string,
778
+ ): string {
779
+ return createBoundedCanonicalFingerprint(
780
+ input,
781
+ maximumBytesInput,
782
+ name,
783
+ limits,
784
+ hashPrefix,
785
+ ).sha256
786
+ }
787
+
788
+ /**
789
+ * Deeply freezes an already JSON-shaped value without consuming the call stack.
790
+ * This is kept for internal values whose byte identity was established earlier.
791
+ */
792
+ export function deepFreezeJson<Value>(value: Value): Value {
793
+ const active = new WeakSet<object>()
794
+ const completed = new WeakSet<object>()
795
+ const pending: FreezeFrame[] = [{ exiting: false, value }]
796
+
797
+ try {
798
+ while (pending.length > 0) {
799
+ const item = pending.pop()
800
+ if (item === undefined) break
801
+ const current = item.value
802
+ if (
803
+ current === null
804
+ || typeof current === "boolean"
805
+ || typeof current === "string"
806
+ ) {
807
+ continue
808
+ }
809
+ if (typeof current === "number") {
810
+ if (!Number.isFinite(current)) {
811
+ return invalidJson("JSON snapshots do not support non-finite numbers.")
812
+ }
813
+ continue
814
+ }
815
+ if (typeof current !== "object") {
816
+ return invalidJson(`JSON snapshots do not support ${typeof current} values.`)
817
+ }
818
+ if (item.exiting) {
819
+ active.delete(current)
820
+ completed.add(current)
821
+ Object.freeze(current)
822
+ continue
823
+ }
824
+ if (completed.has(current)) continue
825
+ if (active.has(current)) {
826
+ return invalidJson("JSON snapshots do not support cycles.")
827
+ }
828
+ active.add(current)
829
+ pending.push({ exiting: true, value: current })
830
+ if (Array.isArray(current)) {
831
+ const length = current.length
832
+ const descriptors = Object.getOwnPropertyDescriptors(current)
833
+ if (enumerableSymbolDescriptor(descriptors)) {
834
+ return invalidJson("JSON snapshots cannot contain enumerable symbol properties.")
835
+ }
836
+ const keys = Object.keys(descriptors)
837
+ .filter(key => descriptors[key]?.enumerable === true)
838
+ if (
839
+ keys.length !== length
840
+ || keys.some((key, index) => key !== String(index))
841
+ ) {
842
+ return invalidJson(
843
+ "JSON snapshot arrays must be dense and cannot have named properties.",
844
+ )
845
+ }
846
+ for (let index = length - 1; index >= 0; index -= 1) {
847
+ const descriptor = descriptors[String(index)]
848
+ if (
849
+ descriptor === undefined
850
+ || descriptor.get !== undefined
851
+ || descriptor.set !== undefined
852
+ ) {
853
+ return invalidJson(
854
+ "JSON snapshot arrays must contain plain data elements.",
855
+ )
856
+ }
857
+ pending.push({ exiting: false, value: descriptor.value })
858
+ }
859
+ continue
860
+ }
861
+
862
+ const prototype: unknown = Object.getPrototypeOf(current)
863
+ if (prototype !== Object.prototype && prototype !== null) {
864
+ return invalidJson("JSON snapshots accept only arrays and plain objects.")
865
+ }
866
+ const descriptors = Object.getOwnPropertyDescriptors(current)
867
+ if (enumerableSymbolDescriptor(descriptors)) {
868
+ return invalidJson("JSON snapshots cannot contain enumerable symbol properties.")
869
+ }
870
+ const keys = Object.keys(descriptors)
871
+ .filter(key => descriptors[key]?.enumerable === true)
872
+ for (let index = keys.length - 1; index >= 0; index -= 1) {
873
+ const key = keys[index]
874
+ if (key === undefined) continue
875
+ const descriptor = descriptors[key]
876
+ if (
877
+ descriptor === undefined
878
+ || descriptor.get !== undefined
879
+ || descriptor.set !== undefined
880
+ ) {
881
+ return invalidJson("JSON snapshot properties must be plain data properties.")
882
+ }
883
+ if (descriptor.value === undefined) {
884
+ return invalidJson(`JSON snapshot property ${key} is undefined.`)
885
+ }
886
+ pending.push({ exiting: false, value: descriptor.value })
887
+ }
888
+ }
889
+ } catch (error) {
890
+ if (error instanceof SlopcameraCodeError) throw error
891
+ throw new SlopcameraCodeError(
892
+ "invalid-data",
893
+ "JSON snapshot could not be safely inspected.",
894
+ { cause: error instanceof Error ? error.message : String(error) },
895
+ )
896
+ }
897
+
898
+ return value
899
+ }