@simulatte/doppler 0.1.5 → 0.1.7

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 (392) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +25 -17
  3. package/package.json +20 -4
  4. package/src/adapters/adapter-registry.js +12 -1
  5. package/src/adapters/lora-loader.js +23 -6
  6. package/src/bridge/extension-client.d.ts +5 -0
  7. package/src/bridge/extension-client.js +40 -0
  8. package/src/bridge/index.d.ts +2 -1
  9. package/src/bridge/index.js +6 -4
  10. package/src/browser/browser-converter.js +26 -1
  11. package/src/browser/file-picker.js +6 -0
  12. package/src/browser/safetensors-parser-browser.js +84 -1
  13. package/src/browser/shard-io-browser.js +2 -2
  14. package/src/browser/tensor-source-download.js +8 -2
  15. package/src/browser/tensor-source-http.d.ts +1 -0
  16. package/src/browser/tensor-source-http.js +5 -1
  17. package/src/client/doppler-api.browser.js +20 -4
  18. package/src/client/doppler-api.js +19 -3
  19. package/src/client/doppler-provider/generation.js +12 -0
  20. package/src/client/doppler-provider/model-manager.d.ts +10 -0
  21. package/src/client/doppler-provider/model-manager.js +91 -19
  22. package/src/client/doppler-provider/source-runtime.d.ts +2 -1
  23. package/src/client/doppler-provider/source-runtime.js +132 -13
  24. package/src/client/doppler-registry.json +8 -7
  25. package/src/config/backward-registry-loader.js +17 -2
  26. package/src/config/execution-v0-contract-check.js +113 -15
  27. package/src/config/kernel-path-contract-check.js +57 -29
  28. package/src/config/kernel-path-loader.js +5 -36
  29. package/src/config/kernels/kernel-ref-digests.js +39 -39
  30. package/src/config/kernels/registry.js +14 -1
  31. package/src/config/kernels/registry.json +49 -7
  32. package/src/config/loader.d.ts +1 -1
  33. package/src/config/loader.js +43 -4
  34. package/src/config/merge-contract-check.js +59 -4
  35. package/src/config/merge-helpers.js +128 -7
  36. package/src/config/merge.d.ts +1 -0
  37. package/src/config/merge.js +28 -0
  38. package/src/config/param-validator.js +47 -2
  39. package/src/config/presets/kernel-paths/{gemma2-q4k-dequant-f32a.json → gemma2-q4k-dequant-f32a-nosubgroups.json} +3 -3
  40. package/src/config/presets/kernel-paths/gemma3-f16-fused-f32a-online-streamingprefill.json +223 -0
  41. package/src/config/presets/kernel-paths/{gemma3-q4k-dequant-f32a.json → gemma3-q4k-dequant-f32a-nosubgroups.json} +3 -3
  42. package/src/config/presets/kernel-paths/registry.json +29 -8
  43. package/src/config/presets/models/gemma2.json +2 -2
  44. package/src/config/presets/models/qwen3.json +9 -2
  45. package/src/config/presets/models/transformer.json +5 -0
  46. package/src/config/presets/runtime/experiments/bench/gemma3-bench-q4k.json +1 -1
  47. package/src/config/presets/runtime/experiments/debug/gemma3-debug-q4k.json +1 -1
  48. package/src/config/presets/runtime/experiments/verify/gemma3-verify.json +1 -1
  49. package/src/config/presets/runtime/kernels/dequant-f16-q4k.json +6 -13
  50. package/src/config/presets/runtime/kernels/dequant-f32-q4k.json +6 -13
  51. package/src/config/presets/runtime/kernels/embeddinggemma-q4k-dequant-f32a.json +37 -0
  52. package/src/config/presets/runtime/kernels/fused-q4k.json +6 -13
  53. package/src/config/presets/runtime/kernels/gemma2-q4k-dequant-f16a.json +33 -0
  54. package/src/config/presets/runtime/kernels/gemma2-q4k-dequant-f32a-nosubgroups.json +33 -0
  55. package/src/config/presets/runtime/kernels/gemma2-q4k-fused-f32a.json +33 -0
  56. package/src/config/presets/runtime/kernels/safe-q4k.json +6 -13
  57. package/src/config/presets/runtime/platform/metal-apple-q4k.json +1 -1
  58. package/src/config/required-inference-fields-contract-check.js +6 -0
  59. package/src/config/runtime.js +6 -1
  60. package/src/config/schema/debug.schema.d.ts +5 -0
  61. package/src/config/schema/doppler.schema.js +16 -21
  62. package/src/config/schema/inference-defaults.schema.js +6 -3
  63. package/src/config/schema/inference.schema.d.ts +9 -0
  64. package/src/config/schema/kernel-path.schema.d.ts +11 -1
  65. package/src/config/schema/kernel-thresholds.schema.js +12 -4
  66. package/src/config/schema/manifest.schema.d.ts +8 -1
  67. package/src/config/schema/manifest.schema.js +19 -3
  68. package/src/config/training-defaults.js +30 -22
  69. package/src/converter/conversion-plan.js +94 -9
  70. package/src/converter/core.d.ts +7 -0
  71. package/src/converter/core.js +14 -9
  72. package/src/converter/execution-v0-manifest.js +4 -1
  73. package/src/converter/index.d.ts +1 -0
  74. package/src/converter/index.js +1 -0
  75. package/src/converter/manifest-inference.js +43 -12
  76. package/src/converter/parsers/diffusion.js +0 -3
  77. package/src/converter/quantization-info.js +35 -15
  78. package/src/converter/rope-config.js +42 -0
  79. package/src/converter/shard-packer.d.ts +1 -1
  80. package/src/converter/shard-packer.js +4 -1
  81. package/src/debug/config.js +123 -11
  82. package/src/debug/signals.js +7 -1
  83. package/src/debug/tensor.d.ts +2 -0
  84. package/src/debug/tensor.js +13 -2
  85. package/src/distribution/p2p-control-plane.js +52 -12
  86. package/src/distribution/p2p-observability.js +43 -7
  87. package/src/distribution/p2p-webrtc-browser.js +20 -0
  88. package/src/distribution/shard-delivery.js +77 -26
  89. package/src/formats/gguf/types.js +33 -16
  90. package/src/formats/rdrr/groups.d.ts +12 -4
  91. package/src/formats/rdrr/groups.js +3 -6
  92. package/src/formats/rdrr/parsing.js +39 -2
  93. package/src/formats/rdrr/types.d.ts +2 -1
  94. package/src/gpu/command-recorder.js +86 -61
  95. package/src/gpu/device.d.ts +1 -0
  96. package/src/gpu/device.js +131 -19
  97. package/src/gpu/kernel-tuner/benchmarks.js +326 -316
  98. package/src/gpu/kernel-tuner/cache.js +71 -4
  99. package/src/gpu/kernel-tuner/tuner.js +22 -4
  100. package/src/gpu/kernels/attention.js +113 -34
  101. package/src/gpu/kernels/backward/adam.js +62 -58
  102. package/src/gpu/kernels/backward/attention_backward.js +257 -169
  103. package/src/gpu/kernels/backward/conv2d_backward.js +14 -1
  104. package/src/gpu/kernels/bias_add.wgsl +8 -6
  105. package/src/gpu/kernels/bias_add_f16.wgsl +8 -5
  106. package/src/gpu/kernels/cast.js +191 -149
  107. package/src/gpu/kernels/check-stop.js +33 -44
  108. package/src/gpu/kernels/conv2d.js +27 -17
  109. package/src/gpu/kernels/conv2d.wgsl +7 -8
  110. package/src/gpu/kernels/conv2d_f16.wgsl +7 -8
  111. package/src/gpu/kernels/cross_entropy_loss.js +21 -15
  112. package/src/gpu/kernels/depthwise_conv2d.js +37 -26
  113. package/src/gpu/kernels/depthwise_conv2d.wgsl +6 -9
  114. package/src/gpu/kernels/depthwise_conv2d_f16.wgsl +6 -9
  115. package/src/gpu/kernels/dequant.js +178 -126
  116. package/src/gpu/kernels/energy.d.ts +3 -21
  117. package/src/gpu/kernels/energy.js +111 -88
  118. package/src/gpu/kernels/feature-check.js +1 -1
  119. package/src/gpu/kernels/fused_ffn.js +84 -65
  120. package/src/gpu/kernels/fused_matmul_residual.js +56 -33
  121. package/src/gpu/kernels/fused_matmul_rmsnorm.js +62 -45
  122. package/src/gpu/kernels/gather.js +33 -15
  123. package/src/gpu/kernels/gelu.js +19 -11
  124. package/src/gpu/kernels/grouped_pointwise_conv2d.js +34 -23
  125. package/src/gpu/kernels/grouped_pointwise_conv2d.wgsl +6 -9
  126. package/src/gpu/kernels/grouped_pointwise_conv2d_f16.wgsl +6 -9
  127. package/src/gpu/kernels/groupnorm.js +34 -23
  128. package/src/gpu/kernels/kv-quantize.js +5 -2
  129. package/src/gpu/kernels/layernorm.js +35 -19
  130. package/src/gpu/kernels/logit-merge.js +5 -3
  131. package/src/gpu/kernels/matmul.js +83 -39
  132. package/src/gpu/kernels/modulate.js +23 -15
  133. package/src/gpu/kernels/moe.js +221 -175
  134. package/src/gpu/kernels/pixel_shuffle.js +22 -14
  135. package/src/gpu/kernels/pixel_shuffle.wgsl +4 -5
  136. package/src/gpu/kernels/pixel_shuffle_f16.wgsl +4 -5
  137. package/src/gpu/kernels/relu.js +31 -10
  138. package/src/gpu/kernels/relu.wgsl +2 -1
  139. package/src/gpu/kernels/relu_f16.wgsl +2 -1
  140. package/src/gpu/kernels/repeat_channels.js +25 -17
  141. package/src/gpu/kernels/repeat_channels.wgsl +4 -5
  142. package/src/gpu/kernels/repeat_channels_f16.wgsl +4 -5
  143. package/src/gpu/kernels/residual.js +69 -23
  144. package/src/gpu/kernels/residual.wgsl +6 -3
  145. package/src/gpu/kernels/residual_f16.wgsl +2 -1
  146. package/src/gpu/kernels/residual_f16_vec4.wgsl +2 -1
  147. package/src/gpu/kernels/residual_vec4.wgsl +2 -1
  148. package/src/gpu/kernels/rmsnorm.js +96 -28
  149. package/src/gpu/kernels/rmsnorm.wgsl +14 -6
  150. package/src/gpu/kernels/rmsnorm_f16.wgsl +10 -2
  151. package/src/gpu/kernels/rope.d.ts +2 -0
  152. package/src/gpu/kernels/rope.js +14 -1
  153. package/src/gpu/kernels/rope.wgsl +56 -40
  154. package/src/gpu/kernels/sample.js +27 -38
  155. package/src/gpu/kernels/sana_linear_attention.js +19 -12
  156. package/src/gpu/kernels/sana_linear_attention_apply.wgsl +4 -5
  157. package/src/gpu/kernels/sana_linear_attention_apply_f16.wgsl +4 -5
  158. package/src/gpu/kernels/sana_linear_attention_summary.wgsl +4 -0
  159. package/src/gpu/kernels/sana_linear_attention_summary_f16.wgsl +4 -0
  160. package/src/gpu/kernels/scale.js +18 -11
  161. package/src/gpu/kernels/shader-cache.js +4 -2
  162. package/src/gpu/kernels/silu.d.ts +1 -0
  163. package/src/gpu/kernels/silu.js +148 -82
  164. package/src/gpu/kernels/silu.wgsl +19 -9
  165. package/src/gpu/kernels/silu_f16.wgsl +19 -9
  166. package/src/gpu/kernels/softmax.js +44 -25
  167. package/src/gpu/kernels/split_qkv.js +23 -13
  168. package/src/gpu/kernels/transpose.js +31 -10
  169. package/src/gpu/kernels/transpose.wgsl +6 -5
  170. package/src/gpu/kernels/upsample2d.js +22 -13
  171. package/src/gpu/kernels/upsample2d.wgsl +6 -9
  172. package/src/gpu/kernels/upsample2d_f16.wgsl +6 -9
  173. package/src/gpu/kernels/utils.js +35 -13
  174. package/src/gpu/partitioned-buffer-pool.js +10 -2
  175. package/src/gpu/perf-guards.js +2 -9
  176. package/src/gpu/profiler.js +27 -22
  177. package/src/gpu/readback-utils.d.ts +16 -0
  178. package/src/gpu/readback-utils.js +41 -0
  179. package/src/gpu/submit-tracker.js +13 -0
  180. package/src/gpu/uniform-cache.d.ts +1 -0
  181. package/src/gpu/uniform-cache.js +30 -9
  182. package/src/hotswap/intent-bundle.js +6 -0
  183. package/src/hotswap/manifest.d.ts +10 -1
  184. package/src/hotswap/manifest.js +12 -2
  185. package/src/hotswap/runtime.js +30 -8
  186. package/src/index-browser.d.ts +44 -0
  187. package/src/index-browser.js +14 -0
  188. package/src/inference/browser-harness-contract-helpers.d.ts +5 -0
  189. package/src/inference/browser-harness-contract-helpers.js +28 -0
  190. package/src/inference/browser-harness-diffusion-energy-suites.d.ts +2 -0
  191. package/src/inference/browser-harness-diffusion-energy-suites.js +269 -0
  192. package/src/inference/browser-harness-model-helpers.d.ts +16 -0
  193. package/src/inference/browser-harness-model-helpers.js +217 -0
  194. package/src/inference/browser-harness-report-helpers.d.ts +7 -0
  195. package/src/inference/browser-harness-report-helpers.js +42 -0
  196. package/src/inference/browser-harness-runtime-helpers.d.ts +61 -0
  197. package/src/inference/browser-harness-runtime-helpers.js +415 -0
  198. package/src/inference/browser-harness-suite-helpers.d.ts +28 -0
  199. package/src/inference/browser-harness-suite-helpers.js +268 -0
  200. package/src/inference/browser-harness-text-helpers.d.ts +27 -0
  201. package/src/inference/browser-harness-text-helpers.js +788 -0
  202. package/src/inference/browser-harness.d.ts +6 -0
  203. package/src/inference/browser-harness.js +130 -1950
  204. package/src/inference/kv-cache/base.js +140 -94
  205. package/src/inference/kv-cache/tiered.js +5 -3
  206. package/src/inference/moe-router.js +88 -56
  207. package/src/inference/multi-model-network.js +5 -3
  208. package/src/inference/network-evolution.d.ts +11 -2
  209. package/src/inference/network-evolution.js +20 -21
  210. package/src/inference/pipelines/context.d.ts +3 -0
  211. package/src/inference/pipelines/context.js +142 -2
  212. package/src/inference/pipelines/diffusion/helpers.js +7 -2
  213. package/src/inference/pipelines/diffusion/pipeline.js +17 -7
  214. package/src/inference/pipelines/diffusion/sd3-transformer.js +10 -10
  215. package/src/inference/pipelines/diffusion/text-encoder-gpu.d.ts +5 -0
  216. package/src/inference/pipelines/diffusion/text-encoder-gpu.js +27 -15
  217. package/src/inference/pipelines/diffusion/vae.js +3 -7
  218. package/src/inference/pipelines/energy/pipeline.js +27 -21
  219. package/src/inference/pipelines/energy/quintel.d.ts +5 -0
  220. package/src/inference/pipelines/energy/quintel.js +11 -0
  221. package/src/inference/pipelines/energy-head/row-head-pipeline.js +17 -13
  222. package/src/inference/pipelines/structured/json-head-pipeline.js +26 -11
  223. package/src/inference/pipelines/text/attention/projections.js +151 -101
  224. package/src/inference/pipelines/text/attention/record.js +73 -10
  225. package/src/inference/pipelines/text/attention/run.js +73 -10
  226. package/src/inference/pipelines/text/chat-format.js +25 -1
  227. package/src/inference/pipelines/text/config.d.ts +4 -0
  228. package/src/inference/pipelines/text/config.js +71 -5
  229. package/src/inference/pipelines/text/embed.js +2 -8
  230. package/src/inference/pipelines/text/execution-plan.js +64 -50
  231. package/src/inference/pipelines/text/execution-v0-contract-helpers.d.ts +59 -0
  232. package/src/inference/pipelines/text/execution-v0-contract-helpers.js +937 -0
  233. package/src/inference/pipelines/text/execution-v0-runtime-builders.d.ts +15 -0
  234. package/src/inference/pipelines/text/execution-v0-runtime-builders.js +279 -0
  235. package/src/inference/pipelines/text/execution-v0.js +78 -1002
  236. package/src/inference/pipelines/text/ffn/standard.js +3 -0
  237. package/src/inference/pipelines/text/generator-steps.d.ts +46 -0
  238. package/src/inference/pipelines/text/generator-steps.js +298 -207
  239. package/src/inference/pipelines/text/generator.js +6 -23
  240. package/src/inference/pipelines/text/init.d.ts +4 -0
  241. package/src/inference/pipelines/text/init.js +134 -29
  242. package/src/inference/pipelines/text/kernel-path-auto-select.js +2 -0
  243. package/src/inference/pipelines/text/kernel-trace.d.ts +2 -0
  244. package/src/inference/pipelines/text/kernel-trace.js +6 -0
  245. package/src/inference/pipelines/text/layer.js +14 -9
  246. package/src/inference/pipelines/text/linear-attention.d.ts +10 -0
  247. package/src/inference/pipelines/text/linear-attention.js +80 -6
  248. package/src/inference/pipelines/text/logits/gpu.js +10 -5
  249. package/src/inference/pipelines/text/logits/index.js +10 -11
  250. package/src/inference/pipelines/text/logits/utils.d.ts +7 -0
  251. package/src/inference/pipelines/text/logits/utils.js +9 -0
  252. package/src/inference/pipelines/text/lora-apply.js +50 -32
  253. package/src/inference/pipelines/text/model-load.js +279 -104
  254. package/src/inference/pipelines/text/moe-cache.js +5 -4
  255. package/src/inference/pipelines/text/moe-cpu-gptoss.js +74 -69
  256. package/src/inference/pipelines/text/moe-cpu.js +42 -38
  257. package/src/inference/pipelines/text/moe-gpu.js +110 -86
  258. package/src/inference/pipelines/text/ops.js +90 -90
  259. package/src/inference/pipelines/text/probes.js +9 -9
  260. package/src/inference/pipelines/text/weights.js +17 -7
  261. package/src/inference/pipelines/text.js +17 -1
  262. package/src/inference/speculative.d.ts +2 -2
  263. package/src/inference/speculative.js +4 -18
  264. package/src/inference/test-harness.d.ts +1 -1
  265. package/src/inference/test-harness.js +15 -5
  266. package/src/inference/tokenizer.d.ts +0 -5
  267. package/src/inference/tokenizer.js +4 -23
  268. package/src/inference/tokenizers/bpe.js +9 -0
  269. package/src/inference/tokenizers/bundled.js +176 -33
  270. package/src/inference/tokenizers/sentencepiece.js +12 -0
  271. package/src/loader/doppler-loader.js +38 -22
  272. package/src/loader/dtype-utils.js +3 -44
  273. package/src/loader/embedding-loader.js +7 -3
  274. package/src/loader/experts/expert-cache.js +13 -6
  275. package/src/loader/experts/expert-loader.js +10 -6
  276. package/src/loader/final-weights-loader.js +8 -4
  277. package/src/loader/layer-loader.js +2 -1
  278. package/src/loader/loader-state.js +2 -2
  279. package/src/loader/memory-monitor.js +8 -0
  280. package/src/loader/multi-model-loader.d.ts +14 -0
  281. package/src/loader/multi-model-loader.js +70 -24
  282. package/src/loader/shard-cache.js +81 -12
  283. package/src/loader/shard-resolver.js +25 -3
  284. package/src/loader/tensors/tensor-loader.js +209 -144
  285. package/src/loader/tensors/tensor-reader.js +76 -19
  286. package/src/loader/weight-downcast.js +1 -1
  287. package/src/memory/buffer-pool.d.ts +9 -1
  288. package/src/memory/buffer-pool.js +109 -44
  289. package/src/memory/unified-detect.js +1 -1
  290. package/src/rules/inference/kernel-path.rules.json +24 -8
  291. package/src/rules/rule-registry.js +25 -1
  292. package/src/rules/tooling/command-runtime.rules.json +18 -0
  293. package/src/storage/backends/opfs-store.js +68 -24
  294. package/src/storage/downloader.js +364 -83
  295. package/src/storage/index.d.ts +3 -0
  296. package/src/storage/index.js +3 -0
  297. package/src/storage/preflight.d.ts +2 -2
  298. package/src/storage/preflight.js +24 -2
  299. package/src/storage/quickstart-downloader.js +11 -5
  300. package/src/storage/registry.js +10 -4
  301. package/src/storage/reports.js +1 -1
  302. package/src/storage/shard-manager.d.ts +15 -1
  303. package/src/storage/shard-manager.js +51 -3
  304. package/src/storage/source-artifact-store.d.ts +52 -0
  305. package/src/storage/source-artifact-store.js +234 -0
  306. package/src/tooling/command-api-constants.d.ts +9 -0
  307. package/src/tooling/command-api-constants.js +9 -0
  308. package/src/tooling/command-api-family-normalizers.d.ts +9 -0
  309. package/src/tooling/command-api-family-normalizers.js +343 -0
  310. package/src/tooling/command-api-helpers.d.ts +25 -0
  311. package/src/tooling/command-api-helpers.js +262 -0
  312. package/src/tooling/command-api.d.ts +27 -1
  313. package/src/tooling/command-api.js +26 -473
  314. package/src/tooling/command-envelope.js +4 -1
  315. package/src/tooling/command-runner-shared.js +52 -18
  316. package/src/tooling/lean-execution-contract.js +150 -3
  317. package/src/tooling/node-browser-command-runner.d.ts +4 -0
  318. package/src/tooling/node-browser-command-runner.js +218 -273
  319. package/src/tooling/node-command-runner.js +44 -3
  320. package/src/tooling/node-converter.js +27 -1
  321. package/src/tooling/node-source-runtime.d.ts +1 -1
  322. package/src/tooling/node-source-runtime.js +84 -3
  323. package/src/tooling/node-webgpu.js +30 -105
  324. package/src/tooling/opfs-cache.js +21 -4
  325. package/src/tooling/runtime-input-composition.d.ts +38 -0
  326. package/src/tooling/runtime-input-composition.js +86 -0
  327. package/src/tooling/source-runtime-bundle.d.ts +40 -5
  328. package/src/tooling/source-runtime-bundle.js +261 -34
  329. package/src/tooling/source-runtime-materializer.d.ts +6 -0
  330. package/src/tooling/source-runtime-materializer.js +93 -0
  331. package/src/training/attention-backward.js +32 -17
  332. package/src/training/autograd.js +80 -52
  333. package/src/training/checkpoint-watch.d.ts +8 -0
  334. package/src/training/checkpoint-watch.js +139 -0
  335. package/src/training/checkpoint.d.ts +6 -1
  336. package/src/training/checkpoint.js +46 -7
  337. package/src/training/clip.js +2 -1
  338. package/src/training/datasets/token-batch.js +20 -8
  339. package/src/training/distillation/artifacts.d.ts +71 -0
  340. package/src/training/distillation/artifacts.js +132 -0
  341. package/src/training/distillation/checkpoint-watch.d.ts +10 -0
  342. package/src/training/distillation/checkpoint-watch.js +58 -0
  343. package/src/training/distillation/dataset.d.ts +59 -0
  344. package/src/training/distillation/dataset.js +337 -0
  345. package/src/training/distillation/eval.d.ts +34 -0
  346. package/src/training/distillation/eval.js +310 -0
  347. package/src/training/distillation/index.d.ts +29 -0
  348. package/src/training/distillation/index.js +29 -0
  349. package/src/training/distillation/runtime.d.ts +20 -0
  350. package/src/training/distillation/runtime.js +121 -0
  351. package/src/training/distillation/scoreboard.d.ts +6 -0
  352. package/src/training/distillation/scoreboard.js +8 -0
  353. package/src/training/distillation/stage-a.d.ts +45 -0
  354. package/src/training/distillation/stage-a.js +338 -0
  355. package/src/training/distillation/stage-b.d.ts +24 -0
  356. package/src/training/distillation/stage-b.js +20 -0
  357. package/src/training/distillation/student-fixture.d.ts +22 -0
  358. package/src/training/distillation/student-fixture.js +846 -0
  359. package/src/training/distillation/suite-data.d.ts +45 -0
  360. package/src/training/distillation/suite-data.js +189 -0
  361. package/src/training/index.d.ts +10 -0
  362. package/src/training/index.js +10 -0
  363. package/src/training/lora-pipeline.d.ts +40 -0
  364. package/src/training/lora-pipeline.js +793 -0
  365. package/src/training/lora.js +26 -12
  366. package/src/training/loss.js +5 -6
  367. package/src/training/objectives/cross_entropy.js +2 -5
  368. package/src/training/objectives/distill_kd.js +4 -8
  369. package/src/training/objectives/distill_triplet.js +4 -8
  370. package/src/training/objectives/ul_stage2_base.js +4 -8
  371. package/src/training/operator-artifacts.d.ts +62 -0
  372. package/src/training/operator-artifacts.js +140 -0
  373. package/src/training/operator-command.d.ts +5 -0
  374. package/src/training/operator-command.js +455 -0
  375. package/src/training/operator-eval.d.ts +48 -0
  376. package/src/training/operator-eval.js +230 -0
  377. package/src/training/operator-scoreboard.d.ts +5 -0
  378. package/src/training/operator-scoreboard.js +44 -0
  379. package/src/training/optimizer.js +19 -7
  380. package/src/training/runner.d.ts +52 -0
  381. package/src/training/runner.js +31 -5
  382. package/src/training/suite.d.ts +112 -0
  383. package/src/training/suite.js +24 -984
  384. package/src/training/tensor-factory.d.ts +9 -0
  385. package/src/training/tensor-factory.js +13 -0
  386. package/src/training/trainer.js +3 -5
  387. package/src/training/ul_dataset.js +3 -5
  388. package/src/training/workloads.d.ts +164 -0
  389. package/src/training/workloads.js +530 -0
  390. package/src/version.js +1 -1
  391. package/tools/convert-safetensors-node.js +22 -16
  392. package/tools/doppler-cli.js +179 -63
