@blockcast/mmt-container 1.0.1 → 2.0.0

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 (100) hide show
  1. package/README.md +9 -7
  2. package/dist/browser-runtime.js +6931 -6685
  3. package/dist/browser-runtime.js.map +4 -4
  4. package/dist/carp-catalog.d.ts +7 -7
  5. package/dist/carp-catalog.d.ts.map +1 -1
  6. package/dist/carp-catalog.js +65 -230
  7. package/dist/carp-catalog.js.map +3 -3
  8. package/dist/catalog-fec-params.js.map +1 -1
  9. package/dist/catalog-network-source.js.map +1 -1
  10. package/dist/delivery-catalog.js.map +1 -1
  11. package/dist/delta-fallback-config.js.map +1 -1
  12. package/dist/fec-manager.d.ts +25 -1
  13. package/dist/fec-manager.d.ts.map +1 -1
  14. package/dist/fec-manager.js +208 -15
  15. package/dist/fec-manager.js.map +2 -2
  16. package/dist/fec-state.js.map +1 -1
  17. package/dist/fec-worker-setup.js.map +1 -1
  18. package/dist/held-layer-admission.d.ts +8 -0
  19. package/dist/held-layer-admission.d.ts.map +1 -1
  20. package/dist/held-layer-admission.js +22 -1
  21. package/dist/held-layer-admission.js.map +2 -2
  22. package/dist/iso-block-locator.js.map +1 -1
  23. package/dist/mfu-reassembler.js.map +1 -1
  24. package/dist/mmtp-container-parser.js.map +1 -1
  25. package/dist/mmtp-container-parser.no-modules.js +127 -322
  26. package/dist/mmtp-container-parser.no-modules.js.map +4 -4
  27. package/dist/mmtp-decode-pipeline.js.map +1 -1
  28. package/dist/mmtp-fec-session.d.ts.map +1 -1
  29. package/dist/mmtp-fec-session.js +61 -15
  30. package/dist/mmtp-fec-session.js.map +3 -3
  31. package/dist/mmtp-router.d.ts +11 -6
  32. package/dist/mmtp-router.d.ts.map +1 -1
  33. package/dist/mmtp-router.js +47 -10
  34. package/dist/mmtp-router.js.map +3 -3
  35. package/dist/multicast-auth-catalog.d.ts +8 -8
  36. package/dist/multicast-auth-catalog.d.ts.map +1 -1
  37. package/dist/multicast-auth-catalog.js +171 -75
  38. package/dist/multicast-auth-catalog.js.map +4 -4
  39. package/dist/multicast-input.d.ts +5 -5
  40. package/dist/multicast-input.d.ts.map +1 -1
  41. package/dist/multicast-input.js +126 -49
  42. package/dist/multicast-input.js.map +3 -3
  43. package/dist/multicast-manager.d.ts +11 -4
  44. package/dist/multicast-manager.d.ts.map +1 -1
  45. package/dist/multicast-manager.js +7922 -7700
  46. package/dist/multicast-manager.js.map +4 -4
  47. package/dist/multicast-transport.d.ts +8 -0
  48. package/dist/multicast-transport.d.ts.map +1 -1
  49. package/dist/multicast-transport.js +27 -3
  50. package/dist/multicast-transport.js.map +3 -3
  51. package/dist/multicast-unwrap.js +2 -2
  52. package/dist/multicast-unwrap.js.map +2 -2
  53. package/dist/no-modules.js +81 -278
  54. package/dist/no-modules.js.map +4 -4
  55. package/dist/repair-fetch.js.map +1 -1
  56. package/dist/signaling.js.map +1 -1
  57. package/dist/timestamp-codec.js.map +1 -1
  58. package/dist/wasm-mfu-reassembler.js.map +1 -1
  59. package/dist/wasm-mmtp-container-parser.js.map +1 -1
  60. package/package.json +5 -5
  61. package/schemas/catalog.schema.json +15 -7
  62. package/src/carp-catalog.ts +14 -13
  63. package/src/fec-manager.ts +89 -14
  64. package/src/held-layer-admission.ts +38 -1
  65. package/src/mmtp-fec-session.ts +20 -12
  66. package/src/mmtp-router.ts +65 -16
  67. package/src/multicast-auth-catalog.ts +43 -36
  68. package/src/multicast-input.ts +28 -8
  69. package/src/multicast-manager.ts +38 -34
  70. package/src/multicast-transport.ts +31 -3
  71. package/test-vectors/bc-provenance-v2.json +614 -175
  72. package/test-vectors/catalog/README.md +24 -8
  73. package/test-vectors/catalog/negative/multicast-auth-asm-missing-sender-address.json +0 -2
  74. package/test-vectors/catalog/negative/{multicast-auth-manifest-port-equals-port.json → multicast-auth-draft01-manifest-port.json} +1 -1
  75. package/test-vectors/catalog/negative/multicast-auth-duplicate-first-epoch.json +0 -1
  76. package/test-vectors/catalog/negative/multicast-auth-duplicate-kid.json +0 -1
  77. package/test-vectors/catalog/negative/multicast-auth-endpoint-auth-without-object-auth.json +0 -1
  78. package/test-vectors/catalog/negative/multicast-auth-endpoint-missing-bandwidth.json +0 -1
  79. package/test-vectors/catalog/negative/multicast-auth-manifest-timer-out-of-range.json +0 -1
  80. package/test-vectors/catalog/negative/multicast-auth-sender-address-on-ssm.json +0 -1
  81. package/test-vectors/catalog/negative/multicast-auth-shared-sg-port.json +73 -0
  82. package/test-vectors/catalog/negative/multicast-auth-version-1.json +0 -1
  83. package/test-vectors/catalog/negative/multicast-auth-version-3.json +0 -1
  84. package/test-vectors/catalog/negative/multicast-auth-version-missing.json +0 -1
  85. package/test-vectors/catalog/negative/multicast-endpoint-address-family-mismatch.json +55 -0
  86. package/test-vectors/catalog/negative/multicast-endpoint-address-host-name.json +55 -0
  87. package/test-vectors/catalog/negative/multicast-endpoint-address-leading-zero.json +55 -0
  88. package/test-vectors/catalog/negative/multicast-endpoint-address-ls32-leading-zero.json +55 -0
  89. package/test-vectors/catalog/negative/multicast-endpoint-address-mapped-family-mismatch.json +55 -0
  90. package/test-vectors/catalog/negative/multicast-endpoint-address-zone-id.json +55 -0
  91. package/test-vectors/catalog/positive/multicast-auth-asm-sender.json +0 -2
  92. package/test-vectors/catalog/{negative/multicast-auth-max-auth-delay-above-max.json → positive/multicast-auth-max-auth-delay-above-max-clamped.json} +0 -1
  93. package/test-vectors/catalog/{negative/multicast-auth-max-auth-delay-below-min.json → positive/multicast-auth-max-auth-delay-below-min-clamped.json} +0 -1
  94. package/test-vectors/catalog/positive/multicast-auth-max-auth-delay-string.json +55 -0
  95. package/test-vectors/catalog/positive/multicast-auth-rotation.json +0 -1
  96. package/test-vectors/catalog/positive/multicast-draft01-7-2-6-example.json +0 -1
  97. package/test-vectors/catalog/section-codes.json +8 -3
  98. package/test-vectors/catalog-multicast-auth-v1.json +0 -1
  99. package/test-vectors/catalog-validation.json +3 -3
  100. package/test-vectors/generate-bc-provenance-v2.mjs +94 -14
