scorezilla 0.1.0-next.3 → 0.3.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/CHANGELOG.md +184 -0
- package/README.md +13 -0
- package/dist/{errors-CUAQsaVS.d.cts → errors-B7hyC-C5.d.cts} +79 -5
- package/dist/{errors-CUAQsaVS.d.ts → errors-B7hyC-C5.d.ts} +79 -5
- package/dist/identity.cjs +87 -0
- package/dist/identity.cjs.map +1 -0
- package/dist/identity.d.cts +151 -0
- package/dist/identity.d.ts +151 -0
- package/dist/identity.js +82 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.cjs +96 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -6
- package/dist/index.d.ts +18 -6
- package/dist/index.js +96 -10
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +95 -9
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +19 -13
- package/dist/server.d.ts +19 -13
- package/dist/server.js +95 -9
- package/dist/server.js.map +1 -1
- package/package.json +14 -1
package/dist/server.cjs
CHANGED
|
@@ -152,7 +152,9 @@ var ScorezillaError = class _ScorezillaError extends Error {
|
|
|
152
152
|
* {@link ScorezillaErrorCode}. For network errors, this is `'network_error'`;
|
|
153
153
|
* for aborts, `'aborted'`; for timeouts, `'timeout'`. */
|
|
154
154
|
code;
|
|
155
|
-
/** Sub-classifier — present on
|
|
155
|
+
/** Sub-classifier — present on:
|
|
156
|
+
* - `out_of_bounds`: `'below_min' | 'above_max'`
|
|
157
|
+
* - `usage_cap_exceeded`: `'over_cap' | 'suspended'`
|
|
156
158
|
* and possibly other codes in future minor releases. */
|
|
157
159
|
reason;
|
|
158
160
|
/** Seconds — present on `rate_limited`. Honored by the transport's retry
|
|
@@ -165,6 +167,20 @@ var ScorezillaError = class _ScorezillaError extends Error {
|
|
|
165
167
|
bound;
|
|
166
168
|
/** Which rate-limit layer fired on `rate_limited`. */
|
|
167
169
|
layer;
|
|
170
|
+
/** Tenant's billing tier — present on `usage_cap_exceeded`. */
|
|
171
|
+
tier;
|
|
172
|
+
/** The cap value crossed on `usage_cap_exceeded`. `0` indicates a
|
|
173
|
+
* suspended tenant. `undefined` on all other error codes. */
|
|
174
|
+
cap;
|
|
175
|
+
/** The post-increment submit count on `usage_cap_exceeded`. Always
|
|
176
|
+
* `> cap` when `reason === 'over_cap'`. */
|
|
177
|
+
count;
|
|
178
|
+
/** The period the count belongs to on `usage_cap_exceeded`, in `YYYY-MM`
|
|
179
|
+
* UTC form. */
|
|
180
|
+
period;
|
|
181
|
+
/** ISO-8601 timestamp of midnight UTC on the 1st of the next month —
|
|
182
|
+
* the counter's natural reset point on `usage_cap_exceeded`. */
|
|
183
|
+
resetsAt;
|
|
168
184
|
/** The underlying cause (e.g., a `TypeError: fetch failed`) for
|
|
169
185
|
* network/abort/timeout paths. `undefined` when the error came from a
|
|
170
186
|
* successfully-parsed API error body. */
|
|
@@ -179,6 +195,11 @@ var ScorezillaError = class _ScorezillaError extends Error {
|
|
|
179
195
|
this.requestId = truncateField(init.requestId);
|
|
180
196
|
this.bound = init.bound;
|
|
181
197
|
this.layer = truncateField(init.layer);
|
|
198
|
+
this.tier = truncateField(init.tier);
|
|
199
|
+
this.cap = init.cap;
|
|
200
|
+
this.count = init.count;
|
|
201
|
+
this.period = truncateField(init.period);
|
|
202
|
+
this.resetsAt = truncateField(init.resetsAt);
|
|
182
203
|
this.cause = init.cause;
|
|
183
204
|
Object.setPrototypeOf(this, _ScorezillaError.prototype);
|
|
184
205
|
if (typeof Error.captureStackTrace === "function") {
|
|
@@ -192,6 +213,22 @@ var ScorezillaError = class _ScorezillaError extends Error {
|
|
|
192
213
|
isRateLimited() {
|
|
193
214
|
return this.code === "rate_limited";
|
|
194
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* `true` when this error is a 402 / `usage_cap_exceeded`. The tenant
|
|
218
|
+
* has either hit their tier's monthly submit cap (`reason ===
|
|
219
|
+
* 'over_cap'`) or is suspended (`reason === 'suspended'`).
|
|
220
|
+
*
|
|
221
|
+
* Consumers SHOULD NOT auto-retry on this error — the cap doesn't lift
|
|
222
|
+
* until `resetsAt`. Surface to the developer with an upgrade prompt
|
|
223
|
+
* (over_cap) or contact-support message (suspended).
|
|
224
|
+
*/
|
|
225
|
+
isUsageCapExceeded() {
|
|
226
|
+
return this.code === "usage_cap_exceeded";
|
|
227
|
+
}
|
|
228
|
+
/** `true` when this error is a 402 + reason 'suspended' (vs over-cap). */
|
|
229
|
+
isSuspended() {
|
|
230
|
+
return this.code === "usage_cap_exceeded" && this.reason === "suspended";
|
|
231
|
+
}
|
|
195
232
|
/** `true` when this error is a 401 / `unauthorized` (or 403 / `forbidden`). */
|
|
196
233
|
isAuth() {
|
|
197
234
|
return this.code === "unauthorized" || this.code === "forbidden";
|
|
@@ -204,13 +241,21 @@ var ScorezillaError = class _ScorezillaError extends Error {
|
|
|
204
241
|
isOutOfBounds() {
|
|
205
242
|
return this.code === "out_of_bounds";
|
|
206
243
|
}
|
|
207
|
-
/** `true` for
|
|
208
|
-
*
|
|
244
|
+
/** `true` for the SDK's retryable conditions: pure network errors, 5xx, and
|
|
245
|
+
* 429. Deliberately excludes `timeout` and `aborted` — those are caller-
|
|
246
|
+
* observable terminal states, not transient. Aligned with `shouldRetryError`
|
|
247
|
+
* in `retry.ts` so a consumer mirroring the SDK's retry policy gets the
|
|
248
|
+
* same answer the transport does. */
|
|
209
249
|
isTransient() {
|
|
210
|
-
if (this.
|
|
250
|
+
if (this.code === "network_error") return true;
|
|
211
251
|
if (this.status >= 500 && this.status < 600) return true;
|
|
212
252
|
return this.isRateLimited();
|
|
213
253
|
}
|
|
254
|
+
/** `true` when this error is a 409 / `conflict` (idempotency-key conflict
|
|
255
|
+
* on retry). */
|
|
256
|
+
isConflict() {
|
|
257
|
+
return this.code === "conflict";
|
|
258
|
+
}
|
|
214
259
|
// ─── Factory ─────────────────────────────────────────────────────────
|
|
215
260
|
/**
|
|
216
261
|
* Build a `ScorezillaError` from a fetch round-trip outcome.
|
|
@@ -231,6 +276,13 @@ var ScorezillaError = class _ScorezillaError extends Error {
|
|
|
231
276
|
retryAfter: body.retryAfter,
|
|
232
277
|
bound: body.bound,
|
|
233
278
|
layer: body.layer,
|
|
279
|
+
// Usage-cap fields from `ApiError` (populated by the server on
|
|
280
|
+
// 402 responses; undefined on other errors).
|
|
281
|
+
tier: body.tier,
|
|
282
|
+
cap: body.cap,
|
|
283
|
+
count: body.count,
|
|
284
|
+
period: body.period,
|
|
285
|
+
resetsAt: body.resetsAt,
|
|
234
286
|
requestId,
|
|
235
287
|
cause
|
|
236
288
|
});
|
|
@@ -272,8 +324,10 @@ var ScorezillaError = class _ScorezillaError extends Error {
|
|
|
272
324
|
};
|
|
273
325
|
function codeForStatus(status) {
|
|
274
326
|
if (status === 401) return "unauthorized";
|
|
327
|
+
if (status === 402) return "usage_cap_exceeded";
|
|
275
328
|
if (status === 403) return "forbidden";
|
|
276
329
|
if (status === 404) return "not_found";
|
|
330
|
+
if (status === 409) return "conflict";
|
|
277
331
|
if (status === 422) return "out_of_bounds";
|
|
278
332
|
if (status === 429) return "rate_limited";
|
|
279
333
|
if (status >= 500) return "internal_error";
|
|
@@ -384,6 +438,7 @@ async function request(opts) {
|
|
|
384
438
|
}
|
|
385
439
|
const response = await fetchImpl(url, init);
|
|
386
440
|
if (response.ok) {
|
|
441
|
+
warnOnDeprecationOnce(response, opts.warnImpl);
|
|
387
442
|
return await parseJson(response);
|
|
388
443
|
}
|
|
389
444
|
const body = await safelyParseErrorBody(response);
|
|
@@ -495,6 +550,29 @@ function readRetryAfter(response) {
|
|
|
495
550
|
const n = Number(raw);
|
|
496
551
|
return Number.isFinite(n) && n >= 0 ? n : void 0;
|
|
497
552
|
}
|
|
553
|
+
var seenDeprecations = /* @__PURE__ */ new Set();
|
|
554
|
+
function warnOnDeprecationOnce(response, warnImpl) {
|
|
555
|
+
const deprecation = response.headers.get("Deprecation");
|
|
556
|
+
const sunset = response.headers.get("Sunset");
|
|
557
|
+
if (!deprecation && !sunset) return;
|
|
558
|
+
const link = response.headers.get("Link") ?? "";
|
|
559
|
+
const key = `${deprecation ?? ""}|${sunset ?? ""}|${link}`;
|
|
560
|
+
if (seenDeprecations.has(key)) return;
|
|
561
|
+
seenDeprecations.add(key);
|
|
562
|
+
const detail = [];
|
|
563
|
+
if (deprecation === "true" || deprecation) detail.push(`Deprecation: ${deprecation}`);
|
|
564
|
+
if (sunset) detail.push(`Sunset: ${sunset}`);
|
|
565
|
+
if (link) {
|
|
566
|
+
const m = link.match(/<([^>]+)>/);
|
|
567
|
+
if (m) detail.push(`Docs: ${m[1]}`);
|
|
568
|
+
}
|
|
569
|
+
const message = `[scorezilla-sdk] API responded with deprecation signal: ${detail.join(" \xB7 ")}. Upgrade your SDK before the sunset date.`;
|
|
570
|
+
if (warnImpl) {
|
|
571
|
+
warnImpl(message);
|
|
572
|
+
} else {
|
|
573
|
+
console.warn(message);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
498
576
|
function combineSignalsWithTimeout(caller, timeoutMs) {
|
|
499
577
|
const ctrl = new AbortController();
|
|
500
578
|
let didTimeOut = false;
|
|
@@ -552,7 +630,7 @@ var Scorezilla = class _Scorezilla {
|
|
|
552
630
|
* Mirrors the static on the public-key client so consumers can log
|
|
553
631
|
* the running SDK build the same way regardless of which surface
|
|
554
632
|
* they imported. */
|
|
555
|
-
static version = "0.
|
|
633
|
+
static version = "0.3.0-next.0";
|
|
556
634
|
#keyId;
|
|
557
635
|
#secret;
|
|
558
636
|
#baseUrl;
|
|
@@ -564,6 +642,7 @@ var Scorezilla = class _Scorezilla {
|
|
|
564
642
|
#timeoutMs;
|
|
565
643
|
#maxRetries;
|
|
566
644
|
#sleepImpl;
|
|
645
|
+
#warnImpl;
|
|
567
646
|
#userAgent;
|
|
568
647
|
constructor(config) {
|
|
569
648
|
if (isRealBrowserEnvironment()) {
|
|
@@ -586,6 +665,7 @@ var Scorezilla = class _Scorezilla {
|
|
|
586
665
|
this.#timeoutMs = config.timeoutMs;
|
|
587
666
|
this.#maxRetries = config.maxRetries;
|
|
588
667
|
this.#sleepImpl = config.sleepImpl;
|
|
668
|
+
this.#warnImpl = config.warn;
|
|
589
669
|
this.#userAgent = config.userAgent ?? defaultUserAgent(_Scorezilla.version);
|
|
590
670
|
}
|
|
591
671
|
/**
|
|
@@ -610,7 +690,8 @@ var Scorezilla = class _Scorezilla {
|
|
|
610
690
|
playerId: input.playerId,
|
|
611
691
|
score: input.score,
|
|
612
692
|
...input.metadata !== void 0 ? { metadata: input.metadata } : {}
|
|
613
|
-
}
|
|
693
|
+
},
|
|
694
|
+
signal: input.signal
|
|
614
695
|
});
|
|
615
696
|
}
|
|
616
697
|
/** Fetch the top-N entries on a board. */
|
|
@@ -620,14 +701,16 @@ var Scorezilla = class _Scorezilla {
|
|
|
620
701
|
...input.top !== void 0 ? { top: input.top } : {},
|
|
621
702
|
...input.offset !== void 0 ? { offset: input.offset } : {}
|
|
622
703
|
}),
|
|
623
|
-
method: "GET"
|
|
704
|
+
method: "GET",
|
|
705
|
+
signal: input.signal
|
|
624
706
|
});
|
|
625
707
|
}
|
|
626
708
|
/** Look up a single player's rank on a board. */
|
|
627
709
|
async getPlayerRank(input) {
|
|
628
710
|
return this.#request({
|
|
629
711
|
path: getPlayerRankPath(input.boardId, input.playerId),
|
|
630
|
-
method: "GET"
|
|
712
|
+
method: "GET",
|
|
713
|
+
signal: input.signal
|
|
631
714
|
});
|
|
632
715
|
}
|
|
633
716
|
/** Fetch the slice of entries surrounding a player. */
|
|
@@ -637,7 +720,8 @@ var Scorezilla = class _Scorezilla {
|
|
|
637
720
|
...input.before !== void 0 ? { before: input.before } : {},
|
|
638
721
|
...input.after !== void 0 ? { after: input.after } : {}
|
|
639
722
|
}),
|
|
640
|
-
method: "GET"
|
|
723
|
+
method: "GET",
|
|
724
|
+
signal: input.signal
|
|
641
725
|
});
|
|
642
726
|
}
|
|
643
727
|
/**
|
|
@@ -668,7 +752,9 @@ var Scorezilla = class _Scorezilla {
|
|
|
668
752
|
})
|
|
669
753
|
};
|
|
670
754
|
if (opts.body !== void 0) requestOpts.body = opts.body;
|
|
755
|
+
if (opts.signal !== void 0) requestOpts.signal = opts.signal;
|
|
671
756
|
if (this.#fetchImpl !== void 0) requestOpts.fetchImpl = this.#fetchImpl;
|
|
757
|
+
if (this.#warnImpl !== void 0) requestOpts.warnImpl = this.#warnImpl;
|
|
672
758
|
if (this.#timeoutMs !== void 0) requestOpts.timeoutMs = this.#timeoutMs;
|
|
673
759
|
if (this.#maxRetries !== void 0 || this.#sleepImpl !== void 0) {
|
|
674
760
|
requestOpts.retry = {
|