Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 35 additions & 27 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -985,11 +985,7 @@ Custom Token Exchange allows you to exchange external identity provider tokens f
```typescript
import React from 'react';
import { Button, Alert } from 'react-native';
import {
useAuth0,
AuthenticationException,
AuthenticationErrorCodes,
} from 'react-native-auth0';
import { useAuth0, AuthError } from 'react-native-auth0';

function TokenExchangeScreen() {
const { customTokenExchange, user, error } = useAuth0();
Expand All @@ -1006,25 +1002,25 @@ function TokenExchangeScreen() {

Alert.alert('Success', `Logged in as ${user?.name}`);
} catch (e) {
if (e instanceof AuthenticationException) {
switch (e.type) {
case AuthenticationErrorCodes.INVALID_SUBJECT_TOKEN:
Alert.alert('Error', 'The external token is invalid or expired');
if (e instanceof AuthError) {
// Custom Token Exchange surfaces the OAuth 2.0 error from the token
// endpoint on `code`. See the RFC 8693 error responses and your Action's
// own failure reasons.
switch (e.code) {
case 'invalid_request':
Alert.alert('Error', 'The external token or token type is invalid');
break;
case AuthenticationErrorCodes.UNSUPPORTED_TOKEN_TYPE:
Alert.alert('Error', 'The token type is not supported');
case 'invalid_grant':
Alert.alert('Error', 'The external token was rejected or expired');
break;
case AuthenticationErrorCodes.TOKEN_EXCHANGE_NOT_CONFIGURED:
case 'unsupported_grant_type':
Alert.alert(
'Error',
'Custom Token Exchange is not configured for this tenant'
'Custom Token Exchange is not enabled for this tenant'
);
break;
case AuthenticationErrorCodes.TOKEN_VALIDATION_FAILED:
Alert.alert('Error', 'Token validation failed in Auth0 Action');
break;
case AuthenticationErrorCodes.NETWORK_ERROR:
Alert.alert('Error', 'Network error. Please check your connection.');
case 'access_denied':
Alert.alert('Error', 'Token validation failed in the Auth0 Action');
Comment on lines +1005 to +1023

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Align the handler with the documented Custom Token Exchange codes.

This handler checks unsupported_grant_type. The reference at lines 1209-1226 lists unsupported_token_type and unauthorized_client instead. The documented alerts for unsupported token types and disabled clients will not run for the listed codes.

Align the handler and reference with the same emitted code set.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@EXAMPLES.md` around lines 1005 - 1023, Update the AuthError switch in the
Custom Token Exchange handler to use the documented emitted codes consistently:
replace unsupported_grant_type with unsupported_token_type and add
unauthorized_client with the corresponding documented disabled-client alert.
Ensure the reference documentation uses the same code set and preserves the
existing access_denied handling.

