@authon/nuxt 0.1.19 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -24,6 +24,7 @@ __export(index_exports, {
24
24
  createAuthMiddleware: () => createAuthMiddleware,
25
25
  createAuthonPlugin: () => createAuthonPlugin,
26
26
  default: () => authonModule,
27
+ renderSocialButtons: () => renderSocialButtons,
27
28
  useAuthon: () => useAuthon,
28
29
  useUser: () => useUser
29
30
  });
@@ -92,11 +93,121 @@ function createAuthMiddleware(authon, redirectTo = "/sign-in") {
92
93
  return void 0;
93
94
  };
94
95
  }
96
+
97
+ // src/SocialButtons.ts
98
+ var import_shared = require("@authon/shared");
99
+ var import_js2 = require("@authon/js");
100
+ function renderSocialButtons(options) {
101
+ const {
102
+ client,
103
+ container,
104
+ onSuccess,
105
+ onError,
106
+ compact = false,
107
+ gap,
108
+ labels,
109
+ iconSize,
110
+ borderRadius = 10,
111
+ height = 48,
112
+ size = 48
113
+ } = options;
114
+ const resolvedGap = gap ?? (compact ? 12 : 10);
115
+ const resolvedIconSize = iconSize ?? (compact ? 24 : 20);
116
+ let loadingProvider = null;
117
+ let buttons = [];
118
+ const handleClick = async (provider, btn) => {
119
+ if (loadingProvider) return;
120
+ loadingProvider = provider;
121
+ btn.innerHTML = '<span style="display:inline-block;width:16px;height:16px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:authon-spin 0.6s linear infinite"></span>';
122
+ buttons.forEach((b) => b.disabled = true);
123
+ try {
124
+ await client.signInWithOAuth(provider);
125
+ onSuccess?.();
126
+ } catch (e) {
127
+ const error = e instanceof Error ? e : new Error(String(e));
128
+ onError?.(error);
129
+ } finally {
130
+ loadingProvider = null;
131
+ renderButtons(providers);
132
+ }
133
+ };
134
+ let providers = [];
135
+ function renderButtons(providerList) {
136
+ container.innerHTML = "";
137
+ buttons = [];
138
+ if (!document.getElementById("authon-spin-style")) {
139
+ const style = document.createElement("style");
140
+ style.id = "authon-spin-style";
141
+ style.textContent = "@keyframes authon-spin{to{transform:rotate(360deg)}}";
142
+ document.head.appendChild(style);
143
+ }
144
+ const wrapper = document.createElement("div");
145
+ wrapper.style.display = "flex";
146
+ wrapper.style.gap = `${resolvedGap}px`;
147
+ if (compact) {
148
+ wrapper.style.flexDirection = "row";
149
+ wrapper.style.flexWrap = "wrap";
150
+ wrapper.style.justifyContent = "center";
151
+ } else {
152
+ wrapper.style.flexDirection = "column";
153
+ }
154
+ for (const provider of providerList) {
155
+ const colors = import_shared.PROVIDER_COLORS[provider] || { bg: "#333", text: "#fff" };
156
+ const displayName = import_shared.PROVIDER_DISPLAY_NAMES[provider] || provider;
157
+ const config = (0, import_js2.getProviderButtonConfig)(provider);
158
+ const iconSvg = config.iconSvg.replace(/width="\d+"/, `width="${resolvedIconSize}"`).replace(/height="\d+"/, `height="${resolvedIconSize}"`);
159
+ const needsBorder = colors.bg.toLowerCase() === "#ffffff";
160
+ const btn = document.createElement("button");
161
+ btn.setAttribute("aria-label", `Sign in with ${displayName}`);
162
+ btn.style.display = "flex";
163
+ btn.style.alignItems = "center";
164
+ btn.style.justifyContent = "center";
165
+ btn.style.border = needsBorder ? "1px solid #dadce0" : "none";
166
+ btn.style.cursor = "pointer";
167
+ btn.style.backgroundColor = colors.bg;
168
+ btn.style.color = colors.text;
169
+ btn.style.borderRadius = `${borderRadius}px`;
170
+ btn.style.transition = "opacity 0.15s";
171
+ btn.style.fontFamily = "inherit";
172
+ if (compact) {
173
+ btn.style.width = `${size}px`;
174
+ btn.style.height = `${size}px`;
175
+ btn.style.padding = "0";
176
+ btn.innerHTML = `<span style="display:flex;align-items:center">${iconSvg}</span>`;
177
+ } else {
178
+ btn.style.width = "100%";
179
+ btn.style.height = `${height}px`;
180
+ btn.style.gap = "10px";
181
+ btn.style.paddingLeft = "16px";
182
+ btn.style.paddingRight = "16px";
183
+ const buttonLabel = labels?.[provider] ?? `Continue with ${displayName}`;
184
+ btn.innerHTML = `<span style="display:flex;align-items:center;flex-shrink:0">${iconSvg}</span><span style="font-size:15px;font-weight:600;white-space:nowrap">${buttonLabel}</span>`;
185
+ }
186
+ btn.addEventListener("click", () => handleClick(provider, btn));
187
+ btn.addEventListener("mouseenter", () => btn.style.opacity = "0.85");
188
+ btn.addEventListener("mouseleave", () => btn.style.opacity = "1");
189
+ buttons.push(btn);
190
+ wrapper.appendChild(btn);
191
+ }
192
+ container.appendChild(wrapper);
193
+ }
194
+ client.getProviders().then((p) => {
195
+ providers = p;
196
+ if (providers.length > 0) {
197
+ renderButtons(providers);
198
+ }
199
+ });
200
+ return () => {
201
+ container.innerHTML = "";
202
+ buttons = [];
203
+ };
204
+ }
95
205
  // Annotate the CommonJS export names for ESM import in node:
