timeback 0.1.3 → 0.1.5

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 (131) hide show
  1. package/README.md +65 -550
  2. package/dist/cli/src/config.d.ts +8 -0
  3. package/dist/cli.js +131552 -0
  4. package/dist/types/src/config.d.ts +186 -0
  5. package/dist/types/src/index.d.ts +5 -0
  6. package/dist/types/src/primitives.d.ts +53 -0
  7. package/package.json +29 -91
  8. package/schema.json +222 -0
  9. package/dist/client/adapters/react/SignInButton.d.ts +0 -60
  10. package/dist/client/adapters/react/SignInButton.d.ts.map +0 -1
  11. package/dist/client/adapters/react/index.d.ts +0 -43
  12. package/dist/client/adapters/react/index.d.ts.map +0 -1
  13. package/dist/client/adapters/react/index.js +0 -478
  14. package/dist/client/adapters/react/provider.d.ts +0 -74
  15. package/dist/client/adapters/react/provider.d.ts.map +0 -1
  16. package/dist/client/adapters/solid/SignInButton.d.ts +0 -52
  17. package/dist/client/adapters/solid/SignInButton.d.ts.map +0 -1
  18. package/dist/client/adapters/solid/SignInButton.tsx +0 -321
  19. package/dist/client/adapters/solid/context.d.ts +0 -73
  20. package/dist/client/adapters/solid/context.d.ts.map +0 -1
  21. package/dist/client/adapters/solid/context.tsx +0 -91
  22. package/dist/client/adapters/solid/index.d.ts +0 -42
  23. package/dist/client/adapters/solid/index.d.ts.map +0 -1
  24. package/dist/client/adapters/solid/index.ts +0 -46
  25. package/dist/client/adapters/svelte/SignInButton.svelte +0 -234
  26. package/dist/client/adapters/svelte/SignInButton.svelte.d.ts +0 -24
  27. package/dist/client/adapters/svelte/index.d.ts +0 -33
  28. package/dist/client/adapters/svelte/index.d.ts.map +0 -1
  29. package/dist/client/adapters/svelte/index.ts +0 -38
  30. package/dist/client/adapters/svelte/stores.d.ts +0 -62
  31. package/dist/client/adapters/svelte/stores.d.ts.map +0 -1
  32. package/dist/client/adapters/svelte/stores.ts +0 -139
  33. package/dist/client/adapters/vue/SignInButton.vue +0 -260
  34. package/dist/client/adapters/vue/SignInButton.vue.d.ts +0 -53
  35. package/dist/client/adapters/vue/index.d.ts +0 -43
  36. package/dist/client/adapters/vue/index.d.ts.map +0 -1
  37. package/dist/client/adapters/vue/index.ts +0 -48
  38. package/dist/client/adapters/vue/provider.d.ts +0 -94
  39. package/dist/client/adapters/vue/provider.d.ts.map +0 -1
  40. package/dist/client/adapters/vue/provider.ts +0 -147
  41. package/dist/client/index.d.ts +0 -9
  42. package/dist/client/index.d.ts.map +0 -1
  43. package/dist/client/lib/activity/activity.class.d.ts +0 -73
  44. package/dist/client/lib/activity/activity.class.d.ts.map +0 -1
  45. package/dist/client/lib/activity/activity.d.ts +0 -16
  46. package/dist/client/lib/activity/activity.d.ts.map +0 -1
  47. package/dist/client/lib/activity/index.d.ts +0 -6
  48. package/dist/client/lib/activity/index.d.ts.map +0 -1
  49. package/dist/client/lib/utils.d.ts +0 -20
  50. package/dist/client/lib/utils.d.ts.map +0 -1
  51. package/dist/client/namespaces/activity.d.ts +0 -37
  52. package/dist/client/namespaces/activity.d.ts.map +0 -1
  53. package/dist/client/namespaces/auth.d.ts +0 -33
  54. package/dist/client/namespaces/auth.d.ts.map +0 -1
  55. package/dist/client/namespaces/index.d.ts +0 -7
  56. package/dist/client/namespaces/index.d.ts.map +0 -1
  57. package/dist/client/namespaces/user.d.ts +0 -29
  58. package/dist/client/namespaces/user.d.ts.map +0 -1
  59. package/dist/client/timeback-client.class.d.ts +0 -37
  60. package/dist/client/timeback-client.class.d.ts.map +0 -1
  61. package/dist/client/timeback-client.d.ts +0 -29
  62. package/dist/client/timeback-client.d.ts.map +0 -1
  63. package/dist/client.d.ts +0 -30
  64. package/dist/client.d.ts.map +0 -1
  65. package/dist/client.js +0 -198
  66. package/dist/edge.d.ts +0 -13
  67. package/dist/edge.d.ts.map +0 -1
  68. package/dist/edge.js +0 -1149
  69. package/dist/identity.d.ts +0 -14
  70. package/dist/identity.d.ts.map +0 -1
  71. package/dist/identity.js +0 -1019
  72. package/dist/index.d.ts +0 -48
  73. package/dist/index.d.ts.map +0 -1
  74. package/dist/index.js +0 -10221
  75. package/dist/server/adapters/express.d.ts +0 -66
  76. package/dist/server/adapters/express.d.ts.map +0 -1
  77. package/dist/server/adapters/express.js +0 -9326
  78. package/dist/server/adapters/native.d.ts +0 -47
  79. package/dist/server/adapters/native.d.ts.map +0 -1
  80. package/dist/server/adapters/native.js +0 -190
  81. package/dist/server/adapters/nextjs.d.ts +0 -32
  82. package/dist/server/adapters/nextjs.d.ts.map +0 -1
  83. package/dist/server/adapters/nextjs.js +0 -202
  84. package/dist/server/adapters/nuxt.d.ts +0 -98
  85. package/dist/server/adapters/nuxt.d.ts.map +0 -1
  86. package/dist/server/adapters/nuxt.js +0 -9395
  87. package/dist/server/adapters/solid-start.d.ts +0 -63
  88. package/dist/server/adapters/solid-start.d.ts.map +0 -1
  89. package/dist/server/adapters/solid-start.js +0 -9294
  90. package/dist/server/adapters/svelte-kit.d.ts +0 -84
  91. package/dist/server/adapters/svelte-kit.d.ts.map +0 -1
  92. package/dist/server/adapters/svelte-kit.js +0 -243
  93. package/dist/server/adapters/tanstack-start.d.ts +0 -42
  94. package/dist/server/adapters/tanstack-start.d.ts.map +0 -1
  95. package/dist/server/adapters/tanstack-start.js +0 -9272
  96. package/dist/server/adapters/types.d.ts +0 -294
  97. package/dist/server/adapters/types.d.ts.map +0 -1
  98. package/dist/server/adapters/utils.d.ts +0 -76
  99. package/dist/server/adapters/utils.d.ts.map +0 -1
  100. package/dist/server/handlers/activity.d.ts +0 -28
  101. package/dist/server/handlers/activity.d.ts.map +0 -1
  102. package/dist/server/handlers/identity-full.d.ts +0 -28
  103. package/dist/server/handlers/identity-full.d.ts.map +0 -1
  104. package/dist/server/handlers/identity-only.d.ts +0 -22
  105. package/dist/server/handlers/identity-only.d.ts.map +0 -1
  106. package/dist/server/handlers/index.d.ts +0 -9
  107. package/dist/server/handlers/index.d.ts.map +0 -1
  108. package/dist/server/handlers/user.d.ts +0 -30
  109. package/dist/server/handlers/user.d.ts.map +0 -1
  110. package/dist/server/index.d.ts +0 -9
  111. package/dist/server/index.d.ts.map +0 -1
  112. package/dist/server/lib/index.d.ts +0 -10
  113. package/dist/server/lib/index.d.ts.map +0 -1
  114. package/dist/server/lib/logger.d.ts +0 -21
  115. package/dist/server/lib/logger.d.ts.map +0 -1
  116. package/dist/server/lib/oidc.d.ts +0 -76
  117. package/dist/server/lib/oidc.d.ts.map +0 -1
  118. package/dist/server/lib/resolve-timeback-user.d.ts +0 -42
  119. package/dist/server/lib/resolve-timeback-user.d.ts.map +0 -1
  120. package/dist/server/lib/utils.d.ts +0 -54
  121. package/dist/server/lib/utils.d.ts.map +0 -1
  122. package/dist/server/timeback-identity.d.ts +0 -19
  123. package/dist/server/timeback-identity.d.ts.map +0 -1
  124. package/dist/server/timeback.d.ts +0 -68
  125. package/dist/server/timeback.d.ts.map +0 -1
  126. package/dist/server/types.d.ts +0 -402
  127. package/dist/server/types.d.ts.map +0 -1
  128. package/dist/shared/constants.d.ts +0 -18
  129. package/dist/shared/constants.d.ts.map +0 -1
  130. package/dist/shared/types.d.ts +0 -144
  131. package/dist/shared/types.d.ts.map +0 -1
