@frigear-nu/account 0.1.0 → 0.1.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/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@frigear-nu/account",
3
3
  "configKey": "account",
4
- "version": "0.1.0",
4
+ "version": "0.1.2",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.3",
7
7
  "unbuild": "unknown"
package/dist/module.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { defineNuxtModule, createResolver, hasNuxtModule, addImports, addLayout, addComponentsDir } from '@nuxt/kit';
2
- import { registerModule, registerTailwindSource, registerLocales, resolvePagePath, addModulePages, addOptionalMenuSource } from '@frigear-nu/module-kit/utils';
1
+ import { defineNuxtModule, createResolver, hasNuxtModule, addImports, addLayout, addTypeTemplate, addRouteMiddleware, addPlugin, addComponentsDir } from '@nuxt/kit';
2
+ import { registerModule, registerTailwindSource, registerLocales, resolvePagePath, addModulePages, defineVirtualRegistry, getBootstrapProviders, addOptionalMenuSource } from '@frigear-nu/module-kit/utils';
3
3
 
4
4
  const module$1 = defineNuxtModule({
5
5
  meta: { name: "@frigear-nu/account", configKey: "account" },
@@ -44,8 +44,12 @@ const module$1 = defineNuxtModule({
44
44
  const profilePath = resolvePagePath(settings.pages?.profile, "/account/profile");
45
45
  const securityPath = resolvePagePath(settings.pages?.security, "/account/security");
46
46
  const documentsPath = resolvePagePath(settings.pages?.documents, "/account/documents");
47
- const apiTokensPath = resolvePagePath(settings.pages?.apiTokens, "/account/api-tokens");
47
+ const withApiTokens = nuxt.options.authentication?.apiTokens !== false;
48
+ const apiTokensPath = withApiTokens ? resolvePagePath(settings.pages?.apiTokens, "/account/api-tokens") : void 0;
48
49
  const indexPath = withMenus ? resolvePagePath(settings.pages?.index, "/account") : void 0;
50
+ if (!withApiTokens && settings.pages?.apiTokens) {
51
+ console.warn("[@frigear-nu/account] `settings.pages.apiTokens` was set, but `authentication.apiTokens` is `false` - skipping the API tokens page.");
52
+ }
49
53
  if (!withMenus && settings.pages?.index) {
50
54
  console.warn("[@frigear-nu/account] `settings.pages.index` was set, but `@frigear-nu/menu` is not installed - skipping the dashboard page.");
51
55
  }
@@ -78,6 +82,28 @@ const module$1 = defineNuxtModule({
78
82
  } else {
79
83
  console.warn("[@frigear-nu/account] @frigear-nu/authorization is not installed: the account pages are not guarded. Install it and guard `/account/**` yourself.");
80
84
  }
85
+ defineVirtualRegistry(nuxt, {
86
+ filename: "account/bootstrap.mjs",
87
+ alias: "#account/bootstrap",
88
+ collect: () => getBootstrapProviders(nuxt, "account").map((provider) => provider.key),
89
+ contents: (keys) => `export const keys = ${JSON.stringify(keys)}
90
+ `
91
+ });
92
+ addTypeTemplate({
93
+ filename: "types/account-bootstrap.d.ts",
94
+ getContents: () => `declare module '#account/bootstrap' {
95
+ export const keys: string[]
96
+ }
97
+ export {}
98
+ `
99
+ });
100
+ addRouteMiddleware({ name: "account-bootstrap", path: resolver.resolve("./runtime/app/middleware/bootstrap") });
101
+ addPlugin({ src: resolver.resolve("./runtime/app/plugins/bootstrap.client"), mode: "client" });
102
+ const areaRules = nuxt.options.routeRules ||= {};
103
+ for (const pattern of areaPatterns) {
104
+ const rule = areaRules[pattern] ?? {};
105
+ areaRules[pattern] = { ...rule, appMiddleware: { "account-bootstrap": true, ...rule.appMiddleware } };
106
+ }
81
107
  addOptionalMenuSource(nuxt, { file: resolver.resolve("./runtime/menus"), menu: "account" });
82
108
  addComponentsDir({
83
109
  path: resolver.resolve("./runtime/app/components"),
@@ -10,7 +10,7 @@ export interface AccountNavItem {
10
10
  /**
11
11
  * The account navigation: the items of the `account` menu (so pages other modules add show up too), or, without
12
12
  * `@frigear-nu/menu`, the account pages enabled in `account.settings.pages`. `links` are ready for
13
- * `UNavigationMenu`/`PageTabs` (localised, `active` set, the overview only active on its own path) and `current` is
13
+ * `UNavigationMenu`/`PageSectionSwitcher` (localised, `active` set, the overview only active on its own path) and `current` is
14
14
  * the item the route belongs to. Await it, so the menu is resolved before the page renders on the server.
15
15
  */
16
16
  export declare function useAccountNav(): Promise<{
@@ -39,11 +39,11 @@ const profile = computed(() => {
39
39
  </template>
40
40
 
41
41
  <div class="flex min-w-0 flex-col gap-6 py-8">
42
- <PageTabs
42
+ <PageSectionSwitcher
43
43
  v-if="links.length"
44
44
  class="lg:hidden"
45
45
  :items="links"
46
- :aria-label="t('nav.label', 'Account navigation')"
46
+ :label="t('nav.label', 'Account navigation')"
47
47
  />
48
48
  <AccountHeader />
49
49
  <slot />
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,5 @@
1
+ import { keys } from "#account/bootstrap";
2
+ export default defineNuxtRouteMiddleware(async () => {
3
+ if (!keys.length || !useUserSession().loggedIn.value) return;
4
+ await loadBootstrap("account");
5
+ });
@@ -9,8 +9,8 @@ const format = useFormat();
9
9
  const { copy } = useCopyToClipboard();
10
10
  const overlay = useOverlay();
11
11
  const createSlideover = overlay.create(AccountApiTokenSlideover, { destroyOnClose: true });
12
- const { data: tokens, refresh, status } = await useFetch("/api/auth/tokens", { key: "account-api-tokens", default: () => [] });
13
- const { data: scopes } = await useFetch("/api/auth/scopes", { key: "account-api-token-scopes", default: () => [] });
12
+ const { data: tokens, refresh, status } = await useCachedFetch("/api/auth/tokens", { key: "account-api-tokens", ttl: 6e4 });
13
+ const { data: scopes } = await useCachedFetch("/api/auth/scopes", { key: "account-api-token-scopes" });
14
14
  const revealed = ref(null);
15
15
  async function onCreate() {
16
16
  const created = await createSlideover.open({ scopes: scopes.value ?? [] });
@@ -33,21 +33,20 @@ async function onSubmitPassword(event) {
33
33
  passwordState.newPassword = "";
34
34
  passwordState.confirmNewPassword = "";
35
35
  }
36
- const passkeyList = ref([]);
37
36
  const passkeyName = ref("");
38
37
  const passkeyPending = ref(false);
39
- const passkeysLoading = ref(false);
40
- async function loadPasskeys() {
41
- if (!passkeys) return;
42
- passkeysLoading.value = true;
43
- try {
44
- passkeyList.value = await passkeys.listPasskeys();
45
- } catch (e) {
46
- showError(e, t("passkeys.loadError", "Could not load your passkeys."));
47
- } finally {
48
- passkeysLoading.value = false;
49
- }
50
- }
38
+ const {
39
+ data: passkeyData,
40
+ error: passkeysError,
41
+ loading: passkeysLoading,
42
+ refresh: loadPasskeys
43
+ } = await useCachedFetch("/api/auth/webauthn/credentials", {
44
+ key: () => `account:passkeys:${user.value?.id ?? "anonymous"}`,
45
+ ttl: 0,
46
+ immediate: webAuthnEnabled,
47
+ watch: false
48
+ });
49
+ const passkeyList = computed(() => passkeyData.value ?? []);
51
50
  async function onRegisterPasskey() {
52
51
  passkeyPending.value = true;
53
52
  try {
@@ -83,7 +82,6 @@ async function onRemovePasskey(passkey) {
83
82
  const passkeyMenu = (passkey) => [[
84
83
  { label: t("passkeys.remove", "Remove passkey"), icon: "i-lucide-trash-2", color: "error", onSelect: () => onRemovePasskey(passkey) }
85
84
  ]];
86
- onMounted(loadPasskeys);
87
85
  </script>
88
86
 
89
87
  <template>
@@ -152,7 +150,7 @@ onMounted(loadPasskeys);
152
150
  v-if="passkeys?.isEnabled.value"
153
151
  :title="t('passkeys.title', 'Passkeys')"
154
152
  :description="t('passkeys.description', 'Sign in with your fingerprint, face or device PIN instead of a password.')"
155
- :loading="passkeysLoading && !passkeyList.length"
153
+ :loading="passkeysLoading"
156
154
  flush
157
155
  >
158
156
  <ul
@@ -184,6 +182,14 @@ onMounted(loadPasskeys);
184
182
  />
185
183
  </li>
186
184
  </ul>
185
+ <UEmpty
186
+ v-else-if="passkeysError"
187
+ variant="naked"
188
+ icon="i-lucide-circle-alert"
189
+ :title="t('passkeys.loadError', 'Could not load your passkeys.')"
190
+ :description="errorMessage(passkeysError, '')"
191
+ :actions="[{ label: t('passkeys.tryAgain', 'Try again'), icon: 'i-lucide-rotate-ccw', color: 'neutral', variant: 'outline', onClick: () => loadPasskeys() }]"
192
+ />
187
193
  <UEmpty
188
194
  v-else
189
195
  variant="naked"
@@ -0,0 +1,2 @@
1
+ declare const _default: any;
2
+ export default _default;
@@ -0,0 +1,4 @@
1
+ export default defineNuxtPlugin(() => {
2
+ const { user } = useUserSession();
3
+ watch(() => user.value?.id ?? null, () => resetBootstrap("account"));
4
+ });
@@ -130,7 +130,8 @@
130
130
  "registerError": "Kunne ikke registrere passkey.",
131
131
  "removed": "Passkey fjernet.",
132
132
  "removeError": "Kunne ikke fjerne passkey.",
133
- "loadError": "Kunne ikke hente dine passkeys."
133
+ "loadError": "Kunne ikke hente dine passkeys.",
134
+ "tryAgain": "Prøv igen"
134
135
  }
135
136
  },
136
137
  "signedInAs": "Logget ind som",
@@ -130,7 +130,8 @@
130
130
  "registerError": "Could not register the passkey.",
131
131
  "removed": "Passkey removed.",
132
132
  "removeError": "Could not remove the passkey.",
133
- "loadError": "Could not load your passkeys."
133
+ "loadError": "Could not load your passkeys.",
134
+ "tryAgain": "Try again"
134
135
  }
135
136
  },
136
137
  "signedInAs": "Signed in as",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frigear-nu/account",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "A module for managing the account area (profile, email, password, security) in the main frigear app.",
5
5
  "homepage": "https://docs.frigear.dev/modules/account",
6
6
  "repository": {
@@ -24,10 +24,10 @@
24
24
  "playground"
25
25
  ],
26
26
  "dependencies": {
27
- "@frigear-nu/module-kit": "1.1.0",
28
- "@frigear-nu/auth": "0.1.0",
29
- "@frigear-nu/ui": "0.1.0",
30
- "@frigear-nu/users": "1.1.0",
27
+ "@frigear-nu/module-kit": "1.1.2",
28
+ "@frigear-nu/auth": "0.1.3",
29
+ "@frigear-nu/ui": "0.1.2",
30
+ "@frigear-nu/users": "1.1.3",
31
31
  "@libsql/client": "^0.18.0",
32
32
  "@nuxt/kit": "^4.6.1",
33
33
  "@vueuse/core": "^15.0.0",
@@ -36,10 +36,10 @@
36
36
  "zod": "^4.6.5"
37
37
  },
38
38
  "peerDependencies": {
39
- "@frigear-nu/authorization": "0.1.0",
40
- "@frigear-nu/menu": "0.1.0",
41
- "@frigear-nu/widget": "0.1.0",
42
- "@nuxt/ui": "^4.11.2",
39
+ "@frigear-nu/authorization": "0.1.2",
40
+ "@frigear-nu/menu": "0.1.2",
41
+ "@frigear-nu/widget": "0.1.2",
42
+ "@nuxt/ui": "^4.12.0",
43
43
  "@nuxthub/core": "^0.10.8"
44
44
  },
45
45
  "peerDependenciesMeta": {
@@ -57,15 +57,15 @@
57
57
  }
58
58
  },
59
59
  "devDependencies": {
60
- "@frigear-nu/authorization": "0.1.0",
61
- "@frigear-nu/menu": "0.1.0",
62
- "@frigear-nu/widget": "0.1.0",
60
+ "@frigear-nu/authorization": "0.1.2",
61
+ "@frigear-nu/menu": "0.1.2",
62
+ "@frigear-nu/widget": "0.1.2",
63
63
  "@nuxt/devtools": "^3.4.2",
64
64
  "@nuxt/eslint-config": "^1.17.0",
65
65
  "@nuxt/module-builder": "^1.0.3",
66
66
  "@nuxt/schema": "^4.6.1",
67
67
  "@nuxt/test-utils": "^4.3.2",
68
- "@nuxt/ui": "^4.11.2",
68
+ "@nuxt/ui": "^4.12.0",
69
69
  "@nuxthub/core": "^0.10.8",
70
70
  "@types/node": "^26.6.5",
71
71
  "changelogen": "^0.6.2",