@@ -32,7 +32,7 @@ export interface MulticastEndpointInput {
32
32
  readonly relayAddress?: string;
33
33
  /** Aggregate bandwidth in bits per second. RECOMMENDED per draft §4.1; REQUIRED with `auth`. */
34
34
  readonly bandwidth?: number;
35
- /** Protected-flow parameters (draft -01 7.2.6); REQUIRED exactly when `MulticastInput.auth` is set. */
35
+ /** Protected-flow parameters (draft -02 7.2.6); REQUIRED exactly when `MulticastInput.auth` is set. */
36
36
  readonly auth?: EndpointAuthConfig;
37
37
  }
38
38
  /**
@@ -74,13 +74,13 @@ export type MulticastInputErrorKind = "empty_endpoints"
74
74
  | "invalid_endpoints" | "ssm_requires_source"
75
75
  /** Input `protocol` "asm" with a `sourceAddress`, which selects SSM. */
76
76
  | "protocol_source_mismatch"
77
- /** `MulticastInput.auth` fails draft -01 7.2.6 (version, keys, a repeated kid or firstEpoch). */
77
+ /** `MulticastInput.auth` fails draft -02 7.2.6 (version, keys, a repeated kid or firstEpoch). */
78
78
  | "invalid_multicast_auth"
79
79
  /**
80
- * An endpoint's `auth` fails draft -01 7.2.6: present without
80
+ * An endpoint's `auth` fails draft -02 7.2.6: present without
81
81
  * `MulticastInput.auth` or absent with it, `senderAddress` on SSM or
82
- * missing on ASM, a timer out of range, no `bandwidth`, or a
83
- * `manifestPort` that is a port or manifestPort of its `(S, G)`.
82
+ * missing on ASM, a timer out of range or not an integer, a non-string
83
+ * `manifestTrack`, or no `bandwidth`.
84
84
  */
