@timeback/qti 0.2.2-beta.20260331191116 → 0.2.2

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/dist/index.d.ts CHANGED
@@ -1,548 +1,1520 @@
1
- import * as _timeback_internal_client_infra from '@timeback/internal-client-infra';
2
- import { RequestOptions, PaginatedResponse, ClientConfig, TransportOnlyConfig, ProviderClientConfig, Paginator as Paginator$1, ListParams, BaseTransport, BaseTransportConfig, ProviderRegistry, TimebackProvider, AuthCheckResult } from '@timeback/internal-client-infra';
3
- export { AuthCheckResult, EnvAuth, Environment, ExplicitAuth, ListParams, PageResult } from '@timeback/internal-client-infra';
1
+ import { ListAssessmentItemsParams, CreateAssessmentItemRequest, UpdateAssessmentItemRequest, ProcessResponseRequest, CreateAssessmentItemXmlRequest, CreateAssessmentItemJsonRequest, ListAssessmentTestsParams, CreateAssessmentTestRequest, UpdateAssessmentTestRequest, UpdateTestMetadataRequest, ListTestPartsParams, CreateTestPartRequest, UpdateTestPartRequest, ListSectionsParams, CreateSectionRequest, UpdateSectionRequest, AddItemRequest, ReorderItemsRequest, SubmitLessonRequest, SubmitQuestionRequest, ListStimuliParams, CreateStimulusRequest, UpdateStimulusRequest, ValidateRequest, ValidateBatchRequest } from '@timeback/types/zod';
2
+ import { ListResponse, AssessmentItem, DeleteResponse, ProcessResponseResult, AssessmentTest, QuestionsResponse, TestPart, AssessmentSection, LessonFeedback, Stimulus, ValidationResult, BatchValidationResult } from '@timeback/types/protocols/qti';
3
+ export { AssessmentItem, AssessmentItemRef, AssessmentItemType, AssessmentSection, AssessmentTest, BaseType, BatchValidationResult, Cardinality, DeleteResponse, Difficulty, FeedbackBlock, FeedbackInline, FeedbackType, Grade, ItemMetadata, LessonFeedback, ListResponse, ModalFeedback, NavigationMode, OutcomeDeclaration, PaginationMeta, ProcessResponseResult, QuestionReference, QuestionWithItem, QuestionsResponse, ResponseDeclaration, ResponseProcessing, Stimulus, SubmissionMode, TestPart, ValidationResult } from '@timeback/types/protocols/qti';
4
4
 
5
5
  /**
6
- * API Response Types
6
+ * Interface for obtaining OAuth2 access tokens.
7
7
  *
8
- * Types for QTI API responses.
8
+ * Implementations handle token caching and refresh automatically.
9
9
  */
10
+ interface TokenProvider {
11
+ /**
12
+ * Get a valid access token.
13
+ *
14
+ * Returns a cached token if still valid, otherwise fetches a new one.
15
+ *
16
+ * @returns A valid access token string
17
+ * @throws {Error} If token acquisition fails
18
+ */
19
+ getToken(): Promise<string>;
20
+ /**
21
+ * Invalidate the cached token.
22
+ *
23
+ * Forces the next getToken() call to fetch a fresh token.
24
+ * Should be called when a request fails with 401 Unauthorized.
25
+ *
26
+ * Optional - not all implementations may support invalidation.
27
+ */
28
+ invalidate?(): void;
29
+ }
10
30
 
11
- // ═══════════════════════════════════════════════════════════════════════════════
12
- // LIST RESPONSE
13
- // ═══════════════════════════════════════════════════════════════════════════════
31
+ /**
32
+ * All supported platforms.
33
+ */
34
+ declare const PLATFORMS: readonly ["BEYOND_AI", "LEARNWITH_AI"];
14
35
 
