@dalgoridim/headless-cms 0.3.0 → 0.4.0

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.
@@ -1,4 +1,4 @@
1
- import * as React from 'react';
1
+ import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { Auth, GoogleAuthProvider, User } from 'firebase/auth';
4
4
 
@@ -22,7 +22,7 @@ interface FirebaseAuthContextValue {
22
22
  loginWithEmail: (email: string, password: string) => Promise<void>;
23
23
  logout: () => Promise<void>;
24
24
  }
25
- declare function FirebaseAuthProvider({ children, auth, googleProvider, cookieName, onLogout, }: FirebaseAuthProviderProps): React.JSX.Element;
25
+ declare function FirebaseAuthProvider({ children, auth, googleProvider, cookieName, onLogout, }: FirebaseAuthProviderProps): react.JSX.Element;
26
26
  /** Extended Firebase auth API (login/logout/user) for login pages and toolbars. */
27
27
  declare function useFirebaseAuth(): FirebaseAuthContextValue;
28
28
 
@@ -1,4 +1,4 @@
1
- import * as React from 'react';
1
+ import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
  import { Auth, GoogleAuthProvider, User } from 'firebase/auth';
4
4
 
@@ -22,7 +22,7 @@ interface FirebaseAuthContextValue {
22
22
  loginWithEmail: (email: string, password: string) => Promise<void>;
23
23
  logout: () => Promise<void>;
24
24
  }
25
- declare function FirebaseAuthProvider({ children, auth, googleProvider, cookieName, onLogout, }: FirebaseAuthProviderProps): React.JSX.Element;
25
+ declare function FirebaseAuthProvider({ children, auth, googleProvider, cookieName, onLogout, }: FirebaseAuthProviderProps): react.JSX.Element;
26
26
  /** Extended Firebase auth API (login/logout/user) for login pages and toolbars. */
27
27
  declare function useFirebaseAuth(): FirebaseAuthContextValue;
28
28
 
@@ -1,9 +1,38 @@
1
1
  "use strict";
2
2
  "use client";
3
3
  var __defProp = Object.defineProperty;
4
+ var __defProps = Object.defineProperties;
4
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
7
  var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
9
  var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
