@providame/vue 0.0.8 → 0.0.11
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/CHANGELOG.md +29 -0
- package/README.md +295 -0
- package/dist/components/Can.vue.d.ts +22 -0
- package/dist/components/CreateOrganization.vue.d.ts +14 -0
- package/dist/components/OrganizationProfile.vue.d.ts +14 -0
- package/dist/components/OrganizationSwitcher.vue.d.ts +21 -0
- package/dist/components/Protect.vue.d.ts +2 -0
- package/dist/composables/useCan.d.ts +16 -0
- package/dist/composables/useOrganization.d.ts +36 -0
- package/dist/composables/useOrganizationList.d.ts +29 -0
- package/dist/index.d.ts +7 -0
- package/dist/providame-vue.cjs +1 -1
- package/dist/providame-vue.css +1 -1
- package/dist/providame-vue.js +1888 -1189
- package/package.json +27 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to `@providame/vue`.
|
|
4
|
+
|
|
5
|
+
This file starts at 0.0.10. Earlier releases predate it and are not
|
|
6
|
+
reconstructed here — inventing entries from commit archaeology would look
|
|
7
|
+
authoritative while being guesswork.
|
|
8
|
+
|
|
9
|
+
All five `@providame/*` packages are released in lockstep on a single
|
|
10
|
+
version, so a bump does not imply this package changed — check the entry
|
|
11
|
+
below. A release with no entry changed nothing here.
|
|
12
|
+
|
|
13
|
+
## 0.0.11
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Added `repository`, `homepage` and `bugs` so the npm page links back to
|
|
18
|
+
the source and issue tracker.
|
|
19
|
+
|
|
20
|
+
## 0.0.10
|
|
21
|
+
|
|
22
|
+
No functional changes to this package's own source.
|
|
23
|
+
|
|
24
|
+
It does re-export `Configuration` from `@providame/core`, and that type lost
|
|
25
|
+
three fields this release — see core's changelog. If you import
|
|
26
|
+
`Configuration` from here and read `authorizationEndpoint`, `tokenEndpoint`
|
|
27
|
+
or `userinfoEndpoint`, that will now fail to type-check.
|
|
28
|
+
|
|
29
|
+
### Changed
|
package/README.md
CHANGED
|
@@ -46,6 +46,26 @@ import { SignIn } from '@providame/vue'
|
|
|
46
46
|
</template>
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
### Component props
|
|
50
|
+
|
|
51
|
+
`SignIn`, `SignUp`, and `ResetPassword` all accept `title`, `subtitle`, and
|
|
52
|
+
`logo`. `title` defaults to a branding-aware heading (e.g. "Sign in to Acme")
|
|
53
|
+
once your environment's app name loads; `logo` defaults to your configured
|
|
54
|
+
branding logo.
|
|
55
|
+
|
|
56
|
+
`SignUp` additionally accepts:
|
|
57
|
+
|
|
58
|
+
- `collect-name` (default `true`) — toggles the first/last name fields.
|
|
59
|
+
- `invite-token` — **required** when your environment's signup-policy mode is
|
|
60
|
+
`invite_only`: the single-use code from an admin-generated signup-invite
|
|
61
|
+
email. Read it from your own app's invite-accept URL and pass it through —
|
|
62
|
+
it threads directly to `CreateSignUpParams.inviteToken`. Every other
|
|
63
|
+
signup-policy mode ignores it if set.
|
|
64
|
+
|
|
65
|
+
```vue
|
|
66
|
+
<SignUp :collect-name="false" invite-token="tok_..." />
|
|
67
|
+
```
|
|
68
|
+
|
|
49
69
|
### Modal vs. page usage
|
|
50
70
|
|
|
51
71
|
These components serve two contexts, and the footer link behaves differently
|
|
@@ -77,9 +97,284 @@ The listener takes precedence when both it and a URL are supplied.
|
|
|
77
97
|
import { useSignIn } from '@providame/vue'
|
|
78
98
|
|
|
79
99
|
const signIn = useSignIn()
|
|
100
|
+
|
|
101
|
+
// Preferred — one guarded call for the common identifier + password case.
|
|
80
102
|
await signIn.signInWithPassword('user@example.com', 'password')
|
|
103
|
+
|
|
104
|
+
// Equivalent, longhand — useful if you need to do something between the two
|
|
105
|
+
// steps, e.g. show a separate password screen after the identifier is entered.
|
|
106
|
+
await signIn.create('user@example.com')
|
|
107
|
+
await signIn.attemptFirstFactor({ strategy: 'password', password: 'password' })
|
|
108
|
+
|
|
109
|
+
if (signIn.isComplete.value) {
|
|
110
|
+
// signIn.session.value is set
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Passwordless sign-in & MFA (`useSignIn`)
|
|
115
|
+
|
|
116
|
+
Beyond password sign-in, `useSignIn()` exposes passkey, email-OTP, SMS-OTP,
|
|
117
|
+
and magic-link sign-in, plus mid-flow TOTP enrollment. The prebuilt `<SignIn>`
|
|
118
|
+
above already wires password and passkey sign-in — email-OTP, SMS-OTP, and
|
|
119
|
+
magic-link are **headless-only** escape hatches; build your own UI around
|
|
120
|
+
them if you need one.
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { useSignIn } from '@providame/vue'
|
|
124
|
+
const signIn = useSignIn()
|
|
125
|
+
|
|
126
|
+
// Passkey — opens the flow and immediately prompts navigator.credentials.get().
|
|
127
|
+
await signIn.signInWithPasskey(identifier, window.location.hostname)
|
|
128
|
+
|
|
129
|
+
// Email one-time code (first factor)
|
|
130
|
+
await signIn.signInWithOTPEmail(identifier)
|
|
131
|
+
await signIn.attemptFirstFactor({ strategy: 'otp_email', code })
|
|
132
|
+
await signIn.resendOTPEmail()
|
|
133
|
+
|
|
134
|
+
// SMS one-time code (first factor) — identifier is still email; the code is
|
|
135
|
+
// texted to whatever phone number is on file for that account.
|
|
136
|
+
await signIn.signInWithOTPSMS(identifier)
|
|
137
|
+
await signIn.attemptFirstFactor({ strategy: 'otp_sms', code })
|
|
138
|
+
await signIn.resendOTPSMS()
|
|
139
|
+
|
|
140
|
+
// Magic link — emails a passwordless "click to sign in" link pointing at returnUrl.
|
|
141
|
+
await signIn.signInWithMagicLink(identifier, 'https://myapp.com/auth/callback')
|
|
142
|
+
|
|
143
|
+
// Mid-flow TOTP, reached from any of the strategies above.
|
|
144
|
+
if (signIn.status.value === 'needs_second_factor') {
|
|
145
|
+
await signIn.attemptSecondFactor({ strategy: 'totp', code })
|
|
146
|
+
}
|
|
147
|
+
if (signIn.status.value === 'needs_mfa_enrollment') {
|
|
148
|
+
await signIn.enrollTotp() // -> signIn.enrollment.value = { secret, otpauthUri }
|
|
149
|
+
await signIn.verifyTotpEnrollment(code)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
signIn.reset() // back to "idle"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`firstFactors`/`secondFactors` report what the backend will accept next;
|
|
156
|
+
`recoveryCodes` is populated right after a mid-flow TOTP enrollment.
|
|
157
|
+
|
|
158
|
+
### Sign-up (`useSignUp`)
|
|
159
|
+
|
|
160
|
+
`useSignUp()` mirrors `useSignIn()` — same reactive-state shape, for
|
|
161
|
+
registration. The prebuilt `<SignUp>` already wires all of this; reach for
|
|
162
|
+
the composable directly to build a fully custom sign-up UI.
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import { useSignUp } from '@providame/vue'
|
|
166
|
+
const signUp = useSignUp()
|
|
167
|
+
|
|
168
|
+
await signUp.create({
|
|
169
|
+
email,
|
|
170
|
+
password,
|
|
171
|
+
firstName, // optional
|
|
172
|
+
lastName, // optional
|
|
173
|
+
inviteToken, // required only when signup-policy mode is invite_only
|
|
174
|
+
turnstileToken, // required only when bot-check is enabled for this environment
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
if (signUp.needsVerification.value) {
|
|
178
|
+
await signUp.attemptVerification({ code })
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Post-registration second factor, if the environment's MFA policy requires one.
|
|
182
|
+
if (signUp.status.value === 'needs_second_factor') {
|
|
183
|
+
await signUp.attemptSecondFactor({ strategy: 'totp', code })
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Forced enrollment on a brand-new account.
|
|
187
|
+
if (signUp.status.value === 'needs_mfa_enrollment') {
|
|
188
|
+
await signUp.enrollTotp() // -> signUp.enrollment.value = { secret, otpauthUri }
|
|
189
|
+
await signUp.verifyTotpEnrollment(code)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (signUp.isComplete.value) {
|
|
193
|
+
// signUp.session.value is set; signUp.recoveryCodes.value may carry backup codes
|
|
194
|
+
} else if (signUp.isWaitlisted.value) {
|
|
195
|
+
// verified, but this environment's signup-policy mode is "waitlist" — no session issued yet
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
signUp.reset()
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Password reset (`useResetPassword`, `<ResetPassword>`)
|
|
202
|
+
|
|
203
|
+
```vue
|
|
204
|
+
<script setup>
|
|
205
|
+
import { ResetPassword } from '@providame/vue'
|
|
206
|
+
</script>
|
|
207
|
+
|
|
208
|
+
<template>
|
|
209
|
+
<ResetPassword sign-in-url="/sign-in" @complete="onComplete" />
|
|
210
|
+
</template>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The prebuilt `<SignIn>` already embeds this whole flow behind its "Forgot
|
|
214
|
+
password?" link — mount `<ResetPassword>` on its own route only if you want
|
|
215
|
+
it reachable directly (e.g. from a password-reset email link).
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import { useResetPassword } from '@providame/vue'
|
|
219
|
+
const reset = useResetPassword()
|
|
220
|
+
|
|
221
|
+
await reset.create({ email })
|
|
222
|
+
await reset.attempt({ code, password }) // sets the new password and signs in
|
|
223
|
+
|
|
224
|
+
// Mid-flow TOTP, same shape as useSignIn/useSignUp.
|
|
225
|
+
if (reset.status.value === 'needs_second_factor') {
|
|
226
|
+
await reset.attemptSecondFactor({ strategy: 'totp', code })
|
|
227
|
+
}
|
|
228
|
+
if (reset.status.value === 'needs_mfa_enrollment') {
|
|
229
|
+
await reset.enrollTotp() // -> reset.enrollment.value = { secret, otpauthUri }
|
|
230
|
+
await reset.verifyTotpEnrollment(code)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
await reset.resend() // re-send the reset code
|
|
234
|
+
reset.reset() // back to "idle"
|
|
81
235
|
```
|
|
82
236
|
|
|
237
|
+
`attempt()`/`attemptSecondFactor()`/`enrollTotp()`/`verifyTotpEnrollment()`/`resend()`
|
|
238
|
+
all throw if called before `create()` — there's no flow yet to act on.
|
|
239
|
+
|
|
240
|
+
## What's in the box
|
|
241
|
+
|
|
242
|
+
**Components** — `SignIn`, `SignUp`, `ResetPassword`, `UserButton`, `UserProfile`,
|
|
243
|
+
`SignInButton`, `SignUpButton`, `SignOutButton`, `SignedIn`, `SignedOut`,
|
|
244
|
+
`Protect`, `RedirectToSignIn`, `OrganizationSwitcher`, `OrganizationProfile`,
|
|
245
|
+
`CreateOrganization`.
|
|
246
|
+
|
|
247
|
+
**Composables** — `useAuth`, `useUser`, `useSession`, `useSignIn`, `useSignUp`,
|
|
248
|
+
`useSignInIdp`, `useResetPassword`, `useOrganization`, `useOrganizationList`,
|
|
249
|
+
`useBranding`, `usePasskeyRegistrationLink`, `useProvidame`.
|
|
250
|
+
|
|
251
|
+
Every composable returns `Ref`s — including the functions, matching Clerk's own
|
|
252
|
+
Vue SDK. So it's `await getToken.value()`, not `getToken()`.
|
|
253
|
+
|
|
254
|
+
## Organizations
|
|
255
|
+
|
|
256
|
+
```vue
|
|
257
|
+
<script setup>
|
|
258
|
+
import { OrganizationSwitcher, OrganizationProfile, useOrganization } from '@providame/vue'
|
|
259
|
+
|
|
260
|
+
// Reads token claims — makes no request
|
|
261
|
+
const { organization, roles, permissions, has } = useOrganization()
|
|
262
|
+
</script>
|
|
263
|
+
|
|
264
|
+
<template>
|
|
265
|
+
<OrganizationSwitcher />
|
|
266
|
+
<OrganizationProfile />
|
|
267
|
+
</template>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
`useOrganization()` covers the **active** organization only and never fetches —
|
|
271
|
+
the token already carries `org_id`/`org_slug`/`org_roles`/`org_permissions`.
|
|
272
|
+
`useOrganizationList()` does fetch, since the full membership list isn't in a
|
|
273
|
+
token; it also exposes `setActive(orgId)`.
|
|
274
|
+
|
|
275
|
+
`<OrganizationProfile>` gates each action on the caller's own `org:sys_*`
|
|
276
|
+
permissions rather than a role name, so it works with roles you define yourself.
|
|
277
|
+
Pass `organization-id` to render a specific organization instead of the
|
|
278
|
+
session's active one.
|
|
279
|
+
|
|
280
|
+
`<OrganizationSwitcher>` accepts `hide-create` (drop "Create organization",
|
|
281
|
+
for apps that provision organizations centrally), `personal-label` (relabel
|
|
282
|
+
the no-organization option — Clerk calls it "Personal account"), and
|
|
283
|
+
`hide-personal` (drop that option entirely, for a B2B-only app where every
|
|
284
|
+
user belongs to an organization).
|
|
285
|
+
|
|
286
|
+
## Gating UI
|
|
287
|
+
|
|
288
|
+
```vue
|
|
289
|
+
<Protect role="admin">...</Protect>
|
|
290
|
+
<Protect org-permission="org:sys_memberships:manage">...</Protect>
|
|
291
|
+
<Protect role="admin" org-role="org:admin">...</Protect> <!-- both must pass -->
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
`role`/`permission` gate on environment-level grants; `org-role`/`org-permission`
|
|
295
|
+
gate on the active organization. Omitted props are not constraints. **This is UI
|
|
296
|
+
gating only** — real enforcement is your backend verifying the token, via
|
|
297
|
+
`@providame/backend`.
|
|
298
|
+
|
|
299
|
+
## Object-scoped access
|
|
300
|
+
|
|
301
|
+
`<Protect>` reads claims already in the token — synchronous, free, never fails.
|
|
302
|
+
`<Can>` asks a question about one specific resource — a network call, so it
|
|
303
|
+
needs a loading state and can fail:
|
|
304
|
+
|
|
305
|
+
```vue
|
|
306
|
+
<Can permission="edit" resource="document:42">
|
|
307
|
+
<template #loading><Spinner /></template>
|
|
308
|
+
<template #fallback>You can't edit this document.</template>
|
|
309
|
+
<button>Edit</button>
|
|
310
|
+
</Can>
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Use `<Protect role="admin">` for "is this user an admin"; use `<Can permission="edit" resource="document:42">`
|
|
314
|
+
for "can this user edit *this* document". `<Can>` fails **closed** — a network
|
|
315
|
+
error renders `#fallback`, never the default slot. `useCan({ permission, resource })`
|
|
316
|
+
is the headless equivalent, returning `{ allowed, isLoading, error }`.
|
|
317
|
+
|
|
318
|
+
Answers are cached per-question for the life of the session (cleared on
|
|
319
|
+
sign-in/out and organization switch) — mounting `<Can>` for the same resource
|
|
320
|
+
twice on one page costs one request, not two.
|
|
321
|
+
|
|
322
|
+
`<Can>` also accepts an optional `context` prop (a plain object) that passes
|
|
323
|
+
extra values through for conditional/attribute-based permissions — e.g.
|
|
324
|
+
checking a permission that's only granted below a certain amount or within a
|
|
325
|
+
certain region.
|
|
326
|
+
|
|
327
|
+
## Branding & the underlying client
|
|
328
|
+
|
|
329
|
+
`useBranding()` is called internally by every prebuilt component above —
|
|
330
|
+
reach for it directly only if you're building a fully custom UI and want the
|
|
331
|
+
same environment-configured colors/logo applied to it:
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
import { useBranding } from '@providame/vue'
|
|
335
|
+
|
|
336
|
+
// Fetched once and memoized on the client instance — mounting several
|
|
337
|
+
// components that call this on one page costs a single request, not one
|
|
338
|
+
// per component. Also applies the colors as global CSS custom properties
|
|
339
|
+
// (--providame-accent/-bg/-fg and their -dark counterparts).
|
|
340
|
+
const branding = useBranding() // Ref<Branding>
|
|
341
|
+
|
|
342
|
+
branding.value.logoUrl // string | undefined
|
|
343
|
+
branding.value.primaryColor
|
|
344
|
+
branding.value.appName
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
`useProvidame()` returns the underlying `Providame` client instance every
|
|
348
|
+
composable in this package is built on — every other composable calls this
|
|
349
|
+
internally, so you only need it directly to reach a client method this
|
|
350
|
+
package doesn't already wrap:
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
import { useProvidame } from '@providame/vue'
|
|
354
|
+
|
|
355
|
+
const providame = useProvidame()
|
|
356
|
+
|
|
357
|
+
await providame.configuration() // { turnstileSiteKey, tosUrl, privacyUrl, helpUrl, supportEmail, ... }
|
|
358
|
+
await providame.branding() // same data useBranding() applies automatically
|
|
359
|
+
providame.organizations // OrganizationsApi — used internally by <OrganizationProfile>
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## Theming
|
|
363
|
+
|
|
364
|
+
Every component accepts an `appearance` prop:
|
|
365
|
+
|
|
366
|
+
```vue
|
|
367
|
+
<SignIn :appearance="{ variables: { colorPrimary: '#111827' } }" />
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Components also fetch your environment's configured branding (colors, logo,
|
|
371
|
+
light and dark) on mount and apply it automatically, so an unstyled `appearance`
|
|
372
|
+
prop is usually unnecessary.
|
|
373
|
+
|
|
374
|
+
## Docs
|
|
375
|
+
|
|
376
|
+
Full reference: `/docs/sdk/vue` in your Providame dashboard.
|
|
377
|
+
|
|
83
378
|
## Build
|
|
84
379
|
|
|
85
380
|
```bash
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
type __VLS_Props = {
|
|
2
|
+
permission: string;
|
|
3
|
+
resource?: string;
|
|
4
|
+
context?: Record<string, unknown>;
|
|
5
|
+
};
|
|
6
|
+
declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {};
|
|
7
|
+
type __VLS_Slots = {} & {
|
|
8
|
+
loading?: (props: typeof __VLS_1) => any;
|
|
9
|
+
} & {
|
|
10
|
+
default?: (props: typeof __VLS_3) => any;
|
|
11
|
+
} & {
|
|
12
|
+
fallback?: (props: typeof __VLS_5) => any;
|
|
13
|
+
};
|
|
14
|
+
declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
15
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
16
|
+
declare const _default: typeof __VLS_export;
|
|
17
|
+
export default _default;
|
|
18
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
19
|
+
new (): {
|
|
20
|
+
$slots: S;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import "../styles.css";
|
|
2
|
+
import { type Appearance } from "@providame/core";
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
appearance?: Appearance;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
7
|
+
created: (organizationId: string) => any;
|
|
8
|
+
cancel: () => any;
|
|
9
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
10
|
+
onCreated?: ((organizationId: string) => any) | undefined;
|
|
11
|
+
onCancel?: (() => any) | undefined;
|
|
12
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
13
|
+
declare const _default: typeof __VLS_export;
|
|
14
|
+
export default _default;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import "../styles.css";
|
|
2
|
+
import { type Appearance } from "@providame/core";
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
appearance?: Appearance;
|
|
5
|
+
/** Defaults to the session's ACTIVE organization. */
|
|
6
|
+
organizationId?: string;
|
|
7
|
+
};
|
|
8
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
9
|
+
close: () => any;
|
|
10
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
11
|
+
onClose?: (() => any) | undefined;
|
|
12
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
13
|
+
declare const _default: typeof __VLS_export;
|
|
14
|
+
export default _default;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import "../styles.css";
|
|
2
|
+
import { type Appearance } from "@providame/core";
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
appearance?: Appearance;
|
|
5
|
+
/** Hide the "Create organization" action for apps that provision centrally. */
|
|
6
|
+
hideCreate?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Label for the no-organization option. Clerk calls this "Personal
|
|
9
|
+
* account"; a B2B app with no personal mode can pass its own wording or
|
|
10
|
+
* drop the option entirely with `hidePersonal`.
|
|
11
|
+
*/
|
|
12
|
+
personalLabel?: string;
|
|
13
|
+
hidePersonal?: boolean;
|
|
14
|
+
};
|
|
15
|
+
declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
16
|
+
change: (organizationId: string | null) => any;
|
|
17
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
18
|
+
onChange?: ((organizationId: string | null) => any) | undefined;
|
|
19
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
20
|
+
declare const _default: typeof __VLS_export;
|
|
21
|
+
export default _default;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
import type { CanInput } from "@providame/core";
|
|
3
|
+
export interface UseCanResult {
|
|
4
|
+
/** null while the first check for the current input is still in flight. */
|
|
5
|
+
allowed: Ref<boolean | null>;
|
|
6
|
+
isLoading: Ref<boolean>;
|
|
7
|
+
error: Ref<Error | null>;
|
|
8
|
+
}
|
|
9
|
+
/** Watches a (possibly reactive) authorization question and keeps `allowed`
|
|
10
|
+
* in step with it, re-checking whenever the input changes.
|
|
11
|
+
*
|
|
12
|
+
* A guard against a superseded response overwriting a newer one: input can
|
|
13
|
+
* change again before a request resolves (e.g. a resource prop changing
|
|
14
|
+
* twice quickly), and without tracking which run is current, a slow first
|
|
15
|
+
* response could land after a faster second one and show stale state. */
|
|
16
|
+
export declare function useCan(input: Ref<CanInput>): UseCanResult;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type ComputedRef } from "vue";
|
|
2
|
+
export interface ActiveOrganizationInfo {
|
|
3
|
+
id: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
name: string;
|
|
6
|
+
}
|
|
7
|
+
export interface UseOrganization {
|
|
8
|
+
/** False until the session's claims have loaded. */
|
|
9
|
+
isLoaded: ComputedRef<boolean>;
|
|
10
|
+
/** The active organization, or null when none is set. */
|
|
11
|
+
organization: ComputedRef<ActiveOrganizationInfo | null>;
|
|
12
|
+
/** Roles held in the active organization, across both tiers. */
|
|
13
|
+
roles: ComputedRef<string[]>;
|
|
14
|
+
/** Permissions resolved from those roles. */
|
|
15
|
+
permissions: ComputedRef<string[]>;
|
|
16
|
+
/** Whether the active organization grants a role. */
|
|
17
|
+
has: (params: {
|
|
18
|
+
role?: string;
|
|
19
|
+
permission?: string;
|
|
20
|
+
}) => boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The session's ACTIVE organization, read straight from its access-token
|
|
24
|
+
* claims.
|
|
25
|
+
*
|
|
26
|
+
* Deliberately makes no network request: the token already carries
|
|
27
|
+
* org_id/org_slug/org_name/org_roles/org_permissions, so anything this
|
|
28
|
+
* composable could fetch would be strictly staler than what it already has.
|
|
29
|
+
* Use `useOrganizationList()` to enumerate every organization the user
|
|
30
|
+
* belongs to, or to switch which one is active.
|
|
31
|
+
*
|
|
32
|
+
* Display and gating only — these claims come from an unverified client-side
|
|
33
|
+
* decode. Real authorization belongs on the customer's own backend, which
|
|
34
|
+
* verifies the token's signature first.
|
|
35
|
+
*/
|
|
36
|
+
export declare function useOrganization(): UseOrganization;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
import type { MyOrganization } from "@providame/core";
|
|
3
|
+
export interface UseOrganizationList {
|
|
4
|
+
/** False until the first load completes (successfully or not). */
|
|
5
|
+
isLoaded: Ref<boolean>;
|
|
6
|
+
/** Every organization the signed-in user belongs to, with their own roles. */
|
|
7
|
+
organizations: Ref<MyOrganization[]>;
|
|
8
|
+
/** The most recent load error, or null. */
|
|
9
|
+
error: Ref<Error | null>;
|
|
10
|
+
/** Re-fetches the list. */
|
|
11
|
+
reload: () => Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* Switches the active organization and returns the freshly minted token's
|
|
14
|
+
* claims to the client, so subsequent `useOrganization()` reads reflect
|
|
15
|
+
* the new organization without waiting out the previous token's ~60s life.
|
|
16
|
+
* Pass null to clear.
|
|
17
|
+
*/
|
|
18
|
+
setActive: (organizationId: string | null) => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Every organization the signed-in user belongs to, plus the ability to
|
|
22
|
+
* switch which one is active.
|
|
23
|
+
*
|
|
24
|
+
* Unlike `useOrganization()` — which reads the active organization straight
|
|
25
|
+
* from token claims and makes no request — this fetches
|
|
26
|
+
* `GET /fapi/v1/me/organizations`, since the full membership list is not
|
|
27
|
+
* something a token carries.
|
|
28
|
+
*/
|
|
29
|
+
export declare function useOrganizationList(): UseOrganizationList;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,9 +5,12 @@ export { useSignUp, type UseSignUp } from "./composables/useSignUp.js";
|
|
|
5
5
|
export { useSession, type UseSession } from "./composables/useSession.js";
|
|
6
6
|
export { useResetPassword, type UseResetPassword } from "./composables/useResetPassword.js";
|
|
7
7
|
export { useAuth, type UseAuth } from "./composables/useAuth.js";
|
|
8
|
+
export { useOrganization, type ActiveOrganizationInfo, type UseOrganization, } from "./composables/useOrganization.js";
|
|
9
|
+
export { useOrganizationList, type UseOrganizationList, } from "./composables/useOrganizationList.js";
|
|
8
10
|
export { useUser, type UseUser } from "./composables/useUser.js";
|
|
9
11
|
export { useBranding } from "./composables/useBranding.js";
|
|
10
12
|
export { usePasskeyRegistrationLink, type UsePasskeyRegistrationLink, } from "./composables/usePasskeyRegistrationLink.js";
|
|
13
|
+
export { useCan, type UseCanResult } from "./composables/useCan.js";
|
|
11
14
|
export { default as SignIn } from "./components/SignIn.vue";
|
|
12
15
|
export { default as SignUp } from "./components/SignUp.vue";
|
|
13
16
|
export { default as ResetPassword } from "./components/ResetPassword.vue";
|
|
@@ -20,5 +23,9 @@ export { default as SignInButton } from "./components/SignInButton.vue";
|
|
|
20
23
|
export { default as SignUpButton } from "./components/SignUpButton.vue";
|
|
21
24
|
export { default as SignOutButton } from "./components/SignOutButton.vue";
|
|
22
25
|
export { default as UserProfile } from "./components/UserProfile.vue";
|
|
26
|
+
export { default as OrganizationSwitcher } from "./components/OrganizationSwitcher.vue";
|
|
27
|
+
export { default as OrganizationProfile } from "./components/OrganizationProfile.vue";
|
|
28
|
+
export { default as CreateOrganization } from "./components/CreateOrganization.vue";
|
|
29
|
+
export { default as Can } from "./components/Can.vue";
|
|
23
30
|
export type { Branding, Configuration, EnabledIdp, EndUserInfo, EnrollTotpResponse, FactorKind, FlowStatus, ProvidameOptions, MeUser, Session, } from "@providame/core";
|
|
24
31
|
export { ProvidameError, verifyToken } from "@providame/core";
|