85
85
  | "invalid_endpoint_auth"
86
86
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"multicast-input.d.ts","sourceRoot":"","sources":["../src/multicast-input.ts"],"names":[],"mappings":"AAiEA,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,sBAAsB,EAE3B,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAE3F;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC;AAErE;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IACrD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,gGAAgG;IAChG,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,uGAAuG;IACvG,QAAQ,CAAC,IAAI,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,sBAAsB,CAAC,CAAC;IAC1D;;;;;;;OAOG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IACtD,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE;QAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,YAAY,CAAC;QAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzF,kEAAkE;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,mBAAmB,CAAC;IACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,uBAAuB,GAChC,iBAAiB;AACnB;;;;;GAKG;GACD,mBAAmB,GACnB,qBAAqB;AACvB,wEAAwE;GACtE,0BAA0B;AAC5B,iGAAiG;GAC/F,wBAAwB;AAC1B;;;;;GAKG;GACD,uBAAuB;AACzB;;;;;;;GAOG;GACD,oBAAoB;AACtB;;;;;;GAMG;GACD,uBAAuB;AACzB;;;;;GAKG;GACD,uBAAuB;AACzB;;;GAGG;GACD,uBAAuB;AACzB;;;GAGG;GACD,wBAAwB,GACxB,uBAAuB,GACvB,wBAAwB,GACxB,cAAc,GACd,cAAc,GACd,oBAAoB,GACpB,mBAAmB;AACrB;;;;GAIG;GACD,qBAAqB,GACrB,qBAAqB,GACrB,0BAA0B,GAC1B,mBAAmB,GACnB,qBAAqB,GACrB,iBAAiB,GACjB,mBAAmB,CAAC;AAEvB,qBAAa,mBAAoB,SAAQ,KAAK;aAE5B,IAAI,EAAE,uBAAuB;aAC7B,aAAa,EAAE,MAAM,GAAG,IAAI;gBAD5B,IAAI,EAAE,uBAAuB,EAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5C,OAAO,EAAE,MAAM;CAKhB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,sBAAsB,CAsKjF;AA6PD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAC/B,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CACqB,GACzD,sBAAsB,GAAG,SAAS,CAgDpC"}
1
+ {"version":3,"file":"multicast-input.d.ts","sourceRoot":"","sources":["../src/multicast-input.ts"],"names":[],"mappings":"AAoEA,OAAO,EACN,KAAK,YAAY,EACjB,KAAK,sBAAsB,EAE3B,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAE3F;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC;AAErE;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAAC;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,oBAAoB,CAAC,CAAC;IACrD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,gGAAgG;IAChG,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,uGAAuG;IACvG,QAAQ,CAAC,IAAI,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,sBAAsB,CAAC,CAAC;IAC1D;;;;;;;OAOG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IACtD,kEAAkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE;QAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,YAAY,CAAC;QAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzF,kEAAkE;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,mBAAmB,CAAC;IACpC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,uBAAuB,GAChC,iBAAiB;AACnB;;;;;GAKG;GACD,mBAAmB,GACnB,qBAAqB;AACvB,wEAAwE;GACtE,0BAA0B;AAC5B,iGAAiG;GAC/F,wBAAwB;AAC1B;;;;;GAKG;GACD,uBAAuB;AACzB;;;;;;;GAOG;GACD,oBAAoB;AACtB;;;;;;GAMG;GACD,uBAAuB;AACzB;;;;;GAKG;GACD,uBAAuB;AACzB;;;GAGG;GACD,uBAAuB;AACzB;;;GAGG;GACD,wBAAwB,GACxB,uBAAuB,GACvB,wBAAwB,GACxB,cAAc,GACd,cAAc,GACd,oBAAoB,GACpB,mBAAmB;AACrB;;;;GAIG;GACD,qBAAqB,GACrB,qBAAqB,GACrB,0BAA0B,GAC1B,mBAAmB,GACnB,qBAAqB,GACrB,iBAAiB,GACjB,mBAAmB,CAAC;AAEvB,qBAAa,mBAAoB,SAAQ,KAAK;aAE5B,IAAI,EAAE,uBAAuB;aAC7B,aAAa,EAAE,MAAM,GAAG,IAAI;gBAD5B,IAAI,EAAE,uBAAuB,EAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5C,OAAO,EAAE,MAAM;CAKhB;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,sBAAsB,CAuLjF;AA6PD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,gBAAgB,CAC/B,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CACqB,GACzD,sBAAsB,GAAG,SAAS,CAgDpC"}
@@ -15,13 +15,74 @@ var FEC_BLOCK_PROFILES = Object.freeze([
15
15
  "iso-ssbg1-v1"
16
16
  ]);