96
206
  0 && (module.exports = {
97
207
  authonModule,
98
208
  createAuthMiddleware,
99
209
  createAuthonPlugin,
210
+ renderSocialButtons,
100
211
  useAuthon,
101
212
  useUser
102
213
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/module.ts","../src/plugin.ts","../src/composables.ts","../src/middleware.ts"],"sourcesContent":["export { authonModule as default } from './module';\nexport { authonModule } from './module';\nexport type { AuthonModuleOptions } from './module';\nexport { createAuthonPlugin } from './plugin';\nexport type { AuthonPluginState } from './plugin';\nexport { useAuthon, useUser } from './composables';\nexport { createAuthMiddleware } from './middleware';\n","import type { AuthonConfig } from '@authon/js';\n\nexport interface AuthonModuleOptions {\n publishableKey: string;\n config?: Omit<AuthonConfig, 'mode'>;\n globalMiddleware?: boolean;\n}\n\n/**\n * Nuxt 3 module definition for Authon.\n *\n * Usage in nuxt.config.ts:\n * ```ts\n * export default defineNuxtConfig({\n * modules: ['@authon/nuxt'],\n * authon: {\n * publishableKey: 'pk_live_...',\n * globalMiddleware: false,\n * }\n * })\n * ```\n *\n * Since tsup can't process Nuxt module macros (defineNuxtModule, etc.),\n * this module exports a setup function that users call in their Nuxt plugin.\n */\nexport function authonModule(options: AuthonModuleOptions) {\n return {\n name: '@authon/nuxt',\n options,\n };\n}\n","import { Authon } from '@authon/js';\nimport type { AuthonConfig } from '@authon/js';\nimport type { AuthonUser } from '@authon/shared';\n\nexport interface AuthonPluginState {\n client: Authon;\n user: AuthonUser | null;\n isSignedIn: boolean;\n isLoading: boolean;\n}\n\n/**\n * Creates an Authon client instance for use as a Nuxt plugin.\n *\n * Usage in plugins/authon.client.ts:\n * ```ts\n * import { createAuthonPlugin } from '@authon/nuxt'\n *\n * export default defineNuxtPlugin(() => {\n * const authon = createAuthonPlugin('pk_live_...', { theme: 'auto' })\n * return { provide: { authon } }\n * })\n * ```\n */\nexport function createAuthonPlugin(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonPluginState {\n const client = new Authon(publishableKey, config);\n const state: AuthonPluginState = {\n client,\n user: null,\n isSignedIn: false,\n isLoading: true,\n };\n\n client.on('signedIn', (user) => {\n state.user = user as AuthonUser;\n state.isSignedIn = true;\n state.isLoading = false;\n });\n\n client.on('signedOut', () => {\n state.user = null;\n state.isSignedIn = false;\n });\n\n client.on('error', () => {\n state.isLoading = false;\n });\n\n const existingUser = client.getUser();\n if (existingUser) {\n state.user = existingUser;\n state.isSignedIn = true;\n }\n state.isLoading = false;\n\n return state;\n}\n","import type { AuthonUser } from '@authon/shared';\nimport type { AuthonPluginState } from './plugin';\n\n/**\n * Access the Authon client and auth state.\n *\n * Requires the Authon plugin to be installed via `createAuthonPlugin`.\n *\n * Usage:\n * ```ts\n * const { isSignedIn, user, client } = useAuthon()\n * await client.openSignIn()\n * ```\n */\nexport function useAuthon(): AuthonPluginState {\n // In a real Nuxt app, users access this via useNuxtApp().$authon\n // This composable is a convenience wrapper that users should adapt\n // to their own plugin setup.\n throw new Error(\n 'useAuthon() must be used within a Nuxt app with the Authon plugin installed. ' +\n 'Access the client via useNuxtApp().$authon instead.',\n );\n}\n\n/**\n * Get the current user and loading state.\n *\n * Usage:\n * ```ts\n * const { user, isLoading } = useUser()\n * ```\n */\nexport function useUser(): { user: AuthonUser | null; isLoading: boolean } {\n const { user, isLoading } = useAuthon();\n return { user, isLoading };\n}\n","import type { AuthonPluginState } from './plugin';\n\n/**\n * Route middleware factory for protecting authenticated routes.\n *\n * Usage in middleware/auth.ts:\n * ```ts\n * import { createAuthMiddleware } from '@authon/nuxt'\n *\n * export default defineNuxtRouteMiddleware((to, from) => {\n * const { $authon } = useNuxtApp()\n * return createAuthMiddleware($authon, '/login')(to, from)\n * })\n * ```\n */\nexport function createAuthMiddleware(\n authon: AuthonPluginState,\n redirectTo = '/sign-in',\n) {\n return (\n to: { path: string; fullPath: string },\n _from: { path: string },\n ): { path: string; query: { redirect: string } } | undefined => {\n if (to.path === redirectTo) return undefined;\n\n if (!authon.isSignedIn) {\n return {\n path: redirectTo,\n query: { redirect: to.fullPath },\n };\n }\n\n return undefined;\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACyBO,SAAS,aAAa,SAA8B;AACzD,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,EACF;AACF;;;AC9BA,gBAAuB;AAwBhB,SAAS,mBACd,gBACA,QACmB;AACnB,QAAM,SAAS,IAAI,iBAAO,gBAAgB,MAAM;AAChD,QAAM,QAA2B;AAAA,IAC/B;AAAA,IACA,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,WAAW;AAAA,EACb;AAEA,SAAO,GAAG,YAAY,CAAC,SAAS;AAC9B,UAAM,OAAO;AACb,UAAM,aAAa;AACnB,UAAM,YAAY;AAAA,EACpB,CAAC;AAED,SAAO,GAAG,aAAa,MAAM;AAC3B,UAAM,OAAO;AACb,UAAM,aAAa;AAAA,EACrB,CAAC;AAED,SAAO,GAAG,SAAS,MAAM;AACvB,UAAM,YAAY;AAAA,EACpB,CAAC;AAED,QAAM,eAAe,OAAO,QAAQ;AACpC,MAAI,cAAc;AAChB,UAAM,OAAO;AACb,UAAM,aAAa;AAAA,EACrB;AACA,QAAM,YAAY;AAElB,SAAO;AACT;;;AC7CO,SAAS,YAA+B;AAI7C,QAAM,IAAI;AAAA,IACR;AAAA,EAEF;AACF;AAUO,SAAS,UAA2D;AACzE,QAAM,EAAE,MAAM,UAAU,IAAI,UAAU;AACtC,SAAO,EAAE,MAAM,UAAU;AAC3B;;;ACpBO,SAAS,qBACd,QACA,aAAa,YACb;AACA,SAAO,CACL,IACA,UAC8D;AAC9D,QAAI,GAAG,SAAS,WAAY,QAAO;AAEnC,QAAI,CAAC,OAAO,YAAY;AACtB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO,EAAE,UAAU,GAAG,SAAS;AAAA,MACjC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/module.ts","../src/plugin.ts","../src/composables.ts","../src/middleware.ts","../src/SocialButtons.ts"],"sourcesContent":["export { authonModule as default } from './module';\nexport { authonModule } from './module';\nexport type { AuthonModuleOptions } from './module';\nexport { createAuthonPlugin } from './plugin';\nexport type { AuthonPluginState } from './plugin';\nexport { useAuthon, useUser } from './composables';\nexport { createAuthMiddleware } from './middleware';\nexport { renderSocialButtons } from './SocialButtons';\nexport type { SocialButtonsConfig } from './SocialButtons';\n","import type { AuthonConfig } from '@authon/js';\n\nexport interface AuthonModuleOptions {\n publishableKey: string;\n config?: Omit<AuthonConfig, 'mode'>;\n globalMiddleware?: boolean;\n}\n\n/**\n * Nuxt 3 module definition for Authon.\n *\n * Usage in nuxt.config.ts:\n * ```ts\n * export default defineNuxtConfig({\n * modules: ['@authon/nuxt'],\n * authon: {\n * publishableKey: 'pk_live_...',\n * globalMiddleware: false,\n * }\n * })\n * ```\n *\n * Since tsup can't process Nuxt module macros (defineNuxtModule, etc.),\n * this module exports a setup function that users call in their Nuxt plugin.\n */\nexport function authonModule(options: AuthonModuleOptions) {\n return {\n name: '@authon/nuxt',\n options,\n };\n}\n","import { Authon } from '@authon/js';\nimport type { AuthonConfig } from '@authon/js';\nimport type { AuthonUser } from '@authon/shared';\n\nexport interface AuthonPluginState {\n client: Authon;\n user: AuthonUser | null;\n isSignedIn: boolean;\n isLoading: boolean;\n}\n\n/**\n * Creates an Authon client instance for use as a Nuxt plugin.\n *\n * Usage in plugins/authon.client.ts:\n * ```ts\n * import { createAuthonPlugin } from '@authon/nuxt'\n *\n * export default defineNuxtPlugin(() => {\n * const authon = createAuthonPlugin('pk_live_...', { theme: 'auto' })\n * return { provide: { authon } }\n * })\n * ```\n */\nexport function createAuthonPlugin(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonPluginState {\n const client = new Authon(publishableKey, config);\n const state: AuthonPluginState = {\n client,\n user: null,\n isSignedIn: false,\n isLoading: true,\n };\n\n client.on('signedIn', (user) => {\n state.user = user as AuthonUser;\n state.isSignedIn = true;\n state.isLoading = false;\n });\n\n client.on('signedOut', () => {\n state.user = null;\n state.isSignedIn = false;\n });\n\n client.on('error', () => {\n state.isLoading = false;\n });\n\n const existingUser = client.getUser();\n if (existingUser) {\n state.user = existingUser;\n state.isSignedIn = true;\n }\n state.isLoading = false;\n\n return state;\n}\n","import type { AuthonUser } from '@authon/shared';\nimport type { AuthonPluginState } from './plugin';\n\n/**\n * Access the Authon client and auth state.\n *\n * Requires the Authon plugin to be installed via `createAuthonPlugin`.\n *\n * Usage:\n * ```ts\n * const { isSignedIn, user, client } = useAuthon()\n * await client.openSignIn()\n * ```\n */\nexport function useAuthon(): AuthonPluginState {\n // In a real Nuxt app, users access this via useNuxtApp().$authon\n // This composable is a convenience wrapper that users should adapt\n // to their own plugin setup.\n throw new Error(\n 'useAuthon() must be used within a Nuxt app with the Authon plugin installed. ' +\n 'Access the client via useNuxtApp().$authon instead.',\n );\n}\n\n/**\n * Get the current user and loading state.\n *\n * Usage:\n * ```ts\n * const { user, isLoading } = useUser()\n * ```\n */\nexport function useUser(): { user: AuthonUser | null; isLoading: boolean } {\n const { user, isLoading } = useAuthon();\n return { user, isLoading };\n}\n","import type { AuthonPluginState } from './plugin';\n\n/**\n * Route middleware factory for protecting authenticated routes.\n *\n * Usage in middleware/auth.ts:\n * ```ts\n * import { createAuthMiddleware } from '@authon/nuxt'\n *\n * export default defineNuxtRouteMiddleware((to, from) => {\n * const { $authon } = useNuxtApp()\n * return createAuthMiddleware($authon, '/login')(to, from)\n * })\n * ```\n */\nexport function createAuthMiddleware(\n authon: AuthonPluginState,\n redirectTo = '/sign-in',\n) {\n return (\n to: { path: string; fullPath: string },\n _from: { path: string },\n ): { path: string; query: { redirect: string } } | undefined => {\n if (to.path === redirectTo) return undefined;\n\n if (!authon.isSignedIn) {\n return {\n path: redirectTo,\n query: { redirect: to.fullPath },\n };\n }\n\n return undefined;\n };\n}\n","import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES, type OAuthProviderType } from '@authon/shared';\nimport { getProviderButtonConfig, type Authon } from '@authon/js';\n\nexport interface SocialButtonsConfig {\n compact?: boolean;\n gap?: number;\n labels?: Partial<Record<OAuthProviderType, string>>;\n iconSize?: number;\n borderRadius?: number;\n height?: number;\n size?: number;\n}\n\n/**\n * Render social login buttons into a container element.\n *\n * Usage in Nuxt page:\n * ```vue\n * <template>\n * <div ref=\"socialContainer\" />\n * </template>\n *\n * <script setup>\n * const { $authon } = useNuxtApp()\n * const socialContainer = ref<HTMLElement>()\n *\n * let cleanup: (() => void) | undefined\n * onMounted(async () => {\n * const { renderSocialButtons } = await import('@authon/nuxt')\n * cleanup = renderSocialButtons({\n * client: $authon.client,\n * container: socialContainer.value!,\n * compact: true,\n * })\n * })\n * onUnmounted(() => cleanup?.())\n * </script>\n * ```\n */\nexport function renderSocialButtons(options: {\n client: Authon;\n container: HTMLElement;\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n} & SocialButtonsConfig): () => void {\n const {\n client,\n container,\n onSuccess,\n onError,\n compact = false,\n gap,\n labels,\n iconSize,\n borderRadius = 10,\n height = 48,\n size = 48,\n } = options;\n\n const resolvedGap = gap ?? (compact ? 12 : 10);\n const resolvedIconSize = iconSize ?? (compact ? 24 : 20);\n let loadingProvider: string | null = null;\n let buttons: HTMLButtonElement[] = [];\n\n const handleClick = async (provider: OAuthProviderType, btn: HTMLButtonElement) => {\n if (loadingProvider) return;\n loadingProvider = provider;\n btn.innerHTML = '<span style=\"display:inline-block;width:16px;height:16px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:authon-spin 0.6s linear infinite\"></span>';\n buttons.forEach((b) => (b.disabled = true));\n\n try {\n await client.signInWithOAuth(provider);\n onSuccess?.();\n } catch (e: any) {\n const error = e instanceof Error ? e : new Error(String(e));\n onError?.(error);\n } finally {\n loadingProvider = null;\n renderButtons(providers);\n }\n };\n\n let providers: OAuthProviderType[] = [];\n\n function renderButtons(providerList: OAuthProviderType[]) {\n container.innerHTML = '';\n buttons = [];\n\n if (!document.getElementById('authon-spin-style')) {\n const style = document.createElement('style');\n style.id = 'authon-spin-style';\n style.textContent = '@keyframes authon-spin{to{transform:rotate(360deg)}}';\n document.head.appendChild(style);\n }\n\n const wrapper = document.createElement('div');\n wrapper.style.display = 'flex';\n wrapper.style.gap = `${resolvedGap}px`;\n\n if (compact) {\n wrapper.style.flexDirection = 'row';\n wrapper.style.flexWrap = 'wrap';\n wrapper.style.justifyContent = 'center';\n } else {\n wrapper.style.flexDirection = 'column';\n }\n\n for (const provider of providerList) {\n const colors = PROVIDER_COLORS[provider] || { bg: '#333', text: '#fff' };\n const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;\n const config = getProviderButtonConfig(provider);\n const iconSvg = config.iconSvg\n .replace(/width=\"\\d+\"/, `width=\"${resolvedIconSize}\"`)\n .replace(/height=\"\\d+\"/, `height=\"${resolvedIconSize}\"`);\n const needsBorder = colors.bg.toLowerCase() === '#ffffff';\n\n const btn = document.createElement('button');\n btn.setAttribute('aria-label', `Sign in with ${displayName}`);\n btn.style.display = 'flex';\n btn.style.alignItems = 'center';\n btn.style.justifyContent = 'center';\n btn.style.border = needsBorder ? '1px solid #dadce0' : 'none';\n btn.style.cursor = 'pointer';\n btn.style.backgroundColor = colors.bg;\n btn.style.color = colors.text;\n btn.style.borderRadius = `${borderRadius}px`;\n btn.style.transition = 'opacity 0.15s';\n btn.style.fontFamily = 'inherit';\n\n if (compact) {\n btn.style.width = `${size}px`;\n btn.style.height = `${size}px`;\n btn.style.padding = '0';\n btn.innerHTML = `<span style=\"display:flex;align-items:center\">${iconSvg}</span>`;\n } else {\n btn.style.width = '100%';\n btn.style.height = `${height}px`;\n btn.style.gap = '10px';\n btn.style.paddingLeft = '16px';\n btn.style.paddingRight = '16px';\n const buttonLabel = labels?.[provider] ?? `Continue with ${displayName}`;\n btn.innerHTML = `<span style=\"display:flex;align-items:center;flex-shrink:0\">${iconSvg}</span><span style=\"font-size:15px;font-weight:600;white-space:nowrap\">${buttonLabel}</span>`;\n }\n\n btn.addEventListener('click', () => handleClick(provider, btn));\n btn.addEventListener('mouseenter', () => (btn.style.opacity = '0.85'));\n btn.addEventListener('mouseleave', () => (btn.style.opacity = '1'));\n\n buttons.push(btn);\n wrapper.appendChild(btn);\n }\n\n container.appendChild(wrapper);\n }\n\n client.getProviders().then((p: OAuthProviderType[]) => {\n providers = p;\n if (providers.length > 0) {\n renderButtons(providers);\n }\n });\n\n return () => {\n container.innerHTML = '';\n buttons = [];\n };\n}\n"],"mappings":"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","names":["import_js"]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AuthonConfig, Authon } from '@authon/js';
2
- import { AuthonUser } from '@authon/shared';
2
+ import { AuthonUser, OAuthProviderType } from '@authon/shared';
3
3
 
4
4
  interface AuthonModuleOptions {
5
5
  publishableKey: string;
@@ -99,4 +99,46 @@ declare function createAuthMiddleware(authon: AuthonPluginState, redirectTo?: st
99
99
  };
100
100
  } | undefined;
101
101
 
102
- export { type AuthonModuleOptions, type AuthonPluginState, authonModule, createAuthMiddleware, createAuthonPlugin, authonModule as default, useAuthon, useUser };
102
+ interface SocialButtonsConfig {
103
+ compact?: boolean;
104
+ gap?: number;
105
+ labels?: Partial<Record<OAuthProviderType, string>>;
106
+ iconSize?: number;
107
+ borderRadius?: number;
108
+ height?: number;
109
+ size?: number;
110
+ }
111
+ /**
112
+ * Render social login buttons into a container element.
113
+ *
114
+ * Usage in Nuxt page:
115
+ * ```vue
116
+ * <template>
117
+ * <div ref="socialContainer" />
118
+ * </template>
119
+ *
120
+ * <script setup>
121
+ * const { $authon } = useNuxtApp()
122
+ * const socialContainer = ref<HTMLElement>()
123
+ *
124
+ * let cleanup: (() => void) | undefined
125
+ * onMounted(async () => {
126
+ * const { renderSocialButtons } = await import('@authon/nuxt')
127
+ * cleanup = renderSocialButtons({
128
+ * client: $authon.client,
129
+ * container: socialContainer.value!,
130
+ * compact: true,
131
+ * })
132
+ * })
133
+ * onUnmounted(() => cleanup?.())
134
+ * </script>
135
+ * ```
136
+ */
137
+ declare function renderSocialButtons(options: {
138
+ client: Authon;
139
+ container: HTMLElement;
140
+ onSuccess?: () => void;
141
+ onError?: (error: Error) => void;
142
+ } & SocialButtonsConfig): () => void;
143
+
144
+ export { type AuthonModuleOptions, type AuthonPluginState, type SocialButtonsConfig, authonModule, createAuthMiddleware, createAuthonPlugin, authonModule as default, renderSocialButtons, useAuthon, useUser };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AuthonConfig, Authon } from '@authon/js';
2
- import { AuthonUser } from '@authon/shared';
2
+ import { AuthonUser, OAuthProviderType } from '@authon/shared';
3
3
 
4
4
  interface AuthonModuleOptions {
5
5
  publishableKey: string;
@@ -99,4 +99,46 @@ declare function createAuthMiddleware(authon: AuthonPluginState, redirectTo?: st
99
99
  };
100
100
  } | undefined;
101
101
 
102
- export { type AuthonModuleOptions, type AuthonPluginState, authonModule, createAuthMiddleware, createAuthonPlugin, authonModule as default, useAuthon, useUser };
102
+ interface SocialButtonsConfig {
103
+ compact?: boolean;
104
+ gap?: number;
105
+ labels?: Partial<Record<OAuthProviderType, string>>;
106
+ iconSize?: number;
107
+ borderRadius?: number;
108
+ height?: number;
109
+ size?: number;
110
+ }
111
+ /**
112
+ * Render social login buttons into a container element.
113
+ *
114
+ * Usage in Nuxt page:
115
+ * ```vue
116
+ * <template>
117
+ * <div ref="socialContainer" />
118
+ * </template>
119
+ *
120
+ * <script setup>
121
+ * const { $authon } = useNuxtApp()
122
+ * const socialContainer = ref<HTMLElement>()
123
+ *
124
+ * let cleanup: (() => void) | undefined
125
+ * onMounted(async () => {
126
+ * const { renderSocialButtons } = await import('@authon/nuxt')
127
+ * cleanup = renderSocialButtons({
128
+ * client: $authon.client,
129
+ * container: socialContainer.value!,
130
+ * compact: true,
131
+ * })
132
+ * })
133
+ * onUnmounted(() => cleanup?.())
134
+ * </script>
135
+ * ```
136
+ */
137
+ declare function renderSocialButtons(options: {
138
+ client: Authon;
139
+ container: HTMLElement;
140
+ onSuccess?: () => void;
141
+ onError?: (error: Error) => void;
142
+ } & SocialButtonsConfig): () => void;
143
+
144
+ export { type AuthonModuleOptions, type AuthonPluginState, type SocialButtonsConfig, authonModule, createAuthMiddleware, createAuthonPlugin, authonModule as default, renderSocialButtons, useAuthon, useUser };
package/dist/index.js CHANGED
@@ -61,11 +61,121 @@ function createAuthMiddleware(authon, redirectTo = "/sign-in") {
61
61
  return void 0;
62
62
  };
63
63
  }
64
+
65
+ // src/SocialButtons.ts
66
+ import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES } from "@authon/shared";
67
+ import { getProviderButtonConfig } from "@authon/js";
68
+ function renderSocialButtons(options) {
69
+ const {
70
+ client,
71
+ container,
72
+ onSuccess,
73
+ onError,
74
+ compact = false,
75
+ gap,
76
+ labels,
77
+ iconSize,
78
+ borderRadius = 10,
79
+ height = 48,
80
+ size = 48
81
+ } = options;
82
+ const resolvedGap = gap ?? (compact ? 12 : 10);
83
+ const resolvedIconSize = iconSize ?? (compact ? 24 : 20);
84
+ let loadingProvider = null;
85
+ let buttons = [];
86
+ const handleClick = async (provider, btn) => {
87
+ if (loadingProvider) return;
88
+ loadingProvider = provider;
89
+ btn.innerHTML = '<span style="display:inline-block;width:16px;height:16px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:authon-spin 0.6s linear infinite"></span>';
90
+ buttons.forEach((b) => b.disabled = true);
91
+ try {
92
+ await client.signInWithOAuth(provider);
93
+ onSuccess?.();
94
+ } catch (e) {
95
+ const error = e instanceof Error ? e : new Error(String(e));
96
+ onError?.(error);
97
+ } finally {
98
+ loadingProvider = null;
99
+ renderButtons(providers);
100
+ }
101
+ };
102
+ let providers = [];
103
+ function renderButtons(providerList) {
104
+ container.innerHTML = "";
105
+ buttons = [];
106
+ if (!document.getElementById("authon-spin-style")) {
107
+ const style = document.createElement("style");
108
+ style.id = "authon-spin-style";
109
+ style.textContent = "@keyframes authon-spin{to{transform:rotate(360deg)}}";
110
+ document.head.appendChild(style);
111
+ }
112
+ const wrapper = document.createElement("div");
113
+ wrapper.style.display = "flex";
114
+ wrapper.style.gap = `${resolvedGap}px`;
115
+ if (compact) {
116
+ wrapper.style.flexDirection = "row";
117
+ wrapper.style.flexWrap = "wrap";
118
+ wrapper.style.justifyContent = "center";
119
+ } else {
120
+ wrapper.style.flexDirection = "column";
121
+ }
122
+ for (const provider of providerList) {
123
+ const colors = PROVIDER_COLORS[provider] || { bg: "#333", text: "#fff" };
124
+ const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;
125
+ const config = getProviderButtonConfig(provider);
126
+ const iconSvg = config.iconSvg.replace(/width="\d+"/, `width="${resolvedIconSize}"`).replace(/height="\d+"/, `height="${resolvedIconSize}"`);
127
+ const needsBorder = colors.bg.toLowerCase() === "#ffffff";
128
+ const btn = document.createElement("button");
129
+ btn.setAttribute("aria-label", `Sign in with ${displayName}`);
130
+ btn.style.display = "flex";
131
+ btn.style.alignItems = "center";
132
+ btn.style.justifyContent = "center";
133
+ btn.style.border = needsBorder ? "1px solid #dadce0" : "none";
134
+ btn.style.cursor = "pointer";
135
+ btn.style.backgroundColor = colors.bg;
136
+ btn.style.color = colors.text;
137
+ btn.style.borderRadius = `${borderRadius}px`;
138
+ btn.style.transition = "opacity 0.15s";
139
+ btn.style.fontFamily = "inherit";
140
+ if (compact) {
141
+ btn.style.width = `${size}px`;
142
+ btn.style.height = `${size}px`;
143
+ btn.style.padding = "0";
144
+ btn.innerHTML = `<span style="display:flex;align-items:center">${iconSvg}</span>`;
145
+ } else {
146
+ btn.style.width = "100%";
147
+ btn.style.height = `${height}px`;
148
+ btn.style.gap = "10px";
149
+ btn.style.paddingLeft = "16px";
150
+ btn.style.paddingRight = "16px";
151
+ const buttonLabel = labels?.[provider] ?? `Continue with ${displayName}`;
152
+ btn.innerHTML = `<span style="display:flex;align-items:center;flex-shrink:0">${iconSvg}</span><span style="font-size:15px;font-weight:600;white-space:nowrap">${buttonLabel}</span>`;
153
+ }
154
+ btn.addEventListener("click", () => handleClick(provider, btn));
155
+ btn.addEventListener("mouseenter", () => btn.style.opacity = "0.85");
156
+ btn.addEventListener("mouseleave", () => btn.style.opacity = "1");
157
+ buttons.push(btn);
158
+ wrapper.appendChild(btn);
159
+ }
160
+ container.appendChild(wrapper);
161
+ }
162
+ client.getProviders().then((p) => {
163
+ providers = p;
164
+ if (providers.length > 0) {
165
+ renderButtons(providers);
166
+ }
167
+ });
168
+ return () => {
169
+ container.innerHTML = "";
170
+ buttons = [];
171
+ };
172
+ }
64
173
  export {
65
174
  authonModule,
66
175
  createAuthMiddleware,
67
176
  createAuthonPlugin,
68
177
  authonModule as default,
178
+ renderSocialButtons,
69
179
  useAuthon,
70
180
  useUser
71
181
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/module.ts","../src/plugin.ts","../src/composables.ts","../src/middleware.ts"],"sourcesContent":["import type { AuthonConfig } from '@authon/js';\n\nexport interface AuthonModuleOptions {\n publishableKey: string;\n config?: Omit<AuthonConfig, 'mode'>;\n globalMiddleware?: boolean;\n}\n\n/**\n * Nuxt 3 module definition for Authon.\n *\n * Usage in nuxt.config.ts:\n * ```ts\n * export default defineNuxtConfig({\n * modules: ['@authon/nuxt'],\n * authon: {\n * publishableKey: 'pk_live_...',\n * globalMiddleware: false,\n * }\n * })\n * ```\n *\n * Since tsup can't process Nuxt module macros (defineNuxtModule, etc.),\n * this module exports a setup function that users call in their Nuxt plugin.\n */\nexport function authonModule(options: AuthonModuleOptions) {\n return {\n name: '@authon/nuxt',\n options,\n };\n}\n","import { Authon } from '@authon/js';\nimport type { AuthonConfig } from '@authon/js';\nimport type { AuthonUser } from '@authon/shared';\n\nexport interface AuthonPluginState {\n client: Authon;\n user: AuthonUser | null;\n isSignedIn: boolean;\n isLoading: boolean;\n}\n\n/**\n * Creates an Authon client instance for use as a Nuxt plugin.\n *\n * Usage in plugins/authon.client.ts:\n * ```ts\n * import { createAuthonPlugin } from '@authon/nuxt'\n *\n * export default defineNuxtPlugin(() => {\n * const authon = createAuthonPlugin('pk_live_...', { theme: 'auto' })\n * return { provide: { authon } }\n * })\n * ```\n */\nexport function createAuthonPlugin(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonPluginState {\n const client = new Authon(publishableKey, config);\n const state: AuthonPluginState = {\n client,\n user: null,\n isSignedIn: false,\n isLoading: true,\n };\n\n client.on('signedIn', (user) => {\n state.user = user as AuthonUser;\n state.isSignedIn = true;\n state.isLoading = false;\n });\n\n client.on('signedOut', () => {\n state.user = null;\n state.isSignedIn = false;\n });\n\n client.on('error', () => {\n state.isLoading = false;\n });\n\n const existingUser = client.getUser();\n if (existingUser) {\n state.user = existingUser;\n state.isSignedIn = true;\n }\n state.isLoading = false;\n\n return state;\n}\n","import type { AuthonUser } from '@authon/shared';\nimport type { AuthonPluginState } from './plugin';\n\n/**\n * Access the Authon client and auth state.\n *\n * Requires the Authon plugin to be installed via `createAuthonPlugin`.\n *\n * Usage:\n * ```ts\n * const { isSignedIn, user, client } = useAuthon()\n * await client.openSignIn()\n * ```\n */\nexport function useAuthon(): AuthonPluginState {\n // In a real Nuxt app, users access this via useNuxtApp().$authon\n // This composable is a convenience wrapper that users should adapt\n // to their own plugin setup.\n throw new Error(\n 'useAuthon() must be used within a Nuxt app with the Authon plugin installed. ' +\n 'Access the client via useNuxtApp().$authon instead.',\n );\n}\n\n/**\n * Get the current user and loading state.\n *\n * Usage:\n * ```ts\n * const { user, isLoading } = useUser()\n * ```\n */\nexport function useUser(): { user: AuthonUser | null; isLoading: boolean } {\n const { user, isLoading } = useAuthon();\n return { user, isLoading };\n}\n","import type { AuthonPluginState } from './plugin';\n\n/**\n * Route middleware factory for protecting authenticated routes.\n *\n * Usage in middleware/auth.ts:\n * ```ts\n * import { createAuthMiddleware } from '@authon/nuxt'\n *\n * export default defineNuxtRouteMiddleware((to, from) => {\n * const { $authon } = useNuxtApp()\n * return createAuthMiddleware($authon, '/login')(to, from)\n * })\n * ```\n */\nexport function createAuthMiddleware(\n authon: AuthonPluginState,\n redirectTo = '/sign-in',\n) {\n return (\n to: { path: string; fullPath: string },\n _from: { path: string },\n ): { path: string; query: { redirect: string } } | undefined => {\n if (to.path === redirectTo) return undefined;\n\n if (!authon.isSignedIn) {\n return {\n path: redirectTo,\n query: { redirect: to.fullPath },\n };\n }\n\n return undefined;\n };\n}\n"],"mappings":";AAyBO,SAAS,aAAa,SAA8B;AACzD,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,EACF;AACF;;;AC9BA,SAAS,cAAc;AAwBhB,SAAS,mBACd,gBACA,QACmB;AACnB,QAAM,SAAS,IAAI,OAAO,gBAAgB,MAAM;AAChD,QAAM,QAA2B;AAAA,IAC/B;AAAA,IACA,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,WAAW;AAAA,EACb;AAEA,SAAO,GAAG,YAAY,CAAC,SAAS;AAC9B,UAAM,OAAO;AACb,UAAM,aAAa;AACnB,UAAM,YAAY;AAAA,EACpB,CAAC;AAED,SAAO,GAAG,aAAa,MAAM;AAC3B,UAAM,OAAO;AACb,UAAM,aAAa;AAAA,EACrB,CAAC;AAED,SAAO,GAAG,SAAS,MAAM;AACvB,UAAM,YAAY;AAAA,EACpB,CAAC;AAED,QAAM,eAAe,OAAO,QAAQ;AACpC,MAAI,cAAc;AAChB,UAAM,OAAO;AACb,UAAM,aAAa;AAAA,EACrB;AACA,QAAM,YAAY;AAElB,SAAO;AACT;;;AC7CO,SAAS,YAA+B;AAI7C,QAAM,IAAI;AAAA,IACR;AAAA,EAEF;AACF;AAUO,SAAS,UAA2D;AACzE,QAAM,EAAE,MAAM,UAAU,IAAI,UAAU;AACtC,SAAO,EAAE,MAAM,UAAU;AAC3B;;;ACpBO,SAAS,qBACd,QACA,aAAa,YACb;AACA,SAAO,CACL,IACA,UAC8D;AAC9D,QAAI,GAAG,SAAS,WAAY,QAAO;AAEnC,QAAI,CAAC,OAAO,YAAY;AACtB,aAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO,EAAE,UAAU,GAAG,SAAS;AAAA,MACjC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/module.ts","../src/plugin.ts","../src/composables.ts","../src/middleware.ts","../src/SocialButtons.ts"],"sourcesContent":["import type { AuthonConfig } from '@authon/js';\n\nexport interface AuthonModuleOptions {\n publishableKey: string;\n config?: Omit<AuthonConfig, 'mode'>;\n globalMiddleware?: boolean;\n}\n\n/**\n * Nuxt 3 module definition for Authon.\n *\n * Usage in nuxt.config.ts:\n * ```ts\n * export default defineNuxtConfig({\n * modules: ['@authon/nuxt'],\n * authon: {\n * publishableKey: 'pk_live_...',\n * globalMiddleware: false,\n * }\n * })\n * ```\n *\n * Since tsup can't process Nuxt module macros (defineNuxtModule, etc.),\n * this module exports a setup function that users call in their Nuxt plugin.\n */\nexport function authonModule(options: AuthonModuleOptions) {\n return {\n name: '@authon/nuxt',\n options,\n };\n}\n","import { Authon } from '@authon/js';\nimport type { AuthonConfig } from '@authon/js';\nimport type { AuthonUser } from '@authon/shared';\n\nexport interface AuthonPluginState {\n client: Authon;\n user: AuthonUser | null;\n isSignedIn: boolean;\n isLoading: boolean;\n}\n\n/**\n * Creates an Authon client instance for use as a Nuxt plugin.\n *\n * Usage in plugins/authon.client.ts:\n * ```ts\n * import { createAuthonPlugin } from '@authon/nuxt'\n *\n * export default defineNuxtPlugin(() => {\n * const authon = createAuthonPlugin('pk_live_...', { theme: 'auto' })\n * return { provide: { authon } }\n * })\n * ```\n */\nexport function createAuthonPlugin(\n publishableKey: string,\n config?: Omit<AuthonConfig, 'mode'>,\n): AuthonPluginState {\n const client = new Authon(publishableKey, config);\n const state: AuthonPluginState = {\n client,\n user: null,\n isSignedIn: false,\n isLoading: true,\n };\n\n client.on('signedIn', (user) => {\n state.user = user as AuthonUser;\n state.isSignedIn = true;\n state.isLoading = false;\n });\n\n client.on('signedOut', () => {\n state.user = null;\n state.isSignedIn = false;\n });\n\n client.on('error', () => {\n state.isLoading = false;\n });\n\n const existingUser = client.getUser();\n if (existingUser) {\n state.user = existingUser;\n state.isSignedIn = true;\n }\n state.isLoading = false;\n\n return state;\n}\n","import type { AuthonUser } from '@authon/shared';\nimport type { AuthonPluginState } from './plugin';\n\n/**\n * Access the Authon client and auth state.\n *\n * Requires the Authon plugin to be installed via `createAuthonPlugin`.\n *\n * Usage:\n * ```ts\n * const { isSignedIn, user, client } = useAuthon()\n * await client.openSignIn()\n * ```\n */\nexport function useAuthon(): AuthonPluginState {\n // In a real Nuxt app, users access this via useNuxtApp().$authon\n // This composable is a convenience wrapper that users should adapt\n // to their own plugin setup.\n throw new Error(\n 'useAuthon() must be used within a Nuxt app with the Authon plugin installed. ' +\n 'Access the client via useNuxtApp().$authon instead.',\n );\n}\n\n/**\n * Get the current user and loading state.\n *\n * Usage:\n * ```ts\n * const { user, isLoading } = useUser()\n * ```\n */\nexport function useUser(): { user: AuthonUser | null; isLoading: boolean } {\n const { user, isLoading } = useAuthon();\n return { user, isLoading };\n}\n","import type { AuthonPluginState } from './plugin';\n\n/**\n * Route middleware factory for protecting authenticated routes.\n *\n * Usage in middleware/auth.ts:\n * ```ts\n * import { createAuthMiddleware } from '@authon/nuxt'\n *\n * export default defineNuxtRouteMiddleware((to, from) => {\n * const { $authon } = useNuxtApp()\n * return createAuthMiddleware($authon, '/login')(to, from)\n * })\n * ```\n */\nexport function createAuthMiddleware(\n authon: AuthonPluginState,\n redirectTo = '/sign-in',\n) {\n return (\n to: { path: string; fullPath: string },\n _from: { path: string },\n ): { path: string; query: { redirect: string } } | undefined => {\n if (to.path === redirectTo) return undefined;\n\n if (!authon.isSignedIn) {\n return {\n path: redirectTo,\n query: { redirect: to.fullPath },\n };\n }\n\n return undefined;\n };\n}\n","import { PROVIDER_COLORS, PROVIDER_DISPLAY_NAMES, type OAuthProviderType } from '@authon/shared';\nimport { getProviderButtonConfig, type Authon } from '@authon/js';\n\nexport interface SocialButtonsConfig {\n compact?: boolean;\n gap?: number;\n labels?: Partial<Record<OAuthProviderType, string>>;\n iconSize?: number;\n borderRadius?: number;\n height?: number;\n size?: number;\n}\n\n/**\n * Render social login buttons into a container element.\n *\n * Usage in Nuxt page:\n * ```vue\n * <template>\n * <div ref=\"socialContainer\" />\n * </template>\n *\n * <script setup>\n * const { $authon } = useNuxtApp()\n * const socialContainer = ref<HTMLElement>()\n *\n * let cleanup: (() => void) | undefined\n * onMounted(async () => {\n * const { renderSocialButtons } = await import('@authon/nuxt')\n * cleanup = renderSocialButtons({\n * client: $authon.client,\n * container: socialContainer.value!,\n * compact: true,\n * })\n * })\n * onUnmounted(() => cleanup?.())\n * </script>\n * ```\n */\nexport function renderSocialButtons(options: {\n client: Authon;\n container: HTMLElement;\n onSuccess?: () => void;\n onError?: (error: Error) => void;\n} & SocialButtonsConfig): () => void {\n const {\n client,\n container,\n onSuccess,\n onError,\n compact = false,\n gap,\n labels,\n iconSize,\n borderRadius = 10,\n height = 48,\n size = 48,\n } = options;\n\n const resolvedGap = gap ?? (compact ? 12 : 10);\n const resolvedIconSize = iconSize ?? (compact ? 24 : 20);\n let loadingProvider: string | null = null;\n let buttons: HTMLButtonElement[] = [];\n\n const handleClick = async (provider: OAuthProviderType, btn: HTMLButtonElement) => {\n if (loadingProvider) return;\n loadingProvider = provider;\n btn.innerHTML = '<span style=\"display:inline-block;width:16px;height:16px;border:2px solid currentColor;border-top-color:transparent;border-radius:50%;animation:authon-spin 0.6s linear infinite\"></span>';\n buttons.forEach((b) => (b.disabled = true));\n\n try {\n await client.signInWithOAuth(provider);\n onSuccess?.();\n } catch (e: any) {\n const error = e instanceof Error ? e : new Error(String(e));\n onError?.(error);\n } finally {\n loadingProvider = null;\n renderButtons(providers);\n }\n };\n\n let providers: OAuthProviderType[] = [];\n\n function renderButtons(providerList: OAuthProviderType[]) {\n container.innerHTML = '';\n buttons = [];\n\n if (!document.getElementById('authon-spin-style')) {\n const style = document.createElement('style');\n style.id = 'authon-spin-style';\n style.textContent = '@keyframes authon-spin{to{transform:rotate(360deg)}}';\n document.head.appendChild(style);\n }\n\n const wrapper = document.createElement('div');\n wrapper.style.display = 'flex';\n wrapper.style.gap = `${resolvedGap}px`;\n\n if (compact) {\n wrapper.style.flexDirection = 'row';\n wrapper.style.flexWrap = 'wrap';\n wrapper.style.justifyContent = 'center';\n } else {\n wrapper.style.flexDirection = 'column';\n }\n\n for (const provider of providerList) {\n const colors = PROVIDER_COLORS[provider] || { bg: '#333', text: '#fff' };\n const displayName = PROVIDER_DISPLAY_NAMES[provider] || provider;\n const config = getProviderButtonConfig(provider);\n const iconSvg = config.iconSvg\n .replace(/width=\"\\d+\"/, `width=\"${resolvedIconSize}\"`)\n .replace(/height=\"\\d+\"/, `height=\"${resolvedIconSize}\"`);\n const needsBorder = colors.bg.toLowerCase() === '#ffffff';\n\n const btn = document.createElement('button');\n btn.setAttribute('aria-label', `Sign in with ${displayName}`);\n btn.style.display = 'flex';\n btn.style.alignItems = 'center';\n btn.style.justifyContent = 'center';\n btn.style.border = needsBorder ? '1px solid #dadce0' : 'none';\n btn.style.cursor = 'pointer';\n btn.style.backgroundColor = colors.bg;\n btn.style.color = colors.text;\n btn.style.borderRadius = `${borderRadius}px`;\n btn.style.transition = 'opacity 0.15s';\n btn.style.fontFamily = 'inherit';\n\n if (compact) {\n btn.style.width = `${size}px`;\n btn.style.height = `${size}px`;\n btn.style.padding = '0';\n btn.innerHTML = `<span style=\"display:flex;align-items:center\">${iconSvg}</span>`;\n } else {\n btn.style.width = '100%';\n btn.style.height = `${height}px`;\n btn.style.gap = '10px';\n btn.style.paddingLeft = '16px';\n btn.style.paddingRight = '16px';\n const buttonLabel = labels?.[provider] ?? `Continue with ${displayName}`;\n btn.innerHTML = `<span style=\"display:flex;align-items:center;flex-shrink:0\">${iconSvg}</span><span style=\"font-size:15px;font-weight:600;white-space:nowrap\">${buttonLabel}</span>`;\n }\n\n btn.addEventListener('click', () => handleClick(provider, btn));\n btn.addEventListener('mouseenter', () => (btn.style.opacity = '0.85'));\n btn.addEventListener('mouseleave', () => (btn.style.opacity = '1'));\n\n buttons.push(btn);\n wrapper.appendChild(btn);\n }\n\n container.appendChild(wrapper);\n }\n\n client.getProviders().then((p: OAuthProviderType[]) => {\n providers = p;\n if (providers.length > 0) {\n renderButtons(providers);\n }\n });\n\n return () => {\n container.innerHTML = '';\n buttons = [];\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authon/nuxt",
3
- "version": "0.1.19",
3
+ "version": "0.2.1",
4
4
  "description": "Authon Nuxt 3 module — auto-imported composables and middleware",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -37,8 +37,8 @@
37
37
  "module"
38
38
  ],
39
39
  "dependencies": {
40
- "@authon/js": "workspace:^",
41
- "@authon/shared": "workspace:^"
40
+ "@authon/js": "^0.2.1",
41
+ "@authon/shared": "^0.2.0"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "nuxt": "^3.0.0"