@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
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Timeback Client Store
|
|
3
3
|
*
|
|
4
|
-
* Svelte
|
|
4
|
+
* Core Svelte store for the Timeback client singleton.
|
|
5
5
|
*/
|
|
6
6
|
import { TimebackClient } from '@timeback/sdk/client';
|
|
7
|
+
import type { Readable } from 'svelte/store';
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
+
* Internal client store.
|
|
10
|
+
*/
|
|
11
|
+
export declare const clientStore: import("svelte/store").Writable<TimebackClient | undefined>;
|
|
12
|
+
/**
|
|
13
|
+
* Get the current client instance (for internal use by other stores).
|
|
9
14
|
*
|
|
10
|
-
*
|
|
15
|
+
* @returns Current client instance or undefined
|
|
11
16
|
*/
|
|
12
|
-
|
|
13
|
-
subscribe: (run: (value: T) => void) => () => void;
|
|
14
|
-
}
|
|
17
|
+
export declare function getClientInstance(): TimebackClient | undefined;
|
|
15
18
|
/**
|
|
16
19
|
* Initialize the Timeback client.
|
|
17
20
|
*
|
|
@@ -61,5 +64,4 @@ export declare function initTimeback(client?: TimebackClient): void;
|
|
|
61
64
|
* ```
|
|
62
65
|
*/
|
|
63
66
|
export declare const timeback: Readable<TimebackClient | undefined>;
|
|
64
|
-
|
|
65
|
-
//# sourceMappingURL=stores.d.ts.map
|
|
67
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../../../src/client/adapters/svelte/stores/client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAErD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAyB5C;;GAEG;AACH,eAAO,MAAM,WAAW,6DAAkD,CAAA;AAE1E;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,cAAc,GAAG,IAAI,CAQ1D;AASD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,QAAQ,EAAE,QAAQ,CAAC,cAAc,GAAG,SAAS,CAAe,CAAA"}
|
|
@@ -1,51 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Timeback Client Store
|
|
3
3
|
*
|
|
4
|
-
* Svelte
|
|
4
|
+
* Core Svelte store for the Timeback client singleton.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Minimal Svelte-compatible store interface.
|
|
11
|
-
*
|
|
12
|
-
* This allows the SDK to work without a direct Svelte dependency.
|
|
13
|
-
*/
|
|
14
|
-
interface Readable<T> {
|
|
15
|
-
subscribe: (run: (value: T) => void) => () => void
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface Writable<T> extends Readable<T> {
|
|
19
|
-
set: (value: T) => void
|
|
20
|
-
update: (updater: (value: T) => T) => void
|
|
21
|
-
}
|
|
7
|
+
import { writable } from 'svelte/store'
|
|
22
8
|
|
|
23
|
-
|
|
24
|
-
* Create a minimal writable store implementation.
|
|
25
|
-
*
|
|
26
|
-
* @param initialValue - Initial store value
|
|
27
|
-
* @returns Writable store
|
|
28
|
-
*/
|
|
29
|
-
function writable<T>(initialValue: T): Writable<T> {
|
|
30
|
-
let value = initialValue
|
|
31
|
-
const subscribers = new Set<(value: T) => void>()
|
|
9
|
+
import { TimebackClient } from '@timeback/sdk/client'
|
|
32
10
|
|
|
33
|
-
|
|
34
|
-
subscribe(run) {
|
|
35
|
-
subscribers.add(run)
|
|
36
|
-
run(value)
|
|
37
|
-
return () => subscribers.delete(run)
|
|
38
|
-
},
|
|
39
|
-
set(newValue) {
|
|
40
|
-
value = newValue
|
|
41
|
-
subscribers.forEach(fn => fn(value))
|
|
42
|
-
},
|
|
43
|
-
update(updater) {
|
|
44
|
-
value = updater(value)
|
|
45
|
-
subscribers.forEach(fn => fn(value))
|
|
46
|
-
},
|
|
47
|
-
}
|
|
48
|
-
}
|
|
11
|
+
import type { Readable } from 'svelte/store'
|
|
49
12
|
|
|
50
13
|
let clientInstance: TimebackClient | undefined
|
|
51
14
|
|
|
@@ -61,10 +24,28 @@ function getOrCreateClient(): TimebackClient {
|
|
|
61
24
|
return clientInstance
|
|
62
25
|
}
|
|
63
26
|
|
|
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
|
+
|
|
64
36
|
/**
|
|
65
37
|
* Internal client store.
|
|
66
38
|
*/
|
|
67
|
-
const clientStore = writable<TimebackClient | undefined>(undefined)
|
|
39
|
+
export const clientStore = writable<TimebackClient | undefined>(undefined)
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get the current client instance (for internal use by other stores).
|
|
43
|
+
*
|
|
44
|
+
* @returns Current client instance or undefined
|
|
45
|
+
*/
|
|
46
|
+
export function getClientInstance(): TimebackClient | undefined {
|
|
47
|
+
return clientInstance
|
|
48
|
+
}
|
|
68
49
|
|
|
69
50
|
/**
|
|
70
51
|
* Initialize the Timeback client.
|
|
@@ -95,15 +76,6 @@ export function initTimeback(client?: TimebackClient): void {
|
|
|
95
76
|
clientStore.set(getOrCreateClient())
|
|
96
77
|
}
|
|
97
78
|
|
|
98
|
-
/**
|
|
99
|
-
* Check if we're running in the browser.
|
|
100
|
-
*
|
|
101
|
-
* @returns True if in browser
|
|
102
|
-
*/
|
|
103
|
-
function isBrowser(): boolean {
|
|
104
|
-
return typeof window !== 'undefined'
|
|
105
|
-
}
|
|
106
|
-
|
|
107
79
|
/**
|
|
108
80
|
* Auto-initialize on first access in browser.
|
|
109
81
|
*/
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte Stores
|
|
3
|
+
*
|
|
4
|
+
* Svelte stores for Timeback SDK reactivity.
|
|
5
|
+
*/
|
|
6
|
+
export { initTimeback, timeback } from './client';
|
|
7
|
+
export { refreshTimebackVerification, timebackVerification } from './verification';
|
|
8
|
+
export { fetchTimebackProfile, refreshTimebackProfile, timebackProfile, timebackProfileCanFetch, } from './profile';
|
|
9
|
+
export type { TimebackProfileState, TimebackVerificationState } from '../types';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/client/adapters/svelte/stores/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAGjD,OAAO,EAAE,2BAA2B,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAGlF,OAAO,EACN,oBAAoB,EACpB,sBAAsB,EACtB,eAAe,EACf,uBAAuB,GACvB,MAAM,WAAW,CAAA;AAGlB,YAAY,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Svelte Stores
|
|
3
|
+
*
|
|
4
|
+
* Svelte stores for Timeback SDK reactivity.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Client
|
|
8
|
+
export { initTimeback, timeback } from './client'
|
|
9
|
+
|
|
10
|
+
// Verification
|
|
11
|
+
export { refreshTimebackVerification, timebackVerification } from './verification'
|
|
12
|
+
|
|
13
|
+
// Profile
|
|
14
|
+
export {
|
|
15
|
+
fetchTimebackProfile,
|
|
16
|
+
refreshTimebackProfile,
|
|
17
|
+
timebackProfile,
|
|
18
|
+
timebackProfileCanFetch,
|
|
19
|
+
} from './profile'
|
|
20
|
+
|
|
21
|
+
// Types re-exported for convenience
|
|
22
|
+
export type { TimebackProfileState, TimebackVerificationState } from '../types'
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeback Profile Store
|
|
3
|
+
*
|
|
4
|
+
* Svelte store for user profile state.
|
|
5
|
+
*/
|
|
6
|
+
import type { TimebackProfileState } from '../types';
|
|
7
|
+
import type { Readable } from 'svelte/store';
|
|
8
|
+
/**
|
|
9
|
+
* Store containing the Timeback profile state.
|
|
10
|
+
*
|
|
11
|
+
* Use this to access the current user's Timeback profile.
|
|
12
|
+
* Call `fetchTimebackProfile()` to manually fetch, or `refreshTimebackProfile()` to force re-fetch.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```svelte
|
|
16
|
+
* <script>
|
|
17
|
+
* import { timebackProfile, timebackProfileCanFetch, fetchTimebackProfile } from '@timeback/sdk/svelte'
|
|
18
|
+
* </script>
|
|
19
|
+
*
|
|
20
|
+
* <button on:click={fetchTimebackProfile} disabled={!$timebackProfileCanFetch}>
|
|
21
|
+
* {$timebackProfile.status === 'loading' ? 'Loading...' : 'Fetch Profile'}
|
|
22
|
+
* </button>
|
|
23
|
+
*
|
|
24
|
+
* {#if $timebackProfile.status === 'loaded'}
|
|
25
|
+
* <pre>{JSON.stringify($timebackProfile.profile, null, 2)}</pre>
|
|
26
|
+
* {/if}
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare const timebackProfile: Readable<TimebackProfileState>;
|
|
30
|
+
/**
|
|
31
|
+
* Store indicating whether the profile can be fetched.
|
|
32
|
+
*
|
|
33
|
+
* True when the user is verified and the client is ready.
|
|
34
|
+
*/
|
|
35
|
+
export declare const timebackProfileCanFetch: Readable<boolean>;
|
|
36
|
+
/**
|
|
37
|
+
* Manually fetch the Timeback profile.
|
|
38
|
+
*
|
|
39
|
+
* No-op if the user is not verified or client is not ready.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```svelte
|
|
43
|
+
* <script>
|
|
44
|
+
* import { fetchTimebackProfile } from '@timeback/sdk/svelte'
|
|
45
|
+
* </script>
|
|
46
|
+
*
|
|
47
|
+
* <button on:click={fetchTimebackProfile}>Fetch Profile</button>
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function fetchTimebackProfile(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Force a re-fetch of the Timeback profile.
|
|
53
|
+
*
|
|
54
|
+
* No-op if the user is not verified or client is not ready.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```svelte
|
|
58
|
+
* <script>
|
|
59
|
+
* import { refreshTimebackProfile } from '@timeback/sdk/svelte'
|
|
60
|
+
* </script>
|
|
61
|
+
*
|
|
62
|
+
* <button on:click={refreshTimebackProfile}>Refresh Profile</button>
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function refreshTimebackProfile(): void;
|
|
66
|
+
//# sourceMappingURL=profile.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -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 { Readable } from 'svelte/store';
|
|
7
|
+
import type { TimebackVerificationState } from '../types';
|
|
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;AASH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAE5C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAA;AAczD,eAAO,MAAM,iBAAiB,4DAA6D,CAAA;AA6I3F;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,yBAAyB,CAAqB,CAAA;AAE1F;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAGlD"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeback Verification Store
|
|
3
|
+
*
|
|
4
|
+
* Svelte store for user verification state.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { writable } from 'svelte/store'
|
|
8
|
+
|
|
9
|
+
import { DEFAULT_RETRY_ATTEMPTS, DEFAULT_RETRY_DELAYS_MS } from '../../../../shared/constants'
|
|
10
|
+
import { getVerifyCache, verifyOnce } from '../../../lib/user-cache'
|
|
11
|
+
import { getRetryDelay, sleep } from '../../../lib/utils'
|
|
12
|
+
import { clientStore, getClientInstance } from './client'
|
|
13
|
+
|
|
14
|
+
import type { Readable } from 'svelte/store'
|
|
15
|
+
import type { TimebackVerifyResult } from '../../../../shared/types'
|
|
16
|
+
import type { TimebackVerificationState } from '../types'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Convert a verify result into a store-friendly state machine.
|
|
20
|
+
*
|
|
21
|
+
* @param result - Raw verify result from the SDK client
|
|
22
|
+
* @returns Mapped verification state
|
|
23
|
+
*/
|
|
24
|
+
function toVerificationState(result: TimebackVerifyResult): TimebackVerificationState {
|
|
25
|
+
return result.verified
|
|
26
|
+
? { status: 'verified', timebackId: result.timebackId }
|
|
27
|
+
: { status: 'unverified' }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const verificationStore = writable<TimebackVerificationState>({ status: 'loading' })
|
|
31
|
+
let verificationInitialized = false
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Counter tracking the current verification run.
|
|
35
|
+
*
|
|
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).
|
|
40
|
+
*/
|
|
41
|
+
let currentRunId = 0
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check if we're running in the browser.
|
|
45
|
+
*
|
|
46
|
+
* @returns True if in browser
|
|
47
|
+
*/
|
|
48
|
+
function isBrowser(): boolean {
|
|
49
|
+
return typeof window !== 'undefined'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Run the verification check with retry logic.
|
|
54
|
+
*
|
|
55
|
+
* Uses a run ID to prevent race conditions when multiple verification
|
|
56
|
+
* attempts overlap. Each run captures the current ID and only updates
|
|
57
|
+
* state if it's still the active run.
|
|
58
|
+
*
|
|
59
|
+
* @param force - If true, bypass cache and force a fresh verification
|
|
60
|
+
*/
|
|
61
|
+
async function runVerification(force: boolean): Promise<void> {
|
|
62
|
+
const client = getClientInstance()
|
|
63
|
+
if (!client) return
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Capture this run's ID. Any subsequent call to runVerification
|
|
67
|
+
* will increment currentRunId, making this run "cancelled".
|
|
68
|
+
*/
|
|
69
|
+
const runId = ++currentRunId
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if this run is still the active one.
|
|
73
|
+
*
|
|
74
|
+
* @returns False if a newer verification run has started
|
|
75
|
+
*/
|
|
76
|
+
const isCurrentRun = () => runId === currentRunId
|
|
77
|
+
|
|
78
|
+
if (!force) {
|
|
79
|
+
const cached = getVerifyCache(client)
|
|
80
|
+
if (cached) {
|
|
81
|
+
if (isCurrentRun()) {
|
|
82
|
+
verificationStore.set(toVerificationState(cached))
|
|
83
|
+
}
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (isCurrentRun()) {
|
|
89
|
+
verificationStore.set({ status: 'loading' })
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
let lastError: Error | undefined
|
|
93
|
+
|
|
94
|
+
for (let attempt = 0; attempt <= DEFAULT_RETRY_ATTEMPTS; attempt++) {
|
|
95
|
+
if (!isCurrentRun()) return
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Wait for a calculated delay before retrying the verification request.
|
|
99
|
+
*
|
|
100
|
+
* This occurs only on subsequent attempts (not the initial try),
|
|
101
|
+
* introducing a backoff based on the configured retry delays.
|
|
102
|
+
*/
|
|
103
|
+
if (attempt > 0) {
|
|
104
|
+
const delay = getRetryDelay(DEFAULT_RETRY_DELAYS_MS, attempt - 1)
|
|
105
|
+
await sleep(delay)
|
|
106
|
+
if (!isCurrentRun()) return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const result = await verifyOnce(client, force || attempt > 0)
|
|
111
|
+
|
|
112
|
+
if (!isCurrentRun()) return
|
|
113
|
+
|
|
114
|
+
verificationStore.set(toVerificationState(result))
|
|
115
|
+
|
|
116
|
+
return
|
|
117
|
+
} catch (err) {
|
|
118
|
+
lastError = err instanceof Error ? err : new Error('Failed to verify Timeback user')
|
|
119
|
+
/**
|
|
120
|
+
* An error occurred during this verification attempt.
|
|
121
|
+
* Proceeding to the next retry attempt if any remain.
|
|
122
|
+
*
|
|
123
|
+
* The last encountered error is saved for potential error state handling.
|
|
124
|
+
*/
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* All verification attempts have failed after exhausting
|
|
130
|
+
* the configured retry policy. The verification state will
|
|
131
|
+
* transition to "error" if this run is still current, and the last
|
|
132
|
+
* encountered error message (if available) will be presented to the consumer.
|
|
133
|
+
*/
|
|
134
|
+
if (isCurrentRun() && lastError) {
|
|
135
|
+
verificationStore.set({
|
|
136
|
+
status: 'error',
|
|
137
|
+
message: lastError.message,
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Initialize verification when client becomes available.
|
|
144
|
+
*/
|
|
145
|
+
function initVerification(): void {
|
|
146
|
+
if (verificationInitialized) return
|
|
147
|
+
verificationInitialized = true
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Subscribes to changes in the Timeback client store.
|
|
151
|
+
*
|
|
152
|
+
* Any time the client instance becomes available or changes,
|
|
153
|
+
* automatically triggers a verification attempt to ensure the
|
|
154
|
+
* user's Timeback verification status is kept up-to-date.
|
|
155
|
+
*
|
|
156
|
+
* This enables responsive status updates when authentication
|
|
157
|
+
* state changes or when the client instance is re-initialized,
|
|
158
|
+
* providing a seamless verification experience in Svelte apps.
|
|
159
|
+
*/
|
|
160
|
+
clientStore.subscribe(client => {
|
|
161
|
+
if (client && isBrowser()) {
|
|
162
|
+
void runVerification(false)
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (isBrowser()) {
|
|
168
|
+
initVerification()
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Store containing the Timeback verification state.
|
|
173
|
+
*
|
|
174
|
+
* Use this to check if the current user is verified in Timeback.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```svelte
|
|
178
|
+
* <script>
|
|
179
|
+
* import { timebackVerification, refreshTimebackVerification } from '@timeback/sdk/svelte'
|
|
180
|
+
* </script>
|
|
181
|
+
*
|
|
182
|
+
* {#if $timebackVerification.status === 'verified'}
|
|
183
|
+
* <p>Verified! Timeback ID: {$timebackVerification.timebackId}</p>
|
|
184
|
+
* {:else if $timebackVerification.status === 'loading'}
|
|
185
|
+
* <p>Verifying...</p>
|
|
186
|
+
* {:else}
|
|
187
|
+
* <p>Not verified</p>
|
|
188
|
+
* {/if}
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
export const timebackVerification: Readable<TimebackVerificationState> = verificationStore
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Force a re-verification of the current user.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```svelte
|
|
198
|
+
* <script>
|
|
199
|
+
* import { refreshTimebackVerification } from '@timeback/sdk/svelte'
|
|
200
|
+
* </script>
|
|
201
|
+
*
|
|
202
|
+
* <button on:click={refreshTimebackVerification}>Refresh Verification</button>
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
export function refreshTimebackVerification(): void {
|
|
206
|
+
if (!isBrowser()) return
|
|
207
|
+
void runVerification(true)
|
|
208
|
+
}
|