@timeback/oneroster 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +295 -0
  2. package/dist/client.d.ts +125 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/constants.d.ts +23 -0
  5. package/dist/constants.d.ts.map +1 -0
  6. package/dist/errors.d.ts +7 -0
  7. package/dist/errors.d.ts.map +1 -0
  8. package/dist/errors.js +1414 -0
  9. package/dist/factory.d.ts +100 -0
  10. package/dist/factory.d.ts.map +1 -0
  11. package/dist/index.d.ts +42 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +2614 -0
  14. package/dist/lib/index.d.ts +11 -0
  15. package/dist/lib/index.d.ts.map +1 -0
  16. package/dist/lib/pagination.d.ts +30 -0
  17. package/dist/lib/pagination.d.ts.map +1 -0
  18. package/dist/lib/resolve.d.ts +21 -0
  19. package/dist/lib/resolve.d.ts.map +1 -0
  20. package/dist/lib/transport.d.ts +32 -0
  21. package/dist/lib/transport.d.ts.map +1 -0
  22. package/dist/resources/assessment/index.d.ts +6 -0
  23. package/dist/resources/assessment/index.d.ts.map +1 -0
  24. package/dist/resources/assessment/line-items.d.ts +38 -0
  25. package/dist/resources/assessment/line-items.d.ts.map +1 -0
  26. package/dist/resources/assessment/results.d.ts +25 -0
  27. package/dist/resources/assessment/results.d.ts.map +1 -0
  28. package/dist/resources/base.d.ts +181 -0
  29. package/dist/resources/base.d.ts.map +1 -0
  30. package/dist/resources/gradebook/categories.d.ts +16 -0
  31. package/dist/resources/gradebook/categories.d.ts.map +1 -0
  32. package/dist/resources/gradebook/index.d.ts +8 -0
  33. package/dist/resources/gradebook/index.d.ts.map +1 -0
  34. package/dist/resources/gradebook/line-items.d.ts +52 -0
  35. package/dist/resources/gradebook/line-items.d.ts.map +1 -0
  36. package/dist/resources/gradebook/results.d.ts +24 -0
  37. package/dist/resources/gradebook/results.d.ts.map +1 -0
  38. package/dist/resources/gradebook/score-scales.d.ts +16 -0
  39. package/dist/resources/gradebook/score-scales.d.ts.map +1 -0
  40. package/dist/resources/index.d.ts +11 -0
  41. package/dist/resources/index.d.ts.map +1 -0
  42. package/dist/resources/resources/index.d.ts +5 -0
  43. package/dist/resources/resources/index.d.ts.map +1 -0
  44. package/dist/resources/resources/resources.d.ts +39 -0
  45. package/dist/resources/resources/resources.d.ts.map +1 -0
  46. package/dist/resources/rostering/academic-sessions.d.ts +97 -0
  47. package/dist/resources/rostering/academic-sessions.d.ts.map +1 -0
  48. package/dist/resources/rostering/classes.d.ts +276 -0
  49. package/dist/resources/rostering/classes.d.ts.map +1 -0
  50. package/dist/resources/rostering/courses.d.ts +75 -0
  51. package/dist/resources/rostering/courses.d.ts.map +1 -0
  52. package/dist/resources/rostering/demographics.d.ts +18 -0
  53. package/dist/resources/rostering/demographics.d.ts.map +1 -0
  54. package/dist/resources/rostering/enrollments.d.ts +28 -0
  55. package/dist/resources/rostering/enrollments.d.ts.map +1 -0
  56. package/dist/resources/rostering/index.d.ts +12 -0
  57. package/dist/resources/rostering/index.d.ts.map +1 -0
  58. package/dist/resources/rostering/orgs.d.ts +18 -0
  59. package/dist/resources/rostering/orgs.d.ts.map +1 -0
  60. package/dist/resources/rostering/schools.d.ts +214 -0
  61. package/dist/resources/rostering/schools.d.ts.map +1 -0
  62. package/dist/resources/rostering/users.d.ts +191 -0
  63. package/dist/resources/rostering/users.d.ts.map +1 -0
  64. package/dist/types/base.d.ts +127 -0
  65. package/dist/types/base.d.ts.map +1 -0
  66. package/dist/types/callable.d.ts +246 -0
  67. package/dist/types/callable.d.ts.map +1 -0
  68. package/dist/types/client.d.ts +58 -0
  69. package/dist/types/client.d.ts.map +1 -0
  70. package/dist/types/filters.d.ts +165 -0
  71. package/dist/types/filters.d.ts.map +1 -0
  72. package/dist/types/gradebook.d.ts +262 -0
  73. package/dist/types/gradebook.d.ts.map +1 -0
  74. package/dist/types/index.d.ts +13 -0
  75. package/dist/types/index.d.ts.map +1 -0
  76. package/dist/types/resources.d.ts +191 -0
  77. package/dist/types/resources.d.ts.map +1 -0
  78. package/dist/types/rostering.d.ts +732 -0
  79. package/dist/types/rostering.d.ts.map +1 -0
  80. package/dist/types.d.ts +10 -0
  81. package/dist/types.d.ts.map +1 -0
  82. package/dist/types.js +0 -0
  83. package/dist/utils.d.ts +51 -0
  84. package/dist/utils.d.ts.map +1 -0
  85. package/package.json +41 -0
