timeback 0.1.0 → 0.1.2

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 (44) hide show
  1. package/README.md +349 -121
  2. package/dist/client/adapters/vue/SignInButton.vue +260 -0
  3. package/dist/client/adapters/vue/SignInButton.vue.d.ts +53 -0
  4. package/dist/client/adapters/vue/index.d.ts +43 -0
  5. package/dist/client/adapters/vue/index.d.ts.map +1 -0
  6. package/dist/client/adapters/vue/index.ts +48 -0
  7. package/dist/client/adapters/vue/provider.d.ts +94 -0
  8. package/dist/client/adapters/vue/provider.d.ts.map +1 -0
  9. package/dist/client/adapters/vue/provider.ts +147 -0
  10. package/dist/index.d.ts +20 -3
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +271 -22
  13. package/dist/server/adapters/express.d.ts +9 -5
  14. package/dist/server/adapters/express.d.ts.map +1 -1
  15. package/dist/server/adapters/express.js +19 -3
  16. package/dist/server/adapters/native.d.ts +5 -3
  17. package/dist/server/adapters/native.d.ts.map +1 -1
  18. package/dist/server/adapters/native.js +11 -3
  19. package/dist/server/adapters/nextjs.d.ts +5 -3
  20. package/dist/server/adapters/nextjs.d.ts.map +1 -1
  21. package/dist/server/adapters/nextjs.js +11 -3
  22. package/dist/server/adapters/nuxt.d.ts +98 -0
  23. package/dist/server/adapters/nuxt.d.ts.map +1 -0
  24. package/dist/server/adapters/nuxt.js +673 -0
  25. package/dist/server/adapters/solid-start.d.ts +5 -3
  26. package/dist/server/adapters/solid-start.d.ts.map +1 -1
  27. package/dist/server/adapters/solid-start.js +16 -6
  28. package/dist/server/adapters/svelte-kit.d.ts +5 -3
  29. package/dist/server/adapters/svelte-kit.d.ts.map +1 -1
  30. package/dist/server/adapters/svelte-kit.js +16 -6
  31. package/dist/server/adapters/tanstack-start.d.ts +42 -0
  32. package/dist/server/adapters/tanstack-start.d.ts.map +1 -0
  33. package/dist/server/adapters/tanstack-start.js +11 -3
  34. package/dist/server/adapters/types.d.ts +91 -9
  35. package/dist/server/adapters/types.d.ts.map +1 -1
  36. package/dist/server/adapters/utils.d.ts +24 -5
  37. package/dist/server/adapters/utils.d.ts.map +1 -1
  38. package/dist/server/index.d.ts +2 -2
  39. package/dist/server/index.d.ts.map +1 -1
  40. package/dist/server/timeback.d.ts +39 -2
  41. package/dist/server/timeback.d.ts.map +1 -1
  42. package/dist/server/types.d.ts +37 -0
  43. package/dist/server/types.d.ts.map +1 -1
  44. package/package.json +14 -2
package/README.md CHANGED
@@ -1,6 +1,26 @@
1
1
  # Timeback SDK
2
2
 
3
- TypeScript SDK for integrating with Timeback, providing both server-side BFF logic and client-side React components.
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)
4
24
 
5
25
  ## Installation
6
26
 
@@ -10,210 +30,418 @@ npm install timeback
10
30
  bun add timeback
11
31
  ```
12
32
 
13
- ## Server-Side Setup
33
+ ## Quick Start
14
34
 
15
- The server SDK auto-discovers your `timeback.config.ts` file and provides route handlers for Next.js.
35
+ 1. Create a server instance with your credentials
36
+ 2. Mount the route handlers for your framework
37
+ 3. Wrap your app with the client provider
38
+ 4. Use hooks/composables to track activities
16
39
 
17
- ### Configuration
40
+ ## Server Adapters
18
41
 
19
- ```typescript
20
- // app/lib/timeback.ts
21
- import { createTimeback } from 'timeback'
42
+ All server adapters use the same core configuration:
22
43
 
