@ankhorage/studio 2.0.1 → 2.0.3
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/README.md +1 -1
- package/dist/host/layout/auth/resolveAuthLayoutPlan.d.ts +1 -1
- package/dist/host/layout/auth/resolveAuthLayoutPlan.d.ts.map +1 -1
- package/dist/host/layout/auth/resolveAuthLayoutPlan.js +24 -1
- package/dist/host/layout/auth/resolveAuthLayoutPlan.js.map +1 -1
- package/dist/host/layout/layoutGenerator.d.ts.map +1 -1
- package/dist/host/layout/layoutGenerator.js +45 -20
- package/dist/host/layout/layoutGenerator.js.map +1 -1
- package/dist/host/layout/templates/auth/adapter.js +1 -1
- package/dist/host/layout/templates/auth/adapter.js.map +1 -1
- package/dist/host/layout/templates/auth/callback.d.ts.map +1 -1
- package/dist/host/layout/templates/auth/callback.js +80 -42
- package/dist/host/layout/templates/auth/callback.js.map +1 -1
- package/dist/host/layout/templates/auth/form.d.ts +2 -0
- package/dist/host/layout/templates/auth/form.d.ts.map +1 -0
- package/dist/host/layout/templates/auth/form.js +238 -0
- package/dist/host/layout/templates/auth/form.js.map +1 -0
- package/dist/host/layout/templates/auth/navigation.d.ts +10 -0
- package/dist/host/layout/templates/auth/navigation.d.ts.map +1 -0
- package/dist/host/layout/templates/auth/navigation.js +183 -0
- package/dist/host/layout/templates/auth/navigation.js.map +1 -0
- package/dist/host/layout/templates/auth/oauth.d.ts.map +1 -1
- package/dist/host/layout/templates/auth/oauth.js +146 -217
- package/dist/host/layout/templates/auth/oauth.js.map +1 -1
- package/dist/host/layout/templates/auth/oauthCompletion.d.ts +6 -0
- package/dist/host/layout/templates/auth/oauthCompletion.d.ts.map +1 -0
- package/dist/host/layout/templates/auth/oauthCompletion.js +147 -0
- package/dist/host/layout/templates/auth/oauthCompletion.js.map +1 -0
- package/dist/host/layout/templates/auth/oauthState.d.ts +2 -0
- package/dist/host/layout/templates/auth/oauthState.d.ts.map +1 -0
- package/dist/host/layout/templates/auth/oauthState.js +45 -0
- package/dist/host/layout/templates/auth/oauthState.js.map +1 -0
- package/dist/host/layout/templates/auth/screen.d.ts +5 -2
- package/dist/host/layout/templates/auth/screen.d.ts.map +1 -1
- package/dist/host/layout/templates/auth/screen.js +296 -531
- package/dist/host/layout/templates/auth/screen.js.map +1 -1
- package/dist/host/layout/templates/auth/session.js +6 -6
- package/dist/host/layout/templates/auth/signOut.d.ts.map +1 -1
- package/dist/host/layout/templates/auth/signOut.js +10 -9
- package/dist/host/layout/templates/auth/signOut.js.map +1 -1
- package/dist/host/layout/templates/index.d.ts +5 -1
- package/dist/host/layout/templates/index.d.ts.map +1 -1
- package/dist/host/layout/templates/index.js +5 -1
- package/dist/host/layout/templates/index.js.map +1 -1
- package/dist/host/layout/templates/rootLayout.d.ts.map +1 -1
- package/dist/host/layout/templates/rootLayout.js +9 -149
- package/dist/host/layout/templates/rootLayout.js.map +1 -1
- package/dist/host/orchestrator/generatedAppFiles.d.ts.map +1 -1
- package/dist/host/orchestrator/generatedAppFiles.js +6 -4
- package/dist/host/orchestrator/generatedAppFiles.js.map +1 -1
- package/dist/host/orchestrator/generatedRouteCleanup.js +1 -1
- package/dist/host/orchestrator/generatedRouteCleanup.js.map +1 -1
- package/dist/host/orchestrator/scaffolder.js +3 -3
- package/dist/host/orchestrator/scaffolder.js.map +1 -1
- package/dist/host/orchestrator/templates.d.ts.map +1 -1
- package/dist/host/orchestrator/templates.js +54 -52
- package/dist/host/orchestrator/templates.js.map +1 -1
- package/dist/projectDeployAssetPicker.d.ts +2 -1
- package/dist/projectDeployAssetPicker.d.ts.map +1 -1
- package/dist/projectDeployAssetPicker.js +27 -21
- package/dist/projectDeployAssetPicker.js.map +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
export function getAuthFormTs() {
|
|
2
|
+
return `import type { AuthIdentifier, AuthSession } from '@ankhorage/contracts/auth';
|
|
3
|
+
import { isEmail, isPhone, isUsername } from '@ankhorage/utility/regex';
|
|
4
|
+
import type { AuthIdentifierKind, SignUpFormField, SignUpFormValues } from '@ankhorage/zora';
|
|
5
|
+
|
|
6
|
+
export interface AuthSubmitValues {
|
|
7
|
+
mode: 'signIn' | 'signUp';
|
|
8
|
+
identifier: string;
|
|
9
|
+
password: string;
|
|
10
|
+
firstName: string;
|
|
11
|
+
lastName: string;
|
|
12
|
+
displayName: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface IdentifierFieldDefinition {
|
|
16
|
+
helper: string;
|
|
17
|
+
keyboardType: 'default' | 'email-address' | 'phone-pad';
|
|
18
|
+
label: string;
|
|
19
|
+
placeholder: string;
|
|
20
|
+
type: 'email' | 'tel' | 'text';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const AUTH_IDENTIFIER_VALIDATORS = [
|
|
24
|
+
{ kind: 'email', label: 'email address', matches: isEmail },
|
|
25
|
+
{ kind: 'phone', label: 'phone number', matches: isPhone },
|
|
26
|
+
{ kind: 'username', label: 'username', matches: isUsername },
|
|
27
|
+
] as const;
|
|
28
|
+
|
|
29
|
+
const OPTIONAL_SIGN_UP_FIELDS = [
|
|
30
|
+
{ key: 'firstname', name: 'firstName', label: 'First name' },
|
|
31
|
+
{ key: 'lastname', name: 'lastName', label: 'Last name' },
|
|
32
|
+
{ key: 'displayname', name: 'displayName', label: 'Display name' },
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
35
|
+
export function buildAuthIdentifierInput(
|
|
36
|
+
identifier: string,
|
|
37
|
+
identifiers: readonly string[],
|
|
38
|
+
): AuthIdentifier | null {
|
|
39
|
+
const normalizedIdentifier = identifier.trim();
|
|
40
|
+
const resolvedIdentifiers = resolveAuthIdentifiers(identifiers);
|
|
41
|
+
const match = AUTH_IDENTIFIER_VALIDATORS.find(
|
|
42
|
+
(candidate) =>
|
|
43
|
+
resolvedIdentifiers.includes(candidate.kind) && candidate.matches(normalizedIdentifier),
|
|
44
|
+
);
|
|
45
|
+
if (match) return { kind: match.kind, value: normalizedIdentifier };
|
|
46
|
+
const [fallbackKind] = resolvedIdentifiers;
|
|
47
|
+
return fallbackKind ? { kind: fallbackKind, value: normalizedIdentifier } : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function buildSignUpFields(args: {
|
|
51
|
+
identifierField: IdentifierFieldDefinition;
|
|
52
|
+
configuredFields: readonly string[];
|
|
53
|
+
requiredFields: readonly string[];
|
|
54
|
+
}): SignUpFormField[] {
|
|
55
|
+
const { identifierField, configuredFields, requiredFields } = args;
|
|
56
|
+
const fields: SignUpFormField[] = [
|
|
57
|
+
{
|
|
58
|
+
name: 'identifier',
|
|
59
|
+
label: identifierField.label,
|
|
60
|
+
helperText: identifierField.helper,
|
|
61
|
+
placeholder: identifierField.placeholder,
|
|
62
|
+
type: identifierField.type,
|
|
63
|
+
autoCapitalize: 'none',
|
|
64
|
+
keyboardType: identifierField.keyboardType,
|
|
65
|
+
required: ['email', 'phone', 'username'].some((field) =>
|
|
66
|
+
hasConfiguredSignUpField(requiredFields, field),
|
|
67
|
+
),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: 'password',
|
|
71
|
+
label: 'Password',
|
|
72
|
+
type: 'password',
|
|
73
|
+
required: hasConfiguredSignUpField(requiredFields, 'password'),
|
|
74
|
+
},
|
|
75
|
+
];
|
|
76
|
+
for (const definition of OPTIONAL_SIGN_UP_FIELDS) {
|
|
77
|
+
if (!hasConfiguredSignUpField(configuredFields, definition.key)) continue;
|
|
78
|
+
fields.push({
|
|
79
|
+
name: definition.name,
|
|
80
|
+
label: definition.label,
|
|
81
|
+
type: 'text',
|
|
82
|
+
required: hasConfiguredSignUpField(requiredFields, definition.key),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return fields;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function buildSignUpProfile(args: {
|
|
89
|
+
firstName: string;
|
|
90
|
+
lastName: string;
|
|
91
|
+
displayName: string;
|
|
92
|
+
}): Record<string, string> | undefined {
|
|
93
|
+
const candidates: [string, string][] = [
|
|
94
|
+
['firstName', args.firstName.trim()],
|
|
95
|
+
['lastName', args.lastName.trim()],
|
|
96
|
+
['displayName', args.displayName.trim()],
|
|
97
|
+
];
|
|
98
|
+
const entries = candidates.filter(([, value]) => value.length > 0);
|
|
99
|
+
return entries.length > 0 ? Object.fromEntries(entries) : undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function getFormValue(values: SignUpFormValues, name: string): string {
|
|
103
|
+
const value: unknown = Reflect.get(values, name);
|
|
104
|
+
return typeof value === 'string' ? value : '';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function isAuthSession(value: unknown): value is AuthSession {
|
|
108
|
+
if (!isRecord(value)) return false;
|
|
109
|
+
const { accessToken, user } = value;
|
|
110
|
+
if (typeof accessToken !== 'string' || accessToken.length === 0 || !isRecord(user)) return false;
|
|
111
|
+
return typeof user.id === 'string' && user.id.length > 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function resolveAuthIdentifiers(identifiers: readonly string[]): AuthIdentifierKind[] {
|
|
115
|
+
const resolved = identifiers
|
|
116
|
+
.map((identifier) => identifier.trim().toLowerCase())
|
|
117
|
+
.filter(isAuthIdentifierKind);
|
|
118
|
+
const unique = [...new Set(resolved)];
|
|
119
|
+
return unique.length > 0 ? unique : ['email'];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function resolveIdentifierFieldDefinition(
|
|
123
|
+
identifiers: readonly string[],
|
|
124
|
+
): IdentifierFieldDefinition {
|
|
125
|
+
const supported = new Set(resolveAuthIdentifiers(identifiers));
|
|
126
|
+
if (supported.size === 1 && supported.has('email')) {
|
|
127
|
+
return createIdentifierField(
|
|
128
|
+
'Use your email to continue.',
|
|
129
|
+
'email-address',
|
|
130
|
+
'Email',
|
|
131
|
+
'hello@example.com',
|
|
132
|
+
'email',
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
if (supported.size === 1 && supported.has('phone')) {
|
|
136
|
+
return createIdentifierField(
|
|
137
|
+
'Use your phone to continue.',
|
|
138
|
+
'phone-pad',
|
|
139
|
+
'Phone',
|
|
140
|
+
'+1 555 123 4567',
|
|
141
|
+
'tel',
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
if (supported.size === 1 && supported.has('username')) {
|
|
145
|
+
return createIdentifierField(
|
|
146
|
+
'Use your username to continue.',
|
|
147
|
+
'default',
|
|
148
|
+
'Username',
|
|
149
|
+
'your-username',
|
|
150
|
+
'text',
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return createIdentifierField(
|
|
154
|
+
'Use your configured identifier to continue.',
|
|
155
|
+
'default',
|
|
156
|
+
supported.has('username') ? 'Identifier' : 'Email or phone',
|
|
157
|
+
'Email or phone',
|
|
158
|
+
'text',
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function validateAuthSubmitValues(
|
|
163
|
+
values: AuthSubmitValues,
|
|
164
|
+
identifiers: readonly string[],
|
|
165
|
+
requiredFields: readonly string[],
|
|
166
|
+
): string | null {
|
|
167
|
+
const identifier = values.identifier.trim();
|
|
168
|
+
if (!identifier || values.password.length === 0)
|
|
169
|
+
return 'Enter both credentials before continuing.';
|
|
170
|
+
const identifierError = validateIdentifier(identifier, identifiers);
|
|
171
|
+
if (identifierError) return identifierError;
|
|
172
|
+
if (values.password.length < 6) return 'Password must be at least 6 characters.';
|
|
173
|
+
if (values.mode !== 'signUp') return null;
|
|
174
|
+
const missing = requiredFields.flatMap((field) =>
|
|
175
|
+
isRequiredFieldMissing(field, values) ? [fieldLabel(field)] : [],
|
|
176
|
+
);
|
|
177
|
+
return missing.length > 0 ? \`Complete required fields: \${missing.join(', ')}.\` : null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function createIdentifierField(
|
|
181
|
+
helper: string,
|
|
182
|
+
keyboardType: IdentifierFieldDefinition['keyboardType'],
|
|
183
|
+
label: string,
|
|
184
|
+
placeholder: string,
|
|
185
|
+
type: IdentifierFieldDefinition['type'],
|
|
186
|
+
): IdentifierFieldDefinition {
|
|
187
|
+
return { helper, keyboardType, label, placeholder, type };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function fieldLabel(field: string): string {
|
|
191
|
+
const normalized = field.trim().toLowerCase();
|
|
192
|
+
const definition = OPTIONAL_SIGN_UP_FIELDS.find((candidate) => candidate.key === normalized);
|
|
193
|
+
return definition?.label.toLowerCase() ?? normalized;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function formatList(values: readonly string[]): string {
|
|
197
|
+
if (values.length < 2) return values[0] ?? 'identifier';
|
|
198
|
+
if (values.length === 2) return \`\${values[0]} or \${values[1]}\`;
|
|
199
|
+
return \`\${values.slice(0, -1).join(', ')}, or \${values.at(-1)}\`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function hasConfiguredSignUpField(fields: readonly string[], field: string): boolean {
|
|
203
|
+
return fields.some((value) => value.trim().toLowerCase() === field);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function isAuthIdentifierKind(value: string): value is AuthIdentifierKind {
|
|
207
|
+
return value === 'email' || value === 'phone' || value === 'username';
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
211
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function isRequiredFieldMissing(field: string, values: AuthSubmitValues): boolean {
|
|
215
|
+
const normalized = field.trim().toLowerCase();
|
|
216
|
+
if (normalized === 'email' || normalized === 'username' || normalized === 'phone') {
|
|
217
|
+
return values.identifier.trim().length === 0;
|
|
218
|
+
}
|
|
219
|
+
if (normalized === 'password') return values.password.length === 0;
|
|
220
|
+
const optional = OPTIONAL_SIGN_UP_FIELDS.find((candidate) => candidate.key === normalized);
|
|
221
|
+
if (!optional) return false;
|
|
222
|
+
const value: unknown = Reflect.get(values, optional.name);
|
|
223
|
+
return typeof value !== 'string' || value.trim().length === 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function validateIdentifier(identifier: string, identifiers: readonly string[]): string | null {
|
|
227
|
+
const supported = new Set(resolveAuthIdentifiers(identifiers));
|
|
228
|
+
const allowed = AUTH_IDENTIFIER_VALIDATORS.filter((validator) => supported.has(validator.kind));
|
|
229
|
+
if (allowed.some((validator) => validator.matches(identifier))) return null;
|
|
230
|
+
const labels = allowed.map((validator) => validator.label);
|
|
231
|
+
if (labels.length === 1 && labels[0] === 'username') {
|
|
232
|
+
return 'Username must be at least 3 characters and use letters, numbers, dot, underscore, or dash.';
|
|
233
|
+
}
|
|
234
|
+
return \`Use a valid \${formatList(labels)}.\`;
|
|
235
|
+
}
|
|
236
|
+
`;
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=form.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"form.js","sourceRoot":"","sources":["../../../../../src/host/layout/templates/auth/form.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,aAAa;IAC3B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0OR,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface GetAuthNavigationTsArgs {
|
|
2
|
+
signInRoute: string;
|
|
3
|
+
signInRouteName: string;
|
|
4
|
+
signUpRouteName: string;
|
|
5
|
+
postSignInRoute: string;
|
|
6
|
+
publicRoutes: string[];
|
|
7
|
+
}
|
|
8
|
+
export declare function getAuthNavigationTs(args: GetAuthNavigationTsArgs): string;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=navigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../../../../src/host/layout/templates/auth/navigation.ts"],"names":[],"mappings":"AAGA,UAAU,uBAAuB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAMD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,uBAAuB,GAAG,MAAM,CAgLzE"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { escapeStringLiteral } from '../../utils/escapeStringLiteral';
|
|
2
|
+
import { routeNameToGroupedHref, routeNameToHref } from '../utils/routes';
|
|
3
|
+
function serializeStringArrayLiteral(values) {
|
|
4
|
+
return `[${values.map((value) => `'${escapeStringLiteral(value)}'`).join(', ')}]`;
|
|
5
|
+
}
|
|
6
|
+
export function getAuthNavigationTs(args) {
|
|
7
|
+
return `import { type Href, usePathname, useRootNavigationState, useRouter } from 'expo-router';
|
|
8
|
+
import { useCallback, useEffect, useState, useSyncExternalStore } from 'react';
|
|
9
|
+
import { AppState } from 'react-native';
|
|
10
|
+
|
|
11
|
+
import { authAdapter } from './adapter';
|
|
12
|
+
import {
|
|
13
|
+
bootstrapAuthSession,
|
|
14
|
+
getStoredAuthSession,
|
|
15
|
+
isAuthenticated,
|
|
16
|
+
refreshAuthSessionIfNeeded,
|
|
17
|
+
subscribeToAuthSessionChanges,
|
|
18
|
+
} from './session';
|
|
19
|
+
|
|
20
|
+
const AUTH_SIGN_IN_ROUTE_SEGMENT = '${escapeStringLiteral(args.signInRouteName)}';
|
|
21
|
+
const AUTH_SIGN_UP_ROUTE_SEGMENT = '${escapeStringLiteral(args.signUpRouteName)}';
|
|
22
|
+
const AUTH_SIGN_IN_ROUTE_PATH = '${escapeStringLiteral(routeNameToHref(args.signInRoute))}';
|
|
23
|
+
const AUTH_SIGN_IN_ROUTE_TARGET: Href = '${escapeStringLiteral(routeNameToGroupedHref(args.signInRoute, 'auth'))}';
|
|
24
|
+
const AUTH_POST_SIGN_IN_ROUTE_PATH = '${escapeStringLiteral(routeNameToHref(args.postSignInRoute))}';
|
|
25
|
+
const AUTH_POST_SIGN_IN_ROUTE_TARGET: Href = '${escapeStringLiteral(routeNameToGroupedHref(args.postSignInRoute, 'app'))}';
|
|
26
|
+
const AUTH_PUBLIC_ROUTES = ${serializeStringArrayLiteral(args.publicRoutes)};
|
|
27
|
+
const AUTH_DISABLE_IN_DEV = process.env.EXPO_PUBLIC_ANKH_AUTH_DISABLE_IN_DEV === 'true';
|
|
28
|
+
|
|
29
|
+
export type GeneratedAuthNavigationState = 'pending' | 'unauthenticated' | 'authenticated';
|
|
30
|
+
|
|
31
|
+
export function useGeneratedAuthNavigation(
|
|
32
|
+
options: { isRouteGuardDisabled?: (pathname: string) => boolean } = {},
|
|
33
|
+
) {
|
|
34
|
+
const router = useRouter();
|
|
35
|
+
const rootNavigationKey = getRootNavigationKey(useRootNavigationState());
|
|
36
|
+
const pathname = usePathname();
|
|
37
|
+
const session = useSyncExternalStore(
|
|
38
|
+
subscribeToAuthSessionChanges,
|
|
39
|
+
getStoredAuthSession,
|
|
40
|
+
getStoredAuthSession,
|
|
41
|
+
);
|
|
42
|
+
const isAuthRuntimeReady = useGeneratedAuthRuntimeReady();
|
|
43
|
+
const [isInnerContentReady, setIsInnerContentReady] = useState(false);
|
|
44
|
+
const authenticated = session !== null && isAuthenticated();
|
|
45
|
+
const authState = resolveAuthNavigationState(isAuthRuntimeReady, authenticated);
|
|
46
|
+
useRefreshAuthSessionOnActive();
|
|
47
|
+
useAuthRouteGuard({
|
|
48
|
+
authenticated,
|
|
49
|
+
isAuthRuntimeReady,
|
|
50
|
+
isInnerContentReady,
|
|
51
|
+
isRouteGuardDisabled: options.isRouteGuardDisabled,
|
|
52
|
+
pathname,
|
|
53
|
+
rootNavigationKey,
|
|
54
|
+
router,
|
|
55
|
+
});
|
|
56
|
+
const handleInnerContentReady = useCallback(() => setIsInnerContentReady(true), []);
|
|
57
|
+
return { authState, handleInnerContentReady, isAuthRuntimeReady, pathname };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function shouldMountAuthenticatedAppHeader(
|
|
61
|
+
pathname: string,
|
|
62
|
+
isAuthRuntimeReady: boolean,
|
|
63
|
+
): boolean {
|
|
64
|
+
if (!isGeneratedAuthEnforced()) return true;
|
|
65
|
+
if (!isAuthRuntimeReady || !isAuthenticated()) return false;
|
|
66
|
+
return !isAuthRoute(pathname);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function useGeneratedAuthRuntimeReady(): boolean {
|
|
70
|
+
const [ready, setReady] = useState(false);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const controller = new AbortController();
|
|
73
|
+
void initializeGeneratedAuthRuntime().then(() => {
|
|
74
|
+
if (!controller.signal.aborted) setReady(true);
|
|
75
|
+
});
|
|
76
|
+
return () => controller.abort();
|
|
77
|
+
}, []);
|
|
78
|
+
return ready;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function initializeGeneratedAuthRuntime(): Promise<void> {
|
|
82
|
+
try {
|
|
83
|
+
await bootstrapAuthSession();
|
|
84
|
+
await refreshAuthSessionIfNeeded(authAdapter);
|
|
85
|
+
} catch {
|
|
86
|
+
// Storage or adapter failures resolve to the unauthenticated route instead of a blank shell.
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function useRefreshAuthSessionOnActive(): void {
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
const subscription = AppState.addEventListener('change', (nextState) => {
|
|
93
|
+
if (nextState === 'active') {
|
|
94
|
+
void refreshAuthSessionIfNeeded(authAdapter).catch(() => undefined);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
return () => subscription.remove();
|
|
98
|
+
}, []);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function resolveAuthNavigationState(
|
|
102
|
+
ready: boolean,
|
|
103
|
+
authenticated: boolean,
|
|
104
|
+
): GeneratedAuthNavigationState {
|
|
105
|
+
if (!isGeneratedAuthEnforced()) return 'authenticated';
|
|
106
|
+
if (!ready) return 'pending';
|
|
107
|
+
return authenticated ? 'authenticated' : 'unauthenticated';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface AuthRouteGuardInput {
|
|
111
|
+
authenticated: boolean;
|
|
112
|
+
isAuthRuntimeReady: boolean;
|
|
113
|
+
isInnerContentReady: boolean;
|
|
114
|
+
isRouteGuardDisabled?: (pathname: string) => boolean;
|
|
115
|
+
pathname: string;
|
|
116
|
+
rootNavigationKey: string;
|
|
117
|
+
router: ReturnType<typeof useRouter>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function useAuthRouteGuard(args: AuthRouteGuardInput): void {
|
|
121
|
+
const { router } = args;
|
|
122
|
+
const target = resolveAuthRouteGuardTarget(args);
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
if (target) router.replace(target);
|
|
125
|
+
}, [router, target]);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function resolveAuthRouteGuardTarget(args: AuthRouteGuardInput): Href | null {
|
|
129
|
+
if (
|
|
130
|
+
!args.isInnerContentReady ||
|
|
131
|
+
!args.isAuthRuntimeReady ||
|
|
132
|
+
args.rootNavigationKey.length === 0
|
|
133
|
+
) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
if (args.isRouteGuardDisabled?.(args.pathname) || !isGeneratedAuthEnforced()) return null;
|
|
137
|
+
|
|
138
|
+
const activeTopLevelRoute = getTopLevelRoute(args.pathname);
|
|
139
|
+
const currentPath = normalizeRoutePath(args.pathname);
|
|
140
|
+
const postSignInPath = normalizeRoutePath(AUTH_POST_SIGN_IN_ROUTE_PATH);
|
|
141
|
+
if (!args.authenticated) {
|
|
142
|
+
return !AUTH_PUBLIC_ROUTES.includes(activeTopLevelRoute) &&
|
|
143
|
+
currentPath !== normalizeRoutePath(AUTH_SIGN_IN_ROUTE_PATH)
|
|
144
|
+
? AUTH_SIGN_IN_ROUTE_TARGET
|
|
145
|
+
: null;
|
|
146
|
+
}
|
|
147
|
+
if (currentPath === '/' && postSignInPath !== '/') return AUTH_POST_SIGN_IN_ROUTE_TARGET;
|
|
148
|
+
const isAuthEntry =
|
|
149
|
+
activeTopLevelRoute === AUTH_SIGN_IN_ROUTE_SEGMENT ||
|
|
150
|
+
activeTopLevelRoute === AUTH_SIGN_UP_ROUTE_SEGMENT;
|
|
151
|
+
return isAuthEntry && currentPath !== postSignInPath ? AUTH_POST_SIGN_IN_ROUTE_TARGET : null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function isGeneratedAuthEnforced(): boolean {
|
|
155
|
+
return !__DEV__ || !AUTH_DISABLE_IN_DEV;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function isAuthRoute(pathname: string): boolean {
|
|
159
|
+
const activeTopLevelRoute = getTopLevelRoute(pathname);
|
|
160
|
+
return (
|
|
161
|
+
activeTopLevelRoute === AUTH_SIGN_IN_ROUTE_SEGMENT ||
|
|
162
|
+
activeTopLevelRoute === AUTH_SIGN_UP_ROUTE_SEGMENT
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function normalizeRoutePath(pathname: string): string {
|
|
167
|
+
const normalized = pathname.replace(/\\\/+$/, '');
|
|
168
|
+
return normalized === '' ? '/' : normalized;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function getTopLevelRoute(pathname: string): string {
|
|
172
|
+
const normalized = normalizeRoutePath(pathname);
|
|
173
|
+
if (normalized === '/') return 'index';
|
|
174
|
+
const [, topLevelRoute = 'index'] = normalized.split('/');
|
|
175
|
+
return topLevelRoute;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function getRootNavigationKey(state: { key?: string } | null | undefined): string {
|
|
179
|
+
return state?.key ?? '';
|
|
180
|
+
}
|
|
181
|
+
`;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=navigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../../../../../src/host/layout/templates/auth/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAU1E,SAAS,2BAA2B,CAAC,MAAyB;IAC5D,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,mBAAmB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAA6B;IAC/D,OAAO;;;;;;;;;;;;;sCAa6B,mBAAmB,CAAC,IAAI,CAAC,eAAe,CAAC;sCACzC,mBAAmB,CAAC,IAAI,CAAC,eAAe,CAAC;mCAC5C,mBAAmB,CAAC,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;2CAC9C,mBAAmB,CAAC,sBAAsB,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;wCACxE,mBAAmB,CAAC,eAAe,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;gDAClD,mBAAmB,CAAC,sBAAsB,CAAC,IAAI,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;6BAC3F,2BAA2B,CAAC,IAAI,CAAC,YAAY,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2J1E,CAAC;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../../../../src/host/layout/templates/auth/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAEnF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAG5E,UAAU,4BAA6B,SAAQ,mBAAmB;IAChE,QAAQ,CAAC,aAAa,EAAE,0BAA0B,CAAC;CACpD;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,4BAA4B,
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../../../../src/host/layout/templates/auth/oauth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAEnF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAG5E,UAAU,4BAA6B,SAAQ,mBAAmB;IAChE,QAAQ,CAAC,aAAa,EAAE,0BAA0B,CAAC;CACpD;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,4BAA4B,UA4SvE"}
|