@vskin/sdk 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/{chunk-KADQENVR.js → chunk-Y4CYVADH.js} +32 -7
- package/dist/chunk-Y4CYVADH.js.map +1 -0
- package/dist/embed.js +30 -4
- package/dist/embed.js.map +1 -1
- package/dist/index.cjs +30 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{interfaces-OatKvSe8.d.cts → interfaces-iOmXANSI.d.cts} +11 -3
- package/dist/{interfaces-OatKvSe8.d.ts → interfaces-iOmXANSI.d.ts} +11 -3
- package/dist/react.cjs +21 -4
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-KADQENVR.js.map +0 -1
package/README.md
CHANGED
|
@@ -20,3 +20,8 @@ viewer.on('change', ({ state }) => render(state));
|
|
|
20
20
|
|
|
21
21
|
Access is granted by the pair key + declared domain: the key identifies your account, your
|
|
22
22
|
domain list is what opens the door. Full documentation comes with your plan.
|
|
23
|
+
|
|
24
|
+
When the embed server refuses the viewer, `createViewer` rejects at once with
|
|
25
|
+
`{ code: 'refused', family }`. `family` says whose problem it is: `setup` (your integration,
|
|
26
|
+
mid-installation), `cut_off` (your account: key, domain or plan) or `outage` (ours). The frame is
|
|
27
|
+
left in place, showing the visitor a translated explanation; hide it or keep it, that is your call.
|
|
@@ -60,8 +60,15 @@ var EErrorCode = /* @__PURE__ */ ((EErrorCode2) => {
|
|
|
60
60
|
EErrorCode2["NOT_READY"] = "not_ready";
|
|
61
61
|
EErrorCode2["RATE_LIMITED"] = "rate_limited";
|
|
62
62
|
EErrorCode2["INTERNAL"] = "internal";
|
|
63
|
+
EErrorCode2["REFUSED"] = "refused";
|
|
63
64
|
return EErrorCode2;
|
|
64
65
|
})(EErrorCode || {});
|
|
66
|
+
var ERefusalFamily = /* @__PURE__ */ ((ERefusalFamily2) => {
|
|
67
|
+
ERefusalFamily2["SETUP"] = "setup";
|
|
68
|
+
ERefusalFamily2["CUT_OFF"] = "cut_off";
|
|
69
|
+
ERefusalFamily2["OUTAGE"] = "outage";
|
|
70
|
+
return ERefusalFamily2;
|
|
71
|
+
})(ERefusalFamily || {});
|
|
65
72
|
var EViewerEvent = /* @__PURE__ */ ((EViewerEvent2) => {
|
|
66
73
|
EViewerEvent2["READY"] = "ready";
|
|
67
74
|
EViewerEvent2["ERROR"] = "error";
|
|
@@ -78,6 +85,7 @@ var EViewerEvent = /* @__PURE__ */ ((EViewerEvent2) => {
|
|
|
78
85
|
EViewerEvent2["PLACEMENT_START"] = "placementstart";
|
|
79
86
|
EViewerEvent2["PLACEMENT_MOVE"] = "placementmove";
|
|
80
87
|
EViewerEvent2["PLACEMENT_END"] = "placementend";
|
|
88
|
+
EViewerEvent2["REFUSED"] = "refused";
|
|
81
89
|
return EViewerEvent2;
|
|
82
90
|
})(EViewerEvent || {});
|
|
83
91
|
|
|
@@ -343,13 +351,15 @@ var performHandshake = ({
|
|
|
343
351
|
transport,
|
|
344
352
|
iframe,
|
|
345
353
|
mirror,
|
|
346
|
-
onGiveUp
|
|
354
|
+
onGiveUp,
|
|
355
|
+
subscribeRefusal
|
|
347
356
|
}) => new Promise((resolve, reject) => {
|
|
348
357
|
let settled = false;
|
|
349
358
|
const beginAt = Date.now();
|
|
350
359
|
const settle = () => {
|
|
351
360
|
settled = true;
|
|
352
361
|
iframe.removeEventListener("load", attempt);
|
|
362
|
+
unsubscribeRefusal();
|
|
353
363
|
};
|
|
354
364
|
const attempt = () => {
|
|
355
365
|
if (settled) return;
|
|
@@ -358,7 +368,7 @@ var performHandshake = ({
|
|
|
358
368
|
onGiveUp();
|
|
359
369
|
return reject({
|
|
360
370
|
code: "invalid_key" /* INVALID_KEY */,
|
|
361
|
-
message: "the viewer never answered: the
|
|
371
|
+
message: "the viewer never answered: the render service is unreachable, or the frame was blocked before the page could boot"
|
|
362
372
|
});
|
|
363
373
|
}
|
|
364
374
|
transport.send({
|
|
@@ -374,6 +384,15 @@ var performHandshake = ({
|
|
|
374
384
|
resolve();
|
|
375
385
|
}).catch(() => setTimeout(attempt, HELLO_RETRY_MS));
|
|
376
386
|
};
|
|
387
|
+
const unsubscribeRefusal = subscribeRefusal(({ family }) => {
|
|
388
|
+
if (settled) return;
|
|
389
|
+
settle();
|
|
390
|
+
reject({
|
|
391
|
+
code: "refused" /* REFUSED */,
|
|
392
|
+
family,
|
|
393
|
+
message: "the embed server refused this viewer; its family says whose problem it is"
|
|
394
|
+
});
|
|
395
|
+
});
|
|
377
396
|
iframe.addEventListener("load", attempt);
|
|
378
397
|
setTimeout(attempt, HELLO_RETRY_MS);
|
|
379
398
|
});
|
|
@@ -408,6 +427,10 @@ var createViewer = ({
|
|
|
408
427
|
let destroyed = false;
|
|
409
428
|
let greeted = false;
|
|
410
429
|
let greetingAgain = false;
|
|
430
|
+
const subscribeRefusal = (handler) => hub.on(
|
|
431
|
+
"refused" /* REFUSED */,
|
|
432
|
+
(payload) => handler(payload)
|
|
433
|
+
);
|
|
411
434
|
const greetAgain = () => {
|
|
412
435
|
if (!greeted || greetingAgain || destroyed) return;
|
|
413
436
|
greetingAgain = true;
|
|
@@ -418,7 +441,8 @@ var createViewer = ({
|
|
|
418
441
|
mirror,
|
|
419
442
|
// A reload that never answers leaves the viewer as it is: the frame is the
|
|
420
443
|
// integrator's to keep or drop, and an error says so.
|
|
421
|
-
onGiveUp: () => void 0
|
|
444
|
+
onGiveUp: () => void 0,
|
|
445
|
+
subscribeRefusal
|
|
422
446
|
}).then(
|
|
423
447
|
() => hub.dispatch({
|
|
424
448
|
name: "reconnect" /* RECONNECT */,
|
|
@@ -459,13 +483,14 @@ var createViewer = ({
|
|
|
459
483
|
transport,
|
|
460
484
|
iframe,
|
|
461
485
|
mirror,
|
|
462
|
-
onGiveUp: destroy
|
|
486
|
+
onGiveUp: destroy,
|
|
487
|
+
subscribeRefusal
|
|
463
488
|
}).then(() => {
|
|
464
489
|
greeted = true;
|
|
465
490
|
return viewer;
|
|
466
491
|
});
|
|
467
492
|
};
|
|
468
493
|
|
|
469
|
-
export { CS2_INSPECT_LINK_PREFIX, EBackgroundType, EErrorCode, EFacing, EFeature, EHandedness, ESubject, EView, EViewerEvent, PROTOCOL_VERSION, createViewer };
|
|
470
|
-
//# sourceMappingURL=chunk-
|
|
471
|
-
//# sourceMappingURL=chunk-
|
|
494
|
+
export { CS2_INSPECT_LINK_PREFIX, EBackgroundType, EErrorCode, EFacing, EFeature, EHandedness, ERefusalFamily, ESubject, EView, EViewerEvent, PROTOCOL_VERSION, createViewer };
|
|
495
|
+
//# sourceMappingURL=chunk-Y4CYVADH.js.map
|
|
496
|
+
//# sourceMappingURL=chunk-Y4CYVADH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/contract/interfaces.ts","../src/protocol/interfaces.ts","../src/protocol/constants.ts","../src/lib/constants.ts","../src/lib/helpers/buildEmbedUrl.ts","../src/lib/helpers/buildViewerHandle.ts","../src/lib/helpers/createEmbedIframe.ts","../src/lib/helpers/createViewerEventHub.ts","../src/lib/helpers/createCommandId.ts","../src/lib/helpers/createViewerTransport.ts","../src/lib/helpers/performHandshake.ts","../src/lib/createViewer.ts"],"names":["EView","EBackgroundType","EFacing","EHandedness","ESubject","EFeature","EErrorCode","ERefusalFamily","EViewerEvent"],"mappings":";AAGO,IAAK,KAAA,qBAAAA,MAAAA,KAAL;AACH,EAAAA,OAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,OAAA,cAAA,CAAA,GAAe,aAAA;AACf,EAAAA,OAAA,cAAA,CAAA,GAAe,aAAA;AAHP,EAAA,OAAAA,MAAAA;AAAA,CAAA,EAAA,KAAA,IAAA,EAAA;AAQL,IAAK,eAAA,qBAAAC,gBAAAA,KAAL;AACH,EAAAA,iBAAA,MAAA,CAAA,GAAO,MAAA;AACP,EAAAA,iBAAA,aAAA,CAAA,GAAc,aAAA;AAFN,EAAA,OAAAA,gBAAAA;AAAA,CAAA,EAAA,eAAA,IAAA,EAAA;AASL,IAAK,OAAA,qBAAAC,QAAAA,KAAL;AACH,EAAAA,SAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,SAAA,MAAA,CAAA,GAAO,MAAA;AAFC,EAAA,OAAAA,QAAAA;AAAA,CAAA,EAAA,OAAA,IAAA,EAAA;AAKL,IAAK,WAAA,qBAAAC,YAAAA,KAAL;AACH,EAAAA,aAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,aAAA,MAAA,CAAA,GAAO,MAAA;AAFC,EAAA,OAAAA,YAAAA;AAAA,CAAA,EAAA,WAAA,IAAA,EAAA;AAOL,IAAK,QAAA,qBAAAC,SAAAA,KAAL;AACH,EAAAA,UAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,UAAA,QAAA,CAAA,GAAS,QAAA;AAFD,EAAA,OAAAA,SAAAA;AAAA,CAAA,EAAA,QAAA,IAAA,EAAA;AA6FL,IAAK,QAAA,qBAAAC,SAAAA,KAAL;AACH,EAAAA,UAAA,eAAA,CAAA,GAAgB,cAAA;AAChB,EAAAA,UAAA,eAAA,CAAA,GAAgB,cAAA;AAChB,EAAAA,UAAA,cAAA,CAAA,GAAe,aAAA;AACf,EAAAA,UAAA,oBAAA,CAAA,GAAqB,mBAAA;AACrB,EAAAA,UAAA,WAAA,CAAA,GAAY,UAAA;AACZ,EAAAA,UAAA,YAAA,CAAA,GAAa,WAAA;AACb,EAAAA,UAAA,eAAA,CAAA,GAAgB,cAAA;AAChB,EAAAA,UAAA,cAAA,CAAA,GAAe,aAAA;AACf,EAAAA,UAAA,gBAAA,CAAA,GAAiB,eAAA;AACjB,EAAAA,UAAA,cAAA,CAAA,GAAe,aAAA;AACf,EAAAA,UAAA,cAAA,CAAA,GAAe,aAAA;AACf,EAAAA,UAAA,eAAA,CAAA,GAAgB,cAAA;AAChB,EAAAA,UAAA,aAAA,CAAA,GAAc,YAAA;AACd,EAAAA,UAAA,gBAAA,CAAA,GAAiB,eAAA;AACjB,EAAAA,UAAA,aAAA,CAAA,GAAc,YAAA;AACd,EAAAA,UAAA,YAAA,CAAA,GAAa,YAAA;AACb,EAAAA,UAAA,cAAA,CAAA,GAAe,aAAA;AACf,EAAAA,UAAA,kBAAA,CAAA,GAAmB,iBAAA;AACnB,EAAAA,UAAA,mBAAA,CAAA,GAAoB,kBAAA;AACpB,EAAAA,UAAA,kBAAA,CAAA,GAAmB,iBAAA;AApBX,EAAA,OAAAA,SAAAA;AAAA,CAAA,EAAA,QAAA,IAAA,EAAA;;;AC/GL,IAAK,UAAA,qBAAAC,WAAAA,KAAL;AACH,EAAAA,YAAA,aAAA,CAAA,GAAc,aAAA;AACd,EAAAA,YAAA,oBAAA,CAAA,GAAqB,oBAAA;AACrB,EAAAA,YAAA,cAAA,CAAA,GAAe,cAAA;AACf,EAAAA,YAAA,kBAAA,CAAA,GAAmB,kBAAA;AACnB,EAAAA,YAAA,kBAAA,CAAA,GAAmB,kBAAA;AACnB,EAAAA,YAAA,kBAAA,CAAA,GAAmB,kBAAA;AACnB,EAAAA,YAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,YAAA,cAAA,CAAA,GAAe,cAAA;AACf,EAAAA,YAAA,UAAA,CAAA,GAAW,UAAA;AAGX,EAAAA,YAAA,SAAA,CAAA,GAAU,SAAA;AAZF,EAAA,OAAAA,WAAAA;AAAA,CAAA,EAAA,UAAA,IAAA,EAAA;AAkBL,IAAK,cAAA,qBAAAC,eAAAA,KAAL;AACH,EAAAA,gBAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,gBAAA,SAAA,CAAA,GAAU,SAAA;AACV,EAAAA,gBAAA,QAAA,CAAA,GAAS,QAAA;AAHD,EAAA,OAAAA,eAAAA;AAAA,CAAA,EAAA,cAAA,IAAA,EAAA;AA8CL,IAAK,YAAA,qBAAAC,aAAAA,KAAL;AACH,EAAAA,cAAA,OAAA,CAAA,GAAQ,OAAA;AACR,EAAAA,cAAA,OAAA,CAAA,GAAQ,OAAA;AAGR,EAAAA,cAAA,WAAA,CAAA,GAAY,WAAA;AACZ,EAAAA,cAAA,UAAA,CAAA,GAAW,UAAA;AACX,EAAAA,cAAA,aAAA,CAAA,GAAc,aAAA;AACd,EAAAA,cAAA,QAAA,CAAA,GAAS,QAAA;AACT,EAAAA,cAAA,aAAA,CAAA,GAAc,YAAA;AACd,EAAAA,cAAA,gBAAA,CAAA,GAAiB,eAAA;AACjB,EAAAA,cAAA,kBAAA,CAAA,GAAmB,iBAAA;AACnB,EAAAA,cAAA,kBAAA,CAAA,GAAmB,iBAAA;AACnB,EAAAA,cAAA,iBAAA,CAAA,GAAkB,gBAAA;AAClB,EAAAA,cAAA,qBAAA,CAAA,GAAsB,oBAAA;AACtB,EAAAA,cAAA,iBAAA,CAAA,GAAkB,gBAAA;AAClB,EAAAA,cAAA,gBAAA,CAAA,GAAiB,eAAA;AACjB,EAAAA,cAAA,eAAA,CAAA,GAAgB,cAAA;AAGhB,EAAAA,cAAA,SAAA,CAAA,GAAU,SAAA;AApBF,EAAA,OAAAA,aAAAA;AAAA,CAAA,EAAA,YAAA,IAAA,EAAA;;;AC3EL,IAAM,eAAA,GAAkB,cAAA;AAIxB,IAAM,gBAAA,GAAmB;;;ACNzB,IAAM,gBAAA,GAAmB,yBAAA;AAIzB,IAAM,cAAA,GAAiB,GAAA;AACvB,IAAM,gBAAA,GAAmB,IAAA;AAIzB,IAAM,kBAAA,GAAqB,GAAA;AAI3B,IAAM,uBAAA,GACT;;;ACVG,IAAM,gBAAgB,CAAC;AAAA,EAC1B,OAAA;AAAA,EACA,GAAA;AAAA,EACA,MAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA;AACJ,CAAA,KASc;AACV,EAAA,MAAM,KAAA,GAAQ,IAAI,eAAA,EAAgB;AAClC,EAAA,IAAI,GAAA,EAAK,KAAA,CAAM,GAAA,CAAI,KAAA,EAAO,GAAG,CAAA;AAC7B,EAAA,IAAI,QAAQ,IAAA,EAAM;AACd,IAAA,KAAA,CAAM,GAAA,CAAI,MAAA,EAAQ,MAAA,CAAO,IAAI,CAAA;AAAA,EACjC,CAAA,MAAA,IACI,UACA,OAAO,MAAA,CAAO,aAAa,QAAA,IAC3B,OAAO,MAAA,CAAO,UAAA,KAAe,QAAA,EAC/B;AACE,IAAA,KAAA,CAAM,GAAA,CAAI,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,QAAQ,CAAC,CAAA;AACxC,IAAA,KAAA,CAAM,GAAA,CAAI,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,UAAU,CAAC,CAAA;AAC5C,IAAA,IAAI,MAAA,CAAO,SAAS,MAAA,EAAW,KAAA,CAAM,IAAI,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,IAAI,CAAC,CAAA;AACpE,IAAA,IAAI,OAAO,KAAA,KAAU,MAAA;AACjB,MAAA,KAAA,CAAM,GAAA,CAAI,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,KAAK,CAAC,CAAA;AAC3C,IAAA,IAAI,MAAA,CAAO,QAAA,EAAU,KAAA,CAAM,GAAA,CAAI,YAAY,GAAG,CAAA;AAC9C,IAAA,IAAI,OAAO,KAAA,KAAU,MAAA;AACjB,MAAA,KAAA,CAAM,GAAA,CAAI,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,KAAK,CAAC,CAAA;AAC3C,IAAA,IAAI,OAAO,IAAA,KAAS,MAAA,QAAiB,GAAA,CAAI,MAAA,EAAQ,OAAO,IAAI,CAAA;AAAA,EAChE;AACA,EAAA,IAAI,WAAA,EAAa,KAAA,CAAM,GAAA,CAAI,KAAA,EAAO,WAAW,CAAA;AAC7C,EAAA,IAAI,UAAA,EAAY,IAAA,KAAA,MAAA;AACZ,IAAA,KAAA,CAAM,GAAA,CAAI,YAAY,OAAO,CAAA;AACjC,EAAA,IAAI,WAAA,KAAgB,KAAA,EAAO,KAAA,CAAM,GAAA,CAAI,eAAe,OAAO,CAAA;AAC3D,EAAA,IAAI,QAAA,EAAU,KAAA,CAAM,GAAA,CAAI,UAAA,EAAY,GAAG,CAAA;AACvC,EAAA,IAAI,MAAA,EAAQ,KAAA,CAAM,GAAA,CAAI,MAAA,EAAQ,MAAM,CAAA;AACpC,EAAA,MAAM,MAAA,GAAS,MAAM,QAAA,EAAS;AAC9B,EAAA,OAAO,GAAG,OAAO,CAAA,OAAA,EAAU,SAAS,CAAA,CAAA,EAAI,MAAM,KAAK,EAAE,CAAA,CAAA;AACzD,CAAA;;;AC3CO,IAAM,oBAAoB,CAAC;AAAA,EAC9B,SAAA;AAAA,EACA,GAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA;AACJ,CAAA,KAMc;AACV,EAAA,MAAM,OAAA,GAAU,CACZ,IAAA,EACA,OAAA,KACgB,SAAA,CAAU,IAAA,CAAK,EAAE,IAAA,EAAM,OAAA,EAAS,CAAA,CAAE,IAAA,CAAK,MAAM,MAAS,CAAA;AAE1E,EAAA,OAAO;AAAA,IACH,IAAI,KAAA,GAAQ;AACR,MAAA,OAAO,MAAA,CAAO,KAAA;AAAA,IAClB,CAAA;AAAA,IACA,IAAI,YAAA,GAAe;AACf,MAAA,OAAO,MAAA,CAAO,YAAA;AAAA,IAClB,CAAA;AAAA,IACA,IAAI,CAAC,KAAA,EAAO,YAAY,GAAA,CAAI,EAAA,CAAG,OAAO,OAAO,CAAA;AAAA,IAC7C,SAAA,EAAW,CAAC,IAAA,KAAS,OAAA,CAAA,WAAA,mBAAqC,IAAI,CAAA;AAAA,IAC9D,SAAA,EAAW,CAAC,IAAA,KAAS,OAAA,CAAA,WAAA,mBAAqC,IAAI,CAAA;AAAA,IAC9D,QAAA,EAAU,CAAC,IAAA,KAAS,OAAA,CAAA,UAAA,kBAAoC,IAAI,CAAA;AAAA,IAC5D,UAAA,EAAY,CAAC,IAAA,KAAS,OAAA,CAAA,YAAA,oBAAsC,IAAI,CAAA;AAAA,IAChE,WAAA,EAAa,CAAC,IAAA,KAAS,OAAA,CAAA,aAAA,qBAAuC,IAAI,CAAA;AAAA,IAClE,QAAA,EAAU,CAAC,IAAA,KAAS,OAAA,CAAA,UAAA,kBAAoC,IAAI,CAAA;AAAA,IAC5D,UAAA,EAAY,CAAC,IAAA,KAAS,OAAA,CAAA,YAAA,oBAAsC,IAAI,CAAA;AAAA,IAChE,OAAA,EAAS,CAAC,IAAA,KAAS,OAAA,CAAA,SAAA,iBAAmC,IAAI,CAAA;AAAA,IAC1D,sBAAA,EAAwB,CAAC,IAAA,KACrB,OAAA,CAAA,wBAAA,kCAAoD,IAAI,CAAA;AAAA,IAC5D,SAAA,EAAW,CAAC,IAAA,KAAS,OAAA,CAAA,WAAA,mBAAqC,IAAI,CAAA;AAAA,IAC9D,cAAA,EAAgB,CAAC,IAAA,KACb,OAAA,CAAA,gBAAA,wBAA0C,IAAI,CAAA;AAAA,IAClD,aAAA,EAAe,CAAC,IAAA,KAAS,OAAA,CAAA,eAAA,uBAAyC,IAAI,CAAA;AAAA,IACtE,QAAA,EAAU,CAAC,IAAA,KAAS,OAAA,CAAA,UAAA,kBAAoC,IAAI,CAAA;AAAA,IAC5D,SAAA,EAAW,CAAC,IAAA,KAAS,OAAA,CAAA,WAAA,mBAAqC,IAAI,CAAA;AAAA,IAC9D,KAAA,EAAO,CAAC,IAAA,KAAS,OAAA,CAAA,OAAA,cAAgC,IAAI,CAAA;AAAA,IACrD,WAAA,EAAa,MAAM,OAAA,CAAA,aAAA,oBAAqC;AAAA,IACxD,IAAA,EAAM,MAAM,OAAA,CAAA,MAAA,YAA6B;AAAA,IACzC,KAAA,EAAO,MAAM,OAAA,CAAA,OAAA,aAA8B;AAAA,IAC3C,aAAA,EAAe,CAAC,IAAA,KAAS,OAAA,CAAA,eAAA,uBAAyC,IAAI,CAAA;AAAA,IACtE,aAAA,EAAe,MAAM,OAAA,CAAA,eAAA,sBAAuC;AAAA,IAC5D,SAAA,EAAW,MAAM,OAAA,CAAA,WAAA,kBAAmC;AAAA,IACpD,QAAA,EAAU,MAAM,OAAA,CAAA,UAAA,iBAAkC;AAAA,IAClD,YAAA,EAAc,CAAC,IAAA,KAAS,OAAA,CAAA,cAAA,sBAAwC,IAAI,CAAA;AAAA,IACpE,gBAAgB,CAAC,EAAE,CAAA,EAAG,CAAA,EAAG,OAAM,KAAM;AACjC,MAAA,MAAM,IAAA,GAAO,OAAO,qBAAA,EAAsB;AAC1C,MAAA,OAAO,OAAA,CAAA,gBAAA,wBAA0C;AAAA,QAC7C,CAAA,EAAG,IAAI,IAAA,CAAK,IAAA;AAAA,QACZ,CAAA,EAAG,IAAI,IAAA,CAAK,GAAA;AAAA,QACZ;AAAA,OACH,CAAA;AAAA,IACL,CAAA;AAAA,IACA,OAAA,EAAS,MACL,SAAA,CACK,IAAA,CAAK,EAAE,IAAA,EAAA,SAAA,gBAAgC,CAAA,CACvC,IAAA,CAAK,CAAC,KAAA,KAAW,KAAA,CAA4B,KAAK,CAAA;AAAA,IAC3D,OAAA,EAAS;AAAA,MACL,OAAA,EAAS,CAAC,KAAA,KACN,SAAA,CAAU,IAAA,CAAK;AAAA,QACX,IAAA,EAAA,gBAAA;AAAA,QACA,OAAA,EAAS;AAAA,OACZ,CAAA;AAAA,MACL,QAAA,EAAU,CAAC,KAAA,KACP,SAAA,CAAU,IAAA,CAAK;AAAA,QACX,IAAA,EAAA,iBAAA;AAAA,QACA,OAAA,EAAS;AAAA,OACZ,CAAA;AAAA,MACL,MAAA,EAAQ,CAAC,KAAA,KACL,SAAA,CAAU,IAAA,CAAK;AAAA,QACX,IAAA,EAAA,eAAA;AAAA,QACA,OAAA,EAAS;AAAA,OACZ,CAAA;AAAA,MACL,OAAA,EAAS,CAAC,KAAA,KACN,SAAA,CAAU,IAAA,CAAK;AAAA,QACX,IAAA,EAAA,gBAAA;AAAA,QACA,OAAA,EAAS;AAAA,OACZ,CAAA;AAAA,MACL,MAAA,EAAQ,CAAC,KAAA,KACL,SAAA,CAAU,IAAA,CAAK;AAAA,QACX,IAAA,EAAA,eAAA;AAAA,QACA,OAAA,EAAS;AAAA,OACZ,CAAA;AAAA,MACL,YAAA,EAAc,MACV,SAAA,CAAU,IAAA,CAAK;AAAA,QACX,IAAA,EAAA,qBAAA;AAAA,OACH,CAAA;AAAA,MACL,YAAA,EAAc,MACV,SAAA,CAAU,IAAA,CAAK;AAAA,QACX,IAAA,EAAA,qBAAA;AAAA,OACH;AAAA,KACT;AAAA,IACA;AAAA,GACJ;AACJ,CAAA;;;AC1GO,IAAM,oBAAoB,CAAC;AAAA,EAC9B;AACJ,CAAA,KAEyB;AACrB,EAAA,MAAM,MAAA,GAAS,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAC9C,EAAA,MAAA,CAAO,GAAA,GAAM,GAAA;AACb,EAAA,MAAA,CAAO,MAAM,KAAA,GAAQ,MAAA;AACrB,EAAA,MAAA,CAAO,MAAM,MAAA,GAAS,MAAA;AACtB,EAAA,MAAA,CAAO,MAAM,OAAA,GAAU,OAAA;AACvB,EAAA,MAAA,CAAO,MAAM,MAAA,GAAS,GAAA;AAGtB,EAAA,MAAA,CAAO,MAAM,WAAA,GAAc,QAAA;AAC3B,EAAA,MAAA,CAAO,KAAA,GAAQ,YAAA;AACf,EAAA,OAAO,MAAA;AACX,CAAA;;;ACLO,IAAM,uBAAuB,CAAC;AAAA,EACjC;AACJ,CAAA,KAEsB;AAClB,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAqC;AAE1D,EAAA,OAAO;AAAA,IACH,EAAA,EAAI,CAAC,KAAA,EAAO,OAAA,KAAY;AACpB,MAAA,MAAM,MAAM,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA,wBAAS,GAAA,EAAI;AAC3C,MAAA,GAAA,CAAI,IAAI,OAAO,CAAA;AACf,MAAA,QAAA,CAAS,GAAA,CAAI,OAAO,GAAG,CAAA;AACvB,MAAA,OAAO,MAAM;AACT,QAAA,GAAA,CAAI,OAAO,OAAO,CAAA;AAAA,MACtB,CAAA;AAAA,IACJ,CAAA;AAAA,IACA,QAAA,EAAU,CAAC,EAAE,IAAA,EAAM,SAAQ,KAAM;AAC7B,MAAA,IAAI,IAAA,KAAA,QAAA,eAA8B;AAC9B,QAAA,MAAM,MAAA,GAAS,OAAA;AACf,QAAA,IAAI,MAAA,EAAQ,KAAA,EAAO,MAAA,CAAO,KAAA,GAAQ,MAAA,CAAO,KAAA;AAAA,MAC7C;AACA,MAAA,IAAI,IAAA,KAAA,oBAAA,4BAA2C;AAC3C,QAAA,MAAM,MAAA,GAAS,OAAA;AAEf,QAAA,IAAI,MAAA,EAAQ,YAAA;AACR,UAAA,MAAA,CAAO,eAAe,MAAA,CAAO,YAAA;AAAA,MACrC;AACA,MAAA,KAAA,MAAW,OAAA,IAAW,SAAS,GAAA,CAAI,IAAI,KAAK,EAAC,UAAW,OAAO,CAAA;AAAA,IACnE,CAAA;AAAA,IACA,KAAA,EAAO,MAAM,QAAA,CAAS,KAAA;AAAM,GAChC;AACJ,CAAA;;;AC5CA,IAAI,QAAA,GAAW,CAAA;AAIf,IAAM,MAAA,GAAS,KAAK,MAAA,EAAO,CAAE,SAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA;AAE7C,IAAM,kBAAkB,MAAc,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,EAAE,QAAQ,CAAA,CAAA;;;ACe7D,IAAM,wBAAwB,CAAC;AAAA,EAClC,MAAA;AAAA,EACA,WAAA;AAAA,EACA,WAAA;AAAA,EACA,OAAA;AAAA,EACA;AACJ,CAAA,KAOuB;AACnB,EAAA,MAAM,OAAA,uBAAc,GAAA,EAOlB;AAEF,EAAA,MAAM,aAAA,GAAgB,CAAC,KAAA,KAAwB;AAC3C,IAAA,IAAI,KAAA,CAAM,MAAA,KAAW,MAAA,CAAO,aAAA,EAAe;AAC3C,IAAA,IAAI,KAAA,CAAM,WAAW,WAAA,EAAa;AAClC,IAAA,MAAM,OAAO,KAAA,CAAM,IAAA;AAEnB,IAAA,IAAI,CAAC,IAAA,IAAQ,IAAA,CAAK,MAAA,KAAW,eAAA,EAAiB;AAC9C,IAAA,IAAI,KAAK,IAAA,KAAA,UAAA,iBAAwC;AAC7C,MAAA,MAAM,WAAW,KAAA,CAAM,IAAA;AAMvB,MAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,GAAA,CAAI,QAAA,CAAS,EAAE,CAAA;AACtC,MAAA,IAAI,CAAC,MAAA,EAAQ;AACb,MAAA,OAAA,CAAQ,MAAA,CAAO,SAAS,EAAE,CAAA;AAC1B,MAAA,YAAA,CAAa,OAAO,KAAK,CAAA;AACzB,MAAA,IAAI,QAAA,CAAS,EAAA,EAAI,MAAA,CAAO,OAAA,CAAQ,SAAS,KAAK,CAAA;AAAA,kBAClC,MAAA,CAAO,QAAA,CAAS,KAAA,IAAS,EAAE,iCAA2B,CAAA;AAClE,MAAA;AAAA,IACJ;AACA,IAAA,IAAI,KAAK,IAAA,KAAA,OAAA,cAAqC;AAC1C,MAAA,MAAM,UAAU,KAAA,CAAM,IAAA;AACtB,MAAA,OAAA,CAAQ,EAAE,IAAA,EAAM,OAAA,CAAQ,MAAM,OAAA,EAAS,OAAA,CAAQ,SAAS,CAAA;AACxD,MAAA;AAAA,IACJ;AACA,IAAA,IAAI,IAAA,CAAK,oCAAwC,UAAA,EAAW;AAAA,EAChE,CAAA;AAEA,EAAA,MAAM,OAAO,CAAC;AAAA,IACV,IAAA;AAAA,IACA,OAAA;AAAA,IACA,SAAA,GAAY;AAAA,GAChB,KAIwB;AACpB,IAAA,IAAI,aAAY,EAAG;AACf,MAAA,OAAO,QAAQ,MAAA,CAAO;AAAA,QAClB,IAAA,EAAA,WAAA;AAAA,QACA,OAAA,EAAS;AAAA,OACU,CAAA;AAAA,IAC3B;AACA,IAAA,MAAM,KAAK,eAAA,EAAgB;AAC3B,IAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACpC,MAAA,MAAM,KAAA,GAAQ,WAAW,MAAM;AAC3B,QAAA,OAAA,CAAQ,OAAO,EAAE,CAAA;AACjB,QAAA,MAAA,CAAO;AAAA,UACH,IAAA,EAAA,UAAA;AAAA,UACA,OAAA,EAAS;AAAA,SACU,CAAA;AAAA,MAC3B,GAAG,SAAS,CAAA;AACZ,MAAA,OAAA,CAAQ,IAAI,EAAA,EAAI,EAAE,OAAA,EAAS,MAAA,EAAQ,OAAO,CAAA;AAC1C,MAAA,MAAA,CAAO,aAAA,EAAe,WAAA;AAAA,QAClB;AAAA,UACI,MAAA,EAAQ,eAAA;AAAA,UACR,OAAA,EAAS,gBAAA;AAAA,UACT,IAAA,EAAA,SAAA;AAAA,UACA,EAAA;AAAA,UACA,IAAA;AAAA,UACA;AAAA,SACJ;AAAA,QACA;AAAA,OACJ;AAAA,IACJ,CAAC,CAAA;AAAA,EACL,CAAA;AAEA,EAAA,OAAO;AAAA,IACH,IAAA;AAAA,IACA,KAAA,EAAO,MAAM,MAAA,CAAO,gBAAA,CAAiB,WAAW,aAAa,CAAA;AAAA,IAC7D,IAAA,EAAM,MAAM,MAAA,CAAO,mBAAA,CAAoB,WAAW,aAAa,CAAA;AAAA,IAC/D,SAAA,EAAW,CAAC,EAAE,OAAA,EAAQ,KAAM;AACxB,MAAA,KAAA,MAAW,GAAG,MAAM,CAAA,IAAK,OAAA,EAAS;AAC9B,QAAA,YAAA,CAAa,OAAO,KAAK,CAAA;AACzB,QAAA,MAAA,CAAO,MAAA,CAAO,EAAE,IAAA,EAAA,WAAA,kBAA4B,OAAA,EAAS,CAAA;AAAA,MACzD;AACA,MAAA,OAAA,CAAQ,KAAA,EAAM;AAAA,IAClB;AAAA,GACJ;AACJ,CAAA;;;ACjHO,IAAM,mBAAmB,CAAC;AAAA,EAC7B,SAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA;AACJ,CAAA,KAYI,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AACnC,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,MAAM,OAAA,GAAU,KAAK,GAAA,EAAI;AAGzB,EAAA,MAAM,SAAS,MAAM;AACjB,IAAA,OAAA,GAAU,IAAA;AACV,IAAA,MAAA,CAAO,mBAAA,CAAoB,QAAQ,OAAO,CAAA;AAC1C,IAAA,kBAAA,EAAmB;AAAA,EACvB,CAAA;AACA,EAAA,MAAM,UAAU,MAAM;AAClB,IAAA,IAAI,OAAA,EAAS;AACb,IAAA,IAAI,IAAA,CAAK,GAAA,EAAI,GAAI,OAAA,GAAU,gBAAA,EAAkB;AACzC,MAAA,MAAA,EAAO;AACP,MAAA,QAAA,EAAS;AACT,MAAA,OAAO,MAAA,CAAO;AAAA,QACV,IAAA,EAAA,aAAA;AAAA,QACA,OAAA,EACI;AAAA,OACe,CAAA;AAAA,IAC3B;AACA,IAAA,SAAA,CACK,IAAA,CAAK;AAAA,MACF,IAAA,EAAA,OAAA;AAAA,MACA,OAAA,EAAS,EAAE,OAAA,EAAS,gBAAA,EAAiB;AAAA,MACrC,SAAA,EAAW;AAAA,KACd,CAAA,CACA,IAAA,CAAK,CAAC,KAAA,KAAU;AACb,MAAA,IAAI,OAAA,EAAS;AACb,MAAA,MAAA,EAAO;AACP,MAAA,MAAM,KAAA,GAAQ,KAAA;AAId,MAAA,MAAA,CAAO,KAAA,GAAQ,KAAA,CAAM,KAAA,IAAS,MAAA,CAAO,KAAA;AACrC,MAAA,MAAA,CAAO,YAAA,GACH,KAAA,CAAM,YAAA,IAAgB,MAAA,CAAO,YAAA;AACjC,MAAA,OAAA,EAAQ;AAAA,IACZ,CAAC,CAAA,CACA,KAAA,CAAM,MAAM,UAAA,CAAW,OAAA,EAAS,cAAc,CAAC,CAAA;AAAA,EACxD,CAAA;AACA,EAAA,MAAM,kBAAA,GAAqB,gBAAA,CAAiB,CAAC,EAAE,QAAO,KAAM;AACxD,IAAA,IAAI,OAAA,EAAS;AACb,IAAA,MAAA,EAAO;AACP,IAAA,MAAA,CAAO;AAAA,MACH,IAAA,EAAA,SAAA;AAAA,MACA,MAAA;AAAA,MACA,OAAA,EACI;AAAA,KACe,CAAA;AAAA,EAC3B,CAAC,CAAA;AACD,EAAA,MAAA,CAAO,gBAAA,CAAiB,QAAQ,OAAO,CAAA;AAEvC,EAAA,UAAA,CAAW,SAAS,cAAc,CAAA;AACtC,CAAC,CAAA;;;ACzDE,IAAM,eAAe,CAAC;AAAA,EACzB,SAAA;AAAA,EACA,GAAA;AAAA,EACA,OAAA,GAAU,gBAAA;AAAA,EACV,MAAA;AAAA,EACA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA;AACJ,CAAA,KAA4C;AACxC,EAAA,MAAM,WAAA,GAAc,IAAI,GAAA,CAAI,OAAO,CAAA,CAAE,MAAA;AACrC,EAAA,MAAM,SAAS,iBAAA,CAAkB;AAAA,IAC7B,KAAK,aAAA,CAAc;AAAA,MACf,OAAA;AAAA,MACA,GAAA;AAAA,MACA,MAAA;AAAA,MACA,WAAA;AAAA,MACA,UAAA;AAAA,MACA,WAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,KACH;AAAA,GACJ,CAAA;AACD,EAAA,MAAM,MAAA,GAAuB,EAAE,KAAA,EAAO,IAAA,EAAM,cAAc,IAAA,EAAK;AAC/D,EAAA,MAAM,GAAA,GAAM,oBAAA,CAAqB,EAAE,MAAA,EAAQ,CAAA;AAC3C,EAAA,IAAI,SAAA,GAAY,KAAA;AAIhB,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,IAAI,aAAA,GAAgB,KAAA;AAGpB,EAAA,MAAM,gBAAA,GAAmB,CACrB,OAAA,KAEA,GAAA,CAAI,EAAA;AAAA,IAAA,SAAA;AAAA,IAAyB,CAAC,OAAA,KAC1B,OAAA,CAAQ,OAAqC;AAAA,GACjD;AAEJ,EAAA,MAAM,aAAa,MAAM;AACrB,IAAA,IAAI,CAAC,OAAA,IAAW,aAAA,IAAiB,SAAA,EAAW;AAC5C,IAAA,aAAA,GAAgB,IAAA;AAEhB,IAAA,SAAA,CAAU,SAAA,CAAU,EAAE,OAAA,EAAS,qBAAA,EAAuB,CAAA;AACtD,IAAA,KAAK,gBAAA,CAAiB;AAAA,MAClB,SAAA;AAAA,MACA,MAAA;AAAA,MACA,MAAA;AAAA;AAAA;AAAA,MAGA,UAAU,MAAM,MAAA;AAAA,MAChB;AAAA,KACH,CAAA,CACI,IAAA;AAAA,MAAK,MACF,IAAI,QAAA,CAAS;AAAA,QACT,IAAA,EAAA,WAAA;AAAA,QACA,OAAA,EAAS;AAAA,OACZ;AAAA,KACL,CACC,KAAA;AAAA,MAAM,CAAC,UACJ,GAAA,CAAI,QAAA,CAAS,EAAE,IAAA,EAAA,OAAA,cAA0B,OAAA,EAAS,OAAO;AAAA,KAC7D,CACC,QAAQ,MAAM;AACX,MAAA,aAAA,GAAgB,KAAA;AAAA,IACpB,CAAC,CAAA;AAAA,EACT,CAAA;AAEA,EAAA,MAAM,YAAY,qBAAA,CAAsB;AAAA,IACpC,MAAA;AAAA,IACA,WAAA;AAAA,IACA,aAAa,MAAM,SAAA;AAAA,IACnB,SAAS,GAAA,CAAI,QAAA;AAAA,IACb,UAAA,EAAY;AAAA,GACf,CAAA;AAED,EAAA,MAAM,UAAU,MAAM;AAClB,IAAA,IAAI,SAAA,EAAW;AAEf,IAAA,KAAK,SAAA,CACA,KAAK,EAAE,IAAA,EAAA,SAAA,gBAAgC,CAAA,CACvC,KAAA,CAAM,MAAM,MAAS,CAAA;AAC1B,IAAA,SAAA,GAAY,IAAA;AACZ,IAAA,SAAA,CAAU,IAAA,EAAK;AACf,IAAA,MAAA,CAAO,MAAA,EAAO;AACd,IAAA,SAAA,CAAU,SAAA,CAAU,EAAE,OAAA,EAAS,2BAAA,EAA6B,CAAA;AAC5D,IAAA,GAAA,CAAI,KAAA,EAAM;AAAA,EACd,CAAA;AAEA,EAAA,MAAM,SAAS,iBAAA,CAAkB;AAAA,IAC7B,SAAA;AAAA,IACA,GAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACH,CAAA;AAED,EAAA,SAAA,CAAU,KAAA,EAAM;AAChB,EAAA,SAAA,CAAU,YAAY,MAAM,CAAA;AAG5B,EAAA,OAAO,gBAAA,CAAiB;AAAA,IACpB,SAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA,EAAU,OAAA;AAAA,IACV;AAAA,GACH,CAAA,CAAE,IAAA,CAAK,MAAM;AACV,IAAA,OAAA,GAAU,IAAA;AACV,IAAA,OAAO,MAAA;AAAA,EACX,CAAC,CAAA;AACL","file":"chunk-Y4CYVADH.js","sourcesContent":["// The viewer's public vocabulary: domain words only, no abbreviation, one options object per\n// method, enums whose values ARE the strings a plain-JavaScript consumer writes.\n\nexport enum EView {\n FREE = 'free',\n FIRST_PERSON = 'firstPerson',\n THIRD_PERSON = 'thirdPerson',\n}\n\n// The background is a VALUE, not a boolean: `none` composites the canvas straight onto your\n// page (put your own background behind the viewer), `environment` shows the map's own decor.\nexport enum EBackgroundType {\n NONE = 'none',\n ENVIRONMENT = 'environment',\n}\n\nexport type Background = {\n type: EBackgroundType;\n};\n\nexport enum EFacing {\n FRONT = 'front',\n BACK = 'back',\n}\n\nexport enum EHandedness {\n RIGHT = 'right',\n LEFT = 'left',\n}\n\n// Which of the two picked items the free view presents when a weapon and a pair of gloves are\n// both on stage.\nexport enum ESubject {\n WEAPON = 'weapon',\n GLOVES = 'gloves',\n}\n\n// The weapon as you describe it — identity, roll, marks — in the market's own words (`seed`\n// and `float`). The viewer owns the road to it: a seed or float alone re-rolls in place, a\n// name alone re-engraves the plate, a new identity re-mounts.\nexport type Weapon = {\n // The inspect certificate (hex), the market door: it carries identity, roll and cosmetics.\n cert?: string;\n // The manual door: the item's definition and finish indices.\n defIndex?: number;\n paintIndex?: number;\n seed?: number;\n float?: number;\n stattrak?: boolean;\n kills?: number;\n name?: string;\n};\n\nexport type Gloves = {\n cert?: string;\n defIndex?: number;\n paintIndex?: number;\n seed?: number;\n float?: number;\n};\n\nexport type Agent = {\n // The inspect certificate (hex): a character travels by cert too, patches included.\n cert?: string;\n defIndex?: number;\n};\n\nexport type Sticker = {\n id: number;\n slot: number;\n wear?: number;\n rotation?: number;\n offsetX?: number;\n offsetY?: number;\n};\n\nexport type Charm = {\n id: number;\n pattern?: number;\n // The display-case family: the sticker sealed inside the slab, by its id.\n wrappedSticker?: number;\n // The charm's placement, as a DELTA from the weapon's default hook in the weapon's model\n // space (Source inches) — the same numbers a free-placement drag settles on. Absent = the\n // authored hook. The state mirror's `charmAnchor` publishes the composed ABSOLUTE.\n offsetX?: number;\n offsetY?: number;\n offsetZ?: number;\n};\n\nexport type Patch = {\n id: number;\n slot: number;\n};\n\n// First-person dials in the GAME's units: `fov` is viewmodel_fov degrees, offsets are\n// viewmodel_offset inches. A new dial is a new FIELD here, never a new method.\nexport type FirstPersonSettings = {\n fov?: number;\n offsetX?: number;\n offsetY?: number;\n offsetZ?: number;\n handedness?: EHandedness;\n};\n\n// The camera's flight target. A sticker is named by its STABLE uid (a certificate may repeat a\n// slot, so a slot is not an identity); the state mirror publishes each sticker's uid.\nexport type FocusTarget =\n | { kind: 'sticker'; uid: string }\n | { kind: 'charm' }\n | { kind: 'patch'; slot: number }\n | null;\n\n// What the free-placement mode is armed on: while armed, pressing that cosmetic in the canvas\n// MOVES it instead of orbiting the item. Null disarms.\nexport type PlacementTarget =\n { kind: 'sticker'; slot: number } | { kind: 'charm' } | null;\n\n// One capability, two layers already intersected for you: `entitled` is what the plan bought\n// (stable for the session), `available` folds in what the current scene can serve (moving;\n// `capabilitieschange` fires when it does).\nexport type Capability = {\n entitled: boolean;\n available: boolean;\n};\n\nexport enum EFeature {\n SELECT_WEAPON = 'selectWeapon',\n SELECT_GLOVES = 'selectGloves',\n SELECT_AGENT = 'selectAgent',\n SELECT_ENVIRONMENT = 'selectEnvironment',\n EDIT_SEED = 'editSeed',\n EDIT_FLOAT = 'editFloat',\n EDIT_STATTRAK = 'editStattrak',\n EDIT_NAMETAG = 'editNametag',\n SELECT_STICKER = 'selectSticker',\n SELECT_CHARM = 'selectCharm',\n SELECT_PATCH = 'selectPatch',\n EDIT_STICKERS = 'editStickers',\n EDIT_CHARMS = 'editCharms',\n PLACE_STICKERS = 'placeStickers',\n PLACE_CHARM = 'placeCharm',\n SCREENSHOT = 'screenshot',\n INSPECT_LINK = 'inspectLink',\n SUBJECT_CONTROLS = 'subjectControls',\n WEAPON_ANIMATIONS = 'weaponAnimations',\n AGENT_ANIMATIONS = 'agentAnimations',\n}\n\nexport type ViewerCapabilities = {\n views: Record<EView, Capability>;\n features: Record<EFeature, Capability>;\n};\n\n// One animation the scene can play, with the one fact you need to offer it honestly: a\n// looping clip is a toggle, a one-shot replays.\nexport type AnimationEntry = {\n name: string;\n loop: boolean;\n};\n\n// The synchronous mirror: the last known truth, re-published whole on every `change` event.\n// Read it without ever awaiting.\nexport type ViewerState = {\n weapon: Weapon | null;\n gloves: Gloves | null;\n agent: Agent | null;\n subject: ESubject | null;\n view: { view: EView; active: boolean };\n environment: string | null;\n background: Background;\n stickers: Array<Sticker & { uid?: string }>;\n charm: Charm | null;\n // Where the charm actually sits, in the form a cert names it: the composed ABSOLUTE\n // position in the weapon's model space, read off the manifest the render is using.\n charmAnchor: [number, number, number] | null;\n patches: Array<Patch>;\n // How many patch slots the mounted character authors — the render's own answer, read off\n // its landed manifest. null while no character manifest has answered (none asked, or still\n // composing): 0 is a real answer, \"this body authors none\".\n patchSlots: number | null;\n playing: boolean;\n animation: string | null;\n animations: Array<AnimationEntry>;\n // The character's celebrations: what the mounted body can play beyond its resting posture,\n // and the one running right now (null at rest). Driven through the same playAnimation /\n // stopAnimation door as the weapon clips.\n agentAnimation: string | null;\n agentAnimations: Array<string>;\n sound: { muted: boolean };\n firstPerson: FirstPersonSettings;\n facing: EFacing;\n placement: PlacementTarget;\n};\n","// The wire format of the embed protocol, client side. Every name here is contract: a change is\n// an addition, never a redefinition.\n\nexport enum EProtocolMessageKind {\n COMMAND = 'command',\n RESPONSE = 'response',\n EVENT = 'event',\n // The viewer page saying \"I just started, greet me\". It carries no data, and the SDK answers\n // it with the handshake — which is what makes a reloaded viewer pick itself back up.\n ANNOUNCE = 'announce',\n}\n\n// The CLOSED error vocabulary. A code's meaning never changes; branch behavior on the code,\n// never on the message text.\nexport enum EErrorCode {\n INVALID_KEY = 'invalid_key',\n ORIGIN_NOT_ALLOWED = 'origin_not_allowed',\n NOT_ENTITLED = 'not_entitled',\n UNSUPPORTED_VIEW = 'unsupported_view',\n UNAVAILABLE_ITEM = 'unavailable_item',\n INVALID_ARGUMENT = 'invalid_argument',\n NOT_READY = 'not_ready',\n RATE_LIMITED = 'rate_limited',\n INTERNAL = 'internal',\n // The embed server refused to serve the viewer at all. `family` on the error says whose\n // problem it is; the frame is left in place, showing the explanation.\n REFUSED = 'refused',\n}\n\n// Whose problem a refusal is, and all you learn of it: your integration mid-setup (a missing\n// key, a page opened outside an iframe), your account no longer opening the door (a revoked or\n// expired key, an undeclared domain, a plan without this surface), or an outage on our side.\nexport enum ERefusalFamily {\n SETUP = 'setup',\n CUT_OFF = 'cut_off',\n OUTAGE = 'outage',\n}\n\nexport enum EProtocolCommand {\n HELLO = 'hello',\n SET_WEAPON = 'setWeapon',\n SET_GLOVES = 'setGloves',\n SET_AGENT = 'setAgent',\n SET_SUBJECT = 'setSubject',\n SET_STICKERS = 'setStickers',\n SET_CHARM = 'setCharm',\n SET_PATCHES = 'setPatches',\n SET_VIEW = 'setView',\n SET_FIRST_PERSON_SETTINGS = 'setFirstPersonSettings',\n SET_FACING = 'setFacing',\n SET_ENVIRONMENT = 'setEnvironment',\n SET_BACKGROUND = 'setBackground',\n FOCUS = 'focus',\n RESET_CAMERA = 'resetCamera',\n PLAY = 'play',\n PAUSE = 'pause',\n PLAY_ANIMATION = 'playAnimation',\n STOP_ANIMATION = 'stopAnimation',\n START_FIRE = 'startFire',\n STOP_FIRE = 'stopFire',\n SET_SOUND = 'setSound',\n SET_LOCALE = 'setLocale',\n ARM_PLACEMENT = 'armPlacement',\n FORWARD_POINTER = 'forwardPointer',\n CAPTURE = 'capture',\n CATALOG_WEAPONS = 'catalogWeapons',\n CATALOG_STICKERS = 'catalogStickers',\n CATALOG_CHARMS = 'catalogCharms',\n CATALOG_PATCHES = 'catalogPatches',\n CATALOG_AGENTS = 'catalogAgents',\n CATALOG_WEAPON_MODELS = 'catalogWeaponModels',\n CATALOG_ENVIRONMENTS = 'catalogEnvironments',\n DESTROY = 'destroy',\n}\n\n// Every event the viewer emits, in glued lowercase: `<thing>change` for a state change (always\n// carrying what changed), a `start`/`move`/`end` triplet for a continuous gesture, a bare noun\n// for an isolated fact.\nexport enum EViewerEvent {\n READY = 'ready',\n ERROR = 'error',\n // The viewer reloaded and was greeted again: everything you had set is gone with the page\n // it lived in, and the scene is blank. Re-apply what you want on screen when you get this.\n RECONNECT = 'reconnect',\n PROGRESS = 'progress',\n UNAVAILABLE = 'unavailable',\n CHANGE = 'change',\n VIEW_CHANGE = 'viewchange',\n SUBJECT_CHANGE = 'subjectchange',\n COSMETICS_CHANGE = 'cosmeticschange',\n ANIMATION_CHANGE = 'animationchange',\n PLAYBACK_CHANGE = 'playbackchange',\n CAPABILITIES_CHANGE = 'capabilitieschange',\n PLACEMENT_START = 'placementstart',\n PLACEMENT_MOVE = 'placementmove',\n PLACEMENT_END = 'placementend',\n // Posted by the embed server's refusal page, never by a booted viewer: the frame is showing\n // a refusal screen. Never reaches `on`: it settles `createViewer` with a `refused` error.\n REFUSED = 'refused',\n}\n\nexport type ViewerError = {\n code: EErrorCode;\n message?: string;\n // With `refused` only: whose problem it is.\n family?: ERefusalFamily;\n};\n\nexport type ProtocolResponseMessage = {\n source: string;\n version: number;\n kind: EProtocolMessageKind.RESPONSE;\n id: string;\n} & ({ ok: true; value?: unknown } | { ok: false; error: ViewerError });\n\nexport type ProtocolEventMessage = {\n source: string;\n version: number;\n kind: EProtocolMessageKind.EVENT;\n name: EViewerEvent;\n payload?: unknown;\n};\n","// The wire identity of every message crossing the iframe boundary. This file is the PUBLIC\n// twin of the render service's own declaration: the package cannot depend on private code, so\n// the wire format is declared on both sides and a conformity bench proves they never drift.\nexport const PROTOCOL_SOURCE = 'vskin-render';\n\n// One dial only: the protocol version this package ships is the version it speaks — never\n// overridden at run time. The service supports the current version and the previous one.\nexport const PROTOCOL_VERSION = 1;\n","// Where the viewer lives when the integration does not say otherwise.\nexport const DEFAULT_BASE_URL = 'https://render.vskin.gg';\n\n// The embed page answers the handshake as soon as its bridge is up — retried because the page\n// boots asynchronously behind the iframe's load event.\nexport const HELLO_RETRY_MS = 300;\nexport const HELLO_TIMEOUT_MS = 15000;\n\n// A command that never comes back (the page died, the network broke) must not hang a client's\n// await forever.\nexport const COMMAND_TIMEOUT_MS = 30000;\n\n// The command Steam itself serves in its inventory and market payloads. The space is\n// percent-encoded on purpose: a literal space truncates the steam:// command.\nexport const CS2_INSPECT_LINK_PREFIX =\n 'steam://run/730//+csgo_econ_action_preview%20';\n","import type { Background, Weapon } from '@/contract/interfaces';\nimport { EBackgroundType } from '@/contract/interfaces';\n\n// The embed page's URL grammar, written once: the boot seed rides the URL, everything after\n// boot rides the protocol.\nexport const buildEmbedUrl = ({\n baseUrl,\n key,\n weapon,\n environment,\n background,\n interactive,\n autoplay,\n locale,\n}: {\n baseUrl: string;\n key?: string;\n weapon?: Weapon;\n environment?: string;\n background?: Background;\n interactive?: boolean;\n autoplay?: boolean;\n locale?: string;\n}): string => {\n const query = new URLSearchParams();\n if (key) query.set('key', key);\n if (weapon?.cert) {\n query.set('cert', weapon.cert);\n } else if (\n weapon &&\n typeof weapon.defIndex === 'number' &&\n typeof weapon.paintIndex === 'number'\n ) {\n query.set('def', String(weapon.defIndex));\n query.set('paint', String(weapon.paintIndex));\n if (weapon.seed !== undefined) query.set('seed', String(weapon.seed));\n if (weapon.float !== undefined)\n query.set('float', String(weapon.float));\n if (weapon.stattrak) query.set('stattrak', '1');\n if (weapon.kills !== undefined)\n query.set('kills', String(weapon.kills));\n if (weapon.name !== undefined) query.set('name', weapon.name);\n }\n if (environment) query.set('env', environment);\n if (background?.type === EBackgroundType.NONE)\n query.set('backdrop', 'false');\n if (interactive === false) query.set('interactive', 'false');\n if (autoplay) query.set('autoplay', '1');\n if (locale) query.set('lang', locale);\n const suffix = query.toString();\n return `${baseUrl}/v1/sdk${suffix ? `?${suffix}` : ''}`;\n};\n","import type { ViewerCapabilities, ViewerState } from '@/contract/interfaces';\nimport type { Viewer, ViewerMirror } from '@/lib/interfaces';\nimport type { ViewerEventHub } from '@/lib/helpers/createViewerEventHub';\nimport type { ViewerTransport } from '@/lib/helpers/createViewerTransport';\nimport { EProtocolCommand } from '@/protocol/interfaces';\n\n// The typed handle the integrator holds: every method one options object, one command, one\n// promise. The state and capability getters read the mirror synchronously — never an await.\nexport const buildViewerHandle = ({\n transport,\n hub,\n mirror,\n iframe,\n destroy,\n}: {\n transport: ViewerTransport;\n hub: ViewerEventHub;\n mirror: ViewerMirror;\n iframe: HTMLIFrameElement;\n destroy: () => void;\n}): Viewer => {\n const command = (\n name: EProtocolCommand,\n payload?: unknown,\n ): Promise<void> => transport.send({ name, payload }).then(() => undefined);\n\n return {\n get state() {\n return mirror.state as ViewerState;\n },\n get capabilities() {\n return mirror.capabilities as ViewerCapabilities;\n },\n on: (event, handler) => hub.on(event, handler),\n setWeapon: (args) => command(EProtocolCommand.SET_WEAPON, args),\n setGloves: (args) => command(EProtocolCommand.SET_GLOVES, args),\n setAgent: (args) => command(EProtocolCommand.SET_AGENT, args),\n setSubject: (args) => command(EProtocolCommand.SET_SUBJECT, args),\n setStickers: (args) => command(EProtocolCommand.SET_STICKERS, args),\n setCharm: (args) => command(EProtocolCommand.SET_CHARM, args),\n setPatches: (args) => command(EProtocolCommand.SET_PATCHES, args),\n setView: (args) => command(EProtocolCommand.SET_VIEW, args),\n setFirstPersonSettings: (args) =>\n command(EProtocolCommand.SET_FIRST_PERSON_SETTINGS, args),\n setFacing: (args) => command(EProtocolCommand.SET_FACING, args),\n setEnvironment: (args) =>\n command(EProtocolCommand.SET_ENVIRONMENT, args),\n setBackground: (args) => command(EProtocolCommand.SET_BACKGROUND, args),\n setSound: (args) => command(EProtocolCommand.SET_SOUND, args),\n setLocale: (args) => command(EProtocolCommand.SET_LOCALE, args),\n focus: (args) => command(EProtocolCommand.FOCUS, args),\n resetCamera: () => command(EProtocolCommand.RESET_CAMERA),\n play: () => command(EProtocolCommand.PLAY),\n pause: () => command(EProtocolCommand.PAUSE),\n playAnimation: (args) => command(EProtocolCommand.PLAY_ANIMATION, args),\n stopAnimation: () => command(EProtocolCommand.STOP_ANIMATION),\n startFire: () => command(EProtocolCommand.START_FIRE),\n stopFire: () => command(EProtocolCommand.STOP_FIRE),\n armPlacement: (args) => command(EProtocolCommand.ARM_PLACEMENT, args),\n forwardPointer: ({ x, y, phase }) => {\n const rect = iframe.getBoundingClientRect();\n return command(EProtocolCommand.FORWARD_POINTER, {\n x: x - rect.left,\n y: y - rect.top,\n phase,\n });\n },\n capture: () =>\n transport\n .send({ name: EProtocolCommand.CAPTURE })\n .then((value) => (value as { image: string }).image),\n catalog: {\n weapons: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_WEAPONS,\n payload: query,\n }),\n stickers: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_STICKERS,\n payload: query,\n }),\n charms: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_CHARMS,\n payload: query,\n }),\n patches: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_PATCHES,\n payload: query,\n }),\n agents: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_AGENTS,\n payload: query,\n }),\n weaponModels: () =>\n transport.send({\n name: EProtocolCommand.CATALOG_WEAPON_MODELS,\n }),\n environments: () =>\n transport.send({\n name: EProtocolCommand.CATALOG_ENVIRONMENTS,\n }),\n },\n destroy,\n };\n};\n","// The viewer's frame: an implementation detail the integrator never touches, filling the\n// container it is handed.\nexport const createEmbedIframe = ({\n src,\n}: {\n src: string;\n}): HTMLIFrameElement => {\n const iframe = document.createElement('iframe');\n iframe.src = src;\n iframe.style.width = '100%';\n iframe.style.height = '100%';\n iframe.style.display = 'block';\n iframe.style.border = '0';\n // Browsers composite a dark-scheme iframe on a light host as OPAQUE: pinning the scheme is\n // what keeps the transparent background actually transparent. You never have to know this.\n iframe.style.colorScheme = 'normal';\n iframe.allow = 'fullscreen';\n return iframe;\n};\n","import type { ViewerCapabilities, ViewerState } from '@/contract/interfaces';\nimport type { ViewerEventHandler, ViewerMirror } from '@/lib/interfaces';\nimport { EViewerEvent } from '@/protocol/interfaces';\n\nexport type ViewerEventHub = {\n on: (event: string, handler: ViewerEventHandler) => () => void;\n dispatch: ({ name, payload }: { name: string; payload: unknown }) => void;\n clear: () => void;\n};\n\n// The subscription side of the viewer, and the mirror's one writer: a `change` carries the\n// whole state, `capabilitieschange` the whole capability map — absorbed before the handlers\n// run, so a handler reading `viewer.state` already sees the truth it was told about.\nexport const createViewerEventHub = ({\n mirror,\n}: {\n mirror: ViewerMirror;\n}): ViewerEventHub => {\n const handlers = new Map<string, Set<ViewerEventHandler>>();\n\n return {\n on: (event, handler) => {\n const set = handlers.get(event) ?? new Set();\n set.add(handler);\n handlers.set(event, set);\n return () => {\n set.delete(handler);\n };\n },\n dispatch: ({ name, payload }) => {\n if (name === EViewerEvent.CHANGE) {\n const change = payload as { state?: ViewerState } | undefined;\n if (change?.state) mirror.state = change.state;\n }\n if (name === EViewerEvent.CAPABILITIES_CHANGE) {\n const change = payload as\n { capabilities?: ViewerCapabilities } | undefined;\n if (change?.capabilities)\n mirror.capabilities = change.capabilities;\n }\n for (const handler of handlers.get(name) ?? []) handler(payload);\n },\n clear: () => handlers.clear(),\n };\n};\n","let sequence = 0;\n\n// Correlation ids only need to be unique within one page's lifetime; the random prefix keeps\n// two viewers on the same page from ever colliding.\nconst prefix = Math.random().toString(36).slice(2, 8);\n\nexport const createCommandId = (): string => `${prefix}-${++sequence}`;\n","import { COMMAND_TIMEOUT_MS } from '@/lib/constants';\nimport { createCommandId } from '@/lib/helpers/createCommandId';\nimport { PROTOCOL_SOURCE, PROTOCOL_VERSION } from '@/protocol/constants';\nimport { EErrorCode, EProtocolMessageKind } from '@/protocol/interfaces';\nimport type { ViewerError } from '@/protocol/interfaces';\n\nexport type ViewerTransport = {\n send: (args: {\n name: string;\n payload?: unknown;\n timeoutMs?: number;\n }) => Promise<unknown>;\n start: () => void;\n stop: () => void;\n // Settles every pending command with the given refusal — the teardown's last word.\n rejectAll: (args: { message: string }) => void;\n};\n\n// The wire side of the viewer: posts commands to the iframe at its precise origin, correlates\n// the one response each command gets, forwards events up. Everything else on the shared\n// `window` is ignored — responses are accepted only from OUR iframe's window AND origin.\nexport const createViewerTransport = ({\n iframe,\n embedOrigin,\n isDestroyed,\n onEvent,\n onAnnounce,\n}: {\n iframe: HTMLIFrameElement;\n embedOrigin: string;\n isDestroyed: () => boolean;\n onEvent: ({ name, payload }: { name: string; payload: unknown }) => void;\n // The page saying it just started. Fires on its first boot and again on every reload.\n onAnnounce: () => void;\n}): ViewerTransport => {\n const pending = new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: ViewerError) => void;\n timer: ReturnType<typeof setTimeout>;\n }\n >();\n\n const handleMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n if (event.origin !== embedOrigin) return;\n const data = event.data as\n { source?: unknown; kind?: unknown } | null | undefined;\n if (!data || data.source !== PROTOCOL_SOURCE) return;\n if (data.kind === EProtocolMessageKind.RESPONSE) {\n const response = event.data as {\n id: string;\n ok: boolean;\n value?: unknown;\n error?: ViewerError;\n };\n const waiter = pending.get(response.id);\n if (!waiter) return;\n pending.delete(response.id);\n clearTimeout(waiter.timer);\n if (response.ok) waiter.resolve(response.value);\n else waiter.reject(response.error ?? { code: EErrorCode.INTERNAL });\n return;\n }\n if (data.kind === EProtocolMessageKind.EVENT) {\n const message = event.data as { name: string; payload?: unknown };\n onEvent({ name: message.name, payload: message.payload });\n return;\n }\n if (data.kind === EProtocolMessageKind.ANNOUNCE) onAnnounce();\n };\n\n const send = ({\n name,\n payload,\n timeoutMs = COMMAND_TIMEOUT_MS,\n }: {\n name: string;\n payload?: unknown;\n timeoutMs?: number;\n }): Promise<unknown> => {\n if (isDestroyed()) {\n return Promise.reject({\n code: EErrorCode.NOT_READY,\n message: 'this viewer was destroyed',\n } satisfies ViewerError);\n }\n const id = createCommandId();\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n pending.delete(id);\n reject({\n code: EErrorCode.INTERNAL,\n message: 'the viewer did not answer',\n } satisfies ViewerError);\n }, timeoutMs);\n pending.set(id, { resolve, reject, timer });\n iframe.contentWindow?.postMessage(\n {\n source: PROTOCOL_SOURCE,\n version: PROTOCOL_VERSION,\n kind: EProtocolMessageKind.COMMAND,\n id,\n name,\n payload,\n },\n embedOrigin,\n );\n });\n };\n\n return {\n send,\n start: () => window.addEventListener('message', handleMessage),\n stop: () => window.removeEventListener('message', handleMessage),\n rejectAll: ({ message }) => {\n for (const [, waiter] of pending) {\n clearTimeout(waiter.timer);\n waiter.reject({ code: EErrorCode.NOT_READY, message });\n }\n pending.clear();\n },\n };\n};\n","import { HELLO_RETRY_MS, HELLO_TIMEOUT_MS } from '@/lib/constants';\nimport type { ViewerMirror } from '@/lib/interfaces';\nimport type { ViewerTransport } from '@/lib/helpers/createViewerTransport';\nimport { PROTOCOL_VERSION } from '@/protocol/constants';\nimport { EErrorCode, EProtocolCommand } from '@/protocol/interfaces';\nimport type { ERefusalFamily, ViewerError } from '@/protocol/interfaces';\nimport type { ViewerCapabilities, ViewerState } from '@/contract/interfaces';\n\n// The handshake: retried until the page's bridge is up, refused whole after the timeout. Its\n// answer seeds the mirror — whatever happened before the client was listening is in it. A page\n// the embed server refused says so itself, and the handshake settles on that word at once.\nexport const performHandshake = ({\n transport,\n iframe,\n mirror,\n onGiveUp,\n subscribeRefusal,\n}: {\n transport: ViewerTransport;\n iframe: HTMLIFrameElement;\n mirror: ViewerMirror;\n // Called once when the handshake is abandoned for want of an answer, before the rejection\n // reaches the caller. NOT called on a refusal: the frame then shows the explanation.\n onGiveUp: () => void;\n // Hands the refusal page's word to the handshake; the returned function unsubscribes.\n subscribeRefusal: (\n handler: (args: { family: ERefusalFamily }) => void,\n ) => () => void;\n}): Promise<void> =>\n new Promise<void>((resolve, reject) => {\n let settled = false;\n const beginAt = Date.now();\n // The listeners outlive nothing: a handshake is run again on every reload, and one\n // left behind per run would pile up on the frame.\n const settle = () => {\n settled = true;\n iframe.removeEventListener('load', attempt);\n unsubscribeRefusal();\n };\n const attempt = () => {\n if (settled) return;\n if (Date.now() - beginAt > HELLO_TIMEOUT_MS) {\n settle();\n onGiveUp();\n return reject({\n code: EErrorCode.INVALID_KEY,\n message:\n 'the viewer never answered: the render service is unreachable, or the frame was blocked before the page could boot',\n } satisfies ViewerError);\n }\n transport\n .send({\n name: EProtocolCommand.HELLO,\n payload: { version: PROTOCOL_VERSION },\n timeoutMs: HELLO_RETRY_MS,\n })\n .then((value) => {\n if (settled) return;\n settle();\n const hello = value as {\n state?: ViewerState;\n capabilities?: ViewerCapabilities;\n };\n mirror.state = hello.state ?? mirror.state;\n mirror.capabilities =\n hello.capabilities ?? mirror.capabilities;\n resolve();\n })\n .catch(() => setTimeout(attempt, HELLO_RETRY_MS));\n };\n const unsubscribeRefusal = subscribeRefusal(({ family }) => {\n if (settled) return;\n settle();\n reject({\n code: EErrorCode.REFUSED,\n family,\n message:\n 'the embed server refused this viewer; its family says whose problem it is',\n } satisfies ViewerError);\n });\n iframe.addEventListener('load', attempt);\n // A cached iframe can be complete before the listener lands.\n setTimeout(attempt, HELLO_RETRY_MS);\n });\n","// The viewer factory: creates the render iframe on the VSkin origin, drives it by messages,\n// and hands back a typed handle plus a catalog client. This file only assembles — the frame,\n// the transport, the event hub, the handle and the handshake are each their own helper.\n\nimport { DEFAULT_BASE_URL } from '@/lib/constants';\nimport { buildEmbedUrl } from '@/lib/helpers/buildEmbedUrl';\nimport { buildViewerHandle } from '@/lib/helpers/buildViewerHandle';\nimport { createEmbedIframe } from '@/lib/helpers/createEmbedIframe';\nimport { createViewerEventHub } from '@/lib/helpers/createViewerEventHub';\nimport { createViewerTransport } from '@/lib/helpers/createViewerTransport';\nimport { performHandshake } from '@/lib/helpers/performHandshake';\nimport type {\n CreateViewerOptions,\n Viewer,\n ViewerMirror,\n} from '@/lib/interfaces';\nimport { EProtocolCommand, EViewerEvent } from '@/protocol/interfaces';\nimport type { ERefusalFamily, ViewerError } from '@/protocol/interfaces';\n\nexport type {\n CatalogQuery,\n CreateViewerOptions,\n Viewer,\n ViewerEventHandler,\n} from '@/lib/interfaces';\n\nexport const createViewer = ({\n container,\n key,\n baseUrl = DEFAULT_BASE_URL,\n weapon,\n environment,\n background,\n interactive,\n autoplay,\n locale,\n}: CreateViewerOptions): Promise<Viewer> => {\n const embedOrigin = new URL(baseUrl).origin;\n const iframe = createEmbedIframe({\n src: buildEmbedUrl({\n baseUrl,\n key,\n weapon,\n environment,\n background,\n interactive,\n autoplay,\n locale,\n }),\n });\n const mirror: ViewerMirror = { state: null, capabilities: null };\n const hub = createViewerEventHub({ mirror });\n let destroyed = false;\n // Whether a bridge has been greeted. What it guards: the FIRST announce is the page's\n // ordinary boot, which the handshake below is already answering; every later one means the\n // frame reloaded under us and the greeting has to be done again.\n let greeted = false;\n let greetingAgain = false;\n\n // The refusal page's word, routed to whichever handshake is waiting for it.\n const subscribeRefusal = (\n handler: (args: { family: ERefusalFamily }) => void,\n ) =>\n hub.on(EViewerEvent.REFUSED, (payload) =>\n handler(payload as { family: ERefusalFamily }),\n );\n\n const greetAgain = () => {\n if (!greeted || greetingAgain || destroyed) return;\n greetingAgain = true;\n // Whatever was in flight died with the page that owed it an answer.\n transport.rejectAll({ message: 'the viewer reloaded' });\n void performHandshake({\n transport,\n iframe,\n mirror,\n // A reload that never answers leaves the viewer as it is: the frame is the\n // integrator's to keep or drop, and an error says so.\n onGiveUp: () => undefined,\n subscribeRefusal,\n })\n .then(() =>\n hub.dispatch({\n name: EViewerEvent.RECONNECT,\n payload: undefined,\n }),\n )\n .catch((error: ViewerError) =>\n hub.dispatch({ name: EViewerEvent.ERROR, payload: error }),\n )\n .finally(() => {\n greetingAgain = false;\n });\n };\n\n const transport = createViewerTransport({\n iframe,\n embedOrigin,\n isDestroyed: () => destroyed,\n onEvent: hub.dispatch,\n onAnnounce: greetAgain,\n });\n\n const destroy = () => {\n if (destroyed) return;\n // The goodbye posts BEFORE the flag flips — a destroyed transport refuses to send.\n void transport\n .send({ name: EProtocolCommand.DESTROY })\n .catch(() => undefined);\n destroyed = true;\n transport.stop();\n iframe.remove();\n transport.rejectAll({ message: 'this viewer was destroyed' });\n hub.clear();\n };\n\n const viewer = buildViewerHandle({\n transport,\n hub,\n mirror,\n iframe,\n destroy,\n });\n\n transport.start();\n container.appendChild(iframe);\n\n // Given up on = destroyed; refused = kept, the frame is showing the visitor why.\n return performHandshake({\n transport,\n iframe,\n mirror,\n onGiveUp: destroy,\n subscribeRefusal,\n }).then(() => {\n greeted = true;\n return viewer;\n });\n};\n"]}
|
package/dist/embed.js
CHANGED
|
@@ -106,8 +106,15 @@ var VskinSdk = (function (exports) {
|
|
|
106
106
|
EErrorCode2["NOT_READY"] = "not_ready";
|
|
107
107
|
EErrorCode2["RATE_LIMITED"] = "rate_limited";
|
|
108
108
|
EErrorCode2["INTERNAL"] = "internal";
|
|
109
|
+
EErrorCode2["REFUSED"] = "refused";
|
|
109
110
|
return EErrorCode2;
|
|
110
111
|
})(EErrorCode || {});
|
|
112
|
+
var ERefusalFamily = /* @__PURE__ */ ((ERefusalFamily2) => {
|
|
113
|
+
ERefusalFamily2["SETUP"] = "setup";
|
|
114
|
+
ERefusalFamily2["CUT_OFF"] = "cut_off";
|
|
115
|
+
ERefusalFamily2["OUTAGE"] = "outage";
|
|
116
|
+
return ERefusalFamily2;
|
|
117
|
+
})(ERefusalFamily || {});
|
|
111
118
|
var EViewerEvent = /* @__PURE__ */ ((EViewerEvent2) => {
|
|
112
119
|
EViewerEvent2["READY"] = "ready";
|
|
113
120
|
EViewerEvent2["ERROR"] = "error";
|
|
@@ -124,6 +131,7 @@ var VskinSdk = (function (exports) {
|
|
|
124
131
|
EViewerEvent2["PLACEMENT_START"] = "placementstart";
|
|
125
132
|
EViewerEvent2["PLACEMENT_MOVE"] = "placementmove";
|
|
126
133
|
EViewerEvent2["PLACEMENT_END"] = "placementend";
|
|
134
|
+
EViewerEvent2["REFUSED"] = "refused";
|
|
127
135
|
return EViewerEvent2;
|
|
128
136
|
})(EViewerEvent || {});
|
|
129
137
|
|
|
@@ -350,13 +358,15 @@ var VskinSdk = (function (exports) {
|
|
|
350
358
|
transport,
|
|
351
359
|
iframe,
|
|
352
360
|
mirror,
|
|
353
|
-
onGiveUp
|
|
361
|
+
onGiveUp,
|
|
362
|
+
subscribeRefusal
|
|
354
363
|
}) => new Promise((resolve, reject) => {
|
|
355
364
|
let settled = false;
|
|
356
365
|
const beginAt = Date.now();
|
|
357
366
|
const settle = () => {
|
|
358
367
|
settled = true;
|
|
359
368
|
iframe.removeEventListener("load", attempt);
|
|
369
|
+
unsubscribeRefusal();
|
|
360
370
|
};
|
|
361
371
|
const attempt = () => {
|
|
362
372
|
if (settled) return;
|
|
@@ -365,7 +375,7 @@ var VskinSdk = (function (exports) {
|
|
|
365
375
|
onGiveUp();
|
|
366
376
|
return reject({
|
|
367
377
|
code: "invalid_key" /* INVALID_KEY */,
|
|
368
|
-
message: "the viewer never answered: the
|
|
378
|
+
message: "the viewer never answered: the render service is unreachable, or the frame was blocked before the page could boot"
|
|
369
379
|
});
|
|
370
380
|
}
|
|
371
381
|
transport.send({
|
|
@@ -382,6 +392,15 @@ var VskinSdk = (function (exports) {
|
|
|
382
392
|
resolve();
|
|
383
393
|
}).catch(() => setTimeout(attempt, HELLO_RETRY_MS));
|
|
384
394
|
};
|
|
395
|
+
const unsubscribeRefusal = subscribeRefusal(({ family }) => {
|
|
396
|
+
if (settled) return;
|
|
397
|
+
settle();
|
|
398
|
+
reject({
|
|
399
|
+
code: "refused" /* REFUSED */,
|
|
400
|
+
family,
|
|
401
|
+
message: "the embed server refused this viewer; its family says whose problem it is"
|
|
402
|
+
});
|
|
403
|
+
});
|
|
385
404
|
iframe.addEventListener("load", attempt);
|
|
386
405
|
setTimeout(attempt, HELLO_RETRY_MS);
|
|
387
406
|
});
|
|
@@ -416,6 +435,10 @@ var VskinSdk = (function (exports) {
|
|
|
416
435
|
let destroyed = false;
|
|
417
436
|
let greeted = false;
|
|
418
437
|
let greetingAgain = false;
|
|
438
|
+
const subscribeRefusal = (handler) => hub.on(
|
|
439
|
+
"refused" /* REFUSED */,
|
|
440
|
+
(payload) => handler(payload)
|
|
441
|
+
);
|
|
419
442
|
const greetAgain = () => {
|
|
420
443
|
if (!greeted || greetingAgain || destroyed) return;
|
|
421
444
|
greetingAgain = true;
|
|
@@ -426,7 +449,8 @@ var VskinSdk = (function (exports) {
|
|
|
426
449
|
mirror,
|
|
427
450
|
// A reload that never answers leaves the viewer as it is: the frame is the
|
|
428
451
|
// integrator's to keep or drop, and an error says so.
|
|
429
|
-
onGiveUp: () => void 0
|
|
452
|
+
onGiveUp: () => void 0,
|
|
453
|
+
subscribeRefusal
|
|
430
454
|
}).then(
|
|
431
455
|
() => hub.dispatch({
|
|
432
456
|
name: "reconnect" /* RECONNECT */,
|
|
@@ -467,7 +491,8 @@ var VskinSdk = (function (exports) {
|
|
|
467
491
|
transport,
|
|
468
492
|
iframe,
|
|
469
493
|
mirror,
|
|
470
|
-
onGiveUp: destroy
|
|
494
|
+
onGiveUp: destroy,
|
|
495
|
+
subscribeRefusal
|
|
471
496
|
}).then(() => {
|
|
472
497
|
greeted = true;
|
|
473
498
|
return viewer;
|
|
@@ -482,6 +507,7 @@ var VskinSdk = (function (exports) {
|
|
|
482
507
|
exports.EFacing = EFacing;
|
|
483
508
|
exports.EFeature = EFeature;
|
|
484
509
|
exports.EHandedness = EHandedness;
|
|
510
|
+
exports.ERefusalFamily = ERefusalFamily;
|
|
485
511
|
exports.ESubject = ESubject;
|
|
486
512
|
exports.EView = EView;
|
|
487
513
|
exports.EViewerEvent = EViewerEvent;
|
package/dist/embed.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/constants.ts","../src/contract/interfaces.ts","../src/lib/helpers/buildEmbedUrl.ts","../src/protocol/interfaces.ts","../src/lib/helpers/buildViewerHandle.ts","../src/lib/helpers/createEmbedIframe.ts","../src/lib/helpers/createViewerEventHub.ts","../src/lib/helpers/createCommandId.ts","../src/protocol/constants.ts","../src/lib/helpers/createViewerTransport.ts","../src/lib/helpers/performHandshake.ts","../src/lib/createViewer.ts","../src/lib/buildInspectLink.ts"],"names":["EView","EBackgroundType","EFacing","EHandedness","ESubject","EFeature","EErrorCode","EViewerEvent"],"mappings":"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","file":"embed.js","sourcesContent":["// Where the viewer lives when the integration does not say otherwise.\nexport const DEFAULT_BASE_URL = 'https://render.vskin.gg';\n\n// The embed page answers the handshake as soon as its bridge is up — retried because the page\n// boots asynchronously behind the iframe's load event.\nexport const HELLO_RETRY_MS = 300;\nexport const HELLO_TIMEOUT_MS = 15000;\n\n// A command that never comes back (the page died, the network broke) must not hang a client's\n// await forever.\nexport const COMMAND_TIMEOUT_MS = 30000;\n\n// The command Steam itself serves in its inventory and market payloads. The space is\n// percent-encoded on purpose: a literal space truncates the steam:// command.\nexport const CS2_INSPECT_LINK_PREFIX =\n 'steam://run/730//+csgo_econ_action_preview%20';\n","// The viewer's public vocabulary: domain words only, no abbreviation, one options object per\n// method, enums whose values ARE the strings a plain-JavaScript consumer writes.\n\nexport enum EView {\n FREE = 'free',\n FIRST_PERSON = 'firstPerson',\n THIRD_PERSON = 'thirdPerson',\n}\n\n// The background is a VALUE, not a boolean: `none` composites the canvas straight onto your\n// page (put your own background behind the viewer), `environment` shows the map's own decor.\nexport enum EBackgroundType {\n NONE = 'none',\n ENVIRONMENT = 'environment',\n}\n\nexport type Background = {\n type: EBackgroundType;\n};\n\nexport enum EFacing {\n FRONT = 'front',\n BACK = 'back',\n}\n\nexport enum EHandedness {\n RIGHT = 'right',\n LEFT = 'left',\n}\n\n// Which of the two picked items the free view presents when a weapon and a pair of gloves are\n// both on stage.\nexport enum ESubject {\n WEAPON = 'weapon',\n GLOVES = 'gloves',\n}\n\n// The weapon as you describe it — identity, roll, marks — in the market's own words (`seed`\n// and `float`). The viewer owns the road to it: a seed or float alone re-rolls in place, a\n// name alone re-engraves the plate, a new identity re-mounts.\nexport type Weapon = {\n // The inspect certificate (hex), the market door: it carries identity, roll and cosmetics.\n cert?: string;\n // The manual door: the item's definition and finish indices.\n defIndex?: number;\n paintIndex?: number;\n seed?: number;\n float?: number;\n stattrak?: boolean;\n kills?: number;\n name?: string;\n};\n\nexport type Gloves = {\n cert?: string;\n defIndex?: number;\n paintIndex?: number;\n seed?: number;\n float?: number;\n};\n\nexport type Agent = {\n // The inspect certificate (hex): a character travels by cert too, patches included.\n cert?: string;\n defIndex?: number;\n};\n\nexport type Sticker = {\n id: number;\n slot: number;\n wear?: number;\n rotation?: number;\n offsetX?: number;\n offsetY?: number;\n};\n\nexport type Charm = {\n id: number;\n pattern?: number;\n // The display-case family: the sticker sealed inside the slab, by its id.\n wrappedSticker?: number;\n // The charm's placement, as a DELTA from the weapon's default hook in the weapon's model\n // space (Source inches) — the same numbers a free-placement drag settles on. Absent = the\n // authored hook. The state mirror's `charmAnchor` publishes the composed ABSOLUTE.\n offsetX?: number;\n offsetY?: number;\n offsetZ?: number;\n};\n\nexport type Patch = {\n id: number;\n slot: number;\n};\n\n// First-person dials in the GAME's units: `fov` is viewmodel_fov degrees, offsets are\n// viewmodel_offset inches. A new dial is a new FIELD here, never a new method.\nexport type FirstPersonSettings = {\n fov?: number;\n offsetX?: number;\n offsetY?: number;\n offsetZ?: number;\n handedness?: EHandedness;\n};\n\n// The camera's flight target. A sticker is named by its STABLE uid (a certificate may repeat a\n// slot, so a slot is not an identity); the state mirror publishes each sticker's uid.\nexport type FocusTarget =\n | { kind: 'sticker'; uid: string }\n | { kind: 'charm' }\n | { kind: 'patch'; slot: number }\n | null;\n\n// What the free-placement mode is armed on: while armed, pressing that cosmetic in the canvas\n// MOVES it instead of orbiting the item. Null disarms.\nexport type PlacementTarget =\n { kind: 'sticker'; slot: number } | { kind: 'charm' } | null;\n\n// One capability, two layers already intersected for you: `entitled` is what the plan bought\n// (stable for the session), `available` folds in what the current scene can serve (moving;\n// `capabilitieschange` fires when it does).\nexport type Capability = {\n entitled: boolean;\n available: boolean;\n};\n\nexport enum EFeature {\n SELECT_WEAPON = 'selectWeapon',\n SELECT_GLOVES = 'selectGloves',\n SELECT_AGENT = 'selectAgent',\n SELECT_ENVIRONMENT = 'selectEnvironment',\n EDIT_SEED = 'editSeed',\n EDIT_FLOAT = 'editFloat',\n EDIT_STATTRAK = 'editStattrak',\n EDIT_NAMETAG = 'editNametag',\n SELECT_STICKER = 'selectSticker',\n SELECT_CHARM = 'selectCharm',\n SELECT_PATCH = 'selectPatch',\n EDIT_STICKERS = 'editStickers',\n EDIT_CHARMS = 'editCharms',\n PLACE_STICKERS = 'placeStickers',\n PLACE_CHARM = 'placeCharm',\n SCREENSHOT = 'screenshot',\n INSPECT_LINK = 'inspectLink',\n SUBJECT_CONTROLS = 'subjectControls',\n WEAPON_ANIMATIONS = 'weaponAnimations',\n AGENT_ANIMATIONS = 'agentAnimations',\n}\n\nexport type ViewerCapabilities = {\n views: Record<EView, Capability>;\n features: Record<EFeature, Capability>;\n};\n\n// One animation the scene can play, with the one fact you need to offer it honestly: a\n// looping clip is a toggle, a one-shot replays.\nexport type AnimationEntry = {\n name: string;\n loop: boolean;\n};\n\n// The synchronous mirror: the last known truth, re-published whole on every `change` event.\n// Read it without ever awaiting.\nexport type ViewerState = {\n weapon: Weapon | null;\n gloves: Gloves | null;\n agent: Agent | null;\n subject: ESubject | null;\n view: { view: EView; active: boolean };\n environment: string | null;\n background: Background;\n stickers: Array<Sticker & { uid?: string }>;\n charm: Charm | null;\n // Where the charm actually sits, in the form a cert names it: the composed ABSOLUTE\n // position in the weapon's model space, read off the manifest the render is using.\n charmAnchor: [number, number, number] | null;\n patches: Array<Patch>;\n // How many patch slots the mounted character authors — the render's own answer, read off\n // its landed manifest. null while no character manifest has answered (none asked, or still\n // composing): 0 is a real answer, \"this body authors none\".\n patchSlots: number | null;\n playing: boolean;\n animation: string | null;\n animations: Array<AnimationEntry>;\n // The character's celebrations: what the mounted body can play beyond its resting posture,\n // and the one running right now (null at rest). Driven through the same playAnimation /\n // stopAnimation door as the weapon clips.\n agentAnimation: string | null;\n agentAnimations: Array<string>;\n sound: { muted: boolean };\n firstPerson: FirstPersonSettings;\n facing: EFacing;\n placement: PlacementTarget;\n};\n","import type { Background, Weapon } from '@/contract/interfaces';\nimport { EBackgroundType } from '@/contract/interfaces';\n\n// The embed page's URL grammar, written once: the boot seed rides the URL, everything after\n// boot rides the protocol.\nexport const buildEmbedUrl = ({\n baseUrl,\n key,\n weapon,\n environment,\n background,\n interactive,\n autoplay,\n locale,\n}: {\n baseUrl: string;\n key?: string;\n weapon?: Weapon;\n environment?: string;\n background?: Background;\n interactive?: boolean;\n autoplay?: boolean;\n locale?: string;\n}): string => {\n const query = new URLSearchParams();\n if (key) query.set('key', key);\n if (weapon?.cert) {\n query.set('cert', weapon.cert);\n } else if (\n weapon &&\n typeof weapon.defIndex === 'number' &&\n typeof weapon.paintIndex === 'number'\n ) {\n query.set('def', String(weapon.defIndex));\n query.set('paint', String(weapon.paintIndex));\n if (weapon.seed !== undefined) query.set('seed', String(weapon.seed));\n if (weapon.float !== undefined)\n query.set('float', String(weapon.float));\n if (weapon.stattrak) query.set('stattrak', '1');\n if (weapon.kills !== undefined)\n query.set('kills', String(weapon.kills));\n if (weapon.name !== undefined) query.set('name', weapon.name);\n }\n if (environment) query.set('env', environment);\n if (background?.type === EBackgroundType.NONE)\n query.set('backdrop', 'false');\n if (interactive === false) query.set('interactive', 'false');\n if (autoplay) query.set('autoplay', '1');\n if (locale) query.set('lang', locale);\n const suffix = query.toString();\n return `${baseUrl}/v1/sdk${suffix ? `?${suffix}` : ''}`;\n};\n","// The wire format of the embed protocol, client side. Every name here is contract: a change is\n// an addition, never a redefinition.\n\nexport enum EProtocolMessageKind {\n COMMAND = 'command',\n RESPONSE = 'response',\n EVENT = 'event',\n // The viewer page saying \"I just started, greet me\". It carries no data, and the SDK answers\n // it with the handshake — which is what makes a reloaded viewer pick itself back up.\n ANNOUNCE = 'announce',\n}\n\n// The CLOSED error vocabulary. A code's meaning never changes; branch behavior on the code,\n// never on the message text.\nexport enum EErrorCode {\n INVALID_KEY = 'invalid_key',\n ORIGIN_NOT_ALLOWED = 'origin_not_allowed',\n NOT_ENTITLED = 'not_entitled',\n UNSUPPORTED_VIEW = 'unsupported_view',\n UNAVAILABLE_ITEM = 'unavailable_item',\n INVALID_ARGUMENT = 'invalid_argument',\n NOT_READY = 'not_ready',\n RATE_LIMITED = 'rate_limited',\n INTERNAL = 'internal',\n}\n\nexport enum EProtocolCommand {\n HELLO = 'hello',\n SET_WEAPON = 'setWeapon',\n SET_GLOVES = 'setGloves',\n SET_AGENT = 'setAgent',\n SET_SUBJECT = 'setSubject',\n SET_STICKERS = 'setStickers',\n SET_CHARM = 'setCharm',\n SET_PATCHES = 'setPatches',\n SET_VIEW = 'setView',\n SET_FIRST_PERSON_SETTINGS = 'setFirstPersonSettings',\n SET_FACING = 'setFacing',\n SET_ENVIRONMENT = 'setEnvironment',\n SET_BACKGROUND = 'setBackground',\n FOCUS = 'focus',\n RESET_CAMERA = 'resetCamera',\n PLAY = 'play',\n PAUSE = 'pause',\n PLAY_ANIMATION = 'playAnimation',\n STOP_ANIMATION = 'stopAnimation',\n START_FIRE = 'startFire',\n STOP_FIRE = 'stopFire',\n SET_SOUND = 'setSound',\n SET_LOCALE = 'setLocale',\n ARM_PLACEMENT = 'armPlacement',\n FORWARD_POINTER = 'forwardPointer',\n CAPTURE = 'capture',\n CATALOG_WEAPONS = 'catalogWeapons',\n CATALOG_STICKERS = 'catalogStickers',\n CATALOG_CHARMS = 'catalogCharms',\n CATALOG_PATCHES = 'catalogPatches',\n CATALOG_AGENTS = 'catalogAgents',\n CATALOG_WEAPON_MODELS = 'catalogWeaponModels',\n CATALOG_ENVIRONMENTS = 'catalogEnvironments',\n DESTROY = 'destroy',\n}\n\n// Every event the viewer emits, in glued lowercase: `<thing>change` for a state change (always\n// carrying what changed), a `start`/`move`/`end` triplet for a continuous gesture, a bare noun\n// for an isolated fact.\nexport enum EViewerEvent {\n READY = 'ready',\n ERROR = 'error',\n // The viewer reloaded and was greeted again: everything you had set is gone with the page\n // it lived in, and the scene is blank. Re-apply what you want on screen when you get this.\n RECONNECT = 'reconnect',\n PROGRESS = 'progress',\n UNAVAILABLE = 'unavailable',\n CHANGE = 'change',\n VIEW_CHANGE = 'viewchange',\n SUBJECT_CHANGE = 'subjectchange',\n COSMETICS_CHANGE = 'cosmeticschange',\n ANIMATION_CHANGE = 'animationchange',\n PLAYBACK_CHANGE = 'playbackchange',\n CAPABILITIES_CHANGE = 'capabilitieschange',\n PLACEMENT_START = 'placementstart',\n PLACEMENT_MOVE = 'placementmove',\n PLACEMENT_END = 'placementend',\n}\n\nexport type ViewerError = {\n code: EErrorCode;\n message?: string;\n};\n\nexport type ProtocolResponseMessage = {\n source: string;\n version: number;\n kind: EProtocolMessageKind.RESPONSE;\n id: string;\n} & ({ ok: true; value?: unknown } | { ok: false; error: ViewerError });\n\nexport type ProtocolEventMessage = {\n source: string;\n version: number;\n kind: EProtocolMessageKind.EVENT;\n name: EViewerEvent;\n payload?: unknown;\n};\n","import type { ViewerCapabilities, ViewerState } from '@/contract/interfaces';\nimport type { Viewer, ViewerMirror } from '@/lib/interfaces';\nimport type { ViewerEventHub } from '@/lib/helpers/createViewerEventHub';\nimport type { ViewerTransport } from '@/lib/helpers/createViewerTransport';\nimport { EProtocolCommand } from '@/protocol/interfaces';\n\n// The typed handle the integrator holds: every method one options object, one command, one\n// promise. The state and capability getters read the mirror synchronously — never an await.\nexport const buildViewerHandle = ({\n transport,\n hub,\n mirror,\n iframe,\n destroy,\n}: {\n transport: ViewerTransport;\n hub: ViewerEventHub;\n mirror: ViewerMirror;\n iframe: HTMLIFrameElement;\n destroy: () => void;\n}): Viewer => {\n const command = (\n name: EProtocolCommand,\n payload?: unknown,\n ): Promise<void> => transport.send({ name, payload }).then(() => undefined);\n\n return {\n get state() {\n return mirror.state as ViewerState;\n },\n get capabilities() {\n return mirror.capabilities as ViewerCapabilities;\n },\n on: (event, handler) => hub.on(event, handler),\n setWeapon: (args) => command(EProtocolCommand.SET_WEAPON, args),\n setGloves: (args) => command(EProtocolCommand.SET_GLOVES, args),\n setAgent: (args) => command(EProtocolCommand.SET_AGENT, args),\n setSubject: (args) => command(EProtocolCommand.SET_SUBJECT, args),\n setStickers: (args) => command(EProtocolCommand.SET_STICKERS, args),\n setCharm: (args) => command(EProtocolCommand.SET_CHARM, args),\n setPatches: (args) => command(EProtocolCommand.SET_PATCHES, args),\n setView: (args) => command(EProtocolCommand.SET_VIEW, args),\n setFirstPersonSettings: (args) =>\n command(EProtocolCommand.SET_FIRST_PERSON_SETTINGS, args),\n setFacing: (args) => command(EProtocolCommand.SET_FACING, args),\n setEnvironment: (args) =>\n command(EProtocolCommand.SET_ENVIRONMENT, args),\n setBackground: (args) => command(EProtocolCommand.SET_BACKGROUND, args),\n setSound: (args) => command(EProtocolCommand.SET_SOUND, args),\n setLocale: (args) => command(EProtocolCommand.SET_LOCALE, args),\n focus: (args) => command(EProtocolCommand.FOCUS, args),\n resetCamera: () => command(EProtocolCommand.RESET_CAMERA),\n play: () => command(EProtocolCommand.PLAY),\n pause: () => command(EProtocolCommand.PAUSE),\n playAnimation: (args) => command(EProtocolCommand.PLAY_ANIMATION, args),\n stopAnimation: () => command(EProtocolCommand.STOP_ANIMATION),\n startFire: () => command(EProtocolCommand.START_FIRE),\n stopFire: () => command(EProtocolCommand.STOP_FIRE),\n armPlacement: (args) => command(EProtocolCommand.ARM_PLACEMENT, args),\n forwardPointer: ({ x, y, phase }) => {\n const rect = iframe.getBoundingClientRect();\n return command(EProtocolCommand.FORWARD_POINTER, {\n x: x - rect.left,\n y: y - rect.top,\n phase,\n });\n },\n capture: () =>\n transport\n .send({ name: EProtocolCommand.CAPTURE })\n .then((value) => (value as { image: string }).image),\n catalog: {\n weapons: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_WEAPONS,\n payload: query,\n }),\n stickers: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_STICKERS,\n payload: query,\n }),\n charms: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_CHARMS,\n payload: query,\n }),\n patches: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_PATCHES,\n payload: query,\n }),\n agents: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_AGENTS,\n payload: query,\n }),\n weaponModels: () =>\n transport.send({\n name: EProtocolCommand.CATALOG_WEAPON_MODELS,\n }),\n environments: () =>\n transport.send({\n name: EProtocolCommand.CATALOG_ENVIRONMENTS,\n }),\n },\n destroy,\n };\n};\n","// The viewer's frame: an implementation detail the integrator never touches, filling the\n// container it is handed.\nexport const createEmbedIframe = ({\n src,\n}: {\n src: string;\n}): HTMLIFrameElement => {\n const iframe = document.createElement('iframe');\n iframe.src = src;\n iframe.style.width = '100%';\n iframe.style.height = '100%';\n iframe.style.display = 'block';\n iframe.style.border = '0';\n // Browsers composite a dark-scheme iframe on a light host as OPAQUE: pinning the scheme is\n // what keeps the transparent background actually transparent. You never have to know this.\n iframe.style.colorScheme = 'normal';\n iframe.allow = 'fullscreen';\n return iframe;\n};\n","import type { ViewerCapabilities, ViewerState } from '@/contract/interfaces';\nimport type { ViewerEventHandler, ViewerMirror } from '@/lib/interfaces';\nimport { EViewerEvent } from '@/protocol/interfaces';\n\nexport type ViewerEventHub = {\n on: (event: string, handler: ViewerEventHandler) => () => void;\n dispatch: ({ name, payload }: { name: string; payload: unknown }) => void;\n clear: () => void;\n};\n\n// The subscription side of the viewer, and the mirror's one writer: a `change` carries the\n// whole state, `capabilitieschange` the whole capability map — absorbed before the handlers\n// run, so a handler reading `viewer.state` already sees the truth it was told about.\nexport const createViewerEventHub = ({\n mirror,\n}: {\n mirror: ViewerMirror;\n}): ViewerEventHub => {\n const handlers = new Map<string, Set<ViewerEventHandler>>();\n\n return {\n on: (event, handler) => {\n const set = handlers.get(event) ?? new Set();\n set.add(handler);\n handlers.set(event, set);\n return () => {\n set.delete(handler);\n };\n },\n dispatch: ({ name, payload }) => {\n if (name === EViewerEvent.CHANGE) {\n const change = payload as { state?: ViewerState } | undefined;\n if (change?.state) mirror.state = change.state;\n }\n if (name === EViewerEvent.CAPABILITIES_CHANGE) {\n const change = payload as\n { capabilities?: ViewerCapabilities } | undefined;\n if (change?.capabilities)\n mirror.capabilities = change.capabilities;\n }\n for (const handler of handlers.get(name) ?? []) handler(payload);\n },\n clear: () => handlers.clear(),\n };\n};\n","let sequence = 0;\n\n// Correlation ids only need to be unique within one page's lifetime; the random prefix keeps\n// two viewers on the same page from ever colliding.\nconst prefix = Math.random().toString(36).slice(2, 8);\n\nexport const createCommandId = (): string => `${prefix}-${++sequence}`;\n","// The wire identity of every message crossing the iframe boundary. This file is the PUBLIC\n// twin of the render service's own declaration: the package cannot depend on private code, so\n// the wire format is declared on both sides and a conformity bench proves they never drift.\nexport const PROTOCOL_SOURCE = 'vskin-render';\n\n// One dial only: the protocol version this package ships is the version it speaks — never\n// overridden at run time. The service supports the current version and the previous one.\nexport const PROTOCOL_VERSION = 1;\n","import { COMMAND_TIMEOUT_MS } from '@/lib/constants';\nimport { createCommandId } from '@/lib/helpers/createCommandId';\nimport { PROTOCOL_SOURCE, PROTOCOL_VERSION } from '@/protocol/constants';\nimport { EErrorCode, EProtocolMessageKind } from '@/protocol/interfaces';\nimport type { ViewerError } from '@/protocol/interfaces';\n\nexport type ViewerTransport = {\n send: (args: {\n name: string;\n payload?: unknown;\n timeoutMs?: number;\n }) => Promise<unknown>;\n start: () => void;\n stop: () => void;\n // Settles every pending command with the given refusal — the teardown's last word.\n rejectAll: (args: { message: string }) => void;\n};\n\n// The wire side of the viewer: posts commands to the iframe at its precise origin, correlates\n// the one response each command gets, forwards events up. Everything else on the shared\n// `window` is ignored — responses are accepted only from OUR iframe's window AND origin.\nexport const createViewerTransport = ({\n iframe,\n embedOrigin,\n isDestroyed,\n onEvent,\n onAnnounce,\n}: {\n iframe: HTMLIFrameElement;\n embedOrigin: string;\n isDestroyed: () => boolean;\n onEvent: ({ name, payload }: { name: string; payload: unknown }) => void;\n // The page saying it just started. Fires on its first boot and again on every reload.\n onAnnounce: () => void;\n}): ViewerTransport => {\n const pending = new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: ViewerError) => void;\n timer: ReturnType<typeof setTimeout>;\n }\n >();\n\n const handleMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n if (event.origin !== embedOrigin) return;\n const data = event.data as\n { source?: unknown; kind?: unknown } | null | undefined;\n if (!data || data.source !== PROTOCOL_SOURCE) return;\n if (data.kind === EProtocolMessageKind.RESPONSE) {\n const response = event.data as {\n id: string;\n ok: boolean;\n value?: unknown;\n error?: ViewerError;\n };\n const waiter = pending.get(response.id);\n if (!waiter) return;\n pending.delete(response.id);\n clearTimeout(waiter.timer);\n if (response.ok) waiter.resolve(response.value);\n else waiter.reject(response.error ?? { code: EErrorCode.INTERNAL });\n return;\n }\n if (data.kind === EProtocolMessageKind.EVENT) {\n const message = event.data as { name: string; payload?: unknown };\n onEvent({ name: message.name, payload: message.payload });\n return;\n }\n if (data.kind === EProtocolMessageKind.ANNOUNCE) onAnnounce();\n };\n\n const send = ({\n name,\n payload,\n timeoutMs = COMMAND_TIMEOUT_MS,\n }: {\n name: string;\n payload?: unknown;\n timeoutMs?: number;\n }): Promise<unknown> => {\n if (isDestroyed()) {\n return Promise.reject({\n code: EErrorCode.NOT_READY,\n message: 'this viewer was destroyed',\n } satisfies ViewerError);\n }\n const id = createCommandId();\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n pending.delete(id);\n reject({\n code: EErrorCode.INTERNAL,\n message: 'the viewer did not answer',\n } satisfies ViewerError);\n }, timeoutMs);\n pending.set(id, { resolve, reject, timer });\n iframe.contentWindow?.postMessage(\n {\n source: PROTOCOL_SOURCE,\n version: PROTOCOL_VERSION,\n kind: EProtocolMessageKind.COMMAND,\n id,\n name,\n payload,\n },\n embedOrigin,\n );\n });\n };\n\n return {\n send,\n start: () => window.addEventListener('message', handleMessage),\n stop: () => window.removeEventListener('message', handleMessage),\n rejectAll: ({ message }) => {\n for (const [, waiter] of pending) {\n clearTimeout(waiter.timer);\n waiter.reject({ code: EErrorCode.NOT_READY, message });\n }\n pending.clear();\n },\n };\n};\n","import { HELLO_RETRY_MS, HELLO_TIMEOUT_MS } from '@/lib/constants';\nimport type { ViewerMirror } from '@/lib/interfaces';\nimport type { ViewerTransport } from '@/lib/helpers/createViewerTransport';\nimport { PROTOCOL_VERSION } from '@/protocol/constants';\nimport { EErrorCode, EProtocolCommand } from '@/protocol/interfaces';\nimport type { ViewerError } from '@/protocol/interfaces';\nimport type { ViewerCapabilities, ViewerState } from '@/contract/interfaces';\n\n// The handshake: retried until the page's bridge is up, refused whole after the timeout. Its\n// answer seeds the mirror — whatever happened before the client was listening is in it.\nexport const performHandshake = ({\n transport,\n iframe,\n mirror,\n onGiveUp,\n}: {\n transport: ViewerTransport;\n iframe: HTMLIFrameElement;\n mirror: ViewerMirror;\n // Called once when the handshake is abandoned, before the rejection reaches the caller.\n onGiveUp: () => void;\n}): Promise<void> =>\n new Promise<void>((resolve, reject) => {\n let settled = false;\n const beginAt = Date.now();\n // The listener outlives nothing: a handshake is run again on every reload, and one\n // left behind per run would pile up on the frame.\n const settle = () => {\n settled = true;\n iframe.removeEventListener('load', attempt);\n };\n const attempt = () => {\n if (settled) return;\n if (Date.now() - beginAt > HELLO_TIMEOUT_MS) {\n settle();\n onGiveUp();\n return reject({\n code: EErrorCode.INVALID_KEY,\n message:\n 'the viewer never answered: the key was refused, this domain is not declared for it, or the render service is unreachable',\n } satisfies ViewerError);\n }\n transport\n .send({\n name: EProtocolCommand.HELLO,\n payload: { version: PROTOCOL_VERSION },\n timeoutMs: HELLO_RETRY_MS,\n })\n .then((value) => {\n if (settled) return;\n settle();\n const hello = value as {\n state?: ViewerState;\n capabilities?: ViewerCapabilities;\n };\n mirror.state = hello.state ?? mirror.state;\n mirror.capabilities =\n hello.capabilities ?? mirror.capabilities;\n resolve();\n })\n .catch(() => setTimeout(attempt, HELLO_RETRY_MS));\n };\n iframe.addEventListener('load', attempt);\n // A cached iframe can be complete before the listener lands.\n setTimeout(attempt, HELLO_RETRY_MS);\n });\n","// The viewer factory: creates the render iframe on the VSkin origin, drives it by messages,\n// and hands back a typed handle plus a catalog client. This file only assembles — the frame,\n// the transport, the event hub, the handle and the handshake are each their own helper.\n\nimport { DEFAULT_BASE_URL } from '@/lib/constants';\nimport { buildEmbedUrl } from '@/lib/helpers/buildEmbedUrl';\nimport { buildViewerHandle } from '@/lib/helpers/buildViewerHandle';\nimport { createEmbedIframe } from '@/lib/helpers/createEmbedIframe';\nimport { createViewerEventHub } from '@/lib/helpers/createViewerEventHub';\nimport { createViewerTransport } from '@/lib/helpers/createViewerTransport';\nimport { performHandshake } from '@/lib/helpers/performHandshake';\nimport type {\n CreateViewerOptions,\n Viewer,\n ViewerMirror,\n} from '@/lib/interfaces';\nimport { EProtocolCommand, EViewerEvent } from '@/protocol/interfaces';\nimport type { ViewerError } from '@/protocol/interfaces';\n\nexport type {\n CatalogQuery,\n CreateViewerOptions,\n Viewer,\n ViewerEventHandler,\n} from '@/lib/interfaces';\n\nexport const createViewer = ({\n container,\n key,\n baseUrl = DEFAULT_BASE_URL,\n weapon,\n environment,\n background,\n interactive,\n autoplay,\n locale,\n}: CreateViewerOptions): Promise<Viewer> => {\n const embedOrigin = new URL(baseUrl).origin;\n const iframe = createEmbedIframe({\n src: buildEmbedUrl({\n baseUrl,\n key,\n weapon,\n environment,\n background,\n interactive,\n autoplay,\n locale,\n }),\n });\n const mirror: ViewerMirror = { state: null, capabilities: null };\n const hub = createViewerEventHub({ mirror });\n let destroyed = false;\n // Whether a bridge has been greeted. What it guards: the FIRST announce is the page's\n // ordinary boot, which the handshake below is already answering; every later one means the\n // frame reloaded under us and the greeting has to be done again.\n let greeted = false;\n let greetingAgain = false;\n\n const greetAgain = () => {\n if (!greeted || greetingAgain || destroyed) return;\n greetingAgain = true;\n // Whatever was in flight died with the page that owed it an answer.\n transport.rejectAll({ message: 'the viewer reloaded' });\n void performHandshake({\n transport,\n iframe,\n mirror,\n // A reload that never answers leaves the viewer as it is: the frame is the\n // integrator's to keep or drop, and an error says so.\n onGiveUp: () => undefined,\n })\n .then(() =>\n hub.dispatch({\n name: EViewerEvent.RECONNECT,\n payload: undefined,\n }),\n )\n .catch((error: ViewerError) =>\n hub.dispatch({ name: EViewerEvent.ERROR, payload: error }),\n )\n .finally(() => {\n greetingAgain = false;\n });\n };\n\n const transport = createViewerTransport({\n iframe,\n embedOrigin,\n isDestroyed: () => destroyed,\n onEvent: hub.dispatch,\n onAnnounce: greetAgain,\n });\n\n const destroy = () => {\n if (destroyed) return;\n // The goodbye posts BEFORE the flag flips — a destroyed transport refuses to send.\n void transport\n .send({ name: EProtocolCommand.DESTROY })\n .catch(() => undefined);\n destroyed = true;\n transport.stop();\n iframe.remove();\n transport.rejectAll({ message: 'this viewer was destroyed' });\n hub.clear();\n };\n\n const viewer = buildViewerHandle({\n transport,\n hub,\n mirror,\n iframe,\n destroy,\n });\n\n transport.start();\n container.appendChild(iframe);\n\n return performHandshake({\n transport,\n iframe,\n mirror,\n onGiveUp: destroy,\n }).then(() => {\n greeted = true;\n return viewer;\n });\n};\n","import { CS2_INSPECT_LINK_PREFIX } from '@/lib/constants';\n\n// The steam:// inspect link for an item certificate. Pure and instant — the certificate IS the\n// link's payload, so this never talks to the viewer and works with no viewer mounted at all.\nexport const buildInspectLink = ({ cert }: { cert: string }): string =>\n `${CS2_INSPECT_LINK_PREFIX}${cert}`;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/constants.ts","../src/contract/interfaces.ts","../src/lib/helpers/buildEmbedUrl.ts","../src/protocol/interfaces.ts","../src/lib/helpers/buildViewerHandle.ts","../src/lib/helpers/createEmbedIframe.ts","../src/lib/helpers/createViewerEventHub.ts","../src/lib/helpers/createCommandId.ts","../src/protocol/constants.ts","../src/lib/helpers/createViewerTransport.ts","../src/lib/helpers/performHandshake.ts","../src/lib/createViewer.ts","../src/lib/buildInspectLink.ts"],"names":["EView","EBackgroundType","EFacing","EHandedness","ESubject","EFeature","EErrorCode","ERefusalFamily","EViewerEvent"],"mappings":"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","file":"embed.js","sourcesContent":["// Where the viewer lives when the integration does not say otherwise.\nexport const DEFAULT_BASE_URL = 'https://render.vskin.gg';\n\n// The embed page answers the handshake as soon as its bridge is up — retried because the page\n// boots asynchronously behind the iframe's load event.\nexport const HELLO_RETRY_MS = 300;\nexport const HELLO_TIMEOUT_MS = 15000;\n\n// A command that never comes back (the page died, the network broke) must not hang a client's\n// await forever.\nexport const COMMAND_TIMEOUT_MS = 30000;\n\n// The command Steam itself serves in its inventory and market payloads. The space is\n// percent-encoded on purpose: a literal space truncates the steam:// command.\nexport const CS2_INSPECT_LINK_PREFIX =\n 'steam://run/730//+csgo_econ_action_preview%20';\n","// The viewer's public vocabulary: domain words only, no abbreviation, one options object per\n// method, enums whose values ARE the strings a plain-JavaScript consumer writes.\n\nexport enum EView {\n FREE = 'free',\n FIRST_PERSON = 'firstPerson',\n THIRD_PERSON = 'thirdPerson',\n}\n\n// The background is a VALUE, not a boolean: `none` composites the canvas straight onto your\n// page (put your own background behind the viewer), `environment` shows the map's own decor.\nexport enum EBackgroundType {\n NONE = 'none',\n ENVIRONMENT = 'environment',\n}\n\nexport type Background = {\n type: EBackgroundType;\n};\n\nexport enum EFacing {\n FRONT = 'front',\n BACK = 'back',\n}\n\nexport enum EHandedness {\n RIGHT = 'right',\n LEFT = 'left',\n}\n\n// Which of the two picked items the free view presents when a weapon and a pair of gloves are\n// both on stage.\nexport enum ESubject {\n WEAPON = 'weapon',\n GLOVES = 'gloves',\n}\n\n// The weapon as you describe it — identity, roll, marks — in the market's own words (`seed`\n// and `float`). The viewer owns the road to it: a seed or float alone re-rolls in place, a\n// name alone re-engraves the plate, a new identity re-mounts.\nexport type Weapon = {\n // The inspect certificate (hex), the market door: it carries identity, roll and cosmetics.\n cert?: string;\n // The manual door: the item's definition and finish indices.\n defIndex?: number;\n paintIndex?: number;\n seed?: number;\n float?: number;\n stattrak?: boolean;\n kills?: number;\n name?: string;\n};\n\nexport type Gloves = {\n cert?: string;\n defIndex?: number;\n paintIndex?: number;\n seed?: number;\n float?: number;\n};\n\nexport type Agent = {\n // The inspect certificate (hex): a character travels by cert too, patches included.\n cert?: string;\n defIndex?: number;\n};\n\nexport type Sticker = {\n id: number;\n slot: number;\n wear?: number;\n rotation?: number;\n offsetX?: number;\n offsetY?: number;\n};\n\nexport type Charm = {\n id: number;\n pattern?: number;\n // The display-case family: the sticker sealed inside the slab, by its id.\n wrappedSticker?: number;\n // The charm's placement, as a DELTA from the weapon's default hook in the weapon's model\n // space (Source inches) — the same numbers a free-placement drag settles on. Absent = the\n // authored hook. The state mirror's `charmAnchor` publishes the composed ABSOLUTE.\n offsetX?: number;\n offsetY?: number;\n offsetZ?: number;\n};\n\nexport type Patch = {\n id: number;\n slot: number;\n};\n\n// First-person dials in the GAME's units: `fov` is viewmodel_fov degrees, offsets are\n// viewmodel_offset inches. A new dial is a new FIELD here, never a new method.\nexport type FirstPersonSettings = {\n fov?: number;\n offsetX?: number;\n offsetY?: number;\n offsetZ?: number;\n handedness?: EHandedness;\n};\n\n// The camera's flight target. A sticker is named by its STABLE uid (a certificate may repeat a\n// slot, so a slot is not an identity); the state mirror publishes each sticker's uid.\nexport type FocusTarget =\n | { kind: 'sticker'; uid: string }\n | { kind: 'charm' }\n | { kind: 'patch'; slot: number }\n | null;\n\n// What the free-placement mode is armed on: while armed, pressing that cosmetic in the canvas\n// MOVES it instead of orbiting the item. Null disarms.\nexport type PlacementTarget =\n { kind: 'sticker'; slot: number } | { kind: 'charm' } | null;\n\n// One capability, two layers already intersected for you: `entitled` is what the plan bought\n// (stable for the session), `available` folds in what the current scene can serve (moving;\n// `capabilitieschange` fires when it does).\nexport type Capability = {\n entitled: boolean;\n available: boolean;\n};\n\nexport enum EFeature {\n SELECT_WEAPON = 'selectWeapon',\n SELECT_GLOVES = 'selectGloves',\n SELECT_AGENT = 'selectAgent',\n SELECT_ENVIRONMENT = 'selectEnvironment',\n EDIT_SEED = 'editSeed',\n EDIT_FLOAT = 'editFloat',\n EDIT_STATTRAK = 'editStattrak',\n EDIT_NAMETAG = 'editNametag',\n SELECT_STICKER = 'selectSticker',\n SELECT_CHARM = 'selectCharm',\n SELECT_PATCH = 'selectPatch',\n EDIT_STICKERS = 'editStickers',\n EDIT_CHARMS = 'editCharms',\n PLACE_STICKERS = 'placeStickers',\n PLACE_CHARM = 'placeCharm',\n SCREENSHOT = 'screenshot',\n INSPECT_LINK = 'inspectLink',\n SUBJECT_CONTROLS = 'subjectControls',\n WEAPON_ANIMATIONS = 'weaponAnimations',\n AGENT_ANIMATIONS = 'agentAnimations',\n}\n\nexport type ViewerCapabilities = {\n views: Record<EView, Capability>;\n features: Record<EFeature, Capability>;\n};\n\n// One animation the scene can play, with the one fact you need to offer it honestly: a\n// looping clip is a toggle, a one-shot replays.\nexport type AnimationEntry = {\n name: string;\n loop: boolean;\n};\n\n// The synchronous mirror: the last known truth, re-published whole on every `change` event.\n// Read it without ever awaiting.\nexport type ViewerState = {\n weapon: Weapon | null;\n gloves: Gloves | null;\n agent: Agent | null;\n subject: ESubject | null;\n view: { view: EView; active: boolean };\n environment: string | null;\n background: Background;\n stickers: Array<Sticker & { uid?: string }>;\n charm: Charm | null;\n // Where the charm actually sits, in the form a cert names it: the composed ABSOLUTE\n // position in the weapon's model space, read off the manifest the render is using.\n charmAnchor: [number, number, number] | null;\n patches: Array<Patch>;\n // How many patch slots the mounted character authors — the render's own answer, read off\n // its landed manifest. null while no character manifest has answered (none asked, or still\n // composing): 0 is a real answer, \"this body authors none\".\n patchSlots: number | null;\n playing: boolean;\n animation: string | null;\n animations: Array<AnimationEntry>;\n // The character's celebrations: what the mounted body can play beyond its resting posture,\n // and the one running right now (null at rest). Driven through the same playAnimation /\n // stopAnimation door as the weapon clips.\n agentAnimation: string | null;\n agentAnimations: Array<string>;\n sound: { muted: boolean };\n firstPerson: FirstPersonSettings;\n facing: EFacing;\n placement: PlacementTarget;\n};\n","import type { Background, Weapon } from '@/contract/interfaces';\nimport { EBackgroundType } from '@/contract/interfaces';\n\n// The embed page's URL grammar, written once: the boot seed rides the URL, everything after\n// boot rides the protocol.\nexport const buildEmbedUrl = ({\n baseUrl,\n key,\n weapon,\n environment,\n background,\n interactive,\n autoplay,\n locale,\n}: {\n baseUrl: string;\n key?: string;\n weapon?: Weapon;\n environment?: string;\n background?: Background;\n interactive?: boolean;\n autoplay?: boolean;\n locale?: string;\n}): string => {\n const query = new URLSearchParams();\n if (key) query.set('key', key);\n if (weapon?.cert) {\n query.set('cert', weapon.cert);\n } else if (\n weapon &&\n typeof weapon.defIndex === 'number' &&\n typeof weapon.paintIndex === 'number'\n ) {\n query.set('def', String(weapon.defIndex));\n query.set('paint', String(weapon.paintIndex));\n if (weapon.seed !== undefined) query.set('seed', String(weapon.seed));\n if (weapon.float !== undefined)\n query.set('float', String(weapon.float));\n if (weapon.stattrak) query.set('stattrak', '1');\n if (weapon.kills !== undefined)\n query.set('kills', String(weapon.kills));\n if (weapon.name !== undefined) query.set('name', weapon.name);\n }\n if (environment) query.set('env', environment);\n if (background?.type === EBackgroundType.NONE)\n query.set('backdrop', 'false');\n if (interactive === false) query.set('interactive', 'false');\n if (autoplay) query.set('autoplay', '1');\n if (locale) query.set('lang', locale);\n const suffix = query.toString();\n return `${baseUrl}/v1/sdk${suffix ? `?${suffix}` : ''}`;\n};\n","// The wire format of the embed protocol, client side. Every name here is contract: a change is\n// an addition, never a redefinition.\n\nexport enum EProtocolMessageKind {\n COMMAND = 'command',\n RESPONSE = 'response',\n EVENT = 'event',\n // The viewer page saying \"I just started, greet me\". It carries no data, and the SDK answers\n // it with the handshake — which is what makes a reloaded viewer pick itself back up.\n ANNOUNCE = 'announce',\n}\n\n// The CLOSED error vocabulary. A code's meaning never changes; branch behavior on the code,\n// never on the message text.\nexport enum EErrorCode {\n INVALID_KEY = 'invalid_key',\n ORIGIN_NOT_ALLOWED = 'origin_not_allowed',\n NOT_ENTITLED = 'not_entitled',\n UNSUPPORTED_VIEW = 'unsupported_view',\n UNAVAILABLE_ITEM = 'unavailable_item',\n INVALID_ARGUMENT = 'invalid_argument',\n NOT_READY = 'not_ready',\n RATE_LIMITED = 'rate_limited',\n INTERNAL = 'internal',\n // The embed server refused to serve the viewer at all. `family` on the error says whose\n // problem it is; the frame is left in place, showing the explanation.\n REFUSED = 'refused',\n}\n\n// Whose problem a refusal is, and all you learn of it: your integration mid-setup (a missing\n// key, a page opened outside an iframe), your account no longer opening the door (a revoked or\n// expired key, an undeclared domain, a plan without this surface), or an outage on our side.\nexport enum ERefusalFamily {\n SETUP = 'setup',\n CUT_OFF = 'cut_off',\n OUTAGE = 'outage',\n}\n\nexport enum EProtocolCommand {\n HELLO = 'hello',\n SET_WEAPON = 'setWeapon',\n SET_GLOVES = 'setGloves',\n SET_AGENT = 'setAgent',\n SET_SUBJECT = 'setSubject',\n SET_STICKERS = 'setStickers',\n SET_CHARM = 'setCharm',\n SET_PATCHES = 'setPatches',\n SET_VIEW = 'setView',\n SET_FIRST_PERSON_SETTINGS = 'setFirstPersonSettings',\n SET_FACING = 'setFacing',\n SET_ENVIRONMENT = 'setEnvironment',\n SET_BACKGROUND = 'setBackground',\n FOCUS = 'focus',\n RESET_CAMERA = 'resetCamera',\n PLAY = 'play',\n PAUSE = 'pause',\n PLAY_ANIMATION = 'playAnimation',\n STOP_ANIMATION = 'stopAnimation',\n START_FIRE = 'startFire',\n STOP_FIRE = 'stopFire',\n SET_SOUND = 'setSound',\n SET_LOCALE = 'setLocale',\n ARM_PLACEMENT = 'armPlacement',\n FORWARD_POINTER = 'forwardPointer',\n CAPTURE = 'capture',\n CATALOG_WEAPONS = 'catalogWeapons',\n CATALOG_STICKERS = 'catalogStickers',\n CATALOG_CHARMS = 'catalogCharms',\n CATALOG_PATCHES = 'catalogPatches',\n CATALOG_AGENTS = 'catalogAgents',\n CATALOG_WEAPON_MODELS = 'catalogWeaponModels',\n CATALOG_ENVIRONMENTS = 'catalogEnvironments',\n DESTROY = 'destroy',\n}\n\n// Every event the viewer emits, in glued lowercase: `<thing>change` for a state change (always\n// carrying what changed), a `start`/`move`/`end` triplet for a continuous gesture, a bare noun\n// for an isolated fact.\nexport enum EViewerEvent {\n READY = 'ready',\n ERROR = 'error',\n // The viewer reloaded and was greeted again: everything you had set is gone with the page\n // it lived in, and the scene is blank. Re-apply what you want on screen when you get this.\n RECONNECT = 'reconnect',\n PROGRESS = 'progress',\n UNAVAILABLE = 'unavailable',\n CHANGE = 'change',\n VIEW_CHANGE = 'viewchange',\n SUBJECT_CHANGE = 'subjectchange',\n COSMETICS_CHANGE = 'cosmeticschange',\n ANIMATION_CHANGE = 'animationchange',\n PLAYBACK_CHANGE = 'playbackchange',\n CAPABILITIES_CHANGE = 'capabilitieschange',\n PLACEMENT_START = 'placementstart',\n PLACEMENT_MOVE = 'placementmove',\n PLACEMENT_END = 'placementend',\n // Posted by the embed server's refusal page, never by a booted viewer: the frame is showing\n // a refusal screen. Never reaches `on`: it settles `createViewer` with a `refused` error.\n REFUSED = 'refused',\n}\n\nexport type ViewerError = {\n code: EErrorCode;\n message?: string;\n // With `refused` only: whose problem it is.\n family?: ERefusalFamily;\n};\n\nexport type ProtocolResponseMessage = {\n source: string;\n version: number;\n kind: EProtocolMessageKind.RESPONSE;\n id: string;\n} & ({ ok: true; value?: unknown } | { ok: false; error: ViewerError });\n\nexport type ProtocolEventMessage = {\n source: string;\n version: number;\n kind: EProtocolMessageKind.EVENT;\n name: EViewerEvent;\n payload?: unknown;\n};\n","import type { ViewerCapabilities, ViewerState } from '@/contract/interfaces';\nimport type { Viewer, ViewerMirror } from '@/lib/interfaces';\nimport type { ViewerEventHub } from '@/lib/helpers/createViewerEventHub';\nimport type { ViewerTransport } from '@/lib/helpers/createViewerTransport';\nimport { EProtocolCommand } from '@/protocol/interfaces';\n\n// The typed handle the integrator holds: every method one options object, one command, one\n// promise. The state and capability getters read the mirror synchronously — never an await.\nexport const buildViewerHandle = ({\n transport,\n hub,\n mirror,\n iframe,\n destroy,\n}: {\n transport: ViewerTransport;\n hub: ViewerEventHub;\n mirror: ViewerMirror;\n iframe: HTMLIFrameElement;\n destroy: () => void;\n}): Viewer => {\n const command = (\n name: EProtocolCommand,\n payload?: unknown,\n ): Promise<void> => transport.send({ name, payload }).then(() => undefined);\n\n return {\n get state() {\n return mirror.state as ViewerState;\n },\n get capabilities() {\n return mirror.capabilities as ViewerCapabilities;\n },\n on: (event, handler) => hub.on(event, handler),\n setWeapon: (args) => command(EProtocolCommand.SET_WEAPON, args),\n setGloves: (args) => command(EProtocolCommand.SET_GLOVES, args),\n setAgent: (args) => command(EProtocolCommand.SET_AGENT, args),\n setSubject: (args) => command(EProtocolCommand.SET_SUBJECT, args),\n setStickers: (args) => command(EProtocolCommand.SET_STICKERS, args),\n setCharm: (args) => command(EProtocolCommand.SET_CHARM, args),\n setPatches: (args) => command(EProtocolCommand.SET_PATCHES, args),\n setView: (args) => command(EProtocolCommand.SET_VIEW, args),\n setFirstPersonSettings: (args) =>\n command(EProtocolCommand.SET_FIRST_PERSON_SETTINGS, args),\n setFacing: (args) => command(EProtocolCommand.SET_FACING, args),\n setEnvironment: (args) =>\n command(EProtocolCommand.SET_ENVIRONMENT, args),\n setBackground: (args) => command(EProtocolCommand.SET_BACKGROUND, args),\n setSound: (args) => command(EProtocolCommand.SET_SOUND, args),\n setLocale: (args) => command(EProtocolCommand.SET_LOCALE, args),\n focus: (args) => command(EProtocolCommand.FOCUS, args),\n resetCamera: () => command(EProtocolCommand.RESET_CAMERA),\n play: () => command(EProtocolCommand.PLAY),\n pause: () => command(EProtocolCommand.PAUSE),\n playAnimation: (args) => command(EProtocolCommand.PLAY_ANIMATION, args),\n stopAnimation: () => command(EProtocolCommand.STOP_ANIMATION),\n startFire: () => command(EProtocolCommand.START_FIRE),\n stopFire: () => command(EProtocolCommand.STOP_FIRE),\n armPlacement: (args) => command(EProtocolCommand.ARM_PLACEMENT, args),\n forwardPointer: ({ x, y, phase }) => {\n const rect = iframe.getBoundingClientRect();\n return command(EProtocolCommand.FORWARD_POINTER, {\n x: x - rect.left,\n y: y - rect.top,\n phase,\n });\n },\n capture: () =>\n transport\n .send({ name: EProtocolCommand.CAPTURE })\n .then((value) => (value as { image: string }).image),\n catalog: {\n weapons: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_WEAPONS,\n payload: query,\n }),\n stickers: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_STICKERS,\n payload: query,\n }),\n charms: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_CHARMS,\n payload: query,\n }),\n patches: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_PATCHES,\n payload: query,\n }),\n agents: (query) =>\n transport.send({\n name: EProtocolCommand.CATALOG_AGENTS,\n payload: query,\n }),\n weaponModels: () =>\n transport.send({\n name: EProtocolCommand.CATALOG_WEAPON_MODELS,\n }),\n environments: () =>\n transport.send({\n name: EProtocolCommand.CATALOG_ENVIRONMENTS,\n }),\n },\n destroy,\n };\n};\n","// The viewer's frame: an implementation detail the integrator never touches, filling the\n// container it is handed.\nexport const createEmbedIframe = ({\n src,\n}: {\n src: string;\n}): HTMLIFrameElement => {\n const iframe = document.createElement('iframe');\n iframe.src = src;\n iframe.style.width = '100%';\n iframe.style.height = '100%';\n iframe.style.display = 'block';\n iframe.style.border = '0';\n // Browsers composite a dark-scheme iframe on a light host as OPAQUE: pinning the scheme is\n // what keeps the transparent background actually transparent. You never have to know this.\n iframe.style.colorScheme = 'normal';\n iframe.allow = 'fullscreen';\n return iframe;\n};\n","import type { ViewerCapabilities, ViewerState } from '@/contract/interfaces';\nimport type { ViewerEventHandler, ViewerMirror } from '@/lib/interfaces';\nimport { EViewerEvent } from '@/protocol/interfaces';\n\nexport type ViewerEventHub = {\n on: (event: string, handler: ViewerEventHandler) => () => void;\n dispatch: ({ name, payload }: { name: string; payload: unknown }) => void;\n clear: () => void;\n};\n\n// The subscription side of the viewer, and the mirror's one writer: a `change` carries the\n// whole state, `capabilitieschange` the whole capability map — absorbed before the handlers\n// run, so a handler reading `viewer.state` already sees the truth it was told about.\nexport const createViewerEventHub = ({\n mirror,\n}: {\n mirror: ViewerMirror;\n}): ViewerEventHub => {\n const handlers = new Map<string, Set<ViewerEventHandler>>();\n\n return {\n on: (event, handler) => {\n const set = handlers.get(event) ?? new Set();\n set.add(handler);\n handlers.set(event, set);\n return () => {\n set.delete(handler);\n };\n },\n dispatch: ({ name, payload }) => {\n if (name === EViewerEvent.CHANGE) {\n const change = payload as { state?: ViewerState } | undefined;\n if (change?.state) mirror.state = change.state;\n }\n if (name === EViewerEvent.CAPABILITIES_CHANGE) {\n const change = payload as\n { capabilities?: ViewerCapabilities } | undefined;\n if (change?.capabilities)\n mirror.capabilities = change.capabilities;\n }\n for (const handler of handlers.get(name) ?? []) handler(payload);\n },\n clear: () => handlers.clear(),\n };\n};\n","let sequence = 0;\n\n// Correlation ids only need to be unique within one page's lifetime; the random prefix keeps\n// two viewers on the same page from ever colliding.\nconst prefix = Math.random().toString(36).slice(2, 8);\n\nexport const createCommandId = (): string => `${prefix}-${++sequence}`;\n","// The wire identity of every message crossing the iframe boundary. This file is the PUBLIC\n// twin of the render service's own declaration: the package cannot depend on private code, so\n// the wire format is declared on both sides and a conformity bench proves they never drift.\nexport const PROTOCOL_SOURCE = 'vskin-render';\n\n// One dial only: the protocol version this package ships is the version it speaks — never\n// overridden at run time. The service supports the current version and the previous one.\nexport const PROTOCOL_VERSION = 1;\n","import { COMMAND_TIMEOUT_MS } from '@/lib/constants';\nimport { createCommandId } from '@/lib/helpers/createCommandId';\nimport { PROTOCOL_SOURCE, PROTOCOL_VERSION } from '@/protocol/constants';\nimport { EErrorCode, EProtocolMessageKind } from '@/protocol/interfaces';\nimport type { ViewerError } from '@/protocol/interfaces';\n\nexport type ViewerTransport = {\n send: (args: {\n name: string;\n payload?: unknown;\n timeoutMs?: number;\n }) => Promise<unknown>;\n start: () => void;\n stop: () => void;\n // Settles every pending command with the given refusal — the teardown's last word.\n rejectAll: (args: { message: string }) => void;\n};\n\n// The wire side of the viewer: posts commands to the iframe at its precise origin, correlates\n// the one response each command gets, forwards events up. Everything else on the shared\n// `window` is ignored — responses are accepted only from OUR iframe's window AND origin.\nexport const createViewerTransport = ({\n iframe,\n embedOrigin,\n isDestroyed,\n onEvent,\n onAnnounce,\n}: {\n iframe: HTMLIFrameElement;\n embedOrigin: string;\n isDestroyed: () => boolean;\n onEvent: ({ name, payload }: { name: string; payload: unknown }) => void;\n // The page saying it just started. Fires on its first boot and again on every reload.\n onAnnounce: () => void;\n}): ViewerTransport => {\n const pending = new Map<\n string,\n {\n resolve: (value: unknown) => void;\n reject: (error: ViewerError) => void;\n timer: ReturnType<typeof setTimeout>;\n }\n >();\n\n const handleMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n if (event.origin !== embedOrigin) return;\n const data = event.data as\n { source?: unknown; kind?: unknown } | null | undefined;\n if (!data || data.source !== PROTOCOL_SOURCE) return;\n if (data.kind === EProtocolMessageKind.RESPONSE) {\n const response = event.data as {\n id: string;\n ok: boolean;\n value?: unknown;\n error?: ViewerError;\n };\n const waiter = pending.get(response.id);\n if (!waiter) return;\n pending.delete(response.id);\n clearTimeout(waiter.timer);\n if (response.ok) waiter.resolve(response.value);\n else waiter.reject(response.error ?? { code: EErrorCode.INTERNAL });\n return;\n }\n if (data.kind === EProtocolMessageKind.EVENT) {\n const message = event.data as { name: string; payload?: unknown };\n onEvent({ name: message.name, payload: message.payload });\n return;\n }\n if (data.kind === EProtocolMessageKind.ANNOUNCE) onAnnounce();\n };\n\n const send = ({\n name,\n payload,\n timeoutMs = COMMAND_TIMEOUT_MS,\n }: {\n name: string;\n payload?: unknown;\n timeoutMs?: number;\n }): Promise<unknown> => {\n if (isDestroyed()) {\n return Promise.reject({\n code: EErrorCode.NOT_READY,\n message: 'this viewer was destroyed',\n } satisfies ViewerError);\n }\n const id = createCommandId();\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n pending.delete(id);\n reject({\n code: EErrorCode.INTERNAL,\n message: 'the viewer did not answer',\n } satisfies ViewerError);\n }, timeoutMs);\n pending.set(id, { resolve, reject, timer });\n iframe.contentWindow?.postMessage(\n {\n source: PROTOCOL_SOURCE,\n version: PROTOCOL_VERSION,\n kind: EProtocolMessageKind.COMMAND,\n id,\n name,\n payload,\n },\n embedOrigin,\n );\n });\n };\n\n return {\n send,\n start: () => window.addEventListener('message', handleMessage),\n stop: () => window.removeEventListener('message', handleMessage),\n rejectAll: ({ message }) => {\n for (const [, waiter] of pending) {\n clearTimeout(waiter.timer);\n waiter.reject({ code: EErrorCode.NOT_READY, message });\n }\n pending.clear();\n },\n };\n};\n","import { HELLO_RETRY_MS, HELLO_TIMEOUT_MS } from '@/lib/constants';\nimport type { ViewerMirror } from '@/lib/interfaces';\nimport type { ViewerTransport } from '@/lib/helpers/createViewerTransport';\nimport { PROTOCOL_VERSION } from '@/protocol/constants';\nimport { EErrorCode, EProtocolCommand } from '@/protocol/interfaces';\nimport type { ERefusalFamily, ViewerError } from '@/protocol/interfaces';\nimport type { ViewerCapabilities, ViewerState } from '@/contract/interfaces';\n\n// The handshake: retried until the page's bridge is up, refused whole after the timeout. Its\n// answer seeds the mirror — whatever happened before the client was listening is in it. A page\n// the embed server refused says so itself, and the handshake settles on that word at once.\nexport const performHandshake = ({\n transport,\n iframe,\n mirror,\n onGiveUp,\n subscribeRefusal,\n}: {\n transport: ViewerTransport;\n iframe: HTMLIFrameElement;\n mirror: ViewerMirror;\n // Called once when the handshake is abandoned for want of an answer, before the rejection\n // reaches the caller. NOT called on a refusal: the frame then shows the explanation.\n onGiveUp: () => void;\n // Hands the refusal page's word to the handshake; the returned function unsubscribes.\n subscribeRefusal: (\n handler: (args: { family: ERefusalFamily }) => void,\n ) => () => void;\n}): Promise<void> =>\n new Promise<void>((resolve, reject) => {\n let settled = false;\n const beginAt = Date.now();\n // The listeners outlive nothing: a handshake is run again on every reload, and one\n // left behind per run would pile up on the frame.\n const settle = () => {\n settled = true;\n iframe.removeEventListener('load', attempt);\n unsubscribeRefusal();\n };\n const attempt = () => {\n if (settled) return;\n if (Date.now() - beginAt > HELLO_TIMEOUT_MS) {\n settle();\n onGiveUp();\n return reject({\n code: EErrorCode.INVALID_KEY,\n message:\n 'the viewer never answered: the render service is unreachable, or the frame was blocked before the page could boot',\n } satisfies ViewerError);\n }\n transport\n .send({\n name: EProtocolCommand.HELLO,\n payload: { version: PROTOCOL_VERSION },\n timeoutMs: HELLO_RETRY_MS,\n })\n .then((value) => {\n if (settled) return;\n settle();\n const hello = value as {\n state?: ViewerState;\n capabilities?: ViewerCapabilities;\n };\n mirror.state = hello.state ?? mirror.state;\n mirror.capabilities =\n hello.capabilities ?? mirror.capabilities;\n resolve();\n })\n .catch(() => setTimeout(attempt, HELLO_RETRY_MS));\n };\n const unsubscribeRefusal = subscribeRefusal(({ family }) => {\n if (settled) return;\n settle();\n reject({\n code: EErrorCode.REFUSED,\n family,\n message:\n 'the embed server refused this viewer; its family says whose problem it is',\n } satisfies ViewerError);\n });\n iframe.addEventListener('load', attempt);\n // A cached iframe can be complete before the listener lands.\n setTimeout(attempt, HELLO_RETRY_MS);\n });\n","// The viewer factory: creates the render iframe on the VSkin origin, drives it by messages,\n// and hands back a typed handle plus a catalog client. This file only assembles — the frame,\n// the transport, the event hub, the handle and the handshake are each their own helper.\n\nimport { DEFAULT_BASE_URL } from '@/lib/constants';\nimport { buildEmbedUrl } from '@/lib/helpers/buildEmbedUrl';\nimport { buildViewerHandle } from '@/lib/helpers/buildViewerHandle';\nimport { createEmbedIframe } from '@/lib/helpers/createEmbedIframe';\nimport { createViewerEventHub } from '@/lib/helpers/createViewerEventHub';\nimport { createViewerTransport } from '@/lib/helpers/createViewerTransport';\nimport { performHandshake } from '@/lib/helpers/performHandshake';\nimport type {\n CreateViewerOptions,\n Viewer,\n ViewerMirror,\n} from '@/lib/interfaces';\nimport { EProtocolCommand, EViewerEvent } from '@/protocol/interfaces';\nimport type { ERefusalFamily, ViewerError } from '@/protocol/interfaces';\n\nexport type {\n CatalogQuery,\n CreateViewerOptions,\n Viewer,\n ViewerEventHandler,\n} from '@/lib/interfaces';\n\nexport const createViewer = ({\n container,\n key,\n baseUrl = DEFAULT_BASE_URL,\n weapon,\n environment,\n background,\n interactive,\n autoplay,\n locale,\n}: CreateViewerOptions): Promise<Viewer> => {\n const embedOrigin = new URL(baseUrl).origin;\n const iframe = createEmbedIframe({\n src: buildEmbedUrl({\n baseUrl,\n key,\n weapon,\n environment,\n background,\n interactive,\n autoplay,\n locale,\n }),\n });\n const mirror: ViewerMirror = { state: null, capabilities: null };\n const hub = createViewerEventHub({ mirror });\n let destroyed = false;\n // Whether a bridge has been greeted. What it guards: the FIRST announce is the page's\n // ordinary boot, which the handshake below is already answering; every later one means the\n // frame reloaded under us and the greeting has to be done again.\n let greeted = false;\n let greetingAgain = false;\n\n // The refusal page's word, routed to whichever handshake is waiting for it.\n const subscribeRefusal = (\n handler: (args: { family: ERefusalFamily }) => void,\n ) =>\n hub.on(EViewerEvent.REFUSED, (payload) =>\n handler(payload as { family: ERefusalFamily }),\n );\n\n const greetAgain = () => {\n if (!greeted || greetingAgain || destroyed) return;\n greetingAgain = true;\n // Whatever was in flight died with the page that owed it an answer.\n transport.rejectAll({ message: 'the viewer reloaded' });\n void performHandshake({\n transport,\n iframe,\n mirror,\n // A reload that never answers leaves the viewer as it is: the frame is the\n // integrator's to keep or drop, and an error says so.\n onGiveUp: () => undefined,\n subscribeRefusal,\n })\n .then(() =>\n hub.dispatch({\n name: EViewerEvent.RECONNECT,\n payload: undefined,\n }),\n )\n .catch((error: ViewerError) =>\n hub.dispatch({ name: EViewerEvent.ERROR, payload: error }),\n )\n .finally(() => {\n greetingAgain = false;\n });\n };\n\n const transport = createViewerTransport({\n iframe,\n embedOrigin,\n isDestroyed: () => destroyed,\n onEvent: hub.dispatch,\n onAnnounce: greetAgain,\n });\n\n const destroy = () => {\n if (destroyed) return;\n // The goodbye posts BEFORE the flag flips — a destroyed transport refuses to send.\n void transport\n .send({ name: EProtocolCommand.DESTROY })\n .catch(() => undefined);\n destroyed = true;\n transport.stop();\n iframe.remove();\n transport.rejectAll({ message: 'this viewer was destroyed' });\n hub.clear();\n };\n\n const viewer = buildViewerHandle({\n transport,\n hub,\n mirror,\n iframe,\n destroy,\n });\n\n transport.start();\n container.appendChild(iframe);\n\n // Given up on = destroyed; refused = kept, the frame is showing the visitor why.\n return performHandshake({\n transport,\n iframe,\n mirror,\n onGiveUp: destroy,\n subscribeRefusal,\n }).then(() => {\n greeted = true;\n return viewer;\n });\n};\n","import { CS2_INSPECT_LINK_PREFIX } from '@/lib/constants';\n\n// The steam:// inspect link for an item certificate. Pure and instant — the certificate IS the\n// link's payload, so this never talks to the viewer and works with no viewer mounted at all.\nexport const buildInspectLink = ({ cert }: { cert: string }): string =>\n `${CS2_INSPECT_LINK_PREFIX}${cert}`;\n"]}
|