@stacksjs/browser 0.70.257 → 0.70.259
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.js +1 -66
- package/dist/composables/auth/useGithub.js +1 -41
- package/dist/composables/csrf.js +1 -24
- package/dist/composables/index.js +1 -112
- package/dist/composables/request-error.js +1 -90
- package/dist/composables/useApi.js +1 -66
- package/dist/composables/useAuth.js +1 -130
- package/dist/composables/useFetch.js +1 -1
- package/dist/composables/useStorage.js +1 -1
- package/dist/index.js +1 -16
- package/dist/model-loader.js +1 -61
- package/dist/types/dashboard.js +1 -3
- package/dist/utils/base.js +1 -4
- package/dist/utils/billable.js +1 -72
- package/dist/utils/date.js +1 -2
- package/dist/utils/debounce.js +1 -67
- package/dist/utils/fetch.js +1 -94
- package/dist/utils/function.js +1 -7
- package/dist/utils/guards.js +1 -12
- package/dist/utils/index.js +1 -17
- package/dist/utils/lazy.js +1 -9
- package/dist/utils/math.js +1 -23
- package/dist/utils/plans.js +1 -135
- package/dist/utils/promise.js +1 -55
- package/dist/utils/random.js +1 -67
- package/dist/utils/regex.js +1 -3
- package/dist/utils/retry.js +1 -28
- package/dist/utils/sleep.js +1 -59
- package/dist/utils/throttle.js +1 -18
- package/dist/utils/vendors.js +1 -37
- package/package.json +3 -3
package/dist/auto-init.js
CHANGED
|
@@ -1,66 +1 @@
|
|
|
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 * as Utils from "./utils";
|
|
14
|
-
import { loadBrowserModels } from "./model-loader";
|
|
15
|
-
let isInitialized = !1;
|
|
16
|
-
export function getBrowserAutoImports() {
|
|
17
|
-
return {
|
|
18
|
-
browserQuery,
|
|
19
|
-
BrowserQueryBuilder,
|
|
20
|
-
BrowserQueryError,
|
|
21
|
-
browserAuth,
|
|
22
|
-
configureBrowser,
|
|
23
|
-
getBrowserConfig,
|
|
24
|
-
createBrowserDb,
|
|
25
|
-
createBrowserModel,
|
|
26
|
-
isBrowser,
|
|
27
|
-
...Utils,
|
|
28
|
-
...Composables
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
function autoInit() {
|
|
32
|
-
if (isInitialized)
|
|
33
|
-
return;
|
|
34
|
-
if (typeof window > "u")
|
|
35
|
-
return;
|
|
36
|
-
if (typeof document > "u")
|
|
37
|
-
return;
|
|
38
|
-
isInitialized = !0;
|
|
39
|
-
const baseUrl = window.__STACKS_API_URL__ || `${window.location.origin}/api`;
|
|
40
|
-
configureBrowser({
|
|
41
|
-
baseUrl,
|
|
42
|
-
getToken: () => {
|
|
43
|
-
try {
|
|
44
|
-
return localStorage.getItem("token");
|
|
45
|
-
} catch {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
onUnauthorized: () => {
|
|
50
|
-
const loginUrl = window.__STACKS_LOGIN_URL__ || "/login";
|
|
51
|
-
if (!window.location.pathname.startsWith(loginUrl))
|
|
52
|
-
window.location.href = loginUrl;
|
|
53
|
-
},
|
|
54
|
-
transformResponse: (data) => {
|
|
55
|
-
return data?.data ?? data;
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
window.StacksBrowser = getBrowserAutoImports();
|
|
59
|
-
loadBrowserModels();
|
|
60
|
-
window.dispatchEvent(new CustomEvent("stacks:api-ready"));
|
|
61
|
-
if (window.__STACKS_DEBUG__)
|
|
62
|
-
console.debug("[Stacks] Browser API initialized:", baseUrl);
|
|
63
|
-
}
|
|
64
|
-
autoInit();
|
|
65
|
-
|
|
66
|
-
export { autoInit, isInitialized };
|
|
1
|
+
import{browserQuery,BrowserQueryBuilder,BrowserQueryError,browserAuth,configureBrowser,getBrowserConfig,createBrowserDb,createBrowserModel,isBrowser}from"bun-query-builder/browser";import*as Composables from"./composables";import*as Utils from"./utils";import{loadBrowserModels}from"./model-loader";let isInitialized=!1;export function getBrowserAutoImports(){return{browserQuery,BrowserQueryBuilder,BrowserQueryError,browserAuth,configureBrowser,getBrowserConfig,createBrowserDb,createBrowserModel,isBrowser,...Utils,...Composables}}function autoInit(){if(isInitialized)return;if(typeof window>"u")return;if(typeof document>"u")return;isInitialized=!0;const baseUrl=window.__STACKS_API_URL__||`${window.location.origin}/api`;configureBrowser({baseUrl,getToken:()=>{try{return localStorage.getItem("token")}catch{return null}},onUnauthorized:()=>{const loginUrl=window.__STACKS_LOGIN_URL__||"/login";if(!window.location.pathname.startsWith(loginUrl))window.location.href=loginUrl},transformResponse:(data)=>{return data?.data??data}});window.StacksBrowser=getBrowserAutoImports();loadBrowserModels();window.dispatchEvent(new CustomEvent("stacks:api-ready"));if(window.__STACKS_DEBUG__)console.debug("[Stacks] Browser API initialized:",baseUrl)}autoInit();export{autoInit,isInitialized};
|
|
@@ -1,41 +1 @@
|
|
|
1
|
-
import
|
|
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
|
-
}
|
|
1
|
+
import{onMounted,ref}from"@stacksjs/stx";export function useGitHubOAuth(){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);function login(){const githubAuthUrl=`https://github.com/login/oauth/authorize?client_id=${clientId}&redirect_uri=${encodeURIComponent(redirectUri)}&scope=read:user user:email`;window.location.href=githubAuthUrl}async function fetchAccessToken(code){isLoading.value=!0;error.value=null;try{const apiUrl=stacksConfig.APP_URL||window.location.origin,res=await fetch(`${apiUrl}/api/github/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code})});if(!res.ok)throw Error("Failed to exchange code for access token");const data=await res.json();accessToken.value=data.access_token}catch(err){error.value=err.message||"Unknown error"}finally{isLoading.value=!1}}function handleCallback(){const code=new URLSearchParams(window.location.search).get("code");if(code)fetchAccessToken(code)}onMounted(()=>{handleCallback()});return{login,accessToken,error,isLoading}}
|
package/dist/composables/csrf.js
CHANGED
|
@@ -1,24 +1 @@
|
|
|
1
|
-
export const CSRF_COOKIE_NAME
|
|
2
|
-
export function readCsrfToken(cookieSource) {
|
|
3
|
-
const raw = cookieSource ?? (typeof document < "u" ? document.cookie : "");
|
|
4
|
-
if (!raw)
|
|
5
|
-
return null;
|
|
6
|
-
for (const part of raw.split(";")) {
|
|
7
|
-
const separator = part.indexOf("=");
|
|
8
|
-
if (separator === -1)
|
|
9
|
-
continue;
|
|
10
|
-
if (part.slice(0, separator).trim() !== CSRF_COOKIE_NAME)
|
|
11
|
-
continue;
|
|
12
|
-
const value = part.slice(separator + 1).trim();
|
|
13
|
-
return value ? decodeURIComponent(value) : null;
|
|
14
|
-
}
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
export function withCsrfHeader(headers = {}, cookieSource) {
|
|
18
|
-
if (headers[CSRF_COOKIE_NAME])
|
|
19
|
-
return headers;
|
|
20
|
-
const token = readCsrfToken(cookieSource);
|
|
21
|
-
if (!token)
|
|
22
|
-
return headers;
|
|
23
|
-
return { ...headers, [CSRF_COOKIE_NAME]: token };
|
|
24
|
-
}
|
|
1
|
+
export const CSRF_COOKIE_NAME="X-CSRF-Token";export function readCsrfToken(cookieSource){const raw=cookieSource??(typeof document<"u"?document.cookie:"");if(!raw)return null;for(const part of raw.split(";")){const separator=part.indexOf("=");if(separator===-1)continue;if(part.slice(0,separator).trim()!==CSRF_COOKIE_NAME)continue;const value=part.slice(separator+1).trim();return value?decodeURIComponent(value):null}return null}export function withCsrfHeader(headers={},cookieSource){if(headers[CSRF_COOKIE_NAME])return headers;const token=readCsrfToken(cookieSource);if(!token)return headers;return{...headers,[CSRF_COOKIE_NAME]:token}}
|
|
@@ -1,112 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export * from "./request-error";
|
|
3
|
-
export * from "./useAuth";
|
|
4
|
-
export * from "./useApi";
|
|
5
|
-
export {
|
|
6
|
-
useColorMode,
|
|
7
|
-
useDark,
|
|
8
|
-
usePreferredColorScheme,
|
|
9
|
-
usePreferredContrast,
|
|
10
|
-
usePreferredDark,
|
|
11
|
-
usePreferredReducedMotion,
|
|
12
|
-
useDateFormat,
|
|
13
|
-
useNow,
|
|
14
|
-
useTimeAgo,
|
|
15
|
-
useTimestamp,
|
|
16
|
-
useFetch,
|
|
17
|
-
useNetwork,
|
|
18
|
-
useOnline,
|
|
19
|
-
useLocalStorage,
|
|
20
|
-
useSessionStorage,
|
|
21
|
-
useStorage,
|
|
22
|
-
useToggle,
|
|
23
|
-
useCounter,
|
|
24
|
-
useStepper,
|
|
25
|
-
useCycleList,
|
|
26
|
-
useActiveElement,
|
|
27
|
-
useClipboard,
|
|
28
|
-
useClipboardItems,
|
|
29
|
-
useCssVar,
|
|
30
|
-
useDocumentVisibility,
|
|
31
|
-
useElementBounding,
|
|
32
|
-
useElementHover,
|
|
33
|
-
useElementSize,
|
|
34
|
-
useElementVisibility,
|
|
35
|
-
useFocus,
|
|
36
|
-
useFocusWithin,
|
|
37
|
-
useFullscreen,
|
|
38
|
-
useScroll,
|
|
39
|
-
useScrollLock,
|
|
40
|
-
useTitle,
|
|
41
|
-
useWindowFocus,
|
|
42
|
-
useWindowScroll,
|
|
43
|
-
useWindowSize,
|
|
44
|
-
onClickOutside,
|
|
45
|
-
onKeyDown,
|
|
46
|
-
onKeyStroke,
|
|
47
|
-
onKeyUp,
|
|
48
|
-
onLongPress,
|
|
49
|
-
onStartTyping,
|
|
50
|
-
useEventListener,
|
|
51
|
-
useKeyModifier,
|
|
52
|
-
useMagicKeys,
|
|
53
|
-
useDraggable,
|
|
54
|
-
useDropZone,
|
|
55
|
-
useMouse,
|
|
56
|
-
useMouseInElement,
|
|
57
|
-
useMousePressed,
|
|
58
|
-
usePointer,
|
|
59
|
-
usePointerSwipe,
|
|
60
|
-
useSwipe,
|
|
61
|
-
useBattery,
|
|
62
|
-
useDeviceMotion,
|
|
63
|
-
useDeviceOrientation,
|
|
64
|
-
useDevicePixelRatio,
|
|
65
|
-
useGeolocation,
|
|
66
|
-
useBreakpoints,
|
|
67
|
-
useMediaQuery,
|
|
68
|
-
breakpointsTailwind,
|
|
69
|
-
breakpointsBootstrapV5,
|
|
70
|
-
useBrowserLocation,
|
|
71
|
-
useIdle,
|
|
72
|
-
useInfiniteScroll,
|
|
73
|
-
useNavigatorLanguage,
|
|
74
|
-
usePageLeave,
|
|
75
|
-
usePreferredLanguages,
|
|
76
|
-
useScreenOrientation,
|
|
77
|
-
useTextDirection,
|
|
78
|
-
useTextSelection,
|
|
79
|
-
useUrlSearchParams,
|
|
80
|
-
useDebounceFn,
|
|
81
|
-
useInterval,
|
|
82
|
-
useIntervalFn,
|
|
83
|
-
useRafFn,
|
|
84
|
-
useThrottleFn,
|
|
85
|
-
useTimeout,
|
|
86
|
-
useTimeoutFn,
|
|
87
|
-
useTimeoutPoll,
|
|
88
|
-
useAnimate,
|
|
89
|
-
useTransition,
|
|
90
|
-
TransitionPresets,
|
|
91
|
-
useBase64,
|
|
92
|
-
useBroadcastChannel,
|
|
93
|
-
useConfirmDialog,
|
|
94
|
-
useEventBus,
|
|
95
|
-
useEventSource,
|
|
96
|
-
useEyeDropper,
|
|
97
|
-
useFavicon,
|
|
98
|
-
useFileDialog,
|
|
99
|
-
useImage,
|
|
100
|
-
useObjectUrl,
|
|
101
|
-
usePermission,
|
|
102
|
-
useShare,
|
|
103
|
-
useVibrate,
|
|
104
|
-
useWakeLock,
|
|
105
|
-
useWebNotification,
|
|
106
|
-
useWebSocket,
|
|
107
|
-
useWebWorker,
|
|
108
|
-
useWebWorkerFn,
|
|
109
|
-
useSupported,
|
|
110
|
-
useMemoize,
|
|
111
|
-
useMounted
|
|
112
|
-
} from "@stacksjs/composables";
|
|
1
|
+
export*from"./csrf";export*from"./request-error";export*from"./useAuth";export*from"./useApi";export{useColorMode,useDark,usePreferredColorScheme,usePreferredContrast,usePreferredDark,usePreferredReducedMotion,useDateFormat,useNow,useTimeAgo,useTimestamp,useFetch,useNetwork,useOnline,useLocalStorage,useSessionStorage,useStorage,useToggle,useCounter,useStepper,useCycleList,useActiveElement,useClipboard,useClipboardItems,useCssVar,useDocumentVisibility,useElementBounding,useElementHover,useElementSize,useElementVisibility,useFocus,useFocusWithin,useFullscreen,useScroll,useScrollLock,useTitle,useWindowFocus,useWindowScroll,useWindowSize,onClickOutside,onKeyDown,onKeyStroke,onKeyUp,onLongPress,onStartTyping,useEventListener,useKeyModifier,useMagicKeys,useDraggable,useDropZone,useMouse,useMouseInElement,useMousePressed,usePointer,usePointerSwipe,useSwipe,useBattery,useDeviceMotion,useDeviceOrientation,useDevicePixelRatio,useGeolocation,useBreakpoints,useMediaQuery,breakpointsTailwind,breakpointsBootstrapV5,useBrowserLocation,useIdle,useInfiniteScroll,useNavigatorLanguage,usePageLeave,usePreferredLanguages,useScreenOrientation,useTextDirection,useTextSelection,useUrlSearchParams,useDebounceFn,useInterval,useIntervalFn,useRafFn,useThrottleFn,useTimeout,useTimeoutFn,useTimeoutPoll,useAnimate,useTransition,TransitionPresets,useBase64,useBroadcastChannel,useConfirmDialog,useEventBus,useEventSource,useEyeDropper,useFavicon,useFileDialog,useImage,useObjectUrl,usePermission,useShare,useVibrate,useWakeLock,useWebNotification,useWebSocket,useWebWorker,useWebWorkerFn,useSupported,useMemoize,useMounted}from"@stacksjs/composables";
|
|
@@ -1,90 +1 @@
|
|
|
1
|
-
function isRuntimeDefect(error) {
|
|
2
|
-
return error instanceof ReferenceError || error instanceof SyntaxError || error instanceof RangeError || error instanceof TypeError && !isNetworkFailure(error);
|
|
3
|
-
}
|
|
4
|
-
function isNetworkFailure(error) {
|
|
5
|
-
if (!(error instanceof TypeError))
|
|
6
|
-
return !1;
|
|
7
|
-
const message = error.message.toLowerCase();
|
|
8
|
-
return message.includes("fetch") || message.includes("network") || message.includes("load failed") || message.includes("connection");
|
|
9
|
-
}
|
|
10
|
-
function flattenFieldErrors(errors) {
|
|
11
|
-
if (!errors || typeof errors !== "object")
|
|
12
|
-
return;
|
|
13
|
-
const out = {};
|
|
14
|
-
for (const [field, value] of Object.entries(errors)) {
|
|
15
|
-
const first = Array.isArray(value) ? value[0] : value;
|
|
16
|
-
if (typeof first === "string" && first.trim())
|
|
17
|
-
out[field] = first.trim();
|
|
18
|
-
}
|
|
19
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
20
|
-
}
|
|
21
|
-
const UNHELPFUL_LABELS = new Set([
|
|
22
|
-
"validation failed",
|
|
23
|
-
"forbidden",
|
|
24
|
-
"unauthorized",
|
|
25
|
-
"unauthenticated",
|
|
26
|
-
"bad request",
|
|
27
|
-
"not found",
|
|
28
|
-
"internal server error",
|
|
29
|
-
"error",
|
|
30
|
-
"csrf token mismatch",
|
|
31
|
-
"invalid csrf token",
|
|
32
|
-
"token mismatch"
|
|
33
|
-
]);
|
|
34
|
-
function usableMessage(body) {
|
|
35
|
-
for (const candidate of [body?.message, body?.error]) {
|
|
36
|
-
if (typeof candidate !== "string")
|
|
37
|
-
continue;
|
|
38
|
-
const trimmed = candidate.trim();
|
|
39
|
-
if (trimmed && !UNHELPFUL_LABELS.has(trimmed.toLowerCase()))
|
|
40
|
-
return trimmed;
|
|
41
|
-
}
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
function messageForStatus(status, fields) {
|
|
45
|
-
if (status === 401)
|
|
46
|
-
return { message: "Those details did not match an account. Check them and try again.", unexpected: !1 };
|
|
47
|
-
if (status === 403)
|
|
48
|
-
return { message: "Your session expired. Refresh the page and try again.", unexpected: !1 };
|
|
49
|
-
if (status === 404)
|
|
50
|
-
return { message: "That is not available anymore.", unexpected: !1 };
|
|
51
|
-
if (status === 409)
|
|
52
|
-
return { message: "That conflicts with something that already exists.", unexpected: !1 };
|
|
53
|
-
if (status === 422)
|
|
54
|
-
return { message: (fields && Object.keys(fields).length === 1 ? Object.values(fields)[0] : void 0) ?? "Some details need fixing before this can be saved.", unexpected: !1 };
|
|
55
|
-
if (status === 429)
|
|
56
|
-
return { message: "Too many attempts. Wait a moment, then try again.", unexpected: !1 };
|
|
57
|
-
if (status >= 500)
|
|
58
|
-
return { message: "Something went wrong on our end. Try again in a moment.", unexpected: !0 };
|
|
59
|
-
return { message: "That request could not be completed. Try again.", unexpected: status >= 500 };
|
|
60
|
-
}
|
|
61
|
-
export function describeResponseError(status, body) {
|
|
62
|
-
const fields = flattenFieldErrors(body?.errors), fromBody = usableMessage(body ?? void 0), fallback = messageForStatus(status, fields);
|
|
63
|
-
return {
|
|
64
|
-
message: fromBody ?? fallback.message,
|
|
65
|
-
...fields && { fields },
|
|
66
|
-
unexpected: fallback.unexpected && !fromBody,
|
|
67
|
-
cause: body ?? void 0
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
export function describeThrownError(error) {
|
|
71
|
-
if (isNetworkFailure(error))
|
|
72
|
-
return {
|
|
73
|
-
message: "Could not reach the server. Check your connection and try again.",
|
|
74
|
-
unexpected: !1,
|
|
75
|
-
cause: error
|
|
76
|
-
};
|
|
77
|
-
if (isRuntimeDefect(error))
|
|
78
|
-
return {
|
|
79
|
-
message: "Something went wrong on our end. Try again in a moment.",
|
|
80
|
-
unexpected: !0,
|
|
81
|
-
cause: error
|
|
82
|
-
};
|
|
83
|
-
if (error instanceof Error && error.message.trim())
|
|
84
|
-
return { message: error.message.trim(), unexpected: !1, cause: error };
|
|
85
|
-
return {
|
|
86
|
-
message: "Something went wrong. Try again in a moment.",
|
|
87
|
-
unexpected: !0,
|
|
88
|
-
cause: error
|
|
89
|
-
};
|
|
90
|
-
}
|
|
1
|
+
function isRuntimeDefect(error){return error instanceof ReferenceError||error instanceof SyntaxError||error instanceof RangeError||error instanceof TypeError&&!isNetworkFailure(error)}function isNetworkFailure(error){if(!(error instanceof TypeError))return!1;const message=error.message.toLowerCase();return message.includes("fetch")||message.includes("network")||message.includes("load failed")||message.includes("connection")}function flattenFieldErrors(errors){if(!errors||typeof errors!=="object")return;const out={};for(const[field,value]of Object.entries(errors)){const first=Array.isArray(value)?value[0]:value;if(typeof first==="string"&&first.trim())out[field]=first.trim()}return Object.keys(out).length>0?out:void 0}const UNHELPFUL_LABELS=new Set(["validation failed","forbidden","unauthorized","unauthenticated","bad request","not found","internal server error","error","csrf token mismatch","invalid csrf token","token mismatch"]);function usableMessage(body){for(const candidate of[body?.message,body?.error]){if(typeof candidate!=="string")continue;const trimmed=candidate.trim();if(trimmed&&!UNHELPFUL_LABELS.has(trimmed.toLowerCase()))return trimmed}return}function messageForStatus(status,fields){if(status===401)return{message:"Those details did not match an account. Check them and try again.",unexpected:!1};if(status===403)return{message:"Your session expired. Refresh the page and try again.",unexpected:!1};if(status===404)return{message:"That is not available anymore.",unexpected:!1};if(status===409)return{message:"That conflicts with something that already exists.",unexpected:!1};if(status===422)return{message:(fields&&Object.keys(fields).length===1?Object.values(fields)[0]:void 0)??"Some details need fixing before this can be saved.",unexpected:!1};if(status===429)return{message:"Too many attempts. Wait a moment, then try again.",unexpected:!1};if(status>=500)return{message:"Something went wrong on our end. Try again in a moment.",unexpected:!0};return{message:"That request could not be completed. Try again.",unexpected:status>=500}}export function describeResponseError(status,body){const fields=flattenFieldErrors(body?.errors),fromBody=usableMessage(body??void 0),fallback=messageForStatus(status,fields);return{message:fromBody??fallback.message,...fields&&{fields},unexpected:fallback.unexpected&&!fromBody,cause:body??void 0}}export function describeThrownError(error){if(isNetworkFailure(error))return{message:"Could not reach the server. Check your connection and try again.",unexpected:!1,cause:error};if(isRuntimeDefect(error))return{message:"Something went wrong on our end. Try again in a moment.",unexpected:!0,cause:error};if(error instanceof Error&&error.message.trim())return{message:error.message.trim(),unexpected:!1,cause:error};return{message:"Something went wrong. Try again in a moment.",unexpected:!0,cause:error}}
|
|
@@ -1,66 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export function initApi(options) {
|
|
3
|
-
const stacksConfig = globalThis.__STACKS_CONFIG__ || {}, baseUrl = options?.baseUrl || (typeof window < "u" ? `${window.location.origin}/api` : "/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
|
-
}
|
|
1
|
+
import{configureBrowser,browserAuth}from"bun-query-builder/browser";export function initApi(options){const stacksConfig=globalThis.__STACKS_CONFIG__||{},baseUrl=options?.baseUrl||(typeof window<"u"?`${window.location.origin}/api`:"/api");configureBrowser({baseUrl,getToken:()=>{if(typeof localStorage<"u")return localStorage.getItem("token");return null},onUnauthorized:options?.onUnauthorized||(()=>{if(typeof window<"u")window.location.href="/login"}),transformResponse:(data)=>{return data?.data??data}})}export const auth=browserAuth;export function formatAreaSize(sqMeters){if(sqMeters>=1e6)return`${(sqMeters/1e6).toFixed(2)} km\xB2`;if(sqMeters>=1e4)return`${(sqMeters/1e4).toFixed(2)} ha`;return`${sqMeters.toLocaleString()} m\xB2`}export function formatDistance(miles){return`${miles.toFixed(1)} mi`}export function formatElevation(feet){return`${feet.toLocaleString()} ft`}export function formatDuration(seconds){const hours=Math.floor(seconds/3600),minutes=Math.floor(seconds%3600/60),secs=seconds%60;if(hours>0)return`${hours}h ${minutes}m`;if(minutes>0)return`${minutes}m ${secs}s`;return`${secs}s`}export function getRelativeTime(dateString){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);if(diffSeconds<60)return"just now";if(diffMinutes<60)return`${diffMinutes} minute${diffMinutes===1?"":"s"} ago`;if(diffHours<24)return`${diffHours} hour${diffHours===1?"":"s"} ago`;if(diffDays<7)return`${diffDays} day${diffDays===1?"":"s"} ago`;if(diffWeeks<4)return`${diffWeeks} week${diffWeeks===1?"":"s"} ago`;return date.toLocaleDateString()}export async function fetchData(fetcher,options){try{return await fetcher()}catch(error){if(options?.onError)options.onError(error);else console.error("API Error:",error);return options?.fallback}}
|
|
@@ -1,130 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ref } from "@stacksjs/stx";
|
|
3
|
-
import { withCsrfHeader } from "./csrf";
|
|
4
|
-
const token = useStorage("token", ""), stacksConfig = globalThis.__STACKS_CONFIG__ || {}, baseUrl = stacksConfig.API_URL || (typeof window < "u" ? window.location.origin : ""), user = ref(null), isAuthenticated = ref(!1);
|
|
5
|
-
export function useAuth() {
|
|
6
|
-
async function fetchAuthUser() {
|
|
7
|
-
try {
|
|
8
|
-
if (!token.value) {
|
|
9
|
-
isAuthenticated.value = !1;
|
|
10
|
-
user.value = null;
|
|
11
|
-
return null;
|
|
12
|
-
}
|
|
13
|
-
const response = await fetch(`${baseUrl}/me`, {
|
|
14
|
-
headers: {
|
|
15
|
-
Authorization: `Bearer ${token.value}`,
|
|
16
|
-
Accept: "application/json"
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
if (!response.ok) {
|
|
20
|
-
token.value = "";
|
|
21
|
-
isAuthenticated.value = !1;
|
|
22
|
-
user.value = null;
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
const data = await response.json();
|
|
26
|
-
user.value = data;
|
|
27
|
-
isAuthenticated.value = !0;
|
|
28
|
-
return data;
|
|
29
|
-
} catch (error) {
|
|
30
|
-
console.error("Error fetching user:", error);
|
|
31
|
-
token.value = "";
|
|
32
|
-
isAuthenticated.value = !1;
|
|
33
|
-
user.value = null;
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
async function checkAuthentication() {
|
|
38
|
-
try {
|
|
39
|
-
return await fetchAuthUser() !== null;
|
|
40
|
-
} catch (error) {
|
|
41
|
-
console.error("Error checking authentication:", error);
|
|
42
|
-
return !1;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
async function register(user) {
|
|
46
|
-
const url = `${baseUrl}/register`, data = await (await fetch(url, {
|
|
47
|
-
method: "POST",
|
|
48
|
-
credentials: "same-origin",
|
|
49
|
-
headers: withCsrfHeader({
|
|
50
|
-
"Content-Type": "application/json"
|
|
51
|
-
}),
|
|
52
|
-
body: JSON.stringify(user)
|
|
53
|
-
})).json();
|
|
54
|
-
if (isRegisterError(data))
|
|
55
|
-
return data;
|
|
56
|
-
if (isRegisterResponse(data)) {
|
|
57
|
-
token.value = data.token;
|
|
58
|
-
return data;
|
|
59
|
-
}
|
|
60
|
-
return data;
|
|
61
|
-
}
|
|
62
|
-
function isRegisterError(data) {
|
|
63
|
-
return "errors" in data;
|
|
64
|
-
}
|
|
65
|
-
function isRegisterResponse(data) {
|
|
66
|
-
return "token" in data && "user" in data;
|
|
67
|
-
}
|
|
68
|
-
async function login(user) {
|
|
69
|
-
try {
|
|
70
|
-
const url = `${baseUrl}/login`, response = await fetch(url, {
|
|
71
|
-
method: "POST",
|
|
72
|
-
credentials: "same-origin",
|
|
73
|
-
headers: withCsrfHeader({
|
|
74
|
-
"Content-Type": "application/json"
|
|
75
|
-
}),
|
|
76
|
-
body: JSON.stringify(user)
|
|
77
|
-
});
|
|
78
|
-
if (!response.ok) {
|
|
79
|
-
const errorData = await response.json();
|
|
80
|
-
throw Error(errorData.message || `HTTP error! status: ${response.status}`);
|
|
81
|
-
}
|
|
82
|
-
const data = await response.json();
|
|
83
|
-
token.value = data.token;
|
|
84
|
-
await fetchAuthUser();
|
|
85
|
-
return data;
|
|
86
|
-
} catch (error) {
|
|
87
|
-
return error;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
async function logout() {
|
|
91
|
-
try {
|
|
92
|
-
const currentToken = token.value;
|
|
93
|
-
if (currentToken)
|
|
94
|
-
await fetch(`${baseUrl}/logout`, {
|
|
95
|
-
method: "POST",
|
|
96
|
-
headers: {
|
|
97
|
-
Authorization: `Bearer ${currentToken}`,
|
|
98
|
-
Accept: "application/json"
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
} catch (error) {
|
|
102
|
-
console.error("Error during logout:", error);
|
|
103
|
-
} finally {
|
|
104
|
-
token.value = "";
|
|
105
|
-
user.value = null;
|
|
106
|
-
isAuthenticated.value = !1;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return {
|
|
110
|
-
user,
|
|
111
|
-
isAuthenticated,
|
|
112
|
-
token,
|
|
113
|
-
getToken: () => token.value,
|
|
114
|
-
register,
|
|
115
|
-
login,
|
|
116
|
-
logout,
|
|
117
|
-
fetchAuthUser,
|
|
118
|
-
checkAuthentication
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
export function authGuard(options = {}) {
|
|
122
|
-
const guest = options.guest ?? !1, { isAuthenticated } = useAuth();
|
|
123
|
-
if (guest) {
|
|
124
|
-
if (isAuthenticated.value)
|
|
125
|
-
window.location.replace("/");
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (!isAuthenticated.value)
|
|
129
|
-
window.location.replace("/login");
|
|
130
|
-
}
|
|
1
|
+
import{useStorage}from"@stacksjs/composables";import{ref}from"@stacksjs/stx";import{withCsrfHeader}from"./csrf";const token=useStorage("token",""),stacksConfig=globalThis.__STACKS_CONFIG__||{},baseUrl=stacksConfig.API_URL||(typeof window<"u"?window.location.origin:""),user=ref(null),isAuthenticated=ref(!1);export function useAuth(){async function fetchAuthUser(){try{if(!token.value){isAuthenticated.value=!1;user.value=null;return null}const response=await fetch(`${baseUrl}/me`,{headers:{Authorization:`Bearer ${token.value}`,Accept:"application/json"}});if(!response.ok){token.value="";isAuthenticated.value=!1;user.value=null;return null}const data=await response.json();user.value=data;isAuthenticated.value=!0;return data}catch(error){console.error("Error fetching user:",error);token.value="";isAuthenticated.value=!1;user.value=null;return null}}async function checkAuthentication(){try{return await fetchAuthUser()!==null}catch(error){console.error("Error checking authentication:",error);return!1}}async function register(user){const url=`${baseUrl}/register`,data=await(await fetch(url,{method:"POST",credentials:"same-origin",headers:withCsrfHeader({"Content-Type":"application/json"}),body:JSON.stringify(user)})).json();if(isRegisterError(data))return data;if(isRegisterResponse(data)){token.value=data.token;return data}return data}function isRegisterError(data){return"errors"in data}function isRegisterResponse(data){return"token"in data&&"user"in data}async function login(user){try{const url=`${baseUrl}/login`,response=await fetch(url,{method:"POST",credentials:"same-origin",headers:withCsrfHeader({"Content-Type":"application/json"}),body:JSON.stringify(user)});if(!response.ok){const errorData=await response.json();throw Error(errorData.message||`HTTP error! status: ${response.status}`)}const data=await response.json();token.value=data.token;await fetchAuthUser();return data}catch(error){return error}}async function logout(){try{const currentToken=token.value;if(currentToken)await fetch(`${baseUrl}/logout`,{method:"POST",headers:{Authorization:`Bearer ${currentToken}`,Accept:"application/json"}})}catch(error){console.error("Error during logout:",error)}finally{token.value="";user.value=null;isAuthenticated.value=!1}}return{user,isAuthenticated,token,getToken:()=>token.value,register,login,logout,fetchAuthUser,checkAuthentication}}export function authGuard(options={}){const guest=options.guest??!1,{isAuthenticated}=useAuth();if(guest){if(isAuthenticated.value)window.location.replace("/");return}if(!isAuthenticated.value)window.location.replace("/login")}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{useFetch}from"@stacksjs/composables/useFetch";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export{useStorage}from"@stacksjs/composables/useStorage";
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1 @@
|
|
|
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";
|
|
1
|
+
import"./auto-init";export*from"./composables";export*from"./utils";export*from"./model-loader";export{browserQuery,BrowserQueryBuilder,BrowserQueryError,browserAuth,configureBrowser,getBrowserConfig,createBrowserDb,createBrowserModel,isBrowser}from"bun-query-builder/browser";
|
package/dist/model-loader.js
CHANGED
|
@@ -1,61 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
const modelModules = typeof import.meta.glob === "function" ? import.meta.glob("~/app/Models/*.ts", { eager: !0 }) : {};
|
|
3
|
-
export function loadBrowserModels() {
|
|
4
|
-
if (typeof window > "u")
|
|
5
|
-
return;
|
|
6
|
-
if (!window.StacksBrowser)
|
|
7
|
-
window.StacksBrowser = {};
|
|
8
|
-
for (const [path, module] of Object.entries(modelModules)) {
|
|
9
|
-
const definition = module.default;
|
|
10
|
-
if (!definition || !definition.name) {
|
|
11
|
-
console.warn(`[model-loader] Skipping ${path}: no valid model definition`);
|
|
12
|
-
continue;
|
|
13
|
-
}
|
|
14
|
-
if (!definition.traits?.useApi?.uri)
|
|
15
|
-
continue;
|
|
16
|
-
try {
|
|
17
|
-
const browserModel = createBrowserModel({
|
|
18
|
-
name: definition.name,
|
|
19
|
-
table: definition.table ?? definition.name.toLowerCase(),
|
|
20
|
-
primaryKey: definition.primaryKey || "id",
|
|
21
|
-
traits: {
|
|
22
|
-
useUuid: definition.traits?.useUuid ?? !1,
|
|
23
|
-
useTimestamps: definition.traits?.useTimestamps ?? !0,
|
|
24
|
-
useSoftDeletes: definition.traits?.useSoftDeletes ?? !1,
|
|
25
|
-
useApi: definition.traits?.useApi
|
|
26
|
-
},
|
|
27
|
-
attributes: extractBrowserAttributes(definition.attributes ?? {})
|
|
28
|
-
});
|
|
29
|
-
window.StacksBrowser[definition.name] = browserModel;
|
|
30
|
-
} catch (error) {
|
|
31
|
-
console.error(`[model-loader] Failed to create browser model for ${definition.name}:`, error);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function extractBrowserAttributes(attributes) {
|
|
36
|
-
const browserAttrs = {};
|
|
37
|
-
for (const [name, attr] of Object.entries(attributes))
|
|
38
|
-
browserAttrs[name] = {
|
|
39
|
-
fillable: attr.fillable ?? !1,
|
|
40
|
-
hidden: attr.hidden ?? !1,
|
|
41
|
-
guarded: attr.guarded ?? !1,
|
|
42
|
-
nullable: attr.nullable ?? !1
|
|
43
|
-
};
|
|
44
|
-
return browserAttrs;
|
|
45
|
-
}
|
|
46
|
-
export function getBrowserModel(name) {
|
|
47
|
-
if (typeof window > "u")
|
|
48
|
-
return null;
|
|
49
|
-
return window.StacksBrowser?.[name] ?? null;
|
|
50
|
-
}
|
|
51
|
-
export function getBrowserModelNames() {
|
|
52
|
-
if (typeof window > "u")
|
|
53
|
-
return [];
|
|
54
|
-
const stacksBrowser = window.StacksBrowser;
|
|
55
|
-
if (!stacksBrowser)
|
|
56
|
-
return [];
|
|
57
|
-
return Object.keys(stacksBrowser).filter((key) => {
|
|
58
|
-
const value = stacksBrowser[key];
|
|
59
|
-
return value != null && typeof value.all === "function" && typeof value.find === "function";
|
|
60
|
-
});
|
|
61
|
-
}
|
|
1
|
+
import{createBrowserModel}from"bun-query-builder/browser";const modelModules=typeof import.meta.glob==="function"?import.meta.glob("~/app/Models/*.ts",{eager:!0}):{};export function loadBrowserModels(){if(typeof window>"u")return;if(!window.StacksBrowser)window.StacksBrowser={};for(const[path,module]of Object.entries(modelModules)){const definition=module.default;if(!definition||!definition.name){console.warn(`[model-loader] Skipping ${path}: no valid model definition`);continue}if(!definition.traits?.useApi?.uri)continue;try{const browserModel=createBrowserModel({name:definition.name,table:definition.table??definition.name.toLowerCase(),primaryKey:definition.primaryKey||"id",traits:{useUuid:definition.traits?.useUuid??!1,useTimestamps:definition.traits?.useTimestamps??!0,useSoftDeletes:definition.traits?.useSoftDeletes??!1,useApi:definition.traits?.useApi},attributes:extractBrowserAttributes(definition.attributes??{})});window.StacksBrowser[definition.name]=browserModel}catch(error){console.error(`[model-loader] Failed to create browser model for ${definition.name}:`,error)}}}function extractBrowserAttributes(attributes){const browserAttrs={};for(const[name,attr]of Object.entries(attributes))browserAttrs[name]={fillable:attr.fillable??!1,hidden:attr.hidden??!1,guarded:attr.guarded??!1,nullable:attr.nullable??!1};return browserAttrs}export function getBrowserModel(name){if(typeof window>"u")return null;return window.StacksBrowser?.[name]??null}export function getBrowserModelNames(){if(typeof window>"u")return[];const stacksBrowser=window.StacksBrowser;if(!stacksBrowser)return[];return Object.keys(stacksBrowser).filter((key)=>{const value=stacksBrowser[key];return value!=null&&typeof value.all==="function"&&typeof value.find==="function"})}
|
package/dist/types/dashboard.js
CHANGED
package/dist/utils/base.js
CHANGED
|
@@ -1,4 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export async function loop(times, callback) {
|
|
3
|
-
Array.from({ length: times }).forEach(async (_, i) => await callback(i));
|
|
4
|
-
}
|
|
1
|
+
export{toString}from"@stacksjs/strings";export async function loop(times,callback){Array.from({length:times}).forEach(async(_,i)=>await callback(i))}
|