package/README.md ADDED
@@ -0,0 +1,295 @@
1
+ # @timeback/oneroster
2
+
3
+ TypeScript client for the OneRoster 1.2 API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @timeback/oneroster
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { OneRosterClient } from '@timeback/oneroster'
15
+
16
+ const client = new OneRosterClient({
17
+ env: 'production',
18
+ auth: {
19
+ clientId: 'your-client-id',
20
+ clientSecret: 'your-client-secret',
21
+ },
22
+ })
23
+
24
+ const students = await client.students.list()
25
+ const user = await client.users.get('user-sourced-id')
26
+ const course = await client.courses.create({
27
+ title: 'Algebra I',
28
+ org: { sourcedId: 'school-id' },
29
+ })
30
+ ```
31
+
32
+ ## API Design
33
+
34
+ ### Standalone vs Composed
35
+
36
+ The client works standalone or composed into `@timeback/core`:
37
+
38
+ ```typescript
39
+ // Composed
40
+ import { TimebackClient } from '@timeback/core'
41
+
42
+ // Standalone
43
+ const oneroster = new OneRosterClient({ env: 'production', auth })
44
+
45
+ const client = new TimebackClient({ baseUrl, auth })
46
+ client.oneroster.users.list()
47
+ ```
48
+
49
+ ### Client Structure
50
+
51
+ ```typescript
52
+ const client = new OneRosterClient(options)
53
+
54
+ // Rostering
55
+ client.users // Users (all roles)
56
+ client.students // Students (filtered users)
57
+ client.teachers // Teachers (filtered users)
58
+ client.classes // Classes
59
+ client.courses // Courses
60
+ client.enrollments // Enrollments
61
+ client.orgs // Organizations
62
+ client.schools // Schools (filtered orgs)
63
+ client.terms // Academic terms
64
+ client.academicSessions
65
+ client.gradingPeriods
66
+ client.demographics
67
+
68
+ // Gradebook
69
+ client.results // Grade results
70
+ client.lineItems // Line items (assignments)
71
+ client.categories // Grading categories
72
+ client.scoreScales // Score scales
73
+ client.assessmentLineItems
74
+ client.assessmentResults
75
+
76
+ // Resources
77
+ client.resources
78
+ ```
79
+
80
+ Resources serve dual purposes — as properties for top-level operations, and as functions for accessing nested resources:
81
+
82
+ ```typescript
83
+ // As property → top-level CRUD
84
+ client.schools.list()
85
+ client.schools.get('id')
86
+
87
+ // As function → scoped operations
88
+ client.schools('id').classes()
89
+ client.schools('id').students()
90
+ ```
91
+
92
+ ### Common Methods
93
+
94
+ Each resource supports:
95
+
96
+ ```typescript
97
+ // List with type-safe filtering
98
+ const activeUsers = await client.users.list({
99
+ where: { status: 'active' },
100
+ sort: 'familyName',
101
+ orderBy: 'asc',
102
+ })
103
+
104
+ // Filter with operators
105
+ const teachers = await client.users.list({
106
+ where: { role: 'teacher', status: 'active' },
107
+ })
108
+
109
+ // Stream for large datasets (lazy pagination)
110
+ for await (const user of client.users.stream()) {
111
+ console.log(user.givenName)
112
+ }
113
+
114
+ // Get by sourcedId
115
+ client.users.get('sourced-id')
116
+
117
+ // Create (where supported)
118
+ client.courses.create({ title: 'Math 101', org: { sourcedId: '...' } })
119
+
120
+ // Update (where supported)
121
+ client.courses.update('sourced-id', { title: 'Math 102' })
122
+
123
+ // Delete (where supported)
124
+ client.courses.delete('sourced-id')
125
+
126
+ // Patch (enrollments, assessmentLineItems, assessmentResults)
127
+ client.enrollments.patch('sourced-id', { status: 'tobedeleted' })
128
+ ```
129
+
130
+ ### Nested Resources
131
+
132
+ The API exposes many nested relationships:
133
+
134
+ ```typescript
135
+ // ── Schools ──────────────────────────────────────────────────────────────────
136
+ client.schools('school-id').classes()
137
+ client.schools('school-id').courses()
138
+ client.schools('school-id').terms()
139
+ client.schools('school-id').teachers()
140
+ client.schools('school-id').students()
141
+ client.schools('school-id').enrollments()
142
+ client.schools('school-id').scoreScales()
143
+ client.schools('school-id').lineItems()
144
+
145
+ // Deeply nested
146
+ client.schools('school-id').classes('class-id').teachers()
147
+ client.schools('school-id').classes('class-id').students()
148
+ client.schools('school-id').classes('class-id').enrollments()
149
+
150
+ // ── Classes ──────────────────────────────────────────────────────────────────
151
+ client.classes('class-id').teachers()
152
+ client.classes('class-id').students()
153
+ client.classes('class-id').lineItems()
154
+ client.classes('class-id').results()
155
+ client.classes('class-id').categories()
156
+ client.classes('class-id').scoreScales()
157
+ client.classes('class-id').resources()
158
+ client.classes('class-id').enroll('user-id', 'student')
159
+
160
+ // Deeply nested gradebook
161
+ client.classes('class-id').lineItems('line-item-id').results()
162
+ client.classes('class-id').students('student-id').results()
163
+
164
+ // ── Users ────────────────────────────────────────────────────────────────────
165
+ client.users('user-id').classes()
166
+ client.users('user-id').demographics()
167
+ client.users('user-id').agents()
168
+ client.users('user-id').addAgent('agent-id')
169
+ client.users('user-id').removeAgent('agent-id')
170
+ client.users('user-id').agentFor()
171
+ client.users('user-id').credentials()
172
+ client.users('user-id').createCredential({ ... })
173
+ client.users('user-id').credentials('cred-id').decrypt()
174
+
175
+ // ── Students/Teachers ────────────────────────────────────────────────────────
176
+ client.students('student-id').classes()
177
+ client.teachers('teacher-id').classes()
178
+
179
+ // ── Courses ──────────────────────────────────────────────────────────────────
180
+ client.courses('course-id').classes()
181
+ client.courses('course-id').resources()
182
+ client.courses.components()
183
+ client.courses.getComponent('component-id')
184
+ client.courses.componentResources()
185
+ client.courses.getComponentResource('id')
186
+ client.courses.createStructure({ ... })
187
+
188
+ // ── Terms ────────────────────────────────────────────────────────────────────
189
+ client.terms('term-id').classes()
190
+ client.terms('term-id').gradingPeriods()
191
+
192
+ // ── Line Items ───────────────────────────────────────────────────────────────
193
+ client.lineItems('line-item-id').results()
194
+
195
+ // ── Resources ────────────────────────────────────────────────────────────────
196
+ client.resources.list()
197
+ client.resources.create({ ... })
198
+ client.resources('resource-id').export()
199
+ ```
200
+
201
+ ### Response Types
202
+
203
+ ```typescript
204
+ // List responses include pagination info
205
+ const response = await client.users.list()
206
+ // {
207
+ // users: User[],
208
+ // offset: number,
209
+ // limit: number,
210
+ // }
211
+
212
+ // Single resource responses
213
+ const user = await client.users.get('id')
214
+ // { user: User }
215
+
216
+ // Create responses
217
+ const created = await client.courses.create(payload)
218
+ // { sourcedIdPairs: { suppliedSourcedId, allocatedSourcedId } }
219
+ ```
220
+
221
+ ### Authentication
222
+
223
+ The client handles OAuth2 token management automatically:
224
+
225
+ ```typescript
226
+ const client = new OneRosterClient({
227
+ baseUrl: 'https://api.timeback.dev',
228
+ auth: {
229
+ clientId: 'xxx',
230
+ clientSecret: 'xxx',
231
+ // Optional: custom token endpoint
232
+ authUrl: 'https://auth.example.com/oauth2/token',
233
+ },
234
+ })
235
+ ```
236
+
237
+ Tokens are cached and refreshed automatically before expiry.
238
+
239
+ ### Error Handling
240
+
241
+ ```typescript
242
+ import { OneRosterError } from '@timeback/oneroster'
243
+
244
+ try {
245
+ await client.users.get('invalid-id')
246
+ } catch (error) {
247
+ if (error instanceof OneRosterError) {
248
+ console.log(error.status) // 404
249
+ console.log(error.message) // 'Not Found'
250
+ console.log(error.imsx_codeMajor) // 'failure'
251
+ }
252
+ }
253
+ ```
254
+
255
+ ## Configuration
256
+
257
+ ```typescript
258
+ new OneRosterClient({
259
+ // Environment-based (recommended)
260
+ env: 'production' | 'staging',
261
+ auth: {
262
+ clientId: string,
263
+ clientSecret: string,
264
+ },
265
+
266
+ // Or explicit URLs
267
+ baseUrl: string,
268
+ auth: {
269
+ clientId: string,
270
+ clientSecret: string,
271
+ authUrl?: string,
272
+ },
273
+
274
+ // Optional
275
+ timeout?: number, // Request timeout in ms (default: 30000)
276
+ })
277
+ ```
278
+
279
+ ## Debug Mode
280
+
281
+ Enable debug logging by setting `DEBUG=1` or `DEBUG=true`:
282
+
283
+ ```bash
284
+ DEBUG=1 bun run my-script.ts
285
+ ```
286
+
287
+ This outputs detailed logs for HTTP requests, authentication, and pagination:
288
+
289
+ ```bash
290
+ [2025-01-15T10:30:00.000Z] DEBUG [oneroster:auth] Fetching new access token...
291
+ [2025-01-15T10:30:00.500Z] DEBUG [oneroster:auth] Token acquired (500ms, expires in 3600s)
292
+ [2025-01-15T10:30:00.501Z] DEBUG [oneroster:http] → GET https://api.example.com/ims/oneroster/.../schools
293
+ [2025-01-15T10:30:00.800Z] DEBUG [oneroster:http] ← 200 OK (299ms)
294
+ [2025-01-15T10:30:00.801Z] DEBUG [oneroster:pagination] First page: 5 items, total: 20, hasMore: true
295
+ ```
@@ -0,0 +1,125 @@
1
+ /**
2
+ * OneRoster Client
3
+ *
4
+ * Main entry point for the OneRoster SDK.
5
+ */
6
+ /**
7
+ * OneRoster API client for rostering and gradebook operations.
8
+ *
9
+ * Provides access to all OneRoster v1.2 resources including users, classes,
10
+ * enrollments, courses, grades, and assessments.
11
+ *
12
+ * @example Environment mode (Timeback APIs)
13
+ * ```typescript
14
+ * const client = new OneRosterClient({
15
+ * env: 'staging', // or 'production'
16
+ * auth: {
17
+ * clientId: 'your-client-id',
18
+ * clientSecret: 'your-client-secret',
19
+ * },
20
+ * })
21
+ * ```
22
+ *
23
+ * @example Platform selection
24
+ * ```typescript
25
+ * const client = new OneRosterClient({
26
+ * platform: 'LEARNWITH_AI', // or 'BEYOND_AI' (default)
27
+ * env: 'production',
28
+ * auth: {
29
+ * clientId: 'your-client-id',
30
+ * clientSecret: 'your-client-secret',
31
+ * },
32
+ * })
33
+ * ```
34
+ *
35
+ * @example Provider mode (pre-built provider)
36
+ * ```typescript
37
+ * import { TimebackProvider } from '@timeback/internal-client-infra'
38
+ *
39
+ * const provider = new TimebackProvider({
40
+ * platform: 'BEYOND_AI',
41
+ * env: 'staging',
42
+ * auth: { clientId: '...', clientSecret: '...' },
43
+ * })
44
+ *
45
+ * const client = new OneRosterClient({ provider })
46
+ * ```
47
+ *
48
+ * @example Explicit mode (custom OneRoster API)
49
+ * ```typescript
50
+ * const client = new OneRosterClient({
51
+ * baseUrl: 'https://api.example.com',
52
+ * auth: {
53
+ * clientId: 'your-client-id',
54
+ * clientSecret: 'your-client-secret',
55
+ * authUrl: 'https://auth.example.com/oauth2/token',
56
+ * },
57
+ * })
58
+ * ```
59
+ *
60
+ * @example Environment variables fallback
61
+ * ```typescript
62
+ * // Set ONEROSTER_BASE_URL, ONEROSTER_TOKEN_URL,
63
+ * // ONEROSTER_CLIENT_ID, ONEROSTER_CLIENT_SECRET
64
+ * const client = new OneRosterClient()
65
+ * ```
66
+ *
67
+ * @example Nested resources
68
+ * ```typescript
69
+ * const classes = await client.schools.for('school-id').classes()
70
+ * const students = await client.classes.for('class-id').students()
71
+ * ```
72
+ *
73
+ * @remarks
74
+ * The client supports four configuration modes:
75
+ *
76
+ * 1. **Provider mode**: `{ provider: TimebackProvider }` — Use a pre-built provider
77
+ * 2. **Environment mode**: `{ env, auth }` — Connect to Timeback platforms
78
+ * 3. **Explicit mode**: `{ baseUrl, auth: { authUrl } }` — Custom OneRoster API
79
+ * 4. **Transport mode**: `{ transport }` — Use existing transport instance
80
+ *
81
+ * If no config is provided, the client reads from environment variables.
82
+ *
83
+ * @see {@link createOneRosterClient} for creating platform-specific clients
84
+ */
85
+ export declare const OneRosterClient: {
86
+ new (config?: import("./types").OneRosterClientConfig): {
87
+ readonly transport: import("./types").OneRosterTransportLike;
88
+ readonly _provider?: import("@timeback/internal-client-infra").TimebackProvider | undefined;
89
+ readonly users: import("./types").UsersCallable;
90
+ readonly students: import("./types").StudentsCallable;
91
+ readonly teachers: import("./types").TeachersCallable;
92
+ readonly orgs: import("./resources").OrgsResource;
93
+ readonly schools: import("./types").SchoolsCallable;
94
+ readonly classes: import("./types").ClassesCallable;
95
+ readonly courses: import("./types").CoursesCallable;
96
+ readonly enrollments: import("./resources").EnrollmentsResource;
97
+ readonly academicSessions: import("./resources").AcademicSessionsResource;
98
+ readonly terms: import("./types").TermsCallable;
99
+ readonly gradingPeriods: import("./resources").GradingPeriodsResource;
100
+ readonly demographics: import("./resources").DemographicsResource;
101
+ readonly lineItems: import("./types").LineItemsCallable;
102
+ readonly results: import("./resources").ResultsResource;
103
+ readonly categories: import("./resources").CategoriesResource;
104
+ readonly scoreScales: import("./resources").ScoreScalesResource;
105
+ readonly assessmentLineItems: import("./types").AssessmentLineItemsCallable;
106
+ readonly assessmentResults: import("./resources").AssessmentResultsResource;
107
+ readonly resources: import("./types").ResourcesCallable;
108
+ getTransport(): import("./types").OneRosterTransportLike;
109
+ checkAuth(): Promise<import("@timeback/internal-client-infra").AuthCheckResult>;
110
+ };
111
+ };
112
+ /**
113
+ * Type representing an instance of OneRosterClient.
114
+ *
115
+ * Use this when you need to type a variable that holds a client instance.
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * function doSomething(client: OneRosterClientInstance) {
120
+ * return client.users.list()
121
+ * }
122
+ * ```
123
+ */
124
+ export type { OneRosterClientInstance } from './types';
125
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8EG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;CAA0B,CAAA;AAEtD;;;;;;;;;;;GAWG;AACH,YAAY,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * OneRoster Constants
3
+ *
4
+ * Configuration constants for the OneRoster client.
5
+ */
6
+ import type { ClientUrlMaps, Platform } from '@timeback/internal-client-infra';
7
+ /**
8
+ * Environment variable names for OneRoster configuration fallback.
9
+ */
10
+ export declare const ONEROSTER_ENV_VARS: {
11
+ readonly baseUrl: "ONEROSTER_BASE_URL";
12
+ readonly clientId: "ONEROSTER_CLIENT_ID";
13
+ readonly clientSecret: "ONEROSTER_CLIENT_SECRET";
14
+ readonly authUrl: "ONEROSTER_TOKEN_URL";
15
+ };
16
+ /**
17
+ * Get URL maps for a specific platform.
18
+ * Defaults to 'BEYOND_AI' for backwards compatibility.
19
+ * @param platform - Platform name
20
+ * @returns Client URL maps for the platform
21
+ */
22
+ export declare function getUrlMapsForPlatform(platform?: Platform): ClientUrlMaps;
23
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAA;AAM9E;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;CAKrB,CAAA;AAMV;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,GAAE,QAA2B,GAAG,aAAa,CAM1F"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * OneRoster Error Classes
3
+ *
4
+ * Re-exports common API errors with IMS Global parsing.
5
+ */
6
+ export { ApiError as OneRosterError, ForbiddenError, NotFoundError, UnauthorizedError, ValidationError, } from '@timeback/internal-client-infra';
7
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACN,QAAQ,IAAI,cAAc,EAC1B,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,eAAe,GACf,MAAM,iCAAiC,CAAA"}