@atlasauth/react 0.1.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/AtlasProvider.d.ts +59 -0
- package/dist/AtlasProvider.js +248 -0
- package/dist/AtlasProvider.js.map +1 -0
- package/dist/appearance.d.ts +85 -0
- package/dist/appearance.js +98 -0
- package/dist/appearance.js.map +1 -0
- package/dist/components.d.ts +40 -0
- package/dist/components.js +241 -0
- package/dist/components.js.map +1 -0
- package/dist/fapi.d.ts +11 -0
- package/dist/fapi.js +24 -0
- package/dist/fapi.js.map +1 -0
- package/dist/hooks.d.ts +62 -0
- package/dist/hooks.js +96 -0
- package/dist/hooks.js.map +1 -0
- package/dist/i18n.d.ts +23 -0
- package/dist/i18n.js +83 -0
- package/dist/i18n.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/protect.d.ts +18 -0
- package/dist/protect.js +24 -0
- package/dist/protect.js.map +1 -0
- package/dist/types.d.ts +42 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +31 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type ProviderToken } from '@atlasauth/js';
|
|
3
|
+
import { type Appearance } from './appearance';
|
|
4
|
+
import { type Catalog } from './i18n';
|
|
5
|
+
import type { AtlasOrganizationMembership, AtlasSession, AtlasUser, SessionClaims } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* §10.1 `<AtlasProvider publishableKey>`.
|
|
8
|
+
*
|
|
9
|
+
* Holds exactly one thing that matters: the token, in memory. Not
|
|
10
|
+
* localStorage, not a cookie this code writes — the refresh token is HttpOnly
|
|
11
|
+
* so browser script never touches it, and the short-lived JWT lives only as
|
|
12
|
+
* long as the tab. A provider that persisted either would survive the tab and
|
|
13
|
+
* become the thing an XSS reads first.
|
|
14
|
+
*/
|
|
15
|
+
export type AuthStatus = 'loading' | 'signed_in' | 'signed_out';
|
|
16
|
+
interface AtlasContextValue {
|
|
17
|
+
publishableKey: string;
|
|
18
|
+
frontendApi: string;
|
|
19
|
+
status: AuthStatus;
|
|
20
|
+
user: AtlasUser | null;
|
|
21
|
+
session: AtlasSession | null;
|
|
22
|
+
claims: SessionClaims | null;
|
|
23
|
+
memberships: AtlasOrganizationMembership[];
|
|
24
|
+
appearance?: Appearance;
|
|
25
|
+
catalog: Catalog;
|
|
26
|
+
/**
|
|
27
|
+
* §5.3: a sign-in attempt handed back by an OAuth redirect that still needs a
|
|
28
|
+
* second factor (no session was issued). Null in the ordinary case. The
|
|
29
|
+
* <SignIn> flow seeds itself from this so the user resumes at the 2FA step
|
|
30
|
+
* instead of starting over.
|
|
31
|
+
*/
|
|
32
|
+
pendingAttempt: {
|
|
33
|
+
id: string;
|
|
34
|
+
status: string;
|
|
35
|
+
} | null;
|
|
36
|
+
getToken(): Promise<string | null>;
|
|
37
|
+
/**
|
|
38
|
+
* §6.6 the signed-in user's OWN live token at a connected provider (Google,
|
|
39
|
+
* GitHub, …), so the app can call that provider's API. Null when there is no
|
|
40
|
+
* usable token — no linked account, or the provider granted / kept none. The
|
|
41
|
+
* server refreshes a stale token on read; the refresh token is never exposed.
|
|
42
|
+
*/
|
|
43
|
+
getProviderToken(provider: string): Promise<ProviderToken | null>;
|
|
44
|
+
signOut(): Promise<void>;
|
|
45
|
+
setActiveOrganization(organizationId: string | null): Promise<void>;
|
|
46
|
+
reload(): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
export interface AtlasProviderProps {
|
|
49
|
+
publishableKey: string;
|
|
50
|
+
/** The instance's FAPI origin. */
|
|
51
|
+
frontendApi?: string;
|
|
52
|
+
appearance?: Appearance;
|
|
53
|
+
localization?: Catalog;
|
|
54
|
+
children: ReactNode;
|
|
55
|
+
fetchImpl?: typeof fetch;
|
|
56
|
+
}
|
|
57
|
+
export declare function AtlasProvider(props: AtlasProviderProps): import("react").JSX.Element;
|
|
58
|
+
export declare function useAtlas(): AtlasContextValue;
|
|
59
|
+
export {};
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AtlasProvider = AtlasProvider;
|
|
4
|
+
exports.useAtlas = useAtlas;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const js_1 = require("@atlasauth/js");
|
|
8
|
+
const appearance_1 = require("./appearance");
|
|
9
|
+
const i18n_1 = require("./i18n");
|
|
10
|
+
const AtlasContext = (0, react_1.createContext)(null);
|
|
11
|
+
function AtlasProvider(props) {
|
|
12
|
+
const { publishableKey, children, appearance, localization } = props;
|
|
13
|
+
const baseUrl = props.frontendApi ?? '';
|
|
14
|
+
const doFetch = props.fetchImpl ?? fetch;
|
|
15
|
+
const [status, setStatus] = (0, react_1.useState)('loading');
|
|
16
|
+
const [user, setUser] = (0, react_1.useState)(null);
|
|
17
|
+
const [session, setSession] = (0, react_1.useState)(null);
|
|
18
|
+
const [claims, setClaims] = (0, react_1.useState)(null);
|
|
19
|
+
const [memberships, setMemberships] = (0, react_1.useState)([]);
|
|
20
|
+
const cache = (0, react_1.useRef)(new js_1.TokenCache());
|
|
21
|
+
const timer = (0, react_1.useRef)(null);
|
|
22
|
+
/**
|
|
23
|
+
* Captured synchronously during the first render (not in an effect) so it is
|
|
24
|
+
* already in context before <SignIn> reads it — a needs_second_factor redirect
|
|
25
|
+
* carries a status but no ticket, and that is what the 2FA resume seeds from.
|
|
26
|
+
*/
|
|
27
|
+
const [pendingAttempt] = (0, react_1.useState)(() => {
|
|
28
|
+
if (typeof window === 'undefined')
|
|
29
|
+
return null;
|
|
30
|
+
const result = (0, js_1.readRedirectResult)(window.location.search);
|
|
31
|
+
return result?.status && !result.ticket
|
|
32
|
+
? { id: result.attemptId, status: result.status }
|
|
33
|
+
: null;
|
|
34
|
+
});
|
|
35
|
+
const call = (0, react_1.useCallback)(async (path, init) => {
|
|
36
|
+
return doFetch(`${baseUrl}${path}`, {
|
|
37
|
+
...init,
|
|
38
|
+
// Cookies carry the session; a fetch without this looks identical to
|
|
39
|
+
// being signed out.
|
|
40
|
+
credentials: 'include',
|
|
41
|
+
headers: {
|
|
42
|
+
'x-publishable-key': publishableKey,
|
|
43
|
+
...(init?.body ? { 'content-type': 'application/json' } : {}),
|
|
44
|
+
...init?.headers,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
}, [baseUrl, doFetch, publishableKey]);
|
|
48
|
+
const applyToken = (0, react_1.useCallback)((jwt, sessionId) => {
|
|
49
|
+
const expiresAt = (0, js_1.readExpiry)(jwt);
|
|
50
|
+
if (!expiresAt)
|
|
51
|
+
return;
|
|
52
|
+
cache.current.set({ jwt, expiresAt, sessionId });
|
|
53
|
+
setClaims(decodeClaims(jwt));
|
|
54
|
+
}, []);
|
|
55
|
+
const reload = (0, react_1.useCallback)(async () => {
|
|
56
|
+
try {
|
|
57
|
+
const response = await call('/v1/client');
|
|
58
|
+
if (!response.ok)
|
|
59
|
+
throw new Error('boot failed');
|
|
60
|
+
const body = (await response.json());
|
|
61
|
+
if (!body.session || !body.user) {
|
|
62
|
+
cache.current.clear();
|
|
63
|
+
setStatus('signed_out');
|
|
64
|
+
setUser(null);
|
|
65
|
+
setSession(null);
|
|
66
|
+
setClaims(null);
|
|
67
|
+
setMemberships([]);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
setUser(body.user);
|
|
71
|
+
setSession(body.session);
|
|
72
|
+
setMemberships(body.organization_memberships ?? []);
|
|
73
|
+
if (body.jwt)
|
|
74
|
+
applyToken(body.jwt, body.session.id);
|
|
75
|
+
setStatus('signed_in');
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
/**
|
|
79
|
+
* A failed boot is NOT signed-out. Rendering a sign-in form because the
|
|
80
|
+
* network hiccuped throws away a perfectly good session and makes the
|
|
81
|
+
* user re-authenticate for no reason.
|
|
82
|
+
*/
|
|
83
|
+
setStatus((current) => (current === 'loading' ? 'signed_out' : current));
|
|
84
|
+
}
|
|
85
|
+
}, [applyToken, call]);
|
|
86
|
+
const refresh = (0, react_1.useCallback)(async () => {
|
|
87
|
+
const current = cache.current.peek();
|
|
88
|
+
if (!current)
|
|
89
|
+
return;
|
|
90
|
+
try {
|
|
91
|
+
const response = await call(`/v1/client/sessions/${current.sessionId}/tokens`, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
});
|
|
94
|
+
if (!response.ok)
|
|
95
|
+
throw new Error('refresh failed');
|
|
96
|
+
const body = (await response.json());
|
|
97
|
+
applyToken(body.jwt, body.session_id);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// The session may genuinely be gone. Re-boot rather than guess.
|
|
101
|
+
await reload();
|
|
102
|
+
}
|
|
103
|
+
}, [applyToken, call, reload]);
|
|
104
|
+
/** §7.4: proactive, jittered refresh scheduled from the token's own expiry. */
|
|
105
|
+
(0, react_1.useEffect)(() => {
|
|
106
|
+
if (status !== 'signed_in')
|
|
107
|
+
return;
|
|
108
|
+
const token = cache.current.peek();
|
|
109
|
+
if (!token)
|
|
110
|
+
return;
|
|
111
|
+
const delay = (0, js_1.msUntilRefresh)({ token, now: Date.now() });
|
|
112
|
+
timer.current = setTimeout(() => void refresh(), delay);
|
|
113
|
+
return () => {
|
|
114
|
+
if (timer.current)
|
|
115
|
+
clearTimeout(timer.current);
|
|
116
|
+
};
|
|
117
|
+
}, [status, claims, refresh]);
|
|
118
|
+
/**
|
|
119
|
+
* §6.3 step 3: complete a redirect-based sign-in. An OAuth or hosted-page flow
|
|
120
|
+
* lands back here with a one-time ticket in the URL; exchange it for cookies,
|
|
121
|
+
* scrub the URL so a reload can't replay it, THEN boot. A redirect that only
|
|
122
|
+
* carries a `__atlas_status` (a second factor is still owed) has no ticket, so
|
|
123
|
+
* nothing is exchanged and the app boots signed-out — exactly as intended.
|
|
124
|
+
*/
|
|
125
|
+
(0, react_1.useEffect)(() => {
|
|
126
|
+
const result = typeof window !== 'undefined' ? (0, js_1.readRedirectResult)(window.location.search) : null;
|
|
127
|
+
void (async () => {
|
|
128
|
+
if (result?.ticket) {
|
|
129
|
+
await call('/v1/client/tickets/exchange', {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
body: JSON.stringify({ attempt_id: result.attemptId, ticket: result.ticket }),
|
|
132
|
+
}).catch(() => undefined);
|
|
133
|
+
}
|
|
134
|
+
if (result && typeof window !== 'undefined') {
|
|
135
|
+
window.history.replaceState({}, document.title, (0, js_1.stripRedirectParams)(window.location.href));
|
|
136
|
+
}
|
|
137
|
+
await reload();
|
|
138
|
+
})();
|
|
139
|
+
// Runs once on mount (empty deps): the redirect is handled exactly once, and
|
|
140
|
+
// re-running would only re-read an already-scrubbed URL. `reload`/`call` are
|
|
141
|
+
// stable across renders.
|
|
142
|
+
}, []);
|
|
143
|
+
const getToken = (0, react_1.useCallback)(async () => {
|
|
144
|
+
// A 5s skew so a token that would die inside the caller's request is
|
|
145
|
+
// refreshed first rather than handed over.
|
|
146
|
+
const usable = cache.current.get(5_000);
|
|
147
|
+
if (usable)
|
|
148
|
+
return usable.jwt;
|
|
149
|
+
await refresh();
|
|
150
|
+
return cache.current.get(5_000)?.jwt ?? null;
|
|
151
|
+
}, [refresh]);
|
|
152
|
+
/**
|
|
153
|
+
* The FAPI client for the token-vault read. Reuses the exact same
|
|
154
|
+
* credentials-and-publishable-key request `call` makes, so the browser sends
|
|
155
|
+
* one consistent shape and the framework-agnostic `@atlasauth/js` helper is the
|
|
156
|
+
* single implementation the React SDK surfaces.
|
|
157
|
+
*/
|
|
158
|
+
const fapiClient = (0, react_1.useMemo)(() => new js_1.FapiClient({ publishableKey, baseUrl, fetchImpl: doFetch }), [publishableKey, baseUrl, doFetch]);
|
|
159
|
+
const getProviderToken = (0, react_1.useCallback)((provider) => (0, js_1.getProviderToken)(fapiClient, provider), [fapiClient]);
|
|
160
|
+
const signOut = (0, react_1.useCallback)(async () => {
|
|
161
|
+
const current = cache.current.peek();
|
|
162
|
+
if (current) {
|
|
163
|
+
await call(`/v1/client/sessions/${current.sessionId}/revoke`, { method: 'POST' }).catch(() => undefined);
|
|
164
|
+
}
|
|
165
|
+
cache.current.clear();
|
|
166
|
+
setStatus('signed_out');
|
|
167
|
+
setUser(null);
|
|
168
|
+
setSession(null);
|
|
169
|
+
setClaims(null);
|
|
170
|
+
setMemberships([]);
|
|
171
|
+
}, [call]);
|
|
172
|
+
const setActiveOrganization = (0, react_1.useCallback)(async (organizationId) => {
|
|
173
|
+
const current = cache.current.peek();
|
|
174
|
+
if (!current)
|
|
175
|
+
return;
|
|
176
|
+
const response = await call(`/v1/client/sessions/${current.sessionId}/touch`, {
|
|
177
|
+
method: 'POST',
|
|
178
|
+
body: JSON.stringify({ active_organization_id: organizationId }),
|
|
179
|
+
});
|
|
180
|
+
if (!response.ok)
|
|
181
|
+
return;
|
|
182
|
+
const body = (await response.json());
|
|
183
|
+
// The new token carries the new org claims; re-boot would work too but
|
|
184
|
+
// costs a round trip for information already in hand.
|
|
185
|
+
applyToken(body.jwt, current.sessionId);
|
|
186
|
+
await reload();
|
|
187
|
+
}, [applyToken, call, reload]);
|
|
188
|
+
const catalog = (0, react_1.useMemo)(() => (localization ? (0, i18n_1.withFallback)(localization) : i18n_1.EN_US), [localization]);
|
|
189
|
+
const value = (0, react_1.useMemo)(() => ({
|
|
190
|
+
publishableKey,
|
|
191
|
+
frontendApi: baseUrl,
|
|
192
|
+
status,
|
|
193
|
+
user,
|
|
194
|
+
session,
|
|
195
|
+
claims,
|
|
196
|
+
memberships,
|
|
197
|
+
appearance,
|
|
198
|
+
catalog,
|
|
199
|
+
pendingAttempt,
|
|
200
|
+
getToken,
|
|
201
|
+
getProviderToken,
|
|
202
|
+
signOut,
|
|
203
|
+
setActiveOrganization,
|
|
204
|
+
reload,
|
|
205
|
+
}), [
|
|
206
|
+
appearance,
|
|
207
|
+
baseUrl,
|
|
208
|
+
publishableKey,
|
|
209
|
+
catalog,
|
|
210
|
+
claims,
|
|
211
|
+
getToken,
|
|
212
|
+
getProviderToken,
|
|
213
|
+
memberships,
|
|
214
|
+
pendingAttempt,
|
|
215
|
+
reload,
|
|
216
|
+
session,
|
|
217
|
+
setActiveOrganization,
|
|
218
|
+
signOut,
|
|
219
|
+
status,
|
|
220
|
+
user,
|
|
221
|
+
]);
|
|
222
|
+
const style = (0, react_1.useMemo)(() => (0, appearance_1.cssVariables)((0, appearance_1.resolveTokens)(appearance)), [appearance]);
|
|
223
|
+
return ((0, jsx_runtime_1.jsx)(AtlasContext.Provider, { value: value, children: (0, jsx_runtime_1.jsx)("div", { className: "atlas-root", style: style, children: children }) }));
|
|
224
|
+
}
|
|
225
|
+
function useAtlas() {
|
|
226
|
+
const value = (0, react_1.useContext)(AtlasContext);
|
|
227
|
+
if (!value) {
|
|
228
|
+
/**
|
|
229
|
+
* A thrown error rather than a null-ish default. A hook returning
|
|
230
|
+
* `{ user: null }` outside the provider is indistinguishable from a
|
|
231
|
+
* signed-out user, and the developer spends an afternoon on it.
|
|
232
|
+
*/
|
|
233
|
+
throw new Error('Atlas hooks must be used inside <AtlasProvider>.');
|
|
234
|
+
}
|
|
235
|
+
return value;
|
|
236
|
+
}
|
|
237
|
+
function decodeClaims(jwt) {
|
|
238
|
+
const parts = jwt.split('.');
|
|
239
|
+
if (parts.length !== 3)
|
|
240
|
+
return null;
|
|
241
|
+
try {
|
|
242
|
+
return JSON.parse(atob(parts[1].replace(/-/g, '+').replace(/_/g, '/')));
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=AtlasProvider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AtlasProvider.js","sourceRoot":"","sources":["../src/AtlasProvider.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §10.3 theming.
|
|
3
|
+
*
|
|
4
|
+
* "appearance prop: design tokens (colors, radius, font, spacing) + per-element
|
|
5
|
+
* class overrides (elements: { formButtonPrimary: "…" }). No iframe — components
|
|
6
|
+
* render in the host DOM so Tailwind/CSS just works."
|
|
7
|
+
*
|
|
8
|
+
* Rendering in the host DOM is the decision everything else follows from. An
|
|
9
|
+
* iframe would isolate styles perfectly and be unusable: it cannot be sized to
|
|
10
|
+
* its content reliably, it breaks password managers, it breaks autofill, and a
|
|
11
|
+
* Tailwind class the customer writes has no effect inside it. Sharing the DOM
|
|
12
|
+
* means the customer's CSS reaches our markup, which is exactly what they want
|
|
13
|
+
* and what makes an override system necessary rather than decorative.
|
|
14
|
+
*
|
|
15
|
+
* Element classes are APPENDED to ours, never replacing them. A customer adding
|
|
16
|
+
* `className="rounded-xl"` wants a rounder button, not an unstyled one — and
|
|
17
|
+
* replacement is a support burden where every override starts by rebuilding the
|
|
18
|
+
* component's base styles from scratch.
|
|
19
|
+
*/
|
|
20
|
+
export interface DesignTokens {
|
|
21
|
+
colorPrimary?: string;
|
|
22
|
+
colorText?: string;
|
|
23
|
+
colorBackground?: string;
|
|
24
|
+
colorDanger?: string;
|
|
25
|
+
colorBorder?: string;
|
|
26
|
+
borderRadius?: string;
|
|
27
|
+
fontFamily?: string;
|
|
28
|
+
spacingUnit?: string;
|
|
29
|
+
}
|
|
30
|
+
/** Every element a customer may target. Named for what it IS, not where it sits. */
|
|
31
|
+
export type ElementKey = 'root' | 'card' | 'headerTitle' | 'headerSubtitle' | 'formField' | 'formFieldLabel' | 'formFieldInput' | 'formFieldError' | 'formButtonPrimary' | 'formButtonSecondary' | 'socialButton' | 'dividerText' | 'footerAction' | 'avatar' | 'menu' | 'menuItem' | 'badge';
|
|
32
|
+
export interface Appearance {
|
|
33
|
+
/** A prebuilt theme to start from. */
|
|
34
|
+
baseTheme?: keyof typeof THEMES;
|
|
35
|
+
variables?: DesignTokens;
|
|
36
|
+
elements?: Partial<Record<ElementKey, string>>;
|
|
37
|
+
}
|
|
38
|
+
export declare const DEFAULT_TOKENS: Required<DesignTokens>;
|
|
39
|
+
/**
|
|
40
|
+
* §10.3: "prebuilt themes shipped (default, dark, neobrutalist as a demo of the
|
|
41
|
+
* override system)". Neobrutalist exists to prove the token set is expressive
|
|
42
|
+
* enough for a theme that looks nothing like the default — a theming system
|
|
43
|
+
* that only produces tasteful variations of itself is not a theming system.
|
|
44
|
+
*/
|
|
45
|
+
export declare const THEMES: {
|
|
46
|
+
readonly default: Required<DesignTokens>;
|
|
47
|
+
readonly dark: {
|
|
48
|
+
readonly colorText: "#e6e9ef";
|
|
49
|
+
readonly colorBackground: "#14171c";
|
|
50
|
+
readonly colorBorder: "#242932";
|
|
51
|
+
readonly colorPrimary: "#6ea8fe";
|
|
52
|
+
readonly colorDanger: string;
|
|
53
|
+
readonly borderRadius: string;
|
|
54
|
+
readonly fontFamily: string;
|
|
55
|
+
readonly spacingUnit: string;
|
|
56
|
+
};
|
|
57
|
+
readonly neobrutalist: {
|
|
58
|
+
readonly colorPrimary: "#ffe600";
|
|
59
|
+
readonly colorText: "#000000";
|
|
60
|
+
readonly colorBackground: "#ffffff";
|
|
61
|
+
readonly colorBorder: "#000000";
|
|
62
|
+
readonly borderRadius: "0px";
|
|
63
|
+
readonly fontFamily: "\"Courier New\", ui-monospace, monospace";
|
|
64
|
+
readonly colorDanger: string;
|
|
65
|
+
readonly spacingUnit: string;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Resolve tokens: defaults, then the base theme, then explicit variables.
|
|
70
|
+
*
|
|
71
|
+
* Later wins, and `undefined` never overwrites. A customer setting one variable
|
|
72
|
+
* on the dark theme expects the other eleven to stay dark, not silently revert
|
|
73
|
+
* to the light defaults.
|
|
74
|
+
*/
|
|
75
|
+
export declare function resolveTokens(appearance?: Appearance): Required<DesignTokens>;
|
|
76
|
+
/** Tokens as CSS custom properties, for the host DOM to inherit. */
|
|
77
|
+
export declare function cssVariables(tokens: Required<DesignTokens>): Record<string, string>;
|
|
78
|
+
/**
|
|
79
|
+
* Combine our base class with the customer's override.
|
|
80
|
+
*
|
|
81
|
+
* Appended, never replaced — a customer adding `rounded-xl` wants a rounder
|
|
82
|
+
* button, not an unstyled one. Theirs comes last so it wins on equal
|
|
83
|
+
* specificity, which is the behaviour anyone writing Tailwind expects.
|
|
84
|
+
*/
|
|
85
|
+
export declare function classFor(element: ElementKey, appearance: Appearance | undefined, base: string): string;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* §10.3 theming.
|
|
4
|
+
*
|
|
5
|
+
* "appearance prop: design tokens (colors, radius, font, spacing) + per-element
|
|
6
|
+
* class overrides (elements: { formButtonPrimary: "…" }). No iframe — components
|
|
7
|
+
* render in the host DOM so Tailwind/CSS just works."
|
|
8
|
+
*
|
|
9
|
+
* Rendering in the host DOM is the decision everything else follows from. An
|
|
10
|
+
* iframe would isolate styles perfectly and be unusable: it cannot be sized to
|
|
11
|
+
* its content reliably, it breaks password managers, it breaks autofill, and a
|
|
12
|
+
* Tailwind class the customer writes has no effect inside it. Sharing the DOM
|
|
13
|
+
* means the customer's CSS reaches our markup, which is exactly what they want
|
|
14
|
+
* and what makes an override system necessary rather than decorative.
|
|
15
|
+
*
|
|
16
|
+
* Element classes are APPENDED to ours, never replacing them. A customer adding
|
|
17
|
+
* `className="rounded-xl"` wants a rounder button, not an unstyled one — and
|
|
18
|
+
* replacement is a support burden where every override starts by rebuilding the
|
|
19
|
+
* component's base styles from scratch.
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.THEMES = exports.DEFAULT_TOKENS = void 0;
|
|
23
|
+
exports.resolveTokens = resolveTokens;
|
|
24
|
+
exports.cssVariables = cssVariables;
|
|
25
|
+
exports.classFor = classFor;
|
|
26
|
+
exports.DEFAULT_TOKENS = {
|
|
27
|
+
colorPrimary: '#5b5bd6',
|
|
28
|
+
colorText: '#1c1c1f',
|
|
29
|
+
colorBackground: '#ffffff',
|
|
30
|
+
colorDanger: '#d64545',
|
|
31
|
+
colorBorder: '#e4e4e9',
|
|
32
|
+
borderRadius: '8px',
|
|
33
|
+
fontFamily: 'ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif',
|
|
34
|
+
spacingUnit: '4px',
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* §10.3: "prebuilt themes shipped (default, dark, neobrutalist as a demo of the
|
|
38
|
+
* override system)". Neobrutalist exists to prove the token set is expressive
|
|
39
|
+
* enough for a theme that looks nothing like the default — a theming system
|
|
40
|
+
* that only produces tasteful variations of itself is not a theming system.
|
|
41
|
+
*/
|
|
42
|
+
exports.THEMES = {
|
|
43
|
+
default: exports.DEFAULT_TOKENS,
|
|
44
|
+
dark: {
|
|
45
|
+
...exports.DEFAULT_TOKENS,
|
|
46
|
+
colorText: '#e6e9ef',
|
|
47
|
+
colorBackground: '#14171c',
|
|
48
|
+
colorBorder: '#242932',
|
|
49
|
+
colorPrimary: '#6ea8fe',
|
|
50
|
+
},
|
|
51
|
+
neobrutalist: {
|
|
52
|
+
...exports.DEFAULT_TOKENS,
|
|
53
|
+
colorPrimary: '#ffe600',
|
|
54
|
+
colorText: '#000000',
|
|
55
|
+
colorBackground: '#ffffff',
|
|
56
|
+
colorBorder: '#000000',
|
|
57
|
+
borderRadius: '0px',
|
|
58
|
+
fontFamily: '"Courier New", ui-monospace, monospace',
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Resolve tokens: defaults, then the base theme, then explicit variables.
|
|
63
|
+
*
|
|
64
|
+
* Later wins, and `undefined` never overwrites. A customer setting one variable
|
|
65
|
+
* on the dark theme expects the other eleven to stay dark, not silently revert
|
|
66
|
+
* to the light defaults.
|
|
67
|
+
*/
|
|
68
|
+
function resolveTokens(appearance) {
|
|
69
|
+
const base = appearance?.baseTheme ? exports.THEMES[appearance.baseTheme] : exports.DEFAULT_TOKENS;
|
|
70
|
+
const overrides = appearance?.variables ?? {};
|
|
71
|
+
const resolved = { ...exports.DEFAULT_TOKENS, ...base };
|
|
72
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
73
|
+
if (value !== undefined)
|
|
74
|
+
resolved[key] = value;
|
|
75
|
+
}
|
|
76
|
+
return resolved;
|
|
77
|
+
}
|
|
78
|
+
/** Tokens as CSS custom properties, for the host DOM to inherit. */
|
|
79
|
+
function cssVariables(tokens) {
|
|
80
|
+
const vars = {};
|
|
81
|
+
for (const [key, value] of Object.entries(tokens)) {
|
|
82
|
+
// camelCase → --atlas-kebab-case, which is what a customer writes in CSS.
|
|
83
|
+
vars[`--atlas-${key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}`] = value;
|
|
84
|
+
}
|
|
85
|
+
return vars;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Combine our base class with the customer's override.
|
|
89
|
+
*
|
|
90
|
+
* Appended, never replaced — a customer adding `rounded-xl` wants a rounder
|
|
91
|
+
* button, not an unstyled one. Theirs comes last so it wins on equal
|
|
92
|
+
* specificity, which is the behaviour anyone writing Tailwind expects.
|
|
93
|
+
*/
|
|
94
|
+
function classFor(element, appearance, base) {
|
|
95
|
+
const override = appearance?.elements?.[element];
|
|
96
|
+
return override ? `${base} ${override}` : base;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=appearance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appearance.js","sourceRoot":"","sources":["../src/appearance.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;AAoFH,sCASC;AAGD,oCAOC;AASD,4BAOC;AA/EY,QAAA,cAAc,GAA2B;IACpD,YAAY,EAAE,SAAS;IACvB,SAAS,EAAE,SAAS;IACpB,eAAe,EAAE,SAAS;IAC1B,WAAW,EAAE,SAAS;IACtB,WAAW,EAAE,SAAS;IACtB,YAAY,EAAE,KAAK;IACnB,UAAU,EAAE,iEAAiE;IAC7E,WAAW,EAAE,KAAK;CACnB,CAAC;AAEF;;;;;GAKG;AACU,QAAA,MAAM,GAAG;IACpB,OAAO,EAAE,sBAAc;IACvB,IAAI,EAAE;QACJ,GAAG,sBAAc;QACjB,SAAS,EAAE,SAAS;QACpB,eAAe,EAAE,SAAS;QAC1B,WAAW,EAAE,SAAS;QACtB,YAAY,EAAE,SAAS;KACxB;IACD,YAAY,EAAE;QACZ,GAAG,sBAAc;QACjB,YAAY,EAAE,SAAS;QACvB,SAAS,EAAE,SAAS;QACpB,eAAe,EAAE,SAAS;QAC1B,WAAW,EAAE,SAAS;QACtB,YAAY,EAAE,KAAK;QACnB,UAAU,EAAE,wCAAwC;KACrD;CACwD,CAAC;AAE5D;;;;;;GAMG;AACH,SAAgB,aAAa,CAAC,UAAuB;IACnD,MAAM,IAAI,GAAG,UAAU,EAAE,SAAS,CAAC,CAAC,CAAC,cAAM,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,sBAAc,CAAC;IACnF,MAAM,SAAS,GAAG,UAAU,EAAE,SAAS,IAAI,EAAE,CAAC;IAE9C,MAAM,QAAQ,GAAG,EAAE,GAAG,sBAAc,EAAE,GAAG,IAAI,EAAE,CAAC;IAChD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACrD,IAAI,KAAK,KAAK,SAAS;YAAE,QAAQ,CAAC,GAAyB,CAAC,GAAG,KAAK,CAAC;IACvE,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,oEAAoE;AACpE,SAAgB,YAAY,CAAC,MAA8B;IACzD,MAAM,IAAI,GAA2B,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,0EAA0E;QAC1E,IAAI,CAAC,WAAW,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC;IACjF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,QAAQ,CACtB,OAAmB,EACnB,UAAkC,EAClC,IAAY;IAEZ,MAAM,QAAQ,GAAG,UAAU,EAAE,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC;IACjD,OAAO,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type ProtectCondition } from './protect';
|
|
3
|
+
export declare function SignedIn({ children }: {
|
|
4
|
+
children: ReactNode;
|
|
5
|
+
}): import("react").JSX.Element | null;
|
|
6
|
+
export declare function SignedOut({ children }: {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}): import("react").JSX.Element | null;
|
|
9
|
+
/** §10.2: render children only while the SDK is still loading. */
|
|
10
|
+
export declare function AtlasLoading({ children }: {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}): import("react").JSX.Element | null;
|
|
13
|
+
/** §10.2: render children only once the SDK has finished loading — the
|
|
14
|
+
* symmetric partner of {@link AtlasLoading}, so a consumer can show a spinner
|
|
15
|
+
* and its resolved content without hand-rolling the inverse condition. */
|
|
16
|
+
export declare function AtlasLoaded({ children }: {
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
}): import("react").JSX.Element | null;
|
|
19
|
+
/**
|
|
20
|
+
* §10.2 `<Protect permission="…">`.
|
|
21
|
+
*
|
|
22
|
+
* A rendering helper, never an authorization boundary — anyone can flip the
|
|
23
|
+
* condition in devtools. The server checking the same permission is what
|
|
24
|
+
* actually stops them, and this component's job is only to avoid showing a
|
|
25
|
+
* button that would fail.
|
|
26
|
+
*/
|
|
27
|
+
export declare function Protect({ children, fallback, ...condition }: ProtectCondition & {
|
|
28
|
+
children: ReactNode;
|
|
29
|
+
fallback?: ReactNode;
|
|
30
|
+
}): import("react").JSX.Element | null;
|
|
31
|
+
export interface FlowProps {
|
|
32
|
+
/** Where to send the user once the flow completes. */
|
|
33
|
+
afterUrl?: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function SignIn(_props?: FlowProps): import("react").JSX.Element;
|
|
36
|
+
export declare function SignUp(_props?: FlowProps): import("react").JSX.Element;
|
|
37
|
+
export declare function UserButton(): import("react").JSX.Element | null;
|
|
38
|
+
export declare function OrganizationSwitcher(): import("react").JSX.Element;
|
|
39
|
+
export declare function UserProfile(): import("react").JSX.Element | null;
|
|
40
|
+
export declare function OrganizationProfile(): import("react").JSX.Element | null;
|