@pixelume/sdk 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,34 @@
1
+ # Changelog
2
+
3
+ Todas as mudanças relevantes deste pacote são documentadas aqui.
4
+ O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/) e o projeto usa
5
+ [Semantic Versioning](https://semver.org/lang/pt-BR/). Antes da 1.0.0, versões _minor_ podem
6
+ conter mudanças incompatíveis, sempre listadas em **Changed/Removed**.
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.1] - 2026-10-13
11
+
12
+ ### Changed
13
+
14
+ - README do pacote: instalação a partir do npm (a 0.1.0 trazia a instrução provisória de instalar
15
+ via `npm pack`). Sem mudanças de código ou de API pública.
16
+ - Primeira versão publicada pelo workflow de release com npm Trusted Publishing (OIDC).
17
+
18
+ ## [0.1.0] - 2026-10-13
19
+
20
+ ### Added
21
+
22
+ - `PixelumeClient` com configuração validada: `apiUrl` (HTTPS; HTTP apenas em loopback ou com
23
+ `allowInsecureHttp`), `gameId` opcional (slug público), `timeoutMs`, política de `credentials`
24
+ e `fetch` injetável.
25
+ - `client.health.check()` para `GET /health` da Pixelume API, com validação do contrato.
26
+ - Cliente HTTP interno com timeout, propagação de `AbortSignal`, tratamento de JSON/texto/corpo
27
+ vazio e sem retries automáticos.
28
+ - Hierarquia de erros `PixelumeError` (`config`, `http`, `network`, `timeout`, `aborted`,
29
+ `invalid_response`) e `isPixelumeError()`.
30
+ - Build ESM + `.d.ts`, CI e workflow de publicação via npm Trusted Publishing.
31
+
32
+ [Unreleased]: https://github.com/ericksonnascimento/pixelume-sdk/compare/v0.1.1...HEAD
33
+ [0.1.1]: https://github.com/ericksonnascimento/pixelume-sdk/compare/v0.1.0...v0.1.1
34
+ [0.1.0]: https://github.com/ericksonnascimento/pixelume-sdk/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2026 Erickson Nascimento / Pixelume. All rights reserved.
2
+
3
+ PROPRIETARY AND CONFIDENTIAL — NOT OPEN SOURCE.
4
+
5
+ This software package ("@pixelume/sdk"), including its source code, compiled
6
+ code, type declarations and documentation, is the exclusive property of the
7
+ copyright holder.
8
+
9
+ Permission is granted only to games and applications developed or explicitly
10
+ authorized by Pixelume to install this package and include its compiled code
11
+ in their distributed builds, solely to integrate with Pixelume services.
12
+
13
+ Except as stated above, no permission is granted to copy, modify, merge,
14
+ publish, distribute, sublicense, sell, reverse engineer or create derivative
15
+ works of this software, in whole or in part, without the prior written
16
+ consent of the copyright holder. Availability of the package on a public
17
+ registry does not grant any additional rights.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY ARISING
23
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR ITS USE.
package/README.md CHANGED
@@ -1,3 +1,146 @@
1
- # Temporary Holding Version
1
+ # @pixelume/sdk
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ SDK TypeScript dos jogos web do estúdio **Pixelume**. Fornece um cliente tipado, seguro e sem
4
+ dependências de runtime para a [Pixelume API](https://api.pixelume.com.br), independente de framework ou
5
+ engine (funciona com Vite, Three.js, React, JavaScript puro, SSR e workers).
6
+
7
+ > **Status: Fase 1 — fundação.** Esta versão oferece configuração, cliente HTTP, erros padronizados e a
8
+ > checagem de disponibilidade da API. Login, sessão, cloud saves e analytics **ainda não existem** e
9
+ > serão adicionados quando os contratos da API estiverem prontos (veja o [roadmap](docs/roadmap.md)).
10
+
11
+ ## Capacidades
12
+
13
+ | Capacidade | Fase 1 | Observação |
14
+ | -------------------------------------------- | :----: | ------------------------------------------------------- |
15
+ | `PixelumeClient` com configuração validada | ✅ | `apiUrl`, `gameId`, `timeoutMs`, `credentials`, `fetch` |
16
+ | `client.health.check()` (`GET /health`) | ✅ | sem CORS na API: servidor/mesma origem (ver abaixo) |
17
+ | Erros padronizados, timeout e cancelamento | ✅ | `PixelumeError` + `kind` |
18
+ | Autenticação / Pixelume Auth | — | aguarda portal central e contratos de redirect |
19
+ | Cloud saves, analytics, rankings, conquistas | — | aguardam contratos na API |
20
+
21
+ ## Pré-requisitos
22
+
23
+ - Navegadores modernos (Chrome/Edge 98+, Firefox 97+, Safari 15.4+) ou Node.js 22+, Deno, Bun, Workers.
24
+ - Projeto ESM (o pacote é ESM-only, com tipos incluídos). Em CommonJS use `await import('@pixelume/sdk')`.
25
+
26
+ ## Instalação
27
+
28
+ ```bash
29
+ npm install @pixelume/sdk
30
+ ```
31
+
32
+ Publicado no npm: [`@pixelume/sdk`](https://www.npmjs.com/package/@pixelume/sdk) (versão atual `0.1.0`).
33
+
34
+ ## Uso
35
+
36
+ ```ts
37
+ import { PixelumeClient } from '@pixelume/sdk';
38
+
39
+ const pixelume = new PixelumeClient({
40
+ apiUrl: 'https://api.pixelume.com.br', // padrão
41
+ gameId: 'rail-puzzle', // opcional nesta fase
42
+ });
43
+
44
+ const health = await pixelume.health.check();
45
+ // { status: 'ok', registration: 'email-verification', release: 'd99d195…' }
46
+ ```
47
+
48
+ Saída real obtida pelo exemplo em 2026-10-13 (Node.js, pacote instalado via `npm pack`):
49
+
50
+ ```json
51
+ {
52
+ "sdk": "0.1.0",
53
+ "apiUrl": "https://api.pixelume.com.br",
54
+ "health": {
55
+ "status": "ok",
56
+ "registration": "email-verification",
57
+ "release": "d99d19543620f09309268f67d959e874ddc78ecf"
58
+ }
59
+ }
60
+ ```
61
+
62
+ ### `health.check()` no navegador
63
+
64
+ A API ainda **não envia cabeçalhos CORS em `/health`** (só em `/v1/*`). Em uma página servida por outra
65
+ origem, o navegador bloqueia a resposta e o SDK rejeita com `PixelumeNetworkError`. Use `health.check()`
66
+ no servidor (Node/SSR/workers), em ferramentas, ou na mesma origem via proxy — o
67
+ [exemplo Vite](examples/vite-basic) usa o proxy do `vite dev`. A correção depende da API
68
+ ([auditoria](docs/api-contract-audit.md)).
69
+
70
+ ### Configuração
71
+
72
+ | Opção | Padrão | Descrição |
73
+ | ------------------- | ----------------------------- | -------------------------------------------------------------------------------------------- |
74
+ | `apiUrl` | `https://api.pixelume.com.br` | `https:` obrigatório; `http:` aceito só para `localhost`/`127.0.0.1`/`[::1]`/`*.localhost`. |
75
+ | `gameId` | — | slug público (`a-z`, `0-9`, `-`, até 64). Identifica o jogo; **não** é credencial. |
76
+ | `timeoutMs` | `10000` | tempo máximo de cada operação, incluindo leitura do corpo. |
77
+ | `credentials` | `'omit'` | política de cookies do `fetch` para módulos futuros. O SDK não presume cookies cross-origin. |
78
+ | `fetch` | `globalThis.fetch` | implementação injetável (testes, SSR, instrumentação). |
79
+ | `allowInsecureHttp` | `false` | permite `http:` em hosts não-loopback (ex.: IP da LAN). Nunca em produção. |
80
+
81
+ Opções inválidas lançam `PixelumeConfigError` no construtor. Cada operação aceita
82
+ `{ signal?: AbortSignal; timeoutMs?: number }`.
83
+
84
+ ### Erros
85
+
86
+ Todas as falhas são `PixelumeError` com um `kind` estável:
87
+
88
+ ```ts
89
+ import { isPixelumeError } from '@pixelume/sdk';
90
+
91
+ const controller = new AbortController();
92
+ try {
93
+ await pixelume.health.check({ signal: controller.signal, timeoutMs: 5_000 });
94
+ } catch (error) {
95
+ if (!isPixelumeError(error)) throw error;
96
+ switch (error.kind) {
97
+ case 'http': // error.status, error.code ('RATE_LIMITED'…), error.requestId
98
+ case 'network': // offline, DNS/TLS ou bloqueio de CORS
99
+ case 'timeout': // error.timeoutMs
100
+ case 'aborted': // cancelado pelo AbortSignal
101
+ case 'invalid_response': // a API respondeu fora do contrato
102
+ case 'config': // configuração inválida
103
+ }
104
+ }
105
+ ```
106
+
107
+ Mensagens de erro não incluem corpo, cookies, cabeçalhos nem query string. O corpo bruto fica em
108
+ `PixelumeHttpError.payload`, propriedade não enumerável (não aparece em `JSON.stringify`). O SDK nunca
109
+ escreve no console.
110
+
111
+ ## Segurança
112
+
113
+ - O SDK não contém nem aceita API keys, tokens administrativos ou senhas.
114
+ - `gameId` é público; autorização sempre vem da API.
115
+ - Não há fluxo de login com senha dentro do jogo — e não haverá: o login será feito pelo Pixelume Auth.
116
+ - O SDK não define `Origin`/`Referer`/`Cookie` e não trata CORS como segurança.
117
+
118
+ ## Desenvolvimento
119
+
120
+ Requer Node.js 24 (`.nvmrc`).
121
+
122
+ ```bash
123
+ npm ci
124
+ npm run lint # ESLint + Prettier
125
+ npm run typecheck
126
+ npm test # unitários + integração com servidor HTTP local (sem rede)
127
+ npm run build # dist/ (ESM + .d.ts + sourcemaps)
128
+ npm run check:package # dist browser-safe, conteúdo do tarball, publint, attw
129
+ npm run example:prepare && npm run example:build # instala o .tgz no exemplo e compila
130
+ npm run example:smoke # exemplo Node contra a API real (rede)
131
+ npm run test:live # teste opcional contra a API real (rede)
132
+ npm run verify # tudo o que a CI executa
133
+ ```
134
+
135
+ ## Documentação
136
+
137
+ - [Arquitetura e decisões técnicas](docs/architecture.md)
138
+ - [Auditoria de contratos da API](docs/api-contract-audit.md)
139
+ - [Publicação, CI/CD e rollback](docs/publishing.md)
140
+ - [Roadmap](docs/roadmap.md)
141
+ - [Changelog](CHANGELOG.md)
142
+
143
+ ## Licença
144
+
145
+ Proprietária — todos os direitos reservados. Veja [LICENSE](LICENSE). A disponibilidade pública no npm
146
+ não concede direitos além dos descritos na licença.
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Wire contracts of the Pixelume API used by the SDK, with hand-written runtime guards.
3
+ *
4
+ * Source of truth: `pixelume-api` (`docs/openapi.yaml` and `src/interfaces/http/app.ts`).
5
+ * See `docs/api-contract-audit.md`. These types describe JSON on the wire and are internal;
6
+ * public modules map them to SDK types so the API can evolve without breaking consumers.
7
+ *
8
+ * Validation is intentionally tolerant to *additive* changes (unknown fields are ignored,
9
+ * unknown enum values are accepted as strings) and strict about the fields the SDK relies on.
10
+ */
11
+ export type ParseResult<T> = {
12
+ readonly ok: true;
13
+ readonly value: T;
14
+ } | {
15
+ readonly ok: false;
16
+ readonly reason: string;
17
+ };
18
+ export type BodyParser<T> = (body: unknown) => ParseResult<T>;
19
+ /** `GET /health` → 200. OpenAPI: `{ status: 'ok', registration: enum, release?: /^[a-f0-9]{7,40}$/ }`. */
20
+ export interface HealthResponseWire {
21
+ status: 'ok';
22
+ registration: string;
23
+ release?: string;
24
+ }
25
+ export declare const parseHealthResponse: BodyParser<HealthResponseWire>;
26
+ /** Error envelope returned by every API error: `{ error: { code, message, requestId } }`. */
27
+ export interface ErrorEnvelopeWire {
28
+ error: {
29
+ code: string;
30
+ message: string;
31
+ requestId: string;
32
+ };
33
+ }
34
+ /** Returns the envelope when `body` matches it, `null` otherwise (e.g. an HTML 502 from a proxy). */
35
+ export declare function parseErrorEnvelope(body: unknown): ErrorEnvelopeWire | null;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Wire contracts of the Pixelume API used by the SDK, with hand-written runtime guards.
3
+ *
4
+ * Source of truth: `pixelume-api` (`docs/openapi.yaml` and `src/interfaces/http/app.ts`).
5
+ * See `docs/api-contract-audit.md`. These types describe JSON on the wire and are internal;
6
+ * public modules map them to SDK types so the API can evolve without breaking consumers.
7
+ *
8
+ * Validation is intentionally tolerant to *additive* changes (unknown fields are ignored,
9
+ * unknown enum values are accepted as strings) and strict about the fields the SDK relies on.
10
+ */
11
+ const ok = (value) => ({ ok: true, value });
12
+ const fail = (reason) => ({ ok: false, reason });
13
+ function isRecord(value) {
14
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
15
+ }
16
+ export const parseHealthResponse = (body) => {
17
+ if (!isRecord(body))
18
+ return fail('expected a JSON object');
19
+ if (body.status !== 'ok')
20
+ return fail('expected "status" to be "ok"');
21
+ if (typeof body.registration !== 'string')
22
+ return fail('expected "registration" to be a string');
23
+ if (body.release !== undefined && typeof body.release !== 'string') {
24
+ return fail('expected "release" to be a string when present');
25
+ }
26
+ return ok({
27
+ status: 'ok',
28
+ registration: body.registration,
29
+ ...(body.release === undefined ? {} : { release: body.release }),
30
+ });
31
+ };
32
+ /** Returns the envelope when `body` matches it, `null` otherwise (e.g. an HTML 502 from a proxy). */
33
+ export function parseErrorEnvelope(body) {
34
+ if (!isRecord(body) || !isRecord(body.error))
35
+ return null;
36
+ const { code, message, requestId } = body.error;
37
+ if (typeof code !== 'string' || typeof message !== 'string' || typeof requestId !== 'string') {
38
+ return null;
39
+ }
40
+ return { error: { code, message, requestId } };
41
+ }
42
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAOH,MAAM,EAAE,GAAG,CAAI,KAAQ,EAAkB,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAClE,MAAM,IAAI,GAAG,CAAC,MAAc,EAAsB,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;AAE7E,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AASD,MAAM,CAAC,MAAM,mBAAmB,GAAmC,CAAC,IAAI,EAAE,EAAE;IAC1E,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,wBAAwB,CAAC,CAAC;IAC3D,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC,8BAA8B,CAAC,CAAC;IACtE,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,wCAAwC,CAAC,CAAC;IACjG,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QACnE,OAAO,IAAI,CAAC,gDAAgD,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,EAAE,CAAC;QACR,MAAM,EAAE,IAAI;QACZ,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,GAAG,CAAC,IAAI,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC;KACjE,CAAC,CAAC;AACL,CAAC,CAAC;AAOF,qGAAqG;AACrG,MAAM,UAAU,kBAAkB,CAAC,IAAa;IAC9C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;IAChD,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC7F,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,CAAC;AACjD,CAAC","sourcesContent":["/**\n * Wire contracts of the Pixelume API used by the SDK, with hand-written runtime guards.\n *\n * Source of truth: `pixelume-api` (`docs/openapi.yaml` and `src/interfaces/http/app.ts`).\n * See `docs/api-contract-audit.md`. These types describe JSON on the wire and are internal;\n * public modules map them to SDK types so the API can evolve without breaking consumers.\n *\n * Validation is intentionally tolerant to *additive* changes (unknown fields are ignored,\n * unknown enum values are accepted as strings) and strict about the fields the SDK relies on.\n */\n\nexport type ParseResult<T> =\n { readonly ok: true; readonly value: T } | { readonly ok: false; readonly reason: string };\n\nexport type BodyParser<T> = (body: unknown) => ParseResult<T>;\n\nconst ok = <T>(value: T): ParseResult<T> => ({ ok: true, value });\nconst fail = (reason: string): ParseResult<never> => ({ ok: false, reason });\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n/** `GET /health` → 200. OpenAPI: `{ status: 'ok', registration: enum, release?: /^[a-f0-9]{7,40}$/ }`. */\nexport interface HealthResponseWire {\n status: 'ok';\n registration: string;\n release?: string;\n}\n\nexport const parseHealthResponse: BodyParser<HealthResponseWire> = (body) => {\n if (!isRecord(body)) return fail('expected a JSON object');\n if (body.status !== 'ok') return fail('expected \"status\" to be \"ok\"');\n if (typeof body.registration !== 'string') return fail('expected \"registration\" to be a string');\n if (body.release !== undefined && typeof body.release !== 'string') {\n return fail('expected \"release\" to be a string when present');\n }\n return ok({\n status: 'ok',\n registration: body.registration,\n ...(body.release === undefined ? {} : { release: body.release }),\n });\n};\n\n/** Error envelope returned by every API error: `{ error: { code, message, requestId } }`. */\nexport interface ErrorEnvelopeWire {\n error: { code: string; message: string; requestId: string };\n}\n\n/** Returns the envelope when `body` matches it, `null` otherwise (e.g. an HTML 502 from a proxy). */\nexport function parseErrorEnvelope(body: unknown): ErrorEnvelopeWire | null {\n if (!isRecord(body) || !isRecord(body.error)) return null;\n const { code, message, requestId } = body.error;\n if (typeof code !== 'string' || typeof message !== 'string' || typeof requestId !== 'string') {\n return null;\n }\n return { error: { code, message, requestId } };\n}\n"]}
@@ -0,0 +1,20 @@
1
+ import { type PixelumeHealthApi } from '../modules/health/HealthModule.js';
2
+ import type { PixelumeClientConfig, PixelumeClientOptions } from './types.js';
3
+ /**
4
+ * Entry point of the SDK. Create one instance per API/game and share it.
5
+ *
6
+ * ```ts
7
+ * const pixelume = new PixelumeClient({ gameId: 'rail-puzzle' });
8
+ * const health = await pixelume.health.check();
9
+ * ```
10
+ *
11
+ * Feature modules are exposed as read-only properties. Phase 1 ships only `health`; future modules
12
+ * (authentication, saves…) will be added as new properties without changing existing ones.
13
+ *
14
+ * @throws {PixelumeConfigError} when the options are invalid.
15
+ */
16
+ export declare class PixelumeClient {
17
+ readonly config: PixelumeClientConfig;
18
+ readonly health: PixelumeHealthApi;
19
+ constructor(options?: PixelumeClientOptions);
20
+ }
@@ -0,0 +1,37 @@
1
+ import { HttpClient } from '../http/HttpClient.js';
2
+ import { HealthModule } from '../modules/health/HealthModule.js';
3
+ import { resolveConfig } from './config.js';
4
+ /**
5
+ * Entry point of the SDK. Create one instance per API/game and share it.
6
+ *
7
+ * ```ts
8
+ * const pixelume = new PixelumeClient({ gameId: 'rail-puzzle' });
9
+ * const health = await pixelume.health.check();
10
+ * ```
11
+ *
12
+ * Feature modules are exposed as read-only properties. Phase 1 ships only `health`; future modules
13
+ * (authentication, saves…) will be added as new properties without changing existing ones.
14
+ *
15
+ * @throws {PixelumeConfigError} when the options are invalid.
16
+ */
17
+ export class PixelumeClient {
18
+ config;
19
+ health;
20
+ constructor(options = {}) {
21
+ const resolved = resolveConfig(options);
22
+ this.config = Object.freeze({
23
+ apiUrl: resolved.apiUrl,
24
+ gameId: resolved.gameId,
25
+ timeoutMs: resolved.timeoutMs,
26
+ credentials: resolved.credentials,
27
+ });
28
+ const http = new HttpClient({
29
+ baseUrl: resolved.apiUrl,
30
+ fetch: resolved.fetch,
31
+ timeoutMs: resolved.timeoutMs,
32
+ credentials: resolved.credentials,
33
+ });
34
+ this.health = new HealthModule(http);
35
+ }
36
+ }
37
+ //# sourceMappingURL=PixelumeClient.js.map
@@ -0,0 +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,EAAE,YAAY,EAA0B,MAAM,mCAAmC,CAAC;AACzF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,cAAc;IAChB,MAAM,CAAuB;IAC7B,MAAM,CAAoB;IAEnC,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,SAAS,EAAE,QAAQ,CAAC,SAAS;YAC7B,WAAW,EAAE,QAAQ,CAAC,WAAW;SAClC,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;IACvC,CAAC;CACF","sourcesContent":["import { HttpClient } from '../http/HttpClient.js';\nimport { HealthModule, type PixelumeHealthApi } from '../modules/health/HealthModule.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. Phase 1 ships only `health`; future modules\n * (authentication, saves…) will be added as new properties without changing existing ones.\n *\n * @throws {PixelumeConfigError} when the options are invalid.\n */\nexport class PixelumeClient {\n readonly config: PixelumeClientConfig;\n readonly health: PixelumeHealthApi;\n\n constructor(options: PixelumeClientOptions = {}) {\n const resolved = resolveConfig(options);\n this.config = Object.freeze({\n apiUrl: resolved.apiUrl,\n gameId: resolved.gameId,\n timeoutMs: resolved.timeoutMs,\n credentials: resolved.credentials,\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 }\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import type { CredentialsPolicy, FetchLike } from '../http/types.js';
2
+ import type { PixelumeClientOptions } from './types.js';
3
+ export declare const DEFAULT_API_URL = "https://api.pixelume.com.br";
4
+ export declare const DEFAULT_TIMEOUT_MS = 10000;
5
+ export interface ResolvedConfig {
6
+ apiUrl: string;
7
+ gameId: string | null;
8
+ timeoutMs: number;
9
+ credentials: CredentialsPolicy;
10
+ fetch: FetchLike;
11
+ }
12
+ export declare function resolveConfig(options: PixelumeClientOptions): ResolvedConfig;
13
+ /**
14
+ * Accepts `https:` URLs, plus `http:` for loopback hosts (`localhost`, `*.localhost`, `127.0.0.0/8`,
15
+ * `[::1]`). Other `http:` hosts (e.g. a LAN IP while testing on a phone) need `allowInsecureHttp`.
16
+ */
17
+ export declare function normalizeApiUrl(value: string, allowInsecureHttp: boolean): string;
18
+ /**
19
+ * Game identifiers are public, non-secret slugs (e.g. `rail-puzzle`): lowercase letters, digits
20
+ * and inner hyphens, 1–64 characters. They identify a game, they never authorize anything.
21
+ */
22
+ export declare function validateGameId(value: string): string;
23
+ /** For feature modules that need a game context (future phases). */
24
+ export declare function requireGameId(config: Pick<ResolvedConfig, 'gameId'>, feature: string): string;
25
+ export declare function validateTimeout(value: number): number;
@@ -0,0 +1,89 @@
1
+ import { PixelumeConfigError } from './errors.js';
2
+ export const DEFAULT_API_URL = 'https://api.pixelume.com.br';
3
+ export const DEFAULT_TIMEOUT_MS = 10_000;
4
+ const MAX_TIMEOUT_MS = 2_147_483_647; // setTimeout limit
5
+ const GAME_ID_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/u;
6
+ const CREDENTIALS = ['omit', 'same-origin', 'include'];
7
+ export function resolveConfig(options) {
8
+ return {
9
+ apiUrl: normalizeApiUrl(options.apiUrl ?? DEFAULT_API_URL, options.allowInsecureHttp === true),
10
+ gameId: options.gameId === undefined ? null : validateGameId(options.gameId),
11
+ timeoutMs: options.timeoutMs === undefined ? DEFAULT_TIMEOUT_MS : validateTimeout(options.timeoutMs),
12
+ credentials: validateCredentials(options.credentials ?? 'omit'),
13
+ fetch: resolveFetch(options.fetch),
14
+ };
15
+ }
16
+ /**
17
+ * Accepts `https:` URLs, plus `http:` for loopback hosts (`localhost`, `*.localhost`, `127.0.0.0/8`,
18
+ * `[::1]`). Other `http:` hosts (e.g. a LAN IP while testing on a phone) need `allowInsecureHttp`.
19
+ */
20
+ export function normalizeApiUrl(value, allowInsecureHttp) {
21
+ let url;
22
+ try {
23
+ url = new URL(value);
24
+ }
25
+ catch {
26
+ throw new PixelumeConfigError('apiUrl must be an absolute URL, e.g. https://api.pixelume.com.br');
27
+ }
28
+ if (url.protocol !== 'https:' && url.protocol !== 'http:') {
29
+ throw new PixelumeConfigError('apiUrl must use https: (or http: for local development)');
30
+ }
31
+ if (url.protocol === 'http:' && !allowInsecureHttp && !isLoopbackHost(url.hostname)) {
32
+ throw new PixelumeConfigError('apiUrl must use https:. Plain http: is only accepted for localhost unless allowInsecureHttp is set');
33
+ }
34
+ if (url.username || url.password) {
35
+ throw new PixelumeConfigError('apiUrl must not contain credentials');
36
+ }
37
+ if (url.search || url.hash) {
38
+ throw new PixelumeConfigError('apiUrl must not contain a query string or fragment');
39
+ }
40
+ return `${url.origin}${url.pathname.replace(/\/+$/u, '')}`;
41
+ }
42
+ function isLoopbackHost(hostname) {
43
+ return (hostname === 'localhost' ||
44
+ hostname.endsWith('.localhost') ||
45
+ hostname === '[::1]' ||
46
+ /^127(?:\.\d{1,3}){3}$/u.test(hostname));
47
+ }
48
+ /**
49
+ * Game identifiers are public, non-secret slugs (e.g. `rail-puzzle`): lowercase letters, digits
50
+ * and inner hyphens, 1–64 characters. They identify a game, they never authorize anything.
51
+ */
52
+ export function validateGameId(value) {
53
+ if (typeof value !== 'string' || !GAME_ID_PATTERN.test(value)) {
54
+ throw new PixelumeConfigError('gameId must be a lowercase slug (a-z, 0-9 and inner hyphens, up to 64 characters)');
55
+ }
56
+ return value;
57
+ }
58
+ /** For feature modules that need a game context (future phases). */
59
+ export function requireGameId(config, feature) {
60
+ if (config.gameId === null) {
61
+ throw new PixelumeConfigError(`${feature} requires the client to be created with a gameId`);
62
+ }
63
+ return config.gameId;
64
+ }
65
+ export function validateTimeout(value) {
66
+ if (!Number.isInteger(value) || value <= 0 || value > MAX_TIMEOUT_MS) {
67
+ throw new PixelumeConfigError('timeoutMs must be a positive integer number of milliseconds');
68
+ }
69
+ return value;
70
+ }
71
+ function validateCredentials(value) {
72
+ if (!CREDENTIALS.includes(value)) {
73
+ throw new PixelumeConfigError(`credentials must be one of: ${CREDENTIALS.join(', ')}`);
74
+ }
75
+ return value;
76
+ }
77
+ function resolveFetch(custom) {
78
+ if (custom !== undefined) {
79
+ if (typeof custom !== 'function')
80
+ throw new PixelumeConfigError('fetch must be a function');
81
+ return custom;
82
+ }
83
+ if (typeof globalThis.fetch !== 'function') {
84
+ throw new PixelumeConfigError('No global fetch available; pass the fetch option');
85
+ }
86
+ // Looked up on each call so polyfills or test doubles installed later are honoured.
87
+ return (input, init) => globalThis.fetch(input, init);
88
+ }
89
+ //# sourceMappingURL=config.js.map
@@ -0,0 +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_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 CREDENTIALS: readonly CredentialsPolicy[] = ['omit', 'same-origin', 'include'];\n\nexport interface ResolvedConfig {\n apiUrl: string;\n gameId: string | null;\n timeoutMs: number;\n credentials: CredentialsPolicy;\n fetch: FetchLike;\n}\n\nexport function resolveConfig(options: PixelumeClientOptions): ResolvedConfig {\n return {\n apiUrl: normalizeApiUrl(options.apiUrl ?? DEFAULT_API_URL, options.allowInsecureHttp === true),\n gameId: options.gameId === undefined ? null : validateGameId(options.gameId),\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\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 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\n/** For feature modules that need a game context (future phases). */\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"]}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Error hierarchy of the SDK.
3
+ *
4
+ * Every error thrown by the SDK is a {@link PixelumeError} with a stable `kind` discriminator.
5
+ * Messages are deterministic and never include request/response bodies, headers, cookies or
6
+ * query strings, so they are safe to log. Raw response payloads are kept on
7
+ * {@link PixelumeHttpError.payload} as a non-enumerable property for programmatic inspection only.
8
+ */
9
+ export type PixelumeErrorKind = 'config' | 'http' | 'network' | 'timeout' | 'aborted' | 'invalid_response';
10
+ /**
11
+ * Error codes documented in the Pixelume API OpenAPI `ErrorEnvelope`.
12
+ * The API may introduce new codes, so any string is accepted at runtime.
13
+ */
14
+ export type PixelumeApiErrorCode = 'INVALID_INPUT' | 'UNAUTHENTICATED' | 'FORBIDDEN_ORIGIN' | 'FORBIDDEN_HOST' | 'RATE_LIMITED' | 'REGISTRATION_DISABLED' | 'AUTH_TOKEN_INVALID' | 'INTERNAL_ERROR' | 'NOT_FOUND' | (string & {});
15
+ /** Identifies the request that failed. `path` never contains the query string. */
16
+ export interface PixelumeRequestInfo {
17
+ readonly method: string;
18
+ readonly path: string;
19
+ }
20
+ /** Base class of every error thrown by the SDK. */
21
+ export declare abstract class PixelumeError extends Error {
22
+ abstract readonly kind: PixelumeErrorKind;
23
+ protected constructor(name: string, message: string, options?: {
24
+ cause?: unknown;
25
+ });
26
+ }
27
+ /** Invalid SDK configuration or invalid use of the SDK. Never caused by the network. */
28
+ export declare class PixelumeConfigError extends PixelumeError {
29
+ readonly kind: "config";
30
+ constructor(message: string);
31
+ }
32
+ /** Base class of errors tied to a specific HTTP request. */
33
+ export declare abstract class PixelumeRequestError extends PixelumeError {
34
+ readonly method: string;
35
+ readonly path: string;
36
+ protected constructor(name: string, message: string, request: PixelumeRequestInfo, options?: {
37
+ cause?: unknown;
38
+ });
39
+ }
40
+ /** The API answered with a non-2xx status. */
41
+ export declare class PixelumeHttpError extends PixelumeRequestError {
42
+ readonly kind: "http";
43
+ readonly status: number;
44
+ /** `error.code` from the API error envelope, or `null` when the body is not an envelope. */
45
+ readonly code: PixelumeApiErrorCode | null;
46
+ /** `error.message` from the API error envelope (already sanitized by the API). */
47
+ readonly apiMessage: string | null;
48
+ /** Request ID from the envelope or the `x-request-id` header, useful for support. */
49
+ readonly requestId: string | null;
50
+ /**
51
+ * Decoded response body (JSON value, text or `undefined`). Non-enumerable so that
52
+ * `JSON.stringify(error)` and structured loggers do not dump it by accident.
53
+ */
54
+ readonly payload: unknown;
55
+ constructor(details: {
56
+ request: PixelumeRequestInfo;
57
+ status: number;
58
+ code: string | null;
59
+ apiMessage: string | null;
60
+ requestId: string | null;
61
+ payload: unknown;
62
+ });
63
+ }
64
+ /**
65
+ * The request did not produce an HTTP response: offline, DNS/TLS failure, connection reset,
66
+ * or a browser CORS rejection (browsers do not distinguish these for security reasons).
67
+ */
68
+ export declare class PixelumeNetworkError extends PixelumeRequestError {
69
+ readonly kind: "network";
70
+ constructor(request: PixelumeRequestInfo, options?: {
71
+ cause?: unknown;
72
+ });
73
+ }
74
+ /** The SDK timeout elapsed before the response (including its body) was received. */
75
+ export declare class PixelumeTimeoutError extends PixelumeRequestError {
76
+ readonly kind: "timeout";
77
+ readonly timeoutMs: number;
78
+ constructor(request: PixelumeRequestInfo, timeoutMs: number, options?: {
79
+ cause?: unknown;
80
+ });
81
+ }
82
+ /** The caller aborted the request through its `AbortSignal`. `cause` holds the abort reason. */
83
+ export declare class PixelumeAbortError extends PixelumeRequestError {
84
+ readonly kind: "aborted";
85
+ constructor(request: PixelumeRequestInfo, options?: {
86
+ cause?: unknown;
87
+ });
88
+ }
89
+ /** A 2xx response did not match the contract expected by the SDK. */
90
+ export declare class PixelumeInvalidResponseError extends PixelumeRequestError {
91
+ readonly kind: "invalid_response";
92
+ readonly status: number;
93
+ /** Short, payload-free description of the contract violation. */
94
+ readonly reason: string;
95
+ constructor(request: PixelumeRequestInfo, status: number, reason: string);
96
+ }
97
+ /**
98
+ * Returns `true` for any error thrown by the SDK. Works across duplicated copies of the package
99
+ * (where `instanceof` would fail) because it checks a global symbol brand.
100
+ */
101
+ export declare function isPixelumeError(value: unknown): value is PixelumeError;