break;
default:
Alert.alert('Error', e.message);
Expand All @@ -1042,10 +1038,7 @@ function TokenExchangeScreen() {
### Using Custom Token Exchange with Auth0 Class

```typescript
import Auth0, {
AuthenticationException,
AuthenticationErrorCodes,
} from 'react-native-auth0';
import Auth0, { AuthError } from 'react-native-auth0';

const auth0 = new Auth0({
domain: 'YOUR_AUTH0_DOMAIN',
Expand All @@ -1064,14 +1057,14 @@ async function exchangeExternalToken(externalToken: string) {
console.log('Exchange successful:', credentials);
return credentials;
} catch (error) {
if (error instanceof AuthenticationException) {
if (error instanceof AuthError) {
// Access the underlying error details
console.error('Error type:', error.type);
console.error('Error code:', error.code);
console.error('Error message:', error.message);
console.error('Underlying error code:', error.underlyingError.code);
console.error('HTTP status:', error.status);

// Handle specific error types
if (error.type === AuthenticationErrorCodes.INVALID_SUBJECT_TOKEN) {
// Handle specific error codes
if (error.code === 'invalid_grant') {
// Token is invalid or expired - prompt user to re-authenticate
throw new Error('Please authenticate again with the external provider');
}
Expand Down Expand Up @@ -1849,6 +1842,21 @@ try {
}
```

The My Account API reports failures as [RFC 7807](https://datatracker.ietf.org/doc/html/rfc7807)
type URIs. `MyAccountError` normalizes those to a `MyAccountErrorCodes` value on `type` so your
error handling matches every other error class in the SDK, and preserves the original URI on
`typeUri` for logging or support tickets:

```typescript
catch (e) {
if (e instanceof MyAccountError) {
console.log(e.type); // "UNAUTHORIZED" — normalized, switch on this
console.log(e.typeUri); // "https://auth0.com/api-errors/A0E-401" — raw, log this
console.log(e.statusCode); // 401
}
}
```

### Platform Support

| Platform | Support | Notes |
Expand Down
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,60 @@ The options for configuring the display of local authentication prompt, authenti

> :warning: You need a real device to test Local Authentication for iOS. Local Authentication is not available in simulators.

### Error taxonomy

Every error the SDK throws extends `AuthError` and carries a **normalized, platform-agnostic**
`type`. Switch on `type` — never on `code` — and your error handling behaves identically on iOS,
Android, and web.

| Property | Use it for |
| --------- | ---------------------------------------------------------------------------------------------------------- |
| `type` | **Control flow.** A normalized code, stable across platforms. Compare against the `…ErrorCodes` constants. |
| `code` | **Diagnostics.** The raw code from the underlying platform SDK or wire response. Varies by platform. |
Comment on lines +673 to +680

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Limit the type guidance to normalized error subclasses.

Custom Token Exchange throws AuthError and uses OAuth error values in code, as shown in EXAMPLES.md lines 1005-1023. The instruction to “never” switch on code conflicts with that supported flow.

State that consumers must use type for normalized error subclasses and use code for generic AuthError flows such as Custom Token Exchange.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 673 - 680, Update the README error-handling guidance
to restrict `type`-based control flow to normalized error subclasses. Clarify
that generic `AuthError` flows, including Custom Token Exchange, should inspect
`code` for OAuth error values, while preserving `code` as the raw platform or
wire diagnostic for normalized errors.

| `message` | Human-readable description. Not stable — do not parse it. |
| `status` | HTTP status, when the failure came from an HTTP response (`0` otherwise). |

Each error class ships a companion constants object and a matching TypeScript union, so a `switch`
on `type` is exhaustively checked at compile time:

| Error class | Constants | Type union | Thrown by |
| ------------------------- | ------------------------------ | ---------------------------------- | ----------------------------------------------- |
| `WebAuthError` | `WebAuthErrorCodes` | `WebAuthErrorCode` | `webAuth.authorize()`, `webAuth.clearSession()` |
| `CredentialsManagerError` | `CredentialsManagerErrorCodes` | `CredentialsManagerErrorCode` | `credentialsManager.*` |
| `MfaError` | `MfaErrorCodes` | `MfaErrorCode` | `mfa.*` |
| `PasskeyError` | `PasskeyErrorCodes` | `PasskeyErrorCode` | passkey signup/login and passkey enrollment |
| `MyAccountError` | `MyAccountErrorCodes` | `MyAccountErrorCode` | `myAccount.*` |
| `DPoPError` | `DPoPErrorCodes` | `DPoPErrorCode` | `getDPoPHeaders()` and DPoP key handling |
| `TimeoutError` | — | `type` is always `'TIMEOUT_ERROR'` | HTTP requests exceeding `timeout` |

```typescript
import { WebAuthError, WebAuthErrorCodes } from 'react-native-auth0';
import type { WebAuthErrorCode } from 'react-native-auth0';

function describe(type: WebAuthErrorCode): string {
switch (type) {
case WebAuthErrorCodes.USER_CANCELLED:
return 'Cancelled';
case WebAuthErrorCodes.NETWORK_ERROR:
return 'Offline';
default:
return 'Login failed';
}
}
Comment on lines +684 to +710

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '675,715p' README.md
printf '\n--- related error-code definitions and examples ---\n'
rg -n -C 3 "WebAuthErrorCode|WebAuthErrorCodes|exhaustively|switch.*type" src README.md EXAMPLES.md

Repository: auth0/react-native-auth0

Length of output: 35642


🏁 Script executed:

command -v tsc || true
command -v npx || true
if command -v tsc >/dev/null 2>&1; then
  tmpdir="$(mktemp -d)"
  cat >"$tmpdir/exhaustiveness.ts" <<'TS'
type Code = 'USER_CANCELLED' | 'NETWORK_ERROR' | 'ACCESS_DENIED';

function withDefault(type: Code): string {
  switch (type) {
    case 'USER_CANCELLED':
      return 'Cancelled';
    default:
      return 'Fallback';
  }
}

function withNever(type: Code): string {
  switch (type) {
    case 'USER_CANCELLED':
      return 'Cancelled';
    case 'NETWORK_ERROR':
      return 'Offline';
    case 'ACCESS_DENIED':
      return 'Denied';
    default: {
      const unreachable: never = type;
      return unreachable;
    }
  }
}
TS
  tsc --strict --noEmit "$tmpdir/exhaustiveness.ts"
  rm -rf "$tmpdir"
fi

Repository: auth0/react-native-auth0

Length of output: 342


🏁 Script executed:

tmpdir="$(mktemp -d)"
cat >"$tmpdir/exhaustiveness.ts" <<'TS'
type Code = 'USER_CANCELLED' | 'NETWORK_ERROR' | 'ACCESS_DENIED';

function withDefault(type: Code): string {
  switch (type) {
    case 'USER_CANCELLED':
      return 'Cancelled';
    default:
      return 'Fallback';
  }
}

function withNever(type: Code): string {
  switch (type) {
    case 'USER_CANCELLED':
      return 'Cancelled';
    case 'NETWORK_ERROR':
      return 'Offline';
    case 'ACCESS_DENIED':
      return 'Denied';
    default: {
      const unreachable: never = type;
      return unreachable;
    }
  }
}
TS
tsc --ignoreConfig --strict --noEmit "$tmpdir/exhaustiveness.ts"
rm -rf "$tmpdir"

Repository: auth0/react-native-auth0

Length of output: 162


Do not describe this switch as exhaustive.

The default branch accepts omitted WebAuthErrorCode cases. State that the union supports exhaustive handling, or show a never guard with every code handled.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 684 - 710, Update the README error-handling example
and surrounding description so it does not call the shown switch exhaustive
while it contains a default branch. Either describe WebAuthErrorCode as
supporting exhaustive handling, or handle every code explicitly and add a never
guard; anchor the change to the WebAuthErrorCode/WebAuthErrorCodes example.

```

`Auth0ErrorCode` is the union of all of the above. Prefer the specific union when handling one error
class — it keeps `switch` statements exhaustive and rejects codes that cannot occur there. Reach for
`Auth0ErrorCode` only in generic code such as logging or telemetry.

> **Stability.** These constants, their unions, and the `type` values they contain are the public
> error contract. Values will not be removed or renamed outside a major version.

`MyAccountError` is the one class with an extra property: the My Account API reports failures as
[RFC 7807](https://datatracker.ietf.org/doc/html/rfc7807) type URIs, so `type` holds the normalized
code while `typeUri` preserves the original URI (e.g. `https://auth0.com/api-errors/A0E-401`) for
logging and support tickets.
Comment on lines +720 to +723

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use the full opaque My Account type value in both examples.

My Account API error types use the A0E-&lt;status&gt;-&lt;numeric&gt; format. A0E-401 omits the numeric component and can mislead users about the raw typeUri value.

  • README.md#L720-L723: change the raw URI example to include a numeric suffix, such as A0E-401-0001.
  • EXAMPLES.md#L1845-L1859: change the typeUri output example to include the same full opaque type format.

Based on learnings, web My Account errors use opaque A0E-&lt;status&gt;-&lt;numeric&gt; type values.

📍 Affects 2 files
  • README.md#L720-L723 (this comment)
  • EXAMPLES.md#L1845-L1859
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README.md` around lines 720 - 723, Update the My Account error examples to
use the full opaque A0E-&lt;status&gt;-&lt;numeric&gt; type format: in README.md
lines 720-723, change the raw URI example to a value such as A0E-401-0001; in
EXAMPLES.md lines 1845-1859, update the typeUri output to use the same complete
format.

Source: Learnings


### Credentials Manager errors

The Credentials Manager will only throw `CredentialsManagerError` exceptions. You can find more information in the details property of the exception.
Expand Down
4 changes: 2 additions & 2 deletions src/Auth0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import type { IMfaClient } from './core/interfaces/IMfaClient';
import { Auth0ClientFactory } from './factory/Auth0ClientFactory';
import type {
Auth0Options,
DPoPHeadersParams,
DPoPHeadersParameters,
CustomTokenExchangeParameters,
PasskeySignupChallengeParameters,
PasskeyLoginChallengeParameters,
Expand Down Expand Up @@ -122,7 +122,7 @@ class Auth0 {
* }
* ```
*/
getDPoPHeaders(params: DPoPHeadersParams) {
getDPoPHeaders(params: DPoPHeadersParameters) {
return this.client.getDPoPHeaders(params);
}

Expand Down
213 changes: 213 additions & 0 deletions src/__tests__/publicApiSurface.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import * as path from 'path';
import * as ts from 'typescript';

/**
* The frozen public API surface of `src/index.ts`.
*
* This list is the stable public contract for v6. Adding an entry is a minor
* change; **removing or renaming an entry is a breaking change** and must be
* treated as such (major version, deprecation cycle, changelog entry).
*
* If this test fails, do not "fix" it by regenerating the list. Confirm the
* change to the surface is intentional and versioned appropriately first.
*/
const FROZEN_PUBLIC_API = [
'ApiCredentials',
'Auth0',
'Auth0ContextInterface',
'Auth0ErrorCode',
'Auth0Options',
'Auth0Provider',
'AuthError',
'AuthState',
'AuthenticationMethod',
'AuthenticationMethodType',
'AuthenticationMethodTypes',
'AuthorizeUrlParameters',
'BiometricPolicy',
'ClearSessionParameters',
'ConfirmOTPEnrollmentParameters',
'ConfirmPushNotificationEnrollmentParameters',
'ConfirmRecoveryCodeEnrollmentParameters',
'CreateUserParameters',
'Credentials',
'CredentialsManagerError',
'CredentialsManagerErrorCode',
'CredentialsManagerErrorCodes',
'CustomTokenExchangeParameters',
'DPoPError',
'DPoPErrorCode',
'DPoPErrorCodes',
'DPoPHeadersParameters',
'DPoPHeadersParams',
'DeleteAuthenticationMethodByIdParameters',
'DeliveryMethod',
'EnrollEmailParameters',
'EnrollPasskeyParameters',
'EnrollPhoneParameters',
'EnrollPushNotificationParameters',
'EnrollRecoveryCodeParameters',
'EnrollTOTPParameters',
'EnrollmentChallenge',
'ExchangeNativeSocialParameters',
'ExchangeParameters',
'Factor',
'GetAuthenticationMethodByIdParameters',
'GetAuthenticationMethodsParameters',
'GetFactorsParameters',
'GetTokenByPasskeyParameters',
'IAuth0Client',
'IAuthenticationProvider',
'ICredentialsManager',
'IMfaClient',
'IMyAccountClient',
'IPasswordlessClient',
'IWebAuthProvider',
'LocalAuthenticationLevel',
'LocalAuthenticationOptions',
'LocalAuthenticationStrategy',
'LoginEmailParameters',
'LoginSmsParameters',
'LogoutUrlParameters',
'MfaAuthenticator',
'MfaChallengeResult',
'MfaChallengeWithAuthenticatorParameters',
'MfaEnrollEmailParameters',
'MfaEnrollOtpParameters',
'MfaEnrollParameters',
'MfaEnrollPushParameters',
'MfaEnrollSmsParameters',
'MfaEnrollVoiceParameters',
'MfaEnrollmentChallenge',
'MfaError',
'MfaErrorCode',
'MfaErrorCodes',
'MfaFactor',
'MfaFactorType',
'MfaGetAuthenticatorsParameters',
'MfaOobEnrollmentChallenge',
'MfaPushEnrollmentChallenge',
'MfaRecoveryCodeEnrollmentChallenge',
'MfaRequiredErrorPayload',
'MfaRequirements',
'MfaTotpEnrollmentChallenge',
'MfaVerifyOobParameters',
'MfaVerifyOtpParameters',
'MfaVerifyParameters',
'MfaVerifyRecoveryCodeParameters',
'MyAccountError',
'MyAccountErrorCode',
'MyAccountErrorCodes',
'NativeAuthorizeOptions',
'NativeClearSessionOptions',
'PasskeyAuthenticationMethod',
'PasskeyChallengeResponse',
'PasskeyEnrollmentChallengeParameters',
'PasskeyEnrollmentChallengeResponse',
'PasskeyError',
'PasskeyErrorCode',
'PasskeyErrorCodes',
'PasskeyLoginChallengeParameters',
'PasskeySignupChallengeParameters',
'PasswordRealmParameters',
'PasswordlessChallenge',
'PasswordlessChallengeEmailParameters',
'PasswordlessChallengePhoneParameters',
'PasswordlessDeliveryMethod',
'PasswordlessEmailParameters',
'PasswordlessLoginOtpParameters',
'PasswordlessSmsParameters',
'PreferredAuthenticationMethods',
'RecoveryCodeEnrollmentChallenge',
'RefreshTokenParameters',
'ResetPasswordParameters',
'RevokeOptions',
'SSOExchangeParameters',
'SafariViewControllerPresentationStyle',
'SessionTransferCredentials',
'TOTPEnrollmentChallenge',
'TimeoutError',
'TokenType',
'UpdateAuthenticationMethodByIdParameters',
'User',
'UserInfoParameters',
'WebAuthError',
'WebAuthErrorCode',
'WebAuthErrorCodes',
'WebAuthorizeOptions',
'WebAuthorizeParameters',
'WebClearSessionOptions',
'default',
'parseIdToken',
'useAuth0',
];

/**
* Resolves the actual exports of `src/index.ts` through the TypeScript compiler.
*
* A runtime `import * from '../index'` cannot be used here: most of the surface
* is type-only and erased at runtime, so it would silently miss regressions in
* the exported types — which are just as breaking as a missing class.
*/
function resolvePublicExports(): string[] {
const projectRoot = path.resolve(__dirname, '..', '..');
const configPath = path.join(projectRoot, 'tsconfig.json');
const entryPoint = path.join(projectRoot, 'src', 'index.ts');

const config = ts.getParsedCommandLineOfConfigFile(configPath, {}, {
...ts.sys,
onUnRecoverableConfigFileDiagnostic: (diagnostic) => {
throw new Error(
ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')
);
},
} as ts.ParseConfigFileHost);

if (!config) {
throw new Error(`Unable to parse ${configPath}`);
}

const program = ts.createProgram([entryPoint], config.options);
const checker = program.getTypeChecker();
const sourceFile = program.getSourceFile(entryPoint);

if (!sourceFile) {
throw new Error(`Unable to load ${entryPoint}`);
}

const moduleSymbol = checker.getSymbolAtLocation(sourceFile);

if (!moduleSymbol) {
throw new Error(`${entryPoint} is not a module`);
}

return checker
.getExportsOfModule(moduleSymbol)
.map((symbol) => symbol.getName())
.sort();
}

describe('public API surface', () => {
// Building a full TS program is slower than a normal unit test.
const actual = resolvePublicExports();

it('matches the frozen v6 contract exactly', () => {
expect(actual).toEqual([...FROZEN_PUBLIC_API].sort());
});

it('exports nothing that has been removed from the contract', () => {
const frozen = new Set(FROZEN_PUBLIC_API);
expect(actual.filter((name) => !frozen.has(name))).toEqual([]);
});

it('still exports everything the contract promises', () => {
const exported = new Set(actual);
expect(FROZEN_PUBLIC_API.filter((name) => !exported.has(name))).toEqual([]);
});

it('exposes the default export under a named alias', () => {
// `default` alone is awkward for consumers doing `import { Auth0 }`.
expect(actual).toContain('default');
expect(actual).toContain('Auth0');
});
Comment on lines +208 to +212

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- target file outline ---'
ast-grep outline src/__tests__/publicApiSurface.spec.ts || true

printf '%s\n' '--- target test context ---'
sed -n '1,250p' src/__tests__/publicApiSurface.spec.ts

printf '%s\n' '--- Auth0 export declarations and related tests ---'
rg -n --glob '!lib/**' --glob '!docs/**' --glob '!coverage/**' \
  "export .*Auth0|default as Auth0|class Auth0|const Auth0|interface .*Auth0|publicApiSurface|typeChecker|getAliasedSymbol" \
  src package.json tsconfig*.json

Repository: auth0/react-native-auth0

Length of output: 9982


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- relevant source declarations ---'
sed -n '1,125p' src/index.ts
sed -n '1,55p' src/Auth0.ts
sed -n '205,225p' src/Auth0.ts

printf '%s\n' '--- TypeScript/test configuration ---'
cat package.json
printf '%s\n' '--- tsconfig ---'
cat tsconfig.json

printf '%s\n' '--- compiler API availability ---'
node - <<'JS'
try {
  const ts = require('typescript');
  console.log(JSON.stringify({ available: true, version: ts.version }));
} catch (error) {
  console.log(JSON.stringify({ available: false, message: String(error) }));
}
JS

Repository: auth0/react-native-auth0

Length of output: 11914


🏁 Script executed:

#!/bin/bash
set -eu

node - <<'JS'
const path = require('path');
const ts = require('typescript');

const projectRoot = process.cwd();
const configPath = path.join(projectRoot, 'tsconfig.json');
const entryPoint = path.join(projectRoot, 'src', 'index.ts');

const config = ts.getParsedCommandLineOfConfigFile(configPath, {}, {
  ...ts.sys,
  onUnRecoverableConfigFileDiagnostic(diagnostic) {
    throw new Error(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
  },
});

if (!config) throw new Error(`Unable to parse ${configPath}`);

const program = ts.createProgram([entryPoint], config.options);
const checker = program.getTypeChecker();
const sourceFile = program.getSourceFile(entryPoint);
if (!sourceFile) throw new Error(`Unable to load ${entryPoint}`);

const moduleSymbol = checker.getSymbolAtLocation(sourceFile);
if (!moduleSymbol) throw new Error('Entry point is not a module');

const exports = new Map(
  checker.getExportsOfModule(moduleSymbol).map((symbol) => [symbol.getName(), symbol])
);
const defaultExport = exports.get('default');
const namedAuth0Export = exports.get('Auth0');

function describe(symbol) {
  if (!symbol) return null;
  const aliased = symbol.flags & ts.SymbolFlags.Alias
    ? checker.getAliasedSymbol(symbol)
    : symbol;
  return {
    name: symbol.getName(),
    flags: symbol.flags,
    isAlias: Boolean(symbol.flags & ts.SymbolFlags.Alias),
    aliasTargetName: aliased.getName(),
    aliasTargetId: aliased.id,
    declarations: (aliased.declarations ?? []).map((declaration) => ({
      file: path.relative(projectRoot, declaration.getSourceFile().fileName),
      kind: ts.SyntaxKind[declaration.kind],
      text: declaration.getText(),
    })),
  };
}

console.log(JSON.stringify({
  defaultExport: describe(defaultExport),
  namedAuth0Export: describe(namedAuth0Export),
  resolvedSymbolsIdentical:
    defaultExport && namedAuth0Export &&
    checker.getAliasedSymbol(defaultExport) === checker.getAliasedSymbol(namedAuth0Export),
}, null, 2));
JS

Repository: auth0/react-native-auth0

Length of output: 12684


Assert that Auth0 resolves to the default export.

The current assertions only check that both names exist. Resolve both symbols with the TypeScript checker and compare their aliased symbols.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/__tests__/publicApiSurface.spec.ts` around lines 208 - 212, Update the
test around “exposes the default export under a named alias” to use the
TypeScript checker to resolve the symbols for “Auth0” and “default”, then assert
that their aliased symbols are identical; retain the existing surface-presence
checks only if needed for setup.

});
Loading
Loading