@timeback/sdk 0.1.8 → 0.1.10

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 (159) hide show
  1. package/README.md +6 -6
  2. package/dist/chunk-3mqpr9vx.js +2 -0
  3. package/dist/chunk-92nnwa7t.js +2 -0
  4. package/dist/{chunk-ewsp6v3b.js → chunk-af3xwwsv.js} +5 -5
  5. package/dist/{chunk-07j8zre9.js → chunk-b8649tw4.js} +1 -1
  6. package/dist/{chunk-3886xy48.js → chunk-bd09q1fw.js} +6 -6
  7. package/dist/chunk-js665z11.js +1 -0
  8. package/dist/chunk-nsr7a2dv.js +2 -0
  9. package/dist/chunk-sgcwg4j6.js +1 -0
  10. package/dist/{chunk-9se82640.js → chunk-txwjkpfz.js} +1 -1
  11. package/dist/client/adapters/react/hooks/types.d.ts +34 -27
  12. package/dist/client/adapters/react/hooks/types.d.ts.map +1 -1
  13. package/dist/client/adapters/react/hooks/useTimebackVerification.d.ts +17 -5
  14. package/dist/client/adapters/react/hooks/useTimebackVerification.d.ts.map +1 -1
  15. package/dist/client/adapters/react/index.d.ts +1 -1
  16. package/dist/client/adapters/react/index.d.ts.map +1 -1
  17. package/dist/client/adapters/react/index.js +2 -2
  18. package/dist/client/adapters/react/provider.d.ts.map +1 -1
  19. package/dist/client/adapters/solid/primitives/createTimebackVerification.d.ts +3 -1
  20. package/dist/client/adapters/solid/primitives/createTimebackVerification.d.ts.map +1 -1
  21. package/dist/client/adapters/solid/primitives/createTimebackVerification.ts +55 -15
  22. package/dist/client/adapters/solid/types.d.ts +25 -29
  23. package/dist/client/adapters/solid/types.d.ts.map +1 -1
  24. package/dist/client/adapters/solid/types.ts +27 -18
  25. package/dist/client/adapters/svelte/stores/client.d.ts.map +1 -1
  26. package/dist/client/adapters/svelte/stores/client.ts +2 -9
  27. package/dist/client/adapters/svelte/stores/profile.d.ts +1 -1
  28. package/dist/client/adapters/svelte/stores/profile.d.ts.map +1 -1
  29. package/dist/client/adapters/svelte/stores/profile.ts +4 -11
  30. package/dist/client/adapters/svelte/stores/verification.d.ts +1 -1
  31. package/dist/client/adapters/svelte/stores/verification.d.ts.map +1 -1
  32. package/dist/client/adapters/svelte/stores/verification.ts +90 -17
  33. package/dist/client/adapters/svelte/types.d.ts +1 -29
  34. package/dist/client/adapters/svelte/types.d.ts.map +1 -1
  35. package/dist/client/adapters/vue/composables/useTimebackVerification.d.ts +3 -1
  36. package/dist/client/adapters/vue/composables/useTimebackVerification.d.ts.map +1 -1
  37. package/dist/client/adapters/vue/composables/useTimebackVerification.ts +59 -18
  38. package/dist/client/adapters/vue/provider.d.ts.map +1 -1
  39. package/dist/client/adapters/vue/provider.ts +4 -11
  40. package/dist/client/adapters/vue/types.d.ts +25 -29
  41. package/dist/client/adapters/vue/types.d.ts.map +1 -1
  42. package/dist/client/adapters/vue/types.ts +27 -18
  43. package/dist/client/auth/types.d.ts +1 -1
  44. package/dist/client/index.d.ts +1 -1
  45. package/dist/client/lib/activity/activity.class.d.ts +130 -22
  46. package/dist/client/lib/activity/activity.class.d.ts.map +1 -1
  47. package/dist/client/lib/activity/transport.d.ts +15 -0
  48. package/dist/client/lib/activity/transport.d.ts.map +1 -0
  49. package/dist/client/lib/activity/types.d.ts +53 -0
  50. package/dist/client/lib/activity/types.d.ts.map +1 -0
  51. package/dist/client/lib/utils.d.ts +33 -0
  52. package/dist/client/lib/utils.d.ts.map +1 -1
  53. package/dist/client/lib/utils.ts +109 -0
  54. package/dist/client/namespaces/activity.d.ts +45 -6
  55. package/dist/client/namespaces/activity.d.ts.map +1 -1
  56. package/dist/client/timeback-client.class.d.ts +7 -1
  57. package/dist/client/timeback-client.class.d.ts.map +1 -1
  58. package/dist/client.d.ts +1 -1
  59. package/dist/client.js +1 -1
  60. package/dist/edge.js +1 -1
  61. package/dist/identity.js +1 -1
  62. package/dist/index.d.ts +3 -3
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +22 -22
  65. package/dist/server/adapters/express.d.ts.map +1 -1
  66. package/dist/server/adapters/express.js +1 -1
  67. package/dist/server/adapters/native.d.ts.map +1 -1
  68. package/dist/server/adapters/native.js +1 -1
  69. package/dist/server/adapters/nextjs.js +1 -1
  70. package/dist/server/adapters/nuxt.d.ts.map +1 -1
  71. package/dist/server/adapters/nuxt.js +1 -1
  72. package/dist/server/adapters/solid-start.d.ts.map +1 -1
  73. package/dist/server/adapters/solid-start.js +1 -1
  74. package/dist/server/adapters/svelte-kit.d.ts.map +1 -1
  75. package/dist/server/adapters/svelte-kit.js +1 -1
  76. package/dist/server/adapters/tanstack-start.d.ts.map +1 -1
  77. package/dist/server/adapters/tanstack-start.js +1 -1
  78. package/dist/server/adapters/utils.d.ts +1 -1
  79. package/dist/server/adapters/utils.d.ts.map +1 -1
  80. package/dist/server/handlers/activity/caliper.d.ts +50 -14
  81. package/dist/server/handlers/activity/caliper.d.ts.map +1 -1
  82. package/dist/server/handlers/activity/heartbeat-handler.d.ts +15 -0
  83. package/dist/server/handlers/activity/heartbeat-handler.d.ts.map +1 -0
  84. package/dist/server/handlers/activity/index.d.ts +5 -3
  85. package/dist/server/handlers/activity/index.d.ts.map +1 -1
  86. package/dist/server/handlers/activity/progress.d.ts +2 -2
  87. package/dist/server/handlers/activity/progress.d.ts.map +1 -1
  88. package/dist/server/handlers/activity/schema.d.ts +40 -6
  89. package/dist/server/handlers/activity/schema.d.ts.map +1 -1
  90. package/dist/server/handlers/activity/submit-handler.d.ts +29 -0
  91. package/dist/server/handlers/activity/submit-handler.d.ts.map +1 -0
  92. package/dist/server/handlers/activity/submit.d.ts +44 -0
  93. package/dist/server/handlers/activity/submit.d.ts.map +1 -0
  94. package/dist/server/handlers/activity/types.d.ts +126 -5
  95. package/dist/server/handlers/activity/types.d.ts.map +1 -1
  96. package/dist/server/handlers/identity/handler.d.ts +23 -4
  97. package/dist/server/handlers/identity/handler.d.ts.map +1 -1
  98. package/dist/server/handlers/identity/index.d.ts +2 -2
  99. package/dist/server/handlers/identity/index.d.ts.map +1 -1
  100. package/dist/server/handlers/identity/oidc.d.ts.map +1 -1
  101. package/dist/server/handlers/identity/types.d.ts +0 -6
  102. package/dist/server/handlers/identity/types.d.ts.map +1 -1
  103. package/dist/server/handlers/index.d.ts +3 -3
  104. package/dist/server/handlers/index.d.ts.map +1 -1
  105. package/dist/server/handlers/user/handler.d.ts.map +1 -1
  106. package/dist/server/handlers/user/profile.d.ts.map +1 -1
  107. package/dist/server/handlers/user/types.d.ts +3 -0
  108. package/dist/server/handlers/user/types.d.ts.map +1 -1
  109. package/dist/server/handlers/user/verify.d.ts.map +1 -1
  110. package/dist/server/index.d.ts +1 -1
  111. package/dist/server/index.d.ts.map +1 -1
  112. package/dist/server/lib/hooks.d.ts +20 -0
  113. package/dist/server/lib/hooks.d.ts.map +1 -0
  114. package/dist/server/lib/index.d.ts +4 -2
  115. package/dist/server/lib/index.d.ts.map +1 -1
  116. package/dist/server/lib/logger.d.ts +36 -9
  117. package/dist/server/lib/logger.d.ts.map +1 -1
  118. package/dist/server/lib/resolve.d.ts +1 -1
  119. package/dist/server/lib/resolve.d.ts.map +1 -1
  120. package/dist/server/lib/utils.d.ts +19 -1
  121. package/dist/server/lib/utils.d.ts.map +1 -1
  122. package/dist/server/lib/validation.d.ts +55 -0
  123. package/dist/server/lib/validation.d.ts.map +1 -0
  124. package/dist/server/namespaces/activity/index.d.ts +8 -0
  125. package/dist/server/namespaces/activity/index.d.ts.map +1 -0
  126. package/dist/server/namespaces/activity/record.d.ts +49 -0
  127. package/dist/server/namespaces/activity/record.d.ts.map +1 -0
  128. package/dist/server/namespaces/activity/schema.d.ts +50 -0
  129. package/dist/server/namespaces/activity/schema.d.ts.map +1 -0
  130. package/dist/server/namespaces/user/get-profile.d.ts +32 -0
  131. package/dist/server/namespaces/user/get-profile.d.ts.map +1 -0
  132. package/dist/server/namespaces/user/index.d.ts +8 -0
  133. package/dist/server/namespaces/user/index.d.ts.map +1 -0
  134. package/dist/server/namespaces/user/verify.d.ts +28 -0
  135. package/dist/server/namespaces/user/verify.d.ts.map +1 -0
  136. package/dist/server/timeback.d.ts +3 -3
  137. package/dist/server/timeback.d.ts.map +1 -1
  138. package/dist/server/types.d.ts +394 -12
  139. package/dist/server/types.d.ts.map +1 -1
  140. package/dist/shared/constants.d.ts +20 -0
  141. package/dist/shared/constants.d.ts.map +1 -1
  142. package/dist/shared/constants.ts +51 -0
  143. package/dist/shared/index.d.ts +9 -0
  144. package/dist/shared/index.d.ts.map +1 -0
  145. package/dist/shared/schemas.d.ts +57 -0
  146. package/dist/shared/schemas.d.ts.map +1 -0
  147. package/dist/shared/types.d.ts +263 -10
  148. package/dist/shared/types.d.ts.map +1 -1
  149. package/dist/shared/types.ts +620 -0
  150. package/package.json +5 -5
  151. package/dist/chunk-ahy54f2r.js +0 -2
  152. package/dist/chunk-j1xdrfqj.js +0 -2
  153. package/dist/chunk-qaa129bd.js +0 -2
  154. package/dist/chunk-qr0bbnsr.js +0 -1
  155. package/dist/chunk-rgbpvxbv.js +0 -1
  156. package/dist/server/handlers/activity/handler.d.ts +0 -32
  157. package/dist/server/handlers/activity/handler.d.ts.map +0 -1
  158. package/dist/shared/xp-calculator.d.ts +0 -25
  159. package/dist/shared/xp-calculator.d.ts.map +0 -1
