@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.
- package/README.md +97 -8
- package/dist/chunk-07j8zre9.js +2 -0
- package/dist/chunk-5171mkp2.js +2 -0
- package/dist/chunk-63afdp3y.js +8 -0
- package/dist/chunk-8gg8n8v9.js +2 -0
- package/dist/chunk-9se82640.js +1 -0
- package/dist/chunk-agpf1x3g.js +16 -0
- package/dist/chunk-hnf0tart.js +2 -0
- package/dist/chunk-qr0bbnsr.js +1 -0
- package/dist/chunk-whc53e0y.js +11 -0
- package/dist/chunk-x9gvef7q.js +1 -0
- package/dist/client/adapters/react/hooks/types.d.ts +80 -0
- package/dist/client/adapters/react/hooks/types.d.ts.map +1 -1
- package/dist/client/adapters/react/hooks/useTimebackProfile.d.ts +42 -0
- package/dist/client/adapters/react/hooks/useTimebackProfile.d.ts.map +1 -0
- package/dist/client/adapters/react/hooks/useTimebackVerification.d.ts +17 -5
- package/dist/client/adapters/react/hooks/useTimebackVerification.d.ts.map +1 -1
- package/dist/client/adapters/react/index.d.ts +2 -1
- package/dist/client/adapters/react/index.d.ts.map +1 -1
- package/dist/client/adapters/react/index.js +2 -494
- package/dist/client/adapters/react/provider.d.ts.map +1 -1
- package/dist/client/adapters/solid/index.d.ts +3 -0
- package/dist/client/adapters/solid/index.d.ts.map +1 -1
- package/dist/client/adapters/solid/index.ts +12 -0
- package/dist/client/adapters/solid/primitives/createTimebackProfile.d.ts +58 -0
- package/dist/client/adapters/solid/primitives/createTimebackProfile.d.ts.map +1 -0
- package/dist/client/adapters/solid/primitives/createTimebackProfile.ts +209 -0
- package/dist/client/adapters/solid/primitives/createTimebackVerification.d.ts +38 -0
- package/dist/client/adapters/solid/primitives/createTimebackVerification.d.ts.map +1 -0
- package/dist/client/adapters/solid/primitives/createTimebackVerification.ts +173 -0
- package/dist/client/adapters/solid/types.d.ts +109 -0
- package/dist/client/adapters/solid/types.d.ts.map +1 -0
- package/dist/client/adapters/solid/types.ts +110 -0
- package/dist/client/adapters/svelte/index.d.ts +2 -1
- package/dist/client/adapters/svelte/index.d.ts.map +1 -1
- package/dist/client/adapters/svelte/index.ts +11 -2
- package/dist/client/adapters/svelte/{stores.d.ts → stores/client.d.ts} +11 -9
- package/dist/client/adapters/svelte/stores/client.d.ts.map +1 -0
- package/dist/client/adapters/svelte/{stores.ts → stores/client.ts} +24 -52
- package/dist/client/adapters/svelte/stores/index.d.ts +10 -0
- package/dist/client/adapters/svelte/stores/index.d.ts.map +1 -0
- package/dist/client/adapters/svelte/stores/index.ts +22 -0
- package/dist/client/adapters/svelte/stores/profile.d.ts +66 -0
- package/dist/client/adapters/svelte/stores/profile.d.ts.map +1 -0
- package/dist/client/adapters/svelte/stores/profile.ts +168 -0
- package/dist/client/adapters/svelte/stores/verification.d.ts +43 -0
- package/dist/client/adapters/svelte/stores/verification.d.ts.map +1 -0
- package/dist/client/adapters/svelte/stores/verification.ts +208 -0
- package/dist/client/adapters/svelte/types.d.ts +35 -0
- package/dist/client/adapters/svelte/types.d.ts.map +1 -0
- package/dist/client/adapters/vue/composables/useTimebackProfile.d.ts +51 -0
- package/dist/client/adapters/vue/composables/useTimebackProfile.d.ts.map +1 -0
- package/dist/client/adapters/vue/composables/useTimebackProfile.ts +186 -0
- package/dist/client/adapters/vue/composables/useTimebackVerification.d.ts +46 -0
- package/dist/client/adapters/vue/composables/useTimebackVerification.d.ts.map +1 -0
- package/dist/client/adapters/vue/composables/useTimebackVerification.ts +169 -0
- package/dist/client/adapters/vue/index.d.ts +3 -0
- package/dist/client/adapters/vue/index.d.ts.map +1 -1
- package/dist/client/adapters/vue/index.ts +12 -1
- package/dist/client/adapters/vue/types.d.ts +109 -0
- package/dist/client/adapters/vue/types.d.ts.map +1 -0
- package/dist/client/adapters/vue/types.ts +110 -0
- package/dist/client/lib/activity/activity.class.d.ts +5 -5
- package/dist/client/lib/activity/activity.class.d.ts.map +1 -1
- package/dist/client/lib/user-cache.d.ts +39 -0
- package/dist/client/lib/user-cache.d.ts.map +1 -0
- package/dist/client/lib/user-cache.ts +168 -0
- package/dist/client/lib/utils.d.ts +15 -0
- package/dist/client/lib/utils.d.ts.map +1 -1
- package/dist/client/namespaces/activity.d.ts +2 -3
- package/dist/client/namespaces/activity.d.ts.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -257
- package/dist/edge.js +1 -86271
- package/dist/identity.js +1 -86131
- package/dist/index.d.ts +2 -2
- package/dist/index.js +22 -104883
- package/dist/server/adapters/express.js +1 -85973
- package/dist/server/adapters/native.js +2 -221
- package/dist/server/adapters/nextjs.js +1 -233
- package/dist/server/adapters/nuxt.js +1 -86046
- package/dist/server/adapters/solid-start.js +1 -85945
- package/dist/server/adapters/svelte-kit.js +1 -279
- package/dist/server/adapters/tanstack-start.js +1 -85918
- package/dist/server/handlers/activity/attempts.d.ts +51 -0
- package/dist/server/handlers/activity/attempts.d.ts.map +1 -0
- package/dist/server/handlers/activity/caliper.d.ts +46 -5
- package/dist/server/handlers/activity/caliper.d.ts.map +1 -1
- package/dist/server/handlers/activity/completion.d.ts +43 -0
- package/dist/server/handlers/activity/completion.d.ts.map +1 -0
- package/dist/server/handlers/activity/handler.d.ts +18 -1
- package/dist/server/handlers/activity/handler.d.ts.map +1 -1
- package/dist/server/handlers/activity/progress.d.ts +47 -0
- package/dist/server/handlers/activity/progress.d.ts.map +1 -0
- package/dist/server/handlers/activity/schema.d.ts +1 -2
- package/dist/server/handlers/activity/schema.d.ts.map +1 -1
- package/dist/server/handlers/activity/types.d.ts +1 -2
- package/dist/server/handlers/activity/types.d.ts.map +1 -1
- package/dist/server/lib/index.d.ts +1 -1
- package/dist/server/lib/index.d.ts.map +1 -1
- package/dist/server/lib/utils.d.ts +61 -0
- package/dist/server/lib/utils.d.ts.map +1 -1
- package/dist/server/timeback.d.ts +2 -2
- package/dist/server/timeback.d.ts.map +1 -1
- package/dist/server/types.d.ts +7 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/shared/constants.d.ts +19 -0
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/types.d.ts +62 -8
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/xp-calculator.d.ts +25 -0
- package/dist/shared/xp-calculator.d.ts.map +1 -0
- package/package.json +6 -4
- package/dist/client/adapters/svelte/stores.d.ts.map +0 -1
- package/dist/server/handlers/activity/gradebook.d.ts +0 -56
- package/dist/server/handlers/activity/gradebook.d.ts.map +0 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createTimebackProfile
|
|
3
|
+
*
|
|
4
|
+
* Solid primitive for fetching the current user's Timeback profile.
|
|
5
|
+
*
|
|
6
|
+
* Composes `createTimebackVerification` internally and only fetches
|
|
7
|
+
* the profile when the user is verified (i.e., exists in Timeback).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createEffect, createMemo, createSignal, onCleanup } from 'solid-js'
|
|
11
|
+
|
|
12
|
+
import { fetchProfileOnce, getProfileCache } from '../../../lib/user-cache'
|
|
13
|
+
import { useTimeback } from '../context'
|
|
14
|
+
import { createTimebackVerification } from './createTimebackVerification'
|
|
15
|
+
|
|
16
|
+
import type {
|
|
17
|
+
CreateTimebackProfileOptions,
|
|
18
|
+
CreateTimebackProfileResult,
|
|
19
|
+
TimebackProfileState,
|
|
20
|
+
} from '../types'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Fetch the current user's Timeback profile.
|
|
24
|
+
*
|
|
25
|
+
* Internally uses `createTimebackVerification` to ensure the user
|
|
26
|
+
* is verified before fetching. When `auto: true`, it automatically fetches
|
|
27
|
+
* the profile once verified. Otherwise, call `fetchProfile()` manually.
|
|
28
|
+
*
|
|
29
|
+
* @param options - Options
|
|
30
|
+
* @param options.enabled - If false, does nothing and stays in idle state. Defaults to true.
|
|
31
|
+
* @param options.auto - If true, automatically fetch once verified. Defaults to false.
|
|
32
|
+
* @returns Profile state and fetch methods
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```tsx
|
|
36
|
+
* // Manual fetch
|
|
37
|
+
* import { createTimebackProfile } from '@timeback/sdk/solid'
|
|
38
|
+
* import { Show } from 'solid-js'
|
|
39
|
+
*
|
|
40
|
+
* function ProfileButton() {
|
|
41
|
+
* const { state, canFetch, fetchProfile } = createTimebackProfile()
|
|
42
|
+
*
|
|
43
|
+
* return (
|
|
44
|
+
* <Show
|
|
45
|
+
* when={state.status === 'loaded'}
|
|
46
|
+
* fallback={
|
|
47
|
+
* <button onClick={fetchProfile} disabled={!canFetch}>
|
|
48
|
+
* {state.status === 'loading' ? 'Loading...' : 'Load Profile'}
|
|
49
|
+
* </button>
|
|
50
|
+
* }
|
|
51
|
+
* >
|
|
52
|
+
* <div>XP: {state.profile.xp}</div>
|
|
53
|
+
* </Show>
|
|
54
|
+
* )
|
|
55
|
+
* }
|
|
56
|
+
*
|
|
57
|
+
* // Auto-fetch
|
|
58
|
+
* function AutoProfile() {
|
|
59
|
+
* const { state } = createTimebackProfile({ auto: true })
|
|
60
|
+
*
|
|
61
|
+
* return (
|
|
62
|
+
* <Show when={state.status === 'loading'}>
|
|
63
|
+
* <div>Loading...</div>
|
|
64
|
+
* </Show>
|
|
65
|
+
* )
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export function createTimebackProfile(
|
|
70
|
+
options: CreateTimebackProfileOptions = {},
|
|
71
|
+
): CreateTimebackProfileResult {
|
|
72
|
+
const { enabled = true, auto = false } = options
|
|
73
|
+
|
|
74
|
+
const timeback = useTimeback()
|
|
75
|
+
const verification = createTimebackVerification({ enabled })
|
|
76
|
+
|
|
77
|
+
const [state, setState] = createSignal<TimebackProfileState>({ status: 'idle' })
|
|
78
|
+
const [fetchNonce, setFetchNonce] = createSignal(0)
|
|
79
|
+
const [refreshNonce, setRefreshNonce] = createSignal(0)
|
|
80
|
+
|
|
81
|
+
let lastHandledFetchNonce = 0
|
|
82
|
+
let lastHandledRefreshNonce = 0
|
|
83
|
+
let autoTriggered = false
|
|
84
|
+
|
|
85
|
+
const canFetch = createMemo(
|
|
86
|
+
() => enabled && verification.state.status === 'verified' && !!timeback,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const fetchProfile = () => {
|
|
90
|
+
if (!canFetch()) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
setFetchNonce(n => n + 1)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const refresh = () => {
|
|
98
|
+
if (!canFetch()) {
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
setRefreshNonce(n => n + 1)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
createEffect(() => {
|
|
106
|
+
const verificationState = verification.state
|
|
107
|
+
const fetchN = fetchNonce()
|
|
108
|
+
const refreshN = refreshNonce()
|
|
109
|
+
|
|
110
|
+
if (!enabled) {
|
|
111
|
+
setState({ status: 'idle' })
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (verificationState.status !== 'verified') {
|
|
116
|
+
setState(current => (current.status === 'idle' ? current : { status: 'idle' }))
|
|
117
|
+
autoTriggered = false
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (auto && !autoTriggered && fetchN === 0 && refreshN === 0) {
|
|
122
|
+
autoTriggered = true
|
|
123
|
+
setFetchNonce(1)
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (fetchN === 0 && refreshN === 0) {
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
let cancelled = false
|
|
132
|
+
const isNewFetch = fetchN > lastHandledFetchNonce
|
|
133
|
+
const isNewRefresh = refreshN > lastHandledRefreshNonce
|
|
134
|
+
const force = isNewRefresh
|
|
135
|
+
|
|
136
|
+
if (!isNewFetch && !isNewRefresh) {
|
|
137
|
+
if (timeback) {
|
|
138
|
+
const cached = getProfileCache(timeback)
|
|
139
|
+
|
|
140
|
+
if (cached) {
|
|
141
|
+
setState({ status: 'loaded', profile: cached })
|
|
142
|
+
onCleanup(() => {
|
|
143
|
+
cancelled = true
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (timeback && !force) {
|
|
153
|
+
const cached = getProfileCache(timeback)
|
|
154
|
+
|
|
155
|
+
if (cached) {
|
|
156
|
+
setState({ status: 'loaded', profile: cached })
|
|
157
|
+
lastHandledFetchNonce = fetchN
|
|
158
|
+
onCleanup(() => {
|
|
159
|
+
cancelled = true
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
setState({ status: 'loading' })
|
|
167
|
+
|
|
168
|
+
void (async () => {
|
|
169
|
+
try {
|
|
170
|
+
if (!timeback) {
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
lastHandledFetchNonce = fetchN
|
|
175
|
+
lastHandledRefreshNonce = refreshN
|
|
176
|
+
|
|
177
|
+
const profile = await fetchProfileOnce(timeback, force)
|
|
178
|
+
|
|
179
|
+
if (cancelled) {
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
setState({ status: 'loaded', profile })
|
|
184
|
+
} catch (err) {
|
|
185
|
+
if (!cancelled) {
|
|
186
|
+
setState({
|
|
187
|
+
status: 'error',
|
|
188
|
+
message: err instanceof Error ? err.message : 'Failed to fetch profile',
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
})()
|
|
193
|
+
|
|
194
|
+
onCleanup(() => {
|
|
195
|
+
cancelled = true
|
|
196
|
+
})
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
return {
|
|
200
|
+
get state() {
|
|
201
|
+
return state()
|
|
202
|
+
},
|
|
203
|
+
get canFetch() {
|
|
204
|
+
return canFetch()
|
|
205
|
+
},
|
|
206
|
+
fetchProfile,
|
|
207
|
+
refresh,
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createTimebackVerification
|
|
3
|
+
*
|
|
4
|
+
* Solid primitive 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 { CreateTimebackVerificationOptions, CreateTimebackVerificationResult } from '../types';
|
|
10
|
+
/**
|
|
11
|
+
* Verify the current user against Timeback.
|
|
12
|
+
*
|
|
13
|
+
* Runs automatically once the Timeback client is available, and
|
|
14
|
+
* provides a `refresh()` method to retry.
|
|
15
|
+
*
|
|
16
|
+
* By default, retries failed verification attempts with exponential
|
|
17
|
+
* backoff to handle race conditions (e.g., user not yet created in backend).
|
|
18
|
+
*
|
|
19
|
+
* @param options - Options
|
|
20
|
+
* @returns Verification state and a refresh method
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```tsx
|
|
24
|
+
* import { createTimebackVerification } from '@timeback/sdk/solid'
|
|
25
|
+
*
|
|
26
|
+
* function VerificationGate() {
|
|
27
|
+
* const { state, refresh } = createTimebackVerification()
|
|
28
|
+
*
|
|
29
|
+
* return (
|
|
30
|
+
* <Show when={state().status === 'verified'}>
|
|
31
|
+
* <p>Verified as {state().timebackId}</p>
|
|
32
|
+
* </Show>
|
|
33
|
+
* )
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function createTimebackVerification(options?: CreateTimebackVerificationOptions): CreateTimebackVerificationResult;
|
|
38
|
+
//# sourceMappingURL=createTimebackVerification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createTimebackVerification.d.ts","sourceRoot":"","sources":["../../../../../src/client/adapters/solid/primitives/createTimebackVerification.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,OAAO,KAAK,EACX,iCAAiC,EACjC,gCAAgC,EAEhC,MAAM,UAAU,CAAA;AAcjB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,0BAA0B,CACzC,OAAO,GAAE,iCAAsC,GAC7C,gCAAgC,CA4GlC"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createTimebackVerification
|
|
3
|
+
*
|
|
4
|
+
* Solid primitive 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 { createEffect, createSignal, onCleanup } from 'solid-js'
|
|
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 '../context'
|
|
16
|
+
|
|
17
|
+
import type { TimebackVerifyResult } from '../../../../shared/types'
|
|
18
|
+
import type {
|
|
19
|
+
CreateTimebackVerificationOptions,
|
|
20
|
+
CreateTimebackVerificationResult,
|
|
21
|
+
TimebackVerificationState,
|
|
22
|
+
} from '../types'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Convert a verify result into a hook-friendly state machine.
|
|
26
|
+
*
|
|
27
|
+
* @param result - Raw verify result from the SDK client
|
|
28
|
+
* @returns Mapped verification state
|
|
29
|
+
*/
|
|
30
|
+
function toState(result: TimebackVerifyResult): TimebackVerificationState {
|
|
31
|
+
return result.verified
|
|
32
|
+
? { status: 'verified', timebackId: result.timebackId }
|
|
33
|
+
: { status: 'unverified' }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Verify the current user against Timeback.
|
|
38
|
+
*
|
|
39
|
+
* Runs automatically once the Timeback client is available, and
|
|
40
|
+
* provides a `refresh()` method to retry.
|
|
41
|
+
*
|
|
42
|
+
* By default, retries failed verification attempts with exponential
|
|
43
|
+
* backoff to handle race conditions (e.g., user not yet created in backend).
|
|
44
|
+
*
|
|
45
|
+
* @param options - Options
|
|
46
|
+
* @returns Verification state and a refresh method
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```tsx
|
|
50
|
+
* import { createTimebackVerification } from '@timeback/sdk/solid'
|
|
51
|
+
*
|
|
52
|
+
* function VerificationGate() {
|
|
53
|
+
* const { state, refresh } = createTimebackVerification()
|
|
54
|
+
*
|
|
55
|
+
* return (
|
|
56
|
+
* <Show when={state().status === 'verified'}>
|
|
57
|
+
* <p>Verified as {state().timebackId}</p>
|
|
58
|
+
* </Show>
|
|
59
|
+
* )
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export function createTimebackVerification(
|
|
64
|
+
options: CreateTimebackVerificationOptions = {},
|
|
65
|
+
): CreateTimebackVerificationResult {
|
|
66
|
+
const enabled = options.enabled ?? true
|
|
67
|
+
const retryAttempts = options.retryAttempts ?? DEFAULT_RETRY_ATTEMPTS
|
|
68
|
+
const retryDelays = options.retryDelays ?? DEFAULT_RETRY_DELAYS_MS
|
|
69
|
+
const timeback = useTimeback()
|
|
70
|
+
|
|
71
|
+
const [state, setState] = createSignal<TimebackVerificationState>({ status: 'loading' })
|
|
72
|
+
const [refreshNonce, setRefreshNonce] = createSignal(0)
|
|
73
|
+
|
|
74
|
+
const refresh = () => {
|
|
75
|
+
if (!enabled) return
|
|
76
|
+
setRefreshNonce(n => n + 1)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let lastHandledRefreshNonce = 0
|
|
80
|
+
|
|
81
|
+
createEffect(() => {
|
|
82
|
+
if (!enabled) return
|
|
83
|
+
|
|
84
|
+
const nonce = refreshNonce()
|
|
85
|
+
const client = timeback
|
|
86
|
+
|
|
87
|
+
let cancelled = false
|
|
88
|
+
const force = nonce > lastHandledRefreshNonce
|
|
89
|
+
|
|
90
|
+
if (client && !force) {
|
|
91
|
+
const cached = getVerifyCache(client)
|
|
92
|
+
|
|
93
|
+
if (cached) {
|
|
94
|
+
setState(toState(cached))
|
|
95
|
+
onCleanup(() => {
|
|
96
|
+
cancelled = true
|
|
97
|
+
})
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
setState({ status: 'loading' })
|
|
103
|
+
|
|
104
|
+
void (async () => {
|
|
105
|
+
if (!client) return
|
|
106
|
+
|
|
107
|
+
if (force) {
|
|
108
|
+
lastHandledRefreshNonce = nonce
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let lastError: Error | undefined
|
|
112
|
+
|
|
113
|
+
for (let attempt = 0; attempt <= retryAttempts; attempt++) {
|
|
114
|
+
if (cancelled) return
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Wait for a calculated delay before retrying the verification request.
|
|
118
|
+
*
|
|
119
|
+
* This occurs only on subsequent attempts (not the initial try),
|
|
120
|
+
* introducing a backoff based on the configured retry delays.
|
|
121
|
+
*/
|
|
122
|
+
if (attempt > 0) {
|
|
123
|
+
const delay = getRetryDelay(retryDelays, attempt - 1)
|
|
124
|
+
await sleep(delay)
|
|
125
|
+
if (cancelled) return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const result = await verifyOnce(client, force || attempt > 0)
|
|
130
|
+
|
|
131
|
+
if (cancelled) return
|
|
132
|
+
|
|
133
|
+
setState(toState(result))
|
|
134
|
+
|
|
135
|
+
return
|
|
136
|
+
} catch (err) {
|
|
137
|
+
lastError =
|
|
138
|
+
err instanceof Error ? err : new Error('Failed to verify Timeback user')
|
|
139
|
+
/**
|
|
140
|
+
* An error occurred during this verification attempt.
|
|
141
|
+
* Proceeding to the next retry attempt if any remain.
|
|
142
|
+
*
|
|
143
|
+
* The last encountered error is saved for potential error state handling.
|
|
144
|
+
*/
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* All verification attempts have failed after exhausting
|
|
150
|
+
* the configured retry policy. The verification state will
|
|
151
|
+
* transition to "error" if not cancelled, and the last encountered
|
|
152
|
+
* error message (if available) will be presented to the consumer.
|
|
153
|
+
*/
|
|
154
|
+
if (!cancelled && lastError) {
|
|
155
|
+
setState({
|
|
156
|
+
status: 'error',
|
|
157
|
+
message: lastError.message,
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
})()
|
|
161
|
+
|
|
162
|
+
onCleanup(() => {
|
|
163
|
+
cancelled = true
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
get state() {
|
|
169
|
+
return state()
|
|
170
|
+
},
|
|
171
|
+
refresh,
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solid Types
|
|
3
|
+
*
|
|
4
|
+
* Types for Solid-specific primitives.
|
|
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 createTimebackVerification.
|
|
37
|
+
*/
|
|
38
|
+
export interface CreateTimebackVerificationOptions {
|
|
39
|
+
/**
|
|
40
|
+
* If false, 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 createTimebackVerification.
|
|
71
|
+
*/
|
|
72
|
+
export interface CreateTimebackVerificationResult {
|
|
73
|
+
/** Current verification state */
|
|
74
|
+
state: TimebackVerificationState;
|
|
75
|
+
/** Force a re-verification. No-op if not enabled. */
|
|
76
|
+
refresh: () => void;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Options for createTimebackProfile.
|
|
80
|
+
*/
|
|
81
|
+
export interface CreateTimebackProfileOptions {
|
|
82
|
+
/**
|
|
83
|
+
* If false, 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 createTimebackProfile.
|
|
98
|
+
*/
|
|
99
|
+
export interface CreateTimebackProfileResult {
|
|
100
|
+
/** Current profile state */
|
|
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/solid/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,iCAAiC;IACjD;;;;;;;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,gCAAgC;IAChD,iCAAiC;IACjC,KAAK,EAAE,yBAAyB,CAAA;IAEhC,qDAAqD;IACrD,OAAO,EAAE,MAAM,IAAI,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;IAEjB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;CACd;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC3C,4BAA4B;IAC5B,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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solid Types
|
|
3
|
+
*
|
|
4
|
+
* Types for Solid-specific primitives.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { TimebackProfile } from '../../../shared/types'
|
|
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 }
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Options for createTimebackVerification.
|
|
29
|
+
*/
|
|
30
|
+
export interface CreateTimebackVerificationOptions {
|
|
31
|
+
/**
|
|
32
|
+
* If false, does nothing and stays in loading state.
|
|
33
|
+
*
|
|
34
|
+
* Use this to delay verification until prerequisites are met
|
|
35
|
+
* (e.g., user exists in your database).
|
|
36
|
+
*
|
|
37
|
+
* @default true
|
|
38
|
+
*/
|
|
39
|
+
enabled?: boolean
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Number of retry attempts on failure.
|
|
43
|
+
*
|
|
44
|
+
* This helps handle race conditions where the user may not be
|
|
45
|
+
* fully set up in the backend when verification first runs.
|
|
46
|
+
*
|
|
47
|
+
* Set to 0 to disable retries.
|
|
48
|
+
*
|
|
49
|
+
* @default 3
|
|
50
|
+
*/
|
|
51
|
+
retryAttempts?: number
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Delay in ms before each retry attempt.
|
|
55
|
+
*
|
|
56
|
+
* Can be a single number (same delay for all retries) or an array
|
|
57
|
+
* of delays for each attempt (e.g., [100, 300, 1000] for exponential backoff).
|
|
58
|
+
*
|
|
59
|
+
* @default [100, 300, 1000]
|
|
60
|
+
*/
|
|
61
|
+
retryDelays?: number | readonly number[]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Return value of createTimebackVerification.
|
|
66
|
+
*/
|
|
67
|
+
export interface CreateTimebackVerificationResult {
|
|
68
|
+
/** Current verification state */
|
|
69
|
+
state: TimebackVerificationState
|
|
70
|
+
|
|
71
|
+
/** Force a re-verification. No-op if not enabled. */
|
|
72
|
+
refresh: () => void
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Options for createTimebackProfile.
|
|
77
|
+
*/
|
|
78
|
+
export interface CreateTimebackProfileOptions {
|
|
79
|
+
/**
|
|
80
|
+
* If false, does nothing and stays in idle state.
|
|
81
|
+
*
|
|
82
|
+
* @default true
|
|
83
|
+
*/
|
|
84
|
+
enabled?: boolean
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* If true, automatically fetch the profile once the user is verified.
|
|
88
|
+
* If false, you must call `fetchProfile()` manually.
|
|
89
|
+
*
|
|
90
|
+
* @default false
|
|
91
|
+
*/
|
|
92
|
+
auto?: boolean
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Return value of createTimebackProfile.
|
|
97
|
+
*/
|
|
98
|
+
export interface CreateTimebackProfileResult {
|
|
99
|
+
/** Current profile state */
|
|
100
|
+
state: TimebackProfileState
|
|
101
|
+
|
|
102
|
+
/** Whether the profile can be fetched (user is verified and client is ready) */
|
|
103
|
+
canFetch: boolean
|
|
104
|
+
|
|
105
|
+
/** Manually trigger a profile fetch. No-op if canFetch is false. */
|
|
106
|
+
fetchProfile: () => void
|
|
107
|
+
|
|
108
|
+
/** Force a re-fetch even if already loaded. No-op if canFetch is false. */
|
|
109
|
+
refresh: () => void
|
|
110
|
+
}
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
*/
|
|
31
31
|
export { Activity, createClient, TimebackClient } from '@timeback/sdk/client';
|
|
32
32
|
export type { ActivityMetrics, ActivityParams, TimebackIdentity } from '@timeback/sdk/client';
|
|
33
|
-
export { initTimeback, timeback } from './stores';
|
|
33
|
+
export { fetchTimebackProfile, initTimeback, refreshTimebackProfile, refreshTimebackVerification, timeback, timebackProfile, timebackProfileCanFetch, timebackVerification, } from './stores';
|
|
34
|
+
export type { TimebackProfileState, TimebackVerificationState } from './stores';
|
|
34
35
|
export { default as SignInButton } from './SignInButton.svelte';
|
|
35
36
|
export type { SignInButtonProps } from './SignInButton.svelte';
|
|
36
37
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/client/adapters/svelte/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/client/adapters/svelte/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;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,EACN,oBAAoB,EACpB,YAAY,EACZ,sBAAsB,EACtB,2BAA2B,EAC3B,QAAQ,EACR,eAAe,EACf,uBAAuB,EACvB,oBAAoB,GACpB,MAAM,UAAU,CAAA;AACjB,YAAY,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AAG/E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAC/D,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA"}
|
|
@@ -29,12 +29,21 @@
|
|
|
29
29
|
* ```
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
-
// Re-export from timeback/client for convenience
|
|
33
32
|
export { Activity, createClient, TimebackClient } from '@timeback/sdk/client'
|
|
34
33
|
export type { ActivityMetrics, ActivityParams, TimebackIdentity } from '@timeback/sdk/client'
|
|
35
34
|
|
|
36
35
|
// Svelte-specific
|
|
37
|
-
export {
|
|
36
|
+
export {
|
|
37
|
+
fetchTimebackProfile,
|
|
38
|
+
initTimeback,
|
|
39
|
+
refreshTimebackProfile,
|
|
40
|
+
refreshTimebackVerification,
|
|
41
|
+
timeback,
|
|
42
|
+
timebackProfile,
|
|
43
|
+
timebackProfileCanFetch,
|
|
44
|
+
timebackVerification,
|
|
45
|
+
} from './stores'
|
|
46
|
+
export type { TimebackProfileState, TimebackVerificationState } from './stores'
|
|
38
47
|
|
|
39
48
|
// Components
|
|
40
49
|
export { default as SignInButton } from './SignInButton.svelte'
|