scorezilla 0.1.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,212 @@
1
+ # scorezilla
2
+
3
+ [![npm version](https://img.shields.io/npm/v/scorezilla.svg)](https://www.npmjs.com/package/scorezilla)
4
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/scorezilla)](https://bundlephobia.com/package/scorezilla)
5
+ [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
6
+ [![CI](https://img.shields.io/github/actions/workflow/status/isco-tec/scorezilla-js/sdk-ci.yml?branch=main)](https://github.com/isco-tec/scorezilla-js/actions)
7
+ [![provenance](https://img.shields.io/badge/npm-provenance-success)](https://docs.npmjs.com/generating-provenance-statements)
8
+
9
+ Official JavaScript / TypeScript SDK for [Scorezilla](https://scorezilla.dev) —
10
+ focused leaderboard infrastructure for indie games, browser games, and
11
+ AI-vibe-coded games.
12
+
13
+ - **Tiny.** ~4 KB gzipped. No runtime dependencies.
14
+ - **Universal.** Browser, Node ≥ 20, Cloudflare Workers, Bun, Deno.
15
+ - **Typed.** First-class TypeScript with strict types and rich JSDoc.
16
+ - **Safe-by-default.** Automatic retries on transient failures with idempotency
17
+ keys; per-request timeouts; cancellation via `AbortSignal`.
18
+ - **Private.** No cookies, no `localStorage`, no fingerprinting beyond runtime
19
+ detection — see [COMPATIBILITY.md](./COMPATIBILITY.md).
20
+
21
+ > **Status:** v0.1.0 ships the **public-key client** (browser-safe). The HMAC
22
+ > server adapter (`scorezilla/server`) lands in v0.2.0; React
23
+ > (`scorezilla/react`) in v0.3.0; Phaser (`scorezilla/phaser`) in v0.4.0. See
24
+ > [CHANGELOG.md](./CHANGELOG.md) and [VERSIONING.md](./VERSIONING.md).
25
+
26
+ > **Commercial context.** Scorezilla is a hosted leaderboard service with free
27
+ > and paid tiers — see [scorezilla.dev/pricing](https://scorezilla.dev/pricing).
28
+ > This SDK is MIT-licensed and works identically across every plan; get your API
29
+ > key from the [operator dashboard](https://dashboard.scorezilla.dev).
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ npm install scorezilla
35
+ # or
36
+ pnpm add scorezilla
37
+ # or
38
+ yarn add scorezilla
39
+ # or
40
+ bun add scorezilla
41
+ ```
42
+
43
+ ## Quickstart
44
+
45
+ ```ts
46
+ import { Scorezilla, ScorezillaError } from 'scorezilla';
47
+
48
+ const sz = new Scorezilla({ publicKey: 'pk_mygame_aBcDeF…' });
49
+
50
+ try {
51
+ const r = await sz.submitScore({
52
+ boardId: 'board-uuid',
53
+ playerId: 'player-uuid',
54
+ score: 9001,
55
+ metadata: { level: 'hard' },
56
+ });
57
+ if (r.isPersonalBest) {
58
+ console.log(`🏆 New personal best! Rank ${r.rank} of ${r.totalEntries}`);
59
+ }
60
+ } catch (e) {
61
+ if (e instanceof ScorezillaError && e.isRateLimited()) {
62
+ console.warn(`Rate-limited. Retry after ${e.retryAfter}s.`);
63
+ } else throw e;
64
+ }
65
+ ```
66
+
67
+ The four public methods:
68
+
69
+ ```ts
70
+ await sz.submitScore({ boardId, playerId, score, metadata? });
71
+ await sz.getLeaderboard({ boardId, top?, offset? });
72
+ await sz.getPlayerRank({ boardId, playerId });
73
+ await sz.getWindowAround({ boardId, playerId, before?, after? });
74
+ ```
75
+
76
+ See [**API.md**](./API.md) for the full reference, including every response
77
+ field, every error code, and advanced patterns.
78
+
79
+ ## Error handling
80
+
81
+ Every failure path — HTTP non-2xx, network error, timeout, abort, JSON parse
82
+ error — throws a single `ScorezillaError`. **Branch on `code`**
83
+ (machine-stable), never on `message` (English-only, may change without a major
84
+ bump):
85
+
86
+ ```ts
87
+ import { ScorezillaError } from 'scorezilla';
88
+
89
+ try {
90
+ await sz.submitScore({ boardId, playerId, score });
91
+ } catch (e) {
92
+ if (!(e instanceof ScorezillaError)) throw e;
93
+
94
+ if (e.isRateLimited()) await sleep((e.retryAfter ?? 30) * 1000);
95
+ else if (e.isAuth()) throw new Error('SDK misconfigured');
96
+ else if (e.code === 'out_of_bounds')
97
+ console.warn(`Score crosses ${e.reason} bound (limit ${e.bound})`);
98
+ else if (e.isTransient()) /* automatic retries already exhausted */ throw e;
99
+ else throw e;
100
+ }
101
+ ```
102
+
103
+ The error class carries the request ID, status, and the underlying cause for
104
+ support tickets:
105
+
106
+ ```ts
107
+ console.error(`Scorezilla ${e.code} (${e.status}) — req ${e.requestId}`);
108
+ ```
109
+
110
+ ## Runtime support
111
+
112
+ | Runtime | Status | Notes |
113
+ | ---------------------- | ----------------------------- | ------------------------------------------------------- |
114
+ | **Node** | ≥ 20 | Hard requirement. Native `fetch` + `crypto.randomUUID`. |
115
+ | **Browsers** | All evergreen | Chrome 92+, Firefox 95+, Safari 15.4+, Edge 92+. |
116
+ | **Cloudflare Workers** | ✅ | Detected via `navigator.userAgent`. |
117
+ | **Bun** | ≥ 1.0 (best-effort in v0.1.0) | Promoted to hard gate in v0.2.0 if stable. |
118
+ | **Deno** | ≥ 1.40 | Native fetch + Web Crypto. |
119
+ | **React Native** | unverified | Requires `react-native-get-random-values` polyfill. |
120
+
121
+ See [COMPATIBILITY.md](./COMPATIBILITY.md) for the detailed matrix, the
122
+ `exactOptionalPropertyTypes` workaround, and the privacy invariants.
123
+
124
+ ## CDN usage
125
+
126
+ For zero-build prototyping, import from jsDelivr. Replace `<VERSION>` with the
127
+ exact release you want (see the [releases page](https://github.com/isco-tec/scorezilla-js/releases)):
128
+
129
+ ```html
130
+ <script type="module">
131
+ import { Scorezilla } from 'https://cdn.jsdelivr.net/npm/scorezilla@<VERSION>/dist/index.js';
132
+ const sz = new Scorezilla({ publicKey: 'pk_…' });
133
+ // …
134
+ </script>
135
+ ```
136
+
137
+ For production, pair the version pin with
138
+ [Subresource Integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity)
139
+ — the SHA-384 hash for each release ships in the GitHub release notes:
140
+
141
+ ```html
142
+ <script
143
+ type="module"
144
+ src="https://cdn.jsdelivr.net/npm/scorezilla@<VERSION>/dist/index.js"
145
+ integrity="sha384-<copy-from-release-notes>"
146
+ crossorigin="anonymous"
147
+ ></script>
148
+ ```
149
+
150
+ A complete vanilla example lives at
151
+ [`examples/vanilla/`](https://github.com/isco-tec/scorezilla-js/tree/main/examples/vanilla)
152
+ and a Node CLI demo at
153
+ [`examples/node-cli/`](https://github.com/isco-tec/scorezilla-js/tree/main/examples/node-cli).
154
+ (The examples are in the source repo only — not in the npm tarball.)
155
+
156
+ ## Custom fetch / polyfills
157
+
158
+ Pass your own `fetch` for environments where the global is missing or you want
159
+ to mock for tests:
160
+
161
+ ```ts
162
+ import nodeFetch from 'node-fetch';
163
+ const sz = new Scorezilla({ publicKey, fetch: nodeFetch });
164
+ ```
165
+
166
+ The signature
167
+ `(input: RequestInfo | URL, init?: RequestInit) => Promise<Response>` is
168
+ intentionally broader than `typeof fetch` so `node-fetch`, `undici`, `vi.fn()`,
169
+ and `jest.fn()` all typecheck cleanly.
170
+
171
+ ## Per-request timeout and retries
172
+
173
+ ```ts
174
+ const sz = new Scorezilla({
175
+ publicKey,
176
+ timeoutMs: 5_000, // default 30_000
177
+ maxRetries: 1, // default 2 (retries 5xx / 429 / network)
178
+ });
179
+ ```
180
+
181
+ Retries automatically reuse the same `Idempotency-Key` across attempts, so
182
+ server-side dedup (when added) is safe by default.
183
+
184
+ ## Versioning
185
+
186
+ Strict SemVer from v0.1.0 onward. The machine-stable surface is `code` strings
187
+ on `ScorezillaError`, response field names, method signatures, and config
188
+ fields. The human-readable `message` is **not** part of the contract.
189
+
190
+ See [VERSIONING.md](./VERSIONING.md) for the full breaking-change rules,
191
+ deprecation policy, and 0.x → 1.0 exit criteria.
192
+
193
+ ## Contributing
194
+
195
+ Issues and PRs welcome. Local development:
196
+
197
+ ```bash
198
+ pnpm install
199
+ pnpm typecheck
200
+ pnpm test # all projects
201
+ pnpm test:unit # unit only (fast)
202
+ pnpm test:coverage # unit + coverage gate
203
+ pnpm build # tsup → dist/
204
+ pnpm check:types-resolution # attw — exports map validation
205
+ pnpm size # size-limit (6 KB gzip ceiling)
206
+ ```
207
+
208
+ Add a release note with `pnpm changeset` — see [`.changeset/README.md`](./.changeset/README.md) for the workflow.
209
+
210
+ ## License
211
+
212
+ [MIT](./LICENSE) © [isco-tec](https://github.com/isco-tec)
package/VERSIONING.md ADDED
@@ -0,0 +1,119 @@
1
+ # Versioning policy
2
+
3
+ Scorezilla follows [Semantic Versioning](https://semver.org/) starting at
4
+ **v0.1.0**. This document defines what counts as a breaking change, what the
5
+ SemVer contract covers (and explicitly does NOT cover), and the path to v1.0.
6
+
7
+ ## The SemVer contract
8
+
9
+ ### Covered by SemVer
10
+
11
+ **Machine-stable.** A change to any of these requires a major version bump.
12
+
13
+ | What | Where | Example |
14
+ | -------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
15
+ | **Method names** | `Scorezilla.*` | Renaming `submitScore` → `postScore` is a major bump. |
16
+ | **Method signatures (required args, return type)** | `Scorezilla.*` | Adding a required parameter is a major bump. Tightening a return type (`number` → `1 \| 2 \| 3`) is a major bump. |
17
+ | **Error codes** | `ScorezillaError.code` | Renaming `out_of_bounds` → `score_out_of_range` is a major bump. Removing a code is a major bump. |
18
+ | **Error sub-fields** | `ScorezillaError.{reason, retryAfter, bound, layer, status, requestId}` | Renaming `retryAfter` → `retryAfterSec` is a major bump. |
19
+ | **Config field names** | `ScorezillaConfig.*` | Renaming `baseUrl` → `apiOrigin` is a major bump. |
20
+ | **Auth mode discriminator** | `publicKey` vs `secretKey` | Adding a third top-level auth field is non-breaking; renaming either is a major bump. |
21
+ | **Type exports** | `import type { … } from 'scorezilla'` | Removing or renaming a type export is a major bump. |
22
+ | **Default behavior of optional knobs** | `timeoutMs`, `maxRetries`, retry windows | Changing the default of `timeoutMs` from 30 000 to 5 000 is a major bump. |
23
+
24
+ ### NOT covered by SemVer
25
+
26
+ **English-flavored and free to change.**
27
+
28
+ | What | Where | Why |
29
+ | ---------------------------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30
+ | **`message` text on errors** | `ScorezillaError.message` | English-only operator hint. Branch on `code` and `reason` instead. |
31
+ | **Error stack format** | `ScorezillaError.stack` | V8 / SpiderMonkey / JavaScriptCore each format differently and update across runtime versions. |
32
+ | **Whitespace / casing in `User-Agent`** | Header value | Cosmetic. |
33
+ | **Internal module layout** | `src/*` | Not part of the public surface. Importing from anywhere other than `'scorezilla'` (or its documented subpaths `'scorezilla/server'` / `'scorezilla/react'` / `'scorezilla/phaser'`) is unsupported. |
34
+ | **Server-side response field additions** | New fields on `*Response` shapes | Response types are open `Record`-friendly — the API may add fields in a minor release without breaking consumers who don't read them. |
35
+
36
+ ## What counts as a breaking change
37
+
38
+ ### Breaking (major bump)
39
+
40
+ - Removing or renaming an exported symbol (class, function, type)
41
+ - Removing a method on `Scorezilla`
42
+ - Removing a field on a published response type
43
+ - Renaming or removing an error code
44
+ - Adding a **required** argument to a method
45
+ - Narrowing a return type (e.g., `number | undefined` → `number`)
46
+ - Changing the default value of an optional knob in a user-visible way
47
+ - Tightening runtime validation that used to accept some input
48
+ - Dropping support for a documented runtime (e.g., Node 20)
49
+
50
+ ### Non-breaking (minor or patch bump)
51
+
52
+ - Adding a new method
53
+ - Adding a new optional argument with a backward-compatible default
54
+ - Adding a new error code that the server already returns
55
+ - Adding a new field on a response type
56
+ - Bug fixes that align behavior with the documented contract
57
+ - Performance / bundle-size improvements
58
+ - New exported type that doesn't replace an existing one
59
+ - New subpath export (e.g., adding `scorezilla/server` in v0.2.0)
60
+
61
+ ### Patch-bump only
62
+
63
+ - Documentation updates
64
+ - Internal refactors that don't change observable behavior
65
+ - Dependency version bumps within `peerDependencies` semver ranges
66
+
67
+ ## Deprecation policy
68
+
69
+ When we need to remove or replace a public symbol:
70
+
71
+ 1. **Mark deprecated.** Add `@deprecated` JSDoc on the symbol with:
72
+ - The version it was deprecated (`@deprecated 0.5.0`)
73
+ - The version it will be removed (`will be removed in 1.0.0`)
74
+ - The replacement, if any
75
+ 2. **Runtime warning.** First call site emits a single `console.warn`:
76
+ ```
77
+ scorezilla: `oldMethod` is deprecated since 0.5.0 and will be removed in 1.0.0. Use `newMethod` instead.
78
+ ```
79
+ Suppressible via `SCOREZILLA_SUPPRESS_DEPRECATION_WARNINGS=1` env or the
80
+ `suppressDeprecationWarnings: true` config field.
81
+ 3. **Minimum window.** A symbol must spend at least **one full minor release**
82
+ in the deprecated state before removal in the next major. This gives
83
+ consumers a guaranteed upgrade path without forced churn.
84
+ 4. **CHANGELOG entry.** Both the deprecation and the removal are explicitly
85
+ called out.
86
+
87
+ ## 0.x → 1.0 exit criteria
88
+
89
+ The SDK stays in `0.x` until ALL of the following are true:
90
+
91
+ - [ ] 30 days have passed on `main` without a breaking change to the core
92
+ surface (the `Scorezilla` class + `ScorezillaError` + `ScorezillaConfig`).
93
+ - [ ] **`scorezilla/server`** (HMAC adapter, v0.2.0) has shipped and seen
94
+ production use.
95
+ - [ ] **`scorezilla/react`** (React adapter, v0.3.0) has shipped and seen
96
+ production use.
97
+ - [ ] Zero open issues tagged `api-shape-drift` from real-world consumers.
98
+ - [ ] The 4-method surface (`submitScore`, `getLeaderboard`, `getPlayerRank`,
99
+ `getWindowAround`) has not had a signature change for the same 30-day
100
+ window.
101
+
102
+ When all five are met, we cut **v1.0.0** with no breaking changes from the last
103
+ 0.x — v1.0 is just "this is the surface we commit to." Subsequent breaking
104
+ changes follow normal SemVer (major bump).
105
+
106
+ ## Pre-release tags
107
+
108
+ | Tag | Meaning |
109
+ | -------------- | ------------------------------------------------------------------------------------------------------------ |
110
+ | `latest` | Stable, recommended for production. |
111
+ | `next` | Release candidate. Installable via `npm install scorezilla@next`. |
112
+ | `experimental` | Alpha features behind a flag. May break without notice between releases. Not covered by the SemVer contract. |
113
+
114
+ ## Reporting drift
115
+
116
+ If you encounter behavior that contradicts this document — e.g., a `code` value
117
+ changed across a minor release — please file an issue at
118
+ <https://github.com/isco-tec/scorezilla-js/issues> with the tag
119
+ `api-shape-drift`. These reports gate the 1.0 cut.