23
- export const timeback = await createTimeback({
24
- env: 'production', // 'development' | 'staging' | 'production'
44
+ ```typescript
45
+ // lib/timeback.ts
46
+ import { createServer } from 'timeback'
25
47
 
26
- // API credentials for Timeback API calls
48
+ export const timeback = await createServer({
49
+ env: 'staging', // 'development' | 'staging' | 'production'
27
50
  api: {
28
51
  clientId: process.env.TIMEBACK_API_CLIENT_ID!,
29
52
  clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET!,
30
53
  },
31
-
32
- // Identity configuration
33
54
  identity: {
34
- // Option A: Timeback SSO
35
55
  mode: 'sso',
36
- clientId: process.env.TIMEBACK_SSO_CLIENT_ID!,
37
- clientSecret: process.env.TIMEBACK_SSO_CLIENT_SECRET!,
38
-
39
- // Option B: Custom identity provider (Clerk, Auth0, Supabase, etc.)
40
- mode: 'custom',
41
- getUser: async req => {
42
- const session = await getSession(req)
43
- return {
44
- id: session.userId,
45
- email: session.user.email,
46
- name: session.user.name,
47
- }
56
+ clientId: process.env.AWS_COGNITO_CLIENT_ID!,
57
+ clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
58
+ redirectUri: 'http://localhost:3000/api/auth/sso/callback/timeback',
59
+ onCallbackSuccess: ({ user, redirect }) => {
60
+ // Set session, then redirect
61
+ return redirect('/')
62
+ },
63
+ onCallbackError: ({ error, redirect }) => {
64
+ console.error('SSO Error:', error)
65
+ return redirect('/?error=sso_failed')
48
66
  },
67
+ getUser: () => getSession(), // Return current user or undefined
49
68
  },
50
69
  })
51
70
  ```
52
71
 
53
- ### Next.js Route Handlers
72
+ ### Next.js
54
73
 
55
74
  ```typescript
56
75
  // app/api/timeback/[...timeback]/route.ts
76
+ import { toNextjsHandler } from 'timeback/nextjs'
77
+
57
78
  import { timeback } from '@/lib/timeback'
58
79
 
59
- export const { GET, POST } = timeback.handlers()
80
+ export const { GET, POST } = toNextjsHandler(timeback)
60
81
  ```
61
82
 
62
- This exposes:
83
+ ### Nuxt
84
+
85
+ ```typescript
86
+ // server/middleware/timeback.ts
87
+ import { nuxtHandler } from 'timeback/nuxt'
63
88
 
64
- - `POST /api/timeback/activity` - Receives activity events from client
65
- - `GET /api/timeback/identity/session` - Returns current user
66
- - `GET /api/timeback/identity/signin` - Initiates SSO flow (SSO mode only)
67
- - `GET /api/timeback/identity/callback` - SSO callback (SSO mode only)
89
+ import { timeback } from '../lib/timeback'
68
90
 
69
- ## Client-Side Setup
91
+ export default defineEventHandler(async event => {
92
+ const response = await nuxtHandler({
93
+ timeback,
94
+ event,
95
+ })
96
+ if (response) return response
97
+ })
98
+ ```
70
99
 
71
- ### Configuration
100
+ ### SvelteKit
72
101
 
73
102
  ```typescript
74
- // app/lib/timeback-client.ts
75
- import { createTimebackClient } from 'timeback/client/react'
103
+ // src/hooks.server.ts
104
+ import { building } from '$app/environment'
105
+ import { timeback } from '$lib/timeback'
106
+ import { svelteKitHandler } from 'timeback/svelte-kit'
107
+
108
+ import type { Handle } from '@sveltejs/kit'
109
+
110
+ export const handle: Handle = ({ event, resolve }) => {
111
+ return svelteKitHandler({
112
+ timeback,
113
+ event,
114
+ resolve,
115
+ building,
116
+ })
117
+ }
118
+ ```
76
119
 
77
- // Zero config - baseURL defaults to window.location.origin + '/api/timeback'
78
- export const timebackClient = createTimebackClient()
120
+ ### SolidStart
79
121
 
80
- // Or with explicit URL
81
- export const timebackClient = createTimebackClient({
82
- baseURL: 'https://myapp.com/api/timeback',
122
+ ```typescript
123
+ // src/middleware.ts
124
+ import { createMiddleware } from '@solidjs/start/middleware'
125
+ import { timeback } from '~/lib/timeback'
126
+ import { solidStartHandler } from 'timeback/solid-start'
127
+
128
+ export default createMiddleware({
129
+ onRequest: [
130
+ async event => {
131
+ const response = await solidStartHandler({
132
+ timeback,
133
+ event,
134
+ })
135
+ if (response) return response
136
+ },
137
+ ],
83
138
  })
84
139
  ```
85
140
 
86
- ### React Provider
141
+ ### TanStack Start
142
+
143
+ ```typescript
144
+ // src/routes/api/timeback/$.ts
145
+ import { createFileRoute } from '@tanstack/react-router'
146
+ import { toTanStackStartHandler } from 'timeback/tanstack-start'
147
+
148
+ import { timeback } from '@/lib/timeback'
149
+
150
+ const handlers = toTanStackStartHandler(timeback)
151
+
152
+ export const Route = createFileRoute('/api/timeback/$')({
153
+ server: { handlers },
154
+ })
155
+ ```
156
+
157
+ ### Express
158
+
159
+ ```typescript
160
+ // server.ts
161
+ import express from 'express'
162
+ import { toExpressMiddleware } from 'timeback/express'
163
+
164
+ import { timeback } from './lib/timeback'
165
+
166
+ const app = express()
167
+ app.use(express.json())
168
+ app.use('/api/timeback', toExpressMiddleware(timeback))
169
+ ```
170
+
171
+ ## Client Adapters
172
+
173
+ ### React
87
174
 
88
175
  ```tsx
89
176
  // app/providers.tsx
90
- import { TimebackProvider } from 'timeback/client/react'
177
+ 'use client'
91
178
 
92
- import { timebackClient } from '@/lib/timeback-client'
179
+ import { TimebackProvider } from 'timeback/react'
93
180
 
94
- export function Providers({ children }) {
95
- return <TimebackProvider client={timebackClient}>{children}</TimebackProvider>
181
+ export function Providers({ children }: { children: React.ReactNode }) {
182
+ return <TimebackProvider>{children}</TimebackProvider>
96
183
  }
97
184
  ```
98
185
 
99
- ### Hooks
100
-
101
186
  ```tsx
102
- import { useActivity, useTimeback } from 'timeback/client/react'
187
+ // components/ActivityTracker.tsx
188
+ import { useEffect } from 'react'
189
+ import { SignInButton, useTimeback } from 'timeback/react'
103
190
 
104
- function UserMenu() {
105
- const { user, isLoading, identityMode, signIn, signOut } = useTimeback()
191
+ function MyComponent() {
192
+ const timeback = useTimeback()
106
193
 
107
- if (isLoading) return <Spinner />
194
+ useEffect(() => {
195
+ if (!timeback) return
108
196
 
109
- if (!user) {
110
- return <button onClick={signIn}>Sign In</button>
111
- }
197
+ const activity = timeback.activity
198
+ .new({ id: 'lesson-1', name: 'Intro', courseCode: 'MATH-101' })
199
+ .start()
112
200
 
113
- return (
114
- <div>
115
- <span>{user.name}</span>
116
- <button onClick={signOut}>Sign Out</button>
117
- </div>
118
- )
201
+ return () => {
202
+ activity.end()
203
+ }
204
+ }, [timeback])
205
+
206
+ return <SignInButton size="lg" />
119
207
  }
120
208
  ```
121
209
 
122
- ### Activity Tracking
210
+ ### Vue
123
211
 
124
- #### React Hook
212
+ ```vue
213
+ <!-- app.vue -->
214
+ <script setup>
215
+ import { TimebackProvider } from 'timeback/vue'
216
+ </script>
125
217
 
126
- ```tsx
127
- function LessonPlayer({ lessonId, courseCode }) {
128
- // Starts on mount, ends on unmount, restarts if deps change
129
- const activity = useActivity({
130
- type: 'lesson',
131
- objectId: lessonId,
132
- courseCode,
218
+ <template>
219
+ <TimebackProvider>
220
+ <NuxtPage />
221
+ </TimebackProvider>
222
+ </template>
223
+ ```
224
+
225
+ ```vue
226
+ <!-- components/ActivityTracker.vue -->
227
+ <script setup>
228
+ import { SignInButton, useTimeback } from 'timeback/vue'
229
+ import { onMounted, onUnmounted } from 'vue'
230
+
231
+ const timeback = useTimeback()
232
+ let activity
233
+
234
+ onMounted(() => {
235
+ if (timeback.value) {
236
+ activity = timeback.value.activity
237
+ .new({ id: 'lesson-1', name: 'Intro', courseCode: 'MATH-101' })
238
+ .start()
239
+ }
240
+ })
241
+
242
+ onUnmounted(() => activity?.end())
243
+ </script>
244
+
245
+ <template>
246
+ <SignInButton size="lg" />
247
+ </template>
248
+ ```
249
+
250
+ ### Svelte
251
+
252
+ ```svelte
253
+ <!-- +layout.svelte -->
254
+ <script>
255
+ import { initTimeback } from 'timeback/svelte'
256
+
257
+ initTimeback()
258
+
259
+ let { children } = $props()
260
+ </script>
261
+
262
+ {@render children()}
263
+ ```
264
+
265
+ ```svelte
266
+ <!-- +page.svelte -->
267
+ <script>
268
+ import { onMount, onDestroy } from 'svelte'
269
+ import { SignInButton, timeback } from 'timeback/svelte'
270
+
271
+ let activity
272
+
273
+ onMount(() => {
274
+ if ($timeback) {
275
+ activity = $timeback.activity
276
+ .new({ id: 'lesson-1', name: 'Intro', courseCode: 'MATH-101' })
277
+ .start()
278
+ }
133
279
  })
134
280
 
281
+ onDestroy(() => activity?.end())
282
+ </script>
283
+
284
+ <SignInButton size="lg" />
285
+ ```
286
+
287
+ ### Solid
288
+
289
+ ```tsx
290
+ // app.tsx
291
+ import { TimebackProvider } from 'timeback/solid'
292
+
293
+ export default function App() {
135
294
  return (
136
- <>
137
- <button onClick={() => activity.pause()}>Pause</button>
138
- <button onClick={() => activity.resume()}>Resume</button>
139
- <span>Time: {activity.elapsedMs}ms</span>
140
- </>
295
+ <TimebackProvider>
296
+ <Router root={props => <Suspense>{props.children}</Suspense>}>
297
+ <FileRoutes />
298
+ </Router>
299
+ </TimebackProvider>
141
300
  )
142
301
  }
143
302
  ```
144
303
 
145
- #### Vanilla API
304
+ ```tsx
305
+ // components/ActivityTracker.tsx
306
+ import { onCleanup, onMount } from 'solid-js'
307
+ import { SignInButton, useTimeback } from 'timeback/solid'
146
308
 
147
- ```typescript
148
- import { createTimebackClient } from 'timeback/client'
309
+ function MyComponent() {
310
+ const timeback = useTimeback()
311
+ let activity
149
312
 
150
- const client = createTimebackClient()
313
+ onMount(() => {
314
+ if (!timeback) return
151
315
 
152
- // Start tracking
153
- const activity = client.startActivity({
154
- type: 'lesson',
155
- objectId: 'lesson-123',
156
- courseCode: 'FASTMATH-1',
157
- })
316
+ activity = timeback.activity
317
+ .new({ id: 'lesson-1', name: 'Intro', courseCode: 'MATH-101' })
318
+ .start()
319
+ })
158
320
 
159
- // Pause/resume as needed
160
- activity.pause()
161
- activity.resume()
321
+ onCleanup(() => activity?.end())
162
322
 
163
- // End tracking - sends ActivityCompletedEvent + TimeSpentEvent to server
164
- await activity.end()
323
+ return <SignInButton size="lg" />
324
+ }
165
325
  ```
166
326
 
167
327
  ## Identity Modes
168
328
 
169
- ### Timeback SSO
329
+ ### SSO Mode
170
330
 
171
- Uses Timeback as the identity provider via OIDC. Users sign in through Timeback's authentication flow.
331
+ Uses Timeback as the identity provider via OIDC:
172
332
 
173
333
  ```typescript
174
334
  identity: {
175
- mode: 'sso',
176
- clientId: process.env.TIMEBACK_SSO_CLIENT_ID!,
177
- clientSecret: process.env.TIMEBACK_SSO_CLIENT_SECRET!,
335
+ mode: 'sso',
336
+ clientId: process.env.AWS_COGNITO_CLIENT_ID!,
337
+ clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
338
+ redirectUri: 'http://localhost:3000/api/auth/sso/callback/timeback',
339
+ onCallbackSuccess: ({ user, state, redirect }) => redirect('/'),
340
+ onCallbackError: ({ error, redirect }) => redirect('/?error=sso_failed'),
341
+ getUser: () => getCurrentSession(),
178
342
  }
179
343
  ```
180
344
 
181
- ### Custom Identity
345
+ ### Custom Mode
182
346
 
183
- For apps with existing authentication (Clerk, Auth0, Supabase, etc.), provide a `getUser` function that returns the current user.
347
+ For apps with existing auth (Clerk, Auth0, Supabase, etc.):
184
348
 
185
349
  ```typescript
186
350
  identity: {
187
- mode: 'custom',
188
- getUser: async (req) => {
189
- // Use your existing auth system
190
- const session = await clerk.getSession(req)
191
- return {
192
- id: session.userId,
193
- email: session.user.email,
194
- name: session.user.name,
195
- }
196
- },
351
+ mode: 'custom',
352
+ getUser: async (req) => {
353
+ const session = await getSession(req)
354
+ if (!session) return undefined
355
+ return { id: session.userId, email: session.email, name: session.name }
356
+ },
197
357
  }
198
358
  ```
199
359
 
200
- ## API Reference
360
+ ## Identity-Only Integration
201
361
 
202
- ### Server
362
+ If you only need Timeback SSO authentication without activity tracking or Timeback API integration, use `createIdentityServer()`. This is a lightweight alternative that:
203
363
 
204
- - `createTimeback(config)` - Create a Timeback server instance
205
- - `timeback.handlers()` - Get Next.js route handlers
364
+ - Does not require Timeback API credentials
365
+ - Does not require `timeback.config.ts`
366
+ - Only exposes identity routes (sign-in, callback, sign-out)
206
367
 
207
- ### Client
368
+ ### Server Setup
208
369
 
209
- - `createTimebackClient(config?)` - Create a Timeback client
210
- - `client.startActivity(params)` - Start tracking an activity
211
- - `client.signIn()` - Initiate SSO sign-in
212
- - `client.signOut()` - Sign out
213
- - `client.fetchSession()` - Fetch current session
370
+ ```typescript
371
+ // lib/timeback.ts
372
+ import { createIdentityServer } from 'timeback'
214
373
 
215
- ### React
374
+ export const timeback = createIdentityServer({
375
+ env: 'production',
376
+ identity: {
377
+ mode: 'sso',
378
+ clientId: process.env.AWS_COGNITO_CLIENT_ID!,
379
+ clientSecret: process.env.AWS_COGNITO_CLIENT_SECRET!,
380
+ onCallbackSuccess: async ({ user, tokens, redirect }) => {
381
+ // Create your own session, then redirect
382
+ await createSession(user)
383
+ return redirect('/')
384
+ },
385
+ onCallbackError: ({ error, redirect }) => {
386
+ console.error('SSO Error:', error)
387
+ return redirect('/login?error=sso_failed')
388
+ },
389
+ getUser: req => getSessionFromRequest(req),
390
+ },
391
+ })
392
+ ```
393
+
394
+ ### Next.js
395
+
396
+ ```typescript
397
+ // app/api/timeback/[...timeback]/route.ts
398
+ import { toNextjsHandler } from 'timeback/nextjs'
399
+
400
+ import { timeback } from '@/lib/timeback'
401
+
402
+ export const { GET, POST } = toNextjsHandler(timeback)
403
+ ```
404
+
405
+ ### Express
406
+
407
+ ```typescript
408
+ // server.ts
409
+ import express from 'express'
410
+ import { toExpressMiddleware } from 'timeback/express'
411
+
412
+ import { timeback } from './lib/timeback'
413
+
414
+ const app = express()
415
+ app.use('/api/timeback', toExpressMiddleware(timeback))
416
+ ```
417
+
418
+ All other framework adapters (Nuxt, SvelteKit, SolidStart, TanStack Start) work identically with identity-only instances.
419
+
420
+ ## Activity Tracking
421
+
422
+ Activities track time spent on learning content:
423
+
424
+ ```typescript
425
+ // Start an activity
426
+ const activity = timeback.activity
427
+ .new({
428
+ id: 'lesson-123',
429
+ name: 'Introduction to Fractions',
430
+ courseCode: 'MATH-101',
431
+ })
432
+ .start()
433
+
434
+ // Pause/resume
435
+ activity.pause()
436
+ activity.resume()
437
+
438
+ // End with metrics
439
+ await activity.end({
440
+ totalQuestions: 10,
441
+ correctQuestions: 8,
442
+ xpEarned: 80,
443
+ masteredUnits: 1,
444
+ })
445
+ ```
216
446
 
217
- - `TimebackProvider` - Context provider
218
- - `useTimeback()` - Hook for identity state and actions
219
- - `useActivity(params)` - Hook for activity tracking with lifecycle management
447
+ The SDK automatically sends activity data to your server, which forwards it to the Timeback API.