@@ -8,6 +8,8 @@ import { writable } from 'svelte/store'
8
8
 
9
9
  import { TimebackClient } from '@timeback/sdk/client'
10
10
 
11
+ import { isBrowser } from '../../../lib/utils'
12
+
11
13
  import type { Readable } from 'svelte/store'
12
14
 
13
15
  let clientInstance: TimebackClient | undefined
@@ -24,15 +26,6 @@ function getOrCreateClient(): TimebackClient {
24
26
  return clientInstance
25
27
  }
26
28
 
27
- /**
28
- * Check if we're running in the browser.
29
- *
30
- * @returns True if in browser
31
- */
32
- function isBrowser(): boolean {
33
- return typeof window !== 'undefined'
34
- }
35
-
36
29
  /**
37
30
  * Internal client store.
38
31
  */
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Svelte store for user profile state.
5
5
  */
6
- import type { TimebackProfileState } from '../types';
7
6
  import type { Readable } from 'svelte/store';
7
+ import type { TimebackProfileState } from '../types';
8
8
  /**
9
9
  * Store containing the Timeback profile state.
10
10
  *
@@ -1 +1 @@
1
- {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../../../../src/client/adapters/svelte/stores/profile.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,oBAAoB,EAA6B,MAAM,UAAU,CAAA;AAC/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AA6E5C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,oBAAoB,CAAgB,CAAA;AAE3E;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,OAAO,CAAiB,CAAA;AAEvE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAS3C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAS7C"}
1
+ {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../../../../src/client/adapters/svelte/stores/profile.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EAAE,oBAAoB,EAA6B,MAAM,UAAU,CAAA;AAoE/E;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,EAAE,QAAQ,CAAC,oBAAoB,CAAgB,CAAA;AAE3E;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,OAAO,CAAiB,CAAA;AAEvE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAS3C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAS7C"}
@@ -4,27 +4,20 @@
4
4
  * Svelte store for user profile state.
5
5
  */
