@quizbase/client 0.5.0 → 0.7.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 CHANGED
@@ -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 `subcategories` 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
+ `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",
@@ -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;
@@ -850,7 +932,7 @@ interface paths {
850
932
  /** @description ISO 8601 datetime — only questions modified after this point. */
851
933
  updated_since?: string;
852
934
  category?: string;
853
- difficulty?: "easy" | "medium" | "hard";
935
+ difficulty?: "trivial" | "easy" | "medium" | "hard" | "expert";
854
936
  type?: "multiple" | "boolean" | "text_input";
855
937
  tags?: string;
856
938
  tags_any?: string;
@@ -859,6 +941,7 @@ interface paths {
859
941
  subcategory?: string;
860
942
  /** @description Quality preset: "high" (default) skips needs_review=true; "all" includes everything */
861
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). */
862
945
  regions?: string;
863
946
  source?: "arc" | "creak" | "entityq" | "kqa-pro" | "mintaka" | "mkqa" | "nq-open" | "opentdb" | "opentriviaqa" | "qasc" | "quizbase" | "webq";
864
947
  license?: "CC-BY-SA-4.0" | "CC-BY-SA-3.0" | "CC-BY-4.0" | "MIT" | "proprietary";
@@ -952,7 +1035,7 @@ interface paths {
952
1035
  /** @description Display language for category names + slug labels (subcategories/tags) */
953
1036
  lang?: "en" | "pl";
954
1037
  category?: string;
955
- difficulty?: "easy" | "medium" | "hard";
1038
+ difficulty?: "trivial" | "easy" | "medium" | "hard" | "expert";
956
1039
  type?: "multiple" | "boolean" | "text_input";
957
1040
  tags?: string;
958
1041
  tags_any?: string;
@@ -961,6 +1044,7 @@ interface paths {
961
1044
  subcategory?: string;
962
1045
  /** @description Quality preset: "high" (default) skips needs_review=true; "all" includes everything */
963
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). */
964
1048
  regions?: string;
965
1049
  source?: "arc" | "creak" | "entityq" | "kqa-pro" | "mintaka" | "mkqa" | "nq-open" | "opentdb" | "opentriviaqa" | "qasc" | "quizbase" | "webq";
966
1050
  license?: "CC-BY-SA-4.0" | "CC-BY-SA-3.0" | "CC-BY-4.0" | "MIT" | "proprietary";
@@ -1200,7 +1284,7 @@ interface components {
1200
1284
  * @example easy
1201
1285
  * @enum {string|null}
1202
1286
  */
1203
- difficulty: "easy" | "medium" | "hard" | null;
1287
+ difficulty: "trivial" | "easy" | "medium" | "hard" | "expert" | null;
1204
1288
  /** @example pl */
1205
1289
  language: string;
1206
1290
  category: components["schemas"]["CategoryRef"];
@@ -1209,6 +1293,7 @@ interface components {
1209
1293
  /** @description Localized `{slug, label}` per `?lang=`. */
1210
1294
  tags: components["schemas"]["SlugLabel"][];
1211
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`.
1212
1297
  * @example [
1213
1298
  * "pl"
1214
1299
  * ]
@@ -1361,6 +1446,8 @@ interface components {
1361
1446
  byDifficulty: components["schemas"]["StatsByDifficulty"];
1362
1447
  byTopic: components["schemas"]["StatsTopicEntry"][];
1363
1448
  byTag: components["schemas"]["StatsTagEntry"][];
1449
+ topRegions: components["schemas"]["StatsTopRegions"];
1450
+ byQualityHigh: components["schemas"]["StatsByQualityHigh"];
1364
1451
  meta: components["schemas"]["MetaStats"];
1365
1452
  };
