@blockcast/mmt-container 2.0.0 → 2.0.1
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/README.md +1 -1
- package/dist/browser-runtime.js +45 -12
- package/dist/browser-runtime.js.map +2 -2
- package/dist/delivery-catalog.js +3 -0
- package/dist/delivery-catalog.js.map +2 -2
- package/dist/mmtp-container-parser.no-modules.js +19 -10
- package/dist/mmtp-container-parser.no-modules.js.map +2 -2
- package/dist/multicast-manager.js +45 -12
- package/dist/multicast-manager.js.map +2 -2
- package/dist/multicast-transport.d.ts +8 -1
- package/dist/multicast-transport.d.ts.map +1 -1
- package/dist/multicast-transport.js +93 -2
- package/dist/multicast-transport.js.map +4 -4
- package/dist/no-modules.js +19 -10
- package/dist/no-modules.js.map +2 -2
- package/package.json +5 -5
- package/src/multicast-transport.ts +43 -4
package/README.md
CHANGED
|
@@ -277,7 +277,7 @@ per-commit `0.1.0-main.<sha>` prerelease from `stamp-prerelease.mjs`), and a
|
|
|
277
277
|
> Superseded by BLO-40665. Every package is now bumped in lockstep by a `v*`
|
|
278
278
|
> tag and the sibling ranges move with it, so the objection above no longer
|
|
279
279
|
> applies -- it was an argument against bumping *one* package. The set is at
|
|
280
|
-
> `2.0.
|
|
280
|
+
> `2.0.1`; see `RELEASES.md`.
|
|
281
281
|
|
|
282
282
|
#### Track-role convention (Phase 14)
|
|
283
283
|
|
package/dist/browser-runtime.js
CHANGED
|
@@ -8514,6 +8514,9 @@ var AMTGatewayManager = class {
|
|
|
8514
8514
|
*/
|
|
8515
8515
|
async subscribe(config) {
|
|
8516
8516
|
const { key } = config;
|
|
8517
|
+
if (config.protected) {
|
|
8518
|
+
throw new Error("AMTGatewayManager refuses a protected join: it reports no per-datagram sender (multicast-02 7.2.7)");
|
|
8519
|
+
}
|
|
8517
8520
|
const keyStr = this.getKeyString(key);
|
|
8518
8521
|
if (this.subscriptions.has(keyStr)) {
|
|
8519
8522
|
console.log(`[AMT] Reusing existing subscription to ${keyStr}`);
|
|
@@ -21977,7 +21980,6 @@ _RistrettoPoint.Fn = /* @__PURE__ */ (() => Fn)();
|
|
|
21977
21980
|
var BCPV2_LEAD_BOUND_MS = 5e3;
|
|
21978
21981
|
var REPLAY_WINDOW = 64n;
|
|
21979
21982
|
var BUCKET_DEPTH = 64;
|
|
21980
|
-
var BUCKET_RATE_PER_MS = 1;
|
|
21981
21983
|
var hex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
21982
21984
|
function sameBytes(a, b) {
|
|
21983
21985
|
if (a.byteLength !== b.byteLength)
|
|
@@ -22006,6 +22008,10 @@ var BcProvenanceV2Receiver = class {
|
|
|
22006
22008
|
verifySig;
|
|
22007
22009
|
accepted;
|
|
22008
22010
|
queueLimit;
|
|
22011
|
+
/** Octets a held datagram is charged beyond its payload: the IPv4 or IPv6 plus UDP headers (step 2). */
|
|
22012
|
+
headerOctets;
|
|
22013
|
+
/** Budget refill, tokens per ms: V / 1000 (step 1). */
|
|
22014
|
+
bucketRatePerMs;
|
|
22009
22015
|
current = null;
|
|
22010
22016
|
previous = null;
|
|
22011
22017
|
graceStart = {};
|
|
@@ -22051,6 +22057,8 @@ var BcProvenanceV2Receiver = class {
|
|
|
22051
22057
|
multicast: this.maxAuthDelayMs * bandwidth / 8e3,
|
|
22052
22058
|
unicast: (this.maxAuthDelayMs + this.widenMs) * bandwidth / 8e3
|
|
22053
22059
|
};
|
|
22060
|
+
this.headerOctets = parseBcProvenanceV2Address(cfg.endpoint.groupAddress).byteLength === 16 ? 48 : 28;
|
|
22061
|
+
this.bucketRatePerMs = Math.max(1e3, 1.25 * bandwidth / (8 * 512 * 37)) / 1e3;
|
|
22054
22062
|
const t = this.now();
|
|
22055
22063
|
this.buckets = {
|
|
22056
22064
|
multicast: { tokens: BUCKET_DEPTH, last: t, dropped: 0 },
|
|
@@ -22149,7 +22157,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
22149
22157
|
arrival: now,
|
|
22150
22158
|
sent,
|
|
22151
22159
|
digest: hex(sha2562(bytes)),
|
|
22152
|
-
pairs: record.digests.map((d, i) => ({ digest: hex(d), seq: record.firstSeq + BigInt(i), admitted: false })),
|
|
22160
|
+
pairs: record.digests.map((d, i) => ({ digest: hex(d), epoch, seq: record.firstSeq + BigInt(i), admitted: false })),
|
|
22153
22161
|
awaitingMulticast: path === "mirror"
|
|
22154
22162
|
};
|
|
22155
22163
|
this.verified.set(id, entry);
|
|
@@ -22216,12 +22224,12 @@ var BcProvenanceV2Receiver = class {
|
|
|
22216
22224
|
digest(datagram) {
|
|
22217
22225
|
return hex(bcProvenanceV2DatagramDigest(this.cfg.endpoint, datagram));
|
|
22218
22226
|
}
|
|
22219
|
-
/** Consume the
|
|
22227
|
+
/** Consume the pending entry for `digest` lowest in (epoch, sequence) order, admitting it. */
|
|
22220
22228
|
consumePending(digest, now) {
|
|
22221
22229
|
const list = this.pending.get(digest);
|
|
22222
22230
|
if (!list?.length)
|
|
22223
22231
|
return false;
|
|
22224
|
-
list.sort((a, b) => a.pair.seq < b.pair.seq ? -1 : a.pair.seq > b.pair.seq ? 1 : 0);
|
|
22232
|
+
list.sort((a, b) => a.pair.epoch - b.pair.epoch || (a.pair.seq < b.pair.seq ? -1 : a.pair.seq > b.pair.seq ? 1 : 0));
|
|
22225
22233
|
const hit = list.shift();
|
|
22226
22234
|
if (list.length === 0)
|
|
22227
22235
|
this.pending.delete(digest);
|
|
@@ -22230,14 +22238,18 @@ var BcProvenanceV2Receiver = class {
|
|
|
22230
22238
|
this.admittedUntil.set(digest, now + this.maxAuthDelayMs + this.leadMs);
|
|
22231
22239
|
return true;
|
|
22232
22240
|
}
|
|
22241
|
+
/** A held datagram's charge against its queue: payload plus IP and UDP headers. */
|
|
22242
|
+
charge(held) {
|
|
22243
|
+
return held.datagram.byteLength + this.headerOctets;
|
|
22244
|
+
}
|
|
22233
22245
|
enqueue(held, path, now) {
|
|
22234
|
-
if (this.holdBytes[path] + held
|
|
22246
|
+
if (this.holdBytes[path] + this.charge(held) > this.queueLimit[path]) {
|
|
22235
22247
|
return { verdict: "overflow", admitted: [] };
|
|
22236
22248
|
}
|
|
22237
22249
|
held.expiry = now + this.maxAuthDelayMs + (path === "unicast" ? this.widenMs : 0);
|
|
22238
22250
|
held.order = this.order++;
|
|
22239
22251
|
this.hold[path].push(held);
|
|
22240
|
-
this.holdBytes[path] += held
|
|
22252
|
+
this.holdBytes[path] += this.charge(held);
|
|
22241
22253
|
return { verdict: "held", admitted: [] };
|
|
22242
22254
|
}
|
|
22243
22255
|
/** Remove and return the oldest held datagram with `digest`, across both queues. */
|
|
@@ -22252,7 +22264,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
22252
22264
|
if (!best)
|
|
22253
22265
|
return void 0;
|
|
22254
22266
|
const [held] = this.hold[best.path].splice(best.index, 1);
|
|
22255
|
-
this.holdBytes[best.path] -= held
|
|
22267
|
+
this.holdBytes[best.path] -= this.charge(held);
|
|
22256
22268
|
return held.datagram;
|
|
22257
22269
|
}
|
|
22258
22270
|
dropAdmittedHeld() {
|
|
@@ -22260,7 +22272,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
22260
22272
|
this.hold[path] = this.hold[path].filter((h) => {
|
|
22261
22273
|
const keep = h.digest === void 0 || !this.admittedUntil.has(h.digest);
|
|
22262
22274
|
if (!keep)
|
|
22263
|
-
this.holdBytes[path] -= h
|
|
22275
|
+
this.holdBytes[path] -= this.charge(h);
|
|
22264
22276
|
return keep;
|
|
22265
22277
|
});
|
|
22266
22278
|
}
|
|
@@ -22344,7 +22356,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
22344
22356
|
}
|
|
22345
22357
|
take(path, now) {
|
|
22346
22358
|
const b = this.buckets[path];
|
|
22347
|
-
b.tokens = Math.min(BUCKET_DEPTH, b.tokens + (now - b.last) *
|
|
22359
|
+
b.tokens = Math.min(BUCKET_DEPTH, b.tokens + (now - b.last) * this.bucketRatePerMs);
|
|
22348
22360
|
b.last = now;
|
|
22349
22361
|
if (b.tokens < 1) {
|
|
22350
22362
|
b.dropped++;
|
|
@@ -22358,7 +22370,7 @@ var BcProvenanceV2Receiver = class {
|
|
|
22358
22370
|
this.hold[path] = this.hold[path].filter((h) => {
|
|
22359
22371
|
if (h.expiry > now)
|
|
22360
22372
|
return true;
|
|
22361
|
-
this.holdBytes[path] -= h
|
|
22373
|
+
this.holdBytes[path] -= this.charge(h);
|
|
22362
22374
|
this.expired[path]++;
|
|
22363
22375
|
return false;
|
|
22364
22376
|
});
|
|
@@ -26069,6 +26081,10 @@ var MulticastTransport = class {
|
|
|
26069
26081
|
#acceptedBytesByPacketId = /* @__PURE__ */ new Map();
|
|
26070
26082
|
#packetIds = null;
|
|
26071
26083
|
#protected = false;
|
|
26084
|
+
// A protected join's S, and the last sender spelling that matched it, so a
|
|
26085
|
+
// matching sender parses once per spelling rather than per datagram.
|
|
26086
|
+
#protectedSource = void 0;
|
|
26087
|
+
#matchedSource = void 0;
|
|
26072
26088
|
// The sender S this join's accepted packets came from, once known; see
|
|
26073
26089
|
// packetsDroppedForeignSender. Cleared by every join and by a silence
|
|
26074
26090
|
// failback, so a replacement publisher is not locked out.
|
|
@@ -26120,6 +26136,11 @@ var MulticastTransport = class {
|
|
|
26120
26136
|
return this.#declined(generation, "unavailable");
|
|
26121
26137
|
}
|
|
26122
26138
|
let failure = "invalid-request";
|
|
26139
|
+
const protectedSource = config.protected === true ? config.sourceAddress ?? config.senderAddress : void 0;
|
|
26140
|
+
if (config.protected === true && (protectedSource === void 0 || config.atsc3)) {
|
|
26141
|
+
console.warn("[MulticastTransport] protected join refused: the path reports no per-datagram sender to check against S");
|
|
26142
|
+
return this.#declined(generation, "invalid-request");
|
|
26143
|
+
}
|
|
26123
26144
|
try {
|
|
26124
26145
|
const packetIds = config.packetIds?.length ? new Set(config.packetIds) : null;
|
|
26125
26146
|
console.log(
|
|
@@ -26155,7 +26176,7 @@ var MulticastTransport = class {
|
|
|
26155
26176
|
this.#subscription = subscription;
|
|
26156
26177
|
const live = () => !this.#disposed && generation === this.#joinGeneration;
|
|
26157
26178
|
const anySource = !config.sourceAddress && !config.senderAddress && !config.atsc3;
|
|
26158
|
-
const packetListener = !anySource && subscriptionDeliversPacketBatches(subscription) ? {
|
|
26179
|
+
const packetListener = !anySource && protectedSource === void 0 && subscriptionDeliversPacketBatches(subscription) ? {
|
|
26159
26180
|
type: "packets",
|
|
26160
26181
|
listener: (event) => {
|
|
26161
26182
|
if (!live()) return;
|
|
@@ -26171,6 +26192,8 @@ var MulticastTransport = class {
|
|
|
26171
26192
|
this.#packetListener = packetListener;
|
|
26172
26193
|
this.#packetIds = packetIds;
|
|
26173
26194
|
this.#protected = config.protected === true;
|
|
26195
|
+
this.#protectedSource = protectedSource;
|
|
26196
|
+
this.#matchedSource = void 0;
|
|
26174
26197
|
this.#providerFilter = {
|
|
26175
26198
|
acknowledged: packetIds,
|
|
26176
26199
|
sent: 0,
|
|
@@ -26630,13 +26653,23 @@ var MulticastTransport = class {
|
|
|
26630
26653
|
this.#delivery = void 0;
|
|
26631
26654
|
this.#packetIds = null;
|
|
26632
26655
|
this.#protected = false;
|
|
26656
|
+
this.#protectedSource = void 0;
|
|
26657
|
+
this.#matchedSource = void 0;
|
|
26633
26658
|
this.#providerFilter = null;
|
|
26634
26659
|
this.#context = void 0;
|
|
26635
26660
|
this.#withdrawnPacketIds.clear();
|
|
26636
26661
|
}
|
|
26637
26662
|
/** The ingress filter; an accepted packet is counted here, once. */
|
|
26638
26663
|
#acceptPacket(packet, sourceAddress) {
|
|
26639
|
-
const
|
|
26664
|
+
const source = this.#protectedSource;
|
|
26665
|
+
if (source !== void 0 && (sourceAddress === void 0 || sourceAddress !== this.#matchedSource)) {
|
|
26666
|
+
if (sourceAddress === void 0 || !sameIpAddress(sourceAddress, source)) {
|
|
26667
|
+
this.#stats.packetsDroppedForeignSender++;
|
|
26668
|
+
return false;
|
|
26669
|
+
}
|
|
26670
|
+
this.#matchedSource = sourceAddress;
|
|
26671
|
+
}
|
|
26672
|
+
const owner = source === void 0 ? this.#lockedTo?.() ?? this.#sender : void 0;
|
|
26640
26673
|
if (sourceAddress !== void 0 && owner !== void 0 && sourceAddress !== owner) {
|
|
26641
26674
|
this.#stats.packetsDroppedForeignSender++;
|
|
26642
26675
|
return false;
|