@quizbase/client 0.3.0 → 0.4.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 +18 -0
- package/dist/index.cjs +78 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -4
- package/dist/index.d.ts +24 -4
- package/dist/index.js +78 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,6 +58,24 @@ client.report.create({ questionId, kind, comment });
|
|
|
58
58
|
|
|
59
59
|
Full parameter docs: [docs.quizbase.runriva.com/docs](https://quizbase.runriva.com/docs) and the interactive [API Reference](https://quizbase.runriva.com/docs/api-reference).
|
|
60
60
|
|
|
61
|
+
## Pagination
|
|
62
|
+
|
|
63
|
+
`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.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
// Iterate every matching question. Filters and limit are reused on each page.
|
|
67
|
+
for await (const q of client.questions.listAll({ lang: 'pl', tags_any: ['einstein'] })) {
|
|
68
|
+
await db.upsert(q);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Iterate pages — useful when you need batch boundaries (checkpoints, logging).
|
|
72
|
+
for await (const page of client.topics.pages({ lang: 'en' })) {
|
|
73
|
+
console.log(page.meta.requestId, page.data.length);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Need manual control? `list()` still exposes `_links.next` and a `cursor` query param.
|
|
78
|
+
|
|
61
79
|
## Errors
|
|
62
80
|
|
|
63
81
|
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
|
@@ -199,26 +199,31 @@ function createClient(options) {
|
|
|
199
199
|
throw lastError ?? new Error("quizbase-client: retry loop exhausted unexpectedly");
|
|
200
200
|
}
|
|
201
201
|
return {
|
|
202
|
-
questions: {
|
|
203
|
-
list
|
|
202
|
+
questions: /* @__PURE__ */ (() => {
|
|
203
|
+
const list = (params) => request({
|
|
204
204
|
endpoint: "questions.list",
|
|
205
205
|
method: "GET",
|
|
206
206
|
path: "/api/v1/questions",
|
|
207
207
|
query: params
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
208
|
+
});
|
|
209
|
+
return {
|
|
210
|
+
list,
|
|
211
|
+
random: (params) => request({
|
|
212
|
+
endpoint: "questions.random",
|
|
213
|
+
method: "GET",
|
|
214
|
+
path: "/api/v1/questions/random",
|
|
215
|
+
query: params
|
|
216
|
+
}),
|
|
217
|
+
get: (id, params) => request({
|
|
218
|
+
endpoint: "questions.get",
|
|
219
|
+
method: "GET",
|
|
220
|
+
path: `/api/v1/questions/${encodeURIComponent(id)}`,
|
|
221
|
+
query: params
|
|
222
|
+
}),
|
|
223
|
+
pages: (params) => paginate(list, params),
|
|
224
|
+
listAll: (params) => paginateItems(list, params)
|
|
225
|
+
};
|
|
226
|
+
})(),
|
|
222
227
|
categories: {
|
|
223
228
|
list: (params) => request({
|
|
224
229
|
endpoint: "categories.list",
|
|
@@ -234,36 +239,51 @@ function createClient(options) {
|
|
|
234
239
|
path: "/api/v1/languages"
|
|
235
240
|
})
|
|
236
241
|
},
|
|
237
|
-
topics: {
|
|
238
|
-
list
|
|
242
|
+
topics: /* @__PURE__ */ (() => {
|
|
243
|
+
const list = (params) => request({
|
|
239
244
|
endpoint: "topics.list",
|
|
240
245
|
method: "GET",
|
|
241
246
|
path: "/api/v1/topics",
|
|
242
247
|
query: params
|
|
243
|
-
})
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
248
|
+
});
|
|
249
|
+
return {
|
|
250
|
+
list,
|
|
251
|
+
get: (slug, params) => request({
|
|
252
|
+
endpoint: "topics.get",
|
|
253
|
+
method: "GET",
|
|
254
|
+
path: `/api/v1/topics/${encodeURIComponent(slug)}`,
|
|
255
|
+
query: params
|
|
256
|
+
}),
|
|
257
|
+
pages: (params) => paginate(list, params),
|
|
258
|
+
listAll: (params) => paginateItems(list, params)
|
|
259
|
+
};
|
|
260
|
+
})(),
|
|
261
|
+
tags: /* @__PURE__ */ (() => {
|
|
262
|
+
const list = (params) => request({
|
|
253
263
|
endpoint: "tags.list",
|
|
254
264
|
method: "GET",
|
|
255
265
|
path: "/api/v1/tags",
|
|
256
266
|
query: params
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
267
|
+
});
|
|
268
|
+
return {
|
|
269
|
+
list,
|
|
270
|
+
pages: (params) => paginate(list, params),
|
|
271
|
+
listAll: (params) => paginateItems(list, params)
|
|
272
|
+
};
|
|
273
|
+
})(),
|
|
274
|
+
subcategories: /* @__PURE__ */ (() => {
|
|
275
|
+
const list = (params) => request({
|
|
261
276
|
endpoint: "subcategories.list",
|
|
262
277
|
method: "GET",
|
|
263
278
|
path: "/api/v1/subcategories",
|
|
264
279
|
query: params
|
|
265
|
-
})
|
|
266
|
-
|
|
280
|
+
});
|
|
281
|
+
return {
|
|
282
|
+
list,
|
|
283
|
+
pages: (params) => paginate(list, params),
|
|
284
|
+
listAll: (params) => paginateItems(list, params)
|
|
285
|
+
};
|
|
286
|
+
})(),
|
|
267
287
|
stats: {
|
|
268
288
|
get: () => request({
|
|
269
289
|
endpoint: "stats.get",
|
|
@@ -296,6 +316,30 @@ function createClient(options) {
|
|
|
296
316
|
}
|
|
297
317
|
};
|
|
298
318
|
}
|
|
319
|
+
function extractCursor(nextUrl) {
|
|
320
|
+
if (!nextUrl) return null;
|
|
321
|
+
try {
|
|
322
|
+
const url = nextUrl.startsWith("http") ? new URL(nextUrl) : new URL(nextUrl, "http://placeholder");
|
|
323
|
+
return url.searchParams.get("cursor");
|
|
324
|
+
} catch {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
async function* paginate(fetchPage, initial) {
|
|
329
|
+
let nextParams = initial;
|
|
330
|
+
while (true) {
|
|
331
|
+
const page = await fetchPage(nextParams);
|
|
332
|
+
yield page;
|
|
333
|
+
const cursor = extractCursor(page._links?.next);
|
|
334
|
+
if (!cursor) return;
|
|
335
|
+
nextParams = { ...nextParams ?? {}, cursor };
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async function* paginateItems(fetchPage, initial) {
|
|
339
|
+
for await (const page of paginate(fetchPage, initial)) {
|
|
340
|
+
for (const item of page.data) yield item;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
299
343
|
function buildUrl(baseUrl, path, query) {
|
|
300
344
|
const url = new URL(baseUrl + path);
|
|
301
345
|
if (query) {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { components } from './types.gen.js';\n\nexport type ProblemDetails = components['schemas']['ProblemDetails'];\n\nexport interface QuizbaseErrorOptions {\n\tstatus: number;\n\tproblem: ProblemDetails;\n\trequestId: string | null;\n\tretryAfter: number | null;\n\turl: string;\n\tmethod: string;\n}\n\n/**\n * Thrown for any non-2xx response from the QuizBase API.\n * Carries the parsed RFC 9457 Problem Details body, X-Request-Id for support,\n * and a parsed retry-after (seconds) for 429s.\n */\nexport class QuizbaseError extends Error {\n\treadonly status: number;\n\treadonly problem: ProblemDetails;\n\treadonly requestId: string | null;\n\treadonly retryAfter: number | null;\n\treadonly url: string;\n\treadonly method: string;\n\n\tconstructor(opts: QuizbaseErrorOptions) {\n\t\tconst title = opts.problem.title ?? `HTTP ${opts.status}`;\n\t\tconst detail = opts.problem.detail ? ` — ${opts.problem.detail}` : '';\n\t\tsuper(`QuizBase ${opts.method} ${opts.url} → ${opts.status} ${title}${detail}`);\n\t\tthis.name = 'QuizbaseError';\n\t\tthis.status = opts.status;\n\t\tthis.problem = opts.problem;\n\t\tthis.requestId = opts.requestId;\n\t\tthis.retryAfter = opts.retryAfter;\n\t\tthis.url = opts.url;\n\t\tthis.method = opts.method;\n\t}\n\n\tget type(): string | undefined {\n\t\treturn this.problem.type;\n\t}\n\n\tget isRateLimited(): boolean {\n\t\treturn this.status === 429;\n\t}\n\n\tget isAuthError(): boolean {\n\t\treturn this.status === 401 || this.status === 403;\n\t}\n\n\tget isServerError(): boolean {\n\t\treturn this.status >= 500;\n\t}\n}\n","import { QuizbaseError, type ProblemDetails } from './errors.js';\nimport type { OnRequestHook } from './telemetry.js';\nimport type { components, paths } from './types.gen.js';\n\ntype Schemas = components['schemas'];\n\n/** Logical endpoint identifiers — stable across path-param values, used for telemetry + per-endpoint timeouts. */\nexport type EndpointKey =\n\t| 'questions.list'\n\t| 'questions.random'\n\t| 'questions.get'\n\t| 'categories.list'\n\t| 'languages.list'\n\t| 'topics.list'\n\t| 'topics.get'\n\t| 'tags.list'\n\t| 'subcategories.list'\n\t| 'stats.get'\n\t| 'me.get'\n\t| 'usage.get'\n\t| 'report.create';\n\nconst DEFAULT_TIMEOUTS: Record<EndpointKey, number> = {\n\t'questions.list': 15_000,\n\t'questions.random': 10_000,\n\t'questions.get': 10_000,\n\t'categories.list': 10_000,\n\t'languages.list': 10_000,\n\t'topics.list': 15_000,\n\t'topics.get': 10_000,\n\t'tags.list': 15_000,\n\t'subcategories.list': 15_000,\n\t'stats.get': 10_000,\n\t'me.get': 10_000,\n\t'usage.get': 10_000,\n\t'report.create': 15_000\n};\n\nexport interface ClientOptions {\n\t/** API key — `qb_pk_*` (publishable, CORS-safe for browsers) or `qb_sk_*` (secret, backend-only). Get one at https://quizbase.runriva.com/dashboard/keys. */\n\tapiKey: string;\n\t/** Override base URL. Defaults to `https://quizbase.runriva.com`. */\n\tbaseUrl?: string;\n\t/** Default request timeout in ms. Defaults to 30_000. Per-endpoint overrides take precedence. */\n\ttimeout?: number;\n\t/** Per-endpoint timeout overrides keyed by `EndpointKey`. */\n\ttimeouts?: Partial<Record<EndpointKey, number>>;\n\t/** Number of retries for 429 / 5xx / network errors. Defaults to 2 (3 total attempts). */\n\tretries?: number;\n\t/** Optional `fetch` implementation. Defaults to global `fetch`. */\n\tfetch?: typeof fetch;\n\t/** Telemetry hook fired after every HTTP attempt (including retries). */\n\tonRequest?: OnRequestHook;\n\t/** User-Agent suffix appended to the SDK identifier. */\n\tuserAgent?: string;\n}\n\nexport interface QuizbaseClient {\n\tquestions: {\n\t\tlist(\n\t\t\tparams?: paths['/api/v1/questions']['get']['parameters']['query']\n\t\t): 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};\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(\n\t\t\tparams?: paths['/api/v1/topics']['get']['parameters']['query']\n\t\t): 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};\n\ttags: {\n\t\tlist(\n\t\t\tparams?: paths['/api/v1/tags']['get']['parameters']['query']\n\t\t): Promise<Schemas['TagsListResponse']>;\n\t};\n\tsubcategories: {\n\t\tlist(\n\t\t\tparams?: paths['/api/v1/subcategories']['get']['parameters']['query']\n\t\t): Promise<Schemas['SubcategoriesListResponse']>;\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\tlist: (params) =>\n\t\t\t\trequest({\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\trandom: (params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'questions.random',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/questions/random',\n\t\t\t\t\tquery: params\n\t\t\t\t}),\n\t\t\tget: (id, params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'questions.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: `/api/v1/questions/${encodeURIComponent(id)}`,\n\t\t\t\t\tquery: params\n\t\t\t\t})\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\tlist: (params) =>\n\t\t\t\trequest({\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\tget: (slug, params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'topics.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: `/api/v1/topics/${encodeURIComponent(slug)}`,\n\t\t\t\t\tquery: params\n\t\t\t\t})\n\t\t},\n\t\ttags: {\n\t\t\tlist: (params) =>\n\t\t\t\trequest({\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},\n\t\tsubcategories: {\n\t\t\tlist: (params) =>\n\t\t\t\trequest({\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},\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\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| '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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1814,11 +1814,19 @@ interface ClientOptions {
|
|
|
1814
1814
|
/** User-Agent suffix appended to the SDK identifier. */
|
|
1815
1815
|
userAgent?: string;
|
|
1816
1816
|
}
|
|
1817
|
+
type QuestionsListParams = paths['/api/v1/questions']['get']['parameters']['query'];
|
|
1818
|
+
type TopicsListParams = paths['/api/v1/topics']['get']['parameters']['query'];
|
|
1819
|
+
type TagsListParams = paths['/api/v1/tags']['get']['parameters']['query'];
|
|
1820
|
+
type SubcategoriesListParams = paths['/api/v1/subcategories']['get']['parameters']['query'];
|
|
1817
1821
|
interface QuizbaseClient {
|
|
1818
1822
|
questions: {
|
|
1819
|
-
list(params?:
|
|
1823
|
+
list(params?: QuestionsListParams): Promise<Schemas['QuestionsListResponse']>;
|
|
1820
1824
|
random(params?: paths['/api/v1/questions/random']['get']['parameters']['query']): Promise<Schemas['QuestionsRandomResponse']>;
|
|
1821
1825
|
get(id: string, params?: paths['/api/v1/questions/{id}']['get']['parameters']['query']): Promise<Schemas['QuestionByIdResponse']>;
|
|
1826
|
+
/** Iterate every page of `/questions`, auto-following `_links.next`. */
|
|
1827
|
+
pages(params?: QuestionsListParams): AsyncIterableIterator<Schemas['QuestionsListResponse']>;
|
|
1828
|
+
/** Iterate every question across all pages. */
|
|
1829
|
+
listAll(params?: QuestionsListParams): AsyncIterableIterator<Schemas['Question']>;
|
|
1822
1830
|
};
|
|
1823
1831
|
categories: {
|
|
1824
1832
|
list(params?: paths['/api/v1/categories']['get']['parameters']['query']): Promise<Schemas['CategoriesResponse']>;
|
|
@@ -1827,14 +1835,26 @@ interface QuizbaseClient {
|
|
|
1827
1835
|
list(): Promise<Schemas['LanguagesResponse']>;
|
|
1828
1836
|
};
|
|
1829
1837
|
topics: {
|
|
1830
|
-
list(params?:
|
|
1838
|
+
list(params?: TopicsListParams): Promise<Schemas['TopicsListResponse']>;
|
|
1831
1839
|
get(slug: string, params?: paths['/api/v1/topics/{slug}']['get']['parameters']['query']): Promise<Schemas['TopicDetailResponse']>;
|
|
1840
|
+
/** Iterate every page of `/topics`, auto-following `_links.next`. */
|
|
1841
|
+
pages(params?: TopicsListParams): AsyncIterableIterator<Schemas['TopicsListResponse']>;
|
|
1842
|
+
/** Iterate every topic across all pages. */
|
|
1843
|
+
listAll(params?: TopicsListParams): AsyncIterableIterator<Schemas['TopicEntry']>;
|
|
1832
1844
|
};
|
|
1833
1845
|
tags: {
|
|
1834
|
-
list(params?:
|
|
1846
|
+
list(params?: TagsListParams): Promise<Schemas['TagsListResponse']>;
|
|
1847
|
+
/** Iterate every page of `/tags`, auto-following `_links.next`. */
|
|
1848
|
+
pages(params?: TagsListParams): AsyncIterableIterator<Schemas['TagsListResponse']>;
|
|
1849
|
+
/** Iterate every tag across all pages. */
|
|
1850
|
+
listAll(params?: TagsListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;
|
|
1835
1851
|
};
|
|
1836
1852
|
subcategories: {
|
|
1837
|
-
list(params?:
|
|
1853
|
+
list(params?: SubcategoriesListParams): Promise<Schemas['SubcategoriesListResponse']>;
|
|
1854
|
+
/** Iterate every page of `/subcategories`, auto-following `_links.next`. */
|
|
1855
|
+
pages(params?: SubcategoriesListParams): AsyncIterableIterator<Schemas['SubcategoriesListResponse']>;
|
|
1856
|
+
/** Iterate every subcategory across all pages. */
|
|
1857
|
+
listAll(params?: SubcategoriesListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;
|
|
1838
1858
|
};
|
|
1839
1859
|
stats: {
|
|
1840
1860
|
get(): Promise<Schemas['StatsResponse']>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1814,11 +1814,19 @@ interface ClientOptions {
|
|
|
1814
1814
|
/** User-Agent suffix appended to the SDK identifier. */
|
|
1815
1815
|
userAgent?: string;
|
|
1816
1816
|
}
|
|
1817
|
+
type QuestionsListParams = paths['/api/v1/questions']['get']['parameters']['query'];
|
|
1818
|
+
type TopicsListParams = paths['/api/v1/topics']['get']['parameters']['query'];
|
|
1819
|
+
type TagsListParams = paths['/api/v1/tags']['get']['parameters']['query'];
|
|
1820
|
+
type SubcategoriesListParams = paths['/api/v1/subcategories']['get']['parameters']['query'];
|
|
1817
1821
|
interface QuizbaseClient {
|
|
1818
1822
|
questions: {
|
|
1819
|
-
list(params?:
|
|
1823
|
+
list(params?: QuestionsListParams): Promise<Schemas['QuestionsListResponse']>;
|
|
1820
1824
|
random(params?: paths['/api/v1/questions/random']['get']['parameters']['query']): Promise<Schemas['QuestionsRandomResponse']>;
|
|
1821
1825
|
get(id: string, params?: paths['/api/v1/questions/{id}']['get']['parameters']['query']): Promise<Schemas['QuestionByIdResponse']>;
|
|
1826
|
+
/** Iterate every page of `/questions`, auto-following `_links.next`. */
|
|
1827
|
+
pages(params?: QuestionsListParams): AsyncIterableIterator<Schemas['QuestionsListResponse']>;
|
|
1828
|
+
/** Iterate every question across all pages. */
|
|
1829
|
+
listAll(params?: QuestionsListParams): AsyncIterableIterator<Schemas['Question']>;
|
|
1822
1830
|
};
|
|
1823
1831
|
categories: {
|
|
1824
1832
|
list(params?: paths['/api/v1/categories']['get']['parameters']['query']): Promise<Schemas['CategoriesResponse']>;
|
|
@@ -1827,14 +1835,26 @@ interface QuizbaseClient {
|
|
|
1827
1835
|
list(): Promise<Schemas['LanguagesResponse']>;
|
|
1828
1836
|
};
|
|
1829
1837
|
topics: {
|
|
1830
|
-
list(params?:
|
|
1838
|
+
list(params?: TopicsListParams): Promise<Schemas['TopicsListResponse']>;
|
|
1831
1839
|
get(slug: string, params?: paths['/api/v1/topics/{slug}']['get']['parameters']['query']): Promise<Schemas['TopicDetailResponse']>;
|
|
1840
|
+
/** Iterate every page of `/topics`, auto-following `_links.next`. */
|
|
1841
|
+
pages(params?: TopicsListParams): AsyncIterableIterator<Schemas['TopicsListResponse']>;
|
|
1842
|
+
/** Iterate every topic across all pages. */
|
|
1843
|
+
listAll(params?: TopicsListParams): AsyncIterableIterator<Schemas['TopicEntry']>;
|
|
1832
1844
|
};
|
|
1833
1845
|
tags: {
|
|
1834
|
-
list(params?:
|
|
1846
|
+
list(params?: TagsListParams): Promise<Schemas['TagsListResponse']>;
|
|
1847
|
+
/** Iterate every page of `/tags`, auto-following `_links.next`. */
|
|
1848
|
+
pages(params?: TagsListParams): AsyncIterableIterator<Schemas['TagsListResponse']>;
|
|
1849
|
+
/** Iterate every tag across all pages. */
|
|
1850
|
+
listAll(params?: TagsListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;
|
|
1835
1851
|
};
|
|
1836
1852
|
subcategories: {
|
|
1837
|
-
list(params?:
|
|
1853
|
+
list(params?: SubcategoriesListParams): Promise<Schemas['SubcategoriesListResponse']>;
|
|
1854
|
+
/** Iterate every page of `/subcategories`, auto-following `_links.next`. */
|
|
1855
|
+
pages(params?: SubcategoriesListParams): AsyncIterableIterator<Schemas['SubcategoriesListResponse']>;
|
|
1856
|
+
/** Iterate every subcategory across all pages. */
|
|
1857
|
+
listAll(params?: SubcategoriesListParams): AsyncIterableIterator<Schemas['RawSlugEntry']>;
|
|
1838
1858
|
};
|
|
1839
1859
|
stats: {
|
|
1840
1860
|
get(): Promise<Schemas['StatsResponse']>;
|
package/dist/index.js
CHANGED
|
@@ -197,26 +197,31 @@ function createClient(options) {
|
|
|
197
197
|
throw lastError ?? new Error("quizbase-client: retry loop exhausted unexpectedly");
|
|
198
198
|
}
|
|
199
199
|
return {
|
|
200
|
-
questions: {
|
|
201
|
-
list
|
|
200
|
+
questions: /* @__PURE__ */ (() => {
|
|
201
|
+
const list = (params) => request({
|
|
202
202
|
endpoint: "questions.list",
|
|
203
203
|
method: "GET",
|
|
204
204
|
path: "/api/v1/questions",
|
|
205
205
|
query: params
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
206
|
+
});
|
|
207
|
+
return {
|
|
208
|
+
list,
|
|
209
|
+
random: (params) => request({
|
|
210
|
+
endpoint: "questions.random",
|
|
211
|
+
method: "GET",
|
|
212
|
+
path: "/api/v1/questions/random",
|
|
213
|
+
query: params
|
|
214
|
+
}),
|
|
215
|
+
get: (id, params) => request({
|
|
216
|
+
endpoint: "questions.get",
|
|
217
|
+
method: "GET",
|
|
218
|
+
path: `/api/v1/questions/${encodeURIComponent(id)}`,
|
|
219
|
+
query: params
|
|
220
|
+
}),
|
|
221
|
+
pages: (params) => paginate(list, params),
|
|
222
|
+
listAll: (params) => paginateItems(list, params)
|
|
223
|
+
};
|
|
224
|
+
})(),
|
|
220
225
|
categories: {
|
|
221
226
|
list: (params) => request({
|
|
222
227
|
endpoint: "categories.list",
|
|
@@ -232,36 +237,51 @@ function createClient(options) {
|
|
|
232
237
|
path: "/api/v1/languages"
|
|
233
238
|
})
|
|
234
239
|
},
|
|
235
|
-
topics: {
|
|
236
|
-
list
|
|
240
|
+
topics: /* @__PURE__ */ (() => {
|
|
241
|
+
const list = (params) => request({
|
|
237
242
|
endpoint: "topics.list",
|
|
238
243
|
method: "GET",
|
|
239
244
|
path: "/api/v1/topics",
|
|
240
245
|
query: params
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
246
|
+
});
|
|
247
|
+
return {
|
|
248
|
+
list,
|
|
249
|
+
get: (slug, params) => request({
|
|
250
|
+
endpoint: "topics.get",
|
|
251
|
+
method: "GET",
|
|
252
|
+
path: `/api/v1/topics/${encodeURIComponent(slug)}`,
|
|
253
|
+
query: params
|
|
254
|
+
}),
|
|
255
|
+
pages: (params) => paginate(list, params),
|
|
256
|
+
listAll: (params) => paginateItems(list, params)
|
|
257
|
+
};
|
|
258
|
+
})(),
|
|
259
|
+
tags: /* @__PURE__ */ (() => {
|
|
260
|
+
const list = (params) => request({
|
|
251
261
|
endpoint: "tags.list",
|
|
252
262
|
method: "GET",
|
|
253
263
|
path: "/api/v1/tags",
|
|
254
264
|
query: params
|
|
255
|
-
})
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
265
|
+
});
|
|
266
|
+
return {
|
|
267
|
+
list,
|
|
268
|
+
pages: (params) => paginate(list, params),
|
|
269
|
+
listAll: (params) => paginateItems(list, params)
|
|
270
|
+
};
|
|
271
|
+
})(),
|
|
272
|
+
subcategories: /* @__PURE__ */ (() => {
|
|
273
|
+
const list = (params) => request({
|
|
259
274
|
endpoint: "subcategories.list",
|
|
260
275
|
method: "GET",
|
|
261
276
|
path: "/api/v1/subcategories",
|
|
262
277
|
query: params
|
|
263
|
-
})
|
|
264
|
-
|
|
278
|
+
});
|
|
279
|
+
return {
|
|
280
|
+
list,
|
|
281
|
+
pages: (params) => paginate(list, params),
|
|
282
|
+
listAll: (params) => paginateItems(list, params)
|
|
283
|
+
};
|
|
284
|
+
})(),
|
|
265
285
|
stats: {
|
|
266
286
|
get: () => request({
|
|
267
287
|
endpoint: "stats.get",
|
|
@@ -294,6 +314,30 @@ function createClient(options) {
|
|
|
294
314
|
}
|
|
295
315
|
};
|
|
296
316
|
}
|
|
317
|
+
function extractCursor(nextUrl) {
|
|
318
|
+
if (!nextUrl) return null;
|
|
319
|
+
try {
|
|
320
|
+
const url = nextUrl.startsWith("http") ? new URL(nextUrl) : new URL(nextUrl, "http://placeholder");
|
|
321
|
+
return url.searchParams.get("cursor");
|
|
322
|
+
} catch {
|
|
323
|
+
return null;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
async function* paginate(fetchPage, initial) {
|
|
327
|
+
let nextParams = initial;
|
|
328
|
+
while (true) {
|
|
329
|
+
const page = await fetchPage(nextParams);
|
|
330
|
+
yield page;
|
|
331
|
+
const cursor = extractCursor(page._links?.next);
|
|
332
|
+
if (!cursor) return;
|
|
333
|
+
nextParams = { ...nextParams ?? {}, cursor };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async function* paginateItems(fetchPage, initial) {
|
|
337
|
+
for await (const page of paginate(fetchPage, initial)) {
|
|
338
|
+
for (const item of page.data) yield item;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
297
341
|
function buildUrl(baseUrl, path, query) {
|
|
298
342
|
const url = new URL(baseUrl + path);
|
|
299
343
|
if (query) {
|
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\nexport interface QuizbaseClient {\n\tquestions: {\n\t\tlist(\n\t\t\tparams?: paths['/api/v1/questions']['get']['parameters']['query']\n\t\t): 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};\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(\n\t\t\tparams?: paths['/api/v1/topics']['get']['parameters']['query']\n\t\t): 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};\n\ttags: {\n\t\tlist(\n\t\t\tparams?: paths['/api/v1/tags']['get']['parameters']['query']\n\t\t): Promise<Schemas['TagsListResponse']>;\n\t};\n\tsubcategories: {\n\t\tlist(\n\t\t\tparams?: paths['/api/v1/subcategories']['get']['parameters']['query']\n\t\t): Promise<Schemas['SubcategoriesListResponse']>;\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\tlist: (params) =>\n\t\t\t\trequest({\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\trandom: (params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'questions.random',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: '/api/v1/questions/random',\n\t\t\t\t\tquery: params\n\t\t\t\t}),\n\t\t\tget: (id, params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'questions.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: `/api/v1/questions/${encodeURIComponent(id)}`,\n\t\t\t\t\tquery: params\n\t\t\t\t})\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\tlist: (params) =>\n\t\t\t\trequest({\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\tget: (slug, params) =>\n\t\t\t\trequest({\n\t\t\t\t\tendpoint: 'topics.get',\n\t\t\t\t\tmethod: 'GET',\n\t\t\t\t\tpath: `/api/v1/topics/${encodeURIComponent(slug)}`,\n\t\t\t\t\tquery: params\n\t\t\t\t})\n\t\t},\n\t\ttags: {\n\t\t\tlist: (params) =>\n\t\t\t\trequest({\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},\n\t\tsubcategories: {\n\t\t\tlist: (params) =>\n\t\t\t\trequest({\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},\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\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":";AAkBO,IAAM,aAAA,GAAN,cAA4B,KAAA,CAAM;AAAA,EAC/B,MAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,UAAA;AAAA,EACA,GAAA;AAAA,EACA,MAAA;AAAA,EAET,YAAY,IAAA,EAA4B;AACvC,IAAA,MAAM,QAAQ,IAAA,CAAK,OAAA,CAAQ,KAAA,IAAS,CAAA,KAAA,EAAQ,KAAK,MAAM,CAAA,CAAA;AACvD,IAAA,MAAM,MAAA,GAAS,KAAK,OAAA,CAAQ,MAAA,GAAS,WAAM,IAAA,CAAK,OAAA,CAAQ,MAAM,CAAA,CAAA,GAAK,EAAA;AACnE,IAAA,KAAA,CAAM,CAAA,SAAA,EAAY,IAAA,CAAK,MAAM,CAAA,CAAA,EAAI,IAAA,CAAK,GAAG,CAAA,QAAA,EAAM,IAAA,CAAK,MAAM,CAAA,CAAA,EAAI,KAAK,CAAA,EAAG,MAAM,CAAA,CAAE,CAAA;AAC9E,IAAA,IAAA,CAAK,IAAA,GAAO,eAAA;AACZ,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AACnB,IAAA,IAAA,CAAK,UAAU,IAAA,CAAK,OAAA;AACpB,IAAA,IAAA,CAAK,YAAY,IAAA,CAAK,SAAA;AACtB,IAAA,IAAA,CAAK,aAAa,IAAA,CAAK,UAAA;AACvB,IAAA,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA;AAChB,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AAAA,EACpB;AAAA,EAEA,IAAI,IAAA,GAA2B;AAC9B,IAAA,OAAO,KAAK,OAAA,CAAQ,IAAA;AAAA,EACrB;AAAA,EAEA,IAAI,aAAA,GAAyB;AAC5B,IAAA,OAAO,KAAK,MAAA,KAAW,GAAA;AAAA,EACxB;AAAA,EAEA,IAAI,WAAA,GAAuB;AAC1B,IAAA,OAAO,IAAA,CAAK,MAAA,KAAW,GAAA,IAAO,IAAA,CAAK,MAAA,KAAW,GAAA;AAAA,EAC/C;AAAA,EAEA,IAAI,aAAA,GAAyB;AAC5B,IAAA,OAAO,KAAK,MAAA,IAAU,GAAA;AAAA,EACvB;AACD;;;AChCA,IAAM,gBAAA,GAAgD;AAAA,EACrD,gBAAA,EAAkB,IAAA;AAAA,EAClB,kBAAA,EAAoB,GAAA;AAAA,EACpB,eAAA,EAAiB,GAAA;AAAA,EACjB,iBAAA,EAAmB,GAAA;AAAA,EACnB,gBAAA,EAAkB,GAAA;AAAA,EAClB,aAAA,EAAe,IAAA;AAAA,EACf,YAAA,EAAc,GAAA;AAAA,EACd,WAAA,EAAa,IAAA;AAAA,EACb,oBAAA,EAAsB,IAAA;AAAA,EACtB,WAAA,EAAa,GAAA;AAAA,EACb,QAAA,EAAU,GAAA;AAAA,EACV,WAAA,EAAa,GAAA;AAAA,EACb,eAAA,EAAiB;AAClB,CAAA;AAgGA,IAAM,gBAAA,GAAmB,8BAAA;AACzB,IAAM,WAAA,GAAc,OAAA;AAUb,SAAS,aAAa,OAAA,EAAwC;AACpE,EAAA,IAAI,CAAC,QAAQ,MAAA,EAAQ;AACpB,IAAA,MAAM,IAAI,MAAM,qCAAqC,CAAA;AAAA,EACtD;AACA,EAAA,MAAM,WAAW,OAAA,CAAQ,OAAA,IAAW,gBAAA,EAAkB,OAAA,CAAQ,QAAQ,EAAE,CAAA;AACxE,EAAA,MAAM,cAAA,GAAiB,QAAQ,OAAA,IAAW,GAAA;AAC1C,EAAA,MAAM,QAAA,GAAwC,EAAE,GAAG,gBAAA,EAAiB;AACpE,EAAA,IAAI,QAAQ,QAAA,EAAU;AACrB,IAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,OAAA,CAAQ,QAAQ,CAAA,EAGrD;AACJ,MAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,QAAA,CAAS,GAAG,CAAA,GAAI,KAAA;AAAA,IAChD;AAAA,EACD;AACA,EAAA,MAAM,UAAU,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAA,CAAQ,WAAW,CAAC,CAAA;AAChD,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,KAAA,IAAS,UAAA,CAAW,KAAA;AAC5C,EAAA,IAAI,OAAO,YAAY,UAAA,EAAY;AAClC,IAAA,MAAM,IAAI,KAAA;AAAA,MACT;AAAA,KACD;AAAA,EACD;AACA,EAAA,MAAM,SAAA,GAAY,CAAA,gBAAA,EAAmB,WAAW,CAAA,EAAG,OAAA,CAAQ,YAAY,CAAA,CAAA,EAAI,OAAA,CAAQ,SAAS,CAAA,CAAA,GAAK,EAAE,CAAA,CAAA;AAEnG,EAAA,eAAe,QAAW,MAAA,EAAmC;AAC5D,IAAA,MAAM,SAAA,GAAY,QAAA,CAAS,MAAA,CAAO,QAAQ,CAAA,IAAK,cAAA;AAC/C,IAAA,MAAM,MAAM,QAAA,CAAS,OAAA,EAAS,MAAA,CAAO,IAAA,EAAM,OAAO,KAAK,CAAA;AACvD,IAAA,MAAM,YAAY,iBAAA,EAAkB;AACpC,IAAA,MAAM,OAAA,GAAkC;AAAA,MACvC,MAAA,EAAQ,kBAAA;AAAA,MACR,aAAA,EAAe,CAAA,OAAA,EAAU,OAAA,CAAQ,MAAM,CAAA,CAAA;AAAA,MACvC,cAAA,EAAgB,SAAA;AAAA,MAChB,YAAA,EAAc;AAAA,KACf;AACA,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI,MAAA,CAAO,SAAS,MAAA,EAAW;AAC9B,MAAA,OAAA,CAAQ,cAAc,CAAA,GAAI,kBAAA;AAC1B,MAAA,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,IAAI,CAAA;AAAA,IAClC;AAEA,IAAA,IAAI,OAAA,GAAU,CAAA;AACd,IAAA,IAAI,SAAA;AACJ,IAAA,OAAO,WAAW,OAAA,EAAS;AAC1B,MAAA,MAAM,SAAA,GAAY,KAAK,GAAA,EAAI;AAC3B,MAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,MAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,SAAS,CAAA;AAC5D,MAAA,IAAI,MAAA,GAAS,CAAA;AACb,MAAA,IAAI,QAAA;AACJ,MAAA,IAAI,YAAA;AACJ,MAAA,IAAI;AACH,QAAA,QAAA,GAAW,MAAM,QAAQ,GAAA,EAAK;AAAA,UAC7B,QAAQ,MAAA,CAAO,MAAA;AAAA,UACf,OAAA;AAAA,UACA,IAAA;AAAA,UACA,QAAQ,UAAA,CAAW;AAAA,SACnB,CAAA;AACD,QAAA,MAAA,GAAS,QAAA,CAAS,MAAA;AAClB,QAAA,IAAI,SAAS,EAAA,EAAI;AAChB,UAAA,MAAM,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAA,EAAK;AAClC,UAAA,MAAM,IAAA,CAAK,QAAQ,SAAA,EAAW;AAAA,YAC7B,QAAQ,MAAA,CAAO,MAAA;AAAA,YACf,UAAU,MAAA,CAAO,QAAA;AAAA,YACjB,GAAA;AAAA,YACA,QAAA,EAAU,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAAA,YACvB,MAAA;AAAA,YACA,SAAA,EAAW,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,SAAA;AAAA,YACnD,UAAA,EAAY,OAAA;AAAA,YACZ,KAAA,EAAO;AAAA,WACP,CAAA;AACD,UAAA,OAAO,IAAA;AAAA,QACR;AACA,QAAA,MAAM,OAAA,GAAU,MAAM,WAAA,CAAY,QAAQ,CAAA;AAC1C,QAAA,MAAM,gBAAA,GAAmB,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAA;AAC3D,QAAA,MAAM,UAAA,GAAa,gBAAA,GAAmB,eAAA,CAAgB,gBAAgB,CAAA,GAAI,IAAA;AAC1E,QAAA,MAAM,GAAA,GAAM,IAAI,aAAA,CAAc;AAAA,UAC7B,MAAA;AAAA,UACA,OAAA;AAAA,UACA,SAAA,EAAW,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,SAAA;AAAA,UACnD,UAAA;AAAA,UACA,GAAA;AAAA,UACA,QAAQ,MAAA,CAAO;AAAA,SACf,CAAA;AACD,QAAA,YAAA,GAAe,GAAA;AACf,QAAA,IAAI,WAAA,CAAY,MAAM,CAAA,IAAK,OAAA,GAAU,OAAA,EAAS;AAC7C,UAAA,MAAM,IAAA,CAAK,QAAQ,SAAA,EAAW;AAAA,YAC7B,QAAQ,MAAA,CAAO,MAAA;AAAA,YACf,UAAU,MAAA,CAAO,QAAA;AAAA,YACjB,GAAA;AAAA,YACA,QAAA,EAAU,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAAA,YACvB,MAAA;AAAA,YACA,WAAW,GAAA,CAAI,SAAA;AAAA,YACf,UAAA,EAAY,OAAA;AAAA,YACZ,KAAA,EAAO,KAAA;AAAA,YACP,KAAA,EAAO;AAAA,WACP,CAAA;AACD,UAAA,MAAM,KAAA,CAAM,SAAA,CAAU,OAAA,EAAS,UAAU,CAAC,CAAA;AAC1C,UAAA,OAAA,IAAW,CAAA;AACX,UAAA,SAAA,GAAY,GAAA;AACZ,UAAA;AAAA,QACD;AACA,QAAA,MAAM,IAAA,CAAK,QAAQ,SAAA,EAAW;AAAA,UAC7B,QAAQ,MAAA,CAAO,MAAA;AAAA,UACf,UAAU,MAAA,CAAO,QAAA;AAAA,UACjB,GAAA;AAAA,UACA,QAAA,EAAU,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAAA,UACvB,MAAA;AAAA,UACA,WAAW,GAAA,CAAI,SAAA;AAAA,UACf,UAAA,EAAY,OAAA;AAAA,UACZ,KAAA,EAAO,IAAA;AAAA,UACP,KAAA,EAAO;AAAA,SACP,CAAA;AACD,QAAA,MAAM,GAAA;AAAA,MACP,SAAS,GAAA,EAAK;AACb,QAAA,IAAI,GAAA,YAAe,eAAe,MAAM,GAAA;AACxC,QAAA,MAAM,UAAA,GAAa,eAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAA;AACrE,QAAA,YAAA,GAAe,UAAA;AACf,QAAA,IAAI,UAAU,OAAA,EAAS;AACtB,UAAA,MAAM,IAAA,CAAK,QAAQ,SAAA,EAAW;AAAA,YAC7B,QAAQ,MAAA,CAAO,MAAA;AAAA,YACf,UAAU,MAAA,CAAO,QAAA;AAAA,YACjB,GAAA;AAAA,YACA,QAAA,EAAU,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAAA,YACvB,MAAA;AAAA,YACA,SAAA,EAAW,IAAA;AAAA,YACX,UAAA,EAAY,OAAA;AAAA,YACZ,KAAA,EAAO,KAAA;AAAA,YACP,KAAA,EAAO;AAAA,WACP,CAAA;AACD,UAAA,MAAM,KAAA,CAAM,SAAA,CAAU,OAAA,EAAS,IAAI,CAAC,CAAA;AACpC,UAAA,OAAA,IAAW,CAAA;AACX,UAAA,SAAA,GAAY,UAAA;AACZ,UAAA;AAAA,QACD;AACA,QAAA,MAAM,IAAA,CAAK,QAAQ,SAAA,EAAW;AAAA,UAC7B,QAAQ,MAAA,CAAO,MAAA;AAAA,UACf,UAAU,MAAA,CAAO,QAAA;AAAA,UACjB,GAAA;AAAA,UACA,QAAA,EAAU,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA;AAAA,UACvB,MAAA;AAAA,UACA,SAAA,EAAW,IAAA;AAAA,UACX,UAAA,EAAY,OAAA;AAAA,UACZ,KAAA,EAAO,IAAA;AAAA,UACP,KAAA,EAAO;AAAA,SACP,CAAA;AACD,QAAA,MAAM,UAAA;AAAA,MACP,CAAA,SAAE;AACD,QAAA,YAAA,CAAa,KAAK,CAAA;AAEb,MACN;AAAA,IACD;AACA,IAAA,MAAM,SAAA,IAAa,IAAI,KAAA,CAAM,oDAAoD,CAAA;AAAA,EAClF;AAEA,EAAA,OAAO;AAAA,IACN,2BAAY,CAAA,MAAM;AACjB,MAAA,MAAM,IAAA,GAAO,CAAC,MAAA,KACb,OAAA,CAA0C;AAAA,QACzC,QAAA,EAAU,gBAAA;AAAA,QACV,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,mBAAA;AAAA,QACN,KAAA,EAAO;AAAA,OACP,CAAA;AACF,MAAA,OAAO;AAAA,QACN,IAAA;AAAA,QACA,MAAA,EAAQ,CAAC,MAAA,KACR,OAAA,CAAQ;AAAA,UACP,QAAA,EAAU,kBAAA;AAAA,UACV,MAAA,EAAQ,KAAA;AAAA,UACR,IAAA,EAAM,0BAAA;AAAA,UACN,KAAA,EAAO;AAAA,SACP,CAAA;AAAA,QACF,GAAA,EAAK,CAAC,EAAA,EAAI,MAAA,KACT,OAAA,CAAQ;AAAA,UACP,QAAA,EAAU,eAAA;AAAA,UACV,MAAA,EAAQ,KAAA;AAAA,UACR,IAAA,EAAM,CAAA,kBAAA,EAAqB,kBAAA,CAAmB,EAAE,CAAC,CAAA,CAAA;AAAA,UACjD,KAAA,EAAO;AAAA,SACP,CAAA;AAAA,QACF,KAAA,EAAO,CAAC,MAAA,KAAW,QAAA,CAAS,MAAM,MAAM,CAAA;AAAA,QACxC,OAAA,EAAS,CAAC,MAAA,KAAW,aAAA,CAAc,MAAM,MAAM;AAAA,OAChD;AAAA,IACD,CAAA,GAAG;AAAA,IACH,UAAA,EAAY;AAAA,MACX,IAAA,EAAM,CAAC,MAAA,KACN,OAAA,CAAQ;AAAA,QACP,QAAA,EAAU,iBAAA;AAAA,QACV,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,oBAAA;AAAA,QACN,KAAA,EAAO;AAAA,OACP;AAAA,KACH;AAAA,IACA,SAAA,EAAW;AAAA,MACV,IAAA,EAAM,MACL,OAAA,CAAQ;AAAA,QACP,QAAA,EAAU,gBAAA;AAAA,QACV,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM;AAAA,OACN;AAAA,KACH;AAAA,IACA,wBAAS,CAAA,MAAM;AACd,MAAA,MAAM,IAAA,GAAO,CAAC,MAAA,KACb,OAAA,CAAuC;AAAA,QACtC,QAAA,EAAU,aAAA;AAAA,QACV,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,gBAAA;AAAA,QACN,KAAA,EAAO;AAAA,OACP,CAAA;AACF,MAAA,OAAO;AAAA,QACN,IAAA;AAAA,QACA,GAAA,EAAK,CAAC,IAAA,EAAM,MAAA,KACX,OAAA,CAAQ;AAAA,UACP,QAAA,EAAU,YAAA;AAAA,UACV,MAAA,EAAQ,KAAA;AAAA,UACR,IAAA,EAAM,CAAA,eAAA,EAAkB,kBAAA,CAAmB,IAAI,CAAC,CAAA,CAAA;AAAA,UAChD,KAAA,EAAO;AAAA,SACP,CAAA;AAAA,QACF,KAAA,EAAO,CAAC,MAAA,KAAW,QAAA,CAAS,MAAM,MAAM,CAAA;AAAA,QACxC,OAAA,EAAS,CAAC,MAAA,KAAW,aAAA,CAAc,MAAM,MAAM;AAAA,OAChD;AAAA,IACD,CAAA,GAAG;AAAA,IACH,sBAAO,CAAA,MAAM;AACZ,MAAA,MAAM,IAAA,GAAO,CAAC,MAAA,KACb,OAAA,CAAqC;AAAA,QACpC,QAAA,EAAU,WAAA;AAAA,QACV,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,cAAA;AAAA,QACN,KAAA,EAAO;AAAA,OACP,CAAA;AACF,MAAA,OAAO;AAAA,QACN,IAAA;AAAA,QACA,KAAA,EAAO,CAAC,MAAA,KAAW,QAAA,CAAS,MAAM,MAAM,CAAA;AAAA,QACxC,OAAA,EAAS,CAAC,MAAA,KAAW,aAAA,CAAc,MAAM,MAAM;AAAA,OAChD;AAAA,IACD,CAAA,GAAG;AAAA,IACH,+BAAgB,CAAA,MAAM;AACrB,MAAA,MAAM,IAAA,GAAO,CAAC,MAAA,KACb,OAAA,CAA8C;AAAA,QAC7C,QAAA,EAAU,oBAAA;AAAA,QACV,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,uBAAA;AAAA,QACN,KAAA,EAAO;AAAA,OACP,CAAA;AACF,MAAA,OAAO;AAAA,QACN,IAAA;AAAA,QACA,KAAA,EAAO,CAAC,MAAA,KAAW,QAAA,CAAS,MAAM,MAAM,CAAA;AAAA,QACxC,OAAA,EAAS,CAAC,MAAA,KAAW,aAAA,CAAc,MAAM,MAAM;AAAA,OAChD;AAAA,IACD,CAAA,GAAG;AAAA,IACH,KAAA,EAAO;AAAA,MACN,GAAA,EAAK,MACJ,OAAA,CAAQ;AAAA,QACP,QAAA,EAAU,WAAA;AAAA,QACV,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM;AAAA,OACN;AAAA,KACH;AAAA,IACA,EAAA,EAAI;AAAA,MACH,GAAA,EAAK,MACJ,OAAA,CAAQ;AAAA,QACP,QAAA,EAAU,QAAA;AAAA,QACV,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM;AAAA,OACN;AAAA,KACH;AAAA,IACA,KAAA,EAAO;AAAA,MACN,GAAA,EAAK,CAAC,MAAA,KACL,OAAA,CAAQ;AAAA,QACP,QAAA,EAAU,WAAA;AAAA,QACV,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,eAAA;AAAA,QACN,KAAA,EAAO;AAAA,OACP;AAAA,KACH;AAAA,IACA,MAAA,EAAQ;AAAA,MACP,MAAA,EAAQ,CAAC,IAAA,KACR,OAAA,CAAQ;AAAA,QACP,QAAA,EAAU,eAAA;AAAA,QACV,MAAA,EAAQ,MAAA;AAAA,QACR,IAAA,EAAM,gBAAA;AAAA,QACN;AAAA,OACA;AAAA;AACH,GACD;AACD;AAgBA,SAAS,cAAc,OAAA,EAA4C;AAClE,EAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AACrB,EAAA,IAAI;AACH,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,UAAA,CAAW,MAAM,CAAA,GAClC,IAAI,GAAA,CAAI,OAAO,CAAA,GACf,IAAI,GAAA,CAAI,OAAA,EAAS,oBAAoB,CAAA;AACxC,IAAA,OAAO,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,QAAQ,CAAA;AAAA,EACrC,CAAA,CAAA,MAAQ;AACP,IAAA,OAAO,IAAA;AAAA,EACR;AACD;AAEA,gBAAgB,QAAA,CACf,WACA,OAAA,EAC2B;AAC3B,EAAA,IAAI,UAAA,GAA4B,OAAA;AAChC,EAAA,OAAO,IAAA,EAAM;AACZ,IAAA,MAAM,IAAA,GAAO,MAAM,SAAA,CAAU,UAAU,CAAA;AACvC,IAAA,MAAM,IAAA;AACN,IAAA,MAAM,MAAA,GAAS,aAAA,CAAc,IAAA,CAAK,MAAA,EAAQ,IAAI,CAAA;AAC9C,IAAA,IAAI,CAAC,MAAA,EAAQ;AACb,IAAA,UAAA,GAAa,EAAE,GAAI,UAAA,IAAe,IAAW,MAAA,EAAO;AAAA,EACrD;AACD;AAEA,gBAAgB,aAAA,CACf,WACA,OAAA,EAC2B;AAC3B,EAAA,WAAA,MAAiB,IAAA,IAAQ,QAAA,CAAS,SAAA,EAAW,OAAO,CAAA,EAAG;AACtD,IAAA,KAAA,MAAW,IAAA,IAAQ,IAAA,CAAK,IAAA,EAAM,MAAM,IAAA;AAAA,EACrC;AACD;AAEA,SAAS,QAAA,CAAS,OAAA,EAAiB,IAAA,EAAc,KAAA,EAAoD;AACpG,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,OAAA,GAAU,IAAI,CAAA;AAClC,EAAA,IAAI,KAAA,EAAO;AACV,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AACjD,MAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AAC3C,MAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACzB,QAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACzB,UAAA,IAAI,IAAA,KAAS,MAAA,IAAa,IAAA,KAAS,IAAA,EAAM;AACxC,YAAA,GAAA,CAAI,YAAA,CAAa,MAAA,CAAO,GAAA,EAAK,MAAA,CAAO,IAAI,CAAC,CAAA;AAAA,UAC1C;AAAA,QACD;AAAA,MACD,CAAA,MAAO;AACN,QAAA,GAAA,CAAI,YAAA,CAAa,MAAA,CAAO,GAAA,EAAK,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,MAC3C;AAAA,IACD;AAAA,EACD;AACA,EAAA,OAAO,IAAI,QAAA,EAAS;AACrB;AAEA,SAAS,iBAAA,GAA4B;AACpC,EAAA,MAAM,YAAgC,UAAA,CAAW,MAAA;AACjD,EAAA,IAAI,SAAA,IAAa,OAAO,SAAA,CAAU,UAAA,KAAe,UAAA,EAAY;AAC5D,IAAA,OAAO,UAAU,UAAA,EAAW;AAAA,EAC7B;AACA,EAAA,OAAO,OAAO,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAC,CAAC,IAAI,IAAA,CAAK,GAAA,EAAI,CAAE,QAAA,CAAS,EAAE,CAAC,CAAA,CAAA;AAC7E;AAEA,eAAe,YAAY,QAAA,EAA6C;AACvE,EAAA,IAAI;AACH,IAAA,MAAM,IAAA,GAAQ,MAAM,QAAA,CAAS,IAAA,EAAK;AAClC,IAAA,IAAI,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU,OAAO,IAAA;AAAA,EAC9C,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO;AAAA,IACN,IAAA,EAAM,aAAA;AAAA,IACN,KAAA,EAAO,QAAA,CAAS,UAAA,IAAc,CAAA,KAAA,EAAQ,SAAS,MAAM,CAAA,CAAA;AAAA,IACrD,QAAQ,QAAA,CAAS,MAAA;AAAA,IACjB,MAAA,EAAQ,EAAA;AAAA,IACR,QAAA,EAAU,EAAA;AAAA,IACV,IAAA,EAAM;AAAA,GACP;AACD;AAEA,SAAS,YAAY,MAAA,EAAyB;AAC7C,EAAA,OAAO,MAAA,KAAW,GAAA,IAAQ,MAAA,IAAU,GAAA,IAAO,MAAA,GAAS,GAAA;AACrD;AAEA,SAAS,gBAAgB,KAAA,EAA8B;AACtD,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,QAAA,CAAS,KAAA,EAAO,EAAE,CAAA;AACzC,EAAA,IAAI,OAAO,QAAA,CAAS,OAAO,CAAA,IAAK,OAAA,IAAW,GAAG,OAAO,OAAA;AACrD,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA;AAC7B,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA,EAAG;AAC1B,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,IAAA,CAAA,CAAM,IAAA,GAAO,IAAA,CAAK,GAAA,EAAI,IAAK,GAAI,CAAC,CAAA;AAC9D,IAAA,OAAO,IAAA;AAAA,EACR;AACA,EAAA,OAAO,IAAA;AACR;AAEA,SAAS,SAAA,CAAU,SAAiB,iBAAA,EAA0C;AAC7E,EAAA,IAAI,iBAAA,KAAsB,IAAA,EAAM,OAAO,iBAAA,GAAoB,GAAA;AAC3D,EAAA,MAAM,IAAA,GAAO,GAAA;AACb,EAAA,MAAM,GAAA,GAAM,OAAO,CAAA,IAAK,OAAA;AACxB,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,MAAA,EAAO,GAAI,GAAA;AAC/B,EAAA,OAAO,IAAA,CAAK,GAAA,CAAI,GAAA,GAAM,MAAA,EAAQ,GAAK,CAAA;AACpC;AAEA,SAAS,MAAM,EAAA,EAA2B;AACzC,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACxD;AAEA,eAAe,IAAA,CACd,MACA,KAAA,EACgB;AAChB,EAAA,IAAI,CAAC,IAAA,EAAM;AACX,EAAA,IAAI;AACH,IAAA,MAAM,KAAK,KAAK,CAAA;AAAA,EACjB,CAAA,CAAA,MAAQ;AAAA,EAER;AACD","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"]}
|
package/package.json
CHANGED