@skinhub/viewer 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/EMBED.md +442 -0
  2. package/README.md +153 -0
  3. package/dist/SkinViewer.d.ts +18 -0
  4. package/dist/SkinViewer.d.ts.map +1 -0
  5. package/dist/SkinViewer.js +404 -0
  6. package/dist/SkinViewer.js.map +1 -0
  7. package/dist/index.d.ts +53 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +51 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/item.d.ts +118 -0
  12. package/dist/item.d.ts.map +1 -0
  13. package/dist/item.js +319 -0
  14. package/dist/item.js.map +1 -0
  15. package/dist/link.d.ts +30 -0
  16. package/dist/link.d.ts.map +1 -0
  17. package/dist/link.js +18 -0
  18. package/dist/link.js.map +1 -0
  19. package/dist/protocol.d.ts +231 -0
  20. package/dist/protocol.d.ts.map +1 -0
  21. package/dist/protocol.js +128 -0
  22. package/dist/protocol.js.map +1 -0
  23. package/dist/state.d.ts +107 -0
  24. package/dist/state.d.ts.map +1 -0
  25. package/dist/state.js +351 -0
  26. package/dist/state.js.map +1 -0
  27. package/dist/types.d.ts +573 -0
  28. package/dist/types.d.ts.map +1 -0
  29. package/dist/types.js +106 -0
  30. package/dist/types.js.map +1 -0
  31. package/dist/useSkinViewer.d.ts +3 -0
  32. package/dist/useSkinViewer.d.ts.map +1 -0
  33. package/dist/useSkinViewer.js +66 -0
  34. package/dist/useSkinViewer.js.map +1 -0
  35. package/dist/weapons.d.ts +109 -0
  36. package/dist/weapons.d.ts.map +1 -0
  37. package/dist/weapons.js +260 -0
  38. package/dist/weapons.js.map +1 -0
  39. package/package.json +65 -0
  40. package/src/SkinViewer.tsx +465 -0
  41. package/src/index.ts +88 -0
  42. package/src/item.ts +373 -0
  43. package/src/link.ts +33 -0
  44. package/src/protocol.ts +241 -0
  45. package/src/state.ts +389 -0
  46. package/src/types.ts +672 -0
  47. package/src/useSkinViewer.ts +80 -0
  48. package/src/weapons.ts +284 -0
