import { AcademicSessionCreateInput, EnrollInput, LineItemCreateInput, ResultCreateInput, AgentInput, OneRosterCredentialInput, UserCreateInput, SchoolCreateInput, ClassCreateInput, CourseCreateInput, CourseComponentCreateInput, ComponentResourceCreateInput, CourseStructureInput, AssessmentLineItemCreateInput, ResourceCreateInput, AssessmentResultCreateInput, CategoryCreateInput, ScoreScaleCreateInput, OrgCreateInput, EnrollmentCreateInput, DemographicsCreateInput } from '@timeback/types/zod'; import { LineItem, Result, CreateResponse, Resource, Base, ResourceType, AcademicSession, AcademicSessionFilterFields, ClassFilterFields, Class, GradingPeriod, UserFilterFields, User, LineItemFilterFields, ResultFilterFields, ResourceFilterFields, ScoreScaleFilterFields, ScoreScale, CategoryFilterFields, Category, Course, CourseComponentFilterFields, CourseComponent, Organization, EnrollmentFilterFields, Enrollment, CourseFilterFields, CredentialCreateResponse, DecryptedCredential, OrganizationFilterFields, ComponentResourceFilterFields, ComponentResource, AssessmentLineItem, AssessmentLineItemFilterFields, AssessmentResult, AssessmentResultFilterFields, Demographics, DemographicsFilterFields } from '@timeback/types/protocols/oneroster'; import { z } from 'zod/v4'; /** * Type Definitions for `@timeback/internal-logger` * * Central type definitions used across all logger components. * These types define the contract between the logger, formatters, and consumers. */ /** * Log severity levels, ordered from least to most severe. * * - debug: Detailed diagnostic information for developers * - info: General operational messages (app started, request received) * - warn: Something unexpected but not breaking (deprecated API used) * - error: Something failed (request failed, database connection lost) */ type LogLevel = 'debug' | 'info' | 'warn' | 'error'; /** * Runtime environments that determine how logs are formatted. * * - terminal: Local development with colors and icons * - ci: CI/CD pipelines with plain text (no ANSI codes) * - production: JSON lines for log aggregation (DataDog, CloudWatch, etc.) * - browser: Browser console with CSS styling * - test: Test environment with no output */ type Environment$2 = 'terminal' | 'ci' | 'production' | 'browser' | 'test'; /** * Arbitrary key-value data attached to log entries. * * Context is merged into log output - in production it becomes top-level * JSON fields, in terminal it's displayed as key=value pairs. * * @example * log.info('User created', { userId: 123, email: 'foo@bar.com' }) */ type LogContext = Record; /** * A single log entry before it's formatted for output. * * This is the internal representation passed to formatters. * Formatters transform this into environment-specific output. */ interface LogEntry { /** Severity level of this log entry */ level: LogLevel; /** Human-readable log message */ message: string; /** Optional namespace/category (e.g., "api", "db", "auth") */ scope?: string; /** Optional structured data attached to this entry */ context?: LogContext; /** When this log entry was created */ timestamp: Date; } /** * Configuration options for creating a logger instance. */ interface LoggerOptions { /** * Logger scope/namespace for categorizing logs. * Child loggers append to this with colons: "api" → "api:users" */ scope?: string; /** * Minimum log level to output. * Logs below this level are silently ignored. * @default 'info' (or 'debug' if DEBUG env var is set) */ minLevel?: LogLevel; /** * Override automatic environment detection. * Useful for testing or forcing a specific format. */ environment?: Environment$2; /** * Default context added to every log entry from this logger. * Useful for request IDs, user IDs, etc. */ defaultContext?: LogContext; /** * Custom formatter override for this logger instance. * * When provided, this takes precedence over environment-based formatting. * Useful for tests, custom sinks, or temporarily suppressing output. */ formatter?: Formatter; } /** * Function that outputs a log entry in a specific format. * * Each environment has its own formatter. Formatters are responsible * for the actual console.log/write calls. */ type Formatter = (entry: LogEntry) => void; /** * Logger instance with environment-aware formatting. * * Instances are lightweight and can be created freely. * Common pattern: one logger per module/component. */ declare class Logger { /** Namespace for this logger (e.g., "api", "api:users") */ private scope?; /** Minimum level to output (logs below this are ignored) */ private minLevel; /** The detected or configured environment */ private environment; /** Optional explicit formatter override for this logger instance */ private explicitFormatter?; /** Context added to every log entry from this logger */ private defaultContext; /** * Create a new Logger instance. * * Usually you'd use createLogger() instead of new Logger(). */ constructor(options?: LoggerOptions); /** * Create a child logger with an additional scope segment. * * Child loggers inherit minLevel and defaultContext from parent. * Scope is appended with a colon separator. * * @param scope - Additional scope segment to append * @returns New Logger instance with extended scope * * @example * const api = createLogger({ scope: 'api' }) * const users = api.child('users') * users.info('Created') // scope: "api:users" */ child(scope: string): Logger; /** * Create a logger with additional default context. * * The new context is merged with existing default context. * Useful for adding request IDs, user IDs, etc. * * @param context - Additional context to include in all logs * @returns New Logger instance with extended context * * @example * const requestLog = log.withContext({ requestId: 'abc123' }) * requestLog.info('Processing') // requestId included automatically */ withContext(context: LogContext): Logger; /** * Log a debug message. * * Use for detailed diagnostic information useful during development. * These are typically filtered out in production. * * @param message - The log message * @param context - Optional key-value context to include with the log */ debug(message: string, context?: LogContext): void; /** * Log an info message. * * Use for general operational information. * Examples: server started, request received, job completed. * * @param message - The log message * @param context - Optional key-value context to include with the log */ info(message: string, context?: LogContext): void; /** * Log a warning message. * * Use for unexpected but non-breaking issues. * Examples: deprecated API used, retrying operation, approaching limit. * * @param message - The log message * @param context - Optional key-value context to include with the log */ warn(message: string, context?: LogContext): void; /** * Log an error message. * * Use for failures that need attention. * Examples: request failed, database error, unhandled exception. * * @param message - The log message * @param context - Optional key-value context to include with the log */ error(message: string, context?: LogContext): void; /** * Internal method that builds the log entry and passes it to the formatter. * * All public log methods (debug, info, warn, error) delegate here. * * @param level - The log level * @param message - The log message * @param context - Optional key-value context to include with the log */ private log; } /** * Interface for obtaining OAuth2 access tokens. * * Implementations handle token caching and refresh automatically. */ interface TokenProvider { /** * Get a valid access token. * * Returns a cached token if still valid, otherwise fetches a new one. * * @returns A valid access token string * @throws {Error} If token acquisition fails */ getToken(): Promise; /** * Invalidate the cached token. * * Forces the next getToken() call to fetch a fresh token. * Should be called when a request fails with 401 Unauthorized. * * Optional - not all implementations may support invalidation. */ invalidate?(): void; } /** * All supported platforms. */ declare const PLATFORMS: readonly ["BEYOND_AI", "LEARNWITH_AI"]; /** * Where Clause Types * * Type-safe object syntax for building filter expressions. */ /** * Primitive value types that can be used in filters. */ type FilterValue = string | number | boolean | Date; /** * Operators for a single field. * * @example * ```typescript * { status: { ne: 'deleted' } } * { score: { gt: 90 } } * { email: { contains: '@school.edu' } } * { role: { in: ['teacher', 'aide'] } } * ``` */ interface FieldOperators { /** Not equal */ ne?: T; /** Greater than */ gt?: T; /** Greater than or equal */ gte?: T; /** Less than */ lt?: T; /** Less than or equal */ lte?: T; /** Contains substring (strings only) */ contains?: T extends string ? string : never; /** Match any of the values */ in?: T[]; /** Match none of the values */ notIn?: T[]; } /** * A field condition can be: * - A direct value (implies equality) * - An object with operators */ type FieldCondition = T | FieldOperators; /** * Map filter field types to field conditions. * * Each field in F becomes an optional filter condition. * * @typeParam F - Filter fields type (e.g., UserFilterFields) */ type FilterFields = { [K in keyof F]?: FieldCondition; }; /** * OR condition for combining multiple field conditions. */ interface OrCondition { OR: WhereClause[]; } /** * A where clause for filtering entities. * * The type parameter F should be a filter fields type that defines * the available fields and their value types for filtering. * * Multiple fields at the same level are combined with AND. * Use `OR` for explicit OR logic. * * @typeParam F - Filter fields type (e.g., UserFilterFields) * * @example * ```typescript * // Simple equality (implicit AND) * { status: 'active', role: 'teacher' } * // → status='active' AND role='teacher' * ``` * * @example * ```typescript * // With operators * { status: { ne: 'deleted' }, score: { gte: 90 } } * // → status!='deleted' AND score>=90 * ``` * * @example * ```typescript * // OR condition * { OR: [{ role: 'teacher' }, { role: 'aide' }] } * // → role='teacher' OR role='aide' * ``` * * @example * ```typescript * // Match multiple values * { role: { in: ['teacher', 'aide'] } } * // → role='teacher' OR role='aide' * ``` */ type WhereClause = FilterFields | OrCondition; /** * Where Clause to Filter String Conversion * * Converts type-safe where clause objects to OneRoster-compatible filter strings. */ /** * Converts a type-safe WhereClause to a OneRoster-compatible filter string. * * This is the primary function for converting the object-based filter syntax * to the string format expected by the OneRoster API. * * @typeParam T - Entity type being filtered * @param where - The where clause object * @returns The filter string, or `undefined` if the clause is empty * * @example * ```typescript * // Simple equality * whereToFilter({ status: 'active' }) * // → "status='active'" * ``` * * @example * ```typescript * // Multiple fields (implicit AND) * whereToFilter({ status: 'active', role: 'teacher' }) * // → "status='active' AND role='teacher'" * ``` * * @example * ```typescript * // Comparison operators * whereToFilter({ score: { gte: 90, lte: 100 } }) * // → "score>=90 AND score<=100" * ``` * * @example * ```typescript * // Not equal * whereToFilter({ status: { ne: 'deleted' } }) * // → "status!='deleted'" * ``` * * @example * ```typescript * // Contains (substring match) * whereToFilter({ email: { contains: '@school.edu' } }) * // → "email~'@school.edu'" * ``` * * @example * ```typescript * // Match any value (OR) * whereToFilter({ role: { in: ['teacher', 'aide'] } }) * // → "role='teacher' OR role='aide'" * ``` * * @example * ```typescript * // Explicit OR across fields * whereToFilter({ OR: [{ role: 'teacher' }, { status: 'active' }] }) * // → "role='teacher' OR status='active'" * ``` */ declare function whereToFilter(where: WhereClause): string | undefined; /** * Shared Types * * Common types for API client infrastructure. */ /** * Fetch function signature for HTTP requests. * Avoids Bun-specific extensions on `typeof fetch`. */ type FetchFn$1 = (input: string | URL | Request, init?: RequestInit) => Promise; /** * Supported Timeback platform implementations. */ type Platform$1 = (typeof PLATFORMS)[number]; /** * Supported deployment environments. */ type Environment$1 = 'staging' | 'production'; /** * Auth credentials for environment mode. * Token URL is derived automatically from environment. */ interface EnvAuth$1 { clientId: string; clientSecret: string; } /** * Auth credentials for explicit mode. * Includes authUrl for custom APIs. * @deprecated Use separate authUrl and ProviderAuth fields instead */ interface ExplicitAuth$1 { clientId: string; clientSecret: string; authUrl: string; } /** * Base configuration options shared by all modes. */ interface BaseConfig$1 { /** Request timeout in milliseconds */ timeout?: number; /** Custom fetch implementation */ fetch?: FetchFn$1; } /** * Environment-based configuration for Timeback APIs. */ interface EnvConfig extends BaseConfig$1 { /** Timeback platform implementation (defaults to 'BEYOND_AI') */ platform?: Platform$1; /** Target environment - determines base URL and token URL */ env: Environment$1; /** OAuth2 client credentials */ auth: EnvAuth$1; } /** * Environment-based configuration with shared token provider. */ interface TokenProviderEnvConfig extends BaseConfig$1 { /** Timeback platform implementation (defaults to 'BEYOND_AI') */ platform?: Platform$1; /** Target environment - determines base URL */ env: Environment$1; /** Shared token provider (from @timeback/auth) */ tokenProvider: TokenProvider; } /** * Explicit URL configuration for custom APIs. * Supports both authenticated and public/no-auth services. */ interface ExplicitConfig extends BaseConfig$1 { /** API base URL */ baseUrl: string; /** * OAuth2 token URL. Omit for public/no-auth services. * Can also be provided via auth.authUrl (legacy format). */ authUrl?: string; /** * OAuth2 credentials. Required if authUrl is provided. * Supports both ExplicitAuth (with authUrl) and ProviderAuth (without). */ auth?: ExplicitAuth$1 | ProviderAuth; /** * Use a built-in path profile by name. * Defaults to 'BEYOND_AI' if neither pathProfile nor paths is specified. */ pathProfile?: Platform$1; /** Custom path overrides (takes precedence over pathProfile) */ paths?: Partial; /** Not applicable to explicit config — use `EnvConfig` instead */ env?: never; } /** * Use pre-configured transport. */ interface TransportConfig extends BaseConfig$1 { /** Transport configuration */ transport: TransportLike; } /** * HTTP request options. */ interface RequestOptions$1 { /** HTTP method */ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; /** Query parameters to append to the URL */ params?: Record; /** Request body (will be JSON-serialized) */ body?: unknown; /** Additional headers to include */ headers?: Record; /** * Unique identifier for this request. * Used for log correlation and debugging. * Auto-generated if not provided. */ requestId?: string; } /** * Duck-typed transport interface for testing and advanced use. */ interface TransportLike { /** Base URL of the API */ baseUrl: string; /** Make an authenticated request */ request(path: string, options?: RequestOptions$1): Promise; } /** * Configuration using a pre-configured transport. * * For advanced use cases like sharing a transport between clients * or using a custom transport implementation. * * @template T - Transport type (defaults to TransportLike, clients should specify their full transport type) */ interface TransportOnlyConfig { /** Existing transport instance */ transport: T; } /** * Union of all client configuration types. */ type ClientConfig = EnvConfig | TokenProviderEnvConfig | ExplicitConfig | TransportConfig | Partial; /** * Parameters for listing resources with pagination, sorting, and filtering. * * Common across all Timeback APIs (OneRoster, Edubridge, QTI, etc.). * * @typeParam T - Entity type for type-safe `where` clause (defaults to unknown) * * @example * ```typescript * // Type-safe where * client.users.list({ * where: { status: 'active', role: 'teacher' }, * sort: 'familyName', * }) * ``` */ interface ListParams { /** * Maximum items per page. * @default 100 */ limit?: number; /** * Number of items to skip (for pagination). * @default 0 */ offset?: number; /** * Field name to sort results by. * * Type-safe: only valid field names for this resource are accepted. * * @example * ```typescript * sort: 'familyName' * ``` */ sort?: keyof T & string; /** * Sort direction. * @default "asc" */ orderBy?: 'asc' | 'desc'; /** * Type-safe filter using object syntax. * * Multiple fields are combined with AND. Use `OR` for OR logic. * * @example * ```typescript * // Simple equality * where: { status: 'active' } * ``` * * @example * ```typescript * // Multiple fields (AND) * where: { status: 'active', role: 'teacher' } * ``` * * @example * ```typescript * // With operators * where: { score: { gte: 90 }, status: { ne: 'deleted' } } * ``` * * @example * ```typescript * // OR condition * where: { role: { in: ['teacher', 'aide'] } } * ``` */ where?: WhereClause; /** * Fields to include in the response. * Reduces payload size by requesting only needed fields. * @example ['sourcedId', 'givenName', 'familyName'] */ fields?: string[]; /** * Free-text search across multiple fields (proprietary extension). * For users: searches givenName, familyName, email. * @example "john@example.com" */ search?: string; /** * Maximum total items to return across all pages. * * Unlike `limit` (which sets page size), `max` caps the total number * of items yielded by the paginator. Pagination stops once this many * items have been returned. * * @example * ```typescript * // Get at most 50 users total * client.users.list({ max: 50 }) * ``` */ max?: number; } /** * Response from a paginated API request. */ interface PaginatedResponse { /** Array of items in this page */ data: T[]; /** Whether more pages are available */ hasMore: boolean; /** Total count of items (if provided by server) */ total?: number; } /** * Result of fetching a single page of resources. * * @typeParam T - The type of items in the page */ interface PageResult { /** Array of items in this page */ data: T[]; /** Whether more pages are available */ hasMore: boolean; /** Total count of items (if provided by server) */ total?: number; /** Offset to use for fetching the next page */ nextOffset?: number; } /** * Options for toArray() method. */ interface ToArrayOptions { /** * Maximum number of items to collect. * * Throws an error if this limit is exceeded, preventing OOM on large datasets. * Use `for await...of` to stream results instead. * * @default 10_000 * @example Set to Infinity to disable (use with caution!) */ maxItems?: number; } /** * Function that fetches a page of results. * Provided by the transport layer. */ type PageFetcher = (path: string, options: { params: Record; }) => Promise>; /** * Pagination style for API requests. * * - `'offset'`: Uses limit/offset parameters (default, e.g., OneRoster) * - `'page'`: Uses limit/page parameters (1-indexed, e.g., QTI) */ type PaginationStyle = 'offset' | 'page'; /** * Options for creating a Paginator. */ interface PaginatorOptions { /** Function to fetch a page of results */ fetcher: PageFetcher; /** API endpoint path */ path: string; /** List parameters (filter, sort, limit, offset) */ params?: ListParams; /** Maximum total items to return across all pages (client-side cap) */ max?: number; /** Response key containing the items array (e.g., "users") */ unwrapKey?: string; /** Logger instance (defaults to client-common logger) */ logger?: Logger; /** Optional transform function applied to each item before yielding */ transform?: (item: T) => T; /** * Pagination style to use for API requests. * * - `'offset'` (default): Sends `limit` and `offset` params * - `'page'`: Sends `limit` and `page` params (1-indexed) * * @default 'offset' */ paginationStyle?: PaginationStyle; } /** * Result of an auth check operation. */ interface AuthCheckResult$1 { /** Whether auth succeeded */ ok: boolean; /** Time taken to complete the check (ms) */ latencyMs: number; /** Error message if failed */ error?: string; /** Detailed check results */ checks: { /** Token acquisition succeeded */ tokenAcquisition: boolean; }; } /** * Config Types * * Types for TimebackProvider and provider resolution. */ /** * Caliper API path profile. * Defines paths for Caliper operations. Use `null` for unsupported operations. */ interface CaliperPaths { /** Path for sending events (POST) */ send: string; /** Path for validating events (POST), null if not supported */ validate: string | null; /** Path for listing events (GET), null if not supported */ list: string | null; /** Path template for getting single event (GET), use {id} placeholder */ get: string | null; /** Path template for job status (GET), use {id} placeholder */ jobStatus: string | null; } /** * Webhook API path profile. * Defines paths for webhook management operations. * Nullability is at the platform level (`webhooks: WebhookPaths | null` in PlatformPaths). */ interface WebhookPaths { /** Path for listing webhooks (GET) */ webhookList: string; /** Path template for getting a single webhook (GET), use {id} placeholder */ webhookGet: string; /** Path for creating a webhook (POST) */ webhookCreate: string; /** Path template for updating a webhook (PUT), use {id} placeholder */ webhookUpdate: string; /** Path template for deleting a webhook (DELETE), use {id} placeholder */ webhookDelete: string; /** Path template for activating a webhook (PUT), use {id} placeholder */ webhookActivate: string; /** Path template for deactivating a webhook (PUT), use {id} placeholder */ webhookDeactivate: string; /** Path for listing all webhook filters (GET) */ webhookFilterList: string; /** Path template for getting a single webhook filter (GET), use {id} placeholder */ webhookFilterGet: string; /** Path for creating a webhook filter (POST) */ webhookFilterCreate: string; /** Path template for updating a webhook filter (PUT), use {id} placeholder */ webhookFilterUpdate: string; /** Path template for deleting a webhook filter (DELETE), use {id} placeholder */ webhookFilterDelete: string; /** Path template for listing filters by webhook (GET), use {webhookId} placeholder */ webhookFiltersByWebhook: string; } /** * Reporting API path profile. * Defines paths for reporting MCP and REST operations. * Nullability is at the platform level (`reporting: ReportingPaths | null` in PlatformPaths). */ interface ReportingPaths { /** Path for the reporting MCP JSON-RPC endpoint (POST) */ mcp: string; /** Path template for executing a saved query (GET), use {id} placeholder */ savedQueryExecute: string; /** Path template for checking reporting group membership (GET), use {email} placeholder */ adminGroupCheck: string; /** Path template for adding a user to the reporting group (POST), use {email} placeholder */ adminGroupAdd: string; /** Path template for removing a user from the reporting group (DELETE), use {email} placeholder */ adminGroupRemove: string; } /** * OneRoster API path profile. * Defines the base path prefix for all OneRoster resources. */ interface OneRosterPaths { /** Base path prefix for rostering resources (users, schools, classes, etc.) */ rostering: string; /** Base path prefix for gradebook resources (lineItems, results, etc.) */ gradebook: string; /** Base path prefix for resources API (digital learning resources) */ resources: string; } /** * Edubridge API path profile. * Defines path prefixes for Edubridge operations. */ interface EdubridgePaths { /** Base path prefix for all Edubridge resources */ base: string; } /** * PowerPath API path profile. * Defines path prefixes for PowerPath operations. */ interface PowerPathPaths { /** Base path prefix for all PowerPath resources */ base: string; } /** * CASE API path profile. * Defines path prefix for CASE (Competency and Academic Standards Exchange) operations. */ interface CasePaths { /** Base path prefix for all CASE resources */ base: string; } /** * CLR API path profile. * Defines path prefixes for CLR (Comprehensive Learner Record) operations. */ interface ClrPaths { /** Path for upserting CLR credentials (POST) */ credentials: string; /** Path for API discovery (GET) */ discovery: string; } /** * Platform path profiles for all services. * Use `null` to indicate a service is not supported on the platform. */ interface PlatformPaths { caliper: CaliperPaths; oneroster: OneRosterPaths; webhooks: WebhookPaths | null; reporting: ReportingPaths | null; edubridge: EdubridgePaths | null; powerpath: PowerPathPaths | null; clr: ClrPaths | null; case: CasePaths | null; } /** * Services that have path configuration. * Subset of ServiceName - excludes services without path profiles (e.g., 'qti'). */ type PathEnabledService = keyof PlatformPaths; /** * Supported Timeback platform implementations. */ type Platform = (typeof PLATFORMS)[number]; /** * Supported deployment environments. */ type Environment = 'staging' | 'production'; /** * Supported service names. */ type ServiceName = 'oneroster' | 'caliper' | 'webhooks' | 'reporting' | 'edubridge' | 'qti' | 'powerpath' | 'clr' | 'case'; /** * Resolved endpoint for a single service. */ interface ResolvedEndpoint { /** Base URL for the service API */ baseUrl: string; /** OAuth2 token URL for this endpoint. Undefined for public/no-auth services. */ authUrl?: string; } /** * Auth credentials for a provider. */ interface ProviderAuth { clientId: string; clientSecret: string; } /** * Configuration for environment-based provider. * Uses known Timeback platform endpoints. */ interface ProviderEnvConfig { /** Timeback platform (defaults to 'BEYOND_AI') */ platform?: Platform; /** Target environment */ env: Environment; /** OAuth2 credentials */ auth: ProviderAuth; /** Request timeout in milliseconds */ timeout?: number; } /** * Configuration for explicit URL provider. * Single base URL for all services. */ interface ProviderExplicitConfig { /** Base URL for all services */ baseUrl: string; /** OAuth2 token URL. Omit for public/no-auth services. */ authUrl?: string; /** OAuth2 credentials. Required if authUrl is provided. */ auth?: ProviderAuth; /** Request timeout in milliseconds */ timeout?: number; /** * Use a built-in path profile by name. * Defaults to 'BEYOND_AI' if neither pathProfile nor paths is specified. */ pathProfile?: Platform; /** Custom path overrides (takes precedence over pathProfile) */ paths?: Partial; } /** * Configuration for multi-service provider. * Different URLs for different services. */ interface ProviderServicesConfig { /** Per-service base URLs */ services: Partial>; /** OAuth2 token URL. Omit for public/no-auth services. */ authUrl?: string; /** OAuth2 credentials. Required if authUrl is provided. */ auth?: ProviderAuth; /** Request timeout in milliseconds */ timeout?: number; /** * Use a built-in path profile by name. * Defaults to 'BEYOND_AI' if neither pathProfile nor paths is specified. */ pathProfile?: Platform; /** Custom path overrides (takes precedence over pathProfile) */ paths?: Partial; } /** * Union of all provider configuration types. */ type TimebackProviderConfig = ProviderEnvConfig | ProviderExplicitConfig | ProviderServicesConfig; /** * Provider template - endpoints without auth. * Used internally to define available platform+env combinations. */ interface ProviderTemplate { platform: Platform; env: Environment; } /** * Registry of provider templates indexed by platform and environment. * * Use `satisfies ProviderRegistry` when defining a registry for type checking. * * @example * const myRegistry = { * defaultPlatform: 'MY_PLATFORM', * templates: { * MY_PLATFORM: { * staging: { platform: 'BEYOND_AI', env: 'staging' }, * production: { platform: 'BEYOND_AI', env: 'production' }, * }, * }, * } satisfies ProviderRegistry */ interface ProviderRegistry { /** Default platform when none specified */ defaultPlatform: string; /** Available templates indexed by platform → env */ templates: Record>; } /** * Client config that accepts a pre-built provider. */ interface ProviderClientConfig { /** Pre-built provider */ provider: TimebackProvider; } /** * Timeback Provider * * Encapsulates platform connection configuration including endpoints and auth. * Providers are complete "connection" objects that clients consume. */ /** * Timeback Provider - encapsulates a complete platform connection. * * A provider contains everything needed to connect to Timeback APIs: * - Service endpoints (URLs) * - Authentication credentials * - Configuration options * * Providers can be created from: * - Platform + environment (uses known Timeback endpoints) * - Explicit base URL (single URL for all services) * - Per-service URLs (different URLs for each service) * * @example * ```typescript * // Environment-based provider (Timeback hosted) * const provider = new TimebackProvider({ * platform: 'BEYOND_AI', * env: 'staging', * auth: { clientId: '...', clientSecret: '...' }, * }) * ``` * * @example * ```typescript * // Explicit URL provider (self-hosted) * const provider = new TimebackProvider({ * baseUrl: 'https://api.myschool.edu', * authUrl: 'https://auth.myschool.edu/oauth/token', * auth: { clientId: '...', clientSecret: '...' }, * }) * ``` * * @example * ```typescript * // Per-service URLs * const provider = new TimebackProvider({ * services: { * oneroster: 'https://roster.myschool.edu', * caliper: 'https://analytics.myschool.edu', * }, * authUrl: 'https://auth.myschool.edu/oauth/token', * auth: { clientId: '...', clientSecret: '...' }, * }) * ``` */ declare class TimebackProvider { /** Platform identifier (if using known platform) */ readonly platform?: Platform; /** Environment (if using known platform) */ readonly env?: Environment; /** OAuth2 credentials. Undefined for public/no-auth services. */ readonly auth?: ProviderAuth; /** Request timeout in milliseconds */ readonly timeout: number; /** Resolved endpoints for each service */ /** @internal */ readonly _endpoints: Partial>; /** Token URL for authentication. Undefined for public/no-auth services. */ /** @internal */ readonly _authUrl?: string; /** OAuth2 scope to request with access tokens. */ /** @internal */ readonly _tokenScope?: string; /** API path profiles for this platform */ /** @internal */ readonly _pathProfiles: PlatformPaths; /** Cached TokenManagers by authUrl (for token sharing) */ /** @internal */ readonly _tokenManagers: Map; /** * Create a new TimebackProvider. * * @param config - Provider configuration (env-based, explicit URL, or per-service) * @throws {Error} If configuration is invalid or missing required fields */ constructor(config: TimebackProviderConfig); /** * Get the resolved endpoint for a specific service. * * @param service - Service name (oneroster, caliper, edubridge, qti, powerpath) * @returns Resolved endpoint with baseUrl and authUrl * @throws If the service is not configured in this provider */ getEndpoint(service: ServiceName): ResolvedEndpoint; /** * Check if a service is available in this provider. * * @param service - Service name to check * @returns True if the service is configured */ hasService(service: ServiceName): boolean; /** * Get all configured service names. * * @returns Array of service names available in this provider */ getAvailableServices(): ServiceName[]; /** * Get the token URL for this provider. * @returns The token URL for authentication */ getTokenUrl(): string | undefined; /** * Get endpoint with paths for a service that has path configuration. * * @param service - Service name that has paths in PlatformPaths * @returns Resolved endpoint with baseUrl, authUrl, and paths * @throws If service is not configured or not supported on this platform */ getEndpointWithPaths(service: S & ServiceName): ResolvedEndpoint & { paths: NonNullable; }; /** * Get all path profiles for this provider (raw, may contain nulls). * * @returns Platform path profiles */ getPaths(): PlatformPaths; /** * Get paths for a specific service. * * @param service - Service name * @returns Path configuration for the service * @throws If the service is not supported on this platform */ getServicePaths(service: S): NonNullable; /** * Check if a service is supported on this platform. * * @param service - Service name * @returns true if the service has path configuration */ hasServiceSupport(service: PathEnabledService): boolean; /** * Get a TokenProvider for a specific service. * * TokenProviders are cached by authUrl, so services sharing the same * token endpoint will share the same cached OAuth tokens. * * @param service - Service name (oneroster, caliper, edubridge, qti, powerpath) * @returns Cached TokenProvider for the service's token endpoint, or undefined for public/no-auth services * @throws If the service is not configured in this provider * @throws If auth is required but not configured */ getTokenProvider(service: ServiceName): TokenProvider | undefined; /** * Verify that OAuth authentication is working. * * Attempts to acquire a token using the provider's credentials. * Returns a health check result with success/failure and latency info. * * @returns Auth check result * @throws {Error} If no auth is configured on this provider */ checkAuth(): Promise; /** * Invalidate all cached OAuth tokens. * * Call this when closing the client or when tokens need to be refreshed. * New tokens will be acquired on the next API call. */ invalidateTokens(): void; } /** * Transport Types * * Types for HTTP transport layer. */ /** * Fetch function signature for HTTP requests. * Avoids Bun-specific extensions on `typeof fetch`. */ type FetchFn = (input: string | URL | Request, init?: RequestInit) => Promise; /** * HTTP request options. */ interface RequestOptions { /** HTTP method */ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'; /** Query parameters to append to the URL */ params?: Record; /** Request body (will be JSON-serialized) */ body?: unknown; /** Additional headers to include */ headers?: Record; /** * Unique identifier for this request. * Used for log correlation and debugging. * Auto-generated if not provided. */ requestId?: string; } /** * Auth credentials for environment mode. * Token URL is derived automatically from environment. */ interface EnvAuth { clientId: string; clientSecret: string; } /** * Auth credentials for explicit mode. * Requires explicit authUrl for custom APIs. */ interface ExplicitAuth { clientId: string; clientSecret: string; authUrl: string; } /** * Base configuration options shared by all modes. */ interface BaseConfig { /** Request timeout in milliseconds */ timeout?: number; /** Custom fetch implementation */ fetch?: FetchFn; } /** * Internal resolved transport configuration. */ interface ResolvedTransportConfig { /** Base URL of the API */ baseUrl: string; /** Request timeout in milliseconds */ timeout: number; /** Fetch implementation */ fetch: FetchFn; /** Token provider for authentication. Undefined for public/no-auth services. */ tokenProvider?: TokenProvider; } /** * Transport configuration with explicit auth. */ interface TransportConfigWithAuth extends BaseConfig { baseUrl: string; auth: ExplicitAuth; tokenProvider?: never; } /** * Transport configuration with shared token provider. */ interface TransportConfigWithTokenProvider extends BaseConfig { baseUrl: string; tokenProvider: TokenProvider; auth?: never; } /** * Transport configuration for public/no-auth services. */ interface TransportConfigNoAuth extends BaseConfig { baseUrl: string; auth?: never; tokenProvider?: never; } /** * Configuration for BaseTransport. */ type BaseTransportConfig = TransportConfigWithAuth | TransportConfigWithTokenProvider | TransportConfigNoAuth; /** * Options for creating a BaseTransport. */ interface BaseTransportOptions { /** Transport configuration */ config: BaseTransportConfig; /** Logger instance for request/response logging */ logger: Logger; } /** * Result of an auth check operation. */ interface AuthCheckResult { /** Whether auth succeeded */ ok: boolean; /** Time taken to complete the check (ms) */ latencyMs: number; /** Error message if failed */ error?: string; /** Detailed check results */ checks: { /** Token acquisition succeeded */ tokenAcquisition: boolean; }; } /** * Base Transport Layer * * HTTP transport with OAuth2 authentication, retries, and error handling. * Clients can extend this for protocol-specific features. * */ /** * Base HTTP transport layer for API communication. * * Handles OAuth2 authentication, request/response lifecycle, * and automatic retries for transient failures. * * Clients can extend this class to add protocol-specific features * like custom error parsing or pagination. */ declare class BaseTransport { protected readonly config: ResolvedTransportConfig; protected readonly log: Logger; /** * Create a new BaseTransport instance. * * @param options - Transport options with config and logger */ constructor(options: BaseTransportOptions); /** * The base URL for API requests. * @returns The base URL */ get baseUrl(): string; /** * Make an authenticated request to the API. * * Automatically retries on transient failures (429, 503). * * @template T - Expected response type * @param path - API endpoint path * @param options - Request options including method, params, body * @returns Parsed JSON response * @throws {ApiError} On API errors (4xx/5xx responses) */ request(path: string, options?: RequestOptions): Promise; /** * Check whether a resource exists at the given path. * * Returns `true` for successful 2xx responses, `false` only for 404 Not Found, * and rethrows all other errors. * * @param path - API endpoint path * @param options - Request options including method, params, body * @returns Promise resolving to whether the resource exists */ exists(path: string, options?: RequestOptions): Promise; /** * Make a raw request, returning the Response object. * * ## Retry Behavior * Automatically retries on transient failures (429, 503) with exponential * backoff. Respects Retry-After header when present. * * ## Timeout Behavior * Uses an operation-level timeout that spans ALL retry attempts. * If configured timeout is 30s, the entire operation (including retries * and backoff delays) must complete within 30s total. * * ## Flow * 1. Build URL from path and query params * 2. Start operation timer * 3. Loop up to MAX_RETRIES times: * a. Check if we've exceeded the operation deadline * b. Get OAuth token (may be cached) * c. Make the HTTP request with per-request timeout * d. If 429/503 and not last attempt → calculate backoff and retry * e. If other error → throw appropriate ApiError subclass * f. If success → return response * 4. If all retries exhausted → throw "Max retries exceeded" * * @param path - API endpoint path (relative to baseUrl) * @param options - Request options (method, params, body, headers) * @returns Raw fetch Response for custom handling * @throws {ApiError} On timeout, non-retryable errors, or max retries exceeded */ requestRaw(path: string, options?: RequestOptions): Promise; /** * Get a valid OAuth2 access token. * @returns Promise resolving to access token or undefined */ protected getAccessToken(): Promise; /** * Construct full URL with query parameters. * * @param path - The relative path or absolute URL * @param params - Query parameters * @returns Full URL string * @throws {Error} If path is an absolute URL (security protection) */ protected buildUrl(path: string, params?: Record): string; /** * Parse successful response or delegate to error handler. * @param response - The fetch Response * @returns Parsed response as type T */ protected handleResponse(response: Response): Promise; /** * Parse JSON response with context-preserving error handling. * * Unlike raw `response.json()`, this method: * - Logs structured error context (URL, status, content-type, body preview) * - Throws `ApiError` with `parseError` and `body` in the response object * - Aids debugging when upstream services return malformed JSON * * @template T - Expected shape of the parsed response * @param response - The fetch Response to parse * @returns Parsed JSON as type T * @throws {ApiError} When JSON parsing fails, with status code and body preview */ protected parseJsonResponse(response: Response): Promise; /** * Parse error response and throw appropriate ApiError subclass. * * Handles both JSON and non-JSON error responses gracefully. * Clients can override this to add protocol-specific error parsing. * * @param response - The error Response (status >= 400) * @param requestId - Request ID for log correlation * @throws {UnauthorizedError} For 401 responses (also invalidates token) * @throws {ForbiddenError} For 403 responses * @throws {NotFoundError} For 404 responses * @throws {ValidationError} For 422 responses * @throws {ApiError} For all other error status codes */ protected handleErrorResponse(response: Response, requestId?: string): Promise; /** * Extract error message from response body. * * Checks common error formats: * - `message` (most APIs) * - `error` (some APIs) * - `imsx_description` (IMS Global: OneRoster, Caliper, QTI) * * Override in client transports for API-specific error formats * not covered here (e.g., Edubridge's `errors[]` array format). * * @param body - The error response body * @param fallback - Fallback message if none found * @returns Extracted error message */ protected extractErrorMessage(body: unknown, fallback: string): string; /** * Delay execution for retry backoff. * * @param ms - Number of milliseconds to delay * @returns Promise that resolves after delay */ protected sleep(ms: number): Promise; /** * Parse Retry-After header value. * * Handles both formats per RFC 7231: * - Numeric seconds: "120" * - HTTP-date: "Wed, 21 Oct 2025 07:28:00 GMT" * * @param retryAfter - Retry-After header value * @param attempt - Current attempt number (0-based) * @returns Delay in milliseconds */ protected parseRetryAfter(retryAfter: string | null, attempt: number): number; } /** * Pagination Utilities * * Helpers for iterating over paginated API responses. */ /** * Async iterator for paginated API responses. * * Automatically fetches subsequent pages as you iterate, making it easy * to process large datasets without manual pagination handling. * * @typeParam T - The type of items being paginated * * @example * ```typescript * // Iterate over all items * for await (const user of paginator) { * console.log(user.name) * } * ``` * * @example * ```typescript * // Collect all items into an array * const allUsers = await paginator.toArray() * ``` * * @example * ```typescript * // Get just the first page * const page = await paginator.firstPage() * console.log(`Got ${page.data.length} of ${page.total} users`) * ``` */ declare class Paginator$1 implements AsyncIterable { private readonly fetcher; private readonly path; private readonly params; private readonly max?; private readonly unwrapKey?; private readonly log; private readonly transform?; private readonly paginationStyle; /** * Create a new Paginator. * * @param options - Paginator configuration */ constructor(options: PaginatorOptions); /** * Builds query parameters for the paginated request. * * Converts the type-safe `where` clause to a filter string for the API, * and merges with other list parameters (sort, orderBy, fields, search). * Excludes client-side params like `max` that shouldn't be sent to the API. * * Uses the configured pagination style: * - `'offset'`: Sends `{ limit, offset }` params * - `'page'`: Sends `{ limit, page }` params (1-indexed) * * @param limit - Maximum items per page * @param offset - Number of items to skip (converted to page if using page style) * @returns Query parameters ready for the request */ private buildRequestParams; /** * Extracts and validates items from response data. * * Handles two response formats: * - Direct array: `[item1, item2, ...]` * - Wrapped object: `{ users: [item1, item2, ...] }` (when unwrapKey is set) * * @param data - Raw response data from the API * @param pageNumber - Current page number (for error messages) * @returns Validated array of items * @throws {Error} If extracted data is not an array */ private extractItems; /** * Validates that extracted data is an array. * * Protects against malformed API responses that could cause: * - Infinite loops (empty non-array values) * - Unexpected iteration (strings yield characters, not items) * - Runtime crashes (objects are not iterable) * * @param data - Data to validate (should be an array) * @param pageNumber - Current page number (for error messages) * @returns The data cast to T[] if valid * @throws {Error} If data is not an array (with helpful message including unwrapKey) */ private validateItems; /** * Determines if more pages are available based on response metadata. * * Uses a three-tier fallback strategy: * 1. Link header (most reliable) * 2. X-Total-Count header * 3. Full page heuristic (assumes more if page is full and no total provided) * * Always returns false if the page is empty to prevent infinite loops * from buggy servers that return hasMore: true with no data. * @param response - Response with pagination metadata * @param itemCount - Number of items in current page * @param offset - Current offset * @param limit - Current limit * @returns True if more pages are available */ private hasMorePages; /** * Async iterator implementation. * * Yields items one at a time, automatically fetching new pages as needed. * Stops when `max` items have been yielded (if specified). * * @yields Items of type T from paginated responses */ [Symbol.asyncIterator](): AsyncIterator; /** * Collect all items into an array. * * **Warning**: Use with caution on large datasets as this loads * all items into memory. Consider iterating with `for await...of` * for better memory efficiency. * * @param options - Optional configuration * @param options.maxItems - Maximum items to collect (default: 10,000). * Throws if limit is reached. Set to `Infinity` to disable. * @returns Promise resolving to an array of all items * @throws {Error} If maxItems limit is exceeded */ toArray(options?: ToArrayOptions): Promise; /** * Fetch only the first page of results. * * Useful when you need pagination metadata (total count, hasMore) * or want to implement custom pagination UI. * * @returns Promise resolving to the first page with metadata */ firstPage(): Promise>; } /** * Common interface for resources that support streaming/listing. * * All OneRoster resource classes implement this interface, allowing * generic code to work with any resource type. * * @typeParam T - The entity type returned by the resource */ interface StreamableResource { /** * List the first page of resources. * @returns Promise resolving to the first page (data + pagination metadata) */ list(params?: Record): Promise>; /** * List all resources, fetching all pages automatically. * @returns Promise resolving to an array of all matching resources */ listAll?(params?: Record): Promise; /** * Get the first matching resource, or undefined if none match. * @returns The first matching resource, or undefined */ first?(params?: Record): Promise; /** * Stream resources with lazy pagination. * @returns Async iterable for streaming results */ stream(params?: Record): AsyncIterable; /** * Get a single resource by sourcedId. */ get(sourcedId: string): Promise; } /** * Pagination Utilities * * Re-exports the common Paginator with OneRoster-specific configuration. */ /** * OneRoster-specific Paginator that uses the OneRoster transport. * * Accepts params with `max` included for consumer convenience, * then extracts and forwards it to the base Paginator. * * Validates list parameters before making any network requests. * * @typeParam T - The type of items being paginated * @typeParam F - The filter fields type for type-safe where/sort */ declare class Paginator extends Paginator$1 { /** * Create a new OneRoster Paginator. * * @param transport - OneRoster transport instance * @param path - API endpoint path * @param params - List parameters including optional max * @param unwrapKey - Response key containing the items array (e.g., "users") * @param transform - Optional transform function applied to each item * @throws {InputValidationError} If params are invalid (e.g., negative limit) */ constructor(transport: OneRosterTransportLike, path: string, params?: ListParams, unwrapKey?: string, transform?: (item: T) => T); } /** * Transport Layer * * Extends BaseTransport for OneRoster API communication. */ /** * HTTP transport layer for OneRoster API communication. * * Uses header-based pagination (Link, X-Total-Count) per IMS Global spec. */ declare class Transport extends BaseTransport { /** API path profiles for OneRoster operations */ readonly paths: OneRosterPaths; constructor(config: OneRosterTransportConfig); /** * Make a paginated request using header-based pagination. * * OneRoster APIs return pagination metadata in HTTP headers: * - `Link` header with `rel="next"` for more pages * - `X-Total-Count` header for total item count * * @template T - Expected item type in the response array * @param path - API endpoint path * @param options - Request options * @returns Response data with hasMore flag and optional total count */ requestPaginated(path: string, options?: RequestOptions): Promise>; } /** * Line Items Resource * * Manage gradebook line items (assignments, assessments, etc.). */ /** * Scoped resource for operations on a specific line item. * * Access via `client.lineItems(lineItemId)`. * * @example * ```typescript * const lineItem = await client.lineItems(lineItemId).get() * * // To get results for a line item, use the class-scoped path: * const results = await client.classes(classId).lineItem(lineItemId).results() * ``` */ declare class ScopedLineItemResource { private readonly transport; private readonly basePath; private readonly lineItemId; constructor(transport: OneRosterTransportLike, lineItemId: string); /** * Get the line item details. * @returns The line item details */ get(): Promise; /** * Check whether this line item exists. * @returns True if found, false for 404 */ exists(): Promise; /** * Bulk create results for this line item. * * Use this to submit multiple student grades at once. * * @param results - Array of results to create * @returns Create response with sourcedIdPairs * @example * ```typescript * await client.lineItems(lineItemId).createResults([ * { student: { sourcedId: 'student1' }, score: 85 }, * { student: { sourcedId: 'student2' }, score: 92 }, * ]) * ``` */ createResults(results: Partial[]): Promise; } /** * Resources Resource * * Manage digital learning resources. */ /** * Scoped resource for operations on a specific resource. * * Access via `client.resources(resourceId)`. * * @example * ```typescript * const resource = await client.resources(resourceId).get() * ``` */ declare class ScopedResourceResource { private readonly transport; private readonly basePath; private readonly resourceId; constructor(transport: OneRosterTransportLike, resourceId: string); /** * Get the resource details. * @returns The resource object */ get(): Promise; /** * Check whether this resource exists. * @returns True if found, false for 404 */ exists(): Promise; /** * Export this resource. * @returns Common Cartridge zip file as an ArrayBuffer */ export(): Promise; } /** * Base Resource * * Abstract base class for all OneRoster resources. * Provides common CRUD operations. */ /** * Abstract base class for all OneRoster resources. * * Provides common CRUD operations with automatic response unwrapping. * * @template T - The resource type (must extend Base) - used for responses * @template F - The filter fields type for type-safe where clauses * @template I - The input type for create/update (defaults to Partial) * @template P - The list params type (defaults to ListParams) * @template W - The write return type for `update()`/`upsert()` (defaults to `void`). * Subclasses whose API returns the updated entity (e.g. gradebook/assessment) * set `W` to the entity type and **must** override `update()` and `upsert()` * with concrete implementations — the base class returns `undefined as W` * which is only sound when `W = void`. */ declare abstract class BaseResource, P extends ListParams = ListParams, W = void> { protected readonly transport: OneRosterTransportLike; /** Full path for this resource (pathPrefix + suffix) */ protected readonly basePath: string; /** * @param transport - Transport instance for making requests * @param resourceType - Type of resource (rostering or gradebook) * @param suffix - Resource suffix (e.g., '/users', '/lineItems') */ constructor(transport: OneRosterTransportLike, resourceType: ResourceType, suffix: string); /** * List the first page of resources (Caliper-style). * * This returns a single page plus metadata (`hasMore`, `total`, `nextOffset`). * Use `stream()` for lazy iteration or `listAll()` to fetch everything. * * @param params - Optional `where` clause, sorting, and pagination parameters * @returns Promise resolving to the first page of matching resources * * @example * ```typescript * const page = await client.users.list() * console.log(page.data.length, page.hasMore) * ``` */ list(params?: P): Promise>; /** * List all resources, fetching all pages automatically. * * @param params - Optional `where` clause, sorting, and pagination parameters * @returns Promise resolving to an array of all matching resources */ listAll(params?: P): Promise; /** * Get the first matching resource, or undefined if none match. * * @param params - Optional `where` clause parameters * @returns The first matching resource, or undefined * * @example * ```typescript * const user = await client.users.first({ where: { email } }) * if (!user) throw new Error('User not found') * ``` */ first(params?: P): Promise; /** * Stream resources with lazy pagination. * * Use this for large datasets where you want to process items * one at a time without loading everything into memory. * * @param params - Optional `where` clause, sorting, and pagination parameters * @returns Async iterable paginator for streaming results * @throws {InputValidationError} If params are invalid (e.g., negative limit) * * @example * ```typescript * for await (const user of client.users.stream()) { * console.log(user.name) * } * ``` */ stream(params?: P): Paginator; /** * Get a single resource by sourcedId. * * @param sourcedId - The unique identifier of the resource * @returns The requested resource * @throws {InputValidationError} If sourcedId is empty * @throws {NotFoundError} If the resource doesn't exist */ get(sourcedId: string): Promise; /** * Check whether a single resource exists by sourcedId. * * @param sourcedId - The unique identifier of the resource * @returns True if found, false for 404 * @throws {InputValidationError} If sourcedId is empty * @throws {ApiError} For non-404 failures */ exists(sourcedId: string): Promise; /** * Create a new resource. * * @param data - The resource data to create * @returns Response containing the created resource's sourcedId * @throws {InputValidationError} If the data fails client-side validation * @throws {ValidationError} If the server rejects the data */ create(data: I): Promise; /** * Update an existing resource (full replacement). * * Always throws NotFoundError if the resource doesn't exist, regardless * of whether the server's PUT handler natively upserts. * * @param sourcedId - The unique identifier of the resource to update * @param data - The fields to update * @returns Void on success * @throws {InputValidationError} If sourcedId is empty or data fails client-side validation * @throws {NotFoundError} If the resource doesn't exist * @throws {ValidationError} If the server rejects the data */ update(sourcedId: string, data: Partial): Promise; /** * Create or update a resource by sourcedId. * * If the resource exists it is updated; otherwise it is created. * Works consistently across all resources regardless of server-side * upsert support. * * @param sourcedId - The unique identifier of the resource * @param data - Resource data (sourcedId is provided separately) * @returns Void on success * @throws {InputValidationError} If sourcedId is empty or data fails validation * @throws {ApiError} For non-404 failures */ upsert(sourcedId: string, data: Omit): Promise; /** * Send a PUT request (no existence pre-check). * Shared by `update()` and `upsert()`. * * @param sourcedId - The unique identifier of the resource to update * @param data - The fields to update * @returns Void on success * @throws {InputValidationError} If data fails client-side validation * @throws {ValidationError} If the server rejects the data */ protected sendUpdate(sourcedId: string, data: Partial): Promise; /** * Send a PUT request and return the updated entity. * * @param sourcedId - The unique identifier of the resource to update * @param data - The fields to update * @returns The updated resource * @throws {InputValidationError} If data fails client-side validation * @throws {ValidationError} If the server rejects the data */ protected sendUpdateAndReturn(sourcedId: string, data: Partial): Promise; /** * Enforce strict update behavior for resources backed by native server upsert. * * @param sourcedId - The unique identifier of the resource to update * @throws {NotFoundError} If the resource doesn't exist */ protected ensureExistsForUpdate(sourcedId: string): Promise; /** * Delete a resource. * * @param sourcedId - The unique identifier of the resource to delete * @throws {InputValidationError} If sourcedId is empty * @throws {NotFoundError} If the resource doesn't exist */ delete(sourcedId: string): Promise; /** * The key used to unwrap list responses (e.g., "users", "classes"). * Override in subclasses. */ protected abstract get unwrapKey(): string; /** * The key used to wrap request bodies (e.g., "user", "class"). * Override in subclasses. */ protected abstract get wrapKey(): string; /** * Transform a response entity before returning it. * * Override in subclasses to normalize API responses (e.g., convert grades from strings to numbers). * Default implementation returns the entity unchanged. * * @param entity - The raw entity from the API * @returns The transformed entity */ protected transform(entity: T): T; /** * Zod schema for validating create input. * * Override in subclasses to enable client-side validation before create requests. * * @returns Zod schema for validation, or undefined to skip validation */ protected get createSchema(): z.ZodTypeAny | undefined; /** * Zod schema for validating update input. * * Override in subclasses to enable client-side validation before update requests. * * @returns Zod schema for validation, or undefined to skip validation */ protected get updateSchema(): z.ZodTypeAny | undefined; /** * Zod schema for validating patch input. * * Override in subclasses to enable client-side validation before patch requests. * * @returns Zod schema for validation, or undefined to skip validation */ protected get patchSchema(): z.ZodTypeAny | undefined; /** * Whether the server's PUT handler creates the resource if it doesn't exist. * * When true, `update()` adds a pre-check via `exists()` so it consistently * throws NotFoundError across all resources. Override in gradebook/assessment * subclasses where the server natively upserts on PUT. * * @returns True if the server natively upserts, false otherwise */ protected get serverNativelyUpserts(): boolean; /** * Human-readable resource name for error messages. * Derived from wrapKey by default (e.g., "user", "class"). * * @returns Resource name for error messages */ protected get resourceName(): string; /** * Unwrap a single-item response. * * @param response - Raw API response object * @returns The unwrapped resource * @throws {Error} If expected key is missing from response */ protected unwrapSingle(response: Record): T; /** * Wrap data for POST/PUT requests. * * @param data - Resource data to wrap * @returns Wrapped request body */ protected wrapBody(data: I): Record; } /** * Academic Sessions Resource * * Manage academic sessions (terms, semesters, school years, grading periods). */ /** * Resource for all academic sessions regardless of type. * * For type-specific access, use TermsResource or GradingPeriodsResource. */ declare class AcademicSessionsResource extends BaseResource> { constructor(transport: OneRosterTransportLike); protected get unwrapKey(): string; protected get wrapKey(): string; protected get createSchema(): z.ZodTypeAny; protected get updateSchema(): z.ZodTypeAny; protected transform(session: AcademicSession): AcademicSession; } /** * Scoped resource for operations on a specific term. * * Access via `client.terms(termId)`. * * @example * ```typescript * const term = await client.terms(termId).get() * const classes = await client.terms(termId).classes() * ``` */ declare class ScopedTermResource { private readonly transport; private readonly basePath; constructor(transport: OneRosterTransportLike, termId: string); /** * Get the term details. * @returns The term object */ get(): Promise; /** * Check whether this term exists. * @returns True if found, false for 404 */ exists(): Promise; /** * List classes in this term. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to class array */ classes(params?: ListParamsNoSearch): Promise; /** * Stream classes with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming classes */ streamClasses(params?: ListParamsNoSearch): Paginator; /** * List grading periods in this term. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to grading period array */ gradingPeriods(params?: ListParamsNoSearch): Promise; /** * Stream grading periods with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming grading periods */ streamGradingPeriods(params?: ListParamsNoSearch): Paginator; /** * Create a grading period in this term. * * @param data - Grading period data * @returns Create response with sourcedIdPairs * @throws {InputValidationError} If required fields are missing */ createGradingPeriod(data: AcademicSessionCreateInput): Promise; } /** * Resource for grading periods only. * * Filtered view of academic sessions with grading period type. */ declare class GradingPeriodsResource extends BaseResource> { constructor(transport: OneRosterTransportLike); protected get unwrapKey(): string; protected get wrapKey(): string; protected get createSchema(): z.ZodTypeAny; protected get updateSchema(): z.ZodTypeAny; protected transform(gp: AcademicSession): AcademicSession; /** * XXX: The Beyond-AI API expects grading period creates/updates to be wrapped as * `{ academicSession: ... }`, even though GET responses use `gradingPeriod`. * * @param data - Academic session input payload for a grading period * @returns Wrapped request body */ protected wrapBody(data: AcademicSessionCreateInput): Record; } /** * Classes Resource * * Manage classes (sections) and their nested resources. */ /** * Scoped resource for a specific academic session within a class. * * Access via `client.classes(classId).academicSession(sessionId)`. * * @example * ```typescript * // Create a result for a student in this class/session * await client.classes(classId).academicSession(sessionId).createResult({ * student: { sourcedId: 'student1' }, * lineItem: { sourcedId: 'li1' }, * scoreStatus: 'fully graded', * scoreDate: '2024-12-24', * score: 85, * }) * ``` */ declare class ScopedClassAcademicSessionResource { private readonly transport; private readonly gradebookPath; constructor(transport: OneRosterTransportLike, classId: string, sessionId: string); /** * Create a result for this class and academic session. * * @param result - Result data including student, lineItem, score, and scoreDate * @returns Create response with sourcedIdPairs * @throws {InputValidationError} If required fields are missing */ createResult(result: ResultCreateInput): Promise; } /** * Scoped resource for a specific student within a class. * * Access via `client.classes(classId).student(studentId)`. * * @example * ```typescript * const results = await client.classes(classId).student(studentId).results() * ``` */ declare class ScopedClassStudentResource { private readonly transport; private readonly gradebookPath; constructor(transport: OneRosterTransportLike, classId: string, studentId: string); /** * List results for this student in this class. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to result array */ results(params?: ListParamsNoSearch): Promise; /** * Stream results with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming results */ streamResults(params?: ListParamsNoSearch): Paginator; } /** * Scoped resource for a specific line item within a class. * * Access via `client.classes(classId).lineItem(lineItemId)`. * * @example * ```typescript * const results = await client.classes(classId).lineItem(lineItemId).results() * ``` */ declare class ScopedClassLineItemResource { private readonly transport; private readonly gradebookPath; constructor(transport: OneRosterTransportLike, classId: string, lineItemId: string); /** * List results for this line item in this class. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to result array */ results(params?: ListParamsNoSearch): Promise; /** * Stream results with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming results */ streamResults(params?: ListParamsNoSearch): Paginator; } /** * Scoped resource for operations on a specific class. * * Access via `client.classes(classId)`. * * @example * ```typescript * const cls = await client.classes(classId).get() * const students = await client.classes(classId).students() * const teachers = await client.classes(classId).teachers() * const lineItems = await client.classes(classId).lineItems() * const studentResults = await client.classes(classId).student(studentId).results() * ``` */ declare class ScopedClassResource { private readonly transport; private readonly rosteringPath; private readonly gradebookPath; private readonly classId; constructor(transport: OneRosterTransportLike, classId: string); /** * Get the class details. * @returns The class object */ get(): Promise; /** * Check whether this class exists. * @returns True if found, false for 404 */ exists(): Promise; /** * List students in this class. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to user array */ students(params?: ListParamsNoSearch): Promise; /** * Stream students with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming students */ streamStudents(params?: ListParamsNoSearch): Paginator; /** * List teachers in this class. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to user array */ teachers(params?: ListParamsNoSearch): Promise; /** * Stream teachers with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming teachers */ streamTeachers(params?: ListParamsNoSearch): Paginator; /** * Enroll a user in this class. * * @param input - User sourcedId, role, and optional enrollment details * @returns Create response * @example * ```typescript * await client.classes(classId).enroll({ sourcedId: studentId, role: 'student' }) * await client.classes(classId).enroll({ sourcedId: teacherId, role: 'teacher', primary: true }) * ``` */ enroll(input: EnrollInput): Promise; /** * List line items (assignments) in this class. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to line item array */ lineItems(params?: ListParamsNoSearch): Promise; /** * Stream line items with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming line items */ streamLineItems(params?: ListParamsNoSearch): Paginator; /** * Create a line item (assignment) in this class. * * @param data - Line item data * @returns Create response with sourcedIdPairs * @example * ```typescript * await client.classes(classId).createLineItem({ * title: 'Homework 1', * class: { sourcedId: classId }, * school: { sourcedId: schoolId }, * category: { sourcedId: categoryId }, * assignDate: '2024-12-20', * dueDate: '2024-12-27', * status: 'active', * }) * ``` */ createLineItem(data: LineItemCreateInput): Promise; /** * List results in this class. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to result array */ results(params?: ListParamsNoSearch): Promise; /** * Stream results with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming results */ streamResults(params?: ListParamsNoSearch): Paginator; /** * List resources for this class. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to resource array */ resources(params?: ListParamsNoSearch): Promise; /** * Stream resources with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming resources */ streamResources(params?: ListParamsNoSearch): Paginator; /** * Get a scoped resource for a specific student in this class. * @param studentId - Student ID * @returns Scoped resource for the student * @example * ```typescript * const results = await client.classes(classId).student(studentId).results() * ``` */ student(studentId: string): ScopedClassStudentResource; /** * Get a scoped resource for a specific line item in this class. * @param lineItemId - Line item ID * @returns Scoped resource for the line item * @example * ```typescript * const results = await client.classes(classId).lineItem(lineItemId).results() * ``` */ lineItem(lineItemId: string): ScopedClassLineItemResource; /** * Get a scoped resource for a specific academic session in this class. * @param sessionId - Academic session ID (term, grading period, etc.) * @returns Scoped resource for session-scoped operations * @example * ```typescript * // Create a result for a grading period * await client.classes(classId).academicSession(sessionId).createResult({ * student: { sourcedId: 'student1' }, * lineItem: { sourcedId: 'lineItem1' }, * scoreStatus: 'fully graded', * scoreDate: '2024-12-24', * score: 85, * }) * ``` */ academicSession(sessionId: string): ScopedClassAcademicSessionResource; /** * List score scales for this class. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to score scale array */ scoreScales(params?: ListParamsNoSearch): Promise; /** * Stream score scales with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming score scales */ streamScoreScales(params?: ListParamsNoSearch): Paginator; /** * List categories for this class. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to category array */ categories(params?: ListParamsNoSearch): Promise; /** * Stream categories with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming categories */ streamCategories(params?: ListParamsNoSearch): Paginator; } /** * Courses Resource * * Manage courses and their nested resources. */ /** * Scoped resource for operations on a specific course. * * Access via `client.courses(courseId)`. * * @example * ```typescript * const course = await client.courses(courseId).get() * const classes = await client.courses(courseId).classes() * const components = await client.courses(courseId).components() * const resources = await client.courses(courseId).resources() * ``` */ declare class ScopedCourseResource { private readonly transport; private readonly basePath; private readonly courseId; constructor(transport: OneRosterTransportLike, courseId: string); /** * Get the course details. * @returns The course object */ get(): Promise; /** * Check whether this course exists. * @returns True if found, false for 404 */ exists(): Promise; /** * List classes for this course. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to class array */ classes(params?: ListParamsNoSearch): Promise; /** * Stream classes with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming classes */ streamClasses(params?: ListParamsNoSearch): Paginator; /** * List components for this course. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to component array */ components(params?: ListParamsNoSearch): Promise; /** * Stream components with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming components */ streamComponents(params?: ListParamsNoSearch): Paginator; /** * List resources for this course. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to resource array */ resources(params?: ListParamsNoSearch): Promise; /** * Stream resources with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming resources */ streamResources(params?: ListParamsNoSearch): Paginator; } /** * Schools Resource * * Schools are a filtered view of organizations with nested resources. */ /** * Scoped resource for a specific class within a school. * * Access via `client.schools(schoolId).class(classId)`. * * @example * ```typescript * const enrollments = await client.schools(schoolId).class(classId).enrollments() * ``` */ declare class ScopedSchoolClassResource { private readonly transport; private readonly basePath; constructor(transport: OneRosterTransportLike, schoolId: string, classId: string); /** * List enrollments for this class within this school. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to enrollment array */ enrollments(params?: ListParamsNoSearch): Promise; /** * Stream enrollments with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming enrollments */ streamEnrollments(params?: ListParamsNoSearch): Paginator; /** * List students in this class at this school. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to user array */ students(params?: ListParamsNoSearch): Promise; /** * Stream students with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming students */ streamStudents(params?: ListParamsNoSearch): Paginator; /** * List teachers for this class at this school. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to user array */ teachers(params?: ListParamsNoSearch): Promise; /** * Stream teachers with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming teachers */ streamTeachers(params?: ListParamsNoSearch): Paginator; } /** * Scoped resource for operations on a specific school. * * Access via `client.schools(schoolId)`. * * @example * ```typescript * const school = await client.schools(schoolId).get() * const classes = await client.schools(schoolId).classes() * const students = await client.schools(schoolId).students() * const teachers = await client.schools(schoolId).teachers() * const lineItems = await client.schools(schoolId).lineItems() * const enrollments = await client.schools(schoolId).class(classId).enrollments() * ``` */ declare class ScopedSchoolResource { private readonly transport; private readonly basePath; private readonly gradebookPath; private readonly schoolId; constructor(transport: OneRosterTransportLike, schoolId: string); /** * Get the school details. * @returns The organization/school object */ get(): Promise; /** * Check whether this school exists. * @returns True if found, false for 404 */ exists(): Promise; /** * Scope to a specific class within this school. * @param classId - The class sourcedId * @returns Scoped class resource for school-class operations */ class(classId: string): ScopedSchoolClassResource; /** * List classes in this school. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to class array */ classes(params?: ListParamsNoSearch): Promise; /** * Stream classes with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming classes */ streamClasses(params?: ListParamsNoSearch): Paginator; /** * List enrollments in this school. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to enrollment array */ enrollments(params?: ListParamsNoSearch): Promise; /** * Stream enrollments with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming enrollments */ streamEnrollments(params?: ListParamsNoSearch): Paginator; /** * List students in this school. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to user array */ students(params?: ListParamsNoSearch): Promise; /** * Stream students with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming students */ streamStudents(params?: ListParamsNoSearch): Paginator; /** * List teachers in this school. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to user array */ teachers(params?: ListParamsNoSearch): Promise; /** * Stream teachers with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming teachers */ streamTeachers(params?: ListParamsNoSearch): Paginator; /** * List courses in this school. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to course array */ courses(params?: ListParamsNoSearch): Promise; /** * Stream courses with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming courses */ streamCourses(params?: ListParamsNoSearch): Paginator; /** * List terms/academic sessions for this school. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to academic session array */ terms(params?: ListParamsNoSearch): Promise; /** * Stream terms with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming academic sessions */ streamTerms(params?: ListParamsNoSearch): Paginator; /** * List line items (assignments) in this school. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to line item array */ lineItems(params?: ListParamsNoSearch): Promise; /** * Stream line items with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming line items */ streamLineItems(params?: ListParamsNoSearch): Paginator; /** * Create a line item (assignment) in this school. * * @param data - Line item data * @returns Create response with sourcedIdPairs * @example * ```typescript * await client.schools(schoolId).createLineItem({ * title: 'Homework 1', * class: { sourcedId: classId }, * school: { sourcedId: schoolId }, * category: { sourcedId: categoryId }, * assignDate: '2024-12-20', * dueDate: '2024-12-27', * status: 'active', * }) * ``` */ createLineItem(data: LineItemCreateInput): Promise; /** * List score scales for this school. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to score scale array */ scoreScales(params?: ListParamsNoSearch): Promise; /** * Stream score scales with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming score scales */ streamScoreScales(params?: ListParamsNoSearch): Paginator; } /** * Users Resource * * Access all users regardless of role. */ /** * Scoped resource for operations on a specific user. * * Access via `client.users(userId)`. * * @example * ```typescript * const user = await client.users(userId).get() * const resources = await client.users(userId).resources() * ``` */ declare class ScopedUserResource { private readonly transport; private readonly basePath; private readonly userId; constructor(transport: OneRosterTransportLike, userId: string); /** * Get the user details. * @returns The user object */ get(): Promise; /** * Check whether this user exists. * @returns True if found, false for 404 */ exists(): Promise; /** * Get the user with their demographic information. * * Returns the user object with the `demographics` field populated. * * @returns The user object with demographics */ demographics(): Promise; /** * List resources for this user. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to resource array */ resources(params?: ListParamsNoSearch): Promise; /** * Stream resources with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming resources */ streamResources(params?: ListParamsNoSearch): Paginator; /** * List classes for this user. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to class array */ classes(params?: ListParamsNoSearch): Promise; /** * Stream classes with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming classes */ streamClasses(params?: ListParamsNoSearch): Paginator; /** * Get users that this user is an agent (guardian/parent) for. * * For example, if this user is a parent, returns their children. * * @returns Array of users this user represents */ agentFor(): Promise; /** * Get agents (guardians/parents) for this user. * * For example, if this user is a student, returns their parents/guardians. * * @returns Array of agent users */ agents(): Promise; /** * Add an agent (guardian/parent) relationship for this user. * * @param data - Agent input with the agent user reference * @returns Promise resolving when complete * @throws {InputValidationError} If agentSourcedId is empty */ addAgent(data: AgentInput): Promise; /** * Remove an agent (guardian/parent) relationship from this user. * * @param agentSourcedId - The sourcedId of the agent to remove * @returns Promise resolving when complete * @throws {InputValidationError} If agentSourcedId is empty */ removeAgent(agentSourcedId: string): Promise; /** * Register credentials for this user for a third-party application. * * @param input - Application name and credentials (username + password) * @returns Response with userProfileId, credentialId, and message * @throws {InputValidationError} If required fields are empty */ registerCredential(input: z.input): Promise; /** * Decrypt a user's credential to retrieve the password. * * @param credentialId - The credential ID to decrypt * @returns Decrypted credential with password * @throws {InputValidationError} If credentialId is empty */ decryptCredential(credentialId: string): Promise; } /** * Scoped resource for operations on a specific student. * * Access via `client.students(studentId)`. * * @example * ```typescript * const student = await client.students(studentId).get() * const classes = await client.students(studentId).classes() * ``` */ declare class ScopedStudentResource { private readonly transport; private readonly basePath; constructor(transport: OneRosterTransportLike, studentId: string); /** * Get the student details. * @returns The user object */ get(): Promise; /** * Check whether this student exists. * @returns True if found, false for 404 */ exists(): Promise; /** * List classes this student is enrolled in. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to class array */ classes(params?: ListParamsNoSearch): Promise; /** * Stream classes with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming classes */ streamClasses(params?: ListParamsNoSearch): Paginator; } /** * Scoped resource for operations on a specific teacher. * * Access via `client.teachers(teacherId)`. * * @example * ```typescript * const teacher = await client.teachers(teacherId).get() * const classes = await client.teachers(teacherId).classes() * ``` */ declare class ScopedTeacherResource { private readonly transport; private readonly basePath; constructor(transport: OneRosterTransportLike, teacherId: string); /** * Get the teacher details. * @returns The user object */ get(): Promise; /** * Check whether this teacher exists. * @returns True if found, false for 404 */ exists(): Promise; /** * List classes this teacher is assigned to. * @param params - Optional `where` clause and pagination parameters * @returns Promise resolving to class array */ classes(params?: ListParamsNoSearch): Promise; /** * Stream classes with lazy pagination. * @param params - Optional `where` clause and pagination parameters * @returns Paginator for streaming classes */ streamClasses(params?: ListParamsNoSearch): Paginator; } /** * List params for endpoints that support the `search` query param. * * NOTE: Timeback currently only honors `search` on a small subset of * OneRoster endpoints. Use `ListParamsNoSearch` everywhere else to avoid * misleading/ignored params. */ type ListParamsWithSearch = ListParams; /** * List params for endpoints that do NOT support the `search` query param. * Passing `search` should be a type error. */ type ListParamsNoSearch = Omit, 'search'>; /** * Base interface for callable resources. * Resources that support scoping by ID implement this pattern. * * @template T - The entity type * @template F - Filter fields type * @template S - Scoped resource type returned when called with an ID * @template I - Input type for create/update (defaults to Partial) */ interface CallableResource, P extends ListParams = ListParams, W = void> { /** Get a scoped resource for a specific entity */ (id: string): S; /** List the first page of entities */ list(params?: P): Promise>; /** List all entities (fetches all pages) */ listAll(params?: P): Promise; /** Get the first matching entity, or undefined if none match */ first(params?: P): Promise; /** Stream entities with lazy pagination */ stream(params?: P): Paginator; /** Get a single entity by ID */ get(id: string): Promise; /** Check whether a single entity exists by ID */ exists(id: string): Promise; /** Create a new entity */ create(data: I): Promise; /** Update an existing entity */ update(id: string, data: Partial): Promise; /** Create or update an entity (falls back to create on 404) */ upsert(id: string, data: Omit): Promise; /** Delete an entity */ delete(id: string): Promise; } /** * Read-only callable resource (no create/update/delete). * * @template T - The entity type * @template F - Filter fields type * @template S - Scoped resource type returned when called with an ID */ interface ReadOnlyCallableResource = ListParams> { /** Get a scoped resource for a specific entity */ (id: string): S; /** List the first page of entities */ list(params?: P): Promise>; /** List all entities (fetches all pages) */ listAll(params?: P): Promise; /** Get the first matching entity, or undefined if none match */ first(params?: P): Promise; /** Stream entities with lazy pagination */ stream(params?: P): Paginator; /** Get a single entity by ID */ get(id: string): Promise; /** Check whether a single entity exists by ID */ exists(id: string): Promise; } /** * Callable courses resource. * * @example * ```typescript * // Collection operations * const courses = await client.courses.list() * const course = await client.courses.get(id) * * // Scoped operations * const classes = await client.courses(courseId).classes() * ``` */ interface CoursesCallable extends CallableResource> { /** List all course components */ components(params?: ListParamsNoSearch): Promise; /** Stream course components with lazy pagination */ streamComponents(params?: ListParamsNoSearch): Paginator; /** Get a specific course component */ getComponent(sourcedId: string): Promise; /** Create a new course component */ createComponent(data: CourseComponentCreateInput): Promise; /** Update a course component */ updateComponent(sourcedId: string, data: CourseComponentCreateInput): Promise; /** Delete a course component */ deleteComponent(sourcedId: string): Promise; /** List all component resources */ componentResources(params?: ListParamsNoSearch): Promise; /** Stream component resources with lazy pagination */ streamComponentResources(params?: ListParamsNoSearch): Paginator; /** Get a specific component resource */ getComponentResource(sourcedId: string): Promise; /** Create a new component resource */ createComponentResource(data: ComponentResourceCreateInput): Promise; /** Update a component resource */ updateComponentResource(sourcedId: string, data: ComponentResourceCreateInput): Promise; /** Delete a component resource */ deleteComponentResource(sourcedId: string): Promise; /** Create a course structure from QTI tests */ createStructure(data: CourseStructureInput): Promise; } /** * Callable classes resource. * * @example * ```typescript * // Collection operations * const classes = await client.classes.list() * const cls = await client.classes.get(id) * * // Scoped operations * const students = await client.classes(classId).students() * const lineItems = await client.classes(classId).lineItems() * ``` */ type ClassesCallable = CallableResource>; /** * Callable schools resource. * * @example * ```typescript * // Collection operations * const schools = await client.schools.list() * const school = await client.schools.get(id) * * // Scoped operations * const classes = await client.schools(schoolId).classes() * const students = await client.schools(schoolId).students() * ``` */ type SchoolsCallable = CallableResource>; /** * Callable users resource. * * @example * ```typescript * // Collection operations * const users = await client.users.list() * const user = await client.users.get(id) * * // Scoped operations * const resources = await client.users(userId).resources() * const classes = await client.users(userId).classes() * ``` */ type UsersCallable = CallableResource>; /** * Callable students resource (read-only). * * @example * ```typescript * // Collection operations * const students = await client.students.list() * const student = await client.students.get(id) * * // Scoped operations * const classes = await client.students(studentId).classes() * ``` */ type StudentsCallable = ReadOnlyCallableResource>; /** * Callable teachers resource (read-only). * * @example * ```typescript * // Collection operations * const teachers = await client.teachers.list() * const teacher = await client.teachers.get(id) * * // Scoped operations * const classes = await client.teachers(teacherId).classes() * ``` */ type TeachersCallable = ReadOnlyCallableResource>; /** * Callable terms resource (read-only). * * @example * ```typescript * // Collection operations * const terms = await client.terms.list() * const term = await client.terms.get(id) * * // Scoped operations * const classes = await client.terms(termId).classes() * ``` */ type TermsCallable = ReadOnlyCallableResource>; /** * Callable line items resource. * * @example * ```typescript * // Collection operations * const lineItems = await client.lineItems.list() * const lineItem = await client.lineItems.get(id) * * // Scoped operations * const results = await client.lineItems(lineItemId).results() * ``` */ interface LineItemsCallable extends CallableResource, LineItem> { } /** * Callable assessment line items resource. * * @example * ```typescript * // Collection operations * const lineItems = await client.assessmentLineItems.list() * const lineItem = await client.assessmentLineItems.get(id) * * // Partial update * await client.assessmentLineItems.patch(id, { score: 95 }) * ``` */ interface AssessmentLineItemsCallable extends CallableResource, AssessmentLineItem> { /** Partially update an assessment line item (only specified fields are changed) */ patch(id: string, data: Partial): Promise; } /** * Callable resources resource. * * @example * ```typescript * // Collection operations * const resources = await client.resources.list() * const resource = await client.resources.get(id) * * // Nested resource queries (following convention) * const classResources = await client.classes(classId).resources() * const courseResources = await client.courses(courseId).resources() * const userResources = await client.users(userId).resources() * * // Export * await client.resources.export(resourceId) * ``` */ interface ResourcesCallable extends CallableResource> { /** Export a resource to Common Cartridge (.zip) as an ArrayBuffer */ export(resourceId: string): Promise; } /** * Transport interface for OneRoster client. * * Extends base transport requirements with OneRoster-specific paths and pagination. * Required when using transport mode with OneRosterClient. */ interface OneRosterTransportLike { /** Base URL of the API */ baseUrl: string; /** API path profiles for OneRoster operations */ paths: OneRosterPaths; /** Make an authenticated request */ request(path: string, options?: RequestOptions): Promise; /** Check whether a resource exists */ exists(path: string, options?: RequestOptions): Promise; /** Make a raw request, returning the Response object (useful for binary responses) */ requestRaw(path: string, options?: RequestOptions): Promise; /** Make a paginated request using header-based pagination */ requestPaginated(path: string, options?: RequestOptions): Promise>; } /** * Configuration options for creating a OneRosterClient. * * Accepts one of six mutually exclusive modes: * 1. **Provider**: Provide a pre-built `TimebackProvider` instance * 2. **Explicit**: Provide `baseUrl` and `auth` with `authUrl` * 3. **Environment**: Provide `platform?`, `env` and `auth` (URLs derived automatically) * 4. **Token Provider**: Provide `platform?`, `env` and `tokenProvider` (shared auth) * 5. **Transport**: Provide a pre-configured `transport` instance with paths * 6. **Env Vars**: Omit config entirely (reads from environment variables) * * The `platform` field selects which Timeback implementation to use: * - `'BEYOND_AI'` (default): BeyondAI's Timeback platform * - `'LEARNWITH_AI'`: Samy's LearnWith.ai platform */ type OneRosterClientConfig = ClientConfig | TransportOnlyConfig | ProviderClientConfig; /** * Configuration for OneRoster transport. */ type OneRosterTransportConfig = BaseTransportConfig & { /** API path profiles for OneRoster operations */ paths: OneRosterPaths; }; /** * Instance type of OneRosterClient. */ type OneRosterClientInstance = InstanceType; /** * Assessment Line Items Resource * * Manage standardized assessment line items. */ /** * Scoped resource for operations on a specific assessment line item. * * Access via `client.assessmentLineItems(lineItemId)`. * * @example * ```typescript * const lineItem = await client.assessmentLineItems(lineItemId).get() * * // To get results for an assessment line item, use assessmentResults with a where clause: * const results = await client.assessmentResults.list({ * where: { 'assessmentLineItem.sourcedId': lineItemId } * }) * ``` */ declare class ScopedAssessmentLineItemResource { private readonly transport; private readonly basePath; constructor(transport: OneRosterTransportLike, lineItemId: string); /** * Get the assessment line item details. * @returns The assessment line item details */ get(): Promise; /** * Check whether this assessment line item exists. * @returns True if found, false for 404 */ exists(): Promise; } /** * Assessment Results Resource * * Manage standardized assessment results. */ /** * Resource for managing assessment results. */ declare class AssessmentResultsResource extends BaseResource, AssessmentResult> { constructor(transport: OneRosterTransportLike); /** * Partially update an assessment result. * * Only the fields provided will be updated. Other fields remain unchanged. * * @param sourcedId - The assessment result sourcedId * @param data - The fields to update * @throws {InputValidationError} If sourcedId is empty */ patch(sourcedId: string, data: Partial): Promise; protected get unwrapKey(): string; protected get wrapKey(): string; protected get createSchema(): z.ZodTypeAny; protected get updateSchema(): z.ZodTypeAny; protected get serverNativelyUpserts(): boolean; update(sourcedId: string, data: Partial): Promise; upsert(sourcedId: string, data: Omit): Promise; } /** * Results Resource * * Manage student grades and scores. */ /** * Resource for managing gradebook results (grades). */ declare class ResultsResource extends BaseResource, Result> { constructor(transport: OneRosterTransportLike); /** * Create a new result (grade). * * @param data - Result data including required lineItem, student, scoreStatus, and scoreDate * @returns Response containing the created result's sourcedId * @throws {InputValidationError} If required fields are missing * @throws {ValidationError} If the server rejects the data */ create(data: ResultCreateInput): Promise; protected get unwrapKey(): string; protected get wrapKey(): string; protected get createSchema(): z.ZodTypeAny; protected get updateSchema(): z.ZodTypeAny; protected get serverNativelyUpserts(): boolean; update(sourcedId: string, data: Partial): Promise; upsert(sourcedId: string, data: Omit): Promise; } /** * Categories Resource * * Manage gradebook categories (e.g., "Homework", "Tests", "Projects"). */ /** * Resource for managing gradebook categories. */ declare class CategoriesResource extends BaseResource, Category> { constructor(transport: OneRosterTransportLike); protected get unwrapKey(): string; protected get wrapKey(): string; protected get createSchema(): z.ZodTypeAny; protected get updateSchema(): z.ZodTypeAny; protected get serverNativelyUpserts(): boolean; update(sourcedId: string, data: Partial): Promise; upsert(sourcedId: string, data: Omit): Promise; } /** * Score Scales Resource * * Manage grading rubrics and score definitions. */ /** * Resource for managing score scales (grading rubrics). */ declare class ScoreScalesResource extends BaseResource, ScoreScale> { constructor(transport: OneRosterTransportLike); protected get unwrapKey(): string; protected get wrapKey(): string; protected get createSchema(): z.ZodTypeAny; protected get updateSchema(): z.ZodTypeAny; protected get serverNativelyUpserts(): boolean; update(sourcedId: string, data: Partial): Promise; upsert(sourcedId: string, data: Omit): Promise; } /** * Organizations Resource * * Manage organizations (schools, districts, departments). */ /** * Resource for all organizations regardless of type. * * For school-specific access with nested resources, use SchoolsResource. */ declare class OrgsResource extends BaseResource> { constructor(transport: OneRosterTransportLike); protected get unwrapKey(): string; protected get wrapKey(): string; protected get createSchema(): z.ZodTypeAny; protected get updateSchema(): z.ZodTypeAny; } /** * Enrollments Resource * * Manage user-to-class enrollments. */ /** * Resource for managing enrollments (user-to-class relationships). * * Uses EnrollmentCreateInput for create/update (simple refs with just sourcedId) * and returns Enrollment for get/list (full refs with href, type, name). */ declare class EnrollmentsResource extends BaseResource> { constructor(transport: OneRosterTransportLike); /** * Partially update an enrollment. * * Only the fields provided will be updated. Other fields remain unchanged. * * @param sourcedId - The enrollment sourcedId * @param data - The fields to update * @throws {InputValidationError} If sourcedId is empty or data fails validation */ patch(sourcedId: string, data: Partial): Promise; protected get unwrapKey(): string; protected get wrapKey(): string; protected get createSchema(): z.ZodTypeAny; protected get updateSchema(): z.ZodTypeAny; protected get patchSchema(): z.ZodTypeAny; } /** * Demographics Resource * * Manage student demographic data. */ /** * Resource for managing student demographics. * * Supports full CRUD operations per the OneRoster spec. */ declare class DemographicsResource extends BaseResource> { constructor(transport: OneRosterTransportLike); protected get unwrapKey(): string; protected get wrapKey(): string; protected get createSchema(): z.ZodTypeAny; protected get updateSchema(): z.ZodTypeAny; } /** * OneRoster Client * * Main entry point for the OneRoster SDK. */ /** * OneRoster API client for rostering and gradebook operations. * * Provides access to all OneRoster v1.2 resources including users, classes, * enrollments, courses, grades, and assessments. * * @example * ```typescript * // Environment mode (Timeback APIs) * const client = new OneRosterClient({ * env: 'staging', // or 'production' * auth: { * clientId: 'your-client-id', * clientSecret: 'your-client-secret', * }, * }) * ``` * * @example * ```typescript * // Platform selection * const client = new OneRosterClient({ * platform: 'LEARNWITH_AI', // or 'BEYOND_AI' (default) * env: 'production', * auth: { * clientId: 'your-client-id', * clientSecret: 'your-client-secret', * }, * }) * ``` * * @example * ```typescript * // Provider mode (pre-built provider) * import { TimebackProvider } from '@timeback/internal-client-infra' * * const provider = new TimebackProvider({ * platform: 'BEYOND_AI', * env: 'staging', * auth: { clientId: '...', clientSecret: '...' }, * }) * * const client = new OneRosterClient({ provider }) * ``` * * @example * ```typescript * // Explicit mode (custom OneRoster API) * const client = new OneRosterClient({ * baseUrl: 'https://api.example.com', * auth: { * clientId: 'your-client-id', * clientSecret: 'your-client-secret', * authUrl: 'https://auth.example.com/oauth2/token', * }, * }) * ``` * * @example * ```typescript * // Environment variables fallback * // Set ONEROSTER_BASE_URL, ONEROSTER_TOKEN_URL, * // ONEROSTER_CLIENT_ID, ONEROSTER_CLIENT_SECRET * const client = new OneRosterClient() * ``` * * @example * ```typescript * // Nested resources * const classes = await client.schools.for('school-id').classes() * const students = await client.classes.for('class-id').students() * ``` * * @remarks * The client supports four configuration modes: * * 1. **Provider mode**: `{ provider: TimebackProvider }` — Use a pre-built provider * 2. **Environment mode**: `{ env, auth }` — Connect to Timeback platforms * 3. **Explicit mode**: `{ baseUrl, auth: { authUrl } }` — Custom OneRoster API * 4. **Transport mode**: `{ transport }` — Use existing transport instance * * If no config is provided, the client reads from environment variables. * * @see {@link createOneRosterClient} for creating platform-specific clients */ declare const OneRosterClient: { new (config?: OneRosterClientConfig): { readonly transport: OneRosterTransportLike; readonly _provider?: TimebackProvider | undefined; readonly users: UsersCallable; readonly students: StudentsCallable; readonly teachers: TeachersCallable; readonly orgs: OrgsResource; readonly schools: SchoolsCallable; readonly classes: ClassesCallable; readonly courses: CoursesCallable; readonly enrollments: EnrollmentsResource; readonly academicSessions: AcademicSessionsResource; readonly terms: TermsCallable; readonly gradingPeriods: GradingPeriodsResource; readonly demographics: DemographicsResource; readonly lineItems: LineItemsCallable; readonly results: ResultsResource; readonly categories: CategoriesResource; readonly scoreScales: ScoreScalesResource; readonly assessmentLineItems: AssessmentLineItemsCallable; readonly assessmentResults: AssessmentResultsResource; readonly resources: ResourcesCallable; getTransport(): OneRosterTransportLike; checkAuth(): Promise; }; }; /** * OneRoster Client Factory * * Creates OneRosterClient classes bound to specific provider registries. * Enables platform-specific packages and custom deployments. */ /** * Create a OneRosterClient class bound to a specific provider registry. * * This factory enables: * - Platform-specific packages (different default registries) * - Custom registries for enterprise deployments * - Easier testing with mock registries * * @param registry - Provider registry to use (defaults to all Timeback platforms) * @returns OneRosterClient class bound to the registry * * @example * ```typescript * // Default usage (all platforms) * const OneRosterClient = createOneRosterClient() * const client = new OneRosterClient({ env: 'staging', auth: { ... } }) * ``` * * @example * ```typescript * // BeyondAI-only package * const BEYONDAI_REGISTRY = { * defaultPlatform: 'BEYOND_AI', * templates: { * BEYOND_AI: { * staging: { platform: 'BEYOND_AI', env: 'staging' }, * production: { platform: 'BEYOND_AI', env: 'production' }, * }, * }, * } * export const OneRosterClient = createOneRosterClient(BEYONDAI_REGISTRY) * ``` */ declare function createOneRosterClient(registry?: ProviderRegistry): { new (config?: OneRosterClientConfig): { /** @internal */ readonly transport: OneRosterTransportLike; /** @internal */ readonly _provider?: TimebackProvider | undefined; /** Query and manage user accounts across the district, including students, teachers, and staff */ readonly users: UsersCallable; /** Query and manage student records, including their classes, enrollments, and demographics */ readonly students: StudentsCallable; /** Query and manage teacher records, including their class assignments and org affiliations */ readonly teachers: TeachersCallable; /** Query and manage organizational hierarchy including districts, schools, and departments */ readonly orgs: OrgsResource; /** Query and manage school records within the district hierarchy */ readonly schools: SchoolsCallable; /** Query and manage class sections, including their teachers, students, and schedules */ readonly classes: ClassesCallable; /** Query and manage course catalog definitions and subject metadata */ readonly courses: CoursesCallable; /** Query and manage student and teacher enrollments in classes */ readonly enrollments: EnrollmentsResource; /** Query and manage academic sessions like school years and semesters */ readonly academicSessions: AcademicSessionsResource; /** Query and manage academic terms within sessions */ readonly terms: TermsCallable; /** Query and manage grading periods for progress reporting */ readonly gradingPeriods: GradingPeriodsResource; /** Query demographic information including race, ethnicity, and other protected data */ readonly demographics: DemographicsResource; /** Query and manage gradebook line items like assignments, quizzes, and assessments */ readonly lineItems: LineItemsCallable; /** Query and manage student grades and scores for line items */ readonly results: ResultsResource; /** Query and manage grading categories for organizing line items (e.g., Homework, Tests) */ readonly categories: CategoriesResource; /** Query and manage score scales that define grading rubrics and point values */ readonly scoreScales: ScoreScalesResource; /** Query and manage formal assessment line items for standardized testing */ readonly assessmentLineItems: AssessmentLineItemsCallable; /** Query and manage student results from formal assessments */ readonly assessmentResults: AssessmentResultsResource; /** Query and manage digital learning resources (content, activities, materials) */ readonly resources: ResourcesCallable; /** * Get the underlying transport for advanced use cases. * @returns The transport instance used by this client */ getTransport(): OneRosterTransportLike; /** * Verify that OAuth authentication is working. * @returns Auth check result * @throws {Error} If client was initialized with custom transport (no provider) */ checkAuth(): Promise; }; }; export { OneRosterClient, Paginator, Transport, createOneRosterClient, whereToFilter }; export type { AuthCheckResult, EnvAuth, Environment, ExplicitAuth, FieldCondition, FieldOperators, FilterFields, FilterValue, ListParams, OneRosterClientConfig, OneRosterClientInstance, OrCondition, PageResult, StreamableResource, WhereClause };