@authsome/ui-vue 1.4.0 → 1.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +232 -0
- package/dist/index.d.ts +333 -16
- package/dist/index.js +48 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# @authsome/ui-vue
|
|
2
|
+
|
|
3
|
+
Vue 3 composables for AuthSome. This package is **headless** — it ships
|
|
4
|
+
composables (`useAuth`, `useClientConfig`, `useUser`, `useOrganizations`,
|
|
5
|
+
`useSessionToken`) and no rendered components, so consumers own all
|
|
6
|
+
markup and styling.
|
|
7
|
+
|
|
8
|
+
## Setup
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
// main.ts
|
|
12
|
+
import { createApp } from "vue";
|
|
13
|
+
import { createAuthPlugin } from "@authsome/ui-vue";
|
|
14
|
+
import App from "./App.vue";
|
|
15
|
+
|
|
16
|
+
const app = createApp(App);
|
|
17
|
+
app.use(
|
|
18
|
+
createAuthPlugin({
|
|
19
|
+
baseURL: "https://api.example.com",
|
|
20
|
+
publishableKey: "pk_…", // required to fetch /v1/client-config
|
|
21
|
+
}),
|
|
22
|
+
);
|
|
23
|
+
app.mount("#app");
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Phase 3B: verification panels and captcha
|
|
27
|
+
|
|
28
|
+
`useAuth` exposes the same surface as `@authsome/ui-react`:
|
|
29
|
+
|
|
30
|
+
- `state.value.status === "verification_pending"` after `signUp()` resolves.
|
|
31
|
+
- `state.value.status === "email_not_verified"` after `signIn()` rejects with
|
|
32
|
+
a backend error of `type: "email_not_verified"`.
|
|
33
|
+
- `resendVerification(email)` calls `POST /v1/verify-email/resend`.
|
|
34
|
+
- `signIn` and `signUp` both accept an optional third/fourth `options` arg of
|
|
35
|
+
shape `{ captchaToken?: string }`.
|
|
36
|
+
- `useClientConfig()` exposes `config.value.captcha = { required, provider, site_key }`
|
|
37
|
+
so the template can decide whether to render Turnstile.
|
|
38
|
+
|
|
39
|
+
### Sign-up template
|
|
40
|
+
|
|
41
|
+
```vue
|
|
42
|
+
<script setup lang="ts">
|
|
43
|
+
import { ref, computed } from "vue";
|
|
44
|
+
import { useAuth, useClientConfig } from "@authsome/ui-vue";
|
|
45
|
+
|
|
46
|
+
const { state, signUp, resendVerification } = useAuth();
|
|
47
|
+
const { config } = useClientConfig();
|
|
48
|
+
|
|
49
|
+
const email = ref("");
|
|
50
|
+
const password = ref("");
|
|
51
|
+
const captchaToken = ref<string | null>(null);
|
|
52
|
+
const error = ref<string | null>(null);
|
|
53
|
+
const isSubmitting = ref(false);
|
|
54
|
+
|
|
55
|
+
const captchaRequired = computed(
|
|
56
|
+
() =>
|
|
57
|
+
config.value?.captcha?.required === true &&
|
|
58
|
+
config.value?.captcha?.provider === "turnstile" &&
|
|
59
|
+
!!config.value?.captcha?.site_key,
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
async function onSubmit() {
|
|
63
|
+
error.value = null;
|
|
64
|
+
isSubmitting.value = true;
|
|
65
|
+
try {
|
|
66
|
+
await signUp(email.value, password.value, undefined, {
|
|
67
|
+
captchaToken: captchaToken.value ?? undefined,
|
|
68
|
+
});
|
|
69
|
+
} catch (e) {
|
|
70
|
+
error.value = e instanceof Error ? e.message : "Sign-up failed";
|
|
71
|
+
} finally {
|
|
72
|
+
isSubmitting.value = false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<template>
|
|
78
|
+
<div v-if="state.status === 'verification_pending'">
|
|
79
|
+
<h2>Check your inbox</h2>
|
|
80
|
+
<p>We sent a verification link to {{ state.email }}.</p>
|
|
81
|
+
<button @click="resendVerification(state.email)">Resend email</button>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<form v-else @submit.prevent="onSubmit">
|
|
85
|
+
<input v-model="email" type="email" required />
|
|
86
|
+
<input v-model="password" type="password" required />
|
|
87
|
+
|
|
88
|
+
<!-- Render Turnstile when captcha is required. See <TurnstileWidget> below. -->
|
|
89
|
+
<TurnstileWidget
|
|
90
|
+
v-if="captchaRequired"
|
|
91
|
+
:site-key="config!.captcha!.site_key!"
|
|
92
|
+
@token="captchaToken = $event"
|
|
93
|
+
/>
|
|
94
|
+
|
|
95
|
+
<button
|
|
96
|
+
type="submit"
|
|
97
|
+
:disabled="isSubmitting || (captchaRequired && !captchaToken)"
|
|
98
|
+
>
|
|
99
|
+
Create account
|
|
100
|
+
</button>
|
|
101
|
+
<p v-if="error">{{ error }}</p>
|
|
102
|
+
</form>
|
|
103
|
+
</template>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Sign-in template
|
|
107
|
+
|
|
108
|
+
```vue
|
|
109
|
+
<script setup lang="ts">
|
|
110
|
+
import { ref } from "vue";
|
|
111
|
+
import { useAuth, useClientConfig } from "@authsome/ui-vue";
|
|
112
|
+
|
|
113
|
+
const { state, signIn, resendVerification } = useAuth();
|
|
114
|
+
const { config } = useClientConfig();
|
|
115
|
+
|
|
116
|
+
const email = ref("");
|
|
117
|
+
const password = ref("");
|
|
118
|
+
const captchaToken = ref<string | null>(null);
|
|
119
|
+
const error = ref<string | null>(null);
|
|
120
|
+
|
|
121
|
+
async function onSubmit() {
|
|
122
|
+
error.value = null;
|
|
123
|
+
try {
|
|
124
|
+
await signIn(email.value, password.value, {
|
|
125
|
+
captchaToken: captchaToken.value ?? undefined,
|
|
126
|
+
});
|
|
127
|
+
} catch (e) {
|
|
128
|
+
// The manager has already promoted email_not_verified errors into
|
|
129
|
+
// state.status === "email_not_verified" — only show the message for
|
|
130
|
+
// unrelated failures.
|
|
131
|
+
if (state.value.status !== "email_not_verified") {
|
|
132
|
+
error.value = e instanceof Error ? e.message : "Sign-in failed";
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
</script>
|
|
137
|
+
|
|
138
|
+
<template>
|
|
139
|
+
<div v-if="state.status === 'email_not_verified'">
|
|
140
|
+
<h2>Verify your email</h2>
|
|
141
|
+
<p>{{ state.email }} hasn't been verified yet.</p>
|
|
142
|
+
<button @click="resendVerification(state.email)">Resend link</button>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<form v-else @submit.prevent="onSubmit">
|
|
146
|
+
<input v-model="email" type="email" required />
|
|
147
|
+
<input v-model="password" type="password" required />
|
|
148
|
+
<TurnstileWidget
|
|
149
|
+
v-if="config?.captcha?.required && config.captcha.provider === 'turnstile' && config.captcha.site_key"
|
|
150
|
+
:site-key="config.captcha.site_key"
|
|
151
|
+
@token="captchaToken = $event"
|
|
152
|
+
/>
|
|
153
|
+
<button type="submit">Sign in</button>
|
|
154
|
+
<p v-if="error">{{ error }}</p>
|
|
155
|
+
</form>
|
|
156
|
+
</template>
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### TurnstileWidget.vue
|
|
160
|
+
|
|
161
|
+
The React package ships a `<TurnstileWidget>` in `@authsome/ui-components`.
|
|
162
|
+
For Vue, drop the following SFC into your project (it loads the Turnstile
|
|
163
|
+
script once and re-uses it across mounts):
|
|
164
|
+
|
|
165
|
+
```vue
|
|
166
|
+
<script setup lang="ts">
|
|
167
|
+
import { onMounted, onBeforeUnmount, ref } from "vue";
|
|
168
|
+
|
|
169
|
+
const props = defineProps<{ siteKey: string }>();
|
|
170
|
+
const emit = defineEmits<{ (e: "token", token: string): void }>();
|
|
171
|
+
|
|
172
|
+
const container = ref<HTMLDivElement | null>(null);
|
|
173
|
+
const widgetId = ref<string | null>(null);
|
|
174
|
+
|
|
175
|
+
const SCRIPT_SRC = "https://challenges.cloudflare.com/turnstile/v0/api.js";
|
|
176
|
+
|
|
177
|
+
declare global {
|
|
178
|
+
interface Window {
|
|
179
|
+
turnstile?: {
|
|
180
|
+
render: (
|
|
181
|
+
el: HTMLElement,
|
|
182
|
+
opts: { sitekey: string; callback: (token: string) => void },
|
|
183
|
+
) => string;
|
|
184
|
+
remove: (id: string) => void;
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function ensureScript(): Promise<void> {
|
|
190
|
+
if (typeof window === "undefined") return Promise.resolve();
|
|
191
|
+
if (window.turnstile) return Promise.resolve();
|
|
192
|
+
if (document.querySelector(`script[src="${SCRIPT_SRC}"]`)) {
|
|
193
|
+
return new Promise((resolve) => {
|
|
194
|
+
const tick = () => (window.turnstile ? resolve() : setTimeout(tick, 50));
|
|
195
|
+
tick();
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
return new Promise((resolve, reject) => {
|
|
199
|
+
const s = document.createElement("script");
|
|
200
|
+
s.src = SCRIPT_SRC;
|
|
201
|
+
s.async = true;
|
|
202
|
+
s.defer = true;
|
|
203
|
+
s.onload = () => resolve();
|
|
204
|
+
s.onerror = () => reject(new Error("Failed to load Turnstile"));
|
|
205
|
+
document.head.appendChild(s);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
onMounted(async () => {
|
|
210
|
+
await ensureScript();
|
|
211
|
+
if (!container.value || !window.turnstile) return;
|
|
212
|
+
widgetId.value = window.turnstile.render(container.value, {
|
|
213
|
+
sitekey: props.siteKey,
|
|
214
|
+
callback: (token) => emit("token", token),
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
onBeforeUnmount(() => {
|
|
219
|
+
if (widgetId.value && window.turnstile) {
|
|
220
|
+
window.turnstile.remove(widgetId.value);
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
</script>
|
|
224
|
+
|
|
225
|
+
<template>
|
|
226
|
+
<div ref="container" />
|
|
227
|
+
</template>
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
The script-load + cleanup logic mirrors
|
|
231
|
+
`ui/packages/components/src/components/turnstile-widget.tsx` in the React
|
|
232
|
+
implementation.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import * as _authsome_ui_core from '@authsome/ui-core';
|
|
2
|
+
import { AuthConfig, User, Session } from '@authsome/ui-core';
|
|
3
|
+
export { APIError, AuthConfig, AuthState, ClientConfig, MFAEnrollment, Member, Organization, Session, TokenStorage, User } from '@authsome/ui-core';
|
|
3
4
|
import * as vue from 'vue';
|
|
4
5
|
import { App, Ref } from 'vue';
|
|
5
6
|
|
|
@@ -14,6 +15,10 @@ import { App, Ref } from 'vue';
|
|
|
14
15
|
declare function createAuthPlugin(config: AuthConfig): {
|
|
15
16
|
install(app: App): void;
|
|
16
17
|
};
|
|
18
|
+
/** Optional second arg for sign-in / sign-up to attach a captcha token. */
|
|
19
|
+
interface AuthSubmitOptions {
|
|
20
|
+
captchaToken?: string;
|
|
21
|
+
}
|
|
17
22
|
/**
|
|
18
23
|
* Main auth composable providing state and actions.
|
|
19
24
|
*/
|
|
@@ -57,11 +62,15 @@ declare function useAuth(): {
|
|
|
57
62
|
readonly status: "unauthenticated";
|
|
58
63
|
} | {
|
|
59
64
|
readonly status: "mfa_required";
|
|
60
|
-
readonly
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
readonly email: string;
|
|
66
|
+
readonly mfaTicket: string;
|
|
67
|
+
readonly availableMethods: readonly string[];
|
|
68
|
+
} | {
|
|
69
|
+
readonly status: "email_not_verified";
|
|
70
|
+
readonly email: string;
|
|
71
|
+
} | {
|
|
72
|
+
readonly status: "verification_pending";
|
|
73
|
+
readonly email: string;
|
|
65
74
|
} | {
|
|
66
75
|
readonly status: "error";
|
|
67
76
|
readonly error: string;
|
|
@@ -104,27 +113,335 @@ declare function useAuth(): {
|
|
|
104
113
|
readonly status: "unauthenticated";
|
|
105
114
|
} | {
|
|
106
115
|
readonly status: "mfa_required";
|
|
107
|
-
readonly
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
readonly email: string;
|
|
117
|
+
readonly mfaTicket: string;
|
|
118
|
+
readonly availableMethods: readonly string[];
|
|
119
|
+
} | {
|
|
120
|
+
readonly status: "email_not_verified";
|
|
121
|
+
readonly email: string;
|
|
122
|
+
} | {
|
|
123
|
+
readonly status: "verification_pending";
|
|
124
|
+
readonly email: string;
|
|
112
125
|
} | {
|
|
113
126
|
readonly status: "error";
|
|
114
127
|
readonly error: string;
|
|
115
128
|
}>>;
|
|
129
|
+
clientConfig: Readonly<Ref<{
|
|
130
|
+
readonly version?: string | undefined;
|
|
131
|
+
readonly app_id?: string | undefined;
|
|
132
|
+
readonly branding?: {
|
|
133
|
+
readonly app_name?: string | undefined;
|
|
134
|
+
readonly logo_url?: string | undefined;
|
|
135
|
+
} | undefined;
|
|
136
|
+
readonly password?: {
|
|
137
|
+
readonly enabled: boolean;
|
|
138
|
+
} | undefined;
|
|
139
|
+
readonly social?: {
|
|
140
|
+
readonly enabled: boolean;
|
|
141
|
+
readonly providers: readonly {
|
|
142
|
+
readonly id: string;
|
|
143
|
+
readonly name: string;
|
|
144
|
+
}[];
|
|
145
|
+
} | undefined;
|
|
146
|
+
readonly passkey?: {
|
|
147
|
+
readonly enabled: boolean;
|
|
148
|
+
} | undefined;
|
|
149
|
+
readonly mfa?: {
|
|
150
|
+
readonly enabled: boolean;
|
|
151
|
+
readonly methods: readonly string[];
|
|
152
|
+
} | undefined;
|
|
153
|
+
readonly magiclink?: {
|
|
154
|
+
readonly enabled: boolean;
|
|
155
|
+
} | undefined;
|
|
156
|
+
readonly sso?: {
|
|
157
|
+
readonly enabled: boolean;
|
|
158
|
+
readonly connections: readonly {
|
|
159
|
+
readonly id: string;
|
|
160
|
+
readonly name: string;
|
|
161
|
+
}[];
|
|
162
|
+
} | undefined;
|
|
163
|
+
readonly supported_plugins?: readonly string[] | undefined;
|
|
164
|
+
readonly signup_fields?: readonly {
|
|
165
|
+
readonly key: string;
|
|
166
|
+
readonly label: string;
|
|
167
|
+
readonly type: string;
|
|
168
|
+
readonly placeholder?: string | undefined;
|
|
169
|
+
readonly description?: string | undefined;
|
|
170
|
+
readonly options?: readonly {
|
|
171
|
+
readonly label: string;
|
|
172
|
+
readonly value: string;
|
|
173
|
+
}[] | undefined;
|
|
174
|
+
readonly default?: string | undefined;
|
|
175
|
+
readonly validation?: {
|
|
176
|
+
readonly required?: boolean | undefined;
|
|
177
|
+
readonly min_len?: number | undefined;
|
|
178
|
+
readonly max_len?: number | undefined;
|
|
179
|
+
readonly pattern?: string | undefined;
|
|
180
|
+
readonly min?: number | undefined;
|
|
181
|
+
readonly max?: number | undefined;
|
|
182
|
+
} | undefined;
|
|
183
|
+
readonly order: number;
|
|
184
|
+
}[] | undefined;
|
|
185
|
+
readonly waitlist?: {
|
|
186
|
+
readonly enabled: boolean;
|
|
187
|
+
} | undefined;
|
|
188
|
+
readonly email_verification?: {
|
|
189
|
+
readonly enabled: boolean;
|
|
190
|
+
readonly required: boolean;
|
|
191
|
+
} | undefined;
|
|
192
|
+
readonly device_authorization?: {
|
|
193
|
+
readonly enabled: boolean;
|
|
194
|
+
} | undefined;
|
|
195
|
+
readonly captcha?: {
|
|
196
|
+
readonly required: boolean;
|
|
197
|
+
readonly provider?: string | undefined;
|
|
198
|
+
readonly site_key?: string | undefined;
|
|
199
|
+
} | undefined;
|
|
200
|
+
} | null, {
|
|
201
|
+
readonly version?: string | undefined;
|
|
202
|
+
readonly app_id?: string | undefined;
|
|
203
|
+
readonly branding?: {
|
|
204
|
+
readonly app_name?: string | undefined;
|
|
205
|
+
readonly logo_url?: string | undefined;
|
|
206
|
+
} | undefined;
|
|
207
|
+
readonly password?: {
|
|
208
|
+
readonly enabled: boolean;
|
|
209
|
+
} | undefined;
|
|
210
|
+
readonly social?: {
|
|
211
|
+
readonly enabled: boolean;
|
|
212
|
+
readonly providers: readonly {
|
|
213
|
+
readonly id: string;
|
|
214
|
+
readonly name: string;
|
|
215
|
+
}[];
|
|
216
|
+
} | undefined;
|
|
217
|
+
readonly passkey?: {
|
|
218
|
+
readonly enabled: boolean;
|
|
219
|
+
} | undefined;
|
|
220
|
+
readonly mfa?: {
|
|
221
|
+
readonly enabled: boolean;
|
|
222
|
+
readonly methods: readonly string[];
|
|
223
|
+
} | undefined;
|
|
224
|
+
readonly magiclink?: {
|
|
225
|
+
readonly enabled: boolean;
|
|
226
|
+
} | undefined;
|
|
227
|
+
readonly sso?: {
|
|
228
|
+
readonly enabled: boolean;
|
|
229
|
+
readonly connections: readonly {
|
|
230
|
+
readonly id: string;
|
|
231
|
+
readonly name: string;
|
|
232
|
+
}[];
|
|
233
|
+
} | undefined;
|
|
234
|
+
readonly supported_plugins?: readonly string[] | undefined;
|
|
235
|
+
readonly signup_fields?: readonly {
|
|
236
|
+
readonly key: string;
|
|
237
|
+
readonly label: string;
|
|
238
|
+
readonly type: string;
|
|
239
|
+
readonly placeholder?: string | undefined;
|
|
240
|
+
readonly description?: string | undefined;
|
|
241
|
+
readonly options?: readonly {
|
|
242
|
+
readonly label: string;
|
|
243
|
+
readonly value: string;
|
|
244
|
+
}[] | undefined;
|
|
245
|
+
readonly default?: string | undefined;
|
|
246
|
+
readonly validation?: {
|
|
247
|
+
readonly required?: boolean | undefined;
|
|
248
|
+
readonly min_len?: number | undefined;
|
|
249
|
+
readonly max_len?: number | undefined;
|
|
250
|
+
readonly pattern?: string | undefined;
|
|
251
|
+
readonly min?: number | undefined;
|
|
252
|
+
readonly max?: number | undefined;
|
|
253
|
+
} | undefined;
|
|
254
|
+
readonly order: number;
|
|
255
|
+
}[] | undefined;
|
|
256
|
+
readonly waitlist?: {
|
|
257
|
+
readonly enabled: boolean;
|
|
258
|
+
} | undefined;
|
|
259
|
+
readonly email_verification?: {
|
|
260
|
+
readonly enabled: boolean;
|
|
261
|
+
readonly required: boolean;
|
|
262
|
+
} | undefined;
|
|
263
|
+
readonly device_authorization?: {
|
|
264
|
+
readonly enabled: boolean;
|
|
265
|
+
} | undefined;
|
|
266
|
+
readonly captcha?: {
|
|
267
|
+
readonly required: boolean;
|
|
268
|
+
readonly provider?: string | undefined;
|
|
269
|
+
readonly site_key?: string | undefined;
|
|
270
|
+
} | undefined;
|
|
271
|
+
} | null>>;
|
|
116
272
|
isAuthenticated: vue.ComputedRef<boolean>;
|
|
117
273
|
isLoading: vue.ComputedRef<boolean>;
|
|
274
|
+
isConfigLoaded: vue.ComputedRef<boolean>;
|
|
118
275
|
user: vue.ComputedRef<User | null>;
|
|
119
276
|
session: vue.ComputedRef<Session | null>;
|
|
120
277
|
error: vue.ComputedRef<string | null>;
|
|
121
|
-
client: AuthClient;
|
|
122
|
-
signIn: (email: string, password: string) => Promise<void>;
|
|
123
|
-
signUp: (email: string, password: string, fields?: Record<string, string
|
|
278
|
+
client: _authsome_ui_core.AuthClient;
|
|
279
|
+
signIn: (email: string, password: string, options?: AuthSubmitOptions) => Promise<void>;
|
|
280
|
+
signUp: (email: string, password: string, fields?: Record<string, string>, options?: AuthSubmitOptions) => Promise<void>;
|
|
124
281
|
signOut: () => Promise<void>;
|
|
282
|
+
resendVerification: (email: string) => Promise<void>;
|
|
125
283
|
submitMFACode: (enrollmentId: string, code: string) => Promise<void>;
|
|
126
284
|
submitRecoveryCode: (code: string) => Promise<void>;
|
|
127
285
|
};
|
|
286
|
+
/**
|
|
287
|
+
* Composable returning the auto-discovered client configuration.
|
|
288
|
+
*
|
|
289
|
+
* Includes feature flags such as `social`, `passkeys`, `email_verification`,
|
|
290
|
+
* and `captcha`. Useful for templates that need to gate UI on backend config.
|
|
291
|
+
*
|
|
292
|
+
* ```ts
|
|
293
|
+
* const { config, isLoaded } = useClientConfig();
|
|
294
|
+
* if (config.value?.captcha?.required) {
|
|
295
|
+
* // render Turnstile
|
|
296
|
+
* }
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
declare function useClientConfig(): {
|
|
300
|
+
config: Readonly<Ref<{
|
|
301
|
+
readonly version?: string | undefined;
|
|
302
|
+
readonly app_id?: string | undefined;
|
|
303
|
+
readonly branding?: {
|
|
304
|
+
readonly app_name?: string | undefined;
|
|
305
|
+
readonly logo_url?: string | undefined;
|
|
306
|
+
} | undefined;
|
|
307
|
+
readonly password?: {
|
|
308
|
+
readonly enabled: boolean;
|
|
309
|
+
} | undefined;
|
|
310
|
+
readonly social?: {
|
|
311
|
+
readonly enabled: boolean;
|
|
312
|
+
readonly providers: readonly {
|
|
313
|
+
readonly id: string;
|
|
314
|
+
readonly name: string;
|
|
315
|
+
}[];
|
|
316
|
+
} | undefined;
|
|
317
|
+
readonly passkey?: {
|
|
318
|
+
readonly enabled: boolean;
|
|
319
|
+
} | undefined;
|
|
320
|
+
readonly mfa?: {
|
|
321
|
+
readonly enabled: boolean;
|
|
322
|
+
readonly methods: readonly string[];
|
|
323
|
+
} | undefined;
|
|
324
|
+
readonly magiclink?: {
|
|
325
|
+
readonly enabled: boolean;
|
|
326
|
+
} | undefined;
|
|
327
|
+
readonly sso?: {
|
|
328
|
+
readonly enabled: boolean;
|
|
329
|
+
readonly connections: readonly {
|
|
330
|
+
readonly id: string;
|
|
331
|
+
readonly name: string;
|
|
332
|
+
}[];
|
|
333
|
+
} | undefined;
|
|
334
|
+
readonly supported_plugins?: readonly string[] | undefined;
|
|
335
|
+
readonly signup_fields?: readonly {
|
|
336
|
+
readonly key: string;
|
|
337
|
+
readonly label: string;
|
|
338
|
+
readonly type: string;
|
|
339
|
+
readonly placeholder?: string | undefined;
|
|
340
|
+
readonly description?: string | undefined;
|
|
341
|
+
readonly options?: readonly {
|
|
342
|
+
readonly label: string;
|
|
343
|
+
readonly value: string;
|
|
344
|
+
}[] | undefined;
|
|
345
|
+
readonly default?: string | undefined;
|
|
346
|
+
readonly validation?: {
|
|
347
|
+
readonly required?: boolean | undefined;
|
|
348
|
+
readonly min_len?: number | undefined;
|
|
349
|
+
readonly max_len?: number | undefined;
|
|
350
|
+
readonly pattern?: string | undefined;
|
|
351
|
+
readonly min?: number | undefined;
|
|
352
|
+
readonly max?: number | undefined;
|
|
353
|
+
} | undefined;
|
|
354
|
+
readonly order: number;
|
|
355
|
+
}[] | undefined;
|
|
356
|
+
readonly waitlist?: {
|
|
357
|
+
readonly enabled: boolean;
|
|
358
|
+
} | undefined;
|
|
359
|
+
readonly email_verification?: {
|
|
360
|
+
readonly enabled: boolean;
|
|
361
|
+
readonly required: boolean;
|
|
362
|
+
} | undefined;
|
|
363
|
+
readonly device_authorization?: {
|
|
364
|
+
readonly enabled: boolean;
|
|
365
|
+
} | undefined;
|
|
366
|
+
readonly captcha?: {
|
|
367
|
+
readonly required: boolean;
|
|
368
|
+
readonly provider?: string | undefined;
|
|
369
|
+
readonly site_key?: string | undefined;
|
|
370
|
+
} | undefined;
|
|
371
|
+
} | null, {
|
|
372
|
+
readonly version?: string | undefined;
|
|
373
|
+
readonly app_id?: string | undefined;
|
|
374
|
+
readonly branding?: {
|
|
375
|
+
readonly app_name?: string | undefined;
|
|
376
|
+
readonly logo_url?: string | undefined;
|
|
377
|
+
} | undefined;
|
|
378
|
+
readonly password?: {
|
|
379
|
+
readonly enabled: boolean;
|
|
380
|
+
} | undefined;
|
|
381
|
+
readonly social?: {
|
|
382
|
+
readonly enabled: boolean;
|
|
383
|
+
readonly providers: readonly {
|
|
384
|
+
readonly id: string;
|
|
385
|
+
readonly name: string;
|
|
386
|
+
}[];
|
|
387
|
+
} | undefined;
|
|
388
|
+
readonly passkey?: {
|
|
389
|
+
readonly enabled: boolean;
|
|
390
|
+
} | undefined;
|
|
391
|
+
readonly mfa?: {
|
|
392
|
+
readonly enabled: boolean;
|
|
393
|
+
readonly methods: readonly string[];
|
|
394
|
+
} | undefined;
|
|
395
|
+
readonly magiclink?: {
|
|
396
|
+
readonly enabled: boolean;
|
|
397
|
+
} | undefined;
|
|
398
|
+
readonly sso?: {
|
|
399
|
+
readonly enabled: boolean;
|
|
400
|
+
readonly connections: readonly {
|
|
401
|
+
readonly id: string;
|
|
402
|
+
readonly name: string;
|
|
403
|
+
}[];
|
|
404
|
+
} | undefined;
|
|
405
|
+
readonly supported_plugins?: readonly string[] | undefined;
|
|
406
|
+
readonly signup_fields?: readonly {
|
|
407
|
+
readonly key: string;
|
|
408
|
+
readonly label: string;
|
|
409
|
+
readonly type: string;
|
|
410
|
+
readonly placeholder?: string | undefined;
|
|
411
|
+
readonly description?: string | undefined;
|
|
412
|
+
readonly options?: readonly {
|
|
413
|
+
readonly label: string;
|
|
414
|
+
readonly value: string;
|
|
415
|
+
}[] | undefined;
|
|
416
|
+
readonly default?: string | undefined;
|
|
417
|
+
readonly validation?: {
|
|
418
|
+
readonly required?: boolean | undefined;
|
|
419
|
+
readonly min_len?: number | undefined;
|
|
420
|
+
readonly max_len?: number | undefined;
|
|
421
|
+
readonly pattern?: string | undefined;
|
|
422
|
+
readonly min?: number | undefined;
|
|
423
|
+
readonly max?: number | undefined;
|
|
424
|
+
} | undefined;
|
|
425
|
+
readonly order: number;
|
|
426
|
+
}[] | undefined;
|
|
427
|
+
readonly waitlist?: {
|
|
428
|
+
readonly enabled: boolean;
|
|
429
|
+
} | undefined;
|
|
430
|
+
readonly email_verification?: {
|
|
431
|
+
readonly enabled: boolean;
|
|
432
|
+
readonly required: boolean;
|
|
433
|
+
} | undefined;
|
|
434
|
+
readonly device_authorization?: {
|
|
435
|
+
readonly enabled: boolean;
|
|
436
|
+
} | undefined;
|
|
437
|
+
readonly captcha?: {
|
|
438
|
+
readonly required: boolean;
|
|
439
|
+
readonly provider?: string | undefined;
|
|
440
|
+
readonly site_key?: string | undefined;
|
|
441
|
+
} | undefined;
|
|
442
|
+
} | null>>;
|
|
443
|
+
isLoaded: vue.ComputedRef<boolean>;
|
|
444
|
+
};
|
|
128
445
|
/**
|
|
129
446
|
* Composable to fetch and reload the user profile.
|
|
130
447
|
*/
|
|
@@ -173,4 +490,4 @@ declare function useOrganizations(): {
|
|
|
173
490
|
*/
|
|
174
491
|
declare function useSessionToken(): vue.ComputedRef<string | null>;
|
|
175
492
|
|
|
176
|
-
export { createAuthPlugin, useAuth, useOrganizations, useSessionToken, useUser };
|
|
493
|
+
export { type AuthSubmitOptions, createAuthPlugin, useAuth, useClientConfig, useOrganizations, useSessionToken, useUser };
|
package/dist/index.js
CHANGED
|
@@ -11,16 +11,24 @@ import {
|
|
|
11
11
|
} from "@authsome/ui-core";
|
|
12
12
|
var AUTH_KEY = /* @__PURE__ */ Symbol("authsome");
|
|
13
13
|
var AUTH_STATE_KEY = /* @__PURE__ */ Symbol("authsome:state");
|
|
14
|
+
var AUTH_CONFIG_KEY = /* @__PURE__ */ Symbol(
|
|
15
|
+
"authsome:config"
|
|
16
|
+
);
|
|
14
17
|
function createAuthPlugin(config) {
|
|
15
18
|
return {
|
|
16
19
|
install(app) {
|
|
17
20
|
const manager = new AuthManager(config);
|
|
18
21
|
const state = ref(manager.getState());
|
|
22
|
+
const clientConfig = ref(manager.getClientConfig());
|
|
19
23
|
manager.subscribe((s) => {
|
|
20
24
|
state.value = s;
|
|
21
25
|
});
|
|
26
|
+
manager.subscribeConfig((c) => {
|
|
27
|
+
clientConfig.value = c;
|
|
28
|
+
});
|
|
22
29
|
app.provide(AUTH_KEY, manager);
|
|
23
30
|
app.provide(AUTH_STATE_KEY, state);
|
|
31
|
+
app.provide(AUTH_CONFIG_KEY, clientConfig);
|
|
24
32
|
void manager.initialize();
|
|
25
33
|
}
|
|
26
34
|
};
|
|
@@ -39,11 +47,20 @@ function useAuthState() {
|
|
|
39
47
|
}
|
|
40
48
|
return state;
|
|
41
49
|
}
|
|
50
|
+
function useAuthConfig() {
|
|
51
|
+
const cfg = inject(AUTH_CONFIG_KEY);
|
|
52
|
+
if (!cfg) {
|
|
53
|
+
throw new Error("useClientConfig requires the authsome plugin. Call app.use(createAuthPlugin(...)).");
|
|
54
|
+
}
|
|
55
|
+
return cfg;
|
|
56
|
+
}
|
|
42
57
|
function useAuth() {
|
|
43
58
|
const manager = useManager();
|
|
44
59
|
const state = useAuthState();
|
|
60
|
+
const clientConfig = useAuthConfig();
|
|
45
61
|
const isAuthenticated = computed(() => state.value.status === "authenticated");
|
|
46
62
|
const isLoading = computed(() => state.value.status === "loading");
|
|
63
|
+
const isConfigLoaded = computed(() => clientConfig.value !== null);
|
|
47
64
|
const user = computed(
|
|
48
65
|
() => state.value.status === "authenticated" ? state.value.user : null
|
|
49
66
|
);
|
|
@@ -53,17 +70,32 @@ function useAuth() {
|
|
|
53
70
|
const error = computed(
|
|
54
71
|
() => state.value.status === "error" ? state.value.error : null
|
|
55
72
|
);
|
|
56
|
-
async function signIn(email, password) {
|
|
57
|
-
await manager.signIn({
|
|
73
|
+
async function signIn(email, password, options) {
|
|
74
|
+
await manager.signIn({
|
|
75
|
+
email,
|
|
76
|
+
password,
|
|
77
|
+
captcha_token: options?.captchaToken
|
|
78
|
+
});
|
|
58
79
|
}
|
|
59
|
-
async function signUp(email, password, fields) {
|
|
80
|
+
async function signUp(email, password, fields, options) {
|
|
60
81
|
const { first_name, last_name, username, ...rest } = fields ?? {};
|
|
61
82
|
const metadata = Object.keys(rest).length > 0 ? rest : void 0;
|
|
62
|
-
await manager.signUp({
|
|
83
|
+
await manager.signUp({
|
|
84
|
+
email,
|
|
85
|
+
password,
|
|
86
|
+
first_name,
|
|
87
|
+
last_name,
|
|
88
|
+
username,
|
|
89
|
+
metadata,
|
|
90
|
+
captcha_token: options?.captchaToken
|
|
91
|
+
});
|
|
63
92
|
}
|
|
64
93
|
async function signOut() {
|
|
65
94
|
await manager.signOut();
|
|
66
95
|
}
|
|
96
|
+
async function resendVerification(email) {
|
|
97
|
+
await manager.resendVerification(email);
|
|
98
|
+
}
|
|
67
99
|
async function submitMFACode(enrollmentId, code) {
|
|
68
100
|
await manager.submitMFACode(enrollmentId, code);
|
|
69
101
|
}
|
|
@@ -72,8 +104,10 @@ function useAuth() {
|
|
|
72
104
|
}
|
|
73
105
|
return {
|
|
74
106
|
state: readonly(state),
|
|
107
|
+
clientConfig: readonly(clientConfig),
|
|
75
108
|
isAuthenticated,
|
|
76
109
|
isLoading,
|
|
110
|
+
isConfigLoaded,
|
|
77
111
|
user,
|
|
78
112
|
session,
|
|
79
113
|
error,
|
|
@@ -81,15 +115,23 @@ function useAuth() {
|
|
|
81
115
|
signIn,
|
|
82
116
|
signUp,
|
|
83
117
|
signOut,
|
|
118
|
+
resendVerification,
|
|
84
119
|
submitMFACode,
|
|
85
120
|
submitRecoveryCode
|
|
86
121
|
};
|
|
87
122
|
}
|
|
123
|
+
function useClientConfig() {
|
|
124
|
+
const config = useAuthConfig();
|
|
125
|
+
const isLoaded = computed(() => config.value !== null);
|
|
126
|
+
return {
|
|
127
|
+
config: readonly(config),
|
|
128
|
+
isLoaded
|
|
129
|
+
};
|
|
130
|
+
}
|
|
88
131
|
function useUser() {
|
|
89
132
|
const { user: authUser, isLoading, session } = useAuth();
|
|
90
133
|
const manager = useManager();
|
|
91
134
|
const localUser = ref(authUser.value);
|
|
92
|
-
const stopWatch = computed(() => authUser.value);
|
|
93
135
|
async function reload() {
|
|
94
136
|
if (!session.value) return;
|
|
95
137
|
const u = await manager.getClient().getMe(session.value.session_token);
|
|
@@ -137,6 +179,7 @@ function useSessionToken() {
|
|
|
137
179
|
export {
|
|
138
180
|
createAuthPlugin,
|
|
139
181
|
useAuth,
|
|
182
|
+
useClientConfig,
|
|
140
183
|
useOrganizations,
|
|
141
184
|
useSessionToken,
|
|
142
185
|
useUser
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/composables.ts"],"sourcesContent":["/**\n * Vue 3 composables for AuthSome authentication.\n *\n * Usage:\n * ```ts\n * // main.ts\n * import { createAuthPlugin } from \"@authsome/ui-vue\";\n * app.use(createAuthPlugin({ baseURL: \"https://api.example.com\" }));\n *\n * // Component.vue\n * import { useAuth, useUser } from \"@authsome/ui-vue\";\n * const { signIn, isAuthenticated, user } = useAuth();\n * ```\n */\n\nimport {\n computed,\n inject,\n onMounted,\n onUnmounted,\n readonly,\n ref,\n type App,\n type InjectionKey,\n type Ref,\n} from \"vue\";\nimport {\n AuthManager,\n type AuthConfig,\n type AuthState,\n type AuthClient,\n type User,\n type Session,\n type Organization,\n type ListResponse,\n} from \"@authsome/ui-core\";\n\n/** Injection key for the AuthManager. */\nconst AUTH_KEY: InjectionKey<AuthManager> = Symbol(\"authsome\");\n\n/** Injection key for the reactive auth state. */\nconst AUTH_STATE_KEY: InjectionKey<Ref<AuthState>> = Symbol(\"authsome:state\");\n\n/**\n * Creates a Vue plugin that provides AuthSome authentication.\n *\n * ```ts\n * const app = createApp(App);\n * app.use(createAuthPlugin({ baseURL: \"https://api.example.com\" }));\n * ```\n */\nexport function createAuthPlugin(config: AuthConfig) {\n return {\n install(app: App) {\n const manager = new AuthManager(config);\n const state = ref<AuthState>(manager.getState());\n\n manager.subscribe((s) => {\n state.value = s;\n });\n\n app.provide(AUTH_KEY, manager);\n app.provide(AUTH_STATE_KEY, state);\n\n // Initialize on mount (hydrate from storage).\n void manager.initialize();\n },\n };\n}\n\nfunction useManager(): AuthManager {\n const manager = inject(AUTH_KEY);\n if (!manager) {\n throw new Error(\"useAuth requires the authsome plugin. Call app.use(createAuthPlugin(...)).\");\n }\n return manager;\n}\n\nfunction useAuthState(): Ref<AuthState> {\n const state = inject(AUTH_STATE_KEY);\n if (!state) {\n throw new Error(\"useAuth requires the authsome plugin. Call app.use(createAuthPlugin(...)).\");\n }\n return state;\n}\n\n/**\n * Main auth composable providing state and actions.\n */\nexport function useAuth() {\n const manager = useManager();\n const state = useAuthState();\n\n const isAuthenticated = computed(() => state.value.status === \"authenticated\");\n const isLoading = computed(() => state.value.status === \"loading\");\n\n const user = computed<User | null>(() =>\n state.value.status === \"authenticated\" ? state.value.user : null,\n );\n\n const session = computed<Session | null>(() =>\n state.value.status === \"authenticated\" ? state.value.session : null,\n );\n\n const error = computed<string | null>(() =>\n state.value.status === \"error\" ? state.value.error : null,\n );\n\n async function signIn(email: string, password: string) {\n await manager.signIn({ email, password });\n }\n\n async function signUp(email: string, password: string, fields?: Record<string, string>) {\n const { first_name, last_name, username, ...rest } = fields ?? {};\n const metadata = Object.keys(rest).length > 0 ? rest : undefined;\n await manager.signUp({ email, password, first_name, last_name, username, metadata });\n }\n\n async function signOut() {\n await manager.signOut();\n }\n\n async function submitMFACode(enrollmentId: string, code: string) {\n await manager.submitMFACode(enrollmentId, code);\n }\n\n async function submitRecoveryCode(code: string) {\n await manager.submitRecoveryCode(code);\n }\n\n return {\n state: readonly(state),\n isAuthenticated,\n isLoading,\n user,\n session,\n error,\n client: manager.getClient(),\n signIn,\n signUp,\n signOut,\n submitMFACode,\n submitRecoveryCode,\n };\n}\n\n/**\n * Composable to fetch and reload the user profile.\n */\nexport function useUser() {\n const { user: authUser, isLoading, session } = useAuth();\n const manager = useManager();\n const localUser = ref<User | null>(authUser.value);\n\n // Keep in sync with auth state.\n const stopWatch = computed(() => authUser.value);\n\n async function reload() {\n if (!session.value) return;\n const u = await manager.getClient().getMe(session.value.session_token);\n localUser.value = u;\n }\n\n return {\n user: computed(() => localUser.value ?? authUser.value),\n isLoading,\n reload,\n };\n}\n\n/**\n * Composable to list organizations.\n */\nexport function useOrganizations() {\n const { session, isAuthenticated } = useAuth();\n const manager = useManager();\n\n const organizations = ref<Organization[]>([]);\n const total = ref(0);\n const isLoading = ref(false);\n\n async function load() {\n if (!session.value) return;\n isLoading.value = true;\n try {\n const res = await manager\n .getClient()\n .listOrganizations(session.value.session_token) as unknown as ListResponse<Organization>;\n organizations.value = res.items;\n total.value = res.total;\n } finally {\n isLoading.value = false;\n }\n }\n\n onMounted(() => {\n if (isAuthenticated.value) {\n void load();\n }\n });\n\n return {\n organizations: readonly(organizations),\n total: readonly(total),\n isLoading: readonly(isLoading),\n reload: load,\n };\n}\n\n/**\n * Composable that returns the current session token.\n */\nexport function useSessionToken() {\n const { session } = useAuth();\n return computed(() => session.value?.session_token ?? null);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/composables.ts"],"sourcesContent":["/**\n * Vue 3 composables for AuthSome authentication.\n *\n * Usage:\n * ```ts\n * // main.ts\n * import { createAuthPlugin } from \"@authsome/ui-vue\";\n * app.use(createAuthPlugin({ baseURL: \"https://api.example.com\" }));\n *\n * // Component.vue\n * import { useAuth, useUser } from \"@authsome/ui-vue\";\n * const { signIn, isAuthenticated, user } = useAuth();\n * ```\n */\n\nimport {\n computed,\n inject,\n onMounted,\n readonly,\n ref,\n type App,\n type InjectionKey,\n type Ref,\n} from \"vue\";\nimport {\n AuthManager,\n type AuthConfig,\n type AuthState,\n type ClientConfig,\n type User,\n type Session,\n type Organization,\n type ListResponse,\n} from \"@authsome/ui-core\";\n\n/** Injection key for the AuthManager. */\nconst AUTH_KEY: InjectionKey<AuthManager> = Symbol(\"authsome\");\n\n/** Injection key for the reactive auth state. */\nconst AUTH_STATE_KEY: InjectionKey<Ref<AuthState>> = Symbol(\"authsome:state\");\n\n/** Injection key for the reactive client config. */\nconst AUTH_CONFIG_KEY: InjectionKey<Ref<ClientConfig | null>> = Symbol(\n \"authsome:config\",\n);\n\n/**\n * Creates a Vue plugin that provides AuthSome authentication.\n *\n * ```ts\n * const app = createApp(App);\n * app.use(createAuthPlugin({ baseURL: \"https://api.example.com\" }));\n * ```\n */\nexport function createAuthPlugin(config: AuthConfig) {\n return {\n install(app: App) {\n const manager = new AuthManager(config);\n const state = ref<AuthState>(manager.getState());\n const clientConfig = ref<ClientConfig | null>(manager.getClientConfig());\n\n manager.subscribe((s) => {\n state.value = s;\n });\n manager.subscribeConfig((c) => {\n clientConfig.value = c;\n });\n\n app.provide(AUTH_KEY, manager);\n app.provide(AUTH_STATE_KEY, state);\n app.provide(AUTH_CONFIG_KEY, clientConfig);\n\n // Initialize on mount (hydrate from storage).\n void manager.initialize();\n },\n };\n}\n\nfunction useManager(): AuthManager {\n const manager = inject(AUTH_KEY);\n if (!manager) {\n throw new Error(\"useAuth requires the authsome plugin. Call app.use(createAuthPlugin(...)).\");\n }\n return manager;\n}\n\nfunction useAuthState(): Ref<AuthState> {\n const state = inject(AUTH_STATE_KEY);\n if (!state) {\n throw new Error(\"useAuth requires the authsome plugin. Call app.use(createAuthPlugin(...)).\");\n }\n return state;\n}\n\nfunction useAuthConfig(): Ref<ClientConfig | null> {\n const cfg = inject(AUTH_CONFIG_KEY);\n if (!cfg) {\n throw new Error(\"useClientConfig requires the authsome plugin. Call app.use(createAuthPlugin(...)).\");\n }\n return cfg;\n}\n\n/** Optional second arg for sign-in / sign-up to attach a captcha token. */\nexport interface AuthSubmitOptions {\n captchaToken?: string;\n}\n\n/**\n * Main auth composable providing state and actions.\n */\nexport function useAuth() {\n const manager = useManager();\n const state = useAuthState();\n const clientConfig = useAuthConfig();\n\n const isAuthenticated = computed(() => state.value.status === \"authenticated\");\n const isLoading = computed(() => state.value.status === \"loading\");\n const isConfigLoaded = computed(() => clientConfig.value !== null);\n\n const user = computed<User | null>(() =>\n state.value.status === \"authenticated\" ? state.value.user : null,\n );\n\n const session = computed<Session | null>(() =>\n state.value.status === \"authenticated\" ? state.value.session : null,\n );\n\n const error = computed<string | null>(() =>\n state.value.status === \"error\" ? state.value.error : null,\n );\n\n async function signIn(\n email: string,\n password: string,\n options?: AuthSubmitOptions,\n ) {\n await manager.signIn({\n email,\n password,\n captcha_token: options?.captchaToken,\n });\n }\n\n async function signUp(\n email: string,\n password: string,\n fields?: Record<string, string>,\n options?: AuthSubmitOptions,\n ) {\n const { first_name, last_name, username, ...rest } = fields ?? {};\n const metadata = Object.keys(rest).length > 0 ? rest : undefined;\n await manager.signUp({\n email,\n password,\n first_name,\n last_name,\n username,\n metadata,\n captcha_token: options?.captchaToken,\n });\n }\n\n async function signOut() {\n await manager.signOut();\n }\n\n async function resendVerification(email: string) {\n await manager.resendVerification(email);\n }\n\n async function submitMFACode(enrollmentId: string, code: string) {\n await manager.submitMFACode(enrollmentId, code);\n }\n\n async function submitRecoveryCode(code: string) {\n await manager.submitRecoveryCode(code);\n }\n\n return {\n state: readonly(state),\n clientConfig: readonly(clientConfig),\n isAuthenticated,\n isLoading,\n isConfigLoaded,\n user,\n session,\n error,\n client: manager.getClient(),\n signIn,\n signUp,\n signOut,\n resendVerification,\n submitMFACode,\n submitRecoveryCode,\n };\n}\n\n/**\n * Composable returning the auto-discovered client configuration.\n *\n * Includes feature flags such as `social`, `passkeys`, `email_verification`,\n * and `captcha`. Useful for templates that need to gate UI on backend config.\n *\n * ```ts\n * const { config, isLoaded } = useClientConfig();\n * if (config.value?.captcha?.required) {\n * // render Turnstile\n * }\n * ```\n */\nexport function useClientConfig() {\n const config = useAuthConfig();\n const isLoaded = computed(() => config.value !== null);\n return {\n config: readonly(config),\n isLoaded,\n };\n}\n\n/**\n * Composable to fetch and reload the user profile.\n */\nexport function useUser() {\n const { user: authUser, isLoading, session } = useAuth();\n const manager = useManager();\n const localUser = ref<User | null>(authUser.value);\n\n async function reload() {\n if (!session.value) return;\n const u = await manager.getClient().getMe(session.value.session_token);\n localUser.value = u;\n }\n\n return {\n user: computed(() => localUser.value ?? authUser.value),\n isLoading,\n reload,\n };\n}\n\n/**\n * Composable to list organizations.\n */\nexport function useOrganizations() {\n const { session, isAuthenticated } = useAuth();\n const manager = useManager();\n\n const organizations = ref<Organization[]>([]);\n const total = ref(0);\n const isLoading = ref(false);\n\n async function load() {\n if (!session.value) return;\n isLoading.value = true;\n try {\n const res = await manager\n .getClient()\n .listOrganizations(session.value.session_token) as unknown as ListResponse<Organization>;\n organizations.value = res.items;\n total.value = res.total;\n } finally {\n isLoading.value = false;\n }\n }\n\n onMounted(() => {\n if (isAuthenticated.value) {\n void load();\n }\n });\n\n return {\n organizations: readonly(organizations),\n total: readonly(total),\n isLoading: readonly(isLoading),\n reload: load,\n };\n}\n\n/**\n * Composable that returns the current session token.\n */\nexport function useSessionToken() {\n const { session } = useAuth();\n return computed(() => session.value?.session_token ?? null);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@authsome/ui-vue",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
4
4
|
"description": "Vue.js bindings for AuthSome authentication UI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"clean": "rm -rf dist"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@authsome/ui-core": "^1.4.
|
|
33
|
+
"@authsome/ui-core": "^1.4.2"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"vue": "^3.3.0"
|