@timeback/sdk 0.1.6 → 0.1.8
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 +91 -2
- package/dist/chunk-07j8zre9.js +2 -0
- package/dist/chunk-3886xy48.js +8 -0
- package/dist/chunk-9se82640.js +1 -0
- package/dist/chunk-ahy54f2r.js +2 -0
- package/dist/chunk-ewsp6v3b.js +16 -0
- package/dist/chunk-j1xdrfqj.js +2 -0
- package/dist/chunk-qaa129bd.js +2 -0
- package/dist/chunk-qr0bbnsr.js +1 -0
- package/dist/chunk-rgbpvxbv.js +1 -0
- package/dist/chunk-whc53e0y.js +11 -0
- package/dist/client/adapters/react/hooks/types.d.ts +61 -0
- package/dist/client/adapters/react/hooks/types.d.ts.map +1 -0
- 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 +18 -0
- package/dist/client/adapters/react/hooks/useTimebackVerification.d.ts.map +1 -0
- package/dist/client/adapters/react/index.d.ts +3 -0
- package/dist/client/adapters/react/index.d.ts.map +1 -1
- package/dist/client/adapters/react/index.js +2 -364
- 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 +36 -0
- package/dist/client/adapters/solid/primitives/createTimebackVerification.d.ts.map +1 -0
- package/dist/client/adapters/solid/primitives/createTimebackVerification.ts +133 -0
- package/dist/client/adapters/solid/types.d.ts +86 -0
- package/dist/client/adapters/solid/types.d.ts.map +1 -0
- package/dist/client/adapters/solid/types.ts +85 -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 +126 -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 +44 -0
- package/dist/client/adapters/vue/composables/useTimebackVerification.d.ts.map +1 -0
- package/dist/client/adapters/vue/composables/useTimebackVerification.ts +128 -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 +86 -0
- package/dist/client/adapters/vue/types.d.ts.map +1 -0
- package/dist/client/adapters/vue/types.ts +85 -0
- package/dist/client/auth/bearer.d.ts +17 -0
- package/dist/client/auth/bearer.d.ts.map +1 -0
- package/dist/client/auth/index.d.ts +3 -0
- package/dist/client/auth/index.d.ts.map +1 -0
- package/dist/client/auth/types.d.ts +39 -0
- package/dist/client/auth/types.d.ts.map +1 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -1
- 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/fetch.d.ts +19 -0
- package/dist/client/lib/fetch.d.ts.map +1 -0
- 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/namespaces/activity.d.ts +2 -3
- package/dist/client/namespaces/activity.d.ts.map +1 -1
- package/dist/client/namespaces/user.d.ts +25 -2
- package/dist/client/namespaces/user.d.ts.map +1 -1
- package/dist/client/timeback-client.class.d.ts +15 -0
- package/dist/client/timeback-client.class.d.ts.map +1 -1
- package/dist/client/timeback-client.d.ts +3 -0
- package/dist/client/timeback-client.d.ts.map +1 -1
- package/dist/client.d.ts +3 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1 -194
- package/dist/edge.js +1 -1149
- package/dist/identity.js +1 -1019
- package/dist/index.js +22 -104603
- package/dist/server/adapters/express.d.ts.map +1 -1
- package/dist/server/adapters/express.js +1 -85997
- package/dist/server/adapters/native.d.ts.map +1 -1
- package/dist/server/adapters/native.js +2 -190
- package/dist/server/adapters/nextjs.js +1 -202
- package/dist/server/adapters/nuxt.d.ts.map +1 -1
- package/dist/server/adapters/nuxt.js +1 -86066
- package/dist/server/adapters/solid-start.d.ts.map +1 -1
- package/dist/server/adapters/solid-start.js +1 -85965
- package/dist/server/adapters/svelte-kit.d.ts.map +1 -1
- package/dist/server/adapters/svelte-kit.js +1 -243
- package/dist/server/adapters/tanstack-start.d.ts.map +1 -1
- package/dist/server/adapters/tanstack-start.js +1 -85943
- package/dist/server/adapters/utils.d.ts +1 -1
- package/dist/server/adapters/utils.d.ts.map +1 -1
- 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 +133 -0
- package/dist/server/handlers/activity/caliper.d.ts.map +1 -0
- 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 +32 -0
- package/dist/server/handlers/activity/handler.d.ts.map +1 -0
- package/dist/server/handlers/activity/index.d.ts +9 -0
- package/dist/server/handlers/activity/index.d.ts.map +1 -0
- 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/resolve.d.ts +39 -0
- package/dist/server/handlers/activity/resolve.d.ts.map +1 -0
- package/dist/server/handlers/activity/schema.d.ts +51 -0
- package/dist/server/handlers/activity/schema.d.ts.map +1 -0
- package/dist/server/handlers/activity/types.d.ts +51 -0
- package/dist/server/handlers/activity/types.d.ts.map +1 -0
- package/dist/server/handlers/identity/handler.d.ts +14 -0
- package/dist/server/handlers/identity/handler.d.ts.map +1 -0
- package/dist/server/handlers/identity/index.d.ts +8 -0
- package/dist/server/handlers/identity/index.d.ts.map +1 -0
- package/dist/server/handlers/identity/oidc.d.ts +43 -0
- package/dist/server/handlers/identity/oidc.d.ts.map +1 -0
- package/dist/server/handlers/identity/types.d.ts +24 -0
- package/dist/server/handlers/identity/types.d.ts.map +1 -0
- package/dist/server/handlers/identity-only/handler.d.ts +15 -0
- package/dist/server/handlers/identity-only/handler.d.ts.map +1 -0
- package/dist/server/handlers/identity-only/index.d.ts +8 -0
- package/dist/server/handlers/identity-only/index.d.ts.map +1 -0
- package/dist/server/handlers/identity-only/oidc.d.ts +26 -0
- package/dist/server/handlers/identity-only/oidc.d.ts.map +1 -0
- package/dist/server/handlers/identity-only/types.d.ts +19 -0
- package/dist/server/handlers/identity-only/types.d.ts.map +1 -0
- package/dist/server/handlers/index.d.ts +5 -2
- package/dist/server/handlers/index.d.ts.map +1 -1
- package/dist/server/{lib/build-user-profile.d.ts → handlers/user/enrollments.d.ts} +7 -2
- package/dist/server/handlers/user/enrollments.d.ts.map +1 -0
- package/dist/server/handlers/user/handler.d.ts +17 -0
- package/dist/server/handlers/user/handler.d.ts.map +1 -0
- package/dist/server/handlers/user/index.d.ts +10 -0
- package/dist/server/handlers/user/index.d.ts.map +1 -0
- package/dist/server/handlers/user/profile.d.ts +22 -0
- package/dist/server/handlers/user/profile.d.ts.map +1 -0
- package/dist/server/handlers/user/types.d.ts +35 -0
- package/dist/server/handlers/user/types.d.ts.map +1 -0
- package/dist/server/handlers/user/verify.d.ts +25 -0
- package/dist/server/handlers/user/verify.d.ts.map +1 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/lib/index.d.ts +4 -5
- package/dist/server/lib/index.d.ts.map +1 -1
- package/dist/server/lib/resolve.d.ts +4 -42
- package/dist/server/lib/resolve.d.ts.map +1 -1
- package/dist/server/lib/sso.d.ts +86 -0
- package/dist/server/lib/sso.d.ts.map +1 -0
- package/dist/server/lib/utils.d.ts +93 -1
- package/dist/server/lib/utils.d.ts.map +1 -1
- package/dist/server/timeback-identity.d.ts.map +1 -1
- package/dist/server/timeback.d.ts.map +1 -1
- package/dist/server/types.d.ts +23 -10
- package/dist/server/types.d.ts.map +1 -1
- package/dist/shared/constants.d.ts +7 -0
- package/dist/shared/constants.d.ts.map +1 -1
- package/dist/shared/types.d.ts +77 -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 +8 -2
- package/dist/client/adapters/svelte/stores.d.ts.map +0 -1
- package/dist/server/handlers/activity.d.ts +0 -25
- package/dist/server/handlers/activity.d.ts.map +0 -1
- package/dist/server/handlers/identity-full.d.ts +0 -28
- package/dist/server/handlers/identity-full.d.ts.map +0 -1
- package/dist/server/handlers/identity-only.d.ts +0 -22
- package/dist/server/handlers/identity-only.d.ts.map +0 -1
- package/dist/server/handlers/user.d.ts +0 -31
- package/dist/server/handlers/user.d.ts.map +0 -1
- package/dist/server/lib/build-activity-events.d.ts +0 -67
- package/dist/server/lib/build-activity-events.d.ts.map +0 -1
- package/dist/server/lib/build-user-profile.d.ts.map +0 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeback Profile Store
|
|
3
|
+
*
|
|
4
|
+
* Svelte store for user profile state.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { fetchProfileOnce, getProfileCache } from '../../../lib/user-cache'
|
|
8
|
+
import { getClientInstance } from './client'
|
|
9
|
+
import { writable } from 'svelte/store'
|
|
10
|
+
import { verificationStore } from './verification'
|
|
11
|
+
|
|
12
|
+
import type { TimebackProfileState, TimebackVerificationState } from '../types'
|
|
13
|
+
import type { Readable } from 'svelte/store'
|
|
14
|
+
|
|
15
|
+
const profileStore = writable<TimebackProfileState>({ status: 'idle' })
|
|
16
|
+
const canFetchStore = writable<boolean>(false)
|
|
17
|
+
let profileInitialized = false
|
|
18
|
+
|
|
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
|
+
/**
|
|
29
|
+
* Run the profile fetch.
|
|
30
|
+
*
|
|
31
|
+
* @param force - If true, bypass cache and force a fresh fetch
|
|
32
|
+
*/
|
|
33
|
+
async function runProfileFetch(force: boolean): Promise<void> {
|
|
34
|
+
const client = getClientInstance()
|
|
35
|
+
if (!client) return
|
|
36
|
+
|
|
37
|
+
if (!force) {
|
|
38
|
+
const cached = getProfileCache(client)
|
|
39
|
+
if (cached) {
|
|
40
|
+
profileStore.set({ status: 'loaded', profile: cached })
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
profileStore.set({ status: 'loading' })
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const profile = await fetchProfileOnce(client, force)
|
|
49
|
+
profileStore.set({ status: 'loaded', profile })
|
|
50
|
+
} catch (err) {
|
|
51
|
+
profileStore.set({
|
|
52
|
+
status: 'error',
|
|
53
|
+
message: err instanceof Error ? err.message : 'Failed to fetch profile',
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Update canFetch based on verification state.
|
|
60
|
+
*
|
|
61
|
+
* @param verification - Current verification state
|
|
62
|
+
*/
|
|
63
|
+
function updateCanFetch(verification: TimebackVerificationState): void {
|
|
64
|
+
const can = verification.status === 'verified' && !!getClientInstance()
|
|
65
|
+
canFetchStore.set(can)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Initialize profile store subscriptions.
|
|
70
|
+
*/
|
|
71
|
+
function initProfile(): void {
|
|
72
|
+
if (profileInitialized) return
|
|
73
|
+
profileInitialized = true
|
|
74
|
+
|
|
75
|
+
verificationStore.subscribe(verification => {
|
|
76
|
+
updateCanFetch(verification)
|
|
77
|
+
|
|
78
|
+
// Reset to idle if not verified
|
|
79
|
+
if (verification.status !== 'verified') {
|
|
80
|
+
profileStore.set({ status: 'idle' })
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Auto-init in browser
|
|
86
|
+
if (isBrowser()) {
|
|
87
|
+
initProfile()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Store containing the Timeback profile state.
|
|
92
|
+
*
|
|
93
|
+
* Use this to access the current user's Timeback profile.
|
|
94
|
+
* Call `fetchTimebackProfile()` to manually fetch, or `refreshTimebackProfile()` to force re-fetch.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```svelte
|
|
98
|
+
* <script>
|
|
99
|
+
* import { timebackProfile, timebackProfileCanFetch, fetchTimebackProfile } from '@timeback/sdk/svelte'
|
|
100
|
+
* </script>
|
|
101
|
+
*
|
|
102
|
+
* <button on:click={fetchTimebackProfile} disabled={!$timebackProfileCanFetch}>
|
|
103
|
+
* {$timebackProfile.status === 'loading' ? 'Loading...' : 'Fetch Profile'}
|
|
104
|
+
* </button>
|
|
105
|
+
*
|
|
106
|
+
* {#if $timebackProfile.status === 'loaded'}
|
|
107
|
+
* <pre>{JSON.stringify($timebackProfile.profile, null, 2)}</pre>
|
|
108
|
+
* {/if}
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export const timebackProfile: Readable<TimebackProfileState> = profileStore
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Store indicating whether the profile can be fetched.
|
|
115
|
+
*
|
|
116
|
+
* True when the user is verified and the client is ready.
|
|
117
|
+
*/
|
|
118
|
+
export const timebackProfileCanFetch: Readable<boolean> = canFetchStore
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Manually fetch the Timeback profile.
|
|
122
|
+
*
|
|
123
|
+
* No-op if the user is not verified or client is not ready.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```svelte
|
|
127
|
+
* <script>
|
|
128
|
+
* import { fetchTimebackProfile } from '@timeback/sdk/svelte'
|
|
129
|
+
* </script>
|
|
130
|
+
*
|
|
131
|
+
* <button on:click={fetchTimebackProfile}>Fetch Profile</button>
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export function fetchTimebackProfile(): void {
|
|
135
|
+
if (!isBrowser()) return
|
|
136
|
+
|
|
137
|
+
let canFetch = false
|
|
138
|
+
canFetchStore.subscribe(v => (canFetch = v))()
|
|
139
|
+
|
|
140
|
+
if (!canFetch) return
|
|
141
|
+
|
|
142
|
+
void runProfileFetch(false)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Force a re-fetch of the Timeback profile.
|
|
147
|
+
*
|
|
148
|
+
* No-op if the user is not verified or client is not ready.
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```svelte
|
|
152
|
+
* <script>
|
|
153
|
+
* import { refreshTimebackProfile } from '@timeback/sdk/svelte'
|
|
154
|
+
* </script>
|
|
155
|
+
*
|
|
156
|
+
* <button on:click={refreshTimebackProfile}>Refresh Profile</button>
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export function refreshTimebackProfile(): void {
|
|
160
|
+
if (!isBrowser()) return
|
|
161
|
+
|
|
162
|
+
let canFetch = false
|
|
163
|
+
canFetchStore.subscribe(v => (canFetch = v))()
|
|
164
|
+
|
|
165
|
+
if (!canFetch) return
|
|
166
|
+
|
|
167
|
+
void runProfileFetch(true)
|
|
168
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeback Verification Store
|
|
3
|
+
*
|
|
4
|
+
* Svelte store for user verification state.
|
|
5
|
+
*/
|
|
6
|
+
import type { TimebackVerificationState } from '../types';
|
|
7
|
+
import type { Readable } from 'svelte/store';
|
|
8
|
+
export declare const verificationStore: import("svelte/store").Writable<TimebackVerificationState>;
|
|
9
|
+
/**
|
|
10
|
+
* Store containing the Timeback verification state.
|
|
11
|
+
*
|
|
12
|
+
* Use this to check if the current user is verified in Timeback.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```svelte
|
|
16
|
+
* <script>
|
|
17
|
+
* import { timebackVerification, refreshTimebackVerification } from '@timeback/sdk/svelte'
|
|
18
|
+
* </script>
|
|
19
|
+
*
|
|
20
|
+
* {#if $timebackVerification.status === 'verified'}
|
|
21
|
+
* <p>Verified! Timeback ID: {$timebackVerification.timebackId}</p>
|
|
22
|
+
* {:else if $timebackVerification.status === 'loading'}
|
|
23
|
+
* <p>Verifying...</p>
|
|
24
|
+
* {:else}
|
|
25
|
+
* <p>Not verified</p>
|
|
26
|
+
* {/if}
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare const timebackVerification: Readable<TimebackVerificationState>;
|
|
30
|
+
/**
|
|
31
|
+
* Force a re-verification of the current user.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```svelte
|
|
35
|
+
* <script>
|
|
36
|
+
* import { refreshTimebackVerification } from '@timeback/sdk/svelte'
|
|
37
|
+
* </script>
|
|
38
|
+
*
|
|
39
|
+
* <button on:click={refreshTimebackVerification}>Refresh Verification</button>
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export declare function refreshTimebackVerification(): void;
|
|
43
|
+
//# sourceMappingURL=verification.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeback Verification Store
|
|
3
|
+
*
|
|
4
|
+
* Svelte store for user verification state.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { getVerifyCache, verifyOnce } from '../../../lib/user-cache'
|
|
8
|
+
import { clientStore, getClientInstance } from './client'
|
|
9
|
+
import { writable } from 'svelte/store'
|
|
10
|
+
|
|
11
|
+
import type { TimebackVerifyResult } from '../../../../shared/types'
|
|
12
|
+
import type { TimebackVerificationState } from '../types'
|
|
13
|
+
import type { Readable } from 'svelte/store'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Convert a verify result into a store-friendly state machine.
|
|
17
|
+
*
|
|
18
|
+
* @param result - Raw verify result from the SDK client
|
|
19
|
+
* @returns Mapped verification state
|
|
20
|
+
*/
|
|
21
|
+
function toVerificationState(result: TimebackVerifyResult): TimebackVerificationState {
|
|
22
|
+
return result.verified
|
|
23
|
+
? { status: 'verified', timebackId: result.timebackId }
|
|
24
|
+
: { status: 'unverified' }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const verificationStore = writable<TimebackVerificationState>({ status: 'loading' })
|
|
28
|
+
let verificationInitialized = false
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if we're running in the browser.
|
|
32
|
+
*
|
|
33
|
+
* @returns True if in browser
|
|
34
|
+
*/
|
|
35
|
+
function isBrowser(): boolean {
|
|
36
|
+
return typeof window !== 'undefined'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Run the verification check.
|
|
41
|
+
*
|
|
42
|
+
* @param force - If true, bypass cache and force a fresh verification
|
|
43
|
+
*/
|
|
44
|
+
async function runVerification(force: boolean): Promise<void> {
|
|
45
|
+
const client = getClientInstance()
|
|
46
|
+
if (!client) return
|
|
47
|
+
|
|
48
|
+
if (!force) {
|
|
49
|
+
const cached = getVerifyCache(client)
|
|
50
|
+
if (cached) {
|
|
51
|
+
verificationStore.set(toVerificationState(cached))
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
verificationStore.set({ status: 'loading' })
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const result = await verifyOnce(client, force)
|
|
60
|
+
verificationStore.set(toVerificationState(result))
|
|
61
|
+
} catch (err) {
|
|
62
|
+
verificationStore.set({
|
|
63
|
+
status: 'error',
|
|
64
|
+
message: err instanceof Error ? err.message : 'Failed to verify Timeback user',
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Initialize verification when client becomes available.
|
|
71
|
+
*/
|
|
72
|
+
function initVerification(): void {
|
|
73
|
+
if (verificationInitialized) return
|
|
74
|
+
verificationInitialized = true
|
|
75
|
+
|
|
76
|
+
// Subscribe to client changes to auto-verify
|
|
77
|
+
clientStore.subscribe(client => {
|
|
78
|
+
if (client && isBrowser()) {
|
|
79
|
+
void runVerification(false)
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Auto-init in browser
|
|
85
|
+
if (isBrowser()) {
|
|
86
|
+
initVerification()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Store containing the Timeback verification state.
|
|
91
|
+
*
|
|
92
|
+
* Use this to check if the current user is verified in Timeback.
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```svelte
|
|
96
|
+
* <script>
|
|
97
|
+
* import { timebackVerification, refreshTimebackVerification } from '@timeback/sdk/svelte'
|
|
98
|
+
* </script>
|
|
99
|
+
*
|
|
100
|
+
* {#if $timebackVerification.status === 'verified'}
|
|
101
|
+
* <p>Verified! Timeback ID: {$timebackVerification.timebackId}</p>
|
|
102
|
+
* {:else if $timebackVerification.status === 'loading'}
|
|
103
|
+
* <p>Verifying...</p>
|
|
104
|
+
* {:else}
|
|
105
|
+
* <p>Not verified</p>
|
|
106
|
+
* {/if}
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export const timebackVerification: Readable<TimebackVerificationState> = verificationStore
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Force a re-verification of the current user.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```svelte
|
|
116
|
+
* <script>
|
|
117
|
+
* import { refreshTimebackVerification } from '@timeback/sdk/svelte'
|
|
118
|
+
* </script>
|
|
119
|
+
*
|
|
120
|
+
* <button on:click={refreshTimebackVerification}>Refresh Verification</button>
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export function refreshTimebackVerification(): void {
|
|
124
|
+
if (!isBrowser()) return
|
|
125
|
+
void runVerification(true)
|
|
126
|
+
}
|
|
@@ -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,44 @@
|
|
|
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
|
+
* @param options - Composable options
|
|
18
|
+
* @param options.enabled - If false, the composable does nothing and stays in loading state.
|
|
19
|
+
* @returns Verification state and a refresh method
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```vue
|
|
23
|
+
* <script setup>
|
|
24
|
+
* import { useTimebackVerification } from '@timeback/sdk/vue'
|
|
25
|
+
*
|
|
26
|
+
* const { state, refresh } = useTimebackVerification()
|
|
27
|
+
* </script>
|
|
28
|
+
*
|
|
29
|
+
* <template>
|
|
30
|
+
* <div v-if="state.status === 'verified'">
|
|
31
|
+
* Verified as {{ state.timebackId }}
|
|
32
|
+
* </div>
|
|
33
|
+
* <div v-else-if="state.status === 'loading'">
|
|
34
|
+
* Verifying...
|
|
35
|
+
* </div>
|
|
36
|
+
* <button @click="refresh">Refresh</button>
|
|
37
|
+
* </template>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function useTimebackVerification(options?: UseTimebackVerificationOptions): {
|
|
41
|
+
state: Ref<TimebackVerificationState>;
|
|
42
|
+
refresh: () => void;
|
|
43
|
+
};
|
|
44
|
+
//# 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;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"}
|