@@ -0,0 +1,404 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * *** `<SkinViewer />` - THE EMBED AS A REACT COMPONENT. ***
5
+ *
6
+ * ═════════════════════════════════════════════════════════════════════════════════════════════
7
+ * *** THE ONE PROPERTY THIS FILE EXISTS TO PROTECT: A CHEAP PROP MUST NOT RELOAD THE FRAME. ***
8
+ *
9
+ * Owner's requirement: `float`, `seed`, `statTrak`, `nameTag`, the stickers and the charm update in
10
+ * place *"to make it feel just like in our website"*; only the weapon, the paint kit and the view go
11
+ * behind a loading card. The renderer guarantees it for a prop change, and `/frame`'s
12
+ * identity-preserving merge guarantees it across the wire - measured there at 140 float messages, 137
13
+ * animation frames, 769 timer samples and ZERO covered frames.
14
+ *
15
+ * *** THIS COMPONENT CAN STILL THROW IT ALL AWAY IN THREE LINES, AND HERE THEY ARE, GUARDED: ***
16
+ *
17
+ * 1. THE `src` IS BUILT ONCE AND NEVER REWRITTEN. Assigning `src` reloads the document, which drops
18
+ * the GL context, re-downloads the model and re-runs every shader compile. Every prop change
19
+ * after mount is a `postMessage`, without exception. `boot` is state that only `reload()` writes.
20
+ *
21
+ * 2. THE `<iframe>` HAS A `key`, AND IT IS DELIBERATELY NOT DERIVED FROM ANY PROP. It is a counter
22
+ * that only `reload()` increments. *** A `key={item.weapon}` HERE - OR ON THIS COMPONENT, IN A
23
+ * CONSUMER'S TREE - WOULD REMOUNT THE FRAME ON EVERY WEAPON CHANGE *** and turn a two-second
24
+ * cross-fade into a full document load. The frame already covers itself on an identity change;
25
+ * it does not need help and cannot be helped this way. The same warning is written into
26
+ * `/frame`'s own file, because the mistake is available at both ends.
27
+ *
28
+ * 3. THE MESSAGE IS A DIFF. See `state.ts`: sending the whole state every tick would be correct on
29
+ * the wire and would still re-seed the renderer's sticker draft sixty times a second.
30
+ *
31
+ * ═════════════════════════════════════════════════════════════════════════════════════════════
32
+ * *** AND THE SECOND: A STALE PACKAGE FAILS LOUDLY, NEVER PARTIALLY. ***
33
+ *
34
+ * No back-compat, no version window - decided policy, and the README's *Versioning* section is its public statement. A protocol mismatch in
35
+ * either direction is terminal: the frame renders nothing, this component stops sending, `onError`
36
+ * carries the sentence naming which side is out of date, and `fallback` gets it too. Nothing is
37
+ * half-applied, because a subtly wrong picture is worse than a blank one with an explanation.
38
+ */
39
+ import { useCallback, useEffect, useRef, useState } from 'react';
40
+ import { hostMessage, readFrameEvent } from './protocol.js';
41
+ import { toPublicItem } from './item.js';
42
+ import { coversCanvas, diffState, frameUrl, resolveState } from './state.js';
43
+ import { LINK } from './link.js';
44
+ /** Where the embed is served from. Overridable per component - see {@link SkinViewerProps.origin}. */
45
+ export const DEFAULT_ORIGIN = 'https://skinhub.gg';
46
+ /**
47
+ * *** THE TWO KINDS OF FAILURE, AND THEY ARE HELD DIFFERENTLY ON PURPOSE. ***
48
+ *
49
+ * A FRAME FAILURE (`protocol-mismatch`, `render-failed`) arrives as an event and is STICKY state: the
50
+ * scene is gone and nothing in the props can bring it back, so it stays until a reload or - for a lost
51
+ * render - until the next identity change gives the frame something new to try.
52
+ *
53
+ * A SUBJECT FAILURE (`no-item`, `bad-inspect-link`, `unknown-weapon`) is DERIVED FROM THE PROPS and is
54
+ * never sticky. It is a fact about this render's arguments, so the moment the arguments are good it is
55
+ * over. That distinction is the difference between a viewer that recovers when a query resolves and
56
+ * one an integrator has to remount.
57
+ */
58
+ const isFatal = (code) => code === 'protocol-mismatch' || code === 'render-failed' || code === 'unreachable';
59
+ /**
60
+ * *** HOW LONG THE EMBED HAS TO ANNOUNCE ITSELF BEFORE WE CALL IT UNREACHABLE. ***
61
+ *
62
+ * The frame posts `hello` as soon as its script runs, so this is a document fetch plus a parse - a few
63
+ * hundred milliseconds on a warm connection. Fifteen seconds is therefore not a performance budget, it
64
+ * is the point past which "still loading" stops being a credible explanation for an empty box.
65
+ *
66
+ * *** DELIBERATELY GENEROUS, BECAUSE A FALSE POSITIVE HERE IS SELF-HEALING AND A FALSE NEGATIVE IS
67
+ * NOT. *** The iframe stays mounted under the `fallback`, so a slow connection that lands at sixteen
68
+ * seconds clears the error and renders. A timer too short would flash an error at people on bad
69
+ * networks; no timer at all leaves them with a blank rectangle and nothing to search for.
70
+ */
71
+ export const CONNECT_TIMEOUT_MS = 15_000;
72
+ export const SkinViewer = (props) => {
73
+ const { className, style, title = 'SkinHub viewer', loading, fallback, handle } = props;
74
+ /*
75
+ * RESOLVED ON EVERY RENDER, NOT MEMOISED, AND THAT IS THE CHEAPER OPTION HERE.
76
+ *
77
+ * A memo would need a dependency array over props a consumer writes INLINE - `item={{…}}`,
78
+ * `settings={{…}}` - so its identity changes every render anyway and the memo would only add a
79
+ * comparison to the work it fails to skip. Everything downstream compares by VALUE for the same
80
+ * reason (see `diffState`), so a fresh object here costs nothing: it produces no patch, no message
81
+ * and no render in the frame.
82
+ */
83
+ const desired = resolveState(props);
84
+ const desiredRef = useRef(desired);
85
+ desiredRef.current = desired;
86
+ /*
87
+ * THE ORIGIN IS READ ONCE. See the prop's own doc: it is the single value that could only be applied
88
+ * by reloading the frame, and a prop that quietly throws away the GL context is the thing this
89
+ * component is built not to have.
90
+ */
91
+ const originRef = useRef(props.origin ?? DEFAULT_ORIGIN);
92
+ /**
93
+ * *** THE `src`, AND THE STATE THAT `src` EXPRESSED. ***
94
+ *
95
+ * Built in a `useState` INITIALISER rather than an effect, so the first frame anyone sees is the
96
+ * integrator's item and not ours corrected a tick later - and so a server render emits the same
97
+ * attribute the browser will, with no hydration mismatch to paper over.
98
+ *
99
+ * `nonce` is the `<iframe>`'s key and only `reload()` moves it. See point 2 in the header.
100
+ */
101
+ const [boot, setBoot] = useState(() => {
102
+ const { src, expressed } = frameUrl(originRef.current, desired);
103
+ return { src, expressed, nonce: 0 };
104
+ });
105
+ /** What the frame has been told so far. The baseline every later diff is measured against. */
106
+ const sent = useRef(boot.expressed);
107
+ /** No `set` may be sent before the frame has announced itself; until then it has no listener. */
108
+ const connected = useRef(false);
109
+ const frame = useRef(null);
110
+ /** Cancelled the moment the frame speaks; see {@link CONNECT_TIMEOUT_MS}. */
111
+ const connectTimer = useRef(null);
112
+ const [status, setStatus] = useState('connecting');
113
+ const [error, setError] = useState(null);
114
+ const [problems, setProblems] = useState([]);
115
+ /*
116
+ * THE CALLBACKS, THROUGH A REF.
117
+ *
118
+ * `onChange` fires on every pointer move of a sticker drag. Putting the handler itself in a
119
+ * dependency array would re-attach the window listener on every render of a consumer who writes
120
+ * `onChange={item => setItem(item)}` inline - which is all of them. One ref, updated during render,
121
+ * and the listener effect below has no dependencies at all.
122
+ */
123
+ const handlers = useRef(props);
124
+ handlers.current = props;
125
+ const post = useCallback((message) => {
126
+ const target = frame.current?.contentWindow;
127
+ if (!target)
128
+ return;
129
+ // TARGETED AT THE FRAME'S ORIGIN AND NEVER `'*'`. We know it - we built the URL - so there is no
130
+ // reason to broadcast a customer's item state to whatever document happens to be there.
131
+ target.postMessage(message, originRef.current);
132
+ }, []);
133
+ const report = useCallback((next) => {
134
+ handlers.current.onError?.(next);
135
+ if (!isFatal(next.code))
136
+ return;
137
+ setError(next);
138
+ setStatus('error');
139
+ // A MISMATCH IS ONE-WAY. There is no path back: a frame that has answered one message this
140
+ // package cannot read is a frame whose next answer it also cannot trust.
141
+ if (next.code === 'protocol-mismatch')
142
+ connected.current = false;
143
+ }, []);
144
+ /**
145
+ * *** THE FLUSH. Every prop change in this package leaves through this function. ***
146
+ *
147
+ * Called after every render and again the moment the frame connects, because a prop can change
148
+ * before the iframe's document exists and that update must not be lost - it has to wait, not
149
+ * evaporate.
150
+ */
151
+ const flush = useCallback(() => {
152
+ if (!connected.current)
153
+ return;
154
+ const next = desiredRef.current;
155
+ const patch = diffState(sent.current, next);
156
+ if (!patch)
157
+ return;
158
+ /*
159
+ * THE BASELINE KEEPS THE LAST ITEM WE ACTUALLY SENT. A render whose `item` was null - a host
160
+ * whose query briefly returned `undefined` - must not record "no item" as the thing the frame is
161
+ * showing, or the next real item would be diffed against a hole and sent in full for no reason.
162
+ */
163
+ sent.current = { ...next, item: next.item ?? sent.current.item };
164
+ if (coversCanvas(patch, next.view)) {
165
+ setStatus('loading');
166
+ // A NEW ITEM IS A NEW CHANCE. A lost GL context or a 404 on one model says nothing about the
167
+ // next one, so the fallback comes down and the frame is allowed to try. A protocol mismatch is
168
+ // not cleared here: that one is about the conversation, not the item.
169
+ setError(current => (current?.code === 'render-failed' ? null : current));
170
+ }
171
+ post(hostMessage(patch));
172
+ }, [post]);
173
+ useEffect(flush);
174
+ /* ── THE CONNECTION DEADLINE ──────────────────────────────────────────────────────────────── */
175
+ /**
176
+ * *** THE ONLY THING STANDING BETWEEN AN UNREACHABLE ORIGIN AND A SILENT EMPTY BOX. ***
177
+ *
178
+ * Keyed on `boot.nonce`, so `reload()` genuinely retries rather than inheriting a spent deadline.
179
+ * See {@link CONNECT_TIMEOUT_MS} and the `unreachable` code for why the browser gives us nothing
180
+ * else to go on.
181
+ */
182
+ useEffect(() => {
183
+ connectTimer.current = setTimeout(() => {
184
+ connectTimer.current = null;
185
+ if (connected.current)
186
+ return;
187
+ report({
188
+ code: 'unreachable',
189
+ message: `The SkinHub viewer embed at ${boot.src} did not respond within ${Math.round(CONNECT_TIMEOUT_MS / 1000)}s. Nothing has rendered. Check that the origin is reachable from this browser, that it serves /frame, and that your page's Content-Security-Policy allows framing it (frame-src).`,
190
+ });
191
+ }, CONNECT_TIMEOUT_MS);
192
+ return () => {
193
+ if (connectTimer.current !== null)
194
+ clearTimeout(connectTimer.current);
195
+ connectTimer.current = null;
196
+ };
197
+ }, [boot.nonce, boot.src, report]);
198
+ /* ── THE CHANNEL ──────────────────────────────────────────────────────────────────────────── */
199
+ useEffect(() => {
200
+ const onMessage = (event) => {
201
+ /*
202
+ * BOTH CHECKS, AND THEY ARE NOT THE SAME CHECK. `event.source` is the sending WINDOW and
203
+ * cannot be spoofed, so it is what stops a sibling frame or an ad tag on the host page from
204
+ * driving this component. `event.origin` is what stops a frame that has been navigated
205
+ * somewhere else from continuing to talk to us. `EMBED.md` §10 tells hand-rolled hosts to do
206
+ * exactly this, and a package that told them to and did not would be worth nothing.
207
+ */
208
+ if (event.source !== frame.current?.contentWindow)
209
+ return;
210
+ if (event.origin !== originRef.current)
211
+ return;
212
+ const reading = readFrameEvent(event.data);
213
+ if (reading.kind === 'ignore')
214
+ return;
215
+ if (reading.kind === 'mismatch') {
216
+ report(reading.error);
217
+ return;
218
+ }
219
+ receive(reading.event);
220
+ };
221
+ /* Shadowing the `handle` PROP here would be a trap, so the reader is named for what it does. */
222
+ const receive = (event) => {
223
+ switch (event.type) {
224
+ case 'hello': {
225
+ connected.current = true;
226
+ if (connectTimer.current !== null) {
227
+ clearTimeout(connectTimer.current);
228
+ connectTimer.current = null;
229
+ }
230
+ /*
231
+ * *** A LATE ARRIVAL UNDOES `unreachable`, AND ONLY THAT ONE. *** The iframe is never
232
+ * unmounted - the `fallback` is drawn OVER it - so a connection that lands after the timer
233
+ * is a working viewer sitting under an error card, which is worse than the blank box the
234
+ * timer was added to prevent. Cleared here rather than in the timer because this is the
235
+ * only place we learn the embed is really there.
236
+ */
237
+ setError(current => (current?.code === 'unreachable' ? null : current));
238
+ setStatus(current => (current === 'connecting' || current === 'error' ? 'loading' : current));
239
+ setProblems(event.problems);
240
+ /*
241
+ * A PROBLEM IN `hello` IS OUR BUG AND NOT THE INTEGRATOR'S. They passed props; this
242
+ * package turned them into a query string; the frame is naming the part of that string it
243
+ * could not read. It is surfaced on the hook and warned about in development rather than
244
+ * routed to `onError`, which is for failures they can act on.
245
+ */
246
+ if (event.problems.length > 0 && process.env.NODE_ENV !== 'production')
247
+ console.warn('[@skinhub/viewer] the embed rejected part of the URL this package built:', event.problems);
248
+ // Anything that changed while the document was still loading goes now.
249
+ flush();
250
+ return;
251
+ }
252
+ case 'ready':
253
+ // A LEVEL, NOT AN EDGE - it fires again after every identity change. And it fires after a
254
+ // FAILED load too, which is why a fatal error is not cleared here.
255
+ setStatus(current => (current === 'error' ? current : 'ready'));
256
+ handlers.current.onReady?.();
257
+ return;
258
+ case 'error':
259
+ report(event.error);
260
+ return;
261
+ case 'change': {
262
+ /*
263
+ * *** THE USER'S EDIT, AND IT IS ALSO WRITTEN INTO OUR BASELINE. ***
264
+ *
265
+ * The second half is the one that is easy to miss: without it, the next patch we send
266
+ * would be diffed against an item that never learned about the drag, so the very next
267
+ * float tick would restate the OLD sticker offsets and yank the sticker back under the
268
+ * user's cursor. The frame is the authority on what the user did; we follow it.
269
+ */
270
+ sent.current = { ...sent.current, item: event.item };
271
+ handlers.current.onChange?.(toPublicItem(event.item));
272
+ return;
273
+ }
274
+ case 'editing-slot':
275
+ handlers.current.onEditingSlotChange?.(event.slot);
276
+ return;
277
+ case 'resize':
278
+ handlers.current.onResize?.({ width: event.width, height: event.height, dpr: event.dpr });
279
+ return;
280
+ }
281
+ };
282
+ window.addEventListener('message', onMessage);
283
+ return () => window.removeEventListener('message', onMessage);
284
+ }, [flush, report]);
285
+ /* ── THE SUBJECT'S OWN FAILURES ───────────────────────────────────────────────────────────── */
286
+ /*
287
+ * `no-item`, `bad-inspect-link` and `unknown-weapon` happen HERE rather than in the frame - they are
288
+ * decided while resolving props, before a message is sent. Reported once per distinct message, not
289
+ * once per render, because a host re-rendering at 60 Hz with a bad link would otherwise fill their
290
+ * console and their error reporter.
291
+ */
292
+ const reported = useRef(null);
293
+ useEffect(() => {
294
+ const subjectError = desiredRef.current.subjectError;
295
+ if (!subjectError) {
296
+ reported.current = null;
297
+ return;
298
+ }
299
+ if (reported.current === subjectError.message)
300
+ return;
301
+ reported.current = subjectError.message;
302
+ handlers.current.onError?.(subjectError);
303
+ });
304
+ /* ── THE HANDLE ───────────────────────────────────────────────────────────────────────────── */
305
+ const link = handle?.[LINK];
306
+ useEffect(() => {
307
+ if (!link)
308
+ return;
309
+ link.reload = () => {
310
+ /*
311
+ * REBUILT FROM WHAT IS ON SCREEN NOW, not from the props this component mounted with - a reload
312
+ * that reverted the item to its first value would be a surprise, not a refresh.
313
+ *
314
+ * AND IT FALLS BACK TO THE LAST ITEM WE SENT, for the case where this render happens to have no
315
+ * item: a `reload()` fired from a button while the host's query is momentarily `undefined` must
316
+ * bring the item back, not the instruction card.
317
+ */
318
+ const current = desiredRef.current;
319
+ const next = current.item
320
+ ? current
321
+ : { ...current, item: sent.current.item, help: sent.current.item ? null : current.help };
322
+ const { src, expressed } = frameUrl(originRef.current, next);
323
+ sent.current = expressed;
324
+ connected.current = false;
325
+ setStatus('connecting');
326
+ setError(null);
327
+ setProblems([]);
328
+ setBoot(previous => ({ src, expressed, nonce: previous.nonce + 1 }));
329
+ };
330
+ return () => {
331
+ link.reload = () => { };
332
+ };
333
+ }, [link]);
334
+ /*
335
+ * WHAT THE HOOK IS TOLD IS THE RESOLVED VIEW OF BOTH FAILURE KINDS, not the raw event state - so a
336
+ * consumer rendering `viewer.status` sees `'error'` for a link that did not decode, without this
337
+ * component having to make a prop-derived fact sticky to get it there.
338
+ */
339
+ const publicError = error ?? desired.subjectError;
340
+ const publicStatus = publicError ? 'error' : status;
341
+ useEffect(() => {
342
+ link?.publish({ status: publicStatus, error: publicError, problems });
343
+ }, [link, publicStatus, publicError, problems]);
344
+ /* ── THE BOX ──────────────────────────────────────────────────────────────────────────────── */
345
+ const box = useRef(null);
346
+ useEffect(() => {
347
+ if (process.env.NODE_ENV === 'production')
348
+ return;
349
+ const element = box.current;
350
+ if (!element || (element.offsetWidth > 0 && element.offsetHeight > 0))
351
+ return;
352
+ console.warn('[@skinhub/viewer] <SkinViewer> measured 0 px in one dimension, so nothing will be visible. ' +
353
+ 'The viewer fills its container and has no intrinsic size - give it one with `style` or `className` ' +
354
+ '(e.g. style={{ width: 640, height: 420 }}), or size the element you put it in.');
355
+ }, []);
356
+ /*
357
+ * ── WHAT IS DRAWN OVER THE FRAME, IN ORDER ────────────────────────────────────────────────
358
+ *
359
+ * 1. A FATAL FAILURE takes the `fallback` slot. The scene is gone underneath it.
360
+ *
361
+ * 2. A SUBJECT FAILURE takes it too WHEN THE CALLER PROVIDED ONE - they said what they want shown
362
+ * for a bad item and that beats anything we would draw. *** WITH NO `fallback` IT DRAWS NOTHING,
363
+ * AND THAT IS THE POINT: *** the frame is showing the instruction card, and covering it with a
364
+ * skeleton would replace the one screen in this product whose whole job is to teach.
365
+ *
366
+ * 3. OTHERWISE `loading`, until the frame says `ready`.
367
+ */
368
+ const overlay = error
369
+ ? renderFallback(fallback, error)
370
+ : desired.subjectError
371
+ ? (renderFallback(fallback, desired.subjectError) ?? null)
372
+ : status === 'ready'
373
+ ? null
374
+ : loading;
375
+ return (_jsxs("div", { ref: box, className: className, style: { position: 'relative', ...style }, children: [_jsx("iframe", { ref: frame, src: boot.src, title: title,
376
+ /*
377
+ * *** NO `referrerPolicy`, AND THAT IS A DECISION. *** The embed reads the framing origin
378
+ * from this request's `Referer` header. Setting `no-referrer` here - which looks like good
379
+ * hygiene - would make every embed anonymous to us, which is not a privacy win for the host
380
+ * (we already know the URL they asked for) and does break the one thing that header decides.
381
+ */
382
+ style: {
383
+ position: 'absolute',
384
+ inset: 0,
385
+ width: '100%',
386
+ height: '100%',
387
+ border: 0,
388
+ // The frame paints nothing of its own, so the canvas composites over the host's page.
389
+ // Without this a browser's default white iframe background would sit in between.
390
+ background: 'transparent',
391
+ display: 'block',
392
+ } }, boot.nonce), overlay === null || overlay === undefined ? null : (
393
+ /*
394
+ * DRAWN OVER THE FRAME, NEVER INSIDE IT - a React element cannot be structured-cloned across
395
+ * a `postMessage` boundary, so this is the only place a consumer's node can live.
396
+ *
397
+ * `pointerEvents: 'none'` so a spinner does not eat the orbit drag underneath it. A consumer
398
+ * whose overlay is interactive turns it back on in their own node, which is the right way
399
+ * round: the common case costs them nothing and the rare one is one line.
400
+ */
401
+ _jsx("div", { style: { position: 'absolute', inset: 0, pointerEvents: 'none' }, children: overlay }))] }));
402
+ };
403
+ const renderFallback = (fallback, error) => typeof fallback === 'function' ? fallback(error) : fallback;
404
+ //# sourceMappingURL=SkinViewer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SkinViewer.js","sourceRoot":"","sources":["../src/SkinViewer.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * *** `@skinhub/viewer` - THE SKINHUB CS2 SKIN VIEWER, AS A REACT COMPONENT. ***
3
+ *
4
+ * import { SkinViewer } from '@skinhub/viewer'
5
+ *
6
+ * <SkinViewer item={{ weapon: 'weapon_ak47', paintIndex: 1449, float: 0.27 }} style={{ height: 420 }} />
7
+ * <SkinViewer inspectLink={tradeOffer.inspectLink} style={{ height: 420 }} />
8
+ *
9
+ * The 3D is not in here. It is a page on our origin that this component embeds and drives over
10
+ * `postMessage`, which is why installing this pulls in no `three`, no `@react-three/fiber` and no
11
+ * asset bundle - the only peer dependency is React. `EMBED.md` documents the same contract for stacks
12
+ * that are not React; this package is the React-shaped door onto it, not a second implementation.
13
+ *
14
+ * ─────────────────────────────────────────────────────────────────────────────────────────────
15
+ * WHAT IS EXPORTED, AND WHY THE LIST IS SHORT.
16
+ *
17
+ * `SkinViewer`, `useSkinViewer` - the component and its verbs.
18
+ * the types - so a consumer can name a prop's shape in their own code.
19
+ * `MAP_NAMES`, `WEAPON_IDS` - the two closed vocabularies, as values, for building a picker.
20
+ * the defindex table - because an integrator with a Steam inventory has numbers.
21
+ *
22
+ * Nothing else FROM HERE. The URL builder and the diff are implementation: if they were public, a
23
+ * customer could build a state this package would then have to keep working, and the whole point of
24
+ * the version integer is that there is exactly one shape to keep working.
25
+ *
26
+ * *** THE WIRE TYPES ARE THE ONE EXCEPTION AND THEY ARE A SEPARATE DOOR - `@skinhub/viewer/protocol`.
27
+ * *** Deliberately not the barrel, so they never appear in an autocomplete next to `SkinViewer` and
28
+ * nobody reaches for them by accident. Exported at all because `EMBED.md` §6 already publishes that
29
+ * exact shape in prose, for hosts that are not React, and because the app repo's conformance test has
30
+ * to import both halves of the wire to prove they still agree. See `protocol.ts` for both.
31
+ *
32
+ * ─────────────────────────────────────────────────────────────────────────────────────────────
33
+ * *** DO NOT ADD `"sideEffects": false` TO `package.json`. Measured, Bun 1.3.13: *** with that flag
34
+ * set, a `bun build` bundle of this barrel tree-shakes the entire package away and emits a 248-byte
35
+ * file that re-exports fourteen names and defines none of them. It builds, it publishes, and it fails
36
+ * at import time in the consumer. The flag is the correct thing to want and it is not worth this.
37
+ *
38
+ * (The package's OWN build is `tsc`, not a bundler - one emitted file per source file, matching
39
+ * `@skinhub/cdn` - so the flag would only ever bite a consumer's bundler. That is worse, not better:
40
+ * it moves the failure to somebody else's build.)
41
+ */
42
+ export { SkinViewer, DEFAULT_ORIGIN } from './SkinViewer.js';
43
+ export { useSkinViewer } from './useSkinViewer.js';
44
+ /**
45
+ * *** ITEM OUT, LINK BACK. *** A picker is not finished when it can show the item - it is finished
46
+ * when it can hand you the link. See `item.ts` for the three easy-to-get-wrong facts these encode.
47
+ */
48
+ export { fromInspectLink, toInspectLink, toPlacement } from './item.js';
49
+ export type { MapName, SkinViewerCharm, SkinViewerError, SkinViewerErrorCode, SkinViewerHandle, SkinViewerItem, SkinViewerProps, SkinViewerSticker, TimeOfDay, ViewerAgent, ViewerBackground, ViewerCameraSettings, ViewerEnvironmentSettings, ViewerGloves, ViewerInteractions, ViewerOverlaySettings, ViewerQualitySettings, ViewerResize, ViewerSettings, ViewerStatus, ViewerSubject, ViewerView, } from './types.js';
50
+ export { CHEAP_FIELDS, MAP_NAMES } from './types.js';
51
+ export type { KnownWeaponId, WeaponId } from './weapons.js';
52
+ export { defindexForWeaponId, isGloveId, isKnownWeaponId, normalizeWeaponId, WEAPON_ID_ALIASES, WEAPON_ID_BY_DEFINDEX, WEAPON_IDS, weaponIdForDefindex, } from './weapons.js';
53
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD;;;GAGG;AACH,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAEvE,YAAY,EACX,OAAO,EACP,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,cAAc,EACd,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,WAAW,EACX,gBAAgB,EAChB,oBAAoB,EACpB,yBAAyB,EACzB,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,aAAa,EACb,UAAU,GACV,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAEpD,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,EACN,mBAAmB,EACnB,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,EACV,mBAAmB,GACnB,MAAM,cAAc,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,51 @@
1
+ /**
2
+ * *** `@skinhub/viewer` - THE SKINHUB CS2 SKIN VIEWER, AS A REACT COMPONENT. ***
3
+ *
4
+ * import { SkinViewer } from '@skinhub/viewer'
5
+ *
6
+ * <SkinViewer item={{ weapon: 'weapon_ak47', paintIndex: 1449, float: 0.27 }} style={{ height: 420 }} />
7
+ * <SkinViewer inspectLink={tradeOffer.inspectLink} style={{ height: 420 }} />
8
+ *
9
+ * The 3D is not in here. It is a page on our origin that this component embeds and drives over
10
+ * `postMessage`, which is why installing this pulls in no `three`, no `@react-three/fiber` and no
11
+ * asset bundle - the only peer dependency is React. `EMBED.md` documents the same contract for stacks
12
+ * that are not React; this package is the React-shaped door onto it, not a second implementation.
13
+ *
14
+ * ─────────────────────────────────────────────────────────────────────────────────────────────
15
+ * WHAT IS EXPORTED, AND WHY THE LIST IS SHORT.
16
+ *
17
+ * `SkinViewer`, `useSkinViewer` - the component and its verbs.
18
+ * the types - so a consumer can name a prop's shape in their own code.
19
+ * `MAP_NAMES`, `WEAPON_IDS` - the two closed vocabularies, as values, for building a picker.
20
+ * the defindex table - because an integrator with a Steam inventory has numbers.
21
+ *
22
+ * Nothing else FROM HERE. The URL builder and the diff are implementation: if they were public, a
23
+ * customer could build a state this package would then have to keep working, and the whole point of
24
+ * the version integer is that there is exactly one shape to keep working.
25
+ *
26
+ * *** THE WIRE TYPES ARE THE ONE EXCEPTION AND THEY ARE A SEPARATE DOOR - `@skinhub/viewer/protocol`.
27
+ * *** Deliberately not the barrel, so they never appear in an autocomplete next to `SkinViewer` and
28
+ * nobody reaches for them by accident. Exported at all because `EMBED.md` §6 already publishes that
29
+ * exact shape in prose, for hosts that are not React, and because the app repo's conformance test has
30
+ * to import both halves of the wire to prove they still agree. See `protocol.ts` for both.
31
+ *
32
+ * ─────────────────────────────────────────────────────────────────────────────────────────────
33
+ * *** DO NOT ADD `"sideEffects": false` TO `package.json`. Measured, Bun 1.3.13: *** with that flag
34
+ * set, a `bun build` bundle of this barrel tree-shakes the entire package away and emits a 248-byte
35
+ * file that re-exports fourteen names and defines none of them. It builds, it publishes, and it fails
36
+ * at import time in the consumer. The flag is the correct thing to want and it is not worth this.
37
+ *
38
+ * (The package's OWN build is `tsc`, not a bundler - one emitted file per source file, matching
39
+ * `@skinhub/cdn` - so the flag would only ever bite a consumer's bundler. That is worse, not better:
40
+ * it moves the failure to somebody else's build.)
41
+ */
42
+ export { SkinViewer, DEFAULT_ORIGIN } from './SkinViewer.js';
43
+ export { useSkinViewer } from './useSkinViewer.js';
44
+ /**
45
+ * *** ITEM OUT, LINK BACK. *** A picker is not finished when it can show the item - it is finished
46
+ * when it can hand you the link. See `item.ts` for the three easy-to-get-wrong facts these encode.
47
+ */
48
+ export { fromInspectLink, toInspectLink, toPlacement } from './item.js';
49
+ export { CHEAP_FIELDS, MAP_NAMES } from './types.js';
50
+ export { defindexForWeaponId, isGloveId, isKnownWeaponId, normalizeWeaponId, WEAPON_ID_ALIASES, WEAPON_ID_BY_DEFINDEX, WEAPON_IDS, weaponIdForDefindex, } from './weapons.js';
51
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAElD;;;GAGG;AACH,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AA0BvE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAGpD,OAAO,EACN,mBAAmB,EACnB,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,EACV,mBAAmB,GACnB,MAAM,cAAc,CAAA"}