bright-auth 0.1.0 → 0.2.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.
package/dist/bright-auth.cjs.js
CHANGED
|
@@ -20,7 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
var src_exports = {};
|
|
21
21
|
__export(src_exports, {
|
|
22
22
|
BrightAuth: () => BrightAuth,
|
|
23
|
-
default: () => src_default
|
|
23
|
+
default: () => src_default,
|
|
24
|
+
registerSignInButton: () => registerSignInButton
|
|
24
25
|
});
|
|
25
26
|
module.exports = __toCommonJS(src_exports);
|
|
26
27
|
var DEFAULT_API_BASE = "https://api.thebrightapp.xyz/v1";
|
|
@@ -124,4 +125,49 @@ var BrightAuth = class {
|
|
|
124
125
|
}
|
|
125
126
|
};
|
|
126
127
|
var src_default = BrightAuth;
|
|
128
|
+
function registerSignInButton() {
|
|
129
|
+
if (customElements.get("bright-signin")) return;
|
|
130
|
+
customElements.define("bright-signin", class BrightSignIn extends HTMLElement {
|
|
131
|
+
connectedCallback() {
|
|
132
|
+
const clientId = this.getAttribute("client-id") || "";
|
|
133
|
+
const clientSecret = this.getAttribute("client-secret") || "";
|
|
134
|
+
const redirectUri = this.getAttribute("redirect-uri") || "";
|
|
135
|
+
const scopes = (this.getAttribute("scopes") || "health").split(",").map((s) => s.trim());
|
|
136
|
+
const auth = new BrightAuth({ clientId, clientSecret, redirectUri });
|
|
137
|
+
const shadow = this.attachShadow({ mode: "closed" });
|
|
138
|
+
const style = document.createElement("style");
|
|
139
|
+
style.textContent = `
|
|
140
|
+
button {
|
|
141
|
+
display: inline-flex;
|
|
142
|
+
align-items: center;
|
|
143
|
+
gap: 10px;
|
|
144
|
+
background: #10b981;
|
|
145
|
+
color: #fff;
|
|
146
|
+
border: none;
|
|
147
|
+
border-radius: 10px;
|
|
148
|
+
padding: 12px 20px;
|
|
149
|
+
font-size: 14px;
|
|
150
|
+
font-weight: 600;
|
|
151
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
152
|
+
cursor: pointer;
|
|
153
|
+
transition: opacity 0.15s;
|
|
154
|
+
white-space: nowrap;
|
|
155
|
+
}
|
|
156
|
+
button:hover { opacity: 0.85; }
|
|
157
|
+
button:active { opacity: 0.7; transform: scale(0.98); }
|
|
158
|
+
svg { flex-shrink: 0; }
|
|
159
|
+
`;
|
|
160
|
+
const btn = document.createElement("button");
|
|
161
|
+
btn.innerHTML = `
|
|
162
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
163
|
+
<circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/>
|
|
164
|
+
</svg>
|
|
165
|
+
Sign in with Bright
|
|
166
|
+
`;
|
|
167
|
+
btn.addEventListener("click", () => auth.signIn(scopes));
|
|
168
|
+
shadow.appendChild(style);
|
|
169
|
+
shadow.appendChild(btn);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
}
|
|
127
173
|
//# sourceMappingURL=bright-auth.cjs.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.js"],
|
|
4
|
-
"sourcesContent": ["const DEFAULT_API_BASE = \"https://api.thebrightapp.xyz/v1\"\nconst DEFAULT_AUTH_BASE = \"https://developers.thebrightapp.xyz\"\nconst TOKEN_KEY = \"bright_access_token\"\n\nexport class BrightAuth {\n /**\n * @param {{ clientId: string, clientSecret: string, redirectUri: string, apiBase?: string, authBase?: string }} config\n */\n constructor({ clientId, clientSecret, redirectUri, apiBase, authBase } = {}) {\n if (!clientId) throw new Error(\"BrightAuth: clientId is required\")\n if (!clientSecret) throw new Error(\"BrightAuth: clientSecret is required\")\n if (!redirectUri) throw new Error(\"BrightAuth: redirectUri is required\")\n\n this._clientId = clientId\n this._clientSecret = clientSecret\n this._redirectUri = redirectUri\n this._apiBase = (apiBase || DEFAULT_API_BASE).replace(/\\/$/, \"\")\n this._authBase = (authBase || DEFAULT_AUTH_BASE).replace(/\\/$/, \"\")\n }\n\n /** Redirect the user to the Bright consent page. */\n signIn(scopes = [\"health\"]) {\n const url = new URL(`${this._authBase}/oauth/authorize`)\n url.searchParams.set(\"client_id\", this._clientId)\n url.searchParams.set(\"redirect_uri\", this._redirectUri)\n scopes.forEach(s => url.searchParams.append(\"scope\", s))\n window.location.href = url.toString()\n }\n\n /**\n * Call on every page load. If the URL contains ?code=, exchanges it for an\n * access token, stores it, and cleans the URL.\n * @returns {Promise<string|null>} The access token, or null if no code in URL.\n */\n async handleCallback() {\n const params = new URLSearchParams(window.location.search)\n const code = params.get(\"code\")\n const error = params.get(\"error\")\n\n if (error) throw new Error(`Bright OAuth error: ${error}`)\n if (!code) return null\n\n window.history.replaceState({}, \"\", window.location.pathname)\n\n const res = await fetch(`${this._apiBase}/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n client_id: this._clientId,\n client_secret: this._clientSecret,\n code,\n }),\n })\n const data = await res.json()\n if (!res.ok) throw new Error(data.detail || \"Token exchange failed\")\n\n localStorage.setItem(TOKEN_KEY, data.access_token)\n return data.access_token\n }\n\n /** Returns the stored access token, or null if not signed in. */\n getToken() {\n return localStorage.getItem(TOKEN_KEY)\n }\n\n /** Remove the stored token (signs the user out locally). */\n signOut() {\n localStorage.removeItem(TOKEN_KEY)\n }\n\n /** Returns true if an access token is stored. */\n isSignedIn() {\n return !!this.getToken()\n }\n\n /**\n * Fetch the signed-in user's identity from Bright.\n * @returns {Promise<{ uid: string, email?: string, name?: string, photo_url?: string }>}\n */\n async getUser() {\n return this._call(\"/oauth/me\")\n }\n\n /**\n * Make an authenticated request to any Bright API endpoint.\n * @param {string} path e.g. \"/sleep_track/home\"\n * @param {object} [init] fetch options (method, body, etc.)\n * @param {object} [query] query params object\n */\n async fetch(path, init = {}, query = {}) {\n const token = this.getToken()\n if (!token) throw new Error(\"BrightAuth: not signed in\")\n\n const url = new URL(`${this._apiBase}${path}`)\n Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v))\n\n const res = await globalThis.fetch(url.toString(), {\n ...init,\n headers: {\n ...(init.headers || {}),\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n })\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}))\n throw new Error(err.detail || `Bright API error ${res.status}`)\n }\n return res.json()\n }\n\n /** @private */\n async _call(path, query = {}) {\n return this.fetch(path, {}, query)\n }\n}\n\nexport default BrightAuth\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["const DEFAULT_API_BASE = \"https://api.thebrightapp.xyz/v1\"\nconst DEFAULT_AUTH_BASE = \"https://developers.thebrightapp.xyz\"\nconst TOKEN_KEY = \"bright_access_token\"\n\nexport class BrightAuth {\n /**\n * @param {{ clientId: string, clientSecret: string, redirectUri: string, apiBase?: string, authBase?: string }} config\n */\n constructor({ clientId, clientSecret, redirectUri, apiBase, authBase } = {}) {\n if (!clientId) throw new Error(\"BrightAuth: clientId is required\")\n if (!clientSecret) throw new Error(\"BrightAuth: clientSecret is required\")\n if (!redirectUri) throw new Error(\"BrightAuth: redirectUri is required\")\n\n this._clientId = clientId\n this._clientSecret = clientSecret\n this._redirectUri = redirectUri\n this._apiBase = (apiBase || DEFAULT_API_BASE).replace(/\\/$/, \"\")\n this._authBase = (authBase || DEFAULT_AUTH_BASE).replace(/\\/$/, \"\")\n }\n\n /** Redirect the user to the Bright consent page. */\n signIn(scopes = [\"health\"]) {\n const url = new URL(`${this._authBase}/oauth/authorize`)\n url.searchParams.set(\"client_id\", this._clientId)\n url.searchParams.set(\"redirect_uri\", this._redirectUri)\n scopes.forEach(s => url.searchParams.append(\"scope\", s))\n window.location.href = url.toString()\n }\n\n /**\n * Call on every page load. If the URL contains ?code=, exchanges it for an\n * access token, stores it, and cleans the URL.\n * @returns {Promise<string|null>} The access token, or null if no code in URL.\n */\n async handleCallback() {\n const params = new URLSearchParams(window.location.search)\n const code = params.get(\"code\")\n const error = params.get(\"error\")\n\n if (error) throw new Error(`Bright OAuth error: ${error}`)\n if (!code) return null\n\n window.history.replaceState({}, \"\", window.location.pathname)\n\n const res = await fetch(`${this._apiBase}/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n client_id: this._clientId,\n client_secret: this._clientSecret,\n code,\n }),\n })\n const data = await res.json()\n if (!res.ok) throw new Error(data.detail || \"Token exchange failed\")\n\n localStorage.setItem(TOKEN_KEY, data.access_token)\n return data.access_token\n }\n\n /** Returns the stored access token, or null if not signed in. */\n getToken() {\n return localStorage.getItem(TOKEN_KEY)\n }\n\n /** Remove the stored token (signs the user out locally). */\n signOut() {\n localStorage.removeItem(TOKEN_KEY)\n }\n\n /** Returns true if an access token is stored. */\n isSignedIn() {\n return !!this.getToken()\n }\n\n /**\n * Fetch the signed-in user's identity from Bright.\n * @returns {Promise<{ uid: string, email?: string, name?: string, photo_url?: string }>}\n */\n async getUser() {\n return this._call(\"/oauth/me\")\n }\n\n /**\n * Make an authenticated request to any Bright API endpoint.\n * @param {string} path e.g. \"/sleep_track/home\"\n * @param {object} [init] fetch options (method, body, etc.)\n * @param {object} [query] query params object\n */\n async fetch(path, init = {}, query = {}) {\n const token = this.getToken()\n if (!token) throw new Error(\"BrightAuth: not signed in\")\n\n const url = new URL(`${this._apiBase}${path}`)\n Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v))\n\n const res = await globalThis.fetch(url.toString(), {\n ...init,\n headers: {\n ...(init.headers || {}),\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n })\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}))\n throw new Error(err.detail || `Bright API error ${res.status}`)\n }\n return res.json()\n }\n\n /** @private */\n async _call(path, query = {}) {\n return this.fetch(path, {}, query)\n }\n}\n\nexport default BrightAuth\n\n/**\n * Registers the <bright-signin> custom element.\n * Styling is locked inside Shadow DOM \u2014 cannot be overridden by the host page.\n *\n * Usage:\n * <bright-signin client-id=\"proj_xxx\" client-secret=\"sk_xxx\" redirect-uri=\"https://yourapp.com\"></bright-signin>\n *\n * Or via JS:\n * registerSignInButton()\n * // then use <bright-signin> in your HTML\n */\nexport function registerSignInButton() {\n if (customElements.get(\"bright-signin\")) return\n\n customElements.define(\"bright-signin\", class BrightSignIn extends HTMLElement {\n connectedCallback() {\n const clientId = this.getAttribute(\"client-id\") || \"\"\n const clientSecret = this.getAttribute(\"client-secret\") || \"\"\n const redirectUri = this.getAttribute(\"redirect-uri\") || \"\"\n const scopes = (this.getAttribute(\"scopes\") || \"health\").split(\",\").map(s => s.trim())\n\n const auth = new BrightAuth({ clientId, clientSecret, redirectUri })\n\n const shadow = this.attachShadow({ mode: \"closed\" })\n\n const style = document.createElement(\"style\")\n style.textContent = `\n button {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n background: #10b981;\n color: #fff;\n border: none;\n border-radius: 10px;\n padding: 12px 20px;\n font-size: 14px;\n font-weight: 600;\n font-family: system-ui, -apple-system, sans-serif;\n cursor: pointer;\n transition: opacity 0.15s;\n white-space: nowrap;\n }\n button:hover { opacity: 0.85; }\n button:active { opacity: 0.7; transform: scale(0.98); }\n svg { flex-shrink: 0; }\n `\n\n const btn = document.createElement(\"button\")\n btn.innerHTML = `\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\">\n <circle cx=\"12\" cy=\"8\" r=\"4\"/><path d=\"M4 20c0-4 3.6-7 8-7s8 3 8 7\"/>\n </svg>\n Sign in with Bright\n `\n btn.addEventListener(\"click\", () => auth.signIn(scopes))\n\n shadow.appendChild(style)\n shadow.appendChild(btn)\n }\n })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/bright-auth.esm.js
CHANGED
|
@@ -100,8 +100,54 @@ var BrightAuth = class {
|
|
|
100
100
|
}
|
|
101
101
|
};
|
|
102
102
|
var src_default = BrightAuth;
|
|
103
|
+
function registerSignInButton() {
|
|
104
|
+
if (customElements.get("bright-signin")) return;
|
|
105
|
+
customElements.define("bright-signin", class BrightSignIn extends HTMLElement {
|
|
106
|
+
connectedCallback() {
|
|
107
|
+
const clientId = this.getAttribute("client-id") || "";
|
|
108
|
+
const clientSecret = this.getAttribute("client-secret") || "";
|
|
109
|
+
const redirectUri = this.getAttribute("redirect-uri") || "";
|
|
110
|
+
const scopes = (this.getAttribute("scopes") || "health").split(",").map((s) => s.trim());
|
|
111
|
+
const auth = new BrightAuth({ clientId, clientSecret, redirectUri });
|
|
112
|
+
const shadow = this.attachShadow({ mode: "closed" });
|
|
113
|
+
const style = document.createElement("style");
|
|
114
|
+
style.textContent = `
|
|
115
|
+
button {
|
|
116
|
+
display: inline-flex;
|
|
117
|
+
align-items: center;
|
|
118
|
+
gap: 10px;
|
|
119
|
+
background: #10b981;
|
|
120
|
+
color: #fff;
|
|
121
|
+
border: none;
|
|
122
|
+
border-radius: 10px;
|
|
123
|
+
padding: 12px 20px;
|
|
124
|
+
font-size: 14px;
|
|
125
|
+
font-weight: 600;
|
|
126
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
127
|
+
cursor: pointer;
|
|
128
|
+
transition: opacity 0.15s;
|
|
129
|
+
white-space: nowrap;
|
|
130
|
+
}
|
|
131
|
+
button:hover { opacity: 0.85; }
|
|
132
|
+
button:active { opacity: 0.7; transform: scale(0.98); }
|
|
133
|
+
svg { flex-shrink: 0; }
|
|
134
|
+
`;
|
|
135
|
+
const btn = document.createElement("button");
|
|
136
|
+
btn.innerHTML = `
|
|
137
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
|
|
138
|
+
<circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/>
|
|
139
|
+
</svg>
|
|
140
|
+
Sign in with Bright
|
|
141
|
+
`;
|
|
142
|
+
btn.addEventListener("click", () => auth.signIn(scopes));
|
|
143
|
+
shadow.appendChild(style);
|
|
144
|
+
shadow.appendChild(btn);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
103
148
|
export {
|
|
104
149
|
BrightAuth,
|
|
105
|
-
src_default as default
|
|
150
|
+
src_default as default,
|
|
151
|
+
registerSignInButton
|
|
106
152
|
};
|
|
107
153
|
//# sourceMappingURL=bright-auth.esm.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.js"],
|
|
4
|
-
"sourcesContent": ["const DEFAULT_API_BASE = \"https://api.thebrightapp.xyz/v1\"\nconst DEFAULT_AUTH_BASE = \"https://developers.thebrightapp.xyz\"\nconst TOKEN_KEY = \"bright_access_token\"\n\nexport class BrightAuth {\n /**\n * @param {{ clientId: string, clientSecret: string, redirectUri: string, apiBase?: string, authBase?: string }} config\n */\n constructor({ clientId, clientSecret, redirectUri, apiBase, authBase } = {}) {\n if (!clientId) throw new Error(\"BrightAuth: clientId is required\")\n if (!clientSecret) throw new Error(\"BrightAuth: clientSecret is required\")\n if (!redirectUri) throw new Error(\"BrightAuth: redirectUri is required\")\n\n this._clientId = clientId\n this._clientSecret = clientSecret\n this._redirectUri = redirectUri\n this._apiBase = (apiBase || DEFAULT_API_BASE).replace(/\\/$/, \"\")\n this._authBase = (authBase || DEFAULT_AUTH_BASE).replace(/\\/$/, \"\")\n }\n\n /** Redirect the user to the Bright consent page. */\n signIn(scopes = [\"health\"]) {\n const url = new URL(`${this._authBase}/oauth/authorize`)\n url.searchParams.set(\"client_id\", this._clientId)\n url.searchParams.set(\"redirect_uri\", this._redirectUri)\n scopes.forEach(s => url.searchParams.append(\"scope\", s))\n window.location.href = url.toString()\n }\n\n /**\n * Call on every page load. If the URL contains ?code=, exchanges it for an\n * access token, stores it, and cleans the URL.\n * @returns {Promise<string|null>} The access token, or null if no code in URL.\n */\n async handleCallback() {\n const params = new URLSearchParams(window.location.search)\n const code = params.get(\"code\")\n const error = params.get(\"error\")\n\n if (error) throw new Error(`Bright OAuth error: ${error}`)\n if (!code) return null\n\n window.history.replaceState({}, \"\", window.location.pathname)\n\n const res = await fetch(`${this._apiBase}/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n client_id: this._clientId,\n client_secret: this._clientSecret,\n code,\n }),\n })\n const data = await res.json()\n if (!res.ok) throw new Error(data.detail || \"Token exchange failed\")\n\n localStorage.setItem(TOKEN_KEY, data.access_token)\n return data.access_token\n }\n\n /** Returns the stored access token, or null if not signed in. */\n getToken() {\n return localStorage.getItem(TOKEN_KEY)\n }\n\n /** Remove the stored token (signs the user out locally). */\n signOut() {\n localStorage.removeItem(TOKEN_KEY)\n }\n\n /** Returns true if an access token is stored. */\n isSignedIn() {\n return !!this.getToken()\n }\n\n /**\n * Fetch the signed-in user's identity from Bright.\n * @returns {Promise<{ uid: string, email?: string, name?: string, photo_url?: string }>}\n */\n async getUser() {\n return this._call(\"/oauth/me\")\n }\n\n /**\n * Make an authenticated request to any Bright API endpoint.\n * @param {string} path e.g. \"/sleep_track/home\"\n * @param {object} [init] fetch options (method, body, etc.)\n * @param {object} [query] query params object\n */\n async fetch(path, init = {}, query = {}) {\n const token = this.getToken()\n if (!token) throw new Error(\"BrightAuth: not signed in\")\n\n const url = new URL(`${this._apiBase}${path}`)\n Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v))\n\n const res = await globalThis.fetch(url.toString(), {\n ...init,\n headers: {\n ...(init.headers || {}),\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n })\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}))\n throw new Error(err.detail || `Bright API error ${res.status}`)\n }\n return res.json()\n }\n\n /** @private */\n async _call(path, query = {}) {\n return this.fetch(path, {}, query)\n }\n}\n\nexport default BrightAuth\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["const DEFAULT_API_BASE = \"https://api.thebrightapp.xyz/v1\"\nconst DEFAULT_AUTH_BASE = \"https://developers.thebrightapp.xyz\"\nconst TOKEN_KEY = \"bright_access_token\"\n\nexport class BrightAuth {\n /**\n * @param {{ clientId: string, clientSecret: string, redirectUri: string, apiBase?: string, authBase?: string }} config\n */\n constructor({ clientId, clientSecret, redirectUri, apiBase, authBase } = {}) {\n if (!clientId) throw new Error(\"BrightAuth: clientId is required\")\n if (!clientSecret) throw new Error(\"BrightAuth: clientSecret is required\")\n if (!redirectUri) throw new Error(\"BrightAuth: redirectUri is required\")\n\n this._clientId = clientId\n this._clientSecret = clientSecret\n this._redirectUri = redirectUri\n this._apiBase = (apiBase || DEFAULT_API_BASE).replace(/\\/$/, \"\")\n this._authBase = (authBase || DEFAULT_AUTH_BASE).replace(/\\/$/, \"\")\n }\n\n /** Redirect the user to the Bright consent page. */\n signIn(scopes = [\"health\"]) {\n const url = new URL(`${this._authBase}/oauth/authorize`)\n url.searchParams.set(\"client_id\", this._clientId)\n url.searchParams.set(\"redirect_uri\", this._redirectUri)\n scopes.forEach(s => url.searchParams.append(\"scope\", s))\n window.location.href = url.toString()\n }\n\n /**\n * Call on every page load. If the URL contains ?code=, exchanges it for an\n * access token, stores it, and cleans the URL.\n * @returns {Promise<string|null>} The access token, or null if no code in URL.\n */\n async handleCallback() {\n const params = new URLSearchParams(window.location.search)\n const code = params.get(\"code\")\n const error = params.get(\"error\")\n\n if (error) throw new Error(`Bright OAuth error: ${error}`)\n if (!code) return null\n\n window.history.replaceState({}, \"\", window.location.pathname)\n\n const res = await fetch(`${this._apiBase}/oauth/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n client_id: this._clientId,\n client_secret: this._clientSecret,\n code,\n }),\n })\n const data = await res.json()\n if (!res.ok) throw new Error(data.detail || \"Token exchange failed\")\n\n localStorage.setItem(TOKEN_KEY, data.access_token)\n return data.access_token\n }\n\n /** Returns the stored access token, or null if not signed in. */\n getToken() {\n return localStorage.getItem(TOKEN_KEY)\n }\n\n /** Remove the stored token (signs the user out locally). */\n signOut() {\n localStorage.removeItem(TOKEN_KEY)\n }\n\n /** Returns true if an access token is stored. */\n isSignedIn() {\n return !!this.getToken()\n }\n\n /**\n * Fetch the signed-in user's identity from Bright.\n * @returns {Promise<{ uid: string, email?: string, name?: string, photo_url?: string }>}\n */\n async getUser() {\n return this._call(\"/oauth/me\")\n }\n\n /**\n * Make an authenticated request to any Bright API endpoint.\n * @param {string} path e.g. \"/sleep_track/home\"\n * @param {object} [init] fetch options (method, body, etc.)\n * @param {object} [query] query params object\n */\n async fetch(path, init = {}, query = {}) {\n const token = this.getToken()\n if (!token) throw new Error(\"BrightAuth: not signed in\")\n\n const url = new URL(`${this._apiBase}${path}`)\n Object.entries(query).forEach(([k, v]) => url.searchParams.set(k, v))\n\n const res = await globalThis.fetch(url.toString(), {\n ...init,\n headers: {\n ...(init.headers || {}),\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n },\n })\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}))\n throw new Error(err.detail || `Bright API error ${res.status}`)\n }\n return res.json()\n }\n\n /** @private */\n async _call(path, query = {}) {\n return this.fetch(path, {}, query)\n }\n}\n\nexport default BrightAuth\n\n/**\n * Registers the <bright-signin> custom element.\n * Styling is locked inside Shadow DOM \u2014 cannot be overridden by the host page.\n *\n * Usage:\n * <bright-signin client-id=\"proj_xxx\" client-secret=\"sk_xxx\" redirect-uri=\"https://yourapp.com\"></bright-signin>\n *\n * Or via JS:\n * registerSignInButton()\n * // then use <bright-signin> in your HTML\n */\nexport function registerSignInButton() {\n if (customElements.get(\"bright-signin\")) return\n\n customElements.define(\"bright-signin\", class BrightSignIn extends HTMLElement {\n connectedCallback() {\n const clientId = this.getAttribute(\"client-id\") || \"\"\n const clientSecret = this.getAttribute(\"client-secret\") || \"\"\n const redirectUri = this.getAttribute(\"redirect-uri\") || \"\"\n const scopes = (this.getAttribute(\"scopes\") || \"health\").split(\",\").map(s => s.trim())\n\n const auth = new BrightAuth({ clientId, clientSecret, redirectUri })\n\n const shadow = this.attachShadow({ mode: \"closed\" })\n\n const style = document.createElement(\"style\")\n style.textContent = `\n button {\n display: inline-flex;\n align-items: center;\n gap: 10px;\n background: #10b981;\n color: #fff;\n border: none;\n border-radius: 10px;\n padding: 12px 20px;\n font-size: 14px;\n font-weight: 600;\n font-family: system-ui, -apple-system, sans-serif;\n cursor: pointer;\n transition: opacity 0.15s;\n white-space: nowrap;\n }\n button:hover { opacity: 0.85; }\n button:active { opacity: 0.7; transform: scale(0.98); }\n svg { flex-shrink: 0; }\n `\n\n const btn = document.createElement(\"button\")\n btn.innerHTML = `\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\">\n <circle cx=\"12\" cy=\"8\" r=\"4\"/><path d=\"M4 20c0-4 3.6-7 8-7s8 3 8 7\"/>\n </svg>\n Sign in with Bright\n `\n btn.addEventListener(\"click\", () => auth.signIn(scopes))\n\n shadow.appendChild(style)\n shadow.appendChild(btn)\n }\n })\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|