@strands.gg/accui 2.9.0 → 2.9.1
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/accui.css +1 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.es.js +40821 -0
- package/dist/nuxt/module.cjs.js +1 -0
- package/dist/nuxt/module.es.js +193 -0
- package/dist/nuxt/runtime/composables/useAuthenticatedFetch.cjs.js +1 -0
- package/dist/nuxt/runtime/composables/useAuthenticatedFetch.es.js +134 -0
- package/dist/nuxt/runtime/composables/useStrandsAuth.cjs.js +1 -0
- package/dist/nuxt/runtime/composables/useStrandsAuth.es.js +56 -0
- package/dist/nuxt/runtime/middleware/auth.global.cjs.js +1 -0
- package/dist/nuxt/runtime/middleware/auth.global.es.js +54 -0
- package/dist/nuxt/runtime/plugin.client.cjs.js +1 -0
- package/dist/nuxt/runtime/plugin.client.es.js +26 -0
- package/dist/nuxt/runtime/plugin.server.cjs.js +1 -0
- package/dist/nuxt/runtime/plugin.server.es.js +17 -0
- package/dist/nuxt/runtime/plugins/auth-interceptor.client.cjs.js +1 -0
- package/dist/nuxt/runtime/plugins/auth-interceptor.client.es.js +77 -0
- package/dist/nuxt.cjs.js +1 -0
- package/dist/nuxt.es.js +11 -0
- package/dist/useStrandsAuth-Bp5aTR3h.cjs.js +1 -0
- package/dist/useStrandsAuth-Xz8lnhv7.es.js +916 -0
- package/dist/useStrandsConfig-CLSzvTvE.cjs.js +1 -0
- package/dist/useStrandsConfig-XmPqJn_B.es.js +241 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const t=require("@nuxt/kit");var e="undefined"!=typeof document?document.currentScript:null;const s=t.defineNuxtModule({meta:{name:"@strands.gg/accui/nuxt",configKey:"strandsAuth",compatibility:{nuxt:"^3.0.0 || ^4.0.0"}},defaults:{baseUrl:"https://your-api.example.com",accentColor:"#EA00A8",redirectUrl:"/",onSignInUrl:"/dashboard",onSignOutUrl:"/",autoRefresh:1,refreshInterval:4,protectedRoutes:[],guestOnlyRoutes:["/auth","/login","/register"],devMode:0,styles:1,supportEmail:"",oauth2RedirectUrl:""},async setup(s,n){const r=t.createResolver("undefined"==typeof document?require("url").pathToFileURL(__filename).href:e&&"SCRIPT"===e.tagName.toUpperCase()&&e.src||new URL("nuxt/module.cjs.js",document.baseURI).href);n.options.runtimeConfig.public=n.options.runtimeConfig.public||{};const o={...s,styles:0!=s.styles};if(n.options.runtimeConfig.public.strandsAuth=o,n.options.appConfig&&(n.options.appConfig.strandsAuth=o),0!=s.styles&&n.options.css.push("@strands.gg/accui/style.css"),s.accentColor&&"#EA00A8"!==s.accentColor){const t=s.accentColor,e=`\n :root {\n --strands-custom-accent: ${t};\n --strands-custom-50: color-mix(in srgb, ${t} 10%, white);\n --strands-custom-100: color-mix(in srgb, ${t} 20%, white);\n --strands-custom-200: color-mix(in srgb, ${t} 30%, white);\n --strands-custom-300: color-mix(in srgb, ${t} 40%, white);\n --strands-custom-400: color-mix(in srgb, ${t} 70%, white);\n --strands-custom-500: ${t};\n --strands-custom-600: color-mix(in srgb, ${t} 85%, black);\n --strands-custom-700: color-mix(in srgb, ${t} 70%, black);\n --strands-custom-800: color-mix(in srgb, ${t} 55%, black);\n --strands-custom-900: color-mix(in srgb, ${t} 40%, black);\n --strands-custom-950: color-mix(in srgb, ${t} 25%, black);\n }\n `;n.options.app.head=n.options.app.head||{},n.options.app.head.style=n.options.app.head.style||[],n.options.app.head.style.unshift({innerHTML:e,key:"strands-accent-override"})}const a="es.js",i=r.resolve(`./runtime/plugin.client.${a}`),u=r.resolve(`./runtime/plugin.server.${a}`),d=r.resolve(`./runtime/middleware/auth.global.${a}`),c=r.resolve(`./runtime/plugins/auth-interceptor.client.${a}`);t.addPlugin({src:i,mode:"client"}),t.addPlugin({src:u,mode:"server"}),t.addPlugin({src:c,mode:"client"}),t.addRouteMiddleware({name:"auth",path:d,global:1}),n.hook("pages:extend",()=>{}),t.addImports([{name:"useStrandsAuth",as:"useStrandsAuth",from:r.resolve(`./runtime/composables/useStrandsAuth.${a}`)},{name:"useAuthUser",as:"useAuthUser",from:r.resolve(`./runtime/composables/useStrandsAuth.${a}`)},{name:"useAuthState",as:"useAuthState",from:r.resolve(`./runtime/composables/useStrandsAuth.${a}`)},{name:"useAuthenticatedFetch",as:"useAuthenticatedFetch",from:r.resolve(`./runtime/composables/useAuthenticatedFetch.${a}`)}]);const l=["StrandsAuth","StrandsSignIn","StrandsSignUp","StrandsCompleteSignUp","StrandsUserProfile","StrandsUserButton","StrandsPasswordReset","StrandsMFASetup","StrandsMfaVerification","StrandsMfaModal","StrandsBackupCodesModal","StrandsConfirmModal","StrandsEmailMfaSetupModal","StrandsHardwareKeySetupModal","StrandsSessionsModal","StrandsSettingsModal","StrandsTotpSetupModal","StrandsConfigProvider","SignedIn","StrandsLogo","StrandsSecuredFooter","StrandsNav","StrandsNavItem","UiAlert","UiApp","UiAvatarEditor","UiButton","UiCard","UiCheckboxGroup","UiColorPicker","UiDateTimePicker","UiDivider","UiHero","UiInput","UiLevelProgress","UiLink","UiLoader","UiModal","UiPill","UiRadioGroup","UiSlider","UiTable","UiTabs","UiThemeToggle","UiToggle","UiTooltip","VirtualList","SvgIcon","IconGithub","IconGoogle"];for(const e of l)t.addComponent({name:e,export:e,filePath:"@strands.gg/accui"});n.hook("prepare:types",t=>{t.references.push({types:"@strands.gg/auth-nuxt"}),t.declarations&&t.declarations.push(`\n declare global {\n const useStrandsAuth: typeof import('${r.resolve("./runtime/composables/useStrandsAuth.d.ts")}')['useStrandsAuth']\n const useAuthUser: typeof import('${r.resolve("./runtime/composables/useStrandsAuth.d.ts")}')['useAuthUser']\n const useAuthState: typeof import('${r.resolve("./runtime/composables/useStrandsAuth.d.ts")}')['useAuthState']\n const useAuthenticatedFetch: typeof import('${r.resolve("./runtime/composables/useAuthenticatedFetch.d.ts")}')['useAuthenticatedFetch']\n }\n `)})}});module.exports=s;
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { defineNuxtModule, createResolver, addPlugin, addRouteMiddleware, addImports, addComponent } from "@nuxt/kit";
|
|
2
|
+
const moduleDefaults = {
|
|
3
|
+
baseUrl: "https://your-api.example.com",
|
|
4
|
+
accentColor: "#EA00A8",
|
|
5
|
+
redirectUrl: "/",
|
|
6
|
+
onSignInUrl: "/dashboard",
|
|
7
|
+
onSignOutUrl: "/",
|
|
8
|
+
autoRefresh: true,
|
|
9
|
+
refreshInterval: 4,
|
|
10
|
+
protectedRoutes: [],
|
|
11
|
+
guestOnlyRoutes: ["/auth", "/login", "/register"],
|
|
12
|
+
devMode: false,
|
|
13
|
+
styles: true,
|
|
14
|
+
supportEmail: "",
|
|
15
|
+
oauth2RedirectUrl: ""
|
|
16
|
+
};
|
|
17
|
+
const module = defineNuxtModule({
|
|
18
|
+
meta: {
|
|
19
|
+
name: "@strands.gg/accui/nuxt",
|
|
20
|
+
configKey: "strandsAuth",
|
|
21
|
+
compatibility: {
|
|
22
|
+
nuxt: "^3.0.0 || ^4.0.0"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
defaults: moduleDefaults,
|
|
26
|
+
async setup(options, nuxt) {
|
|
27
|
+
const resolver = createResolver(import.meta.url);
|
|
28
|
+
nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
|
|
29
|
+
const runtimeConfig = {
|
|
30
|
+
...options,
|
|
31
|
+
styles: options.styles !== false
|
|
32
|
+
};
|
|
33
|
+
nuxt.options.runtimeConfig.public.strandsAuth = runtimeConfig;
|
|
34
|
+
if (nuxt.options.appConfig) {
|
|
35
|
+
nuxt.options.appConfig.strandsAuth = runtimeConfig;
|
|
36
|
+
}
|
|
37
|
+
if (options.styles !== false) {
|
|
38
|
+
nuxt.options.css.push("@strands.gg/accui/style.css");
|
|
39
|
+
}
|
|
40
|
+
if (options.accentColor && options.accentColor !== "#EA00A8") {
|
|
41
|
+
const accentColor = options.accentColor;
|
|
42
|
+
const cssVariables = `
|
|
43
|
+
:root {
|
|
44
|
+
--strands-custom-accent: ${accentColor};
|
|
45
|
+
--strands-custom-50: color-mix(in srgb, ${accentColor} 10%, white);
|
|
46
|
+
--strands-custom-100: color-mix(in srgb, ${accentColor} 20%, white);
|
|
47
|
+
--strands-custom-200: color-mix(in srgb, ${accentColor} 30%, white);
|
|
48
|
+
--strands-custom-300: color-mix(in srgb, ${accentColor} 40%, white);
|
|
49
|
+
--strands-custom-400: color-mix(in srgb, ${accentColor} 70%, white);
|
|
50
|
+
--strands-custom-500: ${accentColor};
|
|
51
|
+
--strands-custom-600: color-mix(in srgb, ${accentColor} 85%, black);
|
|
52
|
+
--strands-custom-700: color-mix(in srgb, ${accentColor} 70%, black);
|
|
53
|
+
--strands-custom-800: color-mix(in srgb, ${accentColor} 55%, black);
|
|
54
|
+
--strands-custom-900: color-mix(in srgb, ${accentColor} 40%, black);
|
|
55
|
+
--strands-custom-950: color-mix(in srgb, ${accentColor} 25%, black);
|
|
56
|
+
}
|
|
57
|
+
`;
|
|
58
|
+
nuxt.options.app.head = nuxt.options.app.head || {};
|
|
59
|
+
nuxt.options.app.head.style = nuxt.options.app.head.style || [];
|
|
60
|
+
nuxt.options.app.head.style.unshift({
|
|
61
|
+
innerHTML: cssVariables,
|
|
62
|
+
key: "strands-accent-override"
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
const ext = "es.js";
|
|
66
|
+
const clientPluginPath = resolver.resolve(`./runtime/plugin.client.${ext}`);
|
|
67
|
+
const serverPluginPath = resolver.resolve(`./runtime/plugin.server.${ext}`);
|
|
68
|
+
const middlewarePath = resolver.resolve(`./runtime/middleware/auth.global.${ext}`);
|
|
69
|
+
const authInterceptorPath = resolver.resolve(`./runtime/plugins/auth-interceptor.client.${ext}`);
|
|
70
|
+
addPlugin({
|
|
71
|
+
src: clientPluginPath,
|
|
72
|
+
mode: "client"
|
|
73
|
+
});
|
|
74
|
+
addPlugin({
|
|
75
|
+
src: serverPluginPath,
|
|
76
|
+
mode: "server"
|
|
77
|
+
});
|
|
78
|
+
addPlugin({
|
|
79
|
+
src: authInterceptorPath,
|
|
80
|
+
mode: "client"
|
|
81
|
+
});
|
|
82
|
+
addRouteMiddleware({
|
|
83
|
+
name: "auth",
|
|
84
|
+
path: middlewarePath,
|
|
85
|
+
global: true
|
|
86
|
+
});
|
|
87
|
+
nuxt.hook("pages:extend", () => {
|
|
88
|
+
});
|
|
89
|
+
addImports([
|
|
90
|
+
{
|
|
91
|
+
name: "useStrandsAuth",
|
|
92
|
+
as: "useStrandsAuth",
|
|
93
|
+
from: resolver.resolve(`./runtime/composables/useStrandsAuth.${ext}`)
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "useAuthUser",
|
|
97
|
+
as: "useAuthUser",
|
|
98
|
+
from: resolver.resolve(`./runtime/composables/useStrandsAuth.${ext}`)
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: "useAuthState",
|
|
102
|
+
as: "useAuthState",
|
|
103
|
+
from: resolver.resolve(`./runtime/composables/useStrandsAuth.${ext}`)
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "useAuthenticatedFetch",
|
|
107
|
+
as: "useAuthenticatedFetch",
|
|
108
|
+
from: resolver.resolve(`./runtime/composables/useAuthenticatedFetch.${ext}`)
|
|
109
|
+
}
|
|
110
|
+
]);
|
|
111
|
+
const components = [
|
|
112
|
+
// Strands Auth Components
|
|
113
|
+
"StrandsAuth",
|
|
114
|
+
"StrandsSignIn",
|
|
115
|
+
"StrandsSignUp",
|
|
116
|
+
"StrandsCompleteSignUp",
|
|
117
|
+
"StrandsUserProfile",
|
|
118
|
+
"StrandsUserButton",
|
|
119
|
+
"StrandsPasswordReset",
|
|
120
|
+
"StrandsMFASetup",
|
|
121
|
+
"StrandsMfaVerification",
|
|
122
|
+
"StrandsMfaModal",
|
|
123
|
+
"StrandsBackupCodesModal",
|
|
124
|
+
"StrandsConfirmModal",
|
|
125
|
+
"StrandsEmailMfaSetupModal",
|
|
126
|
+
"StrandsHardwareKeySetupModal",
|
|
127
|
+
"StrandsSessionsModal",
|
|
128
|
+
"StrandsSettingsModal",
|
|
129
|
+
"StrandsTotpSetupModal",
|
|
130
|
+
"StrandsConfigProvider",
|
|
131
|
+
"SignedIn",
|
|
132
|
+
"StrandsLogo",
|
|
133
|
+
"StrandsSecuredFooter",
|
|
134
|
+
"StrandsNav",
|
|
135
|
+
"StrandsNavItem",
|
|
136
|
+
// UI Components
|
|
137
|
+
"UiAlert",
|
|
138
|
+
"UiApp",
|
|
139
|
+
"UiAvatarEditor",
|
|
140
|
+
"UiButton",
|
|
141
|
+
"UiCard",
|
|
142
|
+
"UiCheckboxGroup",
|
|
143
|
+
"UiColorPicker",
|
|
144
|
+
"UiDateTimePicker",
|
|
145
|
+
"UiDivider",
|
|
146
|
+
"UiHero",
|
|
147
|
+
"UiInput",
|
|
148
|
+
"UiLevelProgress",
|
|
149
|
+
"UiLink",
|
|
150
|
+
"UiLoader",
|
|
151
|
+
"UiModal",
|
|
152
|
+
"UiPill",
|
|
153
|
+
"UiRadioGroup",
|
|
154
|
+
"UiSlider",
|
|
155
|
+
"UiTable",
|
|
156
|
+
"UiTabs",
|
|
157
|
+
"UiThemeToggle",
|
|
158
|
+
"UiToggle",
|
|
159
|
+
"UiTooltip",
|
|
160
|
+
// Utility Components
|
|
161
|
+
"VirtualList",
|
|
162
|
+
"SvgIcon",
|
|
163
|
+
// Icons
|
|
164
|
+
"IconGithub",
|
|
165
|
+
"IconGoogle"
|
|
166
|
+
];
|
|
167
|
+
for (const component of components) {
|
|
168
|
+
addComponent({
|
|
169
|
+
name: component,
|
|
170
|
+
export: component,
|
|
171
|
+
filePath: "@strands.gg/accui"
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
nuxt.hook("prepare:types", (opts) => {
|
|
175
|
+
opts.references.push({
|
|
176
|
+
types: "@strands.gg/auth-nuxt"
|
|
177
|
+
});
|
|
178
|
+
if (opts.declarations) {
|
|
179
|
+
opts.declarations.push(`
|
|
180
|
+
declare global {
|
|
181
|
+
const useStrandsAuth: typeof import('${resolver.resolve(`./runtime/composables/useStrandsAuth.d.ts`)}')['useStrandsAuth']
|
|
182
|
+
const useAuthUser: typeof import('${resolver.resolve(`./runtime/composables/useStrandsAuth.d.ts`)}')['useAuthUser']
|
|
183
|
+
const useAuthState: typeof import('${resolver.resolve(`./runtime/composables/useStrandsAuth.d.ts`)}')['useAuthState']
|
|
184
|
+
const useAuthenticatedFetch: typeof import('${resolver.resolve(`./runtime/composables/useAuthenticatedFetch.d.ts`)}')['useAuthenticatedFetch']
|
|
185
|
+
}
|
|
186
|
+
`);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
export {
|
|
192
|
+
module as default
|
|
193
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("nuxt/app"),e=require("./useStrandsAuth.cjs.js");function n(){const n=t.useRuntimeConfig().public.strandsAuth,{currentSession:o,refreshToken:s,getAuthHeaders:r}=e.useStrandsAuth(),c=async(t,e={})=>{const{autoRefresh:c=1,requireAuth:a=1,baseURL:p,...u}=e;if(a&&!o.value?.accessToken)throw new Error("User is not authenticated");let h=t;const d=p||n.baseUrl;d&&"string"==typeof t&&!t.startsWith("http")&&(h=new URL(t,d).toString());const y=new Headers(u.headers);if(o.value?.accessToken)try{const t=r();Object.entries(t).forEach(([t,e])=>{y.set(t,e)})}catch(b){if(a)throw b}const i={...u,headers:y};let f=await fetch(h,i);if(401===f.status&&c&&o.value?.refreshToken)try{if(await s()&&o.value?.accessToken){const t=r();Object.entries(t).forEach(([t,e])=>{y.set(t,e)}),f=await fetch(h,{...i,headers:y})}}catch(j){}return f};return{authenticatedFetch:c,get:(t,e)=>c(t,{...e,method:"GET"}),post:(t,e,n)=>{const o=new Headers(n?.headers);return e&&"object"==typeof e&&!o.has("Content-Type")&&o.set("Content-Type","application/json"),c(t,{...n,method:"POST",headers:o,body:"object"==typeof e?JSON.stringify(e):e})},put:(t,e,n)=>{const o=new Headers(n?.headers);return e&&"object"==typeof e&&!o.has("Content-Type")&&o.set("Content-Type","application/json"),c(t,{...n,method:"PUT",headers:o,body:"object"==typeof e?JSON.stringify(e):e})},delete:(t,e)=>c(t,{...e,method:"DELETE"}),patch:(t,e,n)=>{const o=new Headers(n?.headers);return e&&"object"==typeof e&&!o.has("Content-Type")&&o.set("Content-Type","application/json"),c(t,{...n,method:"PATCH",headers:o,body:"object"==typeof e?JSON.stringify(e):e})}}}const o={get:async(t,e)=>{const{get:o}=n();return o(t,e)},post:async(t,e,o)=>{const{post:s}=n();return s(t,e,o)},put:async(t,e,o)=>{const{put:s}=n();return s(t,e,o)},delete:async(t,e)=>{const{delete:o}=n();return o(t,e)},patch:async(t,e,o)=>{const{patch:s}=n();return s(t,e,o)}};exports.$authFetch=o,exports.useAuthenticatedFetch=n;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { useRuntimeConfig } from "nuxt/app";
|
|
2
|
+
import { useStrandsAuth } from "./useStrandsAuth.es.js";
|
|
3
|
+
function useAuthenticatedFetch() {
|
|
4
|
+
const config = useRuntimeConfig().public["strandsAuth"];
|
|
5
|
+
const { currentSession, refreshToken, getAuthHeaders } = useStrandsAuth();
|
|
6
|
+
const authenticatedFetch = async (url, options = {}) => {
|
|
7
|
+
const {
|
|
8
|
+
autoRefresh = true,
|
|
9
|
+
requireAuth = true,
|
|
10
|
+
baseURL,
|
|
11
|
+
...fetchOptions
|
|
12
|
+
} = options;
|
|
13
|
+
if (requireAuth && !currentSession.value?.accessToken) {
|
|
14
|
+
throw new Error("User is not authenticated");
|
|
15
|
+
}
|
|
16
|
+
let fullUrl = url;
|
|
17
|
+
const resolvedBaseURL = baseURL || config.baseUrl;
|
|
18
|
+
if (resolvedBaseURL && typeof url === "string" && !url.startsWith("http")) {
|
|
19
|
+
fullUrl = new URL(url, resolvedBaseURL).toString();
|
|
20
|
+
}
|
|
21
|
+
const headers = new Headers(fetchOptions.headers);
|
|
22
|
+
if (currentSession.value?.accessToken) {
|
|
23
|
+
try {
|
|
24
|
+
const authHeaders = getAuthHeaders();
|
|
25
|
+
Object.entries(authHeaders).forEach(([key, value]) => {
|
|
26
|
+
headers.set(key, value);
|
|
27
|
+
});
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.warn("[Strands Auth] Failed to get auth headers:", error);
|
|
30
|
+
if (requireAuth) {
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const enhancedOptions = {
|
|
36
|
+
...fetchOptions,
|
|
37
|
+
headers
|
|
38
|
+
};
|
|
39
|
+
let response = await fetch(fullUrl, enhancedOptions);
|
|
40
|
+
if (response.status === 401 && autoRefresh && currentSession.value?.refreshToken) {
|
|
41
|
+
console.log("[Strands Auth] Request failed with 401, attempting token refresh...");
|
|
42
|
+
try {
|
|
43
|
+
const refreshed = await refreshToken();
|
|
44
|
+
if (refreshed && currentSession.value?.accessToken) {
|
|
45
|
+
const newAuthHeaders = getAuthHeaders();
|
|
46
|
+
Object.entries(newAuthHeaders).forEach(([key, value]) => {
|
|
47
|
+
headers.set(key, value);
|
|
48
|
+
});
|
|
49
|
+
console.log("[Strands Auth] Retrying request with refreshed token");
|
|
50
|
+
response = await fetch(fullUrl, { ...enhancedOptions, headers });
|
|
51
|
+
}
|
|
52
|
+
} catch (refreshError) {
|
|
53
|
+
console.error("[Strands Auth] Token refresh failed:", refreshError);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return response;
|
|
57
|
+
};
|
|
58
|
+
const get = (url, options) => {
|
|
59
|
+
return authenticatedFetch(url, { ...options, method: "GET" });
|
|
60
|
+
};
|
|
61
|
+
const post = (url, body, options) => {
|
|
62
|
+
const headers = new Headers(options?.headers);
|
|
63
|
+
if (body && typeof body === "object" && !headers.has("Content-Type")) {
|
|
64
|
+
headers.set("Content-Type", "application/json");
|
|
65
|
+
}
|
|
66
|
+
return authenticatedFetch(url, {
|
|
67
|
+
...options,
|
|
68
|
+
method: "POST",
|
|
69
|
+
headers,
|
|
70
|
+
body: typeof body === "object" ? JSON.stringify(body) : body
|
|
71
|
+
});
|
|
72
|
+
};
|
|
73
|
+
const put = (url, body, options) => {
|
|
74
|
+
const headers = new Headers(options?.headers);
|
|
75
|
+
if (body && typeof body === "object" && !headers.has("Content-Type")) {
|
|
76
|
+
headers.set("Content-Type", "application/json");
|
|
77
|
+
}
|
|
78
|
+
return authenticatedFetch(url, {
|
|
79
|
+
...options,
|
|
80
|
+
method: "PUT",
|
|
81
|
+
headers,
|
|
82
|
+
body: typeof body === "object" ? JSON.stringify(body) : body
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
const del = (url, options) => {
|
|
86
|
+
return authenticatedFetch(url, { ...options, method: "DELETE" });
|
|
87
|
+
};
|
|
88
|
+
const patch = (url, body, options) => {
|
|
89
|
+
const headers = new Headers(options?.headers);
|
|
90
|
+
if (body && typeof body === "object" && !headers.has("Content-Type")) {
|
|
91
|
+
headers.set("Content-Type", "application/json");
|
|
92
|
+
}
|
|
93
|
+
return authenticatedFetch(url, {
|
|
94
|
+
...options,
|
|
95
|
+
method: "PATCH",
|
|
96
|
+
headers,
|
|
97
|
+
body: typeof body === "object" ? JSON.stringify(body) : body
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
authenticatedFetch,
|
|
102
|
+
get,
|
|
103
|
+
post,
|
|
104
|
+
put,
|
|
105
|
+
delete: del,
|
|
106
|
+
patch
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const $authFetch = {
|
|
110
|
+
get: async (url, options) => {
|
|
111
|
+
const { get } = useAuthenticatedFetch();
|
|
112
|
+
return get(url, options);
|
|
113
|
+
},
|
|
114
|
+
post: async (url, body, options) => {
|
|
115
|
+
const { post } = useAuthenticatedFetch();
|
|
116
|
+
return post(url, body, options);
|
|
117
|
+
},
|
|
118
|
+
put: async (url, body, options) => {
|
|
119
|
+
const { put } = useAuthenticatedFetch();
|
|
120
|
+
return put(url, body, options);
|
|
121
|
+
},
|
|
122
|
+
delete: async (url, options) => {
|
|
123
|
+
const { delete: del } = useAuthenticatedFetch();
|
|
124
|
+
return del(url, options);
|
|
125
|
+
},
|
|
126
|
+
patch: async (url, body, options) => {
|
|
127
|
+
const { patch } = useAuthenticatedFetch();
|
|
128
|
+
return patch(url, body, options);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
export {
|
|
132
|
+
$authFetch,
|
|
133
|
+
useAuthenticatedFetch
|
|
134
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("nuxt/app"),s=require("../../../useStrandsAuth-Bp5aTR3h.cjs.js"),a=()=>{const a=t.useRuntimeConfig().public.strandsAuth,e=s.useStrandsAuth();return{...e,initialize:async()=>{await e.initialize()},signIn:async s=>{const i=await e.signIn(s);return i&&a.onSignInUrl&&await t.navigateTo(a.onSignInUrl),i},signUp:async t=>await e.signUp({email:t.email,password:"",firstName:"",lastName:""}),signOut:async()=>{await e.signOut(),a.onSignOutUrl&&await t.navigateTo(a.onSignOutUrl)}}};exports.useAuthState=()=>{const{isAuthenticated:t,isLoading:s}=a();return{isAuthenticated:t,isLoading:s}},exports.useAuthUser=()=>{const{user:t}=a();return{user:t}},exports.useStrandsAuth=a;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { useRuntimeConfig, navigateTo } from "nuxt/app";
|
|
2
|
+
import { u as useStrandsAuth$1 } from "../../../useStrandsAuth-Xz8lnhv7.es.js";
|
|
3
|
+
const useStrandsAuth = () => {
|
|
4
|
+
const config = useRuntimeConfig().public["strandsAuth"];
|
|
5
|
+
const vueAuth = useStrandsAuth$1();
|
|
6
|
+
const initialize = async () => {
|
|
7
|
+
await vueAuth.initialize();
|
|
8
|
+
};
|
|
9
|
+
const signIn = async (credentials) => {
|
|
10
|
+
const result = await vueAuth.signIn(credentials);
|
|
11
|
+
if (result && config.onSignInUrl) {
|
|
12
|
+
await navigateTo(config.onSignInUrl);
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
};
|
|
16
|
+
const signUp = async (userData) => {
|
|
17
|
+
const result = await vueAuth.signUp({
|
|
18
|
+
email: userData.email,
|
|
19
|
+
password: "",
|
|
20
|
+
// Empty password for magic link
|
|
21
|
+
firstName: "",
|
|
22
|
+
// Will be filled later in the magic link flow
|
|
23
|
+
lastName: ""
|
|
24
|
+
});
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
const signOut = async () => {
|
|
28
|
+
await vueAuth.signOut();
|
|
29
|
+
if (config.onSignOutUrl) {
|
|
30
|
+
await navigateTo(config.onSignOutUrl);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
// Re-export Vue composable state and methods
|
|
35
|
+
...vueAuth,
|
|
36
|
+
// Add initialize function
|
|
37
|
+
initialize,
|
|
38
|
+
// Override methods with Nuxt-specific navigation
|
|
39
|
+
signIn,
|
|
40
|
+
signUp,
|
|
41
|
+
signOut
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
const useAuthUser = () => {
|
|
45
|
+
const { user } = useStrandsAuth();
|
|
46
|
+
return { user };
|
|
47
|
+
};
|
|
48
|
+
const useAuthState = () => {
|
|
49
|
+
const { isAuthenticated, isLoading } = useStrandsAuth();
|
|
50
|
+
return { isAuthenticated, isLoading };
|
|
51
|
+
};
|
|
52
|
+
export {
|
|
53
|
+
useAuthState,
|
|
54
|
+
useAuthUser,
|
|
55
|
+
useStrandsAuth
|
|
56
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const e=require("nuxt/app"),t=e.defineNuxtRouteMiddleware(t=>{const n=e.useRuntimeConfig().public.strandsAuth;return new Promise(r=>{Promise.resolve().then(()=>require("../composables/useStrandsAuth.cjs.js")).then(({useStrandsAuth:o})=>{const{isAuthenticated:i,isLoading:s}=o(),u=()=>{if(s.value)return void setTimeout(u,50);const o=t.path,d=n.protectedRoutes?.some(e=>e.endsWith("*")?o.startsWith(e.slice(0,-1)):o===e||o.startsWith(e+"/")),c=n.guestOnlyRoutes?.some(e=>e.endsWith("*")?o.startsWith(e.slice(0,-1)):o===e||o.startsWith(e+"/"));if(d&&!i.value){const t=n.onSignOutUrl||"/auth",r=t.includes("?")?`${t}&redirect=${encodeURIComponent(o)}`:`${t}?redirect=${encodeURIComponent(o)}`;try{return e.navigateTo(r)}catch(a){if("undefined"!=typeof window)return void(window.location.href=r);throw a}}if(c&&i.value)return e.navigateTo(n.onSignInUrl||"/dashboard");r()};u()})})});module.exports=t;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { defineNuxtRouteMiddleware, useRuntimeConfig, navigateTo } from "nuxt/app";
|
|
2
|
+
const auth_global = defineNuxtRouteMiddleware((to) => {
|
|
3
|
+
const config = useRuntimeConfig().public["strandsAuth"];
|
|
4
|
+
if (import.meta.server) return;
|
|
5
|
+
return new Promise((resolve) => {
|
|
6
|
+
import("../composables/useStrandsAuth.es.js").then(({ useStrandsAuth }) => {
|
|
7
|
+
const { isAuthenticated, isLoading } = useStrandsAuth();
|
|
8
|
+
const checkAuth = () => {
|
|
9
|
+
if (isLoading.value) {
|
|
10
|
+
setTimeout(checkAuth, 50);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
const currentPath = to.path;
|
|
14
|
+
const isProtectedRoute = config.protectedRoutes?.some((route) => {
|
|
15
|
+
if (route.endsWith("*")) {
|
|
16
|
+
return currentPath.startsWith(route.slice(0, -1));
|
|
17
|
+
}
|
|
18
|
+
return currentPath === route || currentPath.startsWith(route + "/");
|
|
19
|
+
});
|
|
20
|
+
const isGuestOnlyRoute = config.guestOnlyRoutes?.some((route) => {
|
|
21
|
+
if (route.endsWith("*")) {
|
|
22
|
+
return currentPath.startsWith(route.slice(0, -1));
|
|
23
|
+
}
|
|
24
|
+
return currentPath === route || currentPath.startsWith(route + "/");
|
|
25
|
+
});
|
|
26
|
+
if (isProtectedRoute && !isAuthenticated.value) {
|
|
27
|
+
console.log("[Strands Auth] Protected route accessed without authentication, redirecting to auth");
|
|
28
|
+
const authUrl = config.onSignOutUrl || "/auth";
|
|
29
|
+
const redirectUrl = authUrl.includes("?") ? `${authUrl}&redirect=${encodeURIComponent(currentPath)}` : `${authUrl}?redirect=${encodeURIComponent(currentPath)}`;
|
|
30
|
+
console.log("[Strands Auth] Redirecting to:", redirectUrl);
|
|
31
|
+
try {
|
|
32
|
+
return navigateTo(redirectUrl);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.warn("[Strands Auth] navigateTo failed, using window.location:", error);
|
|
35
|
+
if (typeof window !== "undefined") {
|
|
36
|
+
window.location.href = redirectUrl;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (isGuestOnlyRoute && isAuthenticated.value) {
|
|
43
|
+
console.log("[Strands Auth] Guest-only route accessed while authenticated, redirecting");
|
|
44
|
+
return navigateTo(config.onSignInUrl || "/dashboard");
|
|
45
|
+
}
|
|
46
|
+
resolve();
|
|
47
|
+
};
|
|
48
|
+
checkAuth();
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
export {
|
|
53
|
+
auth_global as default
|
|
54
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const e=require("nuxt/app"),s=require("../../useStrandsConfig-CLSzvTvE.cjs.js"),n=e.defineNuxtPlugin({name:"strands-auth-client",async setup(){const n=e.useRuntimeConfig().public.strandsAuth,t={...s.STRANDS_AUTH_DEFAULTS,...n};s.setStrandsConfig(t),"undefined"!=typeof window&&(window.__STRANDS_CONFIG__=t),t?.accentColor&&"undefined"!=typeof window&&document.documentElement.style.setProperty("--strands-accent",t.accentColor);const{useStrandsAuth:i}=await Promise.resolve().then(()=>require("./composables/useStrandsAuth.cjs.js")),{initialize:a}=i();await a()}});module.exports=n;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineNuxtPlugin, useRuntimeConfig } from "nuxt/app";
|
|
2
|
+
import { s as setStrandsConfig, S as STRANDS_AUTH_DEFAULTS } from "../../useStrandsConfig-XmPqJn_B.es.js";
|
|
3
|
+
const plugin_client = defineNuxtPlugin({
|
|
4
|
+
name: "strands-auth-client",
|
|
5
|
+
async setup() {
|
|
6
|
+
const config = useRuntimeConfig();
|
|
7
|
+
const strandsConfig = config.public["strandsAuth"];
|
|
8
|
+
const mergedConfig = {
|
|
9
|
+
...STRANDS_AUTH_DEFAULTS,
|
|
10
|
+
...strandsConfig
|
|
11
|
+
};
|
|
12
|
+
setStrandsConfig(mergedConfig);
|
|
13
|
+
if (typeof window !== "undefined") {
|
|
14
|
+
window.__STRANDS_CONFIG__ = mergedConfig;
|
|
15
|
+
}
|
|
16
|
+
if (mergedConfig?.accentColor && typeof window !== "undefined") {
|
|
17
|
+
document.documentElement.style.setProperty("--strands-accent", mergedConfig.accentColor);
|
|
18
|
+
}
|
|
19
|
+
const { useStrandsAuth } = await import("./composables/useStrandsAuth.es.js");
|
|
20
|
+
const { initialize } = useStrandsAuth();
|
|
21
|
+
await initialize();
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
export {
|
|
25
|
+
plugin_client as default
|
|
26
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const s=require("nuxt/app"),e=require("../../useStrandsConfig-CLSzvTvE.cjs.js"),r=s.defineNuxtPlugin({name:"strands-auth-server",setup(){const r=s.useRuntimeConfig().public.strandsAuth,t={...e.STRANDS_AUTH_DEFAULTS,...r};e.setStrandsConfig(t)}});module.exports=r;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineNuxtPlugin, useRuntimeConfig } from "nuxt/app";
|
|
2
|
+
import { s as setStrandsConfig, S as STRANDS_AUTH_DEFAULTS } from "../../useStrandsConfig-XmPqJn_B.es.js";
|
|
3
|
+
const plugin_server = defineNuxtPlugin({
|
|
4
|
+
name: "strands-auth-server",
|
|
5
|
+
setup() {
|
|
6
|
+
const config = useRuntimeConfig();
|
|
7
|
+
const strandsConfig = config.public["strandsAuth"];
|
|
8
|
+
const mergedConfig = {
|
|
9
|
+
...STRANDS_AUTH_DEFAULTS,
|
|
10
|
+
...strandsConfig
|
|
11
|
+
};
|
|
12
|
+
setStrandsConfig(mergedConfig);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
export {
|
|
16
|
+
plugin_server as default
|
|
17
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const t=require("nuxt/app").defineNuxtPlugin({name:"strands-auth-interceptor",setup(){if(process.server)return;const t=globalThis.fetch;globalThis.fetch=async(r,e)=>{try{const{useStrandsAuth:o}=await Promise.resolve().then(()=>require("../composables/useStrandsAuth.cjs.js")),{currentSession:c,getAuthHeaders:i}=o();let n;n="string"==typeof r?r:r instanceof URL?r.toString():r.url;const a=function(t){if(!t.startsWith("http"))return 0;if(["googleapis.com","github.com/api","api.github.com","discord.com/api","graph.microsoft.com","api.stripe.com","api.twilio.com"].some(r=>t.includes(r)))return 0;const r=["/api/","/v1/","/v2/","/graphql","/trpc"].some(r=>t.includes(r)),e=[":3001",":8000",":8080",":5000",":4000"].some(r=>t.includes(r));return r||e}(n);if(a&&c.value?.accessToken)try{const s=i(),o=new Headers(e?.headers);Object.entries(s).forEach(([t,r])=>{o.set(t,r)});const c={...e,headers:o};return t(r,c)}catch(s){}return t(r,e)}catch(s){return t(r,e)}}}});module.exports=t;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { defineNuxtPlugin } from "nuxt/app";
|
|
2
|
+
const authInterceptor_client = defineNuxtPlugin({
|
|
3
|
+
name: "strands-auth-interceptor",
|
|
4
|
+
setup() {
|
|
5
|
+
if (process.server) return;
|
|
6
|
+
const originalFetch = globalThis.fetch;
|
|
7
|
+
globalThis.fetch = async (input, init) => {
|
|
8
|
+
try {
|
|
9
|
+
const { useStrandsAuth } = await import("../composables/useStrandsAuth.es.js");
|
|
10
|
+
const { currentSession, getAuthHeaders } = useStrandsAuth();
|
|
11
|
+
let url;
|
|
12
|
+
if (typeof input === "string") {
|
|
13
|
+
url = input;
|
|
14
|
+
} else if (input instanceof URL) {
|
|
15
|
+
url = input.toString();
|
|
16
|
+
} else {
|
|
17
|
+
url = input.url;
|
|
18
|
+
}
|
|
19
|
+
const shouldAddAuth = shouldInjectAuth(url);
|
|
20
|
+
if (shouldAddAuth && currentSession.value?.accessToken) {
|
|
21
|
+
try {
|
|
22
|
+
const authHeaders = getAuthHeaders();
|
|
23
|
+
const headers = new Headers(init?.headers);
|
|
24
|
+
Object.entries(authHeaders).forEach(([key, value]) => {
|
|
25
|
+
headers.set(key, value);
|
|
26
|
+
});
|
|
27
|
+
const enhancedInit = {
|
|
28
|
+
...init,
|
|
29
|
+
headers
|
|
30
|
+
};
|
|
31
|
+
console.log("[Strands Auth] Injecting auth headers for request:", url);
|
|
32
|
+
return originalFetch(input, enhancedInit);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.warn("[Strands Auth] Failed to inject auth headers:", error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return originalFetch(input, init);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error("[Strands Auth] Error in fetch interceptor:", error);
|
|
40
|
+
return originalFetch(input, init);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
function shouldInjectAuth(url) {
|
|
46
|
+
if (!url.startsWith("http")) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
const skipDomains = [
|
|
50
|
+
"googleapis.com",
|
|
51
|
+
"github.com/api",
|
|
52
|
+
"api.github.com",
|
|
53
|
+
"discord.com/api",
|
|
54
|
+
"graph.microsoft.com",
|
|
55
|
+
"api.stripe.com",
|
|
56
|
+
"api.twilio.com"
|
|
57
|
+
// Add other third-party APIs here
|
|
58
|
+
];
|
|
59
|
+
const shouldSkip = skipDomains.some((domain) => url.includes(domain));
|
|
60
|
+
if (shouldSkip) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const apiPatterns = [
|
|
64
|
+
"/api/",
|
|
65
|
+
"/v1/",
|
|
66
|
+
"/v2/",
|
|
67
|
+
"/graphql",
|
|
68
|
+
"/trpc"
|
|
69
|
+
];
|
|
70
|
+
const isApiRequest = apiPatterns.some((pattern) => url.includes(pattern));
|
|
71
|
+
const apiPorts = [":3001", ":8000", ":8080", ":5000", ":4000"];
|
|
72
|
+
const hasApiPort = apiPorts.some((port) => url.includes(port));
|
|
73
|
+
return isApiRequest || hasApiPort;
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
authInterceptor_client as default
|
|
77
|
+
};
|
package/dist/nuxt.cjs.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperties(exports,{t:{value:1},[Symbol.toStringTag]:{value:"Module"}});const e=require("./nuxt/module.cjs.js"),s=require("./nuxt/runtime/composables/useStrandsAuth.cjs.js"),t=require("./nuxt/runtime/composables/useAuthenticatedFetch.cjs.js");exports.default=e,exports.useAuthState=s.useAuthState,exports.useAuthUser=s.useAuthUser,exports.useStrandsAuth=s.useStrandsAuth,exports.$authFetch=t.$authFetch,exports.useAuthenticatedFetch=t.useAuthenticatedFetch;
|
package/dist/nuxt.es.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { default as default2 } from "./nuxt/module.es.js";
|
|
2
|
+
import { useAuthState, useAuthUser, useStrandsAuth } from "./nuxt/runtime/composables/useStrandsAuth.es.js";
|
|
3
|
+
import { $authFetch, useAuthenticatedFetch } from "./nuxt/runtime/composables/useAuthenticatedFetch.es.js";
|
|
4
|
+
export {
|
|
5
|
+
$authFetch,
|
|
6
|
+
default2 as default,
|
|
7
|
+
useAuthState,
|
|
8
|
+
useAuthUser,
|
|
9
|
+
useAuthenticatedFetch,
|
|
10
|
+
useStrandsAuth
|
|
11
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";const e=require("vue"),t=require("./useStrandsConfig-CLSzvTvE.cjs.js"),n=new class{cache=new Map;DEFAULT_TTL=3e5;async fetch(e,t,n=this.DEFAULT_TTL){const a=Date.now(),i=this.cache.get(e);if(i&&a-i.timestamp<i.ttl)return i.promise;this.cleanExpired();const r=t().finally(()=>{setTimeout(()=>{this.cache.delete(e)},n)});return this.cache.set(e,{promise:r,timestamp:a,ttl:n}),r}clear(){this.cache.clear()}invalidate(e){this.cache.delete(e)}cleanExpired(){const e=Date.now();for(const[t,n]of this.cache.entries())e-n.timestamp>n.ttl&&this.cache.delete(t)}getStats(){return{size:this.cache.size,entries:Array.from(this.cache.keys())}}},a=function(){let e=null;return(...t)=>{e&&clearTimeout(e),e=setTimeout(()=>{((e,t)=>{"undefined"!=typeof window&&localStorage.setItem(e,t)})(...t)},300)}}(),i=e=>({id:e.id,email:e.email,firstName:e.first_name||e.firstName||"",lastName:e.last_name||e.lastName||"",avatar:e.avatar_url||e.avatar,mfaEnabled:e.mfa_enabled??e.mfaEnabled??0,emailVerified:e.email_verified??e.emailVerified??0,passwordUpdatedAt:e.password_updated_at||e.passwordUpdatedAt,settings:e.settings||{},xp:e.xp||0,level:e.level||1,next_level_xp:e.next_level_xp||e.next_level_xp||4,username:e.username,usernameLastChangedAt:e.username_last_changed_at||e.usernameLastChangedAt,createdAt:e.created_at||e.createdAt,updatedAt:e.updated_at||e.updatedAt||(new Date).toISOString()}),r={currentUser:e.ref(null),currentSession:e.ref(null),loadingStates:e.ref({initializing:1,signingIn:0,signingUp:0,signingOut:0,refreshingToken:0,sendingMfaEmail:0,verifyingMfa:0,loadingProfile:0}),isInitialized:e.ref(0),mfaRequired:e.ref(0),mfaSessionId:e.ref(null),availableMfaMethods:e.ref([])};let o=null,s=null;exports.useStrandsAuth=function(){const{getUrl:d,config:c}=t.useStrandsConfig(),{fetch:l,clear:u,invalidate:h}={fetch:n.fetch.bind(n),clear:n.clear.bind(n),invalidate:n.invalidate.bind(n),getStats:n.getStats.bind(n)},{currentUser:f,currentSession:w,loadingStates:y,isInitialized:g,mfaRequired:m,mfaSessionId:p,availableMfaMethods:S}=r,_=()=>{if(f.value=null,w.value=null,m.value=0,p.value=null,S.value=[],"undefined"!=typeof window&&(localStorage.removeItem("strands_auth_session"),localStorage.removeItem("strands_auth_user")),D(),s=null,u(),"undefined"!=typeof window&&c.value?.onSignOutUrl){const e=window.location.pathname+window.location.search,t=c.value.onSignOutUrl;e!==t&&(window.location.href=t)}},T=e.computed(()=>y.value.initializing),E=e.computed(()=>y.value.signingIn),O=e.computed(()=>y.value.signingUp),v=e.computed(()=>y.value.signingOut),A=e.computed(()=>y.value.refreshingToken),$=e.computed(()=>y.value.sendingMfaEmail),N=e.computed(()=>y.value.verifyingMfa);e.computed(()=>y.value.loadingProfile);const b=e.computed(()=>y.value.signingIn||y.value.signingUp||y.value.signingOut||y.value.refreshingToken||y.value.sendingMfaEmail||y.value.verifyingMfa||y.value.loadingProfile),k=e.computed(()=>y.value.initializing||b.value),C=e.computed(()=>{const e=y.value;return e.initializing?"Checking authentication...":e.signingIn?"Signing you in...":e.signingUp?"Creating your account...":e.signingOut?"Signing you out...":e.refreshingToken?"Refreshing session...":e.sendingMfaEmail?"Sending verification code...":e.verifyingMfa?"Verifying code...":e.loadingProfile?"Loading profile...":"Loading..."}),J=()=>{const e={};return w.value?.accessToken&&(e.Authorization=`Bearer ${w.value.accessToken}`),w.value?.refreshToken&&(e["x-refresh-token"]=w.value.refreshToken),e},P=e.computed(()=>null!==f.value),F=async()=>{if(!w.value?.refreshToken)return 0;if(s)return await s;s=(async()=>{y.value.refreshingToken=1;try{const e=await fetch(d("refresh"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:w.value.refreshToken})});if(!e.ok){if(401===e.status)return _(),0;throw new Error(`Token refresh failed: ${e.status} ${e.statusText}`)}const t=await e.json();t.user&&(f.value=i(t.user),f.value&&"undefined"!=typeof window&&localStorage.setItem("strands_auth_user",JSON.stringify(f.value)));const n={accessToken:t.access_token,refreshToken:t.refresh_token,expiresAt:new Date(Date.now()+3e5),userId:t.user?.id||f.value?.id};return w.value=n,"undefined"!=typeof window&&localStorage.setItem("strands_auth_session",JSON.stringify(n)),U(),h(`sessions:${w.value.accessToken.slice(0,20)}`),1}catch(e){return _(),0}finally{y.value.refreshingToken=0}})();const e=await s;return s=null,e},M=async e=>{try{e.user&&(f.value=i(e.user));const t={accessToken:e.access_token,refreshToken:e.refresh_token,expiresAt:new Date(Date.now()+3e5),userId:f.value?.id||e.user?.id};w.value=t,"undefined"!=typeof window&&(localStorage.setItem("strands_auth_session",JSON.stringify(t)),f.value&&localStorage.setItem("strands_auth_user",JSON.stringify(f.value))),U()}catch(t){}},U=()=>{if(o&&clearTimeout(o),!w.value)return;if("undefined"!=typeof document&&"hidden"===document.visibilityState)return;const e=new Date,t=w.value.expiresAt.getTime()-e.getTime()-6e4;t<=0?F():o=setTimeout(async()=>{"undefined"!=typeof document&&"visible"!==document.visibilityState||await F()&&U()},t)},D=()=>{o&&(clearTimeout(o),o=null)},I=async()=>{if(!g.value){y.value.initializing=1;try{if("undefined"!=typeof window){const t=localStorage.getItem("strands_auth_session"),n=localStorage.getItem("strands_auth_user");if(t&&n)try{const e=JSON.parse(t),a=JSON.parse(n);e.expiresAt=new Date(e.expiresAt),e.expiresAt<=new Date&&e.refreshToken?(w.value=e,f.value=a,await F()||_()):e.expiresAt>new Date?(w.value=e,f.value=a,U()):(localStorage.removeItem("strands_auth_session"),localStorage.removeItem("strands_auth_user"))}catch(e){localStorage.removeItem("strands_auth_session"),localStorage.removeItem("strands_auth_user")}}g.value=1,await new Promise(e=>setTimeout(e,50))}catch(e){}finally{y.value.initializing=0}}};"undefined"!=typeof document&&document.addEventListener("visibilitychange",()=>{"visible"===document.visibilityState&&w.value?(U(),j()):"hidden"===document.visibilityState&&D()}),"undefined"!=typeof window&&window.addEventListener("storage",e=>{"strands_auth_session"!==e.key&&"strands_auth_user"!==e.key||!e.newValue&&f.value&&_()});const j=()=>{if("undefined"!=typeof window&&f.value&&w.value){const e=localStorage.getItem("strands_auth_session"),t=localStorage.getItem("strands_auth_user");e&&t||_()}},R=()=>{D(),u()};try{e.getCurrentInstance()&&e.onUnmounted(R)}catch(z){}return g.value||I(),{user:e.computed(()=>f.value),currentUser:e.computed(()=>f.value),currentSession:e.computed(()=>w.value),isAuthenticated:P,isLoading:e.computed(()=>k.value||!g.value),loading:b,loadingMessage:C,isInitializing:T,isSigningIn:E,isSigningUp:O,isSigningOut:v,isRefreshingToken:A,isSendingMfaEmail:$,isVerifyingMfa:N,mfaRequired:e.computed(()=>m.value),mfaSessionId:e.computed(()=>p.value),availableMfaMethods:e.computed(()=>S.value),signIn:async e=>{y.value.signingIn=1;try{m.value=0,p.value=null,S.value=[];const t={"Content-Type":"application/json"};"undefined"!=typeof window&&window.location&&(t.Origin=window.location.origin);const n=await fetch(d("signIn"),{method:"POST",headers:t,body:JSON.stringify(e)});if(!n.ok)throw 401===n.status?new Error("Invalid email or password"):403===n.status?new Error("Please verify your email address before signing in"):new Error(`Sign in failed: ${n.status} ${n.statusText}`);const a=await n.json();if(a.mfa_required){m.value=1,p.value=a.mfa_session_id||null;const e=(a.available_mfa_methods||[]).map(e=>{let t=`${e.device_type.charAt(0).toUpperCase()+e.device_type.slice(1)} Authentication`;return"hardware"===e.device_type?t=e.device_name||"Security Key":"totp"===e.device_type?t=e.device_name||"Authenticator App":"email"===e.device_type&&(t=e.device_name||"Email Verification"),{id:e.device_id,device_type:e.device_type,device_name:e.device_name||t,is_active:1,created_at:(new Date).toISOString(),last_used_at:e.last_used_at,credential_id:e.credential_id,device_info:e.device_info}});return S.value=e,y.value.signingIn=0,a}return await M(a),a}catch(t){throw t}finally{y.value.signingIn=0}},signUp:async e=>{y.value.signingUp=1;try{throw new Error("Sign up not implemented - please integrate with auth SDK")}finally{y.value.signingUp=0}},signOut:async()=>{y.value.signingOut=1;try{D(),s=null,u(),f.value=null,w.value=null,m.value=0,p.value=null,S.value=[],"undefined"!=typeof window&&(localStorage.removeItem("strands_auth_session"),localStorage.removeItem("strands_auth_user"))}finally{y.value.signingOut=0}},refreshToken:F,fetchProfile:async()=>{const e=`profile:${w.value.accessToken.slice(0,20)}`;y.value.loadingProfile=1;try{return await l(e,async()=>{const e=await fetch(d("profile"),{method:"GET",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value?.accessToken}`}});if(!e.ok)throw 401===e.status?new Error("Authentication expired. Please sign in again."):new Error(`Failed to fetch profile: ${e.status} ${e.statusText}`);const t=await e.json();return f.value=i(t),f.value&&"undefined"!=typeof window&&localStorage.setItem("strands_auth_user",JSON.stringify(f.value)),f.value})}finally{y.value.loadingProfile=0}},updateProfile:async e=>{y.value.loadingProfile=1;try{const t=await fetch(d("profile"),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value.accessToken}`},body:JSON.stringify({first_name:e.firstName,last_name:e.lastName})});if(!t.ok)throw 401===t.status?new Error("Authentication expired. Please sign in again."):new Error(`Profile update failed: ${t.status} ${t.statusText}`);const n=await t.json();return f.value=i(n),f.value&&a("strands_auth_user",JSON.stringify(f.value)),f.value}finally{y.value.loadingProfile=0}},updateUserSettings:async e=>{y.value.loadingProfile=1;try{const t=await fetch(d("settings"),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value.accessToken}`},body:JSON.stringify({settings:e})});if(!t.ok)throw 401===t.status?new Error("Authentication expired. Please sign in again."):new Error(`Settings update failed: ${t.status} ${t.statusText}`);const n=await t.json();return f.value=i(n),f.value&&a("strands_auth_user",JSON.stringify(f.value)),f.value}finally{y.value.loadingProfile=0}},changeEmail:async(e,t)=>{y.value.loadingProfile=1;try{const n=await fetch(d("changeEmail"),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value.accessToken}`},body:JSON.stringify({new_email:e,password:t})});if(!n.ok){if(401===n.status)throw new Error("Authentication expired. Please sign in again.");{const e=await n.json().catch(()=>({}));throw new Error(e.message||`Email change failed: ${n.status} ${n.statusText}`)}}const a=await n.json();return f.value&&(f.value={...f.value,email:e,emailVerified:0,updatedAt:(new Date).toISOString()},"undefined"!=typeof window&&localStorage.setItem("strands_auth_user",JSON.stringify(f.value))),a}finally{y.value.loadingProfile=0}},changeUsername:async e=>{y.value.loadingProfile=1;try{const t=await fetch(d("changeUsername"),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value.accessToken}`},body:JSON.stringify({username:e})});if(!t.ok){const e=await t.json().catch(()=>({}));if(409===t.status)throw new Error("Username is already taken");if(e.cooldown_end)throw new Error(`You can only change your username once every 30 days. You can change it again on ${new Date(e.cooldown_end).toLocaleDateString()}`);throw new Error(e.message||`Username change failed: ${t.status} ${t.statusText}`)}const n=await t.json();return f.value&&(f.value={...f.value,username:e,usernameLastChangedAt:(new Date).toISOString(),updatedAt:(new Date).toISOString()},"undefined"!=typeof window&&localStorage.setItem("strands_auth_user",JSON.stringify(f.value))),n}finally{y.value.loadingProfile=0}},getUsernameCooldown:async()=>{const e=await fetch(d("usernameCooldown"),{method:"GET",headers:{Authorization:`Bearer ${w.value.accessToken}`}});if(!e.ok)throw new Error(`Failed to get username cooldown: ${e.status} ${e.statusText}`);return e.json()},checkUsernameAvailability:async e=>{const t=d("checkUsernameAvailability").replace("{username}",encodeURIComponent(e)),n=await fetch(t,{method:"GET",headers:{"Content-Type":"application/json"}});if(!n.ok)throw new Error(`Failed to check username availability: ${n.status} ${n.statusText}`);return n.json()},getUserSessions:async()=>{const e=`sessions:${w.value?.accessToken?.slice(0,20)||"no-token"}`;try{return await l(e,async()=>{const e=J(),t=await fetch(d("sessions"),{method:"GET",headers:e});if(!t.ok)throw await t.text(),new Error(`Failed to get user sessions: ${t.status} ${t.statusText}`);return t.json()},12e4)}catch(t){throw t}},getSessionStats:async()=>{const e=await fetch(d("sessionsStats"),{method:"GET",headers:J()});if(!e.ok)throw new Error(`Failed to get session stats: ${e.status} ${e.statusText}`);return e.json()},revokeSession:async e=>{const t=d("sessionRevoke").replace("{session_id}",encodeURIComponent(e)),n=await fetch(t,{method:"POST",headers:J()});if(!n.ok)throw new Error(`Failed to revoke session: ${n.status} ${n.statusText}`);return 200===n.status},revokeAllOtherSessions:async()=>{const e=await fetch(d("sessionsRevokeAll"),{method:"POST",headers:J()});if(!e.ok)throw new Error(`Failed to revoke all other sessions: ${e.status} ${e.statusText}`);return 200===e.status},initialize:I,setAuthData:M,verifyMfa:async(e,t,n=0)=>{if(!p.value)throw new Error("No MFA session available");y.value.verifyingMfa=1;try{const a=d(n?"mfaBackupCodeVerify":"mfaSigninVerify"),i=n?{mfa_session_id:p.value,backup_code:t}:{mfa_session_id:p.value,device_id:e,code:t},r=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!r.ok){const e=await r.text();let t="MFA verification failed";try{const n=JSON.parse(e);t=n.message||n.error||e}catch{t=e||"MFA verification failed"}throw new Error(t)}const o=await r.json();return m.value=0,p.value=null,S.value=[],await M(o),o}finally{y.value.verifyingMfa=0}},sendMfaEmailCode:async e=>{if(!p.value)throw new Error("No MFA session available");y.value.sendingMfaEmail=1;try{const t=await fetch(d("mfaSigninSendEmail"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({mfa_session_id:p.value,device_id:e})});if(!t.ok){const e=await t.text();let n="Failed to send MFA email code";try{const t=JSON.parse(e);n=t.message||t.error||e}catch{n=e||"Failed to send MFA email code"}throw new Error(n)}return await t.json()}finally{y.value.sendingMfaEmail=0}},getMfaWebAuthnChallenge:async e=>{if(!p.value)throw new Error("No MFA session available");const t=await fetch(d("mfaWebAuthnChallenge"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({mfa_session_id:p.value,device_id:e})});if(!t.ok){const e=await t.text();let n="Failed to get WebAuthn challenge";try{const t=JSON.parse(e);n=t.message||t.error||e}catch{n=e||n}throw new Error(n)}return t.json()},registerHardwareKey:async(e,t,n="hardware")=>{const a=await fetch(d("mfaHardwareStartRegistration"),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value.accessToken}`},body:JSON.stringify({device_name:e,device_type:n})});if(!a.ok){const e=await a.text();let t="Failed to start hardware key registration";try{const n=JSON.parse(e);t=n.message||n.error||e}catch{t=e||"Failed to start hardware key registration"}throw new Error(t)}return a.json()},completeHardwareKeyRegistration:async(e,t,n)=>{const a=await fetch(d("mfaHardwareCompleteRegistration"),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value.accessToken}`},body:JSON.stringify({device_id:e,credential:t})});if(!a.ok){const e=await a.text();let t="Failed to complete hardware key registration";try{const n=JSON.parse(e);t=n.message||n.error||e}catch{t=e||"Failed to complete hardware key registration"}throw new Error(t)}return a.json()},startTokenRefreshTimer:U,stopTokenRefreshTimer:D,getAuthHeaders:J,forceReInit:()=>{g.value=0,y.value.initializing=1,I()}}};
|