@pixelume/sdk 0.5.0 → 0.6.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/CHANGELOG.md +31 -1
- package/README.md +38 -3
- package/dist/core/PixelumeClient.d.ts +10 -2
- package/dist/core/PixelumeClient.js +19 -2
- package/dist/core/PixelumeClient.js.map +1 -1
- package/dist/core/config.d.ts +14 -1
- package/dist/core/config.js +81 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/types.d.ts +49 -0
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js.map +1 -1
- package/dist/modules/analytics/AnalyticsModule.d.ts +93 -0
- package/dist/modules/analytics/AnalyticsModule.js +461 -0
- package/dist/modules/analytics/AnalyticsModule.js.map +1 -0
- package/dist/modules/analytics/AnalyticsProvider.d.ts +51 -0
- package/dist/modules/analytics/AnalyticsProvider.js +23 -0
- package/dist/modules/analytics/AnalyticsProvider.js.map +1 -0
- package/dist/modules/analytics/PostHogAnalyticsProvider.d.ts +39 -0
- package/dist/modules/analytics/PostHogAnalyticsProvider.js +255 -0
- package/dist/modules/analytics/PostHogAnalyticsProvider.js.map +1 -0
- package/dist/modules/analytics/events.d.ts +30 -0
- package/dist/modules/analytics/events.js +153 -0
- package/dist/modules/analytics/events.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,34 @@ conter mudanças incompatíveis, sempre listadas em **Changed/Removed**.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.0] - 2026-10-16
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `pixelume.analytics` (opt-in): `track`, `startSession`/`endSession` (sessão de **jogo**, idempotentes),
|
|
15
|
+
`levelStarted`/`levelCompleted`/`levelFailed`, `setConsent`, `isEnabled` e os estados `consent`,
|
|
16
|
+
`status` e `sessionId`. Catálogo estável: `game_session_started`, `game_session_ended` (com
|
|
17
|
+
`duration_ms` e `visible_duration_ms`), `level_started`, `level_completed`, `level_failed`. Todo
|
|
18
|
+
evento leva `game_id`, `game_version`, `sdk_version`, `session_id` e `platform: 'web'`, protegidos
|
|
19
|
+
contra sobrescrita; nomes e propriedades são validados e limitados.
|
|
20
|
+
- Opção `analytics: { posthog: { projectToken, host }, gameVersion, rememberConsent, onIssue }`, campo
|
|
21
|
+
`config.analytics` e tipos `PixelumeAnalyticsApi`, `PixelumeAnalyticsOptions`, `PixelumePostHogOptions`,
|
|
22
|
+
`PixelumeAnalyticsConsent`, `PixelumeAnalyticsStatus`, `PixelumeAnalyticsProperties`,
|
|
23
|
+
`PixelumeAnalyticsIssue`. Token, host ou `gameVersion` vazios (variável de build definida sem valor)
|
|
24
|
+
equivalem a ausentes: analytics fica no-op, ou o evento sai sem `game_version`.
|
|
25
|
+
- Provider PostHog: `posthog-js` (`1.435.8`, exata) carregado por `import()` dinâmico **somente após o
|
|
26
|
+
consentimento**, em chunk separado (~52 KB gzip); instância própria com storage prefixado; autocapture,
|
|
27
|
+
pageviews, replay, exceções, surveys, flags/remote config e scripts externos desligados; URLs sem query
|
|
28
|
+
string. Revogar descarta o que estava pendente (inclusive o lote interno do PostHog), reseta a
|
|
29
|
+
identidade e apaga somente os identificadores do próprio jogo (outro jogo na mesma origem, mesmo com
|
|
30
|
+
`gameId` de mesmo prefixo, não é afetado). Login identifica com `player.id`; logout/expiração separam.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- O pacote passa a ter uma dependência de runtime (`posthog-js`), baixada pelo navegador apenas com
|
|
35
|
+
analytics configurado e consentido. Sem a opção `analytics`, o comportamento é idêntico ao da 0.5.0
|
|
36
|
+
(no-op: sem rede, storage ou listeners); o chunk principal cresce ~5 KB gzip.
|
|
37
|
+
|
|
10
38
|
## [0.5.0] - 2026-10-14
|
|
11
39
|
|
|
12
40
|
### Added
|
|
@@ -137,7 +165,9 @@ conter mudanças incompatíveis, sempre listadas em **Changed/Removed**.
|
|
|
137
165
|
`invalid_response`) e `isPixelumeError()`.
|
|
138
166
|
- Build ESM + `.d.ts`, CI e workflow de publicação via npm Trusted Publishing.
|
|
139
167
|
|
|
140
|
-
[Unreleased]: https://github.com/ericksonnascimento/pixelume-sdk/compare/v0.
|
|
168
|
+
[Unreleased]: https://github.com/ericksonnascimento/pixelume-sdk/compare/v0.6.0...HEAD
|
|
169
|
+
[0.6.0]: https://github.com/ericksonnascimento/pixelume-sdk/compare/v0.5.0...v0.6.0
|
|
170
|
+
[0.5.0]: https://github.com/ericksonnascimento/pixelume-sdk/compare/v0.4.0...v0.5.0
|
|
141
171
|
[0.4.0]: https://github.com/ericksonnascimento/pixelume-sdk/compare/v0.3.0...v0.4.0
|
|
142
172
|
[0.3.0]: https://github.com/ericksonnascimento/pixelume-sdk/compare/v0.2.0...v0.3.0
|
|
143
173
|
[0.2.0]: https://github.com/ericksonnascimento/pixelume-sdk/compare/v0.1.1...v0.2.0
|
package/README.md
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
SDK TypeScript para jogos web da Pixelume. O core headless continua sendo o padrão e há um widget
|
|
4
4
|
oficial opt-in em `@pixelume/sdk/ui`. Os dois modos usam a mesma autenticação centralizada com OAuth 2.0
|
|
5
5
|
Authorization Code + PKCE S256. A versão 0.4 adiciona **renovação automática de sessão** (refresh tokens
|
|
6
|
-
rotativos, só em memória) e **Cloud Saves** (`pixelume.saves`).
|
|
7
|
-
no jogo nem
|
|
6
|
+
rotativos, só em memória) e **Cloud Saves** (`pixelume.saves`). A versão 0.6 acrescenta **analytics
|
|
7
|
+
de gameplay opt-in** (`pixelume.analytics`), desacoplado do provider. Não há senha no jogo nem
|
|
8
|
+
`client_secret`; a única dependência de runtime (`posthog-js`) só é baixada pelo navegador depois do
|
|
9
|
+
consentimento do jogador, num chunk separado.
|
|
8
10
|
|
|
9
11
|
## Instalação
|
|
10
12
|
|
|
@@ -103,6 +105,34 @@ nunca sobrescreve, mescla ou reenvia sozinho, e não há fila offline. Saves sã
|
|
|
103
105
|
verificados: não servem como prova de conquista, ranking ou prêmio. Guia completo:
|
|
104
106
|
[Cloud Saves](docs/CLOUD_SAVES.md).
|
|
105
107
|
|
|
108
|
+
## Analytics (opt-in, com consentimento)
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
const pixelume = new Pixelume({
|
|
112
|
+
gameId: 'rail-puzzle',
|
|
113
|
+
analytics: {
|
|
114
|
+
posthog: { projectToken: '<POSTHOG_PROJECT_TOKEN>', host: '<POSTHOG_INGESTION_HOST>' },
|
|
115
|
+
gameVersion: '1.0.0',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (pixelume.analytics.consent === 'unknown') showConsentPrompt();
|
|
120
|
+
consentButton.onclick = () => pixelume.analytics.setConsent(true); // false revoga e apaga ids
|
|
121
|
+
|
|
122
|
+
pixelume.analytics.startSession(); // sessão de JOGO, não de login
|
|
123
|
+
pixelume.analytics.levelStarted(3);
|
|
124
|
+
pixelume.analytics.levelCompleted(3, { stars: 2 });
|
|
125
|
+
pixelume.analytics.levelFailed(4, 'out_of_moves');
|
|
126
|
+
pixelume.analytics.track('hint_used', { source: 'menu' });
|
|
127
|
+
pixelume.analytics.endSession();
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Sem a opção `analytics` (ou sem token/host), tudo é no-op: nenhuma rede, storage ou download. Com ela,
|
|
131
|
+
nada é enviado, gravado nem baixado antes de `setConsent(true)`; autocapture, pageviews, replay, surveys,
|
|
132
|
+
flags e scripts externos ficam desligados. Os jogos nunca importam `posthog-js` e nenhum método lança
|
|
133
|
+
exceção nem bloqueia o jogo. Catálogo de eventos, identidade, privacidade, CSP e impacto no bundle:
|
|
134
|
+
[guia de analytics](docs/ANALYTICS.md).
|
|
135
|
+
|
|
106
136
|
Importar ou instanciar o core não cria elementos, não injeta estilos e não ativa UI. O health check
|
|
107
137
|
também é totalmente independente: chamar `/health` nunca monta o widget.
|
|
108
138
|
|
|
@@ -217,6 +247,7 @@ aba apaga o indício. Sem `localStorage` disponível (bloqueado, adaptador injet
|
|
|
217
247
|
| `credentials` | `omit` | política dos módulos genéricos; Auth força `omit` para token/userinfo/revoke |
|
|
218
248
|
| `fetch` | `globalThis.fetch` | injetável para testes/instrumentação |
|
|
219
249
|
| `browser` | globals do browser | adaptador avançado para webviews e testes |
|
|
250
|
+
| `analytics` | — | opt-in: `{ posthog: { projectToken: 'phc_…', host }, gameVersion }`; sem ele, no-op ([guia](docs/ANALYTICS.md)) |
|
|
220
251
|
|
|
221
252
|
`authUrl` é informativo/configurável para ambientes, mas o SDK nunca concatena nem escolhe um retorno
|
|
222
253
|
via portal: `/v1/oauth/authorize` valida o cliente/callback e redireciona para o portal configurado na
|
|
@@ -249,6 +280,9 @@ uma ferramenta de teste, não UI oficial.
|
|
|
249
280
|
- CORS/Origin não são considerados autenticação. PKCE, URI exata, state e vínculos persistidos pela API
|
|
250
281
|
são os controles de segurança.
|
|
251
282
|
- XSS ainda pode ler um token que esteja em memória; jogos devem manter CSP e dependências sob controle.
|
|
283
|
+
- Analytics só transmite após consentimento explícito, usa o token **público** de projeto (`phc_…`; o SDK
|
|
284
|
+
recusa `phx_…`) e nunca recebe tokens, e-mail, nome, dados de save ou URLs com query string. O
|
|
285
|
+
identificador enviado após login é o `player.id` interno.
|
|
252
286
|
- Cloud Saves sempre passam pela autorização do backend (token do próprio jogo + escopo `saves`); o SDK
|
|
253
287
|
nunca envia `playerId`, e a UI nunca prova legitimidade de progresso.
|
|
254
288
|
|
|
@@ -296,7 +330,8 @@ npm run verify
|
|
|
296
330
|
npm run example:smoke # leitura opcional da API real
|
|
297
331
|
```
|
|
298
332
|
|
|
299
|
-
Mais detalhes: [
|
|
333
|
+
Mais detalhes: [analytics](docs/ANALYTICS.md), [renovação de sessão](docs/SESSION_RENEWAL.md),
|
|
334
|
+
[Cloud Saves](docs/CLOUD_SAVES.md),
|
|
300
335
|
[auditoria da Fase 4](docs/PHASE_04_CONTRACT_AUDIT.md), [widget oficial](docs/phase-03-widget.md),
|
|
301
336
|
[integração SSO](docs/SSO_INTEGRATION.md),
|
|
302
337
|
[auditoria do contrato](docs/SSO_CONTRACT_AUDIT.md), [arquitetura](docs/architecture.md) e
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type PixelumeAnalyticsApi } from '../modules/analytics/AnalyticsModule.js';
|
|
1
2
|
import { type PixelumeAuthApi, type PixelumeAuthState } from '../modules/auth/AuthModule.js';
|
|
2
3
|
import { type PixelumeHealthApi } from '../modules/health/HealthModule.js';
|
|
3
4
|
import { type PixelumeSavesApi } from '../modules/saves/SavesModule.js';
|
|
@@ -10,7 +11,7 @@ import type { PixelumeClientConfig, PixelumeClientOptions } from './types.js';
|
|
|
10
11
|
* const health = await pixelume.health.check();
|
|
11
12
|
* ```
|
|
12
13
|
*
|
|
13
|
-
* Feature modules are exposed as read-only properties: `health`, `auth` and `
|
|
14
|
+
* Feature modules are exposed as read-only properties: `health`, `auth`, `saves` and `analytics`. New modules are
|
|
14
15
|
* added as new properties without changing existing ones. Nothing touches the DOM; the visual
|
|
15
16
|
* widget lives in the opt-in `@pixelume/sdk/ui` entry point.
|
|
16
17
|
*
|
|
@@ -23,6 +24,11 @@ export declare class PixelumeClient {
|
|
|
23
24
|
readonly auth: PixelumeAuthApi;
|
|
24
25
|
/** Cloud Saves of the signed-in player for `gameId` (requires the `saves` scope). */
|
|
25
26
|
readonly saves: PixelumeSavesApi;
|
|
27
|
+
/**
|
|
28
|
+
* Gameplay analytics. A no-op unless the `analytics` option is configured, and silent until the
|
|
29
|
+
* player consents (`analytics.setConsent(true)`).
|
|
30
|
+
*/
|
|
31
|
+
readonly analytics: PixelumeAnalyticsApi;
|
|
26
32
|
constructor(options?: PixelumeClientOptions);
|
|
27
33
|
/**
|
|
28
34
|
* Initializes browser authentication once. Callback failures become an anonymous state so a
|
|
@@ -32,7 +38,9 @@ export declare class PixelumeClient {
|
|
|
32
38
|
/**
|
|
33
39
|
* Stops renewal timers and resume listeners and drops credentials from memory, without
|
|
34
40
|
* contacting the API. Call it when the game is torn down (SPA route change, hot reload); call
|
|
35
|
-
* `auth.logout()` first to also revoke the authorization.
|
|
41
|
+
* `auth.logout()` first to also revoke the authorization. Analytics stops delivering (an active
|
|
42
|
+
* game session is not ended; call `analytics.endSession()` first if play really ended). The
|
|
43
|
+
* client is unusable afterwards.
|
|
36
44
|
*/
|
|
37
45
|
destroy(): void;
|
|
38
46
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HttpClient } from '../http/HttpClient.js';
|
|
2
|
+
import { AnalyticsModule, } from '../modules/analytics/AnalyticsModule.js';
|
|
2
3
|
import { AuthModule, } from '../modules/auth/AuthModule.js';
|
|
3
4
|
import { HealthModule } from '../modules/health/HealthModule.js';
|
|
4
5
|
import { SavesModule } from '../modules/saves/SavesModule.js';
|
|
@@ -11,7 +12,7 @@ import { resolveConfig } from './config.js';
|
|
|
11
12
|
* const health = await pixelume.health.check();
|
|
12
13
|
* ```
|
|
13
14
|
*
|
|
14
|
-
* Feature modules are exposed as read-only properties: `health`, `auth` and `
|
|
15
|
+
* Feature modules are exposed as read-only properties: `health`, `auth`, `saves` and `analytics`. New modules are
|
|
15
16
|
* added as new properties without changing existing ones. Nothing touches the DOM; the visual
|
|
16
17
|
* widget lives in the opt-in `@pixelume/sdk/ui` entry point.
|
|
17
18
|
*
|
|
@@ -23,7 +24,13 @@ export class PixelumeClient {
|
|
|
23
24
|
auth;
|
|
24
25
|
/** Cloud Saves of the signed-in player for `gameId` (requires the `saves` scope). */
|
|
25
26
|
saves;
|
|
27
|
+
/**
|
|
28
|
+
* Gameplay analytics. A no-op unless the `analytics` option is configured, and silent until the
|
|
29
|
+
* player consents (`analytics.setConsent(true)`).
|
|
30
|
+
*/
|
|
31
|
+
analytics;
|
|
26
32
|
#authModule;
|
|
33
|
+
#analyticsModule;
|
|
27
34
|
constructor(options = {}) {
|
|
28
35
|
const resolved = resolveConfig(options);
|
|
29
36
|
this.config = Object.freeze({
|
|
@@ -37,6 +44,7 @@ export class PixelumeClient {
|
|
|
37
44
|
restoreAcrossSessions: resolved.crossSessionRestore !== null,
|
|
38
45
|
timeoutMs: resolved.timeoutMs,
|
|
39
46
|
credentials: resolved.credentials,
|
|
47
|
+
analytics: resolved.analytics === null ? null : resolved.analytics.provider.kind,
|
|
40
48
|
});
|
|
41
49
|
const http = new HttpClient({
|
|
42
50
|
baseUrl: resolved.apiUrl,
|
|
@@ -48,6 +56,12 @@ export class PixelumeClient {
|
|
|
48
56
|
this.#authModule = new AuthModule(http, resolved);
|
|
49
57
|
this.auth = this.#authModule;
|
|
50
58
|
this.saves = new SavesModule(this.#authModule.sessionManager, resolved);
|
|
59
|
+
this.#analyticsModule = new AnalyticsModule({
|
|
60
|
+
config: resolved.analytics,
|
|
61
|
+
auth: this.#authModule,
|
|
62
|
+
browser: resolved.browser,
|
|
63
|
+
});
|
|
64
|
+
this.analytics = this.#analyticsModule;
|
|
51
65
|
}
|
|
52
66
|
/**
|
|
53
67
|
* Initializes browser authentication once. Callback failures become an anonymous state so a
|
|
@@ -59,9 +73,12 @@ export class PixelumeClient {
|
|
|
59
73
|
/**
|
|
60
74
|
* Stops renewal timers and resume listeners and drops credentials from memory, without
|
|
61
75
|
* contacting the API. Call it when the game is torn down (SPA route change, hot reload); call
|
|
62
|
-
* `auth.logout()` first to also revoke the authorization.
|
|
76
|
+
* `auth.logout()` first to also revoke the authorization. Analytics stops delivering (an active
|
|
77
|
+
* game session is not ended; call `analytics.endSession()` first if play really ended). The
|
|
78
|
+
* client is unusable afterwards.
|
|
63
79
|
*/
|
|
64
80
|
destroy() {
|
|
81
|
+
this.#analyticsModule.dispose();
|
|
65
82
|
this.#authModule.dispose();
|
|
66
83
|
}
|
|
67
84
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PixelumeClient.js","sourceRoot":"","sources":["../../src/core/PixelumeClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EACL,UAAU,GAGX,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,YAAY,EAA0B,MAAM,mCAAmC,CAAC;AACzF,OAAO,EAAE,WAAW,EAAyB,MAAM,iCAAiC,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,cAAc;IAChB,MAAM,CAAuB;IAC7B,MAAM,CAAoB;IAC1B,IAAI,CAAkB;IAC/B,qFAAqF;IAC5E,KAAK,CAAmB;
|
|
1
|
+
{"version":3,"file":"PixelumeClient.js","sourceRoot":"","sources":["../../src/core/PixelumeClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EACL,eAAe,GAEhB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACL,UAAU,GAGX,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,YAAY,EAA0B,MAAM,mCAAmC,CAAC;AACzF,OAAO,EAAE,WAAW,EAAyB,MAAM,iCAAiC,CAAC;AACrF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,cAAc;IAChB,MAAM,CAAuB;IAC7B,MAAM,CAAoB;IAC1B,IAAI,CAAkB;IAC/B,qFAAqF;IAC5E,KAAK,CAAmB;IACjC;;;OAGG;IACM,SAAS,CAAuB;IAChC,WAAW,CAAa;IACxB,gBAAgB,CAAkB;IAE3C,YAAY,UAAiC,EAAE;QAC7C,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YAC1B,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;YAC3B,WAAW,EAAE,QAAQ,CAAC,WAAW;YACjC,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,cAAc,EAAE,QAAQ,CAAC,cAAc;YACvC,qBAAqB,EAAE,QAAQ,CAAC,mBAAmB,KAAK,IAAI;YAC5D,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,WAAW,EAAE,QAAQ,CAAC,WAAW;YACjC,SAAS,EAAE,QAAQ,CAAC,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI;SACjF,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC;YAC1B,OAAO,EAAE,QAAQ,CAAC,MAAM;YACxB,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,WAAW,EAAE,QAAQ,CAAC,WAAW;SAClC,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,WAAW,GAAG,IAAI,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;QACxE,IAAI,CAAC,gBAAgB,GAAG,IAAI,eAAe,CAAC;YAC1C,MAAM,EAAE,QAAQ,CAAC,SAAS;YAC1B,IAAI,EAAE,IAAI,CAAC,WAAW;YACtB,OAAO,EAAE,QAAQ,CAAC,OAAO;SAC1B,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,gBAAgB,CAAC;IACzC,CAAC;IAED;;;OAGG;IACH,UAAU;QACR,OAAO,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC;IACvC,CAAC;IAED;;;;;;OAMG;IACH,OAAO;QACL,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;QAChC,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;CACF;AAED,8EAA8E;AAC9E,OAAO,EAAE,cAAc,IAAI,QAAQ,EAAE,CAAC","sourcesContent":["import { HttpClient } from '../http/HttpClient.js';\nimport {\n AnalyticsModule,\n type PixelumeAnalyticsApi,\n} from '../modules/analytics/AnalyticsModule.js';\nimport {\n AuthModule,\n type PixelumeAuthApi,\n type PixelumeAuthState,\n} from '../modules/auth/AuthModule.js';\nimport { HealthModule, type PixelumeHealthApi } from '../modules/health/HealthModule.js';\nimport { SavesModule, type PixelumeSavesApi } from '../modules/saves/SavesModule.js';\nimport { resolveConfig } from './config.js';\nimport type { PixelumeClientConfig, PixelumeClientOptions } from './types.js';\n\n/**\n * Entry point of the SDK. Create one instance per API/game and share it.\n *\n * ```ts\n * const pixelume = new PixelumeClient({ gameId: 'rail-puzzle' });\n * const health = await pixelume.health.check();\n * ```\n *\n * Feature modules are exposed as read-only properties: `health`, `auth`, `saves` and `analytics`. New modules are\n * added as new properties without changing existing ones. Nothing touches the DOM; the visual\n * widget lives in the opt-in `@pixelume/sdk/ui` entry point.\n *\n * @throws {PixelumeConfigError} when the options are invalid.\n */\nexport class PixelumeClient {\n readonly config: PixelumeClientConfig;\n readonly health: PixelumeHealthApi;\n readonly auth: PixelumeAuthApi;\n /** Cloud Saves of the signed-in player for `gameId` (requires the `saves` scope). */\n readonly saves: PixelumeSavesApi;\n /**\n * Gameplay analytics. A no-op unless the `analytics` option is configured, and silent until the\n * player consents (`analytics.setConsent(true)`).\n */\n readonly analytics: PixelumeAnalyticsApi;\n readonly #authModule: AuthModule;\n readonly #analyticsModule: AnalyticsModule;\n\n constructor(options: PixelumeClientOptions = {}) {\n const resolved = resolveConfig(options);\n this.config = Object.freeze({\n apiUrl: resolved.apiUrl,\n gameId: resolved.gameId,\n clientId: resolved.clientId,\n redirectUri: resolved.redirectUri,\n scope: resolved.scope,\n authUrl: resolved.authUrl,\n restoreSession: resolved.restoreSession,\n restoreAcrossSessions: resolved.crossSessionRestore !== null,\n timeoutMs: resolved.timeoutMs,\n credentials: resolved.credentials,\n analytics: resolved.analytics === null ? null : resolved.analytics.provider.kind,\n });\n const http = new HttpClient({\n baseUrl: resolved.apiUrl,\n fetch: resolved.fetch,\n timeoutMs: resolved.timeoutMs,\n credentials: resolved.credentials,\n });\n this.health = new HealthModule(http);\n this.#authModule = new AuthModule(http, resolved);\n this.auth = this.#authModule;\n this.saves = new SavesModule(this.#authModule.sessionManager, resolved);\n this.#analyticsModule = new AnalyticsModule({\n config: resolved.analytics,\n auth: this.#authModule,\n browser: resolved.browser,\n });\n this.analytics = this.#analyticsModule;\n }\n\n /**\n * Initializes browser authentication once. Callback failures become an anonymous state so a\n * game can always continue as a visitor; inspect `pixelume.auth.state.error` for diagnostics.\n */\n initialize(): Promise<PixelumeAuthState> {\n return this.#authModule.initialize();\n }\n\n /**\n * Stops renewal timers and resume listeners and drops credentials from memory, without\n * contacting the API. Call it when the game is torn down (SPA route change, hot reload); call\n * `auth.logout()` first to also revoke the authorization. Analytics stops delivering (an active\n * game session is not ended; call `analytics.endSession()` first if play really ended). The\n * client is unusable afterwards.\n */\n destroy(): void {\n this.#analyticsModule.dispose();\n this.#authModule.dispose();\n }\n}\n\n/** Concise alias for new integrations; `PixelumeClient` remains supported. */\nexport { PixelumeClient as Pixelume };\n"]}
|
package/dist/core/config.d.ts
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import type { CredentialsPolicy, FetchLike } from '../http/types.js';
|
|
2
|
-
import type { PixelumeClientOptions } from './types.js';
|
|
2
|
+
import type { PixelumeAnalyticsIssue, PixelumeClientOptions } from './types.js';
|
|
3
3
|
export declare const DEFAULT_API_URL = "https://api.pixelume.com.br";
|
|
4
4
|
export declare const DEFAULT_AUTH_URL = "https://auth.pixelume.com.br";
|
|
5
5
|
export declare const DEFAULT_TIMEOUT_MS = 10000;
|
|
6
|
+
/** Validated analytics configuration. `null` in `ResolvedConfig` means "no-op". */
|
|
7
|
+
export interface ResolvedAnalyticsConfig {
|
|
8
|
+
readonly provider: {
|
|
9
|
+
readonly kind: 'posthog';
|
|
10
|
+
readonly token: string;
|
|
11
|
+
readonly host: string;
|
|
12
|
+
};
|
|
13
|
+
readonly gameId: string;
|
|
14
|
+
readonly gameVersion: string | null;
|
|
15
|
+
readonly rememberConsent: boolean;
|
|
16
|
+
readonly onIssue: ((issue: PixelumeAnalyticsIssue) => void) | null;
|
|
17
|
+
}
|
|
6
18
|
export interface ResolvedConfig {
|
|
7
19
|
apiUrl: string;
|
|
8
20
|
gameId: string | null;
|
|
@@ -20,6 +32,7 @@ export interface ResolvedConfig {
|
|
|
20
32
|
timeoutMs: number;
|
|
21
33
|
credentials: CredentialsPolicy;
|
|
22
34
|
fetch: FetchLike;
|
|
35
|
+
analytics: ResolvedAnalyticsConfig | null;
|
|
23
36
|
}
|
|
24
37
|
export declare function resolveConfig(options: PixelumeClientOptions): ResolvedConfig;
|
|
25
38
|
/**
|
package/dist/core/config.js
CHANGED
|
@@ -6,6 +6,8 @@ const MAX_TIMEOUT_MS = 2_147_483_647; // setTimeout limit
|
|
|
6
6
|
const GAME_ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/u;
|
|
7
7
|
const CLIENT_ID_PATTERN = /^[A-Za-z0-9._-]{1,128}$/u;
|
|
8
8
|
const CREDENTIALS = ['omit', 'same-origin', 'include'];
|
|
9
|
+
const POSTHOG_PROJECT_TOKEN_PATTERN = /^phc_[A-Za-z0-9_-]{8,128}$/u;
|
|
10
|
+
const GAME_VERSION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/u;
|
|
9
11
|
export function resolveConfig(options) {
|
|
10
12
|
const allowInsecureHttp = options.allowInsecureHttp === true;
|
|
11
13
|
const gameId = options.gameId === undefined ? null : validateGameId(options.gameId);
|
|
@@ -23,8 +25,87 @@ export function resolveConfig(options) {
|
|
|
23
25
|
timeoutMs: options.timeoutMs === undefined ? DEFAULT_TIMEOUT_MS : validateTimeout(options.timeoutMs),
|
|
24
26
|
credentials: validateCredentials(options.credentials ?? 'omit'),
|
|
25
27
|
fetch: resolveFetch(options.fetch),
|
|
28
|
+
analytics: resolveAnalytics(options.analytics, gameId, allowInsecureHttp),
|
|
26
29
|
};
|
|
27
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Missing configuration (no object, `enabled: false`, empty token or host) is a no-op, so a build
|
|
33
|
+
* without the variables keeps working. A value that is present but wrong fails fast, like every
|
|
34
|
+
* other option; a personal API key is always refused.
|
|
35
|
+
*/
|
|
36
|
+
function resolveAnalytics(value, gameId, allowInsecureHttp) {
|
|
37
|
+
if (value === undefined)
|
|
38
|
+
return null;
|
|
39
|
+
// Validated at runtime: JavaScript callers may pass anything.
|
|
40
|
+
const raw = value;
|
|
41
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
|
|
42
|
+
throw new PixelumeConfigError('analytics must be an object');
|
|
43
|
+
}
|
|
44
|
+
const enabled = validateBoolean('analytics.enabled', value.enabled ?? true);
|
|
45
|
+
const provider = value.provider ?? 'posthog';
|
|
46
|
+
if (provider !== 'posthog')
|
|
47
|
+
throw new PixelumeConfigError('analytics.provider must be "posthog"');
|
|
48
|
+
const rememberConsent = validateBoolean('analytics.rememberConsent', value.rememberConsent ?? true);
|
|
49
|
+
const onIssue = value.onIssue;
|
|
50
|
+
if (onIssue !== undefined && typeof onIssue !== 'function') {
|
|
51
|
+
throw new PixelumeConfigError('analytics.onIssue must be a function');
|
|
52
|
+
}
|
|
53
|
+
// Like token and host, an empty value (build variable defined but blank) means "not provided".
|
|
54
|
+
const rawGameVersion = optionalString('analytics.gameVersion', value.gameVersion);
|
|
55
|
+
const gameVersion = rawGameVersion === null ? null : validateGameVersion(rawGameVersion);
|
|
56
|
+
const posthog = value.posthog;
|
|
57
|
+
if (posthog !== undefined && (typeof posthog !== 'object' || posthog === null)) {
|
|
58
|
+
throw new PixelumeConfigError('analytics.posthog must be an object');
|
|
59
|
+
}
|
|
60
|
+
const token = optionalString('analytics.posthog.projectToken', value.posthog?.projectToken);
|
|
61
|
+
const host = optionalString('analytics.posthog.host', value.posthog?.host);
|
|
62
|
+
if (token !== null)
|
|
63
|
+
validatePostHogToken(token);
|
|
64
|
+
const normalizedHost = host === null ? null : normalizeAnalyticsHost(host, allowInsecureHttp);
|
|
65
|
+
if (!enabled || token === null || normalizedHost === null)
|
|
66
|
+
return null;
|
|
67
|
+
if (gameId === null) {
|
|
68
|
+
throw new PixelumeConfigError('analytics requires the client to be created with a gameId');
|
|
69
|
+
}
|
|
70
|
+
return Object.freeze({
|
|
71
|
+
provider: Object.freeze({ kind: 'posthog', token, host: normalizedHost }),
|
|
72
|
+
gameId,
|
|
73
|
+
gameVersion,
|
|
74
|
+
rememberConsent,
|
|
75
|
+
onIssue: onIssue ?? null,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
function optionalString(name, value) {
|
|
79
|
+
if (value === undefined || value === null)
|
|
80
|
+
return null;
|
|
81
|
+
if (typeof value !== 'string')
|
|
82
|
+
throw new PixelumeConfigError(`${name} must be a string`);
|
|
83
|
+
const trimmed = value.trim();
|
|
84
|
+
return trimmed === '' ? null : trimmed;
|
|
85
|
+
}
|
|
86
|
+
function validatePostHogToken(token) {
|
|
87
|
+
if (/^phx_/u.test(token)) {
|
|
88
|
+
throw new PixelumeConfigError('analytics.posthog.projectToken must be the public project token (phc_...); personal API keys (phx_...) must never reach the browser');
|
|
89
|
+
}
|
|
90
|
+
if (!POSTHOG_PROJECT_TOKEN_PATTERN.test(token)) {
|
|
91
|
+
throw new PixelumeConfigError('analytics.posthog.projectToken must be a PostHog project token (phc_...)');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** Same rules as `apiUrl`; a path is allowed for reverse proxies (e.g. `https://example.com/ingest`). */
|
|
95
|
+
function normalizeAnalyticsHost(value, allowInsecureHttp) {
|
|
96
|
+
try {
|
|
97
|
+
return normalizeApiUrl(value, allowInsecureHttp);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
throw new PixelumeConfigError('analytics.posthog.host must be an https: URL without credentials, query or fragment, e.g. https://us.i.posthog.com');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function validateGameVersion(value) {
|
|
104
|
+
if (typeof value !== 'string' || !GAME_VERSION_PATTERN.test(value)) {
|
|
105
|
+
throw new PixelumeConfigError('analytics.gameVersion must be 1-64 letters, digits, dots, plus signs, underscores or hyphens');
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
28
109
|
function resolveRedirectUri(options, allowInsecureHttp) {
|
|
29
110
|
const value = options.redirectUri ?? defaultRedirectUri(options.browser);
|
|
30
111
|
return value === null ? null : normalizeRedirectUri(value, allowInsecureHttp);
|
package/dist/core/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CredentialsPolicy, FetchLike } from '../http/types.js';\nimport { PixelumeConfigError } from './errors.js';\nimport type { PixelumeClientOptions } from './types.js';\n\nexport const DEFAULT_API_URL = 'https://api.pixelume.com.br';\nexport const DEFAULT_AUTH_URL = 'https://auth.pixelume.com.br';\nexport const DEFAULT_TIMEOUT_MS = 10_000;\nconst MAX_TIMEOUT_MS = 2_147_483_647; // setTimeout limit\nconst GAME_ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/u;\nconst CLIENT_ID_PATTERN = /^[A-Za-z0-9._-]{1,128}$/u;\nconst CREDENTIALS: readonly CredentialsPolicy[] = ['omit', 'same-origin', 'include'];\n\nexport interface ResolvedConfig {\n apiUrl: string;\n gameId: string | null;\n clientId: string | null;\n redirectUri: string | null;\n scope: 'profile' | 'profile saves';\n authUrl: string;\n restoreSession: boolean;\n /** `null` when cross-session restoration is disabled. */\n crossSessionRestore: { readonly maxAgeMs: number } | null;\n allowInsecureHttp: boolean;\n browser: PixelumeClientOptions['browser'];\n timeoutMs: number;\n credentials: CredentialsPolicy;\n fetch: FetchLike;\n}\n\nexport function resolveConfig(options: PixelumeClientOptions): ResolvedConfig {\n const allowInsecureHttp = options.allowInsecureHttp === true;\n const gameId = options.gameId === undefined ? null : validateGameId(options.gameId);\n return {\n apiUrl: normalizeApiUrl(options.apiUrl ?? DEFAULT_API_URL, allowInsecureHttp),\n authUrl: normalizeOrigin(options.authUrl ?? DEFAULT_AUTH_URL, 'authUrl', allowInsecureHttp),\n gameId,\n clientId: options.clientId === undefined ? gameId : validateClientId(options.clientId),\n redirectUri: resolveRedirectUri(options, allowInsecureHttp),\n scope: validateScope(options.scope ?? 'profile'),\n restoreSession: validateBoolean('restoreSession', options.restoreSession ?? true),\n crossSessionRestore: resolveCrossSessionRestore(options.restoreAcrossSessions),\n allowInsecureHttp,\n browser: options.browser,\n timeoutMs:\n options.timeoutMs === undefined ? DEFAULT_TIMEOUT_MS : validateTimeout(options.timeoutMs),\n credentials: validateCredentials(options.credentials ?? 'omit'),\n fetch: resolveFetch(options.fetch),\n };\n}\n\nfunction resolveRedirectUri(\n options: PixelumeClientOptions,\n allowInsecureHttp: boolean,\n): string | null {\n const value = options.redirectUri ?? defaultRedirectUri(options.browser);\n return value === null ? null : normalizeRedirectUri(value, allowInsecureHttp);\n}\n\nfunction validateScope(value: unknown): 'profile' | 'profile saves' {\n if (value !== 'profile' && value !== 'profile saves') {\n throw new PixelumeConfigError('scope must be \"profile\" or \"profile saves\"');\n }\n return value;\n}\n\nconst DAY_MS = 24 * 60 * 60_000;\n\nfunction resolveCrossSessionRestore(\n value: PixelumeClientOptions['restoreAcrossSessions'],\n): { readonly maxAgeMs: number } | null {\n if (value === undefined || value === false) return null;\n if (value === true) return Object.freeze({ maxAgeMs: 30 * DAY_MS });\n // Validated at runtime: JavaScript callers may pass anything.\n const raw: unknown = value;\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new PixelumeConfigError('restoreAcrossSessions must be a boolean or { maxAgeDays }');\n }\n const days: unknown = (raw as { maxAgeDays?: unknown }).maxAgeDays ?? 30;\n if (typeof days !== 'number' || !Number.isInteger(days) || days < 1 || days > 90) {\n throw new PixelumeConfigError(\n 'restoreAcrossSessions.maxAgeDays must be an integer from 1 to 90',\n );\n }\n return Object.freeze({ maxAgeMs: days * DAY_MS });\n}\n\nfunction validateBoolean(name: string, value: boolean): boolean {\n if (typeof value !== 'boolean') throw new PixelumeConfigError(`${name} must be a boolean`);\n return value;\n}\n\nfunction defaultRedirectUri(browser: PixelumeClientOptions['browser']): string | null {\n const location = Reflect.get(globalThis, 'location') as unknown;\n const current =\n browser?.currentUrl() ??\n (typeof location === 'object' && location !== null && 'href' in location\n ? String(location.href)\n : null);\n if (!current) return null;\n const url = new URL(current);\n return `${url.origin}/oauth/callback`;\n}\n\n/**\n * Accepts `https:` URLs, plus `http:` for loopback hosts (`localhost`, `*.localhost`, `127.0.0.0/8`,\n * `[::1]`). Other `http:` hosts (e.g. a LAN IP while testing on a phone) need `allowInsecureHttp`.\n */\nexport function normalizeApiUrl(value: string, allowInsecureHttp: boolean): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new PixelumeConfigError(\n 'apiUrl must be an absolute URL, e.g. https://api.pixelume.com.br',\n );\n }\n if (url.protocol !== 'https:' && url.protocol !== 'http:') {\n throw new PixelumeConfigError('apiUrl must use https: (or http: for local development)');\n }\n if (url.protocol === 'http:' && !allowInsecureHttp && !isLoopbackHost(url.hostname)) {\n throw new PixelumeConfigError(\n 'apiUrl must use https:. Plain http: is only accepted for localhost unless allowInsecureHttp is set',\n );\n }\n if (url.username || url.password) {\n throw new PixelumeConfigError('apiUrl must not contain credentials');\n }\n if (url.search || url.hash) {\n throw new PixelumeConfigError('apiUrl must not contain a query string or fragment');\n }\n return `${url.origin}${url.pathname.replace(/\\/+$/u, '')}`;\n}\n\nfunction normalizeOrigin(value: string, name: string, allowInsecureHttp: boolean): string {\n const normalized = normalizeApiUrl(value, allowInsecureHttp);\n const url = new URL(normalized);\n if (url.pathname !== '/') {\n throw new PixelumeConfigError(`${name} must be an origin without a path`);\n }\n return url.origin;\n}\n\nexport function normalizeRedirectUri(value: string, allowInsecureHttp: boolean): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new PixelumeConfigError('redirectUri must be an absolute URL registered by the API');\n }\n if (url.protocol !== 'https:' && url.protocol !== 'http:') {\n throw new PixelumeConfigError('redirectUri must use https: (or http: for local development)');\n }\n if (url.protocol === 'http:' && !allowInsecureHttp && !isLoopbackHost(url.hostname)) {\n throw new PixelumeConfigError('redirectUri must use https: outside localhost');\n }\n if (url.username || url.password || url.hash) {\n throw new PixelumeConfigError('redirectUri must not contain credentials or a fragment');\n }\n for (const key of ['code', 'state', 'error', 'error_description']) {\n if (url.searchParams.has(key)) {\n throw new PixelumeConfigError(\n `redirectUri must not contain the reserved query parameter \"${key}\"`,\n );\n }\n }\n return url.toString();\n}\n\nfunction isLoopbackHost(hostname: string): boolean {\n return (\n hostname === 'localhost' ||\n hostname.endsWith('.localhost') ||\n hostname === '[::1]' ||\n /^127(?:\\.\\d{1,3}){3}$/u.test(hostname)\n );\n}\n\n/**\n * Game identifiers are public, non-secret slugs (e.g. `rail-puzzle`): lowercase letters, digits\n * and inner hyphens, 1–64 characters. They identify a game, they never authorize anything.\n */\nexport function validateGameId(value: string): string {\n if (typeof value !== 'string' || !GAME_ID_PATTERN.test(value)) {\n throw new PixelumeConfigError(\n 'gameId must be a lowercase slug (a-z, 0-9 and inner hyphens, up to 64 characters)',\n );\n }\n return value;\n}\n\nexport function validateClientId(value: string): string {\n if (typeof value !== 'string' || !CLIENT_ID_PATTERN.test(value)) {\n throw new PixelumeConfigError(\n 'clientId must contain 1-128 letters, digits, dots, underscores or hyphens',\n );\n }\n return value;\n}\n\n/** For feature modules that need a game context (Cloud Saves). */\nexport function requireGameId(config: Pick<ResolvedConfig, 'gameId'>, feature: string): string {\n if (config.gameId === null) {\n throw new PixelumeConfigError(`${feature} requires the client to be created with a gameId`);\n }\n return config.gameId;\n}\n\nexport function validateTimeout(value: number): number {\n if (!Number.isInteger(value) || value <= 0 || value > MAX_TIMEOUT_MS) {\n throw new PixelumeConfigError('timeoutMs must be a positive integer number of milliseconds');\n }\n return value;\n}\n\nfunction validateCredentials(value: CredentialsPolicy): CredentialsPolicy {\n if (!CREDENTIALS.includes(value)) {\n throw new PixelumeConfigError(`credentials must be one of: ${CREDENTIALS.join(', ')}`);\n }\n return value;\n}\n\nfunction resolveFetch(custom: FetchLike | undefined): FetchLike {\n if (custom !== undefined) {\n if (typeof custom !== 'function') throw new PixelumeConfigError('fetch must be a function');\n return custom;\n }\n if (typeof globalThis.fetch !== 'function') {\n throw new PixelumeConfigError('No global fetch available; pass the fetch option');\n }\n // Looked up on each call so polyfills or test doubles installed later are honoured.\n return (input, init) => globalThis.fetch(input, init);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CredentialsPolicy, FetchLike } from '../http/types.js';\nimport { PixelumeConfigError } from './errors.js';\nimport type { PixelumeAnalyticsIssue, PixelumeClientOptions } from './types.js';\n\nexport const DEFAULT_API_URL = 'https://api.pixelume.com.br';\nexport const DEFAULT_AUTH_URL = 'https://auth.pixelume.com.br';\nexport const DEFAULT_TIMEOUT_MS = 10_000;\nconst MAX_TIMEOUT_MS = 2_147_483_647; // setTimeout limit\nconst GAME_ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/u;\nconst CLIENT_ID_PATTERN = /^[A-Za-z0-9._-]{1,128}$/u;\nconst CREDENTIALS: readonly CredentialsPolicy[] = ['omit', 'same-origin', 'include'];\nconst POSTHOG_PROJECT_TOKEN_PATTERN = /^phc_[A-Za-z0-9_-]{8,128}$/u;\nconst GAME_VERSION_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,63}$/u;\n\n/** Validated analytics configuration. `null` in `ResolvedConfig` means \"no-op\". */\nexport interface ResolvedAnalyticsConfig {\n readonly provider: { readonly kind: 'posthog'; readonly token: string; readonly host: string };\n readonly gameId: string;\n readonly gameVersion: string | null;\n readonly rememberConsent: boolean;\n readonly onIssue: ((issue: PixelumeAnalyticsIssue) => void) | null;\n}\n\nexport interface ResolvedConfig {\n apiUrl: string;\n gameId: string | null;\n clientId: string | null;\n redirectUri: string | null;\n scope: 'profile' | 'profile saves';\n authUrl: string;\n restoreSession: boolean;\n /** `null` when cross-session restoration is disabled. */\n crossSessionRestore: { readonly maxAgeMs: number } | null;\n allowInsecureHttp: boolean;\n browser: PixelumeClientOptions['browser'];\n timeoutMs: number;\n credentials: CredentialsPolicy;\n fetch: FetchLike;\n analytics: ResolvedAnalyticsConfig | null;\n}\n\nexport function resolveConfig(options: PixelumeClientOptions): ResolvedConfig {\n const allowInsecureHttp = options.allowInsecureHttp === true;\n const gameId = options.gameId === undefined ? null : validateGameId(options.gameId);\n return {\n apiUrl: normalizeApiUrl(options.apiUrl ?? DEFAULT_API_URL, allowInsecureHttp),\n authUrl: normalizeOrigin(options.authUrl ?? DEFAULT_AUTH_URL, 'authUrl', allowInsecureHttp),\n gameId,\n clientId: options.clientId === undefined ? gameId : validateClientId(options.clientId),\n redirectUri: resolveRedirectUri(options, allowInsecureHttp),\n scope: validateScope(options.scope ?? 'profile'),\n restoreSession: validateBoolean('restoreSession', options.restoreSession ?? true),\n crossSessionRestore: resolveCrossSessionRestore(options.restoreAcrossSessions),\n allowInsecureHttp,\n browser: options.browser,\n timeoutMs:\n options.timeoutMs === undefined ? DEFAULT_TIMEOUT_MS : validateTimeout(options.timeoutMs),\n credentials: validateCredentials(options.credentials ?? 'omit'),\n fetch: resolveFetch(options.fetch),\n analytics: resolveAnalytics(options.analytics, gameId, allowInsecureHttp),\n };\n}\n\n/**\n * Missing configuration (no object, `enabled: false`, empty token or host) is a no-op, so a build\n * without the variables keeps working. A value that is present but wrong fails fast, like every\n * other option; a personal API key is always refused.\n */\nfunction resolveAnalytics(\n value: PixelumeClientOptions['analytics'],\n gameId: string | null,\n allowInsecureHttp: boolean,\n): ResolvedAnalyticsConfig | null {\n if (value === undefined) return null;\n // Validated at runtime: JavaScript callers may pass anything.\n const raw: unknown = value;\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new PixelumeConfigError('analytics must be an object');\n }\n const enabled = validateBoolean('analytics.enabled', value.enabled ?? true);\n const provider: unknown = value.provider ?? 'posthog';\n if (provider !== 'posthog') throw new PixelumeConfigError('analytics.provider must be \"posthog\"');\n const rememberConsent = validateBoolean(\n 'analytics.rememberConsent',\n value.rememberConsent ?? true,\n );\n const onIssue: unknown = value.onIssue;\n if (onIssue !== undefined && typeof onIssue !== 'function') {\n throw new PixelumeConfigError('analytics.onIssue must be a function');\n }\n // Like token and host, an empty value (build variable defined but blank) means \"not provided\".\n const rawGameVersion = optionalString('analytics.gameVersion', value.gameVersion);\n const gameVersion = rawGameVersion === null ? null : validateGameVersion(rawGameVersion);\n const posthog: unknown = value.posthog;\n if (posthog !== undefined && (typeof posthog !== 'object' || posthog === null)) {\n throw new PixelumeConfigError('analytics.posthog must be an object');\n }\n const token = optionalString('analytics.posthog.projectToken', value.posthog?.projectToken);\n const host = optionalString('analytics.posthog.host', value.posthog?.host);\n if (token !== null) validatePostHogToken(token);\n const normalizedHost = host === null ? null : normalizeAnalyticsHost(host, allowInsecureHttp);\n if (!enabled || token === null || normalizedHost === null) return null;\n if (gameId === null) {\n throw new PixelumeConfigError('analytics requires the client to be created with a gameId');\n }\n return Object.freeze({\n provider: Object.freeze({ kind: 'posthog' as const, token, host: normalizedHost }),\n gameId,\n gameVersion,\n rememberConsent,\n onIssue: (onIssue as ResolvedAnalyticsConfig['onIssue']) ?? null,\n });\n}\n\nfunction optionalString(name: string, value: unknown): string | null {\n if (value === undefined || value === null) return null;\n if (typeof value !== 'string') throw new PixelumeConfigError(`${name} must be a string`);\n const trimmed = value.trim();\n return trimmed === '' ? null : trimmed;\n}\n\nfunction validatePostHogToken(token: string): void {\n if (/^phx_/u.test(token)) {\n throw new PixelumeConfigError(\n 'analytics.posthog.projectToken must be the public project token (phc_...); personal API keys (phx_...) must never reach the browser',\n );\n }\n if (!POSTHOG_PROJECT_TOKEN_PATTERN.test(token)) {\n throw new PixelumeConfigError(\n 'analytics.posthog.projectToken must be a PostHog project token (phc_...)',\n );\n }\n}\n\n/** Same rules as `apiUrl`; a path is allowed for reverse proxies (e.g. `https://example.com/ingest`). */\nfunction normalizeAnalyticsHost(value: string, allowInsecureHttp: boolean): string {\n try {\n return normalizeApiUrl(value, allowInsecureHttp);\n } catch {\n throw new PixelumeConfigError(\n 'analytics.posthog.host must be an https: URL without credentials, query or fragment, e.g. https://us.i.posthog.com',\n );\n }\n}\n\nfunction validateGameVersion(value: unknown): string {\n if (typeof value !== 'string' || !GAME_VERSION_PATTERN.test(value)) {\n throw new PixelumeConfigError(\n 'analytics.gameVersion must be 1-64 letters, digits, dots, plus signs, underscores or hyphens',\n );\n }\n return value;\n}\n\nfunction resolveRedirectUri(\n options: PixelumeClientOptions,\n allowInsecureHttp: boolean,\n): string | null {\n const value = options.redirectUri ?? defaultRedirectUri(options.browser);\n return value === null ? null : normalizeRedirectUri(value, allowInsecureHttp);\n}\n\nfunction validateScope(value: unknown): 'profile' | 'profile saves' {\n if (value !== 'profile' && value !== 'profile saves') {\n throw new PixelumeConfigError('scope must be \"profile\" or \"profile saves\"');\n }\n return value;\n}\n\nconst DAY_MS = 24 * 60 * 60_000;\n\nfunction resolveCrossSessionRestore(\n value: PixelumeClientOptions['restoreAcrossSessions'],\n): { readonly maxAgeMs: number } | null {\n if (value === undefined || value === false) return null;\n if (value === true) return Object.freeze({ maxAgeMs: 30 * DAY_MS });\n // Validated at runtime: JavaScript callers may pass anything.\n const raw: unknown = value;\n if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {\n throw new PixelumeConfigError('restoreAcrossSessions must be a boolean or { maxAgeDays }');\n }\n const days: unknown = (raw as { maxAgeDays?: unknown }).maxAgeDays ?? 30;\n if (typeof days !== 'number' || !Number.isInteger(days) || days < 1 || days > 90) {\n throw new PixelumeConfigError(\n 'restoreAcrossSessions.maxAgeDays must be an integer from 1 to 90',\n );\n }\n return Object.freeze({ maxAgeMs: days * DAY_MS });\n}\n\nfunction validateBoolean(name: string, value: boolean): boolean {\n if (typeof value !== 'boolean') throw new PixelumeConfigError(`${name} must be a boolean`);\n return value;\n}\n\nfunction defaultRedirectUri(browser: PixelumeClientOptions['browser']): string | null {\n const location = Reflect.get(globalThis, 'location') as unknown;\n const current =\n browser?.currentUrl() ??\n (typeof location === 'object' && location !== null && 'href' in location\n ? String(location.href)\n : null);\n if (!current) return null;\n const url = new URL(current);\n return `${url.origin}/oauth/callback`;\n}\n\n/**\n * Accepts `https:` URLs, plus `http:` for loopback hosts (`localhost`, `*.localhost`, `127.0.0.0/8`,\n * `[::1]`). Other `http:` hosts (e.g. a LAN IP while testing on a phone) need `allowInsecureHttp`.\n */\nexport function normalizeApiUrl(value: string, allowInsecureHttp: boolean): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new PixelumeConfigError(\n 'apiUrl must be an absolute URL, e.g. https://api.pixelume.com.br',\n );\n }\n if (url.protocol !== 'https:' && url.protocol !== 'http:') {\n throw new PixelumeConfigError('apiUrl must use https: (or http: for local development)');\n }\n if (url.protocol === 'http:' && !allowInsecureHttp && !isLoopbackHost(url.hostname)) {\n throw new PixelumeConfigError(\n 'apiUrl must use https:. Plain http: is only accepted for localhost unless allowInsecureHttp is set',\n );\n }\n if (url.username || url.password) {\n throw new PixelumeConfigError('apiUrl must not contain credentials');\n }\n if (url.search || url.hash) {\n throw new PixelumeConfigError('apiUrl must not contain a query string or fragment');\n }\n return `${url.origin}${url.pathname.replace(/\\/+$/u, '')}`;\n}\n\nfunction normalizeOrigin(value: string, name: string, allowInsecureHttp: boolean): string {\n const normalized = normalizeApiUrl(value, allowInsecureHttp);\n const url = new URL(normalized);\n if (url.pathname !== '/') {\n throw new PixelumeConfigError(`${name} must be an origin without a path`);\n }\n return url.origin;\n}\n\nexport function normalizeRedirectUri(value: string, allowInsecureHttp: boolean): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new PixelumeConfigError('redirectUri must be an absolute URL registered by the API');\n }\n if (url.protocol !== 'https:' && url.protocol !== 'http:') {\n throw new PixelumeConfigError('redirectUri must use https: (or http: for local development)');\n }\n if (url.protocol === 'http:' && !allowInsecureHttp && !isLoopbackHost(url.hostname)) {\n throw new PixelumeConfigError('redirectUri must use https: outside localhost');\n }\n if (url.username || url.password || url.hash) {\n throw new PixelumeConfigError('redirectUri must not contain credentials or a fragment');\n }\n for (const key of ['code', 'state', 'error', 'error_description']) {\n if (url.searchParams.has(key)) {\n throw new PixelumeConfigError(\n `redirectUri must not contain the reserved query parameter \"${key}\"`,\n );\n }\n }\n return url.toString();\n}\n\nfunction isLoopbackHost(hostname: string): boolean {\n return (\n hostname === 'localhost' ||\n hostname.endsWith('.localhost') ||\n hostname === '[::1]' ||\n /^127(?:\\.\\d{1,3}){3}$/u.test(hostname)\n );\n}\n\n/**\n * Game identifiers are public, non-secret slugs (e.g. `rail-puzzle`): lowercase letters, digits\n * and inner hyphens, 1–64 characters. They identify a game, they never authorize anything.\n */\nexport function validateGameId(value: string): string {\n if (typeof value !== 'string' || !GAME_ID_PATTERN.test(value)) {\n throw new PixelumeConfigError(\n 'gameId must be a lowercase slug (a-z, 0-9 and inner hyphens, up to 64 characters)',\n );\n }\n return value;\n}\n\nexport function validateClientId(value: string): string {\n if (typeof value !== 'string' || !CLIENT_ID_PATTERN.test(value)) {\n throw new PixelumeConfigError(\n 'clientId must contain 1-128 letters, digits, dots, underscores or hyphens',\n );\n }\n return value;\n}\n\n/** For feature modules that need a game context (Cloud Saves). */\nexport function requireGameId(config: Pick<ResolvedConfig, 'gameId'>, feature: string): string {\n if (config.gameId === null) {\n throw new PixelumeConfigError(`${feature} requires the client to be created with a gameId`);\n }\n return config.gameId;\n}\n\nexport function validateTimeout(value: number): number {\n if (!Number.isInteger(value) || value <= 0 || value > MAX_TIMEOUT_MS) {\n throw new PixelumeConfigError('timeoutMs must be a positive integer number of milliseconds');\n }\n return value;\n}\n\nfunction validateCredentials(value: CredentialsPolicy): CredentialsPolicy {\n if (!CREDENTIALS.includes(value)) {\n throw new PixelumeConfigError(`credentials must be one of: ${CREDENTIALS.join(', ')}`);\n }\n return value;\n}\n\nfunction resolveFetch(custom: FetchLike | undefined): FetchLike {\n if (custom !== undefined) {\n if (typeof custom !== 'function') throw new PixelumeConfigError('fetch must be a function');\n return custom;\n }\n if (typeof globalThis.fetch !== 'function') {\n throw new PixelumeConfigError('No global fetch available; pass the fetch option');\n }\n // Looked up on each call so polyfills or test doubles installed later are honoured.\n return (input, init) => globalThis.fetch(input, init);\n}\n"]}
|
package/dist/core/types.d.ts
CHANGED
|
@@ -89,6 +89,53 @@ export interface PixelumeClientOptions {
|
|
|
89
89
|
allowInsecureHttp?: boolean;
|
|
90
90
|
/** Advanced/test-only browser capability injection. Defaults to the current browser globals. */
|
|
91
91
|
browser?: PixelumeBrowserAdapter;
|
|
92
|
+
/**
|
|
93
|
+
* Opt-in gameplay analytics (`pixelume.analytics`). Without this option, with `enabled: false` or
|
|
94
|
+
* without a project token and host, analytics is a no-op: no storage, no network, no provider.
|
|
95
|
+
* Even when configured, nothing is sent before `analytics.setConsent(true)`.
|
|
96
|
+
*/
|
|
97
|
+
analytics?: PixelumeAnalyticsOptions;
|
|
98
|
+
}
|
|
99
|
+
/** Analytics configuration of one game. Contains only public, browser-safe values. */
|
|
100
|
+
export interface PixelumeAnalyticsOptions {
|
|
101
|
+
/** Defaults to `true` when the object is present. `false` keeps analytics a no-op. */
|
|
102
|
+
readonly enabled?: boolean;
|
|
103
|
+
/** Transport used for authorized events. Only `'posthog'` exists today (and is the default). */
|
|
104
|
+
readonly provider?: 'posthog';
|
|
105
|
+
readonly posthog?: PixelumePostHogOptions;
|
|
106
|
+
/** Version of the game build (e.g. `'1.4.0'`), sent as `game_version`. */
|
|
107
|
+
readonly gameVersion?: string;
|
|
108
|
+
/**
|
|
109
|
+
* Remember the player's choice in `localStorage` (`pixelume.analytics.consent.<gameId>`, only
|
|
110
|
+
* `{ version, granted, at }`), so the next visit starts with it. Defaults to `true`. With `false`
|
|
111
|
+
* the game stores the choice itself and calls `setConsent()` on every start.
|
|
112
|
+
*/
|
|
113
|
+
readonly rememberConsent?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Development diagnostics: called when the SDK drops an event or property, or when the provider
|
|
116
|
+
* fails. Never receives property values, tokens or identifiers. Errors thrown here are ignored.
|
|
117
|
+
*/
|
|
118
|
+
readonly onIssue?: (issue: PixelumeAnalyticsIssue) => void;
|
|
119
|
+
}
|
|
120
|
+
export interface PixelumePostHogOptions {
|
|
121
|
+
/**
|
|
122
|
+
* Public **project** token (`phc_…`). Never a personal API key (`phx_…`), which is rejected.
|
|
123
|
+
* Empty or missing keeps analytics a no-op (e.g. a build without the variable).
|
|
124
|
+
*/
|
|
125
|
+
readonly projectToken?: string;
|
|
126
|
+
/**
|
|
127
|
+
* Ingestion host of the project's PostHog instance or of a reverse proxy, e.g.
|
|
128
|
+
* `https://us.i.posthog.com` or `https://eu.i.posthog.com`. Must match the project's region.
|
|
129
|
+
*/
|
|
130
|
+
readonly host?: string;
|
|
131
|
+
}
|
|
132
|
+
/** Why the SDK dropped analytics data or could not deliver it. Never contains values. */
|
|
133
|
+
export interface PixelumeAnalyticsIssue {
|
|
134
|
+
readonly code: 'invalid_event' | 'reserved_event' | 'invalid_level' | 'invalid_property' | 'reserved_property' | 'sensitive_property' | 'too_many_properties' | 'queue_full' | 'provider_unavailable' | 'provider_error';
|
|
135
|
+
/** Event involved, when the name is valid. */
|
|
136
|
+
readonly event: string | null;
|
|
137
|
+
/** Property key involved, when it is a valid key. */
|
|
138
|
+
readonly property: string | null;
|
|
92
139
|
}
|
|
93
140
|
export interface PixelumeCrossSessionRestoreOptions {
|
|
94
141
|
/**
|
|
@@ -110,6 +157,8 @@ export interface PixelumeClientConfig {
|
|
|
110
157
|
readonly restoreAcrossSessions: boolean;
|
|
111
158
|
readonly timeoutMs: number;
|
|
112
159
|
readonly credentials: PixelumeCredentialsPolicy;
|
|
160
|
+
/** Analytics provider configured for `pixelume.analytics`, or `null` when it is a no-op. */
|
|
161
|
+
readonly analytics: 'posthog' | null;
|
|
113
162
|
}
|
|
114
163
|
/** Options accepted by every SDK operation. */
|
|
115
164
|
export interface PixelumeRequestOptions {
|
package/dist/core/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { CredentialsPolicy, FetchLike } from '../http/types.js';\n\nexport type PixelumeCredentialsPolicy = CredentialsPolicy;\nexport type PixelumeFetch = FetchLike;\n\n/** Minimal browser storage contract used for transient PKCE state and a non-secret session hint. */\nexport interface PixelumeSessionStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\n/**\n * Browser capabilities used by the redirect flow. Usually omitted; injection is useful in tests,\n * embedded runtimes and non-standard webviews.\n */\nexport interface PixelumeBrowserAdapter {\n currentUrl(): string;\n navigate(url: string): void;\n replaceUrl(url: string): void;\n readonly sessionStorage: PixelumeSessionStorage;\n /**\n * Optional persistent storage (normally `localStorage`) for the non-secret cross-session hint\n * of `restoreAcrossSessions`. Never receives tokens. When an injected adapter omits it,\n * cross-session restoration is unavailable and the SDK behaves as without the option.\n */\n readonly persistentStorage?: PixelumeSessionStorage | null;\n /** Optional connectivity probe. Defaults to `navigator.onLine` (unknown counts as online). */\n isOnline?(): boolean;\n readonly crypto: Crypto;\n /**\n * Optional: calls `onResume` when the page may have been suspended (tab visible again, restored\n * from the back/forward cache, back online) and returns an unsubscribe function. When omitted,\n * the SDK listens to `visibilitychange`, `pageshow` and `online` on the browser globals.\n */\n subscribeResume?(onResume: () => void): () => void;\n}\n\nexport interface PixelumeClientOptions {\n /**\n * Base URL of the Pixelume API. Defaults to `https://api.pixelume.com.br`.\n * Must be `https:`; `http:` is accepted for loopback hosts during local development.\n */\n apiUrl?: string;\n /**\n * Public identifier of the game, e.g. `rail-puzzle`. Optional in Phase 1 because no current\n * API contract uses it; future modules that need it will fail fast with a config error.\n * It is an identifier, never an authorization credential.\n */\n gameId?: string;\n /**\n * OAuth public client identifier registered by the Pixelume API. Defaults to `gameId`.\n * This is public metadata, never a secret.\n */\n clientId?: string;\n /** Exact callback URI registered for the OAuth client. Defaults to `<origin>/oauth/callback`. */\n redirectUri?: string;\n /**\n * Scope requested by `auth.login()`. Use `'profile saves'` for Cloud Saves; the game must have\n * `saves` enabled in the Pixelume Game Registry. Defaults to `'profile'`.\n */\n scope?: 'profile' | 'profile saves';\n /** Pixelume Auth portal origin. Defaults to `https://auth.pixelume.com.br`. */\n authUrl?: string;\n /**\n * Reauthorize after a same-tab reload when a previous SDK session existed. The SDK stores only\n * a non-secret hint; access tokens remain memory-only. Defaults to `true`.\n */\n restoreSession?: boolean;\n /**\n * Opt-in: recover the account in a new tab or after the browser is reopened, while the central\n * Pixelume session is still valid, without asking for the password again. After a successful\n * login the SDK keeps a **non-secret** hint in `localStorage` (only \"this game had a login on\n * this browser\", the granted scope and when); on a later start without an in-memory session it\n * makes at most one authorization attempt with `prompt=none` — a brief top-level round trip that\n * never shows a screen. If the central session ended, the API answers `login_required`, the hint\n * is removed and the game continues as a visitor (`reason: 'session_ended'`), with no loop.\n * Tokens stay memory-only. `true` uses a 30-day hint; `{ maxAgeDays }` (1–90) changes it.\n * Defaults to `false`.\n */\n restoreAcrossSessions?: boolean | PixelumeCrossSessionRestoreOptions;\n /** Per-request timeout in milliseconds. Defaults to 10 000. */\n timeoutMs?: number;\n /**\n * `fetch` credentials policy used by modules that do not set their own. Defaults to `'omit'`.\n * Cross-origin cookies are subject to the API's CORS allowlist and to browser policies;\n * the SDK never assumes they work.\n */\n credentials?: PixelumeCredentialsPolicy;\n /** Custom `fetch` implementation (tests, SSR, instrumentation). Defaults to `globalThis.fetch`. */\n fetch?: PixelumeFetch;\n /** Allows `http:` for non-loopback hosts (e.g. a LAN IP). Never enable it in production. */\n allowInsecureHttp?: boolean;\n /** Advanced/test-only browser capability injection. Defaults to the current browser globals. */\n browser?: PixelumeBrowserAdapter;\n}\n\nexport interface PixelumeCrossSessionRestoreOptions {\n /**\n * Days after the last successful login during which a new tab/browser start tries to recover the\n * account. Defaults to 30 (the central session lifetime). Integer from 1 to 90.\n */\n readonly maxAgeDays?: number;\n}\n\n/** Effective, validated configuration of a client. */\nexport interface PixelumeClientConfig {\n readonly apiUrl: string;\n readonly gameId: string | null;\n readonly clientId: string | null;\n readonly redirectUri: string | null;\n readonly scope: 'profile' | 'profile saves';\n readonly authUrl: string;\n readonly restoreSession: boolean;\n /** Whether cross-session restoration (`restoreAcrossSessions`) is enabled. */\n readonly restoreAcrossSessions: boolean;\n readonly timeoutMs: number;\n readonly credentials: PixelumeCredentialsPolicy;\n}\n\n/** Options accepted by every SDK operation. */\nexport interface PixelumeRequestOptions {\n /** Aborts the operation; the promise rejects with `PixelumeAbortError`. */\n signal?: AbortSignal;\n /** Overrides the client timeout for this operation. */\n timeoutMs?: number;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { CredentialsPolicy, FetchLike } from '../http/types.js';\n\nexport type PixelumeCredentialsPolicy = CredentialsPolicy;\nexport type PixelumeFetch = FetchLike;\n\n/** Minimal browser storage contract used for transient PKCE state and a non-secret session hint. */\nexport interface PixelumeSessionStorage {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n}\n\n/**\n * Browser capabilities used by the redirect flow. Usually omitted; injection is useful in tests,\n * embedded runtimes and non-standard webviews.\n */\nexport interface PixelumeBrowserAdapter {\n currentUrl(): string;\n navigate(url: string): void;\n replaceUrl(url: string): void;\n readonly sessionStorage: PixelumeSessionStorage;\n /**\n * Optional persistent storage (normally `localStorage`) for the non-secret cross-session hint\n * of `restoreAcrossSessions`. Never receives tokens. When an injected adapter omits it,\n * cross-session restoration is unavailable and the SDK behaves as without the option.\n */\n readonly persistentStorage?: PixelumeSessionStorage | null;\n /** Optional connectivity probe. Defaults to `navigator.onLine` (unknown counts as online). */\n isOnline?(): boolean;\n readonly crypto: Crypto;\n /**\n * Optional: calls `onResume` when the page may have been suspended (tab visible again, restored\n * from the back/forward cache, back online) and returns an unsubscribe function. When omitted,\n * the SDK listens to `visibilitychange`, `pageshow` and `online` on the browser globals.\n */\n subscribeResume?(onResume: () => void): () => void;\n}\n\nexport interface PixelumeClientOptions {\n /**\n * Base URL of the Pixelume API. Defaults to `https://api.pixelume.com.br`.\n * Must be `https:`; `http:` is accepted for loopback hosts during local development.\n */\n apiUrl?: string;\n /**\n * Public identifier of the game, e.g. `rail-puzzle`. Optional in Phase 1 because no current\n * API contract uses it; future modules that need it will fail fast with a config error.\n * It is an identifier, never an authorization credential.\n */\n gameId?: string;\n /**\n * OAuth public client identifier registered by the Pixelume API. Defaults to `gameId`.\n * This is public metadata, never a secret.\n */\n clientId?: string;\n /** Exact callback URI registered for the OAuth client. Defaults to `<origin>/oauth/callback`. */\n redirectUri?: string;\n /**\n * Scope requested by `auth.login()`. Use `'profile saves'` for Cloud Saves; the game must have\n * `saves` enabled in the Pixelume Game Registry. Defaults to `'profile'`.\n */\n scope?: 'profile' | 'profile saves';\n /** Pixelume Auth portal origin. Defaults to `https://auth.pixelume.com.br`. */\n authUrl?: string;\n /**\n * Reauthorize after a same-tab reload when a previous SDK session existed. The SDK stores only\n * a non-secret hint; access tokens remain memory-only. Defaults to `true`.\n */\n restoreSession?: boolean;\n /**\n * Opt-in: recover the account in a new tab or after the browser is reopened, while the central\n * Pixelume session is still valid, without asking for the password again. After a successful\n * login the SDK keeps a **non-secret** hint in `localStorage` (only \"this game had a login on\n * this browser\", the granted scope and when); on a later start without an in-memory session it\n * makes at most one authorization attempt with `prompt=none` — a brief top-level round trip that\n * never shows a screen. If the central session ended, the API answers `login_required`, the hint\n * is removed and the game continues as a visitor (`reason: 'session_ended'`), with no loop.\n * Tokens stay memory-only. `true` uses a 30-day hint; `{ maxAgeDays }` (1–90) changes it.\n * Defaults to `false`.\n */\n restoreAcrossSessions?: boolean | PixelumeCrossSessionRestoreOptions;\n /** Per-request timeout in milliseconds. Defaults to 10 000. */\n timeoutMs?: number;\n /**\n * `fetch` credentials policy used by modules that do not set their own. Defaults to `'omit'`.\n * Cross-origin cookies are subject to the API's CORS allowlist and to browser policies;\n * the SDK never assumes they work.\n */\n credentials?: PixelumeCredentialsPolicy;\n /** Custom `fetch` implementation (tests, SSR, instrumentation). Defaults to `globalThis.fetch`. */\n fetch?: PixelumeFetch;\n /** Allows `http:` for non-loopback hosts (e.g. a LAN IP). Never enable it in production. */\n allowInsecureHttp?: boolean;\n /** Advanced/test-only browser capability injection. Defaults to the current browser globals. */\n browser?: PixelumeBrowserAdapter;\n /**\n * Opt-in gameplay analytics (`pixelume.analytics`). Without this option, with `enabled: false` or\n * without a project token and host, analytics is a no-op: no storage, no network, no provider.\n * Even when configured, nothing is sent before `analytics.setConsent(true)`.\n */\n analytics?: PixelumeAnalyticsOptions;\n}\n\n/** Analytics configuration of one game. Contains only public, browser-safe values. */\nexport interface PixelumeAnalyticsOptions {\n /** Defaults to `true` when the object is present. `false` keeps analytics a no-op. */\n readonly enabled?: boolean;\n /** Transport used for authorized events. Only `'posthog'` exists today (and is the default). */\n readonly provider?: 'posthog';\n readonly posthog?: PixelumePostHogOptions;\n /** Version of the game build (e.g. `'1.4.0'`), sent as `game_version`. */\n readonly gameVersion?: string;\n /**\n * Remember the player's choice in `localStorage` (`pixelume.analytics.consent.<gameId>`, only\n * `{ version, granted, at }`), so the next visit starts with it. Defaults to `true`. With `false`\n * the game stores the choice itself and calls `setConsent()` on every start.\n */\n readonly rememberConsent?: boolean;\n /**\n * Development diagnostics: called when the SDK drops an event or property, or when the provider\n * fails. Never receives property values, tokens or identifiers. Errors thrown here are ignored.\n */\n readonly onIssue?: (issue: PixelumeAnalyticsIssue) => void;\n}\n\nexport interface PixelumePostHogOptions {\n /**\n * Public **project** token (`phc_…`). Never a personal API key (`phx_…`), which is rejected.\n * Empty or missing keeps analytics a no-op (e.g. a build without the variable).\n */\n readonly projectToken?: string;\n /**\n * Ingestion host of the project's PostHog instance or of a reverse proxy, e.g.\n * `https://us.i.posthog.com` or `https://eu.i.posthog.com`. Must match the project's region.\n */\n readonly host?: string;\n}\n\n/** Why the SDK dropped analytics data or could not deliver it. Never contains values. */\nexport interface PixelumeAnalyticsIssue {\n readonly code:\n | 'invalid_event'\n | 'reserved_event'\n | 'invalid_level'\n | 'invalid_property'\n | 'reserved_property'\n | 'sensitive_property'\n | 'too_many_properties'\n | 'queue_full'\n | 'provider_unavailable'\n | 'provider_error';\n /** Event involved, when the name is valid. */\n readonly event: string | null;\n /** Property key involved, when it is a valid key. */\n readonly property: string | null;\n}\n\nexport interface PixelumeCrossSessionRestoreOptions {\n /**\n * Days after the last successful login during which a new tab/browser start tries to recover the\n * account. Defaults to 30 (the central session lifetime). Integer from 1 to 90.\n */\n readonly maxAgeDays?: number;\n}\n\n/** Effective, validated configuration of a client. */\nexport interface PixelumeClientConfig {\n readonly apiUrl: string;\n readonly gameId: string | null;\n readonly clientId: string | null;\n readonly redirectUri: string | null;\n readonly scope: 'profile' | 'profile saves';\n readonly authUrl: string;\n readonly restoreSession: boolean;\n /** Whether cross-session restoration (`restoreAcrossSessions`) is enabled. */\n readonly restoreAcrossSessions: boolean;\n readonly timeoutMs: number;\n readonly credentials: PixelumeCredentialsPolicy;\n /** Analytics provider configured for `pixelume.analytics`, or `null` when it is a no-op. */\n readonly analytics: 'posthog' | null;\n}\n\n/** Options accepted by every SDK operation. */\nexport interface PixelumeRequestOptions {\n /** Aborts the operation; the promise rejects with `PixelumeAbortError`. */\n signal?: AbortSignal;\n /** Overrides the client timeout for this operation. */\n timeoutMs?: number;\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,8 @@ export { DEFAULT_API_URL, DEFAULT_AUTH_URL, DEFAULT_TIMEOUT_MS } from './core/co
|
|
|
3
3
|
export { SDK_VERSION } from './version.js';
|
|
4
4
|
export { PixelumeError, PixelumeConfigError, PixelumeAuthError, PixelumeRequestError, PixelumeHttpError, PixelumeNetworkError, PixelumeTimeoutError, PixelumeAbortError, PixelumeInvalidResponseError, PixelumeSaveConflictError, isPixelumeError, } from './core/errors.js';
|
|
5
5
|
export type { PixelumeErrorKind, PixelumeApiErrorCode, PixelumeAuthErrorReason, PixelumeRequestInfo, PixelumeSaveMetadata, } from './core/errors.js';
|
|
6
|
-
export type { PixelumeClientOptions, PixelumeClientConfig, PixelumeRequestOptions, PixelumeCredentialsPolicy, PixelumeFetch, PixelumeBrowserAdapter, PixelumeSessionStorage, PixelumeCrossSessionRestoreOptions, } from './core/types.js';
|
|
6
|
+
export type { PixelumeClientOptions, PixelumeClientConfig, PixelumeRequestOptions, PixelumeCredentialsPolicy, PixelumeFetch, PixelumeBrowserAdapter, PixelumeSessionStorage, PixelumeCrossSessionRestoreOptions, PixelumeAnalyticsOptions, PixelumePostHogOptions, PixelumeAnalyticsIssue, } from './core/types.js';
|
|
7
|
+
export type { PixelumeAnalyticsApi, PixelumeAnalyticsConsent, PixelumeAnalyticsProperties, PixelumeAnalyticsStatus, } from './modules/analytics/AnalyticsModule.js';
|
|
7
8
|
export type { PixelumeHealthApi, PixelumeHealthStatus, PixelumeRegistrationMode, } from './modules/health/HealthModule.js';
|
|
8
9
|
export type { PixelumeAuthApi, PixelumeAuthState, PixelumeAuthListener, PixelumeAnonymousReason, PixelumeLoginOptions, PixelumePlayer, PixelumeScopeRequest, } from './modules/auth/AuthModule.js';
|
|
9
10
|
export type { PixelumeScope, PixelumeSessionState, PixelumeSessionListener, } from './modules/auth/SessionManager.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,4BAA4B,EAC5B,yBAAyB,EACzB,eAAe,GAChB,MAAM,kBAAkB,CAAC","sourcesContent":["export { PixelumeClient, Pixelume } from './core/PixelumeClient.js';\nexport { DEFAULT_API_URL, DEFAULT_AUTH_URL, DEFAULT_TIMEOUT_MS } from './core/config.js';\nexport { SDK_VERSION } from './version.js';\nexport {\n PixelumeError,\n PixelumeConfigError,\n PixelumeAuthError,\n PixelumeRequestError,\n PixelumeHttpError,\n PixelumeNetworkError,\n PixelumeTimeoutError,\n PixelumeAbortError,\n PixelumeInvalidResponseError,\n PixelumeSaveConflictError,\n isPixelumeError,\n} from './core/errors.js';\nexport type {\n PixelumeErrorKind,\n PixelumeApiErrorCode,\n PixelumeAuthErrorReason,\n PixelumeRequestInfo,\n PixelumeSaveMetadata,\n} from './core/errors.js';\nexport type {\n PixelumeClientOptions,\n PixelumeClientConfig,\n PixelumeRequestOptions,\n PixelumeCredentialsPolicy,\n PixelumeFetch,\n PixelumeBrowserAdapter,\n PixelumeSessionStorage,\n PixelumeCrossSessionRestoreOptions,\n} from './core/types.js';\nexport type {\n PixelumeHealthApi,\n PixelumeHealthStatus,\n PixelumeRegistrationMode,\n} from './modules/health/HealthModule.js';\nexport type {\n PixelumeAuthApi,\n PixelumeAuthState,\n PixelumeAuthListener,\n PixelumeAnonymousReason,\n PixelumeLoginOptions,\n PixelumePlayer,\n PixelumeScopeRequest,\n} from './modules/auth/AuthModule.js';\nexport type {\n PixelumeScope,\n PixelumeSessionState,\n PixelumeSessionListener,\n} from './modules/auth/SessionManager.js';\nexport type {\n PixelumeSavesApi,\n PixelumeSave,\n PixelumeSaveList,\n PixelumeSaveWrite,\n PixelumeSaveResult,\n PixelumeSaveDeleteOptions,\n PixelumeSaveDeleteResult,\n PixelumeSavesActivity,\n PixelumeSavesListener,\n PixelumeSavesOperation,\n} from './modules/saves/SavesModule.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACzF,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,EAClB,4BAA4B,EAC5B,yBAAyB,EACzB,eAAe,GAChB,MAAM,kBAAkB,CAAC","sourcesContent":["export { PixelumeClient, Pixelume } from './core/PixelumeClient.js';\nexport { DEFAULT_API_URL, DEFAULT_AUTH_URL, DEFAULT_TIMEOUT_MS } from './core/config.js';\nexport { SDK_VERSION } from './version.js';\nexport {\n PixelumeError,\n PixelumeConfigError,\n PixelumeAuthError,\n PixelumeRequestError,\n PixelumeHttpError,\n PixelumeNetworkError,\n PixelumeTimeoutError,\n PixelumeAbortError,\n PixelumeInvalidResponseError,\n PixelumeSaveConflictError,\n isPixelumeError,\n} from './core/errors.js';\nexport type {\n PixelumeErrorKind,\n PixelumeApiErrorCode,\n PixelumeAuthErrorReason,\n PixelumeRequestInfo,\n PixelumeSaveMetadata,\n} from './core/errors.js';\nexport type {\n PixelumeClientOptions,\n PixelumeClientConfig,\n PixelumeRequestOptions,\n PixelumeCredentialsPolicy,\n PixelumeFetch,\n PixelumeBrowserAdapter,\n PixelumeSessionStorage,\n PixelumeCrossSessionRestoreOptions,\n PixelumeAnalyticsOptions,\n PixelumePostHogOptions,\n PixelumeAnalyticsIssue,\n} from './core/types.js';\nexport type {\n PixelumeAnalyticsApi,\n PixelumeAnalyticsConsent,\n PixelumeAnalyticsProperties,\n PixelumeAnalyticsStatus,\n} from './modules/analytics/AnalyticsModule.js';\nexport type {\n PixelumeHealthApi,\n PixelumeHealthStatus,\n PixelumeRegistrationMode,\n} from './modules/health/HealthModule.js';\nexport type {\n PixelumeAuthApi,\n PixelumeAuthState,\n PixelumeAuthListener,\n PixelumeAnonymousReason,\n PixelumeLoginOptions,\n PixelumePlayer,\n PixelumeScopeRequest,\n} from './modules/auth/AuthModule.js';\nexport type {\n PixelumeScope,\n PixelumeSessionState,\n PixelumeSessionListener,\n} from './modules/auth/SessionManager.js';\nexport type {\n PixelumeSavesApi,\n PixelumeSave,\n PixelumeSaveList,\n PixelumeSaveWrite,\n PixelumeSaveResult,\n PixelumeSaveDeleteOptions,\n PixelumeSaveDeleteResult,\n PixelumeSavesActivity,\n PixelumeSavesListener,\n PixelumeSavesOperation,\n} from './modules/saves/SavesModule.js';\n"]}
|