@svta/cml-cmcd 2.3.2 → 2.4.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 +27 -0
- package/dist/index.d.ts +404 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +586 -22
- package/dist/index.js.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -74,6 +74,12 @@ const CMCD_EVENT_BITRATE_CHANGE = "bc";
|
|
|
74
74
|
*/
|
|
75
75
|
const CMCD_EVENT_PLAY_STATE = "ps";
|
|
76
76
|
/**
|
|
77
|
+
* CMCD event type for the 'pr' key (playback rate change).
|
|
78
|
+
*
|
|
79
|
+
* @public
|
|
80
|
+
*/
|
|
81
|
+
const CMCD_EVENT_PLAYBACK_RATE = "pr";
|
|
82
|
+
/**
|
|
77
83
|
* CMCD event type for the 'e' key (error).
|
|
78
84
|
*
|
|
79
85
|
* @public
|
|
@@ -175,6 +181,7 @@ const CMCD_EVENT_CUSTOM_EVENT = "ce";
|
|
|
175
181
|
const CmcdEventType = {
|
|
176
182
|
BITRATE_CHANGE: CMCD_EVENT_BITRATE_CHANGE,
|
|
177
183
|
PLAY_STATE: CMCD_EVENT_PLAY_STATE,
|
|
184
|
+
PLAYBACK_RATE: CMCD_EVENT_PLAYBACK_RATE,
|
|
178
185
|
ERROR: CMCD_EVENT_ERROR,
|
|
179
186
|
TIME_INTERVAL: CMCD_EVENT_TIME_INTERVAL,
|
|
180
187
|
CONTENT_ID: CMCD_EVENT_CONTENT_ID,
|
|
@@ -192,6 +199,32 @@ const CmcdEventType = {
|
|
|
192
199
|
CUSTOM_EVENT: CMCD_EVENT_CUSTOM_EVENT
|
|
193
200
|
};
|
|
194
201
|
|
|
202
|
+
//#endregion
|
|
203
|
+
//#region src/CMCD_STATE_EVENT_FIELDS.ts
|
|
204
|
+
/**
|
|
205
|
+
* Maps each state-change event type to the persistent field whose value
|
|
206
|
+
* the event signals.
|
|
207
|
+
*
|
|
208
|
+
* Per CTA-5004-B, the state-change events `ps`, `pr`, `c`, `b`, `bc` are
|
|
209
|
+
* state-transition markers and must carry the field whose value they signal.
|
|
210
|
+
* Consumers force-include the field post-filter (`prepareCmcdData`), dedup
|
|
211
|
+
* against its value (`CmcdReporter`), and check its presence in payloads
|
|
212
|
+
* (`validateCmcdStructure`).
|
|
213
|
+
*
|
|
214
|
+
* Iteration order is load-bearing: `CmcdReporter.update()` fires state-change
|
|
215
|
+
* events in map order when multiple tracked fields change in a single call.
|
|
216
|
+
* Do not reorder entries without auditing reporter behavior.
|
|
217
|
+
*
|
|
218
|
+
* @internal
|
|
219
|
+
*/
|
|
220
|
+
const CMCD_STATE_EVENT_FIELDS = /* @__PURE__ */ new Map([
|
|
221
|
+
[CMCD_EVENT_PLAY_STATE, "sta"],
|
|
222
|
+
[CMCD_EVENT_PLAYBACK_RATE, "pr"],
|
|
223
|
+
[CMCD_EVENT_CONTENT_ID, "cid"],
|
|
224
|
+
[CMCD_EVENT_BACKGROUNDED_MODE, "bg"],
|
|
225
|
+
[CMCD_EVENT_BITRATE_CHANGE, "br"]
|
|
226
|
+
]);
|
|
227
|
+
|
|
195
228
|
//#endregion
|
|
196
229
|
//#region src/CmcdReportingMode.ts
|
|
197
230
|
/**
|
|
@@ -282,7 +315,7 @@ const CMCD_REQUEST_KEYS = [
|
|
|
282
315
|
|
|
283
316
|
//#endregion
|
|
284
317
|
//#region src/isCmcdCustomKey.ts
|
|
285
|
-
const CUSTOM_KEY_REGEX = /^[a-zA-Z0-9
|
|
318
|
+
const CUSTOM_KEY_REGEX = /^[a-zA-Z0-9.-]+$/;
|
|
286
319
|
/**
|
|
287
320
|
* Check if a key is a custom key.
|
|
288
321
|
*
|
|
@@ -291,14 +324,18 @@ const CUSTOM_KEY_REGEX = /^[a-zA-Z0-9-.]+-[a-zA-Z0-9-.]+$/;
|
|
|
291
324
|
* @returns `true` if the key is a custom key, `false` otherwise.
|
|
292
325
|
*
|
|
293
326
|
* @public
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* {@includeCode ../test/isCmcdCustomKey.test.ts#example}
|
|
294
330
|
*/
|
|
295
331
|
function isCmcdCustomKey(key) {
|
|
296
|
-
|
|
332
|
+
const separator = key.indexOf("-", 1);
|
|
333
|
+
return separator > 0 && separator < key.length - 1 && CUSTOM_KEY_REGEX.test(key);
|
|
297
334
|
}
|
|
298
335
|
|
|
299
336
|
//#endregion
|
|
300
337
|
//#region src/isCmcdRequestKey.ts
|
|
301
|
-
const CMCD_REQUEST_KEY_SET = new Set(CMCD_REQUEST_KEYS);
|
|
338
|
+
const CMCD_REQUEST_KEY_SET = /* @__PURE__ */ new Set(CMCD_REQUEST_KEYS);
|
|
302
339
|
/**
|
|
303
340
|
* Check if a key is a valid CMCD request key.
|
|
304
341
|
*
|
|
@@ -335,7 +372,7 @@ const CMCD_RESPONSE_KEYS = [
|
|
|
335
372
|
|
|
336
373
|
//#endregion
|
|
337
374
|
//#region src/isCmcdResponseReceivedKey.ts
|
|
338
|
-
const CMCD_RESPONSE_KEY_SET = new Set(CMCD_RESPONSE_KEYS);
|
|
375
|
+
const CMCD_RESPONSE_KEY_SET = /* @__PURE__ */ new Set(CMCD_RESPONSE_KEYS);
|
|
339
376
|
/**
|
|
340
377
|
* Check if a key is a valid CMCD response key.
|
|
341
378
|
*
|
|
@@ -354,7 +391,7 @@ function isCmcdResponseReceivedKey(key) {
|
|
|
354
391
|
|
|
355
392
|
//#endregion
|
|
356
393
|
//#region src/isCmcdEventKey.ts
|
|
357
|
-
const CMCD_EVENT_KEY_SET = new Set(CMCD_EVENT_KEYS);
|
|
394
|
+
const CMCD_EVENT_KEY_SET = /* @__PURE__ */ new Set(CMCD_EVENT_KEYS);
|
|
358
395
|
/**
|
|
359
396
|
* Check if a key is a valid CMCD event key.
|
|
360
397
|
*
|
|
@@ -427,7 +464,7 @@ const CMCD_V1_KEYS = [
|
|
|
427
464
|
|
|
428
465
|
//#endregion
|
|
429
466
|
//#region src/isCmcdV1Key.ts
|
|
430
|
-
const CMCD_V1_KEY_SET$1 = new Set(CMCD_V1_KEYS);
|
|
467
|
+
const CMCD_V1_KEY_SET$1 = /* @__PURE__ */ new Set(CMCD_V1_KEYS);
|
|
431
468
|
/**
|
|
432
469
|
* Filter function for CMCD v1 keys.
|
|
433
470
|
*
|
|
@@ -446,7 +483,7 @@ function isCmcdV1Key(key) {
|
|
|
446
483
|
|
|
447
484
|
//#endregion
|
|
448
485
|
//#region src/isTokenField.ts
|
|
449
|
-
const TOKEN_FIELDS = new Set([
|
|
486
|
+
const TOKEN_FIELDS = /* @__PURE__ */ new Set([
|
|
450
487
|
"ot",
|
|
451
488
|
"sf",
|
|
452
489
|
"st",
|
|
@@ -550,6 +587,8 @@ function prepareCmcdData(obj, options = {}) {
|
|
|
550
587
|
if (!keys.includes("e") && eventType != null) keys.push("e");
|
|
551
588
|
if (!keys.includes("ts")) keys.push("ts");
|
|
552
589
|
if (!keys.includes("cen") && data["cen"] != null && eventType === CMCD_EVENT_CUSTOM_EVENT) keys.push("cen");
|
|
590
|
+
const requiredField = eventType ? CMCD_STATE_EVENT_FIELDS.get(eventType) : void 0;
|
|
591
|
+
if (requiredField && data[requiredField] != null && !keys.includes(requiredField)) keys.push(requiredField);
|
|
553
592
|
}
|
|
554
593
|
if (keys.length === 0) return results;
|
|
555
594
|
if (version > 1 && !keys.includes("v")) keys.push("v");
|
|
@@ -565,9 +604,10 @@ function prepareCmcdData(obj, options = {}) {
|
|
|
565
604
|
if (typeof formatter === "function") value = formatter(value, formatterOptions);
|
|
566
605
|
if (key === "v") if (version === 1) continue;
|
|
567
606
|
else value = version;
|
|
568
|
-
if (key === "pr" && value === 1) continue;
|
|
607
|
+
if (key === "pr" && value === 1 && !(isEventMode && data["e"] === CMCD_EVENT_PLAYBACK_RATE)) continue;
|
|
569
608
|
if (isEventMode && key === "ts" && !Number.isFinite(value)) value = Date.now();
|
|
570
|
-
|
|
609
|
+
const isBgFalseTransition = isEventMode && value === false && key === "bg" && data["e"] === CMCD_EVENT_BACKGROUNDED_MODE;
|
|
610
|
+
if (!isValid(value) && !isBgFalseTransition) continue;
|
|
571
611
|
if (isTokenField(key) && typeof value === "string") value = new SfToken(value);
|
|
572
612
|
results[key] = value;
|
|
573
613
|
}
|
|
@@ -906,7 +946,7 @@ const CMCD_DEFAULT_TIME_INTERVAL = 30;
|
|
|
906
946
|
*
|
|
907
947
|
* @public
|
|
908
948
|
*/
|
|
909
|
-
const CMCD_KEYS = [
|
|
949
|
+
const CMCD_KEYS = /* @__PURE__ */ [
|
|
910
950
|
...CMCD_V1_KEYS,
|
|
911
951
|
...CMCD_REQUEST_KEYS,
|
|
912
952
|
...CMCD_RESPONSE_KEYS,
|
|
@@ -979,6 +1019,417 @@ const CmcdPlayerState = {
|
|
|
979
1019
|
PRELOADING: "d"
|
|
980
1020
|
};
|
|
981
1021
|
|
|
1022
|
+
//#endregion
|
|
1023
|
+
//#region src/CmcdRecordedReportMode.ts
|
|
1024
|
+
/**
|
|
1025
|
+
* CMCD query reporting mode — CMCD data carried as a `CMCD=` URL
|
|
1026
|
+
* query parameter.
|
|
1027
|
+
*
|
|
1028
|
+
* @public
|
|
1029
|
+
*/
|
|
1030
|
+
const CMCD_RECORDED_REPORT_MODE_QUERY = "query";
|
|
1031
|
+
/**
|
|
1032
|
+
* CMCD header reporting mode — CMCD data carried as `Cmcd-*` HTTP
|
|
1033
|
+
* headers.
|
|
1034
|
+
*
|
|
1035
|
+
* @public
|
|
1036
|
+
*/
|
|
1037
|
+
const CMCD_RECORDED_REPORT_MODE_HEADER = "header";
|
|
1038
|
+
/**
|
|
1039
|
+
* CMCD event reporting mode — CMCD data carried as a POST body to an
|
|
1040
|
+
* event-target URL.
|
|
1041
|
+
*
|
|
1042
|
+
* @public
|
|
1043
|
+
*/
|
|
1044
|
+
const CMCD_RECORDED_REPORT_MODE_EVENT = "event";
|
|
1045
|
+
/**
|
|
1046
|
+
* Reporting mode under which a captured report was observed by
|
|
1047
|
+
* `CmcdReportRecorder`.
|
|
1048
|
+
*
|
|
1049
|
+
* @enum
|
|
1050
|
+
*
|
|
1051
|
+
* @public
|
|
1052
|
+
*/
|
|
1053
|
+
const CmcdRecordedReportMode = {
|
|
1054
|
+
QUERY: CMCD_RECORDED_REPORT_MODE_QUERY,
|
|
1055
|
+
HEADER: CMCD_RECORDED_REPORT_MODE_HEADER,
|
|
1056
|
+
EVENT: CMCD_RECORDED_REPORT_MODE_EVENT
|
|
1057
|
+
};
|
|
1058
|
+
|
|
1059
|
+
//#endregion
|
|
1060
|
+
//#region src/CmcdRecordedRequestType.ts
|
|
1061
|
+
/**
|
|
1062
|
+
* Manifest or playlist request classification.
|
|
1063
|
+
*
|
|
1064
|
+
* @public
|
|
1065
|
+
*/
|
|
1066
|
+
const CMCD_RECORDED_REQUEST_TYPE_MANIFEST = "manifest";
|
|
1067
|
+
/**
|
|
1068
|
+
* Media segment request classification.
|
|
1069
|
+
*
|
|
1070
|
+
* @public
|
|
1071
|
+
*/
|
|
1072
|
+
const CMCD_RECORDED_REQUEST_TYPE_SEGMENT = "segment";
|
|
1073
|
+
/**
|
|
1074
|
+
* CMCD event-mode report request classification (POST to a URL in
|
|
1075
|
+
* `eventTargetUrls`).
|
|
1076
|
+
*
|
|
1077
|
+
* @public
|
|
1078
|
+
*/
|
|
1079
|
+
const CMCD_RECORDED_REQUEST_TYPE_EVENT = "event";
|
|
1080
|
+
/**
|
|
1081
|
+
* Fallback classification for requests the recorder did not recognize
|
|
1082
|
+
* as manifest, segment, or event.
|
|
1083
|
+
*
|
|
1084
|
+
* @public
|
|
1085
|
+
*/
|
|
1086
|
+
const CMCD_RECORDED_REQUEST_TYPE_UNKNOWN = "unknown";
|
|
1087
|
+
/**
|
|
1088
|
+
* Classification of a captured request, used by `CmcdReportRecorder`
|
|
1089
|
+
* for filtering.
|
|
1090
|
+
*
|
|
1091
|
+
* @enum
|
|
1092
|
+
*
|
|
1093
|
+
* @public
|
|
1094
|
+
*/
|
|
1095
|
+
const CmcdRecordedRequestType = {
|
|
1096
|
+
MANIFEST: CMCD_RECORDED_REQUEST_TYPE_MANIFEST,
|
|
1097
|
+
SEGMENT: CMCD_RECORDED_REQUEST_TYPE_SEGMENT,
|
|
1098
|
+
EVENT: CMCD_RECORDED_REQUEST_TYPE_EVENT,
|
|
1099
|
+
UNKNOWN: CMCD_RECORDED_REQUEST_TYPE_UNKNOWN
|
|
1100
|
+
};
|
|
1101
|
+
|
|
1102
|
+
//#endregion
|
|
1103
|
+
//#region src/createFetchTransport.ts
|
|
1104
|
+
async function toHttpRequest(request) {
|
|
1105
|
+
const headers = {};
|
|
1106
|
+
request.headers.forEach((value, name) => {
|
|
1107
|
+
headers[name.toLowerCase()] = value;
|
|
1108
|
+
});
|
|
1109
|
+
let body;
|
|
1110
|
+
if (request.body) try {
|
|
1111
|
+
body = await request.text();
|
|
1112
|
+
} catch {
|
|
1113
|
+
body = void 0;
|
|
1114
|
+
}
|
|
1115
|
+
return {
|
|
1116
|
+
url: request.url,
|
|
1117
|
+
method: request.method,
|
|
1118
|
+
headers,
|
|
1119
|
+
body
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Create a transport adapter that patches `globalThis.fetch` to capture
|
|
1124
|
+
* CMCD-bearing requests, normalizing each to `HttpRequest` (lowercase
|
|
1125
|
+
* headers, body read as UTF-8 string). Returns the adapter object
|
|
1126
|
+
* expected by `CmcdReportRecorder`.
|
|
1127
|
+
*
|
|
1128
|
+
* Bodies are read once via `Request.text()` for inspection. This is
|
|
1129
|
+
* safe for the body types CMCD reports use in practice (`string`,
|
|
1130
|
+
* `Blob`, `ArrayBuffer`, `FormData`, `URLSearchParams`), which can be
|
|
1131
|
+
* read by the wrapper and re-read by the underlying `fetch`. Passing
|
|
1132
|
+
* a `ReadableStream` as `init.body` is not supported — the stream is
|
|
1133
|
+
* consumed by the wrapper and the underlying `fetch` will receive an
|
|
1134
|
+
* already-disturbed stream.
|
|
1135
|
+
*
|
|
1136
|
+
* @public
|
|
1137
|
+
*/
|
|
1138
|
+
function createFetchTransport() {
|
|
1139
|
+
return { attach(deliver) {
|
|
1140
|
+
const origFetch = globalThis.fetch;
|
|
1141
|
+
globalThis.fetch = async (input, init) => {
|
|
1142
|
+
const synthetic = deliver(await toHttpRequest(input instanceof Request ? input.clone() : new Request(input, init)));
|
|
1143
|
+
if (synthetic) return synthetic;
|
|
1144
|
+
return origFetch.call(globalThis, input, init);
|
|
1145
|
+
};
|
|
1146
|
+
return () => {
|
|
1147
|
+
globalThis.fetch = origFetch;
|
|
1148
|
+
};
|
|
1149
|
+
} };
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
//#endregion
|
|
1153
|
+
//#region src/createXhrTransport.ts
|
|
1154
|
+
async function completeXhrWith(xhr, response) {
|
|
1155
|
+
const text = await response.clone().text().catch(() => "");
|
|
1156
|
+
const ProgressEventCtor = typeof ProgressEvent !== "undefined" ? ProgressEvent : Event;
|
|
1157
|
+
queueMicrotask(() => {
|
|
1158
|
+
try {
|
|
1159
|
+
Object.defineProperty(xhr, "status", {
|
|
1160
|
+
value: response.status,
|
|
1161
|
+
configurable: true
|
|
1162
|
+
});
|
|
1163
|
+
Object.defineProperty(xhr, "statusText", {
|
|
1164
|
+
value: response.statusText,
|
|
1165
|
+
configurable: true
|
|
1166
|
+
});
|
|
1167
|
+
Object.defineProperty(xhr, "readyState", {
|
|
1168
|
+
value: 4,
|
|
1169
|
+
configurable: true
|
|
1170
|
+
});
|
|
1171
|
+
Object.defineProperty(xhr, "responseURL", {
|
|
1172
|
+
value: xhr._cmcdUrl ?? "",
|
|
1173
|
+
configurable: true
|
|
1174
|
+
});
|
|
1175
|
+
Object.defineProperty(xhr, "response", {
|
|
1176
|
+
value: text,
|
|
1177
|
+
configurable: true
|
|
1178
|
+
});
|
|
1179
|
+
Object.defineProperty(xhr, "responseText", {
|
|
1180
|
+
value: text,
|
|
1181
|
+
configurable: true
|
|
1182
|
+
});
|
|
1183
|
+
if (typeof xhr.onload === "function") xhr.onload.call(xhr, new ProgressEventCtor("load"));
|
|
1184
|
+
if (typeof xhr.onloadend === "function") xhr.onloadend.call(xhr, new ProgressEventCtor("loadend"));
|
|
1185
|
+
} catch {
|
|
1186
|
+
const target = xhr;
|
|
1187
|
+
target["status"] = response.status;
|
|
1188
|
+
target["statusText"] = response.statusText;
|
|
1189
|
+
target["readyState"] = 4;
|
|
1190
|
+
target["responseURL"] = xhr._cmcdUrl ?? "";
|
|
1191
|
+
target["response"] = text;
|
|
1192
|
+
target["responseText"] = text;
|
|
1193
|
+
if (typeof xhr.onload === "function") xhr.onload.call(xhr, new ProgressEventCtor("load"));
|
|
1194
|
+
if (typeof xhr.onloadend === "function") xhr.onloadend.call(xhr, new ProgressEventCtor("loadend"));
|
|
1195
|
+
}
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
/**
|
|
1199
|
+
* Create a transport adapter that patches `XMLHttpRequest.prototype` to
|
|
1200
|
+
* capture CMCD-bearing requests. Returns the adapter object expected by
|
|
1201
|
+
* `CmcdReportRecorder`.
|
|
1202
|
+
*
|
|
1203
|
+
* @public
|
|
1204
|
+
*/
|
|
1205
|
+
function createXhrTransport() {
|
|
1206
|
+
return { attach(deliver) {
|
|
1207
|
+
const Xhr = globalThis.XMLHttpRequest;
|
|
1208
|
+
if (!Xhr) return () => {};
|
|
1209
|
+
const origOpen = Xhr.prototype.open;
|
|
1210
|
+
const origSetRequestHeader = Xhr.prototype.setRequestHeader;
|
|
1211
|
+
const origSend = Xhr.prototype.send;
|
|
1212
|
+
Xhr.prototype.open = function(method, url) {
|
|
1213
|
+
this._cmcdMethod = method;
|
|
1214
|
+
this._cmcdUrl = typeof url === "string" ? url : url.toString();
|
|
1215
|
+
this._cmcdHeaders = {};
|
|
1216
|
+
return origOpen.apply(this, arguments);
|
|
1217
|
+
};
|
|
1218
|
+
Xhr.prototype.setRequestHeader = function(name, value) {
|
|
1219
|
+
if (this._cmcdHeaders) this._cmcdHeaders[name.toLowerCase()] = value;
|
|
1220
|
+
return origSetRequestHeader.apply(this, [name, value]);
|
|
1221
|
+
};
|
|
1222
|
+
Xhr.prototype.send = function(body) {
|
|
1223
|
+
const synthetic = deliver({
|
|
1224
|
+
url: this._cmcdUrl ?? "",
|
|
1225
|
+
method: (this._cmcdMethod ?? "GET").toUpperCase(),
|
|
1226
|
+
headers: this._cmcdHeaders ?? {},
|
|
1227
|
+
body: (typeof Document !== "undefined" && body instanceof Document ? void 0 : body) ?? void 0
|
|
1228
|
+
});
|
|
1229
|
+
if (synthetic) {
|
|
1230
|
+
completeXhrWith(this, synthetic);
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
return origSend.apply(this, [body]);
|
|
1234
|
+
};
|
|
1235
|
+
return () => {
|
|
1236
|
+
Xhr.prototype.open = origOpen;
|
|
1237
|
+
Xhr.prototype.setRequestHeader = origSetRequestHeader;
|
|
1238
|
+
Xhr.prototype.send = origSend;
|
|
1239
|
+
};
|
|
1240
|
+
} };
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
//#endregion
|
|
1244
|
+
//#region src/CmcdReportRecorder.ts
|
|
1245
|
+
const MANIFEST_EXTENSIONS = /\.(m3u8|mpd)(\?|$|\/)/i;
|
|
1246
|
+
const SEGMENT_EXTENSIONS = /\.(m4s|m4v|m4a|mp4|ts|aac)(\?|$|\/)/i;
|
|
1247
|
+
function classifyUrl(url, isEventTarget) {
|
|
1248
|
+
if (isEventTarget) return CmcdRecordedRequestType.EVENT;
|
|
1249
|
+
if (MANIFEST_EXTENSIONS.test(url)) return CmcdRecordedRequestType.MANIFEST;
|
|
1250
|
+
if (SEGMENT_EXTENSIONS.test(url)) return CmcdRecordedRequestType.SEGMENT;
|
|
1251
|
+
return CmcdRecordedRequestType.UNKNOWN;
|
|
1252
|
+
}
|
|
1253
|
+
function hasCmcdHeader(headers) {
|
|
1254
|
+
if (!headers) return false;
|
|
1255
|
+
for (const name in headers) if (name.toLowerCase().startsWith("cmcd-")) return true;
|
|
1256
|
+
return false;
|
|
1257
|
+
}
|
|
1258
|
+
function detectReportingMode(url, headers, isEventTarget) {
|
|
1259
|
+
if (isEventTarget) return "event";
|
|
1260
|
+
if (hasCmcdHeader(headers)) return "header";
|
|
1261
|
+
return url.includes(`${CMCD_PARAM}=`) ? "query" : null;
|
|
1262
|
+
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Test helper that records CMCD-bearing reports across XHR and fetch
|
|
1265
|
+
* transports for assertion in e2e tests. Each captured request is
|
|
1266
|
+
* normalized to {@link @svta/cml-utils!HttpRequest | HttpRequest} so
|
|
1267
|
+
* tests are identical regardless of which transport the player uses.
|
|
1268
|
+
*
|
|
1269
|
+
* @example
|
|
1270
|
+
* {@includeCode ../test/CmcdReportRecorder.test.ts#example}
|
|
1271
|
+
*
|
|
1272
|
+
* @public
|
|
1273
|
+
*/
|
|
1274
|
+
var CmcdReportRecorder = class {
|
|
1275
|
+
#reports = [];
|
|
1276
|
+
#detachers = [];
|
|
1277
|
+
#attached = false;
|
|
1278
|
+
#eventTargetUrls = [];
|
|
1279
|
+
#waiters = /* @__PURE__ */ new Map();
|
|
1280
|
+
#onReport;
|
|
1281
|
+
#waitTimeout = 15e3;
|
|
1282
|
+
#deliver = (request) => {
|
|
1283
|
+
const url = request.url;
|
|
1284
|
+
const isEventTarget = (request.method ?? "GET").toUpperCase() === "POST" && this.#eventTargetUrls.some((t) => url.startsWith(t));
|
|
1285
|
+
const reportingMode = detectReportingMode(url, request.headers, isEventTarget);
|
|
1286
|
+
if (reportingMode === null) return;
|
|
1287
|
+
const captured = {
|
|
1288
|
+
request,
|
|
1289
|
+
type: classifyUrl(url, isEventTarget),
|
|
1290
|
+
reportingMode,
|
|
1291
|
+
timestamp: Date.now()
|
|
1292
|
+
};
|
|
1293
|
+
this.#reports.push(captured);
|
|
1294
|
+
if (this.#onReport) try {
|
|
1295
|
+
this.#onReport(captured);
|
|
1296
|
+
} catch (err) {
|
|
1297
|
+
console.error("CmcdReportRecorder onReport listener threw:", err);
|
|
1298
|
+
}
|
|
1299
|
+
this.#notifyWaiters();
|
|
1300
|
+
return isEventTarget ? new Response(null, { status: 204 }) : void 0;
|
|
1301
|
+
};
|
|
1302
|
+
#getMatching(type) {
|
|
1303
|
+
return type === void 0 ? [...this.#reports] : this.#reports.filter((r) => r.type === type);
|
|
1304
|
+
}
|
|
1305
|
+
#notifyWaiters() {
|
|
1306
|
+
for (const [timer, waiter] of this.#waiters) {
|
|
1307
|
+
const matching = this.#getMatching(waiter.type);
|
|
1308
|
+
if (matching.length >= waiter.count) {
|
|
1309
|
+
clearTimeout(timer);
|
|
1310
|
+
this.#waiters.delete(timer);
|
|
1311
|
+
waiter.resolve(matching);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
#waitFor(type, options) {
|
|
1316
|
+
const count = options.count ?? 1;
|
|
1317
|
+
const timeout = options.timeout ?? this.#waitTimeout;
|
|
1318
|
+
const matching = this.#getMatching(type);
|
|
1319
|
+
if (matching.length >= count) return Promise.resolve(matching);
|
|
1320
|
+
return new Promise((resolve, reject) => {
|
|
1321
|
+
const timer = setTimeout(() => {
|
|
1322
|
+
this.#waiters.delete(timer);
|
|
1323
|
+
const current = this.#getMatching(type);
|
|
1324
|
+
reject(/* @__PURE__ */ new Error(`Timeout waiting for ${count} ${type ?? "any"} CMCD report(s). Got ${current.length}. Total recorded: ${this.#reports.length}.`));
|
|
1325
|
+
}, timeout);
|
|
1326
|
+
this.#waiters.set(timer, {
|
|
1327
|
+
type,
|
|
1328
|
+
count,
|
|
1329
|
+
resolve,
|
|
1330
|
+
reject
|
|
1331
|
+
});
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
/**
|
|
1335
|
+
* Install transport patches and begin recording CMCD reports.
|
|
1336
|
+
* No-op if already attached — the previously supplied options are
|
|
1337
|
+
* retained and the new `options` argument is silently ignored.
|
|
1338
|
+
* Call `detach()` first if you need to re-attach with different
|
|
1339
|
+
* options.
|
|
1340
|
+
*
|
|
1341
|
+
* @public
|
|
1342
|
+
*/
|
|
1343
|
+
attach(options = {}) {
|
|
1344
|
+
if (this.#attached) return;
|
|
1345
|
+
this.#attached = true;
|
|
1346
|
+
this.#eventTargetUrls = options.eventTargetUrls ?? [];
|
|
1347
|
+
this.#onReport = options.onReport;
|
|
1348
|
+
this.#waitTimeout = options.waitTimeout ?? 15e3;
|
|
1349
|
+
const transports = options.transports ?? [createXhrTransport(), createFetchTransport()];
|
|
1350
|
+
for (const transport of transports) this.#detachers.push(transport.attach(this.#deliver));
|
|
1351
|
+
}
|
|
1352
|
+
/**
|
|
1353
|
+
* Remove transport patches and stop recording. Rejects any pending
|
|
1354
|
+
* wait promises with `Error('Recorder detached while waiting')`.
|
|
1355
|
+
*
|
|
1356
|
+
* @public
|
|
1357
|
+
*/
|
|
1358
|
+
detach() {
|
|
1359
|
+
if (!this.#attached) return;
|
|
1360
|
+
for (const detacher of this.#detachers) detacher();
|
|
1361
|
+
this.#detachers = [];
|
|
1362
|
+
this.#attached = false;
|
|
1363
|
+
this.#eventTargetUrls = [];
|
|
1364
|
+
this.#onReport = void 0;
|
|
1365
|
+
for (const [timer, waiter] of this.#waiters) {
|
|
1366
|
+
clearTimeout(timer);
|
|
1367
|
+
waiter.reject(/* @__PURE__ */ new Error("Recorder detached while waiting"));
|
|
1368
|
+
}
|
|
1369
|
+
this.#waiters.clear();
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Discard all recorded reports. Does not affect the attached state.
|
|
1373
|
+
*
|
|
1374
|
+
* @public
|
|
1375
|
+
*/
|
|
1376
|
+
clear() {
|
|
1377
|
+
this.#reports = [];
|
|
1378
|
+
}
|
|
1379
|
+
/**
|
|
1380
|
+
* Return a defensive copy of all recorded reports.
|
|
1381
|
+
*
|
|
1382
|
+
* @public
|
|
1383
|
+
*/
|
|
1384
|
+
getReports() {
|
|
1385
|
+
return [...this.#reports];
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Wait until at least `count` reports of any type are recorded.
|
|
1389
|
+
* Resolves with all matching reports; rejects with a diagnostic error
|
|
1390
|
+
* on timeout. `count` defaults to 1; `timeout` falls back to the
|
|
1391
|
+
* recorder's `waitTimeout` option (default 15000 ms).
|
|
1392
|
+
*
|
|
1393
|
+
* @public
|
|
1394
|
+
*/
|
|
1395
|
+
waitForReports(options = {}) {
|
|
1396
|
+
return this.#waitFor(void 0, options);
|
|
1397
|
+
}
|
|
1398
|
+
/**
|
|
1399
|
+
* Wait until at least `count` manifest reports are recorded.
|
|
1400
|
+
* Resolves with all matching reports; rejects with a diagnostic error
|
|
1401
|
+
* on timeout. `count` defaults to 1; `timeout` falls back to the
|
|
1402
|
+
* recorder's `waitTimeout` option (default 15000 ms).
|
|
1403
|
+
*
|
|
1404
|
+
* @public
|
|
1405
|
+
*/
|
|
1406
|
+
waitForManifest(options = {}) {
|
|
1407
|
+
return this.#waitFor(CmcdRecordedRequestType.MANIFEST, options);
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Wait until at least `count` segment reports are recorded.
|
|
1411
|
+
* Resolves with all matching reports; rejects with a diagnostic error
|
|
1412
|
+
* on timeout. `count` defaults to 1; `timeout` falls back to the
|
|
1413
|
+
* recorder's `waitTimeout` option (default 15000 ms).
|
|
1414
|
+
*
|
|
1415
|
+
* @public
|
|
1416
|
+
*/
|
|
1417
|
+
waitForSegments(options = {}) {
|
|
1418
|
+
return this.#waitFor(CmcdRecordedRequestType.SEGMENT, options);
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Wait until at least `count` event-mode reports are recorded.
|
|
1422
|
+
* Resolves with all matching reports; rejects with a diagnostic error
|
|
1423
|
+
* on timeout. `count` defaults to 1; `timeout` falls back to the
|
|
1424
|
+
* recorder's `waitTimeout` option (default 15000 ms).
|
|
1425
|
+
*
|
|
1426
|
+
* @public
|
|
1427
|
+
*/
|
|
1428
|
+
waitForEvents(options = {}) {
|
|
1429
|
+
return this.#waitFor(CmcdRecordedRequestType.EVENT, options);
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
|
|
982
1433
|
//#endregion
|
|
983
1434
|
//#region src/CmcdTransmissionMode.ts
|
|
984
1435
|
/**
|
|
@@ -1025,6 +1476,53 @@ function createEncodingOptions(reportingMode, config, baseUrl) {
|
|
|
1025
1476
|
baseUrl
|
|
1026
1477
|
};
|
|
1027
1478
|
}
|
|
1479
|
+
/**
|
|
1480
|
+
* Deep equality for CmcdObjectTypeList (used for `br` dedup).
|
|
1481
|
+
*
|
|
1482
|
+
* Order-sensitive: arrays with the same elements in different positions
|
|
1483
|
+
* are treated as different. Players that construct `br` consistently
|
|
1484
|
+
* get correct dedup; shuffling produces spurious emits, which is the
|
|
1485
|
+
* safer failure mode.
|
|
1486
|
+
*/
|
|
1487
|
+
function cmcdObjectTypeListEqual(a, b) {
|
|
1488
|
+
if (a === b) return true;
|
|
1489
|
+
if (a.length !== b.length) return false;
|
|
1490
|
+
for (let i = 0; i < a.length; i++) {
|
|
1491
|
+
const ai = a[i];
|
|
1492
|
+
const bi = b[i];
|
|
1493
|
+
if (ai === bi) continue;
|
|
1494
|
+
if (typeof ai === "number" || typeof bi === "number") return false;
|
|
1495
|
+
if (ai.value !== bi.value) return false;
|
|
1496
|
+
const ap = ai.params;
|
|
1497
|
+
const bp = bi.params;
|
|
1498
|
+
const ak = ap && Object.keys(ap)[0];
|
|
1499
|
+
const bk = bp && Object.keys(bp)[0];
|
|
1500
|
+
if (ak !== bk) return false;
|
|
1501
|
+
if (ak !== void 0 && bk !== void 0 && ap && bp && ap[ak] !== bp[bk]) return false;
|
|
1502
|
+
}
|
|
1503
|
+
return true;
|
|
1504
|
+
}
|
|
1505
|
+
const equal = Object.is;
|
|
1506
|
+
const identity = (v) => v;
|
|
1507
|
+
/**
|
|
1508
|
+
* Maps each tracked state field to its event type and equality function.
|
|
1509
|
+
* Order matters: `update()` fires events in this order for multi-field updates.
|
|
1510
|
+
*/
|
|
1511
|
+
const STATE_FIELDS = /* @__PURE__ */ Array.from(CMCD_STATE_EVENT_FIELDS, ([event, field]) => {
|
|
1512
|
+
if (field === "br") return {
|
|
1513
|
+
event,
|
|
1514
|
+
field,
|
|
1515
|
+
equal: (a, b) => a === void 0 || b === void 0 ? a === b : cmcdObjectTypeListEqual(a, b),
|
|
1516
|
+
snapshot: (v) => v.slice()
|
|
1517
|
+
};
|
|
1518
|
+
return {
|
|
1519
|
+
event,
|
|
1520
|
+
field,
|
|
1521
|
+
equal,
|
|
1522
|
+
snapshot: identity
|
|
1523
|
+
};
|
|
1524
|
+
});
|
|
1525
|
+
const STATE_FIELDS_BY_EVENT = /* @__PURE__ */ new Map(/* @__PURE__ */ STATE_FIELDS.map((e) => [e.event, e]));
|
|
1028
1526
|
function defaultRequester(request) {
|
|
1029
1527
|
const { url,...init } = request;
|
|
1030
1528
|
return fetch(url, init);
|
|
@@ -1070,6 +1568,7 @@ var CmcdReporter = class {
|
|
|
1070
1568
|
this.data = {};
|
|
1071
1569
|
this.msd = NaN;
|
|
1072
1570
|
this.eventTargets = /* @__PURE__ */ new Map();
|
|
1571
|
+
this.lastEmitted = {};
|
|
1073
1572
|
this.requestTarget = {
|
|
1074
1573
|
sn: 0,
|
|
1075
1574
|
msdSent: false
|
|
@@ -1098,7 +1597,7 @@ var CmcdReporter = class {
|
|
|
1098
1597
|
start() {
|
|
1099
1598
|
this.eventTargets.forEach((target, config) => {
|
|
1100
1599
|
this.disarmInterval(target);
|
|
1101
|
-
if (config.interval === 0 || !config.events.includes(
|
|
1600
|
+
if (config.interval === 0 || !config.events.includes(CMCD_EVENT_TIME_INTERVAL)) return;
|
|
1102
1601
|
const timeIntervalEvent = () => {
|
|
1103
1602
|
this.recordTargetEvent(target, config, CMCD_EVENT_TIME_INTERVAL);
|
|
1104
1603
|
this.processEventTargets();
|
|
@@ -1127,7 +1626,32 @@ var CmcdReporter = class {
|
|
|
1127
1626
|
this.processEventTargets(true);
|
|
1128
1627
|
}
|
|
1129
1628
|
/**
|
|
1130
|
-
* Updates the CMCD data.
|
|
1629
|
+
* Updates the CMCD data.
|
|
1630
|
+
*
|
|
1631
|
+
* Called by the player when data changes. For tracked state fields
|
|
1632
|
+
* (`sta`, `pr`, `cid`, `bg`, `br`), if the new value differs from the
|
|
1633
|
+
* last-reported value (the value most recently emitted on the wire for
|
|
1634
|
+
* that field), the corresponding state-change event is automatically
|
|
1635
|
+
* fired. Comparing against the last-reported value (rather than the
|
|
1636
|
+
* previous persisted value) ensures the first state-change event in a
|
|
1637
|
+
* new session always emits, even when the persisted value didn't change
|
|
1638
|
+
* across the `sid` boundary.
|
|
1639
|
+
*
|
|
1640
|
+
* Multi-field updates fire multiple events in the order: `sta` → `pr` →
|
|
1641
|
+
* `cid` → `bg` → `br`. The order of keys in the input object does not
|
|
1642
|
+
* affect the firing order.
|
|
1643
|
+
*
|
|
1644
|
+
* To attach snapshot context (e.g., `bl`, `mtp`, `pt`, `ltc`) to a
|
|
1645
|
+
* state-change event, ensure those fields are in the reporter's data
|
|
1646
|
+
* before the state field changes. Either include them alongside the
|
|
1647
|
+
* state field in the same `update()` call, or persist them via earlier
|
|
1648
|
+
* `update()` calls — auto-fired events emit whatever is currently in
|
|
1649
|
+
* the persistent data store. This is also how to keep `TIME_INTERVAL`
|
|
1650
|
+
* reports useful — those events draw from the persistent data store
|
|
1651
|
+
* with no caller hook for per-event data, so fields the player wants
|
|
1652
|
+
* in periodic reports must be kept fresh here.
|
|
1653
|
+
*
|
|
1654
|
+
* A `sid` change resets the dedup baseline.
|
|
1131
1655
|
*
|
|
1132
1656
|
* @param data - The data to update.
|
|
1133
1657
|
*/
|
|
@@ -1139,16 +1663,54 @@ var CmcdReporter = class {
|
|
|
1139
1663
|
...data,
|
|
1140
1664
|
msd: void 0
|
|
1141
1665
|
};
|
|
1666
|
+
for (const entry of STATE_FIELDS) if (entry.field in data && !entry.equal(this.data[entry.field], this.lastEmitted[entry.field])) this.recordEvent(entry.event);
|
|
1142
1667
|
}
|
|
1143
1668
|
/**
|
|
1144
1669
|
* Records an event. Called by the player when an event occurs.
|
|
1145
1670
|
*
|
|
1671
|
+
* For state-change events (`PLAY_STATE`, `PLAYBACK_RATE`, `CONTENT_ID`,
|
|
1672
|
+
* `BACKGROUNDED_MODE`, `BITRATE_CHANGE`), this method:
|
|
1673
|
+
* 1. Persists the dedup field from `data` (if present) into the reporter's
|
|
1674
|
+
* persistent data store — equivalent to a write-through `update()`.
|
|
1675
|
+
* 2. Drops the event entirely if the dedup field has no value after the
|
|
1676
|
+
* write-through (never set, or cleared via `update({ field: undefined })`).
|
|
1677
|
+
* State-change events without their required field would violate CTA-5004-B.
|
|
1678
|
+
* 3. Suppresses the event if the field's current value matches the
|
|
1679
|
+
* last-emitted value (no state transition).
|
|
1680
|
+
*
|
|
1681
|
+
* For all other event types, the event is always emitted.
|
|
1682
|
+
*
|
|
1683
|
+
* For state-change events, prefer {@link CmcdReporter.update} — including
|
|
1684
|
+
* for snapshot enrichment via a combined call like
|
|
1685
|
+
* `update({ sta: 'p', bl: [3000], mtp: [8500] })`. Calling `recordEvent()`
|
|
1686
|
+
* for a state-change event after `update()` has already auto-fired it
|
|
1687
|
+
* silently drops the second call's `data`, because dedup suppresses the
|
|
1688
|
+
* second emission.
|
|
1689
|
+
*
|
|
1690
|
+
* Use `recordEvent()` directly for events whose payload is intrinsic to
|
|
1691
|
+
* the event call — `CUSTOM_EVENT` with `cen`, `ERROR` with `ec`,
|
|
1692
|
+
* ad-lifecycle events, `MUTE`/`UNMUTE`, `PLAYER_EXPAND`/`PLAYER_COLLAPSE`,
|
|
1693
|
+
* `SKIP`. For `RESPONSE_RECEIVED`, prefer
|
|
1694
|
+
* {@link CmcdReporter.recordResponseReceived}, which derives the
|
|
1695
|
+
* per-response fields automatically.
|
|
1696
|
+
*
|
|
1146
1697
|
* @param type - The type of event to record.
|
|
1147
1698
|
* @param data - Additional data to record with the event. This data
|
|
1148
|
-
* only applies to this event report
|
|
1149
|
-
*
|
|
1699
|
+
* only applies to this event report, except for the dedup
|
|
1700
|
+
* field of a state-change event, which is also persisted
|
|
1701
|
+
* into the reporter's data store.
|
|
1150
1702
|
*/
|
|
1151
1703
|
recordEvent(type, data = {}) {
|
|
1704
|
+
const entry = STATE_FIELDS_BY_EVENT.get(type);
|
|
1705
|
+
if (entry) {
|
|
1706
|
+
const field = entry.field;
|
|
1707
|
+
const incoming = data[field];
|
|
1708
|
+
if (incoming !== void 0) Object.assign(this.data, { [field]: incoming });
|
|
1709
|
+
const current = this.data[field];
|
|
1710
|
+
if (current === void 0) return;
|
|
1711
|
+
if (entry.equal(current, this.lastEmitted[field])) return;
|
|
1712
|
+
Object.assign(this.lastEmitted, { [field]: entry.snapshot(current) });
|
|
1713
|
+
}
|
|
1152
1714
|
this.eventTargets.forEach((target, config) => {
|
|
1153
1715
|
this.recordTargetEvent(target, config, type, data);
|
|
1154
1716
|
});
|
|
@@ -1351,6 +1913,7 @@ var CmcdReporter = class {
|
|
|
1351
1913
|
resetSession() {
|
|
1352
1914
|
this.eventTargets.forEach((target) => target.sn = 0);
|
|
1353
1915
|
this.requestTarget.sn = 0;
|
|
1916
|
+
this.lastEmitted = {};
|
|
1354
1917
|
}
|
|
1355
1918
|
};
|
|
1356
1919
|
|
|
@@ -1580,8 +2143,8 @@ function resolveVersion(data, options) {
|
|
|
1580
2143
|
|
|
1581
2144
|
//#endregion
|
|
1582
2145
|
//#region src/validateCmcdKeys.ts
|
|
1583
|
-
const CMCD_V1_KEY_SET = new Set(CMCD_V1_KEYS);
|
|
1584
|
-
const CMCD_KEY_SET = new Set(CMCD_KEYS);
|
|
2146
|
+
const CMCD_V1_KEY_SET = /* @__PURE__ */ new Set(CMCD_V1_KEYS);
|
|
2147
|
+
const CMCD_KEY_SET = /* @__PURE__ */ new Set(CMCD_KEYS);
|
|
1585
2148
|
/**
|
|
1586
2149
|
* Validates that all keys in a CMCD payload are recognized spec keys or valid custom keys.
|
|
1587
2150
|
*
|
|
@@ -1680,9 +2243,9 @@ function validateCmcdStructure(data, options) {
|
|
|
1680
2243
|
message: `Response key "${key}" must only be present when e="rr".`,
|
|
1681
2244
|
severity: CMCD_VALIDATION_SEVERITY_ERROR
|
|
1682
2245
|
});
|
|
1683
|
-
if (data["e"] ===
|
|
1684
|
-
key:
|
|
1685
|
-
message:
|
|
2246
|
+
for (const [eventType, requiredField] of CMCD_STATE_EVENT_FIELDS) if (data["e"] === eventType && !(requiredField in data)) issues.push({
|
|
2247
|
+
key: requiredField,
|
|
2248
|
+
message: `State-change event (e="${eventType}") requires the "${requiredField}" key to be present.`,
|
|
1686
2249
|
severity: CMCD_VALIDATION_SEVERITY_ERROR
|
|
1687
2250
|
});
|
|
1688
2251
|
if (data["e"] === CMCD_EVENT_ERROR && !("ec" in data)) issues.push({
|
|
@@ -1860,6 +2423,7 @@ const CMCD_TOKEN_VALUES = {
|
|
|
1860
2423
|
e: [
|
|
1861
2424
|
"bc",
|
|
1862
2425
|
"ps",
|
|
2426
|
+
"pr",
|
|
1863
2427
|
"e",
|
|
1864
2428
|
"t",
|
|
1865
2429
|
"c",
|
|
@@ -1914,14 +2478,14 @@ const CMCD_TOKEN_VALUES = {
|
|
|
1914
2478
|
|
|
1915
2479
|
//#endregion
|
|
1916
2480
|
//#region src/validateCmcdValues.ts
|
|
1917
|
-
const HUNDRED_ROUNDING_KEYS = new Set([
|
|
2481
|
+
const HUNDRED_ROUNDING_KEYS = /* @__PURE__ */ new Set([
|
|
1918
2482
|
"bl",
|
|
1919
2483
|
"dl",
|
|
1920
2484
|
"mtp",
|
|
1921
2485
|
"rtp",
|
|
1922
2486
|
"tbl"
|
|
1923
2487
|
]);
|
|
1924
|
-
const INTEGER_ROUNDING_KEYS = new Set([
|
|
2488
|
+
const INTEGER_ROUNDING_KEYS = /* @__PURE__ */ new Set([
|
|
1925
2489
|
"br",
|
|
1926
2490
|
"d",
|
|
1927
2491
|
"tb"
|
|
@@ -2377,5 +2941,5 @@ function extractHeaderRecord(headers) {
|
|
|
2377
2941
|
}
|
|
2378
2942
|
|
|
2379
2943
|
//#endregion
|
|
2380
|
-
export { CMCD_DEFAULT_TIME_INTERVAL, CMCD_EVENT_AD_BREAK_END, CMCD_EVENT_AD_BREAK_START, CMCD_EVENT_AD_END, CMCD_EVENT_AD_START, CMCD_EVENT_BACKGROUNDED_MODE, CMCD_EVENT_BITRATE_CHANGE, CMCD_EVENT_CONTENT_ID, CMCD_EVENT_CUSTOM_EVENT, CMCD_EVENT_ERROR, CMCD_EVENT_KEYS, CMCD_EVENT_MODE, CMCD_EVENT_MUTE, CMCD_EVENT_PLAYER_COLLAPSE, CMCD_EVENT_PLAYER_EXPAND, CMCD_EVENT_PLAY_STATE, CMCD_EVENT_RESPONSE_RECEIVED, CMCD_EVENT_SKIP, CMCD_EVENT_TIME_INTERVAL, CMCD_EVENT_UNMUTE, CMCD_FORMATTER_MAP, CMCD_HEADERS, CMCD_HEADER_FIELDS, CMCD_HEADER_MAP, CMCD_JSON, CMCD_KEYS, CMCD_MIME_TYPE, CMCD_OBJECT, CMCD_PARAM, CMCD_QUERY, CMCD_REQUEST, CMCD_REQUEST_KEYS, CMCD_REQUEST_MODE, CMCD_RESPONSE_KEYS, CMCD_SESSION, CMCD_STATUS, CMCD_V1, CMCD_V1_KEYS, CMCD_V2, CMCD_VALIDATION_SEVERITY_ERROR, CMCD_VALIDATION_SEVERITY_WARNING, CmcdEventType, CmcdHeaderField, CmcdObjectType, CmcdPlayerState, CmcdReporter, CmcdReportingMode, CmcdStreamType, CmcdStreamingFormat, CmcdTransmissionMode, CmcdValidationSeverity, appendCmcdHeaders, appendCmcdQuery, decodeCmcd, encodeCmcd, fromCmcdHeaders, fromCmcdQuery, fromCmcdUrl, groupCmcdHeaders, isCmcdCustomKey, isCmcdEventKey, isCmcdRequestKey, isCmcdResponseReceivedKey, isCmcdV1Data, isCmcdV1Key, isCmcdV2Data, prepareCmcdData, toCmcdHeaders, toCmcdQuery, toCmcdUrl, toCmcdValue, validateCmcd, validateCmcdEventReport, validateCmcdEvents, validateCmcdHeaders, validateCmcdKeys, validateCmcdRequest, validateCmcdStructure, validateCmcdValues };
|
|
2944
|
+
export { CMCD_DEFAULT_TIME_INTERVAL, CMCD_EVENT_AD_BREAK_END, CMCD_EVENT_AD_BREAK_START, CMCD_EVENT_AD_END, CMCD_EVENT_AD_START, CMCD_EVENT_BACKGROUNDED_MODE, CMCD_EVENT_BITRATE_CHANGE, CMCD_EVENT_CONTENT_ID, CMCD_EVENT_CUSTOM_EVENT, CMCD_EVENT_ERROR, CMCD_EVENT_KEYS, CMCD_EVENT_MODE, CMCD_EVENT_MUTE, CMCD_EVENT_PLAYBACK_RATE, CMCD_EVENT_PLAYER_COLLAPSE, CMCD_EVENT_PLAYER_EXPAND, CMCD_EVENT_PLAY_STATE, CMCD_EVENT_RESPONSE_RECEIVED, CMCD_EVENT_SKIP, CMCD_EVENT_TIME_INTERVAL, CMCD_EVENT_UNMUTE, CMCD_FORMATTER_MAP, CMCD_HEADERS, CMCD_HEADER_FIELDS, CMCD_HEADER_MAP, CMCD_JSON, CMCD_KEYS, CMCD_MIME_TYPE, CMCD_OBJECT, CMCD_PARAM, CMCD_QUERY, CMCD_RECORDED_REPORT_MODE_EVENT, CMCD_RECORDED_REPORT_MODE_HEADER, CMCD_RECORDED_REPORT_MODE_QUERY, CMCD_RECORDED_REQUEST_TYPE_EVENT, CMCD_RECORDED_REQUEST_TYPE_MANIFEST, CMCD_RECORDED_REQUEST_TYPE_SEGMENT, CMCD_RECORDED_REQUEST_TYPE_UNKNOWN, CMCD_REQUEST, CMCD_REQUEST_KEYS, CMCD_REQUEST_MODE, CMCD_RESPONSE_KEYS, CMCD_SESSION, CMCD_STATUS, CMCD_V1, CMCD_V1_KEYS, CMCD_V2, CMCD_VALIDATION_SEVERITY_ERROR, CMCD_VALIDATION_SEVERITY_WARNING, CmcdEventType, CmcdHeaderField, CmcdObjectType, CmcdPlayerState, CmcdRecordedReportMode, CmcdRecordedRequestType, CmcdReportRecorder, CmcdReporter, CmcdReportingMode, CmcdStreamType, CmcdStreamingFormat, CmcdTransmissionMode, CmcdValidationSeverity, appendCmcdHeaders, appendCmcdQuery, createFetchTransport, createXhrTransport, decodeCmcd, encodeCmcd, fromCmcdHeaders, fromCmcdQuery, fromCmcdUrl, groupCmcdHeaders, isCmcdCustomKey, isCmcdEventKey, isCmcdRequestKey, isCmcdResponseReceivedKey, isCmcdV1Data, isCmcdV1Key, isCmcdV2Data, prepareCmcdData, toCmcdHeaders, toCmcdQuery, toCmcdUrl, toCmcdValue, validateCmcd, validateCmcdEventReport, validateCmcdEvents, validateCmcdHeaders, validateCmcdKeys, validateCmcdRequest, validateCmcdStructure, validateCmcdValues };
|
|
2381
2945
|
//# sourceMappingURL=index.js.map
|