package/README.md CHANGED
@@ -1,584 +1,99 @@
1
- # Timeback SDK
1
+ # timeback
2
2
 
3
- TypeScript SDK for integrating Timeback into your application. Provides server-side route handlers and client-side components for activity tracking and SSO authentication.
4
-
5
- ## Table of Contents
6
-
7
- - [Installation](#installation)
8
- - [Quick Start](#quick-start)
9
- - [Server Adapters](#server-adapters)
10
- - [Next.js](#nextjs)
11
- - [Nuxt](#nuxt)
12
- - [SvelteKit](#sveltekit)
13
- - [SolidStart](#solidstart)
14
- - [TanStack Start](#tanstack-start)
15
- - [Express](#express)
16
- - [Client Adapters](#client-adapters)
17
- - [React](#react)
18
- - [Vue](#vue)
19
- - [Svelte](#svelte)
20
- - [Solid](#solid)
21
- - [Identity Modes](#identity-modes)
22
- - [Identity-Only Integration](#identity-only-integration)
23
- - [Activity Tracking](#activity-tracking)
24
- - [Advanced: Direct API Access](#advanced-direct-api-access)
3
+ CLI for the Timeback platform.
25
4
 
26
5
  ## Installation
27
6
 
28
7
  ```bash
29
- npm install timeback
8
+ bun add -g timeback
30
9
  # or
31
- bun add timeback
32
- ```
33
-
34
- ## Quick Start
35
-
36
- 1. Create a server instance with your credentials
37
- 2. Mount the route handlers for your framework
38
- 3. Wrap your app with the client provider
39
- 4. Use hooks/composables to track activities
40
-
41
- ## Server Adapters
42
-
43
- All server adapters use the same core configuration:
44
-
45
- ```typescript
46
- // lib/timeback.ts
47
- import { createTimeback } from 'timeback'
48
-
49
- export const timeback = await createTimeback({
50
- env: 'staging', // 'local' | 'staging' | 'production'
51
- api: {
52
- clientId: process.env.TIMEBACK_API_CLIENT_ID!,
53
- clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
54
- },
55
- identity: {
56
- mode: 'sso',
57
- clientId: process.env.AWS_COGNITO_CLIENT_ID!,
58
- clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
59
- redirectUri: 'http://localhost:3000/api/auth/sso/callback/timeback',
60
- onCallbackSuccess: async ({ user, state, redirect }) => {
61
- // user.id is the timebackId (canonical stable identifier)
62
- await setSession({ id: user.id, email: user.email })
63
- return redirect(state?.returnTo ?? '/')
64
- },
65
- onCallbackError: ({ error, redirect }) => {
66
- console.error('SSO Error:', error)
67
- return redirect('/?error=sso_failed')
68
- },
69
- getUser: () => getSession(), // Return current user or undefined
70
- },
71
- })
72
- ```
73
-
74
- ### Next.js
75
-
76
- ```typescript
77
- // app/api/timeback/[...timeback]/route.ts
78
- import { toNextjsHandler } from 'timeback/nextjs'
79
-
80
- import { timeback } from '@/lib/timeback'
81
-
82
- export const { GET, POST } = toNextjsHandler(timeback)
83
- ```
84
-
85
- ### Nuxt
86
-
87
- ```typescript
88
- // server/middleware/timeback.ts
89
- import { nuxtHandler } from 'timeback/nuxt'
90
-
91
- import { timeback } from '../lib/timeback'
92
-
93
- export default defineEventHandler(async event => {
94
- const response = await nuxtHandler({
95
- timeback,
96
- event,
97
- })
98
- if (response) return response
99
- })
100
- ```
101
-
102
- ### SvelteKit
103
-
104
- ```typescript
105
- // src/hooks.server.ts
106
- import { building } from '$app/environment'
107
- import { timeback } from '$lib/timeback'
108
- import { svelteKitHandler } from 'timeback/svelte-kit'
109
-
110
- import type { Handle } from '@sveltejs/kit'
111
-
112
- export const handle: Handle = ({ event, resolve }) => {
113
- return svelteKitHandler({
114
- timeback,
115
- event,
116
- resolve,
117
- building,
118
- })
119
- }
120
- ```
121
-
122
- ### SolidStart
123
-
124
- ```typescript
125
- // src/middleware.ts
126
- import { createMiddleware } from '@solidjs/start/middleware'
127
- import { timeback } from '~/lib/timeback'
128
- import { solidStartHandler } from 'timeback/solid-start'
129
-
130
- export default createMiddleware({
131
- onRequest: [
132
- async event => {
133
- const response = await solidStartHandler({
134
- timeback,
135
- event,
136
- })
137
- if (response) return response
138
- },
139
- ],
140
- })
141
- ```
142
-
143
- ### TanStack Start
144
-
145
- ```typescript
146
- // src/routes/api/timeback/$.ts
147
- import { createFileRoute } from '@tanstack/react-router'
148
- import { toTanStackStartHandler } from 'timeback/tanstack-start'
149
-
150
- import { timeback } from '@/lib/timeback'
151
-
152
- const handlers = toTanStackStartHandler(timeback)
153
-
154
- export const Route = createFileRoute('/api/timeback/$')({
155
- server: { handlers },
156
- })
157
- ```
158
-
159
- ### Express
160
-
161
- ```typescript
162
- // server.ts
163
- import express from 'express'
164
- import { toExpressMiddleware } from 'timeback/express'
165
-
166
- import { timeback } from './lib/timeback'
167
-
168
- const app = express()
169
- app.use(express.json())
170
- app.use('/api/timeback', toExpressMiddleware(timeback))
171
- ```
172
-
173
- ## Client Adapters
174
-
175
- ### React
176
-
177
- ```tsx
178
- // app/providers.tsx
179
- 'use client'
180
-
181
- import { TimebackProvider } from 'timeback/react'
182
-
183
- export function Providers({ children }: { children: React.ReactNode }) {
184
- return <TimebackProvider>{children}</TimebackProvider>
185
- }
186
- ```
187
-
188
- ```tsx
189
- // components/ActivityTracker.tsx
190
- import { useEffect } from 'react'
191
- import { SignInButton, useTimeback } from 'timeback/react'
192
-
193
- function MyComponent() {
194
- const timeback = useTimeback()
195
-
196
- useEffect(() => {
197
- if (!timeback) return
198
-
199
- const activity = timeback.activity
200
- .new({ id: 'lesson-1', name: 'Intro', courseCode: 'MATH-101' })
201
- .start()
202
-
203
- return () => {
204
- activity.end()
205
- }
206
- }, [timeback])
207
-
208
- return <SignInButton size="lg" />
209
- }
210
- ```
211
-
212
- ### Vue
213
-
214
- ```vue
215
- <!-- app.vue -->
216
- <script setup>
217
- import { TimebackProvider } from 'timeback/vue'
218
- </script>
219
-
220
- <template>
221
- <TimebackProvider>
222
- <NuxtPage />
223
- </TimebackProvider>
224
- </template>
225
- ```
226
-
227
- ```vue
228
- <!-- components/ActivityTracker.vue -->
229
- <script setup>
230
- import { SignInButton, useTimeback } from 'timeback/vue'
231
- import { onMounted, onUnmounted } from 'vue'
232
-
233
- const timeback = useTimeback()
234
- let activity
235
-
236
- onMounted(() => {
237
- if (timeback.value) {
238
- activity = timeback.value.activity
239
- .new({ id: 'lesson-1', name: 'Intro', courseCode: 'MATH-101' })
240
- .start()
241
- }
242
- })
243
-
244
- onUnmounted(() => activity?.end())
245
- </script>
246
-
247
- <template>
248
- <SignInButton size="lg" />
249
- </template>
250
- ```
251
-
252
- ### Svelte
253
-
254
- ```svelte
255
- <!-- +layout.svelte -->
256
- <script>
257
- import { initTimeback } from 'timeback/svelte'
258
-
259
- initTimeback()
260
-
261
- let { children } = $props()
262
- </script>
263
-
264
- {@render children()}
10
+ bunx timeback
265
11
  ```
266
12
 
267
- ```svelte
268
- <!-- +page.svelte -->
269
- <script>
270
- import { onMount, onDestroy } from 'svelte'
271
- import { SignInButton, timeback } from 'timeback/svelte'
13
+ ## Usage
272
14
 
273
- let activity
274
-
275
- onMount(() => {
276
- if ($timeback) {
277
- activity = $timeback.activity
278
- .new({ id: 'lesson-1', name: 'Intro', courseCode: 'MATH-101' })
279
- .start()
280
- }
281
- })
282
-
283
- onDestroy(() => activity?.end())
284
- </script>
285
-
286
- <SignInButton size="lg" />
287
- ```
288
-
289
- ### Solid
290
-
291
- ```tsx
292
- // app.tsx
293
- import { TimebackProvider } from 'timeback/solid'
294
-
295
- export default function App() {
296
- return (
297
- <TimebackProvider>
298
- <Router root={props => <Suspense>{props.children}</Suspense>}>
299
- <FileRoutes />
300
- </Router>
301
- </TimebackProvider>
302
- )
303
- }
304
- ```
305
-
306
- ```tsx
307
- // components/ActivityTracker.tsx
308
- import { onCleanup, onMount } from 'solid-js'
309
- import { SignInButton, useTimeback } from 'timeback/solid'
310
-
311
- function MyComponent() {
312
- const timeback = useTimeback()
313
- let activity
314
-
315
- onMount(() => {
316
- if (!timeback) return
317
-
318
- activity = timeback.activity
319
- .new({ id: 'lesson-1', name: 'Intro', courseCode: 'MATH-101' })
320
- .start()
321
- })
322
-
323
- onCleanup(() => activity?.end())
324
-
325
- return <SignInButton size="lg" />
326
- }
327
- ```
328
-
329
- ## Identity Modes
330
-
331
- ### SSO Mode
332
-
333
- Uses Timeback as the identity provider via OIDC. When using `createTimeback()`, the SDK automatically resolves the Timeback user by email and returns an enriched `TimebackAuthUser` with `user.id` being the canonical `timebackId` (stable identifier).
334
-
335
- ```typescript
336
- identity: {
337
- mode: 'sso',
338
- clientId: process.env.AWS_COGNITO_CLIENT_ID!,
339
- clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
340
- redirectUri: 'http://localhost:3000/api/auth/sso/callback/timeback',
341
- onCallbackSuccess: async ({ user, idp, state, redirect }) => {
342
- // user.id is the timebackId (canonical stable identifier)
343
- // user.email, user.name come from Timeback profile
344
- // user.claims contains IdP data (sub, firstName, lastName, pictureUrl)
345
- // idp.tokens and idp.userInfo contain raw OIDC data if needed
346
- await setSession({ id: user.id, email: user.email })
347
- return redirect(state?.returnTo ?? '/')
348
- },
349
- onCallbackError: ({ error, errorCode, redirect }) => {
350
- // errorCode may be: missing_email, timeback_user_not_found,
351
- // timeback_user_ambiguous, timeback_user_lookup_failed
352
- console.error('SSO Error:', errorCode, error.message)
353
- return redirect('/?error=sso_failed')
354
- },
355
- getUser: () => getCurrentSession(),
356
- }
15
+ ```bash
16
+ timeback <command> [options]
17
+ timeback --help
357
18
  ```
358
19
 
359
- #### TimebackAuthUser Shape
360
-
361
- The `user` in `onCallbackSuccess` is a `TimebackAuthUser` with this structure:
20
+ ## Commands
362
21
 
363
- ```typescript
364
- interface TimebackAuthUser {
365
- id: string // Timeback user ID
366
- email: string
367
- name?: string
368
- school?: { id: string; name: string }
369
- grade?: number
370
- claims: {
371
- sub: string // OIDC subject identifier
372
- email: string
373
- firstName?: string
374
- lastName?: string
375
- pictureUrl?: string
376
- }
377
- }
378
- ```
22
+ ### `timeback api`
379
23
 
380
- #### Session Storage Guidance
24
+ Interact with education data APIs.
381
25
 
382
- For cookie-only sessions, store only the minimal payload to avoid cookie size limits:
26
+ ```bash
27
+ # Discovery
28
+ timeback api describe --service oneroster
383
29
 
384
- ```typescript
385
- // Recommended: store minimal session
386
- await setSession({ id: user.id, email: user.email })
30
+ # CRUD
31
+ timeback api oneroster schools list --env production
32
+ timeback api oneroster users get <id>
33
+ timeback api oneroster users create --file user.json
34
+ timeback api oneroster users delete <id>
387
35
 
388
- // Then in getUser, return what you stored:
389
- getUser: req => {
390
- const session = getSessionFromCookie(req)
391
- return session ? { id: session.id, email: session.email } : undefined
392
- }
36
+ # Filtering
37
+ timeback api oneroster enrollments list --active --classes id1,id2
38
+ timeback api oneroster users list --role teacher --max 100
393
39
  ```
394
40
 
395
- For DB-backed sessions, you can store the full `TimebackAuthUser` if desired.
41
+ See `timeback api oneroster --help` for all resources and options.
396
42
 
397
- ### Custom Mode
43
+ ## Configuration
398
44
 
399
- For apps with existing auth (Clerk, Auth0, Supabase, etc.):
45
+ API clients read credentials from environment variables:
400
46
 
401
- ```typescript
402
- identity: {
403
- mode: 'custom',
404
- getUser: async (req) => {
405
- const session = await getSession(req)
406
- if (!session) return undefined
407
- // Return user with timebackId as the id
408
- return { id: session.timebackId, email: session.email, name: session.name }
409
- },
410
- }
47
+ ```bash
48
+ # OneRoster
49
+ export ONEROSTER_BASE_URL="https://api.example.com"
50
+ export ONEROSTER_TOKEN_URL="https://auth.example.com/oauth2/token"
51
+ export ONEROSTER_CLIENT_ID="your-client-id"
52
+ export ONEROSTER_CLIENT_SECRET="your-client-secret"
411
53
  ```
412
54
 
413
- ## Identity-Only Integration
414
-
415
- If you only need Timeback SSO authentication without activity tracking or Timeback API integration, use `createTimebackIdentity()`. This is a lightweight alternative that:
416
-
417
- - Does not require Timeback API credentials
418
- - Does not require `timeback.config.ts`
419
- - Only exposes identity routes (sign-in, callback, sign-out)
420
- - Returns raw OIDC user info (no Timeback profile enrichment)
421
-
422
- **Note:** Unlike `createTimeback()`, the identity-only callback returns raw OIDC user info (`sub`, `email`, `name`, etc.) without resolving a Timeback user. Use `createTimeback()` if you need the canonical `timebackId`.
423
-
424
- ### Cloudflare Workers / workerd compatibility
55
+ ### `timeback.config.ts`
425
56
 
426
- `createTimebackIdentity()` is runtime-agnostic, but the main `timeback` entrypoint also includes
427
- Node-oriented functionality (notably config loading via `jiti`). Some edge runtimes
428
- (Cloudflare Workers / workerd) do not support the Node modules that `jiti` depends on.
57
+ Many CLI commands (notably `timeback init`, `timeback import`, `timeback edit`, `timeback sync`) read/write a `timeback.config.ts` file.
429
58
 
430
- If you're deploying identity-only SSO on Workers/workerd, import from the worker-safe entrypoint:
59
+ For fields that can differ per environment (staging vs production), use `course.overrides`:
431
60
 
432
61
  ```ts
433
- import { createTimebackIdentity, toNativeHandler } from 'timeback/edge'
434
- ```
435
-
436
- ### Server Setup
437
-
438
- ```typescript
439
- // lib/timeback.ts
440
- import { createTimebackIdentity } from 'timeback'
441
-
442
- export const timeback = createTimebackIdentity({
443
- env: 'production',
444
- identity: {
445
- mode: 'sso',
446
- clientId: process.env.AWS_COGNITO_CLIENT_ID!,
447
- clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
448
- onCallbackSuccess: async ({ user, tokens, redirect }) => {
449
- // user is raw OIDC userInfo (sub, email, name, picture, etc.)
450
- // No Timeback profile enrichment in identity-only mode
451
- await createSession({
452
- sub: user.sub,
453
- email: user.email,
454
- name: user.name,
455
- })
456
- return redirect('/')
62
+ export default {
63
+ name: 'My App',
64
+ // Default Caliper sensor (lowest priority)
65
+ sensor: 'https://my-app.example.com/sensors/default',
66
+ courses: [
67
+ {
68
+ subject: 'Math',
69
+ grade: 3,
70
+ courseCode: 'MATH-3',
71
+ // Base values (apply to all envs unless overridden)
72
+ level: 'Elementary',
73
+ metadata: { publishStatus: 'testing' },
74
+ // Env-specific overrides (applied when syncing/editing in that env)
75
+ overrides: {
76
+ staging: {
77
+ level: 'Staging Level',
78
+ sensor: 'https://staging.my-app.example.com/sensors/math',
79
+ metadata: { publishStatus: 'draft' },
80
+ },
81
+ production: {
82
+ metadata: { publishStatus: 'published' },
83
+ },
84
+ },
457
85
  },
458
- onCallbackError: ({ error, redirect }) => {
459
- console.error('SSO Error:', error)
460
- return redirect('/login?error=sso_failed')
461
- },
462
- getUser: req => getSessionFromRequest(req),
463
- },
464
- })
465
- ```
466
-
467
- ### Next.js
468
-
469
- ```typescript
470
- // app/api/timeback/[...timeback]/route.ts
471
- import { toNextjsHandler } from 'timeback/nextjs'
472
-
473
- import { timeback } from '@/lib/timeback'
474
-
475
- export const { GET, POST } = toNextjsHandler(timeback)
476
- ```
477
-
478
- ### Express
479
-
480
- ```typescript
481
- // server.ts
482
- import express from 'express'
483
- import { toExpressMiddleware } from 'timeback/express'
484
-
485
- import { timeback } from './lib/timeback'
486
-
487
- const app = express()
488
- app.use('/api/timeback', toExpressMiddleware(timeback))
489
- ```
490
-
491
- All other framework adapters (Nuxt, SvelteKit, SolidStart, TanStack Start) work identically with identity-only instances.
492
-
493
- ## Activity Tracking
494
-
495
- Activities track time spent on learning content:
496
-
497
- ```typescript
498
- // Start an activity
499
- const activity = timeback.activity
500
- .new({
501
- id: 'lesson-123',
502
- name: 'Introduction to Fractions',
503
- courseCode: 'MATH-101',
504
- })
505
- .start()
506
-
507
- // Pause/resume
508
- activity.pause()
509
- activity.resume()
510
-
511
- // End with metrics
512
- await activity.end({
513
- totalQuestions: 10,
514
- correctQuestions: 8,
515
- xpEarned: 80,
516
- masteredUnits: 1,
517
- })
518
- ```
519
-
520
- The SDK automatically sends activity data to your server, which forwards it to the Timeback API.
521
-
522
- ## Advanced: Direct API Access
523
-
524
- For advanced use cases that need to call Timeback services (OneRoster, Edubridge, Caliper, QTI) beyond what the SDK handlers provide, use `timeback.api`:
525
-
526
- ```typescript
527
- // Access via the timeback instance (lazy-initialized on first access)
528
- const { data: users } = await timeback.api.oneroster.users.list({
529
- limit: 10,
530
- where: { role: 'student' },
531
- })
532
-
533
- // Access any Timeback service
534
- const { data: orgs } = await timeback.api.oneroster.orgs.list()
535
- await timeback.api.caliper.emit(caliperEvent)
86
+ ],
87
+ }
536
88
  ```
537
89
 
538
- ### Access Patterns
539
-
540
- ```typescript
541
- // lib/timeback.ts
542
- import { createTimeback } from 'timeback'
543
-
544
- export const timeback = await createTimeback({
545
- env: 'staging',
546
- api: {
547
- clientId: process.env.TIMEBACK_API_CLIENT_ID!,
548
- clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
549
- },
550
- identity: { mode: 'custom', getUser: () => getSession() },
551
- })
90
+ Override merge rules:
552
91
 
553
- // Access the API client via timeback.api
554
- const { data: users } = await timeback.api.oneroster.users.list()
555
- ```
92
+ - `defaults` course `overrides[env]` (for `level`, `sensor`, `metadata`)
93
+ - `metadata` is merged (not replaced); `goals` and `metrics` are deep-merged
556
94
 
557
- ```typescript
558
- // Elsewhere in your app
559
- import { timeback } from './lib/timeback'
95
+ ## Debug Mode
560
96
 
561
- // The client is available at timeback.api
562
- const { data: orgs } = await timeback.api.oneroster.orgs.list()
97
+ ```bash
98
+ DEBUG=1 timeback api oneroster schools list
563
99
  ```
564
-
565
- ### Environment Mapping
566
-
567
- The SDK's `env` config controls runtime mode, but for outbound API calls:
568
-
569
- | SDK `env` | API calls use |
570
- | ------------ | ------------- |
571
- | `local` | `staging` |
572
- | `staging` | `staging` |
573
- | `production` | `production` |
574
-
575
- This means `env: 'local'` uses staging Timeback services, so you can develop locally against real (staging) data without additional configuration.
576
-
577
- ### When to Use
578
-
579
- - Fetching data not exposed by SDK handlers (e.g., listing orgs, courses, enrollments)
580
- - Emitting custom Caliper events
581
- - Building admin dashboards or reporting tools
582
- - Any direct Timeback API integration
583
-
584
- **Note:** `timeback.api` is only available on the full SDK (`createTimeback()`), not on identity-only instances (`createTimebackIdentity()`).
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Config types for timeback.config.ts files.
3
+ *
4
+ * Re-exported from `@timeback/types` so users can import `timeback/config`
5
+ * for editor IntelliSense in TS config files. SDK users can also use
6
+ * `@timeback/sdk/config` which exports the same types.
7
+ */
8
+ export type { CourseConfig, CourseDefaults, CourseGoals, CourseIds, CourseMetadata, CourseMetrics, CourseType, PublishStatus, TimebackConfig, } from '@timeback/types';