1366
1453
  StatsCategoryEntry: {
@@ -1371,15 +1458,19 @@ interface components {
1371
1458
  /** @example 146384 */
1372
1459
  count: number;
1373
1460
  };
1374
- /** @description Difficulty distribution. Pre-launch zero-filled snapshot pipeline gap. */
1461
+ /** @description Difficulty distribution across 5 LLM-calibrated levels (Plan 121) plus `unrated` for pre-rescore records. */
1375
1462
  StatsByDifficulty: {
1376
- /** @example 0 */
1463
+ /** @example 12480 */
1464
+ trivial: number;
1465
+ /** @example 73215 */
1377
1466
  easy: number;
1378
- /** @example 0 */
1467
+ /** @example 580212 */
1379
1468
  medium: number;
1380
- /** @example 0 */
1469
+ /** @example 142098 */
1381
1470
  hard: number;
1382
- /** @example 0 */
1471
+ /** @example 12476 */
1472
+ expert: number;
1473
+ /** @example 473528 */
1383
1474
  unrated: number;
1384
1475
  };
1385
1476
  StatsTopicEntry: {
@@ -1398,6 +1489,22 @@ interface components {
1398
1489
  /** @example 15281 */
1399
1490
  count: number;
1400
1491
  };
1492
+ /** @description Top regions by cultural affinity (Plan 120). Code = ISO 3166-1 alpha-2 or cultural identifier; not a geographic filter — indicates "person from this region has higher statistical chance to answer correctly". */
1493
+ StatsTopRegions: {
1494
+ /** @example us */
1495
+ code: string;
1496
+ /** @example United States */
1497
+ label: string;
1498
+ /** @example 309090 */
1499
+ count: number;
1500
+ }[];
1501
+ /** @description Counts of records eligible for `?quality=high` filter (Plan 121: needs_review=false AND content_grade != 0) per language. */
1502
+ StatsByQualityHigh: {
1503
+ /** @example 329266 */
1504
+ en: number;
1505
+ /** @example 491297 */
1506
+ pl: number;
1507
+ };
1401
1508
  MetaStats: {
1402
1509
  /** @example 2026-05-05T16:32:09.001Z */
1403
1510
  generatedAt: string;
@@ -1518,7 +1625,7 @@ interface components {
1518
1625
  /** @enum {string} */
1519
1626
  type: "multiple" | "boolean" | "text_input";
1520
1627
  /** @enum {string|null} */
1521
- difficulty: "easy" | "medium" | "hard" | null;
1628
+ difficulty: "trivial" | "easy" | "medium" | "hard" | "expert" | null;
1522
1629
  language: string;
1523
1630
  };
1524
1631
  MetaBasic: {
@@ -1569,6 +1676,41 @@ interface components {
1569
1676
  meta: components["schemas"]["MetaTagsList"];
1570
1677
  _links?: components["schemas"]["Links"];
1571
1678
  };
1679
+ RegionsListResponse: {
1680
+ data: components["schemas"]["RegionEntry"][];
1681
+ meta: components["schemas"]["MetaRegionsList"];
1682
+ _links?: components["schemas"]["Links"];
1683
+ };
1684
+ RegionEntry: {
1685
+ /** @example pl */
1686
+ code: string;
1687
+ /**
1688
+ * @example country
1689
+ * @enum {string}
1690
+ */
1691
+ kind: "country" | "cultural";
1692
+ /** @example Poland */
1693
+ label: string;
1694
+ /** @example 7388 */
1695
+ count: number;
1696
+ };
1697
+ MetaRegionsList: {
1698
+ /** @example 100 */
1699
+ count: number;
1700
+ /** @example 152 */
1701
+ total: number;
1702
+ /**
1703
+ * @description Language of the response content.
1704
+ * @example en
1705
+ */
1706
+ language: string;
1707
+ /**
1708
+ * Format: uuid
1709
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1710
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1711
+ */
1712
+ requestId: string;
1713
+ };
1572
1714
  ReportAcceptedResponse: {
1573
1715
  /** @enum {boolean} */
1574
1716
  received: true;
@@ -1803,7 +1945,7 @@ interface components {
1803
1945
 
1804
1946
  type Schemas = components['schemas'];
1805
1947
  /** Logical endpoint identifiers — stable across path-param values, used for telemetry + per-endpoint timeouts. */
1806
- 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';
1948
+ 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';
1807
1949
  interface ClientOptions {
1808
1950
  /** API key — `qb_pk_*` (publishable, CORS-safe for browsers) or `qb_sk_*` (secret, backend-only). Get one at https://quizbase.runriva.com/dashboard/keys. */
1809
1951
  apiKey: string;
@@ -1826,6 +1968,7 @@ type QuestionsListParams = paths['/api/v1/questions']['get']['parameters']['quer
1826
1968
  type TopicsListParams = paths['/api/v1/topics']['get']['parameters']['query'];
1827
1969
  type TagsListParams = paths['/api/v1/tags']['get']['parameters']['query'];
1828
1970
  type SubcategoriesListParams = paths['/api/v1/subcategories']['get']['parameters']['query'];
1971
+ type RegionsListParams = paths['/api/v1/regions']['get']['parameters']['query'];
1829
1972
  interface QuizbaseClient {
1830
1973
  questions: {
1831
1974
  list(params?: QuestionsListParams): Promise<Schemas['QuestionsListResponse']>;
@@ -1864,6 +2007,21 @@ interface QuizbaseClient {
1864
2007
  /** Iterate every subcategory across all pages. */
1865
2008
  listAll(params?: SubcategoriesListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;
1866
2009
  };
2010
+ /**
2011
+ * Region codes — **cultural affinity**, not geography. A question is tagged with
2012
+ * a region if residents of that country or members of that cultural/religious
2013
+ * group are statistically more likely to know the answer. Lowercase ISO 3166-1
2014
+ * alpha-2 (`us`, `pl`, `gb`) plus cultural codes (`jewish`, `christian-catholic`,
2015
+ * `islam`). Pair with `questions.random({ regions: [...] })` to fetch matching
2016
+ * questions.
2017
+ */
2018
+ regions: {
2019
+ list(params?: RegionsListParams): Promise<Schemas['RegionsListResponse']>;
2020
+ /** Iterate every page of `/regions`, auto-following `_links.next`. */
2021
+ pages(params?: RegionsListParams): AsyncIterableIterator<Schemas['RegionsListResponse']>;
2022
+ /** Iterate every region across all pages. */
2023
+ listAll(params?: RegionsListParams): AsyncIterableIterator<Schemas['RegionEntry']>;
2024
+ };
1867
2025
  stats: {
1868
2026
  get(): Promise<Schemas['StatsResponse']>;
1869
2027
  };
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;
@@ -850,7 +932,7 @@ interface paths {
850
932
  /** @description ISO 8601 datetime — only questions modified after this point. */
851
933
  updated_since?: string;
852
934
  category?: string;
853
- difficulty?: "easy" | "medium" | "hard";
935
+ difficulty?: "trivial" | "easy" | "medium" | "hard" | "expert";
854
936
  type?: "multiple" | "boolean" | "text_input";
855
937
  tags?: string;
856
938
  tags_any?: string;
@@ -859,6 +941,7 @@ interface paths {
859
941
  subcategory?: string;
860
942
  /** @description Quality preset: "high" (default) skips needs_review=true; "all" includes everything */
861
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). */
862
945
  regions?: string;
863
946
  source?: "arc" | "creak" | "entityq" | "kqa-pro" | "mintaka" | "mkqa" | "nq-open" | "opentdb" | "opentriviaqa" | "qasc" | "quizbase" | "webq";
864
947
  license?: "CC-BY-SA-4.0" | "CC-BY-SA-3.0" | "CC-BY-4.0" | "MIT" | "proprietary";
@@ -952,7 +1035,7 @@ interface paths {
952
1035
  /** @description Display language for category names + slug labels (subcategories/tags) */
953
1036
  lang?: "en" | "pl";
954
1037
  category?: string;
955
- difficulty?: "easy" | "medium" | "hard";
1038
+ difficulty?: "trivial" | "easy" | "medium" | "hard" | "expert";
956
1039
  type?: "multiple" | "boolean" | "text_input";
957
1040
  tags?: string;
958
1041
  tags_any?: string;
@@ -961,6 +1044,7 @@ interface paths {
961
1044
  subcategory?: string;
962
1045
  /** @description Quality preset: "high" (default) skips needs_review=true; "all" includes everything */
963
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). */
964
1048
  regions?: string;
965
1049
  source?: "arc" | "creak" | "entityq" | "kqa-pro" | "mintaka" | "mkqa" | "nq-open" | "opentdb" | "opentriviaqa" | "qasc" | "quizbase" | "webq";
966
1050
  license?: "CC-BY-SA-4.0" | "CC-BY-SA-3.0" | "CC-BY-4.0" | "MIT" | "proprietary";
@@ -1200,7 +1284,7 @@ interface components {
1200
1284
  * @example easy
1201
1285
  * @enum {string|null}
1202
1286
  */
1203
- difficulty: "easy" | "medium" | "hard" | null;
1287
+ difficulty: "trivial" | "easy" | "medium" | "hard" | "expert" | null;
1204
1288
  /** @example pl */
1205
1289
  language: string;
1206
1290
  category: components["schemas"]["CategoryRef"];
@@ -1209,6 +1293,7 @@ interface components {
1209
1293
  /** @description Localized `{slug, label}` per `?lang=`. */
1210
1294
  tags: components["schemas"]["SlugLabel"][];
1211
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`.
1212
1297
  * @example [
1213
1298
  * "pl"
1214
1299
  * ]
@@ -1361,6 +1446,8 @@ interface components {
1361
1446
  byDifficulty: components["schemas"]["StatsByDifficulty"];
1362
1447
  byTopic: components["schemas"]["StatsTopicEntry"][];
1363
1448
  byTag: components["schemas"]["StatsTagEntry"][];
1449
+ topRegions: components["schemas"]["StatsTopRegions"];
1450
+ byQualityHigh: components["schemas"]["StatsByQualityHigh"];
1364
1451
  meta: components["schemas"]["MetaStats"];
1365
1452
  };
1366
1453
  StatsCategoryEntry: {
@@ -1371,15 +1458,19 @@ interface components {
1371
1458
  /** @example 146384 */
1372
1459
  count: number;
1373
1460
  };
1374
- /** @description Difficulty distribution. Pre-launch zero-filled snapshot pipeline gap. */
1461
+ /** @description Difficulty distribution across 5 LLM-calibrated levels (Plan 121) plus `unrated` for pre-rescore records. */
1375
1462
  StatsByDifficulty: {
1376
- /** @example 0 */
1463
+ /** @example 12480 */
1464
+ trivial: number;
1465
+ /** @example 73215 */
1377
1466
  easy: number;
1378
- /** @example 0 */
1467
+ /** @example 580212 */
1379
1468
  medium: number;
1380
- /** @example 0 */
1469
+ /** @example 142098 */
1381
1470
  hard: number;
1382
- /** @example 0 */
1471
+ /** @example 12476 */
1472
+ expert: number;
1473
+ /** @example 473528 */
1383
1474
  unrated: number;
1384
1475
  };
1385
1476
  StatsTopicEntry: {
@@ -1398,6 +1489,22 @@ interface components {
1398
1489
  /** @example 15281 */
1399
1490
  count: number;
1400
1491
  };
1492
+ /** @description Top regions by cultural affinity (Plan 120). Code = ISO 3166-1 alpha-2 or cultural identifier; not a geographic filter — indicates "person from this region has higher statistical chance to answer correctly". */
1493
+ StatsTopRegions: {
1494
+ /** @example us */
1495
+ code: string;
1496
+ /** @example United States */
1497
+ label: string;
1498
+ /** @example 309090 */
1499
+ count: number;
1500
+ }[];
1501
+ /** @description Counts of records eligible for `?quality=high` filter (Plan 121: needs_review=false AND content_grade != 0) per language. */
1502
+ StatsByQualityHigh: {
1503
+ /** @example 329266 */
1504
+ en: number;
1505
+ /** @example 491297 */
1506
+ pl: number;
1507
+ };
1401
1508
  MetaStats: {
1402
1509
  /** @example 2026-05-05T16:32:09.001Z */
1403
1510
  generatedAt: string;
@@ -1518,7 +1625,7 @@ interface components {
1518
1625
  /** @enum {string} */
1519
1626
  type: "multiple" | "boolean" | "text_input";
1520
1627
  /** @enum {string|null} */
1521
- difficulty: "easy" | "medium" | "hard" | null;
1628
+ difficulty: "trivial" | "easy" | "medium" | "hard" | "expert" | null;
1522
1629
  language: string;
1523
1630
  };
1524
1631
  MetaBasic: {
@@ -1569,6 +1676,41 @@ interface components {
1569
1676
  meta: components["schemas"]["MetaTagsList"];
1570
1677
  _links?: components["schemas"]["Links"];
1571
1678
  };
1679
+ RegionsListResponse: {
1680
+ data: components["schemas"]["RegionEntry"][];
1681
+ meta: components["schemas"]["MetaRegionsList"];
1682
+ _links?: components["schemas"]["Links"];
1683
+ };
1684
+ RegionEntry: {
1685
+ /** @example pl */
1686
+ code: string;
1687
+ /**
1688
+ * @example country
1689
+ * @enum {string}
1690
+ */
1691
+ kind: "country" | "cultural";
1692
+ /** @example Poland */
1693
+ label: string;
1694
+ /** @example 7388 */
1695
+ count: number;
1696
+ };
1697
+ MetaRegionsList: {
1698
+ /** @example 100 */
1699
+ count: number;
1700
+ /** @example 152 */
1701
+ total: number;
1702
+ /**
1703
+ * @description Language of the response content.
1704
+ * @example en
1705
+ */
1706
+ language: string;
1707
+ /**
1708
+ * Format: uuid
1709
+ * @description Unique request identifier (also in `X-Request-Id` response header).
1710
+ * @example 127fc6d1-d6fb-4771-b2a7-211a08749e5b
1711
+ */
1712
+ requestId: string;
1713
+ };
1572
1714
  ReportAcceptedResponse: {
1573
1715
  /** @enum {boolean} */
1574
1716
  received: true;
@@ -1803,7 +1945,7 @@ interface components {
1803
1945
 
1804
1946
  type Schemas = components['schemas'];
1805
1947
  /** Logical endpoint identifiers — stable across path-param values, used for telemetry + per-endpoint timeouts. */
1806
- 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';
1948
+ 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';
1807
1949
  interface ClientOptions {
1808
1950
  /** API key — `qb_pk_*` (publishable, CORS-safe for browsers) or `qb_sk_*` (secret, backend-only). Get one at https://quizbase.runriva.com/dashboard/keys. */
1809
1951
  apiKey: string;
@@ -1826,6 +1968,7 @@ type QuestionsListParams = paths['/api/v1/questions']['get']['parameters']['quer
1826
1968
  type TopicsListParams = paths['/api/v1/topics']['get']['parameters']['query'];
1827
1969
  type TagsListParams = paths['/api/v1/tags']['get']['parameters']['query'];
1828
1970
  type SubcategoriesListParams = paths['/api/v1/subcategories']['get']['parameters']['query'];
1971
+ type RegionsListParams = paths['/api/v1/regions']['get']['parameters']['query'];
1829
1972
  interface QuizbaseClient {
1830
1973
  questions: {
1831
1974
  list(params?: QuestionsListParams): Promise<Schemas['QuestionsListResponse']>;
@@ -1864,6 +2007,21 @@ interface QuizbaseClient {
1864
2007
  /** Iterate every subcategory across all pages. */
1865
2008
  listAll(params?: SubcategoriesListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;
1866
2009
  };
2010
+ /**
2011
+ * Region codes — **cultural affinity**, not geography. A question is tagged with
2012
+ * a region if residents of that country or members of that cultural/religious
2013
+ * group are statistically more likely to know the answer. Lowercase ISO 3166-1
2014
+ * alpha-2 (`us`, `pl`, `gb`) plus cultural codes (`jewish`, `christian-catholic`,
2015
+ * `islam`). Pair with `questions.random({ regions: [...] })` to fetch matching
2016
+ * questions.
2017
+ */
2018
+ regions: {
2019
+ list(params?: RegionsListParams): Promise<Schemas['RegionsListResponse']>;
2020
+ /** Iterate every page of `/regions`, auto-following `_links.next`. */
2021
+ pages(params?: RegionsListParams): AsyncIterableIterator<Schemas['RegionsListResponse']>;
2022
+ /** Iterate every region across all pages. */
2023
+ listAll(params?: RegionsListParams): AsyncIterableIterator<Schemas['RegionEntry']>;
2024
+ };
1867
2025
  stats: {
1868
2026
  get(): Promise<Schemas['StatsResponse']>;
1869
2027
  };
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quizbase/client",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "TypeScript SDK for QuizBase API — typed client, retry, RFC 9457 errors, telemetry hook. Generated from openapi.json.",
5
5
  "license": "MIT",
6
6
  "author": "Maciej Dzierżek",