11
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
12
+ var __spreadValues = (a, b) => {
13
+ for (var prop in b || (b = {}))
14
+ if (__hasOwnProp.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ if (__getOwnPropSymbols)
17
+ for (var prop of __getOwnPropSymbols(b)) {
18
+ if (__propIsEnum.call(b, prop))
19
+ __defNormalProp(a, prop, b[prop]);
20
+ }
21
+ return a;
22
+ };
23
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
24
+ var __objRest = (source, exclude) => {
25
+ var target = {};
26
+ for (var prop in source)
27
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
28
+ target[prop] = source[prop];
29
+ if (source != null && __getOwnPropSymbols)
30
+ for (var prop of __getOwnPropSymbols(source)) {
31
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
32
+ target[prop] = source[prop];
33
+ }
34
+ return target;
35
+ };
7
36
  var __export = (target, all) => {
8
37
  for (var name in all)
9
38
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -22,13 +51,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
22
51
  var client_exports = {};
23
52
  __export(client_exports, {
24
53
  GoogleAuthProvider: () => GoogleAuthProvider,
54
+ GoogleSignInButton: () => GoogleSignInButton,
25
55
  useGoogleAuth: () => useGoogleAuth
26
56
  });
27
57
  module.exports = __toCommonJS(client_exports);
28
58
  var import_react = require("react");
59
+ var import_google = require("@react-oauth/google");
29
60
  var import_client = require("@dalgoridim/headless-cms/client");
30
61
  var import_jsx_runtime = require("react/jsx-runtime");
31
- var GSI_SRC = "https://accounts.google.com/gsi/client";
32
62
  var GoogleAuthContext = (0, import_react.createContext)(
33
63
  void 0
34
64
  );
@@ -49,15 +79,13 @@ function decodeJwt(token) {
49
79
  try {
50
80
  const payload = token.split(".")[1];
51
81
  const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
52
- const data = JSON.parse(json);
53
- return data;
82
+ return JSON.parse(json);
54
83
  } catch (e) {
55
84
  return null;
56
85
  }
57
86
  }
58
- function GoogleAuthProvider({
87
+ function AuthState({
59
88
  children,
60
- clientId,
61
89
  adminEmails,
62
90
  cookieName = "adminToken",
63
91
  onLogout
@@ -65,11 +93,10 @@ function GoogleAuthProvider({
65
93
  const [user, setUser] = (0, import_react.useState)(null);
66
94
  const [isAdmin, setIsAdmin] = (0, import_react.useState)(false);
67
95
  const [isEditing, setIsEditing] = (0, import_react.useState)(false);
68
- const [ready, setReady] = (0, import_react.useState)(false);
69
96
  const allowed = (0, import_react.useRef)(
70
97
  (adminEmails != null ? adminEmails : []).map((e) => e.trim().toLowerCase())
71
98
  );
72
- const applyToken = (0, import_react.useCallback)(
99
+ const applyCredential = (0, import_react.useCallback)(
73
100
  (token) => {
74
101
  var _a;
75
102
  const claims = decodeJwt(token);
@@ -93,37 +120,16 @@ function GoogleAuthProvider({
93
120
  );
94
121
  (0, import_react.useEffect)(() => {
95
122
  const existing = readCookie(cookieName);
96
- if (existing) applyToken(existing);
97
- }, [cookieName, applyToken]);
98
- (0, import_react.useEffect)(() => {
99
- function init() {
100
- if (!window.google) return;
101
- window.google.accounts.id.initialize({
102
- client_id: clientId,
103
- callback: (res) => applyToken(res.credential),
104
- auto_select: false,
105
- cancel_on_tap_outside: true
106
- });
107
- setReady(true);
108
- }
109
- if (window.google) {
110
- init();
111
- return;
112
- }
113
- const existing = document.querySelector(
114
- `script[src="${GSI_SRC}"]`
115
- );
116
- if (existing) {
117
- existing.addEventListener("load", init);
118
- return () => existing.removeEventListener("load", init);
119
- }
120
- const script = document.createElement("script");
121
- script.src = GSI_SRC;
122
- script.async = true;
123
- script.defer = true;
124
- script.onload = init;
125
- document.head.appendChild(script);
126
- }, [clientId, applyToken]);
123
+ if (existing) applyCredential(existing);
124
+ }, [cookieName, applyCredential]);
125
+ const logout = (0, import_react.useCallback)(() => {
126
+ (0, import_google.googleLogout)();
127
+ deleteCookie(cookieName);
128
+ setUser(null);
129
+ setIsAdmin(false);
130
+ setIsEditing(false);
131
+ onLogout == null ? void 0 : onLogout();
132
+ }, [cookieName, onLogout]);
127
133
  (0, import_react.useEffect)(() => {
128
134
  const originalFetch = window.fetch;
129
135
  window.fetch = async (...args) => {
@@ -131,7 +137,7 @@ function GoogleAuthProvider({
131
137
  if (response.status === 401) {
132
138
  try {
133
139
  const data = await response.clone().json();
134
- if (data == null ? void 0 : data.logout) doLogout();
140
+ if (data == null ? void 0 : data.logout) logout();
135
141
  } catch (e) {
136
142
  }
137
143
  }
@@ -140,47 +146,48 @@ function GoogleAuthProvider({
140
146
  return () => {
141
147
  window.fetch = originalFetch;
142
148
  };
143
- }, []);
144
- function doLogout() {
145
- var _a;
146
- (_a = window.google) == null ? void 0 : _a.accounts.id.disableAutoSelect();
147
- deleteCookie(cookieName);
148
- setUser(null);
149
- setIsAdmin(false);
150
- setIsEditing(false);
151
- onLogout == null ? void 0 : onLogout();
152
- }
153
- const promptSignIn = (0, import_react.useCallback)(() => {
154
- var _a;
155
- if (!ready) return;
156
- (_a = window.google) == null ? void 0 : _a.accounts.id.prompt();
157
- }, [ready]);
158
- const renderButton = (0, import_react.useCallback)(
159
- (el, options) => {
160
- var _a;
161
- if (!ready) return;
162
- (_a = window.google) == null ? void 0 : _a.accounts.id.renderButton(el, options);
163
- },
164
- [ready]
165
- );
149
+ }, [logout]);
166
150
  const toggleEdit = (0, import_react.useCallback)(() => setIsEditing((p) => !p), []);
167
151
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
168
152
  GoogleAuthContext.Provider,
169
153
  {
170
- value: {
171
- user,
172
- isAdmin,
173
- isEditing,
174
- toggleEdit,
175
- ready,
176
- promptSignIn,
177
- renderButton,
178
- logout: doLogout
179
- },
154
+ value: { user, isAdmin, isEditing, toggleEdit, logout, applyCredential },
180
155
  children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_client.CmsAuthProvider, { value: { isAdmin, isEditing, toggleEdit }, children })
181
156
  }
182
157
  );
183
158
  }
159
+ function GoogleAuthProvider(_a) {
160
+ var _b = _a, {
161
+ children,
162
+ clientId
163
+ } = _b, rest = __objRest(_b, [
164
+ "children",
165
+ "clientId"
166
+ ]);
167
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_google.GoogleOAuthProvider, { clientId, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AuthState, __spreadProps(__spreadValues({}, rest), { children })) });
168
+ }
169
+ function GoogleSignInButton({
170
+ theme = "filled_black",
171
+ shape = "pill",
172
+ size = "large",
173
+ text = "continue_with",
174
+ onError
175
+ }) {
176
+ const { applyCredential } = useGoogleAuth();
177
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
178
+ import_google.GoogleLogin,
179
+ {
180
+ onSuccess: (res) => {
181
+ if (res.credential) applyCredential(res.credential);
182
+ },
183
+ onError,
184
+ theme,
185
+ shape,
186
+ size,
187
+ text
188
+ }
189
+ );
190
+ }
184
191
  function useGoogleAuth() {
185
192
  const ctx = (0, import_react.useContext)(GoogleAuthContext);
186
193
  if (!ctx) {
@@ -191,6 +198,7 @@ function useGoogleAuth() {
191
198
  // Annotate the CommonJS export names for ESM import in node:
192
199
  0 && (module.exports = {
193
200
  GoogleAuthProvider,
201
+ GoogleSignInButton,
194
202
  useGoogleAuth
195
203
  });
196
204
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/auth/google/client/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n// Imported via the public specifier so we share the SAME context instance as\n// the consumer's edit primitives at runtime (see tsup `external` + tsconfig paths).\nimport { CmsAuthProvider } from \"@dalgoridim/headless-cms/client\";\n\n/**\n * Client provider for **Google Identity Services** sign-in — the Firebase-free\n * counterpart to `FirebaseAuthProvider`. It loads the GSI script, lets the user\n * sign in with Google, stashes the resulting ID token in a cookie for the\n * server `googleAuth` adapter to verify, and feeds the shared CMS auth context\n * so the edit primitives light up. Admin status is optimistic on the client\n * (via `adminEmails`); the server gate remains authoritative.\n */\n\nconst GSI_SRC = \"https://accounts.google.com/gsi/client\";\n\n/** Minimal slice of the Google Identity Services API we use. */\ninterface GsiCredentialResponse {\n credential: string;\n}\ninterface GsiButtonOptions {\n type?: \"standard\" | \"icon\";\n theme?: \"outline\" | \"filled_blue\" | \"filled_black\";\n size?: \"small\" | \"medium\" | \"large\";\n text?: \"signin_with\" | \"signup_with\" | \"continue_with\" | \"signin\";\n shape?: \"rectangular\" | \"pill\" | \"circle\" | \"square\";\n width?: number;\n}\ninterface GsiClient {\n accounts: {\n id: {\n initialize: (config: {\n client_id: string;\n callback: (res: GsiCredentialResponse) => void;\n auto_select?: boolean;\n cancel_on_tap_outside?: boolean;\n }) => void;\n prompt: () => void;\n renderButton: (el: HTMLElement, options?: GsiButtonOptions) => void;\n disableAutoSelect: () => void;\n };\n };\n}\n\ndeclare global {\n interface Window {\n google?: GsiClient;\n }\n}\n\nexport interface GoogleUser {\n sub: string;\n email?: string;\n name?: string;\n picture?: string;\n}\n\nexport interface GoogleAuthProviderProps {\n children: ReactNode;\n /** OAuth 2.0 Web client ID (from Google Cloud Console → Credentials). */\n clientId: string;\n /**\n * Optional admin allowlist for *optimistic* client-side `isAdmin`. The server\n * `googleAuth` adapter still enforces the real gate. Omit to treat any\n * successful sign-in as optimistically admin (server will correct via 401).\n */\n adminEmails?: string[];\n /** Cookie name for the ID token. Default `adminToken`. */\n cookieName?: string;\n /** Called when a 401 `{ logout: true }` response is intercepted. */\n onLogout?: () => void;\n}\n\nexport interface GoogleAuthContextValue {\n user: GoogleUser | null;\n isAdmin: boolean;\n isEditing: boolean;\n toggleEdit: () => void;\n /** True once the GSI script has loaded and the client is initialized. */\n ready: boolean;\n /** Trigger Google One Tap / sign-in prompt. */\n promptSignIn: () => void;\n /** Render the official Google button into `el` (most reliable trigger). */\n renderButton: (el: HTMLElement, options?: GsiButtonOptions) => void;\n logout: () => void;\n}\n\nconst GoogleAuthContext = createContext<GoogleAuthContextValue | undefined>(\n undefined,\n);\n\nfunction setCookie(name: string, value: string) {\n document.cookie = `${name}=${encodeURIComponent(value)}; path=/; samesite=lax`;\n}\nfunction deleteCookie(name: string) {\n document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;\n}\nfunction readCookie(name: string): string | null {\n for (const part of document.cookie.split(\";\")) {\n const [k, ...v] = part.trim().split(\"=\");\n if (k === name) return decodeURIComponent(v.join(\"=\"));\n }\n return null;\n}\n\n/** Decode a JWT payload client-side (no verification — the server does that). */\nfunction decodeJwt(token: string): (GoogleUser & { exp?: number }) | null {\n try {\n const payload = token.split(\".\")[1];\n const json = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n const data = JSON.parse(json) as {\n sub: string;\n email?: string;\n name?: string;\n picture?: string;\n exp?: number;\n };\n return data;\n } catch {\n return null;\n }\n}\n\nexport function GoogleAuthProvider({\n children,\n clientId,\n adminEmails,\n cookieName = \"adminToken\",\n onLogout,\n}: GoogleAuthProviderProps) {\n const [user, setUser] = useState<GoogleUser | null>(null);\n const [isAdmin, setIsAdmin] = useState(false);\n const [isEditing, setIsEditing] = useState(false);\n const [ready, setReady] = useState(false);\n const allowed = useRef(\n (adminEmails ?? []).map((e) => e.trim().toLowerCase()),\n );\n\n const applyToken = useCallback(\n (token: string) => {\n const claims = decodeJwt(token);\n if (!claims) return;\n if (claims.exp && claims.exp * 1000 <= Date.now()) {\n deleteCookie(cookieName);\n return;\n }\n const email = claims.email?.toLowerCase();\n // Optimistic: if no allowlist supplied, trust the sign-in and let the\n // server correct a non-admin via the 401 interceptor below.\n const admin =\n allowed.current.length === 0\n ? true\n : !!email && allowed.current.includes(email);\n setCookie(cookieName, token);\n setUser({\n sub: claims.sub,\n email: claims.email,\n name: claims.name,\n picture: claims.picture,\n });\n setIsAdmin(admin);\n },\n [cookieName],\n );\n\n // Restore an existing session from the cookie on mount.\n useEffect(() => {\n const existing = readCookie(cookieName);\n if (existing) applyToken(existing);\n }, [cookieName, applyToken]);\n\n // Load the GSI script and initialize the One Tap / button client.\n useEffect(() => {\n function init() {\n if (!window.google) return;\n window.google.accounts.id.initialize({\n client_id: clientId,\n callback: (res) => applyToken(res.credential),\n auto_select: false,\n cancel_on_tap_outside: true,\n });\n setReady(true);\n }\n\n if (window.google) {\n init();\n return;\n }\n const existing = document.querySelector<HTMLScriptElement>(\n `script[src=\"${GSI_SRC}\"]`,\n );\n if (existing) {\n existing.addEventListener(\"load\", init);\n return () => existing.removeEventListener(\"load\", init);\n }\n const script = document.createElement(\"script\");\n script.src = GSI_SRC;\n script.async = true;\n script.defer = true;\n script.onload = init;\n document.head.appendChild(script);\n }, [clientId, applyToken]);\n\n // Intercept admin 401s so an expired/forbidden session forces sign-out.\n useEffect(() => {\n const originalFetch = window.fetch;\n window.fetch = async (...args: Parameters<typeof fetch>) => {\n const response = await originalFetch(...args);\n if (response.status === 401) {\n try {\n const data = await response.clone().json();\n if (data?.logout) doLogout();\n } catch {\n /* not a JSON body — ignore */\n }\n }\n return response;\n };\n return () => {\n window.fetch = originalFetch;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n function doLogout() {\n window.google?.accounts.id.disableAutoSelect();\n deleteCookie(cookieName);\n setUser(null);\n setIsAdmin(false);\n setIsEditing(false);\n onLogout?.();\n }\n\n const promptSignIn = useCallback(() => {\n if (!ready) return;\n window.google?.accounts.id.prompt();\n }, [ready]);\n\n const renderButton = useCallback(\n (el: HTMLElement, options?: GsiButtonOptions) => {\n if (!ready) return;\n window.google?.accounts.id.renderButton(el, options);\n },\n [ready],\n );\n\n const toggleEdit = useCallback(() => setIsEditing((p) => !p), []);\n\n return (\n <GoogleAuthContext.Provider\n value={{\n user,\n isAdmin,\n isEditing,\n toggleEdit,\n ready,\n promptSignIn,\n renderButton,\n logout: doLogout,\n }}\n >\n <CmsAuthProvider value={{ isAdmin, isEditing, toggleEdit }}>\n {children}\n </CmsAuthProvider>\n </GoogleAuthContext.Provider>\n );\n}\n\n/** Google auth API (user/login/logout) for login pages and toolbars. */\nexport function useGoogleAuth(): GoogleAuthContextValue {\n const ctx = useContext(GoogleAuthContext);\n if (!ctx) {\n throw new Error(\"useGoogleAuth must be used within a GoogleAuthProvider\");\n }\n return ctx;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/auth/google/client/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport {\n GoogleOAuthProvider,\n GoogleLogin,\n googleLogout,\n type CredentialResponse,\n} from \"@react-oauth/google\";\n// Imported via the public specifier so we share the SAME context instance as\n// the consumer's edit primitives at runtime (see tsup `external` + tsconfig paths).\nimport { CmsAuthProvider } from \"@dalgoridim/headless-cms/client\";\n\n/**\n * Client provider for **Google sign-in**, built on `@react-oauth/google` (the\n * official React wrapper for Google Identity Services). It handles the GSI\n * script + button rendering robustly — including remounts, so the button shows\n * reliably every time a sign-in dialog opens. The resulting ID token is stashed\n * in a cookie for the server `googleAuth` adapter to verify; admin status is\n * optimistic on the client (via `adminEmails`), with the server gate\n * authoritative.\n *\n * Render `<GoogleSignInButton />` anywhere inside the provider to show the\n * official Google button.\n */\n\nexport interface GoogleUser {\n sub: string;\n email?: string;\n name?: string;\n picture?: string;\n}\n\nexport interface GoogleAuthProviderProps {\n children: ReactNode;\n /** OAuth 2.0 Web client ID (from Google Cloud Console → Credentials). */\n clientId: string;\n /**\n * Optional admin allowlist for *optimistic* client-side `isAdmin`. The server\n * `googleAuth` adapter still enforces the real gate. Omit to treat any\n * successful sign-in as optimistically admin (server will correct via 401).\n */\n adminEmails?: string[];\n /** Cookie name for the ID token. Default `adminToken`. */\n cookieName?: string;\n /** Called when a 401 `{ logout: true }` response is intercepted. */\n onLogout?: () => void;\n}\n\nexport interface GoogleAuthContextValue {\n user: GoogleUser | null;\n isAdmin: boolean;\n isEditing: boolean;\n toggleEdit: () => void;\n logout: () => void;\n /** Feed a Google ID token credential (used by `GoogleSignInButton`). */\n applyCredential: (credential: string) => void;\n}\n\nconst GoogleAuthContext = createContext<GoogleAuthContextValue | undefined>(\n undefined,\n);\n\nfunction setCookie(name: string, value: string) {\n document.cookie = `${name}=${encodeURIComponent(value)}; path=/; samesite=lax`;\n}\nfunction deleteCookie(name: string) {\n document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;\n}\nfunction readCookie(name: string): string | null {\n for (const part of document.cookie.split(\";\")) {\n const [k, ...v] = part.trim().split(\"=\");\n if (k === name) return decodeURIComponent(v.join(\"=\"));\n }\n return null;\n}\n\n/** Decode a JWT payload client-side (no verification — the server does that). */\nfunction decodeJwt(token: string): (GoogleUser & { exp?: number }) | null {\n try {\n const payload = token.split(\".\")[1];\n const json = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n return JSON.parse(json);\n } catch {\n return null;\n }\n}\n\nfunction AuthState({\n children,\n adminEmails,\n cookieName = \"adminToken\",\n onLogout,\n}: Omit<GoogleAuthProviderProps, \"clientId\">) {\n const [user, setUser] = useState<GoogleUser | null>(null);\n const [isAdmin, setIsAdmin] = useState(false);\n const [isEditing, setIsEditing] = useState(false);\n const allowed = useRef(\n (adminEmails ?? []).map((e) => e.trim().toLowerCase()),\n );\n\n const applyCredential = useCallback(\n (token: string) => {\n const claims = decodeJwt(token);\n if (!claims) return;\n if (claims.exp && claims.exp * 1000 <= Date.now()) {\n deleteCookie(cookieName);\n return;\n }\n const email = claims.email?.toLowerCase();\n // Optimistic: if no allowlist supplied, trust the sign-in and let the\n // server correct a non-admin via the 401 interceptor below.\n const admin =\n allowed.current.length === 0\n ? true\n : !!email && allowed.current.includes(email);\n setCookie(cookieName, token);\n setUser({\n sub: claims.sub,\n email: claims.email,\n name: claims.name,\n picture: claims.picture,\n });\n setIsAdmin(admin);\n },\n [cookieName],\n );\n\n // Restore an existing session from the cookie on mount.\n useEffect(() => {\n const existing = readCookie(cookieName);\n if (existing) applyCredential(existing);\n }, [cookieName, applyCredential]);\n\n const logout = useCallback(() => {\n googleLogout();\n deleteCookie(cookieName);\n setUser(null);\n setIsAdmin(false);\n setIsEditing(false);\n onLogout?.();\n }, [cookieName, onLogout]);\n\n // Intercept admin 401s so an expired/forbidden session forces sign-out.\n useEffect(() => {\n const originalFetch = window.fetch;\n window.fetch = async (...args: Parameters<typeof fetch>) => {\n const response = await originalFetch(...args);\n if (response.status === 401) {\n try {\n const data = await response.clone().json();\n if (data?.logout) logout();\n } catch {\n /* not a JSON body — ignore */\n }\n }\n return response;\n };\n return () => {\n window.fetch = originalFetch;\n };\n }, [logout]);\n\n const toggleEdit = useCallback(() => setIsEditing((p) => !p), []);\n\n return (\n <GoogleAuthContext.Provider\n value={{ user, isAdmin, isEditing, toggleEdit, logout, applyCredential }}\n >\n <CmsAuthProvider value={{ isAdmin, isEditing, toggleEdit }}>\n {children}\n </CmsAuthProvider>\n </GoogleAuthContext.Provider>\n );\n}\n\nexport function GoogleAuthProvider({\n children,\n clientId,\n ...rest\n}: GoogleAuthProviderProps) {\n return (\n <GoogleOAuthProvider clientId={clientId}>\n <AuthState {...rest}>{children}</AuthState>\n </GoogleOAuthProvider>\n );\n}\n\nexport interface GoogleSignInButtonProps {\n /** Forwarded to the underlying `<GoogleLogin>` (theme, shape, size, text…). */\n theme?: \"outline\" | \"filled_blue\" | \"filled_black\";\n shape?: \"rectangular\" | \"pill\" | \"circle\" | \"square\";\n size?: \"small\" | \"medium\" | \"large\";\n text?: \"signin_with\" | \"signup_with\" | \"continue_with\" | \"signin\";\n onError?: () => void;\n}\n\n/** The official Google sign-in button. Must be rendered inside a provider. */\nexport function GoogleSignInButton({\n theme = \"filled_black\",\n shape = \"pill\",\n size = \"large\",\n text = \"continue_with\",\n onError,\n}: GoogleSignInButtonProps) {\n const { applyCredential } = useGoogleAuth();\n return (\n <GoogleLogin\n onSuccess={(res: CredentialResponse) => {\n if (res.credential) applyCredential(res.credential);\n }}\n onError={onError}\n theme={theme}\n shape={shape}\n size={size}\n text={text}\n />\n );\n}\n\n/** Google auth API (user/logout) for login pages and toolbars. */\nexport function useGoogleAuth(): GoogleAuthContextValue {\n const ctx = useContext(GoogleAuthContext);\n if (!ctx) {\n throw new Error(\"useGoogleAuth must be used within a GoogleAuthProvider\");\n }\n return ctx;\n}\n"],"mappings":"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","names":[]}
@@ -1,38 +1,18 @@
1
- import * as React from 'react';
1
+ import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
 
4
- /** Minimal slice of the Google Identity Services API we use. */
5
- interface GsiCredentialResponse {
6
- credential: string;
7
- }
8
- interface GsiButtonOptions {
9
- type?: "standard" | "icon";
10
- theme?: "outline" | "filled_blue" | "filled_black";
11
- size?: "small" | "medium" | "large";
12
- text?: "signin_with" | "signup_with" | "continue_with" | "signin";
13
- shape?: "rectangular" | "pill" | "circle" | "square";
14
- width?: number;
15
- }
16
- interface GsiClient {
17
- accounts: {
18
- id: {
19
- initialize: (config: {
20
- client_id: string;
21
- callback: (res: GsiCredentialResponse) => void;
22
- auto_select?: boolean;
23
- cancel_on_tap_outside?: boolean;
24
- }) => void;
25
- prompt: () => void;
26
- renderButton: (el: HTMLElement, options?: GsiButtonOptions) => void;
27
- disableAutoSelect: () => void;
28
- };
29
- };
30
- }
31
- declare global {
32
- interface Window {
33
- google?: GsiClient;
34
- }
35
- }
4
+ /**
5
+ * Client provider for **Google sign-in**, built on `@react-oauth/google` (the
6
+ * official React wrapper for Google Identity Services). It handles the GSI
7
+ * script + button rendering robustly — including remounts, so the button shows
8
+ * reliably every time a sign-in dialog opens. The resulting ID token is stashed
9
+ * in a cookie for the server `googleAuth` adapter to verify; admin status is
10
+ * optimistic on the client (via `adminEmails`), with the server gate
11
+ * authoritative.
12
+ *
13
+ * Render `<GoogleSignInButton />` anywhere inside the provider to show the
14
+ * official Google button.
15
+ */
36
16
  interface GoogleUser {
37
17
  sub: string;
38
18
  email?: string;
@@ -59,16 +39,22 @@ interface GoogleAuthContextValue {
59
39
  isAdmin: boolean;
60
40
  isEditing: boolean;
61
41
  toggleEdit: () => void;
62
- /** True once the GSI script has loaded and the client is initialized. */
63
- ready: boolean;
64
- /** Trigger Google One Tap / sign-in prompt. */
65
- promptSignIn: () => void;
66
- /** Render the official Google button into `el` (most reliable trigger). */
67
- renderButton: (el: HTMLElement, options?: GsiButtonOptions) => void;
68
42
  logout: () => void;
43
+ /** Feed a Google ID token credential (used by `GoogleSignInButton`). */
44
+ applyCredential: (credential: string) => void;
45
+ }
46
+ declare function GoogleAuthProvider({ children, clientId, ...rest }: GoogleAuthProviderProps): react.JSX.Element;
47
+ interface GoogleSignInButtonProps {
48
+ /** Forwarded to the underlying `<GoogleLogin>` (theme, shape, size, text…). */
49
+ theme?: "outline" | "filled_blue" | "filled_black";
50
+ shape?: "rectangular" | "pill" | "circle" | "square";
51
+ size?: "small" | "medium" | "large";
52
+ text?: "signin_with" | "signup_with" | "continue_with" | "signin";
53
+ onError?: () => void;
69
54
  }
70
- declare function GoogleAuthProvider({ children, clientId, adminEmails, cookieName, onLogout, }: GoogleAuthProviderProps): React.JSX.Element;
71
- /** Google auth API (user/login/logout) for login pages and toolbars. */
55
+ /** The official Google sign-in button. Must be rendered inside a provider. */
56
+ declare function GoogleSignInButton({ theme, shape, size, text, onError, }: GoogleSignInButtonProps): react.JSX.Element;
57
+ /** Google auth API (user/logout) for login pages and toolbars. */
72
58
  declare function useGoogleAuth(): GoogleAuthContextValue;
73
59
 
74
- export { type GoogleAuthContextValue, GoogleAuthProvider, type GoogleAuthProviderProps, type GoogleUser, useGoogleAuth };
60
+ export { type GoogleAuthContextValue, GoogleAuthProvider, type GoogleAuthProviderProps, GoogleSignInButton, type GoogleSignInButtonProps, type GoogleUser, useGoogleAuth };
@@ -1,38 +1,18 @@
1
- import * as React from 'react';
1
+ import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
 
4
- /** Minimal slice of the Google Identity Services API we use. */
5
- interface GsiCredentialResponse {
6
- credential: string;
7
- }
8
- interface GsiButtonOptions {
9
- type?: "standard" | "icon";
10
- theme?: "outline" | "filled_blue" | "filled_black";
11
- size?: "small" | "medium" | "large";
12
- text?: "signin_with" | "signup_with" | "continue_with" | "signin";
13
- shape?: "rectangular" | "pill" | "circle" | "square";
14
- width?: number;
15
- }
16
- interface GsiClient {
17
- accounts: {
18
- id: {
19
- initialize: (config: {
20
- client_id: string;
21
- callback: (res: GsiCredentialResponse) => void;
22
- auto_select?: boolean;
23
- cancel_on_tap_outside?: boolean;
24
- }) => void;
25
- prompt: () => void;
26
- renderButton: (el: HTMLElement, options?: GsiButtonOptions) => void;
27
- disableAutoSelect: () => void;
28
- };
29
- };
30
- }
31
- declare global {
32
- interface Window {
33
- google?: GsiClient;
34
- }
35
- }
4
+ /**
5
+ * Client provider for **Google sign-in**, built on `@react-oauth/google` (the
6
+ * official React wrapper for Google Identity Services). It handles the GSI
7
+ * script + button rendering robustly — including remounts, so the button shows
8
+ * reliably every time a sign-in dialog opens. The resulting ID token is stashed
9
+ * in a cookie for the server `googleAuth` adapter to verify; admin status is
10
+ * optimistic on the client (via `adminEmails`), with the server gate
11
+ * authoritative.
12
+ *
13
+ * Render `<GoogleSignInButton />` anywhere inside the provider to show the
14
+ * official Google button.
15
+ */
36
16
  interface GoogleUser {
37
17
  sub: string;
38
18
  email?: string;
@@ -59,16 +39,22 @@ interface GoogleAuthContextValue {
59
39
  isAdmin: boolean;
60
40
  isEditing: boolean;
61
41
  toggleEdit: () => void;
62
- /** True once the GSI script has loaded and the client is initialized. */
63
- ready: boolean;
64
- /** Trigger Google One Tap / sign-in prompt. */
65
- promptSignIn: () => void;
66
- /** Render the official Google button into `el` (most reliable trigger). */
67
- renderButton: (el: HTMLElement, options?: GsiButtonOptions) => void;
68
42
  logout: () => void;
43
+ /** Feed a Google ID token credential (used by `GoogleSignInButton`). */
44
+ applyCredential: (credential: string) => void;
45
+ }
46
+ declare function GoogleAuthProvider({ children, clientId, ...rest }: GoogleAuthProviderProps): react.JSX.Element;
47
+ interface GoogleSignInButtonProps {
48
+ /** Forwarded to the underlying `<GoogleLogin>` (theme, shape, size, text…). */
49
+ theme?: "outline" | "filled_blue" | "filled_black";
50
+ shape?: "rectangular" | "pill" | "circle" | "square";
51
+ size?: "small" | "medium" | "large";
52
+ text?: "signin_with" | "signup_with" | "continue_with" | "signin";
53
+ onError?: () => void;
69
54
  }
70
- declare function GoogleAuthProvider({ children, clientId, adminEmails, cookieName, onLogout, }: GoogleAuthProviderProps): React.JSX.Element;
71
- /** Google auth API (user/login/logout) for login pages and toolbars. */
55
+ /** The official Google sign-in button. Must be rendered inside a provider. */
56
+ declare function GoogleSignInButton({ theme, shape, size, text, onError, }: GoogleSignInButtonProps): react.JSX.Element;
57
+ /** Google auth API (user/logout) for login pages and toolbars. */
72
58
  declare function useGoogleAuth(): GoogleAuthContextValue;
73
59
 
74
- export { type GoogleAuthContextValue, GoogleAuthProvider, type GoogleAuthProviderProps, type GoogleUser, useGoogleAuth };
60
+ export { type GoogleAuthContextValue, GoogleAuthProvider, type GoogleAuthProviderProps, GoogleSignInButton, type GoogleSignInButtonProps, type GoogleUser, useGoogleAuth };
@@ -1,4 +1,35 @@
1
1
  "use client";
2
+ var __defProp = Object.defineProperty;
3
+ var __defProps = Object.defineProperties;
4
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
20
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
+ var __objRest = (source, exclude) => {
22
+ var target = {};
23
+ for (var prop in source)
24
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
25
+ target[prop] = source[prop];
26
+ if (source != null && __getOwnPropSymbols)
27
+ for (var prop of __getOwnPropSymbols(source)) {
28
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
29
+ target[prop] = source[prop];
30
+ }
31
+ return target;
32
+ };
2
33
 
3
34
  // src/auth/google/client/index.tsx
4
35
  import {
@@ -9,9 +40,13 @@ import {
9
40
  useRef,
10
41
  useState
11
42
  } from "react";
43
+ import {
44
+ GoogleOAuthProvider,
45
+ GoogleLogin,
46
+ googleLogout
47
+ } from "@react-oauth/google";
12
48
  import { CmsAuthProvider } from "@dalgoridim/headless-cms/client";
13
49
  import { jsx } from "react/jsx-runtime";
14
- var GSI_SRC = "https://accounts.google.com/gsi/client";
15
50
  var GoogleAuthContext = createContext(
16
51
  void 0
17
52
  );
@@ -32,15 +67,13 @@ function decodeJwt(token) {
32
67
  try {
33
68
  const payload = token.split(".")[1];
34
69
  const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
35
- const data = JSON.parse(json);
36
- return data;
70
+ return JSON.parse(json);
37
71
  } catch (e) {
38
72
  return null;
39
73
  }
40
74
  }
41
- function GoogleAuthProvider({
75
+ function AuthState({
42
76
  children,
43
- clientId,
44
77
  adminEmails,
45
78
  cookieName = "adminToken",
46
79
  onLogout
@@ -48,11 +81,10 @@ function GoogleAuthProvider({
48
81
  const [user, setUser] = useState(null);
49
82
  const [isAdmin, setIsAdmin] = useState(false);
50
83
  const [isEditing, setIsEditing] = useState(false);
51
- const [ready, setReady] = useState(false);
52
84
  const allowed = useRef(
53
85
  (adminEmails != null ? adminEmails : []).map((e) => e.trim().toLowerCase())
54
86
  );
55
- const applyToken = useCallback(
87
+ const applyCredential = useCallback(
56
88
  (token) => {
57
89
  var _a;
58
90
  const claims = decodeJwt(token);
@@ -76,37 +108,16 @@ function GoogleAuthProvider({
76
108
  );
77
109
  useEffect(() => {
78
110
  const existing = readCookie(cookieName);
79
- if (existing) applyToken(existing);
80
- }, [cookieName, applyToken]);
81
- useEffect(() => {
82
- function init() {
83
- if (!window.google) return;
84
- window.google.accounts.id.initialize({
85
- client_id: clientId,
86
- callback: (res) => applyToken(res.credential),
87
- auto_select: false,
88
- cancel_on_tap_outside: true
89
- });
90
- setReady(true);
91
- }
92
- if (window.google) {
93
- init();
94
- return;
95
- }
96
- const existing = document.querySelector(
97
- `script[src="${GSI_SRC}"]`
98
- );
99
- if (existing) {
100
- existing.addEventListener("load", init);
101
- return () => existing.removeEventListener("load", init);
102
- }
103
- const script = document.createElement("script");
104
- script.src = GSI_SRC;
105
- script.async = true;
106
- script.defer = true;
107
- script.onload = init;
108
- document.head.appendChild(script);
109
- }, [clientId, applyToken]);
111
+ if (existing) applyCredential(existing);
112
+ }, [cookieName, applyCredential]);
113
+ const logout = useCallback(() => {
114
+ googleLogout();
115
+ deleteCookie(cookieName);
116
+ setUser(null);
117
+ setIsAdmin(false);
118
+ setIsEditing(false);
119
+ onLogout == null ? void 0 : onLogout();
120
+ }, [cookieName, onLogout]);
110
121
  useEffect(() => {
111
122
  const originalFetch = window.fetch;
112
123
  window.fetch = async (...args) => {
@@ -114,7 +125,7 @@ function GoogleAuthProvider({
114
125
  if (response.status === 401) {
115
126
  try {
116
127
  const data = await response.clone().json();
117
- if (data == null ? void 0 : data.logout) doLogout();
128
+ if (data == null ? void 0 : data.logout) logout();
118
129
  } catch (e) {
119
130
  }
120
131
  }
@@ -123,47 +134,48 @@ function GoogleAuthProvider({
123
134
  return () => {
124
135
  window.fetch = originalFetch;
125
136
  };
126
- }, []);
127
- function doLogout() {
128
- var _a;
129
- (_a = window.google) == null ? void 0 : _a.accounts.id.disableAutoSelect();
130
- deleteCookie(cookieName);
131
- setUser(null);
132
- setIsAdmin(false);
133
- setIsEditing(false);
134
- onLogout == null ? void 0 : onLogout();
135
- }
136
- const promptSignIn = useCallback(() => {
137
- var _a;
138
- if (!ready) return;
139
- (_a = window.google) == null ? void 0 : _a.accounts.id.prompt();
140
- }, [ready]);
141
- const renderButton = useCallback(
142
- (el, options) => {
143
- var _a;
144
- if (!ready) return;
145
- (_a = window.google) == null ? void 0 : _a.accounts.id.renderButton(el, options);
146
- },
147
- [ready]
148
- );
137
+ }, [logout]);
149
138
  const toggleEdit = useCallback(() => setIsEditing((p) => !p), []);
150
139
  return /* @__PURE__ */ jsx(
151
140
  GoogleAuthContext.Provider,
152
141
  {
153
- value: {
154
- user,
155
- isAdmin,
156
- isEditing,
157
- toggleEdit,
158
- ready,
159
- promptSignIn,
160
- renderButton,
161
- logout: doLogout
162
- },
142
+ value: { user, isAdmin, isEditing, toggleEdit, logout, applyCredential },
163
143
  children: /* @__PURE__ */ jsx(CmsAuthProvider, { value: { isAdmin, isEditing, toggleEdit }, children })
164
144
  }
165
145
  );
166
146
  }
147
+ function GoogleAuthProvider(_a) {
148
+ var _b = _a, {
149
+ children,
150
+ clientId
151
+ } = _b, rest = __objRest(_b, [
152
+ "children",
153
+ "clientId"
154
+ ]);
155
+ return /* @__PURE__ */ jsx(GoogleOAuthProvider, { clientId, children: /* @__PURE__ */ jsx(AuthState, __spreadProps(__spreadValues({}, rest), { children })) });
156
+ }
157
+ function GoogleSignInButton({
158
+ theme = "filled_black",
159
+ shape = "pill",
160
+ size = "large",
161
+ text = "continue_with",
162
+ onError
163
+ }) {
164
+ const { applyCredential } = useGoogleAuth();
165
+ return /* @__PURE__ */ jsx(
166
+ GoogleLogin,
167
+ {
168
+ onSuccess: (res) => {
169
+ if (res.credential) applyCredential(res.credential);
170
+ },
171
+ onError,
172
+ theme,
173
+ shape,
174
+ size,
175
+ text
176
+ }
177
+ );
178
+ }
167
179
  function useGoogleAuth() {
168
180
  const ctx = useContext(GoogleAuthContext);
169
181
  if (!ctx) {
@@ -173,6 +185,7 @@ function useGoogleAuth() {
173
185
  }
174
186
  export {
175
187
  GoogleAuthProvider,
188
+ GoogleSignInButton,
176
189
  useGoogleAuth
177
190
  };
178
191
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/auth/google/client/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\n// Imported via the public specifier so we share the SAME context instance as\n// the consumer's edit primitives at runtime (see tsup `external` + tsconfig paths).\nimport { CmsAuthProvider } from \"@dalgoridim/headless-cms/client\";\n\n/**\n * Client provider for **Google Identity Services** sign-in — the Firebase-free\n * counterpart to `FirebaseAuthProvider`. It loads the GSI script, lets the user\n * sign in with Google, stashes the resulting ID token in a cookie for the\n * server `googleAuth` adapter to verify, and feeds the shared CMS auth context\n * so the edit primitives light up. Admin status is optimistic on the client\n * (via `adminEmails`); the server gate remains authoritative.\n */\n\nconst GSI_SRC = \"https://accounts.google.com/gsi/client\";\n\n/** Minimal slice of the Google Identity Services API we use. */\ninterface GsiCredentialResponse {\n credential: string;\n}\ninterface GsiButtonOptions {\n type?: \"standard\" | \"icon\";\n theme?: \"outline\" | \"filled_blue\" | \"filled_black\";\n size?: \"small\" | \"medium\" | \"large\";\n text?: \"signin_with\" | \"signup_with\" | \"continue_with\" | \"signin\";\n shape?: \"rectangular\" | \"pill\" | \"circle\" | \"square\";\n width?: number;\n}\ninterface GsiClient {\n accounts: {\n id: {\n initialize: (config: {\n client_id: string;\n callback: (res: GsiCredentialResponse) => void;\n auto_select?: boolean;\n cancel_on_tap_outside?: boolean;\n }) => void;\n prompt: () => void;\n renderButton: (el: HTMLElement, options?: GsiButtonOptions) => void;\n disableAutoSelect: () => void;\n };\n };\n}\n\ndeclare global {\n interface Window {\n google?: GsiClient;\n }\n}\n\nexport interface GoogleUser {\n sub: string;\n email?: string;\n name?: string;\n picture?: string;\n}\n\nexport interface GoogleAuthProviderProps {\n children: ReactNode;\n /** OAuth 2.0 Web client ID (from Google Cloud Console → Credentials). */\n clientId: string;\n /**\n * Optional admin allowlist for *optimistic* client-side `isAdmin`. The server\n * `googleAuth` adapter still enforces the real gate. Omit to treat any\n * successful sign-in as optimistically admin (server will correct via 401).\n */\n adminEmails?: string[];\n /** Cookie name for the ID token. Default `adminToken`. */\n cookieName?: string;\n /** Called when a 401 `{ logout: true }` response is intercepted. */\n onLogout?: () => void;\n}\n\nexport interface GoogleAuthContextValue {\n user: GoogleUser | null;\n isAdmin: boolean;\n isEditing: boolean;\n toggleEdit: () => void;\n /** True once the GSI script has loaded and the client is initialized. */\n ready: boolean;\n /** Trigger Google One Tap / sign-in prompt. */\n promptSignIn: () => void;\n /** Render the official Google button into `el` (most reliable trigger). */\n renderButton: (el: HTMLElement, options?: GsiButtonOptions) => void;\n logout: () => void;\n}\n\nconst GoogleAuthContext = createContext<GoogleAuthContextValue | undefined>(\n undefined,\n);\n\nfunction setCookie(name: string, value: string) {\n document.cookie = `${name}=${encodeURIComponent(value)}; path=/; samesite=lax`;\n}\nfunction deleteCookie(name: string) {\n document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;\n}\nfunction readCookie(name: string): string | null {\n for (const part of document.cookie.split(\";\")) {\n const [k, ...v] = part.trim().split(\"=\");\n if (k === name) return decodeURIComponent(v.join(\"=\"));\n }\n return null;\n}\n\n/** Decode a JWT payload client-side (no verification — the server does that). */\nfunction decodeJwt(token: string): (GoogleUser & { exp?: number }) | null {\n try {\n const payload = token.split(\".\")[1];\n const json = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n const data = JSON.parse(json) as {\n sub: string;\n email?: string;\n name?: string;\n picture?: string;\n exp?: number;\n };\n return data;\n } catch {\n return null;\n }\n}\n\nexport function GoogleAuthProvider({\n children,\n clientId,\n adminEmails,\n cookieName = \"adminToken\",\n onLogout,\n}: GoogleAuthProviderProps) {\n const [user, setUser] = useState<GoogleUser | null>(null);\n const [isAdmin, setIsAdmin] = useState(false);\n const [isEditing, setIsEditing] = useState(false);\n const [ready, setReady] = useState(false);\n const allowed = useRef(\n (adminEmails ?? []).map((e) => e.trim().toLowerCase()),\n );\n\n const applyToken = useCallback(\n (token: string) => {\n const claims = decodeJwt(token);\n if (!claims) return;\n if (claims.exp && claims.exp * 1000 <= Date.now()) {\n deleteCookie(cookieName);\n return;\n }\n const email = claims.email?.toLowerCase();\n // Optimistic: if no allowlist supplied, trust the sign-in and let the\n // server correct a non-admin via the 401 interceptor below.\n const admin =\n allowed.current.length === 0\n ? true\n : !!email && allowed.current.includes(email);\n setCookie(cookieName, token);\n setUser({\n sub: claims.sub,\n email: claims.email,\n name: claims.name,\n picture: claims.picture,\n });\n setIsAdmin(admin);\n },\n [cookieName],\n );\n\n // Restore an existing session from the cookie on mount.\n useEffect(() => {\n const existing = readCookie(cookieName);\n if (existing) applyToken(existing);\n }, [cookieName, applyToken]);\n\n // Load the GSI script and initialize the One Tap / button client.\n useEffect(() => {\n function init() {\n if (!window.google) return;\n window.google.accounts.id.initialize({\n client_id: clientId,\n callback: (res) => applyToken(res.credential),\n auto_select: false,\n cancel_on_tap_outside: true,\n });\n setReady(true);\n }\n\n if (window.google) {\n init();\n return;\n }\n const existing = document.querySelector<HTMLScriptElement>(\n `script[src=\"${GSI_SRC}\"]`,\n );\n if (existing) {\n existing.addEventListener(\"load\", init);\n return () => existing.removeEventListener(\"load\", init);\n }\n const script = document.createElement(\"script\");\n script.src = GSI_SRC;\n script.async = true;\n script.defer = true;\n script.onload = init;\n document.head.appendChild(script);\n }, [clientId, applyToken]);\n\n // Intercept admin 401s so an expired/forbidden session forces sign-out.\n useEffect(() => {\n const originalFetch = window.fetch;\n window.fetch = async (...args: Parameters<typeof fetch>) => {\n const response = await originalFetch(...args);\n if (response.status === 401) {\n try {\n const data = await response.clone().json();\n if (data?.logout) doLogout();\n } catch {\n /* not a JSON body — ignore */\n }\n }\n return response;\n };\n return () => {\n window.fetch = originalFetch;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n function doLogout() {\n window.google?.accounts.id.disableAutoSelect();\n deleteCookie(cookieName);\n setUser(null);\n setIsAdmin(false);\n setIsEditing(false);\n onLogout?.();\n }\n\n const promptSignIn = useCallback(() => {\n if (!ready) return;\n window.google?.accounts.id.prompt();\n }, [ready]);\n\n const renderButton = useCallback(\n (el: HTMLElement, options?: GsiButtonOptions) => {\n if (!ready) return;\n window.google?.accounts.id.renderButton(el, options);\n },\n [ready],\n );\n\n const toggleEdit = useCallback(() => setIsEditing((p) => !p), []);\n\n return (\n <GoogleAuthContext.Provider\n value={{\n user,\n isAdmin,\n isEditing,\n toggleEdit,\n ready,\n promptSignIn,\n renderButton,\n logout: doLogout,\n }}\n >\n <CmsAuthProvider value={{ isAdmin, isEditing, toggleEdit }}>\n {children}\n </CmsAuthProvider>\n </GoogleAuthContext.Provider>\n );\n}\n\n/** Google auth API (user/login/logout) for login pages and toolbars. */\nexport function useGoogleAuth(): GoogleAuthContextValue {\n const ctx = useContext(GoogleAuthContext);\n if (!ctx) {\n throw new Error(\"useGoogleAuth must be used within a GoogleAuthProvider\");\n }\n return ctx;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/auth/google/client/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport {\n GoogleOAuthProvider,\n GoogleLogin,\n googleLogout,\n type CredentialResponse,\n} from \"@react-oauth/google\";\n// Imported via the public specifier so we share the SAME context instance as\n// the consumer's edit primitives at runtime (see tsup `external` + tsconfig paths).\nimport { CmsAuthProvider } from \"@dalgoridim/headless-cms/client\";\n\n/**\n * Client provider for **Google sign-in**, built on `@react-oauth/google` (the\n * official React wrapper for Google Identity Services). It handles the GSI\n * script + button rendering robustly — including remounts, so the button shows\n * reliably every time a sign-in dialog opens. The resulting ID token is stashed\n * in a cookie for the server `googleAuth` adapter to verify; admin status is\n * optimistic on the client (via `adminEmails`), with the server gate\n * authoritative.\n *\n * Render `<GoogleSignInButton />` anywhere inside the provider to show the\n * official Google button.\n */\n\nexport interface GoogleUser {\n sub: string;\n email?: string;\n name?: string;\n picture?: string;\n}\n\nexport interface GoogleAuthProviderProps {\n children: ReactNode;\n /** OAuth 2.0 Web client ID (from Google Cloud Console → Credentials). */\n clientId: string;\n /**\n * Optional admin allowlist for *optimistic* client-side `isAdmin`. The server\n * `googleAuth` adapter still enforces the real gate. Omit to treat any\n * successful sign-in as optimistically admin (server will correct via 401).\n */\n adminEmails?: string[];\n /** Cookie name for the ID token. Default `adminToken`. */\n cookieName?: string;\n /** Called when a 401 `{ logout: true }` response is intercepted. */\n onLogout?: () => void;\n}\n\nexport interface GoogleAuthContextValue {\n user: GoogleUser | null;\n isAdmin: boolean;\n isEditing: boolean;\n toggleEdit: () => void;\n logout: () => void;\n /** Feed a Google ID token credential (used by `GoogleSignInButton`). */\n applyCredential: (credential: string) => void;\n}\n\nconst GoogleAuthContext = createContext<GoogleAuthContextValue | undefined>(\n undefined,\n);\n\nfunction setCookie(name: string, value: string) {\n document.cookie = `${name}=${encodeURIComponent(value)}; path=/; samesite=lax`;\n}\nfunction deleteCookie(name: string) {\n document.cookie = `${name}=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT`;\n}\nfunction readCookie(name: string): string | null {\n for (const part of document.cookie.split(\";\")) {\n const [k, ...v] = part.trim().split(\"=\");\n if (k === name) return decodeURIComponent(v.join(\"=\"));\n }\n return null;\n}\n\n/** Decode a JWT payload client-side (no verification — the server does that). */\nfunction decodeJwt(token: string): (GoogleUser & { exp?: number }) | null {\n try {\n const payload = token.split(\".\")[1];\n const json = atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\"));\n return JSON.parse(json);\n } catch {\n return null;\n }\n}\n\nfunction AuthState({\n children,\n adminEmails,\n cookieName = \"adminToken\",\n onLogout,\n}: Omit<GoogleAuthProviderProps, \"clientId\">) {\n const [user, setUser] = useState<GoogleUser | null>(null);\n const [isAdmin, setIsAdmin] = useState(false);\n const [isEditing, setIsEditing] = useState(false);\n const allowed = useRef(\n (adminEmails ?? []).map((e) => e.trim().toLowerCase()),\n );\n\n const applyCredential = useCallback(\n (token: string) => {\n const claims = decodeJwt(token);\n if (!claims) return;\n if (claims.exp && claims.exp * 1000 <= Date.now()) {\n deleteCookie(cookieName);\n return;\n }\n const email = claims.email?.toLowerCase();\n // Optimistic: if no allowlist supplied, trust the sign-in and let the\n // server correct a non-admin via the 401 interceptor below.\n const admin =\n allowed.current.length === 0\n ? true\n : !!email && allowed.current.includes(email);\n setCookie(cookieName, token);\n setUser({\n sub: claims.sub,\n email: claims.email,\n name: claims.name,\n picture: claims.picture,\n });\n setIsAdmin(admin);\n },\n [cookieName],\n );\n\n // Restore an existing session from the cookie on mount.\n useEffect(() => {\n const existing = readCookie(cookieName);\n if (existing) applyCredential(existing);\n }, [cookieName, applyCredential]);\n\n const logout = useCallback(() => {\n googleLogout();\n deleteCookie(cookieName);\n setUser(null);\n setIsAdmin(false);\n setIsEditing(false);\n onLogout?.();\n }, [cookieName, onLogout]);\n\n // Intercept admin 401s so an expired/forbidden session forces sign-out.\n useEffect(() => {\n const originalFetch = window.fetch;\n window.fetch = async (...args: Parameters<typeof fetch>) => {\n const response = await originalFetch(...args);\n if (response.status === 401) {\n try {\n const data = await response.clone().json();\n if (data?.logout) logout();\n } catch {\n /* not a JSON body — ignore */\n }\n }\n return response;\n };\n return () => {\n window.fetch = originalFetch;\n };\n }, [logout]);\n\n const toggleEdit = useCallback(() => setIsEditing((p) => !p), []);\n\n return (\n <GoogleAuthContext.Provider\n value={{ user, isAdmin, isEditing, toggleEdit, logout, applyCredential }}\n >\n <CmsAuthProvider value={{ isAdmin, isEditing, toggleEdit }}>\n {children}\n </CmsAuthProvider>\n </GoogleAuthContext.Provider>\n );\n}\n\nexport function GoogleAuthProvider({\n children,\n clientId,\n ...rest\n}: GoogleAuthProviderProps) {\n return (\n <GoogleOAuthProvider clientId={clientId}>\n <AuthState {...rest}>{children}</AuthState>\n </GoogleOAuthProvider>\n );\n}\n\nexport interface GoogleSignInButtonProps {\n /** Forwarded to the underlying `<GoogleLogin>` (theme, shape, size, text…). */\n theme?: \"outline\" | \"filled_blue\" | \"filled_black\";\n shape?: \"rectangular\" | \"pill\" | \"circle\" | \"square\";\n size?: \"small\" | \"medium\" | \"large\";\n text?: \"signin_with\" | \"signup_with\" | \"continue_with\" | \"signin\";\n onError?: () => void;\n}\n\n/** The official Google sign-in button. Must be rendered inside a provider. */\nexport function GoogleSignInButton({\n theme = \"filled_black\",\n shape = \"pill\",\n size = \"large\",\n text = \"continue_with\",\n onError,\n}: GoogleSignInButtonProps) {\n const { applyCredential } = useGoogleAuth();\n return (\n <GoogleLogin\n onSuccess={(res: CredentialResponse) => {\n if (res.credential) applyCredential(res.credential);\n }}\n onError={onError}\n theme={theme}\n shape={shape}\n size={size}\n text={text}\n />\n );\n}\n\n/** Google auth API (user/logout) for login pages and toolbars. */\nexport function useGoogleAuth(): GoogleAuthContextValue {\n const ctx = useContext(GoogleAuthContext);\n if (!ctx) {\n throw new Error(\"useGoogleAuth must be used within a GoogleAuthProvider\");\n }\n return ctx;\n}\n"],"mappings":"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","names":[]}
@@ -1,5 +1,5 @@
1
- import * as React$1 from 'react';
2
- import React__default, { ReactNode } from 'react';
1
+ import * as react from 'react';
2
+ import react__default, { ReactNode } from 'react';
3
3
  import { NestedSections, ClientStorageAdapter, PendingImage, Section, CmsAuthState } from '../index.cjs';
4
4
  export { AuthAdapter, AuthIdentity, DataAdapter, Editable, EntityAddress, Query, QueryCondition, QueryFilter, QueryFilterGroup, QueryFilterOp, Ref, RelationConfig, SectionMap, ServerStorageAdapter, StorageAdapter } from '../index.cjs';
5
5
 
@@ -37,7 +37,7 @@ interface PageProviderProps {
37
37
  /** Notification sink. Defaults to `sonner` toasts. */
38
38
  notify?: Notifier;
39
39
  }
40
- declare const PageProvider: ({ children, initialSections, apiBasePath, storage, notify, }: PageProviderProps) => React$1.JSX.Element;
40
+ declare const PageProvider: ({ children, initialSections, apiBasePath, storage, notify, }: PageProviderProps) => react.JSX.Element;
41
41
  declare const usePageContext: () => PageContextType;
42
42
 
43
43
  /**
@@ -49,7 +49,7 @@ declare const usePageContext: () => PageContextType;
49
49
  * this context through the package's public `…/client` specifier so they share
50
50
  * the *same* context instance as the consumer's primitives at runtime.
51
51
  */
52
- declare const CmsAuthContext: React$1.Context<CmsAuthState | undefined>;
52
+ declare const CmsAuthContext: react.Context<CmsAuthState | undefined>;
53
53
  declare function useCmsAuth(): CmsAuthState;
54
54
  /**
55
55
  * Controlled provider for consumers wiring their own auth. Pass the resolved
@@ -58,7 +58,7 @@ declare function useCmsAuth(): CmsAuthState;
58
58
  declare function CmsAuthProvider({ value, children, }: {
59
59
  value: CmsAuthState;
60
60
  children: ReactNode;
61
- }): React$1.JSX.Element;
61
+ }): react.JSX.Element;
62
62
 
63
63
  /**
64
64
  * Inline-editable text primitive. Headless: it wires `contentEditable`, reads
@@ -75,13 +75,13 @@ interface ContentEditSpanProps {
75
75
  sectionKey: string;
76
76
  fieldKey: string;
77
77
  className?: string;
78
- children?: React__default.ReactNode;
78
+ children?: react__default.ReactNode;
79
79
  /** Element/tag to render. Defaults to `span`. */
80
- as?: React__default.ElementType;
80
+ as?: react__default.ElementType;
81
81
  /** Render the stored raw string into nodes. Defaults to plain text. */
82
- renderValue?: (raw: string) => React__default.ReactNode;
82
+ renderValue?: (raw: string) => react__default.ReactNode;
83
83
  }
84
- declare function ContentEditSpan({ collection, sectionKey, fieldKey, className, children, as, renderValue, }: ContentEditSpanProps): React__default.JSX.Element;
84
+ declare function ContentEditSpan({ collection, sectionKey, fieldKey, className, children, as, renderValue, }: ContentEditSpanProps): react__default.JSX.Element;
85
85
 
86
86
  /** State + actions handed to the {@link EditableImage} render-prop. */
87
87
  interface EditableImageRenderState {
@@ -116,9 +116,9 @@ interface EditableImageProps {
116
116
  * package ships no visual look — bring your own. When omitted, a bare
117
117
  * unstyled `<img>` is rendered.
118
118
  */
119
- children?: (state: EditableImageRenderState) => React__default.ReactNode;
119
+ children?: (state: EditableImageRenderState) => react__default.ReactNode;
120
120
  }
121
- declare function EditableImage({ sectionKey, fieldKey, src, collection, docId, className, children, }: EditableImageProps): React__default.JSX.Element;
121
+ declare function EditableImage({ sectionKey, fieldKey, src, collection, docId, className, children, }: EditableImageProps): react__default.JSX.Element;
122
122
 
123
123
  interface UseMarkdownEditorOptions {
124
124
  initialValue: string;
@@ -1,5 +1,5 @@
1
- import * as React$1 from 'react';
2
- import React__default, { ReactNode } from 'react';
1
+ import * as react from 'react';
2
+ import react__default, { ReactNode } from 'react';
3
3
  import { NestedSections, ClientStorageAdapter, PendingImage, Section, CmsAuthState } from '../index.js';
4
4
  export { AuthAdapter, AuthIdentity, DataAdapter, Editable, EntityAddress, Query, QueryCondition, QueryFilter, QueryFilterGroup, QueryFilterOp, Ref, RelationConfig, SectionMap, ServerStorageAdapter, StorageAdapter } from '../index.js';
5
5
 
@@ -37,7 +37,7 @@ interface PageProviderProps {
37
37
  /** Notification sink. Defaults to `sonner` toasts. */
38
38
  notify?: Notifier;
39
39
  }
40
- declare const PageProvider: ({ children, initialSections, apiBasePath, storage, notify, }: PageProviderProps) => React$1.JSX.Element;
40
+ declare const PageProvider: ({ children, initialSections, apiBasePath, storage, notify, }: PageProviderProps) => react.JSX.Element;
41
41
  declare const usePageContext: () => PageContextType;
42
42
 
43
43
  /**
@@ -49,7 +49,7 @@ declare const usePageContext: () => PageContextType;
49
49
  * this context through the package's public `…/client` specifier so they share
50
50
  * the *same* context instance as the consumer's primitives at runtime.
51
51
  */
52
- declare const CmsAuthContext: React$1.Context<CmsAuthState | undefined>;
52
+ declare const CmsAuthContext: react.Context<CmsAuthState | undefined>;
53
53
  declare function useCmsAuth(): CmsAuthState;
54
54
  /**
55
55
  * Controlled provider for consumers wiring their own auth. Pass the resolved
@@ -58,7 +58,7 @@ declare function useCmsAuth(): CmsAuthState;
58
58
  declare function CmsAuthProvider({ value, children, }: {
59
59
  value: CmsAuthState;
60
60
  children: ReactNode;
61
- }): React$1.JSX.Element;
61
+ }): react.JSX.Element;
62
62
 
63
63
  /**
64
64
  * Inline-editable text primitive. Headless: it wires `contentEditable`, reads
@@ -75,13 +75,13 @@ interface ContentEditSpanProps {
75
75
  sectionKey: string;
76
76
  fieldKey: string;
77
77
  className?: string;
78
- children?: React__default.ReactNode;
78
+ children?: react__default.ReactNode;
79
79
  /** Element/tag to render. Defaults to `span`. */
80
- as?: React__default.ElementType;
80
+ as?: react__default.ElementType;
81
81
  /** Render the stored raw string into nodes. Defaults to plain text. */
82
- renderValue?: (raw: string) => React__default.ReactNode;
82
+ renderValue?: (raw: string) => react__default.ReactNode;
83
83
  }
84
- declare function ContentEditSpan({ collection, sectionKey, fieldKey, className, children, as, renderValue, }: ContentEditSpanProps): React__default.JSX.Element;
84
+ declare function ContentEditSpan({ collection, sectionKey, fieldKey, className, children, as, renderValue, }: ContentEditSpanProps): react__default.JSX.Element;
85
85
 
86
86
  /** State + actions handed to the {@link EditableImage} render-prop. */
87
87
  interface EditableImageRenderState {
@@ -116,9 +116,9 @@ interface EditableImageProps {
116
116
  * package ships no visual look — bring your own. When omitted, a bare
117
117
  * unstyled `<img>` is rendered.
118
118
  */
119
- children?: (state: EditableImageRenderState) => React__default.ReactNode;
119
+ children?: (state: EditableImageRenderState) => react__default.ReactNode;
120
120
  }
121
- declare function EditableImage({ sectionKey, fieldKey, src, collection, docId, className, children, }: EditableImageProps): React__default.JSX.Element;
121
+ declare function EditableImage({ sectionKey, fieldKey, src, collection, docId, className, children, }: EditableImageProps): react__default.JSX.Element;
122
122
 
123
123
  interface UseMarkdownEditorOptions {
124
124
  initialValue: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dalgoridim/headless-cms",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Database-agnostic, inline-edit headless CMS engine for React / Next.js apps",
5
5
  "license": "UNLICENSED",
6
6
  "author": "dalgoridim",
@@ -119,6 +119,7 @@
119
119
  "peerDependencies": {
120
120
  "@aws-sdk/client-s3": ">=3",
121
121
  "@aws-sdk/s3-request-presigner": ">=3",
122
+ "@react-oauth/google": ">=0.12",
122
123
  "cloudinary": ">=2",
123
124
  "firebase": ">=10",
124
125
  "firebase-admin": ">=12",
@@ -131,6 +132,9 @@
131
132
  "@aws-sdk/client-s3": {
132
133
  "optional": true
133
134
  },
135
+ "@react-oauth/google": {
136
+ "optional": true
137
+ },
134
138
  "@aws-sdk/s3-request-presigner": {
135
139
  "optional": true
136
140
  },
@@ -150,10 +154,10 @@
150
154
  "optional": true
151
155
  }
152
156
  },
153
- "dependencies": {},
154
157
  "devDependencies": {
155
158
  "@aws-sdk/client-s3": "^3.700.0",
156
159
  "@aws-sdk/s3-request-presigner": "^3.700.0",
160
+ "@react-oauth/google": "^0.13.5",
157
161
  "@types/node": "^20.0.0",
158
162
  "@types/pg": "^8.11.10",
159
163
  "@types/react": "^19.0.0",