@sanseng/liveavatar-js-sdk-skywalking 1.0.0 → 1.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 +30 -13
- package/dist/index.cjs.js +141 -18
- package/dist/index.d.ts +41 -2
- package/dist/index.esm.js +141 -18
- package/dist/index.umd.js +141 -18
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
## What it solves
|
|
6
6
|
|
|
7
|
-
- Reports LiveAvatar A/V Sync alert lifecycles with compact, causally ordered diagnostic evidence for investigation in SkyWalking.
|
|
7
|
+
- Reports SDK-generated LiveAvatar A/V Sync alert lifecycles with compact, causally ordered diagnostic evidence for investigation in SkyWalking.
|
|
8
8
|
- Reports qualifying SDK Operational Log errors without uploading the original error message, stack, token, or business payload.
|
|
9
|
+
- Reports every terminal `performance.first-frame` Operational Log, including success, connection failure, and first-frame timeout.
|
|
9
10
|
- Adds observability through the public LiveAvatar Plugin API v1, without subscribing to legacy SDK events or using SDK internals.
|
|
10
11
|
- Bundles and registers a browser SkyWalking client when one has not already been supplied by the application.
|
|
11
12
|
|
|
@@ -19,24 +20,40 @@ npm install @sanseng/liveavatar-js-sdk @sanseng/liveavatar-js-sdk-skywalking
|
|
|
19
20
|
import { createClient } from "@sanseng/liveavatar-js-sdk";
|
|
20
21
|
import { createSkyWalkingPlugin } from "@sanseng/liveavatar-js-sdk-skywalking";
|
|
21
22
|
|
|
23
|
+
const client = createClient({
|
|
24
|
+
plugins: [createSkyWalkingPlugin()],
|
|
25
|
+
connectConfig: {
|
|
26
|
+
// Your normal LiveAvatar connection configuration.
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The plugin registers SkyWalking during client setup. SDK errors, terminal first-frame logs, and SDK-generated A/V Sync alerts are all reported by default. Its service is always `yy-tx::liveavatar-js-sdk`; it cannot be configured. If `window.ClientMonitor` already exists, the plugin treats it as externally registered and never overwrites or registers it. The bundled client registers only once per page; later differing configurations are ignored with a console warning.
|
|
32
|
+
|
|
33
|
+
Enable SDK A/V Sync diagnostics only when automatic A/V Sync alerts and their evidence are needed. When it is disabled, the SDK produces no automatic A/V Sync alerts, so the plugin does not query A/V telemetry or send A/V logs:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
22
36
|
const client = createClient({
|
|
23
37
|
debug: { avSync: { enabled: true } },
|
|
24
|
-
plugins: [
|
|
25
|
-
createSkyWalkingPlugin({
|
|
26
|
-
isOverseas: false,
|
|
27
|
-
serviceVersion: "web_1.0.0",
|
|
28
|
-
jsErrors: true,
|
|
29
|
-
apiErrors: true,
|
|
30
|
-
resourceErrors: true,
|
|
31
|
-
autoTracePerf: true,
|
|
32
|
-
}),
|
|
33
|
-
],
|
|
38
|
+
plugins: [createSkyWalkingPlugin()],
|
|
34
39
|
connectConfig: {
|
|
35
40
|
// Your normal LiveAvatar connection configuration.
|
|
36
41
|
},
|
|
37
42
|
});
|
|
38
43
|
```
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
## Reporting controls
|
|
46
|
+
|
|
47
|
+
Use `reporting` to disable individual automatic report types; every type defaults to `true`:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
createSkyWalkingPlugin({
|
|
51
|
+
reporting: { sdkError: true, firstFrame: false, avSync: true },
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
When all three report types are `false`, the plugin does not initialize SkyWalking or subscribe to SDK extension points.
|
|
56
|
+
|
|
57
|
+
Each SDK `performance.first-frame` Operational Log produces one SkyWalking structured log through `reportFrameErrors` with a one-millisecond dispatch timeout. “First frame” means the SDK-owned private video element reached `loadeddata`; it does not claim that pixels have already appeared in the application's visible UI. See the [API reference](docs/api.en.md#first-frame-log) for outcomes, payload fields, and omission rules.
|
|
41
58
|
|
|
42
|
-
The package requires `@sanseng/liveavatar-js-sdk` `^2.
|
|
59
|
+
The package requires `@sanseng/liveavatar-js-sdk` `^2.4.0`.
|
package/dist/index.cjs.js
CHANGED
|
@@ -46,28 +46,34 @@ class SkyWalkingPlugin {
|
|
|
46
46
|
this.apiVersion = 1;
|
|
47
47
|
this._nextInstallationGeneration = 0;
|
|
48
48
|
this._skyWalkingConfig = { ...config };
|
|
49
|
+
this._reporting = normalizeReportingConfig(config.reporting);
|
|
49
50
|
}
|
|
50
51
|
setup(registrar) {
|
|
51
52
|
this._deactivateInstallation();
|
|
53
|
+
if (!hasEnabledReporting(this._reporting))
|
|
54
|
+
return;
|
|
52
55
|
registerSkyWalking(this._skyWalkingConfig);
|
|
53
|
-
const diagnostics = registrar.consume(liveavatarJsSdk.SDK_AVSYNC_DIAGNOSTICS_V1);
|
|
54
56
|
const installation = {
|
|
55
57
|
generation: ++this._nextInstallationGeneration,
|
|
56
|
-
diagnostics,
|
|
57
58
|
abortController: new AbortController(),
|
|
58
59
|
alertReportStates: new Map(),
|
|
59
60
|
active: true,
|
|
60
61
|
};
|
|
61
62
|
this._installation = installation;
|
|
62
63
|
try {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
if (this._reporting.avSync) {
|
|
65
|
+
installation.diagnostics = registrar.consume(liveavatarJsSdk.SDK_AVSYNC_DIAGNOSTICS_V1);
|
|
66
|
+
registrar.observe(liveavatarJsSdk.SDK_AVSYNC_ALERTS_V1, {
|
|
67
|
+
id: AVSYNC_ALERTS_CONTRIBUTION_ID,
|
|
68
|
+
handler: (alert, context) => this._handleAlertNotification(installation, alert, context.signal),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (this._reporting.sdkError || this._reporting.firstFrame) {
|
|
72
|
+
registrar.observe(liveavatarJsSdk.SDK_OPERATIONAL_LOGS_V1, {
|
|
73
|
+
id: OPERATIONAL_LOGS_CONTRIBUTION_ID,
|
|
74
|
+
handler: (record, context) => this._handleOperationalLog(installation, record, context.signal),
|
|
75
|
+
});
|
|
76
|
+
}
|
|
71
77
|
}
|
|
72
78
|
catch (error) {
|
|
73
79
|
this._deactivateInstallation();
|
|
@@ -116,12 +122,19 @@ class SkyWalkingPlugin {
|
|
|
116
122
|
}
|
|
117
123
|
}
|
|
118
124
|
_handleOperationalLog(installation, record, signal) {
|
|
119
|
-
if (!this._isInvocationActive(installation, signal)
|
|
120
|
-
record.level !== "error" ||
|
|
121
|
-
record.event !== "sdk.error") {
|
|
125
|
+
if (!this._isInvocationActive(installation, signal))
|
|
122
126
|
return Promise.resolve();
|
|
127
|
+
if (this._reporting.sdkError &&
|
|
128
|
+
record.level === "error" &&
|
|
129
|
+
record.event === "sdk.error") {
|
|
130
|
+
return this._runObserverTask(installation, signal, "SDK error log", () => this._reportSdkError(installation, record, signal));
|
|
123
131
|
}
|
|
124
|
-
|
|
132
|
+
if (this._reporting.firstFrame &&
|
|
133
|
+
record.category === "media" &&
|
|
134
|
+
record.event === "performance.first-frame") {
|
|
135
|
+
return this._runObserverTask(installation, signal, "first-frame log", () => this._reportFirstFrameLog(installation, record, signal));
|
|
136
|
+
}
|
|
137
|
+
return Promise.resolve();
|
|
125
138
|
}
|
|
126
139
|
_enqueue(state, task) {
|
|
127
140
|
const queued = state.tail.then(task, task);
|
|
@@ -132,6 +145,9 @@ class SkyWalkingPlugin {
|
|
|
132
145
|
const isActive = () => this._isInvocationActive(installation, signal);
|
|
133
146
|
if (!isActive())
|
|
134
147
|
return;
|
|
148
|
+
const diagnostics = installation.diagnostics;
|
|
149
|
+
if (diagnostics === undefined)
|
|
150
|
+
return;
|
|
135
151
|
const clientMonitor = getClientMonitor();
|
|
136
152
|
if (clientMonitor === undefined) {
|
|
137
153
|
this._warnInvocation(installation, signal, "ClientMonitor.reportFrameErrors is unavailable", {
|
|
@@ -144,7 +160,7 @@ class SkyWalkingPlugin {
|
|
|
144
160
|
const query = await createEvidenceQuery(alert.phase, state.lastSuccessfulToWallClockMs, [signal, installation.abortController.signal]);
|
|
145
161
|
if (!isActive() || query === undefined)
|
|
146
162
|
return;
|
|
147
|
-
const collection = await collectTelemetry(
|
|
163
|
+
const collection = await collectTelemetry(diagnostics, alert, retrievedAtWallClockMs, query, isActive, (message, error) => this._warnInvocationFailure(installation, signal, message, error, {
|
|
148
164
|
alert: toJsonSafeAlert(alert),
|
|
149
165
|
}));
|
|
150
166
|
if (!isActive() || collection === undefined)
|
|
@@ -272,6 +288,43 @@ class SkyWalkingPlugin {
|
|
|
272
288
|
if (!this._isInvocationActive(installation, signal))
|
|
273
289
|
return;
|
|
274
290
|
}
|
|
291
|
+
async _reportFirstFrameLog(installation, record, signal) {
|
|
292
|
+
if (!this._isInvocationActive(installation, signal))
|
|
293
|
+
return;
|
|
294
|
+
const payload = toSkyWalkingFirstFrameLogPayload(record);
|
|
295
|
+
if (payload === undefined) {
|
|
296
|
+
this._warnInvocation(installation, signal, "Skipped invalid first-frame Operational Log", { eventId: record.eventId });
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const clientMonitor = getClientMonitor();
|
|
300
|
+
if (clientMonitor === undefined) {
|
|
301
|
+
this._warnInvocation(installation, signal, "ClientMonitor.reportFrameErrors is unavailable", {
|
|
302
|
+
pluginId: this.id,
|
|
303
|
+
attemptId: payload.attemptId,
|
|
304
|
+
outcome: payload.outcome,
|
|
305
|
+
});
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
if (!this._isInvocationActive(installation, signal))
|
|
309
|
+
return;
|
|
310
|
+
try {
|
|
311
|
+
await Promise.resolve(clientMonitor.reportFrameErrors({
|
|
312
|
+
category: "media",
|
|
313
|
+
grade: payload.outcome === "first-frame" ? "Info" : "Warning",
|
|
314
|
+
content: payload,
|
|
315
|
+
// The bundled client batches error records. Its public timeout
|
|
316
|
+
// option bypasses the default delay for these terminal events.
|
|
317
|
+
timeout: 1,
|
|
318
|
+
}, {
|
|
319
|
+
name: "",
|
|
320
|
+
message: `[LiveAvatar first-frame] ${payload.outcome}`,
|
|
321
|
+
stack: "",
|
|
322
|
+
}));
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
this._warnInvocationFailure(installation, signal, "Failed to report first-frame Operational Log to SkyWalking", error, { attemptId: payload.attemptId, outcome: payload.outcome });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
275
328
|
_deactivateInstallation() {
|
|
276
329
|
const installation = this._installation;
|
|
277
330
|
if (installation === undefined)
|
|
@@ -1231,6 +1284,60 @@ function toJsonSafeAlert(alert) {
|
|
|
1231
1284
|
features: { ...alert.features },
|
|
1232
1285
|
};
|
|
1233
1286
|
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Strictly projects the published first-frame Operational Log. The SDK record
|
|
1289
|
+
* has additional identifiers and arbitrary attributes; none of those can
|
|
1290
|
+
* reach SkyWalking through this path.
|
|
1291
|
+
*/
|
|
1292
|
+
function toSkyWalkingFirstFrameLogPayload(record) {
|
|
1293
|
+
const correlation = toRecord(record.correlation);
|
|
1294
|
+
const attributes = toRecord(record.attributes);
|
|
1295
|
+
const attemptId = correlation.attemptId;
|
|
1296
|
+
const outcome = attributes.outcome;
|
|
1297
|
+
if (!isNonNegativeNumber(record.occurredAtMs) ||
|
|
1298
|
+
!isPositiveInteger(attemptId) ||
|
|
1299
|
+
!isFirstFrameOutcome(outcome)) {
|
|
1300
|
+
return undefined;
|
|
1301
|
+
}
|
|
1302
|
+
const sourceNodes = toRecord(attributes.nodes);
|
|
1303
|
+
const nodes = {};
|
|
1304
|
+
copyNonNegativeNumber(sourceNodes, "configReadyMs", nodes);
|
|
1305
|
+
copyNonNegativeNumber(sourceNodes, "rtcConnectedMs", nodes);
|
|
1306
|
+
copyNonNegativeNumber(sourceNodes, "videoTrackAddedMs", nodes);
|
|
1307
|
+
copyNonNegativeNumber(sourceNodes, "videoMetadataMs", nodes);
|
|
1308
|
+
copyNonNegativeNumber(sourceNodes, "firstFrameMs", nodes);
|
|
1309
|
+
return {
|
|
1310
|
+
schemaVersion: 1,
|
|
1311
|
+
kind: "liveavatar.performance.first-frame",
|
|
1312
|
+
sdkVersion: liveavatarJsSdk.VERSION,
|
|
1313
|
+
occurredAtMs: record.occurredAtMs,
|
|
1314
|
+
attemptId,
|
|
1315
|
+
outcome,
|
|
1316
|
+
...(typeof attributes.renderMode === "string"
|
|
1317
|
+
? { renderMode: attributes.renderMode }
|
|
1318
|
+
: {}),
|
|
1319
|
+
...(isNonNegativeNumber(attributes.timeoutAfterRtcMs)
|
|
1320
|
+
? { timeoutAfterRtcMs: attributes.timeoutAfterRtcMs }
|
|
1321
|
+
: {}),
|
|
1322
|
+
nodes,
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
function copyNonNegativeNumber(source, key, target) {
|
|
1326
|
+
const value = source[key];
|
|
1327
|
+
if (isNonNegativeNumber(value))
|
|
1328
|
+
target[key] = value;
|
|
1329
|
+
}
|
|
1330
|
+
function isFirstFrameOutcome(value) {
|
|
1331
|
+
return (value === "first-frame" ||
|
|
1332
|
+
value === "connect-failed" ||
|
|
1333
|
+
value === "first-frame-timeout");
|
|
1334
|
+
}
|
|
1335
|
+
function isPositiveInteger(value) {
|
|
1336
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
1337
|
+
}
|
|
1338
|
+
function isNonNegativeNumber(value) {
|
|
1339
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1340
|
+
}
|
|
1234
1341
|
function stringifyJson(value) {
|
|
1235
1342
|
const serialized = JSON.stringify(value);
|
|
1236
1343
|
if (serialized === undefined) {
|
|
@@ -1279,9 +1386,8 @@ function registerSkyWalking(config = {}) {
|
|
|
1279
1386
|
}
|
|
1280
1387
|
/** Triggers a SkyWalking performance report through an already registered client. */
|
|
1281
1388
|
function setSkyWalkingPerformance(config = {}, customMetrics) {
|
|
1282
|
-
const clientMonitor =
|
|
1283
|
-
if (clientMonitor === undefined
|
|
1284
|
-
typeof clientMonitor.setPerformance !== "function") {
|
|
1389
|
+
const clientMonitor = getPerformanceClientMonitor();
|
|
1390
|
+
if (clientMonitor === undefined) {
|
|
1285
1391
|
warnOnce("set-performance-unavailable", "[LiveAvatar SkyWalking plugin] ClientMonitor.setPerformance is unavailable; register SkyWalking first");
|
|
1286
1392
|
return;
|
|
1287
1393
|
}
|
|
@@ -1306,6 +1412,16 @@ async function reportSkyWalkingFrameErrors(config, error) {
|
|
|
1306
1412
|
}
|
|
1307
1413
|
await Promise.resolve(clientMonitor.reportFrameErrors(config, error));
|
|
1308
1414
|
}
|
|
1415
|
+
function normalizeReportingConfig(config) {
|
|
1416
|
+
return {
|
|
1417
|
+
sdkError: config?.sdkError ?? true,
|
|
1418
|
+
firstFrame: config?.firstFrame ?? true,
|
|
1419
|
+
avSync: config?.avSync ?? true,
|
|
1420
|
+
};
|
|
1421
|
+
}
|
|
1422
|
+
function hasEnabledReporting(config) {
|
|
1423
|
+
return config.sdkError || config.firstFrame || config.avSync;
|
|
1424
|
+
}
|
|
1309
1425
|
function normalizeSkyWalkingConfig(config) {
|
|
1310
1426
|
return {
|
|
1311
1427
|
collector: config.collector ??
|
|
@@ -1353,6 +1469,13 @@ function getClientMonitor() {
|
|
|
1353
1469
|
? candidate
|
|
1354
1470
|
: undefined;
|
|
1355
1471
|
}
|
|
1472
|
+
/** Finds the performance API without requiring the unrelated error API. */
|
|
1473
|
+
function getPerformanceClientMonitor() {
|
|
1474
|
+
const candidate = getGlobalClientMonitor();
|
|
1475
|
+
return typeof candidate?.setPerformance === "function"
|
|
1476
|
+
? candidate
|
|
1477
|
+
: undefined;
|
|
1478
|
+
}
|
|
1356
1479
|
function getGlobalClientMonitor() {
|
|
1357
1480
|
if (typeof window === "undefined")
|
|
1358
1481
|
return undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -14,11 +14,23 @@ interface SkyWalkingSlowInteractionConfig {
|
|
|
14
14
|
threshold?: number;
|
|
15
15
|
reportInterval?: number;
|
|
16
16
|
}
|
|
17
|
+
/** Selects which SDK-generated records the plugin sends to SkyWalking. */
|
|
18
|
+
interface SkyWalkingReportingConfig {
|
|
19
|
+
/** Report SDK Operational Log errors. Defaults to `true`. */
|
|
20
|
+
sdkError?: boolean;
|
|
21
|
+
/** Report terminal SDK first-frame Operational Logs. Defaults to `true`. */
|
|
22
|
+
firstFrame?: boolean;
|
|
23
|
+
/** Report SDK-generated A/V Sync alert lifecycles and evidence. Defaults to `true`. */
|
|
24
|
+
avSync?: boolean;
|
|
25
|
+
}
|
|
17
26
|
/**
|
|
18
27
|
* Registration options accepted by SkyWalking js-sdk 1.1.27, plus the
|
|
19
|
-
* plugin-level overseas switch. `service` is deliberately
|
|
28
|
+
* plugin-level reporting controls and overseas switch. `service` is deliberately
|
|
29
|
+
* fixed by the plugin.
|
|
20
30
|
*/
|
|
21
31
|
interface SkyWalkingPluginConfig {
|
|
32
|
+
/** Select automatic SDK log types. Every type is enabled by default. */
|
|
33
|
+
reporting?: SkyWalkingReportingConfig;
|
|
22
34
|
/** Explicit collector URL. It takes precedence over `isOverseas`. */
|
|
23
35
|
collector?: string;
|
|
24
36
|
/** `false` selects the domestic collector; `true` selects the overseas one. */
|
|
@@ -241,6 +253,31 @@ interface SkyWalkingFrameErrorReportConfig {
|
|
|
241
253
|
maxReqCostTime?: number;
|
|
242
254
|
timeout?: number;
|
|
243
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Custom performance fields sent for one terminal connect-to-first-frame
|
|
258
|
+
* attempt. Stage fields are present only when the SDK observed that stage.
|
|
259
|
+
*/
|
|
260
|
+
type SkyWalkingFirstFrameOutcome = "first-frame" | "connect-failed" | "first-frame-timeout";
|
|
261
|
+
/** Allowed first-frame timing nodes copied from the SDK Operational Log. */
|
|
262
|
+
interface SkyWalkingFirstFrameLogNodes {
|
|
263
|
+
configReadyMs?: number;
|
|
264
|
+
rtcConnectedMs?: number;
|
|
265
|
+
videoTrackAddedMs?: number;
|
|
266
|
+
videoMetadataMs?: number;
|
|
267
|
+
firstFrameMs?: number;
|
|
268
|
+
}
|
|
269
|
+
/** Structured content of one terminal SDK `performance.first-frame` log. */
|
|
270
|
+
interface SkyWalkingFirstFrameLogPayload {
|
|
271
|
+
schemaVersion: 1;
|
|
272
|
+
kind: "liveavatar.performance.first-frame";
|
|
273
|
+
sdkVersion: string;
|
|
274
|
+
occurredAtMs: number;
|
|
275
|
+
attemptId: number;
|
|
276
|
+
outcome: SkyWalkingFirstFrameOutcome;
|
|
277
|
+
renderMode?: string;
|
|
278
|
+
timeoutAfterRtcMs?: number;
|
|
279
|
+
nodes: SkyWalkingFirstFrameLogNodes;
|
|
280
|
+
}
|
|
244
281
|
/**
|
|
245
282
|
* Reports LiveAvatar A/V-sync incident lifecycle events through the bundled or
|
|
246
283
|
* externally provided SkyWalking client.
|
|
@@ -250,6 +287,7 @@ declare class SkyWalkingPlugin implements SDKPlugin {
|
|
|
250
287
|
/** SDK Plugin API v1 is the registrar-based public plugin contract. */
|
|
251
288
|
readonly apiVersion = 1;
|
|
252
289
|
private readonly _skyWalkingConfig;
|
|
290
|
+
private readonly _reporting;
|
|
253
291
|
private _installation;
|
|
254
292
|
private _nextInstallationGeneration;
|
|
255
293
|
constructor(config?: SkyWalkingPluginConfig);
|
|
@@ -261,6 +299,7 @@ declare class SkyWalkingPlugin implements SDKPlugin {
|
|
|
261
299
|
private _reportActiveAlert;
|
|
262
300
|
private _reportRecovery;
|
|
263
301
|
private _reportSdkError;
|
|
302
|
+
private _reportFirstFrameLog;
|
|
264
303
|
private _deactivateInstallation;
|
|
265
304
|
private _isInvocationActive;
|
|
266
305
|
private _runObserverTask;
|
|
@@ -283,4 +322,4 @@ declare function setSkyWalkingHRT(config?: Partial<SkyWalkingPluginConfig>, cust
|
|
|
283
322
|
declare function reportSkyWalkingFrameErrors(config: SkyWalkingFrameErrorReportConfig, error: SkyWalkingFrameError): Promise<void>;
|
|
284
323
|
|
|
285
324
|
export { SKYWALKING_PLUGIN_ID, SkyWalkingPlugin, createSkyWalkingPlugin, registerSkyWalking, reportSkyWalkingFrameErrors, setSkyWalkingHRT, setSkyWalkingPerformance };
|
|
286
|
-
export type { AvSyncAlertEvidencePayload, AvSyncEvidence, AvSyncEvidenceLogChunkPayload, AvSyncRecoveryPayload, JsonSafeAlert, SkyWalkingFrameError, SkyWalkingFrameErrorReportConfig, SkyWalkingHooks, SkyWalkingPluginConfig, SkyWalkingRegistrationOptions, SkyWalkingSdkErrorPayload, SkyWalkingSlowInteractionConfig };
|
|
325
|
+
export type { AvSyncAlertEvidencePayload, AvSyncEvidence, AvSyncEvidenceLogChunkPayload, AvSyncRecoveryPayload, JsonSafeAlert, SkyWalkingFirstFrameLogNodes, SkyWalkingFirstFrameLogPayload, SkyWalkingFirstFrameOutcome, SkyWalkingFrameError, SkyWalkingFrameErrorReportConfig, SkyWalkingHooks, SkyWalkingPluginConfig, SkyWalkingRegistrationOptions, SkyWalkingReportingConfig, SkyWalkingSdkErrorPayload, SkyWalkingSlowInteractionConfig };
|
package/dist/index.esm.js
CHANGED
|
@@ -44,28 +44,34 @@ class SkyWalkingPlugin {
|
|
|
44
44
|
this.apiVersion = 1;
|
|
45
45
|
this._nextInstallationGeneration = 0;
|
|
46
46
|
this._skyWalkingConfig = { ...config };
|
|
47
|
+
this._reporting = normalizeReportingConfig(config.reporting);
|
|
47
48
|
}
|
|
48
49
|
setup(registrar) {
|
|
49
50
|
this._deactivateInstallation();
|
|
51
|
+
if (!hasEnabledReporting(this._reporting))
|
|
52
|
+
return;
|
|
50
53
|
registerSkyWalking(this._skyWalkingConfig);
|
|
51
|
-
const diagnostics = registrar.consume(SDK_AVSYNC_DIAGNOSTICS_V1);
|
|
52
54
|
const installation = {
|
|
53
55
|
generation: ++this._nextInstallationGeneration,
|
|
54
|
-
diagnostics,
|
|
55
56
|
abortController: new AbortController(),
|
|
56
57
|
alertReportStates: new Map(),
|
|
57
58
|
active: true,
|
|
58
59
|
};
|
|
59
60
|
this._installation = installation;
|
|
60
61
|
try {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
if (this._reporting.avSync) {
|
|
63
|
+
installation.diagnostics = registrar.consume(SDK_AVSYNC_DIAGNOSTICS_V1);
|
|
64
|
+
registrar.observe(SDK_AVSYNC_ALERTS_V1, {
|
|
65
|
+
id: AVSYNC_ALERTS_CONTRIBUTION_ID,
|
|
66
|
+
handler: (alert, context) => this._handleAlertNotification(installation, alert, context.signal),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (this._reporting.sdkError || this._reporting.firstFrame) {
|
|
70
|
+
registrar.observe(SDK_OPERATIONAL_LOGS_V1, {
|
|
71
|
+
id: OPERATIONAL_LOGS_CONTRIBUTION_ID,
|
|
72
|
+
handler: (record, context) => this._handleOperationalLog(installation, record, context.signal),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
69
75
|
}
|
|
70
76
|
catch (error) {
|
|
71
77
|
this._deactivateInstallation();
|
|
@@ -114,12 +120,19 @@ class SkyWalkingPlugin {
|
|
|
114
120
|
}
|
|
115
121
|
}
|
|
116
122
|
_handleOperationalLog(installation, record, signal) {
|
|
117
|
-
if (!this._isInvocationActive(installation, signal)
|
|
118
|
-
record.level !== "error" ||
|
|
119
|
-
record.event !== "sdk.error") {
|
|
123
|
+
if (!this._isInvocationActive(installation, signal))
|
|
120
124
|
return Promise.resolve();
|
|
125
|
+
if (this._reporting.sdkError &&
|
|
126
|
+
record.level === "error" &&
|
|
127
|
+
record.event === "sdk.error") {
|
|
128
|
+
return this._runObserverTask(installation, signal, "SDK error log", () => this._reportSdkError(installation, record, signal));
|
|
121
129
|
}
|
|
122
|
-
|
|
130
|
+
if (this._reporting.firstFrame &&
|
|
131
|
+
record.category === "media" &&
|
|
132
|
+
record.event === "performance.first-frame") {
|
|
133
|
+
return this._runObserverTask(installation, signal, "first-frame log", () => this._reportFirstFrameLog(installation, record, signal));
|
|
134
|
+
}
|
|
135
|
+
return Promise.resolve();
|
|
123
136
|
}
|
|
124
137
|
_enqueue(state, task) {
|
|
125
138
|
const queued = state.tail.then(task, task);
|
|
@@ -130,6 +143,9 @@ class SkyWalkingPlugin {
|
|
|
130
143
|
const isActive = () => this._isInvocationActive(installation, signal);
|
|
131
144
|
if (!isActive())
|
|
132
145
|
return;
|
|
146
|
+
const diagnostics = installation.diagnostics;
|
|
147
|
+
if (diagnostics === undefined)
|
|
148
|
+
return;
|
|
133
149
|
const clientMonitor = getClientMonitor();
|
|
134
150
|
if (clientMonitor === undefined) {
|
|
135
151
|
this._warnInvocation(installation, signal, "ClientMonitor.reportFrameErrors is unavailable", {
|
|
@@ -142,7 +158,7 @@ class SkyWalkingPlugin {
|
|
|
142
158
|
const query = await createEvidenceQuery(alert.phase, state.lastSuccessfulToWallClockMs, [signal, installation.abortController.signal]);
|
|
143
159
|
if (!isActive() || query === undefined)
|
|
144
160
|
return;
|
|
145
|
-
const collection = await collectTelemetry(
|
|
161
|
+
const collection = await collectTelemetry(diagnostics, alert, retrievedAtWallClockMs, query, isActive, (message, error) => this._warnInvocationFailure(installation, signal, message, error, {
|
|
146
162
|
alert: toJsonSafeAlert(alert),
|
|
147
163
|
}));
|
|
148
164
|
if (!isActive() || collection === undefined)
|
|
@@ -270,6 +286,43 @@ class SkyWalkingPlugin {
|
|
|
270
286
|
if (!this._isInvocationActive(installation, signal))
|
|
271
287
|
return;
|
|
272
288
|
}
|
|
289
|
+
async _reportFirstFrameLog(installation, record, signal) {
|
|
290
|
+
if (!this._isInvocationActive(installation, signal))
|
|
291
|
+
return;
|
|
292
|
+
const payload = toSkyWalkingFirstFrameLogPayload(record);
|
|
293
|
+
if (payload === undefined) {
|
|
294
|
+
this._warnInvocation(installation, signal, "Skipped invalid first-frame Operational Log", { eventId: record.eventId });
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const clientMonitor = getClientMonitor();
|
|
298
|
+
if (clientMonitor === undefined) {
|
|
299
|
+
this._warnInvocation(installation, signal, "ClientMonitor.reportFrameErrors is unavailable", {
|
|
300
|
+
pluginId: this.id,
|
|
301
|
+
attemptId: payload.attemptId,
|
|
302
|
+
outcome: payload.outcome,
|
|
303
|
+
});
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (!this._isInvocationActive(installation, signal))
|
|
307
|
+
return;
|
|
308
|
+
try {
|
|
309
|
+
await Promise.resolve(clientMonitor.reportFrameErrors({
|
|
310
|
+
category: "media",
|
|
311
|
+
grade: payload.outcome === "first-frame" ? "Info" : "Warning",
|
|
312
|
+
content: payload,
|
|
313
|
+
// The bundled client batches error records. Its public timeout
|
|
314
|
+
// option bypasses the default delay for these terminal events.
|
|
315
|
+
timeout: 1,
|
|
316
|
+
}, {
|
|
317
|
+
name: "",
|
|
318
|
+
message: `[LiveAvatar first-frame] ${payload.outcome}`,
|
|
319
|
+
stack: "",
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
322
|
+
catch (error) {
|
|
323
|
+
this._warnInvocationFailure(installation, signal, "Failed to report first-frame Operational Log to SkyWalking", error, { attemptId: payload.attemptId, outcome: payload.outcome });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
273
326
|
_deactivateInstallation() {
|
|
274
327
|
const installation = this._installation;
|
|
275
328
|
if (installation === undefined)
|
|
@@ -1229,6 +1282,60 @@ function toJsonSafeAlert(alert) {
|
|
|
1229
1282
|
features: { ...alert.features },
|
|
1230
1283
|
};
|
|
1231
1284
|
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Strictly projects the published first-frame Operational Log. The SDK record
|
|
1287
|
+
* has additional identifiers and arbitrary attributes; none of those can
|
|
1288
|
+
* reach SkyWalking through this path.
|
|
1289
|
+
*/
|
|
1290
|
+
function toSkyWalkingFirstFrameLogPayload(record) {
|
|
1291
|
+
const correlation = toRecord(record.correlation);
|
|
1292
|
+
const attributes = toRecord(record.attributes);
|
|
1293
|
+
const attemptId = correlation.attemptId;
|
|
1294
|
+
const outcome = attributes.outcome;
|
|
1295
|
+
if (!isNonNegativeNumber(record.occurredAtMs) ||
|
|
1296
|
+
!isPositiveInteger(attemptId) ||
|
|
1297
|
+
!isFirstFrameOutcome(outcome)) {
|
|
1298
|
+
return undefined;
|
|
1299
|
+
}
|
|
1300
|
+
const sourceNodes = toRecord(attributes.nodes);
|
|
1301
|
+
const nodes = {};
|
|
1302
|
+
copyNonNegativeNumber(sourceNodes, "configReadyMs", nodes);
|
|
1303
|
+
copyNonNegativeNumber(sourceNodes, "rtcConnectedMs", nodes);
|
|
1304
|
+
copyNonNegativeNumber(sourceNodes, "videoTrackAddedMs", nodes);
|
|
1305
|
+
copyNonNegativeNumber(sourceNodes, "videoMetadataMs", nodes);
|
|
1306
|
+
copyNonNegativeNumber(sourceNodes, "firstFrameMs", nodes);
|
|
1307
|
+
return {
|
|
1308
|
+
schemaVersion: 1,
|
|
1309
|
+
kind: "liveavatar.performance.first-frame",
|
|
1310
|
+
sdkVersion: VERSION,
|
|
1311
|
+
occurredAtMs: record.occurredAtMs,
|
|
1312
|
+
attemptId,
|
|
1313
|
+
outcome,
|
|
1314
|
+
...(typeof attributes.renderMode === "string"
|
|
1315
|
+
? { renderMode: attributes.renderMode }
|
|
1316
|
+
: {}),
|
|
1317
|
+
...(isNonNegativeNumber(attributes.timeoutAfterRtcMs)
|
|
1318
|
+
? { timeoutAfterRtcMs: attributes.timeoutAfterRtcMs }
|
|
1319
|
+
: {}),
|
|
1320
|
+
nodes,
|
|
1321
|
+
};
|
|
1322
|
+
}
|
|
1323
|
+
function copyNonNegativeNumber(source, key, target) {
|
|
1324
|
+
const value = source[key];
|
|
1325
|
+
if (isNonNegativeNumber(value))
|
|
1326
|
+
target[key] = value;
|
|
1327
|
+
}
|
|
1328
|
+
function isFirstFrameOutcome(value) {
|
|
1329
|
+
return (value === "first-frame" ||
|
|
1330
|
+
value === "connect-failed" ||
|
|
1331
|
+
value === "first-frame-timeout");
|
|
1332
|
+
}
|
|
1333
|
+
function isPositiveInteger(value) {
|
|
1334
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
1335
|
+
}
|
|
1336
|
+
function isNonNegativeNumber(value) {
|
|
1337
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1338
|
+
}
|
|
1232
1339
|
function stringifyJson(value) {
|
|
1233
1340
|
const serialized = JSON.stringify(value);
|
|
1234
1341
|
if (serialized === undefined) {
|
|
@@ -1277,9 +1384,8 @@ function registerSkyWalking(config = {}) {
|
|
|
1277
1384
|
}
|
|
1278
1385
|
/** Triggers a SkyWalking performance report through an already registered client. */
|
|
1279
1386
|
function setSkyWalkingPerformance(config = {}, customMetrics) {
|
|
1280
|
-
const clientMonitor =
|
|
1281
|
-
if (clientMonitor === undefined
|
|
1282
|
-
typeof clientMonitor.setPerformance !== "function") {
|
|
1387
|
+
const clientMonitor = getPerformanceClientMonitor();
|
|
1388
|
+
if (clientMonitor === undefined) {
|
|
1283
1389
|
warnOnce("set-performance-unavailable", "[LiveAvatar SkyWalking plugin] ClientMonitor.setPerformance is unavailable; register SkyWalking first");
|
|
1284
1390
|
return;
|
|
1285
1391
|
}
|
|
@@ -1304,6 +1410,16 @@ async function reportSkyWalkingFrameErrors(config, error) {
|
|
|
1304
1410
|
}
|
|
1305
1411
|
await Promise.resolve(clientMonitor.reportFrameErrors(config, error));
|
|
1306
1412
|
}
|
|
1413
|
+
function normalizeReportingConfig(config) {
|
|
1414
|
+
return {
|
|
1415
|
+
sdkError: config?.sdkError ?? true,
|
|
1416
|
+
firstFrame: config?.firstFrame ?? true,
|
|
1417
|
+
avSync: config?.avSync ?? true,
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
function hasEnabledReporting(config) {
|
|
1421
|
+
return config.sdkError || config.firstFrame || config.avSync;
|
|
1422
|
+
}
|
|
1307
1423
|
function normalizeSkyWalkingConfig(config) {
|
|
1308
1424
|
return {
|
|
1309
1425
|
collector: config.collector ??
|
|
@@ -1351,6 +1467,13 @@ function getClientMonitor() {
|
|
|
1351
1467
|
? candidate
|
|
1352
1468
|
: undefined;
|
|
1353
1469
|
}
|
|
1470
|
+
/** Finds the performance API without requiring the unrelated error API. */
|
|
1471
|
+
function getPerformanceClientMonitor() {
|
|
1472
|
+
const candidate = getGlobalClientMonitor();
|
|
1473
|
+
return typeof candidate?.setPerformance === "function"
|
|
1474
|
+
? candidate
|
|
1475
|
+
: undefined;
|
|
1476
|
+
}
|
|
1354
1477
|
function getGlobalClientMonitor() {
|
|
1355
1478
|
if (typeof window === "undefined")
|
|
1356
1479
|
return undefined;
|
package/dist/index.umd.js
CHANGED
|
@@ -48,28 +48,34 @@
|
|
|
48
48
|
this.apiVersion = 1;
|
|
49
49
|
this._nextInstallationGeneration = 0;
|
|
50
50
|
this._skyWalkingConfig = { ...config };
|
|
51
|
+
this._reporting = normalizeReportingConfig(config.reporting);
|
|
51
52
|
}
|
|
52
53
|
setup(registrar) {
|
|
53
54
|
this._deactivateInstallation();
|
|
55
|
+
if (!hasEnabledReporting(this._reporting))
|
|
56
|
+
return;
|
|
54
57
|
registerSkyWalking(this._skyWalkingConfig);
|
|
55
|
-
const diagnostics = registrar.consume(liveavatarJsSdk.SDK_AVSYNC_DIAGNOSTICS_V1);
|
|
56
58
|
const installation = {
|
|
57
59
|
generation: ++this._nextInstallationGeneration,
|
|
58
|
-
diagnostics,
|
|
59
60
|
abortController: new AbortController(),
|
|
60
61
|
alertReportStates: new Map(),
|
|
61
62
|
active: true,
|
|
62
63
|
};
|
|
63
64
|
this._installation = installation;
|
|
64
65
|
try {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
66
|
+
if (this._reporting.avSync) {
|
|
67
|
+
installation.diagnostics = registrar.consume(liveavatarJsSdk.SDK_AVSYNC_DIAGNOSTICS_V1);
|
|
68
|
+
registrar.observe(liveavatarJsSdk.SDK_AVSYNC_ALERTS_V1, {
|
|
69
|
+
id: AVSYNC_ALERTS_CONTRIBUTION_ID,
|
|
70
|
+
handler: (alert, context) => this._handleAlertNotification(installation, alert, context.signal),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (this._reporting.sdkError || this._reporting.firstFrame) {
|
|
74
|
+
registrar.observe(liveavatarJsSdk.SDK_OPERATIONAL_LOGS_V1, {
|
|
75
|
+
id: OPERATIONAL_LOGS_CONTRIBUTION_ID,
|
|
76
|
+
handler: (record, context) => this._handleOperationalLog(installation, record, context.signal),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
73
79
|
}
|
|
74
80
|
catch (error) {
|
|
75
81
|
this._deactivateInstallation();
|
|
@@ -118,12 +124,19 @@
|
|
|
118
124
|
}
|
|
119
125
|
}
|
|
120
126
|
_handleOperationalLog(installation, record, signal) {
|
|
121
|
-
if (!this._isInvocationActive(installation, signal)
|
|
122
|
-
record.level !== "error" ||
|
|
123
|
-
record.event !== "sdk.error") {
|
|
127
|
+
if (!this._isInvocationActive(installation, signal))
|
|
124
128
|
return Promise.resolve();
|
|
129
|
+
if (this._reporting.sdkError &&
|
|
130
|
+
record.level === "error" &&
|
|
131
|
+
record.event === "sdk.error") {
|
|
132
|
+
return this._runObserverTask(installation, signal, "SDK error log", () => this._reportSdkError(installation, record, signal));
|
|
125
133
|
}
|
|
126
|
-
|
|
134
|
+
if (this._reporting.firstFrame &&
|
|
135
|
+
record.category === "media" &&
|
|
136
|
+
record.event === "performance.first-frame") {
|
|
137
|
+
return this._runObserverTask(installation, signal, "first-frame log", () => this._reportFirstFrameLog(installation, record, signal));
|
|
138
|
+
}
|
|
139
|
+
return Promise.resolve();
|
|
127
140
|
}
|
|
128
141
|
_enqueue(state, task) {
|
|
129
142
|
const queued = state.tail.then(task, task);
|
|
@@ -134,6 +147,9 @@
|
|
|
134
147
|
const isActive = () => this._isInvocationActive(installation, signal);
|
|
135
148
|
if (!isActive())
|
|
136
149
|
return;
|
|
150
|
+
const diagnostics = installation.diagnostics;
|
|
151
|
+
if (diagnostics === undefined)
|
|
152
|
+
return;
|
|
137
153
|
const clientMonitor = getClientMonitor();
|
|
138
154
|
if (clientMonitor === undefined) {
|
|
139
155
|
this._warnInvocation(installation, signal, "ClientMonitor.reportFrameErrors is unavailable", {
|
|
@@ -146,7 +162,7 @@
|
|
|
146
162
|
const query = await createEvidenceQuery(alert.phase, state.lastSuccessfulToWallClockMs, [signal, installation.abortController.signal]);
|
|
147
163
|
if (!isActive() || query === undefined)
|
|
148
164
|
return;
|
|
149
|
-
const collection = await collectTelemetry(
|
|
165
|
+
const collection = await collectTelemetry(diagnostics, alert, retrievedAtWallClockMs, query, isActive, (message, error) => this._warnInvocationFailure(installation, signal, message, error, {
|
|
150
166
|
alert: toJsonSafeAlert(alert),
|
|
151
167
|
}));
|
|
152
168
|
if (!isActive() || collection === undefined)
|
|
@@ -274,6 +290,43 @@
|
|
|
274
290
|
if (!this._isInvocationActive(installation, signal))
|
|
275
291
|
return;
|
|
276
292
|
}
|
|
293
|
+
async _reportFirstFrameLog(installation, record, signal) {
|
|
294
|
+
if (!this._isInvocationActive(installation, signal))
|
|
295
|
+
return;
|
|
296
|
+
const payload = toSkyWalkingFirstFrameLogPayload(record);
|
|
297
|
+
if (payload === undefined) {
|
|
298
|
+
this._warnInvocation(installation, signal, "Skipped invalid first-frame Operational Log", { eventId: record.eventId });
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const clientMonitor = getClientMonitor();
|
|
302
|
+
if (clientMonitor === undefined) {
|
|
303
|
+
this._warnInvocation(installation, signal, "ClientMonitor.reportFrameErrors is unavailable", {
|
|
304
|
+
pluginId: this.id,
|
|
305
|
+
attemptId: payload.attemptId,
|
|
306
|
+
outcome: payload.outcome,
|
|
307
|
+
});
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (!this._isInvocationActive(installation, signal))
|
|
311
|
+
return;
|
|
312
|
+
try {
|
|
313
|
+
await Promise.resolve(clientMonitor.reportFrameErrors({
|
|
314
|
+
category: "media",
|
|
315
|
+
grade: payload.outcome === "first-frame" ? "Info" : "Warning",
|
|
316
|
+
content: payload,
|
|
317
|
+
// The bundled client batches error records. Its public timeout
|
|
318
|
+
// option bypasses the default delay for these terminal events.
|
|
319
|
+
timeout: 1,
|
|
320
|
+
}, {
|
|
321
|
+
name: "",
|
|
322
|
+
message: `[LiveAvatar first-frame] ${payload.outcome}`,
|
|
323
|
+
stack: "",
|
|
324
|
+
}));
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
this._warnInvocationFailure(installation, signal, "Failed to report first-frame Operational Log to SkyWalking", error, { attemptId: payload.attemptId, outcome: payload.outcome });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
277
330
|
_deactivateInstallation() {
|
|
278
331
|
const installation = this._installation;
|
|
279
332
|
if (installation === undefined)
|
|
@@ -1233,6 +1286,60 @@
|
|
|
1233
1286
|
features: { ...alert.features },
|
|
1234
1287
|
};
|
|
1235
1288
|
}
|
|
1289
|
+
/**
|
|
1290
|
+
* Strictly projects the published first-frame Operational Log. The SDK record
|
|
1291
|
+
* has additional identifiers and arbitrary attributes; none of those can
|
|
1292
|
+
* reach SkyWalking through this path.
|
|
1293
|
+
*/
|
|
1294
|
+
function toSkyWalkingFirstFrameLogPayload(record) {
|
|
1295
|
+
const correlation = toRecord(record.correlation);
|
|
1296
|
+
const attributes = toRecord(record.attributes);
|
|
1297
|
+
const attemptId = correlation.attemptId;
|
|
1298
|
+
const outcome = attributes.outcome;
|
|
1299
|
+
if (!isNonNegativeNumber(record.occurredAtMs) ||
|
|
1300
|
+
!isPositiveInteger(attemptId) ||
|
|
1301
|
+
!isFirstFrameOutcome(outcome)) {
|
|
1302
|
+
return undefined;
|
|
1303
|
+
}
|
|
1304
|
+
const sourceNodes = toRecord(attributes.nodes);
|
|
1305
|
+
const nodes = {};
|
|
1306
|
+
copyNonNegativeNumber(sourceNodes, "configReadyMs", nodes);
|
|
1307
|
+
copyNonNegativeNumber(sourceNodes, "rtcConnectedMs", nodes);
|
|
1308
|
+
copyNonNegativeNumber(sourceNodes, "videoTrackAddedMs", nodes);
|
|
1309
|
+
copyNonNegativeNumber(sourceNodes, "videoMetadataMs", nodes);
|
|
1310
|
+
copyNonNegativeNumber(sourceNodes, "firstFrameMs", nodes);
|
|
1311
|
+
return {
|
|
1312
|
+
schemaVersion: 1,
|
|
1313
|
+
kind: "liveavatar.performance.first-frame",
|
|
1314
|
+
sdkVersion: liveavatarJsSdk.VERSION,
|
|
1315
|
+
occurredAtMs: record.occurredAtMs,
|
|
1316
|
+
attemptId,
|
|
1317
|
+
outcome,
|
|
1318
|
+
...(typeof attributes.renderMode === "string"
|
|
1319
|
+
? { renderMode: attributes.renderMode }
|
|
1320
|
+
: {}),
|
|
1321
|
+
...(isNonNegativeNumber(attributes.timeoutAfterRtcMs)
|
|
1322
|
+
? { timeoutAfterRtcMs: attributes.timeoutAfterRtcMs }
|
|
1323
|
+
: {}),
|
|
1324
|
+
nodes,
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
function copyNonNegativeNumber(source, key, target) {
|
|
1328
|
+
const value = source[key];
|
|
1329
|
+
if (isNonNegativeNumber(value))
|
|
1330
|
+
target[key] = value;
|
|
1331
|
+
}
|
|
1332
|
+
function isFirstFrameOutcome(value) {
|
|
1333
|
+
return (value === "first-frame" ||
|
|
1334
|
+
value === "connect-failed" ||
|
|
1335
|
+
value === "first-frame-timeout");
|
|
1336
|
+
}
|
|
1337
|
+
function isPositiveInteger(value) {
|
|
1338
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
1339
|
+
}
|
|
1340
|
+
function isNonNegativeNumber(value) {
|
|
1341
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1342
|
+
}
|
|
1236
1343
|
function stringifyJson(value) {
|
|
1237
1344
|
const serialized = JSON.stringify(value);
|
|
1238
1345
|
if (serialized === undefined) {
|
|
@@ -1281,9 +1388,8 @@
|
|
|
1281
1388
|
}
|
|
1282
1389
|
/** Triggers a SkyWalking performance report through an already registered client. */
|
|
1283
1390
|
function setSkyWalkingPerformance(config = {}, customMetrics) {
|
|
1284
|
-
const clientMonitor =
|
|
1285
|
-
if (clientMonitor === undefined
|
|
1286
|
-
typeof clientMonitor.setPerformance !== "function") {
|
|
1391
|
+
const clientMonitor = getPerformanceClientMonitor();
|
|
1392
|
+
if (clientMonitor === undefined) {
|
|
1287
1393
|
warnOnce("set-performance-unavailable", "[LiveAvatar SkyWalking plugin] ClientMonitor.setPerformance is unavailable; register SkyWalking first");
|
|
1288
1394
|
return;
|
|
1289
1395
|
}
|
|
@@ -1308,6 +1414,16 @@
|
|
|
1308
1414
|
}
|
|
1309
1415
|
await Promise.resolve(clientMonitor.reportFrameErrors(config, error));
|
|
1310
1416
|
}
|
|
1417
|
+
function normalizeReportingConfig(config) {
|
|
1418
|
+
return {
|
|
1419
|
+
sdkError: config?.sdkError ?? true,
|
|
1420
|
+
firstFrame: config?.firstFrame ?? true,
|
|
1421
|
+
avSync: config?.avSync ?? true,
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
function hasEnabledReporting(config) {
|
|
1425
|
+
return config.sdkError || config.firstFrame || config.avSync;
|
|
1426
|
+
}
|
|
1311
1427
|
function normalizeSkyWalkingConfig(config) {
|
|
1312
1428
|
return {
|
|
1313
1429
|
collector: config.collector ??
|
|
@@ -1355,6 +1471,13 @@
|
|
|
1355
1471
|
? candidate
|
|
1356
1472
|
: undefined;
|
|
1357
1473
|
}
|
|
1474
|
+
/** Finds the performance API without requiring the unrelated error API. */
|
|
1475
|
+
function getPerformanceClientMonitor() {
|
|
1476
|
+
const candidate = getGlobalClientMonitor();
|
|
1477
|
+
return typeof candidate?.setPerformance === "function"
|
|
1478
|
+
? candidate
|
|
1479
|
+
: undefined;
|
|
1480
|
+
}
|
|
1358
1481
|
function getGlobalClientMonitor() {
|
|
1359
1482
|
if (typeof window === "undefined")
|
|
1360
1483
|
return undefined;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanseng/liveavatar-js-sdk-skywalking",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "SkyWalking plugin for LiveAvatar A/V Sync alerts, first-frame performance, and SDK errors with type-selective reporting",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "sanseng",
|
|
7
7
|
"main": "dist/index.cjs.js",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"observability"
|
|
44
44
|
],
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@sanseng/liveavatar-js-sdk": "^2.
|
|
46
|
+
"@sanseng/liveavatar-js-sdk": "^2.4.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@rollup/plugin-typescript": "^12.3.0",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"rimraf": "^6.1.2",
|
|
54
54
|
"rollup": "^4.55.1",
|
|
55
55
|
"rollup-plugin-dts": "^6.3.0",
|
|
56
|
+
"tslib": "^2.8.1",
|
|
56
57
|
"typescript": "^5.9.3",
|
|
57
58
|
"vite": "^7.3.0",
|
|
58
59
|
"vitest": "^4.1.7"
|