@@ -50,6 +50,108 @@ const originalConsoleInfo = console.info;
50
50
  const originalConsoleWarn = console.warn;
51
51
  let warnedBenchmarkMode = false;
52
52
 
53
+ function requirePlainObject(value, label) {
54
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
55
+ throw new Error(`${label} must be an object when provided.`);
56
+ }
57
+ return value;
58
+ }
59
+
60
+ function requireNonNegativeIntegerArray(value, label) {
61
+ if (!Array.isArray(value)) {
62
+ throw new Error(`${label} must be an array of non-negative integers when provided.`);
63
+ }
64
+ return value.map((entry, index) => {
65
+ const parsed = Number(entry);
66
+ if (!Number.isInteger(parsed) || parsed < 0) {
67
+ throw new Error(`${label}[${index}] must be a non-negative integer.`);
68
+ }
69
+ return parsed;
70
+ });
71
+ }
72
+
73
+ function requireNonNegativeInteger(value, label) {
74
+ const parsed = Number(value);
75
+ if (!Number.isInteger(parsed) || parsed < 0) {
76
+ throw new Error(`${label} must be a non-negative integer when provided.`);
77
+ }
78
+ return parsed;
79
+ }
80
+
81
+ function requireBoolean(value, label) {
82
+ if (typeof value !== 'boolean') {
83
+ throw new Error(`${label} must be a boolean when provided.`);
84
+ }
85
+ return value;
86
+ }
87
+
88
+ function normalizeLogLevel(level) {
89
+ if (typeof level !== 'string' || !level.trim()) {
90
+ throw new Error('setLogLevel(level) requires a non-empty log level string.');
91
+ }
92
+ return level.trim().toLowerCase();
93
+ }
94
+
95
+ function normalizeTraceCategories(categories) {
96
+ if (typeof categories === 'string') {
97
+ const values = categories
98
+ .split(',')
99
+ .map((value) => value.trim())
100
+ .filter(Boolean);
101
+ if (values.length === 0) {
102
+ throw new Error('setTrace(categories) requires at least one trace category.');
103
+ }
104
+ return values;
105
+ }
106
+ if (Array.isArray(categories) && categories.length > 0) {
107
+ return categories.map((value, index) => {
108
+ if (typeof value !== 'string' || !value.trim()) {
109
+ throw new Error(`setTrace(categories)[${index}] must be a non-empty string.`);
110
+ }
111
+ return value.trim();
112
+ });
113
+ }
114
+ throw new Error(
115
+ 'setTrace(categories) requires false, a comma-delimited string, or a non-empty string array.'
116
+ );
117
+ }
118
+
119
+ function validateTraceCategoryToken(token) {
120
+ if (token === 'all') {
121
+ return;
122
+ }
123
+ const value = token.startsWith('-') ? token.slice(1) : token;
124
+ if (!TRACE_CATEGORIES.includes(value)) {
125
+ throw new Error(
126
+ `Unknown trace category "${token}". Allowed categories: all, ${TRACE_CATEGORIES.join(', ')}.`
127
+ );
128
+ }
129
+ }
130
+
131
+ function normalizeTraceOptions(options) {
132
+ if (options == null) {
133
+ return {};
134
+ }
135
+ const normalized = requirePlainObject(options, 'setTrace(options)');
136
+ return {
137
+ ...(normalized.layers === undefined ? {} : {
138
+ layers: requireNonNegativeIntegerArray(normalized.layers, 'setTrace(options).layers'),
139
+ }),
140
+ ...(normalized.maxDecodeSteps === undefined ? {} : {
141
+ maxDecodeSteps: requireNonNegativeInteger(
142
+ normalized.maxDecodeSteps,
143
+ 'setTrace(options).maxDecodeSteps'
144
+ ),
145
+ }),
146
+ ...(normalized.breakOnAnomaly === undefined ? {} : {
147
+ breakOnAnomaly: requireBoolean(
148
+ normalized.breakOnAnomaly,
149
+ 'setTrace(options).breakOnAnomaly'
150
+ ),
151
+ }),
152
+ };
153
+ }
154
+
53
155
  export function setLogLevel(level) {
54
156
  const levelMap = {
55
157
  debug: LOG_LEVELS.DEBUG,
@@ -59,8 +161,14 @@ export function setLogLevel(level) {
59
161
  error: LOG_LEVELS.ERROR,
60
162
  silent: LOG_LEVELS.SILENT,
61
163
  };
62
- currentLogLevel = levelMap[level.toLowerCase()] ?? LOG_LEVELS.INFO;
63
- console.log(`[Doppler] Log level set to: ${level.toUpperCase()}`);
164
+ const normalizedLevel = normalizeLogLevel(level);
165
+ if (!Object.prototype.hasOwnProperty.call(levelMap, normalizedLevel)) {
166
+ throw new Error(
167
+ `Unknown log level "${level}". Allowed levels: ${Object.keys(levelMap).join(', ')}.`
168
+ );
169
+ }
170
+ currentLogLevel = levelMap[normalizedLevel];
171
+ console.log(`[Doppler] Log level set to: ${normalizedLevel.toUpperCase()}`);
64
172
  }
65
173
 
66
174
  export function getLogLevel() {
@@ -77,9 +185,11 @@ export function setTrace(categories, options) {
77
185
  return;
78
186
  }
79
187
 
80
- const catArray = typeof categories === 'string'
81
- ? categories.split(',').map(s => s.trim())
82
- : categories;
188
+ const catArray = normalizeTraceCategories(categories);
189
+ const traceOptions = normalizeTraceOptions(options);
190
+ for (const cat of catArray) {
191
+ validateTraceCategoryToken(cat);
192
+ }
83
193
 
84
194
  enabledTraceCategories.clear();
85
195
 
@@ -101,14 +211,14 @@ export function setTrace(categories, options) {
101
211
  }
102
212
  }
103
213
 
104
- if (options?.layers) {
105
- traceLayerFilter = options.layers;
214
+ if (traceOptions.layers !== undefined) {
215
+ traceLayerFilter = traceOptions.layers;
106
216
  }
107
- if (options?.maxDecodeSteps !== undefined) {
108
- traceMaxDecodeSteps = options.maxDecodeSteps;
217
+ if (traceOptions.maxDecodeSteps !== undefined) {
218
+ traceMaxDecodeSteps = traceOptions.maxDecodeSteps;
109
219
  }
110
- if (options?.breakOnAnomaly !== undefined) {
111
- traceBreakOnAnomaly = options.breakOnAnomaly;
220
+ if (traceOptions.breakOnAnomaly !== undefined) {
221
+ traceBreakOnAnomaly = traceOptions.breakOnAnomaly;
112
222
  }
113
223
 
114
224
  const enabled = [...enabledTraceCategories].join(',') || 'none';
@@ -184,11 +294,13 @@ export function setSilentMode(enabled) {
184
294
  console.log = noop;
185
295
  console.debug = noop;
186
296
  console.info = noop;
297
+ console.warn = noop;
187
298
  originalConsoleLog('[Doppler] Silent mode enabled - logging silenced');
188
299
  } else {
189
300
  console.log = originalConsoleLog;
190
301
  console.debug = originalConsoleDebug;
191
302
  console.info = originalConsoleInfo;
303
+ console.warn = originalConsoleWarn;
192
304
  console.log('[Doppler] Silent mode disabled - logging restored');
193
305
  }
194
306
  }
@@ -24,7 +24,13 @@ export function signalResult(data) {
24
24
 
25
25
 
26
26
  export function signalError(error, details) {
27
- console.log(`${SIGNALS.ERROR} ${JSON.stringify({ error, ...details })}`);
27
+ if (details != null && (typeof details !== 'object' || Array.isArray(details))) {
28
+ throw new Error('signalError details must be an object when provided.');
29
+ }
30
+ if (details && Object.hasOwn(details, 'error')) {
31
+ throw new Error('signalError details.error is reserved. Pass the primary error as the first argument.');
32
+ }
33
+ console.log(`${SIGNALS.ERROR} ${JSON.stringify({ error, ...(details ?? {}) })}`);
28
34
  }
29
35
 
30
36
 
@@ -63,6 +63,8 @@ export interface TensorInspectOptions {
63
63
  }
64
64
 
65
65
  export interface TensorSnapshot {
66
+ ok: boolean;
67
+ error: string | null;
66
68
  shape: number[];
67
69
  dtype: string;
68
70
  stats: {
@@ -202,7 +202,13 @@ export const tensor = {
202
202
 
203
203
  export async function snapshotTensor(buffer, shape, dtype = 'f32') {
204
204
  try {
205
- if (!gpuDevice) {
205
+ if (
206
+ !gpuDevice
207
+ || typeof gpuDevice.createBuffer !== 'function'
208
+ || typeof gpuDevice.createCommandEncoder !== 'function'
209
+ || !gpuDevice.queue
210
+ || typeof gpuDevice.queue.submit !== 'function'
211
+ ) {
206
212
  throw new Error('GPU device not initialized');
207
213
  }
208
214
  const elementSize = dtype === 'f16' ? 2 : 4;
@@ -224,8 +230,11 @@ export async function snapshotTensor(buffer, shape, dtype = 'f32') {
224
230
  staging.destroy();
225
231
  const arr = new Float32Array(data);
226
232
  return snapshotFromArray(arr, shape ?? [arr.length], dtype);
227
- } catch {
233
+ } catch (error) {
234
+ const message = error instanceof Error ? error.message : String(error);
228
235
  return {
236
+ ok: false,
237
+ error: message,
229
238
  shape: shape ?? [0],
230
239
  dtype,
231
240
  stats: { min: 0, max: 0, maxAbs: 0, mean: 0, std: 0 },
@@ -241,6 +250,8 @@ export function snapshotFromArray(arr, shape, dtype = 'f32') {
241
250
  const stats = computeArrayStats(arr, Math.min(arr.length, numElements));
242
251
 
243
252
  return {
253
+ ok: true,
254
+ error: null,
244
255
  shape,
245
256
  dtype,
246
257
  stats: {
@@ -38,10 +38,17 @@ function asOptionalTimestamp(value, label) {
38
38
  return Math.floor(parsed);
39
39
  }
40
40
 
41
- function asNonNegativeInteger(value, fallback) {
41
+ function asOptionalNonNegativeInteger(value, label) {
42
+ if (value === undefined || value === null) {
43
+ return null;
44
+ }
42
45
  const parsed = Number(value);
43
46
  if (!Number.isInteger(parsed) || parsed < 0) {
44
- return fallback;
47
+ throw createP2PTransportError(
48
+ P2P_TRANSPORT_ERROR_CODES.payloadInvalid,
49
+ `${label} must be a non-negative integer when provided.`,
50
+ { label }
51
+ );
45
52
  }
46
53
  return parsed;
47
54
  }
@@ -104,12 +111,11 @@ export function normalizeControlPlaneSessionUpdate(value, label = 'p2p control-p
104
111
 
105
112
  export function normalizeP2PPolicyDecision(value, label = 'p2p control-plane policy decision') {
106
113
  if (value === undefined || value === null) {
107
- return {
108
- allow: true,
109
- reason: null,
110
- sessionUpdate: null,
111
- metadata: null,
112
- };
114
+ throw createP2PTransportError(
115
+ P2P_TRANSPORT_ERROR_CODES.payloadInvalid,
116
+ `${label} must return an explicit boolean or object decision.`,
117
+ { label }
118
+ );
113
119
  }
114
120
 
115
121
  if (typeof value === 'boolean') {
@@ -129,9 +135,40 @@ export function normalizeP2PPolicyDecision(value, label = 'p2p control-plane pol
129
135
  );
130
136
  }
131
137
 
132
- const allow = value.allow === false || value.deny === true
133
- ? false
134
- : true;
138
+ const hasAllow = Object.prototype.hasOwnProperty.call(value, 'allow');
139
+ const hasDeny = Object.prototype.hasOwnProperty.call(value, 'deny');
140
+ if (!hasAllow && !hasDeny) {
141
+ throw createP2PTransportError(
142
+ P2P_TRANSPORT_ERROR_CODES.payloadInvalid,
143
+ `${label} must include allow or deny.`,
144
+ { label }
145
+ );
146
+ }
147
+ if (hasAllow && typeof value.allow !== 'boolean') {
148
+ throw createP2PTransportError(
149
+ P2P_TRANSPORT_ERROR_CODES.payloadInvalid,
150
+ `${label}.allow must be a boolean when provided.`,
151
+ { label }
152
+ );
153
+ }
154
+ if (hasDeny && typeof value.deny !== 'boolean') {
155
+ throw createP2PTransportError(
156
+ P2P_TRANSPORT_ERROR_CODES.payloadInvalid,
157
+ `${label}.deny must be a boolean when provided.`,
158
+ { label }
159
+ );
160
+ }
161
+ if (hasAllow && hasDeny && value.allow === value.deny) {
162
+ throw createP2PTransportError(
163
+ P2P_TRANSPORT_ERROR_CODES.payloadInvalid,
164
+ `${label} has conflicting allow/deny values.`,
165
+ { label }
166
+ );
167
+ }
168
+
169
+ const allow = hasAllow
170
+ ? value.allow
171
+ : value.deny !== true;
135
172
  const reason = asOptionalString(value.reason, `${label}.reason`);
136
173
  const sessionUpdate = normalizeControlPlaneSessionUpdate(
137
174
  {
@@ -180,7 +217,10 @@ export function normalizeP2PControlPlaneConfig(config = {}) {
180
217
  contractVersion: assertSupportedP2PControlPlaneContract(
181
218
  raw.contractVersion ?? P2P_CONTROL_PLANE_CONTRACT_VERSION
182
219
  ),
183
- tokenRefreshSkewMs: asNonNegativeInteger(raw.tokenRefreshSkewMs, DEFAULT_TOKEN_REFRESH_SKEW_MS),
220
+ tokenRefreshSkewMs: asOptionalNonNegativeInteger(
221
+ raw.tokenRefreshSkewMs,
222
+ 'p2p.controlPlane.tokenRefreshSkewMs'
223
+ ) ?? DEFAULT_TOKEN_REFRESH_SKEW_MS,
184
224
  tokenProvider,
185
225
  policyEvaluator,
186
226
  };
@@ -12,6 +12,14 @@ function asFiniteNumber(value, fallback = 0) {
12
12
  return Number.isFinite(parsed) ? parsed : fallback;
13
13
  }
14
14
 
15
+ function assertFiniteNumber(value, label) {
16
+ const parsed = Number(value);
17
+ if (!Number.isFinite(parsed)) {
18
+ throw new Error(`P2P observability ${label} must be a finite number.`);
19
+ }
20
+ return parsed;
21
+ }
22
+
15
23
  function asNonNegativeInteger(value, fallback = 0) {
16
24
  const parsed = Number(value);
17
25
  if (!Number.isInteger(parsed) || parsed < 0) {
@@ -69,15 +77,43 @@ function percentile(values, ratio) {
69
77
  }
70
78
 
71
79
  function resolveSLOTargets(options = {}) {
72
- const targets = options.targets && typeof options.targets === 'object'
73
- ? options.targets
74
- : {};
80
+ const hasExplicitTargets = Object.hasOwn(options, 'targets');
81
+ if (hasExplicitTargets && (options.targets == null || typeof options.targets !== 'object' || Array.isArray(options.targets))) {
82
+ throw new Error('P2P observability targets must be an object when provided.');
83
+ }
84
+ const targets = hasExplicitTargets ? options.targets : {};
85
+
86
+ const minAvailability = Object.hasOwn(targets, 'minAvailability')
87
+ ? assertFiniteNumber(targets.minAvailability, 'targets.minAvailability')
88
+ : DEFAULT_SLO_TARGETS.minAvailability;
89
+ const minP2PHitRate = Object.hasOwn(targets, 'minP2PHitRate')
90
+ ? assertFiniteNumber(targets.minP2PHitRate, 'targets.minP2PHitRate')
91
+ : DEFAULT_SLO_TARGETS.minP2PHitRate;
92
+ const maxHttpFallbackRate = Object.hasOwn(targets, 'maxHttpFallbackRate')
93
+ ? assertFiniteNumber(targets.maxHttpFallbackRate, 'targets.maxHttpFallbackRate')
94
+ : DEFAULT_SLO_TARGETS.maxHttpFallbackRate;
95
+ const maxP95LatencyMs = Object.hasOwn(targets, 'maxP95LatencyMs')
96
+ ? assertFiniteNumber(targets.maxP95LatencyMs, 'targets.maxP95LatencyMs')
97
+ : DEFAULT_SLO_TARGETS.maxP95LatencyMs;
98
+
99
+ if (minAvailability < 0 || minAvailability > 1) {
100
+ throw new Error('P2P observability targets.minAvailability must be between 0 and 1.');
101
+ }
102
+ if (minP2PHitRate < 0 || minP2PHitRate > 1) {
103
+ throw new Error('P2P observability targets.minP2PHitRate must be between 0 and 1.');
104
+ }
105
+ if (maxHttpFallbackRate < 0 || maxHttpFallbackRate > 1) {
106
+ throw new Error('P2P observability targets.maxHttpFallbackRate must be between 0 and 1.');
107
+ }
108
+ if (maxP95LatencyMs < 0) {
109
+ throw new Error('P2P observability targets.maxP95LatencyMs must be >= 0.');
110
+ }
75
111
 
76
112
  return {
77
- minAvailability: asFiniteNumber(targets.minAvailability, DEFAULT_SLO_TARGETS.minAvailability),
78
- minP2PHitRate: asFiniteNumber(targets.minP2PHitRate, DEFAULT_SLO_TARGETS.minP2PHitRate),
79
- maxHttpFallbackRate: asFiniteNumber(targets.maxHttpFallbackRate, DEFAULT_SLO_TARGETS.maxHttpFallbackRate),
80
- maxP95LatencyMs: asFiniteNumber(targets.maxP95LatencyMs, DEFAULT_SLO_TARGETS.maxP95LatencyMs),
113
+ minAvailability,
114
+ minP2PHitRate,
115
+ maxHttpFallbackRate,
116
+ maxP95LatencyMs,
81
117
  };
82
118
  }
83
119
 
@@ -200,6 +200,16 @@ function assertOpenDataChannel(channel, peerId) {
200
200
  }
201
201
 
202
202
  function toRequestMessage(requestId, context) {
203
+ if (context?.contractVersion !== P2P_WEBRTC_DATA_PLANE_CONTRACT_VERSION) {
204
+ throw createP2PTransportError(
205
+ P2P_TRANSPORT_ERROR_CODES.payloadInvalid,
206
+ `Unexpected WebRTC data-plane contractVersion "${context?.contractVersion}".`,
207
+ {
208
+ expectedContractVersion: P2P_WEBRTC_DATA_PLANE_CONTRACT_VERSION,
209
+ actualContractVersion: context?.contractVersion ?? null,
210
+ }
211
+ );
212
+ }
203
213
  return {
204
214
  schemaVersion: P2P_WEBRTC_MESSAGE_SCHEMA_VERSION,
205
215
  contractVersion: P2P_WEBRTC_DATA_PLANE_CONTRACT_VERSION,
@@ -377,6 +387,16 @@ export function createBrowserWebRTCDataPlaneTransport(config = {}) {
377
387
  const maxPayloadBytes = Math.max(1, asNonNegativeInteger(config.maxPayloadBytes, DEFAULT_MAX_PAYLOAD_BYTES));
378
388
 
379
389
  return async function webRtcDataPlaneTransport(context) {
390
+ if (context?.contractVersion !== P2P_WEBRTC_DATA_PLANE_CONTRACT_VERSION) {
391
+ throw createP2PTransportError(
392
+ P2P_TRANSPORT_ERROR_CODES.contractUnsupported,
393
+ `Unsupported p2p.webrtc contractVersion "${context?.contractVersion}". Supported: ${P2P_WEBRTC_DATA_PLANE_CONTRACT_VERSION}.`,
394
+ {
395
+ contractVersion: context?.contractVersion ?? null,
396
+ }
397
+ );
398
+ }
399
+
380
400
  const selection = normalizePeerSelectionResult(
381
401
  selectPeer ? await selectPeer(context) : { peerId: staticPeerId }
382
402
  );
@@ -55,22 +55,30 @@ const inFlightDeliveries = new Map();
55
55
  const p2pTransportPolicyState = new WeakMap();
56
56
 
57
57
  function normalizeDistributionSourceOrder(rawSources = []) {
58
- if (!Array.isArray(rawSources)) {
58
+ if (rawSources === undefined || rawSources === null) {
59
59
  return [...DISTRIBUTION_SOURCES];
60
60
  }
61
+ if (!Array.isArray(rawSources)) {
62
+ throw new Error('distribution.sourceOrder must be an array when provided.');
63
+ }
61
64
 
62
65
  const normalized = [];
63
66
  const seen = new Set();
64
67
 
65
68
  for (const value of rawSources) {
66
69
  const source = String(value || '').trim().toLowerCase();
67
- if (!DISTRIBUTION_SOURCES.includes(source)) continue;
70
+ if (!DISTRIBUTION_SOURCES.includes(source)) {
71
+ throw new Error(`distribution.sourceOrder contains unsupported source "${source || value}".`);
72
+ }
68
73
  if (seen.has(source)) continue;
69
74
  seen.add(source);
70
75
  normalized.push(source);
71
76
  }
72
77
 
73
- return normalized.length > 0 ? normalized : [...DISTRIBUTION_SOURCES];
78
+ if (normalized.length === 0) {
79
+ throw new Error('distribution.sourceOrder must include at least one supported source.');
80
+ }
81
+ return normalized;
74
82
  }
75
83
 
76
84
  function normalizeInteger(value, fallback, allowZero = false) {
@@ -81,6 +89,23 @@ function normalizeInteger(value, fallback, allowZero = false) {
81
89
  : fallback;
82
90
  }
83
91
 
92
+ function normalizeRequiredInteger(value, label, { allowZero = false, fallback = null } = {}) {
93
+ if (value === undefined || value === null) {
94
+ if (fallback !== null) {
95
+ return fallback;
96
+ }
97
+ throw new Error(`${label} is required.`);
98
+ }
99
+ const parsed = Number(value);
100
+ const min = allowZero ? 0 : 1;
101
+ if (!Number.isInteger(parsed) || parsed < min) {
102
+ throw new Error(
103
+ `${label} must be a ${allowZero ? 'non-negative' : 'positive'} integer when provided.`
104
+ );
105
+ }
106
+ return parsed;
107
+ }
108
+
84
109
  function normalizeContentEncodings(value) {
85
110
  if (!value) return [];
86
111
  return value
@@ -95,13 +120,17 @@ function normalizeManifestVersionSet(value) {
95
120
  return normalized || null;
96
121
  }
97
122
 
98
- function normalizeSamplingRate(value, fallback = 1) {
123
+ function normalizeSamplingRate(value, fallback = 1, label = 'distribution.sourceDecision.trace.samplingRate') {
124
+ if (value === undefined || value === null) {
125
+ return fallback;
126
+ }
99
127
  const parsed = Number(value);
100
128
  if (!Number.isFinite(parsed)) {
101
- return fallback;
129
+ throw new Error(`${label} must be a finite number between 0 and 1 when provided.`);
130
+ }
131
+ if (parsed < 0 || parsed > 1) {
132
+ throw new Error(`${label} must be between 0 and 1 when provided.`);
102
133
  }
103
- if (parsed <= 0) return 0;
104
- if (parsed >= 1) return 1;
105
134
  return parsed;
106
135
  }
107
136
 
@@ -479,19 +508,28 @@ function normalizeP2PConfig(config = {}) {
479
508
 
480
509
  return {
481
510
  enabled,
482
- timeoutMs: normalizeInteger(rawTimeoutMs, DEFAULT_P2P_TIMEOUT_MS),
483
- maxRetries: normalizeInteger(rawMaxRetries, DEFAULT_P2P_MAX_RETRIES, true),
484
- retryDelayMs: normalizeInteger(rawRetryDelayMs, DEFAULT_P2P_RETRY_DELAY_MS, true),
511
+ timeoutMs: normalizeRequiredInteger(
512
+ rawTimeoutMs,
513
+ 'distribution.p2p.timeoutMs',
514
+ { fallback: DEFAULT_P2P_TIMEOUT_MS }
515
+ ),
516
+ maxRetries: normalizeRequiredInteger(
517
+ rawMaxRetries,
518
+ 'distribution.p2p.maxRetries',
519
+ { allowZero: true, fallback: DEFAULT_P2P_MAX_RETRIES }
520
+ ),
521
+ retryDelayMs: normalizeRequiredInteger(
522
+ rawRetryDelayMs,
523
+ 'distribution.p2p.retryDelayMs',
524
+ { allowZero: true, fallback: DEFAULT_P2P_RETRY_DELAY_MS }
525
+ ),
485
526
  transport,
486
527
  contractVersion,
487
528
  controlPlane: normalizeP2PControlPlaneConfig({
488
529
  ...DEFAULT_DISTRIBUTION_CONFIG.p2p.controlPlane,
489
530
  ...rawControlPlane,
490
- tokenRefreshSkewMs: normalizeInteger(
491
- rawControlPlane.tokenRefreshSkewMs,
492
- DEFAULT_P2P_CONTROL_PLANE_TOKEN_REFRESH_SKEW_MS,
493
- true
494
- ),
531
+ tokenRefreshSkewMs: rawControlPlane.tokenRefreshSkewMs
532
+ ?? DEFAULT_P2P_CONTROL_PLANE_TOKEN_REFRESH_SKEW_MS,
495
533
  }),
496
534
  security: {
497
535
  requireSessionToken: rawSecurity.requireSessionToken === true,
@@ -499,19 +537,20 @@ function normalizeP2PConfig(config = {}) {
499
537
  tokenExpiresAtMs: normalizeOptionalTimestamp(rawSecurity.tokenExpiresAtMs),
500
538
  },
501
539
  abuse: {
502
- rateLimitPerMinute: normalizeInteger(
540
+ rateLimitPerMinute: normalizeRequiredInteger(
503
541
  rawAbuse.rateLimitPerMinute,
504
- DEFAULT_P2P_RATE_LIMIT_PER_MINUTE,
505
- true
542
+ 'distribution.p2p.abuse.rateLimitPerMinute',
543
+ { allowZero: true, fallback: DEFAULT_P2P_RATE_LIMIT_PER_MINUTE }
506
544
  ),
507
- maxConsecutiveFailures: normalizeInteger(
545
+ maxConsecutiveFailures: normalizeRequiredInteger(
508
546
  rawAbuse.maxConsecutiveFailures,
509
- DEFAULT_P2P_MAX_CONSECUTIVE_FAILURES
547
+ 'distribution.p2p.abuse.maxConsecutiveFailures',
548
+ { fallback: DEFAULT_P2P_MAX_CONSECUTIVE_FAILURES }
510
549
  ),
511
- quarantineMs: normalizeInteger(
550
+ quarantineMs: normalizeRequiredInteger(
512
551
  rawAbuse.quarantineMs,
513
- DEFAULT_P2P_QUARANTINE_MS,
514
- true
552
+ 'distribution.p2p.abuse.quarantineMs',
553
+ { allowZero: true, fallback: DEFAULT_P2P_QUARANTINE_MS }
515
554
  ),
516
555
  },
517
556
  };
@@ -1293,9 +1332,21 @@ async function downloadShardFromHttp(baseUrl, shardInfo, shardIndex, options = {
1293
1332
  const startTime = performance.now();
1294
1333
  const url = buildShardUrl(baseUrl, shardInfo);
1295
1334
  let lastError;
1296
- const maxRetries = normalizeInteger(options.maxRetries, 3, true);
1297
- const initialRetryDelayMs = normalizeInteger(options.initialRetryDelayMs, 1000);
1298
- const maxRetryDelayMs = normalizeInteger(options.maxRetryDelayMs, 30000);
1335
+ const maxRetries = normalizeRequiredInteger(
1336
+ options.maxRetries,
1337
+ 'download.maxRetries',
1338
+ { allowZero: true, fallback: 3 }
1339
+ );
1340
+ const initialRetryDelayMs = normalizeRequiredInteger(
1341
+ options.initialRetryDelayMs,
1342
+ 'download.initialRetryDelayMs',
1343
+ { allowZero: true, fallback: 1000 }
1344
+ );
1345
+ const maxRetryDelayMs = normalizeRequiredInteger(
1346
+ options.maxRetryDelayMs,
1347
+ 'download.maxRetryDelayMs',
1348
+ { allowZero: true, fallback: 30000 }
1349
+ );
1299
1350
  const progressTotalBytes = Number.isFinite(options.expectedSize)
1300
1351
  ? Math.floor(options.expectedSize)
1301
1352
  : (Number.isFinite(shardInfo?.size) ? Math.floor(shardInfo.size) : 0);