@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.
- package/CHANGELOG.md +126 -0
- package/README.md +25 -17
- package/package.json +20 -4
- package/src/adapters/adapter-registry.js +12 -1
- package/src/adapters/lora-loader.js +23 -6
- package/src/bridge/extension-client.d.ts +5 -0
- package/src/bridge/extension-client.js +40 -0
- package/src/bridge/index.d.ts +2 -1
- package/src/bridge/index.js +6 -4
- package/src/browser/browser-converter.js +26 -1
- package/src/browser/file-picker.js +6 -0
- package/src/browser/safetensors-parser-browser.js +84 -1
- package/src/browser/shard-io-browser.js +2 -2
- package/src/browser/tensor-source-download.js +8 -2
- package/src/browser/tensor-source-http.d.ts +1 -0
- package/src/browser/tensor-source-http.js +5 -1
- package/src/client/doppler-api.browser.js +20 -4
- package/src/client/doppler-api.js +19 -3
- package/src/client/doppler-provider/generation.js +12 -0
- package/src/client/doppler-provider/model-manager.d.ts +10 -0
- package/src/client/doppler-provider/model-manager.js +91 -19
- package/src/client/doppler-provider/source-runtime.d.ts +2 -1
- package/src/client/doppler-provider/source-runtime.js +132 -13
- package/src/client/doppler-registry.json +8 -7
- package/src/config/backward-registry-loader.js +17 -2
- package/src/config/execution-v0-contract-check.js +113 -15
- package/src/config/kernel-path-contract-check.js +57 -29
- package/src/config/kernel-path-loader.js +5 -36
- package/src/config/kernels/kernel-ref-digests.js +39 -39
- package/src/config/kernels/registry.js +14 -1
- package/src/config/kernels/registry.json +49 -7
- package/src/config/loader.d.ts +1 -1
- package/src/config/loader.js +43 -4
- package/src/config/merge-contract-check.js +59 -4
- package/src/config/merge-helpers.js +128 -7
- package/src/config/merge.d.ts +1 -0
- package/src/config/merge.js +28 -0
- package/src/config/param-validator.js +47 -2
- package/src/config/presets/kernel-paths/{gemma2-q4k-dequant-f32a.json → gemma2-q4k-dequant-f32a-nosubgroups.json} +3 -3
- package/src/config/presets/kernel-paths/gemma3-f16-fused-f32a-online-streamingprefill.json +223 -0
- package/src/config/presets/kernel-paths/{gemma3-q4k-dequant-f32a.json → gemma3-q4k-dequant-f32a-nosubgroups.json} +3 -3
- package/src/config/presets/kernel-paths/registry.json +29 -8
- package/src/config/presets/models/gemma2.json +2 -2
- package/src/config/presets/models/qwen3.json +9 -2
- package/src/config/presets/models/transformer.json +5 -0
- package/src/config/presets/runtime/experiments/bench/gemma3-bench-q4k.json +1 -1
- package/src/config/presets/runtime/experiments/debug/gemma3-debug-q4k.json +1 -1
- package/src/config/presets/runtime/experiments/verify/gemma3-verify.json +1 -1
- package/src/config/presets/runtime/kernels/dequant-f16-q4k.json +6 -13
- package/src/config/presets/runtime/kernels/dequant-f32-q4k.json +6 -13
- package/src/config/presets/runtime/kernels/embeddinggemma-q4k-dequant-f32a.json +37 -0
- package/src/config/presets/runtime/kernels/fused-q4k.json +6 -13
- package/src/config/presets/runtime/kernels/gemma2-q4k-dequant-f16a.json +33 -0
- package/src/config/presets/runtime/kernels/gemma2-q4k-dequant-f32a-nosubgroups.json +33 -0
- package/src/config/presets/runtime/kernels/gemma2-q4k-fused-f32a.json +33 -0
- package/src/config/presets/runtime/kernels/safe-q4k.json +6 -13
- package/src/config/presets/runtime/platform/metal-apple-q4k.json +1 -1
- package/src/config/required-inference-fields-contract-check.js +6 -0
- package/src/config/runtime.js +6 -1
- package/src/config/schema/debug.schema.d.ts +5 -0
- package/src/config/schema/doppler.schema.js +16 -21
- package/src/config/schema/inference-defaults.schema.js +6 -3
- package/src/config/schema/inference.schema.d.ts +9 -0
- package/src/config/schema/kernel-path.schema.d.ts +11 -1
- package/src/config/schema/kernel-thresholds.schema.js +12 -4
- package/src/config/schema/manifest.schema.d.ts +8 -1
- package/src/config/schema/manifest.schema.js +19 -3
- package/src/config/training-defaults.js +30 -22
- package/src/converter/conversion-plan.js +94 -9
- package/src/converter/core.d.ts +7 -0
- package/src/converter/core.js +14 -9
- package/src/converter/execution-v0-manifest.js +4 -1
- package/src/converter/index.d.ts +1 -0
- package/src/converter/index.js +1 -0
- package/src/converter/manifest-inference.js +43 -12
- package/src/converter/parsers/diffusion.js +0 -3
- package/src/converter/quantization-info.js +35 -15
- package/src/converter/rope-config.js +42 -0
- package/src/converter/shard-packer.d.ts +1 -1
- package/src/converter/shard-packer.js +4 -1
- package/src/debug/config.js +123 -11
- package/src/debug/signals.js +7 -1
- package/src/debug/tensor.d.ts +2 -0
- package/src/debug/tensor.js +13 -2
- package/src/distribution/p2p-control-plane.js +52 -12
- package/src/distribution/p2p-observability.js +43 -7
- package/src/distribution/p2p-webrtc-browser.js +20 -0
- package/src/distribution/shard-delivery.js +77 -26
- package/src/formats/gguf/types.js +33 -16
- package/src/formats/rdrr/groups.d.ts +12 -4
- package/src/formats/rdrr/groups.js +3 -6
- package/src/formats/rdrr/parsing.js +39 -2
- package/src/formats/rdrr/types.d.ts +2 -1
- package/src/gpu/command-recorder.js +86 -61
- package/src/gpu/device.d.ts +1 -0
- package/src/gpu/device.js +131 -19
- package/src/gpu/kernel-tuner/benchmarks.js +326 -316
- package/src/gpu/kernel-tuner/cache.js +71 -4
- package/src/gpu/kernel-tuner/tuner.js +22 -4
- package/src/gpu/kernels/attention.js +113 -34
- package/src/gpu/kernels/backward/adam.js +62 -58
- package/src/gpu/kernels/backward/attention_backward.js +257 -169
- package/src/gpu/kernels/backward/conv2d_backward.js +14 -1
- package/src/gpu/kernels/bias_add.wgsl +8 -6
- package/src/gpu/kernels/bias_add_f16.wgsl +8 -5
- package/src/gpu/kernels/cast.js +191 -149
- package/src/gpu/kernels/check-stop.js +33 -44
- package/src/gpu/kernels/conv2d.js +27 -17
- package/src/gpu/kernels/conv2d.wgsl +7 -8
- package/src/gpu/kernels/conv2d_f16.wgsl +7 -8
- package/src/gpu/kernels/cross_entropy_loss.js +21 -15
- package/src/gpu/kernels/depthwise_conv2d.js +37 -26
- package/src/gpu/kernels/depthwise_conv2d.wgsl +6 -9
- package/src/gpu/kernels/depthwise_conv2d_f16.wgsl +6 -9
- package/src/gpu/kernels/dequant.js +178 -126
- package/src/gpu/kernels/energy.d.ts +3 -21
- package/src/gpu/kernels/energy.js +111 -88
- package/src/gpu/kernels/feature-check.js +1 -1
- package/src/gpu/kernels/fused_ffn.js +84 -65
- package/src/gpu/kernels/fused_matmul_residual.js +56 -33
- package/src/gpu/kernels/fused_matmul_rmsnorm.js +62 -45
- package/src/gpu/kernels/gather.js +33 -15
- package/src/gpu/kernels/gelu.js +19 -11
- package/src/gpu/kernels/grouped_pointwise_conv2d.js +34 -23
- package/src/gpu/kernels/grouped_pointwise_conv2d.wgsl +6 -9
- package/src/gpu/kernels/grouped_pointwise_conv2d_f16.wgsl +6 -9
- package/src/gpu/kernels/groupnorm.js +34 -23
- package/src/gpu/kernels/kv-quantize.js +5 -2
- package/src/gpu/kernels/layernorm.js +35 -19
- package/src/gpu/kernels/logit-merge.js +5 -3
- package/src/gpu/kernels/matmul.js +83 -39
- package/src/gpu/kernels/modulate.js +23 -15
- package/src/gpu/kernels/moe.js +221 -175
- package/src/gpu/kernels/pixel_shuffle.js +22 -14
- package/src/gpu/kernels/pixel_shuffle.wgsl +4 -5
- package/src/gpu/kernels/pixel_shuffle_f16.wgsl +4 -5
- package/src/gpu/kernels/relu.js +31 -10
- package/src/gpu/kernels/relu.wgsl +2 -1
- package/src/gpu/kernels/relu_f16.wgsl +2 -1
- package/src/gpu/kernels/repeat_channels.js +25 -17
- package/src/gpu/kernels/repeat_channels.wgsl +4 -5
- package/src/gpu/kernels/repeat_channels_f16.wgsl +4 -5
- package/src/gpu/kernels/residual.js +69 -23
- package/src/gpu/kernels/residual.wgsl +6 -3
- package/src/gpu/kernels/residual_f16.wgsl +2 -1
- package/src/gpu/kernels/residual_f16_vec4.wgsl +2 -1
- package/src/gpu/kernels/residual_vec4.wgsl +2 -1
- package/src/gpu/kernels/rmsnorm.js +96 -28
- package/src/gpu/kernels/rmsnorm.wgsl +14 -6
- package/src/gpu/kernels/rmsnorm_f16.wgsl +10 -2
- package/src/gpu/kernels/rope.d.ts +2 -0
- package/src/gpu/kernels/rope.js +14 -1
- package/src/gpu/kernels/rope.wgsl +56 -40
- package/src/gpu/kernels/sample.js +27 -38
- package/src/gpu/kernels/sana_linear_attention.js +19 -12
- package/src/gpu/kernels/sana_linear_attention_apply.wgsl +4 -5
- package/src/gpu/kernels/sana_linear_attention_apply_f16.wgsl +4 -5
- package/src/gpu/kernels/sana_linear_attention_summary.wgsl +4 -0
- package/src/gpu/kernels/sana_linear_attention_summary_f16.wgsl +4 -0
- package/src/gpu/kernels/scale.js +18 -11
- package/src/gpu/kernels/shader-cache.js +4 -2
- package/src/gpu/kernels/silu.d.ts +1 -0
- package/src/gpu/kernels/silu.js +148 -82
- package/src/gpu/kernels/silu.wgsl +19 -9
- package/src/gpu/kernels/silu_f16.wgsl +19 -9
- package/src/gpu/kernels/softmax.js +44 -25
- package/src/gpu/kernels/split_qkv.js +23 -13
- package/src/gpu/kernels/transpose.js +31 -10
- package/src/gpu/kernels/transpose.wgsl +6 -5
- package/src/gpu/kernels/upsample2d.js +22 -13
- package/src/gpu/kernels/upsample2d.wgsl +6 -9
- package/src/gpu/kernels/upsample2d_f16.wgsl +6 -9
- package/src/gpu/kernels/utils.js +35 -13
- package/src/gpu/partitioned-buffer-pool.js +10 -2
- package/src/gpu/perf-guards.js +2 -9
- package/src/gpu/profiler.js +27 -22
- package/src/gpu/readback-utils.d.ts +16 -0
- package/src/gpu/readback-utils.js +41 -0
- package/src/gpu/submit-tracker.js +13 -0
- package/src/gpu/uniform-cache.d.ts +1 -0
- package/src/gpu/uniform-cache.js +30 -9
- package/src/hotswap/intent-bundle.js +6 -0
- package/src/hotswap/manifest.d.ts +10 -1
- package/src/hotswap/manifest.js +12 -2
- package/src/hotswap/runtime.js +30 -8
- package/src/index-browser.d.ts +44 -0
- package/src/index-browser.js +14 -0
- package/src/inference/browser-harness-contract-helpers.d.ts +5 -0
- package/src/inference/browser-harness-contract-helpers.js +28 -0
- package/src/inference/browser-harness-diffusion-energy-suites.d.ts +2 -0
- package/src/inference/browser-harness-diffusion-energy-suites.js +269 -0
- package/src/inference/browser-harness-model-helpers.d.ts +16 -0
- package/src/inference/browser-harness-model-helpers.js +217 -0
- package/src/inference/browser-harness-report-helpers.d.ts +7 -0
- package/src/inference/browser-harness-report-helpers.js +42 -0
- package/src/inference/browser-harness-runtime-helpers.d.ts +61 -0
- package/src/inference/browser-harness-runtime-helpers.js +415 -0
- package/src/inference/browser-harness-suite-helpers.d.ts +28 -0
- package/src/inference/browser-harness-suite-helpers.js +268 -0
- package/src/inference/browser-harness-text-helpers.d.ts +27 -0
- package/src/inference/browser-harness-text-helpers.js +788 -0
- package/src/inference/browser-harness.d.ts +6 -0
- package/src/inference/browser-harness.js +130 -1950
- package/src/inference/kv-cache/base.js +140 -94
- package/src/inference/kv-cache/tiered.js +5 -3
- package/src/inference/moe-router.js +88 -56
- package/src/inference/multi-model-network.js +5 -3
- package/src/inference/network-evolution.d.ts +11 -2
- package/src/inference/network-evolution.js +20 -21
- package/src/inference/pipelines/context.d.ts +3 -0
- package/src/inference/pipelines/context.js +142 -2
- package/src/inference/pipelines/diffusion/helpers.js +7 -2
- package/src/inference/pipelines/diffusion/pipeline.js +17 -7
- package/src/inference/pipelines/diffusion/sd3-transformer.js +10 -10
- package/src/inference/pipelines/diffusion/text-encoder-gpu.d.ts +5 -0
- package/src/inference/pipelines/diffusion/text-encoder-gpu.js +27 -15
- package/src/inference/pipelines/diffusion/vae.js +3 -7
- package/src/inference/pipelines/energy/pipeline.js +27 -21
- package/src/inference/pipelines/energy/quintel.d.ts +5 -0
- package/src/inference/pipelines/energy/quintel.js +11 -0
- package/src/inference/pipelines/energy-head/row-head-pipeline.js +17 -13
- package/src/inference/pipelines/structured/json-head-pipeline.js +26 -11
- package/src/inference/pipelines/text/attention/projections.js +151 -101
- package/src/inference/pipelines/text/attention/record.js +73 -10
- package/src/inference/pipelines/text/attention/run.js +73 -10
- package/src/inference/pipelines/text/chat-format.js +25 -1
- package/src/inference/pipelines/text/config.d.ts +4 -0
- package/src/inference/pipelines/text/config.js +71 -5
- package/src/inference/pipelines/text/embed.js +2 -8
- package/src/inference/pipelines/text/execution-plan.js +64 -50
- package/src/inference/pipelines/text/execution-v0-contract-helpers.d.ts +59 -0
- package/src/inference/pipelines/text/execution-v0-contract-helpers.js +937 -0
- package/src/inference/pipelines/text/execution-v0-runtime-builders.d.ts +15 -0
- package/src/inference/pipelines/text/execution-v0-runtime-builders.js +279 -0
- package/src/inference/pipelines/text/execution-v0.js +78 -1002
- package/src/inference/pipelines/text/ffn/standard.js +3 -0
- package/src/inference/pipelines/text/generator-steps.d.ts +46 -0
- package/src/inference/pipelines/text/generator-steps.js +298 -207
- package/src/inference/pipelines/text/generator.js +6 -23
- package/src/inference/pipelines/text/init.d.ts +4 -0
- package/src/inference/pipelines/text/init.js +134 -29
- package/src/inference/pipelines/text/kernel-path-auto-select.js +2 -0
- package/src/inference/pipelines/text/kernel-trace.d.ts +2 -0
- package/src/inference/pipelines/text/kernel-trace.js +6 -0
- package/src/inference/pipelines/text/layer.js +14 -9
- package/src/inference/pipelines/text/linear-attention.d.ts +10 -0
- package/src/inference/pipelines/text/linear-attention.js +80 -6
- package/src/inference/pipelines/text/logits/gpu.js +10 -5
- package/src/inference/pipelines/text/logits/index.js +10 -11
- package/src/inference/pipelines/text/logits/utils.d.ts +7 -0
- package/src/inference/pipelines/text/logits/utils.js +9 -0
- package/src/inference/pipelines/text/lora-apply.js +50 -32
- package/src/inference/pipelines/text/model-load.js +279 -104
- package/src/inference/pipelines/text/moe-cache.js +5 -4
- package/src/inference/pipelines/text/moe-cpu-gptoss.js +74 -69
- package/src/inference/pipelines/text/moe-cpu.js +42 -38
- package/src/inference/pipelines/text/moe-gpu.js +110 -86
- package/src/inference/pipelines/text/ops.js +90 -90
- package/src/inference/pipelines/text/probes.js +9 -9
- package/src/inference/pipelines/text/weights.js +17 -7
- package/src/inference/pipelines/text.js +17 -1
- package/src/inference/speculative.d.ts +2 -2
- package/src/inference/speculative.js +4 -18
- package/src/inference/test-harness.d.ts +1 -1
- package/src/inference/test-harness.js +15 -5
- package/src/inference/tokenizer.d.ts +0 -5
- package/src/inference/tokenizer.js +4 -23
- package/src/inference/tokenizers/bpe.js +9 -0
- package/src/inference/tokenizers/bundled.js +176 -33
- package/src/inference/tokenizers/sentencepiece.js +12 -0
- package/src/loader/doppler-loader.js +38 -22
- package/src/loader/dtype-utils.js +3 -44
- package/src/loader/embedding-loader.js +7 -3
- package/src/loader/experts/expert-cache.js +13 -6
- package/src/loader/experts/expert-loader.js +10 -6
- package/src/loader/final-weights-loader.js +8 -4
- package/src/loader/layer-loader.js +2 -1
- package/src/loader/loader-state.js +2 -2
- package/src/loader/memory-monitor.js +8 -0
- package/src/loader/multi-model-loader.d.ts +14 -0
- package/src/loader/multi-model-loader.js +70 -24
- package/src/loader/shard-cache.js +81 -12
- package/src/loader/shard-resolver.js +25 -3
- package/src/loader/tensors/tensor-loader.js +209 -144
- package/src/loader/tensors/tensor-reader.js +76 -19
- package/src/loader/weight-downcast.js +1 -1
- package/src/memory/buffer-pool.d.ts +9 -1
- package/src/memory/buffer-pool.js +109 -44
- package/src/memory/unified-detect.js +1 -1
- package/src/rules/inference/kernel-path.rules.json +24 -8
- package/src/rules/rule-registry.js +25 -1
- package/src/rules/tooling/command-runtime.rules.json +18 -0
- package/src/storage/backends/opfs-store.js +68 -24
- package/src/storage/downloader.js +364 -83
- package/src/storage/index.d.ts +3 -0
- package/src/storage/index.js +3 -0
- package/src/storage/preflight.d.ts +2 -2
- package/src/storage/preflight.js +24 -2
- package/src/storage/quickstart-downloader.js +11 -5
- package/src/storage/registry.js +10 -4
- package/src/storage/reports.js +1 -1
- package/src/storage/shard-manager.d.ts +15 -1
- package/src/storage/shard-manager.js +51 -3
- package/src/storage/source-artifact-store.d.ts +52 -0
- package/src/storage/source-artifact-store.js +234 -0
- package/src/tooling/command-api-constants.d.ts +9 -0
- package/src/tooling/command-api-constants.js +9 -0
- package/src/tooling/command-api-family-normalizers.d.ts +9 -0
- package/src/tooling/command-api-family-normalizers.js +343 -0
- package/src/tooling/command-api-helpers.d.ts +25 -0
- package/src/tooling/command-api-helpers.js +262 -0
- package/src/tooling/command-api.d.ts +27 -1
- package/src/tooling/command-api.js +26 -473
- package/src/tooling/command-envelope.js +4 -1
- package/src/tooling/command-runner-shared.js +52 -18
- package/src/tooling/lean-execution-contract.js +150 -3
- package/src/tooling/node-browser-command-runner.d.ts +4 -0
- package/src/tooling/node-browser-command-runner.js +218 -273
- package/src/tooling/node-command-runner.js +44 -3
- package/src/tooling/node-converter.js +27 -1
- package/src/tooling/node-source-runtime.d.ts +1 -1
- package/src/tooling/node-source-runtime.js +84 -3
- package/src/tooling/node-webgpu.js +30 -105
- package/src/tooling/opfs-cache.js +21 -4
- package/src/tooling/runtime-input-composition.d.ts +38 -0
- package/src/tooling/runtime-input-composition.js +86 -0
- package/src/tooling/source-runtime-bundle.d.ts +40 -5
- package/src/tooling/source-runtime-bundle.js +261 -34
- package/src/tooling/source-runtime-materializer.d.ts +6 -0
- package/src/tooling/source-runtime-materializer.js +93 -0
- package/src/training/attention-backward.js +32 -17
- package/src/training/autograd.js +80 -52
- package/src/training/checkpoint-watch.d.ts +8 -0
- package/src/training/checkpoint-watch.js +139 -0
- package/src/training/checkpoint.d.ts +6 -1
- package/src/training/checkpoint.js +46 -7
- package/src/training/clip.js +2 -1
- package/src/training/datasets/token-batch.js +20 -8
- package/src/training/distillation/artifacts.d.ts +71 -0
- package/src/training/distillation/artifacts.js +132 -0
- package/src/training/distillation/checkpoint-watch.d.ts +10 -0
- package/src/training/distillation/checkpoint-watch.js +58 -0
- package/src/training/distillation/dataset.d.ts +59 -0
- package/src/training/distillation/dataset.js +337 -0
- package/src/training/distillation/eval.d.ts +34 -0
- package/src/training/distillation/eval.js +310 -0
- package/src/training/distillation/index.d.ts +29 -0
- package/src/training/distillation/index.js +29 -0
- package/src/training/distillation/runtime.d.ts +20 -0
- package/src/training/distillation/runtime.js +121 -0
- package/src/training/distillation/scoreboard.d.ts +6 -0
- package/src/training/distillation/scoreboard.js +8 -0
- package/src/training/distillation/stage-a.d.ts +45 -0
- package/src/training/distillation/stage-a.js +338 -0
- package/src/training/distillation/stage-b.d.ts +24 -0
- package/src/training/distillation/stage-b.js +20 -0
- package/src/training/distillation/student-fixture.d.ts +22 -0
- package/src/training/distillation/student-fixture.js +846 -0
- package/src/training/distillation/suite-data.d.ts +45 -0
- package/src/training/distillation/suite-data.js +189 -0
- package/src/training/index.d.ts +10 -0
- package/src/training/index.js +10 -0
- package/src/training/lora-pipeline.d.ts +40 -0
- package/src/training/lora-pipeline.js +793 -0
- package/src/training/lora.js +26 -12
- package/src/training/loss.js +5 -6
- package/src/training/objectives/cross_entropy.js +2 -5
- package/src/training/objectives/distill_kd.js +4 -8
- package/src/training/objectives/distill_triplet.js +4 -8
- package/src/training/objectives/ul_stage2_base.js +4 -8
- package/src/training/operator-artifacts.d.ts +62 -0
- package/src/training/operator-artifacts.js +140 -0
- package/src/training/operator-command.d.ts +5 -0
- package/src/training/operator-command.js +455 -0
- package/src/training/operator-eval.d.ts +48 -0
- package/src/training/operator-eval.js +230 -0
- package/src/training/operator-scoreboard.d.ts +5 -0
- package/src/training/operator-scoreboard.js +44 -0
- package/src/training/optimizer.js +19 -7
- package/src/training/runner.d.ts +52 -0
- package/src/training/runner.js +31 -5
- package/src/training/suite.d.ts +112 -0
- package/src/training/suite.js +24 -984
- package/src/training/tensor-factory.d.ts +9 -0
- package/src/training/tensor-factory.js +13 -0
- package/src/training/trainer.js +3 -5
- package/src/training/ul_dataset.js +3 -5
- package/src/training/workloads.d.ts +164 -0
- package/src/training/workloads.js +530 -0
- package/src/version.js +1 -1
- package/tools/convert-safetensors-node.js +22 -16
- package/tools/doppler-cli.js +179 -63
package/src/debug/config.js
CHANGED
|
@@ -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
|
-
|
|
63
|
-
|
|
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 =
|
|
81
|
-
|
|
82
|
-
|
|
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 (
|
|
105
|
-
traceLayerFilter =
|
|
214
|
+
if (traceOptions.layers !== undefined) {
|
|
215
|
+
traceLayerFilter = traceOptions.layers;
|
|
106
216
|
}
|
|
107
|
-
if (
|
|
108
|
-
traceMaxDecodeSteps =
|
|
217
|
+
if (traceOptions.maxDecodeSteps !== undefined) {
|
|
218
|
+
traceMaxDecodeSteps = traceOptions.maxDecodeSteps;
|
|
109
219
|
}
|
|
110
|
-
if (
|
|
111
|
-
traceBreakOnAnomaly =
|
|
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
|
}
|
package/src/debug/signals.js
CHANGED
|
@@ -24,7 +24,13 @@ export function signalResult(data) {
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
export function signalError(error, details) {
|
|
27
|
-
|
|
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
|
|
package/src/debug/tensor.d.ts
CHANGED
package/src/debug/tensor.js
CHANGED
|
@@ -202,7 +202,13 @@ export const tensor = {
|
|
|
202
202
|
|
|
203
203
|
export async function snapshotTensor(buffer, shape, dtype = 'f32') {
|
|
204
204
|
try {
|
|
205
|
-
if (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
133
|
-
|
|
134
|
-
|
|
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:
|
|
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
|
|
73
|
-
|
|
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
|
|
78
|
-
minP2PHitRate
|
|
79
|
-
maxHttpFallbackRate
|
|
80
|
-
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 (
|
|
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))
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
483
|
-
|
|
484
|
-
|
|
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:
|
|
491
|
-
|
|
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:
|
|
540
|
+
rateLimitPerMinute: normalizeRequiredInteger(
|
|
503
541
|
rawAbuse.rateLimitPerMinute,
|
|
504
|
-
|
|
505
|
-
true
|
|
542
|
+
'distribution.p2p.abuse.rateLimitPerMinute',
|
|
543
|
+
{ allowZero: true, fallback: DEFAULT_P2P_RATE_LIMIT_PER_MINUTE }
|
|
506
544
|
),
|
|
507
|
-
maxConsecutiveFailures:
|
|
545
|
+
maxConsecutiveFailures: normalizeRequiredInteger(
|
|
508
546
|
rawAbuse.maxConsecutiveFailures,
|
|
509
|
-
|
|
547
|
+
'distribution.p2p.abuse.maxConsecutiveFailures',
|
|
548
|
+
{ fallback: DEFAULT_P2P_MAX_CONSECUTIVE_FAILURES }
|
|
510
549
|
),
|
|
511
|
-
quarantineMs:
|
|
550
|
+
quarantineMs: normalizeRequiredInteger(
|
|
512
551
|
rawAbuse.quarantineMs,
|
|
513
|
-
|
|
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 =
|
|
1297
|
-
|
|
1298
|
-
|
|
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);
|