@timeback/core 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.
package/README.md ADDED
@@ -0,0 +1,216 @@
1
+ # @timeback/core
2
+
3
+ Unified client for Timeback education APIs.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @timeback/core
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { TimebackClient } from '@timeback/core'
15
+
16
+ const timeback = new TimebackClient({
17
+ env: 'staging', // or 'production'
18
+ auth: {
19
+ clientId: process.env.CLIENT_ID,
20
+ clientSecret: process.env.CLIENT_SECRET,
21
+ },
22
+ })
23
+
24
+ // OneRoster - rostering and gradebook
25
+ for await (const school of timeback.oneroster.schools.list()) {
26
+ console.log(school.name)
27
+ }
28
+
29
+ // Edubridge - simplified enrollments and analytics
30
+ const analytics = await timeback.edubridge.analytics.summary()
31
+
32
+ // Caliper - learning analytics events
33
+ await timeback.caliper.events.send(event)
34
+ ```
35
+
36
+ ## Managing Multiple Clients
37
+
38
+ For applications that need to manage multiple `TimebackClient` instances, use `TimebackManager`:
39
+
40
+ ```typescript
41
+ import { TimebackManager } from '@timeback/core'
42
+
43
+ const manager = new TimebackManager()
44
+ .register('alpha', {
45
+ env: 'production',
46
+ auth: { clientId: '...', clientSecret: '...' },
47
+ })
48
+ .register('beta', {
49
+ env: 'production',
50
+ auth: { clientId: '...', clientSecret: '...' },
51
+ })
52
+
53
+ // Target a specific platform
54
+ const users = await manager.get('alpha').oneroster.users.list()
55
+
56
+ // Sync a user across all platforms (uses Promise.allSettled — never throws)
57
+ const results = await manager.broadcast(client => client.oneroster.users.create(user))
58
+
59
+ // Direct property access (like a plain object)
60
+ if (results.alpha.ok) {
61
+ console.log('Created on alpha:', results.alpha.value.id)
62
+ }
63
+
64
+ // Convenience methods for filtering
65
+ if (results.allSucceeded) {
66
+ console.log('Synced to all platforms!')
67
+ }
68
+
69
+ results.succeeded.forEach(([name, user]) => {
70
+ console.log(`Created on ${name}:`, user.id)
71
+ })
72
+
73
+ results.failed.forEach(([name, error]) => {
74
+ console.error(`Failed on ${name}:`, error.message)
75
+ })
76
+ ```
77
+
78
+ ### Manager API
79
+
80
+ | Method | Description |
81
+ | ------------------------ | -------------------------------------------------------------------- |
82
+ | `register(name, config)` | Add a named client |
83
+ | `get(name)` | Retrieve a client by name |
84
+ | `has(name)` | Check if a client is registered |
85
+ | `names` | Get all registered client names |
86
+ | `size` | Get number of registered clients |
87
+ | `broadcast(fn)` | Execute on all clients (never throws), returns `BroadcastResults<T>` |
88
+ | `unregister(name)` | Remove and close a client |
89
+ | `close()` | Close all clients |
90
+
91
+ ### BroadcastResults API
92
+
93
+ The `broadcast()` method returns a hybrid object — use it like a plain `Record<string, BroadcastResult<T>>` with added convenience methods:
94
+
95
+ ```typescript
96
+ // Direct property access
97
+ results.alpha.ok // true or false
98
+ results['beta'].value // the value if ok
99
+ results.gamma.error // the error if not ok
100
+
101
+ // Standard object methods work
102
+ Object.keys(results) // ['alpha', 'beta', 'gamma']
103
+ Object.entries(results) // [[name, result], ...]
104
+ ```
105
+
106
+ | Property/Method | Description |
107
+ | --------------- | ------------------------------------------- |
108
+ | `succeeded` | Get successful results as `[name, value][]` |
109
+ | `failed` | Get failed results as `[name, error][]` |
110
+ | `allSucceeded` | `true` if all operations succeeded |
111
+ | `anyFailed` | `true` if any operation failed |
112
+ | `values()` | Get all values (throws if any failed) |
113
+
114
+ ## Configuration
115
+
116
+ The client supports three configuration modes:
117
+
118
+ ### Environment Mode (Recommended)
119
+
120
+ Derive all URLs from `staging` or `production`:
121
+
122
+ ```typescript
123
+ const client = new TimebackClient({
124
+ env: 'staging', // or 'production'
125
+ auth: { clientId: '...', clientSecret: '...' },
126
+ })
127
+ ```
128
+
129
+ | Environment | API Base URL |
130
+ | ------------ | ------------------------------ |
131
+ | `staging` | `api.staging.alpha-1edtech.ai` |
132
+ | `production` | `api.alpha-1edtech.ai` |
133
+
134
+ ### Base URL Mode
135
+
136
+ For self-hosted or custom deployments with a single base URL:
137
+
138
+ ```typescript
139
+ const client = new TimebackClient({
140
+ baseUrl: 'https://timeback.myschool.edu',
141
+ auth: {
142
+ clientId: '...',
143
+ clientSecret: '...',
144
+ authUrl: 'https://timeback.myschool.edu/oauth/token',
145
+ },
146
+ })
147
+ ```
148
+
149
+ ### Explicit Services Mode
150
+
151
+ Full control over each service URL. Only configure the services you need:
152
+
153
+ ```typescript
154
+ const client = new TimebackClient({
155
+ services: {
156
+ oneroster: 'https://roster.example.com',
157
+ caliper: 'https://analytics.example.com',
158
+ // edubridge not configured — accessing it will throw
159
+ },
160
+ auth: {
161
+ clientId: '...',
162
+ clientSecret: '...',
163
+ authUrl: 'https://auth.example.com/oauth/token',
164
+ },
165
+ })
166
+ ```
167
+
168
+ Each service can optionally override the default token URL:
169
+
170
+ ```typescript
171
+ const client = new TimebackClient({
172
+ services: {
173
+ oneroster: 'https://roster.example.com',
174
+ caliper: {
175
+ baseUrl: 'https://analytics.example.com',
176
+ authUrl: 'https://analytics-auth.example.com/token', // different auth provider
177
+ },
178
+ },
179
+ auth: {
180
+ clientId: '...',
181
+ clientSecret: '...',
182
+ authUrl: 'https://auth.example.com/oauth/token', // default for services without override
183
+ },
184
+ })
185
+ ```
186
+
187
+ OAuth tokens are shared across sub-clients that use the same token URL.
188
+
189
+ ## Individual Clients
190
+
191
+ For standalone usage, install individual packages:
192
+
193
+ ```bash
194
+ bun add @timeback/oneroster
195
+ bun add @timeback/edubridge
196
+ bun add @timeback/caliper
197
+ ```
198
+
199
+ ```typescript
200
+ import { OneRosterClient } from '@timeback/oneroster'
201
+
202
+ const client = new OneRosterClient({
203
+ env: 'staging',
204
+ auth: { clientId: '...', clientSecret: '...' },
205
+ })
206
+ ```
207
+
208
+ ## Debug Mode
209
+
210
+ Enable debug logging with `DEBUG=1`:
211
+
212
+ ```bash
213
+ DEBUG=1 bun run my-script.ts
214
+ ```
215
+
216
+ This outputs detailed logs for HTTP requests and authentication:
@@ -0,0 +1,147 @@
1
+ import type { CaliperClientInstance } from '@timeback/caliper';
2
+ import type { AuthCheckResult, TimebackProvider } from '@timeback/internal-client-infra';
3
+ import type { EdubridgeClientInstance } from '@timeback/edubridge';
4
+ import type { OneRosterClientInstance } from '@timeback/oneroster';
5
+ import type { TimebackClientConfig } from './types/index';
6
+ /**
7
+ * Timeback Client
8
+ *
9
+ * Unified client for all Timeback education APIs.
10
+ */
11
+ /**
12
+ * Unified client for Timeback education APIs.
13
+ *
14
+ * Provides access to all Timeback APIs with shared authentication:
15
+ * - **OneRoster**: Rostering and gradebook data
16
+ * - **Edubridge**: Simplified enrollments and analytics
17
+ * - **Caliper**: Learning analytics events
18
+ *
19
+ * All sub-clients share a single OAuth token, reducing auth requests.
20
+ *
21
+ * @example Environment mode
22
+ * ```typescript
23
+ * const timeback = new TimebackClient({
24
+ * env: 'staging',
25
+ * auth: { clientId: '...', clientSecret: '...' },
26
+ * })
27
+ * ```
28
+ *
29
+ * @example Platform selection
30
+ * ```typescript
31
+ * const timeback = new TimebackClient({
32
+ * platform: 'LEARNWITH_AI',
33
+ * env: 'production',
34
+ * auth: { clientId: '...', clientSecret: '...' },
35
+ * })
36
+ * ```
37
+ *
38
+ * @example Provider mode (pre-built provider)
39
+ * ```typescript
40
+ * import { TimebackProvider } from '@timeback/internal-client-infra'
41
+ *
42
+ * const provider = new TimebackProvider({
43
+ * platform: 'BEYOND_AI',
44
+ * env: 'staging',
45
+ * auth: { clientId: '...', clientSecret: '...' },
46
+ * })
47
+ *
48
+ * const timeback = new TimebackClient({ provider })
49
+ * ```
50
+ *
51
+ * @example Base URL mode (self-hosted)
52
+ * ```typescript
53
+ * const timeback = new TimebackClient({
54
+ * baseUrl: 'https://timeback.myschool.edu',
55
+ * auth: {
56
+ * clientId: '...',
57
+ * clientSecret: '...',
58
+ * authUrl: 'https://timeback.myschool.edu/oauth/token',
59
+ * },
60
+ * })
61
+ * ```
62
+ */
63
+ export declare class TimebackClient {
64
+ private readonly provider;
65
+ private _closed;
66
+ private _oneroster?;
67
+ private _edubridge?;
68
+ private _caliper?;
69
+ /**
70
+ * Creates a new Timeback client.
71
+ *
72
+ * @param config - Client configuration (env, baseUrl, provider, or services mode).
73
+ * If omitted, reads from TIMEBACK_ENV, TIMEBACK_CLIENT_ID,
74
+ * and TIMEBACK_CLIENT_SECRET environment variables.
75
+ */
76
+ constructor(config?: TimebackClientConfig);
77
+ /**
78
+ * OneRoster API client for rostering and gradebook operations.
79
+ *
80
+ * Lazily initialized on first access. Shares OAuth tokens with other
81
+ * sub-clients through the provider.
82
+ *
83
+ * @returns The OneRoster client instance
84
+ * @throws {Error} If client has been closed or service not configured
85
+ */
86
+ get oneroster(): OneRosterClientInstance;
87
+ /**
88
+ * Edubridge API client for simplified enrollments and analytics.
89
+ *
90
+ * Lazily initialized on first access. Shares OAuth tokens with other
91
+ * sub-clients through the provider.
92
+ *
93
+ * @returns The Edubridge client instance
94
+ * @throws {Error} If client has been closed or service not configured
95
+ */
96
+ get edubridge(): EdubridgeClientInstance;
97
+ /**
98
+ * Caliper API client for learning analytics events.
99
+ *
100
+ * Lazily initialized on first access. Shares OAuth tokens with other
101
+ * sub-clients through the provider.
102
+ *
103
+ * @returns The Caliper client instance
104
+ * @throws {Error} If client has been closed or service not configured
105
+ */
106
+ get caliper(): CaliperClientInstance;
107
+ /**
108
+ * Verify that OAuth authentication is working.
109
+ *
110
+ * Attempts to acquire a token using the provider's credentials.
111
+ * Returns a health check result with success/failure and latency info.
112
+ *
113
+ * @returns Auth check result
114
+ * @throws {Error} If client has been closed
115
+ */
116
+ checkAuth(): Promise<AuthCheckResult>;
117
+ /**
118
+ * Close the client and release resources.
119
+ *
120
+ * After calling close():
121
+ * - Cached OAuth tokens are invalidated
122
+ * - Sub-client references are cleared
123
+ * - Further API calls will throw an error
124
+ */
125
+ close(): void;
126
+ /**
127
+ * Check if the client has been closed.
128
+ * @returns True if closed
129
+ */
130
+ get closed(): boolean;
131
+ /**
132
+ * Get the underlying provider for advanced use cases.
133
+ *
134
+ * @returns The TimebackProvider instance used by this client
135
+ */
136
+ getProvider(): TimebackProvider;
137
+ /**
138
+ * Throw if the client has been closed.
139
+ */
140
+ private assertOpen;
141
+ /**
142
+ * Throw if a service is not configured.
143
+ * @param service - Service name to check
144
+ */
145
+ private assertService;
146
+ }
147
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAC9D,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AACxF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAA;AAClE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAEzD;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,qBAAa,cAAc;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,OAAO,CAAQ;IAEvB,OAAO,CAAC,UAAU,CAAC,CAAyB;IAC5C,OAAO,CAAC,UAAU,CAAC,CAAyB;IAC5C,OAAO,CAAC,QAAQ,CAAC,CAAuB;IAExC;;;;;;OAMG;IACH,YAAY,MAAM,CAAC,EAAE,oBAAoB,EAUxC;IAED;;;;;;;;OAQG;IACH,IAAI,SAAS,IAAI,uBAAuB,CASvC;IAED;;;;;;;;OAQG;IACH,IAAI,SAAS,IAAI,uBAAuB,CASvC;IAED;;;;;;;;OAQG;IACH,IAAI,OAAO,IAAI,qBAAqB,CASnC;IAMD;;;;;;;;OAQG;IACH,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC,CAGpC;IAED;;;;;;;OAOG;IACH,KAAK,IAAI,IAAI,CAUZ;IAED;;;OAGG;IACH,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED;;;;OAIG;IACH,WAAW,IAAI,gBAAgB,CAE9B;IAED;;OAEG;IACH,OAAO,CAAC,UAAU;IAMlB;;;OAGG;IACH,OAAO,CAAC,aAAa;CAKrB"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Timeback API Endpoints
3
+ *
4
+ * Re-exports URL constants from `@timeback/internal-constants`.
5
+ */
6
+ import type { ResolveEnvVars } from '@timeback/internal-client-infra';
7
+ import type { Environment, EnvServiceUrls, Platform } from './types';
8
+ /**
9
+ * Environment variable names for Timeback client configuration.
10
+ */
11
+ export declare const TIMEBACK_ENV_VARS: ResolveEnvVars;
12
+ /**
13
+ * Get all service URLs for a given platform and environment.
14
+ *
15
+ * @param env - Target environment (staging or production)
16
+ * @param platform - Target platform (defaults to 'BEYOND_AI')
17
+ * @returns Service URLs for the environment
18
+ */
19
+ export declare function getServiceUrlsForEnv(env: Environment, platform?: Platform): EnvServiceUrls;
20
+ //# 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,cAAc,EAAE,MAAM,iCAAiC,CAAA;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAMpE;;GAEG;AACH,eAAO,MAAM,iBAAiB,EAAE,cAM/B,CAAA;AAMD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CACnC,GAAG,EAAE,WAAW,EAChB,QAAQ,GAAE,QAA2B,GACnC,cAAc,CAQhB"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Error Classes
3
+ *
4
+ * Re-exported from `@timeback/internal-client-infra`.
5
+ */
6
+ export { ApiError, 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,EACR,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,eAAe,GACf,MAAM,iCAAiC,CAAA"}