@pixelume/sdk 0.2.0 → 0.4.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.
Files changed (43) hide show
  1. package/CHANGELOG.md +62 -1
  2. package/README.md +113 -22
  3. package/dist/contracts/index.d.ts +42 -2
  4. package/dist/contracts/index.js +107 -8
  5. package/dist/contracts/index.js.map +1 -1
  6. package/dist/core/PixelumeClient.d.ts +12 -2
  7. package/dist/core/PixelumeClient.js +16 -2
  8. package/dist/core/PixelumeClient.js.map +1 -1
  9. package/dist/core/config.d.ts +2 -1
  10. package/dist/core/config.js +8 -1
  11. package/dist/core/config.js.map +1 -1
  12. package/dist/core/errors.d.ts +44 -2
  13. package/dist/core/errors.js +30 -0
  14. package/dist/core/errors.js.map +1 -1
  15. package/dist/core/types.d.ts +12 -0
  16. package/dist/core/types.js.map +1 -1
  17. package/dist/http/HttpClient.js +1 -0
  18. package/dist/http/HttpClient.js.map +1 -1
  19. package/dist/index.d.ts +5 -3
  20. package/dist/index.js +1 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/modules/auth/AuthModule.d.ts +28 -3
  23. package/dist/modules/auth/AuthModule.js +82 -40
  24. package/dist/modules/auth/AuthModule.js.map +1 -1
  25. package/dist/modules/auth/SessionManager.d.ts +91 -0
  26. package/dist/modules/auth/SessionManager.js +475 -0
  27. package/dist/modules/auth/SessionManager.js.map +1 -0
  28. package/dist/modules/saves/SavesModule.d.ts +100 -0
  29. package/dist/modules/saves/SavesModule.js +238 -0
  30. package/dist/modules/saves/SavesModule.js.map +1 -0
  31. package/dist/ui/index.d.ts +2 -0
  32. package/dist/ui/index.js +2 -0
  33. package/dist/ui/index.js.map +1 -0
  34. package/dist/ui/styles.d.ts +1 -0
  35. package/dist/ui/styles.js +402 -0
  36. package/dist/ui/styles.js.map +1 -0
  37. package/dist/ui/widget.d.ts +29 -0
  38. package/dist/ui/widget.js +435 -0
  39. package/dist/ui/widget.js.map +1 -0
  40. package/dist/version.d.ts +1 -1
  41. package/dist/version.js +1 -1
  42. package/dist/version.js.map +1 -1
  43. package/package.json +7 -2
