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.
- package/README.md +65 -550
- package/dist/cli/src/config.d.ts +8 -0
- package/dist/cli.js +131552 -0
- package/dist/types/src/config.d.ts +186 -0
- package/dist/types/src/index.d.ts +5 -0
- package/dist/types/src/primitives.d.ts +53 -0
- package/package.json +29 -91
- package/schema.json +222 -0
- package/dist/client/adapters/react/SignInButton.d.ts +0 -60
- package/dist/client/adapters/react/SignInButton.d.ts.map +0 -1
- package/dist/client/adapters/react/index.d.ts +0 -43
- package/dist/client/adapters/react/index.d.ts.map +0 -1
- package/dist/client/adapters/react/index.js +0 -478
- package/dist/client/adapters/react/provider.d.ts +0 -74
- package/dist/client/adapters/react/provider.d.ts.map +0 -1
- package/dist/client/adapters/solid/SignInButton.d.ts +0 -52
- package/dist/client/adapters/solid/SignInButton.d.ts.map +0 -1
- package/dist/client/adapters/solid/SignInButton.tsx +0 -321
- package/dist/client/adapters/solid/context.d.ts +0 -73
- package/dist/client/adapters/solid/context.d.ts.map +0 -1
- package/dist/client/adapters/solid/context.tsx +0 -91
- package/dist/client/adapters/solid/index.d.ts +0 -42
- package/dist/client/adapters/solid/index.d.ts.map +0 -1
- package/dist/client/adapters/solid/index.ts +0 -46
- package/dist/client/adapters/svelte/SignInButton.svelte +0 -234
- package/dist/client/adapters/svelte/SignInButton.svelte.d.ts +0 -24
- package/dist/client/adapters/svelte/index.d.ts +0 -33
- package/dist/client/adapters/svelte/index.d.ts.map +0 -1
- package/dist/client/adapters/svelte/index.ts +0 -38
- package/dist/client/adapters/svelte/stores.d.ts +0 -62
- package/dist/client/adapters/svelte/stores.d.ts.map +0 -1
- package/dist/client/adapters/svelte/stores.ts +0 -139
- package/dist/client/adapters/vue/SignInButton.vue +0 -260
- package/dist/client/adapters/vue/SignInButton.vue.d.ts +0 -53
- package/dist/client/adapters/vue/index.d.ts +0 -43
- package/dist/client/adapters/vue/index.d.ts.map +0 -1
- package/dist/client/adapters/vue/index.ts +0 -48
- package/dist/client/adapters/vue/provider.d.ts +0 -94
- package/dist/client/adapters/vue/provider.d.ts.map +0 -1
- package/dist/client/adapters/vue/provider.ts +0 -147
- package/dist/client/index.d.ts +0 -9
- package/dist/client/index.d.ts.map +0 -1
- package/dist/client/lib/activity/activity.class.d.ts +0 -73
- package/dist/client/lib/activity/activity.class.d.ts.map +0 -1
- package/dist/client/lib/activity/activity.d.ts +0 -16
- package/dist/client/lib/activity/activity.d.ts.map +0 -1
- package/dist/client/lib/activity/index.d.ts +0 -6
- package/dist/client/lib/activity/index.d.ts.map +0 -1
- package/dist/client/lib/utils.d.ts +0 -20
- package/dist/client/lib/utils.d.ts.map +0 -1
- package/dist/client/namespaces/activity.d.ts +0 -37
- package/dist/client/namespaces/activity.d.ts.map +0 -1
- package/dist/client/namespaces/auth.d.ts +0 -33
- package/dist/client/namespaces/auth.d.ts.map +0 -1
- package/dist/client/namespaces/index.d.ts +0 -7
- package/dist/client/namespaces/index.d.ts.map +0 -1
- package/dist/client/namespaces/user.d.ts +0 -29
- package/dist/client/namespaces/user.d.ts.map +0 -1
- package/dist/client/timeback-client.class.d.ts +0 -37
- package/dist/client/timeback-client.class.d.ts.map +0 -1
- package/dist/client/timeback-client.d.ts +0 -29
- package/dist/client/timeback-client.d.ts.map +0 -1
- package/dist/client.d.ts +0 -30
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -198
- package/dist/edge.d.ts +0 -13
- package/dist/edge.d.ts.map +0 -1
- package/dist/edge.js +0 -1149
- package/dist/identity.d.ts +0 -14
- package/dist/identity.d.ts.map +0 -1
- package/dist/identity.js +0 -1019
- package/dist/index.d.ts +0 -48
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -10221
- package/dist/server/adapters/express.d.ts +0 -66
- package/dist/server/adapters/express.d.ts.map +0 -1
- package/dist/server/adapters/express.js +0 -9326
- package/dist/server/adapters/native.d.ts +0 -47
- package/dist/server/adapters/native.d.ts.map +0 -1
- package/dist/server/adapters/native.js +0 -190
- package/dist/server/adapters/nextjs.d.ts +0 -32
- package/dist/server/adapters/nextjs.d.ts.map +0 -1
- package/dist/server/adapters/nextjs.js +0 -202
- package/dist/server/adapters/nuxt.d.ts +0 -98
- package/dist/server/adapters/nuxt.d.ts.map +0 -1
- package/dist/server/adapters/nuxt.js +0 -9395
- package/dist/server/adapters/solid-start.d.ts +0 -63
- package/dist/server/adapters/solid-start.d.ts.map +0 -1
- package/dist/server/adapters/solid-start.js +0 -9294
- package/dist/server/adapters/svelte-kit.d.ts +0 -84
- package/dist/server/adapters/svelte-kit.d.ts.map +0 -1
- package/dist/server/adapters/svelte-kit.js +0 -243
- package/dist/server/adapters/tanstack-start.d.ts +0 -42
- package/dist/server/adapters/tanstack-start.d.ts.map +0 -1
- package/dist/server/adapters/tanstack-start.js +0 -9272
- package/dist/server/adapters/types.d.ts +0 -294
- package/dist/server/adapters/types.d.ts.map +0 -1
- package/dist/server/adapters/utils.d.ts +0 -76
- package/dist/server/adapters/utils.d.ts.map +0 -1
- package/dist/server/handlers/activity.d.ts +0 -28
- package/dist/server/handlers/activity.d.ts.map +0 -1
- package/dist/server/handlers/identity-full.d.ts +0 -28
- package/dist/server/handlers/identity-full.d.ts.map +0 -1
- package/dist/server/handlers/identity-only.d.ts +0 -22
- package/dist/server/handlers/identity-only.d.ts.map +0 -1
- package/dist/server/handlers/index.d.ts +0 -9
- package/dist/server/handlers/index.d.ts.map +0 -1
- package/dist/server/handlers/user.d.ts +0 -30
- package/dist/server/handlers/user.d.ts.map +0 -1
- package/dist/server/index.d.ts +0 -9
- package/dist/server/index.d.ts.map +0 -1
- package/dist/server/lib/index.d.ts +0 -10
- package/dist/server/lib/index.d.ts.map +0 -1
- package/dist/server/lib/logger.d.ts +0 -21
- package/dist/server/lib/logger.d.ts.map +0 -1
- package/dist/server/lib/oidc.d.ts +0 -76
- package/dist/server/lib/oidc.d.ts.map +0 -1
- package/dist/server/lib/resolve-timeback-user.d.ts +0 -42
- package/dist/server/lib/resolve-timeback-user.d.ts.map +0 -1
- package/dist/server/lib/utils.d.ts +0 -54
- package/dist/server/lib/utils.d.ts.map +0 -1
- package/dist/server/timeback-identity.d.ts +0 -19
- package/dist/server/timeback-identity.d.ts.map +0 -1
- package/dist/server/timeback.d.ts +0 -68
- package/dist/server/timeback.d.ts.map +0 -1
- package/dist/server/types.d.ts +0 -402
- package/dist/server/types.d.ts.map +0 -1
- package/dist/shared/constants.d.ts +0 -18
- package/dist/shared/constants.d.ts.map +0 -1
- package/dist/shared/types.d.ts +0 -144
- package/dist/shared/types.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -1,584 +1,99 @@
|
|
|
1
|
-
#
|
|
1
|
+
# timeback
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
8
|
+
bun add -g timeback
|
|
30
9
|
# or
|
|
31
|
-
|
|
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
|
-
|
|
268
|
-
<!-- +page.svelte -->
|
|
269
|
-
<script>
|
|
270
|
-
import { onMount, onDestroy } from 'svelte'
|
|
271
|
-
import { SignInButton, timeback } from 'timeback/svelte'
|
|
13
|
+
## Usage
|
|
272
14
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
-
|
|
360
|
-
|
|
361
|
-
The `user` in `onCallbackSuccess` is a `TimebackAuthUser` with this structure:
|
|
20
|
+
## Commands
|
|
362
21
|
|
|
363
|
-
|
|
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
|
-
|
|
24
|
+
Interact with education data APIs.
|
|
381
25
|
|
|
382
|
-
|
|
26
|
+
```bash
|
|
27
|
+
# Discovery
|
|
28
|
+
timeback api describe --service oneroster
|
|
383
29
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
-
|
|
41
|
+
See `timeback api oneroster --help` for all resources and options.
|
|
396
42
|
|
|
397
|
-
|
|
43
|
+
## Configuration
|
|
398
44
|
|
|
399
|
-
|
|
45
|
+
API clients read credentials from environment variables:
|
|
400
46
|
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
+
For fields that can differ per environment (staging vs production), use `course.overrides`:
|
|
431
60
|
|
|
432
61
|
```ts
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
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
|
-
|
|
459
|
-
|
|
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
|
-
|
|
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
|
-
|
|
554
|
-
|
|
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
|
-
|
|
558
|
-
// Elsewhere in your app
|
|
559
|
-
import { timeback } from './lib/timeback'
|
|
95
|
+
## Debug Mode
|
|
560
96
|
|
|
561
|
-
|
|
562
|
-
|
|
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';
|