@llmnative/react 2.1.0 → 2.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/index.css +1 -1
- package/dist/index.js +21 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +10310 -8468
- package/dist/index.mjs.map +1 -1
- package/dist/types/src/App.d.ts +10 -1
- package/dist/types/src/I18n.d.ts +91 -0
- package/dist/types/src/components/auth/AuthGate.d.ts +26 -0
- package/dist/types/src/components/auth/AuthLayout.d.ts +38 -0
- package/dist/types/src/components/auth/ForgotPassword.d.ts +19 -0
- package/dist/types/src/components/auth/OneTimeCodeInput.d.ts +27 -0
- package/dist/types/src/components/auth/ResetPassword.d.ts +31 -0
- package/dist/types/src/components/auth/SignIn.d.ts +42 -0
- package/dist/types/src/components/auth/SignUp.d.ts +43 -0
- package/dist/types/src/components/auth/VerifyCode.d.ts +32 -0
- package/dist/types/src/components/auth/VerifyEmail.d.ts +31 -0
- package/dist/types/src/components/auth/index.d.ts +12 -0
- package/dist/types/src/components/auth/providers.d.ts +22 -0
- package/dist/types/src/components/auth/shared.d.ts +124 -0
- package/dist/types/src/components/auth/useAuthFlows.d.ts +46 -0
- package/dist/types/src/index.d.ts +5 -1
- package/dist/types/src/providers/auth/AuthProvider.d.ts +51 -0
- package/dist/types/src/providers/auth/firebase/FirebaseAuthProvider.d.ts +8 -1
- package/dist/types/src/providers/auth/github/GitHubAuthProvider.d.ts +2 -0
- package/dist/types/src/providers/auth/mock/MockAuthProvider.d.ts +55 -0
- package/dist/types/src/providers/auth/supabase/SupabaseAuthProvider.d.ts +16 -1
- package/dist/types/src/providers/manifest.d.ts +4 -1
- package/llms-full.txt +66 -2
- package/package.json +1 -1
- package/scripts/cli/setup-project.js +678 -667
- package/templates/_shared/conf/access.tsx +23 -0
- package/templates/_shared/pages/access/ForgotPasswordPage.tsx +13 -0
- package/templates/_shared/pages/access/ResetPasswordPage.tsx +19 -0
- package/templates/_shared/pages/access/SignInPage.tsx +21 -0
- package/templates/_shared/pages/access/SignUpPage.tsx +23 -0
- package/templates/_shared/pages/access/VerifyEmailPage.tsx +20 -0
- package/templates/_shared/pages/access/paths.ts +12 -0
- package/templates/_shared/sections/Header.tsx +1 -1
|
@@ -13,6 +13,7 @@ export type { I18nDict, I18nConfig, I18nController, I18nTranslations, I18nLocale
|
|
|
13
13
|
export * as locales from './conf/i18n';
|
|
14
14
|
export type { AppThemeProviderConfig, BuiltInThemeId, CardTheme, ColorScale, GalleryTheme, ListCardTheme, MenuTheme, ModalTheme, ProviderSwitcherTheme, ToolbarTheme, TableTheme, Theme, ThemeConfig, ThemeController, ThemeMode, ThemeDefinition, ThemePresetConfig, } from './Theme';
|
|
15
15
|
export { AuthButton, useAccessToken, getAccessToken } from './auth';
|
|
16
|
+
export * from './components/auth';
|
|
16
17
|
export { useGlobalVars, getGlobalVars, setGlobalVars, removeGlobalVars } from './Global';
|
|
17
18
|
export type { DataProviderAdapter, FieldValue, RecordProps, RecordArray, DatabaseOptions, DBConfig, ReadOptions, WhereClause, OrderClause, Condition } from './providers/data/DataProvider';
|
|
18
19
|
export type { OrderConfig, OrderDirection } from './libs/order';
|
|
@@ -50,7 +51,10 @@ export { FirebaseStorageProvider } from './providers/storage/firebase';
|
|
|
50
51
|
export type { FirebaseStorageProviderConfig } from './providers/storage/firebase';
|
|
51
52
|
export { SupabaseStorageProvider } from './providers/storage/supabase';
|
|
52
53
|
export { useStorageProvider, StorageProvider } from './providers/storage/StorageProviderContext';
|
|
53
|
-
export type { AuthProviderAdapter, AuthIntent, AuthSignInOptions, UserProfile } from './providers/auth/AuthProvider';
|
|
54
|
+
export type { AuthProviderAdapter, AuthIntent, AuthSignInOptions, UserProfile, AuthSignUpInput, AuthSignUpResult, AuthLinkOptions, AuthEmailCodeOptions, AuthErrorReason, } from './providers/auth/AuthProvider';
|
|
55
|
+
export { AuthFlowError } from './providers/auth/AuthProvider';
|
|
56
|
+
export { MockAuthProvider } from './providers/auth/mock/MockAuthProvider';
|
|
57
|
+
export type { MockAuthProviderOptions, MockAuthAccount } from './providers/auth/mock/MockAuthProvider';
|
|
54
58
|
export { useAuthProvider, AuthProvider } from './providers/auth/AuthProviderContext';
|
|
55
59
|
export { GoogleAuthProvider } from './providers/auth/google/GoogleAuthProvider';
|
|
56
60
|
export type { FirebaseAuthProviderOptions, FirebaseSignInOptions } from './providers/auth/firebase/FirebaseAuthProvider';
|
|
@@ -27,4 +27,55 @@ export interface AuthProviderAdapter extends ProviderConfigurable {
|
|
|
27
27
|
* re-verifies the raw token itself rather than trusting client-read claims.
|
|
28
28
|
*/
|
|
29
29
|
getIdTokenClaims?(forceRefresh?: boolean): Promise<Record<string, unknown> | null>;
|
|
30
|
+
/** Resolves once the session of the last visit is restored (signed in or not): `AuthGate` waits for it. */
|
|
31
|
+
whenReady?(): Promise<void>;
|
|
32
|
+
/** Creates an account with email and password; says whether the email must be confirmed first. */
|
|
33
|
+
signUp?(input: AuthSignUpInput): Promise<AuthSignUpResult>;
|
|
34
|
+
/** Sends the link to choose a new password. Never says whether the account exists. */
|
|
35
|
+
sendPasswordReset?(email: string, options?: AuthLinkOptions): Promise<void>;
|
|
36
|
+
/** Sets the new password with the code of the link (`oobCode`, `token_hash`, …). */
|
|
37
|
+
confirmPasswordReset?(code: string, newPassword: string): Promise<void>;
|
|
38
|
+
/** Sends again the link that confirms the email: of the signed-in user, or of `email` when the service allows it. */
|
|
39
|
+
sendEmailVerification?(options?: AuthLinkOptions & {
|
|
40
|
+
email?: string;
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
/** Confirms the email with the code of the link. */
|
|
43
|
+
verifyEmail?(code: string): Promise<void>;
|
|
44
|
+
/** Sends a one-time code by email to sign in. */
|
|
45
|
+
requestEmailCode?(email: string, options?: AuthEmailCodeOptions): Promise<void>;
|
|
46
|
+
/** Signs in with the code received by email. */
|
|
47
|
+
verifyEmailCode?(email: string, code: string): Promise<UserProfile | null>;
|
|
48
|
+
}
|
|
49
|
+
export interface AuthSignUpInput {
|
|
50
|
+
email: string;
|
|
51
|
+
password: string;
|
|
52
|
+
/** The full name, when the screen asks for it. */
|
|
53
|
+
name?: string;
|
|
54
|
+
/** Where the confirmation link brings back (the page that calls `verifyEmail`). */
|
|
55
|
+
redirectTo?: string;
|
|
56
|
+
}
|
|
57
|
+
export interface AuthSignUpResult {
|
|
58
|
+
user: UserProfile | null;
|
|
59
|
+
/** The account works once the email is confirmed: the screen says to check the inbox. */
|
|
60
|
+
needsEmailVerification: boolean;
|
|
61
|
+
}
|
|
62
|
+
export interface AuthLinkOptions {
|
|
63
|
+
/** The page the link of the email opens. */
|
|
64
|
+
redirectTo?: string;
|
|
65
|
+
}
|
|
66
|
+
export interface AuthEmailCodeOptions {
|
|
67
|
+
/** Creates the account when the email has none (default `false`: only existing accounts sign in). */
|
|
68
|
+
createUser?: boolean;
|
|
69
|
+
}
|
|
70
|
+
/** Why an access flow failed, in words a screen can translate (`authErrorMessage`). */
|
|
71
|
+
export type AuthErrorReason = 'credentials' | 'emailInUse' | 'weakPassword' | 'invalidEmail' | 'tooManyRequests' | 'network' | 'invalidCode' | 'expiredLink' | 'disabled' | 'cancelled' | 'notSupported' | 'generic';
|
|
72
|
+
/**
|
|
73
|
+
* An error of an access flow with its reason: a screen shows the translated message of the reason,
|
|
74
|
+
* never the raw text of the service. Throw it from your own functions (e.g. a code checked by your server).
|
|
75
|
+
*/
|
|
76
|
+
export declare class AuthFlowError extends Error {
|
|
77
|
+
readonly reason: AuthErrorReason;
|
|
78
|
+
constructor(reason: AuthErrorReason, message?: string, options?: {
|
|
79
|
+
cause?: unknown;
|
|
80
|
+
});
|
|
30
81
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthProviderAdapter, AuthSignInOptions, UserProfile } from '../AuthProvider';
|
|
1
|
+
import type { AuthLinkOptions, AuthProviderAdapter, AuthSignInOptions, AuthSignUpInput, AuthSignUpResult, UserProfile } from '../AuthProvider';
|
|
2
2
|
import type { ProviderConfigurationState } from '../../ProviderConfiguration';
|
|
3
3
|
import type { FirebaseConfig } from '../../../Config';
|
|
4
4
|
/**
|
|
@@ -46,4 +46,11 @@ export declare class FirebaseAuthProvider implements AuthProviderAdapter {
|
|
|
46
46
|
onAuthChange(callback: (user: UserProfile | null) => void): () => void;
|
|
47
47
|
getAccessToken(): Promise<string>;
|
|
48
48
|
getIdTokenClaims(forceRefresh?: boolean): Promise<Record<string, unknown> | null>;
|
|
49
|
+
private requireAuth;
|
|
50
|
+
whenReady(): Promise<void>;
|
|
51
|
+
signUp({ email, password, name, redirectTo }: AuthSignUpInput): Promise<AuthSignUpResult>;
|
|
52
|
+
sendPasswordReset(email: string, options?: AuthLinkOptions): Promise<void>;
|
|
53
|
+
confirmPasswordReset(code: string, newPassword: string): Promise<void>;
|
|
54
|
+
sendEmailVerification(options?: AuthLinkOptions): Promise<void>;
|
|
55
|
+
verifyEmail(code: string): Promise<void>;
|
|
49
56
|
}
|
|
@@ -32,6 +32,8 @@ export declare class GitHubAuthProvider implements AuthProviderAdapter {
|
|
|
32
32
|
refresh(): Promise<UserProfile | null>;
|
|
33
33
|
/** Leaves for GitHub through the server, and comes back to the page it left from. */
|
|
34
34
|
signIn(options?: AuthSignInOptions): Promise<UserProfile | null>;
|
|
35
|
+
/** Resolves once the server said who is signed in (CR-127): `AuthGate` waits for it. */
|
|
36
|
+
whenReady(): Promise<void>;
|
|
35
37
|
signOut(): Promise<void>;
|
|
36
38
|
onAuthChange(callback: (user: UserProfile | null) => void): () => void;
|
|
37
39
|
private setUser;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { AuthEmailCodeOptions, AuthProviderAdapter, AuthSignInOptions, AuthSignUpInput, AuthSignUpResult, UserProfile } from '../AuthProvider';
|
|
2
|
+
import type { ProviderConfigurationState } from '../../ProviderConfiguration';
|
|
3
|
+
/** An account of the mock: its password, and whether its email is confirmed. */
|
|
4
|
+
export interface MockAuthAccount {
|
|
5
|
+
email: string;
|
|
6
|
+
password: string;
|
|
7
|
+
displayName?: string;
|
|
8
|
+
emailVerified?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface MockAuthProviderOptions {
|
|
11
|
+
/**
|
|
12
|
+
* The accounts that exist. Omitted: any email and any password sign in (a demo); given: only these,
|
|
13
|
+
* and a wrong password fails as a real service would.
|
|
14
|
+
*/
|
|
15
|
+
accounts?: MockAuthAccount[];
|
|
16
|
+
/** The code every email "receives" (default `123456`); any other code fails. */
|
|
17
|
+
code?: string;
|
|
18
|
+
/** Where the session is kept between reloads (default `sessionStorage`); `null` keeps it in memory only. */
|
|
19
|
+
storage?: Storage | null;
|
|
20
|
+
/** A pause before each answer, in ms, to see the loading states (default `400`). */
|
|
21
|
+
latency?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* An auth provider for demos and tests, with no service behind it (CR-127): every flow of the access
|
|
25
|
+
* screens works in memory. Never use it in production: it trusts any credential it is configured to.
|
|
26
|
+
*/
|
|
27
|
+
export declare class MockAuthProvider implements AuthProviderAdapter {
|
|
28
|
+
private user;
|
|
29
|
+
private readonly listeners;
|
|
30
|
+
private readonly accounts;
|
|
31
|
+
private readonly open;
|
|
32
|
+
private readonly code;
|
|
33
|
+
private readonly storage;
|
|
34
|
+
private readonly latency;
|
|
35
|
+
constructor(options?: MockAuthProviderOptions);
|
|
36
|
+
getConfigurationState(): ProviderConfigurationState;
|
|
37
|
+
isConfigured(): boolean;
|
|
38
|
+
getUser(): UserProfile | null;
|
|
39
|
+
isAuthenticated(): boolean;
|
|
40
|
+
whenReady(): Promise<void>;
|
|
41
|
+
onAuthChange(callback: (user: UserProfile | null) => void): () => void;
|
|
42
|
+
signIn(options?: AuthSignInOptions): Promise<UserProfile | null>;
|
|
43
|
+
signOut(): Promise<void>;
|
|
44
|
+
signUp({ email, password, name }: AuthSignUpInput): Promise<AuthSignUpResult>;
|
|
45
|
+
sendPasswordReset(): Promise<void>;
|
|
46
|
+
confirmPasswordReset(code: string, newPassword: string): Promise<void>;
|
|
47
|
+
sendEmailVerification(): Promise<void>;
|
|
48
|
+
verifyEmail(code: string): Promise<void>;
|
|
49
|
+
requestEmailCode(email: string, options?: AuthEmailCodeOptions): Promise<void>;
|
|
50
|
+
verifyEmailCode(email: string, code: string): Promise<UserProfile | null>;
|
|
51
|
+
private profile;
|
|
52
|
+
private setUser;
|
|
53
|
+
private restore;
|
|
54
|
+
private pause;
|
|
55
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthProviderAdapter, AuthSignInOptions, UserProfile } from '../AuthProvider';
|
|
1
|
+
import type { AuthEmailCodeOptions, AuthLinkOptions, AuthProviderAdapter, AuthSignInOptions, AuthSignUpInput, AuthSignUpResult, UserProfile } from '../AuthProvider';
|
|
2
2
|
import type { ProviderConfigurationState } from '../../ProviderConfiguration';
|
|
3
3
|
export interface SupabaseAuthConfig {
|
|
4
4
|
url: string;
|
|
@@ -45,4 +45,19 @@ export declare class SupabaseAuthProvider implements AuthProviderAdapter {
|
|
|
45
45
|
signOut(): Promise<void>;
|
|
46
46
|
onAuthChange(callback: (user: UserProfile | null) => void): () => void;
|
|
47
47
|
getAccessToken(): Promise<string>;
|
|
48
|
+
whenReady(): Promise<void>;
|
|
49
|
+
signUp({ email, password, name, redirectTo }: AuthSignUpInput): Promise<AuthSignUpResult>;
|
|
50
|
+
sendPasswordReset(email: string, options?: AuthLinkOptions): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* `code` is the `token_hash` of the recovery email (template `{{ .TokenHash }}`); empty when the link
|
|
53
|
+
* already opened a recovery session (the default template), and then only the password is set.
|
|
54
|
+
*/
|
|
55
|
+
confirmPasswordReset(code: string, newPassword: string): Promise<void>;
|
|
56
|
+
sendEmailVerification(options?: AuthLinkOptions & {
|
|
57
|
+
email?: string;
|
|
58
|
+
}): Promise<void>;
|
|
59
|
+
/** `code` is the `token_hash` of the confirmation email (template `{{ .TokenHash }}`). */
|
|
60
|
+
verifyEmail(code: string): Promise<void>;
|
|
61
|
+
requestEmailCode(email: string, options?: AuthEmailCodeOptions): Promise<void>;
|
|
62
|
+
verifyEmailCode(email: string, code: string): Promise<UserProfile | null>;
|
|
48
63
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CredentialsAdapter } from './credentials/CredentialsProvider';
|
|
2
2
|
import { MockDataProvider } from './data/mock';
|
|
3
|
+
import { type MockAuthProviderOptions } from './auth/mock/MockAuthProvider';
|
|
3
4
|
import type { AIProviderAdapter } from './ai/AIProvider';
|
|
4
5
|
import type { DataProviderAdapter } from './data/DataProvider';
|
|
5
6
|
import type { StorageProviderAdapter } from './storage/StorageProvider';
|
|
@@ -23,6 +24,8 @@ export type GoogleProviderConfig = GoogleOAuth2 & {
|
|
|
23
24
|
export type SupabaseProviderConfig = SupabaseConfig;
|
|
24
25
|
export type MockProviderConfig = {
|
|
25
26
|
data?: ConstructorParameters<typeof MockDataProvider>[0];
|
|
27
|
+
/** Registers `mockAuth` (CR-127), a provider of access with no service behind it: demos and tests only. */
|
|
28
|
+
auth?: MockAuthProviderOptions | boolean;
|
|
26
29
|
};
|
|
27
30
|
export declare const FIREBASE_MANIFEST: DriverManifest<FirebaseConfig>;
|
|
28
31
|
export declare const GOOGLE_MANIFEST: DriverManifest<GoogleProviderConfig>;
|
|
@@ -34,7 +37,7 @@ export declare const AI_MANIFEST: DriverManifest<AIConfig>;
|
|
|
34
37
|
export declare const PROVIDER_MANIFESTS: Record<string, DriverManifest<any>>;
|
|
35
38
|
export type DataDriverName = 'dbRealtime' | 'firestoreDb' | 'supabaseDb' | 'mock';
|
|
36
39
|
export type StorageDriverName = 'firestorage' | 'supabaseStorage';
|
|
37
|
-
export type AuthDriverName = 'googleAuth' | 'firebaseAuth' | 'dropboxAuth' | 'supabaseAuth' | 'githubAuth';
|
|
40
|
+
export type AuthDriverName = 'googleAuth' | 'firebaseAuth' | 'dropboxAuth' | 'supabaseAuth' | 'githubAuth' | 'mockAuth';
|
|
38
41
|
export type EmailDriverName = 'gmail';
|
|
39
42
|
export type AIDriverName = 'openai' | 'openrouter' | 'opencode' | 'opencode-go' | 'openai-compatible' | 'deepseek' | 'gemini' | 'anthropic' | 'mistral' | 'cloudflare' | 'groq';
|
|
40
43
|
export type CredentialsDriverName = 'googleServiceAccount';
|
package/llms-full.txt
CHANGED
|
@@ -248,6 +248,66 @@ await auth.signIn({ method: 'oauth', provider: 'github' })
|
|
|
248
248
|
const token = await auth.getAccessToken('https://www.googleapis.com/auth/gmail.send')
|
|
249
249
|
```
|
|
250
250
|
|
|
251
|
+
### Schermate di accesso e gate senza redirect (CR-127)
|
|
252
|
+
|
|
253
|
+
Le schermate sono pronte: non scriverne a mano. Non conoscono il backend: ricevono le funzioni da
|
|
254
|
+
chiamare (una promessa) e mostrano l'errore lanciato tradotto, mai il testo grezzo del servizio
|
|
255
|
+
(`authErrorMessage`: codici di Firebase e Supabase, `AuthFlowError(reason)` per le funzioni tue).
|
|
256
|
+
Testi nel namespace `access` delle 6 lingue. Ognuna ha `brand`, `toolbar`, `footer`, `bare` (solo il
|
|
257
|
+
contenuto, in una modale), `errorMessage(error)`.
|
|
258
|
+
|
|
259
|
+
| Componente | Cosa fa | Funzioni |
|
|
260
|
+
|---|---|---|
|
|
261
|
+
| `SignIn` | provider esterni, email + password, codice via email, in qualsiasi combinazione | `onPasswordSignIn({email,password})`, `onRequestCode(email)` + `onVerifyCode(email,code)`, `providers` + `onProviderSignIn(id)` |
|
|
262
|
+
| `SignUp` | nome, email, password con i requisiti mentre si scrive, termini; poi "controlla la tua email" | `onSignUp({name,email,password})` → `{ needsEmailVerification }`, `onResendVerification(email)` |
|
|
263
|
+
| `ForgotPassword` | email → stessa risposta con o senza account, nuovo invio dopo un'attesa | `onSendReset(email)` |
|
|
264
|
+
| `ResetPassword` | nuova password dal link dell'email; link scaduto | `onResetPassword(password)`, `invalidLink` |
|
|
265
|
+
| `VerifyEmail` | attivazione: con `code` verifica subito, senza aspetta la conferma | `onVerify(code)`, `onResend()`, `onContinue()` |
|
|
266
|
+
| `VerifyCode` | codice di N cifre in caselle, inviato a codice completo | `onVerify(code)`, `onResend()`, `onChangeEmail()` |
|
|
267
|
+
| `OneTimeCodeInput` | le caselle da sole (incolla, avanti/indietro, `one-time-code`) | `value`, `onChange`, `onComplete` |
|
|
268
|
+
| `AuthLayout` | la cornice di una schermata (marchio, card, titolo, piè di pagina) | — |
|
|
269
|
+
| `AuthGate` | la pagina solo con la sessione; senza, `fallback` al suo posto | `allow(user)`, `denied`, `loading` |
|
|
270
|
+
|
|
271
|
+
```tsx
|
|
272
|
+
import { App, SignIn, AuthFlowError } from '@llmnative/react';
|
|
273
|
+
|
|
274
|
+
// Tutta l'app riservata: senza sessione ogni pagina mostra l'accesso al suo posto, l'indirizzo
|
|
275
|
+
// resta quello aperto e dopo l'accesso compare la pagina. Nessuna pagina /login, nessun redirect.
|
|
276
|
+
<App
|
|
277
|
+
menuConfig={{ main: [...], access: [{ path: '/invito/:token', page: InvitePage, public: true }] }}
|
|
278
|
+
authGate={{
|
|
279
|
+
fallback: <SignIn providers={['google', 'apple']} onProviderSignIn={enter}
|
|
280
|
+
onRequestCode={sendCode} onVerifyCode={checkCode} appName="CRM" />,
|
|
281
|
+
allow: async (user) => (await isMember(user)), // altrimenti "Accesso non consentito"
|
|
282
|
+
}}
|
|
283
|
+
/>
|
|
284
|
+
|
|
285
|
+
// Una funzione tua che rifiuta: la schermata dice "Il codice non è corretto o è scaduto."
|
|
286
|
+
async function checkCode(email: string, code: string) {
|
|
287
|
+
const { token } = await callFunction('verifyLoginCode', { email, code }).catch(() => {
|
|
288
|
+
throw new AuthFlowError('invalidCode');
|
|
289
|
+
});
|
|
290
|
+
await signInWithCustomToken(token);
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Con gli adapter del framework `useAuthFlows(provider?, { verifyEmailPath, resetPasswordPath })` dà le
|
|
295
|
+
funzioni pronte (`<SignIn {...flows.signIn} />`, `flows.signUp`, `flows.forgotPassword`,
|
|
296
|
+
`flows.resetPassword(linkCode())`, `flows.verifyEmail`). Metodi opzionali di `AuthProviderAdapter`:
|
|
297
|
+
|
|
298
|
+
| Metodo | Firebase | Supabase | `mockAuth` |
|
|
299
|
+
|---|---|---|---|
|
|
300
|
+
| `whenReady` (il gate aspetta la sessione) | ✅ | ✅ | ✅ (anche `githubAuth`) |
|
|
301
|
+
| `signUp`, `sendPasswordReset`, `confirmPasswordReset` | ✅ | ✅ | ✅ |
|
|
302
|
+
| `sendEmailVerification`, `verifyEmail` | ✅ | ✅ | ✅ |
|
|
303
|
+
| `requestEmailCode`, `verifyEmailCode` | — (serve una funzione tua) | ✅ | ✅ |
|
|
304
|
+
|
|
305
|
+
`useAuthSession(provider?)` dà `{ user, ready, signOut }`. `linkCode()` legge il codice del link
|
|
306
|
+
(`oobCode`, `token_hash`, `code`). `mockAuth` (`providers.mock.auth: true`) è solo per demo e test:
|
|
307
|
+
qualsiasi email e password, codice `123456`; con `accounts` rifiuta le credenziali sbagliate.
|
|
308
|
+
Link delle email: in Firebase imposta l'URL di azione sulle tue pagine (`/reset-password`,
|
|
309
|
+
`/verify-email`); in Supabase usa `{{ .TokenHash }}` nei template delle email.
|
|
310
|
+
|
|
251
311
|
### CredentialsAdapter (Google Service Account)
|
|
252
312
|
```tsx
|
|
253
313
|
// Chiama Google APIs lato browser senza Node.js
|
|
@@ -1466,6 +1526,7 @@ src/
|
|
|
1466
1526
|
conf/
|
|
1467
1527
|
app.ts ← providers config letta da .env (VITE_PROVIDER, VITE_AI_PROVIDER...)
|
|
1468
1528
|
menu.ts ← MenuConfig con path, title, icon, page (layout opzionale: c'è LayoutDefault)
|
|
1529
|
+
access.tsx ← rotte pubbliche delle pagine di accesso (CR-127)
|
|
1469
1530
|
data/
|
|
1470
1531
|
mockData.ts ← dati fake per MockDataProvider
|
|
1471
1532
|
layouts/
|
|
@@ -1474,6 +1535,7 @@ src/
|
|
|
1474
1535
|
Header.tsx / Sidebar.tsx / PageHeader.tsx / Footer.tsx ← solo componenti pubblici (SideNav, AuthButton, ActionButton, Brand, Breadcrumbs)
|
|
1475
1536
|
pages/
|
|
1476
1537
|
home/HomePage.tsx
|
|
1538
|
+
access/ ← SignInPage (il fallback del gate), SignUp, ForgotPassword, ResetPassword, VerifyEmail
|
|
1477
1539
|
[altre pagine dal template scelto]
|
|
1478
1540
|
styles/
|
|
1479
1541
|
globals.css ← @import '@llmnative/react/dist/index.css' + Tailwind override
|
|
@@ -1491,11 +1553,12 @@ const env = import.meta.env
|
|
|
1491
1553
|
export const providers: AppProvidersConfig = {
|
|
1492
1554
|
mock: { data: mockData },
|
|
1493
1555
|
ai: { openaiApiKey: env.VITE_OPENAI_API_KEY ?? '' }, // AI dentro providers, non prop <App>
|
|
1494
|
-
|
|
1556
|
+
mock: { data: mockData, auth: true }, // mockAuth: accesso di prova
|
|
1557
|
+
services: { data: 'mock', auth: 'mockAuth' },
|
|
1495
1558
|
}
|
|
1496
1559
|
```
|
|
1497
1560
|
|
|
1498
|
-
Driver per `VITE_PROVIDER`: `firebase` → `firestoreDb` + `firestorage` + `firebaseAuth`; `supabase` → `supabaseDb` + `supabaseStorage` + `supabaseAuth`; `mock` → `mock` + `
|
|
1561
|
+
Driver per `VITE_PROVIDER`: `firebase` → `firestoreDb` + `firestorage` + `firebaseAuth`; `supabase` → `supabaseDb` + `supabaseStorage` + `supabaseAuth`; `mock` → `mock` + `mockAuth`. `index.tsx` passa `LayoutDefault={Default}`, le traduzioni del framework (`translations: { ...locales }`) e `authGate={{ fallback: <SignInPage /> }}`: senza sessione ogni pagina mostra l'accesso al suo posto. Le pagine di accesso (`pages/access/`: SignInPage, SignUpPage, ForgotPasswordPage, ResetPasswordPage, VerifyEmailPage) e le loro rotte pubbliche (`conf/access.tsx`) sono uguali per ogni template; per un'app aperta a tutti togli `authGate`.
|
|
1499
1562
|
|
|
1500
1563
|
---
|
|
1501
1564
|
|
|
@@ -1507,6 +1570,7 @@ Driver per `VITE_PROVIDER`: `firebase` → `firestoreDb` + `firestorage` + `fire
|
|
|
1507
1570
|
| `inputType` o `Input.Number`/`Input.Email` non esistono | Il tipo HTML è la prop `type` | `<Input type="number">`, `<Input type="email">`, `<Input type="date">` |
|
|
1508
1571
|
| `Menu context=` non esiste | La prop si chiama `menuKey` | `<Menu menuKey="main" />` |
|
|
1509
1572
|
| `SignInButton` non esiste | Rinominato | `<AuthButton />` |
|
|
1573
|
+
| Pagina `/login` con redirect, form di accesso fatti a mano | Le schermate e il gate ci sono (CR-127) | `<App authGate={{ fallback: <SignIn … /> }}>`, rotte aperte con `public: true` |
|
|
1510
1574
|
| `firebase: { config: ... }` errato | FirebaseConfig è flat | `firebase: { apiKey, authDomain, databaseURL, ... }` |
|
|
1511
1575
|
| `google: { oAuth2: { clientId } }` errato | GoogleProviderConfig è flat | `google: { clientId, scope }` |
|
|
1512
1576
|
| `children` su `Grid` | Grid non accetta children | Usa `form={<Fields />}` per il form inline |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llmnative/react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "LLM-native React framework for deterministic UI generation. Data-driven by default, schema-driven optional. Ultra-low token consumption — built for AI agents, not just humans.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|