@quizbase/client 0.4.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -2
- package/dist/index.cjs +14 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +149 -5
- package/dist/index.d.ts +149 -5
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @quizbase/client
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for the [QuizBase API](https://quizbase.runriva.com) — multilingual trivia API
|
|
3
|
+
TypeScript SDK for the [QuizBase API](https://quizbase.runriva.com) — a multilingual trivia API. 1.4M+ blended, deduped questions from 11 open datasets (CC, MIT), English and Polish at launch. Per-record license, author, and source on every response.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@quizbase/client)
|
|
6
6
|
[](https://www.npmjs.com/package/@quizbase/client)
|
|
@@ -48,6 +48,7 @@ client.topics.list({ lang });
|
|
|
48
48
|
client.topics.get(slug, { lang });
|
|
49
49
|
client.tags.list({ lang });
|
|
50
50
|
client.subcategories.list({ lang });
|
|
51
|
+
client.regions.list({ lang, kind, q });
|
|
51
52
|
|
|
52
53
|
client.stats.get();
|
|
53
54
|
client.me.get();
|
|
@@ -62,7 +63,7 @@ Full parameter docs: [docs.quizbase.runriva.com/docs](https://quizbase.runriva.c
|
|
|
62
63
|
|
|
63
64
|
## Pagination
|
|
64
65
|
|
|
65
|
-
`questions`, `topics`, `tags`, and `
|
|
66
|
+
`questions`, `topics`, `tags`, `subcategories`, and `regions` are cursor-paginated. Use `listAll()` to iterate every item, or `pages()` to iterate page-by-page — both auto-follow `_links.next` and preserve filters across pages.
|
|
66
67
|
|
|
67
68
|
```ts
|
|
68
69
|
// Iterate every matching question. Filters and limit are reused on each page.
|
|
@@ -130,6 +131,43 @@ Save `{id, lang}` as your card key. When the upstream question changes (typo fix
|
|
|
130
131
|
|
|
131
132
|
Full code samples and edge cases at [/docs/api/questions-by-id § What you can do with a stable id](https://quizbase.runriva.com/docs/api/questions-by-id). For MCP agents, the same playbook is exposed as the `client_mechanics_patterns` prompt.
|
|
132
133
|
|
|
134
|
+
## Regions: cultural affinity, not geography
|
|
135
|
+
|
|
136
|
+
The `regions` field marks **cultural affinity** — residents of a country or members of a cultural/religious group are *statistically more likely to know* the answer. It is **not** a tag for "this question is about country X". The Mona Lisa is universally accessible (`regions: []`), but a question requiring NFL knowledge has `regions: ["us"]`.
|
|
137
|
+
|
|
138
|
+
Values:
|
|
139
|
+
|
|
140
|
+
- **Country codes** — ISO 3166-1 alpha-2 lowercase (`us`, `pl`, `gb`, `de`, `jp`, …)
|
|
141
|
+
- **Cultural codes** — `jewish`, `christian-catholic`, `islam`
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
// US-relevant questions (NFL, US presidents, Super Bowl)
|
|
145
|
+
const { data } = await client.questions.random({ regions: ['us'], amount: 5 });
|
|
146
|
+
|
|
147
|
+
// Catholic doctrine
|
|
148
|
+
const { data } = await client.questions.random({ regions: ['christian-catholic'] });
|
|
149
|
+
|
|
150
|
+
// AND-logic: both Polish AND Catholic
|
|
151
|
+
const { data } = await client.questions.random({
|
|
152
|
+
regions: ['pl', 'christian-catholic']
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Discover the full catalog (~150 codes per language)
|
|
156
|
+
const { data: regions } = await client.regions.list({ lang: 'en', kind: 'cultural' });
|
|
157
|
+
// [
|
|
158
|
+
// { code: 'jewish', kind: 'cultural', label: 'Jewish (cultural/religious)', count: 2698 },
|
|
159
|
+
// { code: 'christian-catholic', kind: 'cultural', label: 'Catholic Christian (cultural/religious)', count: 2859 },
|
|
160
|
+
// { code: 'islam', kind: 'cultural', label: 'Islamic (cultural/religious)', count: 784 }
|
|
161
|
+
// ]
|
|
162
|
+
|
|
163
|
+
// Native labels for `lang=pl`: pl → "Polska", jp → "日本"
|
|
164
|
+
for await (const region of client.regions.listAll({ lang: 'pl' })) {
|
|
165
|
+
console.log(region.code, region.label, region.count);
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Input is **case-insensitive** (`'PL'` and `'pl'` both work — normalized server-side). Output is always lowercase. See [the regions docs](https://quizbase.runriva.com/docs/api/regions) for the full catalog with counts.
|
|
170
|
+
|
|
133
171
|
## Errors
|
|
134
172
|
|
|
135
173
|
Every non-2xx response throws `QuizbaseError` carrying the parsed [RFC 9457 Problem Details](https://www.rfc-editor.org/rfc/rfc9457):
|
package/dist/index.cjs
CHANGED
|
@@ -45,6 +45,7 @@ var DEFAULT_TIMEOUTS = {
|
|
|
45
45
|
"topics.get": 1e4,
|
|
46
46
|
"tags.list": 15e3,
|
|
47
47
|
"subcategories.list": 15e3,
|
|
48
|
+
"regions.list": 1e4,
|
|
48
49
|
"stats.get": 1e4,
|
|
49
50
|
"me.get": 1e4,
|
|
50
51
|
"usage.get": 1e4,
|
|
@@ -284,6 +285,19 @@ function createClient(options) {
|
|
|
284
285
|
listAll: (params) => paginateItems(list, params)
|
|
285
286
|
};
|
|
286
287
|
})(),
|
|
288
|
+
regions: /* @__PURE__ */ (() => {
|
|
289
|
+
const list = (params) => request({
|
|
290
|
+
endpoint: "regions.list",
|
|
291
|
+
method: "GET",
|
|
292
|
+
path: "/api/v1/regions",
|
|
293
|
+
query: params
|
|
294
|
+
});
|
|
295
|
+
return {
|
|
296
|
+
list,
|
|
297
|
+
pages: (params) => paginate(list, params),
|
|
298
|
+
listAll: (params) => paginateItems(list, params)
|
|
299
|
+
};
|
|
300
|
+
})(),
|
|
287
301
|
stats: {
|
|
288
302
|
get: () => request({
|
|
289
303
|
endpoint: "stats.get",
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { components } from './types.gen.js';\n\nexport type ProblemDetails = components['schemas']['ProblemDetails'];\n\nexport interface QuizbaseErrorOptions {\n\tstatus: number;\n\tproblem: ProblemDetails;\n\trequestId: string | null;\n\tretryAfter: number | null;\n\turl: string;\n\tmethod: string;\n}\n\n/**\n * Thrown for any non-2xx response from the QuizBase API.\n * Carries the parsed RFC 9457 Problem Details body, X-Request-Id for support,\n * and a parsed retry-after (seconds) for 429s.\n */\nexport class QuizbaseError extends Error {\n\treadonly status: number;\n\treadonly problem: ProblemDetails;\n\treadonly requestId: string | null;\n\treadonly retryAfter: number | null;\n\treadonly url: string;\n\treadonly method: string;\n\n\tconstructor(opts: QuizbaseErrorOptions) {\n\t\tconst title = opts.problem.title ?? `HTTP ${opts.status}`;\n\t\tconst detail = opts.problem.detail ? ` — ${opts.problem.detail}` : '';\n\t\tsuper(`QuizBase ${opts.method} ${opts.url} → ${opts.status} ${title}${detail}`);\n\t\tthis.name = 'QuizbaseError';\n\t\tthis.status = opts.status;\n\t\tthis.problem = opts.problem;\n\t\tthis.requestId = opts.requestId;\n\t\tthis.retryAfter = opts.retryAfter;\n\t\tthis.url = opts.url;\n\t\tthis.method = opts.method;\n\t}\n\n\tget type(): string | undefined {\n\t\treturn this.problem.type;\n\t}\n\n\tget isRateLimited(): boolean {\n\t\treturn this.status === 429;\n\t}\n\n\tget isAuthError(): boolean {\n\t\treturn this.status === 401 || this.status === 403;\n\t}\n\n\tget isServerError(): boolean {\n\t\treturn this.status >= 500;\n\t}\n}\n","import { QuizbaseError, type ProblemDetails } from './errors.js';\nimport type { OnRequestHook } from './telemetry.js';\nimport type { components, paths } from './types.gen.js';\n\ntype Schemas = components['schemas'];\n\n/** Logical endpoint identifiers — stable across path-param values, used for telemetry + per-endpoint timeouts. */\nexport type EndpointKey =\n\t| 'questions.list'\n\t| 'questions.random'\n\t| 'questions.get'\n\t| 'categories.list'\n\t| 'languages.list'\n\t| 'topics.list'\n\t| 'topics.get'\n\t| 'tags.list'\n\t| 'subcategories.list'\n\t| 'stats.get'\n\t| 'me.get'\n\t| 'usage.get'\n\t| 'report.create';\n\nconst DEFAULT_TIMEOUTS: Record<EndpointKey, number> = {\n\t'questions.list': 15_000,\n\t'questions.random': 10_000,\n\t'questions.get': 10_000,\n\t'categories.list': 10_000,\n\t'languages.list': 10_000,\n\t'topics.list': 15_000,\n\t'topics.get': 10_000,\n\t'tags.list': 15_000,\n\t'subcategories.list': 15_000,\n\t'stats.get': 10_000,\n\t'me.get': 10_000,\n\t'usage.get': 10_000,\n\t'report.create': 15_000\n};\n\nexport interface ClientOptions {\n\t/** API key — `qb_pk_*` (publishable, CORS-safe for browsers) or `qb_sk_*` (secret, backend-only). Get one at https://quizbase.runriva.com/dashboard/keys. */\n\tapiKey: string;\n\t/** Override base URL. Defaults to `https://quizbase.runriva.com`. */\n\tbaseUrl?: string;\n\t/** Default request timeout in ms. Defaults to 30_000. Per-endpoint overrides take precedence. */\n\ttimeout?: number;\n\t/** Per-endpoint timeout overrides keyed by `EndpointKey`. */\n\ttimeouts?: Partial<Record<EndpointKey, number>>;\n\t/** Number of retries for 429 / 5xx / network errors. Defaults to 2 (3 total attempts). */\n\tretries?: number;\n\t/** Optional `fetch` implementation. Defaults to global `fetch`. */\n\tfetch?: typeof fetch;\n\t/** Telemetry hook fired after every HTTP attempt (including retries). */\n\tonRequest?: OnRequestHook;\n\t/** User-Agent suffix appended to the SDK identifier. */\n\tuserAgent?: string;\n}\n\ntype QuestionsListParams = paths['/api/v1/questions']['get']['parameters']['query'];\ntype TopicsListParams = paths['/api/v1/topics']['get']['parameters']['query'];\ntype TagsListParams = paths['/api/v1/tags']['get']['parameters']['query'];\ntype SubcategoriesListParams = paths['/api/v1/subcategories']['get']['parameters']['query'];\n\nexport interface QuizbaseClient {\n\tquestions: {\n\t\tlist(params?: QuestionsListParams): Promise<Schemas['QuestionsListResponse']>;\n\t\trandom(\n\t\t\tparams?: paths['/api/v1/questions/random']['get']['parameters']['query']\n\t\t): Promise<Schemas['QuestionsRandomResponse']>;\n\t\tget(\n\t\t\tid: string,\n\t\t\tparams?: paths['/api/v1/questions/{id}']['get']['parameters']['query']\n\t\t): Promise<Schemas['QuestionByIdResponse']>;\n\t\t/** Iterate every page of `/questions`, auto-following `_links.next`. */\n\t\tpages(params?: QuestionsListParams): AsyncIterableIterator<Schemas['QuestionsListResponse']>;\n\t\t/** Iterate every question across all pages. */\n\t\tlistAll(params?: QuestionsListParams): AsyncIterableIterator<Schemas['Question']>;\n\t};\n\tcategories: {\n\t\tlist(\n\t\t\tparams?: paths['/api/v1/categories']['get']['parameters']['query']\n\t\t): Promise<Schemas['CategoriesResponse']>;\n\t};\n\tlanguages: {\n\t\tlist(): Promise<Schemas['LanguagesResponse']>;\n\t};\n\ttopics: {\n\t\tlist(params?: TopicsListParams): Promise<Schemas['TopicsListResponse']>;\n\t\tget(\n\t\t\tslug: string,\n\t\t\tparams?: paths['/api/v1/topics/{slug}']['get']['parameters']['query']\n\t\t): Promise<Schemas['TopicDetailResponse']>;\n\t\t/** Iterate every page of `/topics`, auto-following `_links.next`. */\n\t\tpages(params?: TopicsListParams): AsyncIterableIterator<Schemas['TopicsListResponse']>;\n\t\t/** Iterate every topic across all pages. */\n\t\tlistAll(params?: TopicsListParams): AsyncIterableIterator<Schemas['TopicEntry']>;\n\t};\n\ttags: {\n\t\tlist(params?: TagsListParams): Promise<Schemas['TagsListResponse']>;\n\t\t/** Iterate every page of `/tags`, auto-following `_links.next`. */\n\t\tpages(params?: TagsListParams): AsyncIterableIterator<Schemas['TagsListResponse']>;\n\t\t/** Iterate every tag across all pages. */\n\t\tlistAll(params?: TagsListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;\n\t};\n\tsubcategories: {\n\t\tlist(params?: SubcategoriesListParams): Promise<Schemas['SubcategoriesListResponse']>;\n\t\t/** Iterate every page of `/subcategories`, auto-following `_links.next`. */\n\t\tpages(\n\t\t\tparams?: SubcategoriesListParams\n\t\t): AsyncIterableIterator<Schemas['SubcategoriesListResponse']>;\n\t\t/** Iterate every subcategory across all pages. */\n\t\tlistAll(params?: SubcategoriesListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;\n\t};\n\tstats: {\n\t\tget(): Promise<Schemas['StatsResponse']>;\n\t};\n\tme: {\n\t\tget(): Promise<Schemas['MeResponse']>;\n\t};\n\tusage: {\n\t\tget(\n\t\t\tparams?: paths['/api/v1/usage']['get']['parameters']['query']\n\t\t): Promise<Schemas['UsageResponse']>;\n\t};\n\treport: {\n\t\tcreate(\n\t\t\tbody: NonNullable<\n\t\t\t\tpaths['/api/v1/report']['post']['requestBody']\n\t\t\t>['content']['application/json']\n\t\t): Promise<Schemas['ReportAcceptedResponse']>;\n\t};\n}\n\nconst DEFAULT_BASE_URL = 'https://quizbase.runriva.com';\nconst SDK_VERSION = '0.1.0';\n\ninterface RequestParams {\n\tendpoint: EndpointKey;\n\tmethod: 'GET' | 'POST';\n\tpath: string;\n\tquery?: Record<string, unknown> | undefined;\n\tbody?: unknown;\n}\n\nexport function createClient(options: ClientOptions): QuizbaseClient {\n\tif (!options.apiKey) {\n\t\tthrow new Error('createClient: `apiKey` is required.');\n\t}\n\tconst baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n\tconst defaultTimeout = options.timeout ?? 30_000;\n\tconst timeouts: Record<EndpointKey, number> = { ...DEFAULT_TIMEOUTS };\n\tif (options.timeouts) {\n\t\tfor (const [key, value] of Object.entries(options.timeouts) as [\n\t\t\tEndpointKey,\n\t\t\tnumber | undefined\n\t\t][]) {\n\t\t\tif (typeof value === 'number') timeouts[key] = value;\n\t\t}\n\t}\n\tconst retries = Math.max(0, options.retries ?? 2);\n\tconst doFetch = options.fetch ?? globalThis.fetch;\n\tif (typeof doFetch !== 'function') {\n\t\tthrow new Error(\n\t\t\t'createClient: global `fetch` is unavailable. Pass `fetch` option (Node ≥20 or polyfill).'\n\t\t);\n\t}\n\tconst userAgent = `quizbase-client/${SDK_VERSION}${options.userAgent ? ` ${options.userAgent}` : ''}`;\n\n\tasync function request<T>(params: RequestParams): Promise<T> {\n\t\tconst timeoutMs = timeouts[params.endpoint] ?? defaultTimeout;\n\t\tconst url = buildUrl(baseUrl, params.path, params.query);\n\t\tconst requestId = generateRequestId();\n\t\tconst headers: Record<string, string> = {\n\t\t\tAccept: 'application/json',\n\t\t\tAuthorization: `Bearer ${options.apiKey}`,\n\t\t\t'X-Request-Id': requestId,\n\t\t\t'User-Agent': userAgent\n\t\t};\n\t\tlet body: string | undefined;\n\t\tif (params.body !== undefined) {\n\t\t\theaders['Content-Type'] = 'application/json';\n\t\t\tbody = JSON.stringify(params.body);\n\t\t}\n\n\t\tlet attempt = 0;\n\t\tlet lastError: unknown;\n\t\twhile (attempt <= retries) {\n\t\t\tconst startedAt = Date.now();\n\t\t\tconst controller = new AbortController();\n\t\t\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\t\t\tlet status = 0;\n\t\t\tlet response: Response | undefined;\n\t\t\tlet attemptError: Error | undefined;\n\t\t\ttry {\n\t\t\t\tresponse = await doFetch(url, {\n\t\t\t\t\tmethod: params.method,\n\t\t\t\t\theaders,\n\t\t\t\t\tbody,\n\t\t\t\t\tsignal: controller.signal\n\t\t\t\t});\n\t\t\t\tstatus = response.status;\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = (await response.json()) as T;\n\t\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\t\tmethod: params.method,\n\t\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trequestId: response.headers.get('x-request-id') ?? requestId,\n\t\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\t\tfinal: true\n\t\t\t\t\t});\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\t\t\t\tconst problem = await safeProblem(response);\n\t\t\t\tconst retryAfterHeader = response.headers.get('retry-after');\n\t\t\t\tconst retryAfter = retryAfterHeader ? parseRetryAfter(retryAfterHeader) : null;\n\t\t\t\tconst err = new QuizbaseError({\n\t\t\t\t\tstatus,\n\t\t\t\t\tproblem,\n\t\t\t\t\trequestId: response.headers.get('x-request-id') ?? requestId,\n\t\t\t\t\tretryAfter,\n\t\t\t\t\turl,\n\t\t\t\t\tmethod: params.method\n\t\t\t\t});\n\t\t\t\tattemptError = err;\n\t\t\t\tif (shouldRetry(status) && attempt < retries) {\n\t\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\t\tmethod: params.method,\n\t\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trequestId: err.requestId,\n\t\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\t\tfinal: false,\n\t\t\t\t\t\terror: err\n\t\t\t\t\t});\n\t\t\t\t\tawait sleep(backoffMs(attempt, retryAfter));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tlastError = err;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\tmethod: params.method,\n\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\turl,\n\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\tstatus,\n\t\t\t\t\trequestId: err.requestId,\n\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\tfinal: true,\n\t\t\t\t\terror: err\n\t\t\t\t});\n\t\t\t\tthrow err;\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof QuizbaseError) throw err;\n\t\t\t\tconst networkErr = err instanceof Error ? err : new Error(String(err));\n\t\t\t\tattemptError = networkErr;\n\t\t\t\tif (attempt < retries) {\n\t\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\t\tmethod: params.method,\n\t\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trequestId: null,\n\t\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\t\tfinal: false,\n\t\t\t\t\t\terror: networkErr\n\t\t\t\t\t});\n\t\t\t\t\tawait sleep(backoffMs(attempt, null));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tlastError = networkErr;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\tmethod: params.method,\n\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\turl,\n\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\tstatus,\n\t\t\t\t\trequestId: null,\n\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\tfinal: true,\n\t\t\t\t\terror: networkErr\n\t\t\t\t});\n\t\t\t\tthrow networkErr;\n\t\t\t} finally {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tvoid attemptError;\n\t\t\t\tvoid response;\n\t\t\t}\n\t\t}\n\t\tthrow lastError ?? new Error('quizbase-client: retry loop exhausted unexpectedly');\n\t}\n\n\treturn {\n\t\tquestions: (() => {\n\t\t\tconst list = (params?: QuestionsListParams) =>\n\t\t\t\trequest<Schemas['QuestionsListResponse']>({\n\t\t\t\t\tendpoint: 'questions.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/questions',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\trandom: (params) =>\n\t\t\t\t\trequest({\n\t\t\t\t\t\tendpoint: 'questions.random',\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\tpath: '/api/v1/questions/random',\n\t\t\t\t\t\tquery: params\n\t\t\t\t\t}),\n\t\t\t\tget: (id, params) =>\n\t\t\t\t\trequest({\n\t\t\t\t\t\tendpoint: 'questions.get',\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\tpath: `/api/v1/questions/${encodeURIComponent(id)}`,\n\t\t\t\t\t\tquery: params\n\t\t\t\t\t}),\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['questions'];\n\t\t})(),\n\t\tcategories: {\n\t\t\tlist: (params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'categories.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/categories',\n\t\t\t\t\tquery: params\n\t\t\t\t})\n\t\t},\n\t\tlanguages: {\n\t\t\tlist: () =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'languages.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/languages'\n\t\t\t\t})\n\t\t},\n\t\ttopics: (() => {\n\t\t\tconst list = (params?: TopicsListParams) =>\n\t\t\t\trequest<Schemas['TopicsListResponse']>({\n\t\t\t\t\tendpoint: 'topics.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/topics',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tget: (slug, params) =>\n\t\t\t\t\trequest({\n\t\t\t\t\t\tendpoint: 'topics.get',\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\tpath: `/api/v1/topics/${encodeURIComponent(slug)}`,\n\t\t\t\t\t\tquery: params\n\t\t\t\t\t}),\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['topics'];\n\t\t})(),\n\t\ttags: (() => {\n\t\t\tconst list = (params?: TagsListParams) =>\n\t\t\t\trequest<Schemas['TagsListResponse']>({\n\t\t\t\t\tendpoint: 'tags.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/tags',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['tags'];\n\t\t})(),\n\t\tsubcategories: (() => {\n\t\t\tconst list = (params?: SubcategoriesListParams) =>\n\t\t\t\trequest<Schemas['SubcategoriesListResponse']>({\n\t\t\t\t\tendpoint: 'subcategories.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/subcategories',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['subcategories'];\n\t\t})(),\n\t\tstats: {\n\t\t\tget: () =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'stats.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/stats'\n\t\t\t\t})\n\t\t},\n\t\tme: {\n\t\t\tget: () =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'me.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/me'\n\t\t\t\t})\n\t\t},\n\t\tusage: {\n\t\t\tget: (params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'usage.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/usage',\n\t\t\t\t\tquery: params\n\t\t\t\t})\n\t\t},\n\t\treport: {\n\t\t\tcreate: (body) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'report.create',\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\tpath: '/api/v1/report',\n\t\t\t\t\tbody\n\t\t\t\t})\n\t\t}\n\t};\n}\n\ninterface PaginatedParams {\n\tcursor?: string;\n\t[key: string]: unknown;\n}\n\ninterface PaginatedPage<T> {\n\tdata: T[];\n\t_links?: { next?: string; prev?: string };\n}\n\n/**\n * Extract the `cursor` query param from an absolute or relative `_links.next` URL.\n * Returns `null` when the URL is absent or malformed — caller treats that as end-of-stream.\n */\nfunction extractCursor(nextUrl: string | undefined): string | null {\n\tif (!nextUrl) return null;\n\ttry {\n\t\tconst url = nextUrl.startsWith('http')\n\t\t\t? new URL(nextUrl)\n\t\t\t: new URL(nextUrl, 'http://placeholder');\n\t\treturn url.searchParams.get('cursor');\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function* paginate<P extends PaginatedParams, R extends PaginatedPage<unknown>>(\n\tfetchPage: (params?: P) => Promise<R>,\n\tinitial: P | undefined\n): AsyncIterableIterator<R> {\n\tlet nextParams: P | undefined = initial;\n\twhile (true) {\n\t\tconst page = await fetchPage(nextParams);\n\t\tyield page;\n\t\tconst cursor = extractCursor(page._links?.next);\n\t\tif (!cursor) return;\n\t\tnextParams = { ...(nextParams ?? ({} as P)), cursor } as P;\n\t}\n}\n\nasync function* paginateItems<P extends PaginatedParams, T>(\n\tfetchPage: (params?: P) => Promise<PaginatedPage<T>>,\n\tinitial: P | undefined\n): AsyncIterableIterator<T> {\n\tfor await (const page of paginate(fetchPage, initial)) {\n\t\tfor (const item of page.data) yield item;\n\t}\n}\n\nfunction buildUrl(baseUrl: string, path: string, query: Record<string, unknown> | undefined): string {\n\tconst url = new URL(baseUrl + path);\n\tif (query) {\n\t\tfor (const [key, value] of Object.entries(query)) {\n\t\t\tif (value === undefined || value === null) continue;\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tfor (const item of value) {\n\t\t\t\t\tif (item !== undefined && item !== null) {\n\t\t\t\t\t\turl.searchParams.append(key, String(item));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\turl.searchParams.append(key, String(value));\n\t\t\t}\n\t\t}\n\t}\n\treturn url.toString();\n}\n\nfunction generateRequestId(): string {\n\tconst cryptoApi: Crypto | undefined = globalThis.crypto;\n\tif (cryptoApi && typeof cryptoApi.randomUUID === 'function') {\n\t\treturn cryptoApi.randomUUID();\n\t}\n\treturn `req-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;\n}\n\nasync function safeProblem(response: Response): Promise<ProblemDetails> {\n\ttry {\n\t\tconst data = (await response.json()) as ProblemDetails;\n\t\tif (data && typeof data === 'object') return data;\n\t} catch {\n\t\t// fall through\n\t}\n\treturn {\n\t\ttype: 'about:blank',\n\t\ttitle: response.statusText || `HTTP ${response.status}`,\n\t\tstatus: response.status,\n\t\tdetail: '',\n\t\tinstance: '',\n\t\tcode: 'unknown'\n\t} satisfies ProblemDetails;\n}\n\nfunction shouldRetry(status: number): boolean {\n\treturn status === 429 || (status >= 500 && status < 600);\n}\n\nfunction parseRetryAfter(value: string): number | null {\n\tconst seconds = Number.parseInt(value, 10);\n\tif (Number.isFinite(seconds) && seconds >= 0) return seconds;\n\tconst date = Date.parse(value);\n\tif (Number.isFinite(date)) {\n\t\tconst diff = Math.max(0, Math.ceil((date - Date.now()) / 1000));\n\t\treturn diff;\n\t}\n\treturn null;\n}\n\nfunction backoffMs(attempt: number, retryAfterSeconds: number | null): number {\n\tif (retryAfterSeconds !== null) return retryAfterSeconds * 1000;\n\tconst base = 250;\n\tconst exp = base * 2 ** attempt;\n\tconst jitter = Math.random() * 100;\n\treturn Math.min(exp + jitter, 5_000);\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function emit(\n\thook: OnRequestHook | undefined,\n\tevent: Parameters<OnRequestHook>[0]\n): Promise<void> {\n\tif (!hook) return;\n\ttry {\n\t\tawait hook(event);\n\t} catch {\n\t\t// telemetry must never break the caller\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { components } from './types.gen.js';\n\nexport type ProblemDetails = components['schemas']['ProblemDetails'];\n\nexport interface QuizbaseErrorOptions {\n\tstatus: number;\n\tproblem: ProblemDetails;\n\trequestId: string | null;\n\tretryAfter: number | null;\n\turl: string;\n\tmethod: string;\n}\n\n/**\n * Thrown for any non-2xx response from the QuizBase API.\n * Carries the parsed RFC 9457 Problem Details body, X-Request-Id for support,\n * and a parsed retry-after (seconds) for 429s.\n */\nexport class QuizbaseError extends Error {\n\treadonly status: number;\n\treadonly problem: ProblemDetails;\n\treadonly requestId: string | null;\n\treadonly retryAfter: number | null;\n\treadonly url: string;\n\treadonly method: string;\n\n\tconstructor(opts: QuizbaseErrorOptions) {\n\t\tconst title = opts.problem.title ?? `HTTP ${opts.status}`;\n\t\tconst detail = opts.problem.detail ? ` — ${opts.problem.detail}` : '';\n\t\tsuper(`QuizBase ${opts.method} ${opts.url} → ${opts.status} ${title}${detail}`);\n\t\tthis.name = 'QuizbaseError';\n\t\tthis.status = opts.status;\n\t\tthis.problem = opts.problem;\n\t\tthis.requestId = opts.requestId;\n\t\tthis.retryAfter = opts.retryAfter;\n\t\tthis.url = opts.url;\n\t\tthis.method = opts.method;\n\t}\n\n\tget type(): string | undefined {\n\t\treturn this.problem.type;\n\t}\n\n\tget isRateLimited(): boolean {\n\t\treturn this.status === 429;\n\t}\n\n\tget isAuthError(): boolean {\n\t\treturn this.status === 401 || this.status === 403;\n\t}\n\n\tget isServerError(): boolean {\n\t\treturn this.status >= 500;\n\t}\n}\n","import { QuizbaseError, type ProblemDetails } from './errors.js';\nimport type { OnRequestHook } from './telemetry.js';\nimport type { components, paths } from './types.gen.js';\n\ntype Schemas = components['schemas'];\n\n/** Logical endpoint identifiers — stable across path-param values, used for telemetry + per-endpoint timeouts. */\nexport type EndpointKey =\n\t| 'questions.list'\n\t| 'questions.random'\n\t| 'questions.get'\n\t| 'categories.list'\n\t| 'languages.list'\n\t| 'topics.list'\n\t| 'topics.get'\n\t| 'tags.list'\n\t| 'subcategories.list'\n\t| 'regions.list'\n\t| 'stats.get'\n\t| 'me.get'\n\t| 'usage.get'\n\t| 'report.create';\n\nconst DEFAULT_TIMEOUTS: Record<EndpointKey, number> = {\n\t'questions.list': 15_000,\n\t'questions.random': 10_000,\n\t'questions.get': 10_000,\n\t'categories.list': 10_000,\n\t'languages.list': 10_000,\n\t'topics.list': 15_000,\n\t'topics.get': 10_000,\n\t'tags.list': 15_000,\n\t'subcategories.list': 15_000,\n\t'regions.list': 10_000,\n\t'stats.get': 10_000,\n\t'me.get': 10_000,\n\t'usage.get': 10_000,\n\t'report.create': 15_000\n};\n\nexport interface ClientOptions {\n\t/** API key — `qb_pk_*` (publishable, CORS-safe for browsers) or `qb_sk_*` (secret, backend-only). Get one at https://quizbase.runriva.com/dashboard/keys. */\n\tapiKey: string;\n\t/** Override base URL. Defaults to `https://quizbase.runriva.com`. */\n\tbaseUrl?: string;\n\t/** Default request timeout in ms. Defaults to 30_000. Per-endpoint overrides take precedence. */\n\ttimeout?: number;\n\t/** Per-endpoint timeout overrides keyed by `EndpointKey`. */\n\ttimeouts?: Partial<Record<EndpointKey, number>>;\n\t/** Number of retries for 429 / 5xx / network errors. Defaults to 2 (3 total attempts). */\n\tretries?: number;\n\t/** Optional `fetch` implementation. Defaults to global `fetch`. */\n\tfetch?: typeof fetch;\n\t/** Telemetry hook fired after every HTTP attempt (including retries). */\n\tonRequest?: OnRequestHook;\n\t/** User-Agent suffix appended to the SDK identifier. */\n\tuserAgent?: string;\n}\n\ntype QuestionsListParams = paths['/api/v1/questions']['get']['parameters']['query'];\ntype TopicsListParams = paths['/api/v1/topics']['get']['parameters']['query'];\ntype TagsListParams = paths['/api/v1/tags']['get']['parameters']['query'];\ntype SubcategoriesListParams = paths['/api/v1/subcategories']['get']['parameters']['query'];\ntype RegionsListParams = paths['/api/v1/regions']['get']['parameters']['query'];\n\nexport interface QuizbaseClient {\n\tquestions: {\n\t\tlist(params?: QuestionsListParams): Promise<Schemas['QuestionsListResponse']>;\n\t\trandom(\n\t\t\tparams?: paths['/api/v1/questions/random']['get']['parameters']['query']\n\t\t): Promise<Schemas['QuestionsRandomResponse']>;\n\t\tget(\n\t\t\tid: string,\n\t\t\tparams?: paths['/api/v1/questions/{id}']['get']['parameters']['query']\n\t\t): Promise<Schemas['QuestionByIdResponse']>;\n\t\t/** Iterate every page of `/questions`, auto-following `_links.next`. */\n\t\tpages(params?: QuestionsListParams): AsyncIterableIterator<Schemas['QuestionsListResponse']>;\n\t\t/** Iterate every question across all pages. */\n\t\tlistAll(params?: QuestionsListParams): AsyncIterableIterator<Schemas['Question']>;\n\t};\n\tcategories: {\n\t\tlist(\n\t\t\tparams?: paths['/api/v1/categories']['get']['parameters']['query']\n\t\t): Promise<Schemas['CategoriesResponse']>;\n\t};\n\tlanguages: {\n\t\tlist(): Promise<Schemas['LanguagesResponse']>;\n\t};\n\ttopics: {\n\t\tlist(params?: TopicsListParams): Promise<Schemas['TopicsListResponse']>;\n\t\tget(\n\t\t\tslug: string,\n\t\t\tparams?: paths['/api/v1/topics/{slug}']['get']['parameters']['query']\n\t\t): Promise<Schemas['TopicDetailResponse']>;\n\t\t/** Iterate every page of `/topics`, auto-following `_links.next`. */\n\t\tpages(params?: TopicsListParams): AsyncIterableIterator<Schemas['TopicsListResponse']>;\n\t\t/** Iterate every topic across all pages. */\n\t\tlistAll(params?: TopicsListParams): AsyncIterableIterator<Schemas['TopicEntry']>;\n\t};\n\ttags: {\n\t\tlist(params?: TagsListParams): Promise<Schemas['TagsListResponse']>;\n\t\t/** Iterate every page of `/tags`, auto-following `_links.next`. */\n\t\tpages(params?: TagsListParams): AsyncIterableIterator<Schemas['TagsListResponse']>;\n\t\t/** Iterate every tag across all pages. */\n\t\tlistAll(params?: TagsListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;\n\t};\n\tsubcategories: {\n\t\tlist(params?: SubcategoriesListParams): Promise<Schemas['SubcategoriesListResponse']>;\n\t\t/** Iterate every page of `/subcategories`, auto-following `_links.next`. */\n\t\tpages(\n\t\t\tparams?: SubcategoriesListParams\n\t\t): AsyncIterableIterator<Schemas['SubcategoriesListResponse']>;\n\t\t/** Iterate every subcategory across all pages. */\n\t\tlistAll(params?: SubcategoriesListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;\n\t};\n\t/**\n\t * Region codes — **cultural affinity**, not geography. A question is tagged with\n\t * a region if residents of that country or members of that cultural/religious\n\t * group are statistically more likely to know the answer. Lowercase ISO 3166-1\n\t * alpha-2 (`us`, `pl`, `gb`) plus cultural codes (`jewish`, `christian-catholic`,\n\t * `islam`). Pair with `questions.random({ regions: [...] })` to fetch matching\n\t * questions.\n\t */\n\tregions: {\n\t\tlist(params?: RegionsListParams): Promise<Schemas['RegionsListResponse']>;\n\t\t/** Iterate every page of `/regions`, auto-following `_links.next`. */\n\t\tpages(params?: RegionsListParams): AsyncIterableIterator<Schemas['RegionsListResponse']>;\n\t\t/** Iterate every region across all pages. */\n\t\tlistAll(params?: RegionsListParams): AsyncIterableIterator<Schemas['RegionEntry']>;\n\t};\n\tstats: {\n\t\tget(): Promise<Schemas['StatsResponse']>;\n\t};\n\tme: {\n\t\tget(): Promise<Schemas['MeResponse']>;\n\t};\n\tusage: {\n\t\tget(\n\t\t\tparams?: paths['/api/v1/usage']['get']['parameters']['query']\n\t\t): Promise<Schemas['UsageResponse']>;\n\t};\n\treport: {\n\t\tcreate(\n\t\t\tbody: NonNullable<\n\t\t\t\tpaths['/api/v1/report']['post']['requestBody']\n\t\t\t>['content']['application/json']\n\t\t): Promise<Schemas['ReportAcceptedResponse']>;\n\t};\n}\n\nconst DEFAULT_BASE_URL = 'https://quizbase.runriva.com';\nconst SDK_VERSION = '0.1.0';\n\ninterface RequestParams {\n\tendpoint: EndpointKey;\n\tmethod: 'GET' | 'POST';\n\tpath: string;\n\tquery?: Record<string, unknown> | undefined;\n\tbody?: unknown;\n}\n\nexport function createClient(options: ClientOptions): QuizbaseClient {\n\tif (!options.apiKey) {\n\t\tthrow new Error('createClient: `apiKey` is required.');\n\t}\n\tconst baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n\tconst defaultTimeout = options.timeout ?? 30_000;\n\tconst timeouts: Record<EndpointKey, number> = { ...DEFAULT_TIMEOUTS };\n\tif (options.timeouts) {\n\t\tfor (const [key, value] of Object.entries(options.timeouts) as [\n\t\t\tEndpointKey,\n\t\t\tnumber | undefined\n\t\t][]) {\n\t\t\tif (typeof value === 'number') timeouts[key] = value;\n\t\t}\n\t}\n\tconst retries = Math.max(0, options.retries ?? 2);\n\tconst doFetch = options.fetch ?? globalThis.fetch;\n\tif (typeof doFetch !== 'function') {\n\t\tthrow new Error(\n\t\t\t'createClient: global `fetch` is unavailable. Pass `fetch` option (Node ≥20 or polyfill).'\n\t\t);\n\t}\n\tconst userAgent = `quizbase-client/${SDK_VERSION}${options.userAgent ? ` ${options.userAgent}` : ''}`;\n\n\tasync function request<T>(params: RequestParams): Promise<T> {\n\t\tconst timeoutMs = timeouts[params.endpoint] ?? defaultTimeout;\n\t\tconst url = buildUrl(baseUrl, params.path, params.query);\n\t\tconst requestId = generateRequestId();\n\t\tconst headers: Record<string, string> = {\n\t\t\tAccept: 'application/json',\n\t\t\tAuthorization: `Bearer ${options.apiKey}`,\n\t\t\t'X-Request-Id': requestId,\n\t\t\t'User-Agent': userAgent\n\t\t};\n\t\tlet body: string | undefined;\n\t\tif (params.body !== undefined) {\n\t\t\theaders['Content-Type'] = 'application/json';\n\t\t\tbody = JSON.stringify(params.body);\n\t\t}\n\n\t\tlet attempt = 0;\n\t\tlet lastError: unknown;\n\t\twhile (attempt <= retries) {\n\t\t\tconst startedAt = Date.now();\n\t\t\tconst controller = new AbortController();\n\t\t\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\t\t\tlet status = 0;\n\t\t\tlet response: Response | undefined;\n\t\t\tlet attemptError: Error | undefined;\n\t\t\ttry {\n\t\t\t\tresponse = await doFetch(url, {\n\t\t\t\t\tmethod: params.method,\n\t\t\t\t\theaders,\n\t\t\t\t\tbody,\n\t\t\t\t\tsignal: controller.signal\n\t\t\t\t});\n\t\t\t\tstatus = response.status;\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = (await response.json()) as T;\n\t\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\t\tmethod: params.method,\n\t\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trequestId: response.headers.get('x-request-id') ?? requestId,\n\t\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\t\tfinal: true\n\t\t\t\t\t});\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\t\t\t\tconst problem = await safeProblem(response);\n\t\t\t\tconst retryAfterHeader = response.headers.get('retry-after');\n\t\t\t\tconst retryAfter = retryAfterHeader ? parseRetryAfter(retryAfterHeader) : null;\n\t\t\t\tconst err = new QuizbaseError({\n\t\t\t\t\tstatus,\n\t\t\t\t\tproblem,\n\t\t\t\t\trequestId: response.headers.get('x-request-id') ?? requestId,\n\t\t\t\t\tretryAfter,\n\t\t\t\t\turl,\n\t\t\t\t\tmethod: params.method\n\t\t\t\t});\n\t\t\t\tattemptError = err;\n\t\t\t\tif (shouldRetry(status) && attempt < retries) {\n\t\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\t\tmethod: params.method,\n\t\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trequestId: err.requestId,\n\t\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\t\tfinal: false,\n\t\t\t\t\t\terror: err\n\t\t\t\t\t});\n\t\t\t\t\tawait sleep(backoffMs(attempt, retryAfter));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tlastError = err;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\tmethod: params.method,\n\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\turl,\n\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\tstatus,\n\t\t\t\t\trequestId: err.requestId,\n\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\tfinal: true,\n\t\t\t\t\terror: err\n\t\t\t\t});\n\t\t\t\tthrow err;\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof QuizbaseError) throw err;\n\t\t\t\tconst networkErr = err instanceof Error ? err : new Error(String(err));\n\t\t\t\tattemptError = networkErr;\n\t\t\t\tif (attempt < retries) {\n\t\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\t\tmethod: params.method,\n\t\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trequestId: null,\n\t\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\t\tfinal: false,\n\t\t\t\t\t\terror: networkErr\n\t\t\t\t\t});\n\t\t\t\t\tawait sleep(backoffMs(attempt, null));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tlastError = networkErr;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\tmethod: params.method,\n\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\turl,\n\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\tstatus,\n\t\t\t\t\trequestId: null,\n\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\tfinal: true,\n\t\t\t\t\terror: networkErr\n\t\t\t\t});\n\t\t\t\tthrow networkErr;\n\t\t\t} finally {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tvoid attemptError;\n\t\t\t\tvoid response;\n\t\t\t}\n\t\t}\n\t\tthrow lastError ?? new Error('quizbase-client: retry loop exhausted unexpectedly');\n\t}\n\n\treturn {\n\t\tquestions: (() => {\n\t\t\tconst list = (params?: QuestionsListParams) =>\n\t\t\t\trequest<Schemas['QuestionsListResponse']>({\n\t\t\t\t\tendpoint: 'questions.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/questions',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\trandom: (params) =>\n\t\t\t\t\trequest({\n\t\t\t\t\t\tendpoint: 'questions.random',\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\tpath: '/api/v1/questions/random',\n\t\t\t\t\t\tquery: params\n\t\t\t\t\t}),\n\t\t\t\tget: (id, params) =>\n\t\t\t\t\trequest({\n\t\t\t\t\t\tendpoint: 'questions.get',\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\tpath: `/api/v1/questions/${encodeURIComponent(id)}`,\n\t\t\t\t\t\tquery: params\n\t\t\t\t\t}),\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['questions'];\n\t\t})(),\n\t\tcategories: {\n\t\t\tlist: (params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'categories.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/categories',\n\t\t\t\t\tquery: params\n\t\t\t\t})\n\t\t},\n\t\tlanguages: {\n\t\t\tlist: () =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'languages.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/languages'\n\t\t\t\t})\n\t\t},\n\t\ttopics: (() => {\n\t\t\tconst list = (params?: TopicsListParams) =>\n\t\t\t\trequest<Schemas['TopicsListResponse']>({\n\t\t\t\t\tendpoint: 'topics.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/topics',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tget: (slug, params) =>\n\t\t\t\t\trequest({\n\t\t\t\t\t\tendpoint: 'topics.get',\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\tpath: `/api/v1/topics/${encodeURIComponent(slug)}`,\n\t\t\t\t\t\tquery: params\n\t\t\t\t\t}),\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['topics'];\n\t\t})(),\n\t\ttags: (() => {\n\t\t\tconst list = (params?: TagsListParams) =>\n\t\t\t\trequest<Schemas['TagsListResponse']>({\n\t\t\t\t\tendpoint: 'tags.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/tags',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['tags'];\n\t\t})(),\n\t\tsubcategories: (() => {\n\t\t\tconst list = (params?: SubcategoriesListParams) =>\n\t\t\t\trequest<Schemas['SubcategoriesListResponse']>({\n\t\t\t\t\tendpoint: 'subcategories.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/subcategories',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['subcategories'];\n\t\t})(),\n\t\tregions: (() => {\n\t\t\tconst list = (params?: RegionsListParams) =>\n\t\t\t\trequest<Schemas['RegionsListResponse']>({\n\t\t\t\t\tendpoint: 'regions.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/regions',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['regions'];\n\t\t})(),\n\t\tstats: {\n\t\t\tget: () =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'stats.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/stats'\n\t\t\t\t})\n\t\t},\n\t\tme: {\n\t\t\tget: () =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'me.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/me'\n\t\t\t\t})\n\t\t},\n\t\tusage: {\n\t\t\tget: (params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'usage.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/usage',\n\t\t\t\t\tquery: params\n\t\t\t\t})\n\t\t},\n\t\treport: {\n\t\t\tcreate: (body) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'report.create',\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\tpath: '/api/v1/report',\n\t\t\t\t\tbody\n\t\t\t\t})\n\t\t}\n\t};\n}\n\ninterface PaginatedParams {\n\tcursor?: string;\n\t[key: string]: unknown;\n}\n\ninterface PaginatedPage<T> {\n\tdata: T[];\n\t_links?: { next?: string; prev?: string };\n}\n\n/**\n * Extract the `cursor` query param from an absolute or relative `_links.next` URL.\n * Returns `null` when the URL is absent or malformed — caller treats that as end-of-stream.\n */\nfunction extractCursor(nextUrl: string | undefined): string | null {\n\tif (!nextUrl) return null;\n\ttry {\n\t\tconst url = nextUrl.startsWith('http')\n\t\t\t? new URL(nextUrl)\n\t\t\t: new URL(nextUrl, 'http://placeholder');\n\t\treturn url.searchParams.get('cursor');\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function* paginate<P extends PaginatedParams, R extends PaginatedPage<unknown>>(\n\tfetchPage: (params?: P) => Promise<R>,\n\tinitial: P | undefined\n): AsyncIterableIterator<R> {\n\tlet nextParams: P | undefined = initial;\n\twhile (true) {\n\t\tconst page = await fetchPage(nextParams);\n\t\tyield page;\n\t\tconst cursor = extractCursor(page._links?.next);\n\t\tif (!cursor) return;\n\t\tnextParams = { ...(nextParams ?? ({} as P)), cursor } as P;\n\t}\n}\n\nasync function* paginateItems<P extends PaginatedParams, T>(\n\tfetchPage: (params?: P) => Promise<PaginatedPage<T>>,\n\tinitial: P | undefined\n): AsyncIterableIterator<T> {\n\tfor await (const page of paginate(fetchPage, initial)) {\n\t\tfor (const item of page.data) yield item;\n\t}\n}\n\nfunction buildUrl(baseUrl: string, path: string, query: Record<string, unknown> | undefined): string {\n\tconst url = new URL(baseUrl + path);\n\tif (query) {\n\t\tfor (const [key, value] of Object.entries(query)) {\n\t\t\tif (value === undefined || value === null) continue;\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tfor (const item of value) {\n\t\t\t\t\tif (item !== undefined && item !== null) {\n\t\t\t\t\t\turl.searchParams.append(key, String(item));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\turl.searchParams.append(key, String(value));\n\t\t\t}\n\t\t}\n\t}\n\treturn url.toString();\n}\n\nfunction generateRequestId(): string {\n\tconst cryptoApi: Crypto | undefined = globalThis.crypto;\n\tif (cryptoApi && typeof cryptoApi.randomUUID === 'function') {\n\t\treturn cryptoApi.randomUUID();\n\t}\n\treturn `req-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;\n}\n\nasync function safeProblem(response: Response): Promise<ProblemDetails> {\n\ttry {\n\t\tconst data = (await response.json()) as ProblemDetails;\n\t\tif (data && typeof data === 'object') return data;\n\t} catch {\n\t\t// fall through\n\t}\n\treturn {\n\t\ttype: 'about:blank',\n\t\ttitle: response.statusText || `HTTP ${response.status}`,\n\t\tstatus: response.status,\n\t\tdetail: '',\n\t\tinstance: '',\n\t\tcode: 'unknown'\n\t} satisfies ProblemDetails;\n}\n\nfunction shouldRetry(status: number): boolean {\n\treturn status === 429 || (status >= 500 && status < 600);\n}\n\nfunction parseRetryAfter(value: string): number | null {\n\tconst seconds = Number.parseInt(value, 10);\n\tif (Number.isFinite(seconds) && seconds >= 0) return seconds;\n\tconst date = Date.parse(value);\n\tif (Number.isFinite(date)) {\n\t\tconst diff = Math.max(0, Math.ceil((date - Date.now()) / 1000));\n\t\treturn diff;\n\t}\n\treturn null;\n}\n\nfunction backoffMs(attempt: number, retryAfterSeconds: number | null): number {\n\tif (retryAfterSeconds !== null) return retryAfterSeconds * 1000;\n\tconst base = 250;\n\tconst exp = base * 2 ** attempt;\n\tconst jitter = Math.random() * 100;\n\treturn Math.min(exp + jitter, 5_000);\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function emit(\n\thook: OnRequestHook | undefined,\n\tevent: Parameters<OnRequestHook>[0]\n): Promise<void> {\n\tif (!hook) return;\n\ttry {\n\t\tawait hook(event);\n\t} catch {\n\t\t// telemetry must never break the caller\n\t}\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -597,6 +597,88 @@ interface paths {
|
|
|
597
597
|
patch?: never;
|
|
598
598
|
trace?: never;
|
|
599
599
|
};
|
|
600
|
+
"/api/v1/regions": {
|
|
601
|
+
parameters: {
|
|
602
|
+
query?: never;
|
|
603
|
+
header?: never;
|
|
604
|
+
path?: never;
|
|
605
|
+
cookie?: never;
|
|
606
|
+
};
|
|
607
|
+
/**
|
|
608
|
+
* List region codes (cultural affinity)
|
|
609
|
+
* @description Discovery dla `regions` filter. **Cultural affinity** — pytanie ma code regionu, jeśli mieszkańcy kraju lub członkowie grupy kulturowej/religijnej są statystycznie częściej w stanie odpowiedzieć (NIE geografia tematu pytania). Lowercase ISO 3166-1 alpha-2 (`us`, `pl`, `gb`) + cultural codes (`jewish`, `christian-catholic`, `islam` — LLM schema whitelist parity). Sourced z `mv_region_counts` (pre-aggregated TABLE). Cardinality ~150 distinct codes per language.
|
|
610
|
+
*/
|
|
611
|
+
get: {
|
|
612
|
+
parameters: {
|
|
613
|
+
query?: {
|
|
614
|
+
/** @description Display language for category names + slug labels (subcategories/tags) */
|
|
615
|
+
lang?: "en" | "pl";
|
|
616
|
+
q?: string;
|
|
617
|
+
kind?: "country" | "cultural";
|
|
618
|
+
cursor?: string;
|
|
619
|
+
limit?: number;
|
|
620
|
+
};
|
|
621
|
+
header?: never;
|
|
622
|
+
path?: never;
|
|
623
|
+
cookie?: never;
|
|
624
|
+
};
|
|
625
|
+
requestBody?: never;
|
|
626
|
+
responses: {
|
|
627
|
+
/** @description Region codes with labels, kind classification, counts. */
|
|
628
|
+
200: {
|
|
629
|
+
headers: {
|
|
630
|
+
[name: string]: unknown;
|
|
631
|
+
};
|
|
632
|
+
content: {
|
|
633
|
+
"application/json": components["schemas"]["RegionsListResponse"];
|
|
634
|
+
};
|
|
635
|
+
};
|
|
636
|
+
/** @description Missing, malformed, invalid, or revoked API key. */
|
|
637
|
+
401: {
|
|
638
|
+
headers: {
|
|
639
|
+
[name: string]: unknown;
|
|
640
|
+
};
|
|
641
|
+
content: {
|
|
642
|
+
"application/problem+json": components["schemas"]["ProblemDetails"];
|
|
643
|
+
};
|
|
644
|
+
};
|
|
645
|
+
/** @description IP not allowed (per-key allowlist). */
|
|
646
|
+
403: {
|
|
647
|
+
headers: {
|
|
648
|
+
[name: string]: unknown;
|
|
649
|
+
};
|
|
650
|
+
content: {
|
|
651
|
+
"application/problem+json": components["schemas"]["ProblemDetails"];
|
|
652
|
+
};
|
|
653
|
+
};
|
|
654
|
+
/** @description Rate limit exceeded. See `Retry-After` header. */
|
|
655
|
+
429: {
|
|
656
|
+
headers: {
|
|
657
|
+
[name: string]: unknown;
|
|
658
|
+
};
|
|
659
|
+
content: {
|
|
660
|
+
"application/problem+json": components["schemas"]["ProblemDetails"];
|
|
661
|
+
};
|
|
662
|
+
};
|
|
663
|
+
/** @description Server error. */
|
|
664
|
+
500: {
|
|
665
|
+
headers: {
|
|
666
|
+
[name: string]: unknown;
|
|
667
|
+
};
|
|
668
|
+
content: {
|
|
669
|
+
"application/problem+json": components["schemas"]["ProblemDetails"];
|
|
670
|
+
};
|
|
671
|
+
};
|
|
672
|
+
};
|
|
673
|
+
};
|
|
674
|
+
put?: never;
|
|
675
|
+
post?: never;
|
|
676
|
+
delete?: never;
|
|
677
|
+
options?: never;
|
|
678
|
+
head?: never;
|
|
679
|
+
patch?: never;
|
|
680
|
+
trace?: never;
|
|
681
|
+
};
|
|
600
682
|
"/api/v1/report": {
|
|
601
683
|
parameters: {
|
|
602
684
|
query?: never;
|
|
@@ -857,9 +939,11 @@ interface paths {
|
|
|
857
939
|
topic?: string;
|
|
858
940
|
topics_any?: string;
|
|
859
941
|
subcategory?: string;
|
|
860
|
-
|
|
942
|
+
/** @description Quality preset: "high" (default) skips needs_review=true; "all" includes everything */
|
|
943
|
+
quality?: "high" | "all";
|
|
944
|
+
/** @description Cultural affinity codes. A question is tagged with a region if residents of that country, or members of that cultural/religious group, are statistically more likely to know the answer (NOT geography of the subject). Lowercase ISO 3166-1 alpha-2 (e.g. `us`, `pl`, `gb`) plus cultural codes (`jewish`, `christian-catholic`, `islam`). AND-logic. Empty array on a question = universally accessible (no cultural advantage). Discover via `GET /api/v1/regions`. Uppercase tolerated on input (normalized to lowercase). */
|
|
861
945
|
regions?: string;
|
|
862
|
-
source?: "
|
|
946
|
+
source?: "arc" | "creak" | "entityq" | "kqa-pro" | "mintaka" | "mkqa" | "nq-open" | "opentdb" | "opentriviaqa" | "qasc" | "quizbase" | "webq";
|
|
863
947
|
license?: "CC-BY-SA-4.0" | "CC-BY-SA-3.0" | "CC-BY-4.0" | "MIT" | "proprietary";
|
|
864
948
|
count?: "estimate" | "exact" | "none";
|
|
865
949
|
};
|
|
@@ -958,9 +1042,11 @@ interface paths {
|
|
|
958
1042
|
topic?: string;
|
|
959
1043
|
topics_any?: string;
|
|
960
1044
|
subcategory?: string;
|
|
961
|
-
|
|
1045
|
+
/** @description Quality preset: "high" (default) skips needs_review=true; "all" includes everything */
|
|
1046
|
+
quality?: "high" | "all";
|
|
1047
|
+
/** @description Cultural affinity codes. A question is tagged with a region if residents of that country, or members of that cultural/religious group, are statistically more likely to know the answer (NOT geography of the subject). Lowercase ISO 3166-1 alpha-2 (e.g. `us`, `pl`, `gb`) plus cultural codes (`jewish`, `christian-catholic`, `islam`). AND-logic. Empty array on a question = universally accessible (no cultural advantage). Discover via `GET /api/v1/regions`. Uppercase tolerated on input (normalized to lowercase). */
|
|
962
1048
|
regions?: string;
|
|
963
|
-
source?: "
|
|
1049
|
+
source?: "arc" | "creak" | "entityq" | "kqa-pro" | "mintaka" | "mkqa" | "nq-open" | "opentdb" | "opentriviaqa" | "qasc" | "quizbase" | "webq";
|
|
964
1050
|
license?: "CC-BY-SA-4.0" | "CC-BY-SA-3.0" | "CC-BY-4.0" | "MIT" | "proprietary";
|
|
965
1051
|
exclude?: string;
|
|
966
1052
|
};
|
|
@@ -1207,12 +1293,19 @@ interface components {
|
|
|
1207
1293
|
/** @description Localized `{slug, label}` per `?lang=`. */
|
|
1208
1294
|
tags: components["schemas"]["SlugLabel"][];
|
|
1209
1295
|
/**
|
|
1296
|
+
* @description Cultural affinity codes — residents/members of these countries or cultural groups are statistically more likely to know the answer. Lowercase ISO 3166-1 alpha-2 (`us`, `pl`, `gb`) + cultural codes (`jewish`, `christian-catholic`, `islam`). Empty array = universally accessible. Discover the full catalog via `GET /api/v1/regions`.
|
|
1210
1297
|
* @example [
|
|
1211
1298
|
* "pl"
|
|
1212
1299
|
* ]
|
|
1213
1300
|
*/
|
|
1214
1301
|
regions: string[];
|
|
1215
1302
|
attribution: components["schemas"]["Attribution"];
|
|
1303
|
+
/**
|
|
1304
|
+
* @description Quality flag. Present only when the client requested non-default content via `?quality=all` (REST) or `quality: "all"` (MCP) — and always on `/api/v1/questions/{id}` deep-link responses. For default `?quality=high` all returned questions are by definition `high`, so the field is omitted to avoid redundancy. `needs_review` = flagged for moderation review (low-quality distractors, trivial answer-in-question patterns).
|
|
1305
|
+
* @example high
|
|
1306
|
+
* @enum {string}
|
|
1307
|
+
*/
|
|
1308
|
+
quality?: "high" | "needs_review";
|
|
1216
1309
|
/**
|
|
1217
1310
|
* Format: uuid
|
|
1218
1311
|
* @description Direct parent (usually English source) when this is a translation.
|
|
@@ -1561,6 +1654,41 @@ interface components {
|
|
|
1561
1654
|
meta: components["schemas"]["MetaTagsList"];
|
|
1562
1655
|
_links?: components["schemas"]["Links"];
|
|
1563
1656
|
};
|
|
1657
|
+
RegionsListResponse: {
|
|
1658
|
+
data: components["schemas"]["RegionEntry"][];
|
|
1659
|
+
meta: components["schemas"]["MetaRegionsList"];
|
|
1660
|
+
_links?: components["schemas"]["Links"];
|
|
1661
|
+
};
|
|
1662
|
+
RegionEntry: {
|
|
1663
|
+
/** @example pl */
|
|
1664
|
+
code: string;
|
|
1665
|
+
/**
|
|
1666
|
+
* @example country
|
|
1667
|
+
* @enum {string}
|
|
1668
|
+
*/
|
|
1669
|
+
kind: "country" | "cultural";
|
|
1670
|
+
/** @example Poland */
|
|
1671
|
+
label: string;
|
|
1672
|
+
/** @example 7388 */
|
|
1673
|
+
count: number;
|
|
1674
|
+
};
|
|
1675
|
+
MetaRegionsList: {
|
|
1676
|
+
/** @example 100 */
|
|
1677
|
+
count: number;
|
|
1678
|
+
/** @example 152 */
|
|
1679
|
+
total: number;
|
|
1680
|
+
/**
|
|
1681
|
+
* @description Language of the response content.
|
|
1682
|
+
* @example en
|
|
1683
|
+
*/
|
|
1684
|
+
language: string;
|
|
1685
|
+
/**
|
|
1686
|
+
* Format: uuid
|
|
1687
|
+
* @description Unique request identifier (also in `X-Request-Id` response header).
|
|
1688
|
+
* @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
|
|
1689
|
+
*/
|
|
1690
|
+
requestId: string;
|
|
1691
|
+
};
|
|
1564
1692
|
ReportAcceptedResponse: {
|
|
1565
1693
|
/** @enum {boolean} */
|
|
1566
1694
|
received: true;
|
|
@@ -1795,7 +1923,7 @@ interface components {
|
|
|
1795
1923
|
|
|
1796
1924
|
type Schemas = components['schemas'];
|
|
1797
1925
|
/** Logical endpoint identifiers — stable across path-param values, used for telemetry + per-endpoint timeouts. */
|
|
1798
|
-
type EndpointKey = 'questions.list' | 'questions.random' | 'questions.get' | 'categories.list' | 'languages.list' | 'topics.list' | 'topics.get' | 'tags.list' | 'subcategories.list' | 'stats.get' | 'me.get' | 'usage.get' | 'report.create';
|
|
1926
|
+
type EndpointKey = 'questions.list' | 'questions.random' | 'questions.get' | 'categories.list' | 'languages.list' | 'topics.list' | 'topics.get' | 'tags.list' | 'subcategories.list' | 'regions.list' | 'stats.get' | 'me.get' | 'usage.get' | 'report.create';
|
|
1799
1927
|
interface ClientOptions {
|
|
1800
1928
|
/** API key — `qb_pk_*` (publishable, CORS-safe for browsers) or `qb_sk_*` (secret, backend-only). Get one at https://quizbase.runriva.com/dashboard/keys. */
|
|
1801
1929
|
apiKey: string;
|
|
@@ -1818,6 +1946,7 @@ type QuestionsListParams = paths['/api/v1/questions']['get']['parameters']['quer
|
|
|
1818
1946
|
type TopicsListParams = paths['/api/v1/topics']['get']['parameters']['query'];
|
|
1819
1947
|
type TagsListParams = paths['/api/v1/tags']['get']['parameters']['query'];
|
|
1820
1948
|
type SubcategoriesListParams = paths['/api/v1/subcategories']['get']['parameters']['query'];
|
|
1949
|
+
type RegionsListParams = paths['/api/v1/regions']['get']['parameters']['query'];
|
|
1821
1950
|
interface QuizbaseClient {
|
|
1822
1951
|
questions: {
|
|
1823
1952
|
list(params?: QuestionsListParams): Promise<Schemas['QuestionsListResponse']>;
|
|
@@ -1856,6 +1985,21 @@ interface QuizbaseClient {
|
|
|
1856
1985
|
/** Iterate every subcategory across all pages. */
|
|
1857
1986
|
listAll(params?: SubcategoriesListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;
|
|
1858
1987
|
};
|
|
1988
|
+
/**
|
|
1989
|
+
* Region codes — **cultural affinity**, not geography. A question is tagged with
|
|
1990
|
+
* a region if residents of that country or members of that cultural/religious
|
|
1991
|
+
* group are statistically more likely to know the answer. Lowercase ISO 3166-1
|
|
1992
|
+
* alpha-2 (`us`, `pl`, `gb`) plus cultural codes (`jewish`, `christian-catholic`,
|
|
1993
|
+
* `islam`). Pair with `questions.random({ regions: [...] })` to fetch matching
|
|
1994
|
+
* questions.
|
|
1995
|
+
*/
|
|
1996
|
+
regions: {
|
|
1997
|
+
list(params?: RegionsListParams): Promise<Schemas['RegionsListResponse']>;
|
|
1998
|
+
/** Iterate every page of `/regions`, auto-following `_links.next`. */
|
|
1999
|
+
pages(params?: RegionsListParams): AsyncIterableIterator<Schemas['RegionsListResponse']>;
|
|
2000
|
+
/** Iterate every region across all pages. */
|
|
2001
|
+
listAll(params?: RegionsListParams): AsyncIterableIterator<Schemas['RegionEntry']>;
|
|
2002
|
+
};
|
|
1859
2003
|
stats: {
|
|
1860
2004
|
get(): Promise<Schemas['StatsResponse']>;
|
|
1861
2005
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -597,6 +597,88 @@ interface paths {
|
|
|
597
597
|
patch?: never;
|
|
598
598
|
trace?: never;
|
|
599
599
|
};
|
|
600
|
+
"/api/v1/regions": {
|
|
601
|
+
parameters: {
|
|
602
|
+
query?: never;
|
|
603
|
+
header?: never;
|
|
604
|
+
path?: never;
|
|
605
|
+
cookie?: never;
|
|
606
|
+
};
|
|
607
|
+
/**
|
|
608
|
+
* List region codes (cultural affinity)
|
|
609
|
+
* @description Discovery dla `regions` filter. **Cultural affinity** — pytanie ma code regionu, jeśli mieszkańcy kraju lub członkowie grupy kulturowej/religijnej są statystycznie częściej w stanie odpowiedzieć (NIE geografia tematu pytania). Lowercase ISO 3166-1 alpha-2 (`us`, `pl`, `gb`) + cultural codes (`jewish`, `christian-catholic`, `islam` — LLM schema whitelist parity). Sourced z `mv_region_counts` (pre-aggregated TABLE). Cardinality ~150 distinct codes per language.
|
|
610
|
+
*/
|
|
611
|
+
get: {
|
|
612
|
+
parameters: {
|
|
613
|
+
query?: {
|
|
614
|
+
/** @description Display language for category names + slug labels (subcategories/tags) */
|
|
615
|
+
lang?: "en" | "pl";
|
|
616
|
+
q?: string;
|
|
617
|
+
kind?: "country" | "cultural";
|
|
618
|
+
cursor?: string;
|
|
619
|
+
limit?: number;
|
|
620
|
+
};
|
|
621
|
+
header?: never;
|
|
622
|
+
path?: never;
|
|
623
|
+
cookie?: never;
|
|
624
|
+
};
|
|
625
|
+
requestBody?: never;
|
|
626
|
+
responses: {
|
|
627
|
+
/** @description Region codes with labels, kind classification, counts. */
|
|
628
|
+
200: {
|
|
629
|
+
headers: {
|
|
630
|
+
[name: string]: unknown;
|
|
631
|
+
};
|
|
632
|
+
content: {
|
|
633
|
+
"application/json": components["schemas"]["RegionsListResponse"];
|
|
634
|
+
};
|
|
635
|
+
};
|
|
636
|
+
/** @description Missing, malformed, invalid, or revoked API key. */
|
|
637
|
+
401: {
|
|
638
|
+
headers: {
|
|
639
|
+
[name: string]: unknown;
|
|
640
|
+
};
|
|
641
|
+
content: {
|
|
642
|
+
"application/problem+json": components["schemas"]["ProblemDetails"];
|
|
643
|
+
};
|
|
644
|
+
};
|
|
645
|
+
/** @description IP not allowed (per-key allowlist). */
|
|
646
|
+
403: {
|
|
647
|
+
headers: {
|
|
648
|
+
[name: string]: unknown;
|
|
649
|
+
};
|
|
650
|
+
content: {
|
|
651
|
+
"application/problem+json": components["schemas"]["ProblemDetails"];
|
|
652
|
+
};
|
|
653
|
+
};
|
|
654
|
+
/** @description Rate limit exceeded. See `Retry-After` header. */
|
|
655
|
+
429: {
|
|
656
|
+
headers: {
|
|
657
|
+
[name: string]: unknown;
|
|
658
|
+
};
|
|
659
|
+
content: {
|
|
660
|
+
"application/problem+json": components["schemas"]["ProblemDetails"];
|
|
661
|
+
};
|
|
662
|
+
};
|
|
663
|
+
/** @description Server error. */
|
|
664
|
+
500: {
|
|
665
|
+
headers: {
|
|
666
|
+
[name: string]: unknown;
|
|
667
|
+
};
|
|
668
|
+
content: {
|
|
669
|
+
"application/problem+json": components["schemas"]["ProblemDetails"];
|
|
670
|
+
};
|
|
671
|
+
};
|
|
672
|
+
};
|
|
673
|
+
};
|
|
674
|
+
put?: never;
|
|
675
|
+
post?: never;
|
|
676
|
+
delete?: never;
|
|
677
|
+
options?: never;
|
|
678
|
+
head?: never;
|
|
679
|
+
patch?: never;
|
|
680
|
+
trace?: never;
|
|
681
|
+
};
|
|
600
682
|
"/api/v1/report": {
|
|
601
683
|
parameters: {
|
|
602
684
|
query?: never;
|
|
@@ -857,9 +939,11 @@ interface paths {
|
|
|
857
939
|
topic?: string;
|
|
858
940
|
topics_any?: string;
|
|
859
941
|
subcategory?: string;
|
|
860
|
-
|
|
942
|
+
/** @description Quality preset: "high" (default) skips needs_review=true; "all" includes everything */
|
|
943
|
+
quality?: "high" | "all";
|
|
944
|
+
/** @description Cultural affinity codes. A question is tagged with a region if residents of that country, or members of that cultural/religious group, are statistically more likely to know the answer (NOT geography of the subject). Lowercase ISO 3166-1 alpha-2 (e.g. `us`, `pl`, `gb`) plus cultural codes (`jewish`, `christian-catholic`, `islam`). AND-logic. Empty array on a question = universally accessible (no cultural advantage). Discover via `GET /api/v1/regions`. Uppercase tolerated on input (normalized to lowercase). */
|
|
861
945
|
regions?: string;
|
|
862
|
-
source?: "
|
|
946
|
+
source?: "arc" | "creak" | "entityq" | "kqa-pro" | "mintaka" | "mkqa" | "nq-open" | "opentdb" | "opentriviaqa" | "qasc" | "quizbase" | "webq";
|
|
863
947
|
license?: "CC-BY-SA-4.0" | "CC-BY-SA-3.0" | "CC-BY-4.0" | "MIT" | "proprietary";
|
|
864
948
|
count?: "estimate" | "exact" | "none";
|
|
865
949
|
};
|
|
@@ -958,9 +1042,11 @@ interface paths {
|
|
|
958
1042
|
topic?: string;
|
|
959
1043
|
topics_any?: string;
|
|
960
1044
|
subcategory?: string;
|
|
961
|
-
|
|
1045
|
+
/** @description Quality preset: "high" (default) skips needs_review=true; "all" includes everything */
|
|
1046
|
+
quality?: "high" | "all";
|
|
1047
|
+
/** @description Cultural affinity codes. A question is tagged with a region if residents of that country, or members of that cultural/religious group, are statistically more likely to know the answer (NOT geography of the subject). Lowercase ISO 3166-1 alpha-2 (e.g. `us`, `pl`, `gb`) plus cultural codes (`jewish`, `christian-catholic`, `islam`). AND-logic. Empty array on a question = universally accessible (no cultural advantage). Discover via `GET /api/v1/regions`. Uppercase tolerated on input (normalized to lowercase). */
|
|
962
1048
|
regions?: string;
|
|
963
|
-
source?: "
|
|
1049
|
+
source?: "arc" | "creak" | "entityq" | "kqa-pro" | "mintaka" | "mkqa" | "nq-open" | "opentdb" | "opentriviaqa" | "qasc" | "quizbase" | "webq";
|
|
964
1050
|
license?: "CC-BY-SA-4.0" | "CC-BY-SA-3.0" | "CC-BY-4.0" | "MIT" | "proprietary";
|
|
965
1051
|
exclude?: string;
|
|
966
1052
|
};
|
|
@@ -1207,12 +1293,19 @@ interface components {
|
|
|
1207
1293
|
/** @description Localized `{slug, label}` per `?lang=`. */
|
|
1208
1294
|
tags: components["schemas"]["SlugLabel"][];
|
|
1209
1295
|
/**
|
|
1296
|
+
* @description Cultural affinity codes — residents/members of these countries or cultural groups are statistically more likely to know the answer. Lowercase ISO 3166-1 alpha-2 (`us`, `pl`, `gb`) + cultural codes (`jewish`, `christian-catholic`, `islam`). Empty array = universally accessible. Discover the full catalog via `GET /api/v1/regions`.
|
|
1210
1297
|
* @example [
|
|
1211
1298
|
* "pl"
|
|
1212
1299
|
* ]
|
|
1213
1300
|
*/
|
|
1214
1301
|
regions: string[];
|
|
1215
1302
|
attribution: components["schemas"]["Attribution"];
|
|
1303
|
+
/**
|
|
1304
|
+
* @description Quality flag. Present only when the client requested non-default content via `?quality=all` (REST) or `quality: "all"` (MCP) — and always on `/api/v1/questions/{id}` deep-link responses. For default `?quality=high` all returned questions are by definition `high`, so the field is omitted to avoid redundancy. `needs_review` = flagged for moderation review (low-quality distractors, trivial answer-in-question patterns).
|
|
1305
|
+
* @example high
|
|
1306
|
+
* @enum {string}
|
|
1307
|
+
*/
|
|
1308
|
+
quality?: "high" | "needs_review";
|
|
1216
1309
|
/**
|
|
1217
1310
|
* Format: uuid
|
|
1218
1311
|
* @description Direct parent (usually English source) when this is a translation.
|
|
@@ -1561,6 +1654,41 @@ interface components {
|
|
|
1561
1654
|
meta: components["schemas"]["MetaTagsList"];
|
|
1562
1655
|
_links?: components["schemas"]["Links"];
|
|
1563
1656
|
};
|
|
1657
|
+
RegionsListResponse: {
|
|
1658
|
+
data: components["schemas"]["RegionEntry"][];
|
|
1659
|
+
meta: components["schemas"]["MetaRegionsList"];
|
|
1660
|
+
_links?: components["schemas"]["Links"];
|
|
1661
|
+
};
|
|
1662
|
+
RegionEntry: {
|
|
1663
|
+
/** @example pl */
|
|
1664
|
+
code: string;
|
|
1665
|
+
/**
|
|
1666
|
+
* @example country
|
|
1667
|
+
* @enum {string}
|
|
1668
|
+
*/
|
|
1669
|
+
kind: "country" | "cultural";
|
|
1670
|
+
/** @example Poland */
|
|
1671
|
+
label: string;
|
|
1672
|
+
/** @example 7388 */
|
|
1673
|
+
count: number;
|
|
1674
|
+
};
|
|
1675
|
+
MetaRegionsList: {
|
|
1676
|
+
/** @example 100 */
|
|
1677
|
+
count: number;
|
|
1678
|
+
/** @example 152 */
|
|
1679
|
+
total: number;
|
|
1680
|
+
/**
|
|
1681
|
+
* @description Language of the response content.
|
|
1682
|
+
* @example en
|
|
1683
|
+
*/
|
|
1684
|
+
language: string;
|
|
1685
|
+
/**
|
|
1686
|
+
* Format: uuid
|
|
1687
|
+
* @description Unique request identifier (also in `X-Request-Id` response header).
|
|
1688
|
+
* @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
|
|
1689
|
+
*/
|
|
1690
|
+
requestId: string;
|
|
1691
|
+
};
|
|
1564
1692
|
ReportAcceptedResponse: {
|
|
1565
1693
|
/** @enum {boolean} */
|
|
1566
1694
|
received: true;
|
|
@@ -1795,7 +1923,7 @@ interface components {
|
|
|
1795
1923
|
|
|
1796
1924
|
type Schemas = components['schemas'];
|
|
1797
1925
|
/** Logical endpoint identifiers — stable across path-param values, used for telemetry + per-endpoint timeouts. */
|
|
1798
|
-
type EndpointKey = 'questions.list' | 'questions.random' | 'questions.get' | 'categories.list' | 'languages.list' | 'topics.list' | 'topics.get' | 'tags.list' | 'subcategories.list' | 'stats.get' | 'me.get' | 'usage.get' | 'report.create';
|
|
1926
|
+
type EndpointKey = 'questions.list' | 'questions.random' | 'questions.get' | 'categories.list' | 'languages.list' | 'topics.list' | 'topics.get' | 'tags.list' | 'subcategories.list' | 'regions.list' | 'stats.get' | 'me.get' | 'usage.get' | 'report.create';
|
|
1799
1927
|
interface ClientOptions {
|
|
1800
1928
|
/** API key — `qb_pk_*` (publishable, CORS-safe for browsers) or `qb_sk_*` (secret, backend-only). Get one at https://quizbase.runriva.com/dashboard/keys. */
|
|
1801
1929
|
apiKey: string;
|
|
@@ -1818,6 +1946,7 @@ type QuestionsListParams = paths['/api/v1/questions']['get']['parameters']['quer
|
|
|
1818
1946
|
type TopicsListParams = paths['/api/v1/topics']['get']['parameters']['query'];
|
|
1819
1947
|
type TagsListParams = paths['/api/v1/tags']['get']['parameters']['query'];
|
|
1820
1948
|
type SubcategoriesListParams = paths['/api/v1/subcategories']['get']['parameters']['query'];
|
|
1949
|
+
type RegionsListParams = paths['/api/v1/regions']['get']['parameters']['query'];
|
|
1821
1950
|
interface QuizbaseClient {
|
|
1822
1951
|
questions: {
|
|
1823
1952
|
list(params?: QuestionsListParams): Promise<Schemas['QuestionsListResponse']>;
|
|
@@ -1856,6 +1985,21 @@ interface QuizbaseClient {
|
|
|
1856
1985
|
/** Iterate every subcategory across all pages. */
|
|
1857
1986
|
listAll(params?: SubcategoriesListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;
|
|
1858
1987
|
};
|
|
1988
|
+
/**
|
|
1989
|
+
* Region codes — **cultural affinity**, not geography. A question is tagged with
|
|
1990
|
+
* a region if residents of that country or members of that cultural/religious
|
|
1991
|
+
* group are statistically more likely to know the answer. Lowercase ISO 3166-1
|
|
1992
|
+
* alpha-2 (`us`, `pl`, `gb`) plus cultural codes (`jewish`, `christian-catholic`,
|
|
1993
|
+
* `islam`). Pair with `questions.random({ regions: [...] })` to fetch matching
|
|
1994
|
+
* questions.
|
|
1995
|
+
*/
|
|
1996
|
+
regions: {
|
|
1997
|
+
list(params?: RegionsListParams): Promise<Schemas['RegionsListResponse']>;
|
|
1998
|
+
/** Iterate every page of `/regions`, auto-following `_links.next`. */
|
|
1999
|
+
pages(params?: RegionsListParams): AsyncIterableIterator<Schemas['RegionsListResponse']>;
|
|
2000
|
+
/** Iterate every region across all pages. */
|
|
2001
|
+
listAll(params?: RegionsListParams): AsyncIterableIterator<Schemas['RegionEntry']>;
|
|
2002
|
+
};
|
|
1859
2003
|
stats: {
|
|
1860
2004
|
get(): Promise<Schemas['StatsResponse']>;
|
|
1861
2005
|
};
|
package/dist/index.js
CHANGED
|
@@ -43,6 +43,7 @@ var DEFAULT_TIMEOUTS = {
|
|
|
43
43
|
"topics.get": 1e4,
|
|
44
44
|
"tags.list": 15e3,
|
|
45
45
|
"subcategories.list": 15e3,
|
|
46
|
+
"regions.list": 1e4,
|
|
46
47
|
"stats.get": 1e4,
|
|
47
48
|
"me.get": 1e4,
|
|
48
49
|
"usage.get": 1e4,
|
|
@@ -282,6 +283,19 @@ function createClient(options) {
|
|
|
282
283
|
listAll: (params) => paginateItems(list, params)
|
|
283
284
|
};
|
|
284
285
|
})(),
|
|
286
|
+
regions: /* @__PURE__ */ (() => {
|
|
287
|
+
const list = (params) => request({
|
|
288
|
+
endpoint: "regions.list",
|
|
289
|
+
method: "GET",
|
|
290
|
+
path: "/api/v1/regions",
|
|
291
|
+
query: params
|
|
292
|
+
});
|
|
293
|
+
return {
|
|
294
|
+
list,
|
|
295
|
+
pages: (params) => paginate(list, params),
|
|
296
|
+
listAll: (params) => paginateItems(list, params)
|
|
297
|
+
};
|
|
298
|
+
})(),
|
|
285
299
|
stats: {
|
|
286
300
|
get: () => request({
|
|
287
301
|
endpoint: "stats.get",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { components } from './types.gen.js';\n\nexport type ProblemDetails = components['schemas']['ProblemDetails'];\n\nexport interface QuizbaseErrorOptions {\n\tstatus: number;\n\tproblem: ProblemDetails;\n\trequestId: string | null;\n\tretryAfter: number | null;\n\turl: string;\n\tmethod: string;\n}\n\n/**\n * Thrown for any non-2xx response from the QuizBase API.\n * Carries the parsed RFC 9457 Problem Details body, X-Request-Id for support,\n * and a parsed retry-after (seconds) for 429s.\n */\nexport class QuizbaseError extends Error {\n\treadonly status: number;\n\treadonly problem: ProblemDetails;\n\treadonly requestId: string | null;\n\treadonly retryAfter: number | null;\n\treadonly url: string;\n\treadonly method: string;\n\n\tconstructor(opts: QuizbaseErrorOptions) {\n\t\tconst title = opts.problem.title ?? `HTTP ${opts.status}`;\n\t\tconst detail = opts.problem.detail ? ` — ${opts.problem.detail}` : '';\n\t\tsuper(`QuizBase ${opts.method} ${opts.url} → ${opts.status} ${title}${detail}`);\n\t\tthis.name = 'QuizbaseError';\n\t\tthis.status = opts.status;\n\t\tthis.problem = opts.problem;\n\t\tthis.requestId = opts.requestId;\n\t\tthis.retryAfter = opts.retryAfter;\n\t\tthis.url = opts.url;\n\t\tthis.method = opts.method;\n\t}\n\n\tget type(): string | undefined {\n\t\treturn this.problem.type;\n\t}\n\n\tget isRateLimited(): boolean {\n\t\treturn this.status === 429;\n\t}\n\n\tget isAuthError(): boolean {\n\t\treturn this.status === 401 || this.status === 403;\n\t}\n\n\tget isServerError(): boolean {\n\t\treturn this.status >= 500;\n\t}\n}\n","import { QuizbaseError, type ProblemDetails } from './errors.js';\nimport type { OnRequestHook } from './telemetry.js';\nimport type { components, paths } from './types.gen.js';\n\ntype Schemas = components['schemas'];\n\n/** Logical endpoint identifiers — stable across path-param values, used for telemetry + per-endpoint timeouts. */\nexport type EndpointKey =\n\t| 'questions.list'\n\t| 'questions.random'\n\t| 'questions.get'\n\t| 'categories.list'\n\t| 'languages.list'\n\t| 'topics.list'\n\t| 'topics.get'\n\t| 'tags.list'\n\t| 'subcategories.list'\n\t| 'stats.get'\n\t| 'me.get'\n\t| 'usage.get'\n\t| 'report.create';\n\nconst DEFAULT_TIMEOUTS: Record<EndpointKey, number> = {\n\t'questions.list': 15_000,\n\t'questions.random': 10_000,\n\t'questions.get': 10_000,\n\t'categories.list': 10_000,\n\t'languages.list': 10_000,\n\t'topics.list': 15_000,\n\t'topics.get': 10_000,\n\t'tags.list': 15_000,\n\t'subcategories.list': 15_000,\n\t'stats.get': 10_000,\n\t'me.get': 10_000,\n\t'usage.get': 10_000,\n\t'report.create': 15_000\n};\n\nexport interface ClientOptions {\n\t/** API key — `qb_pk_*` (publishable, CORS-safe for browsers) or `qb_sk_*` (secret, backend-only). Get one at https://quizbase.runriva.com/dashboard/keys. */\n\tapiKey: string;\n\t/** Override base URL. Defaults to `https://quizbase.runriva.com`. */\n\tbaseUrl?: string;\n\t/** Default request timeout in ms. Defaults to 30_000. Per-endpoint overrides take precedence. */\n\ttimeout?: number;\n\t/** Per-endpoint timeout overrides keyed by `EndpointKey`. */\n\ttimeouts?: Partial<Record<EndpointKey, number>>;\n\t/** Number of retries for 429 / 5xx / network errors. Defaults to 2 (3 total attempts). */\n\tretries?: number;\n\t/** Optional `fetch` implementation. Defaults to global `fetch`. */\n\tfetch?: typeof fetch;\n\t/** Telemetry hook fired after every HTTP attempt (including retries). */\n\tonRequest?: OnRequestHook;\n\t/** User-Agent suffix appended to the SDK identifier. */\n\tuserAgent?: string;\n}\n\ntype QuestionsListParams = paths['/api/v1/questions']['get']['parameters']['query'];\ntype TopicsListParams = paths['/api/v1/topics']['get']['parameters']['query'];\ntype TagsListParams = paths['/api/v1/tags']['get']['parameters']['query'];\ntype SubcategoriesListParams = paths['/api/v1/subcategories']['get']['parameters']['query'];\n\nexport interface QuizbaseClient {\n\tquestions: {\n\t\tlist(params?: QuestionsListParams): Promise<Schemas['QuestionsListResponse']>;\n\t\trandom(\n\t\t\tparams?: paths['/api/v1/questions/random']['get']['parameters']['query']\n\t\t): Promise<Schemas['QuestionsRandomResponse']>;\n\t\tget(\n\t\t\tid: string,\n\t\t\tparams?: paths['/api/v1/questions/{id}']['get']['parameters']['query']\n\t\t): Promise<Schemas['QuestionByIdResponse']>;\n\t\t/** Iterate every page of `/questions`, auto-following `_links.next`. */\n\t\tpages(params?: QuestionsListParams): AsyncIterableIterator<Schemas['QuestionsListResponse']>;\n\t\t/** Iterate every question across all pages. */\n\t\tlistAll(params?: QuestionsListParams): AsyncIterableIterator<Schemas['Question']>;\n\t};\n\tcategories: {\n\t\tlist(\n\t\t\tparams?: paths['/api/v1/categories']['get']['parameters']['query']\n\t\t): Promise<Schemas['CategoriesResponse']>;\n\t};\n\tlanguages: {\n\t\tlist(): Promise<Schemas['LanguagesResponse']>;\n\t};\n\ttopics: {\n\t\tlist(params?: TopicsListParams): Promise<Schemas['TopicsListResponse']>;\n\t\tget(\n\t\t\tslug: string,\n\t\t\tparams?: paths['/api/v1/topics/{slug}']['get']['parameters']['query']\n\t\t): Promise<Schemas['TopicDetailResponse']>;\n\t\t/** Iterate every page of `/topics`, auto-following `_links.next`. */\n\t\tpages(params?: TopicsListParams): AsyncIterableIterator<Schemas['TopicsListResponse']>;\n\t\t/** Iterate every topic across all pages. */\n\t\tlistAll(params?: TopicsListParams): AsyncIterableIterator<Schemas['TopicEntry']>;\n\t};\n\ttags: {\n\t\tlist(params?: TagsListParams): Promise<Schemas['TagsListResponse']>;\n\t\t/** Iterate every page of `/tags`, auto-following `_links.next`. */\n\t\tpages(params?: TagsListParams): AsyncIterableIterator<Schemas['TagsListResponse']>;\n\t\t/** Iterate every tag across all pages. */\n\t\tlistAll(params?: TagsListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;\n\t};\n\tsubcategories: {\n\t\tlist(params?: SubcategoriesListParams): Promise<Schemas['SubcategoriesListResponse']>;\n\t\t/** Iterate every page of `/subcategories`, auto-following `_links.next`. */\n\t\tpages(\n\t\t\tparams?: SubcategoriesListParams\n\t\t): AsyncIterableIterator<Schemas['SubcategoriesListResponse']>;\n\t\t/** Iterate every subcategory across all pages. */\n\t\tlistAll(params?: SubcategoriesListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;\n\t};\n\tstats: {\n\t\tget(): Promise<Schemas['StatsResponse']>;\n\t};\n\tme: {\n\t\tget(): Promise<Schemas['MeResponse']>;\n\t};\n\tusage: {\n\t\tget(\n\t\t\tparams?: paths['/api/v1/usage']['get']['parameters']['query']\n\t\t): Promise<Schemas['UsageResponse']>;\n\t};\n\treport: {\n\t\tcreate(\n\t\t\tbody: NonNullable<\n\t\t\t\tpaths['/api/v1/report']['post']['requestBody']\n\t\t\t>['content']['application/json']\n\t\t): Promise<Schemas['ReportAcceptedResponse']>;\n\t};\n}\n\nconst DEFAULT_BASE_URL = 'https://quizbase.runriva.com';\nconst SDK_VERSION = '0.1.0';\n\ninterface RequestParams {\n\tendpoint: EndpointKey;\n\tmethod: 'GET' | 'POST';\n\tpath: string;\n\tquery?: Record<string, unknown> | undefined;\n\tbody?: unknown;\n}\n\nexport function createClient(options: ClientOptions): QuizbaseClient {\n\tif (!options.apiKey) {\n\t\tthrow new Error('createClient: `apiKey` is required.');\n\t}\n\tconst baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n\tconst defaultTimeout = options.timeout ?? 30_000;\n\tconst timeouts: Record<EndpointKey, number> = { ...DEFAULT_TIMEOUTS };\n\tif (options.timeouts) {\n\t\tfor (const [key, value] of Object.entries(options.timeouts) as [\n\t\t\tEndpointKey,\n\t\t\tnumber | undefined\n\t\t][]) {\n\t\t\tif (typeof value === 'number') timeouts[key] = value;\n\t\t}\n\t}\n\tconst retries = Math.max(0, options.retries ?? 2);\n\tconst doFetch = options.fetch ?? globalThis.fetch;\n\tif (typeof doFetch !== 'function') {\n\t\tthrow new Error(\n\t\t\t'createClient: global `fetch` is unavailable. Pass `fetch` option (Node ≥20 or polyfill).'\n\t\t);\n\t}\n\tconst userAgent = `quizbase-client/${SDK_VERSION}${options.userAgent ? ` ${options.userAgent}` : ''}`;\n\n\tasync function request<T>(params: RequestParams): Promise<T> {\n\t\tconst timeoutMs = timeouts[params.endpoint] ?? defaultTimeout;\n\t\tconst url = buildUrl(baseUrl, params.path, params.query);\n\t\tconst requestId = generateRequestId();\n\t\tconst headers: Record<string, string> = {\n\t\t\tAccept: 'application/json',\n\t\t\tAuthorization: `Bearer ${options.apiKey}`,\n\t\t\t'X-Request-Id': requestId,\n\t\t\t'User-Agent': userAgent\n\t\t};\n\t\tlet body: string | undefined;\n\t\tif (params.body !== undefined) {\n\t\t\theaders['Content-Type'] = 'application/json';\n\t\t\tbody = JSON.stringify(params.body);\n\t\t}\n\n\t\tlet attempt = 0;\n\t\tlet lastError: unknown;\n\t\twhile (attempt <= retries) {\n\t\t\tconst startedAt = Date.now();\n\t\t\tconst controller = new AbortController();\n\t\t\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\t\t\tlet status = 0;\n\t\t\tlet response: Response | undefined;\n\t\t\tlet attemptError: Error | undefined;\n\t\t\ttry {\n\t\t\t\tresponse = await doFetch(url, {\n\t\t\t\t\tmethod: params.method,\n\t\t\t\t\theaders,\n\t\t\t\t\tbody,\n\t\t\t\t\tsignal: controller.signal\n\t\t\t\t});\n\t\t\t\tstatus = response.status;\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = (await response.json()) as T;\n\t\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\t\tmethod: params.method,\n\t\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trequestId: response.headers.get('x-request-id') ?? requestId,\n\t\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\t\tfinal: true\n\t\t\t\t\t});\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\t\t\t\tconst problem = await safeProblem(response);\n\t\t\t\tconst retryAfterHeader = response.headers.get('retry-after');\n\t\t\t\tconst retryAfter = retryAfterHeader ? parseRetryAfter(retryAfterHeader) : null;\n\t\t\t\tconst err = new QuizbaseError({\n\t\t\t\t\tstatus,\n\t\t\t\t\tproblem,\n\t\t\t\t\trequestId: response.headers.get('x-request-id') ?? requestId,\n\t\t\t\t\tretryAfter,\n\t\t\t\t\turl,\n\t\t\t\t\tmethod: params.method\n\t\t\t\t});\n\t\t\t\tattemptError = err;\n\t\t\t\tif (shouldRetry(status) && attempt < retries) {\n\t\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\t\tmethod: params.method,\n\t\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trequestId: err.requestId,\n\t\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\t\tfinal: false,\n\t\t\t\t\t\terror: err\n\t\t\t\t\t});\n\t\t\t\t\tawait sleep(backoffMs(attempt, retryAfter));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tlastError = err;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\tmethod: params.method,\n\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\turl,\n\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\tstatus,\n\t\t\t\t\trequestId: err.requestId,\n\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\tfinal: true,\n\t\t\t\t\terror: err\n\t\t\t\t});\n\t\t\t\tthrow err;\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof QuizbaseError) throw err;\n\t\t\t\tconst networkErr = err instanceof Error ? err : new Error(String(err));\n\t\t\t\tattemptError = networkErr;\n\t\t\t\tif (attempt < retries) {\n\t\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\t\tmethod: params.method,\n\t\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trequestId: null,\n\t\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\t\tfinal: false,\n\t\t\t\t\t\terror: networkErr\n\t\t\t\t\t});\n\t\t\t\t\tawait sleep(backoffMs(attempt, null));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tlastError = networkErr;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\tmethod: params.method,\n\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\turl,\n\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\tstatus,\n\t\t\t\t\trequestId: null,\n\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\tfinal: true,\n\t\t\t\t\terror: networkErr\n\t\t\t\t});\n\t\t\t\tthrow networkErr;\n\t\t\t} finally {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tvoid attemptError;\n\t\t\t\tvoid response;\n\t\t\t}\n\t\t}\n\t\tthrow lastError ?? new Error('quizbase-client: retry loop exhausted unexpectedly');\n\t}\n\n\treturn {\n\t\tquestions: (() => {\n\t\t\tconst list = (params?: QuestionsListParams) =>\n\t\t\t\trequest<Schemas['QuestionsListResponse']>({\n\t\t\t\t\tendpoint: 'questions.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/questions',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\trandom: (params) =>\n\t\t\t\t\trequest({\n\t\t\t\t\t\tendpoint: 'questions.random',\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\tpath: '/api/v1/questions/random',\n\t\t\t\t\t\tquery: params\n\t\t\t\t\t}),\n\t\t\t\tget: (id, params) =>\n\t\t\t\t\trequest({\n\t\t\t\t\t\tendpoint: 'questions.get',\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\tpath: `/api/v1/questions/${encodeURIComponent(id)}`,\n\t\t\t\t\t\tquery: params\n\t\t\t\t\t}),\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['questions'];\n\t\t})(),\n\t\tcategories: {\n\t\t\tlist: (params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'categories.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/categories',\n\t\t\t\t\tquery: params\n\t\t\t\t})\n\t\t},\n\t\tlanguages: {\n\t\t\tlist: () =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'languages.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/languages'\n\t\t\t\t})\n\t\t},\n\t\ttopics: (() => {\n\t\t\tconst list = (params?: TopicsListParams) =>\n\t\t\t\trequest<Schemas['TopicsListResponse']>({\n\t\t\t\t\tendpoint: 'topics.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/topics',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tget: (slug, params) =>\n\t\t\t\t\trequest({\n\t\t\t\t\t\tendpoint: 'topics.get',\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\tpath: `/api/v1/topics/${encodeURIComponent(slug)}`,\n\t\t\t\t\t\tquery: params\n\t\t\t\t\t}),\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['topics'];\n\t\t})(),\n\t\ttags: (() => {\n\t\t\tconst list = (params?: TagsListParams) =>\n\t\t\t\trequest<Schemas['TagsListResponse']>({\n\t\t\t\t\tendpoint: 'tags.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/tags',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['tags'];\n\t\t})(),\n\t\tsubcategories: (() => {\n\t\t\tconst list = (params?: SubcategoriesListParams) =>\n\t\t\t\trequest<Schemas['SubcategoriesListResponse']>({\n\t\t\t\t\tendpoint: 'subcategories.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/subcategories',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['subcategories'];\n\t\t})(),\n\t\tstats: {\n\t\t\tget: () =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'stats.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/stats'\n\t\t\t\t})\n\t\t},\n\t\tme: {\n\t\t\tget: () =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'me.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/me'\n\t\t\t\t})\n\t\t},\n\t\tusage: {\n\t\t\tget: (params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'usage.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/usage',\n\t\t\t\t\tquery: params\n\t\t\t\t})\n\t\t},\n\t\treport: {\n\t\t\tcreate: (body) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'report.create',\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\tpath: '/api/v1/report',\n\t\t\t\t\tbody\n\t\t\t\t})\n\t\t}\n\t};\n}\n\ninterface PaginatedParams {\n\tcursor?: string;\n\t[key: string]: unknown;\n}\n\ninterface PaginatedPage<T> {\n\tdata: T[];\n\t_links?: { next?: string; prev?: string };\n}\n\n/**\n * Extract the `cursor` query param from an absolute or relative `_links.next` URL.\n * Returns `null` when the URL is absent or malformed — caller treats that as end-of-stream.\n */\nfunction extractCursor(nextUrl: string | undefined): string | null {\n\tif (!nextUrl) return null;\n\ttry {\n\t\tconst url = nextUrl.startsWith('http')\n\t\t\t? new URL(nextUrl)\n\t\t\t: new URL(nextUrl, 'http://placeholder');\n\t\treturn url.searchParams.get('cursor');\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function* paginate<P extends PaginatedParams, R extends PaginatedPage<unknown>>(\n\tfetchPage: (params?: P) => Promise<R>,\n\tinitial: P | undefined\n): AsyncIterableIterator<R> {\n\tlet nextParams: P | undefined = initial;\n\twhile (true) {\n\t\tconst page = await fetchPage(nextParams);\n\t\tyield page;\n\t\tconst cursor = extractCursor(page._links?.next);\n\t\tif (!cursor) return;\n\t\tnextParams = { ...(nextParams ?? ({} as P)), cursor } as P;\n\t}\n}\n\nasync function* paginateItems<P extends PaginatedParams, T>(\n\tfetchPage: (params?: P) => Promise<PaginatedPage<T>>,\n\tinitial: P | undefined\n): AsyncIterableIterator<T> {\n\tfor await (const page of paginate(fetchPage, initial)) {\n\t\tfor (const item of page.data) yield item;\n\t}\n}\n\nfunction buildUrl(baseUrl: string, path: string, query: Record<string, unknown> | undefined): string {\n\tconst url = new URL(baseUrl + path);\n\tif (query) {\n\t\tfor (const [key, value] of Object.entries(query)) {\n\t\t\tif (value === undefined || value === null) continue;\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tfor (const item of value) {\n\t\t\t\t\tif (item !== undefined && item !== null) {\n\t\t\t\t\t\turl.searchParams.append(key, String(item));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\turl.searchParams.append(key, String(value));\n\t\t\t}\n\t\t}\n\t}\n\treturn url.toString();\n}\n\nfunction generateRequestId(): string {\n\tconst cryptoApi: Crypto | undefined = globalThis.crypto;\n\tif (cryptoApi && typeof cryptoApi.randomUUID === 'function') {\n\t\treturn cryptoApi.randomUUID();\n\t}\n\treturn `req-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;\n}\n\nasync function safeProblem(response: Response): Promise<ProblemDetails> {\n\ttry {\n\t\tconst data = (await response.json()) as ProblemDetails;\n\t\tif (data && typeof data === 'object') return data;\n\t} catch {\n\t\t// fall through\n\t}\n\treturn {\n\t\ttype: 'about:blank',\n\t\ttitle: response.statusText || `HTTP ${response.status}`,\n\t\tstatus: response.status,\n\t\tdetail: '',\n\t\tinstance: '',\n\t\tcode: 'unknown'\n\t} satisfies ProblemDetails;\n}\n\nfunction shouldRetry(status: number): boolean {\n\treturn status === 429 || (status >= 500 && status < 600);\n}\n\nfunction parseRetryAfter(value: string): number | null {\n\tconst seconds = Number.parseInt(value, 10);\n\tif (Number.isFinite(seconds) && seconds >= 0) return seconds;\n\tconst date = Date.parse(value);\n\tif (Number.isFinite(date)) {\n\t\tconst diff = Math.max(0, Math.ceil((date - Date.now()) / 1000));\n\t\treturn diff;\n\t}\n\treturn null;\n}\n\nfunction backoffMs(attempt: number, retryAfterSeconds: number | null): number {\n\tif (retryAfterSeconds !== null) return retryAfterSeconds * 1000;\n\tconst base = 250;\n\tconst exp = base * 2 ** attempt;\n\tconst jitter = Math.random() * 100;\n\treturn Math.min(exp + jitter, 5_000);\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function emit(\n\thook: OnRequestHook | undefined,\n\tevent: Parameters<OnRequestHook>[0]\n): Promise<void> {\n\tif (!hook) return;\n\ttry {\n\t\tawait hook(event);\n\t} catch {\n\t\t// telemetry must never break the caller\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { components } from './types.gen.js';\n\nexport type ProblemDetails = components['schemas']['ProblemDetails'];\n\nexport interface QuizbaseErrorOptions {\n\tstatus: number;\n\tproblem: ProblemDetails;\n\trequestId: string | null;\n\tretryAfter: number | null;\n\turl: string;\n\tmethod: string;\n}\n\n/**\n * Thrown for any non-2xx response from the QuizBase API.\n * Carries the parsed RFC 9457 Problem Details body, X-Request-Id for support,\n * and a parsed retry-after (seconds) for 429s.\n */\nexport class QuizbaseError extends Error {\n\treadonly status: number;\n\treadonly problem: ProblemDetails;\n\treadonly requestId: string | null;\n\treadonly retryAfter: number | null;\n\treadonly url: string;\n\treadonly method: string;\n\n\tconstructor(opts: QuizbaseErrorOptions) {\n\t\tconst title = opts.problem.title ?? `HTTP ${opts.status}`;\n\t\tconst detail = opts.problem.detail ? ` — ${opts.problem.detail}` : '';\n\t\tsuper(`QuizBase ${opts.method} ${opts.url} → ${opts.status} ${title}${detail}`);\n\t\tthis.name = 'QuizbaseError';\n\t\tthis.status = opts.status;\n\t\tthis.problem = opts.problem;\n\t\tthis.requestId = opts.requestId;\n\t\tthis.retryAfter = opts.retryAfter;\n\t\tthis.url = opts.url;\n\t\tthis.method = opts.method;\n\t}\n\n\tget type(): string | undefined {\n\t\treturn this.problem.type;\n\t}\n\n\tget isRateLimited(): boolean {\n\t\treturn this.status === 429;\n\t}\n\n\tget isAuthError(): boolean {\n\t\treturn this.status === 401 || this.status === 403;\n\t}\n\n\tget isServerError(): boolean {\n\t\treturn this.status >= 500;\n\t}\n}\n","import { QuizbaseError, type ProblemDetails } from './errors.js';\nimport type { OnRequestHook } from './telemetry.js';\nimport type { components, paths } from './types.gen.js';\n\ntype Schemas = components['schemas'];\n\n/** Logical endpoint identifiers — stable across path-param values, used for telemetry + per-endpoint timeouts. */\nexport type EndpointKey =\n\t| 'questions.list'\n\t| 'questions.random'\n\t| 'questions.get'\n\t| 'categories.list'\n\t| 'languages.list'\n\t| 'topics.list'\n\t| 'topics.get'\n\t| 'tags.list'\n\t| 'subcategories.list'\n\t| 'regions.list'\n\t| 'stats.get'\n\t| 'me.get'\n\t| 'usage.get'\n\t| 'report.create';\n\nconst DEFAULT_TIMEOUTS: Record<EndpointKey, number> = {\n\t'questions.list': 15_000,\n\t'questions.random': 10_000,\n\t'questions.get': 10_000,\n\t'categories.list': 10_000,\n\t'languages.list': 10_000,\n\t'topics.list': 15_000,\n\t'topics.get': 10_000,\n\t'tags.list': 15_000,\n\t'subcategories.list': 15_000,\n\t'regions.list': 10_000,\n\t'stats.get': 10_000,\n\t'me.get': 10_000,\n\t'usage.get': 10_000,\n\t'report.create': 15_000\n};\n\nexport interface ClientOptions {\n\t/** API key — `qb_pk_*` (publishable, CORS-safe for browsers) or `qb_sk_*` (secret, backend-only). Get one at https://quizbase.runriva.com/dashboard/keys. */\n\tapiKey: string;\n\t/** Override base URL. Defaults to `https://quizbase.runriva.com`. */\n\tbaseUrl?: string;\n\t/** Default request timeout in ms. Defaults to 30_000. Per-endpoint overrides take precedence. */\n\ttimeout?: number;\n\t/** Per-endpoint timeout overrides keyed by `EndpointKey`. */\n\ttimeouts?: Partial<Record<EndpointKey, number>>;\n\t/** Number of retries for 429 / 5xx / network errors. Defaults to 2 (3 total attempts). */\n\tretries?: number;\n\t/** Optional `fetch` implementation. Defaults to global `fetch`. */\n\tfetch?: typeof fetch;\n\t/** Telemetry hook fired after every HTTP attempt (including retries). */\n\tonRequest?: OnRequestHook;\n\t/** User-Agent suffix appended to the SDK identifier. */\n\tuserAgent?: string;\n}\n\ntype QuestionsListParams = paths['/api/v1/questions']['get']['parameters']['query'];\ntype TopicsListParams = paths['/api/v1/topics']['get']['parameters']['query'];\ntype TagsListParams = paths['/api/v1/tags']['get']['parameters']['query'];\ntype SubcategoriesListParams = paths['/api/v1/subcategories']['get']['parameters']['query'];\ntype RegionsListParams = paths['/api/v1/regions']['get']['parameters']['query'];\n\nexport interface QuizbaseClient {\n\tquestions: {\n\t\tlist(params?: QuestionsListParams): Promise<Schemas['QuestionsListResponse']>;\n\t\trandom(\n\t\t\tparams?: paths['/api/v1/questions/random']['get']['parameters']['query']\n\t\t): Promise<Schemas['QuestionsRandomResponse']>;\n\t\tget(\n\t\t\tid: string,\n\t\t\tparams?: paths['/api/v1/questions/{id}']['get']['parameters']['query']\n\t\t): Promise<Schemas['QuestionByIdResponse']>;\n\t\t/** Iterate every page of `/questions`, auto-following `_links.next`. */\n\t\tpages(params?: QuestionsListParams): AsyncIterableIterator<Schemas['QuestionsListResponse']>;\n\t\t/** Iterate every question across all pages. */\n\t\tlistAll(params?: QuestionsListParams): AsyncIterableIterator<Schemas['Question']>;\n\t};\n\tcategories: {\n\t\tlist(\n\t\t\tparams?: paths['/api/v1/categories']['get']['parameters']['query']\n\t\t): Promise<Schemas['CategoriesResponse']>;\n\t};\n\tlanguages: {\n\t\tlist(): Promise<Schemas['LanguagesResponse']>;\n\t};\n\ttopics: {\n\t\tlist(params?: TopicsListParams): Promise<Schemas['TopicsListResponse']>;\n\t\tget(\n\t\t\tslug: string,\n\t\t\tparams?: paths['/api/v1/topics/{slug}']['get']['parameters']['query']\n\t\t): Promise<Schemas['TopicDetailResponse']>;\n\t\t/** Iterate every page of `/topics`, auto-following `_links.next`. */\n\t\tpages(params?: TopicsListParams): AsyncIterableIterator<Schemas['TopicsListResponse']>;\n\t\t/** Iterate every topic across all pages. */\n\t\tlistAll(params?: TopicsListParams): AsyncIterableIterator<Schemas['TopicEntry']>;\n\t};\n\ttags: {\n\t\tlist(params?: TagsListParams): Promise<Schemas['TagsListResponse']>;\n\t\t/** Iterate every page of `/tags`, auto-following `_links.next`. */\n\t\tpages(params?: TagsListParams): AsyncIterableIterator<Schemas['TagsListResponse']>;\n\t\t/** Iterate every tag across all pages. */\n\t\tlistAll(params?: TagsListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;\n\t};\n\tsubcategories: {\n\t\tlist(params?: SubcategoriesListParams): Promise<Schemas['SubcategoriesListResponse']>;\n\t\t/** Iterate every page of `/subcategories`, auto-following `_links.next`. */\n\t\tpages(\n\t\t\tparams?: SubcategoriesListParams\n\t\t): AsyncIterableIterator<Schemas['SubcategoriesListResponse']>;\n\t\t/** Iterate every subcategory across all pages. */\n\t\tlistAll(params?: SubcategoriesListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;\n\t};\n\t/**\n\t * Region codes — **cultural affinity**, not geography. A question is tagged with\n\t * a region if residents of that country or members of that cultural/religious\n\t * group are statistically more likely to know the answer. Lowercase ISO 3166-1\n\t * alpha-2 (`us`, `pl`, `gb`) plus cultural codes (`jewish`, `christian-catholic`,\n\t * `islam`). Pair with `questions.random({ regions: [...] })` to fetch matching\n\t * questions.\n\t */\n\tregions: {\n\t\tlist(params?: RegionsListParams): Promise<Schemas['RegionsListResponse']>;\n\t\t/** Iterate every page of `/regions`, auto-following `_links.next`. */\n\t\tpages(params?: RegionsListParams): AsyncIterableIterator<Schemas['RegionsListResponse']>;\n\t\t/** Iterate every region across all pages. */\n\t\tlistAll(params?: RegionsListParams): AsyncIterableIterator<Schemas['RegionEntry']>;\n\t};\n\tstats: {\n\t\tget(): Promise<Schemas['StatsResponse']>;\n\t};\n\tme: {\n\t\tget(): Promise<Schemas['MeResponse']>;\n\t};\n\tusage: {\n\t\tget(\n\t\t\tparams?: paths['/api/v1/usage']['get']['parameters']['query']\n\t\t): Promise<Schemas['UsageResponse']>;\n\t};\n\treport: {\n\t\tcreate(\n\t\t\tbody: NonNullable<\n\t\t\t\tpaths['/api/v1/report']['post']['requestBody']\n\t\t\t>['content']['application/json']\n\t\t): Promise<Schemas['ReportAcceptedResponse']>;\n\t};\n}\n\nconst DEFAULT_BASE_URL = 'https://quizbase.runriva.com';\nconst SDK_VERSION = '0.1.0';\n\ninterface RequestParams {\n\tendpoint: EndpointKey;\n\tmethod: 'GET' | 'POST';\n\tpath: string;\n\tquery?: Record<string, unknown> | undefined;\n\tbody?: unknown;\n}\n\nexport function createClient(options: ClientOptions): QuizbaseClient {\n\tif (!options.apiKey) {\n\t\tthrow new Error('createClient: `apiKey` is required.');\n\t}\n\tconst baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, '');\n\tconst defaultTimeout = options.timeout ?? 30_000;\n\tconst timeouts: Record<EndpointKey, number> = { ...DEFAULT_TIMEOUTS };\n\tif (options.timeouts) {\n\t\tfor (const [key, value] of Object.entries(options.timeouts) as [\n\t\t\tEndpointKey,\n\t\t\tnumber | undefined\n\t\t][]) {\n\t\t\tif (typeof value === 'number') timeouts[key] = value;\n\t\t}\n\t}\n\tconst retries = Math.max(0, options.retries ?? 2);\n\tconst doFetch = options.fetch ?? globalThis.fetch;\n\tif (typeof doFetch !== 'function') {\n\t\tthrow new Error(\n\t\t\t'createClient: global `fetch` is unavailable. Pass `fetch` option (Node ≥20 or polyfill).'\n\t\t);\n\t}\n\tconst userAgent = `quizbase-client/${SDK_VERSION}${options.userAgent ? ` ${options.userAgent}` : ''}`;\n\n\tasync function request<T>(params: RequestParams): Promise<T> {\n\t\tconst timeoutMs = timeouts[params.endpoint] ?? defaultTimeout;\n\t\tconst url = buildUrl(baseUrl, params.path, params.query);\n\t\tconst requestId = generateRequestId();\n\t\tconst headers: Record<string, string> = {\n\t\t\tAccept: 'application/json',\n\t\t\tAuthorization: `Bearer ${options.apiKey}`,\n\t\t\t'X-Request-Id': requestId,\n\t\t\t'User-Agent': userAgent\n\t\t};\n\t\tlet body: string | undefined;\n\t\tif (params.body !== undefined) {\n\t\t\theaders['Content-Type'] = 'application/json';\n\t\t\tbody = JSON.stringify(params.body);\n\t\t}\n\n\t\tlet attempt = 0;\n\t\tlet lastError: unknown;\n\t\twhile (attempt <= retries) {\n\t\t\tconst startedAt = Date.now();\n\t\t\tconst controller = new AbortController();\n\t\t\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\t\t\tlet status = 0;\n\t\t\tlet response: Response | undefined;\n\t\t\tlet attemptError: Error | undefined;\n\t\t\ttry {\n\t\t\t\tresponse = await doFetch(url, {\n\t\t\t\t\tmethod: params.method,\n\t\t\t\t\theaders,\n\t\t\t\t\tbody,\n\t\t\t\t\tsignal: controller.signal\n\t\t\t\t});\n\t\t\t\tstatus = response.status;\n\t\t\t\tif (response.ok) {\n\t\t\t\t\tconst data = (await response.json()) as T;\n\t\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\t\tmethod: params.method,\n\t\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trequestId: response.headers.get('x-request-id') ?? requestId,\n\t\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\t\tfinal: true\n\t\t\t\t\t});\n\t\t\t\t\treturn data;\n\t\t\t\t}\n\t\t\t\tconst problem = await safeProblem(response);\n\t\t\t\tconst retryAfterHeader = response.headers.get('retry-after');\n\t\t\t\tconst retryAfter = retryAfterHeader ? parseRetryAfter(retryAfterHeader) : null;\n\t\t\t\tconst err = new QuizbaseError({\n\t\t\t\t\tstatus,\n\t\t\t\t\tproblem,\n\t\t\t\t\trequestId: response.headers.get('x-request-id') ?? requestId,\n\t\t\t\t\tretryAfter,\n\t\t\t\t\turl,\n\t\t\t\t\tmethod: params.method\n\t\t\t\t});\n\t\t\t\tattemptError = err;\n\t\t\t\tif (shouldRetry(status) && attempt < retries) {\n\t\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\t\tmethod: params.method,\n\t\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trequestId: err.requestId,\n\t\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\t\tfinal: false,\n\t\t\t\t\t\terror: err\n\t\t\t\t\t});\n\t\t\t\t\tawait sleep(backoffMs(attempt, retryAfter));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tlastError = err;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\tmethod: params.method,\n\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\turl,\n\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\tstatus,\n\t\t\t\t\trequestId: err.requestId,\n\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\tfinal: true,\n\t\t\t\t\terror: err\n\t\t\t\t});\n\t\t\t\tthrow err;\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof QuizbaseError) throw err;\n\t\t\t\tconst networkErr = err instanceof Error ? err : new Error(String(err));\n\t\t\t\tattemptError = networkErr;\n\t\t\t\tif (attempt < retries) {\n\t\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\t\tmethod: params.method,\n\t\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\t\turl,\n\t\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\t\tstatus,\n\t\t\t\t\t\trequestId: null,\n\t\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\t\tfinal: false,\n\t\t\t\t\t\terror: networkErr\n\t\t\t\t\t});\n\t\t\t\t\tawait sleep(backoffMs(attempt, null));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tlastError = networkErr;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tawait emit(options.onRequest, {\n\t\t\t\t\tmethod: params.method,\n\t\t\t\t\tendpoint: params.endpoint,\n\t\t\t\t\turl,\n\t\t\t\t\tduration: Date.now() - startedAt,\n\t\t\t\t\tstatus,\n\t\t\t\t\trequestId: null,\n\t\t\t\t\tretryCount: attempt,\n\t\t\t\t\tfinal: true,\n\t\t\t\t\terror: networkErr\n\t\t\t\t});\n\t\t\t\tthrow networkErr;\n\t\t\t} finally {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\tvoid attemptError;\n\t\t\t\tvoid response;\n\t\t\t}\n\t\t}\n\t\tthrow lastError ?? new Error('quizbase-client: retry loop exhausted unexpectedly');\n\t}\n\n\treturn {\n\t\tquestions: (() => {\n\t\t\tconst list = (params?: QuestionsListParams) =>\n\t\t\t\trequest<Schemas['QuestionsListResponse']>({\n\t\t\t\t\tendpoint: 'questions.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/questions',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\trandom: (params) =>\n\t\t\t\t\trequest({\n\t\t\t\t\t\tendpoint: 'questions.random',\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\tpath: '/api/v1/questions/random',\n\t\t\t\t\t\tquery: params\n\t\t\t\t\t}),\n\t\t\t\tget: (id, params) =>\n\t\t\t\t\trequest({\n\t\t\t\t\t\tendpoint: 'questions.get',\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\tpath: `/api/v1/questions/${encodeURIComponent(id)}`,\n\t\t\t\t\t\tquery: params\n\t\t\t\t\t}),\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['questions'];\n\t\t})(),\n\t\tcategories: {\n\t\t\tlist: (params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'categories.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/categories',\n\t\t\t\t\tquery: params\n\t\t\t\t})\n\t\t},\n\t\tlanguages: {\n\t\t\tlist: () =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'languages.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/languages'\n\t\t\t\t})\n\t\t},\n\t\ttopics: (() => {\n\t\t\tconst list = (params?: TopicsListParams) =>\n\t\t\t\trequest<Schemas['TopicsListResponse']>({\n\t\t\t\t\tendpoint: 'topics.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/topics',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tget: (slug, params) =>\n\t\t\t\t\trequest({\n\t\t\t\t\t\tendpoint: 'topics.get',\n\t\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\t\tpath: `/api/v1/topics/${encodeURIComponent(slug)}`,\n\t\t\t\t\t\tquery: params\n\t\t\t\t\t}),\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['topics'];\n\t\t})(),\n\t\ttags: (() => {\n\t\t\tconst list = (params?: TagsListParams) =>\n\t\t\t\trequest<Schemas['TagsListResponse']>({\n\t\t\t\t\tendpoint: 'tags.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/tags',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['tags'];\n\t\t})(),\n\t\tsubcategories: (() => {\n\t\t\tconst list = (params?: SubcategoriesListParams) =>\n\t\t\t\trequest<Schemas['SubcategoriesListResponse']>({\n\t\t\t\t\tendpoint: 'subcategories.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/subcategories',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['subcategories'];\n\t\t})(),\n\t\tregions: (() => {\n\t\t\tconst list = (params?: RegionsListParams) =>\n\t\t\t\trequest<Schemas['RegionsListResponse']>({\n\t\t\t\t\tendpoint: 'regions.list',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/regions',\n\t\t\t\t\tquery: params\n\t\t\t\t});\n\t\t\treturn {\n\t\t\t\tlist,\n\t\t\t\tpages: (params) => paginate(list, params),\n\t\t\t\tlistAll: (params) => paginateItems(list, params)\n\t\t\t} satisfies QuizbaseClient['regions'];\n\t\t})(),\n\t\tstats: {\n\t\t\tget: () =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'stats.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/stats'\n\t\t\t\t})\n\t\t},\n\t\tme: {\n\t\t\tget: () =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'me.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/me'\n\t\t\t\t})\n\t\t},\n\t\tusage: {\n\t\t\tget: (params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'usage.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/usage',\n\t\t\t\t\tquery: params\n\t\t\t\t})\n\t\t},\n\t\treport: {\n\t\t\tcreate: (body) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'report.create',\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\tpath: '/api/v1/report',\n\t\t\t\t\tbody\n\t\t\t\t})\n\t\t}\n\t};\n}\n\ninterface PaginatedParams {\n\tcursor?: string;\n\t[key: string]: unknown;\n}\n\ninterface PaginatedPage<T> {\n\tdata: T[];\n\t_links?: { next?: string; prev?: string };\n}\n\n/**\n * Extract the `cursor` query param from an absolute or relative `_links.next` URL.\n * Returns `null` when the URL is absent or malformed — caller treats that as end-of-stream.\n */\nfunction extractCursor(nextUrl: string | undefined): string | null {\n\tif (!nextUrl) return null;\n\ttry {\n\t\tconst url = nextUrl.startsWith('http')\n\t\t\t? new URL(nextUrl)\n\t\t\t: new URL(nextUrl, 'http://placeholder');\n\t\treturn url.searchParams.get('cursor');\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function* paginate<P extends PaginatedParams, R extends PaginatedPage<unknown>>(\n\tfetchPage: (params?: P) => Promise<R>,\n\tinitial: P | undefined\n): AsyncIterableIterator<R> {\n\tlet nextParams: P | undefined = initial;\n\twhile (true) {\n\t\tconst page = await fetchPage(nextParams);\n\t\tyield page;\n\t\tconst cursor = extractCursor(page._links?.next);\n\t\tif (!cursor) return;\n\t\tnextParams = { ...(nextParams ?? ({} as P)), cursor } as P;\n\t}\n}\n\nasync function* paginateItems<P extends PaginatedParams, T>(\n\tfetchPage: (params?: P) => Promise<PaginatedPage<T>>,\n\tinitial: P | undefined\n): AsyncIterableIterator<T> {\n\tfor await (const page of paginate(fetchPage, initial)) {\n\t\tfor (const item of page.data) yield item;\n\t}\n}\n\nfunction buildUrl(baseUrl: string, path: string, query: Record<string, unknown> | undefined): string {\n\tconst url = new URL(baseUrl + path);\n\tif (query) {\n\t\tfor (const [key, value] of Object.entries(query)) {\n\t\t\tif (value === undefined || value === null) continue;\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\tfor (const item of value) {\n\t\t\t\t\tif (item !== undefined && item !== null) {\n\t\t\t\t\t\turl.searchParams.append(key, String(item));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\turl.searchParams.append(key, String(value));\n\t\t\t}\n\t\t}\n\t}\n\treturn url.toString();\n}\n\nfunction generateRequestId(): string {\n\tconst cryptoApi: Crypto | undefined = globalThis.crypto;\n\tif (cryptoApi && typeof cryptoApi.randomUUID === 'function') {\n\t\treturn cryptoApi.randomUUID();\n\t}\n\treturn `req-${Math.random().toString(36).slice(2)}-${Date.now().toString(36)}`;\n}\n\nasync function safeProblem(response: Response): Promise<ProblemDetails> {\n\ttry {\n\t\tconst data = (await response.json()) as ProblemDetails;\n\t\tif (data && typeof data === 'object') return data;\n\t} catch {\n\t\t// fall through\n\t}\n\treturn {\n\t\ttype: 'about:blank',\n\t\ttitle: response.statusText || `HTTP ${response.status}`,\n\t\tstatus: response.status,\n\t\tdetail: '',\n\t\tinstance: '',\n\t\tcode: 'unknown'\n\t} satisfies ProblemDetails;\n}\n\nfunction shouldRetry(status: number): boolean {\n\treturn status === 429 || (status >= 500 && status < 600);\n}\n\nfunction parseRetryAfter(value: string): number | null {\n\tconst seconds = Number.parseInt(value, 10);\n\tif (Number.isFinite(seconds) && seconds >= 0) return seconds;\n\tconst date = Date.parse(value);\n\tif (Number.isFinite(date)) {\n\t\tconst diff = Math.max(0, Math.ceil((date - Date.now()) / 1000));\n\t\treturn diff;\n\t}\n\treturn null;\n}\n\nfunction backoffMs(attempt: number, retryAfterSeconds: number | null): number {\n\tif (retryAfterSeconds !== null) return retryAfterSeconds * 1000;\n\tconst base = 250;\n\tconst exp = base * 2 ** attempt;\n\tconst jitter = Math.random() * 100;\n\treturn Math.min(exp + jitter, 5_000);\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function emit(\n\thook: OnRequestHook | undefined,\n\tevent: Parameters<OnRequestHook>[0]\n): Promise<void> {\n\tif (!hook) return;\n\ttry {\n\t\tawait hook(event);\n\t} catch {\n\t\t// telemetry must never break the caller\n\t}\n}\n"]}
|
package/package.json
CHANGED