diff --git a/.changeset/new-paws-cut.md b/.changeset/new-paws-cut.md new file mode 100644 index 000000000..37daf7bf3 --- /dev/null +++ b/.changeset/new-paws-cut.md @@ -0,0 +1,7 @@ +--- +'@asgardeo/javascript': patch +'@asgardeo/nextjs': patch +'@asgardeo/react': patch +--- + +Introduce Agent token related functions diff --git a/packages/javascript/package.json b/packages/javascript/package.json index 62adfbf74..35fa5ca95 100644 --- a/packages/javascript/package.json +++ b/packages/javascript/package.json @@ -54,7 +54,8 @@ }, "dependencies": { "@asgardeo/i18n": "workspace:*", - "tslib": "2.8.1" + "tslib": "2.8.1", + "jose": "^5.2.0" }, "publishConfig": { "access": "public" diff --git a/packages/javascript/src/AsgardeoJavaScriptClient.ts b/packages/javascript/src/AsgardeoJavaScriptClient.ts index f6746bf67..d1038ac81 100644 --- a/packages/javascript/src/AsgardeoJavaScriptClient.ts +++ b/packages/javascript/src/AsgardeoJavaScriptClient.ts @@ -16,80 +16,235 @@ * under the License. */ +import {AsgardeoAuthClient} from './__legacy__/client'; +import {AuthClientConfig} from './__legacy__/models/client-config'; +import executeEmbeddedSignInFlow from './api/executeEmbeddedSignInFlow'; +import initializeEmbeddedSignInFlow from './api/initializeEmbeddedSignInFlow'; +import {DefaultCacheStore} from './DefaultCacheStore'; +import {DefaultCrypto} from './DefaultCrypto'; +import {AgentConfig} from './models/agent'; +import {AuthCodeResponse} from './models/auth-code-response'; import {AsgardeoClient} from './models/client'; import {Config, SignInOptions, SignOutOptions, SignUpOptions} from './models/config'; -import {EmbeddedFlowExecuteRequestPayload, EmbeddedFlowExecuteResponse} from './models/embedded-flow'; -import {EmbeddedSignInFlowHandleRequestPayload} from './models/embedded-signin-flow'; +import {Crypto} from './models/crypto'; +import { + EmbeddedFlowExecuteRequestConfig, + EmbeddedFlowExecuteRequestPayload, + EmbeddedFlowExecuteResponse, +} from './models/embedded-flow'; +import { + EmbeddedSignInFlowAuthenticator, + EmbeddedSignInFlowHandleResponse, + EmbeddedSignInFlowInitiateResponse, + EmbeddedSignInFlowStatus, +} from './models/embedded-signin-flow'; import {AllOrganizationsApiResponse, Organization} from './models/organization'; import {Storage} from './models/store'; import {TokenExchangeRequestConfig, TokenResponse} from './models/token'; import {User, UserProfile} from './models/user'; +import StorageManager from './StorageManager'; -/** - * Base class for implementing Asgardeo clients. - * This class provides the core functionality for managing user authentication and sessions. - * - * @typeParam T - Configuration type that extends Config. - */ -abstract class AsgardeoJavaScriptClient implements AsgardeoClient { - abstract switchOrganization(organization: Organization, sessionId?: string): Promise; +class AsgardeoJavaScriptClient implements AsgardeoClient { + private cacheStore: Storage; + + private cryptoUtils: Crypto; + + private auth: AsgardeoAuthClient; + + private storageManager: StorageManager; - abstract initialize(config: T, storage?: Storage): Promise; + private baseURL: string; - abstract reInitialize(config: Partial): Promise; + constructor(config?: AuthClientConfig, cacheStore?: Storage, cryptoUtils?: Crypto) { + this.cacheStore = cacheStore ?? new DefaultCacheStore(); + this.cryptoUtils = cryptoUtils ?? new DefaultCrypto(); + this.auth = new AsgardeoAuthClient(); - abstract getUser(options?: any): Promise; + if (config) { + this.auth.initialize(config, this.cacheStore, this.cryptoUtils); + this.storageManager = this.auth.getStorageManager(); + } - abstract getAllOrganizations(options?: any, sessionId?: string): Promise; + this.baseURL = config?.baseUrl ?? ''; + } - abstract getMyOrganizations(options?: any, sessionId?: string): Promise; + /* eslint-disable class-methods-use-this, @typescript-eslint/no-unused-vars */ + switchOrganization(_organization: Organization, _sessionId?: string): Promise { + throw new Error('Method not implemented.'); + } - abstract getCurrentOrganization(sessionId?: string): Promise; + initialize(_config: T, _storage?: Storage): Promise { + throw new Error('Method not implemented.'); + } - abstract getUserProfile(options?: any): Promise; + reInitialize(_config: Partial): Promise { + throw new Error('Method not implemented.'); + } - abstract isLoading(): boolean; + getUser(_options?: any): Promise { + throw new Error('Method not implemented.'); + } - abstract isSignedIn(): Promise; + getAllOrganizations(_options?: any, _sessionId?: string): Promise { + throw new Error('Method not implemented.'); + } - abstract updateUserProfile(payload: any, userId?: string): Promise; + getMyOrganizations(_options?: any, _sessionId?: string): Promise { + throw new Error('Method not implemented.'); + } - abstract getConfiguration(): T; + getCurrentOrganization(_sessionId?: string): Promise { + throw new Error('Method not implemented.'); + } - abstract exchangeToken(config: TokenExchangeRequestConfig, sessionId?: string): Promise; + getUserProfile(_options?: any): Promise { + throw new Error('Method not implemented.'); + } - abstract signIn( - options?: SignInOptions, - sessionId?: string, - onSignInSuccess?: (afterSignInUrl: string) => void, - ): Promise; - abstract signIn( - payload: EmbeddedSignInFlowHandleRequestPayload, - request: Request, - sessionId?: string, - onSignInSuccess?: (afterSignInUrl: string) => void, - ): Promise; + isLoading(): boolean { + throw new Error('Method not implemented.'); + } - abstract signInSilently(options?: SignInOptions): Promise; + isSignedIn(): Promise { + throw new Error('Method not implemented.'); + } - abstract signOut(options?: SignOutOptions, afterSignOut?: (afterSignOutUrl: string) => void): Promise; - abstract signOut( - options?: SignOutOptions, - sessionId?: string, - afterSignOut?: (afterSignOutUrl: string) => void, - ): Promise; + updateUserProfile(_payload: any, _userId?: string): Promise { + throw new Error('Method not implemented.'); + } + + getConfiguration(): T { + throw new Error('Method not implemented.'); + } + + exchangeToken(_config: TokenExchangeRequestConfig, _sessionId?: string): Promise { + throw new Error('Method not implemented.'); + } - abstract signUp(options?: SignUpOptions): Promise; - abstract signUp(payload: EmbeddedFlowExecuteRequestPayload): Promise; - abstract signUp(payload?: unknown): Promise | Promise; + signInSilently(_options?: SignInOptions): Promise { + throw new Error('Method not implemented.'); + } - abstract getAccessToken(sessionId?: string): Promise; + getAccessToken(_sessionId?: string): Promise { + throw new Error('Method not implemented.'); + } - abstract clearSession(sessionId?: string): void; + clearSession(_sessionId?: string): void { + throw new Error('Method not implemented.'); + } - abstract setSession(sessionData: Record, sessionId?: string): Promise; + setSession(_sessionData: Record, _sessionId?: string): Promise { + throw new Error('Method not implemented.'); + } - abstract decodeJwtToken>(token: string): Promise; + decodeJwtToken>(_token: string): Promise { + throw new Error('Method not implemented.'); + } + + signIn(_options?: SignInOptions): Promise { + throw new Error('Method not implemented.'); + } + + signOut( + _options?: SignOutOptions, + _sessionIdOrAfterSignOut?: string | ((afterSignOutUrl: string) => void), + _afterSignOut?: (afterSignOutUrl: string) => void, + ): Promise { + throw new Error('Method not implemented.'); + } + + signUp(options?: SignUpOptions): Promise; + + signUp(payload: EmbeddedFlowExecuteRequestPayload): Promise; + + signUp( + _optionsOrPayload?: SignUpOptions | EmbeddedFlowExecuteRequestPayload, + ): Promise { + throw new Error('Method not implemented.'); + } + /* eslint-enable class-methods-use-this, @typescript-eslint/no-unused-vars */ + + public async getAgentToken(agentConfig: AgentConfig): Promise { + const customParam: Record = { + response_mode: 'direct', + }; + + const authorizeURL: URL = new URL(await this.auth.getSignInUrl(customParam)); + + const authorizeResponse: EmbeddedSignInFlowInitiateResponse = await initializeEmbeddedSignInFlow({ + payload: Object.fromEntries(authorizeURL.searchParams.entries()), + url: `${authorizeURL.origin}${authorizeURL.pathname}`, + }); + + const authenticatorName: string = agentConfig.authenticatorName ?? AgentConfig.DEFAULT_AUTHENTICATOR_NAME; + + const targetAuthenticator: EmbeddedSignInFlowAuthenticator | undefined = + authorizeResponse.nextStep.authenticators.find( + (auth: EmbeddedSignInFlowAuthenticator) => auth.authenticator === authenticatorName, + ); + + if (!targetAuthenticator) { + throw new Error(`Authenticator '${authenticatorName}' not found among authentication steps.`); + } + + const authnRequest: EmbeddedFlowExecuteRequestConfig = { + baseUrl: this.baseURL, + payload: { + flowId: authorizeResponse.flowId, + selectedAuthenticator: { + authenticatorId: targetAuthenticator.authenticatorId, + params: { + password: agentConfig.agentSecret, + username: agentConfig.agentID, + }, + }, + }, + }; + + const authnResponse: EmbeddedSignInFlowHandleResponse = await executeEmbeddedSignInFlow(authnRequest); + + if (authnResponse.flowStatus !== EmbeddedSignInFlowStatus.SuccessCompleted) { + throw new Error('Agent authentication failed.'); + } + + return this.auth.requestAccessToken( + authnResponse.authData['code'], + authnResponse.authData['session_state'], + authnResponse.authData['state'], + ); + } + + public async getOBOSignInURL(agentConfig: AgentConfig): Promise { + const customParam: Record = { + requested_actor: agentConfig.agentID, + }; + + const authURL: string | undefined = await this.auth.getSignInUrl(customParam); + + if (authURL) { + return authURL.toString(); + } + + throw new Error('Could not build Authorize URL'); + } + + public async getOBOToken(agentConfig: AgentConfig, authCodeResponse: AuthCodeResponse): Promise { + const agentToken: TokenResponse = await this.getAgentToken(agentConfig); + + const tokenRequestConfig: {params: {actor_token: string}} = { + params: { + actor_token: agentToken.accessToken, + }, + }; + + return this.auth.requestAccessToken( + authCodeResponse.code, + authCodeResponse.session_state, + authCodeResponse.state, + undefined, + tokenRequestConfig, + ); + } } export default AsgardeoJavaScriptClient; diff --git a/packages/javascript/src/DefaultCacheStore.ts b/packages/javascript/src/DefaultCacheStore.ts new file mode 100644 index 000000000..f749658c1 --- /dev/null +++ b/packages/javascript/src/DefaultCacheStore.ts @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export class DefaultCacheStore implements Storage { + private cache: Map; + + constructor() { + this.cache = new Map(); + } + + public get length(): number { + return this.cache.size; + } + + public getItem(key: string): string | null { + return this.cache.get(key) ?? null; + } + + public setItem(key: string, value: string): void { + this.cache.set(key, value); + } + + public removeItem(key: string): void { + this.cache.delete(key); + } + + public clear(): void { + this.cache.clear(); + } + + public key(index: number): string | null { + const keys: string[] = Array.from(this.cache.keys()); + return keys[index] ?? null; + } + + public async setData(key: string, value: string): Promise { + this.cache.set(key, value); + } + + public async getData(key: string): Promise { + return this.cache.get(key) ?? '{}'; + } + + public async removeData(key: string): Promise { + this.cache.delete(key); + } +} diff --git a/packages/javascript/src/DefaultCrypto.ts b/packages/javascript/src/DefaultCrypto.ts new file mode 100644 index 000000000..f165c5f2b --- /dev/null +++ b/packages/javascript/src/DefaultCrypto.ts @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as jose from 'jose'; +import {Crypto, JWKInterface} from './models/crypto'; + +/** + * Default implementation of the Crypto interface using the 'jose' library + * and the native Web Crypto API. + */ +export class DefaultCrypto implements Crypto { + // eslint-disable-next-line class-methods-use-this + public base64URLDecode(value: string): string { + const decodedArray: Uint8Array = jose.base64url.decode(value); + return new TextDecoder().decode(decodedArray); + } + + // eslint-disable-next-line class-methods-use-this + public base64URLEncode(value: Uint8Array): string { + return jose.base64url.encode(value); + } + + // eslint-disable-next-line class-methods-use-this + public generateRandomBytes(length: number): Uint8Array { + return crypto.getRandomValues(new Uint8Array(length)); + } + + // eslint-disable-next-line class-methods-use-this + public async hashSha256(data: string): Promise { + const encoder: TextEncoder = new TextEncoder(); + const dataBuffer: Uint8Array = encoder.encode(data); + const hashBuffer: ArrayBuffer = await crypto.subtle.digest('SHA-256', dataBuffer); + + return new Uint8Array(hashBuffer); + } + + // eslint-disable-next-line class-methods-use-this + public async verifyJwt( + idToken: string, + jwk: JWKInterface, + algorithms: string[], + clientId: string, + issuer: string, + subject: string, + clockTolerance?: number, + validateJwtIssuer: boolean = true, + ): Promise { + const key: jose.KeyLike | Uint8Array = await jose.importJWK(jwk as jose.JWK); + + await jose.jwtVerify(idToken, key, { + algorithms, + audience: clientId, + clockTolerance, + issuer: validateJwtIssuer ? issuer : undefined, + subject, + }); + + return true; + } +} diff --git a/packages/javascript/src/IsomorphicCrypto.ts b/packages/javascript/src/IsomorphicCrypto.ts index 2e68015c1..c738fc586 100644 --- a/packages/javascript/src/IsomorphicCrypto.ts +++ b/packages/javascript/src/IsomorphicCrypto.ts @@ -43,8 +43,9 @@ export class IsomorphicCrypto { * * @returns - code challenge. */ - public getCodeChallenge(verifier: string): string { - return this.cryptoUtils.base64URLEncode(this.cryptoUtils.hashSha256(verifier)); + public async getCodeChallenge(verifier: string): Promise { + const hashed: T = await this.cryptoUtils.hashSha256(verifier); + return this.cryptoUtils.base64URLEncode(hashed); } /** diff --git a/packages/javascript/src/__legacy__/client.ts b/packages/javascript/src/__legacy__/client.ts index 043829737..4ae0dd79c 100644 --- a/packages/javascript/src/__legacy__/client.ts +++ b/packages/javascript/src/__legacy__/client.ts @@ -242,7 +242,7 @@ export class AsgardeoAuthClient { if (configData.enablePKCE) { codeVerifier = this.cryptoHelper?.getCodeVerifier(); - codeChallenge = this.cryptoHelper?.getCodeChallenge(codeVerifier); + codeChallenge = await this.cryptoHelper?.getCodeChallenge(codeVerifier); await this.storageManager.setTemporaryDataParameter(pkceKey, codeVerifier, userId); } diff --git a/packages/javascript/src/index.ts b/packages/javascript/src/index.ts index 7cc97e360..e549d9ea4 100644 --- a/packages/javascript/src/index.ts +++ b/packages/javascript/src/index.ts @@ -149,6 +149,8 @@ export { SignUpOptions, } from './models/config'; export {TokenResponse, IdToken, TokenExchangeRequestConfig} from './models/token'; +export {AgentConfig} from './models/agent'; +export {AuthCodeResponse} from './models/auth-code-response'; export {Crypto, JWKInterface} from './models/crypto'; export {OAuthResponseMode} from './models/oauth-response'; export { diff --git a/packages/javascript/src/models/agent.ts b/packages/javascript/src/models/agent.ts new file mode 100644 index 000000000..abeb657ff --- /dev/null +++ b/packages/javascript/src/models/agent.ts @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Interface representing the configuration for an agent. + */ +export interface AgentConfig { + /** + * The unique identifier for the agent + */ + agentID: string; + /** + * The secret credential for the agent + */ + agentSecret: string; + /** + * The authenticator name to match during the embedded sign-in flow. + * Defaults to {@link AgentConfig.DEFAULT_AUTHENTICATOR_NAME} if not provided. + */ + authenticatorName?: string; +} + +/** + * Namespace that holds constants related to {@link AgentConfig}. + */ +export namespace AgentConfig { + /** + * Default authenticator name used when none is specified. + */ + export const DEFAULT_AUTHENTICATOR_NAME: string = 'Username & Password'; +} diff --git a/packages/javascript/src/models/auth-code-response.ts b/packages/javascript/src/models/auth-code-response.ts new file mode 100644 index 000000000..f3172a369 --- /dev/null +++ b/packages/javascript/src/models/auth-code-response.ts @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Interface representing the authorization code response from the OAuth2/OIDC flow. + */ +export interface AuthCodeResponse { + /** + * The authorization code returned from the authorization endpoint + */ + code: string; + /** + * The session state identifier + */ + session_state: string; + /** + * The state parameter returned from the authorization endpoint + */ + state: string; +} diff --git a/packages/javascript/src/models/crypto.ts b/packages/javascript/src/models/crypto.ts index cd5bffba6..4ecab6e40 100644 --- a/packages/javascript/src/models/crypto.ts +++ b/packages/javascript/src/models/crypto.ts @@ -85,7 +85,7 @@ export interface Crypto { * * @returns Hashed data. */ - hashSha256(data: string): T; + hashSha256(data: string): T | Promise; /** * Verify the provided JWT. diff --git a/packages/nextjs/src/AsgardeoNextClient.ts b/packages/nextjs/src/AsgardeoNextClient.ts index 537871777..1f7e16277 100644 --- a/packages/nextjs/src/AsgardeoNextClient.ts +++ b/packages/nextjs/src/AsgardeoNextClient.ts @@ -412,7 +412,7 @@ class AsgardeoNextClient exte * otherwise falls back to legacy client method. */ // eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars - async getAccessToken(_sessionId?: string): Promise { + override async getAccessToken(_sessionId?: string): Promise { const {default: getAccessToken} = await import('./server/actions/getAccessToken'); const token: string | undefined = await getAccessToken(); @@ -573,7 +573,7 @@ class AsgardeoNextClient exte } // eslint-disable-next-line class-methods-use-this - public async clearSession(): Promise { + public override async clearSession(): Promise { throw new AsgardeoRuntimeError( 'Not implemented', 'AsgardeoNextClient-clearSession-NotImplementedError-001', diff --git a/packages/react/src/AsgardeoReactClient.ts b/packages/react/src/AsgardeoReactClient.ts index eb3d2884d..365ad2095 100644 --- a/packages/react/src/AsgardeoReactClient.ts +++ b/packages/react/src/AsgardeoReactClient.ts @@ -179,7 +179,7 @@ class AsgardeoReactClient e return this.withLoading(async () => this.asgardeo.getIdToken()); } - async getUserProfile(options?: any): Promise { + override async getUserProfile(options?: any): Promise { return this.withLoading(async () => { try { let baseUrl: string = options?.baseUrl; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dae95cfc2..ff484d7ba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -251,6 +251,9 @@ importers: '@asgardeo/i18n': specifier: workspace:* version: link:../i18n + jose: + specifier: ^5.2.0 + version: 5.10.0 tslib: specifier: 2.8.1 version: 2.8.1 @@ -321,7 +324,7 @@ importers: version: 8.57.0 next: specifier: 15.5.12 - version: 15.5.12(@babel/core@7.27.1)(@playwright/test@1.58.2)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(sass@1.92.1) + version: 15.5.12(@playwright/test@1.58.2)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(sass@1.92.1) prettier: specifier: 2.6.2 version: 2.6.2 @@ -9297,14 +9300,14 @@ snapshots: '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6) '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.1.6) eslint: 8.57.0 - eslint-config-airbnb: 19.0.4(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.37.5(eslint@8.57.0))(eslint@8.57.0) - eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0) - eslint-config-airbnb-typescript: 17.1.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0) + eslint-config-airbnb: 19.0.4(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.37.5(eslint@8.57.0))(eslint@8.57.0) + eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint@8.57.0) + eslint-config-airbnb-typescript: 17.1.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6))(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint@8.57.0) eslint-config-prettier: 8.10.0(eslint@8.57.0) eslint-plugin-eslint-plugin: 5.5.1(eslint@8.57.0) eslint-plugin-header: 3.1.1(eslint@8.57.0) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0) - eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.1.6) + eslint-plugin-jest: 27.9.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.0) eslint-plugin-node: 11.1.0(eslint@8.57.0) eslint-plugin-prettier: 4.2.5(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.6.2) @@ -10431,6 +10434,15 @@ snapshots: escape-string-regexp@4.0.0: {} + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint@8.57.0): + dependencies: + confusing-browser-globals: 1.0.11 + eslint: 8.57.0 + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0) + object.assign: 4.1.7 + object.entries: 1.1.9 + semver: 6.3.1 + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0): dependencies: confusing-browser-globals: 1.0.11 @@ -10440,13 +10452,13 @@ snapshots: object.entries: 1.1.9 semver: 6.3.1 - eslint-config-airbnb-typescript@17.1.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0): + eslint-config-airbnb-typescript@17.1.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6))(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint@8.57.0): dependencies: - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6) '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.1.6) eslint: 8.57.0 - eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0) + eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0) eslint-config-airbnb-typescript@17.1.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0): dependencies: @@ -10456,6 +10468,17 @@ snapshots: eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint@8.57.0) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0) + eslint-config-airbnb@19.0.4(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.37.5(eslint@8.57.0))(eslint@8.57.0): + dependencies: + eslint: 8.57.0 + eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0) + eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.0) + eslint-plugin-react: 7.37.5(eslint@8.57.0) + eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) + object.assign: 4.1.7 + object.entries: 1.1.9 + eslint-config-airbnb@19.0.4(eslint-plugin-import@2.32.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0))(eslint-plugin-jsx-a11y@6.10.2(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.37.5(eslint@8.57.0))(eslint@8.57.0): dependencies: eslint: 8.57.0 @@ -10573,12 +10596,12 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.1.6): + eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6): dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.57.0)(typescript@5.1.6) eslint: 8.57.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.7.2))(eslint@8.57.0)(typescript@5.7.2) + '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.1.6))(eslint@8.57.0)(typescript@5.1.6) transitivePeerDependencies: - supports-color - typescript @@ -11997,7 +12020,7 @@ snapshots: negotiator@1.0.0: {} - next@15.5.12(@babel/core@7.27.1)(@playwright/test@1.58.2)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(sass@1.92.1): + next@15.5.12(@playwright/test@1.58.2)(react-dom@19.1.4(react@19.1.4))(react@19.1.4)(sass@1.92.1): dependencies: '@next/env': 15.5.12 '@swc/helpers': 0.5.15 @@ -12005,7 +12028,7 @@ snapshots: postcss: 8.4.31 react: 19.1.4 react-dom: 19.1.4(react@19.1.4) - styled-jsx: 5.1.6(@babel/core@7.27.1)(react@19.1.4) + styled-jsx: 5.1.6(react@19.1.4) optionalDependencies: '@next/swc-darwin-arm64': 15.5.12 '@next/swc-darwin-x64': 15.5.12 @@ -13356,12 +13379,10 @@ snapshots: style-search@0.1.0: {} - styled-jsx@5.1.6(@babel/core@7.27.1)(react@19.1.4): + styled-jsx@5.1.6(react@19.1.4): dependencies: client-only: 0.0.1 react: 19.1.4 - optionalDependencies: - '@babel/core': 7.27.1 stylehacks@5.1.1(postcss@8.4.31): dependencies: @@ -14051,7 +14072,7 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.15.3 - '@vitest/browser': 3.1.3(playwright@1.55.1)(vite@6.4.1(@types/node@22.15.3)(jiti@2.6.0)(lightningcss@1.30.1)(sass-embedded@1.92.1)(sass@1.92.1)(terser@5.39.2)(tsx@4.21.0)(yaml@2.8.0))(vitest@3.1.3) + '@vitest/browser': 3.1.3(playwright@1.58.2)(vite@6.4.1(@types/node@22.15.3)(jiti@2.6.0)(lightningcss@1.30.1)(sass-embedded@1.92.1)(sass@1.92.1)(terser@5.39.2)(tsx@4.21.0)(yaml@2.8.0))(vitest@3.1.3) jsdom: 27.4.0 transitivePeerDependencies: - jiti