6
6
 
7
+ import { writable } from 'svelte/store'
8
+
7
9
  import { fetchProfileOnce, getProfileCache } from '../../../lib/user-cache'
10
+ import { isBrowser } from '../../../lib/utils'
8
11
  import { getClientInstance } from './client'
9
- import { writable } from 'svelte/store'
10
12
  import { verificationStore } from './verification'
11
13
 
12
- import type { TimebackProfileState, TimebackVerificationState } from '../types'
13
14
  import type { Readable } from 'svelte/store'
15
+ import type { TimebackProfileState, TimebackVerificationState } from '../types'
14
16
 
15
17
  const profileStore = writable<TimebackProfileState>({ status: 'idle' })
16
18
  const canFetchStore = writable<boolean>(false)
17
19
  let profileInitialized = false
18
20
 
19
- /**
20
- * Check if we're running in the browser.
21
- *
22
- * @returns True if in browser
23
- */
24
- function isBrowser(): boolean {
25
- return typeof window !== 'undefined'
26
- }
27
-
28
21
  /**
29
22
  * Run the profile fetch.
30
23
  *
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * Svelte store for user verification state.
5
5
  */
6
- import type { TimebackVerificationState } from '../types';
7
6
  import type { Readable } from 'svelte/store';
7
+ import type { TimebackVerificationState } from '../types';
8
8
  export declare const verificationStore: import("svelte/store").Writable<TimebackVerificationState>;
