@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 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 key was refused, this domain is not declared for it, or the render service is unreachable"
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-KADQENVR.js.map
471
- //# sourceMappingURL=chunk-KADQENVR.js.map
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":"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","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 key was refused, this domain is not declared for it, or the render service is unreachable"
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"]}