15
36
  /**
16
- * Paginated list response from QTI API.
17
- *
18
- * QTI returns pagination metadata directly in the response body alongside items.
19
- */
20
- interface ListResponse<T> {
21
- /** Array of items in this page */
22
- items: T[]
23
- /** Total items across all pages */
24
- total: number
25
- /** Current page number (1-indexed) */
26
- page: number
27
- /** Total number of pages */
28
- pages: number
29
- /** Items per page */
30
- limit: number
31
- /** Sort field */
32
- sort: string
33
- /** Sort order */
34
- order: 'asc' | 'desc'
37
+ * Type Definitions for `@timeback/internal-logger`
38
+ *
39
+ * Central type definitions used across all logger components.
40
+ * These types define the contract between the logger, formatters, and consumers.
41
+ */
42
+ /**
43
+ * Log severity levels, ordered from least to most severe.
44
+ *
45
+ * - debug: Detailed diagnostic information for developers
46
+ * - info: General operational messages (app started, request received)
47
+ * - warn: Something unexpected but not breaking (deprecated API used)
48
+ * - error: Something failed (request failed, database connection lost)
49
+ */
50
+ type LogLevel = 'debug' | 'info' | 'warn' | 'error';
51
+ /**
52
+ * Runtime environments that determine how logs are formatted.
53
+ *
54
+ * - terminal: Local development with colors and icons
55
+ * - ci: CI/CD pipelines with plain text (no ANSI codes)
56
+ * - production: JSON lines for log aggregation (DataDog, CloudWatch, etc.)
57
+ * - browser: Browser console with CSS styling
58
+ * - test: Test environment with no output
59
+ */
60
+ type Environment$2 = 'terminal' | 'ci' | 'production' | 'browser' | 'test';
61
+ /**
62
+ * Arbitrary key-value data attached to log entries.
63
+ *
64
+ * Context is merged into log output - in production it becomes top-level
65
+ * JSON fields, in terminal it's displayed as key=value pairs.
66
+ *
67
+ * @example
68
+ * log.info('User created', { userId: 123, email: 'foo@bar.com' })
69
+ */
70
+ type LogContext = Record<string, unknown>;
71
+ /**
72
+ * Configuration options for creating a logger instance.
73
+ */
74
+ interface LoggerOptions {
75
+ /**
76
+ * Logger scope/namespace for categorizing logs.
77
+ * Child loggers append to this with colons: "api" → "api:users"
78
+ */
79
+ scope?: string;
80
+ /**
81
+ * Minimum log level to output.
82
+ * Logs below this level are silently ignored.
83
+ * @default 'info' (or 'debug' if DEBUG env var is set)
84
+ */
85
+ minLevel?: LogLevel;
86
+ /**
87
+ * Override automatic environment detection.
88
+ * Useful for testing or forcing a specific format.
89
+ */
90
+ environment?: Environment$2;
91
+ /**
92
+ * Default context added to every log entry from this logger.
93
+ * Useful for request IDs, user IDs, etc.
94
+ */
95
+ defaultContext?: LogContext;
35
96
  }
36
97
 
37
- // ═══════════════════════════════════════════════════════════════════════════════
38
- // DELETE RESPONSE
39
- // ═══════════════════════════════════════════════════════════════════════════════
98
+ /**
99
+ * Logger instance with environment-aware formatting.
100
+ *
101
+ * Instances are lightweight and can be created freely.
102
+ * Common pattern: one logger per module/component.
103
+ */
104
+ declare class Logger {
105
+ /** Namespace for this logger (e.g., "api", "api:users") */
106
+ private scope?;
107
+ /** Minimum level to output (logs below this are ignored) */
108
+ private minLevel;
109
+ /** The detected or configured environment */
110
+ private environment;
111
+ /** Function that formats and outputs log entries */
112
+ private formatter;
113
+ /** Context added to every log entry from this logger */
114
+ private defaultContext;
115
+ /**
116
+ * Create a new Logger instance.
117
+ *
118
+ * Usually you'd use createLogger() instead of new Logger().
119
+ */
120
+ constructor(options?: LoggerOptions);
121
+ /**
122
+ * Create a child logger with an additional scope segment.
123
+ *
124
+ * Child loggers inherit minLevel and defaultContext from parent.
125
+ * Scope is appended with a colon separator.
126
+ *
127
+ * @param scope - Additional scope segment to append
128
+ * @returns New Logger instance with extended scope
129
+ *
130
+ * @example
131
+ * const api = createLogger({ scope: 'api' })
132
+ * const users = api.child('users')
133
+ * users.info('Created') // scope: "api:users"
134
+ */
135
+ child(scope: string): Logger;
136
+ /**
137
+ * Create a logger with additional default context.
138
+ *
139
+ * The new context is merged with existing default context.
140
+ * Useful for adding request IDs, user IDs, etc.
141
+ *
142
+ * @param context - Additional context to include in all logs
143
+ * @returns New Logger instance with extended context
144
+ *
145
+ * @example
146
+ * const requestLog = log.withContext({ requestId: 'abc123' })
147
+ * requestLog.info('Processing') // requestId included automatically
148
+ */
149
+ withContext(context: LogContext): Logger;
150
+ /**
151
+ * Log a debug message.
152
+ *
153
+ * Use for detailed diagnostic information useful during development.
154
+ * These are typically filtered out in production.
155
+ *
156
+ * @param message - The log message
157
+ * @param context - Optional key-value context to include with the log
158
+ */
159
+ debug(message: string, context?: LogContext): void;
160
+ /**
161
+ * Log an info message.
162
+ *
163
+ * Use for general operational information.
164
+ * Examples: server started, request received, job completed.
165
+ *
166
+ * @param message - The log message
167
+ * @param context - Optional key-value context to include with the log
168
+ */
169
+ info(message: string, context?: LogContext): void;
170
+ /**
171
+ * Log a warning message.
172
+ *
173
+ * Use for unexpected but non-breaking issues.
174
+ * Examples: deprecated API used, retrying operation, approaching limit.
175
+ *
176
+ * @param message - The log message
177
+ * @param context - Optional key-value context to include with the log
178
+ */
179
+ warn(message: string, context?: LogContext): void;
180
+ /**
181
+ * Log an error message.
182
+ *
183
+ * Use for failures that need attention.
184
+ * Examples: request failed, database error, unhandled exception.
185
+ *
186
+ * @param message - The log message
187
+ * @param context - Optional key-value context to include with the log
188
+ */
189
+ error(message: string, context?: LogContext): void;
190
+ /**
191
+ * Internal method that builds the log entry and passes it to the formatter.
192
+ *
193
+ * All public log methods (debug, info, warn, error) delegate here.
194
+ *
195
+ * @param level - The log level
196
+ * @param message - The log message
197
+ * @param context - Optional key-value context to include with the log
198
+ */
199
+ private log;
200
+ }
40
201
 
41
202
  /**
42
- * Response from DELETE operations.
203
+ * Where Clause Types
204
+ *
205
+ * Type-safe object syntax for building filter expressions.
206
+ */
207
+ /**
208
+ * Primitive value types that can be used in filters.
209
+ */
210
+ type FilterValue = string | number | boolean | Date;
211
+ /**
212
+ * Operators for a single field.
213
+ *
214
+ * @example
215
+ * ```typescript
216
+ * { status: { ne: 'deleted' } }
217
+ * { score: { gt: 90 } }
218
+ * { email: { contains: '@school.edu' } }
219
+ * { role: { in: ['teacher', 'aide'] } }
220
+ * ```
43
221
  */
44
- interface DeleteResponse {
45
- message?: string
222
+ interface FieldOperators<T> {
223
+ /** Not equal */
224
+ ne?: T;
225
+ /** Greater than */
226
+ gt?: T;
227
+ /** Greater than or equal */
228
+ gte?: T;
229
+ /** Less than */
230
+ lt?: T;
231
+ /** Less than or equal */
232
+ lte?: T;
233
+ /** Contains substring (strings only) */
234
+ contains?: T extends string ? string : never;
235
+ /** Match any of the values */
236
+ in?: T[];
237
+ /** Match none of the values */
238
+ notIn?: T[];
46
239
  }
47
-
48
240
  /**
49
- * Base Types
241
+ * A field condition can be:
242
+ * - A direct value (implies equality)
243
+ * - An object with operators
244
+ */
245
+ type FieldCondition<T> = T | FieldOperators<T>;
246
+ /**
247
+ * Map filter field types to field conditions.
248
+ *
249
+ * Each field in F becomes an optional filter condition.
50
250
  *
51
- * Common types shared across QTI resources.
251
+ * @typeParam F - Filter fields type (e.g., UserFilterFields)
52
252
  */
53
-
54
- // ═══════════════════════════════════════════════════════════════════════════════
55
- // ENUMS
56
- // ═══════════════════════════════════════════════════════════════════════════════
57
-
253
+ type FilterFields<F> = {
254
+ [K in keyof F]?: FieldCondition<F[K] & FilterValue>;
255
+ };
58
256
  /**
59
- * Assessment item interaction types.
60
- */
61
- type AssessmentItemType =
62
- | 'choice'
63
- | 'text-entry'
64
- | 'extended-text'
65
- | 'inline-choice'
66
- | 'match'
67
- | 'order'
68
- | 'associate'
69
- | 'select-point'
70
- | 'graphic-order'
71
- | 'graphic-associate'
72
- | 'graphic-gap-match'
73
- | 'hotspot'
74
- | 'hottext'
75
- | 'slider'
76
- | 'drawing'
77
- | 'media'
78
- | 'upload'
79
-
257
+ * OR condition for combining multiple field conditions.
258
+ */
259
+ interface OrCondition<F> {
260
+ OR: WhereClause<F>[];
261
+ }
80
262
  /**
81
- * Cardinality of a response or outcome variable.
263
+ * A where clause for filtering entities.
264
+ *
265
+ * The type parameter F should be a filter fields type that defines
266
+ * the available fields and their value types for filtering.
267
+ *
268
+ * Multiple fields at the same level are combined with AND.
269
+ * Use `OR` for explicit OR logic.
270
+ *
271
+ * @typeParam F - Filter fields type (e.g., UserFilterFields)
272
+ *
273
+ * @example
274
+ * ```typescript
275
+ * // Simple equality (implicit AND)
276
+ * { status: 'active', role: 'teacher' }
277
+ * // → status='active' AND role='teacher'
278
+ * ```
279
+ *
280
+ * @example
281
+ * ```typescript
282
+ * // With operators
283
+ * { status: { ne: 'deleted' }, score: { gte: 90 } }
284
+ * // → status!='deleted' AND score>=90
285
+ * ```
286
+ *
287
+ * @example
288
+ * ```typescript
289
+ * // OR condition
290
+ * { OR: [{ role: 'teacher' }, { role: 'aide' }] }
291
+ * // → role='teacher' OR role='aide'
292
+ * ```
293
+ *
294
+ * @example
295
+ * ```typescript
296
+ * // Match multiple values
297
+ * { role: { in: ['teacher', 'aide'] } }
298
+ * // → role='teacher' OR role='aide'
299
+ * ```
82
300
  */
83
- type Cardinality = 'single' | 'multiple' | 'ordered' | 'record'
301
+ type WhereClause<F> = FilterFields<F> | OrCondition<F>;
84
302
 
85
303
  /**
86
- * Base type of a response or outcome variable.
87
- */
88
- type BaseType =
89
- | 'identifier'
90
- | 'boolean'
91
- | 'integer'
92
- | 'float'
93
- | 'string'
94
- | 'point'
95
- | 'pair'
96
- | 'directedPair'
97
- | 'duration'
98
- | 'file'
99
- | 'uri'
304
+ * Shared Types
305
+ *
306
+ * Common types for API client infrastructure.
307
+ */
100
308
 
101
309
  /**
102
- * Difficulty level for assessment items.
310
+ * Fetch function signature for HTTP requests.
311
+ * Avoids Bun-specific extensions on `typeof fetch`.
103
312
  */
104
- type Difficulty = 'easy' | 'medium' | 'hard'
105
-
313
+ type FetchFn$1 = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
106
314
  /**
107
- * Grade level (K-12 + pre-K as -1).
315
+ * Supported Timeback platform implementations.
108
316
  */
109
- type Grade = -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13
110
-
317
+ type Platform$1 = (typeof PLATFORMS)[number];
111
318
  /**
112
- * Navigation mode for test parts.
319
+ * Supported deployment environments.
113
320
  */
114
- type NavigationMode = 'linear' | 'nonlinear'
115
-
321
+ type Environment$1 = 'staging' | 'production';
116
322
  /**
117
- * Submission mode for test parts.
323
+ * Auth credentials for environment mode.
324
+ * Token URL is derived automatically from environment.
118
325
  */
119
- type SubmissionMode = 'individual' | 'simultaneous'
120
-
326
+ interface EnvAuth$1 {
327
+ clientId: string;
328
+ clientSecret: string;
329
+ }
121
330
  /**
122
- * Show/hide indicator for feedback.
331
+ * Auth credentials for explicit mode.
332
+ * Includes authUrl for custom APIs.
333
+ * @deprecated Use separate authUrl and ProviderAuth fields instead
123
334
  */
124
- type ShowHide = 'show' | 'hide'
125
-
335
+ interface ExplicitAuth$1 {
336
+ clientId: string;
337
+ clientSecret: string;
338
+ authUrl: string;
339
+ }
126
340
  /**
127
- * Sort order for list responses.
341
+ * Base configuration options shared by all modes.
128
342
  */
129
- type SortOrder = 'asc' | 'desc'
130
-
343
+ interface BaseConfig$1 {
344
+ /** Request timeout in milliseconds */
345
+ timeout?: number;
346
+ /** Custom fetch implementation */
347
+ fetch?: FetchFn$1;
348
+ }
349
+ /**
350
+ * Environment-based configuration for Timeback APIs.
351
+ */
352
+ interface EnvConfig extends BaseConfig$1 {
353
+ /** Timeback platform implementation (defaults to 'BEYOND_AI') */
354
+ platform?: Platform$1;
355
+ /** Target environment - determines base URL and token URL */
356
+ env: Environment$1;
357
+ /** OAuth2 client credentials */
358
+ auth: EnvAuth$1;
359
+ }
360
+ /**
361
+ * Environment-based configuration with shared token provider.
362
+ */
363
+ interface TokenProviderEnvConfig extends BaseConfig$1 {
364
+ /** Timeback platform implementation (defaults to 'BEYOND_AI') */
365
+ platform?: Platform$1;
366
+ /** Target environment - determines base URL */
367
+ env: Environment$1;
368
+ /** Shared token provider (from @timeback/auth) */
369
+ tokenProvider: TokenProvider;
370
+ }
371
+ /**
372
+ * Explicit URL configuration for custom APIs.
373
+ * Supports both authenticated and public/no-auth services.
374
+ */
375
+ interface ExplicitConfig extends BaseConfig$1 {
376
+ /** API base URL */
377
+ baseUrl: string;
378
+ /**
379
+ * OAuth2 token URL. Omit for public/no-auth services.
380
+ * Can also be provided via auth.authUrl (legacy format).
381
+ */
382
+ authUrl?: string;
383
+ /**
384
+ * OAuth2 credentials. Required if authUrl is provided.
385
+ * Supports both ExplicitAuth (with authUrl) and ProviderAuth (without).
386
+ */
387
+ auth?: ExplicitAuth$1 | ProviderAuth;
388
+ /**
389
+ * Use a built-in path profile by name.
390
+ * Defaults to 'BEYOND_AI' if neither pathProfile nor paths is specified.
391
+ */
392
+ pathProfile?: Platform$1;
393
+ /** Custom path overrides (takes precedence over pathProfile) */
394
+ paths?: Partial<PlatformPaths>;
395
+ /** Not applicable to explicit config — use `EnvConfig` instead */
396
+ env?: never;
397
+ }
398
+ /**
399
+ * Use pre-configured transport.
400
+ */
401
+ interface TransportConfig extends BaseConfig$1 {
402
+ /** Transport configuration */
403
+ transport: TransportLike;
404
+ }
131
405
  /**
132
- * Feedback type.
406
+ * HTTP request options.
407
+ */
408
+ interface RequestOptions$1 {
409
+ /** HTTP method */
410
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
411
+ /** Query parameters to append to the URL */
412
+ params?: Record<string, string | number | boolean | undefined>;
413
+ /** Request body (will be JSON-serialized) */
414
+ body?: unknown;
415
+ /** Additional headers to include */
416
+ headers?: Record<string, string>;
417
+ /**
418
+ * Unique identifier for this request.
419
+ * Used for log correlation and debugging.
420
+ * Auto-generated if not provided.
421
+ */
422
+ requestId?: string;
423
+ }
424
+ /**
425
+ * Duck-typed transport interface for testing and advanced use.
133
426
  */
134
- type FeedbackType = 'QUESTION' | 'LESSON'
427
+ interface TransportLike {
428
+ /** Base URL of the API */
429
+ baseUrl: string;
430
+ /** Make an authenticated request */
431
+ request<T>(path: string, options?: RequestOptions$1): Promise<T>;
432
+ }
433
+ /**
434
+ * Configuration using a pre-configured transport.
435
+ *
436
+ * For advanced use cases like sharing a transport between clients
437
+ * or using a custom transport implementation.
438
+ *
439
+ * @template T - Transport type (defaults to TransportLike, clients should specify their full transport type)
440
+ */
441
+ interface TransportOnlyConfig<T extends TransportLike = TransportLike> {
442
+ /** Existing transport instance */
443
+ transport: T;
444
+ }
445
+ /**
446
+ * Union of all client configuration types.
447
+ */
448
+ type ClientConfig = EnvConfig | TokenProviderEnvConfig | ExplicitConfig | TransportConfig | Partial<ExplicitConfig>;
449
+ /**
450
+ * Parameters for listing resources with pagination, sorting, and filtering.
451
+ *
452
+ * Common across all Timeback APIs (OneRoster, Edubridge, QTI, etc.).
453
+ *
454
+ * @typeParam T - Entity type for type-safe `where` clause (defaults to unknown)
455
+ *
456
+ * @example
457
+ * ```typescript
458
+ * // Type-safe where
459
+ * client.users.list({
460
+ * where: { status: 'active', role: 'teacher' },
461
+ * sort: 'familyName',
462
+ * })
463
+ * ```
464
+ */
465
+ interface ListParams<T = unknown> {
466
+ /**
467
+ * Maximum items per page.
468
+ * @default 100
469
+ */
470
+ limit?: number;
471
+ /**
472
+ * Number of items to skip (for pagination).
473
+ * @default 0
474
+ */
475
+ offset?: number;
476
+ /**
477
+ * Field name to sort results by.
478
+ *
479
+ * Type-safe: only valid field names for this resource are accepted.
480
+ *
481
+ * @example
482
+ * ```typescript
483
+ * sort: 'familyName'
484
+ * ```
485
+ */
486
+ sort?: keyof T & string;
487
+ /**
488
+ * Sort direction.
489
+ * @default "asc"
490
+ */
491
+ orderBy?: 'asc' | 'desc';
492
+ /**
493
+ * Type-safe filter using object syntax.
494
+ *
495
+ * Multiple fields are combined with AND. Use `OR` for OR logic.
496
+ *
497
+ * @example
498
+ * ```typescript
499
+ * // Simple equality
500
+ * where: { status: 'active' }
501
+ * ```
502
+ *
503
+ * @example
504
+ * ```typescript
505
+ * // Multiple fields (AND)
506
+ * where: { status: 'active', role: 'teacher' }
507
+ * ```
508
+ *
509
+ * @example
510
+ * ```typescript
511
+ * // With operators
512
+ * where: { score: { gte: 90 }, status: { ne: 'deleted' } }
513
+ * ```
514
+ *
515
+ * @example
516
+ * ```typescript
517
+ * // OR condition
518
+ * where: { role: { in: ['teacher', 'aide'] } }
519
+ * ```
520
+ */
521
+ where?: WhereClause<T>;
522
+ /**
523
+ * Fields to include in the response.
524
+ * Reduces payload size by requesting only needed fields.
525
+ * @example ['sourcedId', 'givenName', 'familyName']
526
+ */
527
+ fields?: string[];
528
+ /**
529
+ * Free-text search across multiple fields (proprietary extension).
530
+ * For users: searches givenName, familyName, email.
531
+ * @example "john@example.com"
532
+ */
533
+ search?: string;
534
+ /**
535
+ * Maximum total items to return across all pages.
536
+ *
537
+ * Unlike `limit` (which sets page size), `max` caps the total number
538
+ * of items yielded by the paginator. Pagination stops once this many
539
+ * items have been returned.
540
+ *
541
+ * @example
542
+ * ```typescript
543
+ * // Get at most 50 users total
544
+ * client.users.list({ max: 50 })
545
+ * ```
546
+ */
547
+ max?: number;
548
+ }
549
+ /**
550
+ * Response from a paginated API request.
551
+ */
552
+ interface PaginatedResponse<T> {
553
+ /** Array of items in this page */
554
+ data: T[];
555
+ /** Whether more pages are available */
556
+ hasMore: boolean;
557
+ /** Total count of items (if provided by server) */
558
+ total?: number;
559
+ }
560
+ /**
561
+ * Result of fetching a single page of resources.
562
+ *
563
+ * @typeParam T - The type of items in the page
564
+ */
565
+ interface PageResult<T> {
566
+ /** Array of items in this page */
567
+ data: T[];
568
+ /** Whether more pages are available */
569
+ hasMore: boolean;
570
+ /** Total count of items (if provided by server) */
571
+ total?: number;
572
+ /** Offset to use for fetching the next page */
573
+ nextOffset?: number;
574
+ }
575
+ /**
576
+ * Options for toArray() method.
577
+ */
578
+ interface ToArrayOptions {
579
+ /**
580
+ * Maximum number of items to collect.
581
+ *
582
+ * Throws an error if this limit is exceeded, preventing OOM on large datasets.
583
+ * Use `for await...of` to stream results instead.
584
+ *
585
+ * @default 10_000
586
+ * @example Set to Infinity to disable (use with caution!)
587
+ */
588
+ maxItems?: number;
589
+ }
590
+ /**
591
+ * Function that fetches a page of results.
592
+ * Provided by the transport layer.
593
+ */
594
+ type PageFetcher<T> = (path: string, options: {
595
+ params: Record<string, string | number | boolean | undefined>;
596
+ }) => Promise<PaginatedResponse<T>>;
597
+ /**
598
+ * Pagination style for API requests.
599
+ *
600
+ * - `'offset'`: Uses limit/offset parameters (default, e.g., OneRoster)
601
+ * - `'page'`: Uses limit/page parameters (1-indexed, e.g., QTI)
602
+ */
603
+ type PaginationStyle = 'offset' | 'page';
604
+ /**
605
+ * Options for creating a Paginator.
606
+ */
607
+ interface PaginatorOptions<T, F = unknown> {
608
+ /** Function to fetch a page of results */
609
+ fetcher: PageFetcher<T>;
610
+ /** API endpoint path */
611
+ path: string;
612
+ /** List parameters (filter, sort, limit, offset) */
613
+ params?: ListParams<F>;
614
+ /** Maximum total items to return across all pages (client-side cap) */
615
+ max?: number;
616
+ /** Response key containing the items array (e.g., "users") */
617
+ unwrapKey?: string;
618
+ /** Logger instance (defaults to client-common logger) */
619
+ logger?: Logger;
620
+ /** Optional transform function applied to each item before yielding */
621
+ transform?: (item: T) => T;
622
+ /**
623
+ * Pagination style to use for API requests.
624
+ *
625
+ * - `'offset'` (default): Sends `limit` and `offset` params
626
+ * - `'page'`: Sends `limit` and `page` params (1-indexed)
627
+ *
628
+ * @default 'offset'
629
+ */
630
+ paginationStyle?: PaginationStyle;
631
+ }
632
+ /**
633
+ * Result of an auth check operation.
634
+ */
635
+ interface AuthCheckResult$1 {
636
+ /** Whether auth succeeded */
637
+ ok: boolean;
638
+ /** Time taken to complete the check (ms) */
639
+ latencyMs: number;
640
+ /** Error message if failed */
641
+ error?: string;
642
+ /** Detailed check results */
643
+ checks: {
644
+ /** Token acquisition succeeded */
645
+ tokenAcquisition: boolean;
646
+ };
647
+ }
135
648
 
136
- // ═══════════════════════════════════════════════════════════════════════════════
137
- // COMMON STRUCTURES
138
- // ═══════════════════════════════════════════════════════════════════════════════
649
+ /**
650
+ * Config Types
651
+ *
652
+ * Types for TimebackProvider and provider resolution.
653
+ */
139
654
 
140
655
  /**
141
- * Correct response value.
656
+ * Caliper API path profile.
657
+ * Defines paths for Caliper operations. Use `null` for unsupported operations.
142
658
  */
143
- interface CorrectResponse {
144
- value: string[]
659
+ interface CaliperPaths {
660
+ /** Path for sending events (POST) */
661
+ send: string;
662
+ /** Path for validating events (POST), null if not supported */
663
+ validate: string | null;
664
+ /** Path for listing events (GET), null if not supported */
665
+ list: string | null;
666
+ /** Path template for getting single event (GET), use {id} placeholder */
667
+ get: string | null;
668
+ /** Path template for job status (GET), use {id} placeholder */
669
+ jobStatus: string | null;
670
+ }
671
+ /**
672
+ * Webhook API path profile.
673
+ * Defines paths for webhook management operations.
674
+ * Nullability is at the platform level (`webhooks: WebhookPaths | null` in PlatformPaths).
675
+ */
676
+ interface WebhookPaths {
677
+ /** Path for listing webhooks (GET) */
678
+ webhookList: string;
679
+ /** Path template for getting a single webhook (GET), use {id} placeholder */
680
+ webhookGet: string;
681
+ /** Path for creating a webhook (POST) */
682
+ webhookCreate: string;
683
+ /** Path template for updating a webhook (PUT), use {id} placeholder */
684
+ webhookUpdate: string;
685
+ /** Path template for deleting a webhook (DELETE), use {id} placeholder */
686
+ webhookDelete: string;
687
+ /** Path template for activating a webhook (PUT), use {id} placeholder */
688
+ webhookActivate: string;
689
+ /** Path template for deactivating a webhook (PUT), use {id} placeholder */
690
+ webhookDeactivate: string;
691
+ /** Path for listing all webhook filters (GET) */
692
+ webhookFilterList: string;
693
+ /** Path template for getting a single webhook filter (GET), use {id} placeholder */
694
+ webhookFilterGet: string;
695
+ /** Path for creating a webhook filter (POST) */
696
+ webhookFilterCreate: string;
697
+ /** Path template for updating a webhook filter (PUT), use {id} placeholder */
698
+ webhookFilterUpdate: string;
699
+ /** Path template for deleting a webhook filter (DELETE), use {id} placeholder */
700
+ webhookFilterDelete: string;
701
+ /** Path template for listing filters by webhook (GET), use {webhookId} placeholder */
702
+ webhookFiltersByWebhook: string;
703
+ }
704
+ /**
705
+ * Reporting API path profile.
706
+ * Defines paths for reporting MCP and REST operations.
707
+ * Nullability is at the platform level (`reporting: ReportingPaths | null` in PlatformPaths).
708
+ */
709
+ interface ReportingPaths {
710
+ /** Path for the reporting MCP JSON-RPC endpoint (POST) */
711
+ mcp: string;
712
+ /** Path template for executing a saved query (GET), use {id} placeholder */
713
+ savedQueryExecute: string;
714
+ /** Path template for checking reporting group membership (GET), use {email} placeholder */
715
+ adminGroupCheck: string;
716
+ /** Path template for adding a user to the reporting group (POST), use {email} placeholder */
717
+ adminGroupAdd: string;
718
+ /** Path template for removing a user from the reporting group (DELETE), use {email} placeholder */
719
+ adminGroupRemove: string;
145
720
  }
146
-
147
721
  /**
148
- * Response declaration for assessment items.
722
+ * OneRoster API path profile.
723
+ * Defines the base path prefix for all OneRoster resources.
149
724
  */
150
- interface ResponseDeclaration {
151
- identifier: string
152
- cardinality: Cardinality
153
- baseType?: BaseType
154
- correctResponse: CorrectResponse
725
+ interface OneRosterPaths {
726
+ /** Base path prefix for rostering resources (users, schools, classes, etc.) */
727
+ rostering: string;
728
+ /** Base path prefix for gradebook resources (lineItems, results, etc.) */
729
+ gradebook: string;
730
+ /** Base path prefix for resources API (digital learning resources) */
731
+ resources: string;
155
732
  }
156
-
157
733
  /**
158
- * Outcome declaration for items and tests.
734
+ * Edubridge API path profile.
735
+ * Defines path prefixes for Edubridge operations.
159
736
  */
160
- interface OutcomeDeclaration {
161
- identifier: string
162
- cardinality: Cardinality
163
- baseType?: BaseType
737
+ interface EdubridgePaths {
738
+ /** Base path prefix for all Edubridge resources */
739
+ base: string;
164
740
  }
165
-
166
741
  /**
167
- * Test outcome declaration with additional properties.
168
- */
169
- interface TestOutcomeDeclaration {
170
- identifier: string
171
- cardinality?: Cardinality
172
- baseType: BaseType
173
- normalMaximum?: number
174
- normalMinimum?: number
175
- defaultValue?: {
176
- value?: unknown
177
- }
742
+ * PowerPath API path profile.
743
+ * Defines path prefixes for PowerPath operations.
744
+ */
745
+ interface PowerPathPaths {
746
+ /** Base path prefix for all PowerPath resources */
747
+ base: string;
178
748
  }
179
-
180
749
  /**
181
- * Inline feedback configuration.
750
+ * CASE API path profile.
751
+ * Defines path prefix for CASE (Competency and Academic Standards Exchange) operations.
182
752
  */
183
- interface InlineFeedback {
184
- outcomeIdentifier: string
185
- variableIdentifier: string
753
+ interface CasePaths {
754
+ /** Base path prefix for all CASE resources */
755
+ base: string;
186
756
  }
187
-
188
757
  /**
189
- * Response processing configuration.
758
+ * CLR API path profile.
759
+ * Defines path prefixes for CLR (Comprehensive Learner Record) operations.
190
760
  */
191
- interface ResponseProcessing {
192
- templateType: 'match_correct' | 'map_response'
193
- responseDeclarationIdentifier: string
194
- outcomeIdentifier: string
195
- correctResponseIdentifier: string
196
- incorrectResponseIdentifier: string
197
- inlineFeedback?: InlineFeedback
761
+ interface ClrPaths {
762
+ /** Path for upserting CLR credentials (POST) */
763
+ credentials: string;
764
+ /** Path for API discovery (GET) */
765
+ discovery: string;
766
+ }
767
+ /**
768
+ * Platform path profiles for all services.
769
+ * Use `null` to indicate a service is not supported on the platform.
770
+ */
771
+ interface PlatformPaths {
772
+ caliper: CaliperPaths;
773
+ oneroster: OneRosterPaths;
774
+ webhooks: WebhookPaths | null;
775
+ reporting: ReportingPaths | null;
776
+ edubridge: EdubridgePaths | null;
777
+ powerpath: PowerPathPaths | null;
778
+ clr: ClrPaths | null;
779
+ case: CasePaths | null;
780
+ }
781
+ /**
782
+ * Services that have path configuration.
783
+ * Subset of ServiceName - excludes services without path profiles (e.g., 'qti').
784
+ */
785
+ type PathEnabledService = keyof PlatformPaths;
786
+ /**
787
+ * Supported Timeback platform implementations.
788
+ */
789
+ type Platform = (typeof PLATFORMS)[number];
790
+ /**
791
+ * Supported deployment environments.
792
+ */
793
+ type Environment = 'staging' | 'production';
794
+ /**
795
+ * Supported service names.
796
+ */
797
+ type ServiceName = 'oneroster' | 'caliper' | 'webhooks' | 'reporting' | 'edubridge' | 'qti' | 'powerpath' | 'clr' | 'case';
798
+ /**
799
+ * Resolved endpoint for a single service.
800
+ */
801
+ interface ResolvedEndpoint {
802
+ /** Base URL for the service API */
803
+ baseUrl: string;
804
+ /** OAuth2 token URL for this endpoint. Undefined for public/no-auth services. */
805
+ authUrl?: string;
806
+ }
807
+ /**
808
+ * Auth credentials for a provider.
809
+ */
810
+ interface ProviderAuth {
811
+ clientId: string;
812
+ clientSecret: string;
813
+ }
814
+ /**
815
+ * Configuration for environment-based provider.
816
+ * Uses known Timeback platform endpoints.
817
+ */
818
+ interface ProviderEnvConfig {
819
+ /** Timeback platform (defaults to 'BEYOND_AI') */
820
+ platform?: Platform;
821
+ /** Target environment */
822
+ env: Environment;
823
+ /** OAuth2 credentials */
824
+ auth: ProviderAuth;
825
+ /** Request timeout in milliseconds */
826
+ timeout?: number;
827
+ }
828
+ /**
829
+ * Configuration for explicit URL provider.
830
+ * Single base URL for all services.
831
+ */
832
+ interface ProviderExplicitConfig {
833
+ /** Base URL for all services */
834
+ baseUrl: string;
835
+ /** OAuth2 token URL. Omit for public/no-auth services. */
836
+ authUrl?: string;
837
+ /** OAuth2 credentials. Required if authUrl is provided. */
838
+ auth?: ProviderAuth;
839
+ /** Request timeout in milliseconds */
840
+ timeout?: number;
841
+ /**
842
+ * Use a built-in path profile by name.
843
+ * Defaults to 'BEYOND_AI' if neither pathProfile nor paths is specified.
844
+ */
845
+ pathProfile?: Platform;
846
+ /** Custom path overrides (takes precedence over pathProfile) */
847
+ paths?: Partial<PlatformPaths>;
848
+ }
849
+ /**
850
+ * Configuration for multi-service provider.
851
+ * Different URLs for different services.
852
+ */
853
+ interface ProviderServicesConfig {
854
+ /** Per-service base URLs */
855
+ services: Partial<Record<ServiceName, string>>;
856
+ /** OAuth2 token URL. Omit for public/no-auth services. */
857
+ authUrl?: string;
858
+ /** OAuth2 credentials. Required if authUrl is provided. */
859
+ auth?: ProviderAuth;
860
+ /** Request timeout in milliseconds */
861
+ timeout?: number;
862
+ /**
863
+ * Use a built-in path profile by name.
864
+ * Defaults to 'BEYOND_AI' if neither pathProfile nor paths is specified.
865
+ */
866
+ pathProfile?: Platform;
867
+ /** Custom path overrides (takes precedence over pathProfile) */
868
+ paths?: Partial<PlatformPaths>;
869
+ }
870
+ /**
871
+ * Union of all provider configuration types.
872
+ */
873
+ type TimebackProviderConfig = ProviderEnvConfig | ProviderExplicitConfig | ProviderServicesConfig;
874
+ /**
875
+ * Provider template - endpoints without auth.
876
+ * Used internally to define available platform+env combinations.
877
+ */
878
+ interface ProviderTemplate {
879
+ platform: Platform;
880
+ env: Environment;
881
+ }
882
+ /**
883
+ * Registry of provider templates indexed by platform and environment.
884
+ *
885
+ * Use `satisfies ProviderRegistry` when defining a registry for type checking.
886
+ *
887
+ * @example
888
+ * const myRegistry = {
889
+ * defaultPlatform: 'MY_PLATFORM',
890
+ * templates: {
891
+ * MY_PLATFORM: {
892
+ * staging: { platform: 'BEYOND_AI', env: 'staging' },
893
+ * production: { platform: 'BEYOND_AI', env: 'production' },
894
+ * },
895
+ * },
896
+ * } satisfies ProviderRegistry
897
+ */
898
+ interface ProviderRegistry {
899
+ /** Default platform when none specified */
900
+ defaultPlatform: string;
901
+ /** Available templates indexed by platform → env */
902
+ templates: Record<string, Record<string, ProviderTemplate>>;
198
903
  }
199
-
200
904
  /**
201
- * Learning objective set for metadata.
905
+ * Client config that accepts a pre-built provider.
202
906
  */
203
- interface LearningObjectiveSet {
204
- source: string
205
- learningObjectiveIds: string[]
907
+ interface ProviderClientConfig {
908
+ /** Pre-built provider */
909
+ provider: TimebackProvider;
206
910
  }
207
911
 
208
912
  /**
209
- * Item metadata.
913
+ * Timeback Provider
914
+ *
915
+ * Encapsulates platform connection configuration including endpoints and auth.
916
+ * Providers are complete "connection" objects that clients consume.
917
+ */
918
+
919
+ /**
920
+ * Timeback Provider - encapsulates a complete platform connection.
921
+ *
922
+ * A provider contains everything needed to connect to Timeback APIs:
923
+ * - Service endpoints (URLs)
924
+ * - Authentication credentials
925
+ * - Configuration options
926
+ *
927
+ * Providers can be created from:
928
+ * - Platform + environment (uses known Timeback endpoints)
929
+ * - Explicit base URL (single URL for all services)
930
+ * - Per-service URLs (different URLs for each service)
931
+ *
932
+ * @example
933
+ * ```typescript
934
+ * // Environment-based provider (Timeback hosted)
935
+ * const provider = new TimebackProvider({
936
+ * platform: 'BEYOND_AI',
937
+ * env: 'staging',
938
+ * auth: { clientId: '...', clientSecret: '...' },
939
+ * })
940
+ * ```
941
+ *
942
+ * @example
943
+ * ```typescript
944
+ * // Explicit URL provider (self-hosted)
945
+ * const provider = new TimebackProvider({
946
+ * baseUrl: 'https://api.myschool.edu',
947
+ * authUrl: 'https://auth.myschool.edu/oauth/token',
948
+ * auth: { clientId: '...', clientSecret: '...' },
949
+ * })
950
+ * ```
951
+ *
952
+ * @example
953
+ * ```typescript
954
+ * // Per-service URLs
955
+ * const provider = new TimebackProvider({
956
+ * services: {
957
+ * oneroster: 'https://roster.myschool.edu',
958
+ * caliper: 'https://analytics.myschool.edu',
959
+ * },
960
+ * authUrl: 'https://auth.myschool.edu/oauth/token',
961
+ * auth: { clientId: '...', clientSecret: '...' },
962
+ * })
963
+ * ```
210
964
  */
211
- interface ItemMetadata {
212
- subject?: string
213
- grade?: Grade
214
- difficulty?: Difficulty
215
- learningObjectiveSet?: LearningObjectiveSet[]
965
+ declare class TimebackProvider {
966
+ /** Platform identifier (if using known platform) */
967
+ readonly platform?: Platform;
968
+ /** Environment (if using known platform) */
969
+ readonly env?: Environment;
970
+ /** OAuth2 credentials. Undefined for public/no-auth services. */
971
+ readonly auth?: ProviderAuth;
972
+ /** Request timeout in milliseconds */
973
+ readonly timeout: number;
974
+ /** Resolved endpoints for each service */
975
+ /** @internal */
976
+ readonly _endpoints: Partial<Record<ServiceName, ResolvedEndpoint>>;
977
+ /** Token URL for authentication. Undefined for public/no-auth services. */
978
+ /** @internal */
979
+ readonly _authUrl?: string;
980
+ /** OAuth2 scope to request with access tokens. */
981
+ /** @internal */
982
+ readonly _tokenScope?: string;
983
+ /** API path profiles for this platform */
984
+ /** @internal */
985
+ readonly _pathProfiles: PlatformPaths;
986
+ /** Cached TokenManagers by authUrl (for token sharing) */
987
+ /** @internal */
988
+ readonly _tokenManagers: Map<string, TokenProvider>;
989
+ /**
990
+ * Create a new TimebackProvider.
991
+ *
992
+ * @param config - Provider configuration (env-based, explicit URL, or per-service)
993
+ * @throws {Error} If configuration is invalid or missing required fields
994
+ */
995
+ constructor(config: TimebackProviderConfig);
996
+ /**
997
+ * Get the resolved endpoint for a specific service.
998
+ *
999
+ * @param service - Service name (oneroster, caliper, edubridge, qti, powerpath)
1000
+ * @returns Resolved endpoint with baseUrl and authUrl
1001
+ * @throws If the service is not configured in this provider
1002
+ */
1003
+ getEndpoint(service: ServiceName): ResolvedEndpoint;
1004
+ /**
1005
+ * Check if a service is available in this provider.
1006
+ *
1007
+ * @param service - Service name to check
1008
+ * @returns True if the service is configured
1009
+ */
1010
+ hasService(service: ServiceName): boolean;
1011
+ /**
1012
+ * Get all configured service names.
1013
+ *
1014
+ * @returns Array of service names available in this provider
1015
+ */
1016
+ getAvailableServices(): ServiceName[];
1017
+ /**
1018
+ * Get the token URL for this provider.
1019
+ * @returns The token URL for authentication
1020
+ */
1021
+ getTokenUrl(): string | undefined;
1022
+ /**
1023
+ * Get endpoint with paths for a service that has path configuration.
1024
+ *
1025
+ * @param service - Service name that has paths in PlatformPaths
1026
+ * @returns Resolved endpoint with baseUrl, authUrl, and paths
1027
+ * @throws If service is not configured or not supported on this platform
1028
+ */
1029
+ getEndpointWithPaths<S extends PathEnabledService>(service: S & ServiceName): ResolvedEndpoint & {
1030
+ paths: NonNullable<PlatformPaths[S]>;
1031
+ };
1032
+ /**
1033
+ * Get all path profiles for this provider (raw, may contain nulls).
1034
+ *
1035
+ * @returns Platform path profiles
1036
+ */
1037
+ getPaths(): PlatformPaths;
1038
+ /**
1039
+ * Get paths for a specific service.
1040
+ *
1041
+ * @param service - Service name
1042
+ * @returns Path configuration for the service
1043
+ * @throws If the service is not supported on this platform
1044
+ */
1045
+ getServicePaths<S extends PathEnabledService>(service: S): NonNullable<PlatformPaths[S]>;
1046
+ /**
1047
+ * Check if a service is supported on this platform.
1048
+ *
1049
+ * @param service - Service name
1050
+ * @returns true if the service has path configuration
1051
+ */
1052
+ hasServiceSupport(service: PathEnabledService): boolean;
1053
+ /**
1054
+ * Get a TokenProvider for a specific service.
1055
+ *
1056
+ * TokenProviders are cached by authUrl, so services sharing the same
1057
+ * token endpoint will share the same cached OAuth tokens.
1058
+ *
1059
+ * @param service - Service name (oneroster, caliper, edubridge, qti, powerpath)
1060
+ * @returns Cached TokenProvider for the service's token endpoint, or undefined for public/no-auth services
1061
+ * @throws If the service is not configured in this provider
1062
+ * @throws If auth is required but not configured
1063
+ */
1064
+ getTokenProvider(service: ServiceName): TokenProvider | undefined;
1065
+ /**
1066
+ * Verify that OAuth authentication is working.
1067
+ *
1068
+ * Attempts to acquire a token using the provider's credentials.
1069
+ * Returns a health check result with success/failure and latency info.
1070
+ *
1071
+ * @returns Auth check result
1072
+ * @throws {Error} If no auth is configured on this provider
1073
+ */
1074
+ checkAuth(): Promise<AuthCheckResult$1>;
1075
+ /**
1076
+ * Invalidate all cached OAuth tokens.
1077
+ *
1078
+ * Call this when closing the client or when tokens need to be refreshed.
1079
+ * New tokens will be acquired on the next API call.
1080
+ */
1081
+ invalidateTokens(): void;
216
1082
  }
217
1083
 
218
1084
  /**
219
- * Modal feedback element.
1085
+ * Transport Types
1086
+ *
1087
+ * Types for HTTP transport layer.
220
1088
  */
221
- interface ModalFeedback {
222
- outcomeIdentifier: string
223
- identifier: string
224
- showHide: ShowHide
225
- content: string
226
- title: string
227
- }
228
1089
 
229
1090
  /**
230
- * Inline feedback element.
1091
+ * Fetch function signature for HTTP requests.
1092
+ * Avoids Bun-specific extensions on `typeof fetch`.
231
1093
  */
232
- interface FeedbackInline {
233
- outcomeIdentifier: string
234
- identifier: string
235
- showHide: ShowHide
236
- content: string
237
- class: string[]
238
- }
239
-
1094
+ type FetchFn = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
240
1095
  /**
241
- * Block feedback element.
1096
+ * HTTP request options.
242
1097
  */
243
- interface FeedbackBlock {
244
- outcomeIdentifier: string
245
- identifier: string
246
- showHide: ShowHide
247
- content: string
248
- class: string[]
1098
+ interface RequestOptions {
1099
+ /** HTTP method */
1100
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
1101
+ /** Query parameters to append to the URL */
1102
+ params?: Record<string, string | number | boolean | undefined>;
1103
+ /** Request body (will be JSON-serialized) */
1104
+ body?: unknown;
1105
+ /** Additional headers to include */
1106
+ headers?: Record<string, string>;
1107
+ /**
1108
+ * Unique identifier for this request.
1109
+ * Used for log correlation and debugging.
1110
+ * Auto-generated if not provided.
1111
+ */
1112
+ requestId?: string;
249
1113
  }
250
-
251
1114
  /**
252
- * Stylesheet reference.
1115
+ * Auth credentials for environment mode.
1116
+ * Token URL is derived automatically from environment.
253
1117
  */
254
- interface Stylesheet {
255
- href: string
256
- type: string
1118
+ interface EnvAuth {
1119
+ clientId: string;
1120
+ clientSecret: string;
257
1121
  }
258
-
259
1122
  /**
260
- * Catalog info entry.
1123
+ * Auth credentials for explicit mode.
1124
+ * Requires explicit authUrl for custom APIs.
261
1125
  */
262
- interface CatalogInfo {
263
- id: string
264
- support: string
265
- content: string
266
- }
267
-
268
- // ═══════════════════════════════════════════════════════════════════════════════
269
- // PAGINATION
270
- // ═══════════════════════════════════════════════════════════════════════════════
271
-
272
- /**
273
- * Pagination metadata from QTI list responses.
274
- */
275
- interface PaginationMeta {
276
- /** Total items across all pages */
277
- total: number
278
- /** Current page number (1-indexed) */
279
- page: number
280
- /** Total number of pages */
281
- pages: number
282
- /** Items per page */
283
- limit: number
284
- /** Sort field */
285
- sort: string
286
- /** Sort order */
287
- order: SortOrder
1126
+ interface ExplicitAuth {
1127
+ clientId: string;
1128
+ clientSecret: string;
1129
+ authUrl: string;
288
1130
  }
289
-
290
1131
  /**
291
- * Assessment Items Types
292
- *
293
- * Types for QTI assessment item resources.
1132
+ * Base configuration options shared by all modes.
294
1133
  */
295
-
296
-
297
-
298
- // ═══════════════════════════════════════════════════════════════════════════════
299
- // ASSESSMENT ITEM
300
- // ═══════════════════════════════════════════════════════════════════════════════
301
-
302
- /**
303
- * Assessment item entity.
304
- */
305
- interface AssessmentItem {
306
- identifier: string
307
- title: string
308
- type: AssessmentItemType
309
- qtiVersion: string
310
- timeDependent: boolean
311
- adaptive: boolean
312
- responseDeclarations?: ResponseDeclaration[]
313
- outcomeDeclarations?: OutcomeDeclaration[]
314
- responseProcessing?: ResponseProcessing
315
- metadata?: ItemMetadata
316
- /** Raw QTI XML string */
317
- rawXml: string
318
- /**
319
- * Parsed XML→JSON content.
320
- * Structure varies by item type.
321
- */
322
- content: Record<string, unknown>
323
- modalFeedback?: ModalFeedback[]
324
- feedbackInline?: FeedbackInline[]
325
- feedbackBlock?: FeedbackBlock[]
326
- createdAt: string
327
- updatedAt: string
328
- __v?: number
1134
+ interface BaseConfig {
1135
+ /** Request timeout in milliseconds */
1136
+ timeout?: number;
1137
+ /** Custom fetch implementation */
1138
+ fetch?: FetchFn;
329
1139
  }
330
-
331
1140
  /**
332
- * Result of processing a response.
333
- *
334
- * Feedback is only present when the item has response processing
335
- * with feedback identifiers configured.
1141
+ * Internal resolved transport configuration.
336
1142
  */
337
- interface ProcessResponseResult {
338
- /** Score (0.0–1.0) for the response. */
339
- score: number
340
- /** Feedback for the response (only present when item has feedback configured). */
341
- feedback?: {
342
- identifier: string
343
- value: string
344
- }
1143
+ interface ResolvedTransportConfig {
1144
+ /** Base URL of the API */
1145
+ baseUrl: string;
1146
+ /** Request timeout in milliseconds */
1147
+ timeout: number;
1148
+ /** Fetch implementation */
1149
+ fetch: FetchFn;
1150
+ /** Token provider for authentication. Undefined for public/no-auth services. */
1151
+ tokenProvider?: TokenProvider;
345
1152
  }
346
-
347
- // ═══════════════════════════════════════════════════════════════════════════════
348
- // SECTION ITEM REFERENCE
349
- // ═══════════════════════════════════════════════════════════════════════════════
350
-
351
1153
  /**
352
- * Reference to an assessment item within a section.
1154
+ * Transport configuration with explicit auth.
353
1155
  */
354
- interface AssessmentItemRef {
355
- identifier: string
356
- /** Item reference href */
357
- href: string
358
- sequence?: number
1156
+ interface TransportConfigWithAuth extends BaseConfig {
1157
+ baseUrl: string;
1158
+ auth: ExplicitAuth;
1159
+ tokenProvider?: never;
359
1160
  }
360
-
361
- // ═══════════════════════════════════════════════════════════════════════════════
362
- // SECTION
363
- // ═══════════════════════════════════════════════════════════════════════════════
364
-
365
1161
  /**
366
- * Assessment section within a test part.
1162
+ * Transport configuration with shared token provider.
367
1163
  */
368
- interface AssessmentSection {
369
- identifier: string
370
- title: string
371
- visible: boolean
372
- required?: boolean
373
- fixed?: boolean
374
- sequence: number
375
- 'qti-assessment-item-ref'?: AssessmentItemRef[]
1164
+ interface TransportConfigWithTokenProvider extends BaseConfig {
1165
+ baseUrl: string;
1166
+ tokenProvider: TokenProvider;
1167
+ auth?: never;
376
1168
  }
377
-
378
- // ═══════════════════════════════════════════════════════════════════════════════
379
- // TEST PART
380
- // ═══════════════════════════════════════════════════════════════════════════════
381
-
382
1169
  /**
383
- * Test part within an assessment test.
1170
+ * Transport configuration for public/no-auth services.
384
1171
  */
385
- interface TestPart {
386
- identifier: string
387
- navigationMode: NavigationMode
388
- submissionMode: SubmissionMode
389
- 'qti-assessment-section': AssessmentSection[]
390
- }
391
-
392
- // ═══════════════════════════════════════════════════════════════════════════════
393
- // ASSESSMENT TEST
394
- // ═══════════════════════════════════════════════════════════════════════════════
395
-
396
- /**
397
- * Assessment test entity.
398
- */
399
- interface AssessmentTest {
400
- identifier: string
401
- title: string
402
- qtiVersion: string
403
- 'qti-test-part': TestPart[]
404
- /** Outcome declarations for the test. May be an empty array or absent. */
405
- 'qti-outcome-declaration'?: TestOutcomeDeclaration[]
406
- timeLimit?: number
407
- maxAttempts?: number
408
- toolsEnabled?: Record<string, boolean>
409
- metadata?: Record<string, unknown>
410
- /** Raw QTI XML string */
411
- rawXml: string
412
- /**
413
- * Parsed XML→JSON content.
414
- * Structure varies by test.
415
- */
416
- content: Record<string, unknown>
417
- createdAt: string
418
- updatedAt: string
419
- __v?: number
420
- isValidXml?: boolean
1172
+ interface TransportConfigNoAuth extends BaseConfig {
1173
+ baseUrl: string;
1174
+ auth?: never;
1175
+ tokenProvider?: never;
421
1176
  }
422
-
423
- // ═══════════════════════════════════════════════════════════════════════════════
424
- // QUESTIONS RESPONSE
425
- // ═══════════════════════════════════════════════════════════════════════════════
426
-
427
1177
  /**
428
- * Question reference within a test.
1178
+ * Configuration for BaseTransport.
429
1179
  */
430
- interface QuestionReference {
431
- identifier: string
432
- href: string
433
- testPart: string
434
- section: string
435
- }
436
-
1180
+ type BaseTransportConfig = TransportConfigWithAuth | TransportConfigWithTokenProvider | TransportConfigNoAuth;
437
1181
  /**
438
- * Question with full item details.
1182
+ * Options for creating a BaseTransport.
439
1183
  */
440
- interface QuestionWithItem {
441
- reference: QuestionReference
442
- question: AssessmentItem
1184
+ interface BaseTransportOptions {
1185
+ /** Transport configuration */
1186
+ config: BaseTransportConfig;
1187
+ /** Logger instance for request/response logging */
1188
+ logger: Logger;
443
1189
  }
444
-
445
1190
  /**
446
- * Response from GET /assessment-tests/{identifier}/questions.
447
- */
448
- interface QuestionsResponse {
449
- assessmentTest: string
450
- title: string
451
- totalQuestions: number
452
- questions: QuestionWithItem[]
1191
+ * Result of an auth check operation.
1192
+ */
1193
+ interface AuthCheckResult {
1194
+ /** Whether auth succeeded */
1195
+ ok: boolean;
1196
+ /** Time taken to complete the check (ms) */
1197
+ latencyMs: number;
1198
+ /** Error message if failed */
1199
+ error?: string;
1200
+ /** Detailed check results */
1201
+ checks: {
1202
+ /** Token acquisition succeeded */
1203
+ tokenAcquisition: boolean;
1204
+ };
453
1205
  }
454
1206
 
455
1207
  /**
456
- * Lesson Types
1208
+ * Base Transport Layer
1209
+ *
1210
+ * HTTP transport with OAuth2 authentication, retries, and error handling.
1211
+ * Clients can extend this for protocol-specific features.
457
1212
  *
458
- * Types for QTI lesson and question feedback resources.
459
- */
460
-
461
-
462
-
463
- // ═══════════════════════════════════════════════════════════════════════════════
464
- // LESSON FEEDBACK
465
- // ═══════════════════════════════════════════════════════════════════════════════
466
-
467
- /**
468
- * Lesson feedback entity.
469
1213
  */
470
- interface LessonFeedback {
471
- questionId?: string
472
- userId: string
473
- feedback: string
474
- type: FeedbackType
475
- lessonId: string
476
- humanApproved?: boolean | boolean[]
477
- }
478
-
479
1214
  /**
480
- * Stimuli Types
1215
+ * Base HTTP transport layer for API communication.
1216
+ *
1217
+ * Handles OAuth2 authentication, request/response lifecycle,
1218
+ * and automatic retries for transient failures.
481
1219
  *
482
- * Types for QTI stimulus resources.
1220
+ * Clients can extend this class to add protocol-specific features
1221
+ * like custom error parsing or pagination.
483
1222
  */
484
-
485
-
486
-
487
- // ═══════════════════════════════════════════════════════════════════════════════
488
- // STIMULUS
489
- // ═══════════════════════════════════════════════════════════════════════════════
490
-
491
- /**
492
- * Stimulus entity.
493
- */
494
- interface Stimulus {
495
- identifier: string
496
- title: string
497
- label?: string
498
- language?: string
499
- stylesheet?: Stylesheet
500
- catalogInfo: CatalogInfo[]
501
- toolName?: string
502
- toolVersion?: string
503
- metadata?: Record<string, unknown>
504
- /** Raw QTI XML string */
505
- rawXml: string
506
- /**
507
- * Parsed XML→JSON content.
508
- * Structure varies.
509
- */
510
- content: Record<string, unknown>
511
- createdAt: string
512
- updatedAt: string
513
- __v?: number
1223
+ declare class BaseTransport {
1224
+ protected readonly config: ResolvedTransportConfig;
1225
+ protected readonly log: Logger;
1226
+ /**
1227
+ * Create a new BaseTransport instance.
1228
+ *
1229
+ * @param options - Transport options with config and logger
1230
+ */
1231
+ constructor(options: BaseTransportOptions);
1232
+ /**
1233
+ * The base URL for API requests.
1234
+ * @returns The base URL
1235
+ */
1236
+ get baseUrl(): string;
1237
+ /**
1238
+ * Make an authenticated request to the API.
1239
+ *
1240
+ * Automatically retries on transient failures (429, 503).
1241
+ *
1242
+ * @template T - Expected response type
1243
+ * @param path - API endpoint path
1244
+ * @param options - Request options including method, params, body
1245
+ * @returns Parsed JSON response
1246
+ * @throws {ApiError} On API errors (4xx/5xx responses)
1247
+ */
1248
+ request<T>(path: string, options?: RequestOptions): Promise<T>;
1249
+ /**
1250
+ * Check whether a resource exists at the given path.
1251
+ *
1252
+ * Returns `true` for successful 2xx responses, `false` only for 404 Not Found,
1253
+ * and rethrows all other errors.
1254
+ *
1255
+ * @param path - API endpoint path
1256
+ * @param options - Request options including method, params, body
1257
+ * @returns Promise resolving to whether the resource exists
1258
+ */
1259
+ exists(path: string, options?: RequestOptions): Promise<boolean>;
1260
+ /**
1261
+ * Make a raw request, returning the Response object.
1262
+ *
1263
+ * ## Retry Behavior
1264
+ * Automatically retries on transient failures (429, 503) with exponential
1265
+ * backoff. Respects Retry-After header when present.
1266
+ *
1267
+ * ## Timeout Behavior
1268
+ * Uses an operation-level timeout that spans ALL retry attempts.
1269
+ * If configured timeout is 30s, the entire operation (including retries
1270
+ * and backoff delays) must complete within 30s total.
1271
+ *
1272
+ * ## Flow
1273
+ * 1. Build URL from path and query params
1274
+ * 2. Start operation timer
1275
+ * 3. Loop up to MAX_RETRIES times:
1276
+ * a. Check if we've exceeded the operation deadline
1277
+ * b. Get OAuth token (may be cached)
1278
+ * c. Make the HTTP request with per-request timeout
1279
+ * d. If 429/503 and not last attempt → calculate backoff and retry
1280
+ * e. If other error → throw appropriate ApiError subclass
1281
+ * f. If success → return response
1282
+ * 4. If all retries exhausted → throw "Max retries exceeded"
1283
+ *
1284
+ * @param path - API endpoint path (relative to baseUrl)
1285
+ * @param options - Request options (method, params, body, headers)
1286
+ * @returns Raw fetch Response for custom handling
1287
+ * @throws {ApiError} On timeout, non-retryable errors, or max retries exceeded
1288
+ */
1289
+ requestRaw(path: string, options?: RequestOptions): Promise<Response>;
1290
+ /**
1291
+ * Get a valid OAuth2 access token.
1292
+ * @returns Promise resolving to access token or undefined
1293
+ */
1294
+ protected getAccessToken(): Promise<string | undefined>;
1295
+ /**
1296
+ * Construct full URL with query parameters.
1297
+ *
1298
+ * @param path - The relative path or absolute URL
1299
+ * @param params - Query parameters
1300
+ * @returns Full URL string
1301
+ * @throws {Error} If path is an absolute URL (security protection)
1302
+ */
1303
+ protected buildUrl(path: string, params?: Record<string, string | number | boolean | undefined>): string;
1304
+ /**
1305
+ * Parse successful response or delegate to error handler.
1306
+ * @param response - The fetch Response
1307
+ * @returns Parsed response as type T
1308
+ */
1309
+ protected handleResponse<T>(response: Response): Promise<T>;
1310
+ /**
1311
+ * Parse JSON response with context-preserving error handling.
1312
+ *
1313
+ * Unlike raw `response.json()`, this method:
1314
+ * - Logs structured error context (URL, status, content-type, body preview)
1315
+ * - Throws `ApiError` with `parseError` and `body` in the response object
1316
+ * - Aids debugging when upstream services return malformed JSON
1317
+ *
1318
+ * @template T - Expected shape of the parsed response
1319
+ * @param response - The fetch Response to parse
1320
+ * @returns Parsed JSON as type T
1321
+ * @throws {ApiError} When JSON parsing fails, with status code and body preview
1322
+ */
1323
+ protected parseJsonResponse<T>(response: Response): Promise<T>;
1324
+ /**
1325
+ * Parse error response and throw appropriate ApiError subclass.
1326
+ *
1327
+ * Handles both JSON and non-JSON error responses gracefully.
1328
+ * Clients can override this to add protocol-specific error parsing.
1329
+ *
1330
+ * @param response - The error Response (status >= 400)
1331
+ * @param requestId - Request ID for log correlation
1332
+ * @throws {UnauthorizedError} For 401 responses (also invalidates token)
1333
+ * @throws {ForbiddenError} For 403 responses
1334
+ * @throws {NotFoundError} For 404 responses
1335
+ * @throws {ValidationError} For 422 responses
1336
+ * @throws {ApiError} For all other error status codes
1337
+ */
1338
+ protected handleErrorResponse(response: Response, requestId?: string): Promise<never>;
1339
+ /**
1340
+ * Extract error message from response body.
1341
+ *
1342
+ * Checks common error formats:
1343
+ * - `message` (most APIs)
1344
+ * - `error` (some APIs)
1345
+ * - `imsx_description` (IMS Global: OneRoster, Caliper, QTI)
1346
+ *
1347
+ * Override in client transports for API-specific error formats
1348
+ * not covered here (e.g., Edubridge's `errors[]` array format).
1349
+ *
1350
+ * @param body - The error response body
1351
+ * @param fallback - Fallback message if none found
1352
+ * @returns Extracted error message
1353
+ */
1354
+ protected extractErrorMessage(body: unknown, fallback: string): string;
1355
+ /**
1356
+ * Delay execution for retry backoff.
1357
+ *
1358
+ * @param ms - Number of milliseconds to delay
1359
+ * @returns Promise that resolves after delay
1360
+ */
1361
+ protected sleep(ms: number): Promise<void>;
1362
+ /**
1363
+ * Parse Retry-After header value.
1364
+ *
1365
+ * Handles both formats per RFC 7231:
1366
+ * - Numeric seconds: "120"
1367
+ * - HTTP-date: "Wed, 21 Oct 2025 07:28:00 GMT"
1368
+ *
1369
+ * @param retryAfter - Retry-After header value
1370
+ * @param attempt - Current attempt number (0-based)
1371
+ * @returns Delay in milliseconds
1372
+ */
1373
+ protected parseRetryAfter(retryAfter: string | null, attempt: number): number;
514
1374
  }
515
1375
 
516
1376
  /**
517
- * Validation Types
1377
+ * Pagination Utilities
518
1378
  *
519
- * Types for QTI XML validation resources.
1379
+ * Helpers for iterating over paginated API responses.
520
1380
  */
521
1381
 
522
- // ═══════════════════════════════════════════════════════════════════════════════
523
- // VALIDATION
524
- // ═══════════════════════════════════════════════════════════════════════════════
525
-
526
1382
  /**
527
- * Single validation result.
1383
+ * Async iterator for paginated API responses.
528
1384
  *
529
- * When the entity exists, the response includes `xmlContent` and `validationErrors`.
530
- * When the entity is not found (batch only), the response includes `error` instead.
531
- */
532
- interface ValidationResult {
533
- success: boolean
534
- entityId: string
535
- xmlContent?: string
536
- validationErrors?: string[]
537
- message: string
538
- error?: string
539
- }
540
-
541
- /**
542
- * Batch validation result.
1385
+ * Automatically fetches subsequent pages as you iterate, making it easy
1386
+ * to process large datasets without manual pagination handling.
1387
+ *
1388
+ * @typeParam T - The type of items being paginated
1389
+ *
1390
+ * @example
1391
+ * ```typescript
1392
+ * // Iterate over all items
1393
+ * for await (const user of paginator) {
1394
+ * console.log(user.name)
1395
+ * }
1396
+ * ```
1397
+ *
1398
+ * @example
1399
+ * ```typescript
1400
+ * // Collect all items into an array
1401
+ * const allUsers = await paginator.toArray()
1402
+ * ```
1403
+ *
1404
+ * @example
1405
+ * ```typescript
1406
+ * // Get just the first page
1407
+ * const page = await paginator.firstPage()
1408
+ * console.log(`Got ${page.data.length} of ${page.total} users`)
1409
+ * ```
543
1410
  */
544
- interface BatchValidationResult {
545
- results: ValidationResult[]
1411
+ declare class Paginator$1<T, F = unknown> implements AsyncIterable<T> {
1412
+ private readonly fetcher;
1413
+ private readonly path;
1414
+ private readonly params;
1415
+ private readonly max?;
1416
+ private readonly unwrapKey?;
1417
+ private readonly log;
1418
+ private readonly transform?;
1419
+ private readonly paginationStyle;
1420
+ /**
1421
+ * Create a new Paginator.
1422
+ *
1423
+ * @param options - Paginator configuration
1424
+ */
1425
+ constructor(options: PaginatorOptions<T, F>);
1426
+ /**
1427
+ * Builds query parameters for the paginated request.
1428
+ *
1429
+ * Converts the type-safe `where` clause to a filter string for the API,
1430
+ * and merges with other list parameters (sort, orderBy, fields, search).
1431
+ * Excludes client-side params like `max` that shouldn't be sent to the API.
1432
+ *
1433
+ * Uses the configured pagination style:
1434
+ * - `'offset'`: Sends `{ limit, offset }` params
1435
+ * - `'page'`: Sends `{ limit, page }` params (1-indexed)
1436
+ *
1437
+ * @param limit - Maximum items per page
1438
+ * @param offset - Number of items to skip (converted to page if using page style)
1439
+ * @returns Query parameters ready for the request
1440
+ */
1441
+ private buildRequestParams;
1442
+ /**
1443
+ * Extracts and validates items from response data.
1444
+ *
1445
+ * Handles two response formats:
1446
+ * - Direct array: `[item1, item2, ...]`
1447
+ * - Wrapped object: `{ users: [item1, item2, ...] }` (when unwrapKey is set)
1448
+ *
1449
+ * @param data - Raw response data from the API
1450
+ * @param pageNumber - Current page number (for error messages)
1451
+ * @returns Validated array of items
1452
+ * @throws {Error} If extracted data is not an array
1453
+ */
1454
+ private extractItems;
1455
+ /**
1456
+ * Validates that extracted data is an array.
1457
+ *
1458
+ * Protects against malformed API responses that could cause:
1459
+ * - Infinite loops (empty non-array values)
1460
+ * - Unexpected iteration (strings yield characters, not items)
1461
+ * - Runtime crashes (objects are not iterable)
1462
+ *
1463
+ * @param data - Data to validate (should be an array)
1464
+ * @param pageNumber - Current page number (for error messages)
1465
+ * @returns The data cast to T[] if valid
1466
+ * @throws {Error} If data is not an array (with helpful message including unwrapKey)
1467
+ */
1468
+ private validateItems;
1469
+ /**
1470
+ * Determines if more pages are available based on response metadata.
1471
+ *
1472
+ * Uses a three-tier fallback strategy:
1473
+ * 1. Link header (most reliable)
1474
+ * 2. X-Total-Count header
1475
+ * 3. Full page heuristic (assumes more if page is full and no total provided)
1476
+ *
1477
+ * Always returns false if the page is empty to prevent infinite loops
1478
+ * from buggy servers that return hasMore: true with no data.
1479
+ * @param response - Response with pagination metadata
1480
+ * @param itemCount - Number of items in current page
1481
+ * @param offset - Current offset
1482
+ * @param limit - Current limit
1483
+ * @returns True if more pages are available
1484
+ */
1485
+ private hasMorePages;
1486
+ /**
1487
+ * Async iterator implementation.
1488
+ *
1489
+ * Yields items one at a time, automatically fetching new pages as needed.
1490
+ * Stops when `max` items have been yielded (if specified).
1491
+ *
1492
+ * @yields Items of type T from paginated responses
1493
+ */
1494
+ [Symbol.asyncIterator](): AsyncIterator<T>;
1495
+ /**
1496
+ * Collect all items into an array.
1497
+ *
1498
+ * **Warning**: Use with caution on large datasets as this loads
1499
+ * all items into memory. Consider iterating with `for await...of`
1500
+ * for better memory efficiency.
1501
+ *
1502
+ * @param options - Optional configuration
1503
+ * @param options.maxItems - Maximum items to collect (default: 10,000).
1504
+ * Throws if limit is reached. Set to `Infinity` to disable.
1505
+ * @returns Promise resolving to an array of all items
1506
+ * @throws {Error} If maxItems limit is exceeded
1507
+ */
1508
+ toArray(options?: ToArrayOptions): Promise<T[]>;
1509
+ /**
1510
+ * Fetch only the first page of results.
1511
+ *
1512
+ * Useful when you need pagination metadata (total count, hasMore)
1513
+ * or want to implement custom pagination UI.
1514
+ *
1515
+ * @returns Promise resolving to the first page with metadata
1516
+ */
1517
+ firstPage(): Promise<PageResult<T>>;
546
1518
  }
547
1519
 
548
1520
  /**
@@ -639,291 +1611,6 @@ declare class Transport extends BaseTransport {
639
1611
  requestPaginated<T>(path: string, options?: RequestOptions): Promise<PaginatedResponse<T>>;
640
1612
  }
641
1613
 
642
- type input<T> = T extends {
643
- _zod: {
644
- input: any;
645
- };
646
- } ? T["_zod"]["input"] : unknown;
647
-
648
- /**
649
- * QTI Schemas
650
- *
651
- * Strict Zod schemas for QTI-related CLI/API inputs.
652
- *
653
- * These are intentionally "full-shape" schemas so CLI code can validate input
654
- * and then pass typed values directly into the QTI SDK without widening/casting.
655
- */
656
-
657
-
658
-
659
- // ═══════════════════════════════════════════════════════════════════════════════
660
- // LIST PARAMS
661
- // ═══════════════════════════════════════════════════════════════════════════════
662
-
663
- declare const QtiPaginationParams = z
664
- .object({
665
- page: z.number().int().positive().optional(),
666
- limit: z.number().int().positive().optional(),
667
- sort: z.string().optional(),
668
- order: z.enum(['asc', 'desc']).optional(),
669
- })
670
- .strict()
671
-
672
- // ═══════════════════════════════════════════════════════════════════════════════
673
- // ASSESSMENT ITEMS (REQUEST INPUTS)
674
- // ═══════════════════════════════════════════════════════════════════════════════
675
-
676
- /**
677
- * XML-format creation input.
678
- *
679
- * The preferred way to create assessment items — send raw QTI 3.0 XML and the
680
- * server validates it against IMS QTI XSDs.
681
- */
682
- declare const QtiAssessmentItemXmlCreateInput = z
683
- .object({
684
- format: z.string().pipe(z.literal('xml')),
685
- xml: NonEmptyString,
686
- metadata: QtiItemMetadata.optional(),
687
- })
688
- .strict()
689
-
690
- /**
691
- * JSON-format creation input (experimental on the server side).
692
- *
693
- * Sends a structured/parsed representation of a QTI item.
694
- */
695
- declare const QtiAssessmentItemJsonCreateInput = z
696
- .object({
697
- identifier: NonEmptyString,
698
- title: NonEmptyString,
699
- type: QtiAssessmentItemType,
700
- qtiVersion: z.string().optional(),
701
- timeDependent: z.boolean().optional(),
702
- adaptive: z.boolean().optional(),
703
- responseDeclarations: z.array(QtiResponseDeclaration).optional(),
704
- outcomeDeclarations: z.array(QtiOutcomeDeclaration).optional(),
705
- responseProcessing: QtiResponseProcessing.optional(),
706
- metadata: QtiItemMetadata.optional(),
707
- modalFeedback: z.array(QtiModalFeedback).optional(),
708
- feedbackInline: z.array(QtiFeedbackInline).optional(),
709
- feedbackBlock: z.array(QtiFeedbackBlock).optional(),
710
- })
711
- .strict()
712
-
713
- /**
714
- * Union of XML and JSON creation inputs for assessment items.
715
- *
716
- * Accepts either `{ format: 'xml', xml, metadata? }` or the structured JSON
717
- * shape with `{ identifier, title, type, ... }`.
718
- */
719
- declare const QtiAssessmentItemCreateInput = z.union([
720
- QtiAssessmentItemXmlCreateInput,
721
- QtiAssessmentItemJsonCreateInput,
722
- ])
723
-
724
- declare const QtiAssessmentItemUpdateInput = z
725
- .object({
726
- identifier: NonEmptyString.optional(),
727
- title: NonEmptyString,
728
- type: QtiAssessmentItemType,
729
- qtiVersion: z.string().optional(),
730
- timeDependent: z.boolean().optional(),
731
- adaptive: z.boolean().optional(),
732
- responseDeclarations: z.array(QtiResponseDeclaration).optional(),
733
- outcomeDeclarations: z.array(QtiOutcomeDeclaration).optional(),
734
- responseProcessing: QtiResponseProcessing.optional(),
735
- metadata: QtiItemMetadata.optional(),
736
- modalFeedback: z.array(QtiModalFeedback).optional(),
737
- feedbackInline: z.array(QtiFeedbackInline).optional(),
738
- feedbackBlock: z.array(QtiFeedbackBlock).optional(),
739
- rawXml: z.string(),
740
- content: z.record(z.string(), z.unknown()),
741
- })
742
- .strict()
743
-
744
- declare const QtiAssessmentItemProcessResponseInput = z
745
- .object({
746
- identifier: NonEmptyString,
747
- response: z.union([z.string(), z.array(z.string())]),
748
- })
749
- .strict()
750
-
751
- // ═══════════════════════════════════════════════════════════════════════════════
752
- // ASSESSMENT TESTS (REQUEST INPUTS)
753
- // ═══════════════════════════════════════════════════════════════════════════════
754
-
755
- declare const QtiAssessmentItemRef = z
756
- .object({
757
- identifier: NonEmptyString,
758
- href: NonEmptyString,
759
- sequence: z.number().int().positive().optional(),
760
- })
761
- .strict()
762
-
763
- declare const QtiAssessmentSection = z
764
- .object({
765
- identifier: NonEmptyString,
766
- title: NonEmptyString,
767
- visible: z.boolean(),
768
- required: z.boolean().optional(),
769
- fixed: z.boolean().optional(),
770
- sequence: z.number().int().positive(),
771
- 'qti-assessment-item-ref': z.array(QtiAssessmentItemRef).optional(),
772
- })
773
- .strict()
774
-
775
- declare const QtiTestPart = z
776
- .object({
777
- identifier: NonEmptyString,
778
- navigationMode: z.string().pipe(QtiNavigationMode),
779
- submissionMode: z.string().pipe(QtiSubmissionMode),
780
- 'qti-assessment-section': z.array(QtiAssessmentSection),
781
- })
782
- .strict()
783
-
784
- declare const QtiReorderItemsInput = z
785
- .object({
786
- items: z.array(QtiAssessmentItemRef).min(1),
787
- })
788
- .strict()
789
-
790
- declare const QtiAssessmentTestMetadataUpdateInput = z
791
- .object({
792
- metadata: z.record(z.string(), z.unknown()).optional(),
793
- })
794
- .strict()
795
-
796
- declare const QtiAssessmentTestCreateInput = z
797
- .object({
798
- identifier: NonEmptyString,
799
- title: NonEmptyString,
800
- qtiVersion: z.string().optional(),
801
- toolName: z.string().optional(),
802
- toolVersion: z.string().optional(),
803
- timeLimit: z.number().optional(),
804
- maxAttempts: z.number().optional(),
805
- toolsEnabled: z.record(z.string(), z.boolean()).optional(),
806
- metadata: z.record(z.string(), z.unknown()).optional(),
807
- 'qti-test-part': z.array(QtiTestPart),
808
- 'qti-outcome-declaration': z.array(QtiTestOutcomeDeclaration).optional(),
809
- })
810
- .strict()
811
-
812
- declare const QtiAssessmentTestUpdateInput = z
813
- .object({
814
- identifier: NonEmptyString.optional(),
815
- title: NonEmptyString,
816
- qtiVersion: z.string().optional(),
817
- toolName: z.string().optional(),
818
- toolVersion: z.string().optional(),
819
- timeLimit: z.number().optional(),
820
- maxAttempts: z.number().optional(),
821
- toolsEnabled: z.record(z.string(), z.boolean()).optional(),
822
- metadata: z.record(z.string(), z.unknown()).optional(),
823
- 'qti-test-part': z.array(QtiTestPart),
824
- 'qti-outcome-declaration': z.array(QtiTestOutcomeDeclaration).optional(),
825
- })
826
- .strict()
827
-
828
- // ═══════════════════════════════════════════════════════════════════════════════
829
- // STIMULI (REQUEST INPUTS)
830
- // ═══════════════════════════════════════════════════════════════════════════════
831
-
832
- declare const QtiStimulusCreateInput = z
833
- .object({
834
- identifier: NonEmptyString,
835
- title: NonEmptyString,
836
- label: z.string().optional(),
837
- language: z.string().optional(),
838
- stylesheet: QtiStylesheet.optional(),
839
- content: z.string(),
840
- catalogInfo: z.array(QtiCatalogInfo).optional(),
841
- toolName: z.string().optional(),
842
- toolVersion: z.string().optional(),
843
- metadata: z.record(z.string(), z.unknown()).optional(),
844
- })
845
- .strict()
846
-
847
- declare const QtiStimulusUpdateInput = z
848
- .object({
849
- identifier: NonEmptyString.optional(),
850
- title: NonEmptyString,
851
- label: z.string().optional(),
852
- language: z.string().optional(),
853
- stylesheet: QtiStylesheet.optional(),
854
- content: z.string(),
855
- catalogInfo: z.array(QtiCatalogInfo).optional(),
856
- toolName: z.string().optional(),
857
- toolVersion: z.string().optional(),
858
- metadata: z.record(z.string(), z.unknown()).optional(),
859
- })
860
- .strict()
861
-
862
- // ═══════════════════════════════════════════════════════════════════════════════
863
- // VALIDATION (REQUEST INPUTS)
864
- // ═══════════════════════════════════════════════════════════════════════════════
865
-
866
- declare const QtiValidateInput = z
867
- .object({
868
- xml: z.string().optional(),
869
- schema: QtiValidationSchema,
870
- entityId: z.string().optional(),
871
- })
872
- .strict()
873
-
874
- declare const QtiValidateBatchInput = z
875
- .object({
876
- xml: z.array(z.string()),
877
- schema: QtiValidationSchema,
878
- entityIds: z.array(z.string()),
879
- })
880
- .strict()
881
-
882
- // ═══════════════════════════════════════════════════════════════════════════════
883
- // LESSON FEEDBACK (REQUEST INPUTS)
884
- // ═══════════════════════════════════════════════════════════════════════════════
885
-
886
- declare const QtiLessonFeedbackInput = z
887
- .object({
888
- questionId: z.string().optional(),
889
- userId: NonEmptyString,
890
- feedback: NonEmptyString,
891
- lessonId: NonEmptyString,
892
- humanApproved: z.boolean().optional(),
893
- })
894
- .strict()
895
-
896
- type ListAssessmentTestsParams = input<typeof QtiPaginationParams>
897
- type ListTestPartsParams = input<typeof QtiPaginationParams>
898
- type ListSectionsParams = input<typeof QtiPaginationParams>
899
- type ListAssessmentItemsParams = input<typeof QtiPaginationParams>
900
- type ListStimuliParams = input<typeof QtiPaginationParams>
901
-
902
- type CreateAssessmentTestRequest = input<typeof QtiAssessmentTestCreateInput>
903
- type UpdateAssessmentTestRequest = input<typeof QtiAssessmentTestUpdateInput>
904
- type UpdateTestMetadataRequest = input<typeof QtiAssessmentTestMetadataUpdateInput>
905
- type CreateTestPartRequest = input<typeof QtiTestPart>
906
- type UpdateTestPartRequest = input<typeof QtiTestPart>
907
- type CreateSectionRequest = input<typeof QtiAssessmentSection>
908
- type UpdateSectionRequest = input<typeof QtiAssessmentSection>
909
- type AddItemRequest = input<typeof QtiAssessmentItemRef>
910
- type ReorderItemsRequest = input<typeof QtiReorderItemsInput>
911
-
912
- type CreateAssessmentItemXmlRequest = input<typeof QtiAssessmentItemXmlCreateInput>
913
- type CreateAssessmentItemJsonRequest = input<typeof QtiAssessmentItemJsonCreateInput>
914
- type CreateAssessmentItemRequest = input<typeof QtiAssessmentItemCreateInput>
915
- type UpdateAssessmentItemRequest = input<typeof QtiAssessmentItemUpdateInput>
916
- type ProcessResponseRequest = input<typeof QtiAssessmentItemProcessResponseInput>
917
-
918
- type CreateStimulusRequest = input<typeof QtiStimulusCreateInput>
919
- type UpdateStimulusRequest = input<typeof QtiStimulusUpdateInput>
920
-
921
- type ValidateRequest = input<typeof QtiValidateInput>
922
- type ValidateBatchRequest = input<typeof QtiValidateBatchInput>
923
-
924
- type SubmitLessonRequest = input<typeof QtiLessonFeedbackInput>
925
- type SubmitQuestionRequest = input<typeof QtiLessonFeedbackInput>
926
-
927
1614
  /**
928
1615
  * Assessment Items Resource
929
1616
  *
@@ -1493,7 +2180,7 @@ declare class ValidateResource {
1493
2180
  declare const QtiClient: {
1494
2181
  new (config?: QtiClientConfig): {
1495
2182
  readonly transport: QtiTransportLike;
1496
- readonly _provider?: _timeback_internal_client_infra.TimebackProvider | undefined;
2183
+ readonly _provider?: TimebackProvider | undefined;
1497
2184
  readonly assessmentItems: AssessmentItemsResource;
1498
2185
  readonly assessmentTests: AssessmentTestsResource;
1499
2186
  readonly stimuli: StimuliResource;
@@ -1501,7 +2188,7 @@ declare const QtiClient: {
1501
2188
  readonly lesson: LessonResource;
1502
2189
  readonly general: GeneralResource;
1503
2190
  getTransport(): QtiTransportLike;
1504
- checkAuth(): Promise<_timeback_internal_client_infra.AuthCheckResult>;
2191
+ checkAuth(): Promise<AuthCheckResult>;
1505
2192
  };
1506
2193
  };
1507
2194
 
@@ -1550,4 +2237,4 @@ declare function createQtiClient(registry?: ProviderRegistry): {
1550
2237
  };
1551
2238
 
1552
2239
  export { Paginator, QtiClient, Transport, createQtiClient };
1553
- export type { AssessmentItem, AssessmentItemRef, AssessmentItemType, AssessmentSection, AssessmentTest, BaseType, BatchValidationResult, Cardinality, DeleteResponse, Difficulty, FeedbackBlock, FeedbackInline, FeedbackType, Grade, ItemMetadata, LessonFeedback, ListResponse, ModalFeedback, NavigationMode, OutcomeDeclaration, PaginationMeta, ProcessResponseResult, QtiClientConfig, QtiClientInstance, QuestionReference, QuestionWithItem, QuestionsResponse, ResponseDeclaration, ResponseProcessing, Stimulus, SubmissionMode, TestPart, ValidationResult };
2240
+ export type { AuthCheckResult, EnvAuth, Environment, ExplicitAuth, ListParams, PageResult, QtiClientConfig, QtiClientInstance };