9
9
  /**
10
10
  * Store containing the Timeback verification state.
@@ -1 +1 @@
1
- {"version":3,"file":"verification.d.ts","sourceRoot":"","sources":["../../../../../src/client/adapters/svelte/stores/verification.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAc5C,eAAO,MAAM,iBAAiB,4DAA6D,CAAA;AA8D3F;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,yBAAyB,CAAqB,CAAA;AAE1F;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAGlD"}
1
+ {"version":3,"file":"verification.d.ts","sourceRoot":"","sources":["../../../../../src/client/adapters/svelte/stores/verification.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAE5C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AAczD,eAAO,MAAM,iBAAiB,4DAA6D,CAAA;AAoI3F;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,yBAAyB,CAAqB,CAAA;AAE1F;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAGlD"}
@@ -4,13 +4,16 @@
4
4
  * Svelte store for user verification state.
5
5
  */
6
6
 
7
+ import { writable } from 'svelte/store'
8
+
9
+ import { DEFAULT_RETRY_ATTEMPTS, DEFAULT_RETRY_DELAYS_MS } from '../../../../shared/constants'
7
10
  import { getVerifyCache, verifyOnce } from '../../../lib/user-cache'
11
+ import { getRetryDelay, isBrowser, sleep } from '../../../lib/utils'
8
12
  import { clientStore, getClientInstance } from './client'
9
- import { writable } from 'svelte/store'
10
13
 
14
+ import type { Readable } from 'svelte/store'
11
15
  import type { TimebackVerifyResult } from '../../../../shared/types'
