@stacksjs/browser 0.70.88 → 0.70.91
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/dist/auto-init.d.ts +7 -0
- package/dist/auto-init.js +61 -0
- package/dist/composables/auth/useGithub.d.ts +12 -0
- package/dist/composables/auth/useGithub.js +41 -0
- package/dist/composables/index.d.ts +140 -0
- package/dist/composables/index.js +110 -0
- package/dist/composables/useApi.d.ts +56 -0
- package/dist/composables/useApi.js +66 -0
- package/dist/composables/useAuth.d.ts +6 -0
- package/dist/composables/useAuth.js +127 -0
- package/dist/composables/useFetch.d.ts +1 -0
- package/dist/composables/useFetch.js +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +16 -0
- package/dist/model-loader.d.ts +84 -0
- package/dist/model-loader.js +61 -0
- package/dist/types/dashboard.d.ts +69 -0
- package/dist/types/dashboard.js +3 -0
- package/dist/utils/base.d.ts +7 -0
- package/dist/utils/base.js +4 -0
- package/dist/utils/billable.d.ts +7 -0
- package/dist/utils/billable.js +72 -0
- package/dist/utils/date.d.ts +2 -0
- package/dist/utils/date.js +2 -0
- package/dist/utils/debounce.d.ts +5 -0
- package/dist/utils/debounce.js +43 -0
- package/dist/utils/fetch.d.ts +33 -0
- package/dist/utils/fetch.js +94 -0
- package/dist/utils/function.d.ts +18 -0
- package/dist/utils/function.js +7 -0
- package/dist/utils/guards.d.ts +28 -0
- package/dist/utils/guards.js +12 -0
- package/dist/utils/index.d.ts +17 -0
- package/dist/utils/index.js +17 -0
- package/dist/utils/lazy.d.ts +6 -0
- package/dist/utils/lazy.js +9 -0
- package/dist/utils/math.d.ts +20 -0
- package/dist/utils/math.js +23 -0
- package/dist/utils/plans.d.ts +2 -0
- package/dist/utils/plans.js +135 -0
- package/dist/utils/promise.d.ts +49 -0
- package/dist/utils/promise.js +55 -0
- package/dist/utils/random.d.ts +10 -0
- package/dist/utils/random.js +67 -0
- package/dist/utils/regex.d.ts +38 -0
- package/dist/utils/regex.js +29 -0
- package/dist/utils/retry.d.ts +10 -0
- package/dist/utils/retry.js +28 -0
- package/dist/utils/sleep.d.ts +35 -0
- package/dist/utils/sleep.js +59 -0
- package/dist/utils/throttle.d.ts +20 -0
- package/dist/utils/throttle.js +18 -0
- package/dist/utils/vendors.d.ts +29 -0
- package/dist/utils/vendors.js +37 -0
- package/package.json +4 -4
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
browserQuery,
|
|
3
|
+
BrowserQueryBuilder,
|
|
4
|
+
BrowserQueryError,
|
|
5
|
+
browserAuth,
|
|
6
|
+
configureBrowser,
|
|
7
|
+
getBrowserConfig,
|
|
8
|
+
createBrowserDb,
|
|
9
|
+
createBrowserModel,
|
|
10
|
+
isBrowser
|
|
11
|
+
} from "bun-query-builder/browser";
|
|
12
|
+
import * as Composables from "./composables";
|
|
13
|
+
import { loadBrowserModels } from "./model-loader";
|
|
14
|
+
let isInitialized = !1;
|
|
15
|
+
function autoInit() {
|
|
16
|
+
if (isInitialized)
|
|
17
|
+
return;
|
|
18
|
+
if (typeof window > "u")
|
|
19
|
+
return;
|
|
20
|
+
if (typeof document > "u")
|
|
21
|
+
return;
|
|
22
|
+
isInitialized = !0;
|
|
23
|
+
const baseUrl = window.__STACKS_API_URL__ || `${window.location.origin}/api`;
|
|
24
|
+
configureBrowser({
|
|
25
|
+
baseUrl,
|
|
26
|
+
getToken: () => {
|
|
27
|
+
try {
|
|
28
|
+
return localStorage.getItem("token");
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
onUnauthorized: () => {
|
|
34
|
+
const loginUrl = window.__STACKS_LOGIN_URL__ || "/login";
|
|
35
|
+
if (!window.location.pathname.startsWith(loginUrl))
|
|
36
|
+
window.location.href = loginUrl;
|
|
37
|
+
},
|
|
38
|
+
transformResponse: (data) => {
|
|
39
|
+
return data?.data ?? data;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
window.StacksBrowser = {
|
|
43
|
+
browserQuery,
|
|
44
|
+
BrowserQueryBuilder,
|
|
45
|
+
BrowserQueryError,
|
|
46
|
+
browserAuth,
|
|
47
|
+
configureBrowser,
|
|
48
|
+
getBrowserConfig,
|
|
49
|
+
createBrowserDb,
|
|
50
|
+
createBrowserModel,
|
|
51
|
+
isBrowser,
|
|
52
|
+
...Composables
|
|
53
|
+
};
|
|
54
|
+
loadBrowserModels();
|
|
55
|
+
window.dispatchEvent(new CustomEvent("stacks:api-ready"));
|
|
56
|
+
if (window.__STACKS_DEBUG__)
|
|
57
|
+
console.debug("[Stacks] Browser API initialized:", baseUrl);
|
|
58
|
+
}
|
|
59
|
+
autoInit();
|
|
60
|
+
|
|
61
|
+
export { autoInit, isInitialized };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Ref } from '@stacksjs/stx';
|
|
2
|
+
export declare function useGitHubOAuth(): GitHubOAuthReturn;
|
|
3
|
+
declare interface GitHubOAuthReturn {
|
|
4
|
+
login: () => void
|
|
5
|
+
accessToken: Ref<string | null>
|
|
6
|
+
error: Ref<string | null>
|
|
7
|
+
isLoading: Ref<boolean>
|
|
8
|
+
}
|
|
9
|
+
// @ts-ignore - Window.location override
|
|
10
|
+
declare global {
|
|
11
|
+
interface Window {}
|
|
12
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { onMounted, ref } from "@stacksjs/stx";
|
|
2
|
+
export function useGitHubOAuth() {
|
|
3
|
+
const stacksConfig = window.__STACKS_CONFIG__ || {}, clientId = stacksConfig.GITHUB_CLIENT_ID || "", redirectUri = `${stacksConfig.APP_URL || window.location.origin}/auth/github/callback`, accessToken = ref(null), error = ref(null), isLoading = ref(!1);
|
|
4
|
+
function login() {
|
|
5
|
+
const githubAuthUrl = `https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=read:user user:email`;
|
|
6
|
+
window.location.href = githubAuthUrl;
|
|
7
|
+
}
|
|
8
|
+
async function fetchAccessToken(code) {
|
|
9
|
+
isLoading.value = !0;
|
|
10
|
+
error.value = null;
|
|
11
|
+
try {
|
|
12
|
+
const apiUrl = stacksConfig.APP_URL || window.location.origin, res = await fetch(`${apiUrl}/api/github/token`, {
|
|
13
|
+
method: "POST",
|
|
14
|
+
headers: { "Content-Type": "application/json" },
|
|
15
|
+
body: JSON.stringify({ code })
|
|
16
|
+
});
|
|
17
|
+
if (!res.ok)
|
|
18
|
+
throw Error("Failed to exchange code for access token");
|
|
19
|
+
const data = await res.json();
|
|
20
|
+
accessToken.value = data.access_token;
|
|
21
|
+
} catch (err) {
|
|
22
|
+
error.value = err.message || "Unknown error";
|
|
23
|
+
} finally {
|
|
24
|
+
isLoading.value = !1;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function handleCallback() {
|
|
28
|
+
const code = new URLSearchParams(window.location.search).get("code");
|
|
29
|
+
if (code)
|
|
30
|
+
fetchAccessToken(code);
|
|
31
|
+
}
|
|
32
|
+
onMounted(() => {
|
|
33
|
+
handleCallback();
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
login,
|
|
37
|
+
accessToken,
|
|
38
|
+
error,
|
|
39
|
+
isLoading
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
export * from './useAuth';
|
|
2
|
+
export * from './useApi';
|
|
3
|
+
// Re-export browser-relevant composables from @stacksjs/composables
|
|
4
|
+
export {
|
|
5
|
+
// Color mode / Theme
|
|
6
|
+
useColorMode,
|
|
7
|
+
useDark,
|
|
8
|
+
usePreferredColorScheme,
|
|
9
|
+
usePreferredContrast,
|
|
10
|
+
usePreferredDark,
|
|
11
|
+
usePreferredReducedMotion,
|
|
12
|
+
|
|
13
|
+
// Date / Time
|
|
14
|
+
useDateFormat,
|
|
15
|
+
useNow,
|
|
16
|
+
useTimeAgo,
|
|
17
|
+
useTimestamp,
|
|
18
|
+
|
|
19
|
+
// Network / Online
|
|
20
|
+
useFetch,
|
|
21
|
+
useNetwork,
|
|
22
|
+
useOnline,
|
|
23
|
+
|
|
24
|
+
// Storage
|
|
25
|
+
useLocalStorage,
|
|
26
|
+
useSessionStorage,
|
|
27
|
+
useStorage,
|
|
28
|
+
|
|
29
|
+
// Toggle / State
|
|
30
|
+
useToggle,
|
|
31
|
+
useCounter,
|
|
32
|
+
useStepper,
|
|
33
|
+
useCycleList,
|
|
34
|
+
|
|
35
|
+
// DOM / Elements
|
|
36
|
+
useActiveElement,
|
|
37
|
+
useClipboard,
|
|
38
|
+
useClipboardItems,
|
|
39
|
+
useCssVar,
|
|
40
|
+
useDocumentVisibility,
|
|
41
|
+
useElementBounding,
|
|
42
|
+
useElementHover,
|
|
43
|
+
useElementSize,
|
|
44
|
+
useElementVisibility,
|
|
45
|
+
useFocus,
|
|
46
|
+
useFocusWithin,
|
|
47
|
+
useFullscreen,
|
|
48
|
+
useScroll,
|
|
49
|
+
useScrollLock,
|
|
50
|
+
useTitle,
|
|
51
|
+
useWindowFocus,
|
|
52
|
+
useWindowScroll,
|
|
53
|
+
useWindowSize,
|
|
54
|
+
|
|
55
|
+
// Events / Interaction
|
|
56
|
+
onClickOutside,
|
|
57
|
+
onKeyDown,
|
|
58
|
+
onKeyStroke,
|
|
59
|
+
onKeyUp,
|
|
60
|
+
onLongPress,
|
|
61
|
+
onStartTyping,
|
|
62
|
+
useEventListener,
|
|
63
|
+
useKeyModifier,
|
|
64
|
+
useMagicKeys,
|
|
65
|
+
|
|
66
|
+
// Mouse / Pointer / Swipe
|
|
67
|
+
useDraggable,
|
|
68
|
+
useDropZone,
|
|
69
|
+
useMouse,
|
|
70
|
+
useMouseInElement,
|
|
71
|
+
useMousePressed,
|
|
72
|
+
usePointer,
|
|
73
|
+
usePointerSwipe,
|
|
74
|
+
useSwipe,
|
|
75
|
+
|
|
76
|
+
// Sensors
|
|
77
|
+
useBattery,
|
|
78
|
+
useDeviceMotion,
|
|
79
|
+
useDeviceOrientation,
|
|
80
|
+
useDevicePixelRatio,
|
|
81
|
+
useGeolocation,
|
|
82
|
+
|
|
83
|
+
// Media
|
|
84
|
+
useBreakpoints,
|
|
85
|
+
useMediaQuery,
|
|
86
|
+
breakpointsTailwind,
|
|
87
|
+
breakpointsBootstrapV5,
|
|
88
|
+
|
|
89
|
+
// Page / Navigation
|
|
90
|
+
useBrowserLocation,
|
|
91
|
+
useIdle,
|
|
92
|
+
useInfiniteScroll,
|
|
93
|
+
useNavigatorLanguage,
|
|
94
|
+
usePageLeave,
|
|
95
|
+
usePreferredLanguages,
|
|
96
|
+
useScreenOrientation,
|
|
97
|
+
useTextDirection,
|
|
98
|
+
useTextSelection,
|
|
99
|
+
useUrlSearchParams,
|
|
100
|
+
|
|
101
|
+
// Timing
|
|
102
|
+
useDebounceFn,
|
|
103
|
+
useInterval,
|
|
104
|
+
useIntervalFn,
|
|
105
|
+
useRafFn,
|
|
106
|
+
useThrottleFn,
|
|
107
|
+
useTimeout,
|
|
108
|
+
useTimeoutFn,
|
|
109
|
+
useTimeoutPoll,
|
|
110
|
+
|
|
111
|
+
// Animations
|
|
112
|
+
useAnimate,
|
|
113
|
+
useTransition,
|
|
114
|
+
TransitionPresets,
|
|
115
|
+
|
|
116
|
+
// APIs
|
|
117
|
+
useBase64,
|
|
118
|
+
useBroadcastChannel,
|
|
119
|
+
useConfirmDialog,
|
|
120
|
+
useEventBus,
|
|
121
|
+
useEventSource,
|
|
122
|
+
useEyeDropper,
|
|
123
|
+
useFavicon,
|
|
124
|
+
useFileDialog,
|
|
125
|
+
useImage,
|
|
126
|
+
useObjectUrl,
|
|
127
|
+
usePermission,
|
|
128
|
+
useShare,
|
|
129
|
+
useVibrate,
|
|
130
|
+
useWakeLock,
|
|
131
|
+
useWebNotification,
|
|
132
|
+
useWebSocket,
|
|
133
|
+
useWebWorker,
|
|
134
|
+
useWebWorkerFn,
|
|
135
|
+
|
|
136
|
+
// Utilities
|
|
137
|
+
useSupported,
|
|
138
|
+
useMemoize,
|
|
139
|
+
useMounted,
|
|
140
|
+
} from '@stacksjs/composables';
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export * from "./useAuth";
|
|
2
|
+
export * from "./useApi";
|
|
3
|
+
export {
|
|
4
|
+
useColorMode,
|
|
5
|
+
useDark,
|
|
6
|
+
usePreferredColorScheme,
|
|
7
|
+
usePreferredContrast,
|
|
8
|
+
usePreferredDark,
|
|
9
|
+
usePreferredReducedMotion,
|
|
10
|
+
useDateFormat,
|
|
11
|
+
useNow,
|
|
12
|
+
useTimeAgo,
|
|
13
|
+
useTimestamp,
|
|
14
|
+
useFetch,
|
|
15
|
+
useNetwork,
|
|
16
|
+
useOnline,
|
|
17
|
+
useLocalStorage,
|
|
18
|
+
useSessionStorage,
|
|
19
|
+
useStorage,
|
|
20
|
+
useToggle,
|
|
21
|
+
useCounter,
|
|
22
|
+
useStepper,
|
|
23
|
+
useCycleList,
|
|
24
|
+
useActiveElement,
|
|
25
|
+
useClipboard,
|
|
26
|
+
useClipboardItems,
|
|
27
|
+
useCssVar,
|
|
28
|
+
useDocumentVisibility,
|
|
29
|
+
useElementBounding,
|
|
30
|
+
useElementHover,
|
|
31
|
+
useElementSize,
|
|
32
|
+
useElementVisibility,
|
|
33
|
+
useFocus,
|
|
34
|
+
useFocusWithin,
|
|
35
|
+
useFullscreen,
|
|
36
|
+
useScroll,
|
|
37
|
+
useScrollLock,
|
|
38
|
+
useTitle,
|
|
39
|
+
useWindowFocus,
|
|
40
|
+
useWindowScroll,
|
|
41
|
+
useWindowSize,
|
|
42
|
+
onClickOutside,
|
|
43
|
+
onKeyDown,
|
|
44
|
+
onKeyStroke,
|
|
45
|
+
onKeyUp,
|
|
46
|
+
onLongPress,
|
|
47
|
+
onStartTyping,
|
|
48
|
+
useEventListener,
|
|
49
|
+
useKeyModifier,
|
|
50
|
+
useMagicKeys,
|
|
51
|
+
useDraggable,
|
|
52
|
+
useDropZone,
|
|
53
|
+
useMouse,
|
|
54
|
+
useMouseInElement,
|
|
55
|
+
useMousePressed,
|
|
56
|
+
usePointer,
|
|
57
|
+
usePointerSwipe,
|
|
58
|
+
useSwipe,
|
|
59
|
+
useBattery,
|
|
60
|
+
useDeviceMotion,
|
|
61
|
+
useDeviceOrientation,
|
|
62
|
+
useDevicePixelRatio,
|
|
63
|
+
useGeolocation,
|
|
64
|
+
useBreakpoints,
|
|
65
|
+
useMediaQuery,
|
|
66
|
+
breakpointsTailwind,
|
|
67
|
+
breakpointsBootstrapV5,
|
|
68
|
+
useBrowserLocation,
|
|
69
|
+
useIdle,
|
|
70
|
+
useInfiniteScroll,
|
|
71
|
+
useNavigatorLanguage,
|
|
72
|
+
usePageLeave,
|
|
73
|
+
usePreferredLanguages,
|
|
74
|
+
useScreenOrientation,
|
|
75
|
+
useTextDirection,
|
|
76
|
+
useTextSelection,
|
|
77
|
+
useUrlSearchParams,
|
|
78
|
+
useDebounceFn,
|
|
79
|
+
useInterval,
|
|
80
|
+
useIntervalFn,
|
|
81
|
+
useRafFn,
|
|
82
|
+
useThrottleFn,
|
|
83
|
+
useTimeout,
|
|
84
|
+
useTimeoutFn,
|
|
85
|
+
useTimeoutPoll,
|
|
86
|
+
useAnimate,
|
|
87
|
+
useTransition,
|
|
88
|
+
TransitionPresets,
|
|
89
|
+
useBase64,
|
|
90
|
+
useBroadcastChannel,
|
|
91
|
+
useConfirmDialog,
|
|
92
|
+
useEventBus,
|
|
93
|
+
useEventSource,
|
|
94
|
+
useEyeDropper,
|
|
95
|
+
useFavicon,
|
|
96
|
+
useFileDialog,
|
|
97
|
+
useImage,
|
|
98
|
+
useObjectUrl,
|
|
99
|
+
usePermission,
|
|
100
|
+
useShare,
|
|
101
|
+
useVibrate,
|
|
102
|
+
useWakeLock,
|
|
103
|
+
useWebNotification,
|
|
104
|
+
useWebSocket,
|
|
105
|
+
useWebWorker,
|
|
106
|
+
useWebWorkerFn,
|
|
107
|
+
useSupported,
|
|
108
|
+
useMemoize,
|
|
109
|
+
useMounted
|
|
110
|
+
} from "@stacksjs/composables";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Initialize the API client with custom configuration
|
|
3
|
+
*
|
|
4
|
+
* NOTE: This is now OPTIONAL. The framework auto-initializes with
|
|
5
|
+
* sensible defaults when @stacksjs/browser is imported.
|
|
6
|
+
*
|
|
7
|
+
* Only call this if you need custom configuration:
|
|
8
|
+
* - Different API base URL
|
|
9
|
+
* - Custom unauthorized handler
|
|
10
|
+
* - Custom token retrieval
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // Custom base URL
|
|
14
|
+
* initApi({ baseUrl: 'https://api.example.com' })
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Custom unauthorized handler
|
|
18
|
+
* initApi({
|
|
19
|
+
* onUnauthorized: () => showLoginModal()
|
|
20
|
+
* })
|
|
21
|
+
*/
|
|
22
|
+
export declare function initApi(options?: {
|
|
23
|
+
baseUrl?: string
|
|
24
|
+
onUnauthorized?: () => void
|
|
25
|
+
}): void;
|
|
26
|
+
/**
|
|
27
|
+
* Helper to format area size for display
|
|
28
|
+
*/
|
|
29
|
+
export declare function formatAreaSize(sqMeters: number): string;
|
|
30
|
+
/**
|
|
31
|
+
* Helper to format distance for display
|
|
32
|
+
*/
|
|
33
|
+
export declare function formatDistance(miles: number): string;
|
|
34
|
+
/**
|
|
35
|
+
* Helper to format elevation for display
|
|
36
|
+
*/
|
|
37
|
+
export declare function formatElevation(feet: number): string;
|
|
38
|
+
/**
|
|
39
|
+
* Helper to format duration for display
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatDuration(seconds: number): string;
|
|
42
|
+
/**
|
|
43
|
+
* Helper to get relative time (e.g., "2 hours ago")
|
|
44
|
+
*/
|
|
45
|
+
export declare function getRelativeTime(dateString: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Type-safe API fetch wrapper with error handling
|
|
48
|
+
*/
|
|
49
|
+
export declare function fetchData<T>(fetcher: () => Promise<T>, options?: {
|
|
50
|
+
onError?: (error: Error) => void
|
|
51
|
+
fallback?: T
|
|
52
|
+
}): Promise<T | undefined>;
|
|
53
|
+
/**
|
|
54
|
+
* Auth utilities
|
|
55
|
+
*/
|
|
56
|
+
export declare const auth: unknown;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { configureBrowser, browserAuth } from "bun-query-builder/browser";
|
|
2
|
+
export function initApi(options) {
|
|
3
|
+
const stacksConfig = globalThis.__STACKS_CONFIG__ || {}, baseUrl = options?.baseUrl || (typeof window < "u" ? `${window.location.origin}/api` : `http://localhost:${process.env.PORT_API || "3008"}/api`);
|
|
4
|
+
configureBrowser({
|
|
5
|
+
baseUrl,
|
|
6
|
+
getToken: () => {
|
|
7
|
+
if (typeof localStorage < "u")
|
|
8
|
+
return localStorage.getItem("token");
|
|
9
|
+
return null;
|
|
10
|
+
},
|
|
11
|
+
onUnauthorized: options?.onUnauthorized || (() => {
|
|
12
|
+
if (typeof window < "u")
|
|
13
|
+
window.location.href = "/login";
|
|
14
|
+
}),
|
|
15
|
+
transformResponse: (data) => {
|
|
16
|
+
return data?.data ?? data;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export const auth = browserAuth;
|
|
21
|
+
export function formatAreaSize(sqMeters) {
|
|
22
|
+
if (sqMeters >= 1e6)
|
|
23
|
+
return `${(sqMeters / 1e6).toFixed(2)} km\xB2`;
|
|
24
|
+
if (sqMeters >= 1e4)
|
|
25
|
+
return `${(sqMeters / 1e4).toFixed(2)} ha`;
|
|
26
|
+
return `${sqMeters.toLocaleString()} m\xB2`;
|
|
27
|
+
}
|
|
28
|
+
export function formatDistance(miles) {
|
|
29
|
+
return `${miles.toFixed(1)} mi`;
|
|
30
|
+
}
|
|
31
|
+
export function formatElevation(feet) {
|
|
32
|
+
return `${feet.toLocaleString()} ft`;
|
|
33
|
+
}
|
|
34
|
+
export function formatDuration(seconds) {
|
|
35
|
+
const hours = Math.floor(seconds / 3600), minutes = Math.floor(seconds % 3600 / 60), secs = seconds % 60;
|
|
36
|
+
if (hours > 0)
|
|
37
|
+
return `${hours}h ${minutes}m`;
|
|
38
|
+
if (minutes > 0)
|
|
39
|
+
return `${minutes}m ${secs}s`;
|
|
40
|
+
return `${secs}s`;
|
|
41
|
+
}
|
|
42
|
+
export function getRelativeTime(dateString) {
|
|
43
|
+
const date = new Date(dateString), diffMs = new Date().getTime() - date.getTime(), diffSeconds = Math.floor(diffMs / 1000), diffMinutes = Math.floor(diffSeconds / 60), diffHours = Math.floor(diffMinutes / 60), diffDays = Math.floor(diffHours / 24), diffWeeks = Math.floor(diffDays / 7);
|
|
44
|
+
if (diffSeconds < 60)
|
|
45
|
+
return "just now";
|
|
46
|
+
if (diffMinutes < 60)
|
|
47
|
+
return `${diffMinutes} minute${diffMinutes === 1 ? "" : "s"} ago`;
|
|
48
|
+
if (diffHours < 24)
|
|
49
|
+
return `${diffHours} hour${diffHours === 1 ? "" : "s"} ago`;
|
|
50
|
+
if (diffDays < 7)
|
|
51
|
+
return `${diffDays} day${diffDays === 1 ? "" : "s"} ago`;
|
|
52
|
+
if (diffWeeks < 4)
|
|
53
|
+
return `${diffWeeks} week${diffWeeks === 1 ? "" : "s"} ago`;
|
|
54
|
+
return date.toLocaleDateString();
|
|
55
|
+
}
|
|
56
|
+
export async function fetchData(fetcher, options) {
|
|
57
|
+
try {
|
|
58
|
+
return await fetcher();
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (options?.onError)
|
|
61
|
+
options.onError(error);
|
|
62
|
+
else
|
|
63
|
+
console.error("API Error:", error);
|
|
64
|
+
return options?.fallback;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { AuthComposable } from '../types/dashboard';
|
|
2
|
+
export declare function useAuth(): AuthComposable;
|
|
3
|
+
// Strict auth guard middleware
|
|
4
|
+
// Usage: call in setup() of page/component, or in router beforeEach
|
|
5
|
+
// Pass { guest: true } for guest-only pages
|
|
6
|
+
export declare function authGuard(options?: { guest?: boolean }): void;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { useStorage } from "@stacksjs/composables";
|
|
2
|
+
import { ref } from "@stacksjs/stx";
|
|
3
|
+
const token = useStorage("token", ""), stacksConfig = globalThis.__STACKS_CONFIG__ || {}, baseUrl = stacksConfig.API_URL || (typeof window < "u" ? window.location.origin : `http://localhost:${process.env.PORT_API || "3008"}`), user = ref(null), isAuthenticated = ref(!1);
|
|
4
|
+
export function useAuth() {
|
|
5
|
+
async function fetchAuthUser() {
|
|
6
|
+
try {
|
|
7
|
+
if (!token.value) {
|
|
8
|
+
isAuthenticated.value = !1;
|
|
9
|
+
user.value = null;
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const response = await fetch(`${baseUrl}/me`, {
|
|
13
|
+
headers: {
|
|
14
|
+
Authorization: `Bearer ${token.value}`,
|
|
15
|
+
Accept: "application/json"
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
token.value = "";
|
|
20
|
+
isAuthenticated.value = !1;
|
|
21
|
+
user.value = null;
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const data = await response.json();
|
|
25
|
+
user.value = data;
|
|
26
|
+
isAuthenticated.value = !0;
|
|
27
|
+
return data;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error("Error fetching user:", error);
|
|
30
|
+
token.value = "";
|
|
31
|
+
isAuthenticated.value = !1;
|
|
32
|
+
user.value = null;
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function checkAuthentication() {
|
|
37
|
+
try {
|
|
38
|
+
return await fetchAuthUser() !== null;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error("Error checking authentication:", error);
|
|
41
|
+
return !1;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function register(user) {
|
|
45
|
+
const url = `${baseUrl}/register`, data = await (await fetch(url, {
|
|
46
|
+
method: "POST",
|
|
47
|
+
headers: {
|
|
48
|
+
"Content-Type": "application/json"
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify(user)
|
|
51
|
+
})).json();
|
|
52
|
+
if (isRegisterError(data))
|
|
53
|
+
return data;
|
|
54
|
+
if (isRegisterResponse(data)) {
|
|
55
|
+
token.value = data.token;
|
|
56
|
+
return data;
|
|
57
|
+
}
|
|
58
|
+
return data;
|
|
59
|
+
}
|
|
60
|
+
function isRegisterError(data) {
|
|
61
|
+
return "errors" in data;
|
|
62
|
+
}
|
|
63
|
+
function isRegisterResponse(data) {
|
|
64
|
+
return "token" in data && "user" in data;
|
|
65
|
+
}
|
|
66
|
+
async function login(user) {
|
|
67
|
+
try {
|
|
68
|
+
const url = `${baseUrl}/login`, response = await fetch(url, {
|
|
69
|
+
method: "POST",
|
|
70
|
+
headers: {
|
|
71
|
+
"Content-Type": "application/json"
|
|
72
|
+
},
|
|
73
|
+
body: JSON.stringify(user)
|
|
74
|
+
});
|
|
75
|
+
if (!response.ok) {
|
|
76
|
+
const errorData = await response.json();
|
|
77
|
+
throw Error(errorData.message || `HTTP error! status: ${response.status}`);
|
|
78
|
+
}
|
|
79
|
+
const data = await response.json();
|
|
80
|
+
token.value = data.token;
|
|
81
|
+
await fetchAuthUser();
|
|
82
|
+
return data;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
return error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function logout() {
|
|
88
|
+
try {
|
|
89
|
+
const currentToken = token.value;
|
|
90
|
+
if (currentToken)
|
|
91
|
+
await fetch(`${baseUrl}/logout`, {
|
|
92
|
+
method: "POST",
|
|
93
|
+
headers: {
|
|
94
|
+
Authorization: `Bearer ${currentToken}`,
|
|
95
|
+
Accept: "application/json"
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error("Error during logout:", error);
|
|
100
|
+
} finally {
|
|
101
|
+
token.value = "";
|
|
102
|
+
user.value = null;
|
|
103
|
+
isAuthenticated.value = !1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
user,
|
|
108
|
+
isAuthenticated,
|
|
109
|
+
token,
|
|
110
|
+
getToken: () => token.value,
|
|
111
|
+
register,
|
|
112
|
+
login,
|
|
113
|
+
logout,
|
|
114
|
+
fetchAuthUser,
|
|
115
|
+
checkAuthentication
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
export function authGuard(options = {}) {
|
|
119
|
+
const guest = options.guest ?? !1, { isAuthenticated } = useAuth();
|
|
120
|
+
if (guest) {
|
|
121
|
+
if (isAuthenticated.value)
|
|
122
|
+
window.location.replace("/");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (!isAuthenticated.value)
|
|
126
|
+
window.location.replace("/login");
|
|
127
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useFetch } from '@stacksjs/composables';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useFetch } from "@stacksjs/composables";
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import './auto-init';
|
|
2
|
+
export type {
|
|
3
|
+
BrowserModelDefinition,
|
|
4
|
+
BrowserTypedAttribute,
|
|
5
|
+
BrowserModelInstance,
|
|
6
|
+
BrowserModelQueryBuilder,
|
|
7
|
+
} from 'bun-query-builder/browser';
|
|
8
|
+
// `BrowserConfig` lives in the main `bun-query-builder` types entry, not the
|
|
9
|
+
// `/browser` subpath — re-export it from there. The runtime code in this file
|
|
10
|
+
// uses only the `/browser` entry, so server-only code paths are still avoided.
|
|
11
|
+
export type { BrowserConfig } from 'bun-query-builder';
|
|
12
|
+
export * from './composables/index';
|
|
13
|
+
export * from './utils/index';
|
|
14
|
+
export * from './model-loader';
|
|
15
|
+
// Re-export browser query builder from bun-query-builder.
|
|
16
|
+
//
|
|
17
|
+
// Pull from the dedicated `/browser` subpath instead of the main entry —
|
|
18
|
+
// the main entry pulls in `bun:sqlite`, `child_process`, `stream/promises`,
|
|
19
|
+
// etc. through server-side code branches, which the browser bundler can't
|
|
20
|
+
// elide. The package's own `./browser` export is purpose-built for this:
|
|
21
|
+
// only browser-safe code, no Node/Bun builtins.
|
|
22
|
+
export {
|
|
23
|
+
browserQuery,
|
|
24
|
+
BrowserQueryBuilder,
|
|
25
|
+
BrowserQueryError,
|
|
26
|
+
browserAuth,
|
|
27
|
+
configureBrowser,
|
|
28
|
+
getBrowserConfig,
|
|
29
|
+
createBrowserDb,
|
|
30
|
+
createBrowserModel,
|
|
31
|
+
isBrowser,
|
|
32
|
+
} from 'bun-query-builder/browser';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import"./auto-init";
|
|
2
|
+
|
|
3
|
+
export * from "./composables";
|
|
4
|
+
export * from "./utils";
|
|
5
|
+
export * from "./model-loader";
|
|
6
|
+
export {
|
|
7
|
+
browserQuery,
|
|
8
|
+
BrowserQueryBuilder,
|
|
9
|
+
BrowserQueryError,
|
|
10
|
+
browserAuth,
|
|
11
|
+
configureBrowser,
|
|
12
|
+
getBrowserConfig,
|
|
13
|
+
createBrowserDb,
|
|
14
|
+
createBrowserModel,
|
|
15
|
+
isBrowser
|
|
16
|
+
} from "bun-query-builder/browser";
|