@@ -0,0 +1,435 @@
1
+ import { WIDGET_STYLES } from './styles.js';
2
+ const DEFAULT_POSITION = 'bottom-right';
3
+ const DEFAULT_OFFSET = 16;
4
+ const DEFAULT_Z_INDEX = 2_147_483_000;
5
+ const POSITIONS = [
6
+ 'top-left',
7
+ 'top-right',
8
+ 'bottom-left',
9
+ 'bottom-right',
10
+ ];
11
+ const widgetsByClient = new WeakMap();
12
+ const widgetsByContainer = new WeakMap();
13
+ let nextWidgetId = 1;
14
+ /**
15
+ * Mounts the optional Pixelume account widget. Importing this module alone has no DOM side effects.
16
+ * Repeating the call with the same client and container returns the existing controller.
17
+ */
18
+ export function mountPixelumeWidget(pixelume, options = {}) {
19
+ assertClient(pixelume);
20
+ const document = resolveDocument(options.container);
21
+ const container = options.container ?? document.body;
22
+ const mounted = widgetsByClient.get(pixelume);
23
+ if (mounted) {
24
+ if (mounted.container === container)
25
+ return mounted.controller;
26
+ throw new Error('This Pixelume client already has a widget mounted in another container');
27
+ }
28
+ const containerOwner = widgetsByContainer.get(container);
29
+ if (containerOwner) {
30
+ throw new Error('This container already has a Pixelume widget mounted');
31
+ }
32
+ const position = resolvePosition(options.position);
33
+ const offset = resolveNumber('offset', options.offset, DEFAULT_OFFSET, 0, 256);
34
+ const zIndex = resolveNumber('zIndex', options.zIndex, DEFAULT_Z_INDEX, 0, 2_147_483_647);
35
+ const theme = options.theme;
36
+ if (theme !== undefined && theme !== 'pixel') {
37
+ throw new Error('Pixelume widget theme must be "pixel"');
38
+ }
39
+ const host = document.createElement('div');
40
+ host.dataset.pixelumeWidget = '';
41
+ host.dataset.position = position;
42
+ host.style.setProperty('--pixelume-offset', `${offset}px`);
43
+ host.style.setProperty('--pixelume-z-index', String(zIndex));
44
+ if (options.container)
45
+ host.dataset.contained = '';
46
+ const shadow = host.attachShadow({ mode: 'open' });
47
+ const style = document.createElement('style');
48
+ style.textContent = WIDGET_STYLES;
49
+ const root = document.createElement('div');
50
+ root.className = 'root';
51
+ const panelId = `pixelume-widget-panel-${nextWidgetId++}`;
52
+ const trigger = button(document, 'trigger', 'Abrir conta Pixelume');
53
+ trigger.type = 'button';
54
+ trigger.setAttribute('aria-expanded', 'false');
55
+ trigger.setAttribute('aria-controls', panelId);
56
+ const triggerMark = document.createElement('span');
57
+ triggerMark.className = 'mark';
58
+ triggerMark.setAttribute('aria-hidden', 'true');
59
+ const triggerLabel = document.createElement('span');
60
+ triggerLabel.className = 'trigger-label';
61
+ triggerLabel.textContent = 'Pixelume';
62
+ const presence = document.createElement('span');
63
+ presence.className = 'presence';
64
+ presence.setAttribute('aria-hidden', 'true');
65
+ trigger.append(triggerMark, triggerLabel, presence);
66
+ const panel = document.createElement('section');
67
+ panel.className = 'panel';
68
+ panel.id = panelId;
69
+ panel.hidden = true;
70
+ panel.setAttribute('aria-label', 'Conta Pixelume');
71
+ panel.setAttribute('aria-live', 'polite');
72
+ const header = document.createElement('div');
73
+ header.className = 'panel-header';
74
+ const brand = document.createElement('span');
75
+ brand.className = 'brand';
76
+ const brandMark = document.createElement('span');
77
+ brandMark.className = 'mark';
78
+ brandMark.setAttribute('aria-hidden', 'true');
79
+ const brandText = document.createElement('span');
80
+ brandText.textContent = 'Pixelume';
81
+ brand.append(brandMark, brandText);
82
+ const close = button(document, 'close', 'Fechar painel da conta');
83
+ close.type = 'button';
84
+ close.textContent = '×';
85
+ close.setAttribute('aria-hidden', 'false');
86
+ header.append(brand, close);
87
+ const content = document.createElement('div');
88
+ content.className = 'content';
89
+ panel.append(header, content);
90
+ root.append(trigger, panel);
91
+ shadow.append(style, root);
92
+ let destroyed = false;
93
+ let panelOpen = false;
94
+ let pendingAction = null;
95
+ let localError = null;
96
+ let removeDocumentKeydown = null;
97
+ const onDocumentKeydown = (event) => {
98
+ if (event.key !== 'Escape' || !panelOpen)
99
+ return;
100
+ event.preventDefault();
101
+ event.stopPropagation();
102
+ closePanel(true);
103
+ };
104
+ const openPanel = () => {
105
+ if (destroyed || panelOpen)
106
+ return;
107
+ panelOpen = true;
108
+ panel.hidden = false;
109
+ trigger.setAttribute('aria-expanded', 'true');
110
+ document.addEventListener('keydown', onDocumentKeydown, true);
111
+ removeDocumentKeydown = () => {
112
+ document.removeEventListener('keydown', onDocumentKeydown, true);
113
+ };
114
+ close.focus();
115
+ };
116
+ function closePanel(restoreFocus) {
117
+ if (!panelOpen)
118
+ return;
119
+ panelOpen = false;
120
+ panel.hidden = true;
121
+ trigger.setAttribute('aria-expanded', 'false');
122
+ removeDocumentKeydown?.();
123
+ removeDocumentKeydown = null;
124
+ if (restoreFocus && !host.hidden)
125
+ trigger.focus();
126
+ }
127
+ const togglePanel = () => {
128
+ if (panelOpen)
129
+ closePanel(true);
130
+ else
131
+ openPanel();
132
+ };
133
+ const runLogin = async () => {
134
+ if (destroyed || pendingAction)
135
+ return;
136
+ pendingAction = 'login';
137
+ localError = null;
138
+ render(pixelume.auth.state);
139
+ try {
140
+ await pixelume.auth.login();
141
+ }
142
+ catch {
143
+ localError =
144
+ 'Não foi possível abrir a entrada agora. Você pode continuar jogando e tentar novamente.';
145
+ }
146
+ finally {
147
+ pendingAction = null;
148
+ render(pixelume.auth.state);
149
+ }
150
+ };
151
+ const runLogout = async () => {
152
+ if (destroyed || pendingAction)
153
+ return;
154
+ pendingAction = 'logout';
155
+ localError = null;
156
+ render(pixelume.auth.state);
157
+ try {
158
+ await pixelume.auth.logout();
159
+ }
160
+ catch {
161
+ localError =
162
+ 'Você saiu deste jogo, mas não foi possível confirmar a revogação no servidor. Tente novamente mais tarde.';
163
+ }
164
+ finally {
165
+ pendingAction = null;
166
+ render(pixelume.auth.state);
167
+ }
168
+ };
169
+ const render = (state) => {
170
+ if (destroyed)
171
+ return;
172
+ content.replaceChildren();
173
+ content.removeAttribute('aria-busy');
174
+ const authenticated = state.status === 'authenticated';
175
+ trigger.toggleAttribute('data-authenticated', authenticated);
176
+ trigger.setAttribute('aria-label', authenticated
177
+ ? `Abrir conta Pixelume. Conectado como ${state.player.displayName}`
178
+ : 'Abrir conta Pixelume');
179
+ if (state.status === 'uninitialized' ||
180
+ state.status === 'redirecting' ||
181
+ state.status === 'exchanging') {
182
+ renderLoading(document, content, state.status);
183
+ return;
184
+ }
185
+ if (state.status === 'authenticated') {
186
+ const session = sessionState();
187
+ const identity = document.createElement('div');
188
+ identity.className = 'identity';
189
+ const avatar = document.createElement('span');
190
+ avatar.className = 'avatar';
191
+ avatar.setAttribute('aria-hidden', 'true');
192
+ avatar.textContent = playerInitial(state.player.displayName);
193
+ const copy = document.createElement('div');
194
+ copy.className = 'state-copy';
195
+ copy.append(textElement(document, 'p', 'title', `Olá, ${state.player.displayName}!`), textElement(document, 'p', 'message', 'Sua conta está conectada a este jogo.'));
196
+ identity.append(avatar, copy);
197
+ content.append(identity);
198
+ if (session?.error) {
199
+ content.append(notice(document, 'Não conseguimos falar com a Pixelume agora. Sua conta continua conectada e tentaremos de novo automaticamente.'));
200
+ }
201
+ const sync = syncMessage(savesActivity());
202
+ if (sync)
203
+ content.append(status(document, sync));
204
+ if (localError)
205
+ content.append(notice(document, localError));
206
+ const logout = button(document, 'action secondary', 'Sair da conta Pixelume neste jogo');
207
+ logout.type = 'button';
208
+ logout.textContent = pendingAction === 'logout' ? 'Saindo…' : 'Sair deste jogo';
209
+ logout.disabled = pendingAction !== null;
210
+ logout.addEventListener('click', () => void runLogout());
211
+ content.append(logout);
212
+ return;
213
+ }
214
+ const copy = document.createElement('div');
215
+ copy.className = 'state-copy';
216
+ copy.append(textElement(document, 'p', 'title', anonymousTitle(state)), textElement(document, 'p', 'message', anonymousMessage(state)));
217
+ content.append(copy);
218
+ const failure = localError ?? anonymousNotice(state);
219
+ if (failure)
220
+ content.append(notice(document, failure));
221
+ const login = button(document, 'action', 'Entrar com Pixelume');
222
+ login.type = 'button';
223
+ login.textContent = pendingAction === 'login' ? 'Abrindo Pixelume…' : 'Entrar com Pixelume';
224
+ login.disabled = pendingAction !== null;
225
+ login.addEventListener('click', () => void runLogin());
226
+ content.append(login);
227
+ };
228
+ const unsubscribe = pixelume.auth.subscribe((state) => {
229
+ localError = null;
230
+ render(state);
231
+ });
232
+ // Optional sources (absent in custom/mocked clients): token renewal and explicit Saves calls.
233
+ const auth = pixelume.auth;
234
+ const saves = pixelume.saves;
235
+ const sessionState = () => auth.session ?? null;
236
+ const savesActivity = () => saves?.activity ?? null;
237
+ const rerender = () => {
238
+ render(pixelume.auth.state);
239
+ };
240
+ const unsubscribeSession = typeof auth.subscribeSession === 'function' ? auth.subscribeSession(rerender) : null;
241
+ const unsubscribeSaves = typeof saves?.subscribe === 'function' ? saves.subscribe(rerender) : null;
242
+ const stopInputPropagation = (event) => {
243
+ event.stopPropagation();
244
+ };
245
+ const closeFromButton = () => {
246
+ closePanel(true);
247
+ };
248
+ const isolatedEvents = [
249
+ 'click',
250
+ 'pointerdown',
251
+ 'pointerup',
252
+ 'touchstart',
253
+ 'touchend',
254
+ 'keydown',
255
+ 'keyup',
256
+ ];
257
+ for (const eventName of isolatedEvents)
258
+ host.addEventListener(eventName, stopInputPropagation);
259
+ trigger.addEventListener('click', togglePanel);
260
+ close.addEventListener('click', closeFromButton);
261
+ const controller = Object.freeze({
262
+ element: host,
263
+ hide() {
264
+ if (destroyed)
265
+ return;
266
+ closePanel(false);
267
+ host.hidden = true;
268
+ },
269
+ show() {
270
+ if (destroyed)
271
+ return;
272
+ host.hidden = false;
273
+ },
274
+ destroy() {
275
+ if (destroyed)
276
+ return;
277
+ destroyed = true;
278
+ closePanel(false);
279
+ removeDocumentKeydown?.();
280
+ removeDocumentKeydown = null;
281
+ unsubscribe();
282
+ unsubscribeSession?.();
283
+ unsubscribeSaves?.();
284
+ trigger.removeEventListener('click', togglePanel);
285
+ close.removeEventListener('click', closeFromButton);
286
+ for (const eventName of isolatedEvents)
287
+ host.removeEventListener(eventName, stopInputPropagation);
288
+ host.remove();
289
+ widgetsByClient.delete(pixelume);
290
+ widgetsByContainer.delete(container);
291
+ },
292
+ });
293
+ render(pixelume.auth.state);
294
+ container.append(host);
295
+ widgetsByClient.set(pixelume, { container, controller });
296
+ widgetsByContainer.set(container, pixelume);
297
+ return controller;
298
+ }
299
+ function assertClient(value) {
300
+ if (typeof value !== 'object' || value === null)
301
+ throw new TypeError('Pixelume widget requires a client');
302
+ const auth = Reflect.get(value, 'auth');
303
+ if (typeof auth !== 'object' || auth === null)
304
+ throw new TypeError('Pixelume widget requires client.auth');
305
+ for (const method of ['login', 'logout', 'subscribe']) {
306
+ if (typeof Reflect.get(auth, method) !== 'function') {
307
+ throw new TypeError(`Pixelume widget requires client.auth.${method}()`);
308
+ }
309
+ }
310
+ }
311
+ function resolveDocument(container) {
312
+ if (container) {
313
+ if (typeof container.attachShadow !== 'function') {
314
+ throw new TypeError('Pixelume widget container must be an HTMLElement');
315
+ }
316
+ return container.ownerDocument;
317
+ }
318
+ const candidate = Reflect.get(globalThis, 'document');
319
+ if (typeof candidate !== 'object' || candidate === null || !('createElement' in candidate)) {
320
+ throw new Error('Pixelume widget can only be mounted in a browser document');
321
+ }
322
+ return candidate;
323
+ }
324
+ function resolvePosition(value) {
325
+ if (value === undefined)
326
+ return DEFAULT_POSITION;
327
+ if (!POSITIONS.includes(value)) {
328
+ throw new Error(`Pixelume widget position must be one of: ${POSITIONS.join(', ')}`);
329
+ }
330
+ return value;
331
+ }
332
+ function resolveNumber(name, value, fallback, minimum, maximum) {
333
+ if (value === undefined)
334
+ return fallback;
335
+ if (!Number.isInteger(value) || value < minimum || value > maximum) {
336
+ throw new Error(`Pixelume widget ${name} must be an integer from ${minimum} to ${maximum}`);
337
+ }
338
+ return value;
339
+ }
340
+ function button(document, className, label) {
341
+ const element = document.createElement('button');
342
+ element.className = className;
343
+ element.setAttribute('aria-label', label);
344
+ return element;
345
+ }
346
+ function textElement(document, tag, className, text) {
347
+ const element = document.createElement(tag);
348
+ element.className = className;
349
+ element.textContent = text;
350
+ return element;
351
+ }
352
+ function notice(document, text) {
353
+ const element = textElement(document, 'p', 'notice', text);
354
+ element.setAttribute('role', 'alert');
355
+ return element;
356
+ }
357
+ function status(document, text) {
358
+ const element = textElement(document, 'p', 'message sync', text);
359
+ element.setAttribute('role', 'status');
360
+ return element;
361
+ }
362
+ /** Cloud status only after the game explicitly saved or deleted; nothing is simulated. */
363
+ function syncMessage(activity) {
364
+ if (activity === null)
365
+ return null;
366
+ if (activity.pendingWrites > 0)
367
+ return 'Salvando na nuvem…';
368
+ const last = activity.last;
369
+ if (last === null || (last.operation !== 'save' && last.operation !== 'delete'))
370
+ return null;
371
+ if (last.outcome === 'conflict') {
372
+ return 'Há um progresso mais recente na nuvem. O jogo vai decidir o que manter.';
373
+ }
374
+ if (last.outcome === 'error')
375
+ return 'Não foi possível salvar na nuvem agora.';
376
+ const time = new Date(last.at).toLocaleTimeString('pt-BR', {
377
+ hour: '2-digit',
378
+ minute: '2-digit',
379
+ });
380
+ return last.operation === 'save'
381
+ ? `Progresso salvo na nuvem às ${time}.`
382
+ : `Save removido da nuvem às ${time}.`;
383
+ }
384
+ function anonymousNotice(state) {
385
+ const error = state.error;
386
+ if (error === null)
387
+ return null;
388
+ if (error.kind === 'auth' && Reflect.get(error, 'reason') === 'game_unavailable') {
389
+ return 'O acesso deste jogo à Pixelume está indisponível no momento. Você pode continuar jogando.';
390
+ }
391
+ // An ended session is already explained by the "expired" title and message.
392
+ if (state.reason === 'expired')
393
+ return null;
394
+ return 'Não foi possível verificar sua conta. Você pode continuar jogando e tentar novamente.';
395
+ }
396
+ function renderLoading(document, content, status) {
397
+ const labels = {
398
+ uninitialized: 'Preparando sua conta…',
399
+ redirecting: 'Abrindo o Pixelume Auth…',
400
+ exchanging: 'Concluindo a entrada…',
401
+ };
402
+ content.setAttribute('aria-busy', 'true');
403
+ const visual = document.createElement('div');
404
+ visual.className = 'loading';
405
+ visual.setAttribute('aria-hidden', 'true');
406
+ for (let index = 0; index < 2; index += 1) {
407
+ const line = document.createElement('span');
408
+ line.className = 'loading-line';
409
+ visual.append(line);
410
+ }
411
+ const label = textElement(document, 'p', 'sr-only', labels[status]);
412
+ content.append(visual, label);
413
+ }
414
+ function playerInitial(displayName) {
415
+ return Array.from(displayName.trim())[0]?.toLocaleUpperCase('pt-BR') ?? 'P';
416
+ }
417
+ function anonymousTitle(state) {
418
+ if (state.reason === 'logged_out')
419
+ return 'Você saiu deste jogo';
420
+ if (state.reason === 'expired')
421
+ return 'Sua sessão expirou';
422
+ if (state.reason === 'cancelled')
423
+ return 'Entrada cancelada';
424
+ return 'Sua conta Pixelume';
425
+ }
426
+ function anonymousMessage(state) {
427
+ if (state.reason === 'logged_out')
428
+ return 'Entre novamente quando quiser.';
429
+ if (state.reason === 'expired')
430
+ return 'Entre novamente para continuar com sua conta.';
431
+ if (state.reason === 'cancelled')
432
+ return 'Você pode continuar como visitante ou tentar novamente.';
433
+ return 'Entre pelo portal seguro da Pixelume. Sua senha nunca passa pelo jogo.';
434
+ }
435
+ //# sourceMappingURL=widget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"widget.js","sourceRoot":"","sources":["../../src/ui/widget.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PixelumeClient } from '../core/PixelumeClient.js';\nimport type { PixelumeAuthState } from '../modules/auth/AuthModule.js';\nimport type { PixelumeSessionState } from '../modules/auth/SessionManager.js';\nimport type { PixelumeSavesActivity } from '../modules/saves/SavesModule.js';\nimport { WIDGET_STYLES } from './styles.js';\n\nexport type PixelumeWidgetPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n\nexport interface PixelumeWidgetOptions {\n /** Corner used by the widget. Defaults to `bottom-right`. */\n readonly position?: PixelumeWidgetPosition;\n /** Distance from the selected edges in CSS pixels. Defaults to `16`. */\n readonly offset?: number;\n /** Mount target. Defaults to `document.body`; custom targets should establish a containing block. */\n readonly container?: HTMLElement;\n /** Visual treatment. `pixel` is currently the only supported theme. */\n readonly theme?: 'pixel';\n /** Stacking order for the host. Defaults to `2147483000`. */\n readonly zIndex?: number;\n}\n\nexport interface PixelumeWidgetController {\n /** The Shadow DOM host created by the widget. */\n readonly element: HTMLElement;\n /** Hides the widget and closes its panel without changing authentication state. */\n hide(): void;\n /** Shows a previously hidden widget. */\n show(): void;\n /** Removes DOM/listeners/subscriptions. Safe to call more than once. */\n destroy(): void;\n}\n\nconst DEFAULT_POSITION: PixelumeWidgetPosition = 'bottom-right';\nconst DEFAULT_OFFSET = 16;\nconst DEFAULT_Z_INDEX = 2_147_483_000;\nconst POSITIONS: readonly PixelumeWidgetPosition[] = [\n 'top-left',\n 'top-right',\n 'bottom-left',\n 'bottom-right',\n];\n\ninterface MountedWidget {\n readonly container: HTMLElement;\n readonly controller: PixelumeWidgetController;\n}\n\nconst widgetsByClient = new WeakMap<object, MountedWidget>();\nconst widgetsByContainer = new WeakMap<HTMLElement, object>();\nlet nextWidgetId = 1;\n\n/**\n * Mounts the optional Pixelume account widget. Importing this module alone has no DOM side effects.\n * Repeating the call with the same client and container returns the existing controller.\n */\nexport function mountPixelumeWidget(\n pixelume: PixelumeClient,\n options: PixelumeWidgetOptions = {},\n): PixelumeWidgetController {\n assertClient(pixelume);\n const document = resolveDocument(options.container);\n const container = options.container ?? document.body;\n\n const mounted = widgetsByClient.get(pixelume);\n if (mounted) {\n if (mounted.container === container) return mounted.controller;\n throw new Error('This Pixelume client already has a widget mounted in another container');\n }\n const containerOwner = widgetsByContainer.get(container);\n if (containerOwner) {\n throw new Error('This container already has a Pixelume widget mounted');\n }\n\n const position = resolvePosition(options.position);\n const offset = resolveNumber('offset', options.offset, DEFAULT_OFFSET, 0, 256);\n const zIndex = resolveNumber('zIndex', options.zIndex, DEFAULT_Z_INDEX, 0, 2_147_483_647);\n const theme: unknown = options.theme;\n if (theme !== undefined && theme !== 'pixel') {\n throw new Error('Pixelume widget theme must be \"pixel\"');\n }\n\n const host = document.createElement('div');\n host.dataset.pixelumeWidget = '';\n host.dataset.position = position;\n host.style.setProperty('--pixelume-offset', `${offset}px`);\n host.style.setProperty('--pixelume-z-index', String(zIndex));\n if (options.container) host.dataset.contained = '';\n\n const shadow = host.attachShadow({ mode: 'open' });\n const style = document.createElement('style');\n style.textContent = WIDGET_STYLES;\n\n const root = document.createElement('div');\n root.className = 'root';\n\n const panelId = `pixelume-widget-panel-${nextWidgetId++}`;\n const trigger = button(document, 'trigger', 'Abrir conta Pixelume');\n trigger.type = 'button';\n trigger.setAttribute('aria-expanded', 'false');\n trigger.setAttribute('aria-controls', panelId);\n const triggerMark = document.createElement('span');\n triggerMark.className = 'mark';\n triggerMark.setAttribute('aria-hidden', 'true');\n const triggerLabel = document.createElement('span');\n triggerLabel.className = 'trigger-label';\n triggerLabel.textContent = 'Pixelume';\n const presence = document.createElement('span');\n presence.className = 'presence';\n presence.setAttribute('aria-hidden', 'true');\n trigger.append(triggerMark, triggerLabel, presence);\n\n const panel = document.createElement('section');\n panel.className = 'panel';\n panel.id = panelId;\n panel.hidden = true;\n panel.setAttribute('aria-label', 'Conta Pixelume');\n panel.setAttribute('aria-live', 'polite');\n\n const header = document.createElement('div');\n header.className = 'panel-header';\n const brand = document.createElement('span');\n brand.className = 'brand';\n const brandMark = document.createElement('span');\n brandMark.className = 'mark';\n brandMark.setAttribute('aria-hidden', 'true');\n const brandText = document.createElement('span');\n brandText.textContent = 'Pixelume';\n brand.append(brandMark, brandText);\n const close = button(document, 'close', 'Fechar painel da conta');\n close.type = 'button';\n close.textContent = '×';\n close.setAttribute('aria-hidden', 'false');\n header.append(brand, close);\n\n const content = document.createElement('div');\n content.className = 'content';\n panel.append(header, content);\n root.append(trigger, panel);\n shadow.append(style, root);\n\n let destroyed = false;\n let panelOpen = false;\n let pendingAction: 'login' | 'logout' | null = null;\n let localError: string | null = null;\n let removeDocumentKeydown: (() => void) | null = null;\n\n const onDocumentKeydown = (event: KeyboardEvent): void => {\n if (event.key !== 'Escape' || !panelOpen) return;\n event.preventDefault();\n event.stopPropagation();\n closePanel(true);\n };\n\n const openPanel = (): void => {\n if (destroyed || panelOpen) return;\n panelOpen = true;\n panel.hidden = false;\n trigger.setAttribute('aria-expanded', 'true');\n document.addEventListener('keydown', onDocumentKeydown, true);\n removeDocumentKeydown = () => {\n document.removeEventListener('keydown', onDocumentKeydown, true);\n };\n close.focus();\n };\n\n function closePanel(restoreFocus: boolean): void {\n if (!panelOpen) return;\n panelOpen = false;\n panel.hidden = true;\n trigger.setAttribute('aria-expanded', 'false');\n removeDocumentKeydown?.();\n removeDocumentKeydown = null;\n if (restoreFocus && !host.hidden) trigger.focus();\n }\n\n const togglePanel = (): void => {\n if (panelOpen) closePanel(true);\n else openPanel();\n };\n\n const runLogin = async (): Promise<void> => {\n if (destroyed || pendingAction) return;\n pendingAction = 'login';\n localError = null;\n render(pixelume.auth.state);\n try {\n await pixelume.auth.login();\n } catch {\n localError =\n 'Não foi possível abrir a entrada agora. Você pode continuar jogando e tentar novamente.';\n } finally {\n pendingAction = null;\n render(pixelume.auth.state);\n }\n };\n\n const runLogout = async (): Promise<void> => {\n if (destroyed || pendingAction) return;\n pendingAction = 'logout';\n localError = null;\n render(pixelume.auth.state);\n try {\n await pixelume.auth.logout();\n } catch {\n localError =\n 'Você saiu deste jogo, mas não foi possível confirmar a revogação no servidor. Tente novamente mais tarde.';\n } finally {\n pendingAction = null;\n render(pixelume.auth.state);\n }\n };\n\n const render = (state: PixelumeAuthState): void => {\n if (destroyed) return;\n content.replaceChildren();\n content.removeAttribute('aria-busy');\n const authenticated = state.status === 'authenticated';\n trigger.toggleAttribute('data-authenticated', authenticated);\n trigger.setAttribute(\n 'aria-label',\n authenticated\n ? `Abrir conta Pixelume. Conectado como ${state.player.displayName}`\n : 'Abrir conta Pixelume',\n );\n\n if (\n state.status === 'uninitialized' ||\n state.status === 'redirecting' ||\n state.status === 'exchanging'\n ) {\n renderLoading(document, content, state.status);\n return;\n }\n\n if (state.status === 'authenticated') {\n const session = sessionState();\n const identity = document.createElement('div');\n identity.className = 'identity';\n const avatar = document.createElement('span');\n avatar.className = 'avatar';\n avatar.setAttribute('aria-hidden', 'true');\n avatar.textContent = playerInitial(state.player.displayName);\n const copy = document.createElement('div');\n copy.className = 'state-copy';\n copy.append(\n textElement(document, 'p', 'title', `Olá, ${state.player.displayName}!`),\n textElement(document, 'p', 'message', 'Sua conta está conectada a este jogo.'),\n );\n identity.append(avatar, copy);\n content.append(identity);\n if (session?.error) {\n content.append(\n notice(\n document,\n 'Não conseguimos falar com a Pixelume agora. Sua conta continua conectada e tentaremos de novo automaticamente.',\n ),\n );\n }\n const sync = syncMessage(savesActivity());\n if (sync) content.append(status(document, sync));\n if (localError) content.append(notice(document, localError));\n const logout = button(document, 'action secondary', 'Sair da conta Pixelume neste jogo');\n logout.type = 'button';\n logout.textContent = pendingAction === 'logout' ? 'Saindo…' : 'Sair deste jogo';\n logout.disabled = pendingAction !== null;\n logout.addEventListener('click', () => void runLogout());\n content.append(logout);\n return;\n }\n\n const copy = document.createElement('div');\n copy.className = 'state-copy';\n copy.append(\n textElement(document, 'p', 'title', anonymousTitle(state)),\n textElement(document, 'p', 'message', anonymousMessage(state)),\n );\n content.append(copy);\n const failure = localError ?? anonymousNotice(state);\n if (failure) content.append(notice(document, failure));\n const login = button(document, 'action', 'Entrar com Pixelume');\n login.type = 'button';\n login.textContent = pendingAction === 'login' ? 'Abrindo Pixelume…' : 'Entrar com Pixelume';\n login.disabled = pendingAction !== null;\n login.addEventListener('click', () => void runLogin());\n content.append(login);\n };\n\n const unsubscribe = pixelume.auth.subscribe((state) => {\n localError = null;\n render(state);\n });\n // Optional sources (absent in custom/mocked clients): token renewal and explicit Saves calls.\n const auth = pixelume.auth as Partial<typeof pixelume.auth>;\n const saves = (pixelume as Partial<Pick<PixelumeClient, 'saves'>>).saves;\n const sessionState = (): PixelumeSessionState | null => auth.session ?? null;\n const savesActivity = (): PixelumeSavesActivity | null => saves?.activity ?? null;\n const rerender = (): void => {\n render(pixelume.auth.state);\n };\n const unsubscribeSession =\n typeof auth.subscribeSession === 'function' ? auth.subscribeSession(rerender) : null;\n const unsubscribeSaves =\n typeof saves?.subscribe === 'function' ? saves.subscribe(rerender) : null;\n\n const stopInputPropagation = (event: Event): void => {\n event.stopPropagation();\n };\n const closeFromButton = (): void => {\n closePanel(true);\n };\n const isolatedEvents = [\n 'click',\n 'pointerdown',\n 'pointerup',\n 'touchstart',\n 'touchend',\n 'keydown',\n 'keyup',\n ] as const;\n for (const eventName of isolatedEvents) host.addEventListener(eventName, stopInputPropagation);\n trigger.addEventListener('click', togglePanel);\n close.addEventListener('click', closeFromButton);\n\n const controller: PixelumeWidgetController = Object.freeze({\n element: host,\n hide(): void {\n if (destroyed) return;\n closePanel(false);\n host.hidden = true;\n },\n show(): void {\n if (destroyed) return;\n host.hidden = false;\n },\n destroy(): void {\n if (destroyed) return;\n destroyed = true;\n closePanel(false);\n removeDocumentKeydown?.();\n removeDocumentKeydown = null;\n unsubscribe();\n unsubscribeSession?.();\n unsubscribeSaves?.();\n trigger.removeEventListener('click', togglePanel);\n close.removeEventListener('click', closeFromButton);\n for (const eventName of isolatedEvents)\n host.removeEventListener(eventName, stopInputPropagation);\n host.remove();\n widgetsByClient.delete(pixelume);\n widgetsByContainer.delete(container);\n },\n });\n\n render(pixelume.auth.state);\n container.append(host);\n widgetsByClient.set(pixelume, { container, controller });\n widgetsByContainer.set(container, pixelume);\n return controller;\n}\n\nfunction assertClient(value: unknown): asserts value is PixelumeClient {\n if (typeof value !== 'object' || value === null)\n throw new TypeError('Pixelume widget requires a client');\n const auth = Reflect.get(value, 'auth') as unknown;\n if (typeof auth !== 'object' || auth === null)\n throw new TypeError('Pixelume widget requires client.auth');\n for (const method of ['login', 'logout', 'subscribe'] as const) {\n if (typeof Reflect.get(auth, method) !== 'function') {\n throw new TypeError(`Pixelume widget requires client.auth.${method}()`);\n }\n }\n}\n\nfunction resolveDocument(container: HTMLElement | undefined): Document {\n if (container) {\n if (typeof container.attachShadow !== 'function') {\n throw new TypeError('Pixelume widget container must be an HTMLElement');\n }\n return container.ownerDocument;\n }\n const candidate = Reflect.get(globalThis, 'document') as unknown;\n if (typeof candidate !== 'object' || candidate === null || !('createElement' in candidate)) {\n throw new Error('Pixelume widget can only be mounted in a browser document');\n }\n return candidate as Document;\n}\n\nfunction resolvePosition(value: PixelumeWidgetPosition | undefined): PixelumeWidgetPosition {\n if (value === undefined) return DEFAULT_POSITION;\n if (!POSITIONS.includes(value)) {\n throw new Error(`Pixelume widget position must be one of: ${POSITIONS.join(', ')}`);\n }\n return value;\n}\n\nfunction resolveNumber(\n name: string,\n value: number | undefined,\n fallback: number,\n minimum: number,\n maximum: number,\n): number {\n if (value === undefined) return fallback;\n if (!Number.isInteger(value) || value < minimum || value > maximum) {\n throw new Error(`Pixelume widget ${name} must be an integer from ${minimum} to ${maximum}`);\n }\n return value;\n}\n\nfunction button(document: Document, className: string, label: string): HTMLButtonElement {\n const element = document.createElement('button');\n element.className = className;\n element.setAttribute('aria-label', label);\n return element;\n}\n\nfunction textElement(\n document: Document,\n tag: 'p',\n className: string,\n text: string,\n): HTMLParagraphElement {\n const element = document.createElement(tag);\n element.className = className;\n element.textContent = text;\n return element;\n}\n\nfunction notice(document: Document, text: string): HTMLParagraphElement {\n const element = textElement(document, 'p', 'notice', text);\n element.setAttribute('role', 'alert');\n return element;\n}\n\nfunction status(document: Document, text: string): HTMLParagraphElement {\n const element = textElement(document, 'p', 'message sync', text);\n element.setAttribute('role', 'status');\n return element;\n}\n\n/** Cloud status only after the game explicitly saved or deleted; nothing is simulated. */\nfunction syncMessage(activity: PixelumeSavesActivity | null): string | null {\n if (activity === null) return null;\n if (activity.pendingWrites > 0) return 'Salvando na nuvem…';\n const last = activity.last;\n if (last === null || (last.operation !== 'save' && last.operation !== 'delete')) return null;\n if (last.outcome === 'conflict') {\n return 'Há um progresso mais recente na nuvem. O jogo vai decidir o que manter.';\n }\n if (last.outcome === 'error') return 'Não foi possível salvar na nuvem agora.';\n const time = new Date(last.at).toLocaleTimeString('pt-BR', {\n hour: '2-digit',\n minute: '2-digit',\n });\n return last.operation === 'save'\n ? `Progresso salvo na nuvem às ${time}.`\n : `Save removido da nuvem às ${time}.`;\n}\n\nfunction anonymousNotice(\n state: Extract<PixelumeAuthState, { status: 'anonymous' }>,\n): string | null {\n const error = state.error;\n if (error === null) return null;\n if (error.kind === 'auth' && Reflect.get(error, 'reason') === 'game_unavailable') {\n return 'O acesso deste jogo à Pixelume está indisponível no momento. Você pode continuar jogando.';\n }\n // An ended session is already explained by the \"expired\" title and message.\n if (state.reason === 'expired') return null;\n return 'Não foi possível verificar sua conta. Você pode continuar jogando e tentar novamente.';\n}\n\nfunction renderLoading(\n document: Document,\n content: HTMLElement,\n status: 'uninitialized' | 'redirecting' | 'exchanging',\n): void {\n const labels = {\n uninitialized: 'Preparando sua conta…',\n redirecting: 'Abrindo o Pixelume Auth…',\n exchanging: 'Concluindo a entrada…',\n } as const;\n content.setAttribute('aria-busy', 'true');\n const visual = document.createElement('div');\n visual.className = 'loading';\n visual.setAttribute('aria-hidden', 'true');\n for (let index = 0; index < 2; index += 1) {\n const line = document.createElement('span');\n line.className = 'loading-line';\n visual.append(line);\n }\n const label = textElement(document, 'p', 'sr-only', labels[status]);\n content.append(visual, label);\n}\n\nfunction playerInitial(displayName: string): string {\n return Array.from(displayName.trim())[0]?.toLocaleUpperCase('pt-BR') ?? 'P';\n}\n\nfunction anonymousTitle(state: Extract<PixelumeAuthState, { status: 'anonymous' }>): string {\n if (state.reason === 'logged_out') return 'Você saiu deste jogo';\n if (state.reason === 'expired') return 'Sua sessão expirou';\n if (state.reason === 'cancelled') return 'Entrada cancelada';\n return 'Sua conta Pixelume';\n}\n\nfunction anonymousMessage(state: Extract<PixelumeAuthState, { status: 'anonymous' }>): string {\n if (state.reason === 'logged_out') return 'Entre novamente quando quiser.';\n if (state.reason === 'expired') return 'Entre novamente para continuar com sua conta.';\n if (state.reason === 'cancelled')\n return 'Você pode continuar como visitante ou tentar novamente.';\n return 'Entre pelo portal seguro da Pixelume. Sua senha nunca passa pelo jogo.';\n}\n"]}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Version of the SDK package. Kept in sync with `package.json` (checked by tests and by the release workflow). */
2
- export declare const SDK_VERSION = "0.2.0";
2
+ export declare const SDK_VERSION = "0.4.0";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  /** Version of the SDK package. Kept in sync with `package.json` (checked by tests and by the release workflow). */
2
- export const SDK_VERSION = '0.2.0';
2
+ export const SDK_VERSION = '0.4.0';
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,mHAAmH;AACnH,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC","sourcesContent":["/** Version of the SDK package. Kept in sync with `package.json` (checked by tests and by the release workflow). */\nexport const SDK_VERSION = '0.2.0';\n"]}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,mHAAmH;AACnH,MAAM,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC","sourcesContent":["/** Version of the SDK package. Kept in sync with `package.json` (checked by tests and by the release workflow). */\nexport const SDK_VERSION = '0.4.0';\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pixelume/sdk",
3
- "version": "0.2.0",
4
- "description": "SDK TypeScript da Pixelume para jogos web: SSO com PKCE e cliente seguro para a Pixelume API.",
3
+ "version": "0.4.0",
4
+ "description": "SDK TypeScript da Pixelume para jogos web: core headless, SSO com PKCE e widget oficial opcional.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Erickson Nascimento",
7
7
  "repository": {
@@ -25,6 +25,10 @@
25
25
  "types": "./dist/index.d.ts",
26
26
  "import": "./dist/index.js"
27
27
  },
28
+ "./ui": {
29
+ "types": "./dist/ui/index.d.ts",
30
+ "import": "./dist/ui/index.js"
31
+ },
28
32
  "./package.json": "./package.json"
29
33
  },
30
34
  "types": "./dist/index.d.ts",
@@ -61,6 +65,7 @@
61
65
  "@eslint/js": "10.0.1",
62
66
  "@types/node": "^24.10.1",
63
67
  "eslint": "10.12.0",
68
+ "happy-dom": "21.0.1",
64
69
  "prettier": "3.9.10",
65
70
  "publint": "0.3.25",
66
71
  "typescript": "6.0.3",