abckit 0.0.51 → 0.0.54

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.d.mts CHANGED
@@ -28,21 +28,11 @@ interface SetupConfig {
28
28
  };
29
29
  }
30
30
  interface AuthClientOptions {
31
- /**
32
- * Base URL for Better Auth client
33
- * Required for Capacitor/mobile apps where the default URL is not http/https
34
- * @example 'https://api.example.com'
35
- */
36
- baseURL?: string;
37
- /**
38
- * Base path for Better Auth API endpoints
39
- * @default '/api/auth'
40
- */
41
- basePath?: string;
42
31
  /**
43
32
  * Enable Capacitor mode for mobile apps
44
- * When enabled, uses Bearer token auth with @capacitor/preferences storage
45
- * @default false
33
+ * Injects Capacitor plugin via better-auth:config:extend hook
34
+ * Uses Bearer token auth with @capacitor/preferences storage
35
+ * @default false (or true if MOBILE_BUILD env is set)
46
36
  */
47
37
  capacitor?: boolean;
48
38
  /**
@@ -197,8 +187,6 @@ declare module '@nuxt/schema' {
197
187
  abckit: {
198
188
  sentry: boolean;
199
189
  auth: {
200
- baseURL?: string;
201
- basePath?: string;
202
190
  capacitor?: boolean;
203
191
  oauthProvider?: boolean;
204
192
  };
package/dist/module.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { updateRuntimeConfig, addTypeTemplate, addServerScanDir, addRouteMiddleware, defineNuxtModule, createResolver } from '@nuxt/kit';
1
+ import { updateRuntimeConfig, addTypeTemplate, addServerScanDir, addPlugin, defineNuxtModule, createResolver } from '@nuxt/kit';
2
2
  import { defu } from 'defu';
3
3
  import { join } from 'node:path';
4
4
  import { networkInterfaces } from 'node:os';
@@ -28,6 +28,7 @@ function getModuleDependencies(nuxt) {
28
28
  const opts = nuxt.options.abckit;
29
29
  const isEnabled = createModuleChecker(opts);
30
30
  return {
31
+ "@onmax/nuxt-better-auth": { optional: false },
31
32
  "@nuxtjs/tailwindcss": { optional: !isEnabled("tailwindcss") },
32
33
  "notivue/nuxt": { optional: !isEnabled("notivue") },
33
34
  "@nuxt/icon": { optional: !isEnabled("icon") },
@@ -97,8 +98,8 @@ const ALIAS_PATHS = {
97
98
  "abckit/components": "./runtime/components",
98
99
  "abckit/shadcn": "./runtime/components/ui",
99
100
  "abckit/composables": "./runtime/composables",
100
- "abckit/middleware": "./runtime/middleware",
101
101
  "abckit/plugins": "./runtime/plugins",
102
+ "abckit/plugins/capacitor": "./runtime/plugins/capacitor-client",
102
103
  "abckit/graphql": "./runtime/graphql",
103
104
  "abckit/stores": "./runtime/stores",
104
105
  "abckit/utils": "./runtime/utils",
@@ -115,6 +116,9 @@ const NPM_TS_PATHS = {
115
116
  const H3_TYPE_TEMPLATE = `
116
117
  declare module 'nitro/h3' {
117
118
  interface H3EventContext {
119
+ /**
120
+ * @deprecated Use getUserSession(event) or requireUserSession(event) from nuxt-better-auth
121
+ */
118
122
  auth: {
119
123
  user: {
120
124
  id: string
@@ -124,7 +128,7 @@ declare module 'nitro/h3' {
124
128
  image?: string
125
129
  createdAt: Date
126
130
  updatedAt: Date
127
- role: string
131
+ role?: string
128
132
  }
129
133
  session: {
130
134
  id: string
@@ -141,8 +145,8 @@ declare module 'nitro/h3' {
141
145
  statusCode: number
142
146
  statusMessage: string
143
147
  }
144
- isPremium: boolean
145
- subscription: any | null
148
+ isPremium?: boolean
149
+ subscription?: any | null
146
150
  }
147
151
  }
148
152
 
@@ -169,9 +173,7 @@ async function setupRuntimeConfig(nuxt, options, isSentryEnabled) {
169
173
  nuxt.options.runtimeConfig.public.abckit = {
170
174
  sentry: isSentryEnabled,
171
175
  auth: {
172
- baseURL: isMobileBuild ? mobileBaseURL : nuxt.options.runtimeConfig.public.abckit?.auth?.baseURL ?? options.auth?.baseURL,
173
- basePath: nuxt.options.runtimeConfig.public.abckit?.auth?.basePath ?? options.auth?.basePath,
174
- capacitor: isMobileBuild,
176
+ capacitor: options.auth?.capacitor ?? isMobileBuild,
175
177
  oauthProvider: options.auth?.oauthProvider ?? false
176
178
  }
177
179
  };
@@ -298,9 +300,9 @@ function setupColorMode(nuxt) {
298
300
  }
299
301
  function setupRouting(nuxt, resolve) {
300
302
  addServerScanDir(resolve("./runtime/server"));
301
- addRouteMiddleware({
302
- name: "auth",
303
- path: resolve("./runtime/middleware/auth")
303
+ addPlugin({
304
+ src: resolve("./runtime/plugins/sentry-user.client"),
305
+ mode: "client"
304
306
  });
305
307
  nuxt.options.routeRules = nuxt.options.routeRules || {};
306
308
  nuxt.options.routeRules["/**"] = defu(nuxt.options.routeRules["/**"] || {}, { ssr: false });
@@ -328,6 +330,32 @@ function setupDevtools(nuxt) {
328
330
  enabled: false
329
331
  });
330
332
  }
333
+ function setupBetterAuth(nuxt, options, _resolve) {
334
+ const isCapacitor = options.auth?.capacitor ?? isMobileBuild;
335
+ nuxt.options["nuxt-better-auth"] = defu(nuxt.options["nuxt-better-auth"] || {}, {
336
+ clientOnly: isCapacitor,
337
+ redirects: {
338
+ login: "/auth/login",
339
+ guest: "/"
340
+ }
341
+ });
342
+ nuxt.hook("better-auth:config:extend", async (config) => {
343
+ const plugins = config.plugins || [];
344
+ const { adminClient } = await import('better-auth/client/plugins');
345
+ plugins.push(adminClient());
346
+ if (isCapacitor) {
347
+ const { capacitorClient } = await import('../dist/runtime/plugins/capacitor-client.js');
348
+ plugins.push(capacitorClient({
349
+ storagePrefix: "better-auth"
350
+ }));
351
+ }
352
+ if (options.auth?.oauthProvider) {
353
+ const { oauthProviderClient } = await import('@better-auth/oauth-provider/client');
354
+ plugins.push(oauthProviderClient());
355
+ }
356
+ config.plugins = plugins;
357
+ });
358
+ }
331
359
 
332
360
  const FEATURE_GROUPS = {
333
361
  core: {
@@ -475,8 +503,6 @@ const module$1 = defineNuxtModule({
475
503
  disk: false
476
504
  },
477
505
  auth: {
478
- baseURL: isMobileBuild ? mobileBaseURL : void 0,
479
- basePath: "/api/auth",
480
506
  capacitor: isMobileBuild
481
507
  },
482
508
  npm: false
@@ -512,6 +538,7 @@ const module$1 = defineNuxtModule({
512
538
  setupTypeScript(nuxt);
513
539
  setupColorMode(nuxt);
514
540
  setupRouting(nuxt, resolve);
541
+ setupBetterAuth(nuxt, options);
515
542
  }
516
543
  });
517
544
 
@@ -68,7 +68,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
68
68
  'aria-expanded'?: (boolean | "true" | "false") | undefined;
69
69
  'aria-flowto'?: string | undefined | undefined;
70
70
  'aria-grabbed'?: (boolean | "true" | "false") | undefined;
71
- 'aria-haspopup'?: "dialog" | "menu" | (boolean | "true" | "false") | "listbox" | "tree" | "grid" | undefined;
71
+ 'aria-haspopup'?: "dialog" | "menu" | "grid" | (boolean | "true" | "false") | "listbox" | "tree" | undefined;
72
72
  'aria-hidden'?: (boolean | "true" | "false") | undefined;
73
73
  'aria-invalid'?: (boolean | "true" | "false") | "grammar" | "spelling" | undefined;
74
74
  'aria-keyshortcuts'?: string | undefined | undefined;
@@ -262,7 +262,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
262
262
  'aria-expanded'?: (boolean | "true" | "false") | undefined;
263
263
  'aria-flowto'?: string | undefined | undefined;
264
264
  'aria-grabbed'?: (boolean | "true" | "false") | undefined;
265
- 'aria-haspopup'?: "dialog" | "menu" | (boolean | "true" | "false") | "listbox" | "tree" | "grid" | undefined;
265
+ 'aria-haspopup'?: "dialog" | "menu" | "grid" | (boolean | "true" | "false") | "listbox" | "tree" | undefined;
266
266
  'aria-hidden'?: (boolean | "true" | "false") | undefined;
267
267
  'aria-invalid'?: (boolean | "true" | "false") | "grammar" | "spelling" | undefined;
268
268
  'aria-keyshortcuts'?: string | undefined | undefined;
@@ -68,7 +68,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
68
68
  'aria-expanded'?: (boolean | "true" | "false") | undefined;
69
69
  'aria-flowto'?: string | undefined | undefined;
70
70
  'aria-grabbed'?: (boolean | "true" | "false") | undefined;
71
- 'aria-haspopup'?: "dialog" | "menu" | (boolean | "true" | "false") | "listbox" | "tree" | "grid" | undefined;
71
+ 'aria-haspopup'?: "dialog" | "menu" | "grid" | (boolean | "true" | "false") | "listbox" | "tree" | undefined;
72
72
  'aria-hidden'?: (boolean | "true" | "false") | undefined;
73
73
  'aria-invalid'?: (boolean | "true" | "false") | "grammar" | "spelling" | undefined;
74
74
  'aria-keyshortcuts'?: string | undefined | undefined;
@@ -262,7 +262,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
262
262
  'aria-expanded'?: (boolean | "true" | "false") | undefined;
263
263
  'aria-flowto'?: string | undefined | undefined;
264
264
  'aria-grabbed'?: (boolean | "true" | "false") | undefined;
265
- 'aria-haspopup'?: "dialog" | "menu" | (boolean | "true" | "false") | "listbox" | "tree" | "grid" | undefined;
265
+ 'aria-haspopup'?: "dialog" | "menu" | "grid" | (boolean | "true" | "false") | "listbox" | "tree" | undefined;
266
266
  'aria-hidden'?: (boolean | "true" | "false") | undefined;
267
267
  'aria-invalid'?: (boolean | "true" | "false") | "grammar" | "spelling" | undefined;
268
268
  'aria-keyshortcuts'?: string | undefined | undefined;
@@ -70,7 +70,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
70
70
  'aria-expanded'?: (boolean | "true" | "false") | undefined;
71
71
  'aria-flowto'?: string | undefined | undefined;
72
72
  'aria-grabbed'?: (boolean | "true" | "false") | undefined;
73
- 'aria-haspopup'?: "dialog" | "menu" | (boolean | "true" | "false") | "listbox" | "tree" | "grid" | undefined;
73
+ 'aria-haspopup'?: "dialog" | "menu" | "grid" | (boolean | "true" | "false") | "listbox" | "tree" | undefined;
74
74
  'aria-hidden'?: (boolean | "true" | "false") | undefined;
75
75
  'aria-invalid'?: (boolean | "true" | "false") | "grammar" | "spelling" | undefined;
76
76
  'aria-keyshortcuts'?: string | undefined | undefined;
@@ -266,7 +266,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
266
266
  'aria-expanded'?: (boolean | "true" | "false") | undefined;
267
267
  'aria-flowto'?: string | undefined | undefined;
268
268
  'aria-grabbed'?: (boolean | "true" | "false") | undefined;
269
- 'aria-haspopup'?: "dialog" | "menu" | (boolean | "true" | "false") | "listbox" | "tree" | "grid" | undefined;
269
+ 'aria-haspopup'?: "dialog" | "menu" | "grid" | (boolean | "true" | "false") | "listbox" | "tree" | undefined;
270
270
  'aria-hidden'?: (boolean | "true" | "false") | undefined;
271
271
  'aria-invalid'?: (boolean | "true" | "false") | "grammar" | "spelling" | undefined;
272
272
  'aria-keyshortcuts'?: string | undefined | undefined;
@@ -70,7 +70,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
70
70
  'aria-expanded'?: (boolean | "true" | "false") | undefined;
71
71
  'aria-flowto'?: string | undefined | undefined;
72
72
  'aria-grabbed'?: (boolean | "true" | "false") | undefined;
73
- 'aria-haspopup'?: "dialog" | "menu" | (boolean | "true" | "false") | "listbox" | "tree" | "grid" | undefined;
73
+ 'aria-haspopup'?: "dialog" | "menu" | "grid" | (boolean | "true" | "false") | "listbox" | "tree" | undefined;
74
74
  'aria-hidden'?: (boolean | "true" | "false") | undefined;
75
75
  'aria-invalid'?: (boolean | "true" | "false") | "grammar" | "spelling" | undefined;
76
76
  'aria-keyshortcuts'?: string | undefined | undefined;
@@ -266,7 +266,7 @@ declare const __VLS_base: import("vue").DefineComponent<{
266
266
  'aria-expanded'?: (boolean | "true" | "false") | undefined;
267
267
  'aria-flowto'?: string | undefined | undefined;
268
268
  'aria-grabbed'?: (boolean | "true" | "false") | undefined;
269
- 'aria-haspopup'?: "dialog" | "menu" | (boolean | "true" | "false") | "listbox" | "tree" | "grid" | undefined;
269
+ 'aria-haspopup'?: "dialog" | "menu" | "grid" | (boolean | "true" | "false") | "listbox" | "tree" | undefined;
270
270
  'aria-hidden'?: (boolean | "true" | "false") | undefined;
271
271
  'aria-invalid'?: (boolean | "true" | "false") | "grammar" | "spelling" | undefined;
272
272
  'aria-keyshortcuts'?: string | undefined | undefined;
@@ -1,11 +1,13 @@
1
- type __VLS_Props = {
2
- error: {
3
- statusCode: number;
4
- statusMessage?: string;
5
- message?: string;
6
- stack?: string;
7
- };
8
- };
9
- declare const __VLS_export: 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>;
10
1
  declare const _default: typeof __VLS_export;
11
2
  export default _default;
3
+ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
4
+ error: {
5
+ type: ObjectConstructor;
6
+ required: true;
7
+ };
8
+ }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
9
+ error: {
10
+ type: ObjectConstructor;
11
+ required: true;
12
+ };
13
+ }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -121,59 +121,59 @@ function switchUser() {
121
121
  </script>
122
122
 
123
123
  <template>
124
- <div class="min-h-dvh bg-gradient-to-br from-background via-background to-muted/30 flex items-center justify-center p-6">
125
- <div class="w-full max-w-md text-center space-y-8">
126
- <!-- Main Content -->
127
- <div class="space-y-6">
124
+ <div class="min-h-dvh bg-background flex flex-col safe-area-inset">
125
+ <!-- Mobile App Header -->
126
+ <header class="shrink-0 pt-safe-top border-b border-border/50">
127
+ <div class="h-14 flex items-center justify-between relative">
128
+ <!-- Back Button -->
129
+ <Button
130
+ variant="ghost"
131
+ size="icon"
132
+ class="size-14 rounded-none active:bg-muted transition-colors"
133
+ @click="goBack"
134
+ >
135
+ <Icon name="lucide:chevron-left" class="size-6" />
136
+ </Button>
137
+
138
+ <!-- Center: Error Code Badge -->
139
+ <div class="absolute left-1/2 -translate-x-1/2">
140
+ <span class="px-3 py-1 bg-destructive/10 text-destructive text-sm font-medium rounded-full">
141
+ {{ error.statusCode }}
142
+ </span>
143
+ </div>
144
+
145
+ <!-- Home Button -->
146
+ <Button
147
+ variant="ghost"
148
+ size="icon"
149
+ class="size-14 rounded-none active:bg-muted transition-colors"
150
+ @click="goHome"
151
+ >
152
+ <Icon name="lucide:home" class="size-5" />
153
+ </Button>
154
+ </div>
155
+ </header>
156
+
157
+ <!-- Main Content -->
158
+ <div class="flex-1 flex flex-col items-center justify-center px-6 pb-8">
159
+ <div class="w-full max-w-sm text-center space-y-6">
128
160
  <!-- Emoji Icon -->
129
- <div class="text-8xl select-none">
161
+ <div class="text-7xl select-none animate-bounce-slow">
130
162
  {{ getErrorData().emoji }}
131
163
  </div>
132
164
 
133
165
  <!-- Title & Description -->
134
- <div class="space-y-4">
135
- <h1 class="text-3xl font-bold text-slate-900 dark:text-slate-100">
166
+ <div class="space-y-3">
167
+ <h1 class="text-2xl font-bold text-foreground">
136
168
  {{ getErrorData().title }}
137
169
  </h1>
138
- <p class="text-lg text-slate-600 dark:text-slate-300 leading-relaxed">
170
+ <p class="text-base text-muted-foreground leading-relaxed">
139
171
  {{ getErrorData().description }}
140
172
  </p>
141
173
  </div>
142
174
 
143
- <!-- Action Buttons -->
144
- <div class="space-y-3 pt-4">
145
- <Button
146
- size="lg"
147
- class="w-full h-12 text-base font-medium"
148
- @click="getErrorData().primaryHandler"
149
- >
150
- {{ getErrorData().primaryAction }}
151
- </Button>
152
-
153
- <div class="flex gap-3">
154
- <Button
155
- variant="outline"
156
- size="lg"
157
- class="flex-1 h-12"
158
- @click="goHome"
159
- >
160
- <Icon name="lucide:home" class="mr-2 size-4" />
161
- Home
162
- </Button>
163
- <Button
164
- variant="outline"
165
- size="lg"
166
- class="flex-1 h-12"
167
- @click="goBack"
168
- >
169
- <Icon name="lucide:arrow-left" class="mr-2 size-4" />
170
- Back
171
- </Button>
172
- </div>
173
- </div>
174
-
175
- <!-- Help Text -->
176
- <div class="bg-white/60 dark:bg-slate-800/60 backdrop-blur-sm rounded-xl p-4 text-sm text-slate-600 dark:text-slate-400">
175
+ <!-- Help Text Card -->
176
+ <div class="bg-muted/50 rounded-2xl p-4 text-sm text-muted-foreground">
177
177
  <template v-if="error.statusCode === 401">
178
178
  Your session has ended for security purposes. Please sign in again.
179
179
  </template>
@@ -188,118 +188,78 @@ function switchUser() {
188
188
  </template>
189
189
  </div>
190
190
  </div>
191
+ </div>
192
+
193
+ <!-- Bottom Action Button (Mobile App Style) -->
194
+ <div class="flex-shrink-0 px-6 pb-6 pt-2 space-y-4">
195
+ <Button
196
+ size="lg"
197
+ class="w-full h-14 text-base font-semibold rounded-2xl"
198
+ @click="getErrorData().primaryHandler"
199
+ >
200
+ {{ getErrorData().primaryAction }}
201
+ </Button>
191
202
 
192
203
  <!-- Debug Panel (Development Only) -->
193
- <div v-if="isDev" class="mt-8">
194
- <details class="bg-slate-100 dark:bg-slate-800 rounded-xl border border-slate-200 dark:border-slate-700">
195
- <summary class="cursor-pointer p-4 font-medium text-slate-700 dark:text-slate-300 flex items-center justify-between hover:bg-slate-50 dark:hover:bg-slate-750 rounded-xl">
196
- <div class="flex items-center gap-2">
197
- <Icon name="lucide:bug" class="size-4" />
198
- Debug Information
199
- </div>
200
- <Button
201
- size="sm"
202
- variant="outline"
203
- class="h-8 px-3 text-xs"
204
- @click.stop="copyDebugInfo"
205
- >
206
- <Icon name="lucide:copy" class="size-3 mr-1" />
207
- {{ copied ? "Copied!" : "Copy All" }}
208
- </Button>
209
- </summary>
210
- <div class="p-4 pt-0 space-y-4 text-sm">
211
- <!-- Quick Info -->
212
- <div class="grid grid-cols-2 gap-4 p-3 bg-slate-50 dark:bg-slate-900 rounded-lg">
213
- <div>
214
- <div class="font-medium text-slate-500 dark:text-slate-400 text-xs mb-1">
215
- Status Code
216
- </div>
217
- <div class="font-mono text-slate-900 dark:text-slate-100">
218
- {{ error.statusCode }}
219
- </div>
204
+ <details v-if="isDev" class="bg-muted rounded-2xl">
205
+ <summary class="cursor-pointer p-4 font-medium text-muted-foreground flex items-center justify-between rounded-2xl">
206
+ <div class="flex items-center gap-2">
207
+ <Icon name="lucide:bug" class="size-4" />
208
+ Debug
209
+ </div>
210
+ <Button
211
+ size="sm"
212
+ variant="outline"
213
+ class="h-8 px-3 text-xs rounded-xl"
214
+ @click.stop="copyDebugInfo"
215
+ >
216
+ <Icon name="lucide:copy" class="size-3 mr-1" />
217
+ {{ copied ? "Copied!" : "Copy" }}
218
+ </Button>
219
+ </summary>
220
+ <div class="px-4 pb-4 space-y-3 text-sm">
221
+ <div class="grid grid-cols-2 gap-3 p-3 bg-background rounded-xl">
222
+ <div>
223
+ <div class="text-muted-foreground text-xs mb-1">
224
+ Status
220
225
  </div>
221
- <div>
222
- <div class="font-medium text-slate-500 dark:text-slate-400 text-xs mb-1">
223
- Timestamp
224
- </div>
225
- <div class="font-mono text-slate-900 dark:text-slate-100 text-xs">
226
- {{ (/* @__PURE__ */ new Date()).toLocaleString() }}
227
- </div>
226
+ <div class="font-mono text-foreground">
227
+ {{ error.statusCode }}
228
228
  </div>
229
229
  </div>
230
-
231
- <!-- Error Details -->
232
- <div class="space-y-3">
233
- <div v-if="error.statusMessage">
234
- <div class="flex items-center justify-between mb-2">
235
- <span class="font-medium text-slate-600 dark:text-slate-400">Status Message</span>
236
- <Button
237
- size="sm"
238
- variant="ghost"
239
- class="h-6 w-6 p-0"
240
- @click="copyToClipboard(error.statusMessage || '')"
241
- >
242
- <Icon name="lucide:copy" class="size-3" />
243
- </Button>
244
- </div>
245
- <div class="p-2 bg-slate-50 dark:bg-slate-900 rounded font-mono text-xs break-all">
246
- {{ error.statusMessage }}
247
- </div>
230
+ <div>
231
+ <div class="text-muted-foreground text-xs mb-1">
232
+ Time
248
233
  </div>
249
-
250
- <div v-if="error.message">
251
- <div class="flex items-center justify-between mb-2">
252
- <span class="font-medium text-slate-600 dark:text-slate-400">Error Message</span>
253
- <Button
254
- size="sm"
255
- variant="ghost"
256
- class="h-6 w-6 p-0"
257
- @click="copyToClipboard(error.message || '')"
258
- >
259
- <Icon name="lucide:copy" class="size-3" />
260
- </Button>
261
- </div>
262
- <div class="p-2 bg-slate-50 dark:bg-slate-900 rounded font-mono text-xs break-all">
263
- {{ error.message }}
264
- </div>
234
+ <div class="font-mono text-foreground text-xs">
235
+ {{ (/* @__PURE__ */ new Date()).toLocaleTimeString() }}
265
236
  </div>
237
+ </div>
238
+ </div>
266
239
 
267
- <div v-if="error.stack">
268
- <div class="flex items-center justify-between mb-2">
269
- <span class="font-medium text-slate-600 dark:text-slate-400">Stack Trace</span>
270
- <Button
271
- size="sm"
272
- variant="ghost"
273
- class="h-6 w-6 p-0"
274
- @click="copyToClipboard(error.stack || '')"
275
- >
276
- <Icon name="lucide:copy" class="size-3" />
277
- </Button>
278
- </div>
279
- <pre class="text-xs bg-slate-50 dark:bg-slate-900 rounded p-3 overflow-auto max-h-40 font-mono border">{{ error.stack }}</pre>
280
- </div>
240
+ <div v-if="error.message" class="space-y-2">
241
+ <div class="flex items-center justify-between">
242
+ <span class="text-muted-foreground text-xs">Message</span>
243
+ <Button size="sm" variant="ghost" class="size-6 p-0" @click="copyToClipboard(error.message || '')">
244
+ <Icon name="lucide:copy" class="size-3" />
245
+ </Button>
246
+ </div>
247
+ <div class="p-2 bg-background rounded-xl font-mono text-xs break-all">
248
+ {{ error.message }}
249
+ </div>
250
+ </div>
281
251
 
282
- <!-- URL Info -->
283
- <div>
284
- <div class="flex items-center justify-between mb-2">
285
- <span class="font-medium text-slate-600 dark:text-slate-400">Current URL</span>
286
- <Button
287
- size="sm"
288
- variant="ghost"
289
- class="h-6 w-6 p-0"
290
- @click="copyToClipboard(currentUrl)"
291
- >
292
- <Icon name="lucide:copy" class="size-3" />
293
- </Button>
294
- </div>
295
- <div class="p-2 bg-slate-50 dark:bg-slate-900 rounded font-mono text-xs break-all">
296
- {{ currentUrl }}
297
- </div>
298
- </div>
252
+ <div v-if="error.stack" class="space-y-2">
253
+ <div class="flex items-center justify-between">
254
+ <span class="text-muted-foreground text-xs">Stack</span>
255
+ <Button size="sm" variant="ghost" class="size-6 p-0" @click="copyToClipboard(error.stack || '')">
256
+ <Icon name="lucide:copy" class="size-3" />
257
+ </Button>
299
258
  </div>
259
+ <pre class="text-xs bg-background rounded-xl p-3 overflow-auto max-h-32 font-mono">{{ error.stack }}</pre>
300
260
  </div>
301
- </details>
302
- </div>
261
+ </div>
262
+ </details>
303
263
  </div>
304
264
  </div>
305
265
  </template>
@@ -1,11 +1,13 @@
1
- type __VLS_Props = {
2
- error: {
3
- statusCode: number;
4
- statusMessage?: string;
5
- message?: string;
6
- stack?: string;
7
- };
8
- };
9
- declare const __VLS_export: 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>;
10
1
  declare const _default: typeof __VLS_export;
11
2
  export default _default;
3
+ declare const __VLS_export: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
4
+ error: {
5
+ type: ObjectConstructor;
6
+ required: true;
7
+ };
8
+ }>, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
9
+ error: {
10
+ type: ObjectConstructor;
11
+ required: true;
12
+ };
13
+ }>> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Sentry user tracking plugin
3
+ * Sets user context in Sentry when authenticated
4
+ */
5
+ declare const _default: import("#app").Plugin<Record<string, unknown>> & import("#app").ObjectPlugin<Record<string, unknown>>;
6
+ export default _default;
@@ -0,0 +1,13 @@
1
+ import { defineNuxtPlugin, useRuntimeConfig } from "#app";
2
+ import { useUserSession } from "#imports";
3
+ import { watch } from "vue";
4
+ export default defineNuxtPlugin(() => {
5
+ const config = useRuntimeConfig();
6
+ if (!config.public.abckit?.sentry)
7
+ return;
8
+ const { user } = useUserSession();
9
+ watch(user, async (currentUser) => {
10
+ const Sentry = await import("@sentry/nuxt");
11
+ Sentry.setUser(currentUser ? { id: currentUser.id } : null);
12
+ }, { immediate: true });
13
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "abckit",
3
3
  "type": "module",
4
- "version": "0.0.51",
4
+ "version": "0.0.54",
5
5
  "description": "Nuxt 4 module — UI components, auth, storage, GraphQL",
6
6
  "author": "productdevbook",
7
7
  "license": "MIT",
@@ -62,6 +62,10 @@
62
62
  "./colada": {
63
63
  "types": "./dist/runtime/colada.options.d.ts",
64
64
  "import": "./dist/runtime/colada.options.js"
65
+ },
66
+ "./plugins/capacitor": {
67
+ "types": "./dist/runtime/plugins/capacitor-client.d.ts",
68
+ "import": "./dist/runtime/plugins/capacitor-client.js"
65
69
  }
66
70
  },
67
71
  "types": "./dist/module.d.mts",
@@ -77,6 +81,7 @@
77
81
  },
78
82
  "peerDependencies": {
79
83
  "@better-auth/oauth-provider": "^1.4.12",
84
+ "@onmax/nuxt-better-auth": "^0.0.2-alpha.17",
80
85
  "@capacitor/android": "^8.0.1",
81
86
  "@capacitor/app": "^8.0.0",
82
87
  "@capacitor/browser": "^8.0.0",
@@ -154,6 +159,9 @@
154
159
  "@better-auth/oauth-provider": {
155
160
  "optional": true
156
161
  },
162
+ "@onmax/nuxt-better-auth": {
163
+ "optional": true
164
+ },
157
165
  "@capacitor/android": {
158
166
  "optional": true
159
167
  },