@slytrunk/sly-tifo-client 0.1.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/LICENSE +21 -0
- package/README.md +112 -0
- package/dist/index.cjs +200 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1126 -0
- package/dist/index.d.ts +1126 -0
- package/dist/index.js +169 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,1126 @@
|
|
|
1
|
+
interface operations {
|
|
2
|
+
awardEvent: {
|
|
3
|
+
parameters: {
|
|
4
|
+
query?: never;
|
|
5
|
+
header?: never;
|
|
6
|
+
path?: never;
|
|
7
|
+
cookie?: never;
|
|
8
|
+
};
|
|
9
|
+
requestBody: {
|
|
10
|
+
content: {
|
|
11
|
+
"application/json": {
|
|
12
|
+
/** @description The action definition key, e.g. `purchase`. */
|
|
13
|
+
action: string;
|
|
14
|
+
/** @description Your id for the end user. Required with a secret key; a token already names one. */
|
|
15
|
+
external_id?: string;
|
|
16
|
+
/** @description Optional, and may only name the program the credential is already scoped to. */
|
|
17
|
+
program_id?: string;
|
|
18
|
+
/** @description Used only when enrolling a new end user. */
|
|
19
|
+
display_name?: string;
|
|
20
|
+
/** @description Not accepted on this route today; a browser token sending it is refused outright. */
|
|
21
|
+
avatar_url?: string;
|
|
22
|
+
/** @description Not accepted on this route today; a browser token sending it is refused outright. */
|
|
23
|
+
background_url?: string;
|
|
24
|
+
/** @description When it happened, as an ISO 8601 timestamp — anything Date.parse accepts, UTC offsets included. Defaults to now. */
|
|
25
|
+
occurred_at?: string;
|
|
26
|
+
/** @description A support annotation. Never accepted from a browser token. */
|
|
27
|
+
reason?: string;
|
|
28
|
+
/** @description What the action was about, for definitions that require one. */
|
|
29
|
+
subject?: string;
|
|
30
|
+
/** @description Scored against the action definition payload schema and formula. */
|
|
31
|
+
payload?: unknown;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
responses: {
|
|
36
|
+
/** @description Default Response */
|
|
37
|
+
200: {
|
|
38
|
+
headers: {
|
|
39
|
+
[name: string]: unknown;
|
|
40
|
+
};
|
|
41
|
+
content: {
|
|
42
|
+
"application/json": {
|
|
43
|
+
/** @enum {boolean} */
|
|
44
|
+
accepted: true;
|
|
45
|
+
/** @description True when an Idempotency-Key replayed an earlier award. */
|
|
46
|
+
replayed: boolean;
|
|
47
|
+
event_id: string;
|
|
48
|
+
/** @description Points this event awarded. */
|
|
49
|
+
points: number;
|
|
50
|
+
/** @description The end user score in the current season after it. */
|
|
51
|
+
score: number;
|
|
52
|
+
lifetime_points: number;
|
|
53
|
+
tier_key: string | null;
|
|
54
|
+
/** @description `{ "fromRank": number|null, "toRank": number|null }` when the tier moved, else null. */
|
|
55
|
+
transition: unknown;
|
|
56
|
+
/**
|
|
57
|
+
* @description Present only when the action is a collectible. `earned` on the accepted branch; the rest on refusals.
|
|
58
|
+
* @enum {string}
|
|
59
|
+
*/
|
|
60
|
+
outcome?: "earned" | "already-owned" | "not-yet-open" | "closed" | "tier-locked";
|
|
61
|
+
} | {
|
|
62
|
+
/** @enum {boolean} */
|
|
63
|
+
accepted: false;
|
|
64
|
+
reason: string;
|
|
65
|
+
/**
|
|
66
|
+
* @description Present only when the action is a collectible. `earned` on the accepted branch; the rest on refusals.
|
|
67
|
+
* @enum {string}
|
|
68
|
+
*/
|
|
69
|
+
outcome?: "earned" | "already-owned" | "not-yet-open" | "closed" | "tier-locked";
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
/** @description Default Response */
|
|
74
|
+
400: {
|
|
75
|
+
headers: {
|
|
76
|
+
[name: string]: unknown;
|
|
77
|
+
};
|
|
78
|
+
content: {
|
|
79
|
+
"application/json": {
|
|
80
|
+
error: string;
|
|
81
|
+
message: string;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
/** @description Default Response */
|
|
86
|
+
422: {
|
|
87
|
+
headers: {
|
|
88
|
+
[name: string]: unknown;
|
|
89
|
+
};
|
|
90
|
+
content: {
|
|
91
|
+
"application/json": {
|
|
92
|
+
error: string;
|
|
93
|
+
message: string;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
/** @description Rate limited. */
|
|
98
|
+
429: {
|
|
99
|
+
headers: {
|
|
100
|
+
[name: string]: unknown;
|
|
101
|
+
};
|
|
102
|
+
content: {
|
|
103
|
+
"application/json": {
|
|
104
|
+
error: string;
|
|
105
|
+
message: string;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
getEndUserStanding: {
|
|
112
|
+
parameters: {
|
|
113
|
+
query?: {
|
|
114
|
+
program_id?: string;
|
|
115
|
+
};
|
|
116
|
+
header?: never;
|
|
117
|
+
path: {
|
|
118
|
+
externalId: string;
|
|
119
|
+
};
|
|
120
|
+
cookie?: never;
|
|
121
|
+
};
|
|
122
|
+
requestBody?: never;
|
|
123
|
+
responses: {
|
|
124
|
+
/** @description Default Response */
|
|
125
|
+
200: {
|
|
126
|
+
headers: {
|
|
127
|
+
[name: string]: unknown;
|
|
128
|
+
};
|
|
129
|
+
content: {
|
|
130
|
+
"application/json": {
|
|
131
|
+
end_user: {
|
|
132
|
+
id: string;
|
|
133
|
+
external_id: string;
|
|
134
|
+
display_name: string;
|
|
135
|
+
attributes: {
|
|
136
|
+
[key: string]: string | number | boolean;
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
program: {
|
|
140
|
+
id: string;
|
|
141
|
+
name: string;
|
|
142
|
+
};
|
|
143
|
+
season: {
|
|
144
|
+
slug: string;
|
|
145
|
+
name: string;
|
|
146
|
+
ends_at: string | null;
|
|
147
|
+
};
|
|
148
|
+
score: number;
|
|
149
|
+
lifetime_points: number;
|
|
150
|
+
tier: {
|
|
151
|
+
key: string;
|
|
152
|
+
label: string;
|
|
153
|
+
rank: number;
|
|
154
|
+
threshold: number;
|
|
155
|
+
} | null;
|
|
156
|
+
next_tier: {
|
|
157
|
+
key: string;
|
|
158
|
+
threshold: number;
|
|
159
|
+
points_needed: number;
|
|
160
|
+
} | null;
|
|
161
|
+
peak: {
|
|
162
|
+
rank: number;
|
|
163
|
+
season: {
|
|
164
|
+
slug: string;
|
|
165
|
+
};
|
|
166
|
+
} | null;
|
|
167
|
+
card_url: string | null;
|
|
168
|
+
card_version: string | null;
|
|
169
|
+
cards: {
|
|
170
|
+
slug: string;
|
|
171
|
+
name: string;
|
|
172
|
+
url: string;
|
|
173
|
+
version: string;
|
|
174
|
+
}[];
|
|
175
|
+
collectibles: {
|
|
176
|
+
id: string;
|
|
177
|
+
key: string;
|
|
178
|
+
name: string;
|
|
179
|
+
description: string | null;
|
|
180
|
+
/** @description Collection keys; see `collections`. */
|
|
181
|
+
collections: string[];
|
|
182
|
+
rarity: number;
|
|
183
|
+
owned: boolean;
|
|
184
|
+
earned_at: string | null;
|
|
185
|
+
/**
|
|
186
|
+
* @description Where this collectible stands for EARNING, not for display. `upcoming` — its window has not opened. `open` — earnable now. `closed` — its window has ended. `archived` — retired by the program and no longer earnable whatever its dates say, which is why archived wins over the window. Recomputed from the program’s settings and the CURRENT time on every request, and blind to who is reading: earning a collectible does not change its `state`, so one earned while `open` reports `closed` once its window ends and `archived` once it is retired. Do not cache this as a fact about the earn; read `owned` for whether this end user has it.
|
|
187
|
+
* @enum {string}
|
|
188
|
+
*/
|
|
189
|
+
state: "upcoming" | "open" | "closed" | "archived";
|
|
190
|
+
/** @description True when this end user is below the collectible minimum tier rank. Always false once owned. */
|
|
191
|
+
tier_locked: boolean;
|
|
192
|
+
/** @description Whether a browser holding an end-user token may claim this collectible by POSTing its `key` as an action. False means the secret-key door only. */
|
|
193
|
+
client_fireable: boolean;
|
|
194
|
+
/**
|
|
195
|
+
* @description The picture this end user may see for this collectible right now — public, content-addressed, never expires. For an unearned collectible this is not necessarily the real art: it may be the program's own teased or uncollected treatment instead, when the program has configured one. Most commonly null for an unowned teased collectible still `upcoming` with no teased art configured, on the collectible or as a program default — but null is also possible on any collectible, owned or not, whose own art has no usable image at all (deleted or a non-image asset).
|
|
196
|
+
*
|
|
197
|
+
* Which picture this is follows one rule, ownership first and then `state`: an OWNED collectible always sends its own art; an unowned `teased` one whose `state` is `upcoming` sends the teased picture (its own, else the program default, else null) and never falls through to the uncollected treatment; every other unowned collectible sends the uncollected picture (its own, else the program default, else the real art). An unowned `hidden` collectible is likewise absent from this array entirely only while its `state` is `upcoming`, and reappears as soon as it is not — because the window opened, or because archiving the collectible made its `state` `archived` whatever its dates say — whether or not it has been earned.
|
|
198
|
+
*
|
|
199
|
+
* A CARD MAY DISAGREE, deliberately. A card has no clock, so it keys teased and hidden off ownership alone: a teased collectible goes on showing its teased picture on a card until it is earned, while this field stops teasing the moment the collectible stops being `upcoming`.
|
|
200
|
+
*
|
|
201
|
+
* AUTHORITATIVE, unlike the same collectible’s `art_url` on GET /v1/collectibles: that route cannot know who owns what, so it sends the unearned treatment to everyone. If you read both bodies, prefer this value wherever a collectible appears in both.
|
|
202
|
+
*
|
|
203
|
+
* May be an `image/svg+xml` document rather than a raster image; a native `<Image>` (e.g. React Native) will not render an SVG.
|
|
204
|
+
*/
|
|
205
|
+
art_url: string | null;
|
|
206
|
+
/** @description Public, content-addressed, at most 256px on the longest edge; the full-size `art_url` when no thumbnail exists yet. It follows `art_url` exactly: AUTHORITATIVE, so prefer it over the same collectible’s `art_thumb_url` on GET /v1/collectibles, which is the unearned baseline. For an `image/svg+xml` `art_url`, this is always that same unresized original with no size bound, by design — there is never a thumbnail for vector art. */
|
|
207
|
+
art_thumb_url: string | null;
|
|
208
|
+
}[];
|
|
209
|
+
collections: {
|
|
210
|
+
key: string;
|
|
211
|
+
name: string;
|
|
212
|
+
description: string | null;
|
|
213
|
+
sort: number;
|
|
214
|
+
}[];
|
|
215
|
+
} | {
|
|
216
|
+
/** @enum {boolean} */
|
|
217
|
+
accepted: false;
|
|
218
|
+
reason: string;
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
};
|
|
222
|
+
/** @description Default Response */
|
|
223
|
+
404: {
|
|
224
|
+
headers: {
|
|
225
|
+
[name: string]: unknown;
|
|
226
|
+
};
|
|
227
|
+
content: {
|
|
228
|
+
"application/json": {
|
|
229
|
+
error: string;
|
|
230
|
+
message: string;
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
/** @description Default Response */
|
|
235
|
+
422: {
|
|
236
|
+
headers: {
|
|
237
|
+
[name: string]: unknown;
|
|
238
|
+
};
|
|
239
|
+
content: {
|
|
240
|
+
"application/json": {
|
|
241
|
+
error: string;
|
|
242
|
+
message: string;
|
|
243
|
+
};
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
/** @description Rate limited. */
|
|
247
|
+
429: {
|
|
248
|
+
headers: {
|
|
249
|
+
[name: string]: unknown;
|
|
250
|
+
};
|
|
251
|
+
content: {
|
|
252
|
+
"application/json": {
|
|
253
|
+
error: string;
|
|
254
|
+
message: string;
|
|
255
|
+
};
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
};
|
|
260
|
+
patchEndUser: {
|
|
261
|
+
parameters: {
|
|
262
|
+
query?: never;
|
|
263
|
+
header?: never;
|
|
264
|
+
path: {
|
|
265
|
+
externalId: string;
|
|
266
|
+
};
|
|
267
|
+
cookie?: never;
|
|
268
|
+
};
|
|
269
|
+
/** @description Secret key only — a supporter must not be able to rename themselves or choose their own picture. Attribute keys are validated against the program's declared attribute_schema at request time, so a key legal for one program may be refused for another. An attribute value of null clears that key rather than setting it to null. */
|
|
270
|
+
requestBody: {
|
|
271
|
+
content: {
|
|
272
|
+
"application/json": {
|
|
273
|
+
display_name?: string | null;
|
|
274
|
+
attributes?: {
|
|
275
|
+
[key: string]: (string | number | boolean) | null;
|
|
276
|
+
};
|
|
277
|
+
};
|
|
278
|
+
};
|
|
279
|
+
};
|
|
280
|
+
responses: {
|
|
281
|
+
/** @description Default Response */
|
|
282
|
+
200: {
|
|
283
|
+
headers: {
|
|
284
|
+
[name: string]: unknown;
|
|
285
|
+
};
|
|
286
|
+
content: {
|
|
287
|
+
"application/json": {
|
|
288
|
+
external_id: string;
|
|
289
|
+
display_name: string;
|
|
290
|
+
};
|
|
291
|
+
};
|
|
292
|
+
};
|
|
293
|
+
/** @description Default Response */
|
|
294
|
+
422: {
|
|
295
|
+
headers: {
|
|
296
|
+
[name: string]: unknown;
|
|
297
|
+
};
|
|
298
|
+
content: {
|
|
299
|
+
"application/json": {
|
|
300
|
+
error: string;
|
|
301
|
+
message: string;
|
|
302
|
+
};
|
|
303
|
+
};
|
|
304
|
+
};
|
|
305
|
+
/** @description Rate limited. */
|
|
306
|
+
429: {
|
|
307
|
+
headers: {
|
|
308
|
+
[name: string]: unknown;
|
|
309
|
+
};
|
|
310
|
+
content: {
|
|
311
|
+
"application/json": {
|
|
312
|
+
error: string;
|
|
313
|
+
message: string;
|
|
314
|
+
};
|
|
315
|
+
};
|
|
316
|
+
};
|
|
317
|
+
};
|
|
318
|
+
};
|
|
319
|
+
listEndUserEvents: {
|
|
320
|
+
parameters: {
|
|
321
|
+
query?: {
|
|
322
|
+
program_id?: string;
|
|
323
|
+
limit?: number;
|
|
324
|
+
before?: string;
|
|
325
|
+
};
|
|
326
|
+
header?: never;
|
|
327
|
+
path: {
|
|
328
|
+
externalId: string;
|
|
329
|
+
};
|
|
330
|
+
cookie?: never;
|
|
331
|
+
};
|
|
332
|
+
requestBody?: never;
|
|
333
|
+
responses: {
|
|
334
|
+
/** @description Default Response */
|
|
335
|
+
200: {
|
|
336
|
+
headers: {
|
|
337
|
+
[name: string]: unknown;
|
|
338
|
+
};
|
|
339
|
+
content: {
|
|
340
|
+
"application/json": {
|
|
341
|
+
events: {
|
|
342
|
+
id: string;
|
|
343
|
+
action: string;
|
|
344
|
+
points: number;
|
|
345
|
+
occurred_at: string;
|
|
346
|
+
subject: string | null;
|
|
347
|
+
reason: string | null;
|
|
348
|
+
payload: unknown;
|
|
349
|
+
}[];
|
|
350
|
+
next_cursor: string | null;
|
|
351
|
+
} | {
|
|
352
|
+
/** @enum {boolean} */
|
|
353
|
+
accepted: false;
|
|
354
|
+
reason: string;
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
};
|
|
358
|
+
/** @description Default Response */
|
|
359
|
+
404: {
|
|
360
|
+
headers: {
|
|
361
|
+
[name: string]: unknown;
|
|
362
|
+
};
|
|
363
|
+
content: {
|
|
364
|
+
"application/json": {
|
|
365
|
+
error: string;
|
|
366
|
+
message: string;
|
|
367
|
+
};
|
|
368
|
+
};
|
|
369
|
+
};
|
|
370
|
+
/** @description Default Response */
|
|
371
|
+
422: {
|
|
372
|
+
headers: {
|
|
373
|
+
[name: string]: unknown;
|
|
374
|
+
};
|
|
375
|
+
content: {
|
|
376
|
+
"application/json": {
|
|
377
|
+
error: string;
|
|
378
|
+
message: string;
|
|
379
|
+
};
|
|
380
|
+
};
|
|
381
|
+
};
|
|
382
|
+
/** @description Rate limited. */
|
|
383
|
+
429: {
|
|
384
|
+
headers: {
|
|
385
|
+
[name: string]: unknown;
|
|
386
|
+
};
|
|
387
|
+
content: {
|
|
388
|
+
"application/json": {
|
|
389
|
+
error: string;
|
|
390
|
+
message: string;
|
|
391
|
+
};
|
|
392
|
+
};
|
|
393
|
+
};
|
|
394
|
+
};
|
|
395
|
+
};
|
|
396
|
+
mintEndUserToken: {
|
|
397
|
+
parameters: {
|
|
398
|
+
query?: never;
|
|
399
|
+
header?: never;
|
|
400
|
+
path?: never;
|
|
401
|
+
cookie?: never;
|
|
402
|
+
};
|
|
403
|
+
requestBody: {
|
|
404
|
+
content: {
|
|
405
|
+
"application/json": {
|
|
406
|
+
external_id: string;
|
|
407
|
+
display_name?: string;
|
|
408
|
+
};
|
|
409
|
+
};
|
|
410
|
+
};
|
|
411
|
+
responses: {
|
|
412
|
+
/** @description Default Response */
|
|
413
|
+
200: {
|
|
414
|
+
headers: {
|
|
415
|
+
[name: string]: unknown;
|
|
416
|
+
};
|
|
417
|
+
content: {
|
|
418
|
+
"application/json": {
|
|
419
|
+
token: string;
|
|
420
|
+
expires_at: string;
|
|
421
|
+
};
|
|
422
|
+
};
|
|
423
|
+
};
|
|
424
|
+
/** @description Default Response */
|
|
425
|
+
422: {
|
|
426
|
+
headers: {
|
|
427
|
+
[name: string]: unknown;
|
|
428
|
+
};
|
|
429
|
+
content: {
|
|
430
|
+
"application/json": {
|
|
431
|
+
error: string;
|
|
432
|
+
message: string;
|
|
433
|
+
};
|
|
434
|
+
};
|
|
435
|
+
};
|
|
436
|
+
/** @description Rate limited. */
|
|
437
|
+
429: {
|
|
438
|
+
headers: {
|
|
439
|
+
[name: string]: unknown;
|
|
440
|
+
};
|
|
441
|
+
content: {
|
|
442
|
+
"application/json": {
|
|
443
|
+
error: string;
|
|
444
|
+
message: string;
|
|
445
|
+
};
|
|
446
|
+
};
|
|
447
|
+
};
|
|
448
|
+
};
|
|
449
|
+
};
|
|
450
|
+
getMyStanding: {
|
|
451
|
+
parameters: {
|
|
452
|
+
query?: never;
|
|
453
|
+
header?: never;
|
|
454
|
+
path?: never;
|
|
455
|
+
cookie?: never;
|
|
456
|
+
};
|
|
457
|
+
requestBody?: never;
|
|
458
|
+
responses: {
|
|
459
|
+
/** @description Default Response */
|
|
460
|
+
200: {
|
|
461
|
+
headers: {
|
|
462
|
+
[name: string]: unknown;
|
|
463
|
+
};
|
|
464
|
+
content: {
|
|
465
|
+
"application/json": {
|
|
466
|
+
end_user: {
|
|
467
|
+
id: string;
|
|
468
|
+
external_id: string;
|
|
469
|
+
display_name: string;
|
|
470
|
+
attributes: {
|
|
471
|
+
[key: string]: string | number | boolean;
|
|
472
|
+
};
|
|
473
|
+
};
|
|
474
|
+
program: {
|
|
475
|
+
id: string;
|
|
476
|
+
name: string;
|
|
477
|
+
};
|
|
478
|
+
season: {
|
|
479
|
+
slug: string;
|
|
480
|
+
name: string;
|
|
481
|
+
ends_at: string | null;
|
|
482
|
+
};
|
|
483
|
+
score: number;
|
|
484
|
+
lifetime_points: number;
|
|
485
|
+
tier: {
|
|
486
|
+
key: string;
|
|
487
|
+
label: string;
|
|
488
|
+
rank: number;
|
|
489
|
+
threshold: number;
|
|
490
|
+
} | null;
|
|
491
|
+
next_tier: {
|
|
492
|
+
key: string;
|
|
493
|
+
threshold: number;
|
|
494
|
+
points_needed: number;
|
|
495
|
+
} | null;
|
|
496
|
+
peak: {
|
|
497
|
+
rank: number;
|
|
498
|
+
season: {
|
|
499
|
+
slug: string;
|
|
500
|
+
};
|
|
501
|
+
} | null;
|
|
502
|
+
card_url: string | null;
|
|
503
|
+
card_version: string | null;
|
|
504
|
+
cards: {
|
|
505
|
+
slug: string;
|
|
506
|
+
name: string;
|
|
507
|
+
url: string;
|
|
508
|
+
version: string;
|
|
509
|
+
}[];
|
|
510
|
+
collectibles: {
|
|
511
|
+
id: string;
|
|
512
|
+
key: string;
|
|
513
|
+
name: string;
|
|
514
|
+
description: string | null;
|
|
515
|
+
/** @description Collection keys; see `collections`. */
|
|
516
|
+
collections: string[];
|
|
517
|
+
rarity: number;
|
|
518
|
+
owned: boolean;
|
|
519
|
+
earned_at: string | null;
|
|
520
|
+
/**
|
|
521
|
+
* @description Where this collectible stands for EARNING, not for display. `upcoming` — its window has not opened. `open` — earnable now. `closed` — its window has ended. `archived` — retired by the program and no longer earnable whatever its dates say, which is why archived wins over the window. Recomputed from the program’s settings and the CURRENT time on every request, and blind to who is reading: earning a collectible does not change its `state`, so one earned while `open` reports `closed` once its window ends and `archived` once it is retired. Do not cache this as a fact about the earn; read `owned` for whether this end user has it.
|
|
522
|
+
* @enum {string}
|
|
523
|
+
*/
|
|
524
|
+
state: "upcoming" | "open" | "closed" | "archived";
|
|
525
|
+
/** @description True when this end user is below the collectible minimum tier rank. Always false once owned. */
|
|
526
|
+
tier_locked: boolean;
|
|
527
|
+
/** @description Whether a browser holding an end-user token may claim this collectible by POSTing its `key` as an action. False means the secret-key door only. */
|
|
528
|
+
client_fireable: boolean;
|
|
529
|
+
/**
|
|
530
|
+
* @description The picture this end user may see for this collectible right now — public, content-addressed, never expires. For an unearned collectible this is not necessarily the real art: it may be the program's own teased or uncollected treatment instead, when the program has configured one. Most commonly null for an unowned teased collectible still `upcoming` with no teased art configured, on the collectible or as a program default — but null is also possible on any collectible, owned or not, whose own art has no usable image at all (deleted or a non-image asset).
|
|
531
|
+
*
|
|
532
|
+
* Which picture this is follows one rule, ownership first and then `state`: an OWNED collectible always sends its own art; an unowned `teased` one whose `state` is `upcoming` sends the teased picture (its own, else the program default, else null) and never falls through to the uncollected treatment; every other unowned collectible sends the uncollected picture (its own, else the program default, else the real art). An unowned `hidden` collectible is likewise absent from this array entirely only while its `state` is `upcoming`, and reappears as soon as it is not — because the window opened, or because archiving the collectible made its `state` `archived` whatever its dates say — whether or not it has been earned.
|
|
533
|
+
*
|
|
534
|
+
* A CARD MAY DISAGREE, deliberately. A card has no clock, so it keys teased and hidden off ownership alone: a teased collectible goes on showing its teased picture on a card until it is earned, while this field stops teasing the moment the collectible stops being `upcoming`.
|
|
535
|
+
*
|
|
536
|
+
* AUTHORITATIVE, unlike the same collectible’s `art_url` on GET /v1/collectibles: that route cannot know who owns what, so it sends the unearned treatment to everyone. If you read both bodies, prefer this value wherever a collectible appears in both.
|
|
537
|
+
*
|
|
538
|
+
* May be an `image/svg+xml` document rather than a raster image; a native `<Image>` (e.g. React Native) will not render an SVG.
|
|
539
|
+
*/
|
|
540
|
+
art_url: string | null;
|
|
541
|
+
/** @description Public, content-addressed, at most 256px on the longest edge; the full-size `art_url` when no thumbnail exists yet. It follows `art_url` exactly: AUTHORITATIVE, so prefer it over the same collectible’s `art_thumb_url` on GET /v1/collectibles, which is the unearned baseline. For an `image/svg+xml` `art_url`, this is always that same unresized original with no size bound, by design — there is never a thumbnail for vector art. */
|
|
542
|
+
art_thumb_url: string | null;
|
|
543
|
+
}[];
|
|
544
|
+
collections: {
|
|
545
|
+
key: string;
|
|
546
|
+
name: string;
|
|
547
|
+
description: string | null;
|
|
548
|
+
sort: number;
|
|
549
|
+
}[];
|
|
550
|
+
} | {
|
|
551
|
+
/** @enum {boolean} */
|
|
552
|
+
accepted: false;
|
|
553
|
+
reason: string;
|
|
554
|
+
};
|
|
555
|
+
};
|
|
556
|
+
};
|
|
557
|
+
/** @description Rate limited. */
|
|
558
|
+
429: {
|
|
559
|
+
headers: {
|
|
560
|
+
[name: string]: unknown;
|
|
561
|
+
};
|
|
562
|
+
content: {
|
|
563
|
+
"application/json": {
|
|
564
|
+
error: string;
|
|
565
|
+
message: string;
|
|
566
|
+
};
|
|
567
|
+
};
|
|
568
|
+
};
|
|
569
|
+
};
|
|
570
|
+
};
|
|
571
|
+
listCollections: {
|
|
572
|
+
parameters: {
|
|
573
|
+
query?: never;
|
|
574
|
+
header?: never;
|
|
575
|
+
path?: never;
|
|
576
|
+
cookie?: never;
|
|
577
|
+
};
|
|
578
|
+
requestBody?: never;
|
|
579
|
+
responses: {
|
|
580
|
+
/** @description Default Response */
|
|
581
|
+
200: {
|
|
582
|
+
headers: {
|
|
583
|
+
[name: string]: unknown;
|
|
584
|
+
};
|
|
585
|
+
content: {
|
|
586
|
+
"application/json": {
|
|
587
|
+
collections: {
|
|
588
|
+
key: string;
|
|
589
|
+
name: string;
|
|
590
|
+
description: string | null;
|
|
591
|
+
sort: number;
|
|
592
|
+
}[];
|
|
593
|
+
};
|
|
594
|
+
};
|
|
595
|
+
};
|
|
596
|
+
/** @description Rate limited. */
|
|
597
|
+
429: {
|
|
598
|
+
headers: {
|
|
599
|
+
[name: string]: unknown;
|
|
600
|
+
};
|
|
601
|
+
content: {
|
|
602
|
+
"application/json": {
|
|
603
|
+
error: string;
|
|
604
|
+
message: string;
|
|
605
|
+
};
|
|
606
|
+
};
|
|
607
|
+
};
|
|
608
|
+
};
|
|
609
|
+
};
|
|
610
|
+
listCollectibles: {
|
|
611
|
+
parameters: {
|
|
612
|
+
query?: never;
|
|
613
|
+
header?: never;
|
|
614
|
+
path?: never;
|
|
615
|
+
cookie?: never;
|
|
616
|
+
};
|
|
617
|
+
requestBody?: never;
|
|
618
|
+
responses: {
|
|
619
|
+
/** @description Default Response */
|
|
620
|
+
200: {
|
|
621
|
+
headers: {
|
|
622
|
+
[name: string]: unknown;
|
|
623
|
+
};
|
|
624
|
+
content: {
|
|
625
|
+
"application/json": {
|
|
626
|
+
collectibles: {
|
|
627
|
+
id: string;
|
|
628
|
+
/** @description The identifier you POST to /v1/events to claim this. */
|
|
629
|
+
key: string;
|
|
630
|
+
name: string;
|
|
631
|
+
description: string | null;
|
|
632
|
+
/** @description Collection keys; see GET /v1/collections. */
|
|
633
|
+
collections: string[];
|
|
634
|
+
/** @description Set by the club, not measured. See GET /v1/collectibles/{key}. */
|
|
635
|
+
rarity: number;
|
|
636
|
+
/** @enum {string} */
|
|
637
|
+
state: "upcoming" | "open" | "closed" | "archived";
|
|
638
|
+
starts_at: string | null;
|
|
639
|
+
ends_at: string | null;
|
|
640
|
+
/** @description Whether a browser holding an end-user token may claim this. False means the secret-key door only. */
|
|
641
|
+
client_fireable: boolean;
|
|
642
|
+
/**
|
|
643
|
+
* @description The picture a supporter who has NOT earned this collectible should see — public, content-addressed, never expires. This route is the unearned baseline: it cannot know who owns what, so it sends the program’s "uncollected" treatment when one is configured (on the collectible, else the program default), and the real art only when neither is.
|
|
644
|
+
*
|
|
645
|
+
* PREFER `GET /v1/me`’s `art_url` for any collectible that appears in both bodies. That one knows what the calling supporter owns and sends the REAL art for an earned collectible; this one would render it as unearned. Fall back to this field only for a collectible `/v1/me` did not carry.
|
|
646
|
+
*
|
|
647
|
+
* A `teased` collectible before its window opens is the exception to the baseline: teasing is keyed to the window, which is the same for every caller, so this is its teased art, or the program’s teased default, or null if neither is configured. Null is also possible, in any state, for a collectible whose own art has no usable image at all.
|
|
648
|
+
*
|
|
649
|
+
* May be an `image/svg+xml` document rather than a raster image; a native `<Image>` (e.g. React Native) will not render an SVG.
|
|
650
|
+
*/
|
|
651
|
+
art_url: string | null;
|
|
652
|
+
/** @description At most 256px on the longest edge; the full-size art when none exists yet. It follows `art_url` exactly: the unearned baseline, so prefer `GET /v1/me`’s `art_thumb_url` for any collectible that appears in both bodies. For an `image/svg+xml` `art_url`, this is always that same unresized original with no size bound, by design — there is never a thumbnail for vector art. */
|
|
653
|
+
art_thumb_url: string | null;
|
|
654
|
+
}[];
|
|
655
|
+
};
|
|
656
|
+
};
|
|
657
|
+
};
|
|
658
|
+
/** @description Rate limited. */
|
|
659
|
+
429: {
|
|
660
|
+
headers: {
|
|
661
|
+
[name: string]: unknown;
|
|
662
|
+
};
|
|
663
|
+
content: {
|
|
664
|
+
"application/json": {
|
|
665
|
+
error: string;
|
|
666
|
+
message: string;
|
|
667
|
+
};
|
|
668
|
+
};
|
|
669
|
+
};
|
|
670
|
+
};
|
|
671
|
+
};
|
|
672
|
+
getCollectible: {
|
|
673
|
+
parameters: {
|
|
674
|
+
query?: never;
|
|
675
|
+
header?: never;
|
|
676
|
+
path: {
|
|
677
|
+
key: string;
|
|
678
|
+
};
|
|
679
|
+
cookie?: never;
|
|
680
|
+
};
|
|
681
|
+
requestBody?: never;
|
|
682
|
+
responses: {
|
|
683
|
+
/** @description Default Response */
|
|
684
|
+
200: {
|
|
685
|
+
headers: {
|
|
686
|
+
[name: string]: unknown;
|
|
687
|
+
};
|
|
688
|
+
content: {
|
|
689
|
+
"application/json": {
|
|
690
|
+
id: string;
|
|
691
|
+
/** @description The identifier you POST to /v1/events to claim this. */
|
|
692
|
+
key: string;
|
|
693
|
+
name: string;
|
|
694
|
+
description: string | null;
|
|
695
|
+
/** @description Collection keys; see GET /v1/collections. */
|
|
696
|
+
collections: string[];
|
|
697
|
+
/** @description Set by the club, not measured. See GET /v1/collectibles/{key}. */
|
|
698
|
+
rarity: number;
|
|
699
|
+
/** @enum {string} */
|
|
700
|
+
state: "upcoming" | "open" | "closed" | "archived";
|
|
701
|
+
starts_at: string | null;
|
|
702
|
+
ends_at: string | null;
|
|
703
|
+
/** @description Whether a browser holding an end-user token may claim this. False means the secret-key door only. */
|
|
704
|
+
client_fireable: boolean;
|
|
705
|
+
/**
|
|
706
|
+
* @description The picture a supporter who has NOT earned this collectible should see — public, content-addressed, never expires. This route is the unearned baseline: it cannot know who owns what, so it sends the program’s "uncollected" treatment when one is configured (on the collectible, else the program default), and the real art only when neither is.
|
|
707
|
+
*
|
|
708
|
+
* PREFER `GET /v1/me`’s `art_url` for any collectible that appears in both bodies. That one knows what the calling supporter owns and sends the REAL art for an earned collectible; this one would render it as unearned. Fall back to this field only for a collectible `/v1/me` did not carry.
|
|
709
|
+
*
|
|
710
|
+
* A `teased` collectible before its window opens is the exception to the baseline: teasing is keyed to the window, which is the same for every caller, so this is its teased art, or the program’s teased default, or null if neither is configured. Null is also possible, in any state, for a collectible whose own art has no usable image at all.
|
|
711
|
+
*
|
|
712
|
+
* May be an `image/svg+xml` document rather than a raster image; a native `<Image>` (e.g. React Native) will not render an SVG.
|
|
713
|
+
*/
|
|
714
|
+
art_url: string | null;
|
|
715
|
+
/** @description At most 256px on the longest edge; the full-size art when none exists yet. It follows `art_url` exactly: the unearned baseline, so prefer `GET /v1/me`’s `art_thumb_url` for any collectible that appears in both bodies. For an `image/svg+xml` `art_url`, this is always that same unresized original with no size bound, by design — there is never a thumbnail for vector art. */
|
|
716
|
+
art_thumb_url: string | null;
|
|
717
|
+
/** @description End users who have earned this collectible. */
|
|
718
|
+
owner_count: number;
|
|
719
|
+
/** @description Every end user in the program. The denominator for `owner_count`. */
|
|
720
|
+
program_end_user_count: number;
|
|
721
|
+
};
|
|
722
|
+
};
|
|
723
|
+
};
|
|
724
|
+
/** @description Default Response */
|
|
725
|
+
404: {
|
|
726
|
+
headers: {
|
|
727
|
+
[name: string]: unknown;
|
|
728
|
+
};
|
|
729
|
+
content: {
|
|
730
|
+
"application/json": {
|
|
731
|
+
error: string;
|
|
732
|
+
message: string;
|
|
733
|
+
};
|
|
734
|
+
};
|
|
735
|
+
};
|
|
736
|
+
/** @description Rate limited. */
|
|
737
|
+
429: {
|
|
738
|
+
headers: {
|
|
739
|
+
[name: string]: unknown;
|
|
740
|
+
};
|
|
741
|
+
content: {
|
|
742
|
+
"application/json": {
|
|
743
|
+
error: string;
|
|
744
|
+
message: string;
|
|
745
|
+
};
|
|
746
|
+
};
|
|
747
|
+
};
|
|
748
|
+
};
|
|
749
|
+
};
|
|
750
|
+
listActions: {
|
|
751
|
+
parameters: {
|
|
752
|
+
query?: never;
|
|
753
|
+
header?: never;
|
|
754
|
+
path?: never;
|
|
755
|
+
cookie?: never;
|
|
756
|
+
};
|
|
757
|
+
requestBody?: never;
|
|
758
|
+
responses: {
|
|
759
|
+
/** @description Default Response */
|
|
760
|
+
200: {
|
|
761
|
+
headers: {
|
|
762
|
+
[name: string]: unknown;
|
|
763
|
+
};
|
|
764
|
+
content: {
|
|
765
|
+
"application/json": {
|
|
766
|
+
actions: {
|
|
767
|
+
/** @description What you send as `action` to POST /v1/events. */
|
|
768
|
+
key: string;
|
|
769
|
+
name: string;
|
|
770
|
+
description: string | null;
|
|
771
|
+
/**
|
|
772
|
+
* @description A `collectible` also appears in GET /v1/collectibles, with its art — unless it is an unannounced one (`reveal: hidden`, before its window), which this route omits too.
|
|
773
|
+
* @enum {string}
|
|
774
|
+
*/
|
|
775
|
+
kind: "standard" | "collectible";
|
|
776
|
+
/** @enum {string} */
|
|
777
|
+
state: "upcoming" | "open" | "closed" | "archived";
|
|
778
|
+
starts_at: string | null;
|
|
779
|
+
ends_at: string | null;
|
|
780
|
+
/** @description False means a browser token cannot fire this — secret key only. */
|
|
781
|
+
client_fireable: boolean;
|
|
782
|
+
subject_required: boolean;
|
|
783
|
+
once_per_subject: boolean;
|
|
784
|
+
/** @description What POST /v1/events validates your payload against. */
|
|
785
|
+
payload_schema: unknown;
|
|
786
|
+
cap_max: number | null;
|
|
787
|
+
/** @enum {string|null} */
|
|
788
|
+
cap_window: "day" | "week" | "month" | "season" | "ever" | null;
|
|
789
|
+
}[];
|
|
790
|
+
};
|
|
791
|
+
};
|
|
792
|
+
};
|
|
793
|
+
/** @description Rate limited. */
|
|
794
|
+
429: {
|
|
795
|
+
headers: {
|
|
796
|
+
[name: string]: unknown;
|
|
797
|
+
};
|
|
798
|
+
content: {
|
|
799
|
+
"application/json": {
|
|
800
|
+
error: string;
|
|
801
|
+
message: string;
|
|
802
|
+
};
|
|
803
|
+
};
|
|
804
|
+
};
|
|
805
|
+
};
|
|
806
|
+
};
|
|
807
|
+
inviteMember: {
|
|
808
|
+
parameters: {
|
|
809
|
+
query?: never;
|
|
810
|
+
header?: never;
|
|
811
|
+
path?: never;
|
|
812
|
+
cookie?: never;
|
|
813
|
+
};
|
|
814
|
+
requestBody: {
|
|
815
|
+
content: {
|
|
816
|
+
"application/json": {
|
|
817
|
+
/**
|
|
818
|
+
* Format: uuid
|
|
819
|
+
* @description The org to add the person to. Resolves the caller's own standing against it.
|
|
820
|
+
*/
|
|
821
|
+
org_id: string;
|
|
822
|
+
/** @description The address to invite or grant a membership to. */
|
|
823
|
+
email: string;
|
|
824
|
+
/** @description One of owner, admin, editor or viewer. */
|
|
825
|
+
role: string;
|
|
826
|
+
/** @description Shown on the invited account once it exists. Omitted or blank means no name is stored. */
|
|
827
|
+
full_name?: string;
|
|
828
|
+
};
|
|
829
|
+
};
|
|
830
|
+
};
|
|
831
|
+
responses: {
|
|
832
|
+
/** @description Default Response */
|
|
833
|
+
200: {
|
|
834
|
+
headers: {
|
|
835
|
+
[name: string]: unknown;
|
|
836
|
+
};
|
|
837
|
+
content: {
|
|
838
|
+
"application/json": {
|
|
839
|
+
/** @enum {boolean} */
|
|
840
|
+
invited: true;
|
|
841
|
+
/** @description The invited or matched account id. */
|
|
842
|
+
user_id: string;
|
|
843
|
+
};
|
|
844
|
+
};
|
|
845
|
+
};
|
|
846
|
+
/** @description Default Response */
|
|
847
|
+
400: {
|
|
848
|
+
headers: {
|
|
849
|
+
[name: string]: unknown;
|
|
850
|
+
};
|
|
851
|
+
content: {
|
|
852
|
+
"application/json": {
|
|
853
|
+
error: string;
|
|
854
|
+
message: string;
|
|
855
|
+
};
|
|
856
|
+
};
|
|
857
|
+
};
|
|
858
|
+
/** @description Default Response */
|
|
859
|
+
403: {
|
|
860
|
+
headers: {
|
|
861
|
+
[name: string]: unknown;
|
|
862
|
+
};
|
|
863
|
+
content: {
|
|
864
|
+
"application/json": {
|
|
865
|
+
error: string;
|
|
866
|
+
message: string;
|
|
867
|
+
};
|
|
868
|
+
};
|
|
869
|
+
};
|
|
870
|
+
/** @description Default Response */
|
|
871
|
+
409: {
|
|
872
|
+
headers: {
|
|
873
|
+
[name: string]: unknown;
|
|
874
|
+
};
|
|
875
|
+
content: {
|
|
876
|
+
"application/json": {
|
|
877
|
+
error: string;
|
|
878
|
+
message: string;
|
|
879
|
+
};
|
|
880
|
+
};
|
|
881
|
+
};
|
|
882
|
+
/** @description Rate limited. */
|
|
883
|
+
429: {
|
|
884
|
+
headers: {
|
|
885
|
+
[name: string]: unknown;
|
|
886
|
+
};
|
|
887
|
+
content: {
|
|
888
|
+
"application/json": {
|
|
889
|
+
error: string;
|
|
890
|
+
message: string;
|
|
891
|
+
};
|
|
892
|
+
};
|
|
893
|
+
};
|
|
894
|
+
/** @description Default Response */
|
|
895
|
+
502: {
|
|
896
|
+
headers: {
|
|
897
|
+
[name: string]: unknown;
|
|
898
|
+
};
|
|
899
|
+
content: {
|
|
900
|
+
"application/json": {
|
|
901
|
+
error: string;
|
|
902
|
+
message: string;
|
|
903
|
+
};
|
|
904
|
+
};
|
|
905
|
+
};
|
|
906
|
+
};
|
|
907
|
+
};
|
|
908
|
+
patchMember: {
|
|
909
|
+
parameters: {
|
|
910
|
+
query?: never;
|
|
911
|
+
header?: never;
|
|
912
|
+
path: {
|
|
913
|
+
/** @description The account whose name is being changed. */
|
|
914
|
+
userId: string;
|
|
915
|
+
};
|
|
916
|
+
cookie?: never;
|
|
917
|
+
};
|
|
918
|
+
requestBody: {
|
|
919
|
+
content: {
|
|
920
|
+
"application/json": {
|
|
921
|
+
/**
|
|
922
|
+
* Format: uuid
|
|
923
|
+
* @description The org to resolve both the caller's and the target's standing in.
|
|
924
|
+
*/
|
|
925
|
+
org_id: string;
|
|
926
|
+
/** @description The new name, or null to clear it. A blank string also clears it. */
|
|
927
|
+
full_name: string | null;
|
|
928
|
+
};
|
|
929
|
+
};
|
|
930
|
+
};
|
|
931
|
+
responses: {
|
|
932
|
+
/** @description Default Response */
|
|
933
|
+
200: {
|
|
934
|
+
headers: {
|
|
935
|
+
[name: string]: unknown;
|
|
936
|
+
};
|
|
937
|
+
content: {
|
|
938
|
+
"application/json": {
|
|
939
|
+
/** @enum {boolean} */
|
|
940
|
+
updated: true;
|
|
941
|
+
};
|
|
942
|
+
};
|
|
943
|
+
};
|
|
944
|
+
/** @description Default Response */
|
|
945
|
+
400: {
|
|
946
|
+
headers: {
|
|
947
|
+
[name: string]: unknown;
|
|
948
|
+
};
|
|
949
|
+
content: {
|
|
950
|
+
"application/json": {
|
|
951
|
+
error: string;
|
|
952
|
+
message: string;
|
|
953
|
+
};
|
|
954
|
+
};
|
|
955
|
+
};
|
|
956
|
+
/** @description Default Response */
|
|
957
|
+
403: {
|
|
958
|
+
headers: {
|
|
959
|
+
[name: string]: unknown;
|
|
960
|
+
};
|
|
961
|
+
content: {
|
|
962
|
+
"application/json": {
|
|
963
|
+
error: string;
|
|
964
|
+
message: string;
|
|
965
|
+
};
|
|
966
|
+
};
|
|
967
|
+
};
|
|
968
|
+
/** @description Default Response */
|
|
969
|
+
404: {
|
|
970
|
+
headers: {
|
|
971
|
+
[name: string]: unknown;
|
|
972
|
+
};
|
|
973
|
+
content: {
|
|
974
|
+
"application/json": {
|
|
975
|
+
error: string;
|
|
976
|
+
message: string;
|
|
977
|
+
};
|
|
978
|
+
};
|
|
979
|
+
};
|
|
980
|
+
/** @description Rate limited. */
|
|
981
|
+
429: {
|
|
982
|
+
headers: {
|
|
983
|
+
[name: string]: unknown;
|
|
984
|
+
};
|
|
985
|
+
content: {
|
|
986
|
+
"application/json": {
|
|
987
|
+
error: string;
|
|
988
|
+
message: string;
|
|
989
|
+
};
|
|
990
|
+
};
|
|
991
|
+
};
|
|
992
|
+
/** @description Default Response */
|
|
993
|
+
502: {
|
|
994
|
+
headers: {
|
|
995
|
+
[name: string]: unknown;
|
|
996
|
+
};
|
|
997
|
+
content: {
|
|
998
|
+
"application/json": {
|
|
999
|
+
error: string;
|
|
1000
|
+
message: string;
|
|
1001
|
+
};
|
|
1002
|
+
};
|
|
1003
|
+
};
|
|
1004
|
+
};
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* A refusal that arrived as HTTP 200.
|
|
1010
|
+
*
|
|
1011
|
+
* `{ accepted: false, reason: "cap-exhausted: …" }` is a 200 because a daily
|
|
1012
|
+
* cap, a closed season or a half-built ladder is ordinary program state rather
|
|
1013
|
+
* than a fault in the request. A generated client reports SUCCESS for this
|
|
1014
|
+
* unless the caller reads `accepted` — which is the single most common way to
|
|
1015
|
+
* misuse this API, and the reason this package makes it a typed value rather
|
|
1016
|
+
* than a documented footnote.
|
|
1017
|
+
*
|
|
1018
|
+
* The reason is split so a caller can branch on a stable code without string
|
|
1019
|
+
* matching at every call site. `apps/api` writes it as `code: detail`; an
|
|
1020
|
+
* unprefixed reason is all code and no detail.
|
|
1021
|
+
*/
|
|
1022
|
+
type Refused = {
|
|
1023
|
+
accepted: false;
|
|
1024
|
+
reason: string;
|
|
1025
|
+
code: string;
|
|
1026
|
+
detail: string;
|
|
1027
|
+
};
|
|
1028
|
+
declare function splitRefusal(reason: string): {
|
|
1029
|
+
code: string;
|
|
1030
|
+
detail: string;
|
|
1031
|
+
};
|
|
1032
|
+
/** Narrow a 200 body that may be either an answer or a refusal. */
|
|
1033
|
+
declare function isRefused(body: unknown): body is {
|
|
1034
|
+
accepted: false;
|
|
1035
|
+
reason: string;
|
|
1036
|
+
};
|
|
1037
|
+
|
|
1038
|
+
type MintedToken = {
|
|
1039
|
+
token: string;
|
|
1040
|
+
expiresAt: string;
|
|
1041
|
+
};
|
|
1042
|
+
type GetToken = () => Promise<MintedToken>;
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* One request, one place.
|
|
1046
|
+
*
|
|
1047
|
+
* The rules here are lifted from the admin app's API client, which
|
|
1048
|
+
* solved the same problem for the admin's session credential: read the body
|
|
1049
|
+
* BEFORE branching on `ok`, because this API writes its message on the failure
|
|
1050
|
+
* paths; throw the server's sentence rather than a status, because the sentence
|
|
1051
|
+
* is written for a person to act on; and replace `TypeError: fetch failed` with
|
|
1052
|
+
* something that names the service, because a JavaScript builtin's name is not
|
|
1053
|
+
* a next step.
|
|
1054
|
+
*
|
|
1055
|
+
* What is NOT here, deliberately: retries. The token-aware layer in
|
|
1056
|
+
* `client.ts` retries exactly once on a 401 after refreshing, and that is the
|
|
1057
|
+
* only retry this package performs.
|
|
1058
|
+
*/
|
|
1059
|
+
type Fetch = typeof globalThis.fetch;
|
|
1060
|
+
declare class ApiError extends Error {
|
|
1061
|
+
readonly status: number;
|
|
1062
|
+
readonly code: string;
|
|
1063
|
+
constructor(message: string, status: number, code: string, options?: ErrorOptions);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
/** The 200 JSON body of one operation. */
|
|
1067
|
+
type Json200<Op> = Op extends {
|
|
1068
|
+
responses: {
|
|
1069
|
+
200: {
|
|
1070
|
+
content: {
|
|
1071
|
+
'application/json': infer T;
|
|
1072
|
+
};
|
|
1073
|
+
};
|
|
1074
|
+
};
|
|
1075
|
+
} ? T : never;
|
|
1076
|
+
type Standing = Exclude<Json200<operations['getMyStanding']>, {
|
|
1077
|
+
accepted: false;
|
|
1078
|
+
}>;
|
|
1079
|
+
type AwardAccepted = Extract<Json200<operations['awardEvent']>, {
|
|
1080
|
+
accepted: true;
|
|
1081
|
+
}>;
|
|
1082
|
+
type CatalogCollectible = Json200<operations['listCollectibles']>['collectibles'][number];
|
|
1083
|
+
type Collection = Json200<operations['listCollections']>['collections'][number];
|
|
1084
|
+
type CatalogAction = Json200<operations['listActions']>['actions'][number];
|
|
1085
|
+
type AwardInput = {
|
|
1086
|
+
action: string;
|
|
1087
|
+
payload?: unknown;
|
|
1088
|
+
subject?: string;
|
|
1089
|
+
/** Sent as the `Idempotency-Key` HEADER. The API has never read a body field of that name. */
|
|
1090
|
+
idempotencyKey?: string;
|
|
1091
|
+
};
|
|
1092
|
+
type ClientOptions = {
|
|
1093
|
+
apiBase: string;
|
|
1094
|
+
/**
|
|
1095
|
+
* Where a token comes from. The client NEVER sees a secret key — this is the
|
|
1096
|
+
* property that makes the package publishable, and a caller wiring a secret
|
|
1097
|
+
* key in here has defeated the whole design.
|
|
1098
|
+
*/
|
|
1099
|
+
getToken: GetToken;
|
|
1100
|
+
fetch?: Fetch;
|
|
1101
|
+
now?: () => Date;
|
|
1102
|
+
};
|
|
1103
|
+
type SlyClient = {
|
|
1104
|
+
me(): Promise<Standing | Refused>;
|
|
1105
|
+
award(input: AwardInput): Promise<AwardAccepted | Refused>;
|
|
1106
|
+
collectibles(): Promise<CatalogCollectible[]>;
|
|
1107
|
+
collections(): Promise<Collection[]>;
|
|
1108
|
+
actions(): Promise<CatalogAction[]>;
|
|
1109
|
+
};
|
|
1110
|
+
declare function createClient(opts: ClientOptions): SlyClient;
|
|
1111
|
+
|
|
1112
|
+
/**
|
|
1113
|
+
* The two hosted API origins, so nobody hand-types a hostname.
|
|
1114
|
+
*
|
|
1115
|
+
* Deliberately NOT an `environment: 'stage'` option on `createClient`: a
|
|
1116
|
+
* customer behind their own proxy needs the raw base anyway, and an option
|
|
1117
|
+
* would hide which host is being called. And the environment is decided by
|
|
1118
|
+
* the KEY, not by this: a token minted with a production key and sent to stage
|
|
1119
|
+
* answers 401, retried once, then thrown as `ApiError(401)`.
|
|
1120
|
+
*/
|
|
1121
|
+
declare const API_BASES: {
|
|
1122
|
+
readonly production: "https://api.slytifo.com";
|
|
1123
|
+
readonly stage: "https://api.stage-slytifo.com";
|
|
1124
|
+
};
|
|
1125
|
+
|
|
1126
|
+
export { API_BASES, ApiError, type AwardAccepted, type AwardInput, type CatalogAction, type CatalogCollectible, type ClientOptions, type Collection, type GetToken, type MintedToken, type Refused, type SlyClient, type Standing, createClient, isRefused, splitRefusal };
|