@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 +111 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +44 -2
- package/dist/index.d.ts +44 -2
- package/dist/index.js +110 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
});
|
package/dist/index.cjs.map
CHANGED
|
@@ -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":";;;;;;;;;;;;;;;;;;;;AAAA;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;;;AClCA,oBAAgF;AAChF,IAAAA,aAAqD;AAsC9C,SAAS,oBAAoB,SAKC;AACnC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf,SAAS;AAAA,IACT,OAAO;AAAA,EACT,IAAI;AAEJ,QAAM,cAAc,QAAQ,UAAU,KAAK;AAC3C,QAAM,mBAAmB,aAAa,UAAU,KAAK;AACrD,MAAI,kBAAiC;AACrC,MAAI,UAA+B,CAAC;AAEpC,QAAM,cAAc,OAAO,UAA6B,QAA2B;AACjF,QAAI,gBAAiB;AACrB,sBAAkB;AAClB,QAAI,YAAY;AAChB,YAAQ,QAAQ,CAAC,MAAO,EAAE,WAAW,IAAK;AAE1C,QAAI;AACF,YAAM,OAAO,gBAAgB,QAAQ;AACrC,kBAAY;AAAA,IACd,SAAS,GAAQ;AACf,YAAM,QAAQ,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC;AAC1D,gBAAU,KAAK;AAAA,IACjB,UAAE;AACA,wBAAkB;AAClB,oBAAc,SAAS;AAAA,IACzB;AAAA,EACF;AAEA,MAAI,YAAiC,CAAC;AAEtC,WAAS,cAAc,cAAmC;AACxD,cAAU,YAAY;AACtB,cAAU,CAAC;AAEX,QAAI,CAAC,SAAS,eAAe,mBAAmB,GAAG;AACjD,YAAM,QAAQ,SAAS,cAAc,OAAO;AAC5C,YAAM,KAAK;AACX,YAAM,cAAc;AACpB,eAAS,KAAK,YAAY,KAAK;AAAA,IACjC;AAEA,UAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,YAAQ,MAAM,UAAU;AACxB,YAAQ,MAAM,MAAM,GAAG,WAAW;AAElC,QAAI,SAAS;AACX,cAAQ,MAAM,gBAAgB;AAC9B,cAAQ,MAAM,WAAW;AACzB,cAAQ,MAAM,iBAAiB;AAAA,IACjC,OAAO;AACL,cAAQ,MAAM,gBAAgB;AAAA,IAChC;AAEA,eAAW,YAAY,cAAc;AACnC,YAAM,SAAS,8BAAgB,QAAQ,KAAK,EAAE,IAAI,QAAQ,MAAM,OAAO;AACvE,YAAM,cAAc,qCAAuB,QAAQ,KAAK;AACxD,YAAM,aAAS,oCAAwB,QAAQ;AAC/C,YAAM,UAAU,OAAO,QACpB,QAAQ,eAAe,UAAU,gBAAgB,GAAG,EACpD,QAAQ,gBAAgB,WAAW,gBAAgB,GAAG;AACzD,YAAM,cAAc,OAAO,GAAG,YAAY,MAAM;AAEhD,YAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,UAAI,aAAa,cAAc,gBAAgB,WAAW,EAAE;AAC5D,UAAI,MAAM,UAAU;AACpB,UAAI,MAAM,aAAa;AACvB,UAAI,MAAM,iBAAiB;AAC3B,UAAI,MAAM,SAAS,cAAc,sBAAsB;AACvD,UAAI,MAAM,SAAS;AACnB,UAAI,MAAM,kBAAkB,OAAO;AACnC,UAAI,MAAM,QAAQ,OAAO;AACzB,UAAI,MAAM,eAAe,GAAG,YAAY;AACxC,UAAI,MAAM,aAAa;AACvB,UAAI,MAAM,aAAa;AAEvB,UAAI,SAAS;AACX,YAAI,MAAM,QAAQ,GAAG,IAAI;AACzB,YAAI,MAAM,SAAS,GAAG,IAAI;AAC1B,YAAI,MAAM,UAAU;AACpB,YAAI,YAAY,iDAAiD,OAAO;AAAA,MAC1E,OAAO;AACL,YAAI,MAAM,QAAQ;AAClB,YAAI,MAAM,SAAS,GAAG,MAAM;AAC5B,YAAI,MAAM,MAAM;AAChB,YAAI,MAAM,cAAc;AACxB,YAAI,MAAM,eAAe;AACzB,cAAM,cAAc,SAAS,QAAQ,KAAK,iBAAiB,WAAW;AACtE,YAAI,YAAY,+DAA+D,OAAO,0EAA0E,WAAW;AAAA,MAC7K;AAEA,UAAI,iBAAiB,SAAS,MAAM,YAAY,UAAU,GAAG,CAAC;AAC9D,UAAI,iBAAiB,cAAc,MAAO,IAAI,MAAM,UAAU,MAAO;AACrE,UAAI,iBAAiB,cAAc,MAAO,IAAI,MAAM,UAAU,GAAI;AAElE,cAAQ,KAAK,GAAG;AAChB,cAAQ,YAAY,GAAG;AAAA,IACzB;AAEA,cAAU,YAAY,OAAO;AAAA,EAC/B;AAEA,SAAO,aAAa,EAAE,KAAK,CAAC,MAA2B;AACrD,gBAAY;AACZ,QAAI,UAAU,SAAS,GAAG;AACxB,oBAAc,SAAS;AAAA,IACzB;AAAA,EACF,CAAC;AAED,SAAO,MAAM;AACX,cAAU,YAAY;AACtB,cAAU,CAAC;AAAA,EACb;AACF;","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
|
-
|
|
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
|
-
|
|
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
|
|
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": "
|
|
41
|
-
"@authon/shared": "
|
|
40
|
+
"@authon/js": "^0.2.1",
|
|
41
|
+
"@authon/shared": "^0.2.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"nuxt": "^3.0.0"
|