17
17
 
18
+ // ../mmt-base/dist/ip-address.js
19
+ var DEC_OCTET = /^(?:0|[1-9]\d{0,2})$/;
20
+ var H16 = /^[0-9A-Fa-f]{1,4}$/;
21
+ function parseIpv4(text) {
22
+ const fields = text.split(".");
23
+ if (fields.length !== 4)
24
+ return void 0;
25
+ const octets = [];
26
+ for (const field of fields) {
27
+ if (!DEC_OCTET.test(field))
28
+ return void 0;
29
+ const value = Number(field);
30
+ if (value > 255)
31
+ return void 0;
32
+ octets.push(value);
33
+ }
34
+ return octets;
35
+ }
36
+ function parseIpv6(text) {
37
+ const halves = text.split("::");
38
+ if (halves.length > 2)
39
+ return void 0;
40
+ const groupsOf = (part) => part === "" ? [] : part.split(":");
41
+ const left = groupsOf(halves[0]);
42
+ const right = halves.length === 2 ? groupsOf(halves[1]) : [];
43
+ const words = [];
44
+ const all = [...left, ...right];
45
+ const last = halves.length === 2 && right.length === 0 ? -1 : all.length - 1;
46
+ for (let index = 0; index < all.length; index++) {
47
+ const group = all[index];
48
+ if (index === last && group.includes(".")) {
49
+ const v4 = parseIpv4(group);
50
+ if (!v4)
51
+ return void 0;
52
+ words.push([v4[0] << 8 | v4[1], v4[2] << 8 | v4[3]]);
53
+ } else if (H16.test(group)) {
54
+ words.push([Number.parseInt(group, 16)]);
55
+ } else {
56
+ return void 0;
57
+ }
58
+ }
59
+ const leftWords = words.slice(0, left.length).flat();
60
+ const rightWords = words.slice(left.length).flat();
61
+ const missing = 8 - leftWords.length - rightWords.length;
62
+ if (halves.length === 2 ? missing < 1 : missing !== 0)
63
+ return void 0;
64
+ const out = new Uint8Array(16);
65
+ [...leftWords, ...new Array(missing).fill(0), ...rightWords].forEach((word, index) => {
66
+ out[index * 2] = word >> 8;
67
+ out[index * 2 + 1] = word & 255;
68
+ });
69
+ return out;
70
+ }
71
+ function parseIpAddressLiteral(text) {
72
+ if (typeof text !== "string")
73
+ return void 0;
74
+ if (text.includes(":"))
75
+ return parseIpv6(text);
76
+ const v4 = parseIpv4(text);
77
+ return v4 ? Uint8Array.from(v4) : void 0;
78
+ }
79
+
18
80
  // ../mmt-base/dist/multicast-catalog.js
