@timeback/sdk 0.1.7 → 0.1.9

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 (116) hide show
  1. package/README.md +97 -8
  2. package/dist/chunk-07j8zre9.js +2 -0
  3. package/dist/chunk-5171mkp2.js +2 -0
  4. package/dist/chunk-63afdp3y.js +8 -0
  5. package/dist/chunk-8gg8n8v9.js +2 -0
  6. package/dist/chunk-9se82640.js +1 -0
  7. package/dist/chunk-agpf1x3g.js +16 -0
  8. package/dist/chunk-hnf0tart.js +2 -0
  9. package/dist/chunk-qr0bbnsr.js +1 -0
  10. package/dist/chunk-whc53e0y.js +11 -0
  11. package/dist/chunk-x9gvef7q.js +1 -0
  12. package/dist/client/adapters/react/hooks/types.d.ts +80 -0
  13. package/dist/client/adapters/react/hooks/types.d.ts.map +1 -1
  14. package/dist/client/adapters/react/hooks/useTimebackProfile.d.ts +42 -0
  15. package/dist/client/adapters/react/hooks/useTimebackProfile.d.ts.map +1 -0
  16. package/dist/client/adapters/react/hooks/useTimebackVerification.d.ts +17 -5
  17. package/dist/client/adapters/react/hooks/useTimebackVerification.d.ts.map +1 -1
  18. package/dist/client/adapters/react/index.d.ts +2 -1
  19. package/dist/client/adapters/react/index.d.ts.map +1 -1
  20. package/dist/client/adapters/react/index.js +2 -494
  21. package/dist/client/adapters/react/provider.d.ts.map +1 -1
  22. package/dist/client/adapters/solid/index.d.ts +3 -0
  23. package/dist/client/adapters/solid/index.d.ts.map +1 -1
  24. package/dist/client/adapters/solid/index.ts +12 -0
  25. package/dist/client/adapters/solid/primitives/createTimebackProfile.d.ts +58 -0
  26. package/dist/client/adapters/solid/primitives/createTimebackProfile.d.ts.map +1 -0
  27. package/dist/client/adapters/solid/primitives/createTimebackProfile.ts +209 -0
  28. package/dist/client/adapters/solid/primitives/createTimebackVerification.d.ts +38 -0
  29. package/dist/client/adapters/solid/primitives/createTimebackVerification.d.ts.map +1 -0
  30. package/dist/client/adapters/solid/primitives/createTimebackVerification.ts +173 -0
  31. package/dist/client/adapters/solid/types.d.ts +109 -0
  32. package/dist/client/adapters/solid/types.d.ts.map +1 -0
  33. package/dist/client/adapters/solid/types.ts +110 -0
  34. package/dist/client/adapters/svelte/index.d.ts +2 -1
  35. package/dist/client/adapters/svelte/index.d.ts.map +1 -1
  36. package/dist/client/adapters/svelte/index.ts +11 -2
  37. package/dist/client/adapters/svelte/{stores.d.ts → stores/client.d.ts} +11 -9
  38. package/dist/client/adapters/svelte/stores/client.d.ts.map +1 -0
  39. package/dist/client/adapters/svelte/{stores.ts → stores/client.ts} +24 -52
  40. package/dist/client/adapters/svelte/stores/index.d.ts +10 -0
  41. package/dist/client/adapters/svelte/stores/index.d.ts.map +1 -0
  42. package/dist/client/adapters/svelte/stores/index.ts +22 -0
  43. package/dist/client/adapters/svelte/stores/profile.d.ts +66 -0
  44. package/dist/client/adapters/svelte/stores/profile.d.ts.map +1 -0
  45. package/dist/client/adapters/svelte/stores/profile.ts +168 -0
  46. package/dist/client/adapters/svelte/stores/verification.d.ts +43 -0
  47. package/dist/client/adapters/svelte/stores/verification.d.ts.map +1 -0
  48. package/dist/client/adapters/svelte/stores/verification.ts +208 -0
  49. package/dist/client/adapters/svelte/types.d.ts +35 -0
  50. package/dist/client/adapters/svelte/types.d.ts.map +1 -0
  51. package/dist/client/adapters/vue/composables/useTimebackProfile.d.ts +51 -0
  52. package/dist/client/adapters/vue/composables/useTimebackProfile.d.ts.map +1 -0
  53. package/dist/client/adapters/vue/composables/useTimebackProfile.ts +186 -0
  54. package/dist/client/adapters/vue/composables/useTimebackVerification.d.ts +46 -0
  55. package/dist/client/adapters/vue/composables/useTimebackVerification.d.ts.map +1 -0
  56. package/dist/client/adapters/vue/composables/useTimebackVerification.ts +169 -0
  57. package/dist/client/adapters/vue/index.d.ts +3 -0
  58. package/dist/client/adapters/vue/index.d.ts.map +1 -1
  59. package/dist/client/adapters/vue/index.ts +12 -1
  60. package/dist/client/adapters/vue/types.d.ts +109 -0
  61. package/dist/client/adapters/vue/types.d.ts.map +1 -0
  62. package/dist/client/adapters/vue/types.ts +110 -0
  63. package/dist/client/lib/activity/activity.class.d.ts +5 -5
  64. package/dist/client/lib/activity/activity.class.d.ts.map +1 -1
  65. package/dist/client/lib/user-cache.d.ts +39 -0
  66. package/dist/client/lib/user-cache.d.ts.map +1 -0
  67. package/dist/client/lib/user-cache.ts +168 -0
  68. package/dist/client/lib/utils.d.ts +15 -0
  69. package/dist/client/lib/utils.d.ts.map +1 -1
  70. package/dist/client/namespaces/activity.d.ts +2 -3
  71. package/dist/client/namespaces/activity.d.ts.map +1 -1
  72. package/dist/client.d.ts +1 -1
  73. package/dist/client.js +1 -257
  74. package/dist/edge.js +1 -86271
  75. package/dist/identity.js +1 -86131
  76. package/dist/index.d.ts +2 -2
  77. package/dist/index.js +22 -104883
  78. package/dist/server/adapters/express.js +1 -85973
  79. package/dist/server/adapters/native.js +2 -221
  80. package/dist/server/adapters/nextjs.js +1 -233
  81. package/dist/server/adapters/nuxt.js +1 -86046
  82. package/dist/server/adapters/solid-start.js +1 -85945
  83. package/dist/server/adapters/svelte-kit.js +1 -279
  84. package/dist/server/adapters/tanstack-start.js +1 -85918
  85. package/dist/server/handlers/activity/attempts.d.ts +51 -0
  86. package/dist/server/handlers/activity/attempts.d.ts.map +1 -0
  87. package/dist/server/handlers/activity/caliper.d.ts +46 -5
  88. package/dist/server/handlers/activity/caliper.d.ts.map +1 -1
  89. package/dist/server/handlers/activity/completion.d.ts +43 -0
  90. package/dist/server/handlers/activity/completion.d.ts.map +1 -0
  91. package/dist/server/handlers/activity/handler.d.ts +18 -1
  92. package/dist/server/handlers/activity/handler.d.ts.map +1 -1
  93. package/dist/server/handlers/activity/progress.d.ts +47 -0
  94. package/dist/server/handlers/activity/progress.d.ts.map +1 -0
  95. package/dist/server/handlers/activity/schema.d.ts +1 -2
  96. package/dist/server/handlers/activity/schema.d.ts.map +1 -1
  97. package/dist/server/handlers/activity/types.d.ts +1 -2
  98. package/dist/server/handlers/activity/types.d.ts.map +1 -1
  99. package/dist/server/lib/index.d.ts +1 -1
  100. package/dist/server/lib/index.d.ts.map +1 -1
  101. package/dist/server/lib/utils.d.ts +61 -0
  102. package/dist/server/lib/utils.d.ts.map +1 -1
  103. package/dist/server/timeback.d.ts +2 -2
  104. package/dist/server/timeback.d.ts.map +1 -1
  105. package/dist/server/types.d.ts +7 -1
  106. package/dist/server/types.d.ts.map +1 -1
  107. package/dist/shared/constants.d.ts +19 -0
  108. package/dist/shared/constants.d.ts.map +1 -1
  109. package/dist/shared/types.d.ts +62 -8
  110. package/dist/shared/types.d.ts.map +1 -1
  111. package/dist/shared/xp-calculator.d.ts +25 -0
  112. package/dist/shared/xp-calculator.d.ts.map +1 -0
  113. package/package.json +6 -4
  114. package/dist/client/adapters/svelte/stores.d.ts.map +0 -1
  115. package/dist/server/handlers/activity/gradebook.d.ts +0 -56
  116. package/dist/server/handlers/activity/gradebook.d.ts.map +0 -1
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Svelte Types
3
+ *
4
+ * Types for Svelte-specific stores.
5
+ */
6
+ import type { TimebackProfile } from '../../../shared/types';
7
+ /**
8
+ * Verification state for the current user.
9
+ */
10
+ export type TimebackVerificationState = {
11
+ status: 'loading';
12
+ } | {
13
+ status: 'verified';
14
+ timebackId: string;
15
+ } | {
16
+ status: 'unverified';
17
+ } | {
18
+ status: 'error';
19
+ message: string;
20
+ };
21
+ /**
22
+ * Profile state for the current user.
23
+ */
24
+ export type TimebackProfileState = {
25
+ status: 'idle';
26
+ } | {
27
+ status: 'loading';
28
+ } | {
29
+ status: 'loaded';
30
+ profile: TimebackProfile;
31
+ } | {
32
+ status: 'error';
33
+ message: string;
34
+ };
35
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/client/adapters/svelte/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAE5D;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAClC;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,GACxB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvC;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAC7B;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAClB;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,eAAe,CAAA;CAAE,GAC9C;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * useTimebackProfile
3
+ *
4
+ * Vue composable for fetching the current user's Timeback profile.
5
+ *
6
+ * This composable uses `useTimebackVerification` internally and only fetches
7
+ * the profile when the user is verified (i.e., exists in Timeback).
8
+ */
9
+ import type { ComputedRef, Ref } from 'vue';
10
+ import type { TimebackProfileState, UseTimebackProfileOptions } from '../types';
11
+ /**
12
+ * Fetch the current user's Timeback profile.
13
+ *
14
+ * This composable internally uses `useTimebackVerification` to ensure the user
15
+ * is verified before fetching. When `auto: true`, it automatically fetches
16
+ * the profile once verified. Otherwise, call `fetchProfile()` manually.
17
+ *
18
+ * @param options - Composable options
19
+ * @param options.enabled - If false, the composable does nothing and stays in idle state. Defaults to true.
20
+ * @param options.auto - If true, automatically fetch once verified. Defaults to false.
21
+ * @returns Profile state and fetch methods
22
+ *
23
+ * @example
24
+ * ```vue
25
+ * <script setup>
26
+ * import { useTimebackProfile } from '@timeback/sdk/vue'
27
+ *
28
+ * // Manual fetch
29
+ * const { state, canFetch, fetchProfile } = useTimebackProfile()
30
+ *
31
+ * // Or auto-fetch
32
+ * // const { state } = useTimebackProfile({ auto: true })
33
+ * </script>
34
+ *
35
+ * <template>
36
+ * <div v-if="state.status === 'loaded'">
37
+ * XP: {{ state.profile.xp }}
38
+ * </div>
39
+ * <button v-else @click="fetchProfile" :disabled="!canFetch">
40
+ * {{ state.status === 'loading' ? 'Loading...' : 'Load Profile' }}
41
+ * </button>
42
+ * </template>
43
+ * ```
44
+ */
45
+ export declare function useTimebackProfile(options?: UseTimebackProfileOptions): {
46
+ state: Ref<TimebackProfileState>;
47
+ canFetch: ComputedRef<boolean>;
48
+ fetchProfile: () => void;
49
+ refresh: () => void;
50
+ };
51
+ //# sourceMappingURL=useTimebackProfile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTimebackProfile.d.ts","sourceRoot":"","sources":["../../../../../src/client/adapters/vue/composables/useTimebackProfile.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,KAAK,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,GAAE,yBAA8B,GAAG;IAC5E,KAAK,EAAE,GAAG,CAAC,oBAAoB,CAAC,CAAA;IAChC,QAAQ,EAAE,WAAW,CAAC,OAAO,CAAC,CAAA;IAC9B,YAAY,EAAE,MAAM,IAAI,CAAA;IACxB,OAAO,EAAE,MAAM,IAAI,CAAA;CACnB,CAgIA"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * useTimebackProfile
3
+ *
4
+ * Vue composable for fetching the current user's Timeback profile.
5
+ *
6
+ * This composable uses `useTimebackVerification` internally and only fetches
7
+ * the profile when the user is verified (i.e., exists in Timeback).
8
+ */
9
+
10
+ import { computed, ref, watch } from 'vue'
11
+
12
+ import { fetchProfileOnce, getProfileCache } from '../../../lib/user-cache'
13
+ import { useTimeback } from '../provider'
14
+ import { useTimebackVerification } from './useTimebackVerification'
15
+
16
+ import type { ComputedRef, Ref } from 'vue'
17
+ import type { TimebackProfileState, UseTimebackProfileOptions } from '../types'
18
+
19
+ /**
20
+ * Fetch the current user's Timeback profile.
21
+ *
22
+ * This composable internally uses `useTimebackVerification` to ensure the user
23
+ * is verified before fetching. When `auto: true`, it automatically fetches
24
+ * the profile once verified. Otherwise, call `fetchProfile()` manually.
25
+ *
26
+ * @param options - Composable options
27
+ * @param options.enabled - If false, the composable does nothing and stays in idle state. Defaults to true.
28
+ * @param options.auto - If true, automatically fetch once verified. Defaults to false.
29
+ * @returns Profile state and fetch methods
30
+ *
31
+ * @example
32
+ * ```vue
33
+ * <script setup>
34
+ * import { useTimebackProfile } from '@timeback/sdk/vue'
35
+ *
36
+ * // Manual fetch
37
+ * const { state, canFetch, fetchProfile } = useTimebackProfile()
38
+ *
39
+ * // Or auto-fetch
40
+ * // const { state } = useTimebackProfile({ auto: true })
41
+ * </script>
42
+ *
43
+ * <template>
44
+ * <div v-if="state.status === 'loaded'">
45
+ * XP: {{ state.profile.xp }}
46
+ * </div>
47
+ * <button v-else @click="fetchProfile" :disabled="!canFetch">
48
+ * {{ state.status === 'loading' ? 'Loading...' : 'Load Profile' }}
49
+ * </button>
50
+ * </template>
51
+ * ```
52
+ */
53
+ export function useTimebackProfile(options: UseTimebackProfileOptions = {}): {
54
+ state: Ref<TimebackProfileState>
55
+ canFetch: ComputedRef<boolean>
56
+ fetchProfile: () => void
57
+ refresh: () => void
58
+ } {
59
+ const { enabled = true, auto = false } = options
60
+
61
+ const timebackRef = useTimeback()
62
+ const { state: verificationState } = useTimebackVerification({ enabled })
63
+
64
+ const state = ref<TimebackProfileState>({ status: 'idle' }) as Ref<TimebackProfileState>
65
+ const fetchNonce = ref(0)
66
+ const refreshNonce = ref(0)
67
+
68
+ let lastHandledFetchNonce = 0
69
+ let lastHandledRefreshNonce = 0
70
+ let autoTriggered = false
71
+
72
+ const canFetch = computed(
73
+ () => enabled && verificationState.value.status === 'verified' && !!timebackRef.value,
74
+ )
75
+
76
+ const fetchProfile = () => {
77
+ if (!canFetch.value) {
78
+ return
79
+ }
80
+
81
+ fetchNonce.value++
82
+ }
83
+
84
+ const refresh = () => {
85
+ if (!canFetch.value) {
86
+ return
87
+ }
88
+
89
+ refreshNonce.value++
90
+ }
91
+
92
+ watch(
93
+ [verificationState, fetchNonce, refreshNonce, () => timebackRef.value],
94
+ async ([verification, fetchN, refreshN, timeback], _prev, onCleanup) => {
95
+ if (!enabled) {
96
+ state.value = { status: 'idle' }
97
+
98
+ return
99
+ }
100
+
101
+ let cancelled = false
102
+
103
+ onCleanup(() => {
104
+ cancelled = true
105
+ })
106
+
107
+ if (verification.status !== 'verified') {
108
+ if (state.value.status !== 'idle') {
109
+ state.value = { status: 'idle' }
110
+ }
111
+
112
+ autoTriggered = false
113
+
114
+ return
115
+ }
116
+
117
+ if (auto && !autoTriggered && fetchN === 0 && refreshN === 0) {
118
+ autoTriggered = true
119
+ fetchNonce.value = 1
120
+ return
121
+ }
122
+
123
+ if (fetchN === 0 && refreshN === 0) {
124
+ return
125
+ }
126
+
127
+ const isNewFetch = fetchN > lastHandledFetchNonce
128
+ const isNewRefresh = refreshN > lastHandledRefreshNonce
129
+ const force = isNewRefresh
130
+
131
+ if (!isNewFetch && !isNewRefresh) {
132
+ if (timeback) {
133
+ const cached = getProfileCache(timeback)
134
+
135
+ if (cached) {
136
+ state.value = { status: 'loaded', profile: cached }
137
+
138
+ return
139
+ }
140
+ }
141
+
142
+ return
143
+ }
144
+
145
+ if (timeback && !force) {
146
+ const cached = getProfileCache(timeback)
147
+
148
+ if (cached) {
149
+ state.value = { status: 'loaded', profile: cached }
150
+ lastHandledFetchNonce = fetchN
151
+
152
+ return
153
+ }
154
+ }
155
+
156
+ state.value = { status: 'loading' }
157
+
158
+ try {
159
+ if (!timeback) {
160
+ return
161
+ }
162
+
163
+ lastHandledFetchNonce = fetchN
164
+ lastHandledRefreshNonce = refreshN
165
+
166
+ const profile = await fetchProfileOnce(timeback, force)
167
+
168
+ if (cancelled) {
169
+ return
170
+ }
171
+
172
+ state.value = { status: 'loaded', profile }
173
+ } catch (err) {
174
+ if (!cancelled) {
175
+ state.value = {
176
+ status: 'error',
177
+ message: err instanceof Error ? err.message : 'Failed to fetch profile',
178
+ }
179
+ }
180
+ }
181
+ },
182
+ { immediate: true },
183
+ )
184
+
185
+ return { state, canFetch, fetchProfile, refresh }
186
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * useTimebackVerification
3
+ *
4
+ * Vue composable for checking whether the current user can be verified in Timeback.
5
+ *
6
+ * This is a lightweight check (backed by `/api/timeback/user/verify`) and is
7
+ * intended for partner gating flows (e.g., "free for Timeback users").
8
+ */
9
+ import type { Ref } from 'vue';
10
+ import type { TimebackVerificationState, UseTimebackVerificationOptions } from '../types';
11
+ /**
12
+ * Verify the current user against Timeback.
13
+ *
14
+ * The composable runs automatically once the Timeback client is available, and
15
+ * provides a `refresh()` method to retry.
16
+ *
17
+ * By default, the composable retries failed verification attempts with exponential
18
+ * backoff to handle race conditions (e.g., user not yet created in backend).
19
+ *
20
+ * @param options - Composable options
21
+ * @returns Verification state and a refresh method
22
+ *
23
+ * @example
24
+ * ```vue
25
+ * <script setup>
26
+ * import { useTimebackVerification } from '@timeback/sdk/vue'
27
+ *
28
+ * const { state, refresh } = useTimebackVerification()
29
+ * </script>
30
+ *
31
+ * <template>
32
+ * <div v-if="state.status === 'verified'">
33
+ * Verified as {{ state.timebackId }}
34
+ * </div>
35
+ * <div v-else-if="state.status === 'loading'">
36
+ * Verifying...
37
+ * </div>
38
+ * <button @click="refresh">Refresh</button>
39
+ * </template>
40
+ * ```
41
+ */
42
+ export declare function useTimebackVerification(options?: UseTimebackVerificationOptions): {
43
+ state: Ref<TimebackVerificationState>;
44
+ refresh: () => void;
45
+ };
46
+ //# sourceMappingURL=useTimebackVerification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTimebackVerification.d.ts","sourceRoot":"","sources":["../../../../../src/client/adapters/vue/composables/useTimebackVerification.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAE9B,OAAO,KAAK,EAAE,yBAAyB,EAAE,8BAA8B,EAAE,MAAM,UAAU,CAAA;AAczF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,8BAAmC,GAAG;IACtF,KAAK,EAAE,GAAG,CAAC,yBAAyB,CAAC,CAAA;IACrC,OAAO,EAAE,MAAM,IAAI,CAAA;CACnB,CAsGA"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * useTimebackVerification
3
+ *
4
+ * Vue composable for checking whether the current user can be verified in Timeback.
5
+ *
6
+ * This is a lightweight check (backed by `/api/timeback/user/verify`) and is
7
+ * intended for partner gating flows (e.g., "free for Timeback users").
8
+ */
9
+
10
+ import { ref, watch } from 'vue'
11
+
12
+ import { DEFAULT_RETRY_ATTEMPTS, DEFAULT_RETRY_DELAYS_MS } from '../../../../shared/constants'
13
+ import { getVerifyCache, verifyOnce } from '../../../lib/user-cache'
14
+ import { getRetryDelay, sleep } from '../../../lib/utils'
15
+ import { useTimeback } from '../provider'
16
+
17
+ import type { Ref } from 'vue'
18
+ import type { TimebackVerifyResult } from '../../../../shared/types'
19
+ import type { TimebackVerificationState, UseTimebackVerificationOptions } from '../types'
20
+
21
+ /**
22
+ * Convert a verify result into a composable-friendly state machine.
23
+ *
24
+ * @param result - Raw verify result from the SDK client
25
+ * @returns Mapped verification state
26
+ */
27
+ function toState(result: TimebackVerifyResult): TimebackVerificationState {
28
+ return result.verified
29
+ ? { status: 'verified', timebackId: result.timebackId }
30
+ : { status: 'unverified' }
31
+ }
32
+
33
+ /**
34
+ * Verify the current user against Timeback.
35
+ *
36
+ * The composable runs automatically once the Timeback client is available, and
37
+ * provides a `refresh()` method to retry.
38
+ *
39
+ * By default, the composable retries failed verification attempts with exponential
40
+ * backoff to handle race conditions (e.g., user not yet created in backend).
41
+ *
42
+ * @param options - Composable options
43
+ * @returns Verification state and a refresh method
44
+ *
45
+ * @example
46
+ * ```vue
47
+ * <script setup>
48
+ * import { useTimebackVerification } from '@timeback/sdk/vue'
49
+ *
50
+ * const { state, refresh } = useTimebackVerification()
51
+ * </script>
52
+ *
53
+ * <template>
54
+ * <div v-if="state.status === 'verified'">
55
+ * Verified as {{ state.timebackId }}
56
+ * </div>
57
+ * <div v-else-if="state.status === 'loading'">
58
+ * Verifying...
59
+ * </div>
60
+ * <button @click="refresh">Refresh</button>
61
+ * </template>
62
+ * ```
63
+ */
64
+ export function useTimebackVerification(options: UseTimebackVerificationOptions = {}): {
65
+ state: Ref<TimebackVerificationState>
66
+ refresh: () => void
67
+ } {
68
+ const enabled = options.enabled ?? true
69
+ const retryAttempts = options.retryAttempts ?? DEFAULT_RETRY_ATTEMPTS
70
+ const retryDelays = options.retryDelays ?? DEFAULT_RETRY_DELAYS_MS
71
+ const timebackRef = useTimeback()
72
+
73
+ const state = ref<TimebackVerificationState>({
74
+ status: 'loading',
75
+ }) as Ref<TimebackVerificationState>
76
+ const refreshNonce = ref(0)
77
+
78
+ let lastHandledRefreshNonce = 0
79
+
80
+ const refresh = () => {
81
+ if (!enabled) return
82
+ refreshNonce.value++
83
+ }
84
+
85
+ watch(
86
+ [() => timebackRef.value, refreshNonce],
87
+ async ([timeback, nonce], _prev, onCleanup) => {
88
+ if (!enabled) return
89
+
90
+ let cancelled = false
91
+
92
+ onCleanup(() => {
93
+ cancelled = true
94
+ })
95
+
96
+ const force = nonce > lastHandledRefreshNonce
97
+
98
+ if (timeback && !force) {
99
+ const cached = getVerifyCache(timeback)
100
+
101
+ if (cached) {
102
+ state.value = toState(cached)
103
+ return
104
+ }
105
+ }
106
+
107
+ state.value = { status: 'loading' }
108
+
109
+ if (!timeback) return
110
+
111
+ if (force) {
112
+ lastHandledRefreshNonce = nonce
113
+ }
114
+
115
+ let lastError: Error | undefined
116
+
117
+ for (let attempt = 0; attempt <= retryAttempts; attempt++) {
118
+ if (cancelled) return
119
+
120
+ /**
121
+ * Wait for a calculated delay before retrying the verification request.
122
+ *
123
+ * This occurs only on subsequent attempts (not the initial try),
124
+ * introducing a backoff based on the configured retry delays.
125
+ */
126
+ if (attempt > 0) {
127
+ const delay = getRetryDelay(retryDelays, attempt - 1)
128
+ await sleep(delay)
129
+ if (cancelled) return
130
+ }
131
+
132
+ try {
133
+ const result = await verifyOnce(timeback, force || attempt > 0)
134
+
135
+ if (cancelled) return
136
+
137
+ state.value = toState(result)
138
+
139
+ return
140
+ } catch (err) {
141
+ lastError =
142
+ err instanceof Error ? err : new Error('Failed to verify Timeback user')
143
+ /**
144
+ * An error occurred during this verification attempt.
145
+ * Proceeding to the next retry attempt if any remain.
146
+ *
147
+ * The last encountered error is saved for potential error state handling.
148
+ */
149
+ }
150
+ }
151
+
152
+ /**
153
+ * All verification attempts have failed after exhausting
154
+ * the configured retry policy. The verification state will
155
+ * transition to "error" if not cancelled, and the last encountered
156
+ * error message (if available) will be presented to the consumer.
157
+ */
158
+ if (!cancelled && lastError) {
159
+ state.value = {
160
+ status: 'error',
161
+ message: lastError.message,
162
+ }
163
+ }
164
+ },
165
+ { immediate: true },
166
+ )
167
+
168
+ return { state, refresh }
169
+ }
@@ -37,6 +37,9 @@
37
37
  export { Activity, createClient, TimebackClient } from '@timeback/sdk/client';
38
38
  export type { ActivityMetrics, ActivityParams, TimebackIdentity } from '@timeback/sdk/client';
39
39
  export { TimebackProvider, useTimeback } from './provider';
40
+ export { useTimebackVerification } from './composables/useTimebackVerification';
41
+ export { useTimebackProfile } from './composables/useTimebackProfile';
42
+ export type { TimebackProfileState, TimebackVerificationState, UseTimebackProfileOptions, UseTimebackProfileResult, UseTimebackVerificationOptions, UseTimebackVerificationResult, } from './types';
40
43
  export { default as SignInButton } from './SignInButton.vue';
41
44
  export type { SignInButtonProps } from './SignInButton.vue';
42
45
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/client/adapters/vue/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAGH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC7E,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAG7F,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG1D,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAC5D,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/client/adapters/vue/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC7E,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAG7F,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AACrE,YAAY,EACX,oBAAoB,EACpB,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EACxB,8BAA8B,EAC9B,6BAA6B,GAC7B,MAAM,SAAS,CAAA;AAGhB,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAC5D,YAAY,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA"}
@@ -35,13 +35,24 @@
35
35
  * ```
36
36
  */
37
37
 
38
- // Re-export from timeback/client for convenience
39
38
  export { Activity, createClient, TimebackClient } from '@timeback/sdk/client'
40
39
  export type { ActivityMetrics, ActivityParams, TimebackIdentity } from '@timeback/sdk/client'
41
40
 
42
41
  // Vue-specific
43
42
  export { TimebackProvider, useTimeback } from './provider'
44
43
 
44
+ // Composables
45
+ export { useTimebackVerification } from './composables/useTimebackVerification'
46
+ export { useTimebackProfile } from './composables/useTimebackProfile'
47
+ export type {
48
+ TimebackProfileState,
49
+ TimebackVerificationState,
50
+ UseTimebackProfileOptions,
51
+ UseTimebackProfileResult,
52
+ UseTimebackVerificationOptions,
53
+ UseTimebackVerificationResult,
54
+ } from './types'
55
+
45
56
  // Components
46
57
  export { default as SignInButton } from './SignInButton.vue'
47
58
  export type { SignInButtonProps } from './SignInButton.vue'
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Vue Types
3
+ *
4
+ * Types for Vue-specific composables.
5
+ */
6
+ import type { TimebackProfile } from '../../../shared/types';
7
+ /**
8
+ * Verification state for the current user.
9
+ */
10
+ export type TimebackVerificationState = {
11
+ status: 'loading';
12
+ } | {
13
+ status: 'verified';
14
+ timebackId: string;
15
+ } | {
16
+ status: 'unverified';
17
+ } | {
18
+ status: 'error';
19
+ message: string;
20
+ };
21
+ /**
22
+ * Profile state for the current user.
23
+ */
24
+ export type TimebackProfileState = {
25
+ status: 'idle';
26
+ } | {
27
+ status: 'loading';
28
+ } | {
29
+ status: 'loaded';
30
+ profile: TimebackProfile;
31
+ } | {
32
+ status: 'error';
33
+ message: string;
34
+ };
35
+ /**
36
+ * Options for the useTimebackVerification composable.
37
+ */
38
+ export interface UseTimebackVerificationOptions {
39
+ /**
40
+ * If false, the composable does nothing and stays in loading state.
41
+ *
42
+ * Use this to delay verification until prerequisites are met
43
+ * (e.g., user exists in your database).
44
+ *
45
+ * @default true
46
+ */
47
+ enabled?: boolean;
48
+ /**
49
+ * Number of retry attempts on failure.
50
+ *
51
+ * This helps handle race conditions where the user may not be
52
+ * fully set up in the backend when verification first runs.
53
+ *
54
+ * Set to 0 to disable retries.
55
+ *
56
+ * @default 3
57
+ */
58
+ retryAttempts?: number;
59
+ /**
60
+ * Delay in ms before each retry attempt.
61
+ *
62
+ * Can be a single number (same delay for all retries) or an array
63
+ * of delays for each attempt (e.g., [100, 300, 1000] for exponential backoff).
64
+ *
65
+ * @default [100, 300, 1000]
66
+ */
67
+ retryDelays?: number | readonly number[];
68
+ }
69
+ /**
70
+ * Return value of the useTimebackVerification composable.
71
+ */
72
+ export interface UseTimebackVerificationResult {
73
+ /** Current verification state (reactive ref) */
74
+ state: TimebackVerificationState;
75
+ /** Force a re-verification. No-op if not enabled. */
76
+ refresh: () => void;
77
+ }
78
+ /**
79
+ * Options for the useTimebackProfile composable.
80
+ */
81
+ export interface UseTimebackProfileOptions {
82
+ /**
83
+ * If false, the composable does nothing and stays in idle state.
84
+ *
85
+ * @default true
86
+ */
87
+ enabled?: boolean;
88
+ /**
89
+ * If true, automatically fetch the profile once the user is verified.
90
+ * If false, you must call `fetchProfile()` manually.
91
+ *
92
+ * @default false
93
+ */
94
+ auto?: boolean;
95
+ }
96
+ /**
97
+ * Return value of the useTimebackProfile composable.
98
+ */
99
+ export interface UseTimebackProfileResult {
100
+ /** Current profile state (reactive ref) */
101
+ state: TimebackProfileState;
102
+ /** Whether the profile can be fetched (user is verified and client is ready) */
103
+ canFetch: boolean;
104
+ /** Manually trigger a profile fetch. No-op if canFetch is false. */
105
+ fetchProfile: () => void;
106
+ /** Force a re-fetch even if already loaded. No-op if canFetch is false. */
107
+ refresh: () => void;
108
+ }
109
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/client/adapters/vue/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAE5D;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAClC;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC1C;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,GACxB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvC;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAC7B;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAClB;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GACrB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,OAAO,EAAE,eAAe,CAAA;CAAE,GAC9C;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvC;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC9C;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAA;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC7C,gDAAgD;IAChD,KAAK,EAAE,yBAAyB,CAAA;IAEhC,qDAAqD;IACrD,OAAO,EAAE,MAAM,IAAI,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACzC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACxC,2CAA2C;IAC3C,KAAK,EAAE,oBAAoB,CAAA;IAE3B,gFAAgF;IAChF,QAAQ,EAAE,OAAO,CAAA;IAEjB,oEAAoE;IACpE,YAAY,EAAE,MAAM,IAAI,CAAA;IAExB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,IAAI,CAAA;CACnB"}