12
16
  import type { TimebackVerificationState } from '../types'
13
- import type { Readable } from 'svelte/store'
14
17
 
15
18
  /**
16
19
  * Convert a verify result into a store-friendly state machine.
@@ -28,16 +31,21 @@ export const verificationStore = writable<TimebackVerificationState>({ status: '
28
31
  let verificationInitialized = false
29
32
 
30
33
  /**
31
- * Check if we're running in the browser.
34
+ * Counter tracking the current verification run.
32
35
  *
33
- * @returns True if in browser
36
+ * Each call to `runVerification` increments this counter and captures
37
+ * its value. Before updating state, the run checks if it's still current.
38
+ * This prevents stale results from overwriting fresh ones when multiple
39
+ * verification attempts overlap (e.g., initial retry + user refresh).
34
40
  */
35
- function isBrowser(): boolean {
36
- return typeof window !== 'undefined'
37
- }
41
+ let currentRunId = 0
38
42
 
39
43
  /**
40
- * Run the verification check.
44
+ * Run the verification check with retry logic.
45
+ *
46
+ * Uses a run ID to prevent race conditions when multiple verification
47
+ * attempts overlap. Each run captures the current ID and only updates
48
+ * state if it's still the active run.
41
49
  *
42
50
  * @param force - If true, bypass cache and force a fresh verification
43
51
  */