19
81
  var ED25519_PUBLIC_KEY_BASE64URL = /^[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/;
20
82
  var UINT32_MAX = 4294967295;
21
83
  var AUTH_MEMBERS = /* @__PURE__ */ new Set(["scheme", "version", "sig", "keys", "minEpoch"]);
22
84
  var KEY_MEMBERS = /* @__PURE__ */ new Set(["kid", "publicKey", "firstEpoch"]);
23
85
  var ENDPOINT_AUTH_MEMBERS = /* @__PURE__ */ new Set([
24
- "manifestPort",
25
86
  "streamId",
26
87
  "senderAddress",
27
88
  "maxAuthDelayMs",
@@ -31,17 +92,32 @@ var ENDPOINT_AUTH_MEMBERS = /* @__PURE__ */ new Set([
31
92
  function isObject(value) {
32
93
  return typeof value === "object" && value !== null && !Array.isArray(value);
33
94
  }
34
- function isNonEmptyString(value) {
35
- return typeof value === "string" && value.trim().length > 0;
36
- }
37
95
  function isIntegerIn(value, min, max) {
38
96
  return Number.isSafeInteger(value) && value >= min && value <= max;
39
97
  }
98
+ var BCP_DEFAULT_MAX_AUTH_DELAY_MS = 200;
99
+ var BCP_DEFAULT_MANIFEST_TIMER_MS = 20;
100
+ function isFiniteNumber(value) {
101
+ return typeof value === "number" && Number.isFinite(value);
102
+ }
103
+ function endpointAuthTiming(auth) {
104
+ const record = isObject(auth) ? auth : {};
105
+ const delay = record["maxAuthDelayMs"];
106
+ const timer = record["manifestTimerMs"];
107
+ const track = record["manifestTrack"];
108
+ const maxAuthDelayMs = !isFiniteNumber(delay) ? BCP_DEFAULT_MAX_AUTH_DELAY_MS : delay < 50 ? 50 : delay > 1e3 ? 1e3 : Number.isInteger(delay) ? delay : BCP_DEFAULT_MAX_AUTH_DELAY_MS;
109
+ const manifestTimerMs = !isFiniteNumber(timer) ? BCP_DEFAULT_MANIFEST_TIMER_MS : timer < 1 || Number.isSafeInteger(timer) ? timer : BCP_DEFAULT_MANIFEST_TIMER_MS;
110
+ return {
111
+ maxAuthDelayMs,
112
+ manifestTimerMs,
113
+ ...typeof track === "string" && track.length > 0 ? { manifestTrack: track } : {}
114
+ };
115
+ }
40
116
  function multicastEndpointSource(endpoint) {
41
117
  if (!isObject(endpoint))
42
118
  return void 0;
43
119
  const sourceAddress = endpoint["sourceAddress"];
44
- if (sourceAddress !== void 0 && !isNonEmptyString(sourceAddress))
120
+ if (sourceAddress !== void 0 && !parseIpAddressLiteral(sourceAddress))
45
121
  return void 0;
46
122
  const mode = sourceAddress === void 0 ? "asm" : "ssm";
47
123
  const protocol = endpoint["protocol"];
@@ -53,7 +129,7 @@ function multicastEndpointSource(endpoint) {
53
129
  return senderAddress === void 0 ? { mode, source: sourceAddress } : void 0;
54
130
  if (auth === void 0)
55
131
  return { mode };
56
- return isNonEmptyString(senderAddress) ? { mode, source: senderAddress } : void 0;
132
+ return parseIpAddressLiteral(senderAddress) ? { mode, source: senderAddress } : void 0;
57
133
  }
58
134
  function authIssues(auth) {
59
135
  const base = "$.multicast.auth";
@@ -64,12 +140,12 @@ function authIssues(auth) {
64
140
  return issue("", "auth must be an object");
65
141
  const unknown = Object.keys(auth).filter((key) => !AUTH_MEMBERS.has(key));
66
142
  if (unknown.length > 0) {
67
- return issue("", `auth has unknown member(s) ${unknown.join(", ")}; draft -01 7.2.6 defines scheme, version, sig, keys and minEpoch`);
143
+ return issue("", `auth has unknown member(s) ${unknown.join(", ")}; draft -02 7.2.6 defines scheme, version, sig, keys and minEpoch`);
68
144
  }
69
145
  if (auth["scheme"] !== "bc-provenance")
70
146
  return issue(".scheme", 'scheme must be "bc-provenance"');
71
147
  if (auth["version"] !== 2)
72
- return issue(".version", "version must be 2 (bc-provenance version 2, draft -01 7.2)");
148
+ return issue(".version", "version must be 2 (bc-provenance version 2, draft -02 7.2)");
73
149
  if (auth["sig"] !== "ed25519")
74
150
  return issue(".sig", 'sig must be "ed25519"');
75
151
  const keys = auth["keys"];
@@ -112,26 +188,17 @@ function endpointAuthIssue(endpoint, mode, at) {
112
188
  const unknown = Object.keys(auth).filter((key) => !ENDPOINT_AUTH_MEMBERS.has(key));
113
189
  if (unknown.length > 0)
114
190
  return { path: `${at}.auth`, message: `auth has unknown member(s) ${unknown.join(", ")}` };
115
- if (!isIntegerIn(auth["manifestPort"], 1, 65535)) {
116
- return { path: `${at}.auth.manifestPort`, message: "manifestPort must be an integer from 1 to 65535" };
117
- }
118
191
  if (!isIntegerIn(auth["streamId"], 0, UINT32_MAX)) {
119
192
  return { path: `${at}.auth.streamId`, message: "streamId must be an unsigned 32-bit integer" };
120
193
  }
121
194
  if (mode === "ssm" && auth["senderAddress"] !== void 0) {
122
195
  return { path: `${at}.auth.senderAddress`, message: "senderAddress must not appear on an SSM endpoint; S is sourceAddress" };
123
196
  }
124
- if (mode === "asm" && !isNonEmptyString(auth["senderAddress"])) {
125
- return { path: `${at}.auth.senderAddress`, message: "a protected ASM endpoint requires senderAddress" };
197
+ if (mode === "asm" && !parseIpAddressLiteral(auth["senderAddress"])) {
198
+ return { path: `${at}.auth.senderAddress`, message: "a protected ASM endpoint requires senderAddress, an RFC 3986 IP address literal" };
126
199
  }
127
- if (auth["maxAuthDelayMs"] !== void 0 && !isIntegerIn(auth["maxAuthDelayMs"], 50, 1e3)) {
128
- return { path: `${at}.auth.maxAuthDelayMs`, message: "maxAuthDelayMs must be an integer from 50 to 1000" };
129
- }
130
- if (auth["manifestTimerMs"] !== void 0 && !isIntegerIn(auth["manifestTimerMs"], 1, Number.MAX_SAFE_INTEGER)) {
131
- return { path: `${at}.auth.manifestTimerMs`, message: "manifestTimerMs must be an integer of at least 1" };
132
- }
133
- if (auth["manifestTrack"] !== void 0 && !isNonEmptyString(auth["manifestTrack"])) {
134
- return { path: `${at}.auth.manifestTrack`, message: "manifestTrack must be a non-empty string" };
200
+ if (endpointAuthTiming(auth).manifestTimerMs < 1) {
201
+ return { path: `${at}.auth.manifestTimerMs`, message: "manifestTimerMs must be at least 1" };
135
202
  }
136
203
  if (!isIntegerIn(endpoint["bandwidth"], 1, Number.MAX_SAFE_INTEGER)) {
137
204
  return { path: `${at}.bandwidth`, message: "a protected endpoint requires bandwidth, which bounds the receiver's hold queues" };
@@ -147,7 +214,7 @@ function multicastCatalogIssues(catalog, options = {}) {
147
214
  issues.push({
148
215
  code: "multicast_auth",
149
216
  path: '$["multicast.auth"]',
150
- message: 'the root "multicast.auth" key is retired; declare auth as multicast.auth (draft -01 7.2.6)'
217
+ message: 'the root "multicast.auth" key is retired; declare auth as multicast.auth (draft -02 7.2.6)'
151
218
  });
152
219
  }
153
220
  const multicast = catalog["multicast"];
@@ -181,7 +248,7 @@ function multicastCatalogIssues(catalog, options = {}) {
181
248
  }
182
249
  const protocol = endpoint["protocol"];
183
250
  if (protocol !== void 0 && protocol !== "ssm" && protocol !== "asm") {
184
- fail("multicast_endpoint", `${at}.protocol`, `protocol must be "ssm" or "asm"; a bearer such as ${JSON.stringify(protocol)} is a networkSource entry (draft -01 4.2)`);
251
+ fail("multicast_endpoint", `${at}.protocol`, `protocol must be "ssm" or "asm"; a bearer such as ${JSON.stringify(protocol)} is a networkSource entry (draft -02 4.2)`);
185
252
  continue;
186
253
  }
187
254
  const hasAuth = endpoint["auth"] !== void 0;
@@ -190,8 +257,18 @@ function multicastCatalogIssues(catalog, options = {}) {
190
257
  continue;
191
258
  }
192
259
  const sourceAddress = endpoint["sourceAddress"];
193
- if (sourceAddress !== void 0 && !isNonEmptyString(sourceAddress)) {
194
- fail("multicast_endpoint", `${at}.sourceAddress`, "sourceAddress must be a non-empty string");
260
+ const sourceBytes = sourceAddress === void 0 ? void 0 : parseIpAddressLiteral(sourceAddress);
261
+ if (sourceAddress !== void 0 && !sourceBytes) {
262
+ fail("multicast_endpoint", `${at}.sourceAddress`, "sourceAddress must be an RFC 3986 IPv4address or IPv6address literal");
263
+ continue;
264
+ }
265
+ const groupBytes = parseIpAddressLiteral(endpoint["groupAddress"]);
266
+ if (!groupBytes) {
267
+ fail("multicast_endpoint", `${at}.groupAddress`, "groupAddress must be an RFC 3986 IPv4address or IPv6address literal");
268
+ continue;
269
+ }
270
+ if (sourceBytes && sourceBytes.length !== groupBytes.length) {
271
+ fail("multicast_endpoint", `${at}.sourceAddress`, "sourceAddress and groupAddress are of different address families");
195
272
  continue;
196
273
  }
197
274
  const mode = sourceAddress === void 0 ? "asm" : "ssm";
@@ -210,7 +287,13 @@ function multicastCatalogIssues(catalog, options = {}) {
210
287
  continue;
211
288
  }
212
289
  }
213
- sources[index] = multicastEndpointSource(endpoint);
290
+ const source = multicastEndpointSource(endpoint);
291
+ const senderBytes = source?.mode === "asm" && source.source !== void 0 ? parseIpAddressLiteral(source.source) : void 0;
292
+ if (senderBytes && senderBytes.length !== groupBytes.length) {
293
+ fail("multicast_endpoint_auth", `${at}.auth.senderAddress`, "senderAddress and groupAddress are of different address families");
294
+ continue;
295
+ }
296
+ sources[index] = source;
214
297
  const refs = Array.isArray(endpoint["tracks"]) ? endpoint["tracks"] : [];
215
298
  for (let refIndex = 0; refIndex < refs.length; refIndex++) {
216
299
  const ref = refs[refIndex];
@@ -226,38 +309,23 @@ function multicastCatalogIssues(catalog, options = {}) {
226
309
  }
227
310
  }
228
311
  }
312
+ const addressKey = (text) => {
313
+ const bytes = text === void 0 ? void 0 : parseIpAddressLiteral(text);
314
+ return bytes ? `${bytes.length}:${bytes.join(".")}` : "-";
315
+ };
229
316
  const tupleOf = (index) => {
230
317
  const endpoint = endpoints[index];
231
- const auth = endpoint["auth"];
232
- return {
233
- sg: JSON.stringify([sources[index].source ?? null, endpoint["groupAddress"]]),
234
- port: endpoint["port"],
235
- manifestPort: isObject(auth) ? auth["manifestPort"] : void 0
236
- };
318
+ return `${addressKey(sources[index].source)}|${addressKey(endpoint["groupAddress"])}|${endpoint["port"]}`;
237
319
  };
238
320
  for (let index = 0; index < endpoints.length; index++) {
239
321
  if (!sources[index])
240
322
  continue;
241
323
  const own = tupleOf(index);
242
- const at = `$.multicast.endpoints[${index}]`;
243
- if (own.manifestPort !== void 0 && own.manifestPort === own.port) {
244
- issues.push({ code: "multicast_endpoint_auth", path: `${at}.auth.manifestPort`, message: "manifestPort must differ from the endpoint's own port", endpointIndex: index });
245
- continue;
246
- }
247
324
  for (let other = 0; other < endpoints.length; other++) {
248
- if (other === index || !sources[other])
325
+ if (other === index || !sources[other] || tupleOf(other) !== own)
249
326
  continue;
250
- const peer = tupleOf(other);
251
- if (peer.sg !== own.sg)
252
- continue;
253
- if (peer.port === own.port) {
254
- issues.push({ code: "multicast_endpoint", path: `${at}.port`, message: `(S, G, port) is shared with endpoint ${other}; one endpoint object per multicast tuple`, endpointIndex: index });
255
- break;
256
- }
257
- if (own.manifestPort !== void 0 && (own.manifestPort === peer.port || own.manifestPort === peer.manifestPort)) {
258
- issues.push({ code: "multicast_endpoint_auth", path: `${at}.auth.manifestPort`, message: `manifestPort is a port or manifestPort of endpoint ${other} on the same (S, G)`, endpointIndex: index });
259
- break;
260
- }
327
+ issues.push({ code: "multicast_endpoint", path: `$.multicast.endpoints[${index}].port`, message: `(S, G, port) is shared with endpoint ${other}; one endpoint object per multicast tuple`, endpointIndex: index });
328
+ break;
261
329
  }
262
330
  }
263
331
  return issues;
@@ -357,6 +425,15 @@ function multicastFromConfig(input) {
357
425
  const where = issue.endpointIndex === void 0 ? "" : `endpoint[${issue.endpointIndex}] `;
358
426
  throw new MulticastInputError(kind, issue.endpointIndex ?? null, `${where}${issue.path.replace("$.multicast.", "")}: ${issue.message}`);
359
427
  }
428
+ endpoints.forEach((ep, idx) => {
429
+ const auth = ep.auth;
430
+ if (auth === void 0) return;
431
+ const delay = auth["maxAuthDelayMs"];
432
+ const timer = auth["manifestTimerMs"];
433
+ const track = auth["manifestTrack"];
434
+ const bad = delay !== void 0 && !(Number.isSafeInteger(delay) && delay >= 50 && delay <= 1e3) ? "maxAuthDelayMs must be an integer from 50 to 1000" : timer !== void 0 && !(Number.isSafeInteger(timer) && timer >= 1) ? "manifestTimerMs must be an integer of at least 1" : track !== void 0 && !(typeof track === "string" && track.length > 0) ? "manifestTrack must be a non-empty string" : void 0;
435
+ if (bad) throw new MulticastInputError("invalid_endpoint_auth", idx, `endpoint[${idx}] auth: ${bad}`);
436
+ });
360
437
  if (input.enabled !== void 0) out.enabled = input.enabled;
361
438
  if (input.version !== void 0) out.version = input.version;
362
439
  if (input.validUntil !== void 0) out.validUntil = input.validUntil;