timeback 0.1.3 → 0.1.4
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 +34 -559
- package/dist/cli/src/config.d.ts +8 -0
- package/dist/cli.js +111089 -0
- package/dist/types/src/config.d.ts +106 -0
- package/dist/types/src/index.d.ts +5 -0
- package/dist/types/src/primitives.d.ts +53 -0
- package/package.json +27 -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,59 @@
|
|
|
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
|
-
})
|
|
10
|
+
bunx timeback
|
|
141
11
|
```
|
|
142
12
|
|
|
143
|
-
|
|
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()}
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
```svelte
|
|
268
|
-
<!-- +page.svelte -->
|
|
269
|
-
<script>
|
|
270
|
-
import { onMount, onDestroy } from 'svelte'
|
|
271
|
-
import { SignInButton, timeback } from 'timeback/svelte'
|
|
272
|
-
|
|
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>
|
|
13
|
+
## Usage
|
|
285
14
|
|
|
286
|
-
|
|
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
|
-
}
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
#### TimebackAuthUser Shape
|
|
360
|
-
|
|
361
|
-
The `user` in `onCallbackSuccess` is a `TimebackAuthUser` with this structure:
|
|
362
|
-
|
|
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
|
-
```
|
|
379
|
-
|
|
380
|
-
#### Session Storage Guidance
|
|
381
|
-
|
|
382
|
-
For cookie-only sessions, store only the minimal payload to avoid cookie size limits:
|
|
383
|
-
|
|
384
|
-
```typescript
|
|
385
|
-
// Recommended: store minimal session
|
|
386
|
-
await setSession({ id: user.id, email: user.email })
|
|
387
|
-
|
|
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
|
-
}
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
For DB-backed sessions, you can store the full `TimebackAuthUser` if desired.
|
|
396
|
-
|
|
397
|
-
### Custom Mode
|
|
398
|
-
|
|
399
|
-
For apps with existing auth (Clerk, Auth0, Supabase, etc.):
|
|
400
|
-
|
|
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
|
-
}
|
|
411
|
-
```
|
|
412
|
-
|
|
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
|
|
425
|
-
|
|
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.
|
|
429
|
-
|
|
430
|
-
If you're deploying identity-only SSO on Workers/workerd, import from the worker-safe entrypoint:
|
|
431
|
-
|
|
432
|
-
```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('/')
|
|
457
|
-
},
|
|
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))
|
|
15
|
+
```bash
|
|
16
|
+
timeback <command> [options]
|
|
17
|
+
timeback --help
|
|
489
18
|
```
|
|
490
19
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
## Activity Tracking
|
|
494
|
-
|
|
495
|
-
Activities track time spent on learning content:
|
|
20
|
+
## Commands
|
|
496
21
|
|
|
497
|
-
|
|
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()
|
|
22
|
+
### `timeback api`
|
|
506
23
|
|
|
507
|
-
|
|
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
|
-
```
|
|
24
|
+
Interact with education data APIs.
|
|
519
25
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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`:
|
|
26
|
+
```bash
|
|
27
|
+
# Discovery
|
|
28
|
+
timeback api describe --service oneroster
|
|
525
29
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
})
|
|
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>
|
|
532
35
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
36
|
+
# Filtering
|
|
37
|
+
timeback api oneroster enrollments list --active --classes id1,id2
|
|
38
|
+
timeback api oneroster users list --role teacher --max 100
|
|
536
39
|
```
|
|
537
40
|
|
|
538
|
-
|
|
41
|
+
See `timeback api oneroster --help` for all resources and options.
|
|
539
42
|
|
|
540
|
-
|
|
541
|
-
// lib/timeback.ts
|
|
542
|
-
import { createTimeback } from 'timeback'
|
|
43
|
+
## Configuration
|
|
543
44
|
|
|
544
|
-
|
|
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
|
-
})
|
|
45
|
+
API clients read credentials from environment variables:
|
|
552
46
|
|
|
553
|
-
|
|
554
|
-
|
|
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"
|
|
555
53
|
```
|
|
556
54
|
|
|
557
|
-
|
|
558
|
-
// Elsewhere in your app
|
|
559
|
-
import { timeback } from './lib/timeback'
|
|
55
|
+
## Debug Mode
|
|
560
56
|
|
|
561
|
-
|
|
562
|
-
|
|
57
|
+
```bash
|
|
58
|
+
DEBUG=1 timeback api oneroster schools list
|
|
563
59
|
```
|
|
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';
|