@@ -45,23 +53,79 @@ async function runVerification(force: boolean): Promise<void> {
45
53
  const client = getClientInstance()
46
54
  if (!client) return
47
55
 
56
+ /**
57
+ * Capture this run's ID. Any subsequent call to runVerification
58
+ * will increment currentRunId, making this run "cancelled".
59
+ */
60
+ const runId = ++currentRunId
61
+
62
+ /**
63
+ * Check if this run is still the active one.
64
+ *
65
+ * @returns False if a newer verification run has started
66
+ */
67
+ const isCurrentRun = () => runId === currentRunId
68
+
48
69
  if (!force) {
49
70
  const cached = getVerifyCache(client)
50
71
  if (cached) {
51
- verificationStore.set(toVerificationState(cached))
72
+ if (isCurrentRun()) {
73
+ verificationStore.set(toVerificationState(cached))
74
+ }
52
75
  return
53
76
  }
54
77
  }
55
78
 
56
- verificationStore.set({ status: 'loading' })
79
+ if (isCurrentRun()) {
80
+ verificationStore.set({ status: 'loading' })
81
+ }
82
+
83
+ let lastError: Error | undefined
84
+
85
+ for (let attempt = 0; attempt <= DEFAULT_RETRY_ATTEMPTS; attempt++) {
86
+ if (!isCurrentRun()) return
87
+
88
+ /**
89
+ * Wait for a calculated delay before retrying the verification request.
90
+ *
91
+ * This occurs only on subsequent attempts (not the initial try),
92
+ * introducing a backoff based on the configured retry delays.
93
+ */
94
+ if (attempt > 0) {
95
+ const delay = getRetryDelay(DEFAULT_RETRY_DELAYS_MS, attempt - 1)
96
+ await sleep(delay)
97
+ if (!isCurrentRun()) return
98
+ }
99
+
100
+ try {
101
+ const result = await verifyOnce(client, force || attempt > 0)
102
+
103
+ if (!isCurrentRun()) return
104
+
105
+ verificationStore.set(toVerificationState(result))
106
+
107
+ return
108
+ } catch (err) {
109
+ lastError = err instanceof Error ? err : new Error('Failed to verify Timeback user')
110
+ /**
111
+ * An error occurred during this verification attempt.
112
+ * Proceeding to the next retry attempt if any remain.
113
+ *
114
+ * The last encountered error is saved for potential error state handling.
115
+ */
116
+ }
117
+ }
57
118
 
58
- try {
59
- const result = await verifyOnce(client, force)
60
- verificationStore.set(toVerificationState(result))
61
- } catch (err) {
119
+ /**
120
+ * All verification attempts have failed after exhausting
121
+ * the configured retry policy. The verification state will
122
+ * transition to "error" if this run is still current, and the last
123
+ * encountered error message (if available) will be presented to the consumer.
124
+ */
125
+ if (isCurrentRun() && lastError) {
62
126
  verificationStore.set({
63
127
  status: 'error',
64
- message: err instanceof Error ? err.message : 'Failed to verify Timeback user',
128
+ message: lastError.message,
65
129
  })
66
130
  }
67
131
  }
@@ -73,7 +137,17 @@ function initVerification(): void {
73
137
  if (verificationInitialized) return
74
138
  verificationInitialized = true
75
139
 
76
- // Subscribe to client changes to auto-verify
140
+ /**
141
+ * Subscribes to changes in the Timeback client store.
142
+ *
143
+ * Any time the client instance becomes available or changes,
144
+ * automatically triggers a verification attempt to ensure the
145
+ * user's Timeback verification status is kept up-to-date.
146
+ *
147
+ * This enables responsive status updates when authentication
148
+ * state changes or when the client instance is re-initialized,
149
+ * providing a seamless verification experience in Svelte apps.
150
+ */
77
151
  clientStore.subscribe(client => {
78
152
  if (client && isBrowser()) {
79
153
  void runVerification(false)
@@ -81,7 +155,6 @@ function initVerification(): void {
81
155
  })
82
156
  }
83
157
 
84
- // Auto-init in browser
85
158
  if (isBrowser()) {
86
159
  initVerification()
87
160
  }
@@ -3,33 +3,5 @@
3
3
  *
4
4
  * Types for Svelte-specific stores.
5
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
- };
6
+ export type { TimebackProfileState, TimebackVerificationState } from '../../../shared/types';
35
7
  //# sourceMappingURL=types.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/client/adapters/svelte/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,YAAY,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAA"}
@@ -14,8 +14,10 @@ import type { TimebackVerificationState, UseTimebackVerificationOptions } from '
14
14
  * The composable runs automatically once the Timeback client is available, and
15
15
  * provides a `refresh()` method to retry.
16
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
+ *
17
20
  * @param options - Composable options
18
- * @param options.enabled - If false, the composable does nothing and stays in loading state.
19
21
  * @returns Verification state and a refresh method
20
22
  *
21
23
  * @example
@@ -1 +1 @@
1
- {"version":3,"file":"useTimebackVerification.d.ts","sourceRoot":"","sources":["../../../../../src/client/adapters/vue/composables/useTimebackVerification.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAE9B,OAAO,KAAK,EAAE,yBAAyB,EAAE,8BAA8B,EAAE,MAAM,UAAU,CAAA;AAczF;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,uBAAuB,CACtC,OAAO,GAAE,8BAAmC,GAC1C;IACF,KAAK,EAAE,GAAG,CAAC,yBAAyB,CAAC,CAAA;IACrC,OAAO,EAAE,MAAM,IAAI,CAAA;CACnB,CA+DA"}
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"}
@@ -9,7 +9,9 @@
9
9
 
10
10
  import { ref, watch } from 'vue'
11
11
 
12
+ import { DEFAULT_RETRY_ATTEMPTS, DEFAULT_RETRY_DELAYS_MS } from '../../../../shared/constants'
12
13
  import { getVerifyCache, verifyOnce } from '../../../lib/user-cache'
14
+ import { getRetryDelay, sleep } from '../../../lib/utils'
13
15
  import { useTimeback } from '../provider'
14
16
 
15
17
  import type { Ref } from 'vue'
@@ -34,8 +36,10 @@ function toState(result: TimebackVerifyResult): TimebackVerificationState {
34
36
  * The composable runs automatically once the Timeback client is available, and
35
37
  * provides a `refresh()` method to retry.
36
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
+ *
37
42
  * @param options - Composable options
38
- * @param options.enabled - If false, the composable does nothing and stays in loading state.
39
43
  * @returns Verification state and a refresh method
40
44
  *
41
45
  * @example
@@ -57,16 +61,18 @@ function toState(result: TimebackVerifyResult): TimebackVerificationState {
57
61
  * </template>
58
62
  * ```
59
63
  */
60
- export function useTimebackVerification(
61
- options: UseTimebackVerificationOptions = {},
62
- ): {
64
+ export function useTimebackVerification(options: UseTimebackVerificationOptions = {}): {
63
65
  state: Ref<TimebackVerificationState>
64
66
  refresh: () => void
65
67
  } {
66
68
  const enabled = options.enabled ?? true
69
+ const retryAttempts = options.retryAttempts ?? DEFAULT_RETRY_ATTEMPTS
70
+ const retryDelays = options.retryDelays ?? DEFAULT_RETRY_DELAYS_MS
67
71
  const timebackRef = useTimeback()
68
72
 
69
- const state = ref<TimebackVerificationState>({ status: 'loading' }) as Ref<TimebackVerificationState>
73
+ const state = ref<TimebackVerificationState>({
74
+ status: 'loading',
75
+ }) as Ref<TimebackVerificationState>
70
76
  const refreshNonce = ref(0)
71
77
 
72
78
  let lastHandledRefreshNonce = 0
@@ -100,24 +106,59 @@ export function useTimebackVerification(
100
106
 
101
107
  state.value = { status: 'loading' }
102
108
 
103
- try {
104
- if (!timeback) return
109
+ if (!timeback) return
105
110
 
106
- if (force) {
107
- lastHandledRefreshNonce = nonce
108
- }
111
+ if (force) {
112
+ lastHandledRefreshNonce = nonce
113
+ }
109
114
 
110
- const result = await verifyOnce(timeback, force)
115
+ let lastError: Error | undefined
111
116
 
117
+ for (let attempt = 0; attempt <= retryAttempts; attempt++) {
112
118
  if (cancelled) return
113
119
 
114
- state.value = toState(result)
115
- } catch (err) {
116
- if (!cancelled) {
117
- state.value = {
118
- status: 'error',
119
- message: err instanceof Error ? err.message : 'Failed to verify Timeback user',
120
- }
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,
121
162
  }
122
163
  }
123
164
  },
@@ -1 +1 @@
1
- {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../../src/client/adapters/vue/provider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAGrD,OAAO,KAAK,EAAgB,QAAQ,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAqC7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,gBAAgB;IAG3B,wFAAwF;;;;;;;;IAAxF,wFAAwF;;;;;;;0FAoBxF,CAAA;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,WAAW,IAAI,UAAU,CAAC,cAAc,GAAG,SAAS,CAAC,CAQpE"}
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../../src/client/adapters/vue/provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAIrD,OAAO,KAAK,EAAgB,QAAQ,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AA2B7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,gBAAgB;IAG3B,wFAAwF;;;;;;;;IAAxF,wFAAwF;;;;;;;0FAoBxF,CAAA;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,WAAW,IAAI,UAAU,CAAC,cAAc,GAAG,SAAS,CAAC,CAQpE"}
@@ -1,6 +1,9 @@
1
- import { TimebackClient } from '@timeback/sdk/client'
2
1
  import { defineComponent, inject, onMounted, provide, shallowRef } from 'vue'
3
2
 
3
+ import { TimebackClient } from '@timeback/sdk/client'
4
+
5
+ import { isBrowser } from '../../lib/utils'
6
+
4
7
  import type { InjectionKey, PropType, ShallowRef } from 'vue'
5
8
 
6
9
  /**
@@ -14,16 +17,6 @@ import type { InjectionKey, PropType, ShallowRef } from 'vue'
14
17
  */
15
18
  const TimebackKey: InjectionKey<ShallowRef<TimebackClient | undefined>> = Symbol('timeback')
16
19
 
17
- /**
18
- * Check if we're running in the browser.
19
- *
20
- * @returns True if in browser
21
- */
22
- function isBrowser(): boolean {
23
- return typeof window !== 'undefined'
24
- }
25
-
26
- // Singleton client instance for browser
27
20
  let clientInstance: TimebackClient | undefined
28
21
 
29
22
  /**
@@ -3,35 +3,8 @@
3
3
  *
4
4
  * Types for Vue-specific composables.
5
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
- };
6
+ import type { TimebackProfileState, TimebackVerificationState } from '../../../shared/types';
7
+ export type { TimebackProfileState, TimebackVerificationState } from '../../../shared/types';
35
8
  /**
36
9
  * Options for the useTimebackVerification composable.
37
10
  */
@@ -39,9 +12,32 @@ export interface UseTimebackVerificationOptions {
39
12
  /**
40
13
  * If false, the composable does nothing and stays in loading state.
41
14
  *
15
+ * Use this to delay verification until prerequisites are met
16
+ * (e.g., user exists in your database).
17
+ *
42
18
  * @default true
43
19
  */
44
20
  enabled?: boolean;
21
+ /**
22
+ * Number of retry attempts on failure.
23
+ *
24
+ * This helps handle race conditions where the user may not be
25
+ * fully set up in the backend when verification first runs.
26
+ *
27
+ * Set to 0 to disable retries.
28
+ *
29
+ * @default 3
30
+ */
31
+ retryAttempts?: number;
32
+ /**
33
+ * Delay in ms before each retry attempt.
34
+ *
35
+ * Can be a single number (same delay for all retries) or an array
36
+ * of delays for each attempt (e.g., [100, 300, 1000] for exponential backoff).
37
+ *
38
+ * @default [100, 300, 1000]
39
+ */
40
+ retryDelays?: number | readonly number[];
45
41
  }
46
42
  /**
47
43
  * Return value of the useTimebackVerification composable.
@@ -1 +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;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CACjB;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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/client/adapters/vue/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAA;AAE5F,YAAY,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAA;AAE5F;;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"}
@@ -4,25 +4,9 @@
4
4
  * Types for Vue-specific composables.
5
5
  */
6
6
 
7
- import type { TimebackProfile } from '../../../shared/types'
7
+ import type { TimebackProfileState, TimebackVerificationState } from '../../../shared/types'
8
8
 
9
- /**
10
- * Verification state for the current user.
11
- */
12
- export type TimebackVerificationState =
13
- | { status: 'loading' }
14
- | { status: 'verified'; timebackId: string }
15
- | { status: 'unverified' }
16
- | { status: 'error'; message: string }
17
-
18
- /**
19
- * Profile state for the current user.
20
- */
21
- export type TimebackProfileState =
22
- | { status: 'idle' }
23
- | { status: 'loading' }
24
- | { status: 'loaded'; profile: TimebackProfile }
25
- | { status: 'error'; message: string }
9
+ export type { TimebackProfileState, TimebackVerificationState } from '../../../shared/types'
26
10
 
27
11
  /**
28
12
  * Options for the useTimebackVerification composable.
@@ -31,9 +15,34 @@ export interface UseTimebackVerificationOptions {
31
15
  /**
32
16
  * If false, the composable does nothing and stays in loading state.
33
17
  *
18
+ * Use this to delay verification until prerequisites are met
19
+ * (e.g., user exists in your database).
20
+ *
34
21
  * @default true
35
22
  */
36
23
  enabled?: boolean
24
+
25
+ /**
26
+ * Number of retry attempts on failure.
27
+ *
28
+ * This helps handle race conditions where the user may not be
29
+ * fully set up in the backend when verification first runs.
30
+ *
31
+ * Set to 0 to disable retries.
32
+ *
33
+ * @default 3
34
+ */
35
+ retryAttempts?: number
36
+
37
+ /**
38
+ * Delay in ms before each retry attempt.
39
+ *
40
+ * Can be a single number (same delay for all retries) or an array
41
+ * of delays for each attempt (e.g., [100, 300, 1000] for exponential backoff).
42
+ *
43
+ * @default [100, 300, 1000]
44
+ */
45
+ retryDelays?: number | readonly number[]
37
46
  }
38
47
 
39
48
  /**
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Client Auth Types
3
3
  *
4
- * Small, framework-agnostic extension point for customizing how the SDK makes HTTP requests.
4
+ * Extension point for customizing how the SDK makes HTTP requests.
5
5
  */
6
6
  export type TimebackFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
7
7
  /**
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Client Module
3
3
  *
4
- * Framework-agnostic client exports.
4
+ * Client exports.
5
5
  */
6
6
  export { createClient } from './timeback-client';
7
7
  export